On Tue, 13 Nov 2001 04:28, you wrote:
> I need to make an Apache module (not a Registry script) which will:
>
> 1. Check for a cookie, and if not there, pushhandler to a module for
> logging in (keeping the original request at hand for use after they
> succeed in logging in).
> 2. Extract data from the cookie (encrypted for security?)
> 3. Based on data from both the query string ($r->args?) and from data in
> the cookie, pushhandler to another module.
>
> I have looked at Apache::AuthCookie - it didn't seem to make much sense
> to me.  Apache::Session looks promising, but the instructions don't say
> how to set up the mysql tables, etc. My worst problem is that I haven't
> had occasion to deal with cookies much (setting, checking,etc.) in the
> past and I know this is hampering my understanding.

You should definately be using Auth::Cookie for this.  I would give the docs 
another read.  It took me a couple of tries to get it working successfully, 
but I have implemented it in about 4 or 5 applications, and it works 
flawlessly.

With Auth::Cookie it will handle the cookies for you, it will do the 
redirection to your login page for you, and it can handle logouts as well (by 
expiring the cookie).

As for Apache::Session, it is really just a keyed data store with expiry 
times.  You give it some data, and it gives you a key.  If you come back 
later and give it the same key, it gives you back your data.

A lot of people use Auth::Cookie and Apache::Session together to build a 
session management system, but I prefer just using a ticket based system with 
Auth::Cookie alone (See the Eagle book on how to do ticket based 
authentication).  I have included a sample Auth::Cookie implementation that 
might give you some ideas.  It is not complete, but it might get you going (I 
have stripped out some of the irrelevant code, so it probably won't work out 
of the box).  Also you will have to provide your own login.pl and logout.pl 
scripts (I would use the examples that come with Auth::Cookie until you get a 
working system, then you can look at building your own.

> In conclusion: I'm making a system/site where no .html files even exist.
> I need to handle security via a mysql db, and to push handlers based on
> a part of the url and a piece of the cookie which identifies the user as
> either a teacher, student, or parent (oops... I gave it away ;-)

After Auth::Cookie has finished it's phase, it will set the REMOTE_USER 
environment variable to the user that logged in.  You could just as easily 
set this to 'teacher' or whatever.  Then your content handler can look at 
this variable and decide what to do.  

HTH

Cees



package My::Auth::AuthCookie;

require 5.000;
use strict;
use Apache::AuthCookie;
use My::Crypt;

@My::Auth::AuthCookie::ISA = qw(Apache::AuthCookie);

#
##################################################################################################################

# There are far more secure ways of handling the passphrase then keeping it in
# the actual code, but this is by far the easiest...
sub SECRET () { 'No one will ever guess this passphrase :)'; }

my $CRYPTER ||= new My::Crypt;  # This utility just uses the Crypt::CBC and
                             # Storable modules to encrypt and decrypt perl
                             # data structures. It also does an MD5 checksum

sub authen_cred ($$\@) {
        my $self  = shift;
        my $r     = shift;
        my @creds = @_;

        #
        # get the entered details
        #
        my $email    = $creds[0];
        my $password = $creds[1];

        #
        # ensure that the user is valid and authorized...
        # right now it accepts any email and password combo,
        # so obviously you would check this in a database or
        # something
        #
        if ($email && $password) {
                return $self->makeTicket($r, $email);
        }
        else {
                return;
        }
}

sub authen_ses_key ($$$) {
        my $self   = shift;
        my $r      = shift;
        my $ticket = shift;

        #
        # verify the ticket (to make sure no-one has tampered with it and it hasn't 
expired etc...)
        #
        my ($result, $message) = $self->verifyTicket($r, $ticket);

        #
        # Check the result and act appropriately...
        #
        if (!$result) {
                # $r->log->error("Browser returned bad cookie ($message)");
                return undef;
        }
        else {
                #
                # Make and set a new cookie (so that the 'time' in the cookie is 
updated 
and expires
                # works as expected - since last access etc...)
                #
                my $new_ticket = $self->makeTicket($r, $message);
                $self->send_cookie($new_ticket);

                #
                # Return the 'message/string' which will be set in the environment
                # under REMOTE_USER (aka.. $ENV{REMOTE_USER}).  In our case that is
                # the users email address which we pulled out of the ticket
                #
                return $message;
        }
}

####################################
#
# makeTicket
#

# Generate a cookie/session_id/ticket for a user that has just logged in
#   borrowed from the Eagle book (by Lincoln Stein & Doug MacEachern)

sub makeTicket {
        my $self  = shift;
        my $r     = shift;
        my $email = shift;

        my $ip      = $r->connection->remote_ip;
        my $expires = 30;  # The allowed amount of inactivity in minutes
        my $time    = time;

        my $ticket = {
                  time    => $time,
                  expires => $expires,
                  email   => $email,
                  ip      => $ip,
        };

        #
        # return an encrypted version of the ticket
        #
        return $CRYPTER->encrypt($ticket);
}

####################################
#
# verifyTicket
#

# Verify that the (already logged in user's) cookie/session_id/ticket is valid

#
# Errors
# If there were any errors in verification then one of the following errors 
will
# be set and can be used in the template:
#
# * ticket_invalid    - The ticket could not be decrypted - might have been 
fiddled with by user? or maybe the
#                       'secret' on our side has changed?
# * ticket_expired    - The ticket has expired (aka - the session has 
expired!)
# * ticket_invalid_ip - The connection was made form a different IP to the 
one that the cookie was intially sent to
#

sub verifyTicket {
        my $self        = shift;
        my $r           = shift;
        my $user_ticket = shift;
        my $ticket      = {};

        #
        # decrypt the ticket
        #
        $ticket = eval { $CRYPTER->decrypt($user_ticket); };

        #
        # ensure the ticket is valid
        #
        if ($@) {
                $r->subprocess_env(MyAuthCookieReason => 'ticket_invalid');
                return (0, 'invalid ticket');
        }
        elsif ((time - $ticket->{time}) > $ticket->{expires}) {
                $r->subprocess_env(MyAuthCookieReason => 'ticket_expired');
                return (0, 'ticket has expired');
        }
        elsif (! $ticket->{ip} eq $r->connection->remote_ip) {
                $r->subprocess_env(MyAuthCookieReason => 'ticket_invalid_ip');
                return (0, 'ticket has different ip');
        }

        return (1, $ticket->{email});
}

1;

Reply via email to