JBoss.orgCommunity Documentation

Chapter 8. Authorization Stacks

If a security domain does not define an authorization module, the default jboss-web-policy and jboss-ejb-policy authorization configured in security-policies-jboss-beans.xml is used. If you specify an authorization module, or create a custom deployment descriptor file with valid authorization configuration, these settings override the default settings in security-policies-jboss-beans.xml.

Overriding the default authorization for EJB or Web components is provided for JACC and XACML, apart from the default modules that implement the specification behavior. Users can provide authorization modules that implement custom behavior. Configuring this functionality allows access control stacks to be pluggable for a particular component, overriding the default authorization contained in jboss.xml (for EJBs) and jboss-web.xml (for WAR).

Setting authorization for all EJB and WEB components

You can override authorization for all EJBs and Web components, or for a particular component.

Procedure 8.1. Set authorization policies for all EJB and WAR components

This procedure describes how to define JACC Authorization control for all EJB and WAR components. The example defines application policy modules for Web and EJB applications: jboss-web-policy, and jboss-ejb-policy.

  1. Open the security policy bean

    Navigate to $JBOSS_HOME/server/$PROFILE/deploy/security

    Open the security-policies-jboss-beans.xml file.

    By default, the security-policies-jboss-beans.xml file contains the configuration in Example 8.1, “security-policies default configuration”


  2. Change the application-policy definitions

    To set a single authorization policy for each component using JACC, amend each <policy-module> code attribute with the name of the JACC authorization module.

    
    <?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="jboss-web-policy" extends="other">
          <authorization>
             <policy-module code="org.jboss.security.authorization.modules.JACCAuthorizationModule" flag="required"/>
          </authorization>
       </application-policy>   

       <application-policy xmlns="urn:jboss:security-beans:1.0" name="jboss-ejb-policy" extends="other">
          <authorization>
             <policy-module code="org.jboss.security.authorization.modules.JACCAuthorizationModule" flag="required"/>
          </authorization>
       </application-policy>

       <application-policy xmlns="urn:jboss:security-beans:1.0" name="jacc-test" extends="other">
          <authorization>
             <policy-module code="org.jboss.security.authorization.modules.JACCAuthorizationModule" flag="required"/>
          </authorization>
       </application-policy>

    </deployment>

                
  3. Restart server

    You have now configured the security-policy-jboss-beans.xml file with JACC authorization enabled for each application policy.

    Restart the server to ensure the new security policy takes effect.

Setting authorization for specific EJB and WEB components

If applications require more granular security policies, you can declare multiple authorization security policies for each application policy. New security domains can inherit base settings from another application policy, and override specific settings such as the authorization policy module.

Procedure 8.2. Set authorization policies for specific security domains

This procedure describes how to inherit settings from other application policy definitions, and specify different authorization policies per security domain.

In this procedure, two security domains are defined. The test-domain security domain uses the UsersRolesLoginModule login module and uses JACC authorization. The test-domain-inherited security domain inherits the login module information from test-domain, and specifies XACML authorization must be used.

  1. Open the security policy

    You can specify the security domain settings in the login-config.xml file, or create a deployment descriptor file containing the settings. Choose the deployment descriptor if you want to package the security domain settings with your application.

    • Locate and open login-config.xml

      Navigate to the login-config.xml file for the server profile you are using and open the file for editing. For example:

      $JBOSS_HOME/jboss-as/server/$PROFILE/conf/login.config.xml

    • Create a jboss-beans.xml descriptor

      Create a [prefix]-jboss-beans.xml descriptor, replacing [prefix] with a meaningful name (for example, test-war-jboss-beans.xml)

      Save this file in the deploy directory of the server profile you are configuring. For example:

      $JBOSS_HOME/jboss-as/server/$PROFILE/deploy/test-war-jboss-beans.xml

  2. Specify the test-domain security domain

    In the target file chosen in step 1, specify the test-domain security domain. This domain contains the authentication information, including the <login-module> definition, and the JACC authorization policy module definition.

    
    <?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="test-domain">
         <authentication>
            <login-module code = "org.jboss.security.auth.spi.UsersRolesLoginModule"
                flag = "required">
                <module-option name = "unauthenticatedIdentity">anonymous</module-option>
                <module-option name="usersProperties">u.properties</module-option>
                <module-option name="rolesProperties">r.properties</module-option>
             </login-module>
          </authentication>
          <authorization>
             <policy-module code="org.jboss.security.authorization.modules.JACCAuthorizationModule" flag="required"/>
          </authorization>
       </application-policy>  


    </deployment>

                
  3. Append the test-domain-inherited security domain

    Append the test-domain-inherited application policy definition after the test-domain application policy. Set the extends attribute to other, so the login module information is inherited. Specify the XACML authorization module in the <policy.module> element.

    
    <?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="test-domain">
         <authentication>
            <login-module code = "org.jboss.security.auth.spi.UsersRolesLoginModule"
                flag = "required">
                <module-option name = "unauthenticatedIdentity">anonymous</module-option>
                <module-option name="usersProperties">u.properties</module-option>
                <module-option name="rolesProperties">r.properties</module-option>
             </login-module>
          </authentication>
          <authorization>
             <policy-module code="org.jboss.security.authorization.modules.JACCAuthorizationModule" flag="required"/>
          </authorization>
       </application-policy>  

       <application-policy xmlns="urn:jboss:security-beans:1.0" name="test-domain-inherited" extends="other"> 
          <authorization>
             <policy-module code="org.jboss.security.authorization.modules.XACMLAuthorizationModule" flag="required"/>
          </authorization>
       </application-policy> 

    </deployment>

                
  4. Restart server

    You have now configured the target file with two security domains that use different authorization methods.

    Restart the server to ensure the new security policy takes effect.

