Showing posts with label spring. Show all posts
Showing posts with label spring. Show all posts

Tuesday, August 3, 2010

Maven Integration for Oracle Entitlements Server

This is the final post in what has turned out to be a 3-part series in bringing to life the "hypothetical" OES-Spring integration.. This post is going to focus on the lifecycle aspect. Part of the whole value in how I designed the annotations was to allow the developer to do their work - implementing business logic - while giving enough information to security policy administrators that they could write policy in OES. I used the Java Annotation Process in JDK 1.6 to generate the actions, resources, and attributes that the runtime AuthorizationAspect would be passing to OES. This was a good start, but a long way from having the information stored in OES, let alone working in a "lifecycle".

Introducing the OES Maven Plugin


The way I attempted to address this problem was by creating a Maven plugin for OES. The OES Maven Plugin is part of the oes-jboss project. The information in this post is based on version-1.0

The plugin supports three goals (maven speak for operations)

  • import - Imports policies from the local file system into OES admin server using policyIX
  • export - Exports the policies from OES admin server and into the local file system using policyIX
  • generate-default-policy - Reads the files generated by the OES Annotations Processor and converts them into a policyIX policy file in the file system

The configuration for the plugin basically follows the names of the elements in the policyIX config file.

General Settings



  • server_host_name - Hostname of the OES Admin Server - defaults to localhost
  • server_port - SSL Port for the OES Admin Server - defaults to 7010
  • blm_server_port - The BLM API port for the OES Admin Server - defaults to 7011
  • print_info - Boolean flag that determs if the information about which objects policyIX is loading should be displayed - defaults to true
  • userID - OES Admin user id - defaults to admin
  • password - OES Admin password - defaults to password
  • policyFile - The location of the policy file that policyIX imports into the OES Admin Server or the localtion of where the generated policy file should be saved - defaults to ${project.build.outputDirectory}/com/oracle/security/entitlements/policy/policy.xml

Settings for the generate-default-policy goal



  • policyTemplateFile - the location of the policy template that the plugin should use when generating the default policies - defaults to ${project.build.outputDirectory}/com/oracle/security/entitlements/policy/policy_template.xml
  • resources - the location of the resources file generated by the OES annotations - defaults to ${project.build.outputDirectory}/com/oracle/security/entitlements/policy/object
  • actions - the location of the actions file generated by the OES annotations - defaults to ${project.build.outputDirectory}/com/oracle/security/entitlements/policy/priv
  • attributes - the location of the attributes file generated by the OES annotations -defaults to ${project.build.outputDirectory}/com/oracle/security/entitlements/policy/attr
  • overwrite - boolean indicating that the generated policies should be overwritten, even if th policyFile exists - defaults to true

Settings for the import goal



  • policy_load_procedure - the load procedure the plug in should use when importing the policies into OES - the default is override. The other valid value is delete_existing

Settings for the export goal



  • exportPolicyFile - the location of the file that the OES policies are exported to - defaults to ${project.build.sourceDirectory}/../oes/policy.xml
  • clipping_scope - the clipping scope from which the OES policies are exported - defaults to RootOrg!${oes.organization}!${oes.application}
  • createTemplateOnExport - boolean indicating that the export task should convert the OES policies into a "template", before saving it to disk - default value is true
  • replaceAdminDirInTemplate - boolean indicating that the user and group part of the policies that are defined for the AdminDir should not be templatized - default value is true

Instructions for Installing OES-Maven Plugin


The installation is really in two parts. The first is deploying a special web-application to the OES Admin server. This web-application greatly simplifies the compilation of plugin by materializing the required OES libraries as a moven POM. This is an alternative to the previously suggested ANT based approach or explicitly loading in the OES libraries. Its just too complicated, and when I realized that I needed a slighly different set of libraries to run the policyIX tool, I took the time to build the web app. With the web-app installed, you can easily build the oes-maven plugin.

Building the repository-web-app



  • Build the web-app - this is simple, just type mvn install from oes-maven/repository-web-app
  • Deploy the web-app to the OES Admin Server - This is assuming that the OES Admin Server is running on WLS, but basically, all you're doing is installing a war. You can log into the WLS admin console at https://:7010/console with admin/password, and deploy the web-app.
  • Import the OES policies protecting the web-app - We need to bootstrap the policies for who can access the oes-maven web-application. I included the policyIX policies here.

    <?xml version="1.0" encoding="UTF-8"?>
    <xb:policy_propagation xmlns:xb="http://policypropagation.ales.com/xmlbean">
    <xb:policy_propagation_data_v2>
    <xb:scopes>
    <xb:application_entry value="RootOrg!defaultOrg!oes-maven" boundSSM="asiadmin">
    <xb:admin_roles>
    <xb:admin_role_entry value="AppAdmin" isPrimary="true" description="Primary Application Admin Role of current Application">
    <xb:admin_role_privileges>
    <xb:admin_role_privilege_entry object="action" action="adminmanage"/>
    <xb:admin_role_privilege_entry object="authorizationPolicy" action="adminmanage"/>
    <xb:admin_role_privilege_entry object="authorizationPolicyReport" action="adminmanage"/>
    <xb:admin_role_privilege_entry object="directory" action="adminview"/>
    <xb:admin_role_privilege_entry object="extension" action="adminmanage"/>
    <xb:admin_role_privilege_entry object="group" action="adminview"/>
    <xb:admin_role_privilege_entry object="policyDistribution" action="adminmanage"/>
    <xb:admin_role_privilege_entry object="policySimulator" action="adminmanage"/>
    <xb:admin_role_privilege_entry object="resource" action="adminmanage"/>
    <xb:admin_role_privilege_entry object="role" action="adminmanage"/>
    <xb:admin_role_privilege_entry object="rolePolicy" action="adminmanage"/>
    <xb:admin_role_privilege_entry object="rolePolicyReport" action="adminmanage"/>
    <xb:admin_role_privilege_entry object="user" action="adminview"/>
    </xb:admin_role_privileges>
    </xb:admin_role_entry>
    </xb:admin_roles>
    <xb:resources>
    <xb:resource_entry value="//resources/url" isVirtualResoureAllowed="true"/>
    </xb:resources>
    <xb:actions>
    <xb:action_entry value="any"/>
    </xb:actions>
    <xb:policies>
    <xb:authorization_policy_entry>
    <xb:policy_effect value="grant"/>
    <xb:policy_actions>
    <xb:policy_action_entry value="any"/>
    </xb:policy_actions>
    <xb:policy_resources>
    <xb:policy_resource_entry value="//resources/url"/>
    </xb:policy_resources>
    <xb:policy_subjects>
    <xb:policy_group_entry name="allusers" directory="AdminDir" scope="RootOrg"/>
    </xb:policy_subjects>
    </xb:authorization_policy_entry>
    </xb:policies>
    </xb:application_entry>
    </xb:scopes>
    <xb:security_configuration_data>
    <xb:scms>
    <xb:scm_entry name="adminconfig">
    <xb:ssms>
    <xb:ssm_entry name="asiadmin"/>
    <xb:ssm_entry name="jboss"/>
    <xb:ssm_entry name="oes10gR3cp4wlsssm"/>
    </xb:ssms>
    </xb:scm_entry>
    </xb:scms>
    </xb:security_configuration_data>
    </xb:policy_propagation_data_v2>
    </xb:policy_propagation>

  • Distribute The Policies - Log into the OES Entitlements Administration Server, and deploy the policies.
  • Add an entry for the OES Admin Server in your Maven's settings.xml - Since the application is protected, you'll need to have the OES admin user's credentials. In Maven, this is configured in your settings.xml

    <servers>
    <server>
    <id>oes-admin</id>
    <username>admin</username>
    <password>password</password>
    </server>
    </servers>

    The id oes-admin is just a reference used by the oes-maven-plugin POM.

