NIHVIVO-3988 Change the handling of multiple root user accounts, and remove the back-door login mechanism.
This commit is contained in:
parent
2ecb46adb9
commit
f491a7c95c
3 changed files with 64 additions and 207 deletions
|
@ -2,6 +2,10 @@
|
|||
|
||||
package edu.cornell.mannlib.vitro.webapp.auth.policy;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletContextEvent;
|
||||
import javax.servlet.ServletContextListener;
|
||||
|
@ -26,8 +30,10 @@ import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus;
|
|||
/**
|
||||
* If the user has an IsRootUser identifier, they can do anything!
|
||||
*
|
||||
* On setup, check to see that there is a root user. If not, create one. If we
|
||||
* can't create one, abort.
|
||||
* On setup, check to see that the specified root user exists. If not, create
|
||||
* it. If we can't create it, abort.
|
||||
*
|
||||
* If any other root users exist, warn about them.
|
||||
*/
|
||||
public class RootUserPolicy implements PolicyIface {
|
||||
private static final Log log = LogFactory.getLog(RootUserPolicy.class);
|
||||
|
@ -62,8 +68,10 @@ public class RootUserPolicy implements PolicyIface {
|
|||
public static class Setup implements ServletContextListener {
|
||||
private ServletContext ctx;
|
||||
private StartupStatus ss;
|
||||
private String configRootEmail;
|
||||
private UserAccountsDao uaDao;
|
||||
private String configuredRootUser;
|
||||
private boolean configuredRootUserExists;
|
||||
private TreeSet<String> otherRootUsers;
|
||||
|
||||
@Override
|
||||
public void contextInitialized(ServletContextEvent sce) {
|
||||
|
@ -72,14 +80,23 @@ public class RootUserPolicy implements PolicyIface {
|
|||
|
||||
try {
|
||||
uaDao = getUserAccountsDao();
|
||||
configRootEmail = getRootEmailFromConfig();
|
||||
configuredRootUser = getRootEmailFromConfig();
|
||||
|
||||
checkForWrongRootUser();
|
||||
otherRootUsers = getEmailsOfAllRootUsers();
|
||||
configuredRootUserExists = otherRootUsers
|
||||
.remove(configuredRootUser);
|
||||
|
||||
if (rootUserExists()) {
|
||||
ss.info(this, "root user is " + configRootEmail);
|
||||
if (configuredRootUserExists) {
|
||||
if (otherRootUsers.isEmpty()) {
|
||||
informThatRootUserExists();
|
||||
} else {
|
||||
complainAboutMultipleRootUsers();
|
||||
}
|
||||
} else {
|
||||
createRootUser();
|
||||
if (!otherRootUsers.isEmpty()) {
|
||||
complainAboutWrongRootUsers();
|
||||
}
|
||||
}
|
||||
|
||||
ServletPolicyList.addPolicy(ctx, new RootUserPolicy());
|
||||
|
@ -110,37 +127,14 @@ public class RootUserPolicy implements PolicyIface {
|
|||
}
|
||||
}
|
||||
|
||||
private void checkForWrongRootUser() {
|
||||
UserAccount root = getRootUser();
|
||||
if (root == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String actualRootEmail = root.getEmailAddress();
|
||||
if (actualRootEmail.equals(configRootEmail)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ss.warning(
|
||||
this,
|
||||
"The deploy.properties file specifies a root user of '"
|
||||
+ configRootEmail
|
||||
+ "', but the system already contains a root user named '"
|
||||
+ actualRootEmail + "'. The user '"
|
||||
+ configRootEmail + "' will not be created.");
|
||||
}
|
||||
|
||||
private boolean rootUserExists() {
|
||||
return (getRootUser() != null);
|
||||
}
|
||||
|
||||
private UserAccount getRootUser() {
|
||||
private TreeSet<String> getEmailsOfAllRootUsers() {
|
||||
TreeSet<String> rootUsers = new TreeSet<String>();
|
||||
for (UserAccount ua : uaDao.getAllUserAccounts()) {
|
||||
if (ua.isRootUser()) {
|
||||
return ua;
|
||||
rootUsers.add(ua.getEmailAddress());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return rootUsers;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -148,29 +142,21 @@ public class RootUserPolicy implements PolicyIface {
|
|||
* be forced to edit them. However, that's not in place yet.
|
||||
*/
|
||||
private void createRootUser() {
|
||||
String emailAddress = ConfigurationProperties.getBean(ctx)
|
||||
.getProperty(PROPERTY_ROOT_USER_EMAIL);
|
||||
if (emailAddress == null) {
|
||||
throw new IllegalStateException(
|
||||
"deploy.properties must contain a value for '"
|
||||
+ PROPERTY_ROOT_USER_EMAIL + "'");
|
||||
}
|
||||
|
||||
if (!Authenticator.isValidEmailAddress(emailAddress)) {
|
||||
if (!Authenticator.isValidEmailAddress(configuredRootUser)) {
|
||||
throw new IllegalStateException("Value for '"
|
||||
+ PROPERTY_ROOT_USER_EMAIL
|
||||
+ "' is not a valid email address: '" + emailAddress
|
||||
+ "'");
|
||||
+ "' is not a valid email address: '"
|
||||
+ configuredRootUser + "'");
|
||||
}
|
||||
|
||||
if (null != uaDao.getUserAccountByEmail(emailAddress)) {
|
||||
if (null != uaDao.getUserAccountByEmail(configuredRootUser)) {
|
||||
throw new IllegalStateException("Can't create root user - "
|
||||
+ "an account already exists with email address '"
|
||||
+ emailAddress + "'");
|
||||
+ configuredRootUser + "'");
|
||||
}
|
||||
|
||||
UserAccount ua = new UserAccount();
|
||||
ua.setEmailAddress(emailAddress);
|
||||
ua.setEmailAddress(configuredRootUser);
|
||||
ua.setFirstName("root");
|
||||
ua.setLastName("user");
|
||||
ua.setMd5Password(Authenticator
|
||||
|
@ -182,7 +168,36 @@ public class RootUserPolicy implements PolicyIface {
|
|||
uaDao.insertUserAccount(ua);
|
||||
|
||||
StartupStatus.getBean(ctx).info(this,
|
||||
"Created root user as '" + emailAddress + "'");
|
||||
"Created root user '" + configuredRootUser + "'");
|
||||
}
|
||||
|
||||
private void informThatRootUserExists() {
|
||||
ss.info(this, "Root user is " + configuredRootUser);
|
||||
}
|
||||
|
||||
private void complainAboutMultipleRootUsers() {
|
||||
for (String other : otherRootUsers) {
|
||||
ss.warning(this, "deploy.properties specifies '"
|
||||
+ configuredRootUser + "' as the value for '"
|
||||
+ PROPERTY_ROOT_USER_EMAIL
|
||||
+ "', but the system also contains this root user: "
|
||||
+ other);
|
||||
}
|
||||
ss.warning(this, "For security, "
|
||||
+ "it is best to delete unneeded root user accounts.");
|
||||
}
|
||||
|
||||
private void complainAboutWrongRootUsers() {
|
||||
for (String other : otherRootUsers) {
|
||||
ss.warning(this, "deploy.properties specifies '"
|
||||
+ configuredRootUser + "' as the value for '"
|
||||
+ PROPERTY_ROOT_USER_EMAIL
|
||||
+ "', but the system contains this root user instead: "
|
||||
+ other);
|
||||
}
|
||||
ss.warning(this, "Creating root user '" + configuredRootUser + "'");
|
||||
ss.warning(this, "For security, "
|
||||
+ "it is best to delete unneeded root user accounts.");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,149 +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 java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Date;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.httpclient.HttpStatus;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import com.ibm.icu.text.SimpleDateFormat;
|
||||
|
||||
import edu.cornell.mannlib.vedit.beans.LoginStatusBean.AuthenticationSource;
|
||||
import edu.cornell.mannlib.vitro.webapp.beans.UserAccount;
|
||||
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder;
|
||||
import edu.cornell.mannlib.vitro.webapp.dao.UserAccountsDao;
|
||||
|
||||
/**
|
||||
* Back door to log in as the root user.
|
||||
*
|
||||
* If the classpath contains a file called friend.xml, which contains a magic
|
||||
* line (see below) with today's date, or some date less than a week ago, then
|
||||
* you are logged in as root.
|
||||
*
|
||||
* If anything else, return a 404.
|
||||
*/
|
||||
public class FriendController extends HttpServlet {
|
||||
private static final Log log = LogFactory.getLog(FriendController.class);
|
||||
|
||||
private static final long MILLIS_IN_A_WEEK = 7L * 24L * 60L * 60L * 1000L;
|
||||
|
||||
// To be valid XML, it could look like this: <date value="2011-07-01" />
|
||||
// but we don't care as long as it contains this: 9999-99-99
|
||||
private static final String DATE_PATTERN = "\\d\\d\\d\\d-\\d\\d-\\d\\d";
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
|
||||
throws ServletException, IOException {
|
||||
try {
|
||||
if (fileContentsAreAcceptable()) {
|
||||
writeWarningToTheLog(req);
|
||||
loginAsRootUser(req);
|
||||
redirectToHomePage(resp);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug("problem: " + e.getMessage());
|
||||
resp.sendError(HttpStatus.SC_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean fileContentsAreAcceptable() throws Exception {
|
||||
InputStream stream = null;
|
||||
try {
|
||||
stream = openTheFile();
|
||||
String string = readFromStream(stream);
|
||||
return checkDateString(string);
|
||||
} finally {
|
||||
if (stream != null) {
|
||||
stream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private InputStream openTheFile() throws Exception {
|
||||
InputStream stream = this.getClass().getClassLoader()
|
||||
.getResourceAsStream("/friend.xml");
|
||||
if (stream == null) {
|
||||
throw new Exception("can't find the file.");
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
private String readFromStream(InputStream stream) throws IOException {
|
||||
byte[] buffer = new byte[1024];
|
||||
int howMany = stream.read(buffer);
|
||||
|
||||
if (howMany == -1) {
|
||||
return "";
|
||||
} else {
|
||||
return new String(buffer, 0, howMany);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkDateString(String string) throws Exception {
|
||||
long date = parseDateFromFile(string);
|
||||
compareAgainstDateRange(date);
|
||||
return true;
|
||||
}
|
||||
|
||||
private long parseDateFromFile(String string) throws Exception {
|
||||
Pattern p = Pattern.compile(DATE_PATTERN);
|
||||
Matcher m = p.matcher(string);
|
||||
|
||||
if (!m.find()) {
|
||||
throw new Exception("no date string in the file.");
|
||||
}
|
||||
|
||||
return new SimpleDateFormat("yyyy-MM-dd").parse(m.group()).getTime();
|
||||
}
|
||||
|
||||
private void compareAgainstDateRange(long date) throws Exception {
|
||||
long now = new Date().getTime();
|
||||
long then = now - MILLIS_IN_A_WEEK;
|
||||
if ((date > now) || (date < then)) {
|
||||
throw new Exception("date out of range.");
|
||||
}
|
||||
}
|
||||
|
||||
private void writeWarningToTheLog(HttpServletRequest req) {
|
||||
log.warn("LOGGING IN VIA FRIEND FROM ADDR=" + req.getRemoteAddr()
|
||||
+ ", PORT=" + req.getRemotePort() + ", HOST="
|
||||
+ req.getRemoteHost() + ", USER=" + req.getRemoteUser());
|
||||
}
|
||||
|
||||
private void loginAsRootUser(HttpServletRequest req) throws Exception {
|
||||
UserAccount rootUser = getRootUser(req);
|
||||
Authenticator.getInstance(req).recordLoginAgainstUserAccount(rootUser,
|
||||
AuthenticationSource.INTERNAL);
|
||||
}
|
||||
|
||||
private UserAccount getRootUser(HttpServletRequest req) throws Exception {
|
||||
UserAccountsDao uaDao = new VitroRequest(req).getWebappDaoFactory()
|
||||
.getUserAccountsDao();
|
||||
|
||||
for (UserAccount ua : uaDao.getAllUserAccounts()) {
|
||||
if (ua.isRootUser()) {
|
||||
return ua;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception("couldn't find root user.");
|
||||
}
|
||||
|
||||
private void redirectToHomePage(HttpServletResponse resp)
|
||||
throws IOException {
|
||||
resp.sendRedirect(UrlBuilder.getUrl("/"));
|
||||
}
|
||||
|
||||
}
|
|
@ -1042,15 +1042,6 @@
|
|||
<url-pattern>/admin/login</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>friend</servlet-name>
|
||||
<servlet-class>edu.cornell.mannlib.vitro.webapp.controller.authenticate.FriendController</servlet-class>
|
||||
</servlet>
|
||||
<servlet-mapping>
|
||||
<servlet-name>friend</servlet-name>
|
||||
<url-pattern>/admin/friend</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>logout</servlet-name>
|
||||
<servlet-class>edu.cornell.mannlib.vitro.webapp.controller.edit.Logout</servlet-class>
|
||||
|
|
Loading…
Add table
Reference in a new issue