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