Version 11

    << Go Back to PicketBox Overview

     

    PicketBox (formerly JBoss Security) supports facilities to map a Principal, Role(s) and Attribute(s) in a security process.


    Need for mapping

     

    It is important for any security framework to provide facilities to map principal or roles from one form to another.

    Examples include:

    • The authentication has been performed using X509 Certificates. Now you want to convert the principal from the certificate to a logical name that is meaningful to your application such as display purposes.
    • The authentication process derived a set of roles as part of the security domain. But you want to associate a few more roles with the current subject as part of the deployment archive.

     

    Read more below:

     


    Role Mapping

     

    The conversion of roles during a particular security event may be important for the following reasons:

    • You want to add more roles to the subject than what the authentication process derived.
    • You want to replace/remove one or more roles.

     

    Let us start with some code:

    import java.security.Principal;
    import java.util.HashMap;
    import java.util.Map;
    
    import javax.security.auth.Subject;
    
    import junit.framework.TestCase;
    
    import org.jboss.security.AuthenticationManager;
    import org.jboss.security.SimplePrincipal;
    import org.jboss.security.identity.RoleGroup;
    import org.jboss.security.identity.plugins.SimpleRole;
    import org.jboss.security.mapping.MappingContext;
    import org.jboss.security.mapping.MappingManager;
    import org.jboss.security.mapping.MappingType;
    import org.picketbox.config.PicketBoxConfiguration;
    import org.picketbox.factories.SecurityFactory;
    import org.picketbox.util.PicketBoxUtil;
    
    
    public void testRoleMapping()
       {
          String securityDomainName = "role-mapping-test";
          
          SecurityFactory.prepare();
          try
          {
             String configFile = "config/mapping.conf";
             PicketBoxConfiguration idtrustConfig = new PicketBoxConfiguration();
             idtrustConfig.load(configFile);
    
             AuthenticationManager am = SecurityFactory.getAuthenticationManager(securityDomainName);
             assertNotNull(am);
    
             Subject subject = new Subject();
             Principal principal = new SimplePrincipal("anil");
             Object credential = new String("pass");
    
             boolean result = am.isValid(principal, credential); 
             assertTrue("Valid Auth", result);
             result = am.isValid(principal, credential, subject);
             assertTrue("Valid Auth", result);
             assertTrue("Subject has principals", subject.getPrincipals().size() > 0); 
             
             RoleGroup roles = PicketBoxUtil.getRolesFromSubject(subject);
             if(roles == null)
                throw new RuntimeException("Roles obtained from subject are null");
             
             //Lets do the role mapping now
             MappingManager mm = SecurityFactory.getMappingManager(securityDomainName);
             MappingContext<RoleGroup> mc = mm.getMappingContext(MappingType.ROLE.name());
             
             Map<String,Object> contextMap = new HashMap<String,Object>();
             
             mc.performMapping(contextMap, roles);
             RoleGroup mappedRoles = mc.getMappingResult().getMappedObject(); 
             assertNotNull(mappedRoles);
             //We added two extra roles to the role group
             assertEquals("3 roles", 3, mappedRoles.getRoles().size());
             assertTrue("Contains AuthorizedUser", mappedRoles.containsRole(new SimpleRole("AuthorizedUser")));
             assertTrue("Contains InternalUser", mappedRoles.containsRole(new SimpleRole("InternalUser")));
          }
          finally
          {
             SecurityFactory.release();
          }
       }
    

     

    The entire mapping.conf is shown below:

    <?xml version='1.0'?> 
     
    <policy xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
             xsi:schemaLocation="urn:jboss:security-config:5.0"
             xmlns="urn:jboss:security-config:5.0"
             xmlns:jbxb="urn:jboss:security-config:5.0">
       <application-policy name = "role-mapping-test"> 
           <authentication>
              <login-module code = "org.jboss.security.auth.spi.UsersRolesLoginModule"
                 flag = "required">  
              </login-module> 
           </authentication> 
           <mapping>
             <mapping-module code="org.jboss.security.mapping.providers.OptionsRoleMappingProvider"
              type="role">
                 <module-option name="rolesMap" >
                            <java:properties xmlns:java="urn:jboss:java-properties"
                                     xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
                                     xs:schemaLocation="urn:jboss:java-properties resource:java-properties_1_0.xsd">
                                     <java:property>
                                        <java:key>validuser</java:key>
                                        <java:value>AuthorizedUser,InternalUser</java:value>
                                     </java:property>
                               </java:properties>
                          </module-option>
                       <module-option name="replaceRoles">false</module-option>
                   </mapping-module>
           </mapping>
           
        </application-policy>  
        
        
        <application-policy name = "principal-mapping-test"> 
           <authentication>
              <login-module code = "org.jboss.security.auth.spi.UsersRolesLoginModule"
                 flag = "required">  
              </login-module> 
           </authentication> 
           <mapping>
             <mapping-module
             code="org.jboss.security.mapping.providers.principal.SimplePrincipalMappingProvider" 
             type="principal">
                 <module-option name="principalsMap" >
                            <java:properties xmlns:java="urn:jboss:java-properties"
                                     xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
                                     xs:schemaLocation="urn:jboss:java-properties resource:java-properties_1_0.xsd">
                                     <java:property>
                                        <java:key>anil</java:key>
                                        <java:value>security-anil</java:value>
                                     </java:property>
                               </java:properties>
                          </module-option>
                       <module-option name="replaceRoles">false</module-option>
                   </mapping-module>
           </mapping>
           
        </application-policy>  
    </policy>
    

     

    Here we are trying to use an out of the box Mapping Provider named "org.jboss.security.mapping.providers.OptionsRoleMappingProvider" that takes in a properties style mapping of role name (key) with a comma separated list of roles (values). In this example, we also specify the module option "replaceRoles" as false such that the mapped roles are added to the subject and not replaced.

     


    Principal Mapping

     

    Please refer to the section above on "Need for Mapping".

     

    Let us look at some code:

    import java.security.Principal;
    import java.util.HashMap;
    import java.util.Map;
    
    import javax.security.auth.Subject;
    
    import junit.framework.TestCase;
    
    import org.jboss.security.AuthenticationManager;
    import org.jboss.security.SimplePrincipal;
    import org.jboss.security.identity.RoleGroup;
    import org.jboss.security.identity.plugins.SimpleRole;
    import org.jboss.security.mapping.MappingContext;
    import org.jboss.security.mapping.MappingManager;
    import org.jboss.security.mapping.MappingType;
    import org.picketbox.config.PicketBoxConfiguration;
    import org.picketbox.factories.SecurityFactory;
    import org.picketbox.util.PicketBoxUtil;
    
    
     public void testPrincipalMapping()
     {
     String securityDomainName = "principal-mapping-test";
     
     SecurityFactory.prepare();
     try
     {
     String configFile = "config/mapping.conf";
     PicketBoxConfiguration idtrustConfig = new PicketBoxConfiguration();
     idtrustConfig.load(configFile);
    
     AuthenticationManager am = SecurityFactory.getAuthenticationManager(securityDomainName);
     assertNotNull(am);
    
     Subject subject = new Subject();
     Principal principal = new SimplePrincipal("anil");
     Object credential = new String("pass");
    
     boolean result = am.isValid(principal, credential); 
     assertTrue("Valid Auth", result);
     result = am.isValid(principal, credential, subject);
     assertTrue("Valid Auth", result);
     assertTrue("Subject has principals", subject.getPrincipals().size() > 0); 
     
     //Lets do the role mapping now
     MappingManager mm = SecurityFactory.getMappingManager(securityDomainName);
     MappingContext<Principal> mc = mm.getMappingContext(MappingType.PRINCIPAL.name());
     
     Map<String,Object> contextMap = new HashMap<String,Object>();
     
     mc.performMapping(contextMap, principal);
     Principal mappedPrincipal = mc.getMappingResult().getMappedObject(); 
     
     assertTrue("security-anil".equals(mappedPrincipal.getName()));
     }
     finally
     {
     SecurityFactory.release();
     }
     }
    

     

    The mapping.conf is shown above.

     

    This is an example where we change the name of the principal from "anil" to "security-anil". Toward this, we utilize a mapping provider called as "org.jboss.security.mapping.providers.principal.SimplePrincipalMappingProvider" that takes in a Java properties style module option. We specify a principal ("anil") with another "security-anil".

     

     


    Attribute Mapping

     

    There is an LDAP Attribute Mapping Provider provided out of the box:


    org.jboss.security.mapping.providers.attribute.LdapAttributeMappingProvider

     

    Aim:  Maps attributes from LDAP
    Module Options: The options include whatever options your LDAP JNDI provider supports.

    Examples of standard property names are:

     

    • Context.INITIAL_CONTEXT_FACTORY = "java.naming.factory.initial"
    • Context.SECURITY_PROTOCOL = "java.naming.security.protocol"
    • Context.PROVIDER_URL = "java.naming.provider.url"
    • Context.SECURITY_AUTHENTICATION = "java.naming.security.authentication"

     

    Other Module Options:-

    • bindDN:The DN used to bind against the ldap server for the user and roles queries. This is some DN with read/search permissions on the baseCtxDN and rolesCtxDN values. bindCredential: The password for the bindDN. This can be encrypted if the jaasSecurityDomain is specified.
    • baseCtxDN: The fixed DN of the context to start the user search from.
    • baseFilter:A search filter used to locate the context of the user to authenticate. The input username/userDN as obtained from the login module callback will be substituted into the filter anywhere a "{0}" expression is seen. This substituion behavior comes from the standard __DirContext.search(Name, String, Object[], SearchControls cons)__ method. An common example search filter is "(uid={0})".
    • searchTimeLimit:The timeout in milliseconds for the user/role searches.Defaults to 10000 (10 seconds).
    • attributeList: A comma-separated list of attributes for the user (Example:  mail,cn,sn,employeeType,employeeNumber)
    • jaasSecurityDomain: The JMX ObjectName of the JaasSecurityDomain to useto decrypt the java.naming.security.principal. The encrypted form of the password is that returned by the JaasSecurityDomain#encrypt64(byte[]) method. The org.jboss.security.plugins.PBEUtils can also be used to generate the encrypted form.

    Note: jaasSecurityDomain option works only in JBoss Application Server.

     

    Lets look at an example:

    public void testLDAPAttributes() throws Exception
    {  
       //Get the Mapping Manager via the SecurityFactory etc...
       .... <snip/> ....
    
       MappingManager mm;
    .....
       MappingContext<List<Attribute<String>>> mc = mm.getMappingContext(MappingType.ATTRIBUTE.name());
       map.put(SecurityConstants.PRINCIPAL_IDENTIFIER, new SimplePrincipal("jduke"));
          
       List<Attribute<String>> attList = new ArrayList<Attribute<String>>();
          
          mc.performMapping(map, attList);
          attList = (List<Attribute<String>>) mc.getMappingResult().getMappedObject();
          boolean foundEmail = false;
          boolean foundEmployeeType = false;
          boolean foundEmployeeNumber = false;
          
          for(Attribute<String> att: attList)
          {
             String attName = att.getName();
             if(attName.equals(Attribute.TYPE.EMAIL_ADDRESS.get()))
             {
                assertEquals("theduke@somecastle.man",att.getValue());
                foundEmail = true;
             }
             if(attName.equals("employeeType"))
             {
                assertEquals("permanent",att.getValue());
                foundEmployeeType = true;
             }
             if(attName.equals("employeeNumber"))
             {
                assertEquals("007",att.getValue());
                foundEmployeeNumber = true;
             }
          }
          assertTrue("Found Email", foundEmail);
          assertTrue("Found Emp Type", foundEmployeeType);
          assertTrue("Found Emp Number", foundEmployeeNumber);
       }
    

     

     

    Let us look at the ldap-attributes-config.xml that defines the mapping configuration at the security domain level.

    <policy xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
             xsi:schemaLocation="urn:jboss:security-config:5.0"
             xmlns="urn:jboss:security-config:5.0"
             xmlns:jbxb="urn:jboss:security-config:5.0">
    
    <application-policy name="test">
         <mapping>
         <mapping-module
              code="org.jboss.security.mapping.providers.attribute.LdapAttributeMappingProvider"
             type="attribute">
                   <module-option
    name="attributeList">mail,cn,commonname,givenname,surname,employeeType,employeeNumber,facsimileTelephoneNumber</module-option>
                   <module-option name="bindDN">cn=Directory Manager</module-option>
                   <module-option name="bindCredential">password</module-option>
                   <module-option name="baseFilter">(uid={0})</module-option>
                   <module-option name="java.naming.factory.initial">com.sun.jndi.ldap.LdapCtxFactory</module-option>
                   <module-option name="java.naming.provider.url">ldap://localhost:10389</module-option>
                   <module-option name="baseCtxDN">ou=People,dc=jboss,dc=org</module-option>
              </mapping-module>
         </mapping>
    </application-policy>  
    
    </policy>
    

     

    We did populate the LDAP server with an ldif ldapAttributes.ldif

    You will have to refer to your ldap server documentation to figure out how to populate your ldap server or

    reuse already existing DIT.

     

    dn: dc=jboss,dc=org
    objectclass: dcObject
    objectclass: organization
    o: JBoss
    dc: JBoss
    
    dn: ou=People,dc=jboss,dc=org
    objectclass: top
    objectclass: organizationalUnit
    ou: People
    
    dn: uid=jduke,ou=People,dc=jboss,dc=org
    objectclass: top
    objectclass: uidObject
    objectclass: person
    objectclass: inetOrgPerson
    uid: jduke
    cn: Java Duke
    sn: Duke
    userPassword: theduke
    mail: theduke@somecastle.man
    employeeType:  permanent
    employeeNumber: 007
    

     


    PicketBox Mapping using Java Annotations

     

    Instead of an external xml configuration, you can always provide the information via the @SecurityMapping annotation.

    import org.jboss.security.annotation.Authentication;
    import org.jboss.security.annotation.Module;
    import org.jboss.security.annotation.ModuleOption;
    import org.jboss.security.annotation.SecurityMapping;
    import org.jboss.security.auth.spi.UsersRolesLoginModule;
    
    import org.jboss.security.mapping.providers.OptionsRoleMappingProvider;
    
    @Authentication(modules =
    {@Module(code = UsersRolesLoginModule.class, options =
    {@ModuleOption})})
    
    @SecurityMapping(modules =
    {@Module(code = OptionsRoleMappingProvider.class, type="role", options =
    {@ModuleOption(key="rolesMap",value="validuser=AuthorizedUser,InternalUser", valueType=VALUE_TYPE.JAVA_PROPERTIES),
          @ModuleOption(key="replaceRoles", value="false")})})
    public class AuthPlusMappingAnnotatedPOJO
    { 
    }
    
    

     

    The test code is:

    @Test
       public void testAuthenticationAndMappingAnnotation() throws Exception
       {
          AuthPlusMappingAnnotatedPOJO pojo = new AuthPlusMappingAnnotatedPOJO();
          
          PicketBoxProcessor processor = new PicketBoxProcessor(); 
          processor.setSecurityInfo("anil", "pass");
          processor.process(pojo);
          
          Principal anil = new SimplePrincipal("anil");
          assertEquals("Principal == anil", anil, processor.getCallerPrincipal());
          Subject callerSubject = processor.getCallerSubject();
          assertNotNull("Subject is not null", callerSubject);
          assertTrue("Subject contains principal anil", callerSubject.getPrincipals().contains(anil));
          RoleGroup callerRoles = processor.getCallerRoles();
          assertTrue("InternalUser is a role", callerRoles.containsRole(new SimpleRole("InternalUser")));
          assertTrue("AuthorizedUser is a role", callerRoles.containsRole(new SimpleRole("AuthorizedUser")));
       }
    

     


    @SecurityMapping Annotation

    More details available at PicketBoxSecurityAnnotations

     

    References:

    1. Mapping Roles in JBoss Application Server v5.x

     

    << Go Back to PicketBox Overview