Now throws exceptions when id_field is invalid or can not be determined
[catagits/Catalyst-Authentication-Store-DBIx-Class.git] / lib / Catalyst / Plugin / Authentication / Store / DBIx / Class.pm
1 package Catalyst::Plugin::Authentication::Store::DBIx::Class;
2
3 use strict;
4 use warnings;
5 use base qw/Class::Accessor::Fast/;
6
7 <<<<<<< .mine
8 our $VERSION= "0.10";
9 =======
10 our $VERSION= "0.03";
11 >>>>>>> .r6516
12
13 BEGIN {
14     __PACKAGE__->mk_accessors(qw/config/);
15 }
16
17
18 sub new {
19     my ( $class, $config, $app ) = @_;
20
21     ## figure out if we are overriding the default store user class 
22     $config->{'store_user_class'} = (exists($config->{'store_user_class'})) ? $config->{'store_user_class'} :
23                                         "Catalyst::Plugin::Authentication::Store::DBIx::Class::User";
24
25     ## make sure the store class is loaded.
26     Catalyst::Utils::ensure_class_loaded( $config->{'store_user_class'} );
27     
28     ## fields can be specified to be ignored during user location.  This allows
29     ## the store to ignore certain fields in the authinfo hash.
30     
31     $config->{'ignore_fields_in_find'} ||= [ ];
32
33     my $self = {
34                     config => $config
35                };
36
37     bless $self, $class;
38
39 }
40
41 ## --jk note to self:
42 ## let's use DBICs get_columns method to return a hash and save / restore that
43 ## from the session.  Then we can respond to get() calls, etc. in most cases without
44 ## resorting to a DB call.  If user_object is called, THEN we can hit the DB and
45 ## return a real object.  
46 sub from_session {
47     my ( $self, $c, $frozenuser ) = @_;
48
49     return $frozenuser if ref $frozenuser;
50     
51     my $user = $self->config->{'store_user_class'}->new($self->{'config'}, $c);
52     
53     return $user->from_session($frozenuser, $c);
54 }
55
56 sub for_session {
57     my ($self, $c, $user) = @_;
58     
59     return $user->for_session($c);
60 }
61
62 sub find_user {
63     my ( $self, $authinfo, $c ) = @_;
64     
65     my $user = $self->config->{'store_user_class'}->new($self->{'config'}, $c);
66
67     return $user->load($authinfo, $c);
68
69 }
70
71 sub user_supports {
72     my $self = shift;
73     # this can work as a class method on the user class
74     $self->config->{'store_user_class'}->supports( @_ );
75 }
76
77 __PACKAGE__;
78
79 __END__
80
81 =head1 NAME
82
83 Catalyst::Plugin::Authentication::Store::DBIx::Class - A storage class for Catalyst Authentication using DBIx::Class
84
85 =head1 VERSION
86
87 This documentation refers to version 0.10.
88
89 =head1 SYNOPSIS
90
91     use Catalyst qw/
92                     Authentication
93                     Authorization::Roles/;
94
95     __PACKAGE__->config->{authentication} = 
96                     {  
97                         default_realm => 'members',
98                         realms => {
99                             members => {
100                                 credential => {
101                                     class => 'Password',
102                                     password_field => 'password',
103                                     password_type => 'clear'
104                                 },
105                                 store => {
106                                     class => 'DBIx::Class',
107                                     user_class => 'MyApp::Users',
108                                     id_field => 'user_id',
109                                     role_relation => 'roles',
110                                     role_field => 'rolename',                   
111                                 }
112                             }
113                         }
114                     };
115
116     # Log a user in:
117     
118     sub login : Global {
119         my ( $self, $c ) = @_;
120         
121         $c->authenticate({  
122                           username => $c->req->params->username,
123                           password => $c->req->params->password,
124                           status => [ 'registered', 'loggedin', 'active']
125                           }))
126     }
127     
128     # verify a role 
129     
130     if ( $c->check_user_roles( 'editor' ) ) {
131         # do editor stuff
132     }
133     
134 =head1 DESCRIPTION
135
136 The Catalyst::Plugin::Authentication::Store::DBIx::Class class provides 
137 access to authentication information stored in a database via DBIx::Class.
138
139 =head1 CONFIGURATION
140
141 The DBIx::Class authentication store is activated by setting the store
142 config's B<class> element to DBIx::Class as shown above.  See the 
143 L<Catalyst::Plugin::Authentication> documentation for more details on 
144 configuring the store.
145
146 The DBIx::Class storage module has several configuration options
147
148
149     __PACKAGE__->config->{authentication} = 
150                     {  
151                         default_realm => 'members',
152                         realms => {
153                             members => {
154                                 credential => {
155                                     # ...
156                                 },
157                                 store => {
158                                     class => 'DBIx::Class',
159                                     user_class => 'MyApp::Users',
160                                     id_field => 'user_id',
161                                     role_relation => 'roles',
162                                     role_field => 'rolename',
163                                     ignore_fields_in_find => [ 'remote_name' ]          
164                                 }
165                                 }
166                         }
167                     };
168
169 =over 4
170
171 =item class
172
173 Class is part of the core Catalyst::Authentication::Plugin module, it
174 contains the class name of the store to be used.
175
176 =item user_class
177
178 Contains the class name (as passed to $c->model()) of the DBIx::Class schema
179 to use as the source for user information.  This config item is B<REQUIRED>.
180
181 =item id_field
182
183 Contains the field name containing the unique identifier for a user.  This is 
184 used when storing and retrieving a user from the session.  The value in this
185 field should correspond to a single user in the database.  Defaults to 'id'.
186
187 =item role_column
188
189 If your role information is stored in the same table as the rest of your user
190 information, this item tells the module which field contains your role
191 information.  The DBIx::Class authentication store expects the data in this
192 field to be a series of role names separated by some combination of spaces, 
193 commas or pipe characters.  
194
195 =item role_relation
196
197 If your role information is stored in a separate table, this is the name of
198 the relation that will lead to the roles the user is in.  If this is 
199 specified then a role_field is also required.  Also when using this method
200 it is expected that your role table will return one row for each role 
201 the user is in.
202
203 =item role_field
204
205 This is the name of the field in the role table that contains the string 
206 identifying the role.  
207
208 =item ignore_fields_in_find
209
210 This item is an array containing fields that may be passed to the
211 $c->authenticate() routine (and therefore find_user in the storage class), but
212 which should be ignored when creating the DBIx::Class search to retrieve a
213 user. This makes it possible to avoid problems when a credential requires an
214 authinfo element whose name overlaps with a column name in your users table.
215 If this doesn't make sense to you, you probably don't need it.
216
217 =item store_user_class
218
219 This allows you to override the authentication user class that the 
220 DBIx::Class store module uses to perform it's work.  Most of the
221 work done in this module is actually done by the user class, 
222 L<Catalyst::Plugin::Authentication::Store::DBIx::Class::User>, so
223 overriding this doesn't make much sense unless you are using your
224 own class to extend the functionality of the existing class.  
225 Chances are you do not want to set this.
226
227 =back
228
229 =head1 USAGE 
230
231 The L<Catalyst::Plugin::Authentication::Store::DBIx::Class> storage module
232 is not called directly from application code.  You interface with it 
233 through the $c->authenticate() call.  
234
235 There are three methods you can use to retrieve information from the DBIx::Class
236 storage module.  They are Simple retrieval, and the advanced retrieval methods
237 Searchargs and Resultset.
238
239 =head2 Simple Retrieval 
240
241 The first, and most common, method is simple retrieval. As it's name implies
242 simple retrieval allows you to simply to provide the column => value pairs
243 that should be used to locate the user in question. An example of this usage
244 is below:
245
246     if ($c->authenticate({  
247                           username => $c->req->params->{'username'},
248                           password => $c->req->params->{'password'},
249                           status => [ 'registered', 'active', 'loggedin']
250                          })) {
251
252         # ... authenticated user code here
253     }
254
255 The above example would attempt to retrieve a user whose username column
256 matched the username provided, and whose status column matched one of the
257 values provided. These name => value pairs are used more or less directly in
258 the DBIx::Class' search() routine, so in most cases, you can use DBIx::Class
259 syntax to retrieve the user according to whatever rules you have.
260
261 NOTE: Because the password in most cases is encrypted - it is not used
262 directly but it's encryption and comparison with the value provided is usually
263 handled by the Password Credential. Part of the Password Credential's behavior
264 is to remove the password argument from the authinfo that is passed to the
265 storage module. See L<Catalyst::Plugin::Authentication::Credential::Password>.
266
267 One thing you need to know about this retrieval method is that the name
268 portion of the pair is checked against the user class' column list. Pairs are
269 only used if a matching column is found. Other pairs will be ignored. This
270 means that you can only provide simple name-value pairs, and that some more
271 advanced DBIx::Class constructs, such as '-or', '-and', etc. are in most cases
272 not possible using this method. For queries that require this level of
273 functionality, see the 'searchargs' method below.
274
275 =head2 Advanced Retrieval
276
277 The Searchargs and Resultset retrieval methods are used when more advanced
278 features of the underlying L<DBIx::Class> schema are required. These methods
279 provide a direct interface with the DBIx::Class schema and therefore
280 require a better understanding of the DBIx::Class module.  
281
282 =head3 The dbix_class key
283
284 Since the format of these arguments are often complex, they are not keys in
285 the base authinfo hash.  Instead, both of these arguments are placed within 
286 a hash attached to the store-specific 'dbix_class' key in the base $authinfo 
287 hash.  When the DBIx::Class authentication store sees the 'dbix_class' key
288 in the passed authinfo hash, all the other information in the authinfo hash
289 is ignored and only the values within the 'dbix_class' hash are used as 
290 though they were passed directly within the authinfo hash.  In other words, if
291 'dbix_class' is present, it replaces the authinfo hash for processing purposes.
292
293 The 'dbix_class' hash can be used to directly pass arguments to the
294 DBIx::Class authentication store. Reasons to do this are to avoid credential
295 modification of the authinfo hash, or to avoid overlap between credential and
296 store key names. It's a good idea to avoid using it in this way unless you are
297 sure you have an overlap/modification issue. However, the two advanced
298 retrieval methods, B<searchargs> and B<resultset>, require it's use, as they 
299 are only processed as part of the 'dbix_class' hash
300
301 =over 4
302
303 =item Searchargs
304
305 The B<searchargs> method of retrieval allows you to specify an arrayref containing
306 the two arguments to the search() method from L<DBIx::Class::ResultSet>.  If provided,
307 all other args are ignored, and the search args provided are used directly to locate
308 the user.  An example will probably make more sense:
309
310     if ($c->authenticate(
311         { 
312             password => $password,
313             'dbix_class' => 
314                 {
315                     searchargs = [ { -or => [ username => $username,
316                                               email => $email,
317                                               clientid => $clientid ] 
318                                    },
319                                    { prefetch => qw/ preferences / } 
320                                  ]
321                 }
322         } ) ) 
323     {
324         # do successful authentication actions here.
325     }
326
327 The above would allow authentication based on any of the three items -
328 username, email or clientid and would prefetch the data related to that user
329 from the preferences table. The searchargs array is passed directly to the
330 search() method associated with the user_class.
331
332 =item Resultset
333
334 The B<resultset> method of retrieval allows you to directly specify a
335 resultset to be used for user retrieval. This allows you to create a resultset
336 within your login action and use it for retrieving the user. A simple example:
337
338     my $rs = $c->model('MyApp::User')->search({ email => $c->request->params->{'email'} });
339        ... # further $rs adjustments
340        
341     if ($c->authenticate({ 
342                            password => $password,
343                            'dbix_class' => {  resultset = $rs }
344                          })) {
345        # do successful authentication actions here.
346     } 
347
348 Be aware that the resultset method will not verify that you are passing a
349 resultset that is attached to the same user_class as specified in the config.
350
351 NOTE: All of these methods of user retrieval, including the resultset method,
352 consider the first row returned to be the matching user. In most cases there
353 will be only one matching row, but it is easy to produce multiple rows,
354 especially when using the advanced retrieval methods. Remember, what you get
355 when you use this module is what you would get when calling
356 search(...)->first;
357
358 NOTE ALSO:  The user info used to save the user to the session and to retrieve
359 it is the same regardless of what method of retrieval was used.  In short,
360 the value in the id field (see 'id_field' config item) is used to retrieve the 
361 user from the database upon restoring from the session.  When the DBIx::Class storage
362 module does this, it does so by doing a simple search using the id field.  In other
363 words, it will not use the same arguments you used to request the user initially. 
364 This is especially important to those using the advanced methods of user retrieval. 
365 If you need more complicated logic when reviving the user from the session, you will
366 most likely want to subclass the L<Catalyst::Plugin::Authentication::Store::DBIx::Class::User> class 
367 and provide your own for_session and from_session routines.
368
369 =back
370
371
372 =head1 METHODS
373
374 There are no publicly exported routines in the DBIx::Class authentication 
375 store (or indeed in most authentication stores)  However, below is a 
376 description of the routines required by L<Catalyst::Plugin::Authentication> 
377 for all authentication stores.  Please see the documentation for 
378 L<Catalyst::Plugin::Authentication::Internals> for more information.
379
380 =over 4
381
382 =item new ( $config, $app )
383
384 Constructs a new store object.
385
386 =item find_user ( $authinfo, $c ) 
387
388 Finds a user using the information provided in the $authinfo hashref and
389 returns the user, or undef on failure; This is usually called from the
390 Credential. This translates directly to a call to
391 L<Catalyst::Plugin::Authentication::Store::DBIx::Class::User>'s load() method.
392
393 =item for_session ( $c, $user )
394
395 Prepares a user to be stored in the session. Currently returns the value of
396 the user's id field - (as indicated by the 'id_field' config element)
397
398 =item from_session ( $c, $frozenuser)
399
400 Revives a user from the session based on the info provided in $frozenuser.
401 Currently treats $frozenuser as an id and retrieves a user with a matching id.
402
403 =item user_supports
404
405 Provides information about what the user object supports.  
406
407 =back 
408
409 =head1 NOTES
410
411 As of the current release, session storage consists of simply storing the user's
412 id in the session, and then using that same id to re-retrieve the users information
413 from the database upon restoration from the session.  More dynamic storage of
414 user information in the session is intended for a future release.
415
416 =head1 BUGS AND LIMITATIONS
417
418 None known currently, please email the author if you find any.
419
420 =head1 SEE ALSO
421
422 L<Catalyst::Plugin::Authentication>, L<Catalyst::Plugin::Authentication::Internals>,
423 and L<Catalyst::Plugin::Authorization::Roles>
424
425 =head1 AUTHOR
426
427 Jason Kuri (jayk@cpan.org)
428
429 =head1 LICENSE
430
431 Copyright (c) 2007 the aforementioned authors. All rights
432 reserved. This program is free software; you can redistribute
433 it and/or modify it under the same terms as Perl itself.
434
435 =cut