Build the Plugin


From the oes-maven-plugin directory type

mvn install -Does-admin-url=http://servername:7000/oes-maven

If this works, you'll see a bunch of files downloaded to your local repository, and the oes-maven-plugin will be built successfully! For those of you who are really hard-code, you can actually access the oes-maven web-app over the SSL port by passing -Does-admin-url=https://servername:7010/oes-maven. If order to get the SSL handshake to work, you just need to import the OES Admin Server's CA into the trusted keystore of the JDK (think of this as extra credit)

Using the OES Maven Plugin - an example


I've created a 1.0.1 version of the oes-spring application that demonstrates the OES maven plugin. All of the magic is inside of the oes_spring_aop_test/pom.xml. First let's run through an example of the "lifecycle", and then we can dig into the details of the POM.

  • Developer Builds Simple Spring+OES Application - In this first step of the lifecycle, the developer is just building the objects. They add the annotations as a way to help surface the security meta-data - mainly the resources, actions, and attributes that will be available for policy authoring. As the developer compiles the application, the OES annotations are evaluated, and the OES maven plugin generates the sample policy file based on the template. You can see this by running

    mvn -Does.server.host=<hostname of admin server> compile

    The result of this is the creation of the actions, attributes, and resources files. Notice that with the sample that there were some compiler warnings generated. This is to be expected. Basically, not every Java type can be mapped to a dynamic attribute type inside of the OES console. This is fine, but you can only access these attributes via OES Attribute Retriever or Eval Function, not through the policy. In the case that you're not passing the object and its a type that is not supported in OES, then the compilation fails.
  • Developer tests application - So now the developer wants to start doing some unit testing. At this point, the OES maven plugin will generate the policyIX import file and import it into the OES admin server. The file is stored as a resource in the classes directory, and as such is expected to be a simple temporary set of policies. You can see the policies deployed to OES by running

    mvn -Does.server.host=<hostname of admin server> test

    The unit test that I have in the sample uses a dummy implemenation of the ISecurityContext, but its possible that if there was a local SM, then the policies would be available. NOTE: For the policies to take affect they need to be distributed from the OES Admin Server. The current version of the OES Maven Task does not deploy the policies
  • Security Administrator changes OES policies - The administrator can now log into the OES Admin console. You can see the resources, the actions, and the attributes that are available for policy authoring.





    The security administrator now can go in and change the policies. Since all of the resourecs, actions, and attributes have been created for them, this greatly simplifies the task. At some point, they are happy with the policies, and the policies are exported back to the project.

    mvn -Does.server.host=<hostname of admin server> oes:export

    By default, this generates a policyIX export and adds it to ${project.build.sourceDirectory}/../oes/policy.xml. This is inside of the source of the project. The expectation is that this export file would be added to source control. One interesting this that the export goal does is to change the file back into a template. This is done so that when the import goal is run, the values of a different environment can be used.
  • Policy and Application is deployed to a different environment - Once the policy hasa been exported from OES and is now part of the project, the application and policies can be moved to a different environment - say integration testing or pre-production. The POM checks to see if the OES file exists in main/oes/policy.xml, and if it does, then it uses that file (as opposed to the generated one) to be imported. You can see this by running

    mvn -Does.server.host=<hostname of admin server> install

    You'll notice that the policies that get added back to OES reflect the changes made after running oes:export - not the default policies.

Understanding a some of the oes_spring_aop POM


The project depends on a few properties

<properties>
<oes.ssm>jboss</oes.ssm>
<oes.application>jboss</oes.application>
<oes.organization>MyOrg</oes.organization>
<oes.directory>jboss_dir</oes.directory>
<oes.server.host>oel55-64.localdomain</oes.server.host>
</properties>

The first four are used for the variable substuitution inside of the policies. The properties that I ship with the project are based on the OES JBOSS SPRING application. The values for these properties should match the values used in the config tool used to create the SM. I used the built in filtering capabilities of Maven to do the variable substitution of these properties into the template.

The last property is a convenient way to specify the hostname of the admin server. Notice that in the example above, I used -Does.server.host to specify the servername. You can override any of these properties with command line arguments or as part of Maven profiles.

Summary


The way that I have the goals of the plugin tied to the phases of the Maven lifecycle is definately a matter of debate. I made a deliberate decision not to tie export to an explicit phase, but if I had to pick one...maybe deploy. My thinking is that before the application is put some place else, it makes sense to get a copy of the policies. Ultimately, I don't think there is a total one size fits all lifecycle, but I think that this OES Maven plugin, and the example project provide a very solid foundation. There are definately more goals that could be added - maybe the ability to automatically create and enroll an SM from the config tool. I'm eager to hear your feedback on the life cycle that I put forth, and what variations you've seen or think the maven plugins should support.

Friday, July 23, 2010

Oracle Entitlements Server and Spring Integration Running on JBOSS 5.1

This post is a continuation of the discussion of how to get the "hypothetical" OES+SPRING integration working on an actual platform - in this case JBOSS. This post assumes that you've already got the Java SM running inside of JBOSS 5.1. This post will focus on the technical details of the OES Spring integration and how to extend the JBOSS environment to run a sample application.

Identifying the User


In the "hypothetical" OES-Spring integrtaion post, I said that it was a fairly solved problem to figure out who the user is making the request, and this largely turned out to be true for the OES-Spring integration. I decied to make this part of the model "pluggable" and extended the AuthorizationAspect to use Spring to find an instance of the ISecurityContext interface. This is the ISecurityContext interface:

package com.oracle.oes.springaop.aspect.spi;

import com.bea.security.*;

public interface ISecurityContext {

public AuthenticIdentity getAuthenticIdentity() throws Exception;
public AuthorizationService getAuthorizationService() throws Exception;
public AuthenticationService getAuthenticationService() throws Exception;

}


It seemed logical to bundle the AuthenticIdentity and the Authorization/Authentication Services of the Java SM together. In theory, I guess you could have separarte provides of the AuthorizationService and AuthenticIdentity, but simpler is better.

