Beefy Boxes and Bandwidth Generously Provided by pair Networks
Syntactic Confectionery Delight
 
PerlMonks  

Application Access Control

by notsoevil (Pilgrim)
on Sep 24, 2001 at 03:58 UTC ( #114222=perlquestion: print w/replies, xml ) Need Help??

notsoevil has asked for the wisdom of the Perl Monks concerning the following question:

This is not so much directly related to Perl as it is application development (save that my applications, mentioned soon, are entirely written in Perl). Now that the 'disclaimer' is there, I'll begin:

I have a suite of web-based applications, all performing different tasks of allowing the creation, editing or deletion of 'documents' among other things. Documents is in quotes because all information is really stored in databases and only in metaphor are they actually documents to the end user.

Right now the permission system is patched together -- okay, let me move some code here, change something there, put his name in the code directly over yonder and such. Horrible, I know I should be flogged but a lot of the code was inherited so to speak.

I am re-developing all of the scripts from the ground up and the first thing to change needs to be the access control. But I cannot quite put my finger on a good system or how to implement it. Ultimately, each user should have 'read', 'write', 'edit', 'delete' and/or 'execute' privleges on a number of the applications or documents.

But how to implement, store/retreive permissions in an efficient manner? XML Access Control lists? An application table, detailing who has permissions (comma separated user ids or limited columns?)? User table detailing which applications/documents each individual can access?

So, while I am tackling this issue myself, I am curious as to if and how any of my fellow monks have faced similiar situations.

--
notsoevil
--
Jeremiah 49:32 - And their camels shall be a booty. . .

Replies are listed 'Best First'.
Re (tilly) 1: Application Access Control
by tilly (Archbishop) on Sep 24, 2001 at 07:28 UTC
    Essentially your problem comes down to choosing an implementation of a permissions model. If you find a model that works for your system, then work within that model.

    There are two basic approaches to a permission model that are worth considering. The first is the access control list. In that style of permission system you have concrete lists of information from which you compute whether a given permission exists. The second is the capability model. If you can name the action, you have permission to do it. But you can't name the action without having some existing permission.

    A good example of an access control list approach is Unix. Here is that example in detail. Each person belongs to a list of groups. Each item in your system belongs to a user and a group. Each item gets a set of permissions for its owner, permissions for its group, and permissions for the general public. To see if you have a permission, check whether you are the owner, if so you get the owner's permissions. If you are not the owner, see if you belong to the group that owns it, if so you get the group's permissions. Otherwise you get the permissions that the general public has.

    Access control lists don't have to be done that exact way. Personally I would tend to implement an access control list for a small application by having the lists be little join tables, and picking a permissioning model that can be modelled as a few joins. Access control lists have a lot to say for them. They are simple to explain, fairly easy to implement, and (at least at first) easy for people to understand.

    Capability models take a little adjusting to get used to, but they have many advantages. Much about what they are, how they compare to access control lists, and some notes on how to implement them can be found in the introductory essays for the EROS operating system. (Which is a capability based operating system.) But still a simpler explanation seems warranted.

    A capability permission model is very similar to an OO model of programming. You have opaque capabilities (think objects) through which you can make requests (think method calls). Without the capability (object) there is no way to make the request. In order to get the capability, you need to ask for it from a constructor. That constructor will look at the permission that it was handed and make its own decision about whether or not to give you that capability. The actual permission system is implicit in the constructor.

    There are many advantages to a capability system. The first is that if your overall architecture is already OO, the capability model of doing things maps well into how it is already structured. You can essentially add a security system for negligable energy spent in checks. Another advantage to a capability system is that it is much more flexible and finer grained. If a specific subsystem needs to have a more detailed permission system than you originally planned for, it can. Very easily.

    But the big win is what is called the Confused Deputy Problem. In this problem, think of the permission system as a deputy, constantly regulating what you can do and why you can do it. Now you may have multiple different reasons for why you should have permission to do a particular action. For instance you may have write permission for some control files because you need to be able to communicate between different sessions. You may have write access to project files because they are projects you are working on. And now what happens if the user asks you to treat a control file as a project file? Well they ask the program to do it, the program did not check and so asks the OS to do it, the OS looks, finds that you can (because it is something you need to do for internal needs), and goes ahead and wipes them out.

    Oops.

    The cause of the problem is that your deputy who is keeping track of what you can do, has not kept track of why you are allowed to do it. A request issued for one reason is granted because of a permission issued for an unrelated reason, and something that shouldn't happen does. But this doesn't happen in a capability system! Why not? Well in a capability system if you need to have different sets of permissions for two kinds of actions, you issue capabilities for each kind of action. Now when you ask to exercise your capabilities to handle project files to write to a control file, you fail. It does not matter that your program is theoretically capable of the action, it figures out that users should not be deleting your control files and won't let it happen.

    Now I am a person with a lot of math background. One thing that background has taught me is that patterns tend to repeat. Often they will repeat in places that look different and have different names attached. But find the connections between them and you will learn a lot.

    And so it is with the difference between ACLs and capability systems. Fundamentally any ACL permission is based on global data. Outside lists, and a small bit of global data. The best scoping you can do, and even that takes work, is to do like suid programs do and switch between a few global roles. By contrast any capability system permissions based on private data which is properly scoped and passed into the permission decisions. The confused deputy problem is just the classic problem with global variables, as it manifests in a permission problem. Changes to global data done for one reason interact with actions in an unrelated part of your program, leading to some form of grief. Properly scope your permission to just the right set of requests, and the problem goes away.

    So as an ideal, a capability system is better than an ACL system. It also happens to fit extremely well with an OO system (just name your objects "capabilities" and hide all of the permission decisions behind method calls). However if you don't have a heavily OO system, then a capability system is probably more work than it is worth to implement. But be aware that there are types of problems you will have to live with in an ACL system that capability systems avoid, and there are problems which capability systems can solve that ACL systems simply cannot. (Section 4.2 of this paper gives a number of examples, along with explanations of how to solve them in a capability system.)

Re: Application Access Control
by CubicSpline (Friar) on Sep 24, 2001 at 04:25 UTC
    This is something that i've had to face in some of my applications too. And the only answer i can give is "it depends." There are a few questions that might help you to get to thinking about approaching the problem.

    • How do you anticipate doling out the privileges? Is it a situation where each user will fall into a category of user that will require access to only a certain subset of applications? This kind of scheme would be easily implemented in database tables (a scheme i've used often).
    • Conversely, is every user going to have unique set of applications which they can access? If so, maybe some kind of centralized file could be used that keeps tabs of whether or not each user can access each application ( could just be 0 or 1 to indicate denied or allowed).
    • Is it necessary to hide the applications from all users, or can the applications themselves do user authentication? In other words, is a centralized authentication system necessary?
    Hopefully something useful to think about here.
Re: Application Access Control
by merlyn (Sage) on Sep 24, 2001 at 04:47 UTC
(Ovid) Re: Application Access Control
by Ovid (Cardinal) on Sep 24, 2001 at 05:32 UTC

    For one application, I created a permission system that restricts access per user per section. I designed a database schema that was comprised of six tables. They're as follows (with many fields eliminated for clarity):

    users ------------ userID userCompany userAllCompanyPerms permissions ------------ userID subSectionID permissionTypeID permission (bit) permissionType ------------ permissionTypeID permissionType (Add, Edit, Delete, View) section ------------ sectionID sectionName subSection ------------ subSectionID sectionID subSectionName permissionTypeSubSection ------------ permissionTypeID subSectionID

    Of course, you'd need appropriate foreign key constraints and a several other details that I've left out. To get permissions for a user for a Section, I'd use the following SQL:

    SELECT sb.subSectionID, pt.PermissionTypeID, p.Permission FROM permissions p INNER JOIN SubSection sb ON p.subSectionID = sb.subSectionID INNER JOIN PermissionTypes pt ON p.permissionTypeID = pt.permissionTyp +eID INNER JOIN Section st ON st.SectionID = sb.SectionID WHERE (p.userID = $userID)

    To be perfectly fair, I've simplified this quite a bit and I munged it to change some features that I really don't care to share. If it's not entirely accurate, it's because I did that on the fly. Of course, you'll want to use $dbh->quote or a placeholder for the userID or else you wind up with a huge hole in your security.

    Basically, for the "Corporate" section, you may have subsections such as "Company", "Contacts", "Branch Offices", etc. This schema allows me to individually control all Add, Edit, Delete, and View permissions for each subsection.

    One change I'm planning on making to this in the future: I want to change the permission in the permission table to an integer, with the values of -1, 0 and 1. The benefit of this will be in group creation. You can add a couple of tables, add some fields to the above tables and then assign permissions to a group. Group permission will only be 0 or 1. When you add a user to a group, they inherit the permissions, but have none explicitly set on their own. However, you go to the users permission screen and, if you want to add a permission the group doesn't have, check the permission and their permission is set as a 1. If you don't want them to have the group permission, uncheck the permission and their permission is set as a -1. Later, when determining their actual permissions, you add their personal permissions to the group permission and they only have a particular permission if their sum permission total is greater than zero. This allows you to inherit group permissions, yet still customize their individual permissions any way you wish.

    Cheers,
    Ovid

    Vote for paco!

    Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

      Thanks for posting these snippets. They've moved my own thinking on this subject along....

      Ovid wrote here:
      One change I'm planning on making to this in the future: I want to change the permission in the permission table to an integer, with the values of -1, 0 and 1. The benefit of this will be in group creation.

      I may not understand your algorithm. You might run into problems if somebody is in multiple groups with a permission granted.

      01 group one 01 group two -1 user ---- 1
      update: added attribution to quote from Ovid



      --mandog

        mandog wrote:

        You might run into problems if somebody is in multiple groups with a permission granted.

        That's a good point. In this case, for the application I'm going to port it to, I don't mean "groups" in the Unix sense. I mean "employee type". Users of the system will be a particular type of employee, so it's a one-to-many relationship, rather than the many-to-many scenario you can have on Unix. If I needed to go that route, I would probably do a bitwise OR on the group permissions and then add the user permission.

        perl -e 'print ((1 | 1) - 1)'

        That will print 0 (zero). However, at that point, it's probably better to move that process into a stored procedure, rather than calculate it in Perl.

        Cheers,
        Ovid

        Vote for paco!

        Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

Re: Application Access Control
by ducky (Scribe) on Sep 24, 2001 at 04:53 UTC

    I've had to come up with access control mechanisms before and the only one I've been happy with makes extensive use of a database. Unfortunately, I haven't gotten around to making a decent module out of it, though it's been on my to-do list for quite a while, because the methods were written as HTML::Mason components and I just re-use the component rather than do the Right thing.

    Essentially, it's a system of users, groups and access entities. Everything's assigned an entity. According to memberships between the users<->groups<->entities or users<->entities access is decided. Users explicitly granted or denied to the entity takes priority over users assigned to a group which then have permission granted or denied and the default permission takes effect if the other two come up emtpy.

    It's a bit of a grand system, quite overkill for almost everything I've used it for, but I like it. =) If you'd like table definitions and SQL queries that drive it, I'd be happy to cough those up, just me know.

    -Ducky

    Update:Check this for a mess of SQL statements to create and query what I talked about above. So I coughed. There. =)

    Update II: Since I've gotten some interest in these, here's a quick overview of the tables:

    users ---------- user_id auto-incrementing, primary key name text, for human consumtion entities ---------- entity_id auto-incrementing, primary key name text, for human consumtion permission boolean, default permission groups ---------- group_id auto-incrementing, primary key name text, for human consumtion priority int, optional. to give one group priority over another group/entity membership ---------- group_id int, references groups table entity_id int, references entities table permission boolean, this group's permission on the entity user/entity membership ---------- user_id int, references users table entity_id int, references entities table permission boolean, this user's permission on the entity user/group membership ---------- user_id int, references users table entity_id int, references entities table
    The ugly SQL to pull all these memberships together and determine a user's permission to a given entity based on what groups they're in and what permission has been assigned to them is available here (same link as above)

    Update III: yeesh. Updated the link to my site.

