Difference between revisions of "Shibboleth"

From EPrints Documentation
Jump to: navigation, search
(Updated Shibboleth page with instructions for CentOS 7, that can be augmented for other OS)
(Advice about added ns to metadata generated for service provider)
 
(54 intermediate revisions by 2 users not shown)
Line 1: Line 1:
'''This page details how to install and integrate Shibboleth with EPrints 3.3 on a CentOS 7 operating system.'''  The process should be fairly similar for other modern RedHat-based Linux distributions such as RHEL 7 and Fedora 21/22.  However, it will somewhat different for Debian-based Linux, such as Ubuntu and Debian itself and other Linux distribution.  Typically. this just be different package names and different commands to manage applications.
+
{{Manual}}
 +
'''This page details how to install and integrate Shibboleth with EPrints 3.3.x or 3.4.x on a CentOS 7 operating system.'''  The process should be fairly similar for other comparable Red Hat based Linux distributions such as RHEL 7 and Fedora 21/22.  These instructions shouls also be generally application to later versions of RHEL-based Linux (e.g. Rocky Linux 8, Red Hat Enterprise Linux 9, etc.). However, they ma be somewhat different for Debian-based Linux, such as Ubuntu and Debian itself and other Linux distributions.  Typically, this will just be different package names and different commands to manage applications.
  
'''Generally, it is a good idea to run EPrints with HTTPS when using Shibboleth authentication for increased security on the attributes being sent back by the Shibboleth Identity Provider (IdP).  Therefore, it is assumed that EPrints has already been set up to use HTTPS and there already exists an ssl/securevhost.conf under the archive directory structure.'''
+
Generally, it is a good idea to run EPrints with '''HTTPS''' when using Shibboleth authentication for increased security on the attributes being sent back by the Shibboleth Identity Provider (IdP).  Therefore, it is assumed that EPrints has already been set up to use HTTPS and there already exists an '''ssl/securevhost.conf''' under the archive directory structure.
  
 
== Installing Shibboleth ==
 
