Jan 21, 2014

I was recently given a requirement to drive the entity attribute validations at runtime rather than at design time, with the following criteria.
  1. Ability to change fields to required at runtime.
  2. To be able to define validation message for field at runtime
  3. Define validations such as field must be numeric, or contain only alphabets (Used Regular expression for this)
  4. Localization of validation messages
To accomplish this we can use database dictionary views along with custom database tables to store the validations and perform validations at runtime. The data model diagram is shown below.
DEMO_RUNTIME_VALThe key thing then is to use database dictionary views along with these database tables to validate attributes of the entity for a pattern (regular expression).

The class that contains the method to perform validations is CustomEntityImpl and each entity that one wants to enable for runtime validation must extend this class and call its validateCustomEntity method in its entity method validator.

    protected Boolean validateCustomEntity(){
          
            ResourceBundleDef rbd=getEntityDef().getResourceBundleDef();
            PropertiesBundleDef pbd=null;
            if(rbd==null){
            pbd = new PropertiesBundleDef(this.getEntityDef());
            pbd.setPropertiesFile("com.blogspot.ramannanda.demos.validationapp.model.ModelBundle");
            rbd=pbd;
            }
            ArrayList<RowValException> exceptions=new ArrayList<RowValException>();
            EntityImpl entObj= this;
                       String tableName=this.getEntityDef().getSource();
                       tableName=tableName.substring(tableName.lastIndexOf(".")+1,tableName.length());
                       AttributeDef[] attrDefs= this.getEntityDef().getAttributeDefs();
                       
                       ValidationsVOImpl vo=(ValidationsVOImpl) this.getDBTransaction().getRootApplicationModule().findViewObject("ValidationsVO12");
                       ViewCriteriaManager vcm=vo.getViewCriteriaManager();
                       ViewCriteria vc=vcm.getViewCriteria("ValidationsVOCriteria");
                       vo.setbTableName(tableName);
                       vo.applyViewCriteria(vc, false);
                       vo.executeQuery();
                       RowSetIterator it=vo.createRowSetIterator(null);
                       ArrayList<AttrValException> attrValExceptions=new ArrayList<AttrValException>();
                       while(it.hasNext()){
                          ValidationsVORowImpl row=(ValidationsVORowImpl)it.next();
                          String pattern=row.getValidationPattern();
                          String fieldName=row.getAttribName();
                           
                               for(int i=0;i<attrDefs.length;i++){
                                    String attrName=attrDefs[i].getName();
                                    String columnName=attrDefs[i].getColumnName();
                                   if(columnName.equals(fieldName)){
                                       String attribRequired=row.getAttribRequired();
                                       Object attribValue=entObj.getAttribute(attrName);
                                      
                                       ArrayList<JboException> list=new ArrayList<JboException>(2);
                                       if(attribRequired.equalsIgnoreCase("Y"))
                                       {
                                           if(attribValue==null || ((String)attribValue).isEmpty()){
                                          String errorMessageKey=row.getRequiredMsgKey();
                                           if(errorMessageKey!=null&&!errorMessageKey.isEmpty()){
                                           JboException exception=new JboException(getMessageForKey(errorMessageKey),null,new Object[]{attrName});
                                                   list.add(exception);
                                               }
                                           else{
                                                   AttrSetValException exception=
                                                       new AttrSetValException(AttrValException.TYP_DEF_ENTITY_OBJECT,  rbd,
                                                      "DEFAULT_REQUIRED_ERROR", entObj.getStructureDef().getFullName(),
                                                       attrName,attribValue, null);
                                                       list.add(exception);
                                               }
                                            
                                           }
                                       }
                                       if(((attribValue!=null)&& !((String)attribValue).isEmpty())){
                                       boolean result=validatePattern(pattern,(String)entObj.getAttribute(attrName));
                                       if(!result){
                                          
                                               String errorMessageKey=row.getValidationMsgKey();
                                               if(errorMessageKey!=null&&!errorMessageKey.isEmpty()){
                                                       JboException exception=new JboException(getMessageForKey(errorMessageKey),null,new Object[]{attrName,attribValue});
                                                               list.add(exception);
                                                       list.add(exception);
                                                   }
                                               else{
                                                       AttrSetValException exception=
                                                           new AttrSetValException(AttrValException.TYP_DEF_ENTITY_OBJECT,  rbd,
                                                          "DEFAULT_INCORRECT_VAL_ERROR", entObj.getStructureDef().getFullName(),
                                                           attrName,attribValue, null);
                                                           list.add(exception);
                                                   }
                                           }
                                       }
                                       if(list.size()>0){
                                               AttrValException ave =
                                                        new AttrValException(CSMessageBundle.class, CSMessageBundle.EXC_VAL_VR_VALIDATE_FAILED, entObj.getStructureDef().getFullName(),
                                                                             attrName,attribValue, list, false);
                                               attrValExceptions.add(ave);
                                           }
                                       
                                       
                                   } 
                           }
                      
                   }
                       it.closeRowSetIterator();
                       if(attrValExceptions.size()>0){
                           RowValException rowValException= new RowValException(CSMessageBundle.class, CSMessageBundle.EXC_VAL_VR_VALIDATE_FAILED,
                                entObj.getStructureDef().getFullName(), entObj.getKey(), attrValExceptions);
                           exceptions.add(rowValException);
                           }

                 
     
                   if(exceptions.size()>0){
                       throw new
                        RowValException(CSMessageBundle.class, CSMessageBundle.EXC_VAL_VR_VALIDATE_FAILED,
                                                           getStructureDef().getFullName(), getKey(), exceptions);
                       }
            return true;
        
        }
   

    /**
     * This method returns false or true depending upon whether the field is valid or not
     * @param patternString
     * @param value
     */
    private boolean validatePattern(String patternString, String value) {
        Pattern pattern =Pattern.compile(patternString);
        Matcher matcher= pattern.matcher(value);
        return matcher.matches();
        
    }
    

    
    private String getMessageForKey(String msgKey){
            Locale defaultLocale=Locale.US;
            String defaultMessage=null;
            String localMessage=null;
            Locale locale = this.getDBTransaction().getSession().
              getLocale();
             String localString=locale.toLanguageTag();
            ValidationMessagesVOImpl vo=(ValidationMessagesVOImpl) this.getDBTransaction().getRootApplicationModule().findViewObject("ValidationMessagesVO12");
            ViewCriteriaManager vcm=vo.getViewCriteriaManager();
            ViewCriteria vc=vcm.getViewCriteria("ValidationMessagesVOCriteria1");
            vo.setbMessageKey(msgKey);
            vo.applyViewCriteria(vc, false);
            vo.executeQuery();
            RowSetIterator it=vo.createRowSetIterator(null);
            while(it.hasNext()){
                ValidationMessagesVORowImpl row=(ValidationMessagesVORowImpl) it.next();
                if(row.getMessageLocale().equals(defaultLocale.toLanguageTag())){
                      defaultMessage=row.getMessageText();
                    }
                if(row.getMessageLocale().equals(localString)){
                        localMessage=row.getMessageText();
                        break;
                    }
                }
            it.closeRowSetIterator();
            if(localMessage==null){
                return defaultMessage;
                }
            return localMessage;
        }





