Monday, February 3, 2014

Driving security for ADF Essentials: Part 3

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.