© 2023 iamfortress.net
- This document demonstrates how to build and deploy the fortress rbac with abac sample.
- The intent is to demonstrate using attributes to control role activation within an Apache Wicket Web app.
- For more info about the idea:
- SECTION 1. Prerequisites
- SECTION 2. Prepare Tomcat for Java EE Security
- SECTION 3. Prepare rbac-abac-sample package
- SECTION 4. Build and deploy rbac-abac-sample
- SECTION 5. Understand the security policy
- SECTION 6. Manually Test the RBAC with ABAC sample
- SECTION 7. Automatically Test the RBAC with ABAC sample (using Selenium)
- SECTION 8. Under the Hood (Learn how it works here)
- Java >= 17
- Apache Maven >= 3
- Apache Tomcat >= 10
- Basic LDAP server setup by completing one of these:
This sample web app uses Java EE security.
wget https://repo.maven.apache.org/maven2/org/apache/directory/fortress/fortress-realm-proxy/[version]/fortress-realm-proxy-[version].jar -P $TOMCAT_HOME/lib
- Where
$TOMCAT_HOME
points to the execution env.
Note: The realm proxy enables Tomcat container-managed security functions to call back to fortress.
sudo vi /usr/local/tomcat8/conf/tomcat-users.xml
<role rolename="manager-script"/>
<user username="tcmanager" password="m@nager123" roles="manager-script"/>
a. Download and extract from Github:
wget /~https://github.com/shawnmckinney/rbac-abac-sample/archive/master.zip
-- Or --
b. Or git clone
locally:
git clone /~https://github.com/shawnmckinney/rbac-abac-sample.git
cd rbac-abac-sample
a. Copy the example:
cp src/main/resources/fortress.properties.example src/main/resources/fortress.properties
b. Edit the file:
vi src/main/resources/fortress.properties
Pick either Apache Directory or OpenLDAP server:
c. Prepare fortress for ApacheDS usage:
# This param tells fortress what type of ldap server in use:
ldap.server.type=apacheds
# Use value from [Set Hostname Entry]:
host=localhost
# ApacheDS defaults to this:
port=10389
# These credentials are used for read/write access to all nodes under suffix:
admin.user=uid=admin,ou=system
admin.pw=secret
-- Or --
d. Prepare fortress for OpenLDAP usage:
# This param tells fortress what type of ldap server in use:
ldap.server.type=openldap
# Use value from [Set Hostname Entry]:
host=localhost
# OpenLDAP defaults to this:
port=389
# These credentials are used for read/write access to all nodes under suffix:
admin.user=cn=Manager,dc=example,dc=com
admin.pw=secret
mvn -version
This sample requires Java >= 11 and Maven >= 3 to be setup within the execution env.
mvn install -Dload.file
Build Notes:
-Dload.file
automatically loads the rbac-abac-sample security policy data into ldap.- This load needs to happen just once for the default test cases to work and may be dropped from future
mvn
commands.
a. If using autodeploy feature, verify the Tomcat auto-deploy options are set correctly in the pom.xml file:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>tomcat-maven-plugin</artifactId>
<version>1.0-beta-1</version>
<configuration>
...
<url>http://localhost:8080/manager/text</url>
<path>/${project.artifactId}</path>
<username>tcmanager</username>
<password>m@nager123</password>
</configuration>
</plugin>
b. Now, automatically deploy to tomcat server:
mvn clean tomcat:deploy
c. To automatically redeploy sample app:
mvn clean tomcat:redeploy
d. To manually deploy app to Tomcat:
cp target/rbac-abac-sample.war $TOMCAT_HOME/webapps
- Where
$TOMCAT_HOME
points to the execution env.
To gain full understanding, check out the file used to load it into the LDAP directory: rbac-abac-sample security policy.
App comprised of three pages, each has buttons and links that are guarded by permissions. The permissions are granted to a particular user via their role activations.
For this app, user-to-role assignments are:
user | Tellers | Washers |
---|---|---|
curly | true | true |
moe | true | true |
larry | true | true |
But we want to control role activation using attributes based on Branch location:
user | Tellers | Washers |
---|---|---|
curly | East | North, South |
moe | North | East, South |
larry | South | North, East |
Even though the test users are assigned both roles, they are limited which can be activated by branch.
Furthermore due to toxic combination, we must never let a user activate both roles simultaneously regardless of location. For that, we'll use a dynamic separation of duty policy.
set name | Set Members | Cardinality |
---|---|---|
Bank Safe | Washers | 2 |
Tellers | ||
The page links are guarded by RBAC permissions that dependent on which roles are active in the session.
role | WashersPage | TellersPage |
---|---|---|
Tellers | false | true |
Washers | true | false |
The buttons on the page are also guarded by RBAC permissions.
role | Account.deposit | Account.withdrawal | Account.inquiry | Currency.soak | Currency.rise | Currency.dry |
---|---|---|---|---|---|---|
Tellers | true | true | true | false | false | false |
Washers | false | false | false | true | true | true |
1. Open link to http://localhost:8080/rbac-abac-sample
userId | Password |
---|---|
curly | password |
moe | password |
larry | password |
Enter North, South or East
5. Once the location is set, a link will appear corresponding with the user's allowed role for that location.
This is RBAC with ABAC in action, limiting which role may be activated in the session by location.
Each has different access rights to application.
Run the selenium automated test:
mvn test -Dtest=RbacAbacSampleSeleniumITCase
Selenium Test Notes:
- This test will log in as each user, perform positive and negative test cases.
- Requires Firefox on target machine.
How does this work? Have a look at some code...
Paraphrased from WicketSampleBasePage.java:
// Nothing new here:
String userId = "curly";
User user = new User(userId);
// This is new:
RoleConstraint constraint = new RoleConstraint( );
// In practice we're not gonna pass hard-coded key-values in here, but you get the idea:
constraint.setKey( "locale" );
constraint.setValue( "north" );
// This is just boilerplate goop:
List<RoleConstraint> constraints = new ArrayList();
constraints.add( constraint );
try
{
// Now, create the RBAC session with an ABAC constraint, locale=north, asserted:
Session session = accessMgr.createSession( user, constraints );
...
}
Pushing the locale attribute into the User's RBAC session the runtime will match that instance data with their stored policy.
Notice that this user has been assigned both Teller and Washer, via ftRA attribute, and that another attribute, ftRC, constrains where it can be activated.
- When the runtime iterates over assigned roles (ftRA), trying to activate them one-by-one, it matches the constraint pushed in, e.g. locale=north, with its associated role constraint (ftRC).
- If it finds a match, the role can be activated into the session, otherwise not.
- During the createSession call, there's a role activation phase, where all of the constraints are applied.
- Applying constraints is not a new concept with Fortress, check out, What Are Temporal Constraints?, for more info.
- Constraints are enabled via fortress' configuration subsystem. Currently ABAC (user-role) and temporal constraints are turned on by default.
For example, user-role constraint enabled via the fortress.propeties file:
...
# Enable the ABAC constraint validator:
temporal.validator.5:org.apache.directory.fortress.core.util.time.UserRoleConstraint
- ABAC constraints work with any kind of instance data, e.g. account, organization, etc. Let your imagination set the boundaries.