File Coverage

blib/lib/Catalyst/Authentication/Credential/Password.pm
Criterion Covered Total %
statement 43 56 76.7
branch 10 24 41.6
condition 3 13 23.0
subroutine 8 8 100.0
pod 3 3 100.0
total 67 104 64.4


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