Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

RFC: Authentication/Authorization System

by eric256 (Parson)
on Jul 19, 2006 at 14:06 UTC ( #562370=perlmeditation: print w/replies, xml ) Need Help??

Okay so I'm tired of rewriting user managment code, and a usermanagment backend. It is fun the first time, interesting the second, but beyond that it just gets old. So I want to help/start/assist/poke the design of a bolt-on Authorization system. Basicaly I'm looking for a way to bolt authorization and authentication abilities onto existing scripts easily and provide a nice easy user managment solution that can be used to jump start projects. Sometimes i have projects that I want, but the first step always involves getting user managment/registration out of the way and often I stall out in that process before getting a product out. So this module/script/whatever should be easy to bolt on, easy to start with, and easy to enhance/subclass later on if the app takes off and needs something more robust.

Hopefully that gets the target out there, so we are clear. This is not the be all end all, just something to use easily to enhance old scripts, and jumpstart new projects.

So I decided that the easiest place to start would probably be the interface. I don't care how it does what it does, as long as it provides these features. There should be two parts (devided logical/literaly/however). One is the part a script would use to determine if a user is currently logged in and if so what roles that person has.

#In the application or scripts use My::Authentication; # Loads user information, logs users in and out, controls cookies my $user = My::Authentication::load(); # require a user to be an admin, or give them an "Access denied page" $user->must('admin'); # require a user to be an admin or redirect them to the login page $user->must_or_login('admin'); # require a user to be an admin or redirect them to a specific page $user->must_or_redirect('admin', '/login.html'); #test a users roles print "You can 'dance'\n" if $user->can('dance'); print "You can't 'flip'\n" unless $user->can('flip'); #give the user a role $user->add_role('dance'); # now they can dance $user->del_role('dance'); # now they can't #allow user administration. (for registration etc) My::Authentication::add_user($username, $password, { #hash to store da +ta }, [ roles ]); My::Authentication::del_user($username); #allow role modification of arbitraty users # (not sure why but it seems prudent to let the program do what they +want.) My::Authentication::user_add_role($username, [roles]); My::Authentication::user_del_role($username, [roles]); #roles must be registered to avoid typos My::Authentication::add_role([roles]); My::Authentication::rem_role([roles]);

This interface gives the main app some abilities to manage users (if they want to build their own user management) but hides most the dirty icky stuff away (sessions, logins, logouts, roles, etc).

In addition to that, a user would setup an admin script that invokes a different part of the module which is actualy an entire web based user managment system

#in an user_admin.pl script use My::Authentication; My::Authentication::administer(); # Then the script would handler EVERYTHING ELSE # (list users, allow editing, updating, deleting, roles etc.)

So that looks pretty easy from a programmer stand point. You subclass the main module and in that subclass you store all the relevant info and choices. Then your scripts just load that subclass.

So I'm here, because before I begin to hammer out an implementation I wanted to get a feel for what people think. Would you use something like this? What would it need for you to use it? What should it focus on? What should it pass on to the programmer? What do you think it could be named?

So let the lead fly but be nice, please! ;) Any and all ideas, suggestions, pointers to existing solutions, etc, welcome. Do remember this isn't meant to be an Application framework at all, rather something much smaller and simpler, that could be used until you decide you want to go whole hog with one of the bigger full featured frameworks.


___________
Eric Hodges

