© 2020 iamfortress.net
What's a Groovy Apache Fortress?
This sample uses Apache Groovy to wrap Apache Fortress AccessMgr and AdminMgr APIs. It provides a deep dive into advanced RBAC use cases showing a novel way of invoking its APIs. It's not a basic tutorial on the fundamentals of RBAC or Apache Fortress. There are better places for that, start with the Apache Fortress project page.
Why Apache Groovy?
It's a dynamic language running on top of the Java Virtual Machine. Its relaxed language syntactical rules allow test cases to be written quickly and with better clarity than with traditional Java code.
How This Sample Works
There's one module for adding the RBAC policy (AdminMgr), another for verifying it (AccessMgr).
Each test module uses its wrapper for invoking Apache Fortress manager APIs, in order to do the work.
- GroovyAdminMgrTests -> GroovyAdminMgr -> Apache Fortress AdminMgr
- GroovyAccessMgrTests -> GroovyAccessMgr -> Apache Fortress AccessMgr
Notice how these tests differ when written in Groovy vs Java:
Both do exactly the same thing, Groovy does it with less boilerplate, which makes the code easier to read and write. Test cases are hard enough to write, anything that makes it easier, helps.
More About the Test Cases
Apache Fortress can place dynamic constraints on roles. The idea is limiting when a particular role can be activated, under what conditions.
For more on this concept:
- Towards an Attribute-Based Role-Based Access Control System
- Adding Contextual Information to the RBAC Decision
- Designing an Authorization System: a Dialogue in Five Scenes
How To Run Test Cases
Run from within a particular Java IDE, like Netbeans, Eclipse or Intellij. Or, run them from the command line.
What Else Is Needed to Test
An operational Apache Fortress runtime using one of its supported backends: e.g. OpenLDAP or Apache Directory:
Fortress Config w/ System Properties:
Fortress config is bootstrapped in one of two ways:
Add to runtime parameters to backend:
-Dfortress.admin.user=cn=Manager,dc=example,dc=com -Dfortress.admin.pw=secret -Dfortress.config.root=ou=Config,dc=example,dc=com
Enable an LDAP server:
a. Copy the example fortress.properties.example file.
cp src/test/resources/fortress.properties.example src/test/resources/fortress.properties
b. Edit the file:
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
Setting Up Config Directory in IDE
Be sure to add to the system properties to the runtime classpath of the ide:
src/test/resources which contains the config files for fortress (if not set via sys props), ehcache and log4j.
Or, add the to the runtime config of the ide as in the System env above.
Building the sample
mvn clean install
Running groovy tests from command line
Use the uber jar from the build, located under target folder.
java -classpath target/fortress-core-groovy-1.0.0-jar-with-dependencies.jar:src/test/resources/ org.apache.directory.fortress.GroovyAdminMgrTests java -classpath target/fortress-core-groovy-1.0.0-jar-with-dependencies.jar:src/test/resources/ org.apache.directory.fortress.GroovyAccessMgrTests
Note there will warnings in the console. This is expected as roles that have been assigned aren't being activated due to constraints not matching.
For example, Huey signing into the East, the Washer will not activate due to locale constraint (more later).
start-> userId:null password:null constraints:[userId:Huey, password:password, locale:East, strength:2fa, roles:[Washer, Teller]] Key: locale Value: East Key: strength Value: 2fa 2020-04-12 16:12:025 INFO VUtil:615 - validateConstraints role [Washer] for userId[huey] was deactivated reason code  End Huey Teller in the East.
Running java tests from command line
java -classpath target/fortress-core-groovy-1.0.0-jar-with-dependencies.jar:src/test/resources/:target/test-classes/ org.apache.directory.fortress.AdminMgrTests java -classpath target/fortress-core-groovy-1.0.0-jar-with-dependencies.jar:src/test/resources/:target/test-classes/ org.apache.directory.fortress.AccessMgrTests
Understand the security policy
If you read 'toward an attribute-based' article listed above, here is a similar use case. Instead of stooges, curly, moe, larry we have ducks, huey, dewey and louie. Again centered around a simple banking scenario. These ducks can be either a Teller or Washer but are limited which branches this may occur in, and never both (roles) at the same time, due to a dynamic separation of duty constraint placed between them.
An additional constraint has been placed on the 'Teller' role. That is not only is 'locale' verified, now we must also verify the 'strength' of the authentication. The idea, we want to be sure Tellers have been strongly authenticated. Washers perhaps not as sensitive of operation will only require valid locale.
For example, here are Huey's role assignments:
Huey has two roles assigned, teller and washer. The washer role is constrained by location. Huey may be a washer in the north and south. But, the teller role has two constraints placed upon it, locale and strength (of authN). This means Huey may only be a teller at branches that are in the east AND where he logged on via a two-factor authentication mechanism. If either are missing, that role will not activate.
Here's the raw data for Huey's assignments. This is how its stored on Huey's account in the database:
teller$type$USER$locale$East$ teller$type$USER$strength$2FA$ washer$type$USER$locale$North$ washer$type$USER$locale$South$
Here are Dewey's role assignments:
Dewey can be a teller in the north, if strongly authenticated. And a washer in the east and south.
Louie can be a teller in the south, if strongly authenticated. And a washer in the east and north.
1. User-to-Role Assignment Table
For this app, user-to-role assignments are:
2. User-to-Role Activation Table by Branch
But we want to control role activation using attributes based on Branch location:
Even though the test users are assigned both roles, they are limited which can be activated by branch.
3. Role-to-Role Dynamic Separation of Duty Constraint Table
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|
5. User-Password Table