Version 0.1300
[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
5000f545 241sub AUTOLOAD {
242 my $self = shift;
243 (my $method) = (our $AUTOLOAD =~ /([^:]+)$/);
244 return if $method eq "DESTROY";
245
8e553a46 246 if (my $code = $self->_user->can($method)) {
247 return $self->_user->$code(@_);
248 }
249 elsif (my $accessor =
250 try { $self->_user->result_source->column_info($method)->{accessor} }) {
251 return $self->_user->$accessor(@_);
252 } else {
253 # XXX this should also throw
254 return undef;
255 }
5000f545 256}
257
6d4bea88 258__PACKAGE__->meta->make_immutable(inline_constructor => 0);
259
5000f545 2601;
261__END__
262
263=head1 NAME
264
6727afe2 265Catalyst::Authentication::Store::DBIx::Class::User - The backing user
266class for the Catalyst::Authentication::Store::DBIx::Class storage
c1d29ab7 267module.
5000f545 268
269=head1 VERSION
270
f867a93b 271This documentation refers to version 0.1300.
5000f545 272
273=head1 SYNOPSIS
274
c1d29ab7 275Internal - not used directly, please see
6727afe2 276L<Catalyst::Authentication::Store::DBIx::Class> for details on how to
c1d29ab7 277use this module. If you need more information than is present there, read the
278source.
93102ff5 279
4bebb973 280
5000f545 281
282=head1 DESCRIPTION
283
6727afe2 284The Catalyst::Authentication::Store::DBIx::Class::User class implements user storage
c1d29ab7 285connected to an underlying DBIx::Class schema object.
5000f545 286
287=head1 SUBROUTINES / METHODS
288
4bebb973 289=head2 new
5000f545 290
c1d29ab7 291Constructor.
5000f545 292
4bebb973 293=head2 load ( $authinfo, $c )
5000f545 294
c1d29ab7 295Retrieves a user from storage using the information provided in $authinfo.
5000f545 296
c1d29ab7 297=head2 supported_features
5000f545 298
c1d29ab7 299Indicates the features supported by this class. These are currently Roles and Session.
5000f545 300
301=head2 roles
302
c1d29ab7 303Returns an array of roles associated with this user, if roles are configured for this user class.
5000f545 304
305=head2 for_session
306
4bebb973 307Returns a serialized user for storage in the session.
5000f545 308
fbe76043 309=head2 from_session
310
4bebb973 311Revives a serialized user from storage in the session.
fbe76043 312
c1d29ab7 313=head2 get ( $fieldname )
5000f545 314
4bebb973 315Returns the value of $fieldname for the user in question. Roughly translates to a call to
c1d29ab7 316the DBIx::Class::Row's get_column( $fieldname ) routine.
5000f545 317
4bebb973 318=head2 get_object
5000f545 319
c1d29ab7 320Retrieves the DBIx::Class object that corresponds to this user
5000f545 321
322=head2 obj (method)
323
c1d29ab7 324Synonym for get_object
5000f545 325
69100364 326=head2 auto_create
327
4bebb973 328This is called when the auto_create_user option is turned on in
329Catalyst::Plugin::Authentication and a user matching the authinfo provided is not found.
4117c46f 330By default, this will call the C<auto_create()> method of the resultset associated
69100364 331with this object. It is up to you to implement that method.
332
333=head2 auto_update
334
4117c46f 335This is called when the auto_update_user option is turned on in
cccbdd0a 336Catalyst::Plugin::Authentication. Note that by default the DBIx::Class store
4117c46f 337uses every field in the authinfo hash to match the user. This means any
50631330 338information you provide with the intent to update must be ignored during the
339user search process. Otherwise the information will most likely cause the user
340record to not be found. To ignore fields in the search process, you
341have to add the fields you wish to update to the 'ignore_fields_in_find'
342authinfo element. Alternately, you can use one of the advanced row retrieval
343methods (searchargs or resultset).
4117c46f 344
345By default, auto_update will call the C<auto_update()> method of the
346DBIx::Class::Row object associated with the user. It is up to you to implement
347that method (probably in your schema file)
69100364 348
5000f545 349=head1 BUGS AND LIMITATIONS
350
351None known currently, please email the author if you find any.
352
353=head1 AUTHOR
354
fbe76043 355Jason Kuri (jayk@cpan.org)
5000f545 356
c1d29ab7 357=head1 LICENSE
5000f545 358
c1d29ab7 359Copyright (c) 2007 the aforementioned authors. All rights
360reserved. This program is free software; you can redistribute
361it and/or modify it under the same terms as Perl itself.
5000f545 362
363=cut