[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