ClearPress Framework


As of r327 ClearPress has shipped with modules in the ClearPress::authenticator:: namespace. These are helpers designed to help authenticate users using various different mechanisms. At the time of writing there are four mechanisms:
principally for MySQL-backed accounts
for LDAP and Active Directory
for /etc/passwd (and NIS) support
for cookie-based sessions
db, ldap and passwd verify username and password credentials whilst verifies encrypted cookies from web-requests.

For simplicity let's assume the authentication procedure goes a little like this:

  1. Registered user "U" comes to app
  2. U enters username and password and submits login form
  3. username and password are checked in database (
  4. app serves welcome page with encrypted session cookie containing username+password
  5. Further page requests from U return the session cookie
  6. Each further page request decodes the session cookie and uses the username contained within (

As well as the authenticator modules there are several other components required:

  1. login page
  2. logout page
  3. a modified for cookie handling
  4. a 'user' data-model object
  5. a modified for building information about the requestor (the 'user' data model corresponding to the authenticated user)

login #!/usr/bin/env perl -T use strict; use warnings; use myapp::decor qw($AUTH_COOKIE $TOPSECRETKEY); use ClearPress::authenticator::session; use ClearPress::authenticator::db; main(); exit; sub main { my $decor = myapp::decor->new(); my $cgi = $decor->cgi(); my $username = $cgi->param('cred_0'); my $password = $cgi->param('cred_1'); if(!$username || !$password) { ######### # force a logout # my $cookie = $cgi->cookie( -name => $AUTH_COOKIE, -value => q[], -expires => '-1d', ); $decor->username(q[]); print qq[Set-Cookie: $cookie\n]; print $decor->header(); print login_form($decor); print $decor->footer(); return 1; } my $db = ClearPress::authenticator::db->new(); my $user_info = $db->authen_credentials({ username => $username, password => $password, }); if($user_info) { $decor->username($username); my $session = ClearPress::authenticator::session->new({key => $TOPSECRETKEY}); my $encoded = $session->encode_token($user_info); my $auth_cookie = $cgi->cookie( -name => $AUTH_COOKIE, -value => $encoded, ); print qq[Set-Cookie: $auth_cookie\n]; print $decor->header(); print qq[<h2>Welcome back, $username</h2>]; print $decor->footer(); return 1; } ######### # force a logout # my $cookie = $cgi->cookie( -name => $AUTH_COOKIE, -value => q[], -expires => '-1d', ); $decor->username(q[]); print qq[Set-Cookie: $cookie\n]; print $decor->header(); print q[<h2>Login failed. Please try again</h2>]; print login_form($decor); print $decor->footer(); return; } sub login_form { my $decor = shift; return <<'EOT'; <h2>Login with a registered account</h2> <fieldset> <legend>Login</legend> <form method="post" action="$ENV{SCRIPT_NAME}"> <p> <label for="cred_0">Email address</label> <input type="text" name="cred_0" id="cred_0"/> </p> <p> <label for="cred_1">Password</label> <input type="password" name="cred_1" id="cred_1"/> </p> <p class="actions"><input type="submit" value="Log in"/></p> </form> </fieldset> EOT }
logout #!/usr/bin/perl -T use strict; use warnings; use myapp::decor qw($AUTH_COOKIE); my $decor = myapp::decor->new(); my $cgi = $decor->cgi(); my $cookie = $cgi->cookie( -name => $AUTH_COOKIE, -value => q[], -expires => '-1d', ); $decor->username(q[]); print qq[Set-Cookie: $cookie\n]; print $decor->header(); print q[<h2>Thanks for visiting, come again soon!</h2>]; print $decor->footer();

Strictly this should inherit from but at the time of writing it's not quite ready yet

package myapp::decor; use strict; use warnings; use base qw(Exporter ClearPress::decorator); use ClearPress::authenticator::session; use Readonly; Readonly::Scalar our $AUTH_COOKIE => 'myapp_sso'; Readonly::Scalar our $TOPSECRETKEY => 'top_secret_key_goes_here'; our @EXPORT_OK = qw($AUTH_COOKIE $TOPSECRETKEY); sub username { my ($self, $username) = @_; if(defined $username) { $self->{username} = $username; } if(defined $self->{username}) { return $self->{username}; } my $auth = ClearPress::authenticator::session->new({ key => $TOPSECRETKEY, }); my $cgi = $self->cgi(); my $cookie = $cgi->cookie($AUTH_COOKIE); if(!$cookie) { ######### # no auth cookie. don't bother trying to decrypt # return; } my $ref = $auth->authen_token($cookie); if(!$ref) { ######### # Failed to authenticate session token # return; } return $ref->{username}; } 1;
myapp/model/ package myapp::model::user; use strict; use warnings; use base qw(ClearPress::model); __PACKAGE__->mk_accessors(fields()); sub fields { return qw(id_user username realname pass); } sub secondary_key { ######### # support load-by-username as well as the default load-by-id # return 'username'; } 1;
myapp/ (partial)

There can often be many 'use' statements at the top of the - omitted here for clarity

package myapp::controller; use strict; use warnings; use base qw(ClearPress::controller); sub decorator { my ($self, $util) = @_; my $decor = myapp::decor->new({ stylesheet => [qw(/myapp.css)], }); my $requestor = myapp::model::user->new({ util => $util, username => $decor->username, }); $util->requestor($requestor); return $decor; } 1;