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;
|
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.ServletContext;
|
||||||
import javax.servlet.ServletContextEvent;
|
import javax.servlet.ServletContextEvent;
|
||||||
import javax.servlet.ServletContextListener;
|
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!
|
* 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
|
* On setup, check to see that the specified root user exists. If not, create
|
||||||
* can't create one, abort.
|
* it. If we can't create it, abort.
|
||||||
|
*
|
||||||
|
* If any other root users exist, warn about them.
|
||||||
*/
|
*/
|
||||||
public class RootUserPolicy implements PolicyIface {
|
public class RootUserPolicy implements PolicyIface {
|
||||||
private static final Log log = LogFactory.getLog(RootUserPolicy.class);
|
private static final Log log = LogFactory.getLog(RootUserPolicy.class);
|
||||||
|
@ -62,8 +68,10 @@ public class RootUserPolicy implements PolicyIface {
|
||||||
public static class Setup implements ServletContextListener {
|
public static class Setup implements ServletContextListener {
|
||||||
private ServletContext ctx;
|
private ServletContext ctx;
|
||||||
private StartupStatus ss;
|
private StartupStatus ss;
|
||||||
private String configRootEmail;
|
|
||||||
private UserAccountsDao uaDao;
|
private UserAccountsDao uaDao;
|
||||||
|
private String configuredRootUser;
|
||||||
|
private boolean configuredRootUserExists;
|
||||||
|
private TreeSet<String> otherRootUsers;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void contextInitialized(ServletContextEvent sce) {
|
public void contextInitialized(ServletContextEvent sce) {
|
||||||
|
@ -72,14 +80,23 @@ public class RootUserPolicy implements PolicyIface {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
uaDao = getUserAccountsDao();
|
uaDao = getUserAccountsDao();
|
||||||
configRootEmail = getRootEmailFromConfig();
|
configuredRootUser = getRootEmailFromConfig();
|
||||||
|
|
||||||
checkForWrongRootUser();
|
otherRootUsers = getEmailsOfAllRootUsers();
|
||||||
|
configuredRootUserExists = otherRootUsers
|
||||||
|
.remove(configuredRootUser);
|
||||||
|
|
||||||
if (rootUserExists()) {
|
if (configuredRootUserExists) {
|
||||||
ss.info(this, "root user is " + configRootEmail);
|
if (otherRootUsers.isEmpty()) {
|
||||||
|
informThatRootUserExists();
|
||||||
|
} else {
|
||||||
|
complainAboutMultipleRootUsers();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
createRootUser();
|
createRootUser();
|
||||||
|
if (!otherRootUsers.isEmpty()) {
|
||||||
|
complainAboutWrongRootUsers();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ServletPolicyList.addPolicy(ctx, new RootUserPolicy());
|
ServletPolicyList.addPolicy(ctx, new RootUserPolicy());
|
||||||
|
@ -110,37 +127,14 @@ public class RootUserPolicy implements PolicyIface {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkForWrongRootUser() {
|
private TreeSet<String> getEmailsOfAllRootUsers() {
|
||||||
UserAccount root = getRootUser();
|
TreeSet<String> rootUsers = new TreeSet<String>();
|
||||||
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() {
|
|
||||||
for (UserAccount ua : uaDao.getAllUserAccounts()) {
|
for (UserAccount ua : uaDao.getAllUserAccounts()) {
|
||||||
if (ua.isRootUser()) {
|
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.
|
* be forced to edit them. However, that's not in place yet.
|
||||||
*/
|
*/
|
||||||
private void createRootUser() {
|
private void createRootUser() {
|
||||||
String emailAddress = ConfigurationProperties.getBean(ctx)
|
if (!Authenticator.isValidEmailAddress(configuredRootUser)) {
|
||||||
.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)) {
|
|
||||||
throw new IllegalStateException("Value for '"
|
throw new IllegalStateException("Value for '"
|
||||||
+ PROPERTY_ROOT_USER_EMAIL
|
+ 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 - "
|
throw new IllegalStateException("Can't create root user - "
|
||||||
+ "an account already exists with email address '"
|
+ "an account already exists with email address '"
|
||||||
+ emailAddress + "'");
|
+ configuredRootUser + "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
UserAccount ua = new UserAccount();
|
UserAccount ua = new UserAccount();
|
||||||
ua.setEmailAddress(emailAddress);
|
ua.setEmailAddress(configuredRootUser);
|
||||||
ua.setFirstName("root");
|
ua.setFirstName("root");
|
||||||
ua.setLastName("user");
|
ua.setLastName("user");
|
||||||
ua.setMd5Password(Authenticator
|
ua.setMd5Password(Authenticator
|
||||||
|
@ -182,7 +168,36 @@ public class RootUserPolicy implements PolicyIface {
|
||||||
uaDao.insertUserAccount(ua);
|
uaDao.insertUserAccount(ua);
|
||||||
|
|
||||||
StartupStatus.getBean(ctx).info(this,
|
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
|
@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>
|
<url-pattern>/admin/login</url-pattern>
|
||||||
</servlet-mapping>
|
</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>
|
||||||
<servlet-name>logout</servlet-name>
|
<servlet-name>logout</servlet-name>
|
||||||
<servlet-class>edu.cornell.mannlib.vitro.webapp.controller.edit.Logout</servlet-class>
|
<servlet-class>edu.cornell.mannlib.vitro.webapp.controller.edit.Logout</servlet-class>
|
||||||
|
|
Loading…
Add table
Reference in a new issue