I want to create a subclass of BasicPermission to add actions, which according to the the java docs should be possible:
Subclasses may implement actions on top of BasicPermission, if desired.
Here is my initial attempt:
public class BasicPermissionWithActions extends BasicPermission {
String actions;
String[] actionList;
String name;
public BasicPermissionWithActions(String name, String actions) {
super(name, actions);
this.actions = actions;
this.actionList = actions.split("\\,");
this.name = name;
}
private static final long serialVersionUID = 7608854273379948062L;
@Override
public boolean implies(Permission p) {
// name and class check can be done by super
if (!super.implies(p))
return false;
// now check actions
String requestedActions = p.getActions();
String[] requestedActionList = requestedActions.split("\\,");
for (String requestedAction : requestedActionList) {
if (!hasRequestedAction(requestedAction))
return false;
}
return true;
}
private boolean hasRequestedAction(String requestedAction) {
for (String action : actionList) {
if (action.equals(requestedAction))
return true;
}
return false;
}
@Override
public String getActions() {
return actions;
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((actions == null) ? 0 : actions.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
BasicPermissionWithActions other = (BasicPermissionWithActions) obj;
if (actions == null) {
if (other.actions != null)
return false;
} else if (!actions.equals(other.actions))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public String toString() {
return "(\"" + this.getClass().getName() + "\" \"" + name + "\" \"" + actions + "\")";
}
And an entry in the policy file to grant access using this permission (in this case I'm specify a permission which should be insufficient to allow the desired action):
grant principal sample.principal.SampleGroup "TestGroup" {
permission BasicPermissionWithActions "*", "read";
};
And the code to check the permission:
rep.getAccessControlContext().checkPermission(new BasicPermissionWithActions(getName(), "write"));
I expect this check to fail since the policy has only specified a read action. However the check passes quietly.
The problem is that whenever the permission in the policy file has name "*", the actions are never checked. Running in debug mode shows that the method BasicPermissionWithActions.implies method is never called.
If I omit the permission from the policy file I get a security exception as expected but I cannot make actions work.
The problem is related to PermissionCollection. BasicPermission implements its own PermissionCollection for better performance. Unfortunately, this implementation makes some simplifying assumptions which break the semantics for subclasses. Specifically it implements a shortcut for "*" which bypasses the Permission.implies method and always returns true.
The solution is to implement a custom PermissionCollection which simply calls the Permission.implies methods of its members:
private class CustomPermissionCollection extends PermissionCollection {
private static final long serialVersionUID = 5654758059940546018L;
Collection<Permission> perms = new ArrayList<Permission>();
@Override
public void add(Permission permission) {
perms.add(permission);
}
@Override
public boolean implies(Permission permission) {
for (Permission p : perms) {
if (p.implies(permission))
return true;
}
return false;
}
@Override
public Enumeration<Permission> elements() {
return Collections.enumeration(perms);
}
}
and return this in the newPermissionCollection method of BasicPermissionWithActions
@Override
public PermissionCollection newPermissionCollection() {
return new CustomPermissionCollection();
}