Now when it came down to providing an implemenation for JBOSS, I tried the standarda Java Security - Subject.getSubject(AccessController.getAccessControllContext()) - but that didn't seem to work. Instead, the JACC Policy Context seemed to do the trick. The JBOSS Security FAQ reveals that you can use the key javax.security.auth.Subject.container to get the current subject. Very nice. Unfortunately, this subject is not acceptable by the default WLS.Subject identity asserter, so I needed a way to get an identity from the subject. Here, I went with my good friend the UsernameIdentityAsserter that ships with the product. I pull the name from the Subject and assert it. I think that a "better" implementation would be one that takes the Subject as the token. Under the covers it could still just assert the username, but it could also make use of the JBOSS roles that are included. This is a pretty simple excercise, but again for simplicity and time sake, I just went with the UsernameIdentityAsserter.

This covered the case where there actually was a user, but what about when the Spring Context is getting loaded by the container? This method returns null. I decided that the simplest thing to do was to configure the implementation of the JBOSS ISecurityContext with a username/password of a user and use that user. There are probably better implementations like - to not have the OES Authorization aspect make any calls when the container is initializing OR use something more like a run-as identity, but this was simple to do. I encourage people to try other alternatives.

Here's the relevant code:

public AuthenticIdentity getAuthenticIdentity() throws Exception {

Subject subject = (Subject)javax.security.jacc.PolicyContext.getContext("javax.security.auth.Subject.container");
System.out.println("Subject="+subject);


if (subject==null) {


System.out.println("I'll try to login with "+this.defaultUser+" "+this.defaultPassword);
AuthenticationHandler handler = new AuthenticationHandler(this.defaultUser,this.defaultPassword);

return this.getAuthenticationService().authenticate(handler);

}

//Just get the 1st principal's name

String name = ((Principal)subject.getPrincipals().iterator().next()).getName();

return this.getAuthenticationService().assertIdentity("USERID_TOKEN", name );

}

I pulled all of this together into a separate POM (oes.jboss.spring.securitycontext) that also includes the Spring configuration. The only thing interesting is that this is is where the username and password for the administration user are specified

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">

<bean name="securityContext" class="com.oracle.security.entitlements.jbosssecuritycontext.JBOSSSecurityContext">
<property name="defaultUser">
<value>admin</value>
</property>
<property name="defaultPassword">
<value>password</value>
</property>
</bean>

</beans>

Since we're using the OES API, this user is going to be authenticated by OES (not JBOSS).

Adding Spring AOP to JBOSS Web Application


I don't want to dwell on this too much, but in pulling this sample together, I did stumble in getting the LTW to work. With eough googling, you find that you need project Snowdrop. So, instead of using the default loadtime weaver, you use the one from Snowdrop

<context:load-time-weaver weaver-class="org.jboss.instrument.classloading.JBoss5LoadTimeWeaver"/>

Building the oes-spring project


I continue to be very impressed with Maven - (I know - what took me so long), but it really made putting this thing together incredible simple. You can build the project from the version-1.0 tag from the subversion repository. The project is a collection of the following modules:

  • oes_spring_aop - The core annotations and aspects of the Spring/OES/AOP integration
  • oes_spring_aop_test - A test application and "dummy" ISecurityContext that demonstrates the OES annotations
  • oes.jboss.spring.securitycontext - The ISecurityContext implementation for JBOSS
  • jboss-sample-web - A sample web-application that shows the oes_spring_aop_test application run inside of a web-application running on JBOSS

Configuring JBOSS for running jboss-sample-web


There is a little bit of set-up that is required in order to run the jboss-sample-webapplication.

  • Modify run_with_oes.conf - The JBOSS start script needs to be modified to include the -javaagent setting to enable the Spring LTW.

    if [ "x$JAVA_OPTS" = "x" ]; then
    JAVA_OPTS="-Xms128m -Xmx512m -XX:MaxPermSize=256m -Dorg.jboss.resolver.warning=true -Dsun.rmi.dgc.client.gcInterval=3600000 -Dsun.rmi.dgc.server.gcInterval=3600000 -javaagent:/path to spring/spring-framework-2.5.6.SEC02/dist/weaving/spring-agent.jar $JAVA_OPTIONS"
    fi

    You can either reference the spring-agent.jar from the repository or download it from springsource
  • Modify the server/default/conf/login-config.xml - The JBOSS web-app is protected with JEE security. The application requires the user to be in the role oes_users role. You can do this by configuring JBOSS to use simple files to define users and their passwords, and users and their roles.

    <application-policy name="other">
    <!--
    A simple server login module, which can be used when the number
    of users is relatively small. It uses two properties files:
    users.properties, which holds users (key) and their password (value).
    roles.properties, which holds users (key) and a comma-separated list of
    their roles (value).
    The unauthenticatedIdentity property defines the name of the principal
    that will be used when a null username and password are presented as is
    the case for an unuathenticated web client or MDB. If you want to
    allow such users to be authenticated add the property, e.g.,
    unauthenticatedIdentity="nobody"
    -->
    <authentication>
    <login-module code="org.jboss.security.auth.spi.UsersRolesLoginModule"
    flag="optional">
    <module-option name="usersProperties">oes-users.properties</module-option>
    <module-option name="rolesProperties">oes-roles.properties</module-option>
    <module-option name="unauthenticatedIdentity">anonymous</module-option>
    </login-module>
    </authentication>
    </application-policy>

    Put the oes-users.properties and oes-roles.properties in the same directory as login-config.xml. For example:
    oes-users.properties
    josh=password
    oes-roles.properties
    josh=oes_user
  • Deploy the application - Start JBOSS and goto servername:8080/admin-console. Log in with username admin password admin. You can deploy the web-application by simply uploading the .war. Alternatively, if JBOSS is running on the same machine, simply copy the .war to server/default/deploy

Testing the application with OES


Here is the policyIX export for the sample application