== Installing Shibboleth ==
* First, add the Shibboleth repository to your list of Yum repositories:
+
* First, add the Shibboleth repository to your list of YUM repositories (if you need this for a different RHEL-based Linux distribution fill in the form at https://shibboleth.net/downloads/service-provider/latest/RPMS/):
   root> cd /etc/yum.repos.d/
+
   root> wget -O /etc/yum.repos.d/shibboleth.repo https://shibboleth.net/cgi-bin/sp_repo.cgi?platform=CentOS_7
  root> wget http://download.opensuse.org/repositories/security:shibboleth/CentOS_7/security:shibboleth.repo
 
  
* Now you can uses Yum to install all package dependencies:
+
* Now you can use Yum to install all package dependencies:
 
   root> yum install log4shib opensaml shibboleth unixODBC xerces-c xml-security-c xmltooling  
 
   root> yum install log4shib opensaml shibboleth unixODBC xerces-c xml-security-c xmltooling  
  
* You may be prompted to accept the importing of the key for the Shibboleth repository, for which you should enter '''y''' and press enter.
+
* You may be prompted to accept the importing of the key for the Shibboleth repository, for which you should type '''y''' and press enter.
  
* Once all the packages are installed you will need to specify the use of Shibboleth Curl library should be loaded by adding the following line to '''/etc/ld.so.conf.d/shibboleth_libs.conf''':
+
* Once you have done that, test that '''shibd''' has no issues:
   /opt/shibboleth/lib64
+
   root> LD_LIBRARY_PATH=/opt/shibboleth/lib64 shibd -t
  
* Once you have done that run '''ldconfig''' and then check '''shibd''' has no issues:
+
* ''shibd -t'' should return a couple of warning, like those listed below.  These are due to it not yet being configured.
  root> ldconfig
 
  root> shibd -t
 
 
 
* ''shibd -t'' should return a couple of warning, like those listed below.  These are due to it not being configured yet.
 
 
   2015-05-11 10:39:01 WARN Shibboleth.Application : insecure cookieProps setting, set to "https" for SSL/TLS-only usage
 
   2015-05-11 10:39:01 WARN Shibboleth.Application : insecure cookieProps setting, set to "https" for SSL/TLS-only usage
 
   2015-05-11 10:39:01 WARN Shibboleth.Application : handlerSSL should be enabled for SSL/TLS-enabled web sites
 
   2015-05-11 10:39:01 WARN Shibboleth.Application : handlerSSL should be enabled for SSL/TLS-enabled web sites
Line 26: Line 22:
 
   overall configuration is loadable, check console for non-fatal problems
 
   overall configuration is loadable, check console for non-fatal problems
  
* If there are no other warning or error messages from ''shibd -t'', you can start it properly and check to make sure it is running.
+
* If there are no other warning or error messages from ''shibd -t'', you can start it properly and check to make sure it is running. You may also want ensure Shibboleth starts at boot using '''systemctl  enable'''
 
   root> systemctl start shibd.service
 
   root> systemctl start shibd.service
 
   root> ps aux | grep shib
 
   root> ps aux | grep shib
 
   shibd    29338  0.4  0.7 419784 15024 ?        Ssl  11:16  0:00 /usr/sbin/shibd -p /var/run/shibboleth/shibd.pid -f -w 30
 
   shibd    29338  0.4  0.7 419784 15024 ?        Ssl  11:16  0:00 /usr/sbin/shibd -p /var/run/shibboleth/shibd.pid -f -w 30
 
   root    29345  0.0  0.0 112640  940 pts/2    S+  11:17  0:00 grep --color=auto -i shib
 
   root    29345  0.0  0.0 112640  940 pts/2    S+  11:17  0:00 grep --color=auto -i shib
 
+
  root> systemctl enable shibd.service
  
 
== Configuring Shibboleth ==
 
== Configuring Shibboleth ==
* Replace '''/etc/shibboleth/shibboleth2.xml''' with the following.  Substituting '''foo.eprints.org''' for the hostname of your EPrints repository, '''https://shib.foo.example.org/idp/shibboleth''' with the entity ID for you Shibboleth Identity Provider and '''foo''' in the pathname of files with the name or your repository (e.g. ''foo/attribute-map.xml'' becomes ''myrepo/attribute-map.xml'').
+
* Replace '''/etc/shibboleth/shibboleth2.xml''' with the following.  Substitute '''foo.eprints.org''' for the hostname of your EPrints repository, '''https://shib.foo.example.org/idp/shibboleth''' with the entity ID for you Shibboleth IdP and '''foo''' in the pathname of files with the name or your repository (e.g. ''foo/attribute-map.xml'' becomes ''myrepo/attribute-map.xml''). '''(This configuration is intended for Shibboleth SP version 2.x and is liable to cause deprecation warnings if  you have installed a recent version of Shibboleth from a package repository.  [[Shibboleth/3.x|Here is a default shibboleth2.xml configuration for Shibboleth 3.x]]).'''
  
   <SPConfig xmlns="urn:mace:shibboleth:2.0:native:sp:config"
+
   <SPConfig xmlns="urn:mace:shibboleth:3.0:native:sp:config"
     xmlns:conf="urn:mace:shibboleth:2.0:native:sp:config"
+
     xmlns:conf="urn:mace:shibboleth:3.0:native:sp:config"
    xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
 
    xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"   
 
    xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"
 
 
     clockSkew="180">
 
     clockSkew="180">
 
    
 
    
 
     <ApplicationDefaults entityID="https://foo.eprints.org/shibboleth"
 
     <ApplicationDefaults entityID="https://foo.eprints.org/shibboleth"
                         REMOTE_USER="eppn persistent-id targeted-id">
+
                         REMOTE_USER="eppn subject-id pairwise-id persistent-id"
 +
                        cipherSuites="DEFAULT:!EXP:!LOW:!aNULL:!eNULL:!DES:!IDEA:!SEED:!RC4:!3DES:!kRSA:!SSLv2:!SSLv3:!TLSv1:!TLSv1.1">
 
    
 
    
       <Sessions lifetime="28800" timeout="3600" relayState="ss:mem"
+
       <Sessions lifetime="28800" timeout="3600" relayState="ss:mem"  
                  checkAddress="false" handlerSSL="true" cookieProps="https">
+
                checkAddress="false" handlerSSL="true" cookieProps="https"
         <SSO entityID="https://shib.foo.example.org/idp/shibboleth">
+
                redirectLimit="exact">
              SAML2 SAML1
+
         <SSO entityID="https://shib.foo.example.org/idp/shibboleth">SAML2</SSO>
        </SSO>
 
 
         <Logout>SAML2 Local</Logout>
 
         <Logout>SAML2 Local</Logout>
         <Handler type="MetadataGenerator" Location="/Metadata" signing="false"/>
+
         <LogoutInitiator type="Admin" Location="/Logout/Admin" acl="127.0.0.1 ::1" />
 
         <Handler type="Status" Location="/Status" acl="127.0.0.1 ::1"/>
 
         <Handler type="Status" Location="/Status" acl="127.0.0.1 ::1"/>
 
         <Handler type="Session" Location="/Session" showAttributeValues="false"/>
 
         <Handler type="Session" Location="/Session" showAttributeValues="false"/>
Line 59: Line 52:
 
    
 
    
 
       <Errors supportContact="root@localhost" helpLocation="/about.html" styleSheet="/shibboleth/main.css"/>
 
       <Errors supportContact="root@localhost" helpLocation="/about.html" styleSheet="/shibboleth/main.css"/>
       <MetadataProvider type="XML" file="foo/idp-metadata.xml"/>
+
       <MetadataProvider type="XML" path="foo/idp-metadata.xml"/>
 
       <AttributeExtractor type="XML" validate="true" reloadChanges="false" path="foo/attribute-map.xml"/>
 
       <AttributeExtractor type="XML" validate="true" reloadChanges="false" path="foo/attribute-map.xml"/>
      <AttributeResolver type="Query" subjectMatch="true"/>
 
 
       <AttributeFilter type="XML" validate="true" path="attribute-policy.xml"/>
 
       <AttributeFilter type="XML" validate="true" path="attribute-policy.xml"/>
       <CredentialResolver type="File" key="foo/sp-key.pem" certificate="foo/sp-cert.pem"/>
+
       <CredentialResolver type="File" use="signing" key="foo/sp-key.pem" certificate="foo/sp-cert.pem"/>
 
+
      <CredentialResolver type="File" use="encryption" key="foo/sp-key.pem" certificate="foo/sp-cert.pem"/>
 +
 
     </ApplicationDefaults>
 
     </ApplicationDefaults>
 
    
 
    
Line 83: Line 76:
 
   root> mv sp-key.pem sp-key.pem.old
 
   root> mv sp-key.pem sp-key.pem.old
  
* Run '''keygen.sh''' from the '''/etc/shibboleth/''' directory, as follows replacing '''foo.eprints.org''' with your EPrints repository name.
+
* Run '''keygen.sh''' from the '''/etc/shibboleth/''' directory, as follows replacing '''foo.eprints.org''' with your EPrints repository hostname.
 
   root> cd /etc/shibboleth
 
   root> cd /etc/shibboleth
 
   root> ./keygen.sh -f -h foo.eprints.org -e https://foo.eprints.org/shibboleth
 
   root> ./keygen.sh -f -h foo.eprints.org -e https://foo.eprints.org/shibboleth
Line 91: Line 84:
 
   root> mv sp-cert.pem sp-key.pem foo/
 
   root> mv sp-cert.pem sp-key.pem foo/
 
   root> mv sp-cert.pem.old sp-cert.pem
 
   root> mv sp-cert.pem.old sp-cert.pem
   root> mv sp-key.pem sp-key.pem
+
   root> mv sp-key.pem.old sp-key.pem
  
* Check that '''sp-cert.pem''' and '''sp-key.pem''' in '''/etc/shibboleth/foo/''' are still have the owner and group by the '''shibd'''.
+
* Check that '''sp-cert.pem''' and '''sp-key.pem''' in '''/etc/shibboleth/foo/''' still have the owner and group '''shibd'''.
 
   root> ls -l /etc/shibboleth/foo/sp-*
 
   root> ls -l /etc/shibboleth/foo/sp-*
 
   -rw-r--r-- 1 shibd shibd 1192 May  6 19:04 /etc/shibboleth/foo/sp-cert.pem
 
   -rw-r--r-- 1 shibd shibd 1192 May  6 19:04 /etc/shibboleth/foo/sp-cert.pem
 
   -rw------- 1 shibd shibd 1708 May  6 19:04 /etc/shibboleth/foo/sp-key.pem
 
   -rw------- 1 shibd shibd 1708 May  6 19:04 /etc/shibboleth/foo/sp-key.pem
 +
 +
* Run '''metagen.sh''' from the '''/etc/shibboleth/''' directory, as follows replacing '''foo.eprints.org''' with your EPrints repository hostname.  You will ultimately need to send the output of this to the person managing the Shibboleth IdP server with which you want to register your EPrints repository as a service.
 +
  root> cd /etc/shibboleth
 +
  root> ./metagen.sh -ALO -c foo/sp-cert.pem -h foo.eprints.org -e https://foo.eprints.org/shibboleth > foo/sp_metadata.xml
 +
 +
* Modify ''' foo/sp_metadata.xml''' to add in the namepace definitions by separately changing the <code>md:EntityDescriptor</code> and <code>ds:KeyInfo</code> lines as follows from:
 +
<md:EntityDescriptor entityID="https://for.eprints.org/shibboleth">
 +
  ...
 +
    ...
 +
      <ds:KeyInfo>
 +
to:
 +
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" entityID="https://for.eprints.org/shibboleth">
 +
  ...
 +
    ...
 +
      <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
  
 
* Use ''wget'' to download the metadata from your Shibboleth IdP (e.g. shib.foo.example.org) to the '''/etc/shibboleth/foo/''' directory.
 
* Use ''wget'' to download the metadata from your Shibboleth IdP (e.g. shib.foo.example.org) to the '''/etc/shibboleth/foo/''' directory.
 
   root> wget -O /etc/shibboleth/foo/idp-metadata.xml https://shib.foo.example.org/idp/shibboleth
 
   root> wget -O /etc/shibboleth/foo/idp-metadata.xml https://shib.foo.example.org/idp/shibboleth
 +
 +
=== Using Remote IdP Metatdata ===
 +
 +
As IdP Metadata may expire, you may want to use the remote metadata URL rather than a local copy.  This is what you need to do to switch to using remote IdP metadata:
 +
 +
* Edit the '''MetadataProvider''' line to something like the following.  The reloadInterval is best set to 7200 but this can be set less frequently:
 +
 +
<MetadataProvider type="XML" url="https://shib.foo.example.org/idp/metadata.xml" backingFilePath="foo/idp-metadata.xml" reloadInterval="7200"/>
 +
 +
* Make sure the reloadInterval is appropriate for the IdP metadata you are downloading.  If you have large metadata file,(e.g. from a federated metadata service containing metadata for other IdPs), then it may be better to set this higher than the default.  However, the configuration checker may warn you that your reload interval is too long, so you may have to choose to ignore this, if you want to avoid downloading a large federated metadata file too often.
 +
 +
* Make sure that the directory '''/etc/shibboleth/foo/''' and '''/etc/shibboleth/foo/idp-metadata.xml''' if it already exists) are owned by '''shibd''':
 +
 +
chown shibd:shibd /etc/shibboleth/foo/
 +
chown shibd:shibd /etc/shibboleth/foo/idp-metadata.xml
 +
 +
* To test Shibboleth you will need to make sure your '''LD_LIBRARY_PATH''' is set the same as shibd would have when started using ''systemctl shibd start''.  This should return the message: ''overall configuration is loadable, check console for non-fatal problems''.
 +
 +
LD_LIBRARY_PATH=/opt/shibboleth/lib64:$LD_LIBRARY_PATH shibd -t
 +
 +