Replies are listed 'Best First'.
Re: RFC: Authentication/Authorization System
by rhesa (Vicar) on Jul 19, 2006 at 15:11 UTC
    I can see the value of this. It's the reverse of most of the Auth/Authz modules on CPAN, which actually perform authentication against a pre-existing store. Having something to manage that data seems like a useful thing to have.

    Some quick notes:

    • I noticed Authen::Users, which seems like the only existing attempt at this
    • I'd separate Authentication and Authorization if I were you (think /etc/passwd and /etc/group)
    • I wouldn't bother with performing authentication; Authen::Simple is a nice API for that, and there are hundreds of other modules and frameworks that already provide that service. Restrict yourself to managing the auth data, because that seems to be the one thing that's still missing on CPAN

    At first, I had some reservations against must(), and can(), but if you do drop performing auth, then those would be moot :) If you do want to implement it, I'd suggest must_have_role() and has_role() (or maybe may() or something like that, but can() is already used for OO purposes, and carries too much of that meaning).

    Bottom line: I'd like to see a nice framework for user and role data management. I don't know if it'll be easy for you to make it such that it can target PAM, SASL, Kerberos, Active Directory etc., but I bet there's demand for such a beast.

    Update: I rummaged around CPAN some more, and found Aut. Seems fairly similar to your scope. There's also Tree::Authz, which looks fairly comprehensive as well. If you haven't done so yet (but I bet you have), I recommend searching CPAN for "authentication" and/or "authorization". There's a lot to wade through, but that'll give you a good idea what's out there, and what's still missing.

      Good ideas. I just want to make sure I don't run into the "too many options" problem while trying to get around it. Basicaly I just want a place I can start, and then link to the other modules that do better or have better options. In addition I'm not sure how i'd go about making a program dynamic enough to be able to write to all of those different authentication systems. So I was hoping to make an Authentication/Authorization System in a box, then point people to more powerfull solutions that could be customized to different needs. Does that make more sense? Kinda user managment with training wheels, i'd fully expect people to ditch the training wheels eventualy, but some projects never get off the ground enough to worry about that. At least thats been my experience, which is why i'm meditating on this, to see if i'm alone or if others have similar experiences. ;)


      ___________
      Eric Hodges
        I can sympathise with you not wanting to aim too high just yet :)

        The typical solution to talking to different backends is usually by adding a driver layer (similar to the DBD::* modules for DBI). I've been browsing through the source for Authen::Simple, and the basic design looks very good. It uses Adaptor classes which implement the backend-store specific code behind the public, common API. It may be too abstract for your needs, but I think I can recommend it. It's interesting and educational at least :)

Re: RFC: Authentication/Authorization System
by bart (Canon) on Jul 20, 2006 at 03:31 UTC
    Just a few minor remarks...

    First of all: don't use functions to create objects. Use a class method. It's much more consistent, and I'm sure, more easy to implement — why import constructors into your subclass?

    So: change

    # Loads user information, logs users in and out, controls cookies my $user = My::Authentication::load();
    to
    # Loads user information, logs users in and out, controls cookies my $user = My::Authentication->load;

    More examples:

    #allow user administration. (for registration etc) My::Authentication::add_user($username, $password, { #hash to store da +ta }, [ roles ]); My::Authentication::del_user($username);
    should be
    #allow user administration. (for registration etc) My::Authentication->add_user($username, $password, { #hash to store da +ta }, [ roles ]); My::Authentication->del_user($username);
    Or you can split it up:
    #allow user administration. (for registration etc) my $suspect = My::Authentication->add_user($username, $password, { #ha +sh to store data }); $suspect->add_roles(roles);

    Second: I think you're having too many similar functions with related names. I prefer overloading. I think the default for require or must or whatever you call it (I prefer "require" over "must") should be to redirect to the login page, which you can optimally specify, if the user is not logged in and return a "forbidden" status if he is logged in but too low. Something like:

    # Loads user information, logs users in and out, controls cookies my $user = My::Authentication->load; # require a user to be an admin or redirect them to the login page $user->require('admin'); # require a user to be an admin or redirect them to a specific page $user->require('admin', '/login.html'); # require a user to be an admin, or give them an "Access denied page" $user->require('admin', undef);
    I think there's much less to memorize.

    Well, it could be nice if a user could "upgrade" to a more powerful user, when access is denied.

    Oh, and for the sake of a good user experience: please remember what page the user tried to access when forced to log in. I hate it when on a webforum, the damn think forgets that I intended to comment on a post when it forces me to log in first. Please make it go back to where I wanted to go in the first place.

    Well, this surely isn't the final API spec, it definitely needs some more hammering.

      Hey, Thanks for the input. Yea i started with something that looked like that, but then thought it was confusing to have one require do so much, but the way you did it makes it easy, and looks right. Redirect to undef is pretty obvious an "error" page of some sort. I like it, and other changes welcome too, thats why i shoved it up here!


      ___________
      Eric Hodges
      I would make
      $admin->add_user(...);
      so that the user who wants to add another has to login! And possibly remember which user was created from whom. But My::Authentication->add_role($role) is ok.