<?xml version="1.0" encoding="UTF-8"?>
<xb:policy_propagation xmlns:xb="http://policypropagation.ales.com/xmlbean">
<xb:policy_propagation_data_v2>
<xb:scopes>
<xb:application_entry value="RootOrg!MyOrg!jboss" boundSSM="jboss">
<xb:application_attributes>
<xb:application_attribute_entry name="sys_obj_allow_virtual" ASIType="boolean">
<xb:single_value_entry value="true"/>
</xb:application_attribute_entry>
</xb:application_attributes>
<xb:admin_roles>
<xb:admin_role_entry value="AppAdmin" isPrimary="true" description="Primary Application Admin Role of current Application">
<xb:admin_role_privileges>
<xb:admin_role_privilege_entry object="action" action="adminmanage"/>
<xb:admin_role_privilege_entry object="authorizationPolicy" action="adminmanage"/>
<xb:admin_role_privilege_entry object="authorizationPolicyReport" action="adminmanage"/>
<xb:admin_role_privilege_entry object="directory" action="adminview"/>
<xb:admin_role_privilege_entry object="extension" action="adminmanage"/>
<xb:admin_role_privilege_entry object="group" action="adminview"/>
<xb:admin_role_privilege_entry object="policyDistribution" action="adminmanage"/>
<xb:admin_role_privilege_entry object="policySimulator" action="adminmanage"/>
<xb:admin_role_privilege_entry object="resource" action="adminmanage"/>
<xb:admin_role_privilege_entry object="role" action="adminmanage"/>
<xb:admin_role_privilege_entry object="rolePolicy" action="adminmanage"/>
<xb:admin_role_privilege_entry object="rolePolicyReport" action="adminmanage"/>
<xb:admin_role_privilege_entry object="user" action="adminview"/>
</xb:admin_role_privileges>
</xb:admin_role_entry>
</xb:admin_roles>
<xb:resources>
<xb:resource_entry value="//resources/Person" isVirtualResoureAllowed="true"/>
</xb:resources>
<xb:actions>
<xb:action_entry value="any"/>
<xb:action_entry value="readConfidential"/>
<xb:action_entry value="txfer"/>
</xb:actions>
<xb:dynamic_attributes>
<xb:dynamic_attribute_entry name="amount" type="integer"/>
</xb:dynamic_attributes>
<xb:roles>
<xb:role_entry value="Anonymous" parent=""/>
<xb:role_entry value="Everyone" parent=""/>
</xb:roles>
<xb:policies>
<xb:authorization_policy_entry>
<xb:policy_effect value="grant"/>
<xb:policy_actions>
<xb:policy_action_entry value="any"/>
</xb:policy_actions>
<xb:policy_resources>
<xb:policy_resource_entry value="//resources"/>
</xb:policy_resources>
<xb:policy_subjects>
<xb:policy_user_entry name="weblogic" directory="jboss_dir" scope="RootOrg!MyOrg"/>
</xb:policy_subjects>
</xb:authorization_policy_entry>
<xb:authorization_policy_entry>
<xb:policy_effect value="grant"/>
<xb:policy_actions>
<xb:policy_action_entry value="readConfidential"/>
</xb:policy_actions>
<xb:policy_resources>
<xb:policy_resource_entry value="//resources/Person"/>
</xb:policy_resources>
<xb:policy_subjects>
<xb:policy_user_entry name="josh" directory="jboss_dir" scope="RootOrg!MyOrg"/>
</xb:policy_subjects>
</xb:authorization_policy_entry>
<xb:authorization_policy_entry>
<xb:policy_effect value="grant"/>
<xb:policy_actions>
<xb:policy_action_entry value="any"/>
</xb:policy_actions>
<xb:policy_resources>
<xb:policy_resource_entry value="//resources"/>
</xb:policy_resources>
<xb:policy_subjects>
<xb:policy_user_entry name="admin" directory="AdminDir" scope="RootOrg"/>
</xb:policy_subjects>
</xb:authorization_policy_entry>
<xb:authorization_policy_entry>
<xb:policy_effect value="grant"/>
<xb:policy_actions>
<xb:policy_action_entry value="txfer"/>
</xb:policy_actions>
<xb:policy_resources>
<xb:policy_resource_entry value="//resources/Person"/>
</xb:policy_resources>
<xb:policy_subjects>
<xb:policy_user_entry name="josh" directory="jboss_dir" scope="RootOrg!MyOrg"/>
</xb:policy_subjects>
<xb:policy_constraint value="sys_defined ( amount ) and amount &lt; 75"/>
</xb:authorization_policy_entry>
</xb:policies>
</xb:application_entry>
</xb:scopes>
<xb:security_configuration_data>
<xb:scms>
<xb:scm_entry name="adminconfig">
<xb:ssms>
<xb:ssm_entry name="asiadmin"/>
<xb:ssm_entry name="jboss"/>
<xb:ssm_entry name="oes10gR3cp4wlsssm"/>
</xb:ssms>
</xb:scm_entry>
</xb:scms>
</xb:security_configuration_data>
</xb:policy_propagation_data_v2>
</xb:policy_propagation>


  • Load this policy into OES.
  • Test the application, by going to http://servername:8080/jboss-sample-web-1.0/OESTest.jsp
  • Login with the username and password you added to the oes_user.properties (josh/password)
  • You get access to the sample application

Summary


This is a working example of the runtime aspects of the OES Spring integration working in an actual environment. The code is now available at https://oes-spring.samplecode.oracle.com. I'm really happy (with the help of Maven) how easy is was to pull all of this together. The admin story, still needs some work. Even though the annotations are generating data in a flat file format that OES can understand, its not integrated into the build process. As you saw from the example, I had to give you the export of the policies I worked with....not part of the maven process. I'd be interested in getting people's feedback on this issue - how do think that OES policy should be tied into the overall lifecycle in a scenario like this? I'll share with you my thoughts shortly

Thursday, July 15, 2010

Running Oracle Entitlements Server Java SM inside of JBOSS 5.1

First of all, in a shameless attempt to win an internal contest of which blog can register the most people for the Identity Management 11gR1 Launch, if you register from this link and send me your confirmation email, I will enter you in a drawing for a free copy of Professional Oracle Web Logic Server. Everyone who registers and sends me an email, will recieve a PDF version of this blog for the last year that I generated using Blog2Print.com - a $7.95 value. Sign up and learn about 11gR1, and help Chris, Brian, Alex and I out.

Now, back to business. This post is in response to customers' interest in the OES Spring integration post from a few weeks ago. In the spirit of full disclosure, at the time of the post, I hadn't wired the OES runtime into Spring and was only running Spring from a standalone JVM. Some customers had actually gotten ahead of me, and started trying to perform a similar integration using Spring, OES and JBOSS. I applaud your enthusiasm and thanks for being patient while I catch-up. I'm almost there, so I've decided to cover this topic - a full integration of OES/Spring running inside of JBOSS - in two separate posts. This post will cover the OES-JBOSS integration and a subsequent post will cover the details of pulling the entire solution together.

Overview


