NIHVIVO-2299 Create the root user policy and changes to deploy.properties

This commit is contained in:
j2blake 2011-06-08 21:21:59 +00:00
parent 7a451c069c
commit f88be755cf
11 changed files with 260 additions and 5 deletions

View file

@ -114,8 +114,8 @@ deploy - Deploy the application directly into the Tomcat webapps directory.
message="${deploy.properties.file} must contain a value for VitroConnection.DataSource.username" /> message="${deploy.properties.file} must contain a value for VitroConnection.DataSource.username" />
<fail unless="VitroConnection.DataSource.password" <fail unless="VitroConnection.DataSource.password"
message="${deploy.properties.file} must contain a value for VitroConnection.DataSource.password" /> message="${deploy.properties.file} must contain a value for VitroConnection.DataSource.password" />
<fail unless="initialAdminUser" <fail unless="rootUser.emailAddress"
message="${deploy.properties.file} must contain a value for initialAdminUser" /> message="${deploy.properties.file} must contain a value for rootUser.emailAddress" />
</target> </target>

View file

@ -83,11 +83,11 @@ VitroConnection.DataSource.validationQuery = SELECT 1
# vitro.local.solr.url = # vitro.local.solr.url =
# #
# The name of your first admin user for the Vitro application. The password # The email address of the root user for the VIVO application. The password
# for this user is initially set to "defaultAdmin", but you will be asked to # for this user is initially set to "rootPassword", but you will be asked to
# change the password the first time you log in. # change the password the first time you log in.
# #
initialAdminUser = defaultAdmin rootUser.emailAddress = root@myDomain.com
# #
# How is a logged-in user associated with a particular Individual? One way is # How is a logged-in user associated with a particular Individual? One way is

View file

