[cgiapp] Security, Authentication and Authorization for CGI::App

Mark Rajcok mrajcok at gmail.com
Fri Mar 5 11:53:37 EST 2010


On Thu, Mar 4, 2010 at 5:56 PM, Michael Peters <mpeters at plusthree.com>wrote:

> On 03/04/2010 10:56 AM, Brad Van Sickle wrote:
> > 2) Runmode authorization is a little trickier, but still manageable.  I
> > check the user's session in prerun and if they are not authenticated,
> > redirect to a "not authorized" runmode that redirects them back to the
> > security app (login page) Any module that needs to set security on
> > specific runmodes, inherits prerun and does "if" checks against the
> > runmode/usertype to make sure they are authorized.  Anyone trying to go
> > somewhere they shouldn't is redirected to that same "not authorized"
> > runmode.
>
> I usually generalize this by putting my prerun checking in my base class
> and each sub class tells the base class which run modes are restricted
> to which roles. Sometimes I make this a method that child classes
> override or sometimes I make it a parameter that's passed to new() in
> either a startup script or a C::A::Dispatch table.
>

Hi Brad, great discussion topic.

I recently wrote an Authorization Plugin (it is on my todo list to get it up
on CPAN) to handle "runmode authorization".  It uses attribute handles to
specify which runmodes require authorization:

sub subclass_method_x  :Authz(administrator) {
 # no authorization logic here
 ...
}
sub authz_administrator {
  # authorization logic here
}


The plugin adds a prerun hook such that before subclass_method_x is called,
method authz_administrator() will now automatically get called ("authz_"
gets prepended to the attribute handle data to form the authorization method
name).  I normally define authz_administrator() in the same (sub)class as
subclass_method_x().
If authz_administrator() determines the user is not authorized, the runmode
is changed to authz_error() -- which I define in the base class, since I
normally don't need custom authorization error messages.  However, if a
custom error is needed, the authz_error() method can be overridden in the
subclass.

I like this approach for a few reasons:

1. subclass_method_x() is clearly marked that it requires authorization, and
which type of authorization.  I prefer this over something like
CAP::Authorization's authz_runmodes() concept.  If I want to change how
authorization works for a method, I simply modify the method signature (I
don't have to scroll up to the setup() method and modify a call to
authz_runmodes()).

2. subclass_method_x() doesn't have any authorization checking code --
that's in another "authorization method", and multiple runmodes can call
that same "authorization method" when the same authorization logic is needed

3. I don't have to preconfigure the authorization mechanism in a base class
(which is the typical CAP::Authorization model).  In fact, the base class
doesn't have to know anything about authorization -- only the subclass where
the authorization is needed has to know.  So for instance, in my
AdminUserManager subclass, there are authorization methods for checking
different admin privileges.  That's the only subclass that has this logic.
If I need to change how admin authorization works, the runmodes and the
authorization logic are all in the same file.  I really like that.  (But, I
guess it depends on how you view authorization... if you view it as cutting
across your application, then the base class might then be preferable.)

4. I can be more efficient and save the results of database queries
performed in authz_administrator() for later use in subclass_method_x().
This was one of the main reasons I decided to write an alternative to
CAP::Authorization.  With CAP::Authorization, database hits were being made
to authorize, then I would make additional database hits in my runmode to
obtain more information about the user.  For example, in
authz_administrator() I often grab the user's name out of the database at
the same time I'm pulling out user role/privilege info.  I create and save a
User object for later use in the runmodes.  (I just replied to Nicholas,
where I requested a similar capability for CAP::Authentication -- then only
one DB hit would be needed for Auth, Authz, and some other common user
info.)

As for data authorization (e.g., filtering select queries based on user
roles), I don't have any good solutions.  I dynamically add WHERE clauses
(or dynamically modify the SQL statement appropriately) like you, or I
filter the data after getting back query results.

-- Mark R.


More information about the cgiapp mailing list