[Catalyst-commits] r8583 - in trunk/Catalyst-Plugin-Authentication: lib/Catalyst/Authentication/Realm t t/lib

jshirley at dev.catalyst.perl.org jshirley at dev.catalyst.perl.org
Fri Nov 7 20:14:06 GMT 2008


Author: jshirley
Date: 2008-11-07 20:14:05 +0000 (Fri, 07 Nov 2008)
New Revision: 8583

Added:
   trunk/Catalyst-Plugin-Authentication/lib/Catalyst/Authentication/Realm/Progressive.pm
   trunk/Catalyst-Plugin-Authentication/t/lib/AuthRealmTestAppProgressive.pm
   trunk/Catalyst-Plugin-Authentication/t/live_app_realms_progressive.t
Log:
Adding a progressive realm to try multiple realms in a sequence

Added: trunk/Catalyst-Plugin-Authentication/lib/Catalyst/Authentication/Realm/Progressive.pm
===================================================================
--- trunk/Catalyst-Plugin-Authentication/lib/Catalyst/Authentication/Realm/Progressive.pm	                        (rev 0)
+++ trunk/Catalyst-Plugin-Authentication/lib/Catalyst/Authentication/Realm/Progressive.pm	2008-11-07 20:14:05 UTC (rev 8583)
@@ -0,0 +1,151 @@
+package Catalyst::Authentication::Realm::Progressive;
+
+use Carp;
+use warnings;
+use strict;
+
+use base 'Catalyst::Authentication::Realm';
+
+=head1 NAME
+
+Catalyst::Authentication::Realm::Progressive - Authenticate against multiple realms
+
+=head1 SYNOPSIS
+
+This Realm allows an application to be built so that multiple realms are 
+supported and tried incrementally until a successful authentication.
+
+A simple use case is a Temporary Password that looks and acts exactly as a 
+regular password.  Without changing the authentication code, you can 
+authenticate against multiple realms.
+
+=head2 EXAMPLE
+
+If your application has multiple realms to authenticate, such as a temporary
+password realm and a normal realm, you can configure the progressive realm as
+the default, and configure it to iteratively call the temporary realm and then
+the normal realm.
+
+ __PACKAGE__->config(
+    'Plugin::Authentication' => {
+        default_realm => 'progressive',
+        realms => {
+            progressive => {
+                class => 'Progressive',
+                realms => [ 'temp', 'normal' ],
+                # Modify the authinfo passed into authenticate by merging
+                # these hashes into the realm's authenticate call:
+                authinfo_munge => {
+                    'local'     => { 'realm' => 'normal' },
+                    'temp'      => { 'realm' => 'temp' },
+                }
+            },
+            'normal' => {
+                credential => {
+                    class => 'Password',
+                    password_field => 'secret',
+                    password_type  => 'hashed',
+                    password_hash_type => 'SHA-1',
+                },
+                store => {
+                    class      => 'DBIx::Class',
+                    user_class => 'Schema::Person::Identity',
+                    id_field   => 'id',
+                }
+            },
+            temp => {
+                credential => {
+                    class => 'Password',
+                    password_field => 'secret',
+                    password_type  => 'hashed',
+                    password_hash_type => 'SHA-1',
+                },
+                store => {
+                    class    => 'DBIx::Class',
+                    user_class => 'Schema::Person::Identity',
+                    id_field   => 'id',
+                }
+            },
+        }
+    }
+ ); 
+
+Then, in your controller code, to attempt authentication against both realms
+you just have to do a simple authenticate call:
+
+ if ( $c->authenticate({ id => $username, password => $password }) ) {
+     if ( $c->user->realm eq 'temp' ) {
+         # Force user to change password
+     }
+ }
+
+=head1 CONFIGURATION
+
+=over
+
+=item realms
+
+An array reference consisting of each realm to attempt authentication against, 
+in the order listed.  If the realm does not exist, calling authenticate will
+die.
+
+=item authinfo_munge
+
+A hash reference keyed by realm names, with values being hash references to
+merge into the authinfo call that is subsequently passed into the realm's
+authenticate method.  This is useful if your store uses the same class for each
+realm, separated by some other token (in the L<EXAMPLE> authinfo_mungesection, 
+the 'realm' is a column on C<Schema::Person::Identity> that will be either
+'temp' or 'local', to ensure the query to fetch the user finds the right
+Identity record for that realm.
+
+=back
+
+=head1 METHODS
+
+=head2 authenticate
+
+This method iteratively calls each realm listed in the C<realms> configuration
+key.  It returns after the first successful authentication call is done.
+
+=cut
+
+sub authenticate {
+    my ( $self, $c, $authinfo ) = @_;
+    my $realms = $self->config->{realms};
+    carp "No realms to authenticate against, check configuration"
+        unless $realms;
+    carp "Realms configuration must be an array reference"
+        unless ref $realms eq 'ARRAY';
+    foreach my $realm_name ( @$realms ) {
+        my $realm = $c->get_auth_realm( $realm_name );
+        carp "Unable to find realm: $realm_name, check configuration" 
+            unless $realm;
+        my $auth = { %$authinfo };
+        $auth->{realm} ||= $realm->name;
+        if ( my $info = $realm->config->{authinfo_munge}->{$realm->name} ) {
+            $auth = Catalyst::Utils::merge_hashes($auth, $info);
+        }
+        if ( my $obj = $realm->authenticate( $c, $auth ) ) {
+            $c->set_authenticated( $obj, $realm->name );
+            return $obj;
+        }
+    }
+    return;
+}
+
+=head1 AUTHORS
+
+J. Shirley C<< <jshirley at cpan.org> >>
+
+Jay Kuri C<< <jayk at cpan.org> >>
+
+=head1 COPYRIGHT & LICENSE
+
+Copyright (c) 2008 the aforementioned authors. All rights reserved. This program
+is free software; you can redistribute it and/or modify it under the same terms
+as Perl itself.
+
+=cut
+
+1;

Added: trunk/Catalyst-Plugin-Authentication/t/lib/AuthRealmTestAppProgressive.pm
===================================================================
--- trunk/Catalyst-Plugin-Authentication/t/lib/AuthRealmTestAppProgressive.pm	                        (rev 0)
+++ trunk/Catalyst-Plugin-Authentication/t/lib/AuthRealmTestAppProgressive.pm	2008-11-07 20:14:05 UTC (rev 8583)
@@ -0,0 +1,77 @@
+package AuthRealmTestAppProgressive;
+use warnings;
+use strict;
+
+### using A::Store::minimal with new style realms
+### makes the app blow up, since c::p::a::s::minimal
+### isa c:a::s::minimal, and it's compat setup() gets
+### run, with an unexpected config has (realms on top,
+### not users). This tests makes sure the app no longer
+### blows up when this happens.
+use Catalyst qw/
+    Authentication
+    Authentication::Store::Minimal
+/;
+
+use Test::More;
+use Test::Exception;
+
+our %members = (
+    'members' => {
+        bob => { password => "s00p3r" }
+    },
+    'other' => {
+        sally => { password => "s00p3r" }
+    },
+);
+
+__PACKAGE__->config->{'Plugin::Authentication'} = {
+    default_realm => 'progressive',
+    progressive => {
+        class  => 'Progressive',
+        realms => [ 'other', 'members' ],
+    },
+    other => {
+        credential => {
+            class => 'Password',
+            password_field => 'password',
+            password_type  => 'clear'
+        },
+        store => {
+            class => 'Minimal',
+            users => $members{other},
+        }
+    },
+    members => {
+        credential => {
+            class => 'Password',
+            password_field => 'password',
+            password_type => 'clear'
+        },
+        store => {
+            class => 'Minimal',
+            users => $members{members},
+        }
+    },
+};
+
+sub progressive : Local {
+	my ( $self, $c ) = @_;
+
+    foreach my $realm ( keys %members ) {
+        while ( my ( $user, $info ) = each %{$members{$realm}} ) {
+            my $ok = eval {
+                $c->authenticate(
+                    { username => $user, password => $info->{password} },
+                ); 
+            };
+            ok( !$@, "authentication passed." );
+            ok( $ok, "user authenticated" );
+            ok( $c->user_in_realm($realm), "user in proper realm" );
+        }
+    }
+	$c->res->body( "ok" );
+}
+
+__PACKAGE__->setup;
+

Added: trunk/Catalyst-Plugin-Authentication/t/live_app_realms_progressive.t
===================================================================
--- trunk/Catalyst-Plugin-Authentication/t/live_app_realms_progressive.t	                        (rev 0)
+++ trunk/Catalyst-Plugin-Authentication/t/live_app_realms_progressive.t	2008-11-07 20:14:05 UTC (rev 8583)
@@ -0,0 +1,9 @@
+use strict;
+use warnings;
+
+use Test::More tests => 7;
+
+use lib 't/lib';
+use Catalyst::Test qw/AuthRealmTestAppProgressive/;
+
+ok(get("/progressive"), "get ok");




More information about the Catalyst-commits mailing list