Setting authorization module delegates

Set authorization policies for all EJB and WAR components and Set authorization policies for specific security domains describe simplistic examples that show how authentication and authorization can be configured in security domains.

Because authorization relates to the type of component (not the layer) you want to protect, you can use delegation within a deployment descriptor to specify different authorization policies to the standard authentication in your implementation.

The delegates must be a subclass of AuthorizationModuleDelegate. Example 8.2, “AuthorizationModuleDelegate class” describes the base AuthorizationModuleDelegate interface.

Example 8.2. AuthorizationModuleDelegate class

package org.jboss.security.authorization.modules;

 
import javax.security.auth.Subject;
import org.jboss.logging.Logger;
import org.jboss.security.authorization.AuthorizationModule;
import org.jboss.security.authorization.PolicyRegistration;
import org.jboss.security.authorization.Resource;
import org.jboss.security.identity.RoleGroup;
//$Id$
/**
 *  Delegate for Authorization Module
 *  @author <a href="mailto:Anil.Saldhana@jboss.org">Anil Saldhana</a>
 *  @since  Jun 19, 2006 
 *  @version $Revision$
 */
public abstract class AuthorizationModuleDelegate
{
   protected static Logger log = Logger.getLogger(AuthorizationModuleDelegate.class);
   protected boolean trace = false;
   
   /**
    * Policy Registration Manager Injected
    */
   protected PolicyRegistration policyRegistration = null; 
   
   /**
    * @see AuthorizationModule#authorize(Resource)
    * @param resource
    * @param subject Authenticated Subject
    * @param role RoleGroup
    * @return
    */
   public abstract int authorize(Resource resource, Subject subject, RoleGroup role); 
   
   /**
    * Set the PolicyRegistration manager 
    * Will be used to query for the policies
    * @param authzManager
    */
   public void setPolicyRegistrationManager(PolicyRegistration pm)
   {
      this.policyRegistration = pm;
   } 
}
         

Some examples of authorization delegation are included for reference. Example 8.3, “EJBJACCPolicyModuleDelegate.java” describes an authorization module responsible for authorization decisions for the EJB layer. Example 8.4, “WebJACCPolicyModuleDelegate.java” describes a JACC-based authorization module helper that controls web layer authorization decisions.

Example 8.3. EJBJACCPolicyModuleDelegate.java

package org.jboss.security.authorization.modules.ejb;


import java.lang.reflect.Method;
import java.security.CodeSource;
import java.security.Permission;
import java.security.Policy;
import java.security.Principal;
import java.security.ProtectionDomain;
import java.util.Map;
import javax.security.auth.Subject;
import javax.security.jacc.EJBMethodPermission;
import javax.security.jacc.EJBRoleRefPermission;
import org.jboss.logging.Logger;
import org.jboss.security.authorization.AuthorizationContext;
import org.jboss.security.authorization.PolicyRegistration;
import org.jboss.security.authorization.Resource;
import org.jboss.security.authorization.ResourceKeys;
import org.jboss.security.authorization.modules.AbstractJACCModuleDelegate;
import org.jboss.security.authorization.modules.AuthorizationModuleDelegate;
import org.jboss.security.authorization.resources.EJBResource;
import org.jboss.security.identity.Role;
import org.jboss.security.identity.RoleGroup;
 
