Fix CAS to handle dynamic auth links
authorArt Rhyno <art632000@yahoo.ca>
Fri, 8 Mar 2013 12:55:06 +0000 (07:55 -0500)
committerDan Scott <dscott@laurentian.ca>
Tue, 19 Mar 2013 15:21:56 +0000 (11:21 -0400)
CAS uses a "service" URL to redirect a user back to where an application wants
them to go if authentication is passed. This works well for simple URLs but
becomes problematic for complex URLs, which can get mangled or, worse,
truncated. This doesn't seem to be unique to Windsor's implmentation of CAS.

I tried a few tricks to encode the URLs in strange ways to get the same
URL back from CAS, but I think a better approach is to stuff the URL into
a cookie, and invoke when the request comes back from CAS. Any other approach
seems to result in horrid URLs and there is always the chance that some
character will break the scheme.

One flaw in my approach is that if a TPAC user selects "email" or "place hold"
and invokes the logon screen, the cookie gets set for the "redirect" URL. If,
for some reason, a user decides to do another search and chooses to log in to
their account from a different screen, the CAS URL can be invoked. This
would only happen for CAS, and the cookie itself is only set for 10
minutes, so I don't think this is a major concession. I also try to
invalidate the cookie wherever it is possible to know that the authentication
has not been invoked.

The assumption is that there is a CAS link added to the login form (login/form.tt2),
for example:

 <a href="[% ctx.cas.url %]"
    class="opac-button opac-button-header" id="home_myopac_link_uwin">
    [% l('Log in to Your Account (UWind ID)') %]
 </a>

I had become so used to testing CAS by logging in first, that I totally missed
the links that support authentication at the time of need, e.g. the "email"
or "place hold" links that are displayed prior to authentication. Hopefully,
this branch will address what is probably a common scenario.

Signed-off-by: Art Rhyno <art632000@yahoo.ca>
Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm

index 9e30cfd..b38a9a8 100644 (file)
@@ -29,6 +29,7 @@ my $U = 'OpenILS::Application::AppUtils';
 use constant COOKIE_SES => 'ses';
 use constant COOKIE_LOGGEDIN => 'eg_loggedin';
 use constant COOKIE_LOGGEDIN_CAS => 'eg_CAS';
+use constant COOKIE_URL_CAS => 'eg_CAS_URL';
 use constant COOKIE_PHYSICAL_LOC => 'eg_physical_loc';
 use constant COOKIE_SSS_EXPAND => 'eg_sss_expand';
 
@@ -360,6 +361,10 @@ sub load_login {
     my $org_unit = $ctx->{physical_loc} || $ctx->{aou_tree}->()->id;
     my $persist = $cgi->param('persist');
     my $ticket = $cgi->param('ticket');
+    my $cas_redirect_to = $cgi->param('redirect_to');
+    if ($cgi->cookie(COOKIE_URL_CAS)) {
+       $cas_redirect_to = $cgi->cookie(COOKIE_URL_CAS);
+    }
     my $cas_flag = '0';
 
     # initial log form only
@@ -368,7 +373,28 @@ sub load_login {
         # values for the other checks
         $username = '_CAS_';
         $password = '_CAS_';
+
     }
+
+    my $login_page = sprintf('%s://%s%s/login',($self->ctx->{is_staff} ? 'oils' : 'https'), $self->ctx->{hostname}, $self->ctx->{opac_root});
+
+    # CAS does not handle complex URLs, so we put the URL in a cookie 
+    if (!$cgi->cookie(COOKIE_URL_CAS) && $cas_redirect_to && !$username && !$password) {
+           return $self->generic_redirect(
+              "$login_page?redirect_to=$cas_redirect_to",
+              [
+                 # contains the service url for CAS
+                 $cgi->cookie(
+                    -name => COOKIE_URL_CAS,
+                    -path => '/',
+                    -secure => 0,
+                    -value => $cas_redirect_to,
+                    -expires => '+10m'
+                 )
+              ]
+           );
+    }
+
     return Apache2::Const::OK unless $username and $password;
 
     # Should we append an email hostname to the username?
@@ -420,6 +446,9 @@ sub load_login {
         if ($ticket) {
             $args->{ticket} = $ticket;
             $cas_flag = '1';
+        } else {
+            # zap CAS redirect if not CAS request
+            $cas_redirect_to = undef;
         } 
         $response = $U->simplereq(
             'open-ils.auth_proxy',
@@ -442,6 +471,7 @@ sub load_login {
     my $login_cookie_expires = ($persist) ? CORE::time + $response->{payload}->{authtime} : undef;
 
     return $self->generic_redirect(
+        $cas_redirect_to ||
         $cgi->param('redirect_to') || $acct,
         [
             # contains the actual auth token and should be sent only over https
@@ -461,6 +491,14 @@ sub load_login {
                 -value => '1',
                 -expires => $login_cookie_expires
             ),
+            # invalidate CAS url since it is only needed at point of authentication
+            # if it is used, have it go to login page
+            $cgi->cookie(
+                -name => COOKIE_URL_CAS,
+                -path => '/',
+                -value => $login_page,
+                -expires => '-1h'
+            ),
             # contains only a hint that we are using CAS
             $cgi->cookie(
                 -name => COOKIE_LOGGEDIN_CAS,
@@ -480,6 +518,9 @@ sub load_logout {
     my $self = shift;
     my $redirect_to = shift || $self->cgi->param('redirect_to');
 
+    my $login_page = sprintf('%s://%s%s/login',($self->ctx->{is_staff} ? 'oils' : 'https'), 
+       $self->ctx->{hostname}, $self->ctx->{opac_root});
+
     # If the user was adding anyting to an anonymous cache 
     # while logged in, go ahead and clear it out.
     $self->clear_anon_cache;
@@ -501,6 +542,12 @@ sub load_logout {
                 -expires => '-1h'
             ),
             $self->cgi->cookie(
+                -name => COOKIE_URL_CAS,
+                -path => '/',
+                -value => $login_page,
+                -expires => '-1h'
+            ),
+            $self->cgi->cookie(
                 -name => COOKIE_LOGGEDIN_CAS,
                 -path => '/',
                 -value => '',