@ -24,6 +24,7 @@ import edu.cornell.mannlib.vitro.webapp.beans.Individual;
import edu.cornell.mannlib.vitro.webapp.beans.SelfEditingConfiguration; import edu.cornell.mannlib.vitro.webapp.beans.SelfEditingConfiguration;
import edu.cornell.mannlib.vitro.webapp.beans.UserAccount; import edu.cornell.mannlib.vitro.webapp.beans.UserAccount;
import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao; import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao;
import edu.cornell.mannlib.vitro.webapp.dao.UserAccountsDao;
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory; import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
/** /**
@ -49,6 +50,7 @@ public class CommonIdentifierBundleFactory implements IdentifierBundleFactory {
ArrayIdentifierBundle bundle = new ArrayIdentifierBundle(); ArrayIdentifierBundle bundle = new ArrayIdentifierBundle();
bundle.addAll(createUserIdentifiers(req)); bundle.addAll(createUserIdentifiers(req));
bundle.addAll(createRootUserIdentifiers(req));
bundle.addAll(createRoleLevelIdentifiers(req)); bundle.addAll(createRoleLevelIdentifiers(req));
bundle.addAll(createBlacklistOrAssociatedIndividualIdentifiers(req)); bundle.addAll(createBlacklistOrAssociatedIndividualIdentifiers(req));
@ -68,6 +70,16 @@ public class CommonIdentifierBundleFactory implements IdentifierBundleFactory {
} }
} }
private Collection<? extends Identifier> createRootUserIdentifiers(
HttpServletRequest req) {
UserAccount user = LoginStatusBean.getCurrentUser(req);
if (isRootUser(user)) {
return Collections.singleton(new IsRootUser());
} else {
return Collections.emptySet();
}
}
/** /**
* Create an identifier that shows the role level of the current user, or * Create an identifier that shows the role level of the current user, or
* PUBLIC if the user is not logged in. * PUBLIC if the user is not logged in.
@ -131,6 +143,25 @@ public class CommonIdentifierBundleFactory implements IdentifierBundleFactory {
return individuals; return individuals;
} }
/**
* Is this user a root user?
*/
private boolean isRootUser(UserAccount user) {
if (user == null) {
return false;
}
WebappDaoFactory wdf = (WebappDaoFactory) context
.getAttribute("webappDaoFactory");
if (wdf == null) {
log.error("Could not get a WebappDaoFactory from the ServletContext");
return false;
}
UserAccountsDao uaDao = wdf.getUserAccountsDao();
return uaDao.isRootUser(user);
}
@Override @Override
public String toString() { public String toString() {
return this.getClass().getSimpleName() + " - " + hashCode(); return this.getClass().getSimpleName() + " - " + hashCode();

View file

@ -0,0 +1,21 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.auth.identifier.common;
import edu.cornell.mannlib.vitro.webapp.auth.identifier.Identifier;
import edu.cornell.mannlib.vitro.webapp.auth.identifier.IdentifierBundle;
/**
* The current user is a root user.
*/
public class IsRootUser extends AbstractCommonIdentifier implements Identifier {
public static boolean isRootUser(IdentifierBundle ids) {
return !getIdentifiersForClass(ids, IsRootUser.class).isEmpty();
}
@Override
public String toString() {
return "IsRootUser";
}
}

View file

@ -0,0 +1,164 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.auth.policy;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.hp.hpl.jena.ontology.OntModel;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.shared.Lock;
import com.hp.hpl.jena.vocabulary.RDF;
import edu.cornell.mannlib.vitro.webapp.auth.identifier.IdentifierBundle;
import edu.cornell.mannlib.vitro.webapp.auth.identifier.common.IsRootUser;
import edu.cornell.mannlib.vitro.webapp.auth.policy.ifaces.Authorization;
import edu.cornell.mannlib.vitro.webapp.auth.policy.ifaces.PolicyDecision;
import edu.cornell.mannlib.vitro.webapp.auth.policy.ifaces.PolicyIface;
import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.ifaces.RequestedAction;
import edu.cornell.mannlib.vitro.webapp.beans.UserAccount;
import edu.cornell.mannlib.vitro.webapp.beans.UserAccount.Status;
import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties;
import edu.cornell.mannlib.vitro.webapp.controller.authenticate.Authenticator;
import edu.cornell.mannlib.vitro.webapp.dao.UserAccountsDao;
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
import edu.cornell.mannlib.vitro.webapp.dao.jena.ModelContext;
import edu.cornell.mannlib.vitro.webapp.servlet.setup.AbortStartup;
/**
* 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.
*/
public class RootUserPolicy implements PolicyIface {
private static final Log log = LogFactory.getLog(RootUserPolicy.class);
private static final String PROPERTY_ROOT_USER_EMAIL = "rootUser.emailAddress";
private static final String ROOT_USER_INITIAL_PASSWORD = "rootPassword";
/**
* This is the entire policy. If you are a root user, you are authorized.
*/
@Override
public PolicyDecision isAuthorized(IdentifierBundle whoToAuth,
RequestedAction whatToAuth) {
if (IsRootUser.isRootUser(whoToAuth)) {
return new BasicPolicyDecision(Authorization.AUTHORIZED,
"RootUserPolicy: approved");
} else {
return new BasicPolicyDecision(Authorization.INCONCLUSIVE,
"not root user");
}
}
// ----------------------------------------------------------------------
// Setup class
// ----------------------------------------------------------------------
public static class Setup implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext ctx = sce.getServletContext();
if (AbortStartup.isStartupAborted(ctx)) {
return;
}
try {
UserAccountsDao uaDao = getUserAccountsDao(ctx);
OntModel userAccountsModel = getUserAccountsModel(ctx);
if (!rootUserExists(uaDao)) {
createRootUser(ctx, uaDao, userAccountsModel);
}
ServletPolicyList.addPolicy(ctx, new RootUserPolicy());
} catch (Exception e) {
log.error("could not run " + this.getClass().getSimpleName()
+ ": " + e);
AbortStartup.abortStartup(ctx);
throw new RuntimeException(e);
}
}
private UserAccountsDao getUserAccountsDao(ServletContext ctx) {
WebappDaoFactory wadf = (WebappDaoFactory) ctx
.getAttribute("webappDaoFactory");
if (wadf == null) {
throw new IllegalStateException(
"No webappDaoFactory on the servlet context");
}
return wadf.getUserAccountsDao();
}
private OntModel getUserAccountsModel(ServletContext ctx) {
return ModelContext.getBaseOntModelSelector(ctx)
.getUserAccountsModel();
}
private boolean rootUserExists(UserAccountsDao uaDao) {
for (UserAccount ua : uaDao.getAllUserAccounts()) {
if (uaDao.isRootUser(ua)) {
return true;
}
}
return false;
}
/**
* TODO The first and last name should be left blank, so the user will
* be forced to edit them. However, that's not in place yet.
*/
private void createRootUser(ServletContext ctx, UserAccountsDao uaDao,
OntModel userAccountsModel) {
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 (null != uaDao.getUserAccountByEmail(emailAddress)) {
throw new IllegalStateException("Can't create root user - "
+ "an account already exists with email address '"
+ emailAddress + "'");
}
UserAccount ua = new UserAccount();
ua.setEmailAddress(emailAddress);
ua.setFirstName("root");
ua.setLastName("user");
ua.setMd5Password(Authenticator
.applyMd5Encoding(ROOT_USER_INITIAL_PASSWORD));
ua.setPasswordChangeRequired(true);
ua.setStatus(Status.ACTIVE);
uaDao.insertUserAccount(ua);
userAccountsModel.enterCriticalSection(Lock.WRITE);
try {
Resource r = userAccountsModel.getResource(ua.getUri());
Resource t = userAccountsModel
.getResource(VitroVocabulary.USERACCOUNT_ROOT_USER);
userAccountsModel.add(r, RDF.type, t);
} finally {
userAccountsModel.leaveCriticalSection();
}
log.info("Created root user as '" + emailAddress + "'");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
// Nothing to destroy
}
}
}

