CAS Support for Evergreen
authorArt Rhyno <art632000@yahoo.ca>
Wed, 23 Jan 2013 04:35:40 +0000 (23:35 -0500)
committerDan Scott <dscott@laurentian.ca>
Wed, 23 Jan 2013 05:47:39 +0000 (00:47 -0500)
This branch contains one approach to supporting CAS within Evergreen, I
had originally thought of CAS as an "all or nothing" option but I have
tried to make it possible to support standard authentication at the same
time since we will have a transition period where some accounts won't be
CAS-enabled and I suspect this is typical. As well, it is possible that
a library will need to have the ability to add accounts outside of the
campus directory.

In /openils/conf/opensrf.xml, you would add CAS-specific values,for
example

 <app_settings>
    <!-- 'enabled' is the master switch; set to 'true' to enable proxied logins -->
    <enabled>true</enabled>
    <authenticators>
        <authenticator>
           <name>cas</name>
           <module>OpenILS::Application::AuthProxy::CAS_Auth_Conifer</module>
           <cas_validate_url>https://uwinid.uwindsor.ca/cas/proxyValidate</cas_validate_url>
           <cas_service>https://localhost/eg/opac/login</cas_service>
           <cas_suffix>@uwindsor.ca</cas_suffix>
        </authenticator>
        <!-- 'native' is a proxied version of Evergreen's standard authentication -->

        <authenticator>
            <name>native</name>
            <!-- you can add 'login_types' and 'org_units' limits to this authenticator as well, if needed -->
        </authenticator>
    </authenticators>
 </app_settings>

The entry point for CAS is in topnav.tt2, I have added a "cas_intro"
option in order to give an introduction screen before passing a user to
the CAS service:

 <div id="your-acct-login-uwin">
    <a href="[% mkurl(ctx.opac_root _ '/cas_intro') %]"
       class="opac-button opac-button-header" id="home_myopac_link_uwin">
       [% l('Log in to Your Account (UWind ID)') %]
    </a>
 </div>

but you could just go directly to cas at this point:

 <div id="your-acct-login-uwin">
    <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>
 </div>

where ctx.cas.url is set in the config.tt2 file, for example:

 ctx.cas.url = 'https://uwinid.uwindsor.ca/cas/login?service=https://localhost/eg/opac/login';

I use a cookie to indicate that CAS has been used to authenticate, since
it requires a slightly different logout sequence. This also gets
reflected in topnav.tt2 if you are using both CAS and standard
authentication:

[% IF CGI.cookie('eg_CAS') %]
   <a href="[% mkurl(ctx.opac_root _ '/logout?redirect_to=' _ ctx.cas.logout, {}, 1) %]"
      class="opac-button" id="logout_link">[% l('Logout') %]</a>
[% ELSE %]
   <a href="[% mkurl(ctx.opac_root _ '/logout', {}, 1) %]"
      class="opac-button" id="logout_link">[% l('Logout') %]</a>
[% END %]

Note the "redirect_to", you will want to clear the session for logging
out of tpac so that another user on a public station won't stumble into
someone else's account. Again, if authentication is strictly CAS, then
you can just use the CAS form of the logout.

