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