In the sample application i have enabled a required and pattern constraint as shown in the following screenshots for dummy entity object’s attribute.





runtimevalidation_defineruntimevalidation_define_messagesruntimevalidation_define_1


runtimevalidationerror   





The application source and sql script can be downloaded from the below mentioned link.


Source Code





Note: If you want to test the application you must create tables under user “raman”.





Edit: To skip validation for existing attribute that has not been modified you can use the following snippet.


                                       Object attribValue=getAttribute(attrName);
                                       int attribIndex=getAttributeIndexOf(attrName);
                                       Object oldAttribValue=getPostedAttribute(attribIndex);
                                       if(attribValue==null || !attribValue.equals(oldAttribValue)){
                                       ArrayList<JboException> list=new ArrayList<JboException>(2);
                                       if(attribRequired.equalsIgnoreCase("Y"))
                                       {
                                       ....................

Posted on Tuesday, January 21, 2014 by Unknown

Jan 11, 2014

In the previous posts i have covered the API usage and configuration for fortress and a sample login process. In this post i will give an example of how to write your custom ELResolver to check for permission or roles.

The following snippet contains codes for custom ELResolver and helper classes used by it. The following class checks for permission and roles.

 

public class FortressSecurityResolver extends ELResolver {
public static final ADFLogger FortressRoleResolver = ADFLogger.createADFLogger(FortressSecurityResolver.class);


public FortressSecurityResolver() {
super();
}

@Override
public Object getValue(ELContext elContext, Object base,
Object property) {
String methodName = Thread.currentThread().getStackTrace()[1].getMethodName();
if(property!=null && property instanceof String && ((String)property).startsWith("Fortress")){
elContext.setPropertyResolved(true);
return new PropertyEvaluator((String)property);
}
else if(base instanceof PropertyEvaluator){
String origProperty=((PropertyEvaluator)base).getProperty();
FacesContext context = FacesContext.getCurrentInstance();
if(context!=null){
HttpSession obj=(HttpSession) context.getExternalContext().getSession(false);
HttpSession session=obj;
//this was already stored during login process
if(session.getAttribute("RBACSESSION")!=null){
if(property!=null){
elContext.setPropertyResolved(true);
}
if(origProperty.equals("FortressUserInRole")){
return FortressSecurityController.isUserInRole((String)property,(Session)session.getAttribute("RBACSESSION"));
}
if(origProperty.equals("FortressAllowed")){
Map map=(Map)session.getAttribute("FortressPermissionMap");
if(map==null){
map=FortressSecurityController.getPermissions((Session)session.getAttribute("RBACSESSION"));
session.setAttribute("FortressPermissionMap", map);
}
return map.get(property);
}

}
}
}
return null;
}

@Override
public Class<?> getType(ELContext elContext,Object base,
Object property) {

return Object.class;
}

@Override
public void setValue(ELContext elContext, Object base,
Object property, Object value) {
}

@Override
public boolean isReadOnly(ELContext elContext, Object base,
Object property) {
return false;
}

@Override
public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext elContext,
Object base) {
if (base != null) return null;
ArrayList<FeatureDescriptor> list = new ArrayList<FeatureDescriptor>(14);
list.add(Util.getFeatureDescriptor(
"FortressUserInRole",
"FortressUserInRole",
"Checks whether user is in role",
Boolean.FALSE,
Boolean.FALSE,
Boolean.TRUE,
Boolean.class,
Boolean.FALSE)
);
list.add(Util.getFeatureDescriptor(
"FortressAllowed",
"FortressAllowed",
"Checks whether user has the required permission",
Boolean.FALSE,
Boolean.FALSE,
Boolean.TRUE,
Boolean.class,
Boolean.FALSE)
);
return list.iterator();


}

@Override
public Class<?> getCommonPropertyType(ELContext elContext, Object object) {
return null;
}
}


The propertyevaluator class.



public class PropertyEvaluator {
private String property;
private PropertyEvaluator parent;
public PropertyEvaluator(String propertyName) {
this(null,propertyName);
}
public PropertyEvaluator(PropertyEvaluator base,String propertyName) {
this.property=propertyName;
this.parent=base;
}

public void setProperty(String property) {
this.property = property;
}

public String getProperty() {
return property;
}

public void setParent(PropertyEvaluator parent) {
this.parent = parent;
}

public PropertyEvaluator getParent() {
return parent;
}
}


The SecurityController utility class.



public class FortressSecurityController {
public static final ADFLogger FortressSecurityControllerLogger = ADFLogger.createADFLogger(FortressSecurityController.class);
public FortressSecurityController() {
super();
}
/**
* To check whether user is in role
* @return
*/
public static boolean isUserInRole(String roleName,Session rbacSession){
boolean userInRole=false;
List<UserRole> roles=rbacSession.getRoles();
List<UserAdminRole> adminRoles=rbacSession.getAdminRoles();
if(roles!=null&&!roles.isEmpty()){
Iterator it=roles.iterator();
while(it.hasNext()){
UserRole role=(UserRole)it.next();
if(role.getName().equals(roleName)){
return true;
}
}
}
if(adminRoles!=null&&!adminRoles.isEmpty()){
Iterator it=adminRoles.iterator();
while(it.hasNext()){
UserAdminRole role=(UserAdminRole)it.next();
if(role.getName().equals(roleName)){
return true;
}
}
}
return userInRole;
}
/**
* This method returns users permissions in a map
* @return
*/
public static Map getPermissions(Session rbacSession){
String methodName = Thread.currentThread().getStackTrace()[1].getMethodName();

Map permsMap=new HashMap();
LDAPOperations ops=new LDAPOperations();
AccessMgr mgr=ops.createAndGetAccessMgr();
DelAccessMgr delMgr=ops.createAndGetDelAccessMgr();

try {
List<Permission> perms=delMgr.sessionPermissions(rbacSession);
if(perms!=null){
Iterator it=perms.iterator();
while(it.hasNext()){
Permission permn=(Permission) it.next();
String mapKey=permn.getObjectName()+":"+permn.getOpName()+":Admin";
permsMap.put(mapKey, true);
}

}
} catch (SecurityException e) {
FortressSecurityControllerLogger.fine("[" + methodName + "]" + "The user ["+rbacSession.getUserId()+"] does not have access",
e);
}

try {
List<Permission> perms=mgr.sessionPermissions(rbacSession);
if(perms!=null){
Iterator it=perms.iterator();
while(it.hasNext()){
Permission permn=(Permission) it.next();
String mapKey=permn.getObjectName()+":"+permn.getOpName()+":Normal";
permsMap.put(mapKey, true);
}

}
} catch (SecurityException e) {
FortressSecurityControllerLogger.fine("[" + methodName + "]" + "The user ["+rbacSession.getUserId()+"] does not have access",
e);
}

return permsMap;
}
}


Now to check for permissions or roles all you have to do is write expressions such as the ones mentioned below in your fragment or jsf page.



//check for permission



#{FortressAllowed[‘taskflowId:permnName:Admin']}



or



#{FortressAllowed[‘taskflowId:permnName:Normal]}



and this is not limited to taskflowId you can create any sort of permission object and corresponding permission you like.



 



What’s next ? Maybe a post on custom authentication realm for weblogic and glassfish for fortress.



 



Note: Application should in general be driven by permissions rather than roles because permissions don’t generally change but roles can be added or removed. So if your application is based on permission it will tend to change less because you can assign or deassign roles the same permission.



If you would like to see a live example PM me and i will provide you with details.

Posted on Saturday, January 11, 2014 by Unknown

In the previous post i discussed about fortress and its directory structure. In this post i will cover the configuration for securing ADF application, using fortress API, writing your own custom ELResolver for doing permission or role checks.

 

Configuration: Fortress uses a properties file fortress.properties for storing configuration that it uses at runtime to communicate with the openldap server. It also uses ehcache.xml file for caching the retrieved entities from the openldap server to boost performance. Both these files need to be present in your classpath. The sample file structure for fortress.properties and ehcache.xml is shown below.

# Host name and port of LDAP DIT:
host=localhost
port=389

# These credentials are used for read/write access to all nodes under suffix:
admin.user=cn=Manager,dc=sample,dc=com
# LDAP admin root pass is encrypted using 'encrypt' target in build.xml:
admin.pw=W7T0G9hyr344K+DF8gfgA==

# This is min/max settings for LDAP administrator pool connections that have read/write access to all nodes under suffix:
min.admin.conn=10
max.admin.conn=100

# This node contains fortress properties stored on behalf of connecting LDAP clients:
#the config.realm is a node stored in ou=config
config.realm=DEFAULT
config.root=ou=Config,dc=sample,dc=com

# enable this to see trace statements when connection pool allocates new connections:
debug.ldap.pool=true

# Default for pool reconnect flag is false:
enable.pool.reconnect=true

crypto.prop=uiote12434

ehcache.config.file=ehcache.xml


<?xml version="1.0" encoding="UTF-8"?>

<!--
Fortress CacheManager Configuration
==========================
This ehcache.xml corresponds to a single CacheManager.
-->
<ehcache name="fortress-realm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd"
updateCheck="true" monitoring="autodetect"
dynamicConfig="true"
>

<cacheManagerEventListenerFactory class="" properties=""/>

<!--
Default Cache configuration. These settings will be applied to caches
created programmatically using CacheManager.add(String cacheName).
This element is optional, and using CacheManager.add(String cacheName) when
its not present will throw CacheException

The defaultCache has an implicit name "default" which is a reserved cache name.
-->
<defaultCache
eternal="false"
overflowToDisk="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskSpoolBufferSizeMB="30"
maxElementsOnDisk="10"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
/>

<!--
Thic cache contains password policy entries. It is used to save a read on User password policy edits. There should be one element for every tenant.
-->
<cache name="fortress.policies"
maxElementsInMemory="10"
maxElementsOnDisk="10"
eternal="false"
overflowToDisk="false"
diskSpoolBufferSizeMB="2"
timeToIdleSeconds="600"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LFU"
/>

<!--
Contains the value OrgUnits for User and Permissions. There should be two elements for every tenant.
-->
<cache name="fortress.ous"
maxElementsInMemory="2"
maxElementsOnDisk="2"
eternal="false"
overflowToDisk="false"
diskSpoolBufferSizeMB="2"
timeToIdleSeconds="600"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LFU"
/>

<!--
Contains the JGraphT hierarchies for RBAC roles. There should be one element for every tenant.
-->
<cache name="fortress.roles"
maxElementsInMemory="10"
maxElementsOnDisk="10"
eternal="false"
overflowToDisk="false"
diskSpoolBufferSizeMB="2"
timeToIdleSeconds="600"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LFU"
/>

<!--
Contains the JGraphT hierarchies for ARBAC roles. There should be one element for every tenant.
-->
<cache name="fortress.admin.roles"
maxElementsInMemory="10"
maxElementsOnDisk="10"
eternal="false"
overflowToDisk="false"
diskSpoolBufferSizeMB="2"
timeToIdleSeconds="600"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LFU"
/>

<!--
Contains the JGraphT hierarchies for Perm OUs. There should be one element for every tenant.
-->
<cache name="fortress.pso"
maxElementsInMemory="10"
maxElementsOnDisk="10"
eternal="false"
overflowToDisk="false"
diskSpoolBufferSizeMB="2"
timeToIdleSeconds="600"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LFU"
/>

<!--
Contains the JGraphT hierarchies for User OUs. There should be one element for every tenant.
-->
<cache name="fortress.uso"
maxElementsInMemory="10"
maxElementsOnDisk="10"
eternal="false"
overflowToDisk="false"
diskSpoolBufferSizeMB="2"
timeToIdleSeconds="600"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LFU"
/>

<!--
Searchable cache contains Role<->DSD mapping. This configuration sets a fairly long TTL of 1 hour.
-->
<cache name="fortress.dsd"
maxElementsInMemory="1000"
maxElementsOnDisk="10"
eternal="false"
overflowToDisk="false"
diskSpoolBufferSizeMB="20"
timeToIdleSeconds="3600"
timeToLiveSeconds="3600"
memoryStoreEvictionPolicy="LFU">
<searchable>
<searchAttribute name="member" expression="value.getMember()"/>
<searchAttribute name="name" expression="value.getName()"/>
<searchAttribute name="contextId" expression="value.getContextId()"/>
</searchable>
</cache>

<!--
Cache contains Role<->SSD mapping.
-->
<cache name="fortress.ssd"
maxElementsInMemory="1000"
maxElementsOnDisk="10"
eternal="false"
overflowToDisk="false"
diskSpoolBufferSizeMB="20"
timeToIdleSeconds="600"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LFU"
/>

</ehcache>


 



Library Dependencies:



You will need the following jars in your classpath which can be added as maven dependencies.



fortress-RC34.jar



ehcache-core.jar (version 2.6.0)



jasypt.jar (version 1.8)



commons-lang (version 2.6)



commons-configuration (version 1.10)



jgrapht-jdk (version 0.7.3)



slf4j-api



unboundid-ldapsdk-2.3.5.jar



 



This basically finishes your configuration.



 



Brief overview of fortress java API:- Though the java docs link i provided in the previous post should be apt. I will just provide a brief overview of the major API classes or interfaces.



 




  1. AdminMgr: Use the implementation of this class for managing users, application roles management, application permission managment.


  2. DelAdminMgr: This is used to perform action on ARBAC entities. You use this for organization creation, admin roles and permission management.


  3. ReviewMgr: Use this for seaching existing users, roles, permissions or permission objects.


  4. DelReviewMgr: Use this for searching organizations, searching roles.


  5. PwPolicyMgr: this can be used for managing password policies for user entities.


  6. AccessMgr: this is used to authenticate user and check for his/her access to a particular permission or role.


  7. DelAccessMgr: this can be used to check for applicable admin permissions and admin roles.



To instantiate each of the objects you can use the corresponding factory class. For example to get a AdminMgr implementation you use the following method.



    private AdminMgr createAndGetAdminMgr() {
String methodName = Thread.currentThread().getStackTrace()[1].getMethodName();
AdminMgr mgr = null;
try {
mgr = AdminMgrFactory.createInstance(GlobalIds.HOME);
} catch (SecurityException e) {
logger.severe("[" + methodName + "]" + "Unable to create AdminMgr Instance", e);
throw new RuntimeException("Unable to create AdminMgr Instance", e
);
}
return mgr;
}


After you have obtained the instance you can perform the corresponding operations on it.



 



Securing your application:



Although fortress comes with its security realm for tomcat or jboss. If you’d like to customize the login process you can do so using its API.



 



Login Process:



In your login method of the servlet or managed bean you use the following method to authenticate and create user session.



Session rbacSession= acMgr.createSession(user, false);


 



This method will validate the user’s password and temporal constraints. It will throw exception in case the user’s password is expired and warning if the user’s password is about to expire and user is using grace login. the way to handle these scenarios would be that if a user’s password is about to expire display this information to him/her by providing option to changePassword or continue to the application.



 



login_flow



The following method is a sample for the login process:



    public String doLogin() {
String un = (String)userName.getValue();
char[] pw = ((String)password.getValue()).toCharArray();
//my custom utility class that contains utility methods for fortress
LDAPOperations ops=new LDAPOperations();
AccessMgr acMgr=ops.createAndGetAccessMgr();
Map pfsScope=ADFContext.getCurrent().getPageFlowScope();
FacesContext context = FacesContext.getCurrentInstance();
HttpSession session=(HttpSession)context.getExternalContext().getSession(true);
//check whether RBACSESSION already exists if so redirect the user to the home page
if(session.getAttribute("RBACSESSION")!=null){
return "success";
}
User user=new User();
user.setUserId(un);
user.setPassword(pw);
try {
Session rbacSession= acMgr.createSession(user, false);
if(rbacSession!=null)
{
//check for warnings if they contain password expiration warning warn him of the same
//and provide option to change the password
List<Warning> warnings=rbacSession.getWarnings();
if(warnings!=null && !warnings.isEmpty())
{
Iterator <Warning> it=warnings.iterator();
while(it.hasNext()){
Warning warning=it.next();
if(warning.getId()==GlobalPwMsgIds.PASSWORD_EXPIRATION_WARNING){
pfsScope.put("errorMessage",warning.getMsg());
session.setAttribute("RBACSESSION", rbacSession);
ExtendedRenderKitService erks =
Service.getRenderKitService(context, ExtendedRenderKitService.class);
//show popup
erks.addScript(context,"AdfPage.PAGE.findComponent('"+popUp.getClientId()+"').show();");
pfsScope.put("expireWarning",Boolean.TRUE);
return "";
}
}
}
session.setAttribute("RBACSESSION", rbacSession);
context.responseComplete();
return "success";
}

} catch (PasswordException e) {
//this means the user's password was reset by admin and hence redirect user to change his password afterward you can log him out
if(e.getErrorId()==GlobalErrIds.USER_PW_RESET){
if(session!=null){
//temporary user id and user's pw policy which can be shown on the change password page
session.setAttribute("userIdTemp", un);
List<User> users=new ArrayList();
users=ops.searchUsers(un);
String pwPol=users.get(0).getPwPolicy();
session.setAttribute("pwPol", pwPol);
context.responseComplete();
return "changePassword";
}
} else{
loginLogger.fine("Login Failed due to authentication failure"+e.getMessage(),e);
pfsScope.put("errorMessage","Login Failed due to authentication failure"+e.getMessage());
}

}
catch (ValidationException e) {
loginLogger.severe("User is not allowed access"+e.getMessage(),e);
pfsScope.put("errorMessage","User is not allowed access"+e.getMessage());
}
catch (FinderException e) {
loginLogger.severe("User does not exist"+e.getMessage(),e);
pfsScope.put("errorMessage","User does not exist in the system");
}
catch (SecurityException e) {
loginLogger.fine("Authentication failed due to"+e.getMessage(),e);
pfsScope.put("errorMessage","System Error Occured"+e.getMessage());
}
ExtendedRenderKitService erks =
Service.getRenderKitService(context, ExtendedRenderKitService.class);
//show popup
erks.addScript(context,"AdfPage.PAGE.findComponent('"+popUp.getClientId()+"').show();");
return "";
}


Here the utility class LDAPOperations is a custom class written by me which in turn uses fortress API’s to access the information.



 



Checking permissions and roles:



 



To perform runtime role or permission checks you can use the following methods.



    public static boolean isUserInRole(String roleName,Session rbacSession){
boolean userInRole=false;
List<UserRole> roles=rbacSession.getRoles();
List<UserAdminRole> adminRoles=rbacSession.getAdminRoles();
//check both in admin and normal roles
if(roles!=null&&!roles.isEmpty()){
Iterator it=roles.iterator();
while(it.hasNext()){
UserRole role=(UserRole)it.next();
if(role.getName().equals(roleName)){
return true;
}
}
}
if(adminRoles!=null&&!adminRoles.isEmpty()){
Iterator it=adminRoles.iterator();
while(it.hasNext()){
UserAdminRole role=(UserAdminRole)it.next();
if(role.getName().equals(roleName)){
return true;
}
}
}
return userInRole;
}

To check for permissions:-

If its a admin permission you use DelAccessMgr for a normal permission you use a AccessMgr instance.



 



DelAccessMgr delMgr=ops.createAndGetDelAccessMgr();
Permission permn=new Permission();
permn.setAdmin(true);
//This is a top level object under which individual permissions are stored
permn.setObjectName("taskflowId");
permn.setOpName("view");
//returns true if user has access to the permission else returns false.
delMgr.checkAccess(rbacSession, permn);


 



That is all fine but then one may ask ADF security provides me with el methods such as isUserInRole and SecurityContext.taskflowViewable etc. To accomplish the same here you can implement your own custom ELResolver.



I will cover the same in the next post.

Posted on Saturday, January 11, 2014 by Unknown

In this post i will discuss a security solution that one can use to secure their ADF essentials or ADF application. The solution to secure the application utilizes OpenLDAP and fortress. Fortress provides both RBAC(Role based access control) and ARBAC(Administrative role based access control) and OpenLDAP serves as a LDAP directory. Fortress also comes with a set of easy to use API’s through which you can manage permissions, users, roles, organizations,SOD (segregation of duties) policies both dynamic and static, temporal constraints etc, so all in all its a complete open source identity and access management solution and if you don’t want to go through the trouble of using the API’s and implementing your own solution, you can use the enmasse policy server or commander application that comes with the download to manage these things for you.

The added advantage here is that it includes policy and permission enforcement for which, if you were using ADF security, you’d have to use OES and some other servers and integrate them with your application.

 

Basic directory structure :-

A fortress domain includes majorly five organizational units as mentioned below:-

  1. ou=ARBAC: This organizational unit further contains admin permissions, admin roles, permission organizations and user organizations.
  2. ou=RBAC: This organizational unit further contains user’s and roles’ constraints, applicable SOD policies , normal permissions and application roles.
  3. ou=Policies: This node contains the password policies which can be assigned to the user.
  4. ou=config: This node contains the configuration information which at runtime will be used to determine the directory information by fortress.
  5. ou=people: This is the default organizational unit where the users are created.

fortress_directory_structure

I will leave this post here with the links and references where you can read more about fortress and install it. In the next post i will cover how to configure and use fortress with a ADF application.

 

References:

  1. Fortress Java  API : Fortress JAVA API Doc
  2. What is RBAC ? : RBAC
  3. Fortress Downloads : Downloads

Posted on Saturday, January 11, 2014 by Unknown