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