NIHVIVO-1207 Add the LoginShibboleth servlet.
This commit is contained in:
parent
160f8da487
commit
bb614c833b
2 changed files with 287 additions and 0 deletions
|
@ -1068,6 +1068,11 @@
|
|||
<servlet-class>edu.cornell.mannlib.vitro.webapp.controller.edit.Login</servlet-class>
|
||||
</servlet>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>loginShibboleth</servlet-name>
|
||||
<servlet-class>edu.cornell.mannlib.vitro.webapp.controller.authenticate.LoginShibboleth</servlet-class>
|
||||
</servlet>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>logout</servlet-name>
|
||||
<servlet-class>edu.cornell.mannlib.vitro.webapp.controller.edit.Logout</servlet-class>
|
||||
|
@ -1229,6 +1234,10 @@
|
|||
<servlet-name>login</servlet-name>
|
||||
<url-pattern>/login</url-pattern>
|
||||
</servlet-mapping>
|
||||
<servlet-mapping>
|
||||
<servlet-name>loginShibboleth</servlet-name>
|
||||
<url-pattern>/loginShibboleth</url-pattern>
|
||||
</servlet-mapping>
|
||||
<servlet-mapping>
|
||||
<!-- for backward-compatibility -->
|
||||
<servlet-name>login</servlet-name>
|
||||
|
|
|
@ -0,0 +1,278 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.webapp.controller.authenticate;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Enumeration;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import edu.cornell.mannlib.vitro.webapp.ConfigurationProperties;
|
||||
import edu.cornell.mannlib.vitro.webapp.controller.login.LoginProcessBean;
|
||||
import edu.cornell.mannlib.vitro.webapp.controller.login.LoginProcessBean.Message;
|
||||
|
||||
/**
|
||||
* This servlet acts as the interface to the Shibboleth authentication server.
|
||||
*
|
||||
* If the request has the "setup" property, it is coming from the Vivo login
|
||||
* screen and going to the Shibboleth server.
|
||||
*
|
||||
* Otherwise, the request is coming from the Shibboleth server and going back to
|
||||
* Vivo.
|
||||
*/
|
||||
public class LoginShibboleth extends HttpServlet {
|
||||
private static final Log log = LogFactory.getLog(LoginShibboleth.class);
|
||||
|
||||
private static final String CLASSNAME = LoginShibboleth.class.getName();
|
||||
|
||||
/** This session attribute tells where we came from. */
|
||||
private static final String ATTRIBUTE_REFERRER = CLASSNAME + ".referrer";
|
||||
|
||||
/** This request parameter indicates that we are setting up the login. */
|
||||
private static final String PARAMETER_SETUP = "setup";
|
||||
|
||||
/** This http header holds the referring page. */
|
||||
private static final String HEADING_REFERRER = "referer";
|
||||
|
||||
/** On return froma Shibboleth login, this header holds the provider name. */
|
||||
private static final String HEADING_SHIBBOLETH_PROVIDER = "shib-identity-provider";
|
||||
|
||||
/** On return froma Shibboleth login, this header holds the user name. */
|
||||
private static final String HEADING_SHIBBOLETH_USERNAME = "glid";
|
||||
|
||||
/** The configuration property that points to the Shibboleth server. */
|
||||
private static final String PROPERTY_SHIBBOLETH_SERVER_URL = "shibboleth.server.url";
|
||||
|
||||
/** The configuration property that tells what provider name we expect. */
|
||||
private static final String PROPERTY_SHIBBOLETH_PROVIDER = "shibboleth.provider";
|
||||
|
||||
private static final Message MESSAGE_NO_SHIBBOLETH_SERVER = new LoginProcessBean.Message(
|
||||
"deploy.properties doesn't contain a value for '"
|
||||
+ PROPERTY_SHIBBOLETH_SERVER_URL + "'",
|
||||
LoginProcessBean.MLevel.ERROR);
|
||||
|
||||
private static final Message MESSAGE_NO_SHIBBOLETH_PROVIDER = new LoginProcessBean.Message(
|
||||
"deploy.properties doesn't contain a value for '"
|
||||
+ PROPERTY_SHIBBOLETH_PROVIDER + "'",
|
||||
LoginProcessBean.MLevel.ERROR);
|
||||
|
||||
private static final Message MESSAGE_LOGIN_FAILED = new LoginProcessBean.Message(
|
||||
"Shibboleth login failed.", LoginProcessBean.MLevel.ERROR);
|
||||
|
||||
private static final Message MESSAGE_NO_SUCH_USER = new LoginProcessBean.Message(
|
||||
"Shibboleth login succeeded, but user {0} is unknown to VIVO.",
|
||||
LoginProcessBean.MLevel.ERROR);
|
||||
|
||||
private static final String ERROR_NO_PARAMETERS = "Likely error in the template: "
|
||||
+ "'setup' parameter was not found, "
|
||||
+ "but there was no info from the Shibboleth server either.";
|
||||
|
||||
private final LoginRedirector loginRedirector = new LoginRedirector();
|
||||
|
||||
private String shibbolethServerUrl;
|
||||
private String shibbolethProvider;
|
||||
private static boolean isFirstCallToServlet = true;
|
||||
|
||||
/** Get the configuration properties. */
|
||||
@Override
|
||||
public void init() throws ServletException {
|
||||
shibbolethServerUrl = ConfigurationProperties
|
||||
.getProperty(PROPERTY_SHIBBOLETH_SERVER_URL);
|
||||
shibbolethProvider = ConfigurationProperties
|
||||
.getProperty(PROPERTY_SHIBBOLETH_PROVIDER);
|
||||
}
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* The first request to this servlet must include the setup parameter.
|
||||
* If it doesn't, it is totally bogus and will cause a complaint.
|
||||
* This means that the login form isn't coded correctly,
|
||||
* We try to notify the sysadmin in a meaningful way.
|
||||
*
|
||||
* On setup, write down the referring page and redirect to the shib server URL
|
||||
* a URL that comes from the deploy.properties via the widget code.
|
||||
* if no such property, set error message and return to referring page.
|
||||
* it returns to this page
|
||||
*
|
||||
* Not on setup
|
||||
* check for the site name and the username
|
||||
* if either is missing,
|
||||
* return to the referring page with an error message: login failed.
|
||||
* if both there and the provider is wrong
|
||||
* return to the referring page with an error message: login failed.
|
||||
* if both there and the user doesn't exist
|
||||
* return to the referring page with an error message: no such user.
|
||||
* otherwise (successful)
|
||||
* record the login and redirect like we would on a normal login.
|
||||
* </pre>
|
||||
*/
|
||||
@Override
|
||||
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
|
||||
throws ServletException, IOException {
|
||||
setupLoginProcessBean(req);
|
||||
|
||||
boolean setupParmIsSet = checkSetupParameterIsSet(req);
|
||||
|
||||
if (requestIsTotallyBogus(setupParmIsSet)) {
|
||||
resp.sendError(500, ERROR_NO_PARAMETERS);
|
||||
} else if (setupParmIsSet) {
|
||||
settingUpShibbolethLogin(req, resp);
|
||||
} else {
|
||||
returningFromShibbolethLogin(req, resp);
|
||||
}
|
||||
}
|
||||
|
||||
/** Record that the login is in progress. */
|
||||
private void setupLoginProcessBean(HttpServletRequest req) {
|
||||
LoginProcessBean bean = LoginProcessBean.getBean(req);
|
||||
bean.setState(LoginProcessBean.State.LOGGING_IN);
|
||||
}
|
||||
|
||||
/** Does the request contain a "setup" parameter? */
|
||||
private boolean checkSetupParameterIsSet(HttpServletRequest req) {
|
||||
String setupParm = req.getParameter(PARAMETER_SETUP);
|
||||
log.debug("setup=" + setupParm);
|
||||
if ((setupParm == null) || setupParm.isEmpty()) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/** If the first call doesn't include "setup", the template is broken. */
|
||||
private boolean requestIsTotallyBogus(boolean setupParmIsSet) {
|
||||
boolean bogosity = isFirstCallToServlet && !setupParmIsSet;
|
||||
isFirstCallToServlet = false;
|
||||
return bogosity;
|
||||
}
|
||||
|
||||
/** On setup, hand over to the Shibboleth server. */
|
||||
private void settingUpShibbolethLogin(HttpServletRequest req,
|
||||
HttpServletResponse resp) throws IOException {
|
||||
storeTheReferringPage(req);
|
||||
if (shibbolethServerUrl == null) {
|
||||
log.debug("No shibboleth server in deploy.properties");
|
||||
complainAndReturnToReferrer(req, resp, MESSAGE_NO_SHIBBOLETH_SERVER);
|
||||
} else if (shibbolethProvider == null) {
|
||||
log.debug("No shibboleth provider in deploy.properties");
|
||||
complainAndReturnToReferrer(req, resp,
|
||||
MESSAGE_NO_SHIBBOLETH_PROVIDER);
|
||||
} else {
|
||||
log.debug("Sending to shibboleth server.");
|
||||
resp.sendRedirect(buildShibbolethRedirectUrl(req));
|
||||
}
|
||||
}
|
||||
|
||||
/** Remember where we came from - we'll need to go back there. */
|
||||
private void storeTheReferringPage(HttpServletRequest req) {
|
||||
String referrer = req.getHeader(HEADING_REFERRER);
|
||||
if (referrer == null) {
|
||||
dumpRequestHeaders(req);
|
||||
referrer = figureHomePageUrl(req);
|
||||
}
|
||||
log.debug("Referring page is '" + referrer + "'");
|
||||
req.getSession().setAttribute(ATTRIBUTE_REFERRER, referrer);
|
||||
}
|
||||
|
||||
/** How do we get to the Shibboleth server and back? */
|
||||
private String buildShibbolethRedirectUrl(HttpServletRequest req) {
|
||||
try {
|
||||
String returnUrl = req.getRequestURL().toString();
|
||||
String encodedReturnUrl = URLEncoder.encode(returnUrl, "UTF-8");
|
||||
String shibbolethUrl = shibbolethServerUrl + "?target="
|
||||
+ encodedReturnUrl;
|
||||
log.debug("shibbolethURL is '" + shibbolethUrl + "'");
|
||||
return shibbolethUrl;
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e); // No UTF-8? Really?
|
||||
}
|
||||
}
|
||||
|
||||
/** On return from the Shibboleth server, try to apply the results. */
|
||||
private void returningFromShibbolethLogin(HttpServletRequest req,
|
||||
HttpServletResponse resp) throws IOException {
|
||||
String provider = req.getHeader(HEADING_SHIBBOLETH_PROVIDER);
|
||||
String user = req.getHeader(HEADING_SHIBBOLETH_USERNAME);
|
||||
log.debug("Info from Shibboleth: user=" + user + ", provider="
|
||||
+ provider);
|
||||
|
||||
if ((provider == null) || (user == null)) {
|
||||
complainAndReturnToReferrer(req, resp, MESSAGE_LOGIN_FAILED);
|
||||
} else if (!this.shibbolethProvider.equals(provider)) {
|
||||
log.error("Wrong shibboleth provider: " + provider);
|
||||
complainAndReturnToReferrer(req, resp, MESSAGE_LOGIN_FAILED);
|
||||
} else if (!getAuthenticator(req).isExistingUser(user)) {
|
||||
log.debug("No such user: " + user);
|
||||
complainAndReturnToReferrer(req, resp, MESSAGE_NO_SUCH_USER, user);
|
||||
} else {
|
||||
log.debug("Logging in as " + user);
|
||||
recordLoginAndRedirect(req, resp, user);
|
||||
}
|
||||
}
|
||||
|
||||
/** Success. Record the login and send them to the appropriate page. */
|
||||
private void recordLoginAndRedirect(HttpServletRequest req,
|
||||
HttpServletResponse resp, String username)
|
||||
throws UnsupportedEncodingException, IOException {
|
||||
getAuthenticator(req).recordUserIsLoggedIn(username);
|
||||
req.getSession().removeAttribute(ATTRIBUTE_REFERRER);
|
||||
loginRedirector.redirectLoggedInUser(req, resp);
|
||||
}
|
||||
|
||||
/** Store an error message in the login bean and go back where we came from. */
|
||||
private void complainAndReturnToReferrer(HttpServletRequest req,
|
||||
HttpServletResponse resp, Message message, Object... args)
|
||||
throws IOException {
|
||||
log.debug(message.getMessageLevel() +": "+ message.formatMessage(args));
|
||||
LoginProcessBean.getBean(req).setMessage(message, args);
|
||||
|
||||
String referrer = (String) req.getSession().getAttribute(
|
||||
ATTRIBUTE_REFERRER);
|
||||
log.debug("returning to referrer: " + referrer);
|
||||
if (referrer == null) {
|
||||
referrer = figureHomePageUrl(req);
|
||||
log.debug("returning to home page: " + referrer);
|
||||
}
|
||||
|
||||
req.getSession().removeAttribute(ATTRIBUTE_REFERRER);
|
||||
resp.sendRedirect(referrer);
|
||||
}
|
||||
|
||||
private String figureHomePageUrl(HttpServletRequest req) {
|
||||
StringBuffer url = req.getRequestURL();
|
||||
String uri = req.getRequestURI();
|
||||
int authLength = url.length() - uri.length();
|
||||
String auth = url.substring(0, authLength);
|
||||
return auth + req.getContextPath();
|
||||
}
|
||||
|
||||
private Authenticator getAuthenticator(HttpServletRequest req) {
|
||||
return Authenticator.getInstance(req);
|
||||
}
|
||||
|
||||
private void dumpRequestHeaders(HttpServletRequest req) {
|
||||
if (log.isDebugEnabled()) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Enumeration<String> names = req.getHeaderNames();
|
||||
while (names.hasMoreElements()) {
|
||||
String name = names.nextElement();
|
||||
log.debug("header: " + name + "=" + req.getHeader(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest request,
|
||||
HttpServletResponse response) throws ServletException, IOException {
|
||||
doPost(request, response);
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Reference in a new issue