Re: Application Access Control
by jeorgen (Pilgrim) on Sep 24, 2001 at 16:44 UTC
    There are a number of ways of implementing users and groups in Perl scripts. By using Lincoln Stein's user_manage you can tie it into Apache's authentication system, and use .htpasswd files or a database as a backend, among other options. In this way you get the following benefits:

    1) You get one user management system valid both for cgi<->database scripts and html files (with .htaccess files in the protected directories for html files)

    2) You get a ready-made management user interface in user_manage to manage users and groups, user_manage also allows you to store more items for each user than name and passwd.

    Here is some code, taken largely from the POD, that implements an authentication system with Apache's htpasswd system in a perl script. (I'm working on it right now so /msg me if you want some session handling code, how to get groups, etc..).

    #!/usr/bin/perl use HTTPD::Authen; print qq§Content-type: text/plain\n\n§; $auth = new HTTPD::Authen(DBType=>"Text",DB=>"/path/to/passwd_file/use +r_manage/users/passwd", Server=>"Apache"); $authen = new HTTPD::Authen::Basic($auth); if($authen->check("jeorgen", "foo")) { print "Well, the passwords match at least\n"; } else { print "Password mismatch! Intruder alert! Intruder alert!\n"; }

    /jeorgen

Re: Application Access Control
by Nitsuj (Hermit) on Sep 24, 2001 at 18:49 UTC
    2 solutions grab me as being correct.
    1) Create user groups and permissions in the database using the databases permissions set up... This is of course given that all of the users are "users" in the database. My guess though, is that the script is the only user.

    2) Use binary data permissions and groups like in unix. You can have a column for each group if that seems necessary/prudent and binary permissions related to the "file"/executable. Groups could be established in a separate table by over user/group. This also obviously has no problems if a user needs to belong to multiple groups.

    Just Another Perl Backpacker
Re: Application Access Control
by dga (Hermit) on Sep 24, 2001 at 20:33 UTC

    In addition to the excellent information here, I have one thing to add about my implentation which allows some future flexibility.

    I have used a bit model i.e. 1, 0 type of system for can they read, write, run etc. but I added another field in the table which is plain text which can contain special permissions.

    This field is read by the applications which provide some access other than the simple common stuff when a relevant type of request comes in. We have an online MOTD type deal, and the people who can post to that have the text "news" in their special access field.

    The upside is that most programs only look at the read/write which are integers and can be gotten really quickly but the news authoring program looks at the text field when someone requests the article authoring screen or sends in an article. This allows a new program to have a new capcbility added in this field but only those program which need the special features ask and only when a special feature is actually requested by the user.

Re: Application Access Control
by mcarthur (Initiate) on Oct 08, 2001 at 09:47 UTC
    You may like to look at role based access control as another option. (This may be similar to the capability comment above - it sounds close but I'm not sure).

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://114222]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others surveying the Monastery: (3)
As of 2020-02-25 01:55 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    What numbers are you going to focus on primarily in 2020?










    Results (108 votes). Check out past polls.

    Notices?