In setting off on the task of getting the Java SM to run inside of JBOSS, I decided to start with the most direct class loader approach - try to get everything in the system class path. Alex can attest to the pain that we encountered in getting the OES/OVD integration going - only to settle on adding everything to the system classpath. With that experience fresh in my mind, I started simply and added the jars from the Java SM into the JBOSS system classpath ( I'll explain the details of how to actually do this in a second ). After the normal "issues" with Log4J libraries, I got the server started. I installed a web-app with a JSP that invokes the JavaAPIExample that ships with the product, and got the Java SM running! I was very pleased with my self, until I tried to authenticate.

I tried both authentication and identity assertion with the Username identity asserter and in both cases, for whatever reason, instead of tyring to authenticate with the Authentication Providers configured inside of OES, it was trying the JAAS Login Modules associated with JBOSS. I could tell by looking at the stack trace. The exception was in coming from org.jboss.security.auth.spi.UsernamePasswordLoginModule. When I tried to authenticate originally, it complained that I missing a users.properties and roles.properties file - these are the files that this login module uses to identify users,password, and roles. How could this be happening?

JAAS Login Configuration


The java.security.auth.login.Configuration class controls the configuration of the JAAS Login modules inside a JVM. The default implementation simply reads the config the a file, but like a lot of things in Java security, you can change the default behavior, and this is exactly what JBOSS does. JBOSS uses its own XML format to define the JAAS Login Modules. By default, this information is stored in the JBOSS_HOME/server/servername/conf/login-config.xml and loaded at runtime into a custom implementation of the Configuration class. There is nothing sinister about this, in fact OES does a very similar thing. Instead of getting its configuration from a local file, it receives it from the SCM, and it two uses a custom Configuration class. Notice in the class definition that the setters and getters for the Configuration are static. This means there is 1 and only 1 defined for the entire JVM.

oes-jboss project


The good news is that JBOSS has a way to replace their implementation of the Configuration with a custom one, and this is what I did. I found a very good over-view of the JBOSS Security model and was able to get something going pretty quickly. Basically, all that my Configuration implementation does is first check what is defined in OES. If there is no configuration for the application, the I defer to JBOSS. This means that JBOSS is getting called for authentication and OES is getting called for the AuthenticationService. This is not a full JBOSS SM. This is just enough glue code to run the Java SM inside of JBOSS.

I created an oes-jboss project on samplecode.oracle.com. I've been starting to learn more about Maven and actually used it to build this project. The one "tricky" part is that there is no POM for OES, so I created one. I used an ant task to create a MANIFEST file that contained references to all of the OES jar files in the Class-Path. The POM then includes that MANIFEST in the jar file and then copies all of the libraries to the lib directory in the Maven repository (symbolic link would have worked too I suppose). All told, I think it works really nicely. These are the steps:

  • Check out the oes-jboss Subversion Repository
  • Download the Maven ANT task. Copy the jar file to MAVEN_HOME/lib.
  • Modify the oes-home,maven-home and maven-repo values build.xml for your environment
  • run ant - this should build the OES POM
  • mvn install - this should build the custom oes-jboss configuration. You should wind up with the file oes.jboss.auth.spi-0.0.1-SNAPSHOT.jar in the target directory.

If you don't want to build the jar, I've uploaded it here

Installing OES on JBOSS 5.1


These are basically the steps for getting the JavaAPIExample running from inside a JSP on JBOSS 5.1. Again, this is not a JBOSS SM. Authentication is being handled by JBOSS for all JBOSS resources. OES is only called for the Java API.

  • Create an instance of the Java SM on the same machine as JBOSS
  • Run the JavaAPIExample for that instance - this way you'll have sure that its actually working and that you won't be trying to debug two issues at once
  • Make a copy of the JAVA-SSM/instance/instance-name/bin/set-env.sh to set-env-jboss.sh
  • In the set-env-jboss.sh comment out the JAVA_HOME, the log4j properties, and the servlet and jasper jars

    # Comment this out
    #JAVA_HOME="/home/oracle/Oracle/Middleware/11gR1PS1/jrockit150_06/jre"
    #export JAVA_HOME

    BEA_HOME=/home/oracle/Oracle/Middleware/11gR1PS1
    export BEA_HOME

    INSTALL_HOME=/home/oracle/Oracle/Middleware/11gR1PS1/ales32-ssm/java-ssm
    export INSTALL_HOME

    ALES_SHARED_HOME=/home/oracle/Oracle/Middleware/11gR1PS1/ales32-shared
    export ALES_SHARED_HOME

    INSTANCE_HOME=/home/oracle/Oracle/Middleware/11gR1PS1/ales32-ssm/java-ssm/instance/jboss_inst
    export INSTANCE_HOME

    JAVA_OPTIONS="-Dwles.scm.port=7013 -Dwles.arme.port=18081"
    JAVA_OPTIONS="$JAVA_OPTIONS -Dwles.config.signer=oel55-64.localdomain"
    # Comment this out
    #JAVA_OPTIONS="$JAVA_OPTIONS -Dlog4j.configuration=file:$INSTANCE_HOME/config/log4j.properties -Dlog4j.ignoreTCL=true"
    JAVA_OPTIONS="$JAVA_OPTIONS -Dwles.ssl.passwordFile=$ALES_SHARED_HOME/keys/password.xml"
    JAVA_OPTIONS="$JAVA_OPTIONS -Dwles.ssl.passwordKeyFile=$ALES_SHARED_HOME/keys/password.key"
    JAVA_OPTIONS="$JAVA_OPTIONS -Dwles.ssl.identityKeyStore=$ALES_SHARED_HOME/keys/identity.jceks"
    JAVA_OPTIONS="$JAVA_OPTIONS -Dwles.ssl.identityKeyAlias=wles-ssm"
    JAVA_OPTIONS="$JAVA_OPTIONS -Dwles.ssl.identityKeyPasswordAlias=wles-ssm"
    JAVA_OPTIONS="$JAVA_OPTIONS -Dwles.ssl.trustedCAKeyStore=$ALES_SHARED_HOME/keys/trust.jks"
    JAVA_OPTIONS="$JAVA_OPTIONS -Dwles.ssl.trustedPeerKeyStore=$ALES_SHARED_HOME/keys/peer.jks"
    JAVA_OPTIONS="$JAVA_OPTIONS -Djava.io.tmpdir=$INSTANCE_HOME/work/jar_temp"
    JAVA_OPTIONS="$JAVA_OPTIONS -Darme.configuration=$INSTANCE_HOME/config/WLESarme.properties"
    JAVA_OPTIONS="$JAVA_OPTIONS -Dales.blm.home=$INSTANCE_HOME"
    JAVA_OPTIONS="$JAVA_OPTIONS -Dwles.scm.useSSL=true"
    export JAVA_OPTIONS

    # Set the OES classpath:
    CLASSPATH="$INSTALL_HOME/lib/api.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/css.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/asn1.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/saaj.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/framework.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/scmapi.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/log4j.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/jmx.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/connector.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/asi_classes.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/EccpressoCore.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/EccpressoJcae.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/jsafe.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/jsafeJCE.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/jsafeFIPS.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/jsafeJCEFIPS.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/sslplus.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/ssladapter.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/wlcipher.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/asitools.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/process.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/webservice.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/webserviceclient.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/org.mortbay.jetty.jar"
    # Comment these two out
    #CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/javax.servlet.jar"
    #CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/org.apache.jasper.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/sslserver.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/sslclient.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/pdsoap.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/antlr.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/ld-server-core.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/wlsdo.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/wlxbean.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/xbean.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/xqrl.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/ld-client.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/axis.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/commons-logging-1.0.4.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/commons-discovery-0.2.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/orawsdl14.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/jaxrpc.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/rmi-ssm.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/rmi-types.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/providers/ales/CR338979_414_jdk1.4.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/providers/ales/kodo-runtime.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/providers/ales/jdo.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/providers/ales/openjpa.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/providers/ales/commons-lang-2.1.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/providers/ales/jta-spec1_0_1.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/providers/ales/openjpa.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/providers/ales/commons-collections-3.2.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/providers/ales/commons-pool-1.3.jar"
    CLASSPATH="$CLASSPATH:$INSTALL_HOME/lib/providers/ales/serp.jar"
    CLASSPATH="$CLASSPATH:$INSTANCE_HOME/config"
    CLASSPATH="$CLASSPATH:$BEA_HOME"
    export CLASSPATH

  • Create a copy of the JBOSS_HOME/bin/run.conf called run_oes.conf. Modify it to call the set-env-jboss.sh and pass the JAVA_OPTS and CLASSPATH into JBOSS. Make sure to set the value of the -Dwles.realm to the name of the SM configuration. This is the name that appears in the OES Admin Console, not the name of the instance.

    . /home/oracle/Oracle/Middleware/11gR1PS1/ales32-ssm/java-ssm/instance/jboss_inst/bin/set-env-jboss.sh

    echo "$JAVA_OPTIONS"

    #
    # Specify options to pass to the Java VM.
    #
    if [ "x$JAVA_OPTS" = "x" ]; then
    JAVA_OPTS="-Xms128m -Xmx512m -XX:MaxPermSize=256m -Dorg.jboss.resolver.warning=true -Dsun.rmi.dgc.client.gcInterval=3600000 -Dsun.rmi.dgc.server.gcInterval=3600000 $JAVA_OPTIONS -Dwles.realm=your_realm_name"
    fi

    JBOSS_CLASSPATH="$CLASSPATH"

  • Copy JBOSS_HOME/bin/run.sh to run_with_oes.sh

    #!/bin/sh
    ### ====================================================================== ###
    ## ##
    ## JBoss OES Bootstrap Script ##
    ## ##
    ### ====================================================================== ###

    export RUN_CONF=run_oes.conf

    ./run.sh

  • Modify the JBOSS_HOME/server/default/deploy/security/security-jboss-beans.xml - this is where we tell JBOSS to use our implementation of the Configuration by adding the OES Login Configuration MBean and configuring the SecurityConfig to use it

    <!-- Establish the basic XML Based Configuration-->
    <bean name="XMLLoginConfig" class="org.jboss.security.auth.login.XMLLoginConfig">
    <property name="configResource">login-config.xml</property>
    </bean>

    <bean name="OESLoginConfiguration" class="oes.jboss.auth.spi.LoginConfiguration">
    <property name="loginConfig"><inject bean="XMLLoginConfig"/></property>
    </bean>

    <bean name="SecurityConfig" class="org.jboss.security.plugins.SecurityConfig">
    <property name="mbeanServer"><inject bean="JMXKernel" property="mbeanServer"/></property>
    <!--
    <property name="defaultLoginConfig"><inject bean="XMLLoginConfig"/></property>
    -->
    <property name="loginConfig">jboss.security:service=OESLoginConfiguration</property>
    </bean>

  • Copy the oes.jboss.auth.spi-0.0.1-SNAPSHOT.jar to JBOSS_HOME/lib
  • Create a symbolic link from JBOSS_HOME/server/default/lib to JBOSS_HOME/lib - This causes the OES MBean to be loaded with all of the classes in the JBOSS_HOME/lib. I'm sure there are other ways to achieve the same thing by copying the oes.jboss.auth.spi-0.0.1-SNAPSHOT.jar to JBOSS_HOME/server/default/lib and then moving other jars from JBOSS_HOME/lib to there. If anyone works out the specifics, I'd love to hear them.
  • Copy exampleNames.xml from the JavaAPIExample/config to JBOSS_HOME/bin
  • Restart JBOSS - you should see the OES Runtime initializing.
  • Go a head and run the JavaAPIExample from inside a JSP - it should work!

Summary


In this instance, it wasn't classloading that was the issue, but rather the fact that both JBOSS and OES thought that they were controlling the list of JAAS Login Modules. With the oes.jboss.auth.spi-0.0.1-SNAPSHOT.jar and these instructions, it should be pretty straight forward for other people to get over this hump, and using the Java SM from inside JBOSS. My next post will expand on this example and show the OES Spring integration working.

Until then, don't forget to register for the Identity Management 11gR1 Launch.

Friday, May 28, 2010

Using Oracle Entitlements Server to Secure Spring Applications

About 7 years ago, when I worked at Netegrity in the Architecture group, I remember a meeting that Deepak Taneja (CTO) and I had with some guy who was talking about combining SiteMinder with Aspect Oriented Programming (AOP). The guy (apologies...I have three kids now, and can barely remember my own name) was saying you could just "inject security" right into the application. I was like "Huh?"....It all sounded very cool and clever but this was right when Java was still very young, and SiteMinder (was and still is) a Web Access Management product. So the prospect of making a lot of calls with the SiteMinder Agent API - which I think used JNI then - out to provide authorization for Java methods seemed like "crazy talk".

Fast forward to the last day of FY2010, and here I am writing a post about how to integrate Oracle Entitlements Server with Spring using AOP. What is interesting is that just because the technology has improved (both on the Java side and on the Identity Management side) and the solution is possible, there are still some fundamental questions about the viability of such a model for enterprises. In the enterprise model, security is a coordinated effort between application developers and security administrators to make sure that applications are secure. The security model needs to be sufficiently flexible to meet the security requirements of the business and enable meaningful policy changes made with out having to engage expensive development resources to do so. Its with these goals in mind that I set off to build a "real" solution.

Annotations - A Brave New World, at least for me


I knew that I wanted to use aspects to automatically make the calls to OES. This is more of an AOP thing than per se a Spring thing, but I wanted a solution that was fairly generic and didn't rely on other frameworks. I also in a way wanted to see if there was a way to just magically get authorization called - security along with logging and exception handling has always been held out there as a good use case for AOP. The first challenge was to figure out how to get my code - Aspect - invoked. Obviously, getting called before every Java method made no sense. The good news is that you can define Pointcuts as part of spring-configuration. The bad news is that the syntax for this stuff is pretty messy. Example:


<aop:aspect id="beforeExample" ref="aBean">

<aop:before
pointcut="execution(* com.xyz.myapp.dao.*.*(..))"
method="doAccessCheck"/>

...

</aop:aspect>


I think this means whenever you're executing a method of any protection level in the package com.xyx.myapp.dao. This is pretty tricky stuff and if you wanted to secure a different method, you need to go change this. Also, the person doing this is obviously the developer, not a security person since all of the definitions are using Java packages. So, people would probably do when confronted with something really hard...either not use it or set the pointcut like the example above...very broad...all methods result in a call to the authorization aspect. So, what is the simpler way for a developer to indicate that a method should be secured? Annotations to the rescue. In SpringAOP, you can also define point cuts that look for annotations on methods or classes to indicate a pointcut. Here's the poincut I'm using for this example:


@Around("@annotation(com.oracle.oes.springaop.annotation.Protected) ||

(@target(com.oracle.oes.springaop.annotation.Protected)&&

!@annotation(com.oracle.oes.springaop.annotation.UnProtected) ) ")



This means call my aspect, if the method has an @Protected annotation or the class does and the method doesn't have an @UnProtected annotation. Basically, if you mark a class as @Protected all of the methods are protected by default. You can override that with an @UnProtected. You can also have a class which is unprotected, but mark individual methods as @Protected. My feeling is with this model, you'll probably never have to go change the pointcut. The annotations do all of the work.

In addition to @Protected and @UnProtected, I added three more tags:

  • @Resource - this is used at the class level to indicate where in the OES resource hierarchy this class sits or at the method level to indicate that when making an authorization call on a method of this object that this method is used to represent the OES resource. In a class has both, then the resource will be the concatenation of the class and method level @Resource values. If no @Resource annotations are found then the name of the class is used.
  • @Privilege - this is used at the class level to indicate the default privilege in OES for all of the @Protected methods on the class. It can also be used to override the default with a specific value for the method. If no @Privilege annotation is found, then the method signature is used (yuck)
  • @AppContext - this is used at the method or parameter level to indicate that this value should be passed to OES as an attribute it the AppContext. All methods in a class that have the @AppContext annotation will be called and have their values passed to OES. All of the parameters in a method call will be passed, even if they don't have an @AppContext - they get names param1, param2,..,paramN. @AppContext also has some optional parameters - passObject and isProtected. The passObject parameter is set to false by default which means that the authorization aspect will convert the object to a set of primitives. If this is set to true, then the object itself is passed. This can have advantages when working with the Java SM and custom extensions - attribute retrievers and eval functions. The other parameter, isProtected determines if when calling the method to retrieve the appContext and the method has an @Protected annotation should the authorization aspect make a call to OES. This attribute defaults to false, but there are cases where the authorization check should still occur


Here are some sample classes that illustrate more of the annotations that I'm using for this solution:

AccountImpl.java



package test;

import com.oracle.oes.springaop.annotation.AppContext;
import com.oracle.oes.springaop.annotation.Privilege;
import com.oracle.oes.springaop.annotation.Protected;
import com.oracle.oes.springaop.annotation.Resource;
import com.oracle.oes.springaop.annotation.UnProtected;

@Protected()
@Privilege("read")
@Resource("/MyOrg/Account")
public class AccountImpl implements IAccount {



private String accountId;
private double balance;
private String accountType;
private IPerson accountOwner;

public AccountImpl() {
super();
// TODO Auto-generated constructor stub
}


public AccountImpl(String accountId, double balance, String accountType,
IPerson owner) {
super();
this.accountId = accountId;
this.balance = balance;
this.accountType = accountType;
this.accountOwner = owner;
}

@Privilege("readBalance")
@AppContext(value="balance")
public double getBalance() {

return this.balance;
}


@UnProtected
public String toString() {
return super.toString();
}

@UnProtected
@Resource
public String getAccountId() {
return this.accountId;
}

@AppContext("owner")
public IPerson getAccountOwner() {
// TODO Auto-generated method stub
return this.accountOwner;
}

@UnProtected
public String getAccountType() {
// TODO Auto-generated method stub
return this.accountType;
}

@UnProtected
public void setAccountOwner(IPerson owner) {
this.accountOwner = owner;
}
@UnProtected
public void setAccountId(String accountId) {
this.accountId = accountId;
}
@UnProtected
public void setBalance(double balance) {
this.balance = balance;
}
@UnProtected
public void setAccountType(String accountType) {
this.accountType = accountType;
}


}


PersonImpl.java



package test;

import java.util.List;

import com.oracle.oes.springaop.annotation.AppContext;
import com.oracle.oes.springaop.annotation.Privilege;
import com.oracle.oes.springaop.annotation.Protected;
import com.oracle.oes.springaop.annotation.Resource;


public class PersonImpl implements IPerson {

private String name;
private String SSN;

private List<IAccount> accounts;

@Protected()
@Privilege("readConfidential")
@AppContext(value="accounts")
public List<IAccount> getAccounts() {
return accounts;
}

public void setAccounts(List<IAccount> accounts) {
this.accounts = accounts;
}

public PersonImpl() {
super();
// TODO Auto-generated constructor stub
}

public PersonImpl(String name, String sSN) {
super();
this.name = name;
SSN = sSN;
}

@AppContext("name")
@Privilege("read")
public String getName() {
return name;
}

@Protected
@Privilege("write")
public void setName(String name) {
this.name = name;
}

@AppContext(value="ssn")
@Protected()
@Privilege("readConfidential")
public String getSSN() {
return SSN;
}

@Protected
@Privilege("writeConfidential")
public void setSSN(String sSN) {
SSN = sSN;
}


@Protected
@Privilege("txfer")
public void transfer(
@AppContext(value="from") IAccount acct1,
@AppContext(value="to") IAccount acct2,
@AppContext(value="amount", passObject=true) double amount) {

}

}

Some Design Considerations with Annotations


I made a conscious decision to limit the Annotations to the basic OES runtime API, which is very aligned with the XACML concept of Subjects, Actions, Resources and Environment with attributes. I did not expose the ability for developers to author policy or roles (@AllowableRoles) in the annotations. Other models support this, but you're creating a brittleness here, and if things change then developers will have to go back in and change the roles/policy annotations. OES has facilities for authoring these policies so all I'm trying to do here is get the integration between the OES runtime API and Spring simple, and then make the authoring of those policies sensible.

Before we move on its worth noting that I didn't do anything explicitly here about identifying the subject. This is basically a solved problem, where I'm assuming that the user is already authenticated and there is a simple way inside of the aspect to know who the user is. OES has the ability through the identity asserter to take a token (username or JAASSubject) and use that as the subject in the authorization decision.

Now, back to the thinking around these particular annotations. One of the other really hard problems in writing custom PEPs is getting the admin artifacts aligned with the runtime calls. OES has the ability to address this through discovery mode - which basically just captures the calls, and the resources, attributes and privileges in a file format that can be imported into OES admin console. This works fine, but with annotations we can actually do much better. For people who are steady readers of this blog, they'll remember that I used the Java Annotations Compiler capabilities in JDK 1.6 in the OWSMAC (OWSM assertions compiler project) to simplify the creation of the XML deployment descriptors for OWSM custom assertions. As architects, we have a tendency to fall in love with solutions, but I do think in this regard, the use of the annotations is the best choice. I created a simple annotation processor, and now when you compile the classes above, you get this output:


Buildfile: C:\Documents and Settings\jbregman\workspace\oes_spring_aop_test\build.xml
clean:
[delete] Deleting directory C:\Documents and Settings\jbregman\workspace\oes_spring_aop_test\bin
init:
[mkdir] Created dir: C:\Documents and Settings\jbregman\workspace\oes_spring_aop_test\bin
[copy] Copying 1 file to C:\Documents and Settings\jbregman\workspace\oes_spring_aop_test\bin
build-project:
[echo] oes_spring_aop_test: C:\Documents and Settings\jbregman\workspace\oes_spring_aop_test\build.xml
[javac] Compiling 5 source files to C:\Documents and Settings\jbregman\workspace\oes_spring_aop_test\bin
[javac] Processing Annotations: [com.oracle.oes.springaop.annotation.Protected, com.oracle.oes.springaop.annotation.AppContext, com.oracle.oes.springaop.annotation.UnProtected, com.oracle.oes.springaop.annotation.Privilege, com.oracle.oes.springaop.annotation.Resource]
[javac] Protected Classes to Process: [test.PersonImpl, test.AccountImpl]
[javac] PRIV FILE=file:/C:/Documents%20and%20Settings/jbregman/workspace/oes_spring_aop_test/bin/oes/priv
[javac] OBJECT FILE=file:/C:/Documents%20and%20Settings/jbregman/workspace/oes_spring_aop_test/bin/oes/object
[javac] ATTR FILE=file:/C:/Documents%20and%20Settings/jbregman/workspace/oes_spring_aop_test/bin/oes/attr

That's right - based on the annotations in the code, it creates OES import files. You can pull these directly into the OES Admin console. This saves a lot of time and effort in getting the coordination between runtime and admin, since they are using the same tooling and model. The most interesting of the files is the attr - this is list of dynamic attributes that are available at runtime.

to
from.balance
from.resource
accounts.count
param1
resource
ssn
owner.ssn
from
owner.accounts.count
to.resource
to.owner
amount
balance
owner.from
to.balance
from.owner
owner.to
name
owner
owner.name

These are some really nice attributes to write policies on...there is one param1 that I left there from testing, but basically you have a seamless binding between the code and the policy artifacts, for little more than adding some basic annotations.

Pulling it all together - the runtime


Now that we've build this model, let's see how it works at runtime. I have a very basic spring-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">



<bean id="josh" class="test.PersonImpl">
<property name="name" value="Josh"/>
<property name="SSN" value="123-45-6789"/>
<property name="accounts">
<list>
<ref bean="acct2"/>
</list>
</property>
</bean>



<bean id="chris" class="test.PersonImpl">
<constructor-arg type="java.lang.String" value="Chris"/>
<constructor-arg type="java.lang.String" value="987-65-4321"/>
</bean>


<bean id="acct1" class="test.AccountImpl">
<property name="accountId" value="11111111"/>
<property name="balance" value="6000"/>
<property name="accountType" value="CHK"/>
<property name="accountOwner" ref="chris"/>
</bean>

<bean id="acct2" class="test.AccountImpl">
<property name="accountId" value="22222222"/>
<property name="balance" value="7000"/>
<property name="accountType" value="SAV"/>
<property name="accountOwner" ref="josh"/>
</bean>

<context:load-time-weaver/>

</beans>

Notice I'm using the Load Time Weaver (LTW) to pull in the aspects. I did this in the META-INF/aop.xml file

<!DOCTYPE aspectj PUBLIC
"-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>

<weaver options="-showWeaveInfo">

<!-- only weave classes in our application-specific packages -->
<include within="test.*" />

</weaver>

<aspects>

<!-- weave in just this aspect -->
<aspect name="com.oracle.oes.springaop.aspect.AuthorizationAspect"/>

</aspects>

</aspectj>

The aop.xml and the aspects are all of the "overhead" that is placed on the developer. Here's the test program:

package test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.*;


public class Test {

public static void main(String[] args) {

ApplicationContext ctx =
new ClassPathXmlApplicationContext(args[0],Test.class);

IPerson josh = (IPerson)ctx.getBean("josh");
IPerson chris = (IPerson)ctx.getBean("chris");
IAccount acct1 = (IAccount)ctx.getBean("acct1");
IAccount acct2 = (IAccount)ctx.getBean("acct2");

System.out.println("Chris SSN="+chris.getSSN());

josh.transfer(acct1, acct2, 63.45);

}

}

And this is the output:

>method-execution PersonImpl.setName(..)
>IsAccessAllowed action=write resource=test/PersonImpl appContext={
param1=Josh
}
>method-execution PersonImpl.setSSN(..)
>IsAccessAllowed action=writeConfidential resource=test/PersonImpl appContext={
{
param1=123-45-6789,
name=Josh
}
>method-execution PersonImpl.getSSN()
>IsAccessAllowed action=readConfidential resource=test/PersonImpl appContext={
name=Chris,
ssn=987-65-4321
}
Chris SSN=987-65-4321
>method-execution PersonImpl.transfer(..)
>IsAccessAllowed action=txfer resource=test/PersonImpl appContext=

{
to.owner.ssn=123-45-6789,
from.balance=6000.0,
from.resource=11111111,
accounts.count=1,
ssn=123-45-6789,
from.owner.name=Chris,
accounts.1.resource=22222222,
to.owner.accounts.count=1,
to.resource=22222222,
amount=63.45,
to.balance=7000.0,
to.owner.name=Josh,
name=Josh,
accounts.1.balance=7000.0,
from.owner.ssn=987-65-4321}

There are 4 authorization calls made in the sample. The first two are made when setting the SSN and Name of the josh bean. There are no corresponding calls on the chris bean because in the spring-config.xml the chris bean is created using the constructor, while josh is done using the setters. I made constructors unprotected by rule, because it seemed like too many issues with having constructors protected. The next authorization call is made when getting chris SSN. The final authorization is the really interesting one where you see all of the context of the transfer being passed. You see that the List of IAccount are passed as items. I could have set the @AppContext to passObject, and then it would have simply passed the list. Either way, there is all of the information needed to make a meaningful authorization decision in OES.

Summary


Java/Spring/AOP have all come a long way since that meeting seven years ago. This sample demonstrates some of the "magic", the ability to seamlessly apply authorization policies with very little coding. Furthermore, it does it in such a way where I think there is a good balance between what the developer has to do to get this model enabled, and what security policy administrators/authors can then do with the artifacts the model generates. Finally, what really makes this solution possible is the performance and scalability of the OES engine. The fact that, unlike SiteMinder or centralized models, the Java SM can run in process, and make the decisions very very fast. With the passObject=true option, all of the information can be available in its native form to OES. This is another way that expensive serializing and de-serializing can be avoided. This authorization aspect discussed is functionally pretty basic - it does inbound checks and then calls proceed. You could image doing some interesting this with responses from OES - like adding additional where clauses to queries or data masking. Now that would be something.