Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3

...

Specific changes that will be made to implement the solutions described above.

5.

...

05 Permissions behavioral rules

  • we only support comparing 2 permissions of type WebloggerPermission, and WebloggerPermission cannot be subclassed
  • a permission with type = null indicates a global (application wide) permission
  • permissions that are global (type = null) cannot specify an object (global perms do not pertain to objects)
  • permissions of a specific type must also specify an object (typed perms must correspond to an object)
  • permissions must be of the same type to be compared (we do not compare global and 'weblog' permissions)
  • permissions of a specific type must also specify the same object to be compared ('weblog' perms for different weblogs are never comparable)
  • a permission action can only imply other permissions of the same type ('weblog' perms can only imply other 'weblog' perms)
  • the 'all' action is a special action that implies all possible actions within the specified permission type/object combo
  • a global permission with the 'all' action implies all permissions in the system (the global admin role)
  • a permission can imply any number of other permissions

5.1 Define new permissions classes

Define new permissions classes using java.security.Permission as the base class. A Permssion object defines a list of "actions" that are permitted.

Class WebloggerPermission extends java.security.Permission. This is the permission class for all Roller Weblogger permissions and really only extends the base Permission class to include the ideas that a Permission has both a "type" and an "object" to which the set of actions apply. This class also implements the very important Permission method implies() which contains the logic sorts out if one permission implies another.

Key differences between this class and the version(s) suggested in the proposal:

  • All instances of this class are immutable, which is supposed to be part of the contract when using any java.security.Permission object. From the javadoc "Permission objects are similar to String objects in that they are immutable once they have been created. Subclasses should not provide methods that can change the state of a permission once it has been created."
  • This class is not meant to be subclassed and thus "could" potentially be written as a final class. The implementation is purposely generic and does not require a subclass per "type" such as WeblogPermission. This is beneficial because the security system can then be expanded dynamically without requiring new code and Permission classes.
Code Block

package

Define new permissions classes using java.security.Permission as the base class. A Permssion object defines a list of "actions" that are permitted.

Class WebloggerPermission extends java.security.Permission. This is the permission class for all Roller Weblogger permissions and really only extends the base Permission class to include the ideas that a Permission has both a "type" and an "object" to which the set of actions apply. This class also implements the very important Permission method implies() which contains the logic sorts out if one permission implies another.

Key differences between this class and the version(s) suggested in the proposal:

  • All instances of this class are immutable, which is supposed to be part of the contract when using any java.security.Permission object. From the javadoc "Permission objects are similar to String objects in that they are immutable once they have been created. Subclasses should not provide methods that can change the state of a permission once it has been created."
  • This class is not meant to be subclassed and thus "could" potentially be written as a final class. The implementation is purposely generic and does not require a subclass per "type" such as WeblogPermission. This is beneficial because the security system can then be expanded dynamically without requiring new code and Permission classes.
Code Block

package org.apache.roller.weblogger.pojos;

import java.security.Permission;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.roller.weblogger.util.Utilitiespojos;


/**
 import java.security.Permission;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.apache.roller.weblogger.util.Utilities;


/**
 * Base permission class for Weblogger. 
 */
public final class WebloggerPermission extends java.security.Permission {
    
    private staticfinal Log log = LogFactory.getLog(WebloggerPermission.class);
    String type;
    private final String typeobject;
    private final String objectactions;
    private final StringSet<String> actionsactionsAsSet;
    private final Set<String> actionsAsSetcompleteActionsAsSet;
    private final Set<String> completeActionsAsSet
    
    public WebloggerPermission(String actions) {
        this(null, null, actions);
    }
    
    public WebloggerPermission(String type, String object, String actions) {
        super("WebloggerPermission");
        this.type
 = type;
      if((type != this.object =null && object;
 == null) || (type ==   this.actions = actions;
null && object != null))
            this.actionsAsSetthrow =new Utilities.stringToStringSet(actions, ",");
        this.completeActionsAsSet = buildFullActionsSet(actionsAsSetIllegalArgumentException("'type' and 'object' must be either both null or both non-null.");
    }
    
    
    publicthis.type String getType() {= type;
        return typethis.object = object;
    }
    
this.actions = actions;
  public String getObject() {
   this.actionsAsSet =    return objectUtilities.stringToStringSet(actions, ",");
    }
    
this.completeActionsAsSet    public String getActions() {= buildFullActionsSet(actionsAsSet);
        return actions;}
    }

