chiark / gitweb /
wip
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Mon, 19 Nov 2012 18:06:42 +0000 (18:06 +0000)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Mon, 19 Nov 2012 18:06:42 +0000 (18:06 +0000)
cgi-auth-hybrid.pm

index 070603d..6f66113 100644 (file)
@@ -34,9 +34,9 @@ use CGI;
 
 #---------- default callbacks ----------
 
-sub _def_is_logout ($$) {
-    my ($c,$r) = @_;
-    foreach my $pn (@{ $r->{S}{logout_param_names} }) {
+sub has_a_param ($$) {
+    my ($c,$cn) = @_;
+    foreach my $pn (@{ $r->{S}{$cn} }) {
        return 1 if $r->_cm('get_param')($pn);
     }
     return 0;
@@ -58,14 +58,16 @@ sub new_verifier {
            login_timeout => 86400, # seconds
            assoc_param_name => 'cah_associd',
            password_param_name => 'password',
-           logout_param_names => [qw(logout)],
+           logout_param_names => [qw(cah_logout)],
+           loggedout_param_names => [qw(cah_loggedout)],
            promise_check_mutate => 0,
            get_param => sub { $_[0]->param($_[2]) },
            get_cah_cookie => sub { $_[0]->cookie($s->{S}{cookie_name}) },
            get_method => sub { $_[0]->request_method() },
             is_login => sub { defined $_[1]->_rp('password_param_name') },
             login_ok => sub { die },
-           is_logout => \&_def_is_logout,
+           is_logout => sub { $_[1]->has_a_param('logout_param_names') },
+           is_loggedout => sub { $_[1]->has_a_param('loggedout_param_names') },
            is_page => sub { return 1 },
        },
        Dbh => undef,
@@ -143,7 +145,7 @@ sub _rp ($$@) {
 #   u update of information by JS, mutating
 #   i login
 #   o logout
-
+#   O "you have just logged out" page load
 
 # in cook and par,
 #    a, aN     anything including -
@@ -166,7 +168,10 @@ sub _rp ($$@) {
     #  any -   POST  nrmuoi   bug or attack, fail
     #  any -   GET    rmuoi   bug or attack, fail
     #  any any GET     muoi   bug or attack, fail
-    #  any t   any   nrmuo    bug or attack, fail
+    #  any t   any   nrmu     bug or attack, fail
+    #
+    #  -   -   GET         O  "just logged out" page
+    #  (any other)         O  bug or attack, fail
     #
     #  a1  a2  POST      o    logout
     #                           if a1 is valid, revoke it
@@ -176,15 +181,19 @@ sub _rp ($$@) {
     #                             (which contains link to login form)
     #
     #  -   t   POST       i   complain about cookies being disabled
+    #                           (with link to login form)
     #
     #  any n   POST       i   complain about stale login form
     #                           show new login form
     #
     #  x1  t2  POST       i   login (or switch user)
-    #                           revoke x1 if it was valid and !=t2
-    #                           upgrade t2 to y2 in our db (setting username)
-    #                           set cookie to t2
-    #                           redirect to GET of remaining params
+    #                           if bad
+    #                             show new login form
+    #                           if good
+    #                             revoke x1 if it was valid and !=t2
+    #                             upgrade t2 to y2 in our db (setting username)
+    #                             set cookie to t2
+    #                             redirect to GET of remaining params
     #
     #  t1  a2  ANY   nrmu     treat as  - a2 ANY
     #
@@ -229,12 +238,16 @@ sub _rp ($$@) {
     #  -/n n   GET    rmu     user not logged in
     #                           fail
     #
-    #  -/n n   POST  nrmu     user not logged in
+    #  -/n n   POST  n m      user not logged in
+    #                           show login form
+    #
+    #  -/n n   POST   r u     user not logged in
     #                           fail
 
 sub check_divert ($) {
     my ($r) = @_;
 
+    my $meth = $r->_ch('get_method');
     my $cookv = $r->_ch('get_cah_cookie');
     my $parmv = $r->_rp('assoc_param_name');
 
@@ -247,24 +260,71 @@ sub check_divert ($) {
        $r->_db_perhaps_revoke($cookv);
        $r->_db_perhaps_revoke($parmv);
        $r->_queue_set_cookie('');
-       return 'REDIRECT-LOGGEDOUT';
+       return 'REDIRECT-LOGGEDOUT'
+    }
+    if ($r->_ch('is_loggedout')) {
+       die unless $meth eq 'GET';
+       die unless $cookt;
+       die unless $parmt;
+       return ('SMALLPAGE-LOGGEDOUT', "You have been logged out.");
     }
     if ($r->_ch('is_login')) {
-       return 'NOCOOKIE' if !$cookt && $parmt eq 't';
-       return 'LOGIN-STALE' if $parmt eq 'n';
+       $r->_must_be_post();
+       return ('LOGIN-STALE',
+               "This session was stale and you need to log in again.")
+           if $parmt eq 'n';
+       die unless $parmt eq 't' || $parmt eq 'y';
+       $r->_queue_preserve_params();
+       return ('SMALLPAGE-NOCOOKIE',
+               "You do not seem to have cookies enabled.  ".
+               "You must enable cookies as we use them for login.")
+           if !$cookt && $parmt eq 't';
+       return ('LOGIN-BAD',
+               "Incorrect username/password.")
+           unless defined $username && length $username;
        $r->_db_perhaps_revoke($cookv) 
            if defined $cookv && !(defined $parmv && $cookv eq $parmv);
        $r->_queue_set_cookie($parmv);
        my $username = $r->_ch('login_ok');
-       return 'LOGIN-BAD' unless defined $username && length $username;
        $r->_db_record_login_ok($parmv,$username);
        return 'REDIRECT-LOGGEDIN';
     }
     if (!$r->{S}{promise_check_mutate}) {
-       something with method get, check parameter, etc.
-       return 'FRONTPAGE';
+       if ($meth ne 'POST') {
+           return 'MAINPAGEONLY';
+           # NB caller must then ignore params & path!
+           # if this is too hard they can spit out a small form
+           # with a "click to continue"
+       }
+    }
+    if ($cookt eq 't') {
+       $cookt = undef;
     }
-    
+    die if $parmt eq 't';
+
+    if ($cookt eq 'y' && $parmt eq 'y' && $cookv ne $parmv) {
+       $r->_db_perhaps_revoke($parmv) if $meth eq 'POST';
+       $parmt = 'n';
+    }
+
+    if ($cookt ne 'y') {
+       die unless !$cookt || $cookt eq 'n';
+       die unless !$parmt || $parmt eq 'n' || $parmt eq 'y';
+       if ($meth eq 'GET') {
+           $r->_queue_preserve_params();
+           return ('LOGIN-INCOMINGLINK',
+                   "You need to log in again.");
+       } else {
+           return ('LOGIN-FRESH',
+                   "You need to log in again.");
+       }
+    }
+
+    die unless $cookt eq 'y';
+    die unless $parmt eq 'y';
+    die unless $cookv eq $parmv;
+    return '';
+}
 
 UP TO HERE