mirror of
https://github.com/DSpace/DSpace.git
synced 2025-10-16 22:43:12 +00:00
[DS-48] shibboleth+dspace1.5.1 patch - ID: 2412723
git-svn-id: http://scm.dspace.org/svn/repo/branches/dspace-1_5_x@3642 9c30dcfa-912a-0410-8fc2-9e0234be79fd
This commit is contained in:
@@ -0,0 +1,423 @@
|
||||
/*
|
||||
* ShibAuthentication.java
|
||||
*
|
||||
* Version: $Revision$
|
||||
*
|
||||
* Copyright (c) 2009, The DSpace Foundation. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of the Hewlett-Packard Company nor the name of the
|
||||
* Massachusetts Institute of Technology nor the names of their
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*/
|
||||
|
||||
package org.dspace.authenticate;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Collection;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.core.ConfigurationManager;
|
||||
import org.dspace.core.LogManager;
|
||||
import org.dspace.authenticate.AuthenticationManager;
|
||||
import org.dspace.authenticate.AuthenticationMethod;
|
||||
import org.dspace.eperson.EPerson;
|
||||
import org.dspace.eperson.Group;
|
||||
|
||||
/**
|
||||
* Shibboleth authentication for DSpace, tested on Shibboleth 1.3.x and
|
||||
* Shibboleth 2.x. Read <a href=
|
||||
* "https://mams.melcoe.mq.edu.au/zope/mams/pubs/Installation/dspace15/view"
|
||||
* >Shib DSpace 1.5</a> for installation procedure. Read dspace.cfg for details
|
||||
* on options available.
|
||||
*
|
||||
* @author <a href="mailto:bliong@melcoe.mq.edu.au">Bruc Liong, MELCOE</a>
|
||||
* @author <a href="mailto:kli@melcoe.mq.edu.au">Xiang Kevin Li, MELCOE</a>
|
||||
* @version $Revision$
|
||||
*/
|
||||
public class ShibAuthentication implements AuthenticationMethod
|
||||
{
|
||||
/** log4j category */
|
||||
private static Logger log = Logger.getLogger(ShibAuthentication.class);
|
||||
|
||||
public int authenticate(Context context, String username, String password,
|
||||
String realm, HttpServletRequest request) throws SQLException
|
||||
{
|
||||
log.info("Shibboleth login started...");
|
||||
|
||||
java.util.Enumeration names = request.getHeaderNames();
|
||||
String name;
|
||||
while (names.hasMoreElements())
|
||||
log.debug("header:" + (name = names.nextElement().toString()) + "="
|
||||
+ request.getHeader(name));
|
||||
|
||||
boolean isUsingTomcatUser = ConfigurationManager
|
||||
.getBooleanProperty("authentication.shib.email-use-tomcat-remote-user");
|
||||
String emailHeader = ConfigurationManager
|
||||
.getProperty("authentication.shib.email-header");
|
||||
String fnameHeader = ConfigurationManager
|
||||
.getProperty("authentication.shib.firstname-header");
|
||||
String lnameHeader = ConfigurationManager
|
||||
.getProperty("authentication.shib.lastname-header");
|
||||
|
||||
String email = null;
|
||||
String fname = null;
|
||||
String lname = null;
|
||||
|
||||
if (emailHeader != null)
|
||||
{
|
||||
// try to grab email from the header
|
||||
email = request.getHeader(emailHeader);
|
||||
|
||||
// fail, try lower case
|
||||
if (email == null)
|
||||
email = request.getHeader(emailHeader.toLowerCase());
|
||||
}
|
||||
|
||||
// try to pull the "REMOTE_USER" info instead of the header
|
||||
if (email == null && isUsingTomcatUser)
|
||||
{
|
||||
email = request.getRemoteUser();
|
||||
log.info("RemoteUser identified as: " + email);
|
||||
}
|
||||
|
||||
// No email address, perhaps the eperson has been setup, better check it
|
||||
if (email == null)
|
||||
{
|
||||
EPerson p = context.getCurrentUser();
|
||||
if (p != null)
|
||||
email = p.getEmail();
|
||||
}
|
||||
|
||||
if (email == null)
|
||||
{
|
||||
log
|
||||
.error("No email is given, you're denied access by Shib, please release email address");
|
||||
return AuthenticationMethod.BAD_ARGS;
|
||||
}
|
||||
|
||||
email = email.toLowerCase();
|
||||
|
||||
if (fnameHeader != null)
|
||||
{
|
||||
// try to grab name from the header
|
||||
fname = request.getHeader(fnameHeader);
|
||||
|
||||
// fail, try lower case
|
||||
if (fname == null)
|
||||
fname = request.getHeader(fnameHeader.toLowerCase());
|
||||
}
|
||||
if (lnameHeader != null)
|
||||
{
|
||||
// try to grab name from the header
|
||||
lname = request.getHeader(lnameHeader);
|
||||
|
||||
// fail, try lower case
|
||||
if (lname == null)
|
||||
lname = request.getHeader(lnameHeader.toLowerCase());
|
||||
}
|
||||
|
||||
// future version can offer auto-update feature, this needs testing
|
||||
// before inclusion to core code
|
||||
|
||||
EPerson eperson = null;
|
||||
try
|
||||
{
|
||||
eperson = EPerson.findByEmail(context, email);
|
||||
context.setCurrentUser(eperson);
|
||||
}
|
||||
catch (AuthorizeException e)
|
||||
{
|
||||
log.warn("Fail to locate user with email:" + email, e);
|
||||
eperson = null;
|
||||
}
|
||||
|
||||
// auto create user if needed
|
||||
if (eperson == null
|
||||
&& ConfigurationManager
|
||||
.getBooleanProperty("authentication.shib.autoregister"))
|
||||
{
|
||||
log.info(LogManager.getHeader(context, "autoregister", "email="
|
||||
+ email));
|
||||
|
||||
// TEMPORARILY turn off authorisation
|
||||
context.setIgnoreAuthorization(true);
|
||||
try
|
||||
{
|
||||
eperson = EPerson.create(context);
|
||||
eperson.setEmail(email);
|
||||
if (fname != null)
|
||||
eperson.setFirstName(fname);
|
||||
if (lname != null)
|
||||
eperson.setLastName(lname);
|
||||
eperson.setCanLogIn(true);
|
||||
AuthenticationManager.initEPerson(context, request, eperson);
|
||||
eperson.update();
|
||||
context.commit();
|
||||
context.setCurrentUser(eperson);
|
||||
}
|
||||
catch (AuthorizeException e)
|
||||
{
|
||||
log.warn("Fail to authorize user with email:" + email, e);
|
||||
eperson = null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
context.setIgnoreAuthorization(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (eperson == null)
|
||||
{
|
||||
return AuthenticationMethod.NO_SUCH_USER;
|
||||
}
|
||||
else
|
||||
{
|
||||
// the person exists, just return ok
|
||||
context.setCurrentUser(eperson);
|
||||
}
|
||||
|
||||
return AuthenticationMethod.SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Grab the special groups to be automatically provisioned for the current
|
||||
* user. Currently the mapping for the groups is done one-to-one, future
|
||||
* version can consider the usage of regex for such mapping.
|
||||
*/
|
||||
public int[] getSpecialGroups(Context context, HttpServletRequest request)
|
||||
{
|
||||
|
||||
if (request.getSession().getAttribute("shib.specialgroup") != null)
|
||||
{
|
||||
return (int[]) request.getSession().getAttribute(
|
||||
"shib.specialgroup");
|
||||
}
|
||||
|
||||
java.util.Set groups = new java.util.HashSet();
|
||||
String roleHeader = ConfigurationManager
|
||||
.getProperty("authentication.shib.role-header");
|
||||
if (roleHeader == null || roleHeader.trim().length() == 0)
|
||||
roleHeader = "Shib-EP-UnscopedAffiliation"; // fall back to default
|
||||
String affiliations = request.getHeader(roleHeader);
|
||||
|
||||
// try again with all lower case...maybe has better luck
|
||||
if (affiliations == null)
|
||||
affiliations = request.getHeader(roleHeader.toLowerCase());
|
||||
|
||||
// default role when fully authN but not releasing any roles?
|
||||
String defaultRoles = ConfigurationManager
|
||||
.getProperty("authentication.shib.default-roles");
|
||||
if (affiliations == null && defaultRoles != null)
|
||||
{
|
||||
affiliations = defaultRoles;
|
||||
}
|
||||
|
||||
if (affiliations != null)
|
||||
{
|
||||
java.util.StringTokenizer st = new java.util.StringTokenizer(
|
||||
affiliations, ";,");
|
||||
// do the mapping here
|
||||
while (st.hasMoreTokens())
|
||||
{
|
||||
String affiliation = st.nextToken().trim();
|
||||
|
||||
// perform mapping here if necessary
|
||||
String groupLabels = ConfigurationManager
|
||||
.getProperty("authentication.shib.role." + affiliation);
|
||||
if (groupLabels == null || groupLabels.trim().length() == 0)
|
||||
groupLabels = ConfigurationManager
|
||||
.getProperty("authentication.shib.role."
|
||||
+ affiliation.toLowerCase());
|
||||
|
||||
// revert back to original entry when no mapping is provided
|
||||
if (groupLabels == null)
|
||||
groupLabels = affiliation;
|
||||
|
||||
String[] labels = groupLabels.split(",");
|
||||
for (int i = 0; i < labels.length; i++)
|
||||
addGroup(groups, context, labels[i].trim());
|
||||
}
|
||||
}
|
||||
|
||||
int ids[] = new int[groups.size()];
|
||||
java.util.Iterator it = groups.iterator();
|
||||
for (int i = 0; it.hasNext(); i++)
|
||||
ids[i] = ((Integer) it.next()).intValue();
|
||||
|
||||
// store the special group, if already transformed from headers
|
||||
// since subsequent header may not have the values anymore
|
||||
if (ids.length != 0)
|
||||
{
|
||||
request.getSession().setAttribute("shib.specialgroup", ids);
|
||||
}
|
||||
|
||||
return ids;
|
||||
}
|
||||
|
||||
/** Find dspaceGroup in DSpace database, if found, include it into groups */
|
||||
private void addGroup(Collection groups, Context context, String dspaceGroup)
|
||||
{
|
||||
try
|
||||
{
|
||||
Group g = Group.findByName(context, dspaceGroup);
|
||||
if (g == null)
|
||||
{
|
||||
// oops - no group defined
|
||||
log.warn(LogManager.getHeader(context, dspaceGroup
|
||||
+ " group is not found!! Admin needs to create one!",
|
||||
"requiredGroup=" + dspaceGroup));
|
||||
groups.add(new Integer(0));
|
||||
}
|
||||
else
|
||||
{
|
||||
groups.add(new Integer(g.getID()));
|
||||
}
|
||||
log.info("Mapping group: " + dspaceGroup + " to groupID: "
|
||||
+ (g == null ? 0 : g.getID()));
|
||||
}
|
||||
catch (SQLException e)
|
||||
{
|
||||
log.error("Mapping group:" + dspaceGroup + " failed with error", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate whether or not a particular self-registering user can set
|
||||
* themselves a password in the profile info form.
|
||||
*
|
||||
* @param context
|
||||
* DSpace context
|
||||
* @param request
|
||||
* HTTP request, in case anything in that is used to decide
|
||||
* @param email
|
||||
* e-mail address of user attempting to register
|
||||
*
|
||||
*/
|
||||
public boolean allowSetPassword(Context context,
|
||||
HttpServletRequest request, String email) throws SQLException
|
||||
{
|
||||
// don't use password at all
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Predicate, is this an implicit authentication method. An implicit method
|
||||
* gets credentials from the environment (such as an HTTP request or even
|
||||
* Java system properties) rather than the explicit username and password.
|
||||
* For example, a method that reads the X.509 certificates in an HTTPS
|
||||
* request is implicit.
|
||||
*
|
||||
* @return true if this method uses implicit authentication.
|
||||
*/
|
||||
public boolean isImplicit()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate whether or not a particular user can self-register, based on
|
||||
* e-mail address.
|
||||
*
|
||||
* @param context
|
||||
* DSpace context
|
||||
* @param request
|
||||
* HTTP request, in case anything in that is used to decide
|
||||
* @param email
|
||||
* e-mail address of user attempting to register
|
||||
*
|
||||
*/
|
||||
public boolean canSelfRegister(Context context, HttpServletRequest request,
|
||||
String username) throws SQLException
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise a new e-person record for a self-registered new user.
|
||||
*
|
||||
* @param context
|
||||
* DSpace context
|
||||
* @param request
|
||||
* HTTP request, in case it's needed
|
||||
* @param eperson
|
||||
* newly created EPerson record - email + information from the
|
||||
* registration form will have been filled out.
|
||||
*
|
||||
*/
|
||||
public void initEPerson(Context context, HttpServletRequest request,
|
||||
EPerson eperson) throws SQLException
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get login page to which to redirect. Returns URL (as string) to which to
|
||||
* redirect to obtain credentials (either password prompt or e.g. HTTPS port
|
||||
* for client cert.); null means no redirect.
|
||||
*
|
||||
* @param context
|
||||
* DSpace context, will be modified (ePerson set) upon success.
|
||||
*
|
||||
* @param request
|
||||
* The HTTP request that started this operation, or null if not
|
||||
* applicable.
|
||||
*
|
||||
* @param response
|
||||
* The HTTP response from the servlet method.
|
||||
*
|
||||
* @return fully-qualified URL or null
|
||||
*/
|
||||
public String loginPageURL(Context context, HttpServletRequest request,
|
||||
HttpServletResponse response)
|
||||
{
|
||||
return response.encodeRedirectURL(request.getContextPath()
|
||||
+ "/shibboleth-login");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get title of login page to which to redirect. Returns a <i>message
|
||||
* key</i> that gets translated into the title or label for "login page" (or
|
||||
* null, if not implemented) This title may be used to identify the link to
|
||||
* the login page in a selection menu, when there are multiple ways to
|
||||
* login.
|
||||
*
|
||||
* @param context
|
||||
* DSpace context, will be modified (ePerson set) upon success.
|
||||
*
|
||||
* @return title text.
|
||||
*/
|
||||
public String loginPageTitle(Context context)
|
||||
{
|
||||
return "org.dspace.authenticate.ShibAuthentication.title";
|
||||
}
|
||||
}
|
@@ -1354,6 +1354,7 @@ org.dspace.content.Community.untitled
|
||||
org.dspace.eperson.LDAPAuthentication.title = Enter LDAP Netid and Password
|
||||
org.dspace.eperson.PasswordAuthentication.title = Enter DSpace Username and Password
|
||||
org.dspace.eperson.X509Authentication.title = Enter DSpace using Web Certificate
|
||||
org.dspace.authenticate.ShibAuthentication.title = Login via Shibboleth
|
||||
org.dspace.eperson.Subscribe.authors = Authors:
|
||||
org.dspace.eperson.Subscribe.id = ID:
|
||||
org.dspace.eperson.Subscribe.new-items = New Items:
|
||||
|
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* ShibbolethFilter.java
|
||||
*
|
||||
* Version: $Revision$
|
||||
*
|
||||
* Copyright (c) 2009, The DSpace Foundation. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of the Hewlett-Packard Company nor the name of the
|
||||
* Massachusetts Institute of Technology nor the names of their
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*/
|
||||
|
||||
package org.dspace.app.webui.filter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import org.dspace.app.webui.util.Authenticate;
|
||||
import org.dspace.app.webui.util.JSPManager;
|
||||
import org.dspace.app.webui.util.UIUtil;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.core.LogManager;
|
||||
|
||||
/**
|
||||
* DSpace filter that only allows requests from authenticated shib users
|
||||
* to proceed. Anonymous requests prompt the authentication procedure.
|
||||
*
|
||||
* @author <a href="mailto:bliong@melcoe.mq.edu.au">Bruc Liong, MELCOE</a>
|
||||
* @author <a href="mailto:kli@melcoe.mq.edu.au">Xiang Kevin Li, MELCOE</a>
|
||||
* @version $Revision$
|
||||
*/
|
||||
public class ShibbolethFilter implements Filter
|
||||
{
|
||||
/** log4j category */
|
||||
private static Logger log = Logger.getLogger(ShibbolethFilter.class);
|
||||
|
||||
|
||||
public void init(FilterConfig config)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
|
||||
public void doFilter(ServletRequest request,
|
||||
ServletResponse response,
|
||||
FilterChain chain)
|
||||
throws ServletException, IOException
|
||||
{
|
||||
Context context = null;
|
||||
|
||||
// We need HTTP request objects
|
||||
HttpServletRequest hrequest = (HttpServletRequest) request;
|
||||
HttpServletResponse hresponse = (HttpServletResponse) response;
|
||||
|
||||
try
|
||||
{
|
||||
// Obtain a context
|
||||
context = UIUtil.obtainContext(hrequest);
|
||||
|
||||
if (context.getCurrentUser() == null)
|
||||
{
|
||||
java.util.Enumeration names = ((HttpServletRequest) request).getHeaderNames();
|
||||
String name;
|
||||
while(names.hasMoreElements()) log.debug("header:"+(name = names.nextElement().toString())+"="+((HttpServletRequest)request).getHeader(name));
|
||||
|
||||
// No current user, prompt authentication
|
||||
Authenticate.startAuthentication(context, hrequest, hresponse);
|
||||
}else{
|
||||
chain.doFilter(hrequest, hresponse);
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (SQLException se)
|
||||
{
|
||||
log.warn(LogManager.getHeader(context,
|
||||
"database_error",
|
||||
se.toString()), se);
|
||||
JSPManager.showInternalError(hrequest, hresponse);
|
||||
}
|
||||
|
||||
// Abort the context if it's still valid
|
||||
if (context != null && context.isValid())
|
||||
{
|
||||
context.abort();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void destroy()
|
||||
{
|
||||
// Nothing
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* ShibbolethServlet.java
|
||||
*
|
||||
* Version: $Revision$
|
||||
*
|
||||
* Copyright (c) 2009, The DSpace Foundation. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of the Hewlett-Packard Company nor the name of the
|
||||
* Massachusetts Institute of Technology nor the names of their
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*/
|
||||
|
||||
package org.dspace.app.webui.servlet;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import org.dspace.app.webui.util.Authenticate;
|
||||
import org.dspace.app.webui.util.JSPManager;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.core.LogManager;
|
||||
import org.dspace.eperson.EPerson;
|
||||
import org.dspace.app.webui.servlet.DSpaceServlet;
|
||||
import org.dspace.authenticate.AuthenticationManager;
|
||||
import org.dspace.authenticate.AuthenticationMethod;
|
||||
import org.dspace.eperson.Group;
|
||||
|
||||
/**
|
||||
* Shibbolize dspace. Follow instruction at
|
||||
* http://mams.melcoe.mq.edu.au/zope/mams/pubs/Installation/dspace15
|
||||
*
|
||||
* Pull information from the header as released by Shibboleth target.
|
||||
* The header required are:
|
||||
* <ol><li>user email</li>
|
||||
* <li>first name (optional)</li>
|
||||
* <li>last name (optional)</li>
|
||||
* <li>user roles</li>
|
||||
* </ol>.
|
||||
*
|
||||
* All these info are configurable from the configuration file (dspace.cfg).
|
||||
*
|
||||
* @author <a href="mailto:bliong@melcoe.mq.edu.au">Bruc Liong, MELCOE</a>
|
||||
* @author <a href="mailto:kli@melcoe.mq.edu.au">Xiang Kevin Li, MELCOE</a>
|
||||
* @version $Revision$
|
||||
*/
|
||||
public class ShibbolethServlet extends DSpaceServlet {
|
||||
/** log4j logger */
|
||||
private static Logger log = Logger.getLogger(ShibbolethServlet.class);
|
||||
|
||||
protected void doDSGet(Context context,
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response)
|
||||
throws ServletException, IOException, SQLException, AuthorizeException {
|
||||
//debugging, show all headers
|
||||
java.util.Enumeration names = request.getHeaderNames();
|
||||
String name;
|
||||
while(names.hasMoreElements()) log.info("header:"+(name=names.nextElement().toString())+"="+request.getHeader(name));
|
||||
|
||||
String jsp = null;
|
||||
|
||||
// Locate the eperson
|
||||
int status = AuthenticationManager.authenticate(context, null, null, null, request);
|
||||
|
||||
if (status == AuthenticationMethod.SUCCESS){
|
||||
// Logged in OK.
|
||||
Authenticate.loggedIn(context, request, context.getCurrentUser());
|
||||
|
||||
log.info(LogManager.getHeader(context, "login", "type=shibboleth"));
|
||||
|
||||
// resume previous request
|
||||
Authenticate.resumeInterruptedRequest(request, response);
|
||||
|
||||
return;
|
||||
}else if (status == AuthenticationMethod.CERT_REQUIRED){
|
||||
jsp = "/error/require-certificate.jsp";
|
||||
}else if(status == AuthenticationMethod.NO_SUCH_USER){
|
||||
jsp = "/login/no-single-sign-out.jsp";
|
||||
}else if(status == AuthenticationMethod.BAD_ARGS){
|
||||
jsp = "/login/no-email.jsp";
|
||||
}
|
||||
|
||||
// If we reach here, supplied email/password was duff.
|
||||
log.info(LogManager.getHeader(context, "failed_login","result="+String.valueOf(status)));
|
||||
JSPManager.showJSP(request, response, jsp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@@ -397,6 +397,18 @@
|
||||
</servlet>
|
||||
|
||||
|
||||
<!-- shibbolized dspace -->
|
||||
<servlet>
|
||||
<servlet-name>shibboleth-login</servlet-name>
|
||||
<servlet-class>org.dspace.app.webui.servlet.ShibbolethServlet</servlet-class>
|
||||
</servlet>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>shibboleth-login</servlet-name>
|
||||
<url-pattern>/shibboleth-login</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
|
||||
<!-- Servlet Mappings -->
|
||||
|
||||
<servlet-mapping>
|
||||
|
@@ -0,0 +1,18 @@
|
||||
<%--
|
||||
- should be put under $DSPACE_SRC_HOME/dspace-jspui/dspace-jspui-webapp/src/main/webapp/login
|
||||
- no-email.jsp
|
||||
--%>
|
||||
|
||||
<%--
|
||||
- Display message indicating that the Shibboleth target is not configured properly to release the "user" information
|
||||
--%>
|
||||
|
||||
<%@ page contentType="text/html;charset=UTF-8" %>
|
||||
|
||||
<%@ taglib uri="http://www.dspace.org/dspace-tags.tld" prefix="dspace" %>
|
||||
|
||||
<dspace:layout title="No User email is provided">
|
||||
<h1>User Email is required</h1>
|
||||
|
||||
<P>Your SSO system is not configured properly to release user email info</P>
|
||||
</dspace:layout>
|
@@ -0,0 +1,18 @@
|
||||
<%--
|
||||
- should be put under $DSPACE_SRC_HOME/dspace-jspui/dspace-jspui-webapp/src/main/webapp/login
|
||||
- no-single-sign-out.jsp
|
||||
--%>
|
||||
|
||||
<%--
|
||||
- Display message indicating that the Shibboleth does not support single sign out yet
|
||||
--%>
|
||||
|
||||
<%@ page contentType="text/html;charset=UTF-8" %>
|
||||
|
||||
<%@ taglib uri="http://www.dspace.org/dspace-tags.tld" prefix="dspace" %>
|
||||
|
||||
<dspace:layout title="Single Sign Out feature is not implemented">
|
||||
<h1>Single Sign Out feature is not implemented</h1>
|
||||
|
||||
<P>The protection provided by Shibboleth does not have single sign out feature implemented yet. Please simply close the browser and re-open it to clear cookie</P>
|
||||
</dspace:layout>
|
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* ShibbolethAction.java
|
||||
*
|
||||
* Version: $Revision$
|
||||
*
|
||||
* Date: $Date$
|
||||
*
|
||||
* Copyright (c) 2009, The DSpace Foundation. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of The DSpace Foundation nor the names of their
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*/
|
||||
package org.dspace.app.xmlui.aspect.eperson;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.avalon.framework.parameters.Parameters;
|
||||
import org.apache.cocoon.acting.AbstractAction;
|
||||
import org.apache.cocoon.environment.ObjectModelHelper;
|
||||
import org.apache.cocoon.environment.Redirector;
|
||||
import org.apache.cocoon.environment.Request;
|
||||
import org.apache.cocoon.environment.SourceResolver;
|
||||
import org.apache.cocoon.environment.http.HttpEnvironment;
|
||||
import org.apache.cocoon.sitemap.PatternException;
|
||||
import org.dspace.app.xmlui.utils.AuthenticationUtil;
|
||||
import org.dspace.core.ConfigurationManager;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.eperson.EPerson;
|
||||
|
||||
/**
|
||||
* Attempt to authenticate the user based upon their presented shibboleth credentials.
|
||||
* This action uses the http parameters as supplied by Shibboleth SP.
|
||||
* Read dspace.cfg for configuration detail.
|
||||
*
|
||||
* If the authentication attempt is successfull then an HTTP redirect will be
|
||||
* sent to the browser redirecting them to their original location in the
|
||||
* system before authenticated or if none is supplied back to the DSpace
|
||||
* homepage. The action will also return true, thus contents of the action will
|
||||
* be excuted.
|
||||
*
|
||||
* If the authentication attempt fails, the action returns false.
|
||||
*
|
||||
* Example use:
|
||||
*
|
||||
* <map:act name="Shibboleth">
|
||||
* <map:serialize type="xml"/>
|
||||
* </map:act>
|
||||
* <map:transform type="try-to-login-again-transformer">
|
||||
*
|
||||
* @author <a href="mailto:bliong@melcoe.mq.edu.au">Bruc Liong, MELCOE</a>
|
||||
*/
|
||||
|
||||
public class ShibbolethAction extends AbstractAction
|
||||
{
|
||||
|
||||
/**
|
||||
* Attempt to authenticate the user.
|
||||
*/
|
||||
public Map act(Redirector redirector, SourceResolver resolver, Map objectModel,
|
||||
String source, Parameters parameters) throws Exception
|
||||
{
|
||||
try
|
||||
{
|
||||
//rely on implicit authN of Shib
|
||||
Context context = AuthenticationUtil.Authenticate(objectModel, null, null, null);
|
||||
|
||||
EPerson eperson = null;
|
||||
if(context != null) eperson = context.getCurrentUser();
|
||||
|
||||
if (eperson != null)
|
||||
{
|
||||
Request request = ObjectModelHelper.getRequest(objectModel);
|
||||
// The user has successfully logged in
|
||||
String redirectURL = request.getContextPath();
|
||||
|
||||
if (AuthenticationUtil.isInterupptedRequest(objectModel))
|
||||
{
|
||||
// Resume the request and set the redirect target URL to
|
||||
// that of the originaly interrupted request.
|
||||
redirectURL += AuthenticationUtil.resumeInterruptedRequest(objectModel);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise direct the user to the login page
|
||||
String loginRedirect = ConfigurationManager.getProperty("xmlui.user.loginredirect");
|
||||
redirectURL += (loginRedirect != null) ? loginRedirect.trim() : "";
|
||||
}
|
||||
|
||||
// Authentication successfull send a redirect.
|
||||
final HttpServletResponse httpResponse = (HttpServletResponse) objectModel.get(HttpEnvironment.HTTP_RESPONSE_OBJECT);
|
||||
|
||||
httpResponse.sendRedirect(redirectURL);
|
||||
|
||||
// log the user out for the rest of this current request, however they will be reauthenticated
|
||||
// fully when they come back from the redirect. This prevents caching problems where part of the
|
||||
// request is preformed for the user was authenticated and the other half after it succedded. This
|
||||
// way the user is fully authenticated from the start of the request.
|
||||
//
|
||||
// TODO: have no idea what this is, but leave it as it is, could be broken
|
||||
context.setCurrentUser(null);
|
||||
|
||||
return new HashMap();
|
||||
}
|
||||
}
|
||||
catch (SQLException sqle)
|
||||
{
|
||||
throw new PatternException("Unable to preform Shibboleth authentication",
|
||||
sqle);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@@ -70,6 +70,7 @@ registration, forgotten passwords, editing profiles, and changing passwords.
|
||||
|
||||
<map:actions>
|
||||
<map:action name="AuthenticateAction" src="org.dspace.app.xmlui.aspect.eperson.AuthenticateAction"/>
|
||||
<map:action name="ShibbolethAction" src="org.dspace.app.xmlui.aspect.eperson.ShibbolethAction"/>
|
||||
<map:action name="LDAPAuthenticateAction" src="org.dspace.app.xmlui.aspect.eperson.LDAPAuthenticateAction"/>
|
||||
<map:action name="UnAuthenticateAction" src="org.dspace.app.xmlui.aspect.eperson.UnAuthenticateAction"/>
|
||||
<map:action name="LoginRedirect" src="org.dspace.app.xmlui.aspect.eperson.LoginRedirect" />
|
||||
@@ -193,7 +194,7 @@ registration, forgotten passwords, editing profiles, and changing passwords.
|
||||
</map:match>
|
||||
|
||||
<map:match pattern="shibboleth-login">
|
||||
<map:act type="AuthenticateAction">
|
||||
<map:act type="ShibbolethAction">
|
||||
<!-- Loggin succeeded, request will be forwarded. -->
|
||||
<map:serialize type="xml"/>
|
||||
</map:act>
|
||||
|
@@ -428,7 +428,7 @@
|
||||
<message key="xmlui.EPerson.LoginChooser.trail">Choose Login</message>
|
||||
<message key="xmlui.EPerson.LoginChooser.head1">Choose a Login Method</message>
|
||||
<message key="xmlui.EPerson.LoginChooser.para1">Log in via:</message>
|
||||
<message key="au.edu.mq.melcoe.mams.dspace.eperson.ShibAuthentication.title">Shibboleth Authentication</message>
|
||||
<message key="org.dspace.authenticate.ShibAuthentication.title">Shibboleth Authentication</message>
|
||||
<message key="org.dspace.eperson.LDAPAuthentication.title">LDAP Authentication</message>
|
||||
<message key="org.dspace.eperson.PasswordAuthentication.title">Password Authentication</message>
|
||||
<message key="org.dspace.eperson.X509Authentication.title">Web Certificate Authentication</message>
|
||||
|
@@ -279,9 +279,76 @@ handle.dir = ${dspace.dir}/handle-server
|
||||
|
||||
# Stack of authentication methods
|
||||
# (See org.dspace.authenticate.AuthenticationManager)
|
||||
# Example:
|
||||
# plugin.sequence.org.dspace.authenticate.AuthenticationMethod = \
|
||||
# org.dspace.authenticate.ShibAuthentication, \
|
||||
# org.dspace.authenticate.PasswordAuthentication
|
||||
plugin.sequence.org.dspace.authenticate.AuthenticationMethod = \
|
||||
org.dspace.authenticate.PasswordAuthentication
|
||||
|
||||
#### Shibboleth Authentication Configuration Settings ####
|
||||
# Check https://mams.melcoe.mq.edu.au/zope/mams/pubs/Installation/dspace15/view
|
||||
# for installation detail.
|
||||
#
|
||||
# DSpace requires email as user's credential. There are 2 ways of providing
|
||||
# email to DSpace:
|
||||
# 1) by explicitly specifying to the user which attribute (header)
|
||||
# carries the email address.
|
||||
# 2) by turning on the user-email-using-tomcat=true which means
|
||||
# the software will try to acquire the user's email from Tomcat
|
||||
# The first option takes PRECEDENCE when specified. Both options can
|
||||
# be enabled to allow fallback.
|
||||
|
||||
# this option below specifies that the email comes from the mentioned header.
|
||||
# The value is CASE-Sensitive.
|
||||
authentication.shib.email-header = MAIL
|
||||
|
||||
# optional. Specify the header that carries user's first name
|
||||
# this is going to be used for creation of new-user
|
||||
authentication.shib.firstname-header = SHIB-EP-GIVENNAME
|
||||
|
||||
# optional. Specify the header that carries user's last name
|
||||
# this is used for creation of new user
|
||||
authentication.shib.lastname-header = SHIB-EP-SURNAME
|
||||
|
||||
# this option below forces the software to acquire the email from Tomcat.
|
||||
authentication.shib.email-use-tomcat-remote-user = true
|
||||
|
||||
# should we allow new users to be registered automtically
|
||||
# if the IdP provides sufficient info (and user not exists in DSpace)
|
||||
authentication.shib.autoregister = true
|
||||
|
||||
# this header here specifies which attribute that is responsible
|
||||
# for providing user's roles to DSpace. When not specified, it is
|
||||
# defaulted to 'Shib-EP-UnscopedAffiliation'. The value is specified
|
||||
# in AAP.xml (Shib 1.3.x) or attribute-filter.xml (Shib 2.x).
|
||||
# The value is CASE-Sensitive. The values provided in this
|
||||
# header are separated by semi-colon or comma.
|
||||
# authentication.shib.role-header = Shib-EP-UnscopedAffiliation
|
||||
|
||||
# when user is fully authN on IdP but would not like to release
|
||||
# his/her roles to DSpace (for privacy reason?), what should be
|
||||
# the default roles be given to such users?
|
||||
# The values are separated by semi-colon or comma
|
||||
# authentication.shib.default-roles = Staff, Walk-ins
|
||||
|
||||
# The following mappings specify role mapping between IdP and Dspace.
|
||||
# the left side of the entry is IdP's role (prefixed with
|
||||
# "authentication.shib.role.") which will be mapped to
|
||||
# the right entry from DSpace. DSpace's group as indicated on the
|
||||
# right entry has to EXIST in DSpace, otherwise user will be identified
|
||||
# as 'anonymous'. Multiple values on the right entry should be separated
|
||||
# by comma. The values are CASE-Sensitive. Heuristic one-to-one mapping
|
||||
# will be done when the IdP groups entry are not listed below (i.e.
|
||||
# if "X" group in IdP is not specified here, then it will be mapped
|
||||
# to "X" group in DSpace if it exists, otherwise it will be mapped
|
||||
# to simply 'anonymous')
|
||||
#
|
||||
# Given sufficient demand, future release could support regex for the mapping
|
||||
# special characters need to be escaped by \
|
||||
authentication.shib.role.Senior\ Researcher = Researcher, Staff
|
||||
authentication.shib.role.Librarian = Administrator
|
||||
|
||||
#### PasswordAuthentication options ####
|
||||
|
||||
# Only emails ending in the following domains are allowed to self-register
|
||||
|
Reference in New Issue
Block a user