    public Set<String> getActionsAsSet() {
    // ---------------------------------------------------   return actionsAsSet;
    }Public Methods
    
    
    public booleanString impliesgetType(Permission permission) {
        return type;
    }
    //
 only compare WebloggerPermission objects
public String getObject() {
     if  (!(permission instanceof WebloggerPermission))return object;
    }
    
    returnpublic false;
  String getActions() {
        return  actions;
    }

    WebloggerPermissionpublic thatSet<String> = getActionsAsSet(WebloggerPermission) permission;{
        return actionsAsSet;
    }
    
 // only compare 2
 permissions of the samepublic nameboolean ("type")implies(Permission permission) {
        // name ("type") cannot be null
        if(this.getName() == null || that.getName() == null ||// only compare WebloggerPermission objects
        if (!(permission       !(this.getName().equals(that.getName())))instanceof WebloggerPermission))
            return false;
        
    
    // does "this" permission containWebloggerPermission allthat actions= of "that" permission?(WebloggerPermission) permission;
        
        // weonly considercompare a2 permissionpermissions impliedof ifthe oursame settype
 of granted actions
     if(that.getType() != null) //{
 contains all of the granted actions of the permission we are checking
// this type == null or the 2 types are not equal, invalid comparison
            if(this.completeActionsAsSet.containsAllgetType() == null || !(this.getType().equals(that.completeActionsAsSetgetType())))
            return true;
       return false;
        return} false;
    }else if(this.getType() != null) {
    
    
    public// booleanthat equals(Object arg0) {
        throw new UnsupportedOperationException("Not supported yet.");
has type == null but this has type != null, invalid comparison
       }

    public int hashCode() {return false;
        throw}
 new UnsupportedOperationException("Not supported yet.");
    }
    
    @Override
// only compare 2 publicpermissions String toString() {of the same object
        StringBuilder sb = new StringBuilder();if(that.getObject() != null) {
        sb.append(getName()).append(" : ");
        for (String action : getActionsAsSet()) {     // this object == null or the 2 objects are not equal, invalid comparison
            sbif(this.append(" ").append(action).append(" ");getObject() == null || !that.getObject().equals(this.getObject()))
        }
        return sb.toString()false;
        } else if(this.getObject() != null) {
    
    
    // --------------------------------------------------- Private Methods
     that has object == null but this has object != null, invalid comparison
    
    // build an expanded Setreturn offalse;
 all actions implied by a Set of actions}
    private Set<String> buildFullActionsSet(Set<String> actions) {
        
// does "this" permission contain all actions of  Set<String> fullSet = new HashSet<String>();
"that" permission?
        
     
   // we consider a permission for(implied Stringif actionour :set actionsof )granted {actions
        // contains all of Set<String>the impliedActionsactions = getImpliedActions(action);
    of the permission we are checking
        if(impliedActionsthis.size() > 0) {completeActionsAsSet.containsAll(that.completeActionsAsSet))
            return true;
   // if an action implies other
 actions, recurse
      return false;
    }
     fullSet.addAll(buildFullActionsSet(impliedActions));
    
    public boolean   } elseequals(Object arg0) {
        throw new UnsupportedOperationException("Not supported     fullSet.add(actionyet.");
    }

    public int  hashCode() }{
        }
throw new UnsupportedOperationException("Not supported yet.");
    }
    
    return fullSet;
    }@Override
    public String toString() {
    
    //StringBuilder getsb implied= actions for an action
new StringBuilder();
      private Set<String> getImpliedActions(String action) { sb.append(getName()).append(" : ");
        // TODO: lookup if action implies other actions
for (String action : getActionsAsSet()) { 
            // this would likely be a function of the UserManager somehowsb.append(" ").append(action).append(" ");
        }
        return Collections.EMPTY_SETsb.toString();
    }
    
}

Class UserPermission is the persisted version of Roller Weblogger user permission data. This class is present purely for the use by the built in JPAUserManagerImpl class so that user permission data can be persisted to a relational db. In the event that a UserManager implementation was managing users/security via an alternate system this class would not need to be used. We could potentially make this class confined to use by the JPAUserManagerImpl by moving it and setting it to package level privacy.

