Not needed in BEGIN
[catagits/Catalyst-Plugin-Authentication.git] / lib / Catalyst / Authentication / Credential / Password.pm
CommitLineData
5c5af345 1package Catalyst::Authentication::Credential::Password;
2
3use strict;
4use warnings;
5
6use base qw/Class::Accessor::Fast/;
7
8use Scalar::Util ();
9use Catalyst::Exception ();
10use Digest ();
11
6c9482fe 12__PACKAGE__->mk_accessors(qw/_config realm/);
5c5af345 13
14sub new {
15 my ($class, $config, $app, $realm) = @_;
16
106913db 17 my $self = { _config => $config };
5c5af345 18 bless $self, $class;
19
20 $self->realm($realm);
21
106913db 22 $self->_config->{'password_field'} ||= 'password';
23 $self->_config->{'password_type'} ||= 'clear';
24 $self->_config->{'password_hash_type'} ||= 'SHA-1';
5c5af345 25
106913db 26 my $passwordtype = $self->_config->{'password_type'};
5c5af345 27 if (!grep /$passwordtype/, ('none', 'clear', 'hashed', 'salted_hash', 'crypted', 'self_check')) {
106913db 28 Catalyst::Exception->throw(__PACKAGE__ . " used with unsupported password type: " . $self->_config->{'password_type'});
5c5af345 29 }
30 return $self;
31}
32
33sub authenticate {
34 my ( $self, $c, $realm, $authinfo ) = @_;
35
36 ## because passwords may be in a hashed format, we have to make sure that we remove the
37 ## password_field before we pass it to the user routine, as some auth modules use
38 ## all data passed to them to find a matching user...
39 my $userfindauthinfo = {%{$authinfo}};
106913db 40 delete($userfindauthinfo->{$self->_config->{'password_field'}});
5c5af345 41
42 my $user_obj = $realm->find_user($userfindauthinfo, $c);
43 if (ref($user_obj)) {
44 if ($self->check_password($user_obj, $authinfo)) {
45 return $user_obj;
46 }
47 } else {
48 $c->log->debug("Unable to locate user matching user info provided") if $c->debug;
49 return;
50 }
51}
52
53sub check_password {
54 my ( $self, $user, $authinfo ) = @_;
55
106913db 56 if ($self->_config->{'password_type'} eq 'self_check') {
57 return $user->check_password($authinfo->{$self->_config->{'password_field'}});
5c5af345 58 } else {
106913db 59 my $password = $authinfo->{$self->_config->{'password_field'}};
60 my $storedpassword = $user->get($self->_config->{'password_field'});
5c5af345 61
106913db 62 if ($self->_config->{'password_type'} eq 'none') {
5c5af345 63 return 1;
106913db 64 } elsif ($self->_config->{'password_type'} eq 'clear') {
1ae2143a 65 # FIXME - Should we warn in the $storedpassword undef case,
66 # as the user probably fluffed the config?
67 return unless defined $storedpassword;
5c5af345 68 return $password eq $storedpassword;
106913db 69 } elsif ($self->_config->{'password_type'} eq 'crypted') {
5c5af345 70 return $storedpassword eq crypt( $password, $storedpassword );
106913db 71 } elsif ($self->_config->{'password_type'} eq 'salted_hash') {
5c5af345 72 require Crypt::SaltedHash;
106913db 73 my $salt_len = $self->_config->{'password_salt_len'} ? $self->_config->{'password_salt_len'} : 0;
5c5af345 74 return Crypt::SaltedHash->validate( $storedpassword, $password,
75 $salt_len );
106913db 76 } elsif ($self->_config->{'password_type'} eq 'hashed') {
5c5af345 77
106913db 78 my $d = Digest->new( $self->_config->{'password_hash_type'} );
79 $d->add( $self->_config->{'password_pre_salt'} || '' );
5c5af345 80 $d->add($password);
106913db 81 $d->add( $self->_config->{'password_post_salt'} || '' );
5c5af345 82
83 my $computed = $d->clone()->digest;
84 my $b64computed = $d->clone()->b64digest;
85 return ( ( $computed eq $storedpassword )
86 || ( unpack( "H*", $computed ) eq $storedpassword )
87 || ( $b64computed eq $storedpassword)
88 || ( $b64computed.'=' eq $storedpassword) );
89 }
90 }
91}
92
5c5af345 93__PACKAGE__;
94
95__END__
96
97=pod
98
99=head1 NAME
100
101Catalyst::Authentication::Credential::Password - Authenticate a user
102with a password.
103
104=head1 SYNOPSIS
105
106 use Catalyst qw/
107 Authentication
108 /;
109
110 package MyApp::Controller::Auth;
111
112 sub login : Local {
113 my ( $self, $c ) = @_;
114
115 $c->authenticate( { username => $c->req->param('username'),
116 password => $c->req->param('password') });
117 }
118
119=head1 DESCRIPTION
120
121This authentication credential checker takes authentication information
122(most often a username) and a password, and attempts to validate the password
123provided against the user retrieved from the store.
124
125=head1 CONFIGURATION
126
127 # example
47074ecb 128 __PACKAGE__->config('Plugin::Authentication' =>
5c5af345 129 {
130 default_realm => 'members',
131 realms => {
132 members => {
133
134 credential => {
135 class => 'Password',
136 password_field => 'password',
137 password_type => 'hashed',
138 password_hash_type => 'SHA-1'
139 },
140 ...
141
142
143The password module is capable of working with several different password
144encryption/hashing algorithms. The one the module uses is determined by the
145credential configuration.
146
147Those who have used L<Catalyst::Plugin::Authentication> prior to the 0.10 release
148should note that the password field and type information is no longer part
149of the store configuration and is now part of the Password credential configuration.
150
151=over 4
152
153=item class
154
155The classname used for Credential. This is part of
156L<Catalyst::Plugin::Authentication> and is the method by which
157Catalyst::Authentication::Credential::Password is loaded as the
158credential validator. For this module to be used, this must be set to
159'Password'.
160
161=item password_field
162
163The field in the user object that contains the password. This will vary
164depending on the storage class used, but is most likely something like
165'password'. In fact, this is so common that if this is left out of the config,
166it defaults to 'password'. This field is obtained from the user object using
8a7bd676 167the get() method. Essentially: $user->get('passwordfieldname');
168B<NOTE> If the password_field is something other than 'password', you must
169be sure to use that same field name when calling $c->authenticate().
5c5af345 170
171=item password_type
172
173This sets the password type. Often passwords are stored in crypted or hashed
174formats. In order for the password module to verify the plaintext password
175passed in, it must be told what format the password will be in when it is retreived
176from the user object. The supported options are:
177
178=over 8
179
180=item none
181
182No password check is done. An attempt is made to retrieve the user based on
183the information provided in the $c->authenticate() call. If a user is found,
184authentication is considered to be successful.
185
186=item clear
187
188The password in user is in clear text and will be compared directly.
189
190=item self_check
191
192This option indicates that the password should be passed to the check_password()
193routine on the user object returned from the store.
194
195=item crypted
196
197The password in user is in UNIX crypt hashed format.
198
199=item salted_hash
200
201The password in user is in salted hash format, and will be validated
202using L<Crypt::SaltedHash>. If this password type is selected, you should
203also provide the B<password_salt_len> config element to define the salt length.
204
205=item hashed
206
207If the user object supports hashed passwords, they will be used in conjunction
208with L<Digest>. The following config elements affect the hashed configuration:
209
210=over 8
211
212=item password_hash_type
213
214The hash type used, passed directly to L<Digest/new>.
215
216=item password_pre_salt
217
218Any pre-salt data to be passed to L<Digest/add> before processing the password.
219
220=item password_post_salt
221
222Any post-salt data to be passed to L<Digest/add> after processing the password.
223
224=back
225
226=back
227
228=back
229
230=head1 USAGE
231
232The Password credential module is very simple to use. Once configured as
233indicated above, authenticating using this module is simply a matter of
234calling $c->authenticate() with an authinfo hashref that includes the
235B<password> element. The password element should contain the password supplied
236by the user to be authenticated, in clear text. The other information supplied
237in the auth hash is ignored by the Password module, and simply passed to the
238auth store to be used to retrieve the user. An example call follows:
239
240 if ($c->authenticate({ username => $username,
241 password => $password} )) {
242 # authentication successful
243 } else {
244 # authentication failed
245 }
246
247=head1 METHODS
248
249There are no publicly exported routines in the Password module (or indeed in
250most credential modules.) However, below is a description of the routines
251required by L<Catalyst::Plugin::Authentication> for all credential modules.
252
8a7bd676 253=head2 new( $config, $app, $realm )
5c5af345 254
255Instantiate a new Password object using the configuration hash provided in
256$config. A reference to the application is provided as the second argument.
257Note to credential module authors: new() is called during the application's
258plugin setup phase, which is before the application specific controllers are
259loaded. The practical upshot of this is that things like $c->model(...) will
260not function as expected.
261
262=head2 authenticate( $authinfo, $c )
263
264Try to log a user in, receives a hashref containing authentication information
265as the first argument, and the current context as the second.
266
267=head2 check_password( )
268
5c5af345 269=cut