File Coverage

blib/lib/Catalyst/Authentication/Store/FromSub.pm
Criterion Covered Total %
statement 6 35 17.1
branch 0 12 0.0
condition 0 2 0.0
subroutine 2 7 28.5
pod 0 5 0.0
total 8 61 13.1


line stmt bran cond sub pod time code
1             package Catalyst::Authentication::Store::FromSub;
2             our $VERSION = '0.01';
3              
4 1     1   82006 use warnings;
  1         3  
  1         62  
5 1     1   6 use strict;
  1         2  
  1         1349  
6              
7             # ABSTRACT: A storage class for Catalyst Authentication using one Catalyst Model class
8              
9             sub new {
10 0     0 0   my ( $class, $config, $app ) = @_;
11              
12             # load User or Object module
13 0           my $user_type = $config->{user_type};
14 0 0         if ( $user_type eq 'Hash' ) {
    0          
15 0           Catalyst::Utils::ensure_class_loaded(
16             'Catalyst::Authentication::User::Hash');
17 0           $config->{user_type} = 'Catalyst::Authentication::User::Hash';
18             }
19             elsif ( $user_type eq 'Object' ) {
20 0           Catalyst::Utils::ensure_class_loaded(
21             'Catalyst::Authentication::FromSub::User::Object');
22 0           $config->{user_type} =
23             'Catalyst::Authentication::FromSub::User::Object';
24             }
25             else {
26 0           Catalyst::Utils::ensure_class_loaded($user_type);
27             }
28              
29 0           bless { config => $config }, $class;
30             }
31              
32             sub from_session {
33 0     0 0   my ( $self, $c, $id ) = @_;
34              
35             # Don't use data in session because data maybe changed in model_class sub auth.
36             # return $id if ref $id;
37              
38 0   0       my $id_field = $self->{config}->{id_field} || 'id';
39 0 0         if ( ref $id ) {
40 0 0         if ( exists $id->{$id_field} ) {
41 0           return $self->find_user( { $id_field => $id->{$id_field} }, $c );
42             }
43             else {
44 0           return $id;
45             }
46             }
47              
48 0           $self->find_user( { $id_field => $id }, $c );
49             }
50              
51             sub for_session {
52 0     0 0   my ( $self, $c, $user ) = @_;
53              
54 0           return $user->for_session($c);
55             }
56              
57             sub find_user {
58 0     0 0   my ( $self, $userinfo, $c ) = @_;
59              
60 0           my $config = $self->{config};
61 0           my $model_class = $config->{model_class};
62 0           my $model = $c->model($model_class);
63              
64 0           my $user = $model->auth( $c, $userinfo );
65 0 0         return unless $user;
66              
67 0 0         if ( $config->{user_type} eq 'Catalyst::Authentication::User::Hash' ) {
68 0           return $config->{user_type}->new($user);
69             }
70             else {
71 0           return $config->{user_type}
72             ->new( { user => $user, storage => $self }, $c );
73             }
74             }
75              
76             sub user_supports {
77 0     0 0   my $self = shift;
78              
79             # this can work as a class method on the user class
80 0           $self->{config}->{user_type}->supports(@_);
81             }
82              
83             1;
84             __END__
85              
86             =head1 NAME
87              
88             Catalyst::Authentication::Store::FromSub - A storage class for Catalyst Authentication using one Catalyst Model class
89              
90             =head1 VERSION
91              
92             version 0.01
93              
94             =head1 SYNOPSIS
95              
96             use Catalyst qw/Authentication/;
97              
98             __PACKAGE__->config->{authentication} = {
99             default_realm => 'members',
100             realms => {
101             members => {
102             credential => {
103             class => 'Password',
104             password_field => 'password',
105             password_type => 'clear'
106             },
107             store => {
108             class => 'FromSub', # or 'Object'
109             user_type => 'Hash',
110             model_class => 'UserAuth',
111             id_field => 'user_id',
112             }
113             }
114             } };
115              
116             # Log a user in:
117             sub login : Global {
118             my ( $self, $c ) = @_;
119              
120             $c->authenticate( {
121             username => $c->req->params->username,
122             password => $c->req->params->password,
123             } );
124             }
125              
126             package MyApp::Model::UserAuth; # map with model_class in config above
127             use base qw/Catalyst::Model/;
128             use strict;
129              
130             sub auth { # sub name needs to be 'auth'
131             my ($self, $c, $userinfo) = @_;
132              
133             my $where;
134             if (exists $userinfo->{user_id}) { # restore from session (id_field => 'user_id')
135             $where = { user_id => $userinfo->{user_id} };
136             } elsif (exists $userinfo->{username}) { # from authenticate
137             $where = { username => $userinfo->{username} };
138             } else { return; }
139              
140             # deal with cache
141             # if (my $val = $c->cache->get($key) {
142             # return $val;
143             # } else {
144             my $user = $c->model('TestApp')->resultset('User')->search( $where )->first;
145             $user = $user->{_column_data}; # hash
146             # $c->cache->set($key, $user);
147             # }
148              
149             return $user;
150             }
151              
152             =head1 DESCRIPTION
153              
154             Catalyst::Authentication::Store::FromSub class provides
155             access to authentication information by using a Catalyst Model sub B<auth>.
156              
157             In sub auth of the Catalyst model, we can use cache there (or do some complicated code). it would avoid the hit of db every request.
158              
159             =head2 CONFIGURATION
160              
161             The FromSub authentication store is activated by setting the store
162             config B<class> element to 'FromSub'. See the
163             L<Catalyst::Plugin::Authentication> documentation for more details on
164             configuring the store.
165              
166             The FromSub storage module has several configuration options
167              
168             __PACKAGE__->config->{authentication} = {
169             default_realm => 'members',
170             realms => {
171             members => {
172             credential => {
173             # ...
174             },
175             store => {
176             class => 'FromSub',
177             user_type => 'Object',
178             model_class => 'UserAuth',
179             id_field => 'user_id',
180             }
181             }
182             }
183             };
184              
185             authentication:
186             default_realm: 'members'
187             realms:
188             members:
189             credential:
190             class: 'Password'
191             store:
192             class: 'FromSub'
193             user_type: 'Object'
194             model_class: "UserAuth"
195              
196             =over 4
197              
198             =item class
199              
200             Class is part of the core Catalyst::Authentication::Plugin module, it
201             contains the class name of the store to be used. it must be 'FromSub' here.
202              
203             =item user_type
204              
205             'Hash' or 'Object', depends on the return value in sub auth, B<REQUIRED>.
206              
207             =item model_class
208              
209             Contains the class name (as passed to $c->model()) of Catalyst. This config item is B<REQUIRED>.
210              
211             =item id_field
212              
213             For restore from session, we pass { $id_field => $c->session->{__user}->{$id_field} } to sub auth, so be sure you deal with this $userinfo in sub auth like
214              
215             sub auth { # sub name needs to be 'auth'
216             my ($self, $c, $userinfo) = @_;
217              
218             my $where;
219             if (exists $userinfo->{user_id}) { # restore from session (id_field => 'user_id')
220             $where = { user_id => $userinfo->{user_id} };
221             } elsif (exists $userinfo->{username}) { # from authenticate
222             $where = { username => $userinfo->{username} };
223             } else { return; }
224              
225             It is a primary key return by sub auth. Default as 'id'
226              
227             =back
228              
229             =head2 USAGE
230              
231             The L<Catalyst::Authentication::Store::FromSub> storage module
232             is not called directly from application code. You interface with it
233             through the $c->authenticate() call.
234              
235             =head2 EXAMPLES
236              
237             =head3 Adv.
238              
239             # for login
240             sub login : Global {
241             my ( $self, $c ) = @_;
242              
243             $c->authenticate( {
244             username => $c->req->params->username,
245             password => $c->req->params->password,
246             status => [ 'active', 'registered' ],
247             } );
248             }
249              
250             sub is_admin : Global {
251             my ( $self, $c ) = @_;
252              
253             # use Set::Object in C::P::A::Roles
254             eval {
255             if ( $c->assert_user_roles( qw/admin/ ) ) {
256             $c->res->body( 'ok' );
257             }
258             };
259             if ($@) {
260             $c->res->body( 'failed' );
261             }
262             }
263              
264             package MyApp::Model::UserAuth; # map with model_class in config above
265             use base qw/Catalyst::Model/;
266             use strict;
267              
268             sub auth {
269             my ($self, $c, $userinfo) = @_;
270              
271             my ($where, $cache_key);
272             if (exists $userinfo->{user_id}) {
273             $where = { user_id => $userinfo->{user_id} };
274             $cache_key = 'global|user|user_id=' . $userinfo->{user_id};
275             } elsif (exists $userinfo->{username}) {
276             $where = { username => $userinfo->{username} };
277             $cache_key = 'global|user|username=' . $userinfo->{username};
278             } else { return; }
279              
280             my $user;
281             if (my $val = $c->cache->get($cache_key) {
282             $user = $val;
283             } else {
284             $user = $c->model('TestApp')->resultset('User')->search( $where )->first;
285             $user = $user->{_column_data}; # hash to cache
286             # get user roles
287             my $role_rs = $c->model('TestApp')->resultset('UserRole')->search( {
288             user => $user->{id}
289             } );
290             while (my $r = $role_rs->next) {
291             my $role = $c->model('TestApp')->resultset('Role')->find( {
292             id => $r->roleid
293             } );
294             push @{$user->{roles}}, $role->role;
295             }
296             # $user = {
297             # 'roles' => [
298             # 'admin',
299             # 'user'
300             # ],
301             # 'status' => 'active',
302             # 'session_data' => undef,
303             # 'username' => 'jayk',
304             # 'email' => 'j@cpants.org',
305             # 'password' => 'letmein',
306             # 'id' => '3'
307             #}
308             $c->cache->set($cache_key, $user);
309             }
310              
311             # validate status
312             if ( exists $userinfo->{status} and ref $userinfo->{status} eq 'ARRAY') {
313             unless (grep { $_ eq $user->{status} } @{$userinfo->{status}}) {
314             return;
315             }
316             }
317              
318             return $user;
319             }
320              
321             =head2 SEE ALSO
322              
323             L<Catalyst::Plugin::Authentication>, L<Catalyst::Plugin::Authentication::Internals>, L<Catalyst::Plugin::Authorization::Roles>
324              
325             =head1 AUTHOR
326              
327             Fayland Lam <fayland@gmail.com>
328              
329             =head1 COPYRIGHT AND LICENSE
330              
331             This software is copyright (c) 2009 by Fayland Lam.
332              
333             This is free software; you can redistribute it and/or modify it under
334             the same terms as perl itself.
335              
336             =pod