Key differences between this class and the version(s) suggested in the proposal:

  • This class does not subclass the java.security.Permission class or any of its subclasses such as WebloggerPermission. It's sole purpose is for persisting user permission information, not for actual security checks.
  • This class does not require any subclassing to support alternative types of permissions, it is generic enough to support any "type" of permission.
Code Block

package org.apache.roller.weblogger.pojos;

import java.io.Serializable;
import java.util.Date;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.roller.util.UUIDGenerator;
import org.apache.roller.weblogger.util.Utilities;


/**
 * Permissions for a specific user. 
 */
public class UserPermission implements Serializable {
    
    private static Log log = LogFactory.getLog(UserPermission.class);

    private String id = UUIDGenerator.generateUUID();
    private User user = null;
    private String type = null;
    private String object = null;
    private String actions = null;
    private Date dateCreated = new Date();
    private boolean pending = false;
    
    
    public UserPermission() {}
    
    public UserPermission(User user, String type, String object, String actions) {    
    // --------------------------------------------------- Private Methods
    
    
    // build an expanded Set of all actions implied by a Set of actions
    private Set<String> buildFullActionsSet(Set<String> actions) {
        
        Set<String> fullSet = new HashSet<String>();
        
        for( String action : actions ) {
            Set<String> impliedActions = getImpliedActions(action);
            if(impliedActions.size() > 0) {
                // if an action implies other actions, recurse
                fullSet.addAll(buildFullActionsSet(impliedActions));
            } else {
                fullSet.add(action);
            }
        this.user = user;}
        this.type = type;
        this.object = objectreturn fullSet;
    }
    this.actions
 = actions;
  // get }
implied actions for an action
    private publicSet<String> getImpliedActions(String getId(action) {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }
    
    public User getUser() {// TODO: lookup if action implies other actions
        // this would likely be a function of the UserManager somehow
        return userCollections.EMPTY_SET;
    }

    public void setUser(User user) {
        this.user = user;
}

Class UserPermission is the persisted version of Roller Weblogger user permission data. This class is present purely for the use by the built in JPAUserManagerImpl class so that user permission data can be persisted to a relational db. In the event that a UserManager implementation was managing users/security via an alternate system this class would not need to be used. We could potentially make this class confined to use by the JPAUserManagerImpl by moving it and setting it to package level privacy.

Key differences between this class and the version(s) suggested in the proposal:

  • This class does not subclass the java.security.Permission class or any of its subclasses such as WebloggerPermission. It's sole purpose is for persisting user permission information, not for actual security checks.
  • This class does not require any subclassing to support alternative types of permissions, it is generic enough to support any "type" of permission.
Code Block

package org.apache.roller.weblogger.pojos;

import java.io.Serializable;
import java.util.Date;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.roller.util.UUIDGenerator;
import org.apache.roller.weblogger.util.Utilities;


/**
 * Permissions for a specific user. 
 */
public class UserPermission implements Serializable {
    
    private static Log log = LogFactory.getLog(UserPermission.class);

    private String id = UUIDGenerator.generateUUID();
    private User user = null;
    private String type = null;
    private String object = null;
    private String actions = null;
    private Date dateCreated = new Date();
    private boolean pending = false;
        }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }
    
    public String getObject() {
        return object;
    }

    public void setObject(String object) {
        this.object = object;
    }
    
    public void setActions(String actions) {
        this.actions = actions;
    }

    public String getActions() {
        return actions;
    }

    public Date getDateCreated() {
        return dateCreated;
    }

    public void setDateCreated(Date dateCreated) {
        this.dateCreated = dateCreated;
    }

    public boolean isPending() {
        return pending;
    }

    public void setPending(boolean pending) {
        this.pending = pending;
    }
    
    public void addActions(String newActionsUserPermission() {}
    
    public Set<String> existing = Utilities.stringToStringSet(actions, ",");UserPermission(User user, String type, String object, String actions) {
        Set<String>this.user newActs= = Utilities.stringToStringSet(newActions, ",")user;
        this.type = type;
        existing.addAll(newActs)this.object = object;
        setActions(Utilities.stringSetToString(existing, ","))this.actions = actions;
    }
    
    public voidString removeActionsgetId(String removeActions) {
        return id;
    }

    public Set<String>void existing = Utilities.stringToStringSet(actions, ",");
        Set<String> removeActs = Utilities.stringToStringSet(removeActions, ",");
        existing.removeAll(removeActs);
        setActions(Utilities.stringSetToString(existing, ","));
    }
    
}

5.2 Define new permissions properties

The need for roles is no longer necessary as a concept separate from permissions, but roles did provide one very crucial benefit that we need to retain. They provided the ability to dynamically group and imply permissions. i.e. they allowed a user with the role "editor" to imply having the permissions "login,weblog". We can accomplish the same thing by allowing any permission to imply additional permissions.

Like roles we'll want to that to be configurable and dynamic so that a permission can be modified to imply more and less other permissions as elements of the system change. Exactly how this would work is not definite, but similar to the original proposal a permission could be defined to imply other permissions, such as ...

Code Block

permission.anonymous=comment
permission.editor=login,comment,createWeblog
permission.admin=login,comment,createWeblog,admin

It's also important that this be expandable to any number of levels, so it would be okay do define ...

Code Block

permission.level1=action0
permission.level2=level1,action1,action2
permission.level3=level2,action3

Thus, a user with permission "level3" is equivalent to having actions "action0,action1,action2,action3"

setId(String id) {
        this.id = id;
    }
    
    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }
    
    public String getObject() {
        return object;
    }

    public void setObject(String object) {
        this.object = object;
    }
    
    public void setActions(String actions) {
        this.actions = actions;
    }

    public String getActions() {
        return actions;
    }

    public Date getDateCreated() {
        return dateCreated;
    }

    public void setDateCreated(Date dateCreated) {
        this.dateCreated = dateCreated;
    }

    public boolean isPending() {
        return pending;
    }

    public void setPending(boolean pending) {
        this.pending = pending;
    }
    
    public void addActions(String newActions) {
        Set<String> existing = Utilities.stringToStringSet(actions, ",");
        Set<String> newActs = Utilities.stringToStringSet(newActions, ",");
        existing.addAll(newActs);
        setActions(Utilities.stringSetToString(existing, ","));
    }
    
    public void removeActions(String removeActions) {
        Set<String> existing = Utilities.stringToStringSet(actions, ",");
        Set<String> removeActs = Utilities.stringToStringSet(removeActions, ",");
        existing.removeAll(removeActs);
        setActions(Utilities.stringSetToString(existing, ","));
    }
    
}

