Security Features of JBoss AS 5.1 - Part 3 - XACML authorization for EJB Applications
Join the DZone community and get the full member experience.
Join For FreeOasis XACML v2.0 is a robust open standard catering to access control. Starting JBoss Application Server v5.0, users have the opportunity to use XACML as container authorization mechanism for EJBs similar to JACC or the EJB role based access control.
This article will focus on JBoss Application Server v5.1.0.
In this article, we will take a look at an EJB2 application (attached to this article) and use XACML as the authorization mechanism.
Assumptions
This article assumes that the reader is familiar with EJB Container security and the deployment descriptors such as ejb-jar.xml. This article is catered toward advanced users of JBoss AS.
Why is there a need for XACML in Java EE?
The Java EE specifications offer coarse grained security that is based on roles. If an user wishes to apply fine grained access control to Java EE components such as the web or ejb applications, then he needs to look at non-standard ways of doing it. JACC (JSR-115) is an authorization specification that is mandated in the Java EE world, since v1.4 but it is a specification that is tied to the web and ejb standard deployment descriptors, to provide the authorization support.
If the users wish to provide fine grained authorization in the containers, that may be tied to some context, then they will probably have to get away from container security and write their own via servlet filters or some type of server interceptors.
Some examples of container authorization that can be tied to business context are:
- Allow access to this web resource to employees who are active and between the business hours of 9am-5pm on Mondays through Thursday and 9am -2pm on Fridays and no access on weekends.
- Do not allow access to this EJB application from this particular subnet.
Limitations of application of XACML in Java EE
- The feature is an extension of security provided by JavaEE. So in some ways, it is a non-standard behavior.
- There are no standard xacml attributes for EJBs. So there is a need for custom schema from the vendor.
Step 1: Let us take a look at the ejb-jar deployment descriptor.
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/ejb-jar_2_1.xsd"
version="2.1">
<enterprise-beans>
<session>
<description>A secured stateless session bean</description>
<ejb-name>StatelessSession</ejb-name>
<home>org.jboss.test.security.interfaces.StatelessSessionHome</home>
<remote>org.jboss.test.security.interfaces.StatelessSession</remote>
<ejb-class>org.jboss.test.security.ejb.StatelessSessionBean4</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
</session>
</enterprise-beans>
<assembly-descriptor>
<security-role>
<role-name>CallerInfoFacadeRole</role-name>
</security-role>
<security-role>
<role-name>CallerInfoStatelessRole</role-name>
</security-role>
<security-role>
<role-name>CallerInfoStatefulRole</role-name>
</security-role>
<security-role>
<role-name>CallerInfoEntityRole</role-name>
</security-role>
<method-permission>
<role-name>CallerInfoStatelessRole</role-name>
<method>
<ejb-name>StatelessSession</ejb-name>
<method-name>*</method-name>
</method>
</method-permission>
</assembly-descriptor>
</ejb-jar>
The jboss.xml will basically define the security domain to be used by this ejb application.
<?xml version="1.0"?>
<!DOCTYPE jboss PUBLIC
"-//JBoss//DTD JBOSS 4.0//EN"
"http://www.jboss.org/j2ee/dtd/jboss_4_0.dtd">
<jboss>
<security-domain>java:/jaas/xacml-test</security-domain>
<enterprise-beans>
<session>
<ejb-name>StatelessSession</ejb-name>
<jndi-name>spec.StatelessSession</jndi-name>
</session>
</enterprise-beans>
</jboss>
Step 2: Define the security domain configuration in a xxx-jboss-beans.xml (In our case, we will call the file ejbxacml-jboss-beans.xml)
<?xml version="1.0" encoding="UTF-8"?>
<deployment xmlns="urn:jboss:bean-deployer:2.0">
<application-policy xmlns="urn:jboss:security-beans:1.0" name="xacml-test">
<authentication>
<login-module code = "org.jboss.security.auth.spi.UsersRolesLoginModule"
flag = "required">
</authentication>
<authorization>
<policy-module code="org.jboss.security.authorization.modules.XACMLAuthorizationModule" flag="required" />
</authorization>
</application-policy>
</deployment>
Step 3: Define the JBossXACML configuration file
<ns:jbosspdp xmlns:ns="urn:jboss:xacml:2.0">
<ns:Policies>
<ns:Policy>
<ns:Location>META-INF/jboss-xacml-policy.xml</ns:Location>
</ns:Policy>
</ns:Policies>
<ns:Locators>
<ns:Locator Name="org.jboss.security.xacml.locators.JBossPolicySetLocator"/>
<ns:Locator Name="org.jboss.security.xacml.locators.JBossPolicyLocator"/>
</ns:Locators>
</ns:jbosspdp>
Step 4: Define a standard XACML policy file governing your ejb application.
<?xml version="1.0" encoding="UTF-8"?>
<Policy xmlns="urn:oasis:names:tc:xacml:2.0:policy:schema:os"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:oasis:names:tc:xacml:2.0:policy:schema:os
access_control-xacml-2.0-policy-schema-os.xsd"
PolicyId="urn:oasis:names:tc:xacml:2.0:jboss-test:XV:policy"
RuleCombiningAlgId="urn:oasis:names:tc:xacml:1.0:rule-combining-algorithm:deny-overrides">
<Description> Policy for Subject RBAC</Description>
<Target/>
<Rule RuleId="urn:oasis:names:tc:xacml:2.0:jboss-test:XVI:rule"
Effect="Permit">
<Description>
scott can create,remove and invoke echo method of StatelessSession EJB when
he has a role of ProjectUser
</Description>
<Target>
<Subjects>
<Subject>
<SubjectMatch
MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
<AttributeValue
DataType="http://www.w3.org/2001/XMLSchema#string">scott</AttributeValue>
<SubjectAttributeDesignator
AttributeId="urn:oasis:names:tc:xacml:1.0:subject:subject-id"
DataType="http://www.w3.org/2001/XMLSchema#string"/>
</SubjectMatch>
<SubjectMatch
MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
<AttributeValue
DataType="http://www.w3.org/2001/XMLSchema#string">ProjectUser</AttributeValue>
<SubjectAttributeDesignator
AttributeId="urn:oasis:names:tc:xacml:2.0:subject:role"
DataType="http://www.w3.org/2001/XMLSchema#string"/>
</SubjectMatch>
</Subject>
</Subjects>
<Resources>
<Resource>
<ResourceMatch
MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
<AttributeValue
DataType="http://www.w3.org/2001/XMLSchema#string">StatelessSession</AttributeValue>
<ResourceAttributeDesignator
AttributeId="urn:oasis:names:tc:xacml:1.0:resource:resource-id"
DataType="http://www.w3.org/2001/XMLSchema#string"/>
</ResourceMatch>
</Resource>
</Resources>
<Actions>
<Action>
<ActionMatch
MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
<AttributeValue
DataType="http://www.w3.org/2001/XMLSchema#string">create</AttributeValue>
<ActionAttributeDesignator
AttributeId="urn:oasis:names:tc:xacml:1.0:action:action-id"
DataType="http://www.w3.org/2001/XMLSchema#string"/>
</ActionMatch>
</Action>
<Action>
<ActionMatch
MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
<AttributeValue
DataType="http://www.w3.org/2001/XMLSchema#string">remove</AttributeValue>
<ActionAttributeDesignator
AttributeId="urn:oasis:names:tc:xacml:1.0:action:action-id"
DataType="http://www.w3.org/2001/XMLSchema#string"/>
</ActionMatch>
</Action>
<Action>
<ActionMatch
MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
<AttributeValue
DataType="http://www.w3.org/2001/XMLSchema#string">echo</AttributeValue>
<ActionAttributeDesignator
AttributeId="urn:oasis:names:tc:xacml:1.0:action:action-id"
DataType="http://www.w3.org/2001/XMLSchema#string"/>
</ActionMatch>
</Action>
</Actions>
</Target>
</Rule>
</Policy>
Generation of XACML policy files is a tough task in the absence of good xacml policy editors (which unfortunately is the current status in the open source world). Hopefully the future should be better with open source tools propping up for xacml policy editing.
Step 5: Package your ejb application
Mainly the xml configuration files go into META-INF of the ejb-jar
Step 6: Take a look at the test java code. It is straight ejb client code (of course, disguised as a JUnit test case). No sign of any xacml related code.
package org.jboss.test.security.test.authorization;
import java.rmi.RemoteException;
import javax.rmi.PortableRemoteObject;
import javax.security.auth.login.LoginContext;
import junit.extensions.TestSetup;
import junit.framework.Test;
import junit.framework.TestSuite;
import org.jboss.test.JBossTestCase;
import org.jboss.test.JBossTestSetup;
import org.jboss.test.security.interfaces.StatelessSession;
import org.jboss.test.security.interfaces.StatelessSessionHome;
import org.jboss.test.util.AppCallbackHandler;
public class XACMLEJBIntegrationUnitTestCase extends JBossTestCase
{
static String username = "scott";
static char[] password = "echoman".toCharArray();
LoginContext lc;
boolean loggedIn;
private static String login_config = "security/authorization/xacml-ejb/app-policy-service.xml";
public XACMLEJBIntegrationUnitTestCase(String name)
{
super(name);
}
public static Test suite() throws Exception
{
TestSuite suite = new TestSuite();
suite.addTest(new TestSuite(XACMLEJBIntegrationUnitTestCase.class));
// Create an initializer for the test suite
TestSetup wrapper = new JBossTestSetup(suite)
{
@Override
protected void setUp() throws Exception
{
super.setUp();
deploy("xacml-ejb.jar");
deploy(getResourceURL(login_config));
}
@Override
protected void tearDown() throws Exception
{
undeploy(getResourceURL(login_config));
undeploy("xacml-ejb.jar");
super.tearDown();
}
};
return wrapper;
}
/**
* Test that the echo method is accessible by an Echo role. Since the noop() method of the StatelessSession bean was
* not assigned any permissions it should be unchecked.
*/
public void testMethodAccess() throws Exception
{
log.debug("+++ testMethodAccess");
process();
}
private void process() throws Exception
{
login();
Object obj = getInitialContext().lookup("spec.StatelessSession");
obj = PortableRemoteObject.narrow(obj, StatelessSessionHome.class);
StatelessSessionHome home = (StatelessSessionHome) obj;
log.debug("Found StatelessSessionHome");
StatelessSession bean = home.create();
log.debug("Created spec.StatelessSession");
log.debug("Bean.echo('Hello') -> " + bean.echo("Hello"));
try
{
// This should not be allowed
bean.noop();
fail("Was able to call StatelessSession.noop");
}
catch (RemoteException e)
{
log.debug("StatelessSession.noop failed as expected");
}
bean.remove();
logout();
}
/**
* Login as user scott using the conf.name login config or 'spec-test' if conf.name is not defined.
*/
private void login() throws Exception
{
login(username, password);
}
private void login(String username, char[] password) throws Exception
{
if (loggedIn)
return;
lc = null;
String confName = System.getProperty("conf.name", "spec-test");
AppCallbackHandler handler = new AppCallbackHandler(username, password);
log.debug("Creating LoginContext(" + confName + ")");
lc = new LoginContext(confName, handler);
lc.login();
log.debug("Created LoginContext, subject=" + lc.getSubject());
loggedIn = true;
}
private void logout() throws Exception
{
if (loggedIn)
{
loggedIn = false;
lc.logout();
}
}
}
About the Author
Anil Saldhana is the lead security architect at JBoss. He blogs at http://anil-identity.blogspot.com
Opinions expressed by DZone contributors are their own.
Comments