* The above message is because it is now advised not to leave the MetadataGenerator enabled unnecessarily.  However, whilst you are setting up Shibboleth, it is useful to have the metadata generator, save you needing to build your own Service Provider metadata file to register with your Identity Provider.  Later on you can comment out the MetadataGenerator line to stop getting this warning message.
 +
 +
* Now you can restart '''shibd''' properly:
 +
 +
systemctl restart shibd
  
 
== Configuring Apache and EPrints ==
 
== Configuring Apache and EPrints ==
 
'''N.B. All these actions should be carried out by the ''eprints'' user, except when prepended with ''root>'' which means the command should be run as the ''root'' user.'''
 
'''N.B. All these actions should be carried out by the ''eprints'' user, except when prepended with ''root>'' which means the command should be run as the ''root'' user.'''
* Add the following configuration to your archive's '''ssl/securevhost.conf''', after the '''Include /opt/eprints3/cfg/apache_ssl/foo.conf''', substituting '''foo''' for your archive's name where appropriate.  (This assumes you are running Apache 2.4 or greater).
+
* Add the following configuration to your archive's '''ssl/securevhost.conf''', after the '''Include /opt/eprints3/cfg/apache_ssl/foo.conf''', substituting '''foo''' for your archive's name where appropriate.  (This assumes you are running Apache 2.4 or greater). See [[#Apache 2.2 (and lower) Configuration for EPrints Shibboleth Integration |Troubleshooting]] for instructions on the configuration to use for Apache 2.2. or lower.
 +
 
 
  Alias /shibboleth /opt/eprints3/archives/foo/shibboleth
 
  Alias /shibboleth /opt/eprints3/archives/foo/shibboleth
 
  <Location "/shibboleth">
 
  <Location "/shibboleth">
Line 128: Line 162:
 
  exit( 0 ) unless( defined $session );
 
  exit( 0 ) unless( defined $session );
 
   
 
   
  $session->send_http_header( content_type=>"text/html" );
+
  $session->send_http_header( "content_type" => "text/html" );
 
   
 
   
 
  print "&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;code&gt;\n";
 
  print "&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;code&gt;\n";
Line 144: Line 178:
 
   root> apachectl restart
 
   root> apachectl restart
  
* In a web browser go the the '''/cgi/shibboleth''' page for your repository, (e.g. ''https://foo.eprints.org/cgi/shibboleth'').  You should be redirected to an error page for your your Shibboleth IdP (e.g. ''https://shib.foo.example.org/idp/profile/SAML2/Redirect/SSO?...'').   
+
* In a web browser go the '''/cgi/shibboleth''' page for your repository, (e.g. ''https://foo.eprints.org/cgi/shibboleth'').  You should be redirected to an error page for your your Shibboleth IdP (e.g. ''https://shib.foo.example.org/idp/profile/SAML2/Redirect/SSO?...'').   
  
* If instead you are displayed with a list of key values or are forbidden to access the page you have not configured Apache properly, see [[#Apache_Configuration_Issues|Apache_Configuration_Issues]] under [[#Troubleshooting|Troubleshooting]].  If you see an error message like the one below you have not set up Shibboleth properly, see [[#Shibboleth_Configuration_Issues|Shibboleth_Configuration_Issues]] under [[#Troubleshooting|Troubleshooting]].
+
* If instead you are displayed with a list of key values or are forbidden to access the page, you have not configured Apache properly, if so, see [[#Apache_Configuration_Issues|Apache_Configuration_Issues]] under [[#Troubleshooting|Troubleshooting]].  If you see an error message like the one below, you have not set up Shibboleth properly, if so, see [[#Shibboleth_Configuration_Issues|Shibboleth_Configuration_Issues]] under [[#Troubleshooting|Troubleshooting]].
 
  opensaml::saml2md::MetadataException
 
  opensaml::saml2md::MetadataException
 
  The system encountered an error at Wed May 6 15:19:27 2015
 
  The system encountered an error at Wed May 6 15:19:27 2015
Line 154: Line 188:
 
  Unable to locate metadata for identity provider (https://shib.foo.example.org/idp/shibboleth)
 
  Unable to locate metadata for identity provider (https://shib.foo.example.org/idp/shibboleth)
  
* Next, copy the following code into your archive (e.g. ''/opt/eprints3/archives/foo/'') ad '''cfg/cfg.d/zz_shibboleth.pl'''.  This is needed to redirect login and logout to use Shibboleth rather than local login.
+
* Next, copy the following code into your archive (e.g. ''/opt/eprints3/archives/foo/'') as '''cfg/cfg.d/zz_shibboleth.pl'''.  This is needed to redirect login and logout to use Shibboleth rather than local login.
 
  $c->{get_login_url} = sub {
 
  $c->{get_login_url} = sub {
 
   my( $session, $target ) = @_;
 
   my( $session, $target ) = @_;
Line 204: Line 238:
 
  eprints> cp /usr/share/shibboleth/main.css /opt/eprints3/archives/foo/shibboleth/
 
  eprints> cp /usr/share/shibboleth/main.css /opt/eprints3/archives/foo/shibboleth/
  
* Now, copy the following code into your archive (e.g. ''/opt/eprints3/archives/foo/'') as '''shibboleth/login'''.  This is the most basic login script that should work with the minimal attributes any Shibboleth IdP returns logging in users with existing accounts and creating new accounts for users logging in for the first time. Look under the [[#Customisation|Customisation]] section for advice on how to modify this script to meet your purposes.
+
* Now, copy the following code into your archive (e.g. ''/opt/eprints3/archives/foo/'') as '''shibboleth/login'''.  This is the most basic login script that should work with the minimal attributes any Shibboleth IdP returns and '''only logging in users with existing accounts'''. Look under the [[#Customisation|Customisation]] section for advice on how to modify this script to meet your requirements, such as creation user accounts on-the-fly.
 
  use EPrints;
 
  use EPrints;
 
  use strict;
 
  use strict;
Line 210: Line 244:
 
  my $session = EPrints::Session->new();
 
  my $session = EPrints::Session->new();
 
  my $url = $session->param( "target" );
 
  my $url = $session->param( "target" );
 +
if ( defined $url )
 +
{
 +
  my $target_uri = URI->new( $url );
 +
  my $repository_uri = URI->new( $session->get_repository->get_conf( 'base_url' ) );
 +
  if ( !$target_uri->can( 'host' ) || $target_uri->host ne $repository_uri->host )
 +
  {
 +
    $url = undef;
 +
  }
 +
}
 
  $url = $session->get_repository->get_conf( "userhome" ) unless EPrints::Utils::is_set( $url );
 
  $url = $session->get_repository->get_conf( "userhome" ) unless EPrints::Utils::is_set( $url );
 
   
 
   
 
  my $user = &get_user;
 
  my $user = &get_user;
 
   
 
   
  if( defined $user ) {
+
  if( defined $user )
 +
{
 
   $user->set_value( "last_login", EPrints::Time::get_iso_timestamp() );
 
   $user->set_value( "last_login", EPrints::Time::get_iso_timestamp() );
 
   $user->commit;
 
   $user->commit;
Line 222: Line 266:
 
     userid => $user->id,
 
     userid => $user->id,
 
   })->set_cookies();
 
   })->set_cookies();
 
  $session->redirect( $url );
 
 
  }
 
  }
  else {
+
  else
   $session->redirect( $session->get_repository->get_conf( "base_dir" ) . "/account_required.html");
+
{
 +
   $url = $session->get_repository->get_conf( "base_url" ) . "/account_required.html";
 
  }
 
  }
 
   
 
   
 +
$session->send_http_header( "content-type" => "text/html" );
 +
print '<html><head><meta http-equiv="refresh" content="0;url='.$url.'"/></head><body></body></html>';
 
  $session->terminate;
 
  $session->terminate;
 
    
 
    
  sub get_user {
+
  sub get_user  
   my ( $username, $email ) = ( undef, '');
+
{
   if( $ENV{eppn} ) {
+
   my ( $username, $email ) = ( undef, "" );
 +
   if( $ENV{eppn} )
 +
  {
 
     ( $username ) = split( /@/, $ENV{eppn}, 2);
 
     ( $username ) = split( /@/, $ENV{eppn}, 2);
 
     $username = lc( $username );
 
     $username = lc( $username );
Line 240: Line 287:
 
   return unless EPrints::Utils::is_set( $username );
 
   return unless EPrints::Utils::is_set( $username );
 
   my $user = $session->user_by_username( $username );
 
   my $user = $session->user_by_username( $username );
   $user->set_value( "email", $email );
+
   if( defined $user && defined $email )
  $user->commit;
+
  {
 +
    $user->set_value( "email", $email );
 +
    $user->commit;
 +
  }
 
   return $user;
 
   return $user;
 
  }
 
  }
  
* Next, add the following markup to '''cfg/lang/en/static/account_required.xpage''' under your archive (e.g. ''/opt/eprints3/archives/foo/'').  Substituting ''staff and students of the University of Foo'' to describe the particular group of people logged in access is restricted to.
+
* Next, add the following markup to '''cfg/lang/en/static/account_required.xpage''' under your archive (e.g. ''/opt/eprints3/archives/foo/'').  Substituting ''staff and students of the University of Foo'' to describe to which particular group of people logged in access is restricted.
  
 
  <?xml version="1.0" standalone="no" ?>
 
  <?xml version="1.0" standalone="no" ?>
Line 261: Line 311:
 
* In a web browser go to the '''/shibboleth/login''' page for your repository, (e.g. ''https://foo.eprints.org/shibboleth/login'').  Like before with ''/cgi/shibboleth'' you should be taken to your Shibboleth IdP's site albeit displaying an error message.
 
* In a web browser go to the '''/shibboleth/login''' page for your repository, (e.g. ''https://foo.eprints.org/shibboleth/login'').  Like before with ''/cgi/shibboleth'' you should be taken to your Shibboleth IdP's site albeit displaying an error message.
  
* The Shibboleth Identity Provider shows an error message because EPrints as a Shibboleth Service Provider is not yet registered with it.  To do this you need to send the administrator of the Shibboleth Identity Provider the metadata for your Service Provider.  By default this is not external accessible so you will need to do a wget from the command line:
+
* The Shibboleth IdP shows an error message because EPrints as a Shibboleth Service Provider is not yet registered with it.  To do this you need to send the administrator of the Shibboleth IdP the metadata for your Service Provider.  You will have generated this earlier when you ran <code>metagen.sh</code>.  Copy off your EPrints server the file that this wrote (e.g. to <tt>/etc/shibboleth/foo/sp_metadata.xml</tt>) and send it to the Shibboleth IdP administrator.  They should be able to upload this to register EPrints as a Service Provider application.
   wget --no-check-certificate -O - -o /dev/null https://localhost/Shibboleth.sso/Metadata | xmllint --format - > ~/sp-metadata.xml
+
 
 +
* Once registered, use a web browser to go to '''/shibboleth/login''' page for your repository, (e.g. ''https://foo.eprints.org/shibboleth/login'') again.  This time you should be prompted for a username and password on the Shibboleth IdP site. Once you have typed this in and clicked to login, you should be returned to EPrints on the '''/cgi/users/home''' page for your repository.  If not, see [[#Login_Issues|Login Issues]] under [[#Troubleshooting|Troubleshooting]] below.
 +
 
 +
== Troubleshooting ==
 +
 
 +
=== Apache Configuration Issues ===
 +
==== Apache 2.2 (and lower) Configuration for EPrints Shibboleth Integration ====
 +
* Similarly to the instructions for Apache 2.4 and above, place the slightly different following configuration after the '''Include''' line for ''apache_ssl/foo.conf'', (substituting '''foo''' for your archive's name):
 +
 
 +
  Alias /shibboleth /opt/eprints3/archives/foo/shibboleth
 +
   <Directory "/opt/eprints3/archives/foo/shibboleth">
 +
    SetHandler perl-script
 +
    PerlHandler ModPerl::Registry
 +
    PerlSendHeader Off
 +
    Options ExecCGI FollowSymLinks
 +
 
 +
    AuthType shibboleth
 +
    ShibRequireSession On
 +
    require valid-user
 +
  </Directory>
 +
 
 +
  <Location /cgi/shibboleth>
 +
    AuthType shibboleth
 +
    ShibRequireSession On
 +
    require valid-user
 +
  </Location>
 +
 
 +
=== Shibboleth Configuration Issues ===
 +
==== With attribute-map.xml ====
 +
When authenticating using Shibboleth to login to EPrints you may see the following line in '''/var/log/shibboleth.shibd.log'''
 +
 
 +
  2015-09-09 09:26:43 INFO Shibboleth.AttributeExtractor.XML [2]: skipping unmapped SAML 2.0 Attribute with Name:  urn:mace:dir:attribute-def:department
 +
 
 +
In some cases this might not be an issue, as EPrints does not necessarily to make use of all the attributes returned by the IdP but without a mapping in attribute-map.xml.  In this case '''urn:mace:dir:attribute-def:department''' is not by default needed by EPrints to create/update a user account.  However, values like '''sn''', '''givenName''' and '''mail''' are but if you have used the attribute-map.xml provided later on the page you should not see a line like above in '''shibd.log'''.  In some cases you may still see an line like this in the log even if you think you have defined the attribute.  The line below demonstrate two known issues:
 +
 
 +
  2015-09-09 09:26:43 INFO Shibboleth.AttributeExtractor.XML [2]: skipping unmapped SAML 2.0 Attribute with Name:  givenName, Format:urn:oasis:names:tc:SAML:2.0:attrname-format:basic
  
* If you get no error messages, this command will have saved you Shibboleth Service Provider metadata in a file in your home directory called '''sp-metadata.xml''', which you can now email to the administrator of the Shibboleth Identity Provider and then wait for them to get back to you to tell you it is registered.
+
# The attribute itself has no namespace it is just ''''givenName''' rather than '''urn:mace:dir:attribute-def:givenName'''
 +