Signed-off-by: Art Rhyno <art632000@yahoo.ca>
Open-ILS/src/perlmods/lib/OpenILS/Application/AuthProxy/CAS_Auth_Conifer.pm [new file with mode: 0644]
Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm
Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Container.pm
Open-ILS/src/templates/opac/cas_intro.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac/parts/config.tt2

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/AuthProxy/CAS_Auth_Conifer.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/AuthProxy/CAS_Auth_Conifer.pm
new file mode 100644 (file)
index 0000000..83b1abb
--- /dev/null
@@ -0,0 +1,57 @@
+package OpenILS::Application::AuthProxy::CAS_Auth_Conifer;
+use strict;
+use warnings;
+use base 'OpenILS::Application::AuthProxy::AuthBase';
+use OpenILS::Event;
+use OpenSRF::Utils::SettingsClient;
+use OpenSRF::Utils::Logger qw(:logger);
+use LWP::UserAgent;
+
+sub authenticate {
+    my ( $self, $args ) = @_;
+
+    my $ticket = $args->{'ticket'};
+    my $cas_validate_url = $self->{'cas_validate_url'};
+    my $cas_service = $self->{'cas_service'};
+    my $cas_suffix = $self->{'cas_suffix'};
+    my $login_succeeded  = 0;
+
+    if ($ticket && $cas_validate_url && $cas_service) {
+       my $ua = LWP::UserAgent->new; 
+
+       # We can now go to the service with this ticket. 
+       my $response = $ua->get( $cas_validate_url . '?ticket=' . $ticket . '&service=' . $cas_service);
+          
+       if ($response->is_success) {
+          my $content_str = $response->as_string;
+          # Does this ever vary? Every example seems to use this namespace
+          $content_str =~ /<cas:user>(.*)<\/cas:user>/;
+          my $username = $1;
+          if ($username) {
+             # The suffix is typically for e-mail
+             if ($cas_suffix) {
+                $username = $username . $cas_suffix;
+             }
+             # We now set the username
+             $args->{username} = $username;
+             # and add a flag for this type of authentication
+             $login_succeeded = 1;
+          }
+       }
+    }
+
+    if ( $login_succeeded ) {
+        return OpenILS::Event->new('SUCCESS');
+    } elsif ( !$ticket ) {
+        $logger->debug("CAS User login failed: Missing ticket");
+        return OpenILS::Event->new( 'LOGIN_FAILED' );
+    } elsif ( !$cas_validate_url || !$cas_service) {
+        $logger->debug("CAS User login failed: The CAS configuration is not complete");
+        return OpenILS::Event->new( 'LOGIN_FAILED' );
+    } else {
+        $logger->debug("User login failed: invalid CAS ticket");
+        return OpenILS::Event->new( 'LOGIN_FAILED' );
+    }
+}
+
+1;
index 6c88f83..9e30cfd 100644 (file)
@@ -28,6 +28,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_PHYSICAL_LOC => 'eg_physical_loc';
 use constant COOKIE_SSS_EXPAND => 'eg_sss_expand';
 
@@ -131,6 +132,9 @@ sub load {
     return $self->load_cache_clear if $path =~ m|opac/cache/clear|;
     return $self->load_temp_warn_post if $path =~ m|opac/temp_warn/post|;
     return $self->load_temp_warn if $path =~ m|opac/temp_warn|;
+    # added for CAS support - we probably want a place to explain why someone is being 
+    # redirected to a third party site
+    return $self->load_cas_intro if $path =~ m|opac/cas_intro|;
 
     # ----------------------------------------------------------------
     #  Everything below here requires SSL
@@ -355,8 +359,16 @@ sub load_login {
     my $password = $cgi->param('password');
     my $org_unit = $ctx->{physical_loc} || $ctx->{aou_tree}->()->id;
     my $persist = $cgi->param('persist');
+    my $ticket = $cgi->param('ticket');
+    my $cas_flag = '0';
 
     # initial log form only
+    if ($ticket) {
+        # CAS does not have either username or password at this point, so we fudge these 
+        # values for the other checks
+        $username = '_CAS_';
+        $password = '_CAS_';
+    }
     return Apache2::Const::OK unless $username and $password;
 
     # Should we append an email hostname to the username?
@@ -405,6 +417,10 @@ sub load_login {
             'open-ils.auth', 'open-ils.auth.authenticate.complete', $args);
     } else {
         $args->{password} = $password;
+        if ($ticket) {
+            $args->{ticket} = $ticket;
+            $cas_flag = '1';
+        } 
         $response = $U->simplereq(
             'open-ils.auth_proxy',
             'open-ils.auth_proxy.login', $args);
@@ -416,7 +432,7 @@ sub load_login {
         $ctx->{login_failed_event} = $response;
         return Apache2::Const::OK;
     }
-
+        
     # login succeeded, redirect as necessary
 
     my $acct = $self->apache->unparsed_uri;
@@ -444,6 +460,14 @@ sub load_login {
                 -secure => 0,
                 -value => '1',
                 -expires => $login_cookie_expires
+            ),
+            # contains only a hint that we are using CAS
+            $cgi->cookie(
+                -name => COOKIE_LOGGEDIN_CAS,
+                -path => '/',
+                -secure => 0,
+                -value => $cas_flag,
+                -expires => $login_cookie_expires
             )
         ]
     );