//$Id$
/**
 *  Authorization Module delegate that deals with the authorization decisions
 *  for the EJB Layer
 *  @author <a href="mailto:Anil.Saldhana@jboss.org">Anil Saldhana</a>
 *  @since  Jul 6, 2006 
 *  @version $Revision$
 */
public class EJBJACCPolicyModuleDelegate extends AbstractJACCModuleDelegate
{  
   private String ejbName = null;
   private Method ejbMethod = null; 
   private String methodInterface = null;
   private CodeSource ejbCS = null;
   private String roleName = null;  
   private Boolean roleRefCheck = Boolean.FALSE;  
   
   public EJBJACCPolicyModuleDelegate()
   {
      log = Logger.getLogger(getClass());
      trace = log.isTraceEnabled();
   }
   
   /**
    * @see AuthorizationModuleDelegate#authorize(Resource)
    */
   public int authorize(Resource resource, Subject callerSubject, RoleGroup role)
   {
      if(resource instanceof EJBResource == false)
         throw new IllegalArgumentException("resource is not an EJBResource");
      
      EJBResource ejbResource = (EJBResource) resource;
      
      //Get the context map
      Map<String,Object> map = resource.getMap();
      if(map == null)
         throw new IllegalStateException("Map from the Resource is null");
      this.policyRegistration = (PolicyRegistration) map.get(ResourceKeys.POLICY_REGISTRATION);
      
      this.ejbCS = ejbResource.getCodeSource();
      this.ejbMethod = ejbResource.getEjbMethod();
      this.ejbName = ejbResource.getEjbName();
      this.methodInterface = ejbResource.getEjbMethodInterface();
      
      //isCallerInRole checks
      this.roleName = (String)map.get(ResourceKeys.ROLENAME); 
      
      this.roleRefCheck = (Boolean)map.get(ResourceKeys.ROLEREF_PERM_CHECK);
      if(this.roleRefCheck == Boolean.TRUE)
         return checkRoleRef(callerSubject, role);
      else
         return process(callerSubject, role);
   } 
   
   //Private Methods
   /**
    * Process the request
    * @param request
    * @param sc
    * @return
    */
   private int process(Subject callerSubject, Role role) 
   {  
      EJBMethodPermission methodPerm = 
         new EJBMethodPermission(ejbName, methodInterface, ejbMethod); 
      boolean policyDecision = checkWithPolicy(methodPerm, callerSubject, role); 
      if( policyDecision == false )
      {
         String msg = "Denied: "+methodPerm+", caller=" + callerSubject+", role="+role;
         if(trace)
            log.trace("EJB Jacc Delegate:"+msg);  
      }  
      return policyDecision ? AuthorizationContext.PERMIT : AuthorizationContext.DENY;
   }
   
   private int checkRoleRef(Subject callerSubject, RoleGroup callerRoles)
   { 
      //This has to be the EJBRoleRefPermission  
      EJBRoleRefPermission ejbRoleRefPerm = new EJBRoleRefPermission(ejbName,roleName); 
      boolean policyDecision = checkWithPolicy(ejbRoleRefPerm, callerSubject, callerRoles); 
      if( policyDecision == false )
      {
         String msg = "Denied: "+ejbRoleRefPerm+", caller=" + callerSubject;
         if(trace)
            log.trace("EJB Jacc Delegate:"+msg);  
      }  
      return policyDecision ? AuthorizationContext.PERMIT : AuthorizationContext.DENY; 
   }
   
   private boolean checkWithPolicy(Permission ejbPerm, Subject subject, Role role)
   {
      Principal[] principals = this.getPrincipals(subject, role);  
      ProtectionDomain pd = new ProtectionDomain (ejbCS, null, null, principals);
      return Policy.getPolicy().implies(pd, ejbPerm); 
   }
}
         

Example 8.4. WebJACCPolicyModuleDelegate.java

package org.jboss.security.authorization.modules.web;


