Search code examples
javasecuritypermissionsjaas

How to subclass BasicPermission to add actions


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.


Solution

  • 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();
    }