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