import java.io.IOException;
import java.security.CodeSource;
import java.security.Permission;
import java.security.Policy;
import java.security.Principal;
import java.security.ProtectionDomain;
import java.util.Map;
import java.util.Set;
import javax.security.auth.Subject;
import javax.security.jacc.WebResourcePermission;
import javax.security.jacc.WebRoleRefPermission;
import javax.security.jacc.WebUserDataPermission;
import javax.servlet.http.HttpServletRequest;
import org.jboss.logging.Logger;
import org.jboss.security.authorization.AuthorizationContext;
import org.jboss.security.authorization.PolicyRegistration;
import org.jboss.security.authorization.Resource;
import org.jboss.security.authorization.ResourceKeys;
import org.jboss.security.authorization.modules.AbstractJACCModuleDelegate;
import org.jboss.security.authorization.modules.AuthorizationModuleDelegate;
import org.jboss.security.authorization.resources.WebResource;
import org.jboss.security.identity.Role;
import org.jboss.security.identity.RoleGroup;
//$Id: WebJACCPolicyModuleDelegate.java 62923 2007-05-09 03:08:14Z anil.saldhana@jboss.com $
/**
 *  JACC based authorization module helper that deals with the web layer 
 *  authorization decisions
 *  @author <a href="mailto:Anil.Saldhana@jboss.org">Anil Saldhana</a>
 *  @since  July 7, 2006 
 *  @version $Revision: 62923 $
 */
public class WebJACCPolicyModuleDelegate extends AbstractJACCModuleDelegate
{   
   private Policy policy = Policy.getPolicy(); 
   private HttpServletRequest request = null;
   private CodeSource webCS = null;
   
   private String canonicalRequestURI = null; 
   public WebJACCPolicyModuleDelegate()
   {  
      log = Logger.getLogger(WebJACCPolicyModuleDelegate.class);
      trace = log.isTraceEnabled();
   }
   /**
    * @see AuthorizationModuleDelegate#authorize(Resource)
    */
   @SuppressWarnings("unchecked")
   public int authorize(Resource resource, Subject callerSubject, RoleGroup role)
   {
      if(resource instanceof WebResource == false)
         throw new IllegalArgumentException("resource is not a WebResource");
      
      WebResource webResource = (WebResource) resource;
      
      //Get the context map
      Map<String,Object> map = resource.getMap();
      if(map == null)
         throw new IllegalStateException("Map from the Resource is null"); 
      
      //Get the Request Object
      request = (HttpServletRequest) webResource.getServletRequest();
      
      webCS = webResource.getCodeSource();
      this.canonicalRequestURI = webResource.getCanonicalRequestURI();      
      String roleName = (String)map.get(ResourceKeys.ROLENAME);
      Principal principal = (Principal)map.get(ResourceKeys.HASROLE_PRINCIPAL);
      Set<Principal> roles = (Set<Principal>)map.get(ResourceKeys.PRINCIPAL_ROLES); 
      String servletName = webResource.getServletName();
      Boolean resourceCheck = checkBooleanValue((Boolean)map.get(ResourceKeys.RESOURCE_PERM_CHECK));
      Boolean userDataCheck = checkBooleanValue((Boolean)map.get(ResourceKeys.USERDATA_PERM_CHECK));
      Boolean roleRefCheck = checkBooleanValue((Boolean)map.get(ResourceKeys.ROLEREF_PERM_CHECK)); 
      
      validatePermissionChecks(resourceCheck,userDataCheck,roleRefCheck);
      
      boolean decision = false;
      
      try
      {
         if(resourceCheck)
            decision = this.hasResourcePermission(callerSubject, role);
         else
         if(userDataCheck)
           decision = this.hasUserDataPermission();
         else
         if(roleRefCheck)
            decision = this.hasRole(principal, roleName, roles, servletName);
         else
            if(trace)
              log.trace("Check is not for resourcePerm, userDataPerm or roleRefPerm.");
      }
      catch(IOException ioe)
      {
         if(trace)
            log.trace("IOException:",ioe);
      } 
      return decision ? AuthorizationContext.PERMIT : AuthorizationContext.DENY;
   }
   /**
    * @see AuthorizationModuleDelegate#setPolicyRegistrationManager(PolicyRegistration)
    */
   public void setPolicyRegistrationManager(PolicyRegistration authzM)
   { 
     this.policyRegistration = authzM;
   }     
   //****************************************************************************
   //  PRIVATE METHODS
   //****************************************************************************
   /** See if the given JACC permission is implied using the caller as
    * obtained from either the
    * PolicyContext.getContext(javax.security.auth.Subject.container) or
    * the info associated with the requestPrincipal.
    * 
    * @param perm - the JACC permission to check
    * @param requestPrincpal - the http request getPrincipal
    * @param caller the authenticated subject obtained by establishSubjectContext
    * @return true if the permission is allowed, false otherwise
    */ 
   private boolean checkPolicy(Permission perm, Principal requestPrincpal,
         Subject caller, Role role)
   {  
      // Get the caller principals, its null if there is no caller
      Principal[] principals = getPrincipals(caller,role); 
      
      return checkPolicy(perm, principals);
   }
   
   
   /** See if the given permission is implied by the Policy. This calls
    * Policy.implies(pd, perm) with the ProtectionDomain built from the
    * active CodeSource set by the JaccContextValve, and the given
    * principals.
    * 
    * @param perm - the JACC permission to evaluate
    * @param principals - the possibly null set of principals for the caller
    * @return true if the permission is allowed, false otherwise
    */ 
   private boolean checkPolicy(Permission perm, Principal[] principals)
   { 
      ProtectionDomain pd = new ProtectionDomain(webCS, null, null, principals);
      boolean allowed = policy.implies(pd, perm);
      if( trace )
      {
         String msg = (allowed ? "Allowed: " : "Denied: ") +perm;
         log.trace(msg);
      }
      return allowed;
   } 
   