# The attribute has a format that most also be defined in the attribute-map.xml attribute.
  
* Once registered use a web browser to go to '''/shibboleth/login''' page for your repository, (e.g. ''https://foo.eprints.org/shibboleth/login'') again.  This time you should be prompted for a username and password on the Shibboleth Identity Provider site. Once you have typed this in and clicked to loginYou should be returned to EPrints and be on the '''/cgi/users/home''' page for your repository.  If not see [[#Login_Issues|Login_Issues]] under [[#Troubleshooting|Troubleshooting]] below.
+
Below shows how to both include the format, which is required for the attribute to be successful mappedAs well as define the name of the attribute without a namespace:
  
== Troubleshooting ==
+
  <Attribute name="givenName" nameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" id="givenName"/>
 +
 
 +
A typical Shibboleth IdP would have both a namespaced attribute name and would not apply a format to an attribute that is a simple text string.  Therefore it is worth enquiring with the IdP provider if either of these two happen to be the case.
  
=== 1. Apache Configuration Issues ===
+
==== With idp-metadata.xml ====
To be added.
+
If you want to be able extract scoped attributes (e.g. '''eduPersonPrincipalName''' otherwise described as '''eppn''').  Then you will need to ensure that the expected scope of this attribute, (effectively the domain part in eduPersonPrincipalName is the scope or one of the scopes in the idp-metadata.xml you downloaded from the Shibboleth IdP.  For example if the '''eduPersonPrincipalName''' is '''bar@foo.ac.uk'''.  Then the following should be defined in idp-metadata.xml ('''N.B. the namespace abbreviations (md: and shibmd:) may be different for the IdP you are working with'''):
  
=== 2. Shibboleth Configuration Issues ===
+
  <md:Extensions>
To be added.
+
    <shibmd:Scope regexp="false">foo.ac.uk</shibmd:Scope>
 +
  </md:Extensions>
  
=== 3. Login Issues ===
+
=== Login Issues ===
 
To be added.
 
To be added.
  
Line 283: Line 372:
  
 
=== Shibboleth /etc/shibboleth/foo/attribute-map.xml config ===
 
=== Shibboleth /etc/shibboleth/foo/attribute-map.xml config ===
To be added.
+
This is adapted from the default '''attribute-map.xml''' provided when the Shibboleth SP package is installed to only include the attribute subsequently used by EPrints Shibboleth Perl script that can be found below.  Namely:
 +
 
 +
{| border="1" cellpadding="10" cellspacing="0"
 +
!Field name
 +
!Field description
 +
!SAML v1.1 attribute URN
 +
!SAMLv2 attribute URN
 +
|-
 +
|eppn
 +
|Edu Person Principal Name
 +
|urn:mace:dir:attribute-def:eduPersonPrincipalName
 +
|urn:oid:1.3.6.1.4.1.5923.1.1.1.6
 +
|-
 +
|sn
 +
|Surname
 +
|urn:mace:dir:attribute-def:sn
 +
|urn:oid:2.5.4.4
 +
|-
 +
|givenName
 +
|Given (first) name(s)
 +
|urn:mace:dir:attribute-def:givenName
 +
|urn:oid:2.5.4.42
 +
|-
 +
|mail
 +
|Email address
 +
|urn:mace:dir:attribute-def:mail
 +
|urn:oid:0.9.2342.19200300.100.1.3 
 +
|-
 +
|}
 +
 
 +
 
 +
You may wish to refer to the default '''attribute-map.xml''' is you want to use other attributes.
 +
 
 +
  <Attributes xmlns="urn:mace:shibboleth:2.0:attribute-map" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 +
 
 +
    <Attribute name="urn:mace:dir:attribute-def:eduPersonPrincipalName" id="eppn">
 +
      <AttributeDecoder xsi:type="ScopedAttributeDecoder"/>
 +
    </Attribute>
 +
    <Attribute name="urn:oid:1.3.6.1.4.1.5923.1.1.1.6" id="eppn">
 +
      <AttributeDecoder xsi:type="ScopedAttributeDecoder"/>
 +
    </Attribute>
 +
 
 +
    <Attribute name="urn:mace:dir:attribute-def:sn" id="sn"/>
 +
    <Attribute name="urn:mace:dir:attribute-def:givenName" id="givenName"/>
 +
    <Attribute name="urn:mace:dir:attribute-def:mail" id="mail"/>
 +
 
 +
    <Attribute name="urn:oid:2.5.4.4" nameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" id="sn"/>
 +
    <Attribute name="urn:oid:2.5.4.42" nameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" id="givenName"/>
 +
    <Attribute name="urn:oid:0.9.2342.19200300.100.1.3" nameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" id="mail"/>
 +
 
 +
  </Attributes>
  
=== EPrints /shibboleth/login script ===
+
=== EPrints /shibboleth/login script with user account creation ===
To be added.
+
This is the standard EPrints Shibboleth login script.  It makes a number of assumptions:
 +
# That accounts should be created if they do not already exist for a particular user.
 +
# That you wish to create a standard user account (not an editor administrator) account.
 +
# That the Shibboleth IdP provides all the attributes (eduPersonPrinicpalName (seen as eppn), sn, givenName and mail) required.
  
 +
use EPrints;
 +
use strict;
 +
 +
my $session = EPrints::Session->new();
 +
my $url = $session->param( "target" );
 +
if ( defined $url )
 +
{
 +
  my $target_uri = URI->new( $url );
 +
  my $repository_uri = URI->new( $session->get_repository->get_conf( 'base_url' ) );
 +
  if ( !$target_uri->can( 'host' ) || $target_uri->host ne $repository_uri->host )
 +
  {
 +
    $url = undef;
 +
  }
 +
}
 +
$url = $session->get_repository->get_conf( "userhome" ) unless EPrints::Utils::is_set( $url );
 +
 +
my $user = &get_user;
 +
 +
if( defined $user )
 +
{
 +
  $user->set_value( "last_login", EPrints::Time::get_iso_timestamp() );
 +
  $user->commit;
 +
 +
  EPrints::DataObj::LoginTicket->expire_all( $session );
 +
  $session->dataset( "loginticket" )->create_dataobj({
 +
    userid => $user->id,
 +
  })->set_cookies();
 +
}
 +
else
 +
{
 +
  $url = $session->get_repository->get_conf( "base_url" ) . "/account_required.html";
 +
}
 +
 +
$session->send_http_header( "content-type" => "text/html" );
 +
print '<html><head><meta http-equiv="refresh" content="0;url='.$url.'"/></head><body></body></html>';
 +
$session->terminate;
 +
 +
sub get_user
 +
{
 +
  my ($username, $given, $family, $email) = (undef, "", "", "");
 +
 +
  if( $ENV{eppn} )
 +
  {
 +
    ( $username ) = split( /@/, $ENV{eppn}, 2);
 +
    $username = lc( $username );
 +
  }
 +
  $email = $ENV{mail} if $ENV{mail};
 +
  if( $ENV{givenName} )
 +
  {
 +
    $given = lc( $ENV{givenName} );
 +
    $given =~ s/^(.)/uc($1)/e;
 +
    $given =~ s/([- ].)/uc($1)/e;
 +
  }
 +
  if( $ENV{sn} )
 +
  {
 +
    $family = lc( $ENV{sn} );
 +
    $family =~ s/^(.)/uc($1)/e;
 +
    $family =~ s/([- ].)/uc($1)/e;
 +
  }
 +
 +
  return unless EPrints::Utils::is_set( $username );
 +
 +
  my $user = $session->user_by_username( $username ); # relying on this to be case insensitive
 +
 +
  if( !defined $user )
 +
  {
 +
    my $usertype = 'user';
 +
    $user = EPrints::DataObj::User::create( $session, $usertype );
 +
    $user->set_value( "username", $username );
 +
  }
 +
  my $name = {
 +
    given => $given,
 +
    family => $family,
 +
  };
 +
  $user->set_value( "name", $name );
 +
  $user->set_value( "email", $email );
 +
  $user->commit;
 +
  return $user;
 +
}
  
 
== Further Information ==
 
== Further Information ==
Line 293: Line 514:
  
 
* For general information about installing and configuring Shibboleth [http://shibboleth.internet2.edu/ click here].
 
* For general information about installing and configuring Shibboleth [http://shibboleth.internet2.edu/ click here].
 +
 +
* [https://docs.openathens.net/display/public/TPA/Sign+in+to+a+generic+application+using+OpenAthens#SignintoagenericapplicationusingOpenAthens-SetupthecustomSAMLresourceinOpenAthens Instructions of connection a Shibboleth Service Provider with OpenAthens]
  
  

Latest revision as of 15:23, 25 October 2024

Manual Sections

This page details how to install and integrate Shibboleth with EPrints 3.3.x or 3.4.x on a CentOS 7 operating system. The process should be fairly similar for other comparable Red Hat based Linux distributions such as RHEL 7 and Fedora 21/22. These instructions shouls also be generally application to later versions of RHEL-based Linux (e.g. Rocky Linux 8, Red Hat Enterprise Linux 9, etc.). However, they ma be somewhat different for Debian-based Linux, such as Ubuntu and Debian itself and other Linux distributions. Typically, this will just be different package names and different commands to manage applications.

Generally, it is a good idea to run EPrints with HTTPS when using Shibboleth authentication for increased security on the attributes being sent back by the Shibboleth Identity Provider (IdP). Therefore, it is assumed that EPrints has already been set up to use HTTPS and there already exists an ssl/securevhost.conf under the archive directory structure.

Installing Shibboleth

 root> wget -O /etc/yum.repos.d/shibboleth.repo https://shibboleth.net/cgi-bin/sp_repo.cgi?platform=CentOS_7
  • Now you can use Yum to install all package dependencies:
 root> yum install log4shib opensaml shibboleth unixODBC xerces-c xml-security-c xmltooling 
  • You may be prompted to accept the importing of the key for the Shibboleth repository, for which you should type y and press enter.
  • Once you have done that, test that shibd has no issues:
 root> LD_LIBRARY_PATH=/opt/shibboleth/lib64 shibd -t
  • shibd -t should return a couple of warning, like those listed below. These are due to it not yet being configured.
 2015-05-11 10:39:01 WARN Shibboleth.Application : insecure cookieProps setting, set to "https" for SSL/TLS-only usage
 2015-05-11 10:39:01 WARN Shibboleth.Application : handlerSSL should be enabled for SSL/TLS-enabled web sites
 2015-05-11 10:39:01 WARN Shibboleth.Application : no MetadataProvider available, configure at least one for standard SSO usage
 overall configuration is loadable, check console for non-fatal problems
  • If there are no other warning or error messages from shibd -t, you can start it properly and check to make sure it is running. You may also want ensure Shibboleth starts at boot using systemctl enable
 root> systemctl start shibd.service
 root> ps aux | grep shib
 shibd    29338  0.4  0.7 419784 15024 ?        Ssl  11:16   0:00 /usr/sbin/shibd -p /var/run/shibboleth/shibd.pid -f -w 30
 root     29345  0.0  0.0 112640   940 pts/2    S+   11:17   0:00 grep --color=auto -i shib
 root> systemctl enable shibd.service

Configuring Shibboleth

  • Replace /etc/shibboleth/shibboleth2.xml with the following. Substitute foo.eprints.org for the hostname of your EPrints repository, https://shib.foo.example.org/idp/shibboleth with the entity ID for you Shibboleth IdP and foo in the pathname of files with the name or your repository (e.g. foo/attribute-map.xml becomes myrepo/attribute-map.xml). (This configuration is intended for Shibboleth SP version 2.x and is liable to cause deprecation warnings if you have installed a recent version of Shibboleth from a package repository. Here is a default shibboleth2.xml configuration for Shibboleth 3.x).
 <SPConfig xmlns="urn:mace:shibboleth:3.0:native:sp:config"
   xmlns:conf="urn:mace:shibboleth:3.0:native:sp:config"
   clockSkew="180">
 
   <ApplicationDefaults entityID="https://foo.eprints.org/shibboleth"
                        REMOTE_USER="eppn subject-id pairwise-id persistent-id"
                        cipherSuites="DEFAULT:!EXP:!LOW:!aNULL:!eNULL:!DES:!IDEA:!SEED:!RC4:!3DES:!kRSA:!SSLv2:!SSLv3:!TLSv1:!TLSv1.1">
 
     <Sessions lifetime="28800" timeout="3600" relayState="ss:mem" 
               checkAddress="false" handlerSSL="true" cookieProps="https"
               redirectLimit="exact">
       <SSO entityID="https://shib.foo.example.org/idp/shibboleth">SAML2</SSO>
       <Logout>SAML2 Local</Logout>
       <LogoutInitiator type="Admin" Location="/Logout/Admin" acl="127.0.0.1 ::1" />
       <Handler type="Status" Location="/Status" acl="127.0.0.1 ::1"/>
       <Handler type="Session" Location="/Session" showAttributeValues="false"/>
       <Handler type="DiscoveryFeed" Location="/DiscoFeed"/>
     </Sessions>
 
     <Errors supportContact="root@localhost" helpLocation="/about.html" styleSheet="/shibboleth/main.css"/>
     <MetadataProvider type="XML" path="foo/idp-metadata.xml"/>
     <AttributeExtractor type="XML" validate="true" reloadChanges="false" path="foo/attribute-map.xml"/>
     <AttributeFilter type="XML" validate="true" path="attribute-policy.xml"/>
     <CredentialResolver type="File" use="signing" key="foo/sp-key.pem" certificate="foo/sp-cert.pem"/>
     <CredentialResolver type="File" use="encryption" key="foo/sp-key.pem" certificate="foo/sp-cert.pem"/>

   </ApplicationDefaults>
 
   <SecurityPolicyProvider type="XML" validate="true" path="security-policy.xml"/>
   <ProtocolProvider type="XML" validate="true" reloadChanges="false" path="protocols.xml"/>
   
 </SPConfig>
  • Create the directory /etc/shibboleth/foo, substituting foo for your repository name.
 root> mkdir /etc/shibboleth/foo
  • Copy attribute-map.xml into this new directory.
 cp /etc/shibboleth/attribute-map.xml /etc/shibboleth/foo/
  • Temporarily rename sp-cert.pem and sp-key.pem in /etc/shibboleth/ to sp-cert.pem.old and sp-key.pem.old.
 root> cd /etc/shibboleth
 root> mv sp-cert.pem sp-cert.pem.old
 root> mv sp-key.pem sp-key.pem.old
  • Run keygen.sh from the /etc/shibboleth/ directory, as follows replacing foo.eprints.org with your EPrints repository hostname.
 root> cd /etc/shibboleth
 root> ./keygen.sh -f -h foo.eprints.org -e https://foo.eprints.org/shibboleth
  • Move the new sp-cert.pem and sp-key.pem to /etc/shibboleth/foo/ amd move the .old files back in place:
 root> cd /etc/shibboleth
 root> mv sp-cert.pem sp-key.pem foo/
 root> mv sp-cert.pem.old sp-cert.pem
 root> mv sp-key.pem.old sp-key.pem
  • Check that sp-cert.pem and sp-key.pem in /etc/shibboleth/foo/ still have the owner and group shibd.
 root> ls -l /etc/shibboleth/foo/sp-*
 -rw-r--r-- 1 shibd shibd 1192 May  6 19:04 /etc/shibboleth/foo/sp-cert.pem
 -rw------- 1 shibd shibd 1708 May  6 19:04 /etc/shibboleth/foo/sp-key.pem
  • Run metagen.sh from the /etc/shibboleth/ directory, as follows replacing foo.eprints.org with your EPrints repository hostname. You will ultimately need to send the output of this to the person managing the Shibboleth IdP server with which you want to register your EPrints repository as a service.
 root> cd /etc/shibboleth
 root> ./metagen.sh -ALO -c foo/sp-cert.pem -h foo.eprints.org -e https://foo.eprints.org/shibboleth > foo/sp_metadata.xml
  • Modify foo/sp_metadata.xml to add in the namepace definitions by separately changing the md:EntityDescriptor and ds:KeyInfo lines as follows from:
<md:EntityDescriptor entityID="https://for.eprints.org/shibboleth">
  ...
    ...
      <ds:KeyInfo>

to:

<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" entityID="https://for.eprints.org/shibboleth"> 
  ...
    ...
      <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
  • Use wget to download the metadata from your Shibboleth IdP (e.g. shib.foo.example.org) to the /etc/shibboleth/foo/ directory.
 root> wget -O /etc/shibboleth/foo/idp-metadata.xml https://shib.foo.example.org/idp/shibboleth

Using Remote IdP Metatdata

As IdP Metadata may expire, you may want to use the remote metadata URL rather than a local copy. This is what you need to do to switch to using remote IdP metadata:

  • Edit the MetadataProvider line to something like the following. The reloadInterval is best set to 7200 but this can be set less frequently:
<MetadataProvider type="XML" url="https://shib.foo.example.org/idp/metadata.xml" backingFilePath="foo/idp-metadata.xml" reloadInterval="7200"/>
  • Make sure the reloadInterval is appropriate for the IdP metadata you are downloading. If you have large metadata file,(e.g. from a federated metadata service containing metadata for other IdPs), then it may be better to set this higher than the default. However, the configuration checker may warn you that your reload interval is too long, so you may have to choose to ignore this, if you want to avoid downloading a large federated metadata file too often.
  • Make sure that the directory /etc/shibboleth/foo/ and /etc/shibboleth/foo/idp-metadata.xml if it already exists) are owned by shibd:
chown shibd:shibd /etc/shibboleth/foo/
chown shibd:shibd /etc/shibboleth/foo/idp-metadata.xml
  • To test Shibboleth you will need to make sure your LD_LIBRARY_PATH is set the same as shibd would have when started using systemctl shibd start. This should return the message: overall configuration is loadable, check console for non-fatal problems.
LD_LIBRARY_PATH=/opt/shibboleth/lib64:$LD_LIBRARY_PATH shibd -t
  • The above message is because it is now advised not to leave the MetadataGenerator enabled unnecessarily. However, whilst you are setting up Shibboleth, it is useful to have the metadata generator, save you needing to build your own Service Provider metadata file to register with your Identity Provider. Later on you can comment out the MetadataGenerator line to stop getting this warning message.
  • Now you can restart shibd properly:
systemctl restart shibd

Configuring Apache and EPrints

N.B. All these actions should be carried out by the eprints user, except when prepended with root> which means the command should be run as the root user.

  • Add the following configuration to your archive's ssl/securevhost.conf, after the Include /opt/eprints3/cfg/apache_ssl/foo.conf, substituting foo for your archive's name where appropriate. (This assumes you are running Apache 2.4 or greater). See Troubleshooting for instructions on the configuration to use for Apache 2.2. or lower.
Alias /shibboleth /opt/eprints3/archives/foo/shibboleth
<Location "/shibboleth">
  SetHandler perl-script
  PerlHandler ModPerl::Registry
  PerlSendHeader Off
  Options ExecCGI FollowSymLinks

  AuthType shibboleth
  ShibRequestSetting requireSession 1
  require shib-session
</Location>

<Location /cgi/shibboleth>
  AuthType shibboleth
  ShibRequestSetting requireSession 1
  require shib-session
</Location>
  • Copy the following code into your archive (e.g. /opt/eprints3/archives/foo/) as cgi/shibboleth.
use EPrints;
use strict;
my $session = new EPrints::Session;
exit( 0 ) unless( defined $session );

$session->send_http_header( "content_type" => "text/html" );

print "<html><head/><body><code>\n";

foreach my $key (sort keys(%ENV)) {
  print "<p>$key = $ENV{$key}</p>";
}
 
print "</code></body></html>";
$session->terminate;
exit;
  • Now restart Shibboleth and Apache:
 root> systemctl restart shibd.service
 root> apachectl restart
opensaml::saml2md::MetadataException
The system encountered an error at Wed May 6 15:19:27 2015
To report this problem, please contact the site administrator at root@localhost.
Please include the following message in any email:
opensaml::saml2md::MetadataException at (http://foo.eprints.org/cgi/shibboleth)
Unable to locate metadata for identity provider (https://shib.foo.example.org/idp/shibboleth)
  • Next, copy the following code into your archive (e.g. /opt/eprints3/archives/foo/) as cfg/cfg.d/zz_shibboleth.pl. This is needed to redirect login and logout to use Shibboleth rather than local login.
$c->{get_login_url} = sub {
  my( $session, $target ) = @_;

  # preserve CGI params
  $session->read_params;
  $target = $session->get_url(
    host => 1,
    path => "auto",
    query => 1,
  );

  my $url = URI->new( $session->config( "https_url" )  . "/shibboleth/login" );
  $url->query_form( target => "$target" );
  return "$url";
};

$c->{on_logout} = sub {
  my( $session ) = @_;
  my $query = $session->query;
  return unless defined $query;

  # remove _shibsession_ cookie
  my( $shibname, $shibvalue );
  for( $query->cookie() ) {
    if( $_ =~ /^_shibsession/ ) {
      $shibname = $_;
      $shibvalue = $query->cookie( $shibname );
    }
  }

  my $cookie = $query->cookie(
    -name    => $shibname,
    -path    => "/",
    -value   => "",
    -host  => $session->config("cookie_domain"),
    -expires => "-1d",
  );
  EPrints::Apache::AnApache::header_out(
    $session->{request},
    "Set-Cookie" => $cookie 
  );
};

push @{$c->{rewrite_exceptions}}, "/shibboleth/";
  • Create a folder at the top level of your archive (e.g. /opt/eprints3/archives/foo/) called shibboleth and copy the main CSS file for Shibboleth into this folder:
eprints> mkdir /opt/eprints3/archives/foo/shibboleth/
eprints> cp /usr/share/shibboleth/main.css /opt/eprints3/archives/foo/shibboleth/
  • Now, copy the following code into your archive (e.g. /opt/eprints3/archives/foo/) as shibboleth/login. This is the most basic login script that should work with the minimal attributes any Shibboleth IdP returns and only logging in users with existing accounts. Look under the Customisation section for advice on how to modify this script to meet your requirements, such as creation user accounts on-the-fly.
use EPrints;
use strict;

my $session = EPrints::Session->new();
my $url = $session->param( "target" );
if ( defined $url )
{
  my $target_uri = URI->new( $url );
  my $repository_uri = URI->new( $session->get_repository->get_conf( 'base_url' ) );
  if ( !$target_uri->can( 'host' ) || $target_uri->host ne $repository_uri->host )
  {
    $url = undef;
  }
}
$url = $session->get_repository->get_conf( "userhome" ) unless EPrints::Utils::is_set( $url );

my $user = &get_user;

if( defined $user )
{
  $user->set_value( "last_login", EPrints::Time::get_iso_timestamp() );
  $user->commit;

  EPrints::DataObj::LoginTicket->expire_all( $session );
  $session->dataset( "loginticket" )->create_dataobj({
    userid => $user->id,
  })->set_cookies();
}
else
{
  $url = $session->get_repository->get_conf( "base_url" ) . "/account_required.html";
}

$session->send_http_header( "content-type" => "text/html" );
print '<html><head><meta http-equiv="refresh" content="0;url='.$url.'"/></head><body></body></html>';
$session->terminate;
  
sub get_user 
{
  my ( $username, $email ) = ( undef, "" );
  if( $ENV{eppn} )
  {
   ( $username ) = split( /@/, $ENV{eppn}, 2);
   $username = lc( $username );
   $email = $ENV{eppn};
  }
  return unless EPrints::Utils::is_set( $username );
  my $user = $session->user_by_username( $username );
  if( defined $user && defined $email )
  {
    $user->set_value( "email", $email );
    $user->commit;
  }
  return $user;
}
  • Next, add the following markup to cfg/lang/en/static/account_required.xpage under your archive (e.g. /opt/eprints3/archives/foo/). Substituting staff and students of the University of Foo to describe to which particular group of people logged in access is restricted.
<?xml version="1.0" standalone="no" ?>
<!DOCTYPE page SYSTEM "entities.dtd" >
<xpage:page xmlns="http://www.w3.org/1999/xhtml" xmlns:xpage="http://eprints.org/ep3/xpage" xmlns:epc="http://eprints.org/ep3/control">
<xpage:title>Login Failed</xpage:title>
<xpage:body>
   <p style='text-align: center;'>Please note that only staff and students of the University of Foo may log in to <epc:phrase ref="archive_name" /></p>
</xpage:body>
</xpage:page>
  • Now, reload Apache.
 root> apachectl reload
  • In a web browser go to the /shibboleth/login page for your repository, (e.g. https://foo.eprints.org/shibboleth/login). Like before with /cgi/shibboleth you should be taken to your Shibboleth IdP's site albeit displaying an error message.
  • The Shibboleth IdP shows an error message because EPrints as a Shibboleth Service Provider is not yet registered with it. To do this you need to send the administrator of the Shibboleth IdP the metadata for your Service Provider. You will have generated this earlier when you ran metagen.sh. Copy off your EPrints server the file that this wrote (e.g. to /etc/shibboleth/foo/sp_metadata.xml) and send it to the Shibboleth IdP administrator. They should be able to upload this to register EPrints as a Service Provider application.
  • Once registered, use a web browser to go to /shibboleth/login page for your repository, (e.g. https://foo.eprints.org/shibboleth/login) again. This time you should be prompted for a username and password on the Shibboleth IdP site. Once you have typed this in and clicked to login, you should be returned to EPrints on the /cgi/users/home page for your repository. If not, see Login Issues under Troubleshooting below.

Troubleshooting

Apache Configuration Issues

Apache 2.2 (and lower) Configuration for EPrints Shibboleth Integration

  • Similarly to the instructions for Apache 2.4 and above, place the slightly different following configuration after the Include line for apache_ssl/foo.conf, (substituting foo for your archive's name):
 Alias /shibboleth /opt/eprints3/archives/foo/shibboleth
 <Directory "/opt/eprints3/archives/foo/shibboleth">
   SetHandler perl-script
   PerlHandler ModPerl::Registry
   PerlSendHeader Off
   Options ExecCGI FollowSymLinks
 
   AuthType shibboleth
   ShibRequireSession On
   require valid-user
 </Directory>
 
 <Location /cgi/shibboleth>
   AuthType shibboleth
   ShibRequireSession On
   require valid-user
 </Location>

Shibboleth Configuration Issues

With attribute-map.xml

When authenticating using Shibboleth to login to EPrints you may see the following line in /var/log/shibboleth.shibd.log

 2015-09-09 09:26:43 INFO Shibboleth.AttributeExtractor.XML [2]: skipping unmapped SAML 2.0 Attribute with Name:  urn:mace:dir:attribute-def:department

In some cases this might not be an issue, as EPrints does not necessarily to make use of all the attributes returned by the IdP but without a mapping in attribute-map.xml. In this case urn:mace:dir:attribute-def:department is not by default needed by EPrints to create/update a user account. However, values like sn, givenName and mail are but if you have used the attribute-map.xml provided later on the page you should not see a line like above in shibd.log. In some cases you may still see an line like this in the log even if you think you have defined the attribute. The line below demonstrate two known issues:

 2015-09-09 09:26:43 INFO Shibboleth.AttributeExtractor.XML [2]: skipping unmapped SAML 2.0 Attribute with Name:  givenName, Format:urn:oasis:names:tc:SAML:2.0:attrname-format:basic
  1. The attribute itself has no namespace it is just 'givenName rather than urn:mace:dir:attribute-def:givenName
  2. The attribute has a format that most also be defined in the attribute-map.xml attribute.

Below shows how to both include the format, which is required for the attribute to be successful mapped. As well as define the name of the attribute without a namespace:

 <Attribute name="givenName" nameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" id="givenName"/>

A typical Shibboleth IdP would have both a namespaced attribute name and would not apply a format to an attribute that is a simple text string. Therefore it is worth enquiring with the IdP provider if either of these two happen to be the case.

With idp-metadata.xml

If you want to be able extract scoped attributes (e.g. eduPersonPrincipalName otherwise described as eppn). Then you will need to ensure that the expected scope of this attribute, (effectively the domain part in eduPersonPrincipalName is the scope or one of the scopes in the idp-metadata.xml you downloaded from the Shibboleth IdP. For example if the eduPersonPrincipalName is bar@foo.ac.uk. Then the following should be defined in idp-metadata.xml (N.B. the namespace abbreviations (md: and shibmd:) may be different for the IdP you are working with):

 <md:Extensions>
   <shibmd:Scope regexp="false">foo.ac.uk</shibmd:Scope>
 </md:Extensions>

Login Issues

To be added.


Customisation

Shibboleth /etc/shibboleth/foo/attribute-map.xml config

This is adapted from the default attribute-map.xml provided when the Shibboleth SP package is installed to only include the attribute subsequently used by EPrints Shibboleth Perl script that can be found below. Namely:

Field name Field description SAML v1.1 attribute URN SAMLv2 attribute URN
eppn Edu Person Principal Name urn:mace:dir:attribute-def:eduPersonPrincipalName urn:oid:1.3.6.1.4.1.5923.1.1.1.6
sn Surname urn:mace:dir:attribute-def:sn urn:oid:2.5.4.4
givenName Given (first) name(s) urn:mace:dir:attribute-def:givenName urn:oid:2.5.4.42
mail Email address urn:mace:dir:attribute-def:mail urn:oid:0.9.2342.19200300.100.1.3


You may wish to refer to the default attribute-map.xml is you want to use other attributes.

 <Attributes xmlns="urn:mace:shibboleth:2.0:attribute-map" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 
   <Attribute name="urn:mace:dir:attribute-def:eduPersonPrincipalName" id="eppn">
     <AttributeDecoder xsi:type="ScopedAttributeDecoder"/>
   </Attribute>
   <Attribute name="urn:oid:1.3.6.1.4.1.5923.1.1.1.6" id="eppn">
     <AttributeDecoder xsi:type="ScopedAttributeDecoder"/>
   </Attribute>
 
   <Attribute name="urn:mace:dir:attribute-def:sn" id="sn"/>
   <Attribute name="urn:mace:dir:attribute-def:givenName" id="givenName"/>
   <Attribute name="urn:mace:dir:attribute-def:mail" id="mail"/>
 
   <Attribute name="urn:oid:2.5.4.4" nameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" id="sn"/>
   <Attribute name="urn:oid:2.5.4.42" nameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" id="givenName"/>
   <Attribute name="urn:oid:0.9.2342.19200300.100.1.3" nameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" id="mail"/>
 
 </Attributes>

EPrints /shibboleth/login script with user account creation

This is the standard EPrints Shibboleth login script. It makes a number of assumptions:

  1. That accounts should be created if they do not already exist for a particular user.
  2. That you wish to create a standard user account (not an editor administrator) account.
  3. That the Shibboleth IdP provides all the attributes (eduPersonPrinicpalName (seen as eppn), sn, givenName and mail) required.
use EPrints;
use strict;

my $session = EPrints::Session->new();
my $url = $session->param( "target" );
if ( defined $url )
{
  my $target_uri = URI->new( $url );
  my $repository_uri = URI->new( $session->get_repository->get_conf( 'base_url' ) );
  if ( !$target_uri->can( 'host' ) || $target_uri->host ne $repository_uri->host )
  {
    $url = undef;
  }
}
$url = $session->get_repository->get_conf( "userhome" ) unless EPrints::Utils::is_set( $url );

my $user = &get_user;

if( defined $user )
{
  $user->set_value( "last_login", EPrints::Time::get_iso_timestamp() );
  $user->commit;

  EPrints::DataObj::LoginTicket->expire_all( $session );
  $session->dataset( "loginticket" )->create_dataobj({
    userid => $user->id,
  })->set_cookies();
}
else
{
  $url = $session->get_repository->get_conf( "base_url" ) . "/account_required.html";
}

$session->send_http_header( "content-type" => "text/html" );
print '<html><head><meta http-equiv="refresh" content="0;url='.$url.'"/></head><body></body></html>';
$session->terminate;

sub get_user
{
  my ($username, $given, $family, $email) = (undef, "", "", "");

  if( $ENV{eppn} )
  {
    ( $username ) = split( /@/, $ENV{eppn}, 2);
    $username = lc( $username );
  }
  $email = $ENV{mail} if $ENV{mail};
  if( $ENV{givenName} )
  {
    $given = lc( $ENV{givenName} );
    $given =~ s/^(.)/uc($1)/e;
    $given =~ s/([- ].)/uc($1)/e;
  }
  if( $ENV{sn} )
  {
    $family = lc( $ENV{sn} );
    $family =~ s/^(.)/uc($1)/e;
    $family =~ s/([- ].)/uc($1)/e;
  }

  return unless EPrints::Utils::is_set( $username );

  my $user = $session->user_by_username( $username ); # relying on this to be case insensitive

  if( !defined $user )
  {
    my $usertype = 'user';
    $user = EPrints::DataObj::User::create( $session, $usertype );
    $user->set_value( "username", $username );
  }
  my $name = {
    given => $given,
    family => $family,
  };
  $user->set_value( "name", $name );
  $user->set_value( "email", $email );
  $user->commit;
  return $user;
}

Further Information

  • Older instructions of how to set up EPrints for Shibboleth using UK Access management Federation discovery service is available here.
  • For general information about installing and configuring Shibboleth click here.