--- loncom/auth/lonshibauth.pm 2021/06/22 16:56:35 1.5
+++ loncom/auth/lonshibauth.pm 2021/12/12 20:56:36 1.14.2.1
@@ -1,7 +1,8 @@
# The LearningOnline Network
-# Redirect Shibboleth authentication to designated URL (/adm/sso).
+# Redirect Single Sign On authentication to designated URL:
+# /adm/sso, by default.
#
-# $Id: lonshibauth.pm,v 1.5 2021/06/22 16:56:35 raeburn Exp $
+# $Id: lonshibauth.pm,v 1.14.2.1 2021/12/12 20:56:36 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -28,14 +29,15 @@
=head1 NAME
-Apache::lonshibauth - Redirect Shibboleth authentication
+Apache::lonshibauth - Redirect Single Sign On authentication
=head1 SYNOPSIS
-Invoked when lonOtherAuthen is set to yes, and type is Shibboleth
+Invoked when an Apache config file includes:
+PerlAuthenHandler Apache::lonshibauth
If server is configured as a Shibboleth SP, the main Apache
-configuration file, e.g., /etc/httpd/conf/httpd.conf
+configuration file, e.g., /etc/httpd/conf/httpd.conf
(for RHEL/CentOS/Scentific Linux/Fedora) should contain:
LoadModule mod_shib /usr/lib/shibboleth/mod_shib_22.so
@@ -43,10 +45,99 @@ LoadModule mod_shib /usr/lib/shibboleth/
or equivalent (depending on Apache version)
before the line to include conf/loncapa_apache.conf
+If some other Apache module is in use for Single Sign On
+authentication e.g., mod_auth_cas or mod_sentinel,
+then a separate config file should be created which
+includes settings for the authentication module.
+
=head1 INTRODUCTION
-Redirects a user requiring Single Sign On via Shibboleth to a
-URL -- /adm/sso -- on the server which is configured to use that service.
+Redirects a user requiring Single Sign On to a URL on the server
+which is configured to use that service. The default URL is:
+/adm/sso.
+
+If this is to be used with a Single Sign On service other Shibboleth
+then an Apache config file needs to be loaded which:
+
+(a) loads the corresponding Apache module, and
+(b) sets appropriate values for perl vars in
+an block, and
+(c) sets an appropriate value for AuthType in a
+
+ block, which also contains
+
+require valid-user
+
+PerlAuthzHandler Apache::lonshibacc
+
+PerlAuthzHandler Apache::lonacc
+
+In the case of Shibboleth no additional file is needed
+because loncapa_apache.conf already contains:
+
+
+ PerlAuthenHandler Apache::lonshibauth
+ PerlSetVar lonOtherAuthen yes
+ PerlSetVar lonOtherAuthenType Shibboleth
+
+
+
+and
+
+
+ Header set Cache-Control "private,no-store,no-cache,max-age=0"
+
+ AuthType shibboleth
+ ShibUseEnvironment On
+ ShibRequestSetting requireSession 1
+ ShibRequestSetting redirectToSSL 443
+ require valid-user
+ PerlAuthzHandler Apache::lonshibacc
+ PerlAuthzHandler Apache::lonacc
+ ErrorDocument 403 /adm/login
+ ErrorDocument 500 /adm/errorhandler
+
+
+ PerlTypeHandler Apache::lonnoshib
+
+
+
+
+If the service is not Shibboleth, then (optionally) a URL that is
+not /adm/sso can be used as the URL for the service, e.g., /adm/cas
+or /adm/sentinel, by setting a lonOtherAuthenUrl perl var
+in an Apache config file containing (for example):
+
+PerlSetVar lonOtherAuthenUrl /adm/sentinel
+
+
+ PerlAuthenHandler Apache::lonshibauth
+ PerlSetVar lonOtherAuthen yes
+ PerlSetVar lonOtherAuthenType Sentinel
+
+
+
+
+ Header set Cache-Control "private,no-store,no-cache,max-age=0"
+
+ AuthType Sentinel
+ require valid-user
+ PerlAuthzHandler Apache::lonshibacc
+ PerlAuthzHandler Apache::lonacc
+ ErrorDocument 403 /adm/login
+ ErrorDocument 500 /adm/errorhandler
+
+
+ PerlTypeHandler Apache::lonnoshib
+
+
+
+
+In the example above for Sentinel SSO, it would also be possible to
+use /adm/sso instead of /adm/sentinel, in which case (i) there would be
+no need to define lonOtherAuthenUrl, (ii) there would be
+and (iii) the block would not be needed as
+it is already present in /etc/httpd/conf/loncapa_apache.conf.
=head1 HANDLER SUBROUTINE
@@ -54,10 +145,56 @@ This routine is called by Apache and mod
=over 4
-If $r->user defined and requested uri not /adm/sso
-redirect to /adm/sso
+If $r->user is defined and requested URL is not /adm/sso or
+other specific URL as set by a lonOtherAuthenUrl perlvar,
+then redirect to /adm/sso (or to the specific URL).
+
+Otherwise return DECLINED.
+
+In the case of redirection a query string is appended,
+which will contain either (a) the originally requested URL,
+if not /adm/sso (or lonOtherAuthenUrl URL), and
+any existing query string in the original request, or
+(b) if redirect is to /adm/login to support dual SSO and
+non-SSO, a query string which contains sso=tokenID, where the
+token contains information for deep-linking to course/resource.
+
+Support is included for use of LON-CAPA's standard log-in
+page -- /adm/login -- to support dual SSO and non-SSO
+authentication from that "landing" page.
+
+To enable dual SSO and non-SSO access from /adm/login
+a Domain Coordinator will use the web GUI:
+Main Menu > Set domain configuration > Display
+("Log-in page options" checked)
+and for any of the LON-CAPA domain's servers which
+will offer dual login will check "Yes" and then set:
+(a) SSO Text, Image, Alt Text, URL, Tool Tip
+(b) non-SSO: Text
+
+The value in the URL field should be /adm/sso,
+or the same URL as set for the lonOtherAuthenUrl
+perl var, e.g., /adm/sentinel.
-Otherwise return DECLINED
+=back
+
+=head1 NOTABLE SUBROUTINES
+
+=over 4
+
+=item set_token()
+
+Inputs: 2
+$r - request object
+$lonhost - hostID of current server
+
+Output: 1
+$querystring - query string to append to URL
+when redirecting.
+
+If role and/or symb are present in the original query string:
+then they will be stored in the token file on the server,
+for access later to support deep-linking.
=back
@@ -68,29 +205,47 @@ package Apache::lonshibauth;
use strict;
use lib '/home/httpd/lib/perl/';
use Apache::lonnet;
+use Apache::loncommon;
+use Apache::lonacc;
use Apache::Constants qw(:common REDIRECT);
-use LONCAPA qw(:DEFAULT);
+use LONCAPA qw(:DEFAULT :match);
sub handler {
my $r = shift;
- my $target = '/adm/sso';
- my $uri = $r->uri;
- if (($r->user eq '') && ($uri ne $target)) {
+ my $ssourl = '/adm/sso';
+ if ($r->dir_config('lonOtherAuthenUrl') ne '') {
+ $ssourl = $r->dir_config('lonOtherAuthenUrl');
+ }
+ my $target = $ssourl;
+ if (&Apache::lonnet::get_saml_landing()) {
+ $target = '/adm/login';
+ }
+ if (($r->user eq '') && ($r->uri ne $target) && ($r->uri ne $ssourl)) {
my $lonhost = $Apache::lonnet::perlvar{'lonHostID'};
my $hostname = &Apache::lonnet::hostname($lonhost);
if (!$hostname) { $hostname = $r->hostname(); }
my $protocol = $Apache::lonnet::protocol{$lonhost};
unless ($protocol eq 'https') { $protocol = 'http'; }
my $alias = &Apache::lonnet::use_proxy_alias($r,$lonhost);
- $hostname = $alias if ($alias ne '');
- my $dest = $protocol.'://'.$hostname.$target;
- $r->subprocess_env;
- if ($ENV{'QUERY_STRING'} ne '') {
- $dest .= '?'.$ENV{'QUERY_STRING'};
+ if (($alias ne '') &&
+ (&Apache::lonnet::alias_sso($lonhost))) {
+ $hostname = $alias;
}
- if ($uri ne '/adm/roles/') {
- unless ($ENV{'QUERY_STRING'} =~ /origurl=/) {
- $dest.=(($dest=~/\?/)?'&':'?').'origurl='.$uri;
+ my $dest = $protocol.'://'.$hostname.$target;
+ if ($target eq '/adm/login') {
+ my $querystring = &set_token($r,$lonhost);
+ if ($querystring ne '') {
+ $dest .= '?'.$querystring;
+ }
+ } else {
+ my $uri = $r->uri;
+ if ($r->args ne '') {
+ $dest .= (($dest=~/\?/)?'&':'?').$r->args;
+ }
+ unless (($uri eq '/adm/roles') || ($uri eq '/adm/logout')) {
+ unless ($r->args =~ /origurl=/) {
+ $dest.=(($dest=~/\?/)?'&':'?').'origurl='.$uri;
+ }
}
}
$r->header_out(Location => $dest);
@@ -100,5 +255,40 @@ sub handler {
}
}
+sub set_token {
+ my ($r,$lonhost) = @_;
+ my ($firsturl,$querystring,$ssotoken,@names,%token);
+ @names = ('role','symb');
+ map { $token{$_} = 1; } @names;
+ unless (($r->uri eq '/adm/roles') || ($r->uri eq '/adm/logout')) {
+ $firsturl = $r->uri;
+ }
+ if ($r->args ne '') {
+ &Apache::loncommon::get_unprocessed_cgi($r->args);
+ }
+ my $extras;
+ foreach my $name (@names) {
+ if ($env{'form.'.$name} ne '') {
+ $extras .= '&'.$name.'='.&escape($env{'form.'.$name});
+ }
+ }
+ if (($firsturl ne '') || ($extras ne '')) {
+ $extras .= ':sso';
+ $ssotoken = &Apache::lonnet::reply('tmpput:'.&escape($firsturl).
+ $extras,$lonhost);
+ $querystring = 'sso='.$ssotoken;
+ }
+ if ($r->args ne '') {
+ foreach my $key (sort(keys(%env))) {
+ if ($key =~ /^form\.(.+)$/) {
+ my $name = $1;
+ next if ($token{$name});
+ $querystring .= '&'.$name.'='.$env{$key};
+ }
+ }
+ }
+ return $querystring;
+}
+
1;
__END__