@@ -475,6 +499,12 @@ sub load_logout {
                 -path => '/',
                 -value => '',
                 -expires => '-1h'
+            ),
+            $self->cgi->cookie(
+                -name => COOKIE_LOGGEDIN_CAS,
+                -path => '/',
+                -value => '',
+                -expires => '-1h'
             )
         ]
     );
index 445654d..b093e62 100644 (file)
@@ -221,4 +221,10 @@ sub load_temp_warn {
     return Apache2::Const::OK;
 }
 
+sub load_cas_intro {
+    my $self = shift;
+    $self->ctx->{'redirect_to'} = $self->cgi->param('redirect_to');
+    return Apache2::Const::OK;
+}
+
 1;
diff --git a/Open-ILS/src/templates/opac/cas_intro.tt2 b/Open-ILS/src/templates/opac/cas_intro.tt2
new file mode 100644 (file)
index 0000000..7903349
--- /dev/null
@@ -0,0 +1,38 @@
+[%  PROCESS "opac/parts/header.tt2";
+    PROCESS "opac/parts/misc_util.tt2";
+    WRAPPER "opac/parts/base.tt2";
+    INCLUDE "opac/parts/topnav.tt2";
+
+    cas_alert = l('You have selected CAS Authentication. This will use your campus userid (also known as login or user name).');
+    ctx.page_title = l("CAS Information") -%]
+    <div id="content-wrapper">
+        <div id="main-content">
+        <div style="height:20px;"></div>
+        <table cellpadding="0" cellspacing="0" border="0">
+            <tr>
+                <td valign="top" width="676" class="login_boxes left_brain">
+                    <table cellpadding="10" cellspacing="0" border="0"
+                        width="100%">
+                        <tr>
+                                <td colspan="2" style="padding-bottom: 10px;">
+                                <h1>[% cas_alert %]</h1>
+                                <br /><br />
+                            </td>
+                        </tr>
+                        <tr>
+                                <td colspan="2" style="padding-bottom: 10px;">
+            <a href="[% ctx.cas.url %]"
+                class="opac-button opac-button-header" id="home_myopac_link">
+                [% l('Continue to Your Account with Your CAS ID') %]
+            </a>
+                                <br /><br />
+                            </td>
+                        </tr>
+                    </table> 
+                </td>
+            </tr>
+        </table>
+        <div style="height:20px;"></div>
+        </div>
+    </div>
+[% END %]
index 737c9a8..5fc219c 100644 (file)
@@ -28,6 +28,15 @@ ctx.refworks.url = 'http://www.refworks.com';
 # ctx.refworks.url = 'http://librweb.laurentian.ca/login?url=http://refworks.scholarsportal.info';
 
 ##############################################################################
+# CAS support
+##############################################################################
+# Specify CAS URL for Login
+# ctx.cas.url = 'https://myorg.org/cas/login?service=https://mylibrary.org/eg/opac/login';
+# Specify CAS URL for Logout
+# ctx.cas.logout = 'https://myorg.org/cas/logout';
+
+
+##############################################################################
 # OpenURL resolution
 ##############################################################################
 # Evergreen provides the ability to point at an OpenURL resolver to find