NIHVIVO-3523 Write the PermissionSetsLoader.

This commit is contained in:
j2blake 2011-12-20 21:24:52 +00:00
parent a9c7b3fead
commit 20b7f694d1
2 changed files with 313 additions and 34 deletions

View file

@ -4,6 +4,11 @@ package edu.cornell.mannlib.vitro.webapp.auth.permissions;
import static edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary.VITRO_AUTH; import static edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary.VITRO_AUTH;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener; import javax.servlet.ServletContextListener;
@ -12,12 +17,24 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import com.hp.hpl.jena.ontology.OntModel; import com.hp.hpl.jena.ontology.OntModel;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.Property; import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.Resource; import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.Selector;
import com.hp.hpl.jena.rdf.model.SimpleSelector;
import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.rdf.model.StmtIterator;
import com.hp.hpl.jena.shared.Lock; import com.hp.hpl.jena.shared.Lock;
import com.hp.hpl.jena.util.iterator.ClosableIterator;
import com.hp.hpl.jena.vocabulary.RDF;
import com.hp.hpl.jena.vocabulary.RDFS;
import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties; import edu.cornell.mannlib.vitro.webapp.beans.PermissionSet;
import edu.cornell.mannlib.vitro.webapp.beans.UserAccount;
import edu.cornell.mannlib.vitro.webapp.dao.UserAccountsDao;
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary; 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.dao.jena.ModelContext;
import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus; import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus;
@ -26,12 +43,14 @@ import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus;
* *
* The UserAccounts model must be created before this runs. * The UserAccounts model must be created before this runs.
* *
* For now, we just use the four hard-coded "roles". * The PermissionRegistry must be created before this runs.
*/ */
public class PermissionSetsLoader implements ServletContextListener { public class PermissionSetsLoader implements ServletContextListener {
private static final Log log = LogFactory private static final Log log = LogFactory
.getLog(PermissionSetsLoader.class); .getLog(PermissionSetsLoader.class);
public static final String FILE_OF_PERMISSION_SETS_INFO = "/WEB-INF/resources/permission_config.n3";
public static final String URI_SELF_EDITOR = VITRO_AUTH + "SELF_EDITOR"; public static final String URI_SELF_EDITOR = VITRO_AUTH + "SELF_EDITOR";
public static final String URI_EDITOR = VITRO_AUTH + "EDITOR"; public static final String URI_EDITOR = VITRO_AUTH + "EDITOR";
public static final String URI_CURATOR = VITRO_AUTH + "CURATOR"; public static final String URI_CURATOR = VITRO_AUTH + "CURATOR";
@ -43,19 +62,10 @@ public class PermissionSetsLoader implements ServletContextListener {
StartupStatus ss = StartupStatus.getBean(ctx); StartupStatus ss = StartupStatus.getBean(ctx);
try { try {
String ns = ConfigurationProperties.getBean(ctx).getProperty( new Loader(this, ctx, ss).load();
"Vitro.defaultNamespace"); new SmokeTester(this, ctx, ss).test();
OntModel model = ModelContext.getBaseOntModelSelector(ctx)
.getUserAccountsModel();
ModelWrapper wrapper = new ModelWrapper(model);
wrapper.createPermissionSet(URI_SELF_EDITOR, "Self Editor");
wrapper.createPermissionSet(URI_EDITOR, "Editor");
wrapper.createPermissionSet(URI_CURATOR, "Curator");
wrapper.createPermissionSet(URI_DBA, "Site Admin");
} catch (Exception e) { } catch (Exception e) {
ss.fatal(this, "could not run PermissionSetsLoader" + e); ss.fatal(this, "Failed to load the PermissionSets", e);
} }
} }
@ -64,32 +74,284 @@ public class PermissionSetsLoader implements ServletContextListener {
// Nothing to tear down. // Nothing to tear down.
} }
private static class ModelWrapper { // ----------------------------------------------------------------------
private final OntModel model; // Loader class
// ----------------------------------------------------------------------
private final Property typeProperty; private static class Loader {
private final Property labelProperty; private static final int MAX_STATEMENTS_IN_WARNING = 5;
private final Resource permissionSet;
public ModelWrapper(OntModel model) { private ServletContextListener listener;
this.model = model; private final ServletContext ctx;
private final StartupStatus ss;
private final OntModel userAccountsModel;
private final Property permissionSetType;
private Model modelFromFile;
private Model filteredModel;
private int howManyNewPermissionSets;
private int howManyOldPermissionSets;
public Loader(ServletContextListener listener, ServletContext ctx,
StartupStatus ss) {
this.listener = listener;
this.ctx = ctx;
this.ss = ss;
this.userAccountsModel = ModelContext.getBaseOntModelSelector(ctx)
.getUserAccountsModel();
this.permissionSetType = this.userAccountsModel
.getProperty(VitroVocabulary.PERMISSIONSET);
typeProperty = model.createProperty(VitroVocabulary.RDF_TYPE);
labelProperty = model.createProperty(VitroVocabulary.LABEL);
permissionSet = model.createResource(VitroVocabulary.PERMISSIONSET);
} }
public void createPermissionSet(String uri, String label) { public void load() {
model.enterCriticalSection(Lock.WRITE);
try { try {
Resource r = model.createResource(uri); createModelFromFile();
model.add(r, typeProperty, permissionSet); filterModelFromFile();
model.add(r, labelProperty, label); checkForLeftoverStatements();
log.debug("Created permission set: '" + uri + "', '" + label removeExistingPermissionSetsFromUserAccountsModel();
+ "'"); addNewStatementsToUserAccountsModel();
} finally {
model.leaveCriticalSection(); ss.info(listener, buildInfoMessage());
} catch (LoaderException e) {
Throwable cause = e.getCause();
if (cause == null) {
ss.warning(listener, e.getMessage());
} else {
ss.warning(listener, e.getMessage(), e.getCause());
}
} }
} }
private void createModelFromFile() throws LoaderException {
InputStream stream = ctx
.getResourceAsStream(FILE_OF_PERMISSION_SETS_INFO);
if (stream == null) {
throw new LoaderException("The permission sets config file "
+ "doesn't exist in the servlet context: '"
+ FILE_OF_PERMISSION_SETS_INFO + "'");
}
try {
modelFromFile = ModelFactory.createDefaultModel();
modelFromFile.read(stream, null, "N3");
} finally {
try {
stream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
log.debug("Loaded " + modelFromFile.size() + " statements");
}
/**
* Move all statements that relate to PermissionSets from the loaded
* model to the filtered model.
*/
private void filterModelFromFile() {
filteredModel = ModelFactory.createDefaultModel();
for (Resource r : iterable(modelFromFile.listResourcesWithProperty(
RDF.type, permissionSetType))) {
moveStatementsToFilteredModel(r);
howManyNewPermissionSets++;
}
log.debug("Filtered " + filteredModel.size() + " statements for "
+ howManyNewPermissionSets + " PermissionSets; "
+ modelFromFile.size() + " extraneous statements.");
}
/**
* Move the statements about this PermissionSet from the loaded model to
* the filtered model.
*/
private void moveStatementsToFilteredModel(Resource ps) {
Selector sel = new SimpleSelector(ps, null, (String) null);
for (Statement stmt : iterable(modelFromFile.listStatements(sel))) {
filteredModel.add(stmt);
modelFromFile.remove(stmt);
}
}
/**
* Complain about any statements that were not moved to the filtered
* model.
*/
private void checkForLeftoverStatements() {
List<Statement> list = iterable(modelFromFile.listStatements());
if (list.isEmpty()) {
return;
}
String message = "The PermissionSets configuration file contained "
+ list.size()
+ " statements that didn't describe PermissionSets: ";
for (int i = 0; i < Math
.min(list.size(), MAX_STATEMENTS_IN_WARNING); i++) {
Statement stmt = list.get(i);
message += "(" + stmt.asTriple() + ") ";
}
if (list.size() > MAX_STATEMENTS_IN_WARNING) {
message += ", ...";
}
ss.warning(listener, message);
}
private void removeExistingPermissionSetsFromUserAccountsModel() {
userAccountsModel.enterCriticalSection(Lock.WRITE);
try {
for (Resource r : iterable(userAccountsModel
.listResourcesWithProperty(RDF.type, permissionSetType))) {
Selector sel = new SimpleSelector(r, null, (String) null);
StmtIterator stmts = userAccountsModel.listStatements(sel);
userAccountsModel.remove(stmts);
howManyOldPermissionSets++;
}
} finally {
userAccountsModel.leaveCriticalSection();
}
log.debug("Deleted " + howManyOldPermissionSets
+ " old PermissionSets from the user model.");
}
private void addNewStatementsToUserAccountsModel() {
userAccountsModel.enterCriticalSection(Lock.WRITE);
try {
userAccountsModel.add(filteredModel);
} finally {
userAccountsModel.leaveCriticalSection();
}
}
private String buildInfoMessage() {
String message = "Loaded " + howManyNewPermissionSets
+ " PermissionSets: ";
Selector sel = new SimpleSelector(null, RDFS.label, (String) null);
for (Statement stmt : iterable(filteredModel.listStatements(sel))) {
String label = stmt.getObject().asLiteral().getString();
message += "'" + label + "' ";
}
return message;
}
private <T> List<T> iterable(ClosableIterator<T> iterator) {
List<T> list = new ArrayList<T>();
try {
while (iterator.hasNext()) {
list.add(iterator.next());
}
} finally {
iterator.close();
}
return list;
}
}
// ----------------------------------------------------------------------
// SmokeTester class
// ----------------------------------------------------------------------
private static class SmokeTester {
private ServletContextListener listener;
private final ServletContext ctx;
private final StartupStatus ss;
private final UserAccountsDao uaDao;
public SmokeTester(ServletContextListener listener, ServletContext ctx,
StartupStatus ss) {
this.listener = listener;
this.ctx = ctx;
this.ss = ss;
WebappDaoFactory wadf = (WebappDaoFactory) ctx
.getAttribute("webappDaoFactory");
if (wadf == null) {
throw new IllegalStateException(
"No webappDaoFactory on the servlet context");
}
this.uaDao = wadf.getUserAccountsDao();
}
public void test() {
checkForPermissionSetsWithoutLabels();
checkForReferencesToNonexistentPermissionSets();
checkForReferencesToNonexistentPermissions();
warnIfNoDefaultPermissionSetsForNewUsers();
}
private void checkForPermissionSetsWithoutLabels() {
for (PermissionSet ps : uaDao.getAllPermissionSets()) {
if (ps.getLabel().isEmpty()) {
ss.warning(listener, "This PermissionSet has no label: "
+ ps.getUri());
}
}
}
private void checkForReferencesToNonexistentPermissionSets() {
for (UserAccount user : uaDao.getAllUserAccounts()) {
for (String psUri : user.getPermissionSetUris()) {
if (uaDao.getPermissionSetByUri(psUri) == null) {
ss.warning(listener, "The user '" + user.getFirstName()
+ " " + user.getLastName()
+ "' has the PermissionSet '" + psUri
+ "', but the PermissionSet doesn't exist.");
}
}
}
}
private void checkForReferencesToNonexistentPermissions() {
PermissionRegistry registry = PermissionRegistry.getRegistry(ctx);
for (PermissionSet ps : uaDao.getAllPermissionSets()) {
for (String pUri : ps.getPermissionUris()) {
if (!registry.isPermission(pUri)) {
ss.warning(listener,
"The PermissionSet '" + ps.getLabel()
+ "' has the Permission '" + pUri
+ "', but the Permission "
+ "is not found in the registry.");
}
}
}
}
private void warnIfNoDefaultPermissionSetsForNewUsers() {
for (PermissionSet ps : uaDao.getAllPermissionSets()) {
if (ps.isDefaultForNewUsers()) {
return;
}
}
ss.warning(listener, "No PermissionSet has been declared to be a "
+ "Default PermissionSet for new users.");
}
}
// ----------------------------------------------------------------------
// Handy dandy exception.
// ----------------------------------------------------------------------
private static class LoaderException extends Exception {
public LoaderException(String message) {
super(message);
}
public LoaderException(String message, Throwable cause) {
super(message, cause);
}
} }
} }

View file

@ -1,10 +1,27 @@
# $This file is distributed under the terms of the license in /doc/license.txt$ # $This file is distributed under the terms of the license in /doc/license.txt$
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix auth: <http://vitro.mannlib.cornell.edu/ns/vitro/authorization#> . @prefix auth: <http://vitro.mannlib.cornell.edu/ns/vitro/authorization#> .
@prefix simplePermission: <java://edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission#> . @prefix simplePermission: <java:edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission#> .
auth:ADMIN auth:ADMIN
a auth:PermissionSet ; a auth:PermissionSet ;
rdfs:label "Site Admin" ;
auth:hasPermission simplePermission:ManageMenus ; auth:hasPermission simplePermission:ManageMenus ;
. .
auth:CURATOR
a auth:PermissionSet ;
rdfs:label "Curator" ;
.
auth:EDITOR
a auth:PermissionSet ;
rdfs:label "Editor" ;
.
auth:SELF_EDITOR
a auth:PermissionSet ;
a auth:DefaultPermissionSetForNewUsers ;
rdfs:label "Self Editor" ;
.