5.2 Define new permissions properties

The need for roles is no longer necessary as a concept separate from permissions, but roles did provide one very crucial benefit that we need to retain. They provided the ability to dynamically group and imply permissions. i.e. they allowed a user with the role "editor" to imply having the permissions "login,weblog". We can accomplish the same thing by allowing any permission to imply additional permissions.

Like roles we'll want to that to be configurable and dynamic so that a permission can be modified to imply more and less other permissions as elements of the system change. Exactly how this would work is not definite, but similar to the original proposal a permission could be defined to imply other permissions, such as ...

Code Block

permission.anonymous=comment
permission.editor=login,comment,createWeblog
permission.admin=login,comment,createWeblog,admin

It's also important that this be expandable to any number of levels, so it would be okay do define ...

Code Block

permission.level1=action0
permission.level2=level1,action1,action2
permission.level3=level2,action3

Thus, a user with permission "level3" is equivalent to having actions "action0,action1,action2,action3"

NOTE: we would probably also want to create a special action for "all" which can be used for admin purposes and implies all actions automatically.

Based on the behavior in Roller 4.0 we would have these permissions to start from ...

Code Block

# Application Permissions
admin=*all*
editor=login,mainMenu,editProfile,createWeblog

# Weblog Permissions
admin=*all*
author=entries,comments,categories,bookmarks,resources
limited=editDraft

The application would not need to actually check for individual actions such as "categories" or "editProfile" when the security model is first updated, it can continue to simply check for "admin" or "author", but eventually the true power of the security system comes in when we make use of more finely grained action controls. That way each action can be granted/revoked to individual users and combined in any way the owner of the application desiresNOTE: we would probably also want to create a special action for "all" which can be used for admin purposes and implies all actions automatically.

5.3 Add new UserManager methods

...