Commit | Line | Data |
6727afe2 |
1 | package Catalyst::Authentication::Store::DBIx::Class::User; |
5000f545 |
2 | |
3 | use strict; |
4 | use warnings; |
6727afe2 |
5 | use base qw/Catalyst::Authentication::User/; |
ff7203cb |
6 | use base qw/Class::Accessor::Fast/; |
7 | |
8 | BEGIN { |
9 | __PACKAGE__->mk_accessors(qw/config resultset _user _roles/); |
10 | } |
5000f545 |
11 | |
12 | sub new { |
ff7203cb |
13 | my ( $class, $config, $c) = @_; |
5000f545 |
14 | |
f55cb81e |
15 | if (!defined($config->{'user_model'})) { |
16 | $config->{'user_model'} = $config->{'user_class'}; |
17 | } |
18 | |
ff7203cb |
19 | my $self = { |
f55cb81e |
20 | resultset => $c->model($config->{'user_model'}), |
ff7203cb |
21 | config => $config, |
078727e0 |
22 | _roles => undef, |
ff7203cb |
23 | _user => undef |
24 | }; |
4bebb973 |
25 | |
5000f545 |
26 | bless $self, $class; |
f55cb81e |
27 | |
4bebb973 |
28 | |
f55cb81e |
29 | |
30 | if (not $self->{'resultset'}) { |
31 | Catalyst::Exception->throw("\$c->model('${ \$self->config->{user_model} }') did not return a resultset. Did you set user_model correctly?"); |
32 | } |
33 | |
f26005a7 |
34 | ## Note to self- add handling of multiple-column primary keys. |
93102ff5 |
35 | if (!exists($self->config->{'id_field'})) { |
ad93b3e9 |
36 | my @pks = $self->{'resultset'}->result_source->primary_columns; |
37 | if ($#pks == 0) { |
38 | $self->config->{'id_field'} = $pks[0]; |
39 | } else { |
40 | Catalyst::Exception->throw("user table does not contain a single primary key column - please specify 'id_field' in config!"); |
41 | } |
42 | } |
f55cb81e |
43 | |
ad93b3e9 |
44 | if (!$self->{'resultset'}->result_source->has_column($self->config->{'id_field'})) { |
45 | Catalyst::Exception->throw("id_field set to " . $self->config->{'id_field'} . " but user table has no column by that name!"); |
93102ff5 |
46 | } |
4bebb973 |
47 | |
5000f545 |
48 | ## if we have lazyloading turned on - we should not query the DB unless something gets read. |
49 | ## that's the idea anyway - still have to work out how to manage that - so for now we always force |
50 | ## lazyload to off. |
ff7203cb |
51 | $self->config->{lazyload} = 0; |
4bebb973 |
52 | |
ff7203cb |
53 | # if (!$self->config->{lazyload}) { |
54 | # return $self->load_user($authinfo, $c); |
55 | # } else { |
56 | # ## what do we do with a lazyload? |
4bebb973 |
57 | # ## presumably this is coming out of session storage. |
ff7203cb |
58 | # ## use $authinfo to fill in the user in that case? |
59 | # } |
60 | |
5000f545 |
61 | return $self; |
62 | } |
63 | |
64 | |
ff7203cb |
65 | sub load { |
5000f545 |
66 | my ($self, $authinfo, $c) = @_; |
4bebb973 |
67 | |
ff7203cb |
68 | my $dbix_class_config = 0; |
4bebb973 |
69 | |
ff7203cb |
70 | if (exists($authinfo->{'dbix_class'})) { |
71 | $authinfo = $authinfo->{'dbix_class'}; |
72 | $dbix_class_config = 1; |
73 | } |
4bebb973 |
74 | |
5000f545 |
75 | ## User can provide an arrayref containing the arguments to search on the user class. |
ff7203cb |
76 | ## or even provide a prepared resultset, allowing maximum flexibility for user retreival. |
4bebb973 |
77 | ## these options are only available when using the dbix_class authinfo hash. |
ff7203cb |
78 | if ($dbix_class_config && exists($authinfo->{'resultset'})) { |
79 | $self->_user($authinfo->{'resultset'}->first); |
80 | } elsif ($dbix_class_config && exists($authinfo->{'searchargs'})) { |
4bebb973 |
81 | $self->_user($self->resultset->search(@{$authinfo->{'searchargs'}})->first); |
5000f545 |
82 | } else { |
83 | ## merge the ignore fields array into a hash - so we can do an easy check while building the query |
4bebb973 |
84 | my %ignorefields = map { $_ => 1} @{$self->config->{'ignore_fields_in_find'}}; |
5000f545 |
85 | my $searchargs = {}; |
4bebb973 |
86 | |
5000f545 |
87 | # now we walk all the fields passed in, and build up a search hash. |
88 | foreach my $key (grep {!$ignorefields{$_}} keys %{$authinfo}) { |
ff7203cb |
89 | if ($self->resultset->result_source->has_column($key)) { |
5000f545 |
90 | $searchargs->{$key} = $authinfo->{$key}; |
91 | } |
ff7203cb |
92 | } |
87920e64 |
93 | if (keys %{$searchargs}) { |
94 | $self->_user($self->resultset->search($searchargs)->first); |
95 | } else { |
c388ac9d |
96 | Catalyst::Exception->throw("Failed to load user data. You passed [" . join(',', keys %{$authinfo}) . "] to authenticate() but your user source (" . $self->config->{'user_model'} . ") only has these columns: [" . join( ",", $self->resultset->result_source->columns ) . "] Check your authenticate() call."); |
87920e64 |
97 | } |
ff7203cb |
98 | } |
99 | |
100 | if ($self->get_object) { |
93102ff5 |
101 | return $self; |
ff7203cb |
102 | } else { |
103 | return undef; |
5000f545 |
104 | } |
5000f545 |
105 | |
106 | } |
107 | |
108 | sub supported_features { |
109 | my $self = shift; |
5000f545 |
110 | |
111 | return { |
5000f545 |
112 | session => 1, |
113 | roles => 1, |
114 | }; |
115 | } |
116 | |
117 | |
118 | sub roles { |
b5c13b47 |
119 | my ( $self ) = shift; |
120 | ## this used to load @wantedroles - but that doesn't seem to be used by the roles plugin, so I dropped it. |
5000f545 |
121 | |
122 | ## shortcut if we have already retrieved them |
ff7203cb |
123 | if (ref $self->_roles eq 'ARRAY') { |
124 | return(@{$self->_roles}); |
5000f545 |
125 | } |
4bebb973 |
126 | |
5000f545 |
127 | my @roles = (); |
ff7203cb |
128 | if (exists($self->config->{'role_column'})) { |
ad93b3e9 |
129 | my $role_data = $self->get($self->config->{'role_column'}); |
4bebb973 |
130 | if ($role_data) { |
87920e64 |
131 | @roles = split /[\s,\|]+/, $self->get($self->config->{'role_column'}); |
ad93b3e9 |
132 | } |
078727e0 |
133 | $self->_roles(\@roles); |
ff7203cb |
134 | } elsif (exists($self->config->{'role_relation'})) { |
135 | my $relation = $self->config->{'role_relation'}; |
136 | if ($self->_user->$relation->result_source->has_column($self->config->{'role_field'})) { |
078727e0 |
137 | @roles = map { $_->get_column($self->config->{'role_field'}) } $self->_user->$relation->search(undef, { columns => [ $self->config->{'role_field'}]})->all(); |
138 | $self->_roles(\@roles); |
5000f545 |
139 | } else { |
ff7203cb |
140 | Catalyst::Exception->throw("role table does not have a column called " . $self->config->{'role_field'}); |
5000f545 |
141 | } |
5000f545 |
142 | } else { |
143 | Catalyst::Exception->throw("user->roles accessed, but no role configuration found"); |
144 | } |
145 | |
ff7203cb |
146 | return @{$self->_roles}; |
5000f545 |
147 | } |
148 | |
149 | sub for_session { |
ff7203cb |
150 | my $self = shift; |
4bebb973 |
151 | |
f26005a7 |
152 | #return $self->get($self->config->{'id_field'}); |
4bebb973 |
153 | |
be7c0c30 |
154 | #my $frozenuser = $self->_user->result_source->schema->freeze( $self->_user ); |
155 | #return $frozenuser; |
4bebb973 |
156 | |
f26005a7 |
157 | my %userdata = $self->_user->get_columns(); |
158 | return \%userdata; |
ff7203cb |
159 | } |
160 | |
161 | sub from_session { |
162 | my ($self, $frozenuser, $c) = @_; |
4bebb973 |
163 | |
be7c0c30 |
164 | #my $obj = $self->resultset->result_source->schema->thaw( $frozenuser ); |
165 | #$self->_user($obj); |
4bebb973 |
166 | |
be7c0c30 |
167 | #if (!exists($self->config->{'use_userdata_from_session'}) || $self->config->{'use_userdata_from_session'} == 0) { |
168 | # $self->_user->discard_changes(); |
169 | # } |
4bebb973 |
170 | # |
be7c0c30 |
171 | # return $self; |
4bebb973 |
172 | # |
be7c0c30 |
173 | ## if use_userdata_from_session is defined in the config, we fill in the user data from the session. |
174 | if (exists($self->config->{'use_userdata_from_session'}) && $self->config->{'use_userdata_from_session'} != 0) |
175 | { |
f26005a7 |
176 | my $obj = $self->resultset->new_result({ %$frozenuser }); |
177 | $obj->in_storage(1); |
178 | $self->_user($obj); |
179 | return $self; |
180 | } else { |
be7c0c30 |
181 | my $id; |
182 | if (ref($frozenuser) eq 'HASH') { |
183 | $id = $frozenuser->{$self->config->{'id_field'}}; |
184 | } else { |
185 | $id = $frozenuser; |
186 | } |
187 | return $self->load( { $self->config->{'id_field'} => $id }, $c); |
f26005a7 |
188 | } |
5000f545 |
189 | } |
190 | |
191 | sub get { |
192 | my ($self, $field) = @_; |
4bebb973 |
193 | |
ff7203cb |
194 | if ($self->_user->can($field)) { |
195 | return $self->_user->$field; |
5000f545 |
196 | } else { |
197 | return undef; |
198 | } |
199 | } |
200 | |
c1d29ab7 |
201 | sub get_object { |
f26005a7 |
202 | my ($self, $force) = @_; |
4bebb973 |
203 | |
f26005a7 |
204 | if ($force) { |
205 | $self->_user->discard_changes; |
206 | } |
207 | |
c1d29ab7 |
208 | return $self->_user; |
5000f545 |
209 | } |
210 | |
c1d29ab7 |
211 | sub obj { |
f26005a7 |
212 | my ($self, $force) = @_; |
4bebb973 |
213 | |
f26005a7 |
214 | return $self->get_object($force); |
5000f545 |
215 | } |
216 | |
69100364 |
217 | sub auto_create { |
218 | my $self = shift; |
219 | $self->_user( $self->resultset->auto_create( @_ ) ); |
220 | return $self; |
221 | } |
222 | |
223 | sub auto_update { |
224 | my $self = shift; |
225 | $self->_user->auto_update( @_ ); |
226 | } |
227 | |
5000f545 |
228 | sub AUTOLOAD { |
229 | my $self = shift; |
230 | (my $method) = (our $AUTOLOAD =~ /([^:]+)$/); |
231 | return if $method eq "DESTROY"; |
232 | |
ff7203cb |
233 | $self->_user->$method(@_); |
5000f545 |
234 | } |
235 | |
236 | 1; |
237 | __END__ |
238 | |
239 | =head1 NAME |
240 | |
6727afe2 |
241 | Catalyst::Authentication::Store::DBIx::Class::User - The backing user |
242 | class for the Catalyst::Authentication::Store::DBIx::Class storage |
c1d29ab7 |
243 | module. |
5000f545 |
244 | |
245 | =head1 VERSION |
246 | |
ad93b3e9 |
247 | This documentation refers to version 0.10. |
5000f545 |
248 | |
249 | =head1 SYNOPSIS |
250 | |
c1d29ab7 |
251 | Internal - not used directly, please see |
6727afe2 |
252 | L<Catalyst::Authentication::Store::DBIx::Class> for details on how to |
c1d29ab7 |
253 | use this module. If you need more information than is present there, read the |
254 | source. |
93102ff5 |
255 | |
4bebb973 |
256 | |
5000f545 |
257 | |
258 | =head1 DESCRIPTION |
259 | |
6727afe2 |
260 | The Catalyst::Authentication::Store::DBIx::Class::User class implements user storage |
c1d29ab7 |
261 | connected to an underlying DBIx::Class schema object. |
5000f545 |
262 | |
263 | =head1 SUBROUTINES / METHODS |
264 | |
4bebb973 |
265 | =head2 new |
5000f545 |
266 | |
c1d29ab7 |
267 | Constructor. |
5000f545 |
268 | |
4bebb973 |
269 | =head2 load ( $authinfo, $c ) |
5000f545 |
270 | |
c1d29ab7 |
271 | Retrieves a user from storage using the information provided in $authinfo. |
5000f545 |
272 | |
c1d29ab7 |
273 | =head2 supported_features |
5000f545 |
274 | |
c1d29ab7 |
275 | Indicates the features supported by this class. These are currently Roles and Session. |
5000f545 |
276 | |
277 | =head2 roles |
278 | |
c1d29ab7 |
279 | Returns an array of roles associated with this user, if roles are configured for this user class. |
5000f545 |
280 | |
281 | =head2 for_session |
282 | |
4bebb973 |
283 | Returns a serialized user for storage in the session. |
5000f545 |
284 | |
fbe76043 |
285 | =head2 from_session |
286 | |
4bebb973 |
287 | Revives a serialized user from storage in the session. |
fbe76043 |
288 | |
c1d29ab7 |
289 | =head2 get ( $fieldname ) |
5000f545 |
290 | |
4bebb973 |
291 | Returns the value of $fieldname for the user in question. Roughly translates to a call to |
c1d29ab7 |
292 | the DBIx::Class::Row's get_column( $fieldname ) routine. |
5000f545 |
293 | |
4bebb973 |
294 | =head2 get_object |
5000f545 |
295 | |
c1d29ab7 |
296 | Retrieves the DBIx::Class object that corresponds to this user |
5000f545 |
297 | |
298 | =head2 obj (method) |
299 | |
c1d29ab7 |
300 | Synonym for get_object |
5000f545 |
301 | |
69100364 |
302 | =head2 auto_create |
303 | |
4bebb973 |
304 | This is called when the auto_create_user option is turned on in |
305 | Catalyst::Plugin::Authentication and a user matching the authinfo provided is not found. |
4117c46f |
306 | By default, this will call the C<auto_create()> method of the resultset associated |
69100364 |
307 | with this object. It is up to you to implement that method. |
308 | |
309 | =head2 auto_update |
310 | |
4117c46f |
311 | This is called when the auto_update_user option is turned on in |
cccbdd0a |
312 | Catalyst::Plugin::Authentication. Note that by default the DBIx::Class store |
4117c46f |
313 | uses every field in the authinfo hash to match the user. This means any |
50631330 |
314 | information you provide with the intent to update must be ignored during the |
315 | user search process. Otherwise the information will most likely cause the user |
316 | record to not be found. To ignore fields in the search process, you |
317 | have to add the fields you wish to update to the 'ignore_fields_in_find' |
318 | authinfo element. Alternately, you can use one of the advanced row retrieval |
319 | methods (searchargs or resultset). |
4117c46f |
320 | |
321 | By default, auto_update will call the C<auto_update()> method of the |
322 | DBIx::Class::Row object associated with the user. It is up to you to implement |
323 | that method (probably in your schema file) |
69100364 |
324 | |
5000f545 |
325 | =head1 BUGS AND LIMITATIONS |
326 | |
327 | None known currently, please email the author if you find any. |
328 | |
329 | =head1 AUTHOR |
330 | |
fbe76043 |
331 | Jason Kuri (jayk@cpan.org) |
5000f545 |
332 | |
c1d29ab7 |
333 | =head1 LICENSE |
5000f545 |
334 | |
c1d29ab7 |
335 | Copyright (c) 2007 the aforementioned authors. All rights |
336 | reserved. This program is free software; you can redistribute |
337 | it and/or modify it under the same terms as Perl itself. |
5000f545 |
338 | |
339 | =cut |