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