From a2f50b1efd2bc986617e1de5f5a0bfd8a2953b0e Mon Sep 17 00:00:00 2001 From: Paul Seamons Date: Fri, 5 Oct 2007 00:00:00 +0000 Subject: [PATCH] CGI::Ex 2.19 --- Changes | 10 + META.yml | 23 ++- lib/CGI/Ex.pm | 2 +- lib/CGI/Ex/App.pm | 19 +- lib/CGI/Ex/App.pod | 8 +- lib/CGI/Ex/Auth.pm | 403 ++++++++++++++++++++++++++++------------- lib/CGI/Ex/Conf.pm | 2 +- lib/CGI/Ex/Die.pm | 2 +- lib/CGI/Ex/Dump.pm | 2 +- lib/CGI/Ex/Fill.pm | 2 +- lib/CGI/Ex/JSONDump.pm | 2 +- lib/CGI/Ex/Template.pm | 2 +- lib/CGI/Ex/Validate.pm | 2 +- 13 files changed, 326 insertions(+), 153 deletions(-) diff --git a/Changes b/Changes index c5d4506..635b3c5 100644 --- a/Changes +++ b/Changes @@ -1,3 +1,13 @@ +2.19 + 2007-10-05 + * Make verify_token more granualar in Auth + * Added parse_token method and/or property + * Added verify_password method and/or property + * Added verify_payload method and/or property + * Fix filename based conf reading in App + * Added check_valid_auth, handle_success, handle_failure + key_loggedout, bounce_on_logout to Auth. + 2.18 2007-07-24 * Merry Pioneer Day - http://en.wikipedia.org/wiki/Pioneer_Day_(Utah) diff --git a/META.yml b/META.yml index e53e35e..5df1a6f 100644 --- a/META.yml +++ b/META.yml @@ -1,11 +1,14 @@ -# http://module-build.sourceforge.net/META-spec.html -#XXXXXXX This is a prototype!!! It will change in the future!!! XXXXX# -name: CGI-Ex -version: 2.18 -version_from: lib/CGI/Ex.pm -installdirs: site -requires: +--- #YAML:1.0 +name: CGI-Ex +version: 2.19 +abstract: CGI utility suite - makes powerful application writing fun and easy +license: ~ +generated_by: ExtUtils::MakeMaker version 6.36 +distribution_type: module +requires: Template::Alloy: 1.004 - -distribution_type: module -generated_by: ExtUtils::MakeMaker version 6.30_01 +meta-spec: + url: http://module-build.sourceforge.net/META-spec-v1.2.html + version: 1.2 +author: + - Paul Seamons diff --git a/lib/CGI/Ex.pm b/lib/CGI/Ex.pm index fa848bc..f33b9b3 100644 --- a/lib/CGI/Ex.pm +++ b/lib/CGI/Ex.pm @@ -24,7 +24,7 @@ use vars qw($VERSION use base qw(Exporter); BEGIN { - $VERSION = '2.18'; + $VERSION = '2.19'; $PREFERRED_CGI_MODULE ||= 'CGI'; @EXPORT = (); @EXPORT_OK = qw(get_form diff --git a/lib/CGI/Ex/App.pm b/lib/CGI/Ex/App.pm index f85becd..da5904f 100644 --- a/lib/CGI/Ex/App.pm +++ b/lib/CGI/Ex/App.pm @@ -13,7 +13,7 @@ BEGIN { eval { use Scalar::Util }; } -our $VERSION = '2.18'; +our $VERSION = '2.19'; sub new { my $class = shift || croak "Usage: ".__PACKAGE__."->new"; @@ -332,7 +332,15 @@ sub template_obj { my ($self, $args) = @_; return $self->{'template_obj'} || do { require Template::Alloy; - my $t = Template::Alloy->new($args); + Template::Alloy->new($args); + }; +} + +sub auth_obj { + my ($self, $args) = @_; + return $self->{'auth_obj'} || do { + require CGI::Ex::Auth; + CGI::Ex::Auth->new($args); }; } @@ -365,7 +373,9 @@ sub conf { $self->{'conf'} = pop if @_ == 1; return $self->{'conf'} ||= do { my $conf = $self->conf_file; - $conf = ($self->conf_obj->read($conf, {no_warn_on_fail => 1}) || $self->conf_die_on_fail ? croak $@ : {}) if ! $conf; + if (! ref $conf) { + $conf = $self->conf_obj->read($conf, {no_warn_on_fail => 1}) || $self->conf_die_on_fail ? croak $@ : {}; + } my $hash = $self->conf_validation; if ($hash && scalar keys %$hash) { my $err_obj = $self->val_obj->validate($conf, $hash); @@ -935,8 +945,7 @@ sub _do_auth { $args->{'verify_user'} ||= sub { my ($auth, $user) = @_; $self->verify_user( $user, $auth) }; $args->{'cleanup_user'} ||= sub { my ($auth, $user) = @_; $self->cleanup_user( $user, $auth) }; - require CGI::Ex::Auth; - my $obj = CGI::Ex::Auth->new($args); + my $obj = $self->auth_obj($args); my $resp = $obj->get_valid_auth; my $data = $obj->last_auth_data; diff --git a/lib/CGI/Ex/App.pod b/lib/CGI/Ex/App.pod index b231a0b..8194920 100644 --- a/lib/CGI/Ex/App.pod +++ b/lib/CGI/Ex/App.pod @@ -1028,7 +1028,8 @@ steps to the end of the current path. =item auth_args (method) -Should return a hashref that will be passed to the new method of CGI::Ex::Auth. +Should return a hashref that will be passed to the auth_obj method +which should return a CGI::Ex::Auth compatible object. It is augmented with arguments that integrate it into CGI::Ex::App. See the get_valid_auth method and the CGI::Ex::Auth documentation. @@ -1051,6 +1052,11 @@ was successful - so this data can be defined but false. See the get_valid_auth method. +=item auth_obj (method) + +Passed auth_args. Should return a CGI::Ex::Auth compatible object. Default +is to call CGI::Ex::Auth->new with the passed args. + =item base_dir_abs (method) Used as the absolute base directory to find template, validation and conf files. diff --git a/lib/CGI/Ex/Auth.pm b/lib/CGI/Ex/Auth.pm index 70d754e..85c28fe 100644 --- a/lib/CGI/Ex/Auth.pm +++ b/lib/CGI/Ex/Auth.pm @@ -18,7 +18,7 @@ use MIME::Base64 qw(encode_base64 decode_base64); use Digest::MD5 qw(md5_hex); use CGI::Ex; -$VERSION = '2.18'; +$VERSION = '2.19'; ###----------------------------------------------------------------### @@ -31,6 +31,7 @@ sub new { sub get_valid_auth { my $self = shift; $self = $self->new(@_) if ! ref $self; + delete $self->{'_last_auth_data'}; ### shortcut that will print a js file as needed (such as the md5.js) if ($self->script_name . $self->path_info eq $self->js_uri_path . "/CGI/Ex/md5.js") { @@ -39,32 +40,48 @@ sub get_valid_auth { return; } - my $form = $self->form; - my $cookies = $self->cookies; - my $key_l = $self->key_logout; - my $key_c = $self->key_cookie; - my $has_cookies = scalar %$cookies; + my $form = $self->form; ### allow for logout - if ($form->{$key_l}) { - $self->delete_cookie({key => $key_c});; - $self->location_bounce($self->logout_redirect); - eval { die "Logging out" }; - return; + if ($form->{$self->key_logout} && ! $self->{'_logout_looking_for_user'}) { + local $self->{'_logout_looking_for_user'} = 1; + local $self->{'no_set_cookie'} = 1; + local $self->{'no_cookie_verify'} = 1; + $self->check_valid_auth; # verify the logout so we can capture the username if possible + + if ($self->bounce_on_logout) { + my $key_c = $self->key_cookie; + $self->delete_cookie({key => $key_c}) if $self->cookies->{$key_c}; + my $user = $self->last_auth_data ? $self->last_auth_data->{'user'} : undef; + $self->location_bounce($self->logout_redirect(defined($user) ? $user : '')); + eval { die "Logging out" }; + return; + } else { + $self->form({}); + $self->handle_failure; + return; + } } - my $had_form_info; - foreach ([$form, $self->key_user, 1], - [$cookies, $key_c, 0], + ### look first in form, then in cookies for valid tokens + my $had_form_data; + foreach ([$form, $self->key_user, 1], + [$self->cookies, $self->key_cookie, 0], ) { my ($hash, $key, $is_form) = @$_; next if ! defined $hash->{$key}; - $had_form_info ++ if $is_form; + last if ! $is_form && $had_form_data; # if form info was passed in - we must use it only + $had_form_data = 1 if $is_form; ### if it looks like a bare username (as in they didn't have javascript) - add in other items my $data; - if ($is_form - && $hash->{$key} !~ m|^[^/]+/| + if ($is_form && delete $form->{$self->key_loggedout}) { # don't validate the form on a logout + my $key_u = $self->key_user; + $self->new_auth_data({user => delete($form->{$key_u})}); + $had_form_data = 0; + next; + } elsif ($is_form + && $hash->{$key} !~ m|^[^/]+/| # looks like a cram token && defined $hash->{ $self->key_pass }) { $data = $self->verify_token({ token => { @@ -84,7 +101,7 @@ sub get_valid_auth { ### generate a fresh cookie if they submitted info on plaintext types if ($self->use_plaintext || ($data->{'type'} && $data->{'type'} eq 'crypt')) { $self->set_cookie({ - key => $key_c, + key => $self->key_cookie, val => $self->generate_token($data), no_expires => ($data->{ $self->key_save } ? 0 : 1), # make it a session cookie unless they ask for saving }) if $is_form; # only set the cookie if we found info in the form - the cookie will be a session cookie after that @@ -92,40 +109,61 @@ sub get_valid_auth { ### always generate a cookie on types that have expiration } else { $self->set_cookie({ - key => $key_c, + key => $self->key_cookie, val => $self->generate_token($data), no_expires => 0, }); } ### successful login + return $self->handle_success({is_form => $is_form}); + } - ### bounce to redirect - if (my $redirect = $form->{ $self->key_redirect }) { - $self->location_bounce($redirect); - eval { die "Success login - bouncing to redirect" }; - return; + return $self->handle_failure({had_form_data => $had_form_data}); +} + +sub handle_success { + my $self = shift; + my $args = shift || {}; + if (my $meth = $self->{'handle_success'}) { + return $meth->($self, $args); + } + my $form = $self->form; + + ### bounce to redirect + if (my $redirect = $form->{ $self->key_redirect }) { + $self->location_bounce($redirect); + eval { die "Success login - bouncing to redirect" }; + return; - ### if they have cookies we are done - } elsif ($has_cookies || $self->no_cookie_verify) { - return $self; + ### if they have cookies we are done + } elsif (scalar(keys %{$self->cookies}) || $self->no_cookie_verify) { + return $self; - ### need to verify cookies are set-able - } elsif ($is_form) { - $form->{$self->key_verify} = $self->server_time; - my $query = $self->cgix->make_form($form); - my $url = $self->script_name . $self->path_info . ($query ? "?$query" : ""); + ### need to verify cookies are set-able + } elsif ($args->{'is_form'}) { + $form->{$self->key_verify} = $self->server_time; + my $url = $self->script_name . $self->path_info . "?". $self->cgix->make_form($form); - $self->location_bounce($url); - eval { die "Success login - bouncing to test cookie" }; - return; - } + $self->location_bounce($url); + eval { die "Success login - bouncing to test cookie" }; + return; } +} + +sub handle_failure { + my $self = shift; + my $args = shift || {}; + if (my $meth = $self->{'handle_failure'}) { + return $meth->($self, $args); + } + my $form = $self->form; ### make sure the cookie is gone - $self->delete_cookie({key => $key_c}) if $cookies->{$key_c}; + my $key_c = $self->key_cookie; + $self->delete_cookie({key => $key_c}) if $self->cookies->{$key_c}; - ### nothing found - see if they have cookies + ### no valid login and we are checking for cookies - see if they have cookies if (my $value = delete $form->{$self->key_verify}) { if (abs(time() - $value) < 15) { $self->no_cookies_print; @@ -135,12 +173,8 @@ sub get_valid_auth { ### oh - you're still here - well then - ask for login credentials my $key_r = $self->key_redirect; - if (! $form->{$key_r}) { - my $query = $self->cgix->make_form($form); - $form->{$key_r} = $self->script_name . $self->path_info . ($query ? "?$query" : ""); - } - - $form->{'had_form_data'} = $had_form_info; + local $form->{$key_r} = $form->{$key_r} || $self->script_name . $self->path_info . (scalar(keys %$form) ? "?".$self->cgix->make_form($form) : ''); + local $form->{'had_form_data'} = $args->{'had_form_data'} || 0; $self->login_print; my $data = $self->last_auth_data; eval { die defined($data) ? $data : "Requesting credentials" }; @@ -151,6 +185,16 @@ sub get_valid_auth { return; } +sub check_valid_auth { + my $self = shift; + $self = $self->new(@_) if ! ref $self; + + local $self->{'location_bounce'} = sub {}; # but don't bounce to other locations + local $self->{'login_print'} = sub {}; # check only - don't login if not + local $self->{'set_cookie'} = $self->{'no_set_cookie'} ? sub {} : $self->{'set_cookie'}; + return $self->get_valid_auth; +} + ###----------------------------------------------------------------### sub script_name { shift->{'script_name'} || $ENV{'SCRIPT_NAME'} || die "Missing SCRIPT_NAME" } @@ -161,19 +205,19 @@ sub server_time { time } sub cgix { my $self = shift; - $self->{'cgix'} = shift if $#_ != -1; + $self->{'cgix'} = shift if @_ == 1; return $self->{'cgix'} ||= CGI::Ex->new; } sub form { my $self = shift; - $self->{'form'} = shift if $#_ != -1; + $self->{'form'} = shift if @_ == 1; return $self->{'form'} ||= $self->cgix->get_form; } sub cookies { my $self = shift; - $self->{'cookies'} = shift if $#_ != -1; + $self->{'cookies'} = shift if @_ == 1; return $self->{'cookies'} ||= $self->cgix->get_cookies; } @@ -226,7 +270,10 @@ sub form_name { shift->{'form_name'} ||= 'cea_form' } sub key_verify { shift->{'key_verify'} ||= 'cea_verify' } sub key_redirect { shift->{'key_redirect'} ||= 'cea_redirect' } sub key_payload { shift->{'key_payload'} ||= 'cea_payload' } +sub key_loggedout { shift->{'key_loggedout'} ||= 'loggedout' } +sub bounce_on_logout { shift->{'bounce_on_logout'} ||= 0 } sub secure_hash_keys { shift->{'secure_hash_keys'} ||= [] } +#perl -e 'use Digest::MD5 qw(md5_hex); open(my $fh, "<", "/dev/urandom"); for (1..10) { read $fh, my $t, 5_000_000; print md5_hex($t),"\n"}' sub no_cookie_verify { shift->{'no_cookie_verify'} ||= 0 } sub use_crypt { shift->{'use_crypt'} ||= 0 } sub use_blowfish { shift->{'use_blowfish'} ||= '' } @@ -236,8 +283,9 @@ sub expires_min { my $s = shift; $s->{'expires_min'} = 6 * 60 if ! defined sub failed_sleep { shift->{'failed_sleep'} ||= 0 } sub logout_redirect { - my $self = shift; - return $self->{'logout_redirect'} || $self->script_name ."?loggedout=1"; + my ($self, $user) = @_; + my $form = $self->cgix->make_form({$self->key_loggedout => 1, (length($user) ? ($self->key_user => $user) : ()) }); + return $self->{'logout_redirect'} || $self->script_name ."?$form"; } sub js_uri_path { @@ -294,8 +342,7 @@ sub template_include_path { shift->{'template_include_path'} || '' } sub login_hash_common { my $self = shift; my $form = $self->form; - my $data = $self->last_auth_data; - $data = {} if ! defined $data; + my $data = $self->last_auth_data || {}; return { %$form, @@ -321,6 +368,8 @@ sub login_hash_common { text_user => $self->text_user, text_pass => $self->text_pass, text_save => $self->text_save, + text_submit => $self->text_submit, + hide_save => $self->hide_save, }; } @@ -330,52 +379,27 @@ sub verify_token { my $self = shift; my $args = shift; my $token = delete $args->{'token'} || die "Missing token"; - my $data = $self->{'_last_auth_data'} = $self->new_auth_data({token => $token, %$args}); + my $data = $self->new_auth_data({token => $token, %$args}); + my $meth; - ### token already parsed - if (ref $token) { + ### make sure the token is parsed to usable data + if (ref $token) { # token already parsed $data->add_data({%$token, armor => 'none'}); - ### parse token for info - } else { - my $found; - my $key; - for my $armor ('none', 'base64', 'blowfish') { # try with and without base64 encoding - my $copy = ($armor eq 'none') ? $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) { - $data->add_data({ - user => $1, - cram_time => $2, - expires_min => $3, - payload => $4, - test_pass => $5, - secure_hash => $6 || '', - armor => $armor, - }); - $found = 1; - last; - } elsif ($copy =~ m|^ ([^/]+) / (.*) $|x) { - $data->add_data({ - user => $1, - test_pass => $2, - armor => $armor, - }); - $found = 1; - last; - } + } elsif (my $meth = $self->{'parse_token'}) { + if (! $meth->($self, $args)) { + $data->error('Invalid custom parsed token') if ! $data->error; # add error if not already added + return $data; } - if (! $found) { - $data->error('Invalid token'); + } else { + if (! $self->parse_token($token, $data)) { + $data->error('Invalid token') if ! $data->error; # add error if not already added return $data; } } - ### verify the user and get the pass - my $pass; + ### verify the user if (! defined($data->{'user'})) { $data->error('Missing user'); @@ -385,7 +409,12 @@ sub verify_token { } elsif (! $self->verify_user($data->{'user'} = $self->cleanup_user($data->{'user'}))) { $data->error('Invalid user'); - } elsif (! defined($pass = eval { $self->get_pass_by_user($data->{'user'}) })) { + } + return $data if $data->error; + + ### get the pass + my $pass; + if (! 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') { @@ -397,81 +426,139 @@ sub verify_token { $data->add_data($extra); } return $data if $data->error; + $data->add_data({real_pass => $pass}); # store - to allow generate_token to not need to relookup the pass + + + ### validate the pass + if ($meth = $self->{'verify_password'}) { + if (! $meth->($self, $pass, $data)) { + $data->error('Password failed verification') if ! $data->error; + } + } else{ + if (! $self->verify_password($pass, $data)) { + $data->error('Password failed verification') if ! $data->error; + } + } + return $data if $data->error; + + + ### validate the payload + if ($meth = $self->{'verify_payload'}) { + if (! $meth->($self, $data->{'payload'}, $data)) { + $data->error('Payload failed custom verification') if ! $data->error; + } + } else { + if (! $self->verify_payload($data->{'payload'}, $data)) { + $data->error('Payload failed verification') if ! $data->error; + } + } + + return $data; +} +sub new_auth_data { + my $self = shift; + return $self->{'_last_auth_data'} = CGI::Ex::Auth::Data->new(@_); +} - ### store - to allow generate_token to not need to relookup the pass - $data->add_data({real_pass => $pass}); +sub parse_token { + my ($self, $token, $data) = @_; + my $found; + my $key; + for my $armor ('none', 'base64', 'blowfish') { # try with and without base64 encoding + my $copy = ($armor eq 'none') ? $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) { + $data->add_data({ + user => $1, + cram_time => $2, + expires_min => $3, + payload => $4, + test_pass => $5, + secure_hash => $6 || '', + armor => $armor, + }); + $found = 1; + last; + } elsif ($copy =~ m|^ ([^/]+) / (.*) $|x) { + $data->add_data({ + user => $1, + test_pass => $2, + armor => $armor, + }); + $found = 1; + last; + } + } + return $found; +} +sub verify_password { + my ($self, $pass, $data) = @_; + my $err; ### looks like a secure_hash cram if ($data->{'secure_hash'}) { $data->add_data(type => 'secure_hash_cram'); my $array = eval {$self->secure_hash_keys }; if (! $array) { - $data->error('secure_hash_keys not found'); + $err = 'secure_hash_keys not found'; } elsif (! @$array) { - $data->error('secure_hash_keys empty'); + $err = 'secure_hash_keys empty'; } elsif ($data->{'secure_hash'} !~ /^sh\.(\d+)\.(\d+)$/ || $1 > $#$array) { - $data->error('Invalid secure hash'); + $err = 'Invalid secure hash'; } else { my $rand1 = $1; my $rand2 = $2; - my $real = $data->{'real_pass'} =~ /^[a-f0-9]{32}$/ ? lc($data->{'real_pass'}) : md5_hex($data->{'real_pass'}); + my $real = $pass =~ /^[a-f0-9]{32}$/ ? lc($pass) : md5_hex($pass); my $str = join("/", @{$data}{qw(user cram_time expires_min payload)}); my $sum = md5_hex($str .'/'. $real .('/sh.'.$array->[$rand1].'.'.$rand2)); if ($data->{'expires_min'} > 0 && ($self->server_time - $data->{'cram_time'}) > $data->{'expires_min'} * 60) { - $data->error('Login expired'); + $err = 'Login expired'; } elsif (lc($data->{'test_pass'}) ne $sum) { - $data->error('Invalid login'); + $err = 'Invalid login'; } } ### looks like a normal cram } elsif ($data->{'cram_time'}) { $data->add_data(type => 'cram'); - my $real = $data->{'real_pass'} =~ /^[a-f0-9]{32}$/ ? lc($data->{'real_pass'}) : md5_hex($data->{'real_pass'}); + my $real = $pass =~ /^[a-f0-9]{32}$/ ? lc($pass) : md5_hex($pass); my $str = join("/", @{$data}{qw(user cram_time expires_min payload)}); my $sum = md5_hex($str .'/'. $real); if ($data->{'expires_min'} > 0 && ($self->server_time - $data->{'cram_time'}) > $data->{'expires_min'} * 60) { - $data->error('Login expired'); + $err = 'Login expired'; } elsif (lc($data->{'test_pass'}) ne $sum) { - $data->error('Invalid login'); + $err = 'Invalid login'; } ### plaintext_crypt - } elsif ($data->{'real_pass'} =~ m|^([./0-9A-Za-z]{2})([./0-9A-Za-z]{11})$| - && crypt($data->{'test_pass'}, $1) eq $data->{'real_pass'}) { + } elsif ($pass =~ m|^([./0-9A-Za-z]{2})([./0-9A-Za-z]{11})$| + && crypt($data->{'test_pass'}, $1) eq $pass) { $data->add_data(type => 'crypt', was_plaintext => 1); ### failed plaintext crypt } elsif ($self->use_crypt) { - $data->error('Invalid login'); + $err = 'Invalid login'; $data->add_data(type => 'crypt', was_plaintext => ($data->{'test_pass'} =~ /^[a-f0-9]{32}$/ ? 0 : 1)); ### plaintext and md5 } else { my $is_md5_t = $data->{'test_pass'} =~ /^[a-f0-9]{32}$/; - my $is_md5_r = $data->{'real_pass'} =~ /^[a-f0-9]{32}$/; + my $is_md5_r = $pass =~ /^[a-f0-9]{32}$/; my $test = $is_md5_t ? lc($data->{'test_pass'}) : md5_hex($data->{'test_pass'}); - my $real = $is_md5_r ? lc($data->{'real_pass'}) : md5_hex($data->{'real_pass'}); + my $real = $is_md5_r ? lc($pass) : md5_hex($pass); $data->add_data(type => ($is_md5_r ? 'md5' : 'plaintext'), was_plaintext => ($is_md5_t ? 0 : 1)); - $data->error('Invalid login') + $err = 'Invalid login' if $test ne $real; } - ### check the payload - if (! $data->error && ! $self->verify_payload($data->{'payload'})) { - $data->error('Invalid payload'); - } - - return $data; -} - -sub new_auth_data { - my $self = shift; - return CGI::Ex::Auth::Data->new(@_); + $data->error($err) if $err; + return ! $err; } sub last_auth_data { shift->{'_last_auth_data'} } @@ -529,6 +616,9 @@ sub generate_token { sub generate_payload { my $self = shift; my $args = shift; + if (my $meth = $self->{'generate_payload'}) { + return $meth->($self, $args); + } return defined($args->{'payload'}) ? $args->{'payload'} : ''; } @@ -561,10 +651,9 @@ sub get_pass_by_user { } sub verify_payload { - my $self = shift; - my $payload = shift; + my ($self, $payload, $data) = @_; if (my $meth = $self->{'verify_payload'}) { - return $meth->($self, $payload); + return $meth->($self, $payload, $data); } return 1; } @@ -628,7 +717,7 @@ sub login_form { return shift->{'login_form'} || q {
- @@ -823,11 +921,15 @@ defined separately. key_time key_user key_verify + key_loggedout + bounce_on_logout login_footer login_form login_header login_script login_template + handle_success + handle_failure no_cookie_verify path_info script_name @@ -837,6 +939,8 @@ defined separately. text_user text_pass text_save + text_submit + hide_save use_base64 use_blowfish use_crypt @@ -953,6 +1057,12 @@ new object using the passed arguments, and then run get_valid_auth. CGI::Ex::Auth->get_valid_auth({key_user => 'my_user'}) || exit; +=item C + +Runs get_valid_auth with login_print and location_bounce set to do nothing. +This allows for obtaining login data without forcing an html login +page to appear. + =item C Called if login errored. Defaults to printing a very basic (but @@ -989,6 +1099,18 @@ Passed to the template swapped during login_print. 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 ? + text_submit # $self->text_submit # template text Login + hide_save # $self->hide_save # 0 + +=item C + +Default 0. If true, will location bounce to script returned by logout_redirect +passing the key key_logout. If false, will simply show the login screen. + +=item C + +Key to bounce with in the form during a logout should bounce_on_logout return true. +Default is "loggedout". =item C @@ -1053,6 +1175,11 @@ for creating tokes is listed below). It also allows for armoring the token with base64 encoding, or using blowfish encryption. A listing of creating these tokens can be found under generate_token. +=item C + +Used by verify_token to remove armor from the passed tokens and split the token into its parts. +Returns true if it was able to parse the passed token. + =item C Called by verify_token. Default is to do no modification. Allows for usernames to @@ -1092,6 +1219,24 @@ in the data object will override those in the data object. }; } +=item C + +Called by verify_token. Passed the password to check as well as the +auth data object. Should return true if the password matches. +Default method can handle md5, crypt, cram, secure_hash_cram, and +plaintext (all of the default types supported by generate_token). If +a property named verify_password exists, it will be used and called as +a coderef rather than using the default method. + +=item C + +Called by verify_token. Passed the password to check as well as the +auth data object. Should return true if the payload is valid. +Default method returns true without performing any checks on the +payload. If a property named verify_password exists, it will be used +and called as a coderef rather than using the default method. + + =item C Returns a CGI::Ex object. diff --git a/lib/CGI/Ex/Conf.pm b/lib/CGI/Ex/Conf.pm index 420f4de..b62674b 100644 --- a/lib/CGI/Ex/Conf.pm +++ b/lib/CGI/Ex/Conf.pm @@ -29,7 +29,7 @@ use vars qw($VERSION ); @EXPORT_OK = qw(conf_read conf_write in_cache); -$VERSION = '2.18'; +$VERSION = '2.19'; $DEFAULT_EXT = 'conf'; diff --git a/lib/CGI/Ex/Die.pm b/lib/CGI/Ex/Die.pm index 876f6ad..62f8deb 100644 --- a/lib/CGI/Ex/Die.pm +++ b/lib/CGI/Ex/Die.pm @@ -23,7 +23,7 @@ use CGI::Ex; use CGI::Ex::Dump qw(debug ctrace dex_html); BEGIN { - $VERSION = '2.18'; + $VERSION = '2.19'; $SHOW_TRACE = 0 if ! defined $SHOW_TRACE; $IGNORE_EVAL = 0 if ! defined $IGNORE_EVAL; $EXTENDED_ERRORS = 1 if ! defined $EXTENDED_ERRORS; diff --git a/lib/CGI/Ex/Dump.pm b/lib/CGI/Ex/Dump.pm index d3ecd7a..5039cc2 100644 --- a/lib/CGI/Ex/Dump.pm +++ b/lib/CGI/Ex/Dump.pm @@ -17,7 +17,7 @@ use vars qw(@ISA @EXPORT @EXPORT_OK $VERSION use strict; use Exporter; -$VERSION = '2.18'; +$VERSION = '2.19'; @ISA = qw(Exporter); @EXPORT = qw(dex dex_warn dex_text dex_html ctrace dex_trace); @EXPORT_OK = qw(dex dex_warn dex_text dex_html ctrace dex_trace debug); diff --git a/lib/CGI/Ex/Fill.pm b/lib/CGI/Ex/Fill.pm index 2966817..9641f18 100644 --- a/lib/CGI/Ex/Fill.pm +++ b/lib/CGI/Ex/Fill.pm @@ -24,7 +24,7 @@ use vars qw($VERSION use base qw(Exporter); BEGIN { - $VERSION = '2.18'; + $VERSION = '2.19'; @EXPORT = qw(form_fill); @EXPORT_OK = qw(fill form_fill html_escape get_tagval_by_key swap_tagval_by_key); }; diff --git a/lib/CGI/Ex/JSONDump.pm b/lib/CGI/Ex/JSONDump.pm index 0c7f9bb..6d58f09 100644 --- a/lib/CGI/Ex/JSONDump.pm +++ b/lib/CGI/Ex/JSONDump.pm @@ -17,7 +17,7 @@ use strict; use base qw(Exporter); BEGIN { - $VERSION = '2.18'; + $VERSION = '2.19'; @EXPORT = qw(JSONDump); @EXPORT_OK = @EXPORT; diff --git a/lib/CGI/Ex/Template.pm b/lib/CGI/Ex/Template.pm index c4374ae..d0a837e 100644 --- a/lib/CGI/Ex/Template.pm +++ b/lib/CGI/Ex/Template.pm @@ -25,7 +25,7 @@ use vars qw($VERSION $VOBJS ); -$VERSION = '2.18'; +$VERSION = '2.19'; ### install true symbol table aliases that can be localized *QR_PRIVATE = *Template::Alloy::QR_PRIVATE; diff --git a/lib/CGI/Ex/Validate.pm b/lib/CGI/Ex/Validate.pm index 146a10e..651b3c9 100644 --- a/lib/CGI/Ex/Validate.pm +++ b/lib/CGI/Ex/Validate.pm @@ -22,7 +22,7 @@ use vars qw($VERSION @UNSUPPORTED_BROWSERS ); -$VERSION = '2.18'; +$VERSION = '2.19'; $DEFAULT_EXT = 'val'; $QR_EXTRA = qr/^(\w+_error|as_(array|string|hash)_\w+|no_\w+)/; -- 2.43.0