Initial draft of auth modules
Yuval Kogman [Fri, 4 Nov 2005 00:17:47 +0000 (00:17 +0000)]
lib/Catalyst/Plugin/Authentication.pm [new file with mode: 0644]
lib/Catalyst/Plugin/Authentication/Store.pm [new file with mode: 0644]
lib/Catalyst/Plugin/Authentication/Store/Minimal.pm [new file with mode: 0644]
lib/Catalyst/Plugin/Authentication/Store/Minimal/Backend.pm [new file with mode: 0644]
lib/Catalyst/Plugin/Authentication/User.pm [new file with mode: 0644]
lib/Catalyst/Plugin/Authentication/User/Hash.pm [new file with mode: 0644]

diff --git a/lib/Catalyst/Plugin/Authentication.pm b/lib/Catalyst/Plugin/Authentication.pm
new file mode 100644 (file)
index 0000000..2a67859
--- /dev/null
@@ -0,0 +1,135 @@
+#!/usr/bin/perl
+
+package Catalyst::Plugin::Authentication;
+
+use base qw/Class::Accessor::Fast/;
+
+BEGIN { __PACKAGE__->mk_accessors(qw/user/) }
+
+use strict;
+use warnings;
+
+sub set_authenticated {
+    my ( $c, $user ) = @_;
+
+    $c->user($user);
+
+    if (    $c->isa("Catalyst::Plugin::Session")
+        and $c->config->{authentication}{use_session} )
+    {
+        $c->session->{__user} = $user->for_session if $user->supperts("session");
+        $c->session->{__user_class} = ref $user;
+    }
+}
+
+sub logout {
+    my $c = shift;
+
+    $c->user(undef);
+    delete @{ $c->session }{qw/__user __user_class/};
+}
+
+sub prepare {
+    my $c = shift->NEXT::prepare(@_);
+
+    if (    $c->isa("Catalyst::Plugin::Session")
+        and $c->config->{authentication}{use_session}
+        and !$c->user )
+    {
+        if ( $c->sessionid and my $user = $c->session->{__user} ) {
+            $c->user( $c->session->{__user_class}->from_session( $c, $user ) );
+        }
+    }
+
+    return $c;
+}
+
+sub setup {
+    my $c = shift;
+
+    my $cfg = $c->config->{authentication};
+
+    %$cfg = (
+        use_session => 1,
+        %$cfg,
+    );
+}
+
+__PACKAGE__;
+
+__END__
+
+=pod
+
+=head1 NAME
+
+Catalyst::Plugin::Authentication - 
+
+=head1 SYNOPSIS
+
+       use Catalyst qw/
+               Authentication
+               Authentication::Store::Foo
+               Authentication::Credential::Password
+       /;
+
+=head1 DESCRIPTION
+
+The authentication plugin is used by the various authentication and
+authorization plugins in catalyst.
+
+It defines the notion of a logged in user, and provides integration with the 
+
+=head1 METHODS
+
+=over 4 
+
+=item logout
+
+Delete the currently logged in user from C<user> and the session.
+
+=item user
+
+Returns the currently logged user or undef if there is none.
+
+=back
+
+=head1 INTERNAL METHODS
+
+=over 4
+
+=item set_authenticated $user
+
+Marks a user as authenticated. Should be called from a
+C<Catalyst::Plugin::Authentication::Credential> plugin after successful
+authentication.
+
+This involves setting C<user> and the internal data in C<session> if
+L<Catalyst::Plugin::Session> is loaded.
+
+=item prepare
+
+Revives a user from the session object if there is one.
+
+=item setup
+
+Sets the default configuration parameters.
+
+=item 
+
+=back
+
+=head1 CONFIGURATION
+
+=over 4
+
+=item use_session
+
+Whether or not to store the user's logged in state in the session, if the
+application is also using the L<Catalyst::Plugin::Authentication> plugin.
+
+=back
+
+=cut
+
+
diff --git a/lib/Catalyst/Plugin/Authentication/Store.pm b/lib/Catalyst/Plugin/Authentication/Store.pm
new file mode 100644 (file)
index 0000000..f47b24a
--- /dev/null
@@ -0,0 +1,30 @@
+#!/usr/bin/perl
+
+package Catalyst::Plugin::Authentication::Store;
+
+use strict;
+use warnings;
+
+sub get_user { die "virtual" }
+
+sub user_supports { die "virtual" }
+
+__PACKAGE__;
+
+__END__
+
+=pod
+
+=head1 NAME
+
+Catalyst::Plugin::Authentication::Store - 
+
+=head1 SYNOPSIS
+
+       use Catalyst::Plugin::Authentication::Store;
+
+=head1 DESCRIPTION
+
+=cut
+
+
diff --git a/lib/Catalyst/Plugin/Authentication/Store/Minimal.pm b/lib/Catalyst/Plugin/Authentication/Store/Minimal.pm
new file mode 100644 (file)
index 0000000..a5477a1
--- /dev/null
@@ -0,0 +1,79 @@
+#!/usr/bin/perl
+
+package Catalyst::Plugin::Authentication::Store::Minimal;
+use base qw/Catalyst::Plugin::Authentication::Store/;
+
+use strict;
+use warnings;
+
+use Catalyst::Plugin::Authentication::Store::Minimal::Backend;
+
+sub setup {
+    my $c = shift;
+
+    $c->config->{authentication}{store} =
+      Catalyst::Plugin::Authentication::Store::Minimal::Backend->new(
+        $c->config->{authentication}{users} );
+
+}
+
+__PACKAGE__;
+
+__END__
+
+=pod
+
+=head1 NAME
+
+Catalyst::Plugin::Authentication::Store::Minimal - Authentication
+database in C<<$c->config>>.
+
+=head1 SYNOPSIS
+
+    use Catalyst qw/
+      Authentication
+      Authentication::Store::Minimal
+      Authentication::Credential::Password
+      /;
+
+    __PACKAGE__->config->{authentication}{users} = {
+        name => {
+            password => "s3cr3t",
+            roles    => [qw/admin editor/],
+            ...
+        },
+    };
+
+    sub login : Global {
+        my ( $self, $c ) = @_;
+
+        $c->login( $c->req->param("login"), $c->req->param("password"), );
+    }
+
+=head1 DESCRIPTION
+
+This authentication store plugin lets you create a very quick and dirty user
+database in your application's config hash.
+
+It's purpose is mainly for testing, and it should probably be replaced by a
+more "serious" store for production.
+
+The hash in the config, as well as the user objects/hashes are freely mutable
+at runtime.
+
+This plugin inherits L<Catalyst::Plugin::Authentication::Store>.
+
+=head1 METHODS
+
+=over 4
+
+=item setup
+
+This method will popultate C<< $c->config->{authentication}{store} >> so that
+L<Catalyst::Plugin::Authentication::Store> can use it.
+
+=back
+
+=cut
+
+
diff --git a/lib/Catalyst/Plugin/Authentication/Store/Minimal/Backend.pm b/lib/Catalyst/Plugin/Authentication/Store/Minimal/Backend.pm
new file mode 100644 (file)
index 0000000..5eecd3d
--- /dev/null
@@ -0,0 +1,105 @@
+#!/usr/bin/perl
+
+package Catalyst::Plugin::Authentication::Store::Minimal::Backend;
+
+use strict;
+use warnings;
+
+use Catalyst::Plugin::Authentication::User::Hash;
+use Scalar::Util ();
+
+sub new {
+    my ( $class, $hash ) = @_;
+
+    bless { hash => $hash }, $class;
+}
+
+sub get_user {
+    my ( $self, $id ) = @_;
+
+       my $user = $self->{hash}{$id};
+
+       bless $user, "Catalyst::Plugin::Authentication::User::Hash"
+         unless Scalar::Util::blessed($user);
+
+    return $user;
+}
+
+sub user_supports {
+    my $self = shift;
+
+    # choose a random user
+    scalar keys %{ $self->{hash} };
+    ( undef, my $user ) = each %{ $self->{hash} };
+
+    $user->supports(@_);
+}
+
+__PACKAGE__;
+
+__END__
+
+=pod
+
+=head1 NAME
+
+Catalyst::Plugin::Authentication::Store::Minimal::Backend - Minimal
+authentication storage backend.
+
+=head1 SYNOPSIS
+
+    # you probably just want Store::Minimal under most cases,
+    # but if you insist you can instantiate your own store:
+
+    use Catalyst::Plugin::Authentication::Store::Minimal::Backend;
+
+    use Catalyst qw/
+        Authentication
+        Authentication::Credential::Password
+    /;
+
+    my %users = (
+        user => { password => "s3cr3t" },
+    );
+    
+    our $users = Catalyst::Plugin::Authentication::Store::Minimal::Backend->new(\%users);
+
+    sub action : Local {
+        my ( $self, $c ) = @_;
+
+        $c->login( $users->get_user( $c->req->param("login") ),
+            $c->req->param("password") );
+    }
+
+=head1 DESCRIPTION
+
+You probably want L<Catalyst::Plugin::Authentication::Store::Minimal>, unless
+you are mixing several stores in a single app and one of them is Minimal.
+
+Otherwise, this lets you create a store manually.
+
+=head1 METHODS
+
+=over 4
+
+=item new $hash_ref
+
+Constructs a new store object, which uses the supplied hash ref as it's backing
+structure.
+
+=item get_user $id
+
+Keys the hash by $id and returns the value.
+
+If the return value is unblessed it will be blessed as
+L<Catalyst::Plugin::Authentication::User::Hash>.
+
+=item user_supports
+
+Chooses a random user from the hash and delegates to it.
+
+=back
+
+=cut
+
+
diff --git a/lib/Catalyst/Plugin/Authentication/User.pm b/lib/Catalyst/Plugin/Authentication/User.pm
new file mode 100644 (file)
index 0000000..7c33c98
--- /dev/null
@@ -0,0 +1,56 @@
+#!/usr/bin/perl
+
+package Catalyst::Plugin::Authentication::User;
+
+use strict;
+use warnings;
+
+sub id { die "virtual" }
+
+sub store { die "virtual" }
+
+sub supports { die "virtual" }
+
+__PACKAGE__;
+
+__END__
+
+=pod
+
+=head1 NAME
+
+Catalyst::Plugin::Authentication::User - Base class for user objects.
+
+=head1 SYNOPSIS
+
+       package MyStore::User;
+       use base qw/Catalyst::Plugin::Authentication::User/;
+
+=head1 DESCRIPTION
+
+This is the base class for authenticated 
+
+=head1 METHODS
+
+=over 4
+
+=item id
+
+A unique ID by which a user can be retrieved from the store.
+
+=item store
+
+Should return a class name that can be used to refetch the user using it's
+ID.
+
+=item supports
+
+An introspection method used to determine what features a user object has, to support credential and authorization plugins.
+
+=item 
+
+=back
+
+=cut
+
+
diff --git a/lib/Catalyst/Plugin/Authentication/User/Hash.pm b/lib/Catalyst/Plugin/Authentication/User/Hash.pm
new file mode 100644 (file)
index 0000000..0b7bb55
--- /dev/null
@@ -0,0 +1,122 @@
+#!/usr/bin/perl
+
+package Catalyst::Plugin::Authentication::User::Hash;
+use base qw/Catalyst::Plugin::Authentication::User/;
+
+use strict;
+use warnings;
+
+sub new {
+       my $class = shift;
+
+       bless { @_ }, $class;
+}
+
+sub AUTOLOAD {
+    my $self = shift;
+    ( my $key ) = ( our $AUTOLOAD =~ m/([^:]*)$/ );
+
+    $self->{$key} = shift if @_;
+    $self->{$key};
+}
+
+my %features = (
+    password => {
+        clear   => ["password"],
+        crypted => ["crypted_password"],
+        hashed  => ["hashed_password hash_algorithm"],
+    },
+    session => 1,
+);
+
+sub supports {
+    my ( $self, @spec ) = @_;
+
+    my $cursor = \%features;
+
+    # traverse the feature list,
+    for (@spec) {
+        die "bad feature spec: @spec"
+          if ref($cursor) ne "HASH"
+          or !ref( $cursor = $cursor->{$_} );
+    }
+
+    die "bad feature spec: @spec" unless ref $cursor eq "ARRAY";
+
+    # check that all the keys required for a feature are in here
+    foreach my $key (@$cursor) {
+        return undef unless exists $self->{$key};
+    }
+
+    return 1;
+}
+
+sub for_session {
+    my $self = shift;
+
+    return $self;    # let's hope we're serialization happy
+}
+
+sub from_session {
+    my ( $self, $c, $user ) = @_;
+
+    return $user;    # if we're serialization happy this should work
+}
+
+__PACKAGE__;
+
+__END__
+
+=pod
+
+=head1 NAME
+
+Catalyst::Plugin::Authentication::User::Hash - An easy authentication user
+object based on hashes.
+
+=head1 SYNOPSIS
+
+       use Catalyst::Plugin::Authentication::User::Hash;
+       
+       Catalyst::Plugin::Authentication::User::Hash->new(
+               password => "s3cr3t",
+       );
+
+=head1 DESCRIPTION
+
+This implementation of authentication user handles is supposed to go hand in
+hand with L<Catalyst::Plugin::Authentication::Store::Minimal>.
+
+=head1 METHODS
+
+=over 4
+
+=item new @pairs
+
+Create a new object with the key-value-pairs listed in the arg list.
+
+=item supports
+
+Checks for existence of keys that correspond with features.
+
+=item for_session
+
+Just returns $self, expecting it to be serializable.
+
+=item from_session
+
+Just passes returns the unserialized object, hoping it's intact.
+
+=item AUTOLOAD
+
+Accessor for the key whose name is the method.
+
+=back
+
+=head1 SEE ALSO
+
+L<Hash::AsObject>
+
+=cut
+
+