Indeed
Looks well thought out and clean.
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
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ā¦
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.
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ā¦
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.
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
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ā¦
$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
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:
<?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
Hiā¦
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?
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
We donāt have to stick to the spec, but I donāt see anything horrible about it.
Two questions:
What is wrong with a user object? To me something like:
if ($user->hasPermission('edit', 'article')) {
}
is self-documenting. A User is a real world entity that has roles and permissions, etcā¦ It is almost directly codifying the question that we want to ask: āDoes this user have permission to do this operation on this object?ā
Secondly, what is wrong with unique identifiers?
Thanks,
JT
Hiā¦
For a library itās scope is too large. For example if you decide to add email contact to your application users where does it go? Sounds like it should go into the same user that is the authentication system. You canāt though, as that is supposed to be decoupled from the rest of the system. By simply changing the authorisation systemās name to Usage/UserIdentifier/Fingerprint you have restricted itās scope. Now it is something that can be attached to the user in the application (the one with an email address maybe). It also means that there can be multiple logins for a single person. Having multiple userās for a single person on the other hand just does not make sense.
Another problem I outlined before. You donāt ask the user for permission. You ask the authoriser for permissions that you query. That extra step, of turning an entity (user) into a simple value object (permission), really adds lotās of flexibility. It is a safely copyable object for example and is a less complex item to load.
I once saw a User class/table (actually called Subscriber) destroy a whole project. The notion is deceptively vague.
Nothing (?). Do you mean for object IDs that we reference? In that case plenty if we are a library and we are forcing them on an application. Our tendrils are spreading faster than they should. If you can come up with a clean way of introducing them as an optional item, then there is no problem.
yours, Marcus
Here is a simplification of my previous XML document. I merged together operations and objects and I removed any references to IDās.
<?xml version="1.0"?>
<rbac>
<rbac-users>
<rbac-user name="John"/>
<rbac-user name="Jane"/>
</rbac-users>
<rbac-roles>
<rbac-role name="Employee"/>
<rbac-role name="Manager"/>
</rbac-roles>
<rbac-permissions>
<rbac-permission name="CreateDocument"/>
<rbac-permission name="RetreiveDocument"/>
<rbac-permission name="UpdateDocument"/>
<rbac-permission name="DeleteDocument"/>
</rbac-permission>
<rbac-user-roles>
<rbac-user-role rbac-user="John" rbac-role="Employee"/>
<rbac-user-role rbac-user="Jane" rbac-role="Employee"/>
<rbac-user-role rbac-user="Jane" rbac-role="Manager"/>
</rbac-user-roles>
<rbac-role-permissions>
<rbac-role-permission rbac-role="Employee" rbac-permission="RetreiveDocument"/>
<rbac-role-permission rbac-role="Manager" rbac-permission="CreateDocument"/>
</rbac-role-permissions>
</rbac>
Please let me know what you think.
Thanks,
JT
Hiā¦
Looks fine to me. If you use āidā fields doesnāt that guarantee uniqueness. Are you thinking of storing things this way?
Some rapid hacking using MySql as storageā¦
class Authenticator {
...
function &getPermissions($username, $password) {
$this->_storage->getPermissionsFinder();
return $finder->findByLogin($username, $password);
}
}
class MySqlPermissionsFinder(&$connection) {
...
function &findByLogin($username, $password) {
return new Permissions($this->_connection->select(
'select from permissions, roles, logins where...'));
}
}
class Permissions {
var $_operations;
function Permissions(&$iterator) {
$this->_operations = array();
if (! $iterator) {
return;
}
while ($row = $iterator->next()) {
$this->_operations[] = $row['operation'];
}
}
function can($operation) {
return in_array($operation, $this->_operations);
}
}
A sideline: Does anyone know anything about LDAP? I thought it was designed for this kind of thing?
yours, Marcus
Yes, as an alternative to storage in an RDBMS even though an RDBMS seems more suitable in how you can handle relationships.
JT
Letās flesh out the rest of your implementation (if you havenāt already) and identify the various design patterns used. I would also like to implement XmlPermissionsFinder as well to use some sort of format as above.
JT
There are two ways to get around this. Have your custom User class extend this User class. So if you want to add an email address and/or other attributes you could:
class MyUser extends User {
var $email;
function MyUser($username, $password) {
parent::User($username, $password);
}
function setEmail($email) {
$this->email = $email;
}
function getEmail($email) {
return $this->email;
}
}
Or secondly:
You could create a generic mechanism for dynamically attaching attributes to a user object:
class User {
var $id;
var $username;
var $password;
var $attrs;
var $authenticated;
var $permissions;
var $roles;
function User($username, $password) {
$this->username = $username;
$this->password = $password;
$this->attrs = array();
}
function authenticate() {
// ... authenticate the user against the database;
}
function isAuthenticated() {
return $this->authenticated;
}
function hasPermission($name) {
if (!isset($this->permissions)) {
$this->permissions = array();
// ... get permissions from the database
}
return (in_array($name, $this->permissions));
}
function hasRole($name) {
if (!isset($this->roles)) {
$this->roles= array();
// ... get roles from the database
}
return (in_array($name, $this->roles));
}
function setAttribute($name, $value) {
$this->attrs[$name] = $value;
}
function getAttribute($name) {
return $this->attrs[$name];
}
function removeAttribute($name) {
unset($this->attrs[$name]);
}
function hasAttribute($name) {
return isset($this->attrs[$name]);
}
}
Then you could create a custom UserFinder which extends the base UserFinder and put whatever attributes you wanted into the User object.
JT
Firstly, your root node indicates that the schema will be limited. If you use an XML document to store configuration information, you will be parsing several XML documents, which would be unnecessary overhead. You would find it much more beneficial to use a schema such as ASP.NETs Web.Config schema. This will expose many benefits for adoptionā¦
[color=black]
[/color]
[color=black]<configuration>
<system.web>[/color]
[color=black] [b][font=Verdana]...[/font][/b][/color]
[color=black]<authentication mode="[Windows|Forms|Passport|None]">
<forms name="[name]"
loginUrl=""
protection="[All|None|Encryption|Validation]"
path="[path]" timeout="[minutes]"
requireSSL="[true|false]"
slidingExpiration="[true|false]">
<credentials passwordFormat="[Clear|MD5|SHA1]">
<user name="[UserName]"
password="[password]"/>
</credentials>
</forms>
<passport redirectUrl="internal"/>
</authentication>[/color]
[color=black]<authorization>
<allow users="[comma separated list of users]"
roles="[comma separated list of roles]"/>
<deny users="[comma separated list of users]"
roles="[comma separated list of roles]"/>
</authorization>[/color]
[color=black]<identity impersonate ="[true|false]"
userName="[domain\\user_name]"
password="[user_password]"/>[/color]
[color=black]<trust level="[Full|High|Medium|Low|Minimal]"
originUrl=""/>
<securityPolicy>
<trustLevel name="Full" policyFile="internal"/>
<trustLevel name="High" policyFile="web_hightrust.config"/>
<trustLevel name="Medium" policyFile="web_mediumtrust.config"/>
<trustLevel name="Low" policyFile="web_lowtrust.config"/>
<trustLevel name="Minimal" policyFile="web_minimaltrust.config"/>
</securityPolicy>
[/color]
Defining an Administrator class is an extremely bad in terms of OOD, however as LastCraft has mentioned, you are asking for a headache unless you sit back and clearly design this.
There are a number of components, which make up security:
I must admit I really am not a fan of the above implementation, as it appears that things will become bloated and unethical. Why declare a class for each implementation of MySQL or XML? If this series is trying to demonstrate OO then demonstrate data abstraction and reuse. However, I think a prerequisite would be to give definitions of the terminology used. (Realm, Digest, Nonce)
Now Authentication simply does not stand within the (Application:rolleyes:) use of MySQL and its permissions can come into play to simplify Role Based Authentication & Authorization, which adds countless, advantages. In addition, some common scenarios in security are image verification, resolving DNS, SMTP and as a result expand the specification.
If I can take you back a step, how are you identifying, which pages are secure and require an authentication scheme? You are already trying to implement a PermissionsFinder and skipping a number of stages prior to requiring the knowledge of any Permission. If a security scheme is required, what type of scheme should be applied, HTTP Authentication, Forms Authentication by default? Which digest will be used by default? Page level re-authentication would be required, to add security.
[ot]
Sorry, if it sounds like a rant
Has anyone got any idea whats happening with Pears Auth_Enterprise There has never been anything published, yet the description sounds interestingā¦
[size=1][color=black]
[/color][/size]
As the name implies, this package aims to provide an enterprise level authentication & authorization service. There are two parts to this package, the service layer which handles A&A requests and a PHP client. Support for other clients (e.g. Java, ASP/VB, etc) is possible further supporting cross-platform enterprise needs. Main features are: 1) Web Service-based 2) implements notion of a Provider which is capable of hitting a specific data store (DBMS, LDAP, etc) 3) Implements a single credential set across a single provider 4) 100% OO-PHP with the client producing a user object that can be serialized to a PHP4 session.
[/ot]
We are focusing our attention on Role-Based Access Control. Access to particular resources is granted based on roles and permissions associated with those roles. We have limited the scope such that authentication returns a set of permissions and the set of permissions is inspected to determine whether the user can access the resource (authorization). One desire that we have is to make the system generic enough to support several persistence mechanisms (e.g. RDBMS, XML, etc.). The goal is to illustrate the use of design patterns such as Domain Model, Data Mapper / Finder, Memento, Iterator, Factory, etc. within the context of a well-constrained problem.
JT
Iāve never implemented it but googling a little tells me that yes :tup: this can be one of the uses of LDAP particularly if you want to have a central mechanism of Authentication and Access Control or for example need to use the same user/login accounts from a NT domain in your web application. I believe LDAP would be a valid storage and in fact a possible alternative of rbacā¦
As I commented before I was already working on my own version of rbac (some 4 months agoā¦never finished) and when thinking about how to integrate it to existing or new apps I realized that in order to follow the spec I would need to have some way of storing and/or mapping the application objects and the application users to the rbac system BUT doing so would mean a great loss in flexibility because we would force the programmer to adapt his/her app to our system.
I think this is a critical point and this was precisely what stopped me from going on
By reading your posts I agree that to deal with the application objects the best is to simply leave that to the application.
[quote=āāseratonināā]
There are two ways to get around this. Have your custom User class extend this User class. So if you want to add an email address and/or other attributesā¦[/quote]
mmm I have to say that I donāt feel very comfortable with a User class in the rbac systemā¦why:
However I still donāt see clearly how are we going to communicate with the users or store and map this information to rbac
I think that if we ever use XML to store rbac information a caching mechanism would be devised also for any user the rbac information would be retrieved only once and then persisted in the session.
Security is a big big part of any application with rbac we only are focusing on access control (authorization) not authentication. The idea is that you should be able to use any kind of authentication.
maybe in order to support different storage mechanism/drivers.
anyway will be playing with your interfaces
l8r
Hiā¦
Do you have an alternate scheme? Writing completely abstract persistence layers is a big job.
Also you can only get reuse if itās there to be had. If you want to take advantage of complex queries and caching (likely here), they will all be quite different. You will be able to get some reuse within the storage families by inheritance (thatās the advantage of the mappers, they bridge to the domain objects). At least this way itās all hidden by the Storage abstract factory.
I like the idea of my code being unethical :). Should I be struck off?
I do agree that my Authorisor class should be something like AccessController and I should take the password out. It is more client code, but it is that little bit more decoupled as oivaf points out.
yours, Marcus
ā¦more suitable in how you can handle relationships.
Exactly Sorry Marcus, but I cannot for the life of me, understand your gripe about using a database, though Iāve read every post
I think for this type of data and itās relationships, XML is bad - This is not what XML was originally designed for.
A question though Marcus, your point on storage, are you suggesting a Memento Pattern ?
I was searching the Web and Binary Cloudās RBAC implementation here:
http://webstract.org/api//__filesource/fsource_binarycloud.auth__authPerm.php.html
JT
Hiā¦
Well I may have been a bit silly :). Maybe I should be struck offā¦
The thing is, the point where it needs to be fast is at the point of loading the permission set. This scheme involves a five table join in a DB. It will still work though, and I guess the dataset will not be large.
I am now thinking that a caching scheme may be the way to handle this whilst still using the RDBMS to administer things. The permission sets could be stored as serialised classes in a DBM hash for example. The cache mangement could decorate the finder.
I agree. I am not sure XML is the way to go for storage. However it could be a good way to configure the system, especially for testing or for bootstrapping an application. Basically a loader could parse the XML into a storage solution when it is edited.
I think LDAP is a serious contender as it has very fast reads. RDBMS remove redundancy, but I donāt see that as an issue. The main thing is that the issue can be postponed whilst things are built top-down.
I was only thinking of the data transfer between the data mapper and the authentication map so far. It is still rather fuzzy until some more code is written.
yours, Marcus
Iām thinking that we do the JOIN intially and then just cache just the permission set as XML on the server in the session.
JT