View file

@ -40,6 +40,11 @@ public interface UserAccountsDao {
*/ */
UserAccount getUserAccountByExternalAuthId(String externalAuthId); UserAccount getUserAccountByExternalAuthId(String externalAuthId);
/**
* Is this UserAccount a root user?
*/
boolean isRootUser(UserAccount userAccount);
/** /**
* Create a new UserAccount in the model. * Create a new UserAccount in the model.
* *

View file

@ -174,6 +174,7 @@ public class VitroVocabulary {
// =============== Vitro UserAccount and PermissionSet vocabulary =========== // =============== Vitro UserAccount and PermissionSet vocabulary ===========
public static final String USERACCOUNT = VITRO_AUTH + "UserAccount"; public static final String USERACCOUNT = VITRO_AUTH + "UserAccount";
public static final String USERACCOUNT_ROOT_USER = VITRO_AUTH + "RootUserAccount";
public static final String USERACCOUNT_EMAIL_ADDRESS = VITRO_AUTH + "emailAddress"; public static final String USERACCOUNT_EMAIL_ADDRESS = VITRO_AUTH + "emailAddress";
public static final String USERACCOUNT_FIRST_NAME = VITRO_AUTH + "firstName"; public static final String USERACCOUNT_FIRST_NAME = VITRO_AUTH + "firstName";
public static final String USERACCOUNT_LAST_NAME = VITRO_AUTH + "lastName"; public static final String USERACCOUNT_LAST_NAME = VITRO_AUTH + "lastName";

View file

@ -47,6 +47,11 @@ public class UserAccountsDaoFiltering extends BaseFiltering implements
return innerDao.getUserAccountByExternalAuthId(externalAuthId); return innerDao.getUserAccountByExternalAuthId(externalAuthId);
} }
@Override
public boolean isRootUser(UserAccount userAccount) {
return innerDao.isRootUser(userAccount);
}
@Override @Override
public String insertUserAccount(UserAccount userAccount) { public String insertUserAccount(UserAccount userAccount) {
return innerDao.insertUserAccount(userAccount); return innerDao.insertUserAccount(userAccount);

View file

@ -155,6 +155,7 @@ public class JenaBaseDaoCon {
/* ***************** User Account Model constants ***************** */ /* ***************** User Account Model constants ***************** */
protected OntClass USERACCOUNT = _constModel.createClass(VitroVocabulary.USERACCOUNT); protected OntClass USERACCOUNT = _constModel.createClass(VitroVocabulary.USERACCOUNT);
protected OntClass USERACCOUNT_ROOT_USER = _constModel.createClass(VitroVocabulary.USERACCOUNT_ROOT_USER);
protected DatatypeProperty USERACCOUNT_EMAIL_ADDRESS = _constModel.createDatatypeProperty(VitroVocabulary.USERACCOUNT_EMAIL_ADDRESS); protected DatatypeProperty USERACCOUNT_EMAIL_ADDRESS = _constModel.createDatatypeProperty(VitroVocabulary.USERACCOUNT_EMAIL_ADDRESS);
protected DatatypeProperty USERACCOUNT_FIRST_NAME = _constModel.createDatatypeProperty(VitroVocabulary.USERACCOUNT_FIRST_NAME); protected DatatypeProperty USERACCOUNT_FIRST_NAME = _constModel.createDatatypeProperty(VitroVocabulary.USERACCOUNT_FIRST_NAME);
protected DatatypeProperty USERACCOUNT_LAST_NAME = _constModel.createDatatypeProperty(VitroVocabulary.USERACCOUNT_LAST_NAME); protected DatatypeProperty USERACCOUNT_LAST_NAME = _constModel.createDatatypeProperty(VitroVocabulary.USERACCOUNT_LAST_NAME);

View file

@ -157,6 +157,21 @@ public class UserAccountsDaoJena extends JenaBaseDao implements UserAccountsDao
return getUserAccountByUri(userUri); return getUserAccountByUri(userUri);
} }
@Override
public boolean isRootUser(UserAccount userAccount) {
if (userAccount == null) {
return false;
}
getOntModel().enterCriticalSection(Lock.READ);
try {
OntResource r = getOntModel().getOntResource(userAccount.getUri());
return isResourceOfType(r, USERACCOUNT_ROOT_USER);
} finally {
getOntModel().leaveCriticalSection();
}
}
@Override @Override
public String insertUserAccount(UserAccount userAccount) { public String insertUserAccount(UserAccount userAccount) {
if (userAccount == null) { if (userAccount == null) {
@ -361,6 +376,13 @@ public class UserAccountsDaoJena extends JenaBaseDao implements UserAccountsDao
* There should already be a lock on the model when this is called. * There should already be a lock on the model when this is called.
*/ */
private boolean isResourceOfType(OntResource r, OntClass type) { private boolean isResourceOfType(OntResource r, OntClass type) {
if (r == null) {
return false;
}
if (type == null) {
return false;
}
StmtIterator stmts = getOntModel().listStatements(r, RDF.type, type); StmtIterator stmts = getOntModel().listStatements(r, RDF.type, type);
if (stmts.hasNext()) { if (stmts.hasNext()) {
stmts.close(); stmts.close();

View file

@ -48,6 +48,11 @@ public class UserAccountsDaoStub implements UserAccountsDao {
"UserAccountsDaoStub.getUserAccountByEmail() not implemented."); "UserAccountsDaoStub.getUserAccountByEmail() not implemented.");
} }
@Override
public boolean isRootUser(UserAccount userAccount) {
throw new RuntimeException("UserAccountsDao.isRootUser() not implemented.");
}
@Override @Override
public String insertUserAccount(UserAccount userAccount) { public String insertUserAccount(UserAccount userAccount) {
throw new RuntimeException( throw new RuntimeException(