Adding 'None' option to password_type - allowing 'retrieve only' authentication
[catagits/Catalyst-Plugin-Authentication.git] / lib / Catalyst / Plugin / Authentication / Credential / Password.pm
CommitLineData
a90296d4 1#!/usr/bin/perl
2
3package Catalyst::Plugin::Authentication::Credential::Password;
45c7644b 4use base qw/Class::Accessor::Fast/;
a90296d4 5
6use strict;
7use warnings;
8
9use Scalar::Util ();
10use Catalyst::Exception ();
11use Digest ();
12
45c7644b 13BEGIN {
14 __PACKAGE__->mk_accessors(qw/_config/);
15}
16
54c8dc06 17sub new {
18 my ($class, $config, $app) = @_;
19
45c7644b 20 my $self = { _config => $config };
21 bless $self, $class;
22
23 $self->_config->{'password_field'} ||= 'password';
24 $self->_config->{'password_type'} ||= 'clear';
25 $self->_config->{'password_hash_type'} ||= 'SHA-1';
54c8dc06 26
45c7644b 27 my $passwordtype = $self->_config->{'password_type'};
28 if (!grep /$passwordtype/, ('clear', 'hashed', 'salted_hash', 'crypted', 'self_check')) {
29 Catalyst::Exception->throw(__PACKAGE__ . " used with unsupported password type: " . $self->_config->{'password_type'});
54c8dc06 30 }
45c7644b 31 return $self;
54c8dc06 32}
33
34sub authenticate {
35 my ( $self, $c, $authstore, $authinfo ) = @_;
36
45c7644b 37 ## because passwords may be in a hashed format, we have to make sure that we remove the
38 ## password_field before we pass it to the user routine, as some auth modules use
39 ## all data passed to them to find a matching user...
40 my $userfindauthinfo = {%{$authinfo}};
41 delete($userfindauthinfo->{$self->_config->{'password_field'}});
42
43 my $user_obj = $authstore->find_user($userfindauthinfo, $c);
649de93b 44 if (ref($user_obj)) {
54c8dc06 45 if ($self->check_password($user_obj, $authinfo)) {
46 return $user_obj;
a93f1197 47 }
54c8dc06 48 } else {
49 $c->log->debug("Unable to locate user matching user info provided");
50 return;
51 }
52}
a93f1197 53
54c8dc06 54sub check_password {
55 my ( $self, $user, $authinfo ) = @_;
56
45c7644b 57 if ($self->_config->{'password_type'} eq 'self_check') {
58 return $user->check_password($authinfo->{$self->_config->{'password_field'}});
54c8dc06 59 } else {
45c7644b 60 my $password = $authinfo->{$self->_config->{'password_field'}};
61 my $storedpassword = $user->get($self->_config->{'password_field'});
54c8dc06 62
17185597 63 if ($self->_config->{'password_type'} eq 'none') {
64 return 1;
65 } elsif ($self->_config->{'password_type'} eq 'clear') {
54c8dc06 66 return $password eq $storedpassword;
17185597 67 } elsif ($self->_config->{'password_type'} eq 'crypted') {
54c8dc06 68 return $storedpassword eq crypt( $password, $storedpassword );
45c7644b 69 } elsif ($self->_config->{'password_type'} eq 'salted_hash') {
54c8dc06 70 require Crypt::SaltedHash;
45c7644b 71 my $salt_len = $self->_config->{'password_salt_len'} ? $self->_config->{'password_salt_len'} : 0;
54c8dc06 72 return Crypt::SaltedHash->validate( $storedpassword, $password,
73 $salt_len );
45c7644b 74 } elsif ($self->_config->{'password_type'} eq 'hashed') {
54c8dc06 75
45c7644b 76 my $d = Digest->new( $self->_config->{'password_hash_type'} );
77 $d->add( $self->_config->{'password_pre_salt'} || '' );
54c8dc06 78 $d->add($password);
45c7644b 79 $d->add( $self->_config->{'password_post_salt'} || '' );
54c8dc06 80
81 my $computed = $d->clone()->digest;
82 my $b64computed = $d->clone()->b64digest;
83 return ( ( $computed eq $storedpassword )
84 || ( unpack( "H*", $computed ) eq $storedpassword )
85 || ( $b64computed eq $storedpassword)
86 || ( $b64computed.'=' eq $storedpassword) );
a93f1197 87 }
a90296d4 88 }
54c8dc06 89}
90
91## BACKWARDS COMPATIBILITY - all subs below here are deprecated
92## They are here for compatibility with older modules that use / inherit from C::P::A::Password
c5fbff80 93## login()'s existance relies rather heavily on the fact that only Credential::Password
54c8dc06 94## is being used as a credential. This may not be the case. This is only here
95## for backward compatibility. It will go away in a future version
96## login should not be used in new applications.
a90296d4 97
54c8dc06 98sub login {
99 my ( $c, $user, $password, @rest ) = @_;
100
101 unless (
102 defined($user)
103 or
104 $user = $c->request->param("login")
105 || $c->request->param("user")
106 || $c->request->param("username")
107 ) {
108 $c->log->debug(
109 "Can't login a user without a user object or user ID param")
110 if $c->debug;
111 return;
112 }
113
114 unless (
115 defined($password)
116 or
117 $password = $c->request->param("password")
118 || $c->request->param("passwd")
119 || $c->request->param("pass")
120 ) {
121 $c->log->debug("Can't login a user without a password")
122 if $c->debug;
123 return;
124 }
125
a93f1197 126 unless ( Scalar::Util::blessed($user)
85d1d92d 127 and $user->isa("Catalyst::Plugin::Authentication::User") )
a93f1197 128 {
2f7d8b59 129 if ( my $user_obj = $c->get_user( $user, $password, @rest ) ) {
a93f1197 130 $user = $user_obj;
131 }
132 else {
133 $c->log->debug("User '$user' doesn't exist in the default store")
134 if $c->debug;
135 return;
136 }
137 }
a90296d4 138
139 if ( $c->_check_password( $user, $password ) ) {
140 $c->set_authenticated($user);
a93f1197 141 $c->log->debug("Successfully authenticated user '$user'.")
142 if $c->debug;
a90296d4 143 return 1;
144 }
145 else {
a93f1197 146 $c->log->debug(
85d1d92d 147 "Failed to authenticate user '$user'. Reason: 'Incorrect password'")
a93f1197 148 if $c->debug;
a90296d4 149 return;
150 }
54c8dc06 151
a90296d4 152}
153
54c8dc06 154## also deprecated. Here for compatibility with older credentials which do not inherit from C::P::A::Password
a90296d4 155sub _check_password {
156 my ( $c, $user, $password ) = @_;
54c8dc06 157
a90296d4 158 if ( $user->supports(qw/password clear/) ) {
159 return $user->password eq $password;
160 }
161 elsif ( $user->supports(qw/password crypted/) ) {
162 my $crypted = $user->crypted_password;
163 return $crypted eq crypt( $password, $crypted );
164 }
165 elsif ( $user->supports(qw/password hashed/) ) {
166
167 my $d = Digest->new( $user->hash_algorithm );
168 $d->add( $user->password_pre_salt || '' );
169 $d->add($password);
170 $d->add( $user->password_post_salt || '' );
171
fd5b31a0 172 my $stored = $user->hashed_password;
173 my $computed = $d->clone()->digest;
174 my $b64computed = $d->clone()->b64digest;
a90296d4 175
176 return ( ( $computed eq $stored )
fd5b31a0 177 || ( unpack( "H*", $computed ) eq $stored )
178 || ( $b64computed eq $stored)
179 || ( $b64computed.'=' eq $stored) );
a90296d4 180 }
181 elsif ( $user->supports(qw/password salted_hash/) ) {
182 require Crypt::SaltedHash;
183
184 my $salt_len =
185 $user->can("password_salt_len") ? $user->password_salt_len : 0;
186
187 return Crypt::SaltedHash->validate( $user->hashed_password, $password,
188 $salt_len );
189 }
190 elsif ( $user->supports(qw/password self_check/) ) {
191
192 # while somewhat silly, this is to prevent code duplication
193 return $user->check_password($password);
194
195 }
196 else {
197 Catalyst::Exception->throw(
198 "The user object $user does not support any "
199 . "known password authentication mechanism." );
200 }
201}
202
203__PACKAGE__;
204
205__END__
206
207=pod
208
209=head1 NAME
210
211Catalyst::Plugin::Authentication::Credential::Password - Authenticate a user
212with a password.
213
214=head1 SYNOPSIS
215
216 use Catalyst qw/
217 Authentication
a90296d4 218 /;
219
8d3ca09c 220 package MyApp::Controller::Auth;
221
a90296d4 222 sub login : Local {
223 my ( $self, $c ) = @_;
224
89505ffb 225 $c->authenticate( { username => $c->req->param('username'),
226 password => $c->req->param('password') });
a90296d4 227 }
228
229=head1 DESCRIPTION
230
89505ffb 231This authentication credential checker takes authentication information
232(most often a username) and a password, and attempts to validate the password
233provided against the user retrieved from the store.
a90296d4 234
89505ffb 235=head1 CONFIGURATION
a90296d4 236
89505ffb 237 # example
238 __PACKAGE__->config->{authentication} =
239 {
240 default_realm => 'members',
241 realms => {
242 members => {
243
244 credential => {
245 class => 'Password',
246 password_field => 'password',
247 password_type => 'hashed',
248 password_hash_type => 'SHA-1'
249 },
250 ...
a90296d4 251
a90296d4 252
89505ffb 253The password module is capable of working with several different password
254encryption/hashing algorithms. The one the module uses is determined by the
255credential configuration.
a90296d4 256
c5fbff80 257Those who have used L<Catalyst::Plugin::Authentication> prior to the 0.10 release
258should note that the password field and type information is no longer part
259of the store configuration and is now part of the Password credential configuration.
260
89505ffb 261=over 4
a90296d4 262
89505ffb 263=item class
a90296d4 264
89505ffb 265The classname used for Credential. This is part of
266L<Catalyst::Plugin::Authentication> and is the method by which
267Catalyst::Plugin::Authentication::Credential::Password is loaded as the
268credential validator. For this module to be used, this must be set to
269'Password'.
a90296d4 270
89505ffb 271=item password_field
a90296d4 272
89505ffb 273The field in the user object that contains the password. This will vary
274depending on the storage class used, but is most likely something like
275'password'. In fact, this is so common that if this is left out of the config,
276it defaults to 'password'. This field is obtained from the user object using
277the get() method. Essentially: $user->get('passwordfieldname');
a90296d4 278
89505ffb 279=item password_type
9b09fd1c 280
89505ffb 281This sets the password type. Often passwords are stored in crypted or hashed
282formats. In order for the password module to verify the plaintext password
283passed in, it must be told what format the password will be in when it is retreived
284from the user object. The supported options are:
9b09fd1c 285
89505ffb 286=over 8
9b09fd1c 287
17185597 288=item none
289
290No password check is done. An attempt is made to retrieve the user based on
291the information provided in the $c->authenticate() call. If a user is found,
292authentication is considered to be successful.
293
89505ffb 294=item clear
9b09fd1c 295
89505ffb 296The password in user is in clear text and will be compared directly.
9b09fd1c 297
89505ffb 298=item self_check
9b09fd1c 299
89505ffb 300This option indicates that the password should be passed to the check_password()
301routine on the user object returned from the store.
9b09fd1c 302
89505ffb 303=item crypted
a90296d4 304
89505ffb 305The password in user is in UNIX crypt hashed format.
a90296d4 306
89505ffb 307=item salted_hash
a90296d4 308
89505ffb 309The password in user is in salted hash format, and will be validated
310using L<Crypt::SaltedHash>. If this password type is selected, you should
311also provide the B<password_salt_len> config element to define the salt length.
a90296d4 312
89505ffb 313=item hashed
a90296d4 314
89505ffb 315If the user object supports hashed passwords, they will be used in conjunction
316with L<Digest>. The following config elements affect the hashed configuration:
a90296d4 317
89505ffb 318=over 8
a90296d4 319
89505ffb 320=item password_hash_type
a90296d4 321
89505ffb 322The hash type used, passed directly to L<Digest/new>.
a90296d4 323
89505ffb 324=item password_pre_salt
a90296d4 325
89505ffb 326Any pre-salt data to be passed to L<Digest/add> before processing the password.
a90296d4 327
328=item password_post_salt
329
89505ffb 330Any post-salt data to be passed to L<Digest/add> after processing the password.
a90296d4 331
332=back
333
89505ffb 334=back
a90296d4 335
89505ffb 336=back
a90296d4 337
89505ffb 338=head1 USAGE
a90296d4 339
c5fbff80 340The Password credential module is very simple to use. Once configured as
341indicated above, authenticating using this module is simply a matter of
342calling $c->authenticate() with an authinfo hashref that includes the
343B<password> element. The password element should contain the password supplied
344by the user to be authenticated, in clear text. The other information supplied
345in the auth hash is ignored by the Password module, and simply passed to the
346auth store to be used to retrieve the user. An example call follows:
a90296d4 347
89505ffb 348 if ($c->authenticate({ username => $username,
349 password => $password} )) {
350 # authentication successful
351 } else {
352 # authentication failed
353 }
a90296d4 354
89505ffb 355=head1 METHODS
a90296d4 356
89505ffb 357There are no publicly exported routines in the Password module (or indeed in
358most credential modules.) However, below is a description of the routines
359required by L<Catalyst::Plugin::Authentication> for all credential modules.
a90296d4 360
361=over 4
362
89505ffb 363=item new ( $config, $app )
a90296d4 364
89505ffb 365Instantiate a new Password object using the configuration hash provided in
366$config. A reference to the application is provided as the second argument.
367Note to credential module authors: new() is called during the application's
368plugin setup phase, which is before the application specific controllers are
369loaded. The practical upshot of this is that things like $c->model(...) will
370not function as expected.
a90296d4 371
89505ffb 372=item authenticate ( $authinfo, $c )
a90296d4 373
89505ffb 374Try to log a user in, receives a hashref containing authentication information
375as the first argument, and the current context as the second.
a90296d4 376
89505ffb 377=back