SitePoint Sponsor

User Tag List

Page 1 of 6 12345 ... LastLast
Results 1 to 25 of 267

Hybrid View

  1. #1
    SitePoint Evangelist
    Join Date
    Dec 2003
    Location
    Arizona
    Posts
    411
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Patterns Tutorial Series (part 1): RBAC Domain Model

    For our first collaborative effort it has been suggested by several users that we implement a Role-Based Access Control system using Design Patterns and Best Practices. There is an official ANSI spec for Role-Based Access Control here:

    http://csrc.nist.gov/rbac/rbac-std-ncits.pdf

    Approach:

    I think we should start with the domain model and then move onto the data model (database design) which will include a mapping between the two. Once we have a solid Domain Model and Data Model we can start talking about whether we want a service layer, what the application layer would look like, and finally the presentation layer. I think that making a service layer would be nice enhancement because then we could have a Web-based presentation layer and build some Web services on top as well. I think we could purposefully start out simple and go through some refactoring to illustrate how that process fits into the picture.

    So, let's start with the domain model.

    JT

    ----------------------------------------------------------------------
    Update:

    Here is the progress that has been made so far:

    We started off by looking at the NIST RBAC specification. Realizing that the spec was way too large for our purposes, we decided to focus on the core RBAC. We removed the concept of sessions and concentrated on the relationship between Users, Roles, and Permissions. It was proposed that we have a User class but ultimately we decided that the scope of a user class would be too large. Instead, we would have a Authorizer class which returned a set of permissions. The set of permissions would be inspected to determine whether or not a user had permission to do a particular operation. An interface and some test cases were created and we are now at the point where we will begin to flesh out the classes and indentify patterns that will be used in the implementation.
    ----------------------------------------------------------------------
    Last edited by seratonin; Apr 10, 2004 at 12:51.

  2. #2
    SitePoint Evangelist
    Join Date
    Dec 2003
    Location
    Arizona
    Posts
    411
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    The core spec identifies the following entities:

    User
    Role
    Object
    Operation
    Permission
    Session

    There is a many-to-many relationship between Users and Roles
    There is a many-to-many relationship between Roles and Permissions.
    There is a one-to-many relationship between Users and Sessions.
    There is a many-to-many relationship between Sessions and Roles.
    There is a many-to-many relationship between Objects and Operations.
    There is a containment relationship between Permissions and (Objects and Operations).

    A session is really just a set of active roles for a particular user.

    Does anybody want to whip up a quick UML diagram to illustrate this or is the drawing in the spec sufficient? This is a nice spec BTW.

    Thanks,

    JT
    Last edited by seratonin; Apr 4, 2004 at 18:59.

  3. #3
    SitePoint Zealot oivaf's Avatar
    Join Date
    Apr 2003
    Location
    Mexico
    Posts
    138
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I think maybe is not that necessary as you commented the spec is very good (y) but maybe including a diagram from the spec for example

    could do ?

  4. #4
    SitePoint Evangelist
    Join Date
    Dec 2003
    Location
    Arizona
    Posts
    411
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    The application will need to do the user assignments and permission assignments as well as CRUD for each entity identified above.

    JT

  5. #5
    SitePoint Evangelist
    Join Date
    Dec 2003
    Location
    Arizona
    Posts
    411
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    A permission is really just a container of objects and operations on those objects. So we want to be able ask the system questions like:

    Does this user have permission to do this operation on this object?

    PHP Code:
    if ($user->hasPermission('edit''article')) {
        
    // do edit on article
    } else {
        
    // error: you do not have permission to edit this article

    JT

  6. #6
    SitePoint Zealot oivaf's Avatar
    Join Date
    Apr 2003
    Location
    Mexico
    Posts
    138
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by seratonin
    The application will need to do the user assignments and permission assignments as well as CRUD for each entity identified above.
    agreed .

    Now what about using the proposed function/method names and prototypes by the spec ?

    ...

    I believe that the only tricky part of this, considering that there is already an evolved spec, is to come up with a solution or implementation that is flexible, highly reusable and adapted to target the web development environtment.

    more later....
    Last edited by oivaf; Apr 4, 2004 at 22:49. Reason: Removed db schema to avoid confusion/loss of focus

  7. #7
    SitePoint Evangelist
    Join Date
    Dec 2003
    Location
    Arizona
    Posts
    411
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Your preliminary schema looks good. I was hoping that we could tackle the object model first and then proceed to the database design, but it appears that you have captured the core relationships in your data model.

    JT

  8. #8
    SitePoint Evangelist
    Join Date
    Dec 2003
    Location
    Arizona
    Posts
    411
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    So we have the following entities for core RBAC:

    User
    Role
    Permission
    Operation
    Object
    Session

    The spec also defines the following "functions":

    Administrative commands:

    AddUser
    DeleteUser
    AddRole
    DeleteRole
    AssignUser
    DeassignUser
    GrantPermission
    RevokePermission

    Supporting system functions:

    CreateSession
    DeleteSession
    AddActiveRole
    DropActiveRole
    CheckAccess

    Review functions:

    AssignedUsers
    AssignedRoles

    Advanced review functions:

    RolePermissions
    UserPermissions
    SessionRoles
    SessionPermissions
    RoleOperationsOnObject
    UserOperationsOnObject

    So now we have the task of determining the classes, attributes, and methods from this information.

    JT

  9. #9
    ********* Victim lastcraft's Avatar
    Join Date
    Apr 2003
    Location
    London
    Posts
    2,423
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Hi...

    Quote Originally Posted by seratonin
    Your preliminary schema looks good. I was hoping that we could tackle the object model first and then proceed to the database design, but it appears that you have captured the core relationships in your data model.
    Guys, please stop!

    You've started off with a huge spec, but no goal or context. You will end up with a design that is overkill and then takes ages to code. Modern development is iterative, not waterfall, and this could turn into a worst practices thread rather than a best practices one. Already it's too complicated for just about any web app. I've ever seen. Here are some places where things have gone overboard...

    The spec includes methods for setting up roles and permissions for a continually running system. With PHP you can do that by editing a file. It looks very much to me like a spec. for a centralised authentication and authorisation server, rather than a single application. Is that what you want? If not then just edit a single web page and so doing ditch over half the methods.

    What do you want the client code to look like? The only operation an authorisation system ultimately has to accomplish is this...
    PHP Code:
    $permissions $authenticator->getPermissions($username$password);
    if (
    $permissions->can_edit()) {
        ...

    Everything else is complication and coupling. The management of the logins would be handled by a separate interface, say an authorisor.

    You are jumping straight into a model. Who says the essential entities are those listed above? They may exist as something (being conceptual they probably will), but that something could just be the act of one referencing the other. You have started with a relational DB schema. Who says we need a relational database? Authorisation data is read often, but written rarely. This opens up lot's of possibilities. It is probable that an RDBMS system will still be used, but it is still an implementation decision that can be put off.

    Just suppose that we do indeed want an authentication system that could be remoted. Here is one facade and one memento that will do the trick just following the spec. given...
    PHP Code:
    class Authoriser {
        function 
    Authoriser() { ... }
        function 
    getOperations($username$password) { ... }
        function 
    addUsage($username$password) { ... }
        function 
    dropUsage($username);
        function 
    addRole($role) { ... }
        function 
    dropRole($role) { ... }
        function 
    grantOperation($role$operation) { ... }
        function 
    revokeOperation($role$operation) { ... }
    }
    class 
    Permissions {
        function 
    Permissions(&$authoriser$username$password) {
            
    $this->_allowed $authoriser->getOperations(
                    
    $username$password);
        }
        function 
    __call() { ... }

    Because the permissions are read only after they are fetched, the getOperations() method can pass a simple list (memento pattern) that the dynamic proxy can make into an object. Everything else can be either a SOAP interface or a local interface without affecting the client code. In fact even simpler is to replace all of the twiddly granting and revocation with a simple XML list of roles and operations...
    Code:
    <?xml?>
    <rbac version="214">
        <role name="superuser">
            <operation name="edit"/>
        </role>
    </rbac>
    Now the first part of the interface is...
    PHP Code:
    class Authoriser {
        function 
    Authoriser() { ... }
        function 
    getOperations($username$password) { ... }
        function 
    addUsage($username$password) { ... }
        function 
    dropUsage($username) { ... }
        function 
    queryAccessList() { ... }
        function 
    commitAccessList($xml) { ... }

    This has the advantage of making the access system more resilient to changes later on.

    This is all worst case of course. No one has said that we will ever need remote access.

    yours, Marcus
    Marcus Baker
    Testing: SimpleTest, Cgreen, Fakemail
    Other: Phemto dependency injector
    Books: PHP in Action, 97 things

  10. #10
    SitePoint Evangelist
    Join Date
    Dec 2003
    Location
    Arizona
    Posts
    411
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I believe the goal here should be to implement the Core RBAC spec. The spec is huge, we only need the core for now. The core is pretty simple as I have outlined above. What I think we need to do is define what classes we are going to have and how the operations fit into those classes. I already have a DomainModel in my head that should accommodate the core RBAC using the entities above. If we find that the entities are insufficient, we can always create different one's. My idea was to start with the spec and diverge if necessary.

    JT
    Last edited by seratonin; Apr 4, 2004 at 23:28.

  11. #11
    SitePoint Evangelist
    Join Date
    Dec 2003
    Location
    Arizona
    Posts
    411
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Edit.
    Last edited by seratonin; Apr 5, 2004 at 11:15.

  12. #12
    SitePoint Evangelist
    Join Date
    Dec 2003
    Location
    Arizona
    Posts
    411
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Here is a set of simple interfaces and some ideas of where I think the various functions belong.

    PHP Code:
    // User class will contain a set of Sessions.
    // User Assignment (UA) associates this User with a particular Role.
    class User {
        function 
    User($name) {}
        function 
    createSession() {}
        function 
    deleteSession(&$session) {}
        function 
    assignUser(&$role) {}
        function 
    deassignUser(&$role) {}
    }

    // Session class will have a set of active Roles.
    class Session {
        function 
    Session($name) {}
        function 
    addActiveRole(&role) {}
        function 
    dropActiveRole(&role) {}
        function 
    checkAccess($operation$object) {}
    }

    // Role class will have a set of Permissions.
    // Permission Assignment (PA) says that Permissions are granted and revoked to/from Roles.
    class Role {
        function 
    Role($name) {}
        function 
    grantPermission(&$permission) {}
        function 
    revokePermission(&$permission) {}
    }

    // A Permission is simply an Operation on an Object.
    class Permission {
        function 
    Permission($operation$object) {}
    }

    // The client code would look something like this:
    $session =& $user->createSession();

    if (
    $session->checkAccess('edit''article')) {
        
    // user is allowed to edit the article
    } else {
        
    // error

    Comments on this design are welcome (appreciated).

    JT
    Last edited by seratonin; Apr 4, 2004 at 23:22.

  13. #13
    SitePoint Zealot oivaf's Avatar
    Join Date
    Apr 2003
    Location
    Mexico
    Posts
    138
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi,
    first of all I don't have much experience so pardon my (future) mistakes , as lastcraf says maybe we should state what is going to be the goal or context for this project or tutorial or whatever

    I never thought to implement all the entire spec but instead adapt it to web applications. I believe the client code will be very similar to the one seratonin and lastcraft posted.

    By skimming the spec you can realize that there are at least 2 sets of functions for core rbac: one for the end user and one for the rbac system administration and yes you could say it is very basic and easy to implement because it is. The complexity increases with the other levels of rbac like Hierarchical RBAC, SSD relation and DSD relation for which I still haven't seen a use in web development.

    I really find it very easy to code an rbac system the wrong way so this thread is a great oportunity to do the oposite

    So where should we start ? maybe a list of requirements ? ......mm I'll remove the db schema

  14. #14
    SitePoint Zealot oivaf's Avatar
    Join Date
    Apr 2003
    Location
    Mexico
    Posts
    138
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    First I think we should divide the functions/classes on

    1) client side - the programmer who wants to implement rbac on a new or existing application

    2) administration side - this is the interface that interacts with the objects and users of our new or existing application, creating, editing, assigning roles, creating and assigning permissions, etc.

    To clarify an object could be for example: the authoring suite of Serendipity then the possible operations would be create, edit or delete entries.

    Now about the interfaces ... on the client side

    ideally IMO our rbac implementation shouldn't have to know anything about authentication nor sessions. You could authenticate to an LDAP server or via SOAP, or you could have file or db sessions with you own Session class. I know you can have wrappers to integrate everything but I think that that task should be left to the end user.

    So for example using the proposed functions we just need:
    PHP Code:
    /* mm what about using functions instead of a class ??? */
    class RBAC {
        
    /***
        * LoadRoles should only be called once commonly at the start of 
        * an authenticated user session
        * @param UserID - your mapping to your users system/table
        */
        
    function &LoadRoles($UserId) {}
        function 
    CheckAccess(&$session$operation$object) {}
    }

    // some very basic simplistic example for an application that requires
    // a user to be authenticated
    session_start(); // or Session :: start() or $session->start :p
    if (!$_SESSION['auth']) {
        require 
    'auth.inc.php';
        
    $authenticator =& new Authenticator($username$password);
        if (
    $authenticator->isAuthenticated()) {
            
    $_SESSION['permissions'] =& RBAC::LoadRoles($_SESSION['user_id']);
        }
    }

    ......

    //later in other part of the app
    if (RBAC::CheckAccess($_SESSION['permissions'], 'add''comment')) {
        
    // do it

    what do you think about this ?

    seratonin :
    in your Session interface regarding your methods addActiveRole and dropActiveRole, I don't think we're going to need those because usually a user is authenticated only once and at that moment all the roles corresponding to the user are loaded (is this a good or bad thing ?). Also I believe that this methods are part of the higher levels of RBAC

    about your user and session interfaces see my comments above and tell me what you think

  15. #15
    Non-Member
    Join Date
    Jan 2004
    Location
    Planet Earth
    Posts
    1,764
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I never thought to implement all the entire spec but instead adapt it to web applications.


    As Marcus has said - my interpretation - to implement the whole specification would be way over the top ?

    Also, I think the original purpose of this thread, we should remain OO all the way, to design with OO and Proc Prog would proberly do more harm than good for those who are interested in learning OO Design, Design Patterns and Refactoring, et al.


  16. #16
    Non-Member
    Join Date
    Jan 2004
    Location
    Planet Earth
    Posts
    1,764
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    PHP Code:
    //later in other part of the app 
    if (RBAC::CheckAccess($_SESSION['permissions'], 'add''comment')) { 
        
    // do it 

    Not to happy with this. In my view, it could be a class method that fetches the permissions, as it is not set in stone that you'd be using SESSIONs ?

    This requirement could change from application to application is what I'm saying I suppose

  17. #17
    SitePoint Evangelist
    Join Date
    Dec 2003
    Location
    Arizona
    Posts
    411
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Let's say we started off with a more course-grained, "service-oriented" approach and we treated the core RBAC functional specification as a set of "use cases".

    We would have:

    PHP Code:
    class RBAC {
        
    // Admin Functions
        
    function AddUser($name) {}
        function 
    DeleteUser($name) {}
        function 
    AddRole($name) {}
        function 
    DeleteRole($name) {}
        function 
    AssignUser($user$role) {}
        function 
    DeassignUser($user$role) {}
        function 
    GrantPermission($object$operation$role) {}
        function 
    RevokePermission($object$operation$role) {}

        
    // System Functions
        
    function CreateSession($user$session) {}
        function 
    DeleteSession($user$session) {}
        function 
    AddActiveRole($user$session$name) {}
        function 
    DropActiveRole($user$session$name) {}
        function 
    CheckAccess($session$operation$object$result) {}

        
    // Review Functions
        
    function AssignedUsers($role) {}
        function 
    AssignedRoles($user) {}

        
    // Advanced Review Functions
        
    function RolePermissions($role$result) {}
        function 
    UserPermissions($user$result) {}
        function 
    SessionRoles($session$result) {}
        function 
    SessionPermissions($session$result) {}
        function 
    RoleOperationsOnObject($role$object$result) {}
        function 
    UserOperationsOnObject($user$object$result) {}

    Obviously that is quite a "god" class, so we could break it down further into (where the classes are more like groups of related transaction scripts):

    PHP Code:
    class RBACAdmin {
        
    // Admin Functions
        
    function AddUser($name) {}
        function 
    DeleteUser($name) {}
        function 
    AddRole($name) {}
        function 
    DeleteRole($name) {}
        function 
    AssignUser($user$role) {}
        function 
    DeassignUser($user$role) {}
        function 
    GrantPermission($object$operation$role) {}
        function 
    RevokePermission($object$operation$role) {}
    }

    class 
    RBACSystem {
        
    // System Functions
        
    function CreateSession($user$session) {}
        function 
    DeleteSession($user$session) {}
        function 
    AddActiveRole($user$session$name) {}
        function 
    DropActiveRole($user$session$name) {}
        function 
    CheckAccess($session$operation$object$result) {}
    }

    class 
    RBACReview {
        
    // Review Functions
        
    function AssignedUsers($role) {}
        function 
    AssignedRoles($user) {}
    }

    class 
    RBACAdvancedReview {
        
    // Advanced Review Functions
        
    function RolePermissions($role$result) {}
        function 
    UserPermissions($user$result) {}
        function 
    SessionRoles($session$result) {}
        function 
    SessionPermissions($session$result) {}
        function 
    RoleOperationsOnObject($role$object$result) {}
        function 
    UserOperationsOnObject($user$object$result) {}

    Even that still seems too procedural for my taste. I know that we might want a service layer later, but I what I was trying to do is come up with a Domain Model -- something that encapsulates both the data and the behavior of the system which brought me to create the set of interfaces in my previous post. Now, I can see how you would create permissions, assign those permissions to a role and then assign roles to users with that model, but it is still lacking and that is where I was hoping others might help improve it.

    Thanks,

    JT

  18. #18
    SitePoint Evangelist
    Join Date
    Dec 2003
    Location
    Arizona
    Posts
    411
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    After reviewing the original specification and the various posts, I think we should refine/descope the spec a little bit. As Marcus stated in his post, ultimately the client needs to be able to ask the system the following question:

    "Does this user have permission to do this operation on this object?"

    The original spec calls for "sessions" but I don't think that is necessary for our treatment. I think we should focus on User, Role, and Permission (where a permission contains an operation and an object). Now, typically there are two stages, authentication and authorization. Authentication usually happens once and Authorization will happen many times once the user has been authenticated. So if we want to include authentication, our system must also ask:

    "Is the user authethenticated?"

    and also provide some mechanism to authenticate the user.

    We could have two stateless classes that are responsible for these two activities:

    Authenticator
    Authorizer

    Or we could somehow integrate the behavior directly into our Domain Model. I think that we can expose the functionality with Authenticator and Authorizer interfaces but have then directing calls to the Domain model (User, Role, Permission) which behind those interfaces. Now keep in mind I have not even begun to think about the admin functions (create user, create role etc).

    JT

  19. #19
    Non-Member
    Join Date
    Jan 2004
    Location
    Planet Earth
    Posts
    1,764
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I can see how you would create permissions, assign those permissions to a role and then assign roles to users with that model, but it is still lacking and that is where I was hoping others might help improve it.
    I would from this quotation, create 3 classes from it, no ? ie

    PHP Code:
    CreatePermissions { ... }
    AssignPermissions { ... }
    ManageRoles { ... } 
    As, for example, when you create a permission, you'd then need to be given to a role(s) - which has nothing to do with Roles in it's self, it's a standalone entity as I see it

    Just my thoughts.

  20. #20
    SitePoint Evangelist
    Join Date
    Dec 2003
    Location
    Arizona
    Posts
    411
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I like the idea of breaking things up a bit so that we don't have any "god" classes but those classes (at least by the names) sound more like they should be a group of related domain logic in some domain model. Okay. So we have these administrative functions. Who or what would normally take care of these functions? Most likely it would be a system administrator. If we put the functionality in the User Domain Model, then you will have a bunch of functionality that is not used by 99% of the users. Where to put the administrative functions... Well, we could have an Administrator class which has the functions addUser, deleteUser, addRole, deleteRole, assignUser, deassignUser, grantPermission, revokePermission. So we would have three classes:

    PHP Code:
    class User {
        function 
    User($username$password) {}
        function 
    authenticate() {}
        function 
    isAuthenticated() {}
        function 
    hasPermission($operation$object) {
            
    $flag false;

            foreach (
    $this->permissions as $p) {
                if (
    $p->getOperation() == $operation && $p->getObject() == $object) {
                    
    $flag true;
                }
            }

            return 
    $flag;
        }
    }

    // read-only container
    class Permission {
        var 
    $operation;
        var 
    $object;

        function 
    Permission($operation$object) {
            
    $this->operation $operation;
            
    $this->object $object;
        }

        function 
    getOperation() {
            return 
    $this->operation;
        }

        function 
    getObject() {
            return 
    $this->object;
        }
    }

    class 
    Administrator {
        function 
    addUser($user) {}
        function 
    deleteUser($user) {}
        function 
    addRole($role) {}
        function 
    deleteRole($role) {}
        function 
    assignUser($user$role) {}
        function 
    deassignUser($user$role) {}
        function 
    grantPermission($role$permission) {}
        function 
    revokePermission($role$permission) {}
    }

    // client code (administrative functions)
    $admin =& new Administrator();

    // create a new user
    $admin->addUser('John''username''password');

    // create a new role
    $admin->addRole('author');

    // create a permission (operation/object pair)
    $perm =& new Permission('edit''article');

    // grant the permission to the new role
    $admin->grantPermission('author'$perm);

    // assign the role to the user
    $admin->assignUser('John''author');

    // client code (general)
    $user =& new User($username$password);
    $user->authenticate();

    if (
    $user->isAuthenticated()) {
        if (
    $user->hasPermission('edit''article')) {
            
    // do edit
        
    } else {
            
    // error
        
    }
    } else {
        
    // redirect to login

    It seems like a simple enough interface...

    JT
    Last edited by seratonin; Apr 5, 2004 at 12:24.

  21. #21
    Non-Member
    Join Date
    Jan 2004
    Location
    Planet Earth
    Posts
    1,764
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Indeed

    Looks well thought out and clean.

  22. #22
    SitePoint Evangelist
    Join Date
    Dec 2003
    Location
    Arizona
    Posts
    411
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Widow Maker
    Indeed

    Looks well thought out and clean.
    We want the interfaces to be generic so that we can persist the RBAC data in either a database or a file system. That is our next step. How to apply the correct patterns to acheive that level of abstraction. I have seen some of Lastcraft's code implement a Persistent interface which might be the way to go with this. Maybe he would be obliged to go over something like that.

    JT

  23. #23
    ********* Victim lastcraft's Avatar
    Join Date
    Apr 2003
    Location
    London
    Posts
    2,423
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Hi...

    I am still trying to reduce the scope so that something can be demonstrated working. I favour use cases over structural specs. for this. We have two use cases...

    1) We ask the authenticator to turn an identity into a set of permissions. These permissions can be queried (they are read only in this context) and return simple boolean values.

    2) An administrator/script can add and remove people and permissions. To save effort, permissions are grouped into roles. A user can have multiple roles in RBAC.

    I have already spotted some potential pitfalls in the discussions above...

    1) I don't think it is a good idea to have object IDs mixed in. This will entangle the permissions system with application specific stuff. You probably have an idea of ownership and this can be expressed within the application code by adding more permissions. E.g. Have edit_own_documents (author) as well as edit_documents (editor in chief). How these are handled is up to the application - as it should be. Ownership of objects within the application is likely to be complex and highly variable.

    2) Don't involve sessions as the core problem has nothing to do with session management. Basically once you have a set of permissions you can place that set into the session. You no longer need the user's identity because the sesssion system takes care of that for you. You also involve synchronisation issues this way. Because permissions are constant after the login (invariant) they can be cached into the session easily. A user on the other hand may change and you will likely have to fetch it from deep storage on every request.

    I am going to shamelessly plug my own tool here and turn the use cases into a test case...
    PHP Code:
    <?php
    class RoleBasedAuthenticationTest extends UnitTestCase {
        function 
    RoleBasedAuthenticationTest() {
            
    $this->UnitTestCase();
        }
        function 
    setUp() {
            
    $authorisor = &new Authoriser();
            
    $authorisor->addUsage('fred''secret');
            
    $authorisor->addRole('pleb');
            
    $authorisor->addOperation('do_stuff');
            
    $authorisor->attachRole('fred''pleb');
            
    $authorisor->permit('pleb''do_stuff');
        }
        function 
    tearDown() {
            
    $authorisor = &new Authoriser();
            
    $authorisor->dropUsage('fred');
            
    $authorisor->dropRole('pleb');
            
    $authorisor->dropOperation('do_stuff');
        }
        function 
    testNonUserHasNothingAllowed() {
            
    $permissions = &$authenticator->('public''');
            
    $this->assertFalse($permissions->can('do_stuff'));
        }
        function 
    testBadPasswordHasNothingAllowed() {
            
    $permissions = &$authenticator->('fred''wrong');
            
    $this->assertFalse($permissions->can('do_stuff'));
        }
        function 
    testLegitimateUserHasActionAllowed() {
            
    $permissions = &$authenticator->('fred''secret');
            
    $this->assertTrue($permissions->can('do_stuff'));
        }
        function 
    testUserCannotDoNonAction() {
            
    $permissions = &$authenticator->('fred''secret');
            
    $this->assertFalse($permissions->can('do_unknown'));
        }
    }
    ?>
    The permissions class is following the NullValue pattern here. Feel free to play with this spec. though.

    Other use cases and test cases can probably be fleshed out. My only thought on persistence is that the queries will be quite involved, but it is otherwise data centric. A PermissionsFinder makes sense in each type of storage. e.g...
    PHP Code:
    $storage = &new Storage(new Configuration());
    $finder = &$storage->getPermissionsFinder();
    $permissions = &$finder->findByUser($name); 
    I still feel that the best way to persist the roles and operations is as a single mapping object. This way it is inherently transactional. It is likely to be edited rarely so the overhead of loading the whole thing should be inconsequential. The same can not be said for adding Usages, which are likely more frequent as people may sign up with automated scripts. This gives candidate persistence classes of: Configuration, Storage, AuthorityMapper, UsageMapper, PermissionsFinder, UsageFinder. These multiply up by the ways of storage (DBM files, RDBMS), but are easy to code.

    I am avoiding classes for roles and operations as these objects are purely passive data. They would be represented in the storage schema only. This is one of the ways that thinking in data terms rather than in jobs can throw you off track and make things seem more complicated than they really are.

    yours, Marcus
    Marcus Baker
    Testing: SimpleTest, Cgreen, Fakemail
    Other: Phemto dependency injector
    Books: PHP in Action, 97 things

  24. #24
    SitePoint Evangelist
    Join Date
    Dec 2003
    Location
    Arizona
    Posts
    411
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    The one thing I want to keep from the original spec is the fact that a permission is an operation/object pair. Your permission system mixes the two. edit_own_document is actually a permission to do an 'edit' on a 'document'. I think allowing for mixing an matching operations/objects keeps the system flexible and doesn't add too much overhead.

    Another thing that is important is the many-to-many relationship between users and roles and the many-to-many relationship between roles and permissions. This makes the system really flexible and avoids redundant data storage. Unfortunately, without using id's, it is difficult to model a many-to-many relationship in say an XML document. You would have something like this:

    Code:
    <?xml version="1.0"?>
    <rbac>
      <users>
        <user id="1" name="John" username="user" password="pass"/>
        <user id="2" name="Jane" username="user" password="pass"/>
      </users>
    
      <roles>
        <role id="1" name="Sales Person"/>
        <role id="2" name="Sales Manager"/>
      </roles>
    
      <!-- a permission is a mapping between an operation and an object -->
      <permissions>
        <permission id="1" operation="1" object="1"/>
        <permission id="2" operation="2" object="1"/>
        <permission id="3" operation="3" object="1"/>
      </permissions>
    
      <operations>
        <operation id="1" name="add"/>
        <operation id="2" name="update"/>
        <operation id="3" name="delete"/>
      </operations>
    
      <objects>
        <object id="1" name="article"/>
      </objects>
    
      <!-- User Assignment -->
      <user_roles>
        <user_role user="1" role="1"/>
        <user_role user="2" role="1"/>
      </user_roles>
    
      <!-- Permission Assignment -->
      <role_permissions>
        <role_permission role="1" permission="1"/>
        <role_permission role="2" permission="1"/>
        <role_permission role="2" permission="2"/>
        <role_permission role="2" permission="3"/>
      </role_permissions>
    </rbac>
    Although the structure realizes the relationships I am going for, it is difficult to manage. It is flexible at the cost of complexity. A RDBMS would be a better candidate for these various many-to-many relationships because then at least you could do efficient joins.

    JT
    Last edited by seratonin; Apr 5, 2004 at 16:52.

  25. #25
    ********* Victim lastcraft's Avatar
    Join Date
    Apr 2003
    Location
    London
    Posts
    2,423
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Hi...

    Quote Originally Posted by seratonin
    The one thing I want to keep from the original spec is the fact that a permission is an operation/object pair. Your permission system mixes the two. edit_own_document is actually a permission to do an 'edit' on a 'document'. I think allowing for mixing an matching operations/objects keeps the system flexible and doesn't add too much overhead.
    Actually I am leaving the separation up to the application, and certainly not mixing them. If anything I am unrolling them. I think mixing ownership IDs with the permissions system is a mistake and the more I read that document the less happy I am with it. Having a User object is the start of rot in my experience. Sounds like the whole set up was pinched straight out of a database or document management system. In that situation the authorisation system would be tightly integrated which is possibly not what we want for a web component.

    It's not the end of the world however. Just a change of character. How about we add object IDs later on, perhaps as an extension?

    Quote Originally Posted by seratonin
    Another thing that is important is the many-to-many relationship between users and roles and the many-to-many relationship between roles and permissions. This makes the system really flexible and avoids redundant data storage.
    I think this is an assumption. The only point where the data is really required to be normalised is when an operation is removed across the board. As application code could be broken by this I cannot imagine this being allowed by the system. Likely the fixed list of operations would be shipped with the app. and installed at the start. Again, this doesn't sound like a natural RDBMS solution to me (although it can be done of course). I think RDMBS storage would be chosen by people who have a lot of roles only.

    How closely do you want to stick to the spec?

    yours, Marcus
    Marcus Baker
    Testing: SimpleTest, Cgreen, Fakemail
    Other: Phemto dependency injector
    Books: PHP in Action, 97 things


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •