File Coverage

blib/lib/Catalyst/Authentication/Store/FromSub/Hash.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package Catalyst::Authentication::Store::FromSub::Hash;
2              
3              
4 1     1   24743 use warnings;
  1         3  
  1         38  
5 1     1   6 use strict;
  1         3  
  1         46  
6 1     1   6 use vars qw/$VERSION/;
  1         5  
  1         54  
7             $VERSION = '0.09';
8              
9 1     1   451 use Catalyst::Authentication::User::Hash;
  0            
  0            
10              
11             sub new {
12             my ( $class, $config, $app, $realm) = @_;
13              
14             bless { config => $config }, $class;
15             }
16              
17             sub from_session {
18             my ( $self, $c, $id ) = @_;
19              
20             # XXX? Don't use data in session because data maybe changed in model_class sub auth.
21             # return $id if ref $id;
22            
23             my $id_field = $self->{config}->{id_field} || 'user_id';
24             if (ref $id) {
25             if ( exists $id->{$id_field} ) {
26             return $self->find_user( { $id_field => $id->{$id_field} }, $c );
27             } else {
28             return $id;
29             }
30             }
31              
32             $self->find_user( { $id_field => $id }, $c );
33             }
34              
35             sub find_user {
36             my ( $self, $userinfo, $c ) = @_;
37              
38             my $model_class = $self->{config}->{model_class};
39             my $model = $c->model($model_class);
40            
41             my $user = $model->auth($c, $userinfo);
42             return unless $user;
43              
44             if ( ref($user) eq 'HASH') {
45             my $id_field = $self->{config}->{id_field} || 'user_id';
46             my $id = $user->{ $id_field };
47             $user->{id} ||= $id;
48             return bless $user, "Catalyst::Authentication::User::Hash";
49             } else {
50             Catalyst::Exception->throw( "The user return by 'sub auth' must be a hash reference");
51             }
52             return $user;
53             }
54              
55             sub user_supports {
56             my $self = shift;
57              
58             my $model = $self->{config}->{model_class};
59              
60             $model->supports(@_);
61             }
62              
63             ## Backwards compatibility
64             #
65             # This is a backwards compatible routine. get_user is specifically for loading a user by it's unique id
66             # find_user is capable of doing the same by simply passing { id => $id }
67             # no new code should be written using get_user as it is deprecated.
68             sub get_user {
69             my ( $self, $id ) = @_;
70             $self->find_user({id => $id});
71             }
72              
73             ## backwards compatibility
74             sub setup {
75             my $c = shift;
76              
77             $c->default_auth_store(
78             __PACKAGE__->new(
79             $c->config->{authentication}, $c
80             )
81             );
82              
83             $c->NEXT::setup(@_);
84             }
85              
86             1;
87             __END__
88              
89             =head1 NAME
90              
91             Catalyst::Authentication::Store::FromSub::Hash - A storage class for Catalyst Authentication using one Catalyst Model class (hash returned)
92              
93             =head1 SYNOPSIS
94              
95             use Catalyst qw/Authentication/;
96              
97             __PACKAGE__->config->{authentication} =
98             {
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::Hash',
109             model_class => 'UserAuth',
110             id_field => 'user_id',
111             }
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            
141             # deal with cache
142             # if (my $val = $c->cache->get($key) {
143             # return $val;
144             # } else {
145             my $user = $c->model('TestApp')->resultset('User')->search( $where )->first;
146             $user = $user->{_column_data}; # hash
147             # $c->cache->set($key, $user);
148             # }
149              
150             return $user;
151             }
152              
153            
154             =head1 DESCRIPTION
155              
156             Catalyst::Authentication::Store::FromSub::Hash class provides
157             access to authentication information by using a Catalyst Model sub auth.
158              
159             In sub auth of the Catalyst model, we can use cache there. it would avoid the hit of db every request.
160              
161             =head1 CONFIGURATION
162              
163             The FromSub::Hash authentication store is activated by setting the store
164             config B<class> element to 'FromSub::Hash'. See the
165             L<Catalyst::Plugin::Authentication> documentation for more details on
166             configuring the store.
167              
168             The FromSub::Hash storage module has several configuration options
169              
170              
171             __PACKAGE__->config->{authentication} =
172             {
173             default_realm => 'members',
174             realms => {
175             members => {
176             credential => {
177             # ...
178             },
179             store => {
180             class => 'FromSub::Hash',
181             model_class => 'UserAuth',
182             id_field => 'user_id',
183             }
184             }
185             }
186             };
187              
188             authentication:
189             default_realm: 'members'
190             password_hash_type: "clear"
191             realms:
192             members:
193             credential:
194             class: 'Password'
195             password_field: 'password'
196             password_type: "hashed"
197             password_hash_type: "SHA-1"
198             store:
199             class: 'FromSub::Hash'
200             model_class: "UserAuth"
201              
202             =over 4
203              
204             =item class
205              
206             Class is part of the core Catalyst::Authentication::Plugin module, it
207             contains the class name of the store to be used.
208              
209             =item user_class
210              
211             Contains the class name (as passed to $c->model()) of Catalyst. This config item is B<REQUIRED>.
212              
213             =item id_field
214              
215             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
216              
217             sub auth { # sub name needs to be 'auth'
218             my ($self, $c, $userinfo) = @_;
219            
220             my $where;
221             if (exists $userinfo->{user_id}) { # restore from session (id_field => 'user_id')
222             $where = { user_id => $userinfo->{user_id} };
223             } elsif (exists $userinfo->{username}) { # from authenticate
224             $where = { username => $userinfo->{username} };
225             } else { return; }
226              
227             It is a primary key in the hash return by sub auth. Default is 'user_id'
228              
229             =back
230              
231             =head1 USAGE
232              
233             The L<Catalyst::Authentication::Store::FromSub::Hash> storage module
234             is not called directly from application code. You interface with it
235             through the $c->authenticate() call.
236              
237             =head1 EXAMPLES
238              
239             =head2 Adv.
240              
241             # for login
242             sub login : Global {
243             my ( $self, $c ) = @_;
244            
245             $c->authenticate({
246             username => $c->req->params->username,
247             password => $c->req->params->password,
248             status => [ 'active', 'registered' ],
249             }))
250             }
251              
252             sub is_admin : Global {
253             my ( $self, $c ) = @_;
254            
255             # use Set::Object in C::P::A::Roles
256             eval {
257             if ( $c->assert_user_roles( qw/admin/ ) ) {
258             $c->res->body( 'ok' );
259             }
260             };
261             if ($@) {
262             $c->res->body( 'failed' );
263             }
264             }
265            
266             package MyApp::Model::UserAuth; # map with model_class in config above
267             use base qw/Catalyst::Model/;
268             use strict;
269            
270             sub auth {
271             my ($self, $c, $userinfo) = @_;
272            
273             my ($where, $cache_key);
274             if (exists $userinfo->{user_id}) {
275             $where = { user_id => $userinfo->{user_id} };
276             $cache_key = 'global|user|user_id=' . $userinfo->{user_id};
277             } elsif (exists $userinfo->{username}) {
278             $where = { username => $userinfo->{username} };
279             $cache_key = 'global|user|username=' . $userinfo->{username};
280             } else { return; }
281            
282             my $user;
283             if (my $val = $c->cache->get($cache_key) {
284             $user = $val;
285             } else {
286             $user = $c->model('TestApp')->resultset('User')->search( $where )->first;
287             $user = $user->{_column_data}; # hash to cache
288             # get user roles
289             my $role_rs = $c->model('TestApp')->resultset('UserRole')->search( {
290             user => $user->{id}
291             } );
292             while (my $r = $role_rs->next) {
293             my $role = $c->model('TestApp')->resultset('Role')->find( {
294             id => $r->roleid
295             } );
296             push @{$user->{roles}}, $role->role;
297             }
298             # $user = {
299             # 'roles' => [
300             # 'admin',
301             # 'user'
302             # ],
303             # 'status' => 'active',
304             # 'session_data' => undef,
305             # 'username' => 'jayk',
306             # 'email' => 'j@cpants.org',
307             # 'password' => 'letmein',
308             # 'id' => '3'
309             #}
310             $c->cache->set($cache_key, $user);
311             }
312            
313             # validate status
314             if ( exists $userinfo->{status} and ref $userinfo->{status} eq 'ARRAY') {
315             unless (grep { $_ eq $user->{status} } @{$userinfo->{status}}) {
316             return;
317             }
318             }
319              
320             return $user;
321             }
322              
323             =head1 CODE USED IN LIVE
324              
325             L<http://foorum.googlecode.com/svn/trunk/>
326              
327             =head1 BUGS
328              
329             None known currently, please email the author if you find any.
330              
331             =head1 SEE ALSO
332              
333             L<Catalyst::Plugin::Authentication>, L<Catalyst::Plugin::Authentication::Internals>, L<Catalyst::Plugin::Authorization::Roles>
334              
335             =head1 AUTHOR
336              
337             Fayland Lam, C<< <fayland at gmail.com> >>
338              
339             =head1 COPYRIGHT & LICENSE
340              
341             Copyright 2007 Fayland Lam, all rights reserved.
342              
343             This program is free software; you can redistribute it and/or modify it
344             under the same terms as Perl itself.
345              
346             =cut