X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fp5-CGI-Ex;a=blobdiff_plain;f=lib%2FCGI%2FEx%2FAuth.pm;h=0c384949746fcc3936c321fc1185069a6997b81c;hp=49c9a198e1ce3958713536d9a1bffe5118349306;hb=6c57b3331d84010b9e2031f8e3c8937c3117e8fc;hpb=4eee158dce82376f2f37de29d91c53f60a24aebe diff --git a/lib/CGI/Ex/Auth.pm b/lib/CGI/Ex/Auth.pm index 49c9a19..0c38494 100644 --- a/lib/CGI/Ex/Auth.pm +++ b/lib/CGI/Ex/Auth.pm @@ -7,7 +7,7 @@ CGI::Ex::Auth - Handle logins nicely. =cut ###----------------------------------------------------------------### -# Copyright 2006 - Paul Seamons # +# Copyright 2007 - Paul Seamons # # Distributed under the Perl Artistic License without warranty # ###----------------------------------------------------------------### @@ -18,7 +18,7 @@ use MIME::Base64 qw(encode_base64 decode_base64); use Digest::MD5 qw(md5_hex); use CGI::Ex; -$VERSION = '2.00'; +$VERSION = '2.11'; ###----------------------------------------------------------------### @@ -144,6 +144,10 @@ sub get_valid_auth { $self->login_print; my $data = $self->last_auth_data; eval { die defined($data) ? $data : "Requesting credentials" }; + + ### allow for a sleep to help prevent brute force + sleep($self->failed_sleep) if defined($data) && $data->error ne 'Login expired' && $self->failed_sleep; + return; } @@ -226,6 +230,7 @@ sub use_blowfish { shift->{'use_blowfish'} ||= '' } sub use_plaintext { my $s = shift; $s->use_crypt || ($s->{'use_plaintext'} ||= 0) } sub use_base64 { my $s = shift; $s->{'use_base64'} = 1 if ! defined $s->{'use_base64'}; $s->{'use_base64'} } sub expires_min { my $s = shift; $s->{'expires_min'} = 6 * 60 if ! defined $s->{'expires_min'}; $s->{'expires_min'} } +sub failed_sleep { shift->{'failed_sleep'} ||= 0 } sub logout_redirect { my $self = shift; @@ -310,7 +315,9 @@ sub login_hash_common { $self->key_time => $self->server_time, $self->key_payload => $self->generate_payload({%$data, login_form => 1}), $self->key_expires_min => $self->expires_min, - + text_user => $self->text_user, + text_pass => $self->text_pass, + text_save => $self->text_save, }; } @@ -332,7 +339,7 @@ sub verify_token { my $key; for my $armor ('none', 'base64', 'blowfish') { # try with and without base64 encoding my $copy = ($armor eq 'none') ? $token - : ($armor eq 'base64') ? decode_base64($token) + : ($armor eq 'base64') ? eval { local $^W; decode_base64($token) } : ($key = $self->use_blowfish) ? decrypt_blowfish($token, $key) : next; if ($copy =~ m|^ ([^/]+) / (\d+) / (-?\d+) / (.*) / ([a-fA-F0-9]{32}) (?: / (sh\.\d+\.\d+))? $|x) { @@ -378,6 +385,13 @@ sub verify_token { } elsif (! defined($pass = eval { $self->get_pass_by_user($data->{'user'}) })) { $data->add_data({details => $@}); $data->error('Could not get pass'); + } elsif (ref $pass eq 'HASH') { + my $extra = $pass; + $pass = exists($extra->{'real_pass'}) ? delete($extra->{'real_pass'}) + : exists($extra->{'password'}) ? delete($extra->{'password'}) + : do { $data->error('Data returned by get_pass_by_user did not contain real_pass or password'); undef }; + $data->error('Invalid login') if ! defined $pass && ! $data->error; + $data->add_data($extra); } return $data if $data->error; @@ -470,7 +484,8 @@ sub generate_token { if ( (defined($data->{'use_plaintext'}) ? $data->{'use_plaintext'} : $self->use_plaintext) # ->use_plaintext is true if ->use_crypt is || (defined($data->{'use_crypt'}) && $data->{'use_crypt'}) || (defined($data->{'type'}) && $data->{'type'} eq 'crypt')) { - $token = $data->{'user'} .'/'. $data->{'real_pass'}; + my $pass = defined($data->{'test_pass'}) ? $data->{'test_pass'} : $data->{'real_pass'}; + $token = $data->{'user'} .'/'. $pass; ### all other types go to cram - secure_hash_cram, cram, plaintext and md5 } else { @@ -617,16 +632,16 @@ sub login_form { - + - + @@ -640,6 +655,10 @@ sub login_form { }; } +sub text_user { my $self = shift; return defined($self->{'text_user'}) ? $self->{'text_user'} : 'Username:' } +sub text_pass { my $self = shift; return defined($self->{'text_pass'}) ? $self->{'text_pass'} : 'Password:' } +sub text_save { my $self = shift; return defined($self->{'text_save'}) ? $self->{'text_save'} : 'Save Password ?' } + sub login_script { return q { [%~ IF ! use_plaintext %] @@ -710,18 +729,38 @@ __END__ =head1 SYNOPSIS - ### authorize the user - my $auth = $self->get_valid_auth({ - get_pass_by_user => \&get_pass_by_user, - }); + use CGI::Ex::Auth; + ### authorize the user + my $auth = CGI::Ex::Auth->get_valid_auth({ + get_pass_by_user => \&get_pass_by_user, + }); - sub get_pass_by_user { - my $auth = shift; - my $user = shift; - my $pass = some_way_of_getting_password($user); - return $pass; - } + + sub get_pass_by_user { + my $auth = shift; + my $user = shift; + my $pass = some_way_of_getting_password($user); + return $pass; + } + + ### OR - if you are using a OO based CGI or Application + + sub require_authentication { + my $self = shift; + + return $self->{'auth'} = CGI::Ex::Auth->get_valid_auth({ + get_pass_by_user => sub { + my ($auth, $user) = @_; + return $self->get_pass($user); + }, + }); + } + + sub get_pass { + my ($self, $user) = @_; + return $self->loopup_and_cache_pass($user); + } =head1 DESCRIPTION @@ -792,6 +831,9 @@ defined separately. secure_hash_keys template_args template_include_path + text_user + text_pass + text_save use_base64 use_blowfish use_crypt @@ -941,6 +983,9 @@ Passed to the template swapped during login_print. $self->key_time # $self->server_time, # the server's time $self->key_payload # $data->{'payload'} # the payload (if any) $self->key_expires_min # $self->expires_min # how many minutes crams are valid + text_user # $self->text_user # template text Username: + text_pass # $self->text_pass # template text Password: + text_save # $self->text_save # template text Save Password ? =item C @@ -987,6 +1032,12 @@ This value will have no effect when use_plaintext or use_crypt is set. A value of -1 means no expiration. +=item C + +Number of seconds to sleep if the passed tokens are invalid. Does not apply +if validation failed because of expired tokens. Default value is 0. +Setting to 0 disables any sleeping. + =item C The name of the html login form to attach the javascript to. Default is "cea_form". @@ -1017,6 +1068,27 @@ valid password for the user. It can always return plaintext. If use_crypt is enabled, it should return the crypted password. If use_plaintext and use_crypt are not enabled, it may return the md5 sum of the password. + get_pass_by_user => sub { + my ($auth_obj, $user) = @_; + my $pass = $some_obj->get_pass({user => $user}); + return $pass; + } + +Alternately, get_pass_by_user may return a hashref of data items that +will be added to the data object if the token is valid. The hashref +must also contain a key named real_pass or password that contains the +password. Note that keys passed back in the hashref that are already +in the data object will override those in the data object. + + get_pass_by_user => sub { + my ($auth_obj, $user) = @_; + my ($pass, $user_id) = $some_obj->get_pass({user => $user}); + return { + password => $pass, + user_id => $user_id, + }; + } + =item C Returns a CGI::Ex object. @@ -1069,8 +1141,18 @@ Contains javascript that will attach to the form from login_form. This script is capable of taking the login_fields and creating an md5 cram which prevents the password from being passed plaintext. +=item C + +The text items shown in the default login template. The default values are: + + text_user "Username:" + text_pass "Password:" + text_save "Save Password ?" + +=back + =head1 AUTHORS -Paul Seamons +Paul Seamons =cut