From 37c9eaf4a141ab9152059e36f1313c5d7fa7aed6 Mon Sep 17 00:00:00 2001 From: jeb228 Date: Wed, 17 Nov 2010 19:25:55 +0000 Subject: [PATCH] NIHVIVO-1207 Rename Shibboleth servlets to more general ExternalAuth servlets. Add functionality to deal with self-editors as well as Users. --- webapp/config/web.xml | 26 +++- .../authenticate/LoginExternalAuthReturn.java | 117 ++++++++++++++++++ ...Setup.java => LoginExternalAuthSetup.java} | 62 +++++----- .../authenticate/LoginRedirector.java | 28 ++++- .../authenticate/LoginShibbolethReturn.java | 116 ----------------- .../UnrecognizedUserController.java | 35 ++++++ .../freemarker/body/unrecognizedUser.ftl | 15 +++ 7 files changed, 246 insertions(+), 153 deletions(-) create mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginExternalAuthReturn.java rename webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/{LoginShibbolethSetup.java => LoginExternalAuthSetup.java} (61%) delete mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginShibbolethReturn.java create mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/UnrecognizedUserController.java create mode 100644 webapp/web/templates/freemarker/body/unrecognizedUser.ftl diff --git a/webapp/config/web.xml b/webapp/config/web.xml index 23a727523..8e7f69fa4 100644 --- a/webapp/config/web.xml +++ b/webapp/config/web.xml @@ -1087,8 +1087,13 @@ - loginShibboleth - edu.cornell.mannlib.vitro.webapp.controller.authenticate.LoginShibboleth + loginExternalAuthSetup + edu.cornell.mannlib.vitro.webapp.controller.authenticate.LoginExternalAuthSetup + + + + loginExternalAuthReturn + edu.cornell.mannlib.vitro.webapp.controller.authenticate.LoginExternalAuthReturn @@ -1096,6 +1101,15 @@ edu.cornell.mannlib.vitro.webapp.controller.edit.Logout + + unrecognizedUser + edu.cornell.mannlib.vitro.webapp.controller.authenticate.UnrecognizedUserController + + + unrecognizedUser + /unrecognizedUser + + browsecontroller edu.cornell.mannlib.vitro.webapp.controller.freemarker.BrowseController @@ -1258,6 +1272,14 @@ loginShibboleth /loginShibboleth + + loginExternalAuthSetup + /loginExternalAuth + + + loginExternalAuthReturn + /loginExternalAuthReturn + login diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginExternalAuthReturn.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginExternalAuthReturn.java new file mode 100644 index 000000000..04368a2b7 --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginExternalAuthReturn.java @@ -0,0 +1,117 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.controller.authenticate; + +import static edu.cornell.mannlib.vitro.webapp.controller.authenticate.LoginExternalAuthSetup.ATTRIBUTE_REFERRER; + +import java.io.IOException; + +import javax.servlet.ServletException; +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.VitroRequest; +import edu.cornell.mannlib.vitro.webapp.controller.login.LoginProcessBean; +import edu.cornell.mannlib.vitro.webapp.controller.login.LoginProcessBean.Message; +import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory; + +/** + * Handle the return from the external authorization login server. If we are + * successful, record the login. Otherwise, display the failure. + */ +public class LoginExternalAuthReturn extends BaseLoginServlet { + private static final Log log = LogFactory + .getLog(LoginExternalAuthReturn.class); + + /* This configuration property tells us what header contains the username. */ + public static final String PROPERTY_EXTERNAL_AUTH_USERNAME_HEADER = "externalAuth.headerName"; + + /** The complaint we make if there is no such property. */ + private static final Message MESSAGE_NO_EXTERNAL_AUTH_USERNAME = new LoginProcessBean.Message( + "deploy.properties doesn't contain a value for '" + + PROPERTY_EXTERNAL_AUTH_USERNAME_HEADER + "'", + LoginProcessBean.MLevel.ERROR); + + private static final Message MESSAGE_LOGIN_FAILED = new LoginProcessBean.Message( + "External login failed.", LoginProcessBean.MLevel.ERROR); + + private final LoginRedirector loginRedirector = new LoginRedirector(); + private String externalAuthUsernameHeader; + + /** Get the configuration properties. */ + @Override + public void init() throws ServletException { + externalAuthUsernameHeader = ConfigurationProperties + .getProperty(PROPERTY_EXTERNAL_AUTH_USERNAME_HEADER); + } + + /** + *
+	 * Returning from the external authorization server. If we were successful,
+	 * the header will contain the name of the user who just logged in.
+	 * 
+	 * Deal with these possibilities: 
+	 * - The header name was not configured in deploy.properties. Complain.
+	 * - No username: the login failed. Complain 
+	 * - User corresponds to a User acocunt. Record the login. 
+	 * - User corresponds to an Individual (self-editor). 
+	 * - User is not recognized.
+	 * 
+ */ + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + if (externalAuthUsernameHeader == null) { + complainAndReturnToReferrer(req, resp, ATTRIBUTE_REFERRER, + MESSAGE_NO_EXTERNAL_AUTH_USERNAME); + return; + } + + String username = req.getHeader(externalAuthUsernameHeader); + String uri = getAssociatedIndividualUri(username, req); + + if (username == null) { + log.debug("No username."); + complainAndReturnToReferrer(req, resp, ATTRIBUTE_REFERRER, + MESSAGE_LOGIN_FAILED); + } else if (getAuthenticator(req).isExistingUser(username)) { + log.debug("Logging in as " + username); + getAuthenticator(req).recordUserIsLoggedIn(username); + removeLoginProcessArtifacts(req); + loginRedirector.redirectLoggedInUser(req, resp); + } else if (uri != null) { + log.debug("Recognize '' as self-editor for " + uri); + removeLoginProcessArtifacts(req); + loginRedirector.redirectSelfEditingUser(req, resp, uri); + } else { + log.debug("User is not recognized: " + username); + removeLoginProcessArtifacts(req); + loginRedirector.redirectUnrecognizedUser(req, resp, username); + } + } + + private String getAssociatedIndividualUri(String username, + HttpServletRequest req) { + if (username == null) { + return null; + } + VitroRequest vreq = new VitroRequest(req); + WebappDaoFactory wdf = vreq.getWebappDaoFactory(); + return wdf.getIndividualDao().getIndividualURIFromNetId(username); + } + + private void removeLoginProcessArtifacts(HttpServletRequest req) { + LoginProcessBean.removeBean(req); + req.getSession().removeAttribute(ATTRIBUTE_REFERRER); + } + + @Override + protected void doGet(HttpServletRequest request, + HttpServletResponse response) throws ServletException, IOException { + doPost(request, response); + } +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginShibbolethSetup.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginExternalAuthSetup.java similarity index 61% rename from webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginShibbolethSetup.java rename to webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginExternalAuthSetup.java index f54f7e6b5..624a281e0 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginShibbolethSetup.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginExternalAuthSetup.java @@ -19,69 +19,71 @@ import edu.cornell.mannlib.vitro.webapp.controller.login.LoginProcessBean; import edu.cornell.mannlib.vitro.webapp.controller.login.LoginProcessBean.Message; /** - * Set up the Shibboleth login process. + * Set up the external authorization process. * * Write down the page that triggered the request, so we can get back to it. * - * Send a request to the Shibboleth server that will return us to the - * LoginShibbolethReturn servlet for further processing. + * Send a request to the external authorization server that will return us to + * the LoginExternalAuthReturn servlet for further processing. */ -public class LoginShibbolethSetup extends BaseLoginServlet { +public class LoginExternalAuthSetup extends BaseLoginServlet { private static final Log log = LogFactory - .getLog(LoginShibbolethSetup.class); + .getLog(LoginExternalAuthSetup.class); /** This session attribute tells where we came from. */ - static final String ATTRIBUTE_REFERRER = LoginShibbolethSetup.class + static final String ATTRIBUTE_REFERRER = LoginExternalAuthSetup.class .getName() + ".referrer"; - private static final String RETURN_SERVLET_URL = "/loginShibbolethReturn"; + private static final String RETURN_SERVLET_URL = "/loginExternalAuthReturn"; /** This http header holds the referring page. */ private static final String HEADING_REFERRER = "referer"; - /** The configuration property that points to the Shibboleth server. */ - private static final String PROPERTY_SHIBBOLETH_SERVER_URL = "shibboleth.server.url"; + /** + * The configuration property that points to the external authorization + * server. + */ + private static final String PROPERTY_EXTERNAL_AUTH_SERVER_URL = "externalAuth.serverUrl"; - /** The complaint we make if there is no Shibbolet server property. */ - private static final Message MESSAGE_NO_SHIBBOLETH_SERVER = new LoginProcessBean.Message( + /** + * The complaint we make if there is no external authorization server + * property. + */ + private static final Message MESSAGE_NO_EXTERNAL_AUTH_SERVER = new LoginProcessBean.Message( "deploy.properties doesn't contain a value for '" - + PROPERTY_SHIBBOLETH_SERVER_URL + "'", + + PROPERTY_EXTERNAL_AUTH_SERVER_URL + "'", LoginProcessBean.MLevel.ERROR); - private String shibbolethServerUrl; + private String extrnalAuthServerUrl; /** Get the configuration property. */ @Override public void init() throws ServletException { - shibbolethServerUrl = ConfigurationProperties - .getProperty(PROPERTY_SHIBBOLETH_SERVER_URL); + extrnalAuthServerUrl = ConfigurationProperties + .getProperty(PROPERTY_EXTERNAL_AUTH_SERVER_URL); } /** * Write down the referring page, record that we are logging in, and - * redirect to the shib server URL. + * redirect to the external authorization server URL. */ @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - // Record where we came from, so we can get back there. storeTheReferringPage(req); - // If we have no URL for the Shibboleth server, give up. - if (shibbolethServerUrl == null) { - log.debug("No shibboleth server in deploy.properties"); + if (extrnalAuthServerUrl == null) { + log.debug("No external authorization server in deploy.properties"); complainAndReturnToReferrer(req, resp, ATTRIBUTE_REFERRER, - MESSAGE_NO_SHIBBOLETH_SERVER); + MESSAGE_NO_EXTERNAL_AUTH_SERVER); return; } - // Record that we are in the process of logging in. LoginProcessBean.getBean(req).setState( LoginProcessBean.State.LOGGING_IN); - // Hand over to Shibboleth. - log.debug("Sending to shibboleth server."); - resp.sendRedirect(buildShibbolethRedirectUrl(req)); + log.debug("Sending to external authorization server."); + resp.sendRedirect(buildExternalAuthRedirectUrl(req)); } /** Remember where we came from - we'll need to go back there. */ @@ -95,15 +97,15 @@ public class LoginShibbolethSetup extends BaseLoginServlet { req.getSession().setAttribute(ATTRIBUTE_REFERRER, referrer); } - /** How do we get to the Shibboleth server and back? */ - private String buildShibbolethRedirectUrl(HttpServletRequest req) { + /** How do we get to the external authorization server and back? */ + private String buildExternalAuthRedirectUrl(HttpServletRequest req) { try { String returnUrl = figureHomePageUrl(req) + RETURN_SERVLET_URL; String encodedReturnUrl = URLEncoder.encode(returnUrl, "UTF-8"); - String shibbolethUrl = shibbolethServerUrl + "?target=" + String externalAuthUrl = extrnalAuthServerUrl + "?target=" + encodedReturnUrl; - log.debug("shibbolethURL is '" + shibbolethUrl + "'"); - return shibbolethUrl; + log.debug("externalAuthUrl is '" + externalAuthUrl + "'"); + return externalAuthUrl; } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); // No UTF-8? Really? } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginRedirector.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginRedirector.java index e96138c36..b186cd475 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginRedirector.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginRedirector.java @@ -3,6 +3,7 @@ package edu.cornell.mannlib.vitro.webapp.controller.authenticate; import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.List; @@ -23,9 +24,23 @@ import edu.cornell.mannlib.vitro.webapp.controller.Controllers; public class LoginRedirector { private static final Log log = LogFactory.getLog(LoginRedirector.class); + public void redirectSelfEditingUser(HttpServletRequest request, + HttpServletResponse response, String uri) throws IOException { + String userHomePage = assembleUserHomePageUrl(request, uri); + log.debug("Redirecting self-editor to " + userHomePage); + response.sendRedirect(userHomePage); + } + + public void redirectUnrecognizedUser(HttpServletRequest request, + HttpServletResponse response, String username) throws IOException { + log.debug("Redirecting unrecognized user: " + username); + response.sendRedirect(request.getContextPath() + + "/unrecognizedUser?username=" + username); + } + /** *
-	 * Exit: the user is logged in. They might go to:
+	 * The user is logged in. They might go to:
 	 * - A one-time redirect, stored in the session, if they had tried to
 	 *     bookmark to a page that requires login.
 	 * - An application-wide redirect, stored in the servlet context.
@@ -68,9 +83,8 @@ public class LoginRedirector {
 			List uris = getAuthenticator(request)
 					.asWhomMayThisUserEdit(user);
 			if (uris != null && uris.size() > 0) {
-				String userHomePage = request.getContextPath()
-						+ "/individual?uri="
-						+ URLEncoder.encode(uris.get(0), "UTF-8");
+				String userHomePage = assembleUserHomePageUrl(request,
+						uris.get(0));
 				log.debug("User is logged in. Redirect as self-editor to "
 						+ userHomePage);
 				response.sendRedirect(userHomePage);
@@ -106,7 +120,6 @@ public class LoginRedirector {
 
 	/** What's the URL for the site admin screen? */
 	private String getSiteAdminUrl(HttpServletRequest request) {
-	    // return Route.SITE_ADMIN.url();
 		String contextPath = request.getContextPath();
 		return contextPath + Controllers.SITE_ADMIN;
 	}
@@ -116,4 +129,9 @@ public class LoginRedirector {
 		return Authenticator.getInstance(request);
 	}
 
+	private String assembleUserHomePageUrl(HttpServletRequest request,
+			String uri) throws UnsupportedEncodingException {
+		return request.getContextPath() + "/individual?uri="
+				+ URLEncoder.encode(uri, "UTF-8");
+	}
 }
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginShibbolethReturn.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginShibbolethReturn.java
deleted file mode 100644
index e8669a85a..000000000
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginShibbolethReturn.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/* $This file is distributed under the terms of the license in /doc/license.txt$ */
-
-package edu.cornell.mannlib.vitro.webapp.controller.authenticate;
-
-import static edu.cornell.mannlib.vitro.webapp.controller.authenticate.LoginShibbolethSetup.ATTRIBUTE_REFERRER;
-
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-
-import javax.servlet.ServletException;
-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;
-
-/**
- * Handle the return from the Shibboleth login server. If we are successful,
- * record the login. Otherwise, display the failure.
- */
-public class LoginShibbolethReturn extends BaseLoginServlet {
-	private static final Log log = LogFactory
-			.getLog(LoginShibbolethReturn.class);
-
-	/** The configuration property that tells what provider name we expect. */
-	private static final String PROPERTY_SHIBBOLETH_PROVIDER = "shibboleth.provider";
-
-	/** 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";
-
-	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 Message MESSAGE_NO_SHIBBOLETH_PROVIDER = new LoginProcessBean.Message(
-			"deploy.properties doesn't contain a value for '"
-					+ PROPERTY_SHIBBOLETH_PROVIDER + "'",
-			LoginProcessBean.MLevel.ERROR);
-
-	private final LoginRedirector loginRedirector = new LoginRedirector();
-	private String shibbolethProvider;
-
-	/** Get the configuration properties. */
-	@Override
-	public void init() throws ServletException {
-		shibbolethProvider = ConfigurationProperties
-				.getProperty(PROPERTY_SHIBBOLETH_PROVIDER);
-	}
-
-	/**
-	 * Returning from the Shibboleth server. If we were successful, the headers
-	 * will contain the Shibboleth provider name and the name of the user who
-	 * just logged in.
-	 * 
-	 * We report problems if the provider name is missing or we don't know the
-	 * correct value for it. We also report problems if the username is missing
-	 * or if we don't recognize that user.
-	 * 
-	 * If there are no problems, record the login and redirect like we would on
-	 * a normal login.
-	 */
-	@Override
-	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
-			throws ServletException, 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, ATTRIBUTE_REFERRER,
-					MESSAGE_LOGIN_FAILED);
-		} else if (shibbolethProvider == null) {
-			log.debug("No shibboleth provider in deploy.properties");
-			complainAndReturnToReferrer(req, resp, ATTRIBUTE_REFERRER,
-					MESSAGE_NO_SHIBBOLETH_PROVIDER);
-		} else if (!this.shibbolethProvider.equals(provider)) {
-			log.error("Wrong shibboleth provider: " + provider);
-			complainAndReturnToReferrer(req, resp, ATTRIBUTE_REFERRER,
-					MESSAGE_LOGIN_FAILED);
-		} else if (!getAuthenticator(req).isExistingUser(user)) {
-			log.debug("No such user: " + user);
-			complainAndReturnToReferrer(req, resp, ATTRIBUTE_REFERRER,
-					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);
-		LoginProcessBean.removeBean(req);
-		req.getSession().removeAttribute(ATTRIBUTE_REFERRER);
-		loginRedirector.redirectLoggedInUser(req, resp);
-	}
-
-	@Override
-	protected void doGet(HttpServletRequest request,
-			HttpServletResponse response) throws ServletException, IOException {
-		doPost(request, response);
-	}
-}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/UnrecognizedUserController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/UnrecognizedUserController.java
new file mode 100644
index 000000000..1721a46d8
--- /dev/null
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/UnrecognizedUserController.java
@@ -0,0 +1,35 @@
+/* $This file is distributed under the terms of the license in /doc/license.txt$ */
+
+package edu.cornell.mannlib.vitro.webapp.controller.authenticate;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
+import edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerHttpServlet;
+
+/**
+ * TODO
+ */
+public class UnrecognizedUserController extends FreemarkerHttpServlet {
+	private static final String TEMPLATE_DEFAULT = "unrecognizedUser.ftl";
+	private static final String PARAMETER_USERNAME = "username";
+
+	@Override
+	protected ResponseValues processRequest(VitroRequest vreq) {
+		Map body = new HashMap();
+
+		String username = vreq.getParameter(PARAMETER_USERNAME);
+		if (username != null) {
+			body.put("username", username);
+		}
+
+		return new TemplateResponseValues(TEMPLATE_DEFAULT, body);
+	}
+
+	@Override
+	protected String getTitle(String siteName) {
+		return "Unrecognized user " + siteName;
+	}
+
+}
diff --git a/webapp/web/templates/freemarker/body/unrecognizedUser.ftl b/webapp/web/templates/freemarker/body/unrecognizedUser.ftl
new file mode 100644
index 000000000..105f5e199
--- /dev/null
+++ b/webapp/web/templates/freemarker/body/unrecognizedUser.ftl
@@ -0,0 +1,15 @@
+<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
+
+<#-- Template for the Unrecognized User page. -->
+
+
+

Unrecognized user

+ +

+ For some reason, there is no individual in VIVO that is associated with your Net ID. + Perhaps you should contact your VIVO administrator. +

+ +
+ Continue +