Re: RFC: Authentication/Authorization System
by CountZero (Bishop) on Jul 19, 2006 at 16:46 UTC
    You might get some ideas by taking a look at the Catalyst::Plugin::Authentication and Catalyst::Plugin::Authorization modules. In each category there is a basic module which provides the public interface and then a host of lower level modules which provide the actual code and interface with the various back-end stores and such. It's an elegant and flexible system, provided you get the API right (and you seem to have already given it some good thoughts).

    For the role-based authorization, the access control list approach has real merit. It ties together some resource (actually a Catalyst action with a role) and provide for automatic "You are not allowed to access this page" replies.

    So the first part of your project is already written (well at least within Catalyst's little world). Now if you could tie in your user-management to the Catalyst plugins and have it discover and service the actual back-end stores used, that would surely make it a winning module.

    CountZero

    "If you have four groups working on a compiler, you'll get a 4-pass compiler." - Conway's Law

      The more I look at this the more it seems like we need a DBI-esq (as in a general API with several backends) for querying AND maintaining user/role/group data. Authen::Simple is half way there with the query interface but it doesn't provide a way to update the backend. Then my project would be a simple application based on this Auth.* API. But then I wonder if those different backends provide ways to be updated via Perl. For instance the POP3 backend is easy to query but doesn't provide a way to update it.

      Perhaps I look more into expanding the existing modules to allow updateing of the data in a consisten manner, and then build my application/module on top of that.

      I could always start with just a single DBI based driver layer and let others expand it to other auth.* systems


      ___________
      Eric Hodges
Re: RFC: Authentication/Authorization System
by diotalevi (Canon) on Jul 19, 2006 at 16:13 UTC

    Whatever you name this, you might consider not stomping on the namespace. I assume you'll make a typical role or group based system. Someone else might come by later and make a capability based system to solve your Confused Deputy Problem and it'd be nice if there were a place in the namespace for them to live.

    ⠤⠤ ⠙⠊⠕⠞⠁⠇⠑⠧⠊

Re: RFC: Authentication/Authorization System
by reneeb (Chaplain) on Jul 20, 2006 at 02:40 UTC
    There is Authen::PAAS on CPAN which is the Perl equivalent to Javas Java Authentication and Authorization System.
    An other module is PAM - an interface to the Pluggable Authentication Model.
Re: RFC: Authentication/Authorization System
by ColinHorne (Sexton) on Jul 20, 2006 at 16:26 UTC
    I like it :-)

    I agree with everything else, but I would suggest against...
    o making it tied to CGI (or perhaps have My::Auth::CGI for that purpose).
    o forcing the user (programmer) to use the modules own authentication system (perhaps make it optional, for convenience, though) - possibly authenticate a user with $user->authenticate(1), etc - this lets the user authenticate their users however they want (and takes some load off your hands ;-) )

    That's all I can think of for now. I'm keen to see this finished :-)

Re: RFC: Authentication/Authorization System
by Discipulus (Abbot) on Jul 20, 2006 at 11:17 UTC
    wow I wait your module with open arms !!
Re: RFC: Authentication/Authorization System
by muba (Priest) on Jul 24, 2006 at 11:20 UTC
    I'm not sure whether using can is a good idea - there already is Universal::can which lets you check if a certain namespace or object instance has a method with the name you provide.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlmeditation [id://562370]
Approved by VSarkiss
Front-paged by CountZero
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others having an uproarious good time at the Monastery: (11)
As of 2019-12-11 14:47 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?