mirror of
https://github.com/DSpace/DSpace.git
synced 2025-10-12 12:33:18 +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";
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user