From 9d89fc291c1d054100cc3ec76cc011e5e2adc8bd Mon Sep 17 00:00:00 2001 From: j2blake Date: Sat, 17 Dec 2011 23:48:29 +0000 Subject: [PATCH] NIHVIVO-3522 Changed to the new PermissionSet URIs, with updater. --- .../auth/permissions/PermissionRegistry.java | 78 +++-- .../permissions/PermissionSetsLoader.java | 12 +- .../auth/permissions/SimplePermission.java | 17 +- .../setup/UpdatePermissionSetUris.java | 270 ++++++++++++++++++ .../WEB-INF/resources/startup_listeners.txt | 9 +- 5 files changed, 349 insertions(+), 37 deletions(-) create mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/UpdatePermissionSetUris.java diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/PermissionRegistry.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/PermissionRegistry.java index f45a13dd0..d402a207f 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/PermissionRegistry.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/PermissionRegistry.java @@ -2,31 +2,54 @@ package edu.cornell.mannlib.vitro.webapp.auth.permissions; +import java.util.HashMap; +import java.util.Map; + import javax.servlet.ServletContext; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + /** * Holds a map of known Permission objects by URI. Resides in the * ServletContext. + * + * This is not thread-safe, so all Permissions should be added during context + * initialization. */ public class PermissionRegistry { - /** - * Get the registry from the context. If the context doesn't contain a - * registry yet, write a warning and return an immutable registry with no - * permissions. - */ - public static PermissionRegistry getRegistry(ServletContext ctx) { - throw new RuntimeException( - "PermissionRegistry.getBean not implemented."); - } + private static final Log log = LogFactory.getLog(PermissionRegistry.class); + + private static final String ATTRIBUTE_NAME = PermissionRegistry.class + .getName(); /** - * Create an empty registry and set it into the context. This should only be - * called from PermissionSetsLoader. + * Get the registry from the context. If the context doesn't contain a + * registry yet, create one. */ - protected static void setRegistry(ServletContext ctx, - PermissionRegistry registry) { - throw new RuntimeException( - "PermissionRegistry.setRegistry not implemented."); + public static PermissionRegistry getRegistry(ServletContext ctx) { + if (ctx == null) { + throw new NullPointerException("ctx may not be null."); + } + + Object o = ctx.getAttribute(ATTRIBUTE_NAME); + if (o instanceof PermissionRegistry) { + return (PermissionRegistry) o; + } + if (o != null) { + log.error("Error: PermissionRegistry was set to an " + + "invalid object: " + o); + } + + PermissionRegistry registry = new PermissionRegistry(); + ctx.setAttribute(ATTRIBUTE_NAME, registry); + return registry; + } + + private final Map permissionsMap = new HashMap(); + + private PermissionRegistry() { + // nothing to initialize; } /** @@ -34,24 +57,37 @@ public class PermissionRegistry { * already present, throw an IllegalStateException. */ public void addPermission(Permission p) { - throw new RuntimeException( - "PermissionRegistry.addPermission not implemented."); + if (p == null) { + throw new NullPointerException("p may not be null."); + } + + String uri = p.getUri(); + if (isPermission(uri)) { + throw new IllegalStateException( + "A Permission is already registered with this URI: '" + uri + + "'."); + } + + permissionsMap.put(uri, p); } /** * Is there already a Permission registered with this URI? */ public boolean isPermission(String uri) { - throw new RuntimeException( - "PermissionRegistry.isPermission not implemented."); + return permissionsMap.containsKey(uri); } /** * Get the permission that is registered with this URI. If there is no such * Permission, return a dummy Permission that always denies authorization. + * + * If you want to know whether an actual Permission has been registered at + * this URI, call isPermission() instead. */ public Permission getPermission(String uri) { - throw new RuntimeException( - "PermissionRegistry.getPermission not implemented."); + Permission p = permissionsMap.get(uri); + return (p == null) ? Permission.NOT_AUTHORIZED : p; } + } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/PermissionSetsLoader.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/PermissionSetsLoader.java index 7e8fb1d50..330383f50 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/PermissionSetsLoader.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/PermissionSetsLoader.java @@ -2,6 +2,8 @@ package edu.cornell.mannlib.vitro.webapp.auth.permissions; +import static edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary.VITRO_AUTH; + import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; @@ -30,11 +32,11 @@ public class PermissionSetsLoader implements ServletContextListener { private static final Log log = LogFactory .getLog(PermissionSetsLoader.class); - public static final String URI_SELF_EDITOR = "http://permissionSet-1"; - public static final String URI_EDITOR = "http://permissionSet-4"; - public static final String URI_CURATOR = "http://permissionSet-5"; - public static final String URI_DBA = "http://permissionSet-50"; - + 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_CURATOR = VITRO_AUTH + "CURATOR"; + public static final String URI_DBA = VITRO_AUTH + "ADMIN"; + @Override public void contextInitialized(ServletContextEvent sce) { ServletContext ctx = sce.getServletContext(); diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/SimplePermission.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/SimplePermission.java index a9acce512..73b7f6b8a 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/SimplePermission.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/SimplePermission.java @@ -3,11 +3,14 @@ package edu.cornell.mannlib.vitro.webapp.auth.permissions; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import edu.cornell.mannlib.vitro.webapp.auth.permissions.Permission; import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.Actions; import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.SimpleRequestedAction; import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.ifaces.RequestedAction; @@ -22,16 +25,17 @@ public class SimplePermission implements Permission { private static final String NAMESPACE = "java://" + SimplePermission.class.getName() + "#"; - private static final List allInstances = new ArrayList(); + private static final Map allInstances = new HashMap(); public static final SimplePermission MANAGE_MENUS = new SimplePermission( "ManageMenus"); public static List getAllInstances() { - return new ArrayList(allInstances); + return new ArrayList(allInstances.values()); } private final String localName; + private final String uri; public final RequestedAction ACTION; public final Actions ACTIONS; @@ -41,11 +45,16 @@ public class SimplePermission implements Permission { } this.localName = localName; + this.uri = NAMESPACE + localName; + this.ACTION = new SimpleRequestedAction(localName); this.ACTIONS = new Actions(this.ACTION); - // TODO -- we need to throw an exception if another Permission already has this localname. - allInstances.add(this); + if (allInstances.containsKey(this.uri)) { + throw new IllegalStateException("A SimplePermission named '" + + this.uri + "' already exists."); + } + allInstances.put(uri, this); } @Override diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/UpdatePermissionSetUris.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/UpdatePermissionSetUris.java new file mode 100644 index 000000000..212154ff4 --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/UpdatePermissionSetUris.java @@ -0,0 +1,270 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.servlet.setup; + +import static edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary.VITRO_AUTH; + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.servlet.ServletContext; +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; + +import edu.cornell.mannlib.vitro.webapp.beans.UserAccount; +import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties; +import edu.cornell.mannlib.vitro.webapp.dao.UserAccountsDao; +import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory; +import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus; + +/** + * The URIs for Admin, Curator, Editor and SelfEditor changed from 1.4 to 1.5. + * + * If the old ones are still in the User Accounts Model, replace them with the + * new ones. + */ +public class UpdatePermissionSetUris implements ServletContextListener { + + @Override + public void contextInitialized(ServletContextEvent sce) { + ServletContext ctx = sce.getServletContext(); + StartupStatus ss = StartupStatus.getBean(ctx); + Stats stats = new Stats(); + + try { + Updater updater = new Updater(ctx, stats); + if (updater.isThereAnythingToDo()) { + updater.update(); + ss.info(this, "Updated " + stats.updatedUris + + "URIs of PermissionSets on " + stats.updatedUsers + + "User Accounts, out of a total of " + + stats.allUserAccounts + " User Accounts."); + } else { + ss.info(this, "URIs of PermissionSets were up to date on all " + + stats.allUserAccounts + " User Accounts."); + } + } catch (Exception e) { + ss.fatal(this, "Failed to update URIs of PermissionSets " + + "on User Accounts", e); + } + } + + @Override + public void contextDestroyed(ServletContextEvent sce) { + // Nothing to tear down. + } + + // ---------------------------------------------------------------------- + // The Updater class + // ---------------------------------------------------------------------- + + private static class Updater { + private static final String OLD_ADMIN_URI = "http://permissionSet-50"; + private static final String OLD_CURATOR_URI = "http://permissionSet-5"; + private static final String OLD_EDITOR_URI = "http://permissionSet-4"; + private static final String OLD_SELF_EDITOR_URI = "http://permissionSet-1"; + private static final String NEW_ADMIN_URI = VITRO_AUTH + "ADMIN"; + private static final String NEW_CURATOR_URI = VITRO_AUTH + "CURATOR"; + private static final String NEW_EDITOR_URI = VITRO_AUTH + "EDITOR"; + private static final String NEW_SELF_EDITOR_URI = VITRO_AUTH + + "SELF_EDITOR"; + + private static final Map updateMap = buildUpdateMap(); + + private static Map buildUpdateMap() { + Map map = new HashMap(); + map.put(OLD_ADMIN_URI, NEW_ADMIN_URI); + map.put(OLD_CURATOR_URI, NEW_CURATOR_URI); + map.put(OLD_EDITOR_URI, NEW_EDITOR_URI); + map.put(OLD_SELF_EDITOR_URI, NEW_SELF_EDITOR_URI); + return Collections.unmodifiableMap(map); + } + + private final ServletContext ctx; + private final Stats stats; + private final UserAccountsDao userAccountsDao; + + private Journal journal; + + public Updater(ServletContext ctx, Stats stats) { + this.ctx = ctx; + this.stats = stats; + + WebappDaoFactory wadf = (WebappDaoFactory) ctx + .getAttribute("webappDaoFactory"); + userAccountsDao = wadf.getUserAccountsDao(); + } + + /** + * If none of the existing Users have Permission Sets with the obsolete + * URIs, then we don't do anything. We don't even create a Journal. + */ + public boolean isThereAnythingToDo() { + Collection allUserAccounts = userAccountsDao + .getAllUserAccounts(); + stats.allUserAccounts = allUserAccounts.size(); + + for (UserAccount user : allUserAccounts) { + for (String psUri : user.getPermissionSetUris()) { + if (updateMap.keySet().contains(psUri)) { + return true; + } + } + } + return false; + } + + public void update() throws IOException { + journal = new Journal(ctx); + try { + for (UserAccount user : userAccountsDao.getAllUserAccounts()) { + updateUserAccount(user); + } + } finally { + journal.close(); + } + } + + private void updateUserAccount(UserAccount user) { + boolean updated = false; + List newUris = new ArrayList(); + + for (String oldUri : user.getPermissionSetUris()) { + if (updateMap.keySet().contains(oldUri)) { + String newUri = updateMap.get(oldUri); + newUris.add(newUri); + + updated = true; + stats.updatedUris++; + journal.noteUpdate(user, oldUri, newUri); + } else { + newUris.add(oldUri); + } + } + + if (updated) { + user.setPermissionSetUris(newUris); + userAccountsDao.updateUserAccount(user); + + stats.updatedUsers++; + } + } + + } + + // ---------------------------------------------------------------------- + // The Stats class + // ---------------------------------------------------------------------- + + private static class Stats { + int allUserAccounts; + int updatedUsers; + int updatedUris; + } + + // ---------------------------------------------------------------------- + // The Journal class + // ---------------------------------------------------------------------- + + private static class Journal { + private final File file; + private final PrintWriter w; + private int errorCount; + + Journal(ServletContext ctx) throws IOException { + String homeDirectoryPath = ConfigurationProperties.getBean(ctx) + .getProperty("vitro.home.directory"); + if (homeDirectoryPath == null) { + throw new IllegalStateException( + "No value found for vitro.home.directory"); + } + File homeDirectory = new File(homeDirectoryPath); + confirmIsDirectory(homeDirectory); + + File upgradeDirectory = createDirectory(homeDirectory, "upgrade"); + String filename = timestampedFilename("UpgradePermissionSetUris", + ".txt"); + this.file = new File(upgradeDirectory, filename); + + this.w = new PrintWriter(this.file); + } + + public String getPath() { + return file.getAbsolutePath(); + } + + public void note(String... notes) { + w.println(); + for (String note : notes) { + w.println("# " + note); + } + } + + public void noteUpdate(UserAccount user, String oldPermissionSetUri, + String newPermissionSetUri) { + note(String.format("For user %1$s, replaced '%2$s' with '%3$s'", + user.getUri(), oldPermissionSetUri, newPermissionSetUri)); + } + + public void close() { + w.println("upgrade complete with " + errorCount + " errors."); + w.close(); + } + + private void confirmIsDirectory(File home) { + if (!home.exists()) { + throw new IllegalStateException("Vitro home directory '" + + home.getPath() + "' does not exist."); + } + if (!home.isDirectory()) { + throw new IllegalStateException("Vitro home '" + home.getPath() + + "' is not a directory."); + } + if (!home.canWrite()) { + throw new IllegalStateException( + "Can't write to Vitro home directory '" + + home.getPath() + "'."); + } + } + + private File createDirectory(File home, String name) { + File newDir = new File(home, name); + if (!newDir.exists()) { + newDir.mkdirs(); + if (!newDir.exists()) { + throw new IllegalStateException( + "Failed to create the upgrade directory '" + + newDir.getPath() + "'"); + } + } + + if (!newDir.isDirectory()) { + throw new IllegalStateException("Upgrade directory '" + + newDir.getPath() + "' is not a directory."); + } + if (!newDir.canWrite()) { + throw new IllegalStateException( + "Can't write to Upgrade directory '" + newDir.getPath() + + "'."); + } + + return newDir; + } + + private String timestampedFilename(String prefix, String suffix) { + SimpleDateFormat sdf = new SimpleDateFormat( + "yyyy-MM-dd'T'HH-mm-sss"); + return prefix + "." + sdf.format(new Date()) + suffix; + } + } + +} diff --git a/webapp/web/WEB-INF/resources/startup_listeners.txt b/webapp/web/WEB-INF/resources/startup_listeners.txt index ac5c92252..b74d6f4c3 100644 --- a/webapp/web/WEB-INF/resources/startup_listeners.txt +++ b/webapp/web/WEB-INF/resources/startup_listeners.txt @@ -24,13 +24,8 @@ edu.cornell.mannlib.vitro.webapp.servlet.setup.UpdateKnowledgeBase edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorageSetup -# Invokes a process to move any uploaded files into the new file storage system. -# Needs to run after FileStorageSetup and JenaDataSourceSetup. -# Should run before Pellet is set up. -edu.cornell.mannlib.vitro.webapp.servlet.setup.UpdateUploadedFiles - -# Update to the new UserAccounts model (1.3). Needs to run after JenaDataSourceSetup. -edu.cornell.mannlib.vitro.webapp.servlet.setup.UpdateUserAccounts +# Update the URIs on Permission Sets on UserAccounts from model (1.4) to 1.5. +edu.cornell.mannlib.vitro.webapp.servlet.setup.UpdatePermissionSetUris edu.cornell.mannlib.vitro.webapp.servlet.setup.FileGraphSetup