Go Back   SitePoint Forums > Forum Index > Program Your Site > PHP > PHP Application Design
Newsletter FAQ Members List Calendar Mark Forums Read

New to SitePoint Forums? Register here for free!

SitePoint Sponsor
 
Reply
 
Thread Tools Display Modes
Old Apr 4, 2004, 18:14   #1
seratonin
SitePoint Evangelist
 
Join Date: Dec 2003
Location: Arizona
Posts: 418
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 13:51.
seratonin is offline   Reply With Quote
Old Apr 4, 2004, 18:25   #2
seratonin
SitePoint Evangelist
 
Join Date: Dec 2003
Location: Arizona
Posts: 418
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 19:59.
seratonin is offline   Reply With Quote
Old Apr 4, 2004, 18:39   #3
oivaf
SitePoint Zealot
 
oivaf's Avatar
 
Join Date: Apr 2003
Location: Mexico
Posts: 138
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 ?
oivaf is offline   Reply With Quote
Old Apr 4, 2004, 19:33   #4
seratonin
SitePoint Evangelist
 
Join Date: Dec 2003
Location: Arizona
Posts: 418
The application will need to do the user assignments and permission assignments as well as CRUD for each entity identified above.

JT
seratonin is offline   Reply With Quote
Old Apr 4, 2004, 19:37   #5
seratonin
SitePoint Evangelist
 
Join Date: Dec 2003
Location: Arizona
Posts: 418
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
seratonin is offline   Reply With Quote
Old Apr 4, 2004, 20:51   #6
oivaf
SitePoint Zealot
 
oivaf's Avatar
 
Join Date: Apr 2003
Location: Mexico
Posts: 138
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 23:49. Reason: Removed db schema to avoid confusion/loss of focus
oivaf is offline   Reply With Quote
Old Apr 4, 2004, 21:07   #7
seratonin
SitePoint Evangelist
 
Join Date: Dec 2003
Location: Arizona
Posts: 418
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
seratonin is offline   Reply With Quote
Old Apr 4, 2004, 22:43   #8
seratonin
SitePoint Evangelist
 
Join Date: Dec 2003
Location: Arizona
Posts: 418
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
seratonin is offline   Reply With Quote
Old Apr 4, 2004, 22:46   #9
lastcraft
SitePoint Victim
 
lastcraft's Avatar
 
Join Date: Apr 2003
Location: London
Posts: 2,273
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
lastcraft is offline   Reply With Quote
Old Apr 4, 2004, 22:51   #10
seratonin
SitePoint Evangelist
 
Join Date: Dec 2003
Location: Arizona
Posts: 418
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 5, 2004 at 00:28.
seratonin is offline   Reply With Quote
Old Apr 4, 2004, 23:07   #11
seratonin
SitePoint Evangelist
 
Join Date: Dec 2003
Location: Arizona
Posts: 418
Edit.

Last edited by seratonin; Apr 5, 2004 at 12:15.
seratonin is offline   Reply With Quote
Old Apr 4, 2004, 23:14   #12
seratonin
SitePoint Evangelist
 
Join Date: Dec 2003
Location: Arizona
Posts: 418
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 5, 2004 at 00:22.
seratonin is offline   Reply With Quote
Old Apr 4, 2004, 23:43   #13
oivaf
SitePoint Zealot
 
oivaf's Avatar
 
Join Date: Apr 2003
Location: Mexico
Posts: 138
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
oivaf is offline   Reply With Quote
Old Apr 5, 2004, 01:02   #14
oivaf
SitePoint Zealot
 
oivaf's Avatar
 
Join Date: Apr 2003
Location: Mexico
Posts: 138
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
oivaf is offline   Reply With Quote
Old Apr 5, 2004, 06:14   #15
Widow Maker
Non-Member
 
Join Date: Jan 2004
Location: Planet Earth
Posts: 1,807
Quote:
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.

Widow Maker is offline   Reply With Quote
Old Apr 5, 2004, 06:18   #16
Widow Maker
Non-Member
 
Join Date: Jan 2004
Location: Planet Earth
Posts: 1,807
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
Widow Maker is offline   Reply With Quote
Old Apr 5, 2004, 11:12   #17
seratonin
SitePoint Evangelist
 
Join Date: Dec 2003
Location: Arizona
Posts: 418
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
seratonin is offline   Reply With Quote
Old Apr 5, 2004, 11:38   #18
seratonin
SitePoint Evangelist
 
Join Date: Dec 2003
Location: Arizona
Posts: 418
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
seratonin is offline   Reply With Quote
Old Apr 5, 2004, 11:42   #19
Widow Maker
Non-Member
 
Join Date: Jan 2004
Location: Planet Earth
Posts: 1,807
Quote:
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.
Widow Maker is offline   Reply With Quote
Old Apr 5, 2004, 12:36   #20
seratonin
SitePoint Evangelist
 
Join Date: Dec 2003
Location: Arizona
Posts: 418
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 13:24.
seratonin is offline   Reply With Quote
Old Apr 5, 2004, 13:31   #21
Widow Maker
Non-Member
 
Join Date: Jan 2004
Location: Planet Earth
Posts: 1,807
Indeed

Looks well thought out and clean.
Widow Maker is offline   Reply With Quote
Old Apr 5, 2004, 13:38   #22
seratonin
SitePoint Evangelist
 
Join Date: Dec 2003
Location: Arizona
Posts: 418
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
seratonin is offline   Reply With Quote
Old Apr 5, 2004, 17:09   #23
lastcraft
SitePoint Victim
 
lastcraft's Avatar
 
Join Date: Apr 2003
Location: London
Posts: 2,273
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
lastcraft is offline   Reply With Quote
Old Apr 5, 2004, 17:21   #24
seratonin
SitePoint Evangelist
 
Join Date: Dec 2003
Location: Arizona
Posts: 418
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 17:52.
seratonin is offline   Reply With Quote
Old Apr 5, 2004, 17:59   #25
lastcraft
SitePoint Victim
 
lastcraft's Avatar
 
Join Date: Apr 2003
Location: London
Posts: 2,273
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
lastcraft is offline   Reply With Quote
Reply

Bookmarks

« Previous Thread | Next Thread »

Thread Tools
Display Modes

 
Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Sponsored Links
 
Forum Jump


All times are GMT -7. The time now is 14:31.


Powered by vBulletin® Version 3.7.1
Copyright ©2000 - 2010, Jelsoft Enterprises Ltd.
Copyright 1998-2009, SitePoint Pty Ltd. All Rights Reserved