views:

285

answers:

8

I'm in the process of a building an access control system as part of a web framework I am developing. I want to make it super flexible and awesome. Can you help me by providing input and insight on my design? Here is my work so far (my specific questions are at the bottom):

Users

  • Users have a username (32 characters, no spaces) and password
  • Users have one or more e-mail addresses that must be verified
  • Users may login using either their username or any of their e-mail addresses
  • Users may be associated to zero or many accounts

Accounts

  • Accounts represent one or more users
  • Each user may have specific permissions or roles for an account (e.g., account owner or "can add new user")
  • All accounts are tied to an account type

Account Types

  • Account types have zero or many account type roles
  • Account types have zero or many account type features

Account Type Roles

  • E.g., "Owner", "Administrator", "Power User", "Guest", etc.
  • Account type roles are a collection of account type permissions

Account Type Permissions

  • Account type permissions are specific actions in the system that application logic will verify against
  • They may reference a parent, so they can be hierarchically grouped
  • E.g.:
    • "User Management"
      • "Add User"
      • "Remove User"
  • These permissions may be specifically for an account type feature

Account Type Features

  • Account type features may be activated on an account to give it more permissions
  • E.g., "Standard Account" or "Premium Account"
  • These features, if activated on an account, will give the account owner greater access to the system
  • They are tracked when they are activated or deactivated and can be billed against periodically or on demand

Questions

What is the best way to have application logic check against a user action? I was thinking of storing all of a user's permissions in an object for their session (which would require a logout/login to refresh permissions, which I am not a fan of - any ideas on real time permission management?):

{
  "All Permissions": {
    "User Management": {
        "Add User",
        "Delete User"
    },
    "Premium Account": {
        "Download Files",
        "Upload Files"
    },
  }
}

I would then declare permissions that are required for a specific action in the system. Maybe something like:

Permission::require('Add User');

If the declared permissions were not in the users permission object, the request would fail. This seems kind of intense for each user action though. Also, what if another subset of permissions has the string "Add User"?

Thanks in advance for any help with this!

+3  A: 

One way I've seen that I like is a sort of "cascading" permissions. You have a core set of permissions - let's say read, write, delete - and those can be assigned to a group or a user.

READ    USER1
READ    USER2
WRITE   USER2
READ    USER3
WRITE   USER3
DELETE  USER3

Alternatively you can specify a "group" instead of a username.

READ    SUBSCRIBER
READ    EDITOR
READ    ADMIN
WRITE   EDITOR
WRITE   ADMIN
DELETE  ADMIN
USER1   SUBSCRIBER
USER2   EDITOR
USER3   ADMIN

And then you can simply use the values in the table to sort and look for records. This allows for the flexibility of being a member of multiple groups with mutually exclusive permissions, etc.

infamouse
+6  A: 

Looking at your Account Type Permissions, it appears you have an Access Control List (ACL) style system design in mind.

If you want to make it super flexible and awesome, then I'd suggest this is not a good design. ACL system's work for simple permissions - and maybe that actually is ok in your scenario - but as soon as the rules for granting permission become even the slightest bit dynamic - that is, relying on any contextual data beyond the user's identity or roles - ACL's fall flat fast.

This video goes into some detail about the failings of ACL's and discusses alternate ways to implement access control that accounts for real-world situations.

Also, this has been done before (though there's surprisingly little out there for implementations we can look at); perhaps have a look at Rhino Security.

qstarin
+1  A: 

I don't have any specific advice but two systems I'm familiar with that have very good flexible access/permission systems are Drupal and Plone. You could do far worse than copying how either of them work. They've had years of real-world testing behind them.

Kyle Mathews
+2  A: 

I would take a look at the Java system for permissions:

http://download.oracle.com/javase/6/docs/api/java/security/Permission.html

It uses "implication logic"; that is, the permission object is what decides whether the given action is allowed (i.e. whether the permissions "implies" access to a resource). I would also check out the BasicPermission as it has a pretty straight-forward namespace spec for permissions. In your example it would be (following the CRUD nomenclature)

  • user.create
  • user.delete
  • file.read
  • file.create

In our webapp, we assign a permission to each resource or procedure that can be requested, and a set of permissions to each user. We then do a

boolean isAuthorized = user.permissions.implies(requestedResource.permission); (standard encapsulation implied)

to determine if the user is allowed access.

Nathan
+2  A: 

Zed Shaw had some interesting things to say about ACL's and their limitations. Definitely worth watching before you go any further down this route.

http://vimeo.com/2723800

mjijackson
That is the video I also linked to in my answer
qstarin
+1  A: 

The semantics you are using are a bit confusing. For example Account Types of "Owner", "Administrator", "Power User", "Guest" seem more like "User Types".

Also, perhaps you could make the things you are calling "AccountPermissions" be a subclass of Account. This way depending on the type of the account, different permissions will apply.

Paul Fryer
+2  A: 

Here is my two sense, for what it worth.

First I would say, when you start the design of this, think of OOP and how it would apply to entities within the system. Users, User_Role, Roles, Role_Permissions, Accounts, Account_Types, Account_Type_Features, etc.

USERS: - Should be allowed to use OpenID as it is gaining traction - The option to select between an ID or UUID for database portability

USER ROLES: (not ACCOUNT TYPE ROLES) I would encourage you to be very specific here. For example, where do you draw the line between power user and administrator? What's the difference between ADMIN and OWNER? As long as these are clearly defined (and not blurry), then it will work. If there is any question amongst your user base, pretty soon you will have a convoluted set of roles and permissions. I would keep this to a minimum to keep things clean. Users will figure out how to work with what they are given. In addition, I would change this to USER TYPE ROLES. The roles should apply to the user, not the account.

ROLE PERMISSIONS: (not ACCOUNT TYPE PERMISSIONS) This should be changed to ROLE PERMISSIONS. The permissions are extended to a users role, not an account or a user. In my experience the more clear the design, the less room for confusion down the road. Also, avoid ACL like the plague. Make it a simple one-to-one relationship. I have yet to find a reason to implement ACL for any web based system. Other permission based systems are much easier to understand, maintain, and use. There is no sense in complicating the matter.

ACCOUNT TYPE FEATURES: Be careful not to obscure the Account Type Permissions and Account Type Features. Your first bullet point uses the word permissions. Change it to features. The account type will activate more advanced / premium features (not permissions).

Permission Management: For a stateless application running on the web, sessions are the way to go. The advantage is no roundtrips to the DB to constantly check if a user is authorized.

Permission::require() should follow the same parameter definitions as the Sessions though. This will prevent overlap of other subset of permissions. So the call would be something like Permission::require('User Management', 'Add User'); This means it would look for $_SESSION['All Permissions']['User Management']['Add User'] This will prevent ambiguity.

Remember SIMPLE is BETTER.

cdburgess
+1  A: 

I advise you to look into the Zend_Acl from Zend Framework. Like most Zend packages it has a steep learning curve. But when you fully grasp the resource, action, role relationship it becomes a verry versatile and powerfull foundation for your own ACL implementations.

Do some research into existing ACL packages and patterns.

jpluijmers