Sunday, January 17, 2010

Using JAAS auhtorization to protect business objects

You will frequently a scenario in your application where different roles should be permitted access to different business objects.  
Consider an application where different types of users can save different types of content e.g. Audio users can save audio content; Text users can save text content only and similarly. Admin can save whatever it wants. You can apply these constraints in JAAS.


grant principal weblogic.security.principal.WLSGroupImpl "audioUser" 
{
 permission com.test.security.ResourcePermission "com.test.entity.audio.*", "create,read,update,delete";
}; 

grant principal weblogic.security.principal.WLSGroupImpl "textUser" 
{
 permission com.test.security.ResourcePermission "com.test.entity.text.*", "create,read,update,delete";
}; 

grant principal weblogic.security.principal.WLSGroupImpl "admin" 
{
 permission com.test.security.ResourcePermission "*", "create,read,update,delete";
}; 



Note:
1- I put all business objects permitted to audioUser in package com.test.entity.audio.* and similarly for textUser. I achieved these benefits using BasicPermission
2- I have used weblogic.security.principal.WLSGroupImpl for my role principals because i was using Weblogic authentication.
3- You can specify above constraints in java.policy. If you are using Weblogic you can also specify it in weblogic.policy but it depends on how you are staring weblogic server either:
-Djava.security.policy==%WL_HOME%\server\lib\weblogic.policy
OR
-Djava.security.policy=%WL_HOME%\server\lib\weblogic.policy
== enforces only weblogic.policy to be used, whereas = appends weblogic.policy to other policies specified in java.security.
4- To use JAAS authorization you need not enable Security Manager. And i would recommend not to do so as enabling it will also make weblogic code and other applications to pass through security checks and you may get all sorts of exceptions.

Usually code applies authorization checks like below:

if(System.getSecurityManager() != null){
 System.getSecurityManager().checkPermission(...);
}

So disabling security manager also disables authorization checks. Instead of above i would apply authorization directly using AccessController.

To authorize update operation you would:









AccessController.checkPermission(weblogic.security.Security.getCurrentSubject(), new ResourcePermission(this.getClass()
.getCanonicalName(), EnumSet.of(Action.update)));


Or better yet put above code of checking permission in Subject.doAs() so as to avoid protection domain checks even if security manager is enabled.




Now the code, you require just two classes to implement this. You do not need to place below classes in JAVA_HOME/lib/ext, you can put these in your project. This is the magic of UnresolvedPermission.










package com.test.security;


import java.security.BasicPermission;
import java.security.Permission;
import java.util.EnumSet;


public class ResourcePermission extends BasicPermission {
private EnumSet<Action> actionSet;


public ResourcePermission(String name, String actions) {
super(name == null ? "" : name);
actionSet = Action.getActionSet(actions);
}


public ResourcePermission(String name, EnumSet<Action> actionSet) {
super(name == null ? "" : name);
this.actionSet = actionSet;
}


@Override
public boolean equals(Object obj) {
if (obj != null && obj instanceof ResourcePermission) {
ResourcePermission other = (ResourcePermission) obj;
return super.equals(obj) && actionSet.equals(other.getActionSet());
}
return false;
}


@Override
public String getActions() {
return Action.toString(actionSet);
}


public EnumSet<Action> getActionSet() {
return actionSet;
}


@Override
public int hashCode() {
return actionSet.hashCode() + getName().hashCode();
}


@Override
public boolean implies(Permission permission) {
if (permission != null && permission instanceof ResourcePermission) {
ResourcePermission other = (ResourcePermission) permission;
return super.implies(permission)
&& actionSet.containsAll(other.getActionSet());
}
return false;
}
}


///////////////////////////////////////////////////////////////////////////////////////


package com.test.security;


import java.util.EnumSet;


public enum Action {
create, update, delete, read;


public static EnumSet<Action> getActionSet(String serializedActions) {
EnumSet<Action> actionSet = EnumSet.noneOf(Action.class);
if (serializedActions != null) {
String[] serializedActionsSet = serializedActions.split(",");
for (String serializedActionItem : serializedActionsSet) {
actionSet.add(Action.valueOf(serializedActionItem));
}
}
return actionSet;
}


public static String toString(EnumSet<Action> actionSet) {
StringBuilder actions = new StringBuilder();
for (Action action : actionSet) {
actions.append(action.toString() + ",");
}
if (actions.lastIndexOf(",") == actions.length() - 1) {
actions.replace(actions.length() - 1, actions.length() - 1, "");
}
return actions.toString();
}


}


No comments: