Allow authenticating against fully loaded users
Florian Ragwitz [Thu, 12 May 2011 12:26:34 +0000 (12:26 +0000)]
lib/Catalyst/Authentication/Store/DBIx/Class.pm
lib/Catalyst/Authentication/Store/DBIx/Class/User.pm
t/03-authtest.t
t/lib/TestApp/Controller/Root.pm

index 33155e0..25c0cea 100644 (file)
@@ -332,8 +332,8 @@ DBIx::Class authentication store. Reasons to do this are to avoid credential
 modification of the authinfo hash, or to avoid overlap between credential and
 store key names. It's a good idea to avoid using it in this way unless you are
 sure you have an overlap/modification issue. However, the two advanced
-retrieval methods, B<searchargs> and B<resultset>, require its use, as they
-are only processed as part of the 'dbix_class' hash.
+retrieval methods, B<searchargs>, B<result> and B<resultset>, require its use,
+as they are only processed as part of the 'dbix_class' hash.
 
 =over 4
 
@@ -366,6 +366,22 @@ username, email, or clientid - and would prefetch the data related to that user
 from the preferences table. The searchargs array is passed directly to the
 search() method associated with the user_model.
 
+=item Result
+
+The B<result> method of retrieval allows you to look up the user yourself and
+pass on the loaded user to the authentication store.
+
+    my $user = $ctx->model('MyApp::User')->find({ ... });
+
+    if ($ctx->authenticate({ dbix_class => { result => $user } })) {
+        ...
+    }
+
+Be aware that the result method will not verify that you are passing a result
+that is attached to the same user_model as specified in the config or even
+loaded from the database, as opposed to existing only in memory. It's your
+responsibility to make sure of that.
+
 =item Resultset
 
 The B<resultset> method of retrieval allows you to directly specify a
@@ -385,12 +401,11 @@ within your login action and use it for retrieving the user. A simple example:
 Be aware that the resultset method will not verify that you are passing a
 resultset that is attached to the same user_model as specified in the config.
 
-NOTE: All of these methods of user retrieval, including the resultset method,
-consider the first row returned to be the matching user. In most cases there
-will be only one matching row, but it is easy to produce multiple rows,
-especially when using the advanced retrieval methods. Remember, what you get
-when you use this module is what you would get when calling
-search(...)->first;
+NOTE: The resultset and searchargs methods of user retrieval, consider the first
+row returned to be the matching user. In most cases there will be only one
+matching row, but it is easy to produce multiple rows, especially when using the
+advanced retrieval methods. Remember, what you get when you use this module is
+what you would get when calling search(...)->first;
 
 NOTE ALSO:  The user info used to save the user to the session and to retrieve
 it is the same regardless of what method of retrieval was used.  In short,
index 5c5c93f..aa26fc6 100644 (file)
@@ -74,7 +74,9 @@ sub load {
     ## User can provide an arrayref containing the arguments to search on the user class.
     ## or even provide a prepared resultset, allowing maximum flexibility for user retreival.
     ## these options are only available when using the dbix_class authinfo hash.
-    if ($dbix_class_config && exists($authinfo->{'resultset'})) {
+    if ($dbix_class_config && exists($authinfo->{'result'})) {
+       $self->_user($authinfo->{'result'});
+    } elsif ($dbix_class_config && exists($authinfo->{'resultset'})) {
         $self->_user($authinfo->{'resultset'}->first);
     } elsif ($dbix_class_config && exists($authinfo->{'searchargs'})) {
         $self->_user($self->resultset->search(@{$authinfo->{'searchargs'}})->first);
index 7948513..f704281 100644 (file)
@@ -17,7 +17,7 @@ BEGIN {
         or plan skip_all =>
         "DBIx::Class is required for this test";
 
-    plan tests => 17;
+    plan tests => 19;
 
     $ENV{TESTAPP_CONFIG} = {
         name => 'TestApp',
@@ -82,8 +82,13 @@ use Catalyst::Test 'TestApp';
     is( $res->content, 'nuffin logged in', 'searchargs based login ok' );
 }
 
+# result test
+{
+    ok( my $res = request('http://localhost/result_login?email=j%40cpants.org&password=letmein'), 'request ok' );
+    is( $res->content, 'jayk logged in', 'resultset based login ok' );
+}
+
 # resultset test
-# searchargs test
 {
     ok( my $res = request('http://localhost/resultset_login?email=j%40cpants.org&password=letmein'), 'request ok' );
     is( $res->content, 'jayk logged in', 'resultset based login ok' );
index 37e40f9..3c1ba32 100644 (file)
@@ -77,6 +77,32 @@ sub searchargs_login : Global {
     }
 }
 
+sub result_login : Global {
+    my ($self, $ctx) = @_;
+
+    my $user = $ctx->model('TestApp::User')->find({
+        email => $ctx->request->params->{email},
+    });
+
+    if ($user->password_accessor ne $ctx->request->params->{password}) {
+        $ctx->response->status(403);
+        $ctx->response->body('password mismatch');
+        $ctx->detach;
+    }
+
+    $ctx->authenticate({
+        dbix_class => { result => $user },
+        password   => $ctx->request->params->{password},
+    });
+
+    if ($ctx->user_exists) {
+        $ctx->res->body( $ctx->user->get('username') .  ' logged in' );
+    }
+    else {
+        $ctx->res->body('not logged in');
+    }
+}
+
 sub resultset_login : Global {
     my ( $self, $c ) = @_;