X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?a=blobdiff_plain;f=cgi-auth-hybrid.pm;h=c5530e071886f2f359f106f8cdc8bf09cba1f112;hb=420e69289e89656d88b551ab7e7aebda12f497b9;hp=4b413671798f845206dad619fd2796f8501b0a69;hpb=3ac28d0f039545458b36a15065333ae586dd2ead;p=cgi-auth-flexible.git diff --git a/cgi-auth-hybrid.pm b/cgi-auth-hybrid.pm index 4b41367..c5530e0 100644 --- a/cgi-auth-hybrid.pm +++ b/cgi-auth-hybrid.pm @@ -2,6 +2,7 @@ # This is part of CGI::Auth::Hybrid, a perl CGI authentication module. # Copyright (C) 2012 Ian Jackson. +# Copyright (C) 2012 Citrix. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by @@ -72,13 +73,13 @@ sub get_cookie_domain ($$$) { } sub construct_cookie ($$$) { - my ($c, $r, $cookv) = @_; - return $c->cookie(-name => $r->{S}{cookie_name}, - -value => $cookv, - -path => $r->{S}{cookie_path}, - -domain => $r->_ch('get_cookie_domain'), - -expires => '+'.$r->{S}{login_timeout}.'s', - -secure => $r->{S}{encrypted_only}); + my ($r, $cookv) = @_; + return $r->{Cgi}->cookie(-name => $r->{S}{cookie_name}, + -value => $cookv, + -path => $r->{S}{cookie_path}, + -domain => $r->_ch('get_cookie_domain'), + -expires => '+'.$r->{S}{login_timeout}.'s', + -secure => $r->{S}{encrypted_only}); } sub do_redirect_cgi ($$$$) { @@ -225,6 +226,7 @@ sub new_request { die if @extra; } my $r = { + V => $classbase, S => $classbase->{S}, Dbh => $classbase->{Dbh}, Cgi => $cgi, @@ -355,6 +357,7 @@ sub _print ($$) { my ($r, @t) = @_; return $r->_ch('print')(join '', @t); } sub _check_divert_core ($) { fixme needs wrapping with something to make and commit a transaction +wrapper should also store answers in the $r object for later retrieval my ($r) = @_; my $meth = $r->_ch('get_method'); @@ -371,7 +374,7 @@ fixme needs wrapping with something to make and commit a transaction $r->_db_revoke($parmv); return ({ Kind => 'REDIRECT-LOGGEDOUT', Message => "Logging out...", - Cookie => '', + CookieVal => '', Params => { } }); } if ($r->_ch('is_loggedout')) { @@ -380,35 +383,35 @@ fixme needs wrapping with something to make and commit a transaction die unless $parmt; return ({ Kind => 'SMALLPAGE-LOGGEDOUT', Message => "You have been logged out.", - Cookie => '', + CookieVal => '', Params => { } }); } if ($r->_ch('is_login')) { $r->_must_be_post(); return ({ Kind => 'LOGIN-STALE', Message => "Stale session; you need to log in again.", - Cookie => $r->_fresh_cookie(), + CookieVal => $r->_fresh_cookie(), Params => { } }) if $parmt eq 'n'; die unless $parmt eq 't' || $parmt eq 'y'; return ({ Kind => 'SMALLPAGE-NOCOOKIE', Message => "You do not seem to have cookies enabled. ". "You must enable cookies as we use them for login.", - Cookie => $r->_fresh_cookie(), + CookieVal => $r->_fresh_cookie(), Params => $r->_chain_params() }) if !$cookt && $parmt eq 't'; + my $username = $r->_ch('login_ok'); return ({ Kind => 'LOGIN-BAD', Message => "Incorrect username/password.", - Cookie => $cookv, + CookieVal => $cookv, Params => $r->_chain_params() }) unless defined $username && length $username; $r->_db_revoke($cookv) if defined $cookv && !(defined $parmv && $cookv eq $parmv); - my $username = $r->_ch('login_ok'); $r->_db_record_login_ok($parmv,$username); return ({ Kind => 'REDIRECT-LOGGEDIN', Message => "Logging in...", - Cookie => $parmv, + CookieVal => $parmv, Params => $r->_chain_params() }); } if ($cookt eq 't') { @@ -427,12 +430,12 @@ fixme needs wrapping with something to make and commit a transaction if ($meth eq 'GET') { return ({ Kind => 'LOGIN-INCOMINGLINK', Message => "You need to log in again.", - Cookie => $parmv, + CookieVal => $parmv, Params => $r->_chain_params() }); } else { return ((Kind => 'LOGIN-FRESH', Message => "You need to log in again.", - Cookie => $parmv, + CookieVal => $parmv, Params => { }); } } @@ -441,7 +444,7 @@ fixme needs wrapping with something to make and commit a transaction if ($meth ne 'POST') { return ({ Kind => 'MAINPAGEONLY', Message => 'Entering via cross-site link.', - Cookie => $cookv, + CookieVal => $cookv, Params => { } }); # NB caller must then ignore params & path! # if this is too hard they can spit out a small form @@ -452,6 +455,7 @@ fixme needs wrapping with something to make and commit a transaction die unless $cookt eq 'y'; die unless $parmt eq 'y'; die unless $cookv eq $parmv; + $r->{Assoc} = $cookv; $r->{UserOK} = $cooku; return undef; } @@ -516,6 +520,38 @@ sub _db_record_login_ok ($$$) { $v, $user, time); } +sub check_divert ($) { + my ($r) = @_; + my $divert; + if (exists $r->{Divert}) { + return $r->{Divert}; + } + $dbh->do("BEGIN"); + if (!eval { + $divert = $r->_check_divert_core(); + 1; + }) { + $dbh->do("ABORT"); + die $@; + } + $r->{Divert} = $divert; + $dbh->do("COMMIT"); + return $divert; +} + +sub get_divert ($) { + my ($r) = @_; + die "unchecked" unless exists $r->{Divert}; + return $r->{Divert}; +} + +sub get_username ($) { + my ($r) = @_; + my $divert = $r->get_divert(); + return undef if $divert; + return $r->{UserOK}; +} + sub url_with_query_params ($$) { my ($r, $params) = @_; my $uri = URI->new($r->_ch('get_url')); @@ -533,7 +569,7 @@ sub check_ok ($) { return 0 if $handled; my $kind = $divert->{Kind}; - my $cookie = $divert->{Cookie}; + my $cookieval = $divert->{CookieVal}; my $params = $divert->{Params}; if ($kind =~ m/^REDIRECT-/) { @@ -548,6 +584,7 @@ sub check_ok ($) { die; } my $new_url = $r->url_with_query_params($params); + my $cookie = $r->construct_cookie($r, $cookieval); $r->_ch('do_redirect')($new_url, $cookie); return 0; } @@ -570,93 +607,53 @@ sub check_ok ($) { $r->_ch('end_html')); return 0; } - -UP TO HERE -sub record_login ($$) { - my ($r,$nusername) = @_; - my $rsp = $r->{S}{random_source}; - my $rsf = new IO::File $rsp, '<' or die "$rsp $!"; - my $bytes = ($r->{S}{associdlen} + 7) >> 3; - my $nassocbin; +sub _random ($$) { + my ($r, $bytes) = @_; + my $v = $r->{V}; + if (!$v->{RandomHandle}) { + my $rsp = $r->{S}{random_source}; + my $rsf = new IO::File $rsp, '<' or die "$rsp $!"; + $v->{RandomHandle} = $rsf; + } + my $bin; $!=0; - read($rsf,$nassocbin,$bytes) == $bytes or die "$rsp $!"; + read($rsf,$bin,$bytes) == $bytes or die "$rsp $!"; close $rsf; - my $nassoc = unpack "H*", $nassocbin; - my $dbh = $r->{Dbh}; - $dbh->do("INSERT INTO $r->{S}{assocdb_table}". - " (associd, username, last) VALUES (?,?,?)", {}, - $nassoc, $nusername, time); - $dbh->do("COMMIT"); - $r->{U} = $nusername; - $r->{A} = $nassoc; -} - -sub _check ($) { - my ($r) = @_; - - return if exists $r->{Username}; - ($r->{Username}, $r->{Assoc}, $r->{Mutate}) = $r->_check(); - - if (defined $r->{Assoc}) { - $dbh->do("UPDATE $r->{S}{assocdb_table}". - " SET last = ?". - " WHERE associd = ?", {}, time, $nassoc); - $dbh->do("COMMIT"); - } -} - -sub logout ($) { - my ($r) = @_; - - my ($nusername, $nassoc, $nmutate) = $r->_check(); - return undef unless $nmutate; - $dbh->do("DELETE FROM $r->{S}{assocdb_table}". - " WHERE associd = ?", {}, $nassoc); - $dbh->do("COMMIT"); - return $nusername; + return unpack "H*", $bin; } -sub check ($) { +sub _fresh_cookie ($) { my ($r) = @_; - $r->_check(); - return !!defined $r->{Username}; + my $bytes = ($r->{S}{associdlen} + 7) >> 3; + return $r->_random($bytes); } sub check_mutate ($) { my ($r) = @_; - $r->check(); - return $r->{Mutate}; + die "unchecked" unless exists $r->{Divert}; + die if $r->{Divert}; + my $meth = $r->_ch('get_method'); + die "mutating non-POST" if $meth ne 'POST'; } -sub username ($) { - my ($r) = @_; - $r->check(); - return $r->{Username}; +#---------- output ---------- -sub hidden_val ($) { +sub secret_val ($) { my ($r) = @_; $r->check(); return defined $r->{Assoc} ? $r->{Assoc} : ''; } -#---------- simple wrappers ---------- - -sub hidden_hargs ($) { +sub secret_hidden_html ($) { my ($r) = @_; - return (-name => $r->{S}{param_name}, - -default => $r->hidden_val()); + return $r->{Cgi}->hidden(-name => $r->{S}{assoc_param_name}, + -default => $r->secret_val()); } -sub hidden_html ($) { +sub secret_cookie ($) { my ($r) = @_; - return hidden($r->hidden_hargs()); -} - -sub cookiea_cargs ($) { - my ($r) = @_; - return (-name => $r->{S}{cookie_name}, - -value => hidden_val()); + return $r->construct_cookie($r->secret_val()); } __END__ @@ -678,7 +675,7 @@ CGI::Auth::Hybrid - web authentication optionally using cookies $authreq->check_ok() or return; blah blah blah - $authreq->mutating(); + $authreq->check_mutate(); blah blah blah =head1 USAGE PATTERN FOR FANCY APPLICATIONS @@ -693,3 +690,6 @@ CGI::Auth::Hybrid - web authentication optionally using cookies } } + blah blah blah + $authreq->check_mutate(); + blah blah blah