4

I'm looking for good reference on patterns that apply to user security and ACLs for multi-user network applications.

I'm re-writing a fairly large application from scratch. Now I'm looking at the user management and security. I'm not so interested in user meta data, but rather ACLs and security. Right now the application has users, groups, and roles. Users and roles have ACLs. Groups do not. A user can belong to none or any number of groups and/or roles. I cannot find a good reason not to combine roles and groups. However, I'd like to hear the various opinions on the subject.

It would be nice to be pointed to some design patterns and references on the subject.

7
  • If you're rewriting a large application from scratch, I would recommend reading this first "Things you Should Never Do" by Joel Spolsky
    – jmq
    Commented Mar 25, 2011 at 4:15
  • 3
    @jmquigley: What has that to do with security? I can point out many flaws in Joel's logic when out of context: it does not apply because there is no team, no budget, etc. I'm writing 2.0 from scratch with a bunch of new functionality that requires a re-write. So please keep on topic.
    – d-_-b
    Commented Mar 25, 2011 at 4:25
  • It has nothing to do with security, it was a suggestion, that's why it's a comment and not an answer that is voted on.
    – jmq
    Commented Mar 25, 2011 at 4:28
  • @jmquigley: I've thought about this one for a long time. I know the code base well. I am the sole author. It will be faster to re-write, and I will end up with a more flexible and robust code base that's easier to maintain.
    – d-_-b
    Commented Mar 25, 2011 at 4:36
  • Good luck to you with your project. I was just trying to be helpful.
    – jmq
    Commented Mar 25, 2011 at 4:37

2 Answers 2

5

In my experience, users, groups, and roles are fine (though groups-roles seem redundant), but what really simplifies security is the concept of privileges.

if not currentUser.canDoThat():
    raise SomethingWrongGoingOn()
else:
   # do this
   # do that

The privilege scheme can be as complex as required.

class User(object):
    def canDoThat(self):
        for group in self.groups:
            if group.has_privilege(CAN_DO_THAT):
                return True
        return False

In such a scheme code gets protected with mostly atomic privileges defined over risks and confidentiality, while the user->group->privilege scheme can be handled by layers closer to the UI, or delegated to something centralised like an LDAP/AD repository.

In short, guard the code with queries about privileges (the simplest code), publish the per-module privileges so they can be aliased and synthesised to a minimal set, and let an authentication authority decide which user has which privileges.

0
2

What is the distinction between roles and groups in your software? Very frequently the two are interchangeable, but without knowing how you handle them, I can't say whether you can merge them.

Now it sounds to me like you have already decided on an ACL based system. (If you weren't aware that this was a decision, read up on capability systems for a very different approach to security.) Your question is how to handle permissions.

Some years ago I built an interesting permissioning system that had some ideas that you may find interesting. It had some downsides as well. I'll describe it, and you can decide whether you want to borrow anything from it.

My central thinking was the following. Groups can belong to roles. Roles (and users) can have permissions. All of these permissions can be handed out on either a temporary or permanent basis. If you got the permission in multiple ways, you'd get the permission for the longest possible period of time.

First the positive. The users found it really easy to manage permissions. For instance a common activity would be to hand out a free trial to a company. To do that you took the accounts for that company, said that they belonged to the company, and then gave the company temporary membership in the free trial role. Voila! Later when we came out with a new product and we wanted to add it to what people on free trial got, they just added the new permission to the free trial and everyone got it.

Now the negative. Conceptually it was really clean. But whenever a permission was changed, I'd have to follow the logic to figure out all of the cascading permission changes. This piece of logic was slow. The users didn't care much - they just handed out permissions then did something else while the system chugged away. But it caused me headaches. Mostly it was my implementation, and partially it was inherent in how the system worked. In our system we had thousands of users with thousands of permissions each. Tracing through that mess of permissions to figure out everything that just changed was a challenge. If you have fewer users (hopefully not) or fewer permissions per user (quite likely), this will hopefully be less of an issue.

I actually implemented it in a database. Here are the necessary tables for the approach that I took:

user:
  id
  name
  ...

permission:
  id
  type
  name
  description

role:
  id
  // Only one of the following two is non-zero, and
  // there is a unique index on the pair.
  user_id
  permission_id
  name

role_permission:
  id
  role_id
  permission_id
  expires
  granting_role_id
  created
  created_by

Now how did it work? Each user had a corresponding user role. At run-time user joins to role, joins to role_permission (peeking at whether it has expired), joins to permission to get all of the user's permissions. (In practice on the website for a permission check we had both the user and the permission, and this lookup was quite fast.)

In the permission panel you could look at a user or role, and it would show all of the permissions that that user directly had, when they expired (if ever), and which role they were granted through (if not granted directly). You could also add permissions, possibly with a time period set. Many of those possible permissions tied to a permission role. (For instance "free trial".)

The fun part was the logic to track through the cascade of possible changes when a permission was changed. The idea there was that I would run a query looking for permissions that needed revoking and ones that needed adding. As long as I was changing permissions, I'd do it again. That's where I ran into performance problems.

I think that if I had to do it over again with what I know now, I could solve those performance problems much better than I did then. But that logic was surprisingly tricky to get right.

Good luck on your project. Hopefully seeing how someone else tackled a problem like this can give you ideas for how you might want to do it as well.

2
  • 1
    +1 already just for mentioning capability systems! Also look fun things like the Confused Deputy. Commented Apr 24, 2011 at 8:38
  • @Frank-shearar: I thought about giving a whole explanation of capability systems and confused deputy, but then realized that it would take a long time, and there was a good chance that it would force the questioner into a significant redesign. So I decided to merely make it clear that there were completely different approaches out there, and left it at that.
    – btilly
    Commented Apr 24, 2011 at 22:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.