   /**
    * Ensure that the bool is a valid value
    * @param bool
    * @return bool or Boolean.FALSE (when bool is null)
    */
   private Boolean checkBooleanValue(Boolean bool)
   {
      if(bool == null)
         return Boolean.FALSE;
      return bool;
   } 
   
   /**
    * Perform hasResourcePermission Check
    * @param request
    * @param response
    * @param securityConstraints
    * @param context
    * @param caller
    * @return
    * @throws IOException
    */
   private boolean hasResourcePermission(Subject caller, Role  role)
   throws IOException
   { 
      Principal requestPrincipal = request.getUserPrincipal(); 
      WebResourcePermission perm = new WebResourcePermission(this.canonicalRequestURI, 
                                                     request.getMethod());
      boolean allowed = checkPolicy(perm, requestPrincipal, caller, role );
      if( trace )
         log.trace("hasResourcePermission, perm="+perm+", allowed="+allowed); 
      return allowed;
   }
   /**
    * Perform hasRole check 
    * @param principal
    * @param role
    * @param roles
    * @return
    */
   private boolean hasRole(Principal principal, String roleName, 
         Set<Principal> roles, String servletName)
   { 
      if(servletName == null)
         throw new IllegalArgumentException("servletName is null");
      
      WebRoleRefPermission perm = new WebRoleRefPermission(servletName, roleName);
      Principal[] principals = {principal}; 
      if( roles != null )
      {
         principals = new Principal[roles.size()];
         roles.toArray(principals);
      }
      boolean allowed = checkPolicy(perm, principals);
      if( trace )
         log.trace("hasRole, perm="+perm+", allowed="+allowed);
      return allowed;
   }
   /**
    * Perform hasUserDataPermission check for the realm.
    * If this module returns false, the base class (Realm) will
    * make the decision as to whether a redirection to the ssl
    * port needs to be done
    * @param request
    * @param response
    * @param constraints
    * @return
    * @throws IOException
    */
   private boolean hasUserDataPermission() throws IOException
   { 
      WebUserDataPermission perm = new WebUserDataPermission(this.canonicalRequestURI,
                                               request.getMethod());
      if( trace )
         log.trace("hasUserDataPermission, p="+perm);
      boolean ok = false;
      try
      {
         Principal[] principals = null;
         ok = checkPolicy(perm, principals);
      }
      catch(Exception e)
      {
         if( trace )
            log.trace("Failed to checkSecurityAssociation", e);
      } 
      return ok;
   }
   /**
    * Validate that the access check is made only for one of the 
    * following
    * @param resourceCheck
    * @param userDataCheck
    * @param roleRefCheck
    */
   private void validatePermissionChecks(Boolean resourceCheck,
         Boolean userDataCheck, Boolean roleRefCheck)
   {
      if(trace)
         log.trace("resourceCheck="+resourceCheck + " : userDataCheck=" + userDataCheck
               + " : roleRefCheck=" + roleRefCheck); 
      if((resourceCheck == Boolean.TRUE && userDataCheck == Boolean.TRUE && roleRefCheck == Boolean.TRUE ) 
           || (resourceCheck == Boolean.TRUE && userDataCheck == Boolean.TRUE) 
           || (userDataCheck == Boolean.TRUE && roleRefCheck == Boolean.TRUE))
         throw new IllegalStateException("Permission checks must be different"); 
   }
}