From 6986eb6308aeed8e0ec35c44ab68ee595fc79420 Mon Sep 17 00:00:00 2001 From: j2blake Date: Wed, 1 Feb 2012 22:03:11 +0000 Subject: [PATCH] NIHVIVO-2694 Add methods and an exception to Authenticator, so we can tell when a user is not allowed to login. --- .../UserAccountsFirstTimeExternalPage.java | 5 +++ .../user/UserAccountsUserController.java | 14 +++++-- .../authenticate/AdminLoginController.java | 31 +++++++++++----- .../authenticate/Authenticator.java | 37 +++++++++++++++++-- .../authenticate/BaseLoginServlet.java | 5 +++ .../authenticate/BasicAuthenticator.java | 7 ++++ .../authenticate/LoginExternalAuthReturn.java | 19 +++++++++- .../controller/authenticate/ProgramLogin.java | 27 +++++++++++--- .../webapp/controller/edit/Authenticate.java | 36 +++++++++++++----- .../controller/login/LoginProcessBean.java | 4 ++ .../authenticate/AuthenticatorStub.java | 7 +++- .../freemarker/body/login/adminLogin.ftl | 4 ++ 12 files changed, 164 insertions(+), 32 deletions(-) diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsFirstTimeExternalPage.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsFirstTimeExternalPage.java index fd556b155..78ea1c7ec 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsFirstTimeExternalPage.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsFirstTimeExternalPage.java @@ -124,6 +124,11 @@ public class UserAccountsFirstTimeExternalPage extends UserAccountsPage { + "'"; return; } + if (!Authenticator.getInstance(vreq).isUserPermittedToLogin(null)) { + bogusMessage = "User logins are temporarily disabled " + + "while the system is being maintained."; + return; + } } public boolean isBogus() { diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsUserController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsUserController.java index ac1a73519..e2f7a47e6 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsUserController.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsUserController.java @@ -13,6 +13,7 @@ import edu.cornell.mannlib.vitro.webapp.beans.DisplayMessage; import edu.cornell.mannlib.vitro.webapp.beans.UserAccount; import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; import edu.cornell.mannlib.vitro.webapp.controller.authenticate.Authenticator; +import edu.cornell.mannlib.vitro.webapp.controller.authenticate.Authenticator.LoginNotPermitted; import edu.cornell.mannlib.vitro.webapp.controller.authenticate.LoginRedirector; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerHttpServlet; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.RedirectResponseValues; @@ -108,10 +109,15 @@ public class UserAccountsUserController extends FreemarkerHttpServlet { if (page.isBogus()) { return showHomePage(vreq, page.getBogusMessage()); } else if (page.isSubmit() && page.isValid()) { - UserAccount userAccount = page.createAccount(); - Authenticator auth = Authenticator.getInstance(vreq); - auth.recordLoginAgainstUserAccount(userAccount, EXTERNAL); - return showLoginRedirection(vreq, page.getAfterLoginUrl()); + try { + UserAccount userAccount = page.createAccount(); + Authenticator auth = Authenticator.getInstance(vreq); + auth.recordLoginAgainstUserAccount(userAccount, EXTERNAL); + return showLoginRedirection(vreq, page.getAfterLoginUrl()); + } catch (LoginNotPermitted e) { + // This should have been anticipated by the page. + return showHomePage(vreq, BOGUS_STANDARD_MESSAGE); + } } else { return page.showPage(); } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/AdminLoginController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/AdminLoginController.java index 66dc2c3ae..9863a3436 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/AdminLoginController.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/AdminLoginController.java @@ -15,6 +15,7 @@ import org.apache.commons.logging.LogFactory; import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.Actions; import edu.cornell.mannlib.vitro.webapp.beans.UserAccount; import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; +import edu.cornell.mannlib.vitro.webapp.controller.authenticate.Authenticator.LoginNotPermitted; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerHttpServlet; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.RedirectResponseValues; @@ -44,6 +45,7 @@ public class AdminLoginController extends FreemarkerHttpServlet { private static final String MESSAGE_NO_EMAIL_ADDRESS = "errorNoEmail"; private static final String MESSAGE_NO_PASSWORD = "errorNoPassword"; + private static final String MESSAGE_LOGIN_DISABLED = "errorLoginDisabled"; private static final String MESSAGE_LOGIN_FAILED = "errorLoginFailed"; private static final String MESSAGE_NEW_PASSWORD_REQUIRED = "newPasswordRequired"; private static final String MESSAGE_NEW_PASSWORD_WRONG_LENGTH = "errorNewPasswordWrongLength"; @@ -101,6 +103,9 @@ public class AdminLoginController extends FreemarkerHttpServlet { if (password.isEmpty()) { return showForm(MESSAGE_NO_PASSWORD); } + if (!loginPermitted()) { + return showForm(MESSAGE_LOGIN_DISABLED); + } if (newPasswordRequired()) { if (newPassword.isEmpty()) { return showForm(MESSAGE_NEW_PASSWORD_REQUIRED); @@ -127,6 +132,10 @@ public class AdminLoginController extends FreemarkerHttpServlet { return showForm(MESSAGE_LOGIN_FAILED); } + private boolean loginPermitted() { + return auth.isUserPermittedToLogin(userAccount); + } + private boolean newPasswordRequired() { return auth.isCurrentPassword(userAccount, password) && (userAccount.isPasswordChangeRequired()); @@ -138,17 +147,21 @@ public class AdminLoginController extends FreemarkerHttpServlet { } private boolean tryToLogin() { - if (auth.isCurrentPassword(userAccount, password)) { - auth.recordLoginAgainstUserAccount(userAccount, INTERNAL); - - if (!newPassword.isEmpty()) { - auth.recordNewPassword(userAccount, newPassword); - } - - return true; - } else { + if (!auth.isCurrentPassword(userAccount, password)) { return false; } + + try { + auth.recordLoginAgainstUserAccount(userAccount, INTERNAL); + } catch (LoginNotPermitted e) { + return false; + } + + if (!newPassword.isEmpty()) { + auth.recordNewPassword(userAccount, newPassword); + } + + return true; } private ResponseValues showForm(String... codes) { diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/Authenticator.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/Authenticator.java index fb86eb1d1..6cb4b49cf 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/Authenticator.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/Authenticator.java @@ -23,6 +23,14 @@ import edu.cornell.mannlib.vitro.webapp.beans.UserAccount; * This needs to be based on a HttpSession, because things like the UserDAO are * tied to the session. It seemed easier to base it on a HttpServletRequest, * which we can use to get the session. + * + * TODO: Wouldn't it be cool if we could remove the LoginNotPermitted exception? + * Perhaps we could have a sub-object called an Authenticator.ForUser, and you + * call a getAuthenticatorForUser() method which returns null if your login has + * been disabled. Then, that object would provide these methods: + * accountRequiresEditing(), getAssociatedIndividualUris(), isCurrentPassword(), + * recordLoginAgainstUserAccount(), recordNewPassword(). If you didn't have such + * an object, you couldn't even call these methods. */ public abstract class Authenticator { // ---------------------------------------------------------------------- @@ -48,11 +56,11 @@ public abstract class Authenticator { public static Authenticator getInstance(HttpServletRequest request) { ServletContext ctx = request.getSession().getServletContext(); Object attribute = ctx.getAttribute(FACTORY_ATTRIBUTE_NAME); - if (! (attribute instanceof AuthenticatorFactory)) { + if (!(attribute instanceof AuthenticatorFactory)) { attribute = new BasicAuthenticator.Factory(); ctx.setAttribute(FACTORY_ATTRIBUTE_NAME, attribute); } - AuthenticatorFactory factory = (AuthenticatorFactory) attribute; + AuthenticatorFactory factory = (AuthenticatorFactory) attribute; return factory.getInstance(request); } @@ -77,6 +85,16 @@ public abstract class Authenticator { */ public abstract UserAccount getAccountForInternalAuth(String emailAddress); + /** + * Is this user permitted to login? Some Authenticators might disable logins + * for certain users. + * + * Behavior when userAccount is null depends on the particular + * Authenticator. An answer of "true" presumably means that the user will be + * permitted to login and create an account on the fly. + */ + public abstract boolean isUserPermittedToLogin(UserAccount userAccount); + /** * Internal: does this UserAccount have this password? False if the * userAccount is null. @@ -113,9 +131,14 @@ public abstract class Authenticator { * - record the user in the session map * - notify other users of the model * + * + * @throws LoginNotPermitted + * if the Authenticator denies this user the ability to login. + * This should be thrown if and only if isUserPermittedToLogin() + * returns false. */ public abstract void recordLoginAgainstUserAccount(UserAccount userAccount, - AuthenticationSource authSource); + AuthenticationSource authSource) throws LoginNotPermitted; /** *
@@ -173,4 +196,12 @@ public abstract class Authenticator {
 			return false;
 		}
 	}
+
+	// ----------------------------------------------------------------------
+	// Exceptions
+	// ----------------------------------------------------------------------
+
+	public static class LoginNotPermitted extends Exception {
+		// no other information
+	}
 }
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/BaseLoginServlet.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/BaseLoginServlet.java
index 22698eda5..20bcd402e 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/BaseLoginServlet.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/BaseLoginServlet.java
@@ -24,6 +24,11 @@ public class BaseLoginServlet extends HttpServlet {
 	protected static final Message MESSAGE_LOGIN_FAILED = new LoginProcessBean.Message(
 			"External login failed.", LoginProcessBean.MLevel.ERROR);
 
+	/** Tell the user that it's nothing personal, they just aren't allowed in. */
+	protected static final Message MESSAGE_LOGIN_DISABLED = new LoginProcessBean.Message(
+			"User logins are temporarily disabled while the system is being maintained.",
+			LoginProcessBean.MLevel.ERROR);
+
 	protected Authenticator getAuthenticator(HttpServletRequest req) {
 		return Authenticator.getInstance(req);
 	}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/BasicAuthenticator.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/BasicAuthenticator.java
index 347d6dc86..918630107 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/BasicAuthenticator.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/BasicAuthenticator.java
@@ -76,6 +76,13 @@ public class BasicAuthenticator extends Authenticator {
 		return userAccountsDao.getUserAccountByExternalAuthId(externalAuthId);
 	}
 
+	@Override
+	public boolean isUserPermittedToLogin(UserAccount userAccount) {
+		// All users are permitted to login. If the user doesn't have an account
+		// yet (userAccount is null), an account should be created for them.
+		return true;
+	}
+
 	@Override
 	public boolean isCurrentPassword(UserAccount userAccount,
 			String clearTextPassword) {
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
index e8dfd4cea..216bb12cd 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginExternalAuthReturn.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginExternalAuthReturn.java
@@ -17,6 +17,7 @@ import org.apache.commons.logging.LogFactory;
 import edu.cornell.mannlib.vedit.beans.LoginStatusBean.AuthenticationSource;
 import edu.cornell.mannlib.vitro.webapp.beans.UserAccount;
 import edu.cornell.mannlib.vitro.webapp.controller.accounts.user.UserAccountsFirstTimeExternalPage;
+import edu.cornell.mannlib.vitro.webapp.controller.authenticate.Authenticator.LoginNotPermitted;
 import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder;
 import edu.cornell.mannlib.vitro.webapp.controller.login.LoginProcessBean;
 
@@ -77,6 +78,14 @@ public class LoginExternalAuthReturn extends BaseLoginServlet {
 
 		UserAccount userAccount = getAuthenticator(req)
 				.getAccountForExternalAuth(externalAuthId);
+
+		if (!getAuthenticator(req).isUserPermittedToLogin(userAccount)) {
+			log.debug("Logins disabled for " + userAccount);
+			complainAndReturnToReferrer(req, resp, ATTRIBUTE_REFERRER,
+					MESSAGE_LOGIN_DISABLED);
+			return;
+		}
+
 		if (userAccount == null) {
 			log.debug("Creating new account for " + externalAuthId
 					+ ", return to '" + afterLoginUrl + "'");
@@ -84,12 +93,20 @@ public class LoginExternalAuthReturn extends BaseLoginServlet {
 					externalAuthId, afterLoginUrl);
 			resp.sendRedirect(UrlBuilder.getUrl("/accounts/firstTimeExternal"));
 			return;
-		} else {
+		}
+
+		try {
 			log.debug("Logging in as " + userAccount.getUri());
 			getAuthenticator(req).recordLoginAgainstUserAccount(userAccount,
 					AuthenticationSource.EXTERNAL);
 			new LoginRedirector(req, afterLoginUrl).redirectLoggedInUser(resp);
 			return;
+		} catch (LoginNotPermitted e) {
+			// should have been caught by isUserPermittedToLogin()
+			log.debug("Logins disabled for " + userAccount);
+			complainAndReturnToReferrer(req, resp, ATTRIBUTE_REFERRER,
+					MESSAGE_LOGIN_DISABLED);
+			return;
 		}
 	}
 
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/ProgramLogin.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/ProgramLogin.java
index bdaef9162..ab3c05577 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/ProgramLogin.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/ProgramLogin.java
@@ -18,6 +18,7 @@ import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
 import edu.cornell.mannlib.vitro.webapp.beans.UserAccount;
+import edu.cornell.mannlib.vitro.webapp.controller.authenticate.Authenticator.LoginNotPermitted;
 
 /**
  * Provide a means for programmatic login If they provide the right parameters,
@@ -29,13 +30,18 @@ public class ProgramLogin extends HttpServlet {
 	@Override
 	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
 			throws ServletException, IOException {
-		new ProgramLoginCore(req, resp).process();
+		try {
+			new ProgramLoginCore(req, resp).process();
+		} catch (LoginNotPermitted e) {
+			// This should have been prevented by the test for loginDisabled()
+			throw new ServletException(e);
+		}
 	}
 
 	@Override
 	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
 			throws ServletException, IOException {
-		new ProgramLoginCore(req, resp).process();
+		doGet(req, resp);
 	}
 
 	static class ProgramLoginCore {
@@ -50,6 +56,8 @@ public class ProgramLogin extends HttpServlet {
 				+ " parameter is required.";
 		private static final String MESSAGE_WRONG_USER_OR_PASSWORD = PARAM_EMAIL_ADDRESS
 				+ " or " + PARAM_PASSWORD + " is incorrect.";
+		private static final String MESSAGE_LOGIN_DISABLED = "User logins are "
+				+ "temporarily disabled while the system is being maintained.";
 		private static final String MESSAGE_NEED_NEW_PASSWORD = "first-time login: "
 				+ PARAM_NEW_PASSWORD + " parameter is required.";
 		private static final String MESSAGE_NEW_PASSWORD_NOT_NEEDED = "not first-time login: "
@@ -90,7 +98,7 @@ public class ProgramLogin extends HttpServlet {
 					.getAccountForInternalAuth(this.emailAddress);
 		}
 
-		void process() throws IOException {
+		void process() throws IOException, LoginNotPermitted {
 			if (emailAddress.isEmpty()) {
 				sendError(MESSAGE_NEED_EMAIL_ADDRESS);
 				return;
@@ -104,6 +112,11 @@ public class ProgramLogin extends HttpServlet {
 				return;
 			}
 
+			if (loginDisabled()) {
+				sendError(MESSAGE_LOGIN_DISABLED);
+				return;
+			}
+
 			if (!isPasswordChangeRequired()) {
 				if (!newPassword.isEmpty()) {
 					sendError(MESSAGE_NEW_PASSWORD_NOT_NEEDED);
@@ -147,6 +160,10 @@ public class ProgramLogin extends HttpServlet {
 			return auth.isCurrentPassword(userAccount, password);
 		}
 
+		private boolean loginDisabled() {
+			return !auth.isUserPermittedToLogin(userAccount);
+		}
+
 		private boolean newPasswordIsValidPasswordLength() {
 			return (newPassword.length() >= MIN_PASSWORD_LENGTH)
 					&& (newPassword.length() <= MAX_PASSWORD_LENGTH);
@@ -160,11 +177,11 @@ public class ProgramLogin extends HttpServlet {
 			return (userAccount.isPasswordChangeRequired());
 		}
 
-		private void recordLogin() {
+		private void recordLogin() throws LoginNotPermitted {
 			auth.recordLoginAgainstUserAccount(userAccount, INTERNAL);
 		}
 
-		private void recordLoginWithPasswordChange() {
+		private void recordLoginWithPasswordChange() throws LoginNotPermitted {
 			auth.recordNewPassword(userAccount, newPassword);
 			auth.recordLoginAgainstUserAccount(userAccount, INTERNAL);
 		}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/Authenticate.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/Authenticate.java
index 1949009e6..df1e5bbca 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/Authenticate.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/Authenticate.java
@@ -33,6 +33,7 @@ import edu.cornell.mannlib.vitro.webapp.controller.Controllers;
 import edu.cornell.mannlib.vitro.webapp.controller.VitroHttpServlet;
 import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
 import edu.cornell.mannlib.vitro.webapp.controller.authenticate.Authenticator;
+import edu.cornell.mannlib.vitro.webapp.controller.authenticate.Authenticator.LoginNotPermitted;
 import edu.cornell.mannlib.vitro.webapp.controller.authenticate.LoginInProcessFlag;
 import edu.cornell.mannlib.vitro.webapp.controller.authenticate.LoginRedirector;
 import edu.cornell.mannlib.vitro.webapp.controller.login.LoginProcessBean;
@@ -327,6 +328,11 @@ public class Authenticate extends VitroHttpServlet {
 			return;
 		}
 
+		if (!getAuthenticator(request).isUserPermittedToLogin(user)) {
+			bean.setMessage(Message.LOGIN_DISABLED);
+			return;
+		}
+
 		if (!getAuthenticator(request).isCurrentPassword(user, password)) {
 			bean.setMessage(Message.INCORRECT_PASSWORD);
 			return;
@@ -336,7 +342,13 @@ public class Authenticate extends VitroHttpServlet {
 		if (user.isPasswordChangeRequired()) {
 			transitionToForcedPasswordChange(request);
 		} else {
-			transitionToLoggedIn(request, user);
+			try {
+				transitionToLoggedIn(request, user);
+			} catch (LoginNotPermitted e) {
+				// This should have been caught by isUserPermittedToLogin()
+				bean.setMessage(Message.LOGIN_DISABLED);
+				return;
+			}
 		}
 	}
 
@@ -392,7 +404,13 @@ public class Authenticate extends VitroHttpServlet {
 		}
 
 		// New password is acceptable. Store it and go on.
-		transitionToLoggedIn(request, user, newPassword);
+		try {
+			transitionToLoggedIn(request, user, newPassword);
+		} catch (LoginNotPermitted e) {
+			// This should have been caught by isUserPermittedToLogin()
+			bean.setMessage(Message.LOGIN_DISABLED);
+			return;
+		}
 	}
 
 	/**
@@ -424,7 +442,7 @@ public class Authenticate extends VitroHttpServlet {
 	 * State change: all requirements are satisfied. Log them in.
 	 */
 	private void transitionToLoggedIn(HttpServletRequest request,
-			UserAccount user) {
+			UserAccount user) throws LoginNotPermitted {
 		log.debug("Completed login: " + user.getEmailAddress());
 		getAuthenticator(request).recordLoginAgainstUserAccount(user,
 				AuthenticationSource.INTERNAL);
@@ -435,7 +453,7 @@ public class Authenticate extends VitroHttpServlet {
 	 * log them in.
 	 */
 	private void transitionToLoggedIn(HttpServletRequest request,
-			UserAccount user, String newPassword) {
+			UserAccount user, String newPassword) throws LoginNotPermitted {
 		log.debug("Completed login: " + user.getEmailAddress()
 				+ ", password changed.");
 		getAuthenticator(request).recordNewPassword(user, newPassword);
@@ -477,9 +495,10 @@ public class Authenticate extends VitroHttpServlet {
 		response.sendRedirect(loginProcessPage);
 		return;
 	}
-	
+
 	/**
-	 * Exit: user has completed the login. Redirect appropriately and clear the bean.
+	 * Exit: user has completed the login. Redirect appropriately and clear the
+	 * bean.
 	 */
 	private void showLoginComplete(HttpServletResponse response,
 			VitroRequest vreq) throws IOException {
@@ -497,12 +516,11 @@ public class Authenticate extends VitroHttpServlet {
 	}
 
 	private LoginRedirector getLoginRedirector(VitroRequest vreq) {
-		String afterLoginUrl = LoginProcessBean.getBean(vreq).getAfterLoginUrl();
+		String afterLoginUrl = LoginProcessBean.getBean(vreq)
+				.getAfterLoginUrl();
 		return new LoginRedirector(vreq, afterLoginUrl);
 	}
 
-
-
 	/** Get a reference to the Authenticator. */
 	private Authenticator getAuthenticator(HttpServletRequest request) {
 		return Authenticator.getInstance(request);
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/login/LoginProcessBean.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/login/LoginProcessBean.java
index 80b28c2d8..5c4c5017c 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/login/LoginProcessBean.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/login/LoginProcessBean.java
@@ -125,6 +125,10 @@ public class LoginProcessBean {
 		public static final Message UNKNOWN_USERNAME = new Message(
 				"The email or password you entered is incorrect.", MLevel.ERROR);
 
+		public static final Message LOGIN_DISABLED = new Message(
+				"User logins are temporarily disabled while the system is being maintained.",
+				MLevel.ERROR);
+
 		public static final Message INCORRECT_PASSWORD = new Message(
 				"The email or password you entered is incorrect.", MLevel.ERROR);
 
diff --git a/webapp/test/edu/cornell/mannlib/vitro/webapp/controller/authenticate/AuthenticatorStub.java b/webapp/test/edu/cornell/mannlib/vitro/webapp/controller/authenticate/AuthenticatorStub.java
index f97d55032..84fba7c0d 100644
--- a/webapp/test/edu/cornell/mannlib/vitro/webapp/controller/authenticate/AuthenticatorStub.java
+++ b/webapp/test/edu/cornell/mannlib/vitro/webapp/controller/authenticate/AuthenticatorStub.java
@@ -20,7 +20,7 @@ import edu.cornell.mannlib.vitro.webapp.beans.UserAccount;
 public class AuthenticatorStub extends Authenticator {
 	public static final String FACTORY_ATTRIBUTE_NAME = AuthenticatorFactory.class
 			.getName();
-	
+
 	// ----------------------------------------------------------------------
 	// factory - store this in the context.
 	//
@@ -97,6 +97,11 @@ public class AuthenticatorStub extends Authenticator {
 		return usersByExternalAuthId.get(externalAuthId);
 	}
 
+	@Override
+	public boolean isUserPermittedToLogin(UserAccount userAccount) {
+		return true;
+	}
+
 	@Override
 	public boolean isCurrentPassword(UserAccount userAccount,
 			String clearTextPassword) {
diff --git a/webapp/web/templates/freemarker/body/login/adminLogin.ftl b/webapp/web/templates/freemarker/body/login/adminLogin.ftl
index 6bf7228f2..d8d856dea 100644
--- a/webapp/web/templates/freemarker/body/login/adminLogin.ftl
+++ b/webapp/web/templates/freemarker/body/login/adminLogin.ftl
@@ -13,6 +13,10 @@
         <#assign errorMessage = "No password supplied." />
     
     
+    <#if errorLoginDisabled??>
+        <#assign errorMessage = "User logins are temporarily disabled while the system is being maintained." />
+    
+    
     <#if errorLoginFailed??>
         <#assign errorMessage = "Email or Password was incorrect." />