NIHVIVO-3523 Write the PermissionSetsLoader.
This commit is contained in:
parent
a9c7b3fead
commit
20b7f694d1
2 changed files with 313 additions and 34 deletions
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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" ;
|
||||||
|
.
|
||||||
|
|
Loading…
Add table
Reference in a new issue