diff --git a/config/alfresco/avm-services-context.xml b/config/alfresco/avm-services-context.xml index f335103458..9a8a048ba6 100644 --- a/config/alfresco/avm-services-context.xml +++ b/config/alfresco/avm-services-context.xml @@ -154,17 +154,7 @@ - - - - - - - - - - - + diff --git a/config/alfresco/cache-context.xml b/config/alfresco/cache-context.xml index ed811bcd2c..6218260b5d 100644 --- a/config/alfresco/cache-context.xml +++ b/config/alfresco/cache-context.xml @@ -423,42 +423,6 @@ - - - - - - - - - - - - - org.alfresco.repo.avm.lookupSharedCache - - - - - - - - - - - - - - - - org.alfresco.repo.avm.lookupTransactionalCache - - - 100 - - - - diff --git a/config/alfresco/ehcache-default.xml b/config/alfresco/ehcache-default.xml index 84f1057c32..a5e893385b 100644 --- a/config/alfresco/ehcache-default.xml +++ b/config/alfresco/ehcache-default.xml @@ -207,15 +207,17 @@ eternal="true" overflowToDisk="false" /> + + diff --git a/config/alfresco/extension/avm-lookup-cache-context.xml.sample b/config/alfresco/extension/avm-lookup-cache-context.xml.sample new file mode 100644 index 0000000000..cd7f13953d --- /dev/null +++ b/config/alfresco/extension/avm-lookup-cache-context.xml.sample @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.alfresco.repo.avm.lookupSharedCache + + + + + + + + + + + + + + + + org.alfresco.repo.avm.lookupTransactionalCache + + + 100 + + + + diff --git a/config/alfresco/public-services-security-context.xml b/config/alfresco/public-services-security-context.xml index c62f51a069..722098322f 100644 --- a/config/alfresco/public-services-security-context.xml +++ b/config/alfresco/public-services-security-context.xml @@ -702,6 +702,8 @@ org.alfresco.service.cmr.security.AuthorityService.getName=ACL_ALLOW org.alfresco.service.cmr.security.AuthorityService.authorityExists=ACL_METHOD.ROLE_ADMINISTRATOR org.alfresco.service.cmr.security.AuthorityService.getAuthoritiesForUser=ACL_METHOD.ROLE_ADMINISTRATOR + org.alfresco.service.cmr.security.AuthorityService.setAuthorityDisplayName=ACL_METHOD.ROLE_ADMINISTRATOR + org.alfresco.service.cmr.security.AuthorityService.getAuthorityDisplayName=ACL_ALLOW diff --git a/source/java/org/alfresco/model/ContentModel.java b/source/java/org/alfresco/model/ContentModel.java index 5c8cacc96f..bda5e4dcf7 100644 --- a/source/java/org/alfresco/model/ContentModel.java +++ b/source/java/org/alfresco/model/ContentModel.java @@ -294,6 +294,8 @@ public interface ContentModel static final QName TYPE_AUTHORITY_CONTAINER = QName.createQName(USER_MODEL_URI, "authorityContainer"); static final QName PROP_AUTHORITY_NAME = QName.createQName(USER_MODEL_URI, "authorityName"); + static final QName PROP_AUTHORITY_DISPLAY_NAME = QName.createQName(USER_MODEL_URI, "authorityDisplayName"); + static final QName ASSOC_MEMBER = QName.createQName(USER_MODEL_URI, "member"); static final QName PROP_MEMBERS = QName.createQName(USER_MODEL_URI, "members"); diff --git a/source/java/org/alfresco/repo/avm/LookupCache.java b/source/java/org/alfresco/repo/avm/LookupCache.java index ec2dc7fc89..b507e7c74c 100644 --- a/source/java/org/alfresco/repo/avm/LookupCache.java +++ b/source/java/org/alfresco/repo/avm/LookupCache.java @@ -1,305 +1,62 @@ -/** +/* + * Copyright (C) 2005-2009 Alfresco Software Limited. * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" */ package org.alfresco.repo.avm; -import java.util.ArrayList; -import java.util.List; - import org.alfresco.repo.avm.util.SimplePath; -import org.alfresco.repo.cache.SimpleCache; -import org.alfresco.repo.security.permissions.AccessDeniedException; -import org.alfresco.service.cmr.security.PermissionService; -import org.alfresco.util.Pair; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; /** * All lookup traffic goes through here. - * @author britt */ -public class LookupCache +public interface LookupCache { - private static Log fgLogger = LogFactory.getLog(LookupCache.class); + + public Lookup lookup(AVMStore store, int version, SimplePath path, boolean write, boolean includeDeleted); - /** - * The Map of of keys to lookups. - */ - private SimpleCache fCache; + // Following are the cache invalidation calls. - /** - * Reference to the Node DAO. - */ - private AVMNodeDAO fAVMNodeDAO; + /** + * Called when a simple write operation occurs. This + * invalidates all read lookups and all layered lookups. + */ + public void onWrite(String storeName); - /** - * Reference to the Store DAO. - */ - private AVMStoreDAO fAVMStoreDAO; + /** + * Called when a delete has occurred in a store. This invalidates both + * reads and write lookups in that store. + */ + public void onDelete(String storeName); - /** - * Make one up. - */ - public LookupCache() - { - } + /** + * Called when a snapshot occurs in a store. This invalidates write + * lookups. Read lookups stay untouched. + */ + public void onSnapshot(String storeName); - /** - * Set up the node dao. - * @param dao The dao to set. - */ - public void setAvmNodeDAO(AVMNodeDAO dao) - { - fAVMNodeDAO = dao; - } + /** + * Full reset of cache. + */ + public void reset(); - /** - * Set the store dao. - * @param dao The dao to set. - */ - public void setAvmStoreDAO(AVMStoreDAO dao) - { - fAVMStoreDAO = dao; - } - - public void setTransactionalCache(SimpleCache cache) - { - fCache = cache; - } - - /** - * Lookup a path. Try to fulfill the request from the cache. - * @param store The AVMStore. - * @param version The versions. - * @param path The path we are looking up. - * @param write Whether this is a write lookup. - * @param includeDeleted - * @return - */ - public Lookup lookup(AVMStore store, int version, SimplePath path, - boolean write, boolean includeDeleted) - { - // Create a key object. - LookupKey key = new LookupKey(version, path, store.getName(), write, includeDeleted); - // Is it in the cache? - Lookup found = findInCache(key); - // TODO Testing. - // found = null; - if (found != null) - { - if (fgLogger.isDebugEnabled()) - { - fgLogger.debug("Cache Hit: " + key + ", " + found.getCurrentNode().getId()); - } - return found; - } - // Make up a Lookup to hold the results. - if (path.size() == 0) - { - return null; - } - Lookup result = new Lookup(store, store.getName(), version); - // Grab the root node to start the lookup. - DirectoryNode dir = null; - // Versions less than 0 mean get current. - if (version < 0) - { - dir = store.getRoot(); - } - else - { - VersionRoot vRoot = AVMDAOs.Instance().fVersionRootDAO.getByVersionID(store, version); - if (vRoot != null) - { - dir = vRoot.getRoot(); - } -// dir = fAVMNodeDAO.getAVMStoreRoot(store, version); - } - if (dir == null) - { - return null; - } - dir = (DirectoryNode)AVMNodeUnwrapper.Unwrap(dir); - // Add an entry for the root. - result.add(dir, "", true, write); - dir = (DirectoryNode)result.getCurrentNode(); - if (path.size() == 1 && path.get(0).equals("")) - { - fCache.put(key, result); - return result; - } - // Now look up each path element in sequence up to one - // before the end. - for (int i = 0; i < path.size() - 1; i++) - { - if (!AVMRepository.GetInstance().can(null, dir, PermissionService.READ_CHILDREN)) - { - throw new AccessDeniedException("Not allowed to read children: " + path.get(i)); - } - Pair child = dir.lookupChild(result, path.get(i), includeDeleted); - if (child == null) - { - return null; - } - // Every element that is not the last needs to be a directory. - if (child.getFirst().getType() != AVMNodeType.PLAIN_DIRECTORY && - child.getFirst().getType() != AVMNodeType.LAYERED_DIRECTORY) - { - return null; - } - result.add(child.getFirst(), path.get(i), child.getSecond(), write); - dir = (DirectoryNode)result.getCurrentNode(); - } - // Now look up the last element. - if (!AVMRepository.GetInstance().can(null, dir, PermissionService.READ_CHILDREN)) - { - throw new AccessDeniedException("Not allowed to read children: " + path.get(path.size() - 1)); - } - Pair child = dir.lookupChild(result, path.get(path.size() - 1), - includeDeleted); - if (child == null) - { - return null; - } - result.add(child.getFirst(), path.get(path.size() - 1), child.getSecond(), write); - fCache.put(key, result); - return result; - } - - /** - * Try to find a match in the cache. - * @param key The lookup key. - * @return A valid for this session Lookup or null if not found. - */ - private synchronized Lookup findInCache(LookupKey key) - { - Lookup found = fCache.get(key); - if (found != null) - { - // Get a freshened Lookup. - Lookup result = new Lookup(found, fAVMNodeDAO, fAVMStoreDAO); - // Check that nothing horrible is wrong. This should - // be assertible, but I'll leave the check in for now. - if (!result.isValid()) - { - fgLogger.error("Invalid entry in cache: " + key); - return null; - } - return result; - } - // Alternatively for a read lookup a write can match. - if (!key.isWrite()) - { - // Make a copy of the key and set it to 'write' - LookupKey newKey = new LookupKey(key); - newKey.setWrite(true); - found = fCache.get(newKey); - if (found != null) - { - // We found it. Make a freshened copy of the Lookup. - Lookup result = new Lookup(found, fAVMNodeDAO, fAVMStoreDAO); - // Check for badness. This should be assertible but I'll - // leave the check in for now. - if (!result.isValid()) - { - fgLogger.error("Invalid entry in cache: " + newKey); - return null; - } - return result; - } - } - return null; - } - - // Following are the cache invalidation calls. - - /** - * Called when a simple write operation occurs. This - * invalidates all read lookups and all layered lookups. - */ - public synchronized void onWrite(String storeName) - { - // Invalidate if it's a read lookup in the store, or - // any read lookup is it's layered. - List keys = new ArrayList(); - for (LookupKey key : fCache.getKeys()) - { - keys.add(key); - } - for (LookupKey key : keys) - { - Lookup value = fCache.get(key); - if ((key.getStoreName().equals(storeName) && - !key.isWrite()) || value == null || - (!key.isWrite() && value.isLayered())) - { - if (fgLogger.isDebugEnabled()) - { - fgLogger.debug("Invalidating: " + key + ", " + (value != null ? value.getCurrentNode().getId() : -2)); - } - fCache.remove(key); - } - } - } - - /** - * Called when a delete has occurred in a store. This invalidates both - * reads and write lookups in that store. - */ - public synchronized void onDelete(String storeName) - { - // Invalidate any entries that are in the store or are layered lookups. - List keys = new ArrayList(); - for (LookupKey key : fCache.getKeys()) - { - keys.add(key); - } - for (LookupKey key : keys) - { - Lookup value = fCache.get(key); - if (key.getStoreName().equals(storeName) || - value == null || value.isLayered()) - { - if (fgLogger.isDebugEnabled()) - { - fgLogger.debug("Invalidating: " + key + ", " + (value != null ? value.getCurrentNode().getId() : -2)); - } - fCache.remove(key); - } - } - } - - /** - * Called when a snapshot occurs in a store. This invalidates write - * lookups. Read lookups stay untouched. - */ - public synchronized void onSnapshot(String storeName) - { - // Invalidate any entries that in the store and writes or - // any layered lookups. - List keys = new ArrayList(); - for (LookupKey key : fCache.getKeys()) - { - keys.add(key); - } - for (LookupKey key : keys) - { - Lookup value = fCache.get(key); - if ((key.getStoreName().equals(storeName) && - key.isWrite()) || - value == null || value.isLayered()) - { - if (fgLogger.isDebugEnabled()) - { - fgLogger.debug("Invalidating: " + key + ", " + (value != null ? value.getCurrentNode().getId() : -2)); - } - fCache.remove(key); - } - } - } - - public synchronized void reset() - { - fCache.clear(); - } } diff --git a/source/java/org/alfresco/repo/avm/NOOPLookupCache.java b/source/java/org/alfresco/repo/avm/NOOPLookupCache.java new file mode 100644 index 0000000000..64f81c3c47 --- /dev/null +++ b/source/java/org/alfresco/repo/avm/NOOPLookupCache.java @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2005-2009 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.avm; + +import org.alfresco.repo.avm.util.SimplePath; +import org.alfresco.repo.security.permissions.AccessDeniedException; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.util.Pair; + +/** + * A NO-OP implementation of AVM path lookup cache + */ +public class NOOPLookupCache implements LookupCache +{ + /** + * Make one up. + */ + public NOOPLookupCache() + { + } + + /** + * Lookup a path. + * + * @param store The AVMStore. + * @param version The versions. + * @param path The path we are looking up. + * @param write Whether this is a write lookup. + * @param includeDeleted + * @return + */ + public Lookup lookup(AVMStore store, int version, SimplePath path, + boolean write, boolean includeDeleted) + { + // Make up a Lookup to hold the results. + if (path.size() == 0) + { + return null; + } + Lookup result = new Lookup(store, store.getName(), version); + // Grab the root node to start the lookup. + DirectoryNode dir = null; + // Versions less than 0 mean get current. + if (version < 0) + { + dir = store.getRoot(); + } + else + { + VersionRoot vRoot = AVMDAOs.Instance().fVersionRootDAO.getByVersionID(store, version); + if (vRoot != null) + { + dir = vRoot.getRoot(); + } +// dir = fAVMNodeDAO.getAVMStoreRoot(store, version); + } + if (dir == null) + { + return null; + } + dir = (DirectoryNode)AVMNodeUnwrapper.Unwrap(dir); + // Add an entry for the root. + result.add(dir, "", true, write); + dir = (DirectoryNode)result.getCurrentNode(); + if (path.size() == 1 && path.get(0).equals("")) + { + return result; + } + // Now look up each path element in sequence up to one + // before the end. + for (int i = 0; i < path.size() - 1; i++) + { + if (!AVMRepository.GetInstance().can(null, dir, PermissionService.READ_CHILDREN)) + { + throw new AccessDeniedException("Not allowed to read children: " + path.get(i)); + } + Pair child = dir.lookupChild(result, path.get(i), includeDeleted); + if (child == null) + { + return null; + } + // Every element that is not the last needs to be a directory. + if (child.getFirst().getType() != AVMNodeType.PLAIN_DIRECTORY && + child.getFirst().getType() != AVMNodeType.LAYERED_DIRECTORY) + { + return null; + } + result.add(child.getFirst(), path.get(i), child.getSecond(), write); + dir = (DirectoryNode)result.getCurrentNode(); + } + // Now look up the last element. + if (!AVMRepository.GetInstance().can(null, dir, PermissionService.READ_CHILDREN)) + { + throw new AccessDeniedException("Not allowed to read children: " + path.get(path.size() - 1)); + } + Pair child = dir.lookupChild(result, path.get(path.size() - 1), + includeDeleted); + if (child == null) + { + return null; + } + result.add(child.getFirst(), path.get(path.size() - 1), child.getSecond(), write); + return result; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.avm.LookupCache#onWrite(java.lang.String) + */ + public void onWrite(String storeName) + { + } + + /* (non-Javadoc) + * @see org.alfresco.repo.avm.LookupCache#onDelete(java.lang.String) + */ + public void onDelete(String storeName) + { + } + + /* (non-Javadoc) + * @see org.alfresco.repo.avm.LookupCache#onSnapshot(java.lang.String) + */ + public void onSnapshot(String storeName) + { + } + + /* (non-Javadoc) + * @see org.alfresco.repo.avm.LookupCache#reset() + */ + public void reset() + { + } + +} diff --git a/source/java/org/alfresco/repo/avm/TransactionalLookupCache.java b/source/java/org/alfresco/repo/avm/TransactionalLookupCache.java new file mode 100644 index 0000000000..abcece7207 --- /dev/null +++ b/source/java/org/alfresco/repo/avm/TransactionalLookupCache.java @@ -0,0 +1,339 @@ +/* + * Copyright (C) 2005-2009 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.avm; + +import java.util.ArrayList; +import java.util.List; + +import org.alfresco.repo.avm.util.SimplePath; +import org.alfresco.repo.cache.SimpleCache; +import org.alfresco.repo.security.permissions.AccessDeniedException; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.util.Pair; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.InitializingBean; + +/** + * A Transactional? implementation of AVM path lookup cache + * + * @author britt + */ +public class TransactionalLookupCache implements LookupCache, InitializingBean +{ + private static Log fgLogger = LogFactory.getLog(TransactionalLookupCache.class); + private static Log fgLoggerInit = LogFactory.getLog(TransactionalLookupCache.class.getName() + ".init"); + + + /** + * The Map of of keys to lookups. + */ + private SimpleCache fCache; + + /** + * Reference to the Node DAO. + */ + private AVMNodeDAO fAVMNodeDAO; + + /** + * Reference to the Store DAO. + */ + private AVMStoreDAO fAVMStoreDAO; + + /** + * Make one up. + */ + public TransactionalLookupCache() + { + } + + /** + * Set up the node dao. + * @param dao The dao to set. + */ + public void setAvmNodeDAO(AVMNodeDAO dao) + { + fAVMNodeDAO = dao; + } + + /** + * Set the store dao. + * @param dao The dao to set. + */ + public void setAvmStoreDAO(AVMStoreDAO dao) + { + fAVMStoreDAO = dao; + } + + public void setTransactionalCache(SimpleCache cache) + { + fCache = cache; + } + + /* (non-Javadoc) + * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() + */ + public void afterPropertiesSet() throws Exception + { + if (fgLoggerInit.isDebugEnabled()) + fgLoggerInit.debug("Transactional AVM lookup cache initialised"); + } + + /** + * Lookup a path. Try to fulfill the request from the cache. + * @param store The AVMStore. + * @param version The versions. + * @param path The path we are looking up. + * @param write Whether this is a write lookup. + * @param includeDeleted + * @return + */ + public Lookup lookup(AVMStore store, int version, SimplePath path, + boolean write, boolean includeDeleted) + { + // Create a key object. + LookupKey key = new LookupKey(version, path, store.getName(), write, includeDeleted); + // Is it in the cache? + Lookup found = findInCache(key); + // TODO Testing. + // found = null; + if (found != null) + { + if (fgLogger.isDebugEnabled()) + { + fgLogger.debug("Cache Hit: " + key + ", " + found.getCurrentNode().getId()); + } + return found; + } + // Make up a Lookup to hold the results. + if (path.size() == 0) + { + return null; + } + Lookup result = new Lookup(store, store.getName(), version); + // Grab the root node to start the lookup. + DirectoryNode dir = null; + // Versions less than 0 mean get current. + if (version < 0) + { + dir = store.getRoot(); + } + else + { + VersionRoot vRoot = AVMDAOs.Instance().fVersionRootDAO.getByVersionID(store, version); + if (vRoot != null) + { + dir = vRoot.getRoot(); + } +// dir = fAVMNodeDAO.getAVMStoreRoot(store, version); + } + if (dir == null) + { + return null; + } + dir = (DirectoryNode)AVMNodeUnwrapper.Unwrap(dir); + // Add an entry for the root. + result.add(dir, "", true, write); + dir = (DirectoryNode)result.getCurrentNode(); + if (path.size() == 1 && path.get(0).equals("")) + { + fCache.put(key, result); + return result; + } + // Now look up each path element in sequence up to one + // before the end. + for (int i = 0; i < path.size() - 1; i++) + { + if (!AVMRepository.GetInstance().can(null, dir, PermissionService.READ_CHILDREN)) + { + throw new AccessDeniedException("Not allowed to read children: " + path.get(i)); + } + Pair child = dir.lookupChild(result, path.get(i), includeDeleted); + if (child == null) + { + return null; + } + // Every element that is not the last needs to be a directory. + if (child.getFirst().getType() != AVMNodeType.PLAIN_DIRECTORY && + child.getFirst().getType() != AVMNodeType.LAYERED_DIRECTORY) + { + return null; + } + result.add(child.getFirst(), path.get(i), child.getSecond(), write); + dir = (DirectoryNode)result.getCurrentNode(); + } + // Now look up the last element. + if (!AVMRepository.GetInstance().can(null, dir, PermissionService.READ_CHILDREN)) + { + throw new AccessDeniedException("Not allowed to read children: " + path.get(path.size() - 1)); + } + Pair child = dir.lookupChild(result, path.get(path.size() - 1), + includeDeleted); + if (child == null) + { + return null; + } + result.add(child.getFirst(), path.get(path.size() - 1), child.getSecond(), write); + fCache.put(key, result); + return result; + } + + /** + * Try to find a match in the cache. + * @param key The lookup key. + * @return A valid for this session Lookup or null if not found. + */ + private synchronized Lookup findInCache(LookupKey key) + { + Lookup found = fCache.get(key); + if (found != null) + { + // Get a freshened Lookup. + Lookup result = new Lookup(found, fAVMNodeDAO, fAVMStoreDAO); + // Check that nothing horrible is wrong. This should + // be assertible, but I'll leave the check in for now. + if (!result.isValid()) + { + fgLogger.error("Invalid entry in cache: " + key); + return null; + } + return result; + } + // Alternatively for a read lookup a write can match. + if (!key.isWrite()) + { + // Make a copy of the key and set it to 'write' + LookupKey newKey = new LookupKey(key); + newKey.setWrite(true); + found = fCache.get(newKey); + if (found != null) + { + // We found it. Make a freshened copy of the Lookup. + Lookup result = new Lookup(found, fAVMNodeDAO, fAVMStoreDAO); + // Check for badness. This should be assertible but I'll + // leave the check in for now. + if (!result.isValid()) + { + fgLogger.error("Invalid entry in cache: " + newKey); + return null; + } + return result; + } + } + return null; + } + + // Following are the cache invalidation calls. + + /** + * Called when a simple write operation occurs. This + * invalidates all read lookups and all layered lookups. + */ + public synchronized void onWrite(String storeName) + { + // Invalidate if it's a read lookup in the store, or + // any read lookup is it's layered. + List keys = new ArrayList(); + for (LookupKey key : fCache.getKeys()) + { + keys.add(key); + } + for (LookupKey key : keys) + { + Lookup value = fCache.get(key); + if ((key.getStoreName().equals(storeName) && + !key.isWrite()) || value == null || + (!key.isWrite() && value.isLayered())) + { + if (fgLogger.isDebugEnabled()) + { + fgLogger.debug("Invalidating: " + key + ", " + (value != null ? value.getCurrentNode().getId() : -2)); + } + fCache.remove(key); + } + } + } + + /** + * Called when a delete has occurred in a store. This invalidates both + * reads and write lookups in that store. + */ + public synchronized void onDelete(String storeName) + { + // Invalidate any entries that are in the store or are layered lookups. + List keys = new ArrayList(); + for (LookupKey key : fCache.getKeys()) + { + keys.add(key); + } + for (LookupKey key : keys) + { + Lookup value = fCache.get(key); + if (key.getStoreName().equals(storeName) || + value == null || value.isLayered()) + { + if (fgLogger.isDebugEnabled()) + { + fgLogger.debug("Invalidating: " + key + ", " + (value != null ? value.getCurrentNode().getId() : -2)); + } + fCache.remove(key); + } + } + } + + /** + * Called when a snapshot occurs in a store. This invalidates write + * lookups. Read lookups stay untouched. + */ + public synchronized void onSnapshot(String storeName) + { + // Invalidate any entries that in the store and writes or + // any layered lookups. + List keys = new ArrayList(); + for (LookupKey key : fCache.getKeys()) + { + keys.add(key); + } + for (LookupKey key : keys) + { + Lookup value = fCache.get(key); + if ((key.getStoreName().equals(storeName) && + key.isWrite()) || + value == null || value.isLayered()) + { + if (fgLogger.isDebugEnabled()) + { + fgLogger.debug("Invalidating: " + key + ", " + (value != null ? value.getCurrentNode().getId() : -2)); + } + fCache.remove(key); + } + } + } + + public synchronized void reset() + { + fCache.clear(); + } +} diff --git a/source/java/org/alfresco/repo/avm/hibernate/AVMStoreDAOHibernate.java b/source/java/org/alfresco/repo/avm/hibernate/AVMStoreDAOHibernate.java index bd3e2d44b3..3d30eddad4 100644 --- a/source/java/org/alfresco/repo/avm/hibernate/AVMStoreDAOHibernate.java +++ b/source/java/org/alfresco/repo/avm/hibernate/AVMStoreDAOHibernate.java @@ -153,7 +153,7 @@ class AVMStoreDAOHibernate extends HibernateDaoSupport implements /* (non-Javadoc) * @see org.alfresco.repo.avm.AVMStoreDAO#invalidateCache() */ - public synchronized void invalidateCache() + public void invalidateCache() { fNameCache.clear(); } diff --git a/source/java/org/alfresco/repo/domain/hibernate/AbstractPermissionsDaoComponentImpl.java b/source/java/org/alfresco/repo/domain/hibernate/AbstractPermissionsDaoComponentImpl.java index 4a0c0b8472..9e52ddc576 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/AbstractPermissionsDaoComponentImpl.java +++ b/source/java/org/alfresco/repo/domain/hibernate/AbstractPermissionsDaoComponentImpl.java @@ -427,6 +427,12 @@ public abstract class AbstractPermissionsDaoComponentImpl implements Permissions return; } + // avoid NullPointerException if it was not created + if (acl == null) + { + return; + } + switch (acl.getAclType()) { case FIXED: diff --git a/source/java/org/alfresco/repo/security/authentication/userModel.xml b/source/java/org/alfresco/repo/security/authentication/userModel.xml index 53b765ad7f..a80e02439c 100644 --- a/source/java/org/alfresco/repo/security/authentication/userModel.xml +++ b/source/java/org/alfresco/repo/security/authentication/userModel.xml @@ -90,6 +90,9 @@ d:text true + + + d:text diff --git a/source/java/org/alfresco/repo/security/authority/AuthorityDAO.java b/source/java/org/alfresco/repo/security/authority/AuthorityDAO.java index 07549210d9..e16f3200a8 100644 --- a/source/java/org/alfresco/repo/security/authority/AuthorityDAO.java +++ b/source/java/org/alfresco/repo/security/authority/AuthorityDAO.java @@ -44,8 +44,9 @@ public interface AuthorityDAO * * @param parentName * @param name + * @param authorityDisplayName */ - void createAuthority(String parentName, String name); + void createAuthority(String parentName, String name, String authorityDisplayName); /** * Delete an authority. @@ -122,4 +123,20 @@ public interface AuthorityDAO */ public String getAuthorityName(NodeRef authorityRef); + /** + * Get the display name for an authority + * + * @param authorityName + * @return the display name + */ + String getAuthorityDisplayName(String authorityName); + + /** + * Set the display name for an authority + * + * @param authorityName + * @param authorityDisplayName + */ + void setAuthorityDisplayName(String authorityName, String authorityDisplayName); + } diff --git a/source/java/org/alfresco/repo/security/authority/AuthorityDAOImpl.java b/source/java/org/alfresco/repo/security/authority/AuthorityDAOImpl.java index f441c0d3e7..ea05ab9c86 100644 --- a/source/java/org/alfresco/repo/security/authority/AuthorityDAOImpl.java +++ b/source/java/org/alfresco/repo/security/authority/AuthorityDAOImpl.java @@ -144,10 +144,11 @@ public class AuthorityDAOImpl implements AuthorityDAO } } - public void createAuthority(String parentName, String name) + public void createAuthority(String parentName, String name, String authorityDisplayName) { HashMap props = new HashMap(); props.put(ContentModel.PROP_AUTHORITY_NAME, name); + props.put(ContentModel.PROP_AUTHORITY_DISPLAY_NAME, authorityDisplayName); if (parentName != null) { NodeRef parentRef = getAuthorityOrNull(parentName); @@ -543,4 +544,30 @@ public class AuthorityDAOImpl implements AuthorityDAO return name; } + public String getAuthorityDisplayName(String authorityName) + { + NodeRef ref = getAuthorityOrNull(authorityName); + if(ref == null) + { + return null; + } + Serializable value = nodeService.getProperty(ref, ContentModel.PROP_AUTHORITY_DISPLAY_NAME); + if(value == null) + { + return null; + } + return DefaultTypeConverter.INSTANCE.convert(String.class, value); + } + + public void setAuthorityDisplayName(String authorityName, String authorityDisplayName) + { + NodeRef ref = getAuthorityOrNull(authorityName); + if(ref == null) + { + return; + } + nodeService.setProperty(ref, ContentModel.PROP_AUTHORITY_DISPLAY_NAME, authorityDisplayName); + + } + } diff --git a/source/java/org/alfresco/repo/security/authority/AuthorityServiceImpl.java b/source/java/org/alfresco/repo/security/authority/AuthorityServiceImpl.java index 1c45c1f9f0..60e1c4e556 100644 --- a/source/java/org/alfresco/repo/security/authority/AuthorityServiceImpl.java +++ b/source/java/org/alfresco/repo/security/authority/AuthorityServiceImpl.java @@ -261,10 +261,7 @@ public class AuthorityServiceImpl implements AuthorityService, InitializingBean public String createAuthority(AuthorityType type, String parentName, String shortName) { - checkTypeIsMutable(type); - String name = getName(type, shortName); - authorityDAO.createAuthority(parentName, name); - return name; + return createAuthority(type, parentName, shortName, shortName); } public void deleteAuthority(String name) @@ -334,4 +331,29 @@ public class AuthorityServiceImpl implements AuthorityService, InitializingBean return authorityDAO.authorityExists(name); } + public String createAuthority(AuthorityType type, String parentName, String shortName, String authorityDisplayName) + { + checkTypeIsMutable(type); + String name = getName(type, shortName); + authorityDAO.createAuthority(parentName, name, authorityDisplayName); + return name; + } + + public String getAuthorityDisplayName(String name) + { + String displayName = authorityDAO.getAuthorityDisplayName(name); + if(displayName == null) + { + displayName = getShortName(name); + } + return displayName; + } + + public void setAuthorityDisplayName(String authorityName, String authorityDisplayName) + { + AuthorityType type = AuthorityType.getAuthorityType(authorityName); + checkTypeIsMutable(type); + authorityDAO.setAuthorityDisplayName(authorityName, authorityDisplayName); + } + } diff --git a/source/java/org/alfresco/repo/security/authority/AuthorityServiceTest.java b/source/java/org/alfresco/repo/security/authority/AuthorityServiceTest.java index ef147aeb40..80360364d9 100644 --- a/source/java/org/alfresco/repo/security/authority/AuthorityServiceTest.java +++ b/source/java/org/alfresco/repo/security/authority/AuthorityServiceTest.java @@ -867,4 +867,30 @@ public class AuthorityServiceTest extends TestCase properties.put(ContentModel.PROP_ORGID, orgId); return properties; } + + public void testAuthorityDisplayNames() + { + String authOne = pubAuthorityService.createAuthority(AuthorityType.GROUP, null, "One"); + assertEquals(pubAuthorityService.getAuthorityDisplayName(authOne), "One"); + pubAuthorityService.setAuthorityDisplayName(authOne, "Selfish Crocodile"); + assertEquals(pubAuthorityService.getAuthorityDisplayName(authOne), "Selfish Crocodile"); + + String authTwo = pubAuthorityService.createAuthority(AuthorityType.GROUP, null, "Two", "Lamp posts"); + assertEquals(pubAuthorityService.getAuthorityDisplayName(authTwo), "Lamp posts"); + pubAuthorityService.setAuthorityDisplayName(authTwo, "Happy Hippos"); + assertEquals(pubAuthorityService.getAuthorityDisplayName(authTwo), "Happy Hippos"); + + assertEquals(pubAuthorityService.getAuthorityDisplayName("GROUP_Loon"), "Loon"); + assertEquals(pubAuthorityService.getAuthorityDisplayName("ROLE_Gibbon"), "Gibbon"); + assertEquals(pubAuthorityService.getAuthorityDisplayName("Monkey"), "Monkey"); + + authenticationComponent.setCurrentUser("andy"); + assertEquals(pubAuthorityService.getAuthorityDisplayName(authOne), "Selfish Crocodile"); + assertEquals(pubAuthorityService.getAuthorityDisplayName(authTwo), "Happy Hippos"); + assertEquals(pubAuthorityService.getAuthorityDisplayName("GROUP_Loon"), "Loon"); + assertEquals(pubAuthorityService.getAuthorityDisplayName("GROUP_Loon"), "Loon"); + assertEquals(pubAuthorityService.getAuthorityDisplayName("ROLE_Gibbon"), "Gibbon"); + assertEquals(pubAuthorityService.getAuthorityDisplayName("Monkey"), "Monkey"); + + } } diff --git a/source/java/org/alfresco/repo/security/authority/SimpleAuthorityServiceImpl.java b/source/java/org/alfresco/repo/security/authority/SimpleAuthorityServiceImpl.java index 5e548c331d..8484bc939d 100644 --- a/source/java/org/alfresco/repo/security/authority/SimpleAuthorityServiceImpl.java +++ b/source/java/org/alfresco/repo/security/authority/SimpleAuthorityServiceImpl.java @@ -262,4 +262,19 @@ public class SimpleAuthorityServiceImpl implements AuthorityService return authorities; } + public String createAuthority(AuthorityType type, String parentName, String shortName, String authorityDisplayName) + { + return ""; + } + + public String getAuthorityDisplayName(String name) + { + return ""; + } + + public void setAuthorityDisplayName(String authorityName, String authorityDisplayName) + { + + } + } diff --git a/source/java/org/alfresco/service/cmr/security/AuthorityService.java b/source/java/org/alfresco/service/cmr/security/AuthorityService.java index 3fd92d42bc..1f644bb287 100644 --- a/source/java/org/alfresco/service/cmr/security/AuthorityService.java +++ b/source/java/org/alfresco/service/cmr/security/AuthorityService.java @@ -113,6 +113,7 @@ public interface AuthorityService * authority is created. * @param shortName - * the short name of the authority to create + * this will also be set as the default display name for the authority * * @return the name of the authority (this will be the prefix, if any * associated with the type appended with the short name) @@ -120,6 +121,26 @@ public interface AuthorityService @Auditable(parameters = {"type", "parentName", "shortName"}) public String createAuthority(AuthorityType type, String parentName, String shortName); + /** + * Create an authority. If the parent is null thisw method creates a root + * authority. + * + * @param type - + * the type of the authority + * @param parentName - + * the name of the parent authority. If this is null then a root + * authority is created. + * @param shortName - + * the short name of the authority to create + * @param authorityDisplayName + * the display name for the authority + * + * @return the name of the authority (this will be the prefix, if any + * associated with the type appended with the short name) + */ + @Auditable(parameters = {"type", "parentName", "shortName", "authorityDisplayName"}) + public String createAuthority(AuthorityType type, String parentName, String shortName, String authorityDisplayName); + /** * Set an authority to include another authority. For example, adding a * group to a group or adding a user to a group. @@ -218,5 +239,24 @@ public interface AuthorityService */ @Auditable(parameters = {"name"}) public boolean authorityExists(String name); + + /** + * Get the display name for the given authority. + * + * @param name - the full authority string including any prefix (e.g. GROUP_woof) + * @return - the display name + */ + @Auditable(parameters = {"name"}) + public String getAuthorityDisplayName(String name); + + /** + * Set the display name for the given authority. + * Setting the display name is only supported for authorities of type group + * + * @param authorityName + * @param authorityDisplayName + */ + @Auditable(parameters = {"authorityName", "authorityDisplayName"}) + public void setAuthorityDisplayName(String authorityName, String authorityDisplayName); } diff --git a/source/java/org/alfresco/wcm/sandbox/SandboxFactory.java b/source/java/org/alfresco/wcm/sandbox/SandboxFactory.java index baf3fc1982..5af355db80 100644 --- a/source/java/org/alfresco/wcm/sandbox/SandboxFactory.java +++ b/source/java/org/alfresco/wcm/sandbox/SandboxFactory.java @@ -28,6 +28,7 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; +import java.util.Set; import org.alfresco.config.JNDIConstants; import org.alfresco.error.AlfrescoRuntimeException; @@ -340,7 +341,7 @@ public final class SandboxFactory extends WCMUtil permissionService.setPermission(dirRef, PermissionService.ALL_AUTHORITIES, PermissionService.READ, true); } - private void updateStagingAreaManagers(String storeId, NodeRef webProjectNodeRef, final List managers) + private void updateStagingAreaManagers(String storeId, final List managers) { // The stores have the mask set in updateSandboxManagers String storeName = WCMUtil.buildStagingStoreName(storeId); @@ -931,9 +932,9 @@ public final class SandboxFactory extends WCMUtil } } - public void updateSandboxManagers(final String wpStoreId, NodeRef wpNodeRef, List managers) + public void updateSandboxManagers(final String wpStoreId, List managers) { - // walk existing user sandboxes and reapply manager permissions to include new manager user + // walk existing user sandboxes and reapply manager permissions to include new managers List sbInfos = AuthenticationUtil.runAs(new RunAsWork>() { public List doWork() throws Exception @@ -951,7 +952,7 @@ public final class SandboxFactory extends WCMUtil } } - updateStagingAreaManagers(wpStoreId, wpNodeRef, managers); + updateStagingAreaManagers(wpStoreId, managers); } /** @@ -985,6 +986,174 @@ public final class SandboxFactory extends WCMUtil } } + public void removeSandboxManagers(final String wpStoreId, List managers) + { + // walk existing user sandboxes and remove manager permissions to exclude old managers + List sbInfos = AuthenticationUtil.runAs(new RunAsWork>() + { + public List doWork() throws Exception + { + return listSandboxes(wpStoreId, AuthenticationUtil.getSystemUserName()); + } + }, AuthenticationUtil.getSystemUserName()); + + for (SandboxInfo sbInfo : sbInfos) + { + if (sbInfo.getSandboxType().equals(SandboxConstants.PROP_SANDBOX_AUTHOR_MAIN)) + { + String username = sbInfo.getName(); + removeUserSandboxManagers(wpStoreId, managers, username); + } + } + + removeStagingAreaManagers(wpStoreId, managers); + } + + /** + * Removes the permissions for the list of sandbox ex-managers. + * + * @param storeId The store id of the sandbox to update + * @param managersToRemove The list of authorities who have had ContentManager role in the web project + * @param username Username of the user sandbox to update + */ + private void removeUserSandboxManagers(String storeId, List managersToRemove, String username) + { + final String userStoreName = WCMUtil.buildUserMainStoreName(storeId, username); + final String previewStoreName = WCMUtil.buildUserPreviewStoreName(storeId, username); + + final NodeRef mainDirRef = AVMNodeConverter.ToNodeRef(-1, WCMUtil.buildStoreRootPath(userStoreName)); + final NodeRef previewDirRef = AVMNodeConverter.ToNodeRef(-1, WCMUtil.buildStoreRootPath(previewStoreName)); + for (String manager : managersToRemove) + { + permissionService.deletePermission(mainDirRef.getStoreRef(), manager, WCMUtil.ROLE_CONTENT_MANAGER); + permissionService.deletePermission(previewDirRef.getStoreRef(), manager, WCMUtil.ROLE_CONTENT_MANAGER); + } + } + /** + * Removes the ContentManager role on staging area to ex-managers. + * + * @param storeId The store id of the sandbox to update + * @param managersToRemove The list of authorities who have had ContentManager role in the web project + */ + private void removeStagingAreaManagers(String storeId, List managersToRemove) + { + final String storeName = WCMUtil.buildStagingStoreName(storeId); + + final NodeRef dirRef = AVMNodeConverter.ToNodeRef(-1, WCMUtil.buildStoreRootPath(storeName)); + for (String manager : managersToRemove) + { + permissionService.deletePermission(dirRef, manager, WCMUtil.ROLE_CONTENT_MANAGER); + + permissionService.deletePermission(dirRef.getStoreRef(), manager, + PermissionService.CHANGE_PERMISSIONS); + permissionService.deletePermission(dirRef.getStoreRef(), manager, + PermissionService.READ_PERMISSIONS); + } + + } + + public void updateSandboxRoles(final String wpStoreId, List usersToUpdate, Set permissionsList) + { + // walk existing user sandboxes and remove manager permissions to exclude old managers + List sbInfos = AuthenticationUtil.runAs(new RunAsWork>() + { + public List doWork() throws Exception + { + return listSandboxes(wpStoreId, AuthenticationUtil.getSystemUserName()); + } + }, AuthenticationUtil.getSystemUserName()); + + for (SandboxInfo sbInfo : sbInfos) + { + if (sbInfo.getSandboxType().equals(SandboxConstants.PROP_SANDBOX_AUTHOR_MAIN)) + { + String username = sbInfo.getName(); + updateUserSandboxRole(wpStoreId, username ,usersToUpdate, permissionsList); + } + } + + updateStagingAreaRole(wpStoreId, usersToUpdate, permissionsList); + } + + /** + * Updates roles on the sandbox identified by username to users from usersToUpdate list. + * + * @param storeId The store id of the sandbox to update + * @param username Username of the user sandbox to update + * @param usersToUpdate The list of users who have role changes + * @param permissionsList List of permissions @see org.alfresco.web.bean.wcm.InviteWebsiteUsersWizard.getPermissionsForType(). It is not mandatory. + */ + private void updateUserSandboxRole(String storeId, String username, List usersToUpdate, Set permissionsList) + { + final String userStoreName = WCMUtil.buildUserMainStoreName(storeId, username); + final String previewStoreName = WCMUtil.buildUserPreviewStoreName(storeId, username); + + final NodeRef mainDirRef = AVMNodeConverter.ToNodeRef(-1, WCMUtil.buildStoreRootPath(userStoreName)); + final NodeRef previewDirRef = AVMNodeConverter.ToNodeRef(-1, WCMUtil.buildStoreRootPath(previewStoreName)); + + // If permissionsList is set remove all possible user permissions and set only necessary. + // This will fix previous wrong role changes. (paranoid) + // For little better performance just set permissionsList to null. + // But in this case it removes only previous permission. + if (permissionsList != null && permissionsList.size() != 0) + { + for (UserRoleWrapper user : usersToUpdate) + { + for (String permission : permissionsList) + { + permissionService.deletePermission(mainDirRef, user.getUserAuth(), permission); + permissionService.deletePermission(previewDirRef, user.getUserAuth(), permission); + } + + permissionService.setPermission(mainDirRef, user.getUserAuth(), user.getNewRole(), true); + permissionService.setPermission(previewDirRef, user.getUserAuth(), user.getNewRole(), true); + } + } + else + { + for (UserRoleWrapper user: usersToUpdate) + { + permissionService.deletePermission(mainDirRef, user.getUserAuth(), user.getOldRole()); + permissionService.deletePermission(previewDirRef, user.getUserAuth(), user.getOldRole()); + permissionService.setPermission(mainDirRef, user.getUserAuth(), user.getNewRole(), true); + permissionService.setPermission(previewDirRef, user.getUserAuth(), user.getNewRole(), true); + } + } + } + + /** + * Updates roles on staging sandbox to users from usersToUpdate list. + * + * @param storeId The store id of the sandbox to update + * @param usersToUpdate The list of users who have role changes + * @param permissionsList List of permissions @see org.alfresco.web.bean.wcm.InviteWebsiteUsersWizard.getPermissionsForType(). It is not mandatory. + */ + private void updateStagingAreaRole(String storeId, List usersToUpdate, Set permissionsList) + { + final String storeName = WCMUtil.buildStagingStoreName(storeId); + final NodeRef dirRef = AVMNodeConverter.ToNodeRef(-1, WCMUtil.buildStoreRootPath(storeName)); + + if (permissionsList != null && permissionsList.size() != 0) + { + for (UserRoleWrapper user : usersToUpdate) + { + for (String permission : permissionsList) + { + permissionService.deletePermission(dirRef, user.getUserAuth(), permission); + } + permissionService.setPermission(dirRef, user.getUserAuth(), user.getNewRole(), true); + } + } + else + { + for (UserRoleWrapper user : usersToUpdate) + { + permissionService.deletePermission(dirRef, user.getUserAuth(), user.getOldRole()); + permissionService.setPermission(dirRef, user.getUserAuth(), user.getNewRole(), true); + } + } + } + /** * Tag a named store with a DNS path meta-data attribute. * The DNS meta-data attribute is set to the system path 'store:/www/avm_webapps' @@ -1049,4 +1218,43 @@ public final class SandboxFactory extends WCMUtil logger.debug(" " + name + ": " + props.get(name)); } } + + public class UserRoleWrapper + { + private String newRole; + private String oldRole; + private String userAuth; + + public UserRoleWrapper(String userAuth, String oldRole, String newRole) + { + this.userAuth = userAuth; + this.oldRole = oldRole; + this.newRole = newRole; + } + + public String getNewRole() + { + return newRole; + } + public void setNewRole(String newRole) + { + this.newRole = newRole; + } + public String getOldRole() + { + return oldRole; + } + public void setOldRole(String oldRole) + { + this.oldRole = oldRole; + } + public String getUserAuth() + { + return userAuth; + } + public void setUserAuth(String userAuth) + { + this.userAuth = userAuth; + } + } } diff --git a/source/java/org/alfresco/wcm/webproject/WebProjectServiceImpl.java b/source/java/org/alfresco/wcm/webproject/WebProjectServiceImpl.java index 4f17bcecb7..651c1da8d1 100644 --- a/source/java/org/alfresco/wcm/webproject/WebProjectServiceImpl.java +++ b/source/java/org/alfresco/wcm/webproject/WebProjectServiceImpl.java @@ -69,6 +69,7 @@ import org.alfresco.util.DNSNameMangler; import org.alfresco.util.ParameterCheck; import org.alfresco.wcm.sandbox.SandboxFactory; import org.alfresco.wcm.sandbox.SandboxInfo; +import org.alfresco.wcm.sandbox.SandboxFactory.UserRoleWrapper; import org.alfresco.wcm.util.WCMUtil; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -790,9 +791,19 @@ public class WebProjectServiceImpl extends WCMUtil implements WebProjectService private String getWebUserRoleImpl(NodeRef wpNodeRef, String userName) { - long start = System.currentTimeMillis(); + NodeRef userRef = getWebUserRef(wpNodeRef, userName); String userRole = null; - + + if (userRef != null) + { + userRole = (String)nodeService.getProperty(userRef, WCMAppModel.PROP_WEBUSERROLE); + } + + return userRole; + } + + private NodeRef getWebUserRef(NodeRef wpNodeRef, String userName) + { StringBuilder query = new StringBuilder(128); query.append("+PARENT:\"").append(wpNodeRef).append("\" "); query.append("+TYPE:\"").append(WCMAppModel.TYPE_WEBUSER).append("\" "); @@ -820,27 +831,23 @@ public class WebProjectServiceImpl extends WCMUtil implements WebProjectService if (nodes.size() == 1) { - userRole = (String)nodeService.getProperty(nodes.get(0), WCMAppModel.PROP_WEBUSERROLE); + return nodes.get(0); } else if (nodes.size() == 0) { if (logger.isTraceEnabled()) { - logger.trace("getWebProjectUserRole: user role not found for " + userName); + logger.trace("getWebUserRef: web user ("+userName+") not found in web project: "+wpNodeRef); } } else { - logger.warn("getWebProjectUserRole: more than one user role found for " + userName); + logger.error("getWebUserRef: more than one web user ("+userName+") found in web project: "+wpNodeRef); } - - if (logger.isTraceEnabled()) - { - logger.trace("getWebProjectUserRole: "+userName+" "+userRole+" in "+(System.currentTimeMillis()-start)+" ms"); - } - - return userRole; + + return null; } + /* (non-Javadoc) * @see org.alfresco.wcm.webproject.WebProjectService#findWebProjectNodeFromPath(java.lang.String) @@ -889,7 +896,9 @@ public class WebProjectServiceImpl extends WCMUtil implements WebProjectService // build a list of managers who will have full permissions on ALL staging areas List managers = new ArrayList(4); - Set existingUsers = new HashSet(8); + Map webSiteUsers = new HashMap(8); + List managersToRemove = new LinkedList(); + List usersToUpdate = new LinkedList(); // retrieve the list of managers from the existing users for (Map.Entry userRole : userGroupRoles.entrySet()) @@ -906,19 +915,21 @@ public class WebProjectServiceImpl extends WCMUtil implements WebProjectService } } - Map existingUserRoles = listWebUsers(wpNodeRef); - for (Map.Entry userRole : existingUserRoles.entrySet()) + List userInfoRefs = nodeService.getChildAssocs(wpNodeRef, WCMAppModel.ASSOC_WEBUSER, RegexQNamePattern.MATCH_ALL); + + for (ChildAssociationRef ref : userInfoRefs) { - String username = userRole.getKey(); - String userrole = userRole.getValue(); - + NodeRef userInfoRef = ref.getChildRef(); + String username = (String)nodeService.getProperty(userInfoRef, WCMAppModel.PROP_WEBUSERNAME); + String userrole = (String)nodeService.getProperty(userInfoRef, WCMAppModel.PROP_WEBUSERROLE); + if (WCMUtil.ROLE_CONTENT_MANAGER.equals(userrole) && managers.contains(username) == false) { managers.add(username); } - // add each existing user to the exclude this - we cannot add them more than once! - existingUsers.add(username); + // add each existing user to the map which will be rechecked for update changed user permissions + webSiteUsers.put(username, userInfoRef); } List sandboxInfoList = new LinkedList(); @@ -933,7 +944,7 @@ public class WebProjectServiceImpl extends WCMUtil implements WebProjectService for (String userAuth : findNestedUserAuthorities(authority)) { - if (existingUsers.contains(userAuth) == false) + if (webSiteUsers.keySet().contains(userAuth) == false) { if (autoCreateAuthorSandbox) { @@ -955,18 +966,54 @@ public class WebProjectServiceImpl extends WCMUtil implements WebProjectService } else { - logger.warn("User '"+userAuth+"' already invited to web project: "+wpNodeRef+"(store id: "+wpStoreId+")"); + // TODO - split out into separate 'change role' + // if user role have been changed then update required properties etc. + NodeRef userRef = webSiteUsers.get(userAuth); + String oldUserRole = (String)nodeService.getProperty(userRef, WCMAppModel.PROP_WEBUSERROLE); + + if (!role.equals(oldUserRole)) + { + // change in role + Map props = nodeService.getProperties(userRef); + props.put(WCMAppModel.PROP_WEBUSERNAME, userAuth); + props.put(WCMAppModel.PROP_WEBUSERROLE, role); + nodeService.setProperties(userRef, props); + + if (WCMUtil.ROLE_CONTENT_MANAGER.equals(role)) + { + managersUpdateRequired = true; + } + else if (WCMUtil.ROLE_CONTENT_MANAGER.equals(oldUserRole)) + { + managersToRemove.add(userAuth); + } + + usersToUpdate.add(sandboxFactory.new UserRoleWrapper(userAuth, oldUserRole, role)); + + if (logger.isDebugEnabled()) + { + logger.debug(userAuth +"'s role has been changed from '" + oldUserRole + + "' to '" + role + "'"); + } + } } } } - + // Bind the post-commit transaction listener with data required for virtualization server notification CreateSandboxTransactionListener tl = new CreateSandboxTransactionListener(sandboxInfoList, listWebApps(wpNodeRef)); AlfrescoTransactionSupport.bindListener(tl); if (managersUpdateRequired == true) { - sandboxFactory.updateSandboxManagers(wpStoreId, wpNodeRef, managers); + sandboxFactory.updateSandboxManagers(wpStoreId, managers); + } + + // TODO - split out into separate 'change role' + // remove ex-managers from sandboxes + if (managersToRemove.size() != 0) + { + sandboxFactory.removeSandboxManagers(wpStoreId, managersToRemove); } // get permissions and roles for a web project folder type @@ -990,6 +1037,13 @@ public class WebProjectServiceImpl extends WCMUtil implements WebProjectService } } } + + // TODO - split out into separate 'change role' + // update user's roles + if (usersToUpdate.size() != 0) + { + sandboxFactory.updateSandboxRoles(wpStoreId, usersToUpdate, perms); + } if (logger.isInfoEnabled()) { @@ -1027,29 +1081,65 @@ public class WebProjectServiceImpl extends WCMUtil implements WebProjectService WebProjectInfo wpInfo = getWebProject(wpNodeRef); final String wpStoreId = wpInfo.getStoreId(); - if (isWebUser(wpNodeRef, userAuth)) + // build a list of managers who will have full permissions on ALL staging areas + List managers = new ArrayList(4); + + // retrieve the list of managers from the existing users + Map existingUserRoles = listWebUsers(wpNodeRef); + for (Map.Entry userRole : existingUserRoles.entrySet()) { - logger.warn("User '"+userAuth+"' already invited to web project: "+wpNodeRef+" (store id: "+wpStoreId+")"); - return; + String username = userRole.getKey(); + String userrole = userRole.getValue(); + + if (WCMUtil.ROLE_CONTENT_MANAGER.equals(userrole) && managers.contains(username) == false) + { + managers.add(username); + } + } + + // get permissions and roles for a web project folder type + Set perms = permissionService.getSettablePermissions(WCMAppModel.TYPE_AVMWEBFOLDER); + + NodeRef userRef = getWebUserRef(wpNodeRef, userAuth); + if (userRef != null) + { + // TODO - split out into separate 'change role' + // if user role has been changed then update required properties etc. + String oldUserRole = (String)nodeService.getProperty(userRef, WCMAppModel.PROP_WEBUSERROLE); + if (!role.equals(oldUserRole)) + { + // change in role + Map props = nodeService.getProperties(userRef); + props.put(WCMAppModel.PROP_WEBUSERNAME, userAuth); + props.put(WCMAppModel.PROP_WEBUSERROLE, role); + nodeService.setProperties(userRef, props); + + if (WCMUtil.ROLE_CONTENT_MANAGER.equals(role)) + { + managers.add(userAuth); + sandboxFactory.updateSandboxManagers(wpStoreId, managers); + } + else if (WCMUtil.ROLE_CONTENT_MANAGER.equals(oldUserRole)) + { + List managersToRemove = new LinkedList(); + managersToRemove.add(userAuth); + + sandboxFactory.removeSandboxManagers(wpStoreId, managersToRemove); + } + + List usersToUpdate = new LinkedList(); + usersToUpdate.add(sandboxFactory.new UserRoleWrapper(userAuth, oldUserRole, role)); + + sandboxFactory.updateSandboxRoles(wpStoreId, usersToUpdate, perms); + + if (logger.isInfoEnabled()) + { + logger.info("Web user "+userAuth +"'s role has been changed from '" + oldUserRole + "' to '" + role + "' (store id: "+wpStoreId+")"); + } + } } else { - // build a list of managers who will have full permissions on ALL staging areas - List managers = new ArrayList(4); - - // retrieve the list of managers from the existing users - Map existingUserRoles = listWebUsers(wpNodeRef); - for (Map.Entry userRole : existingUserRoles.entrySet()) - { - String username = userRole.getKey(); - String userrole = userRole.getValue(); - - if (WCMUtil.ROLE_CONTENT_MANAGER.equals(userrole) && managers.contains(username) == false) - { - managers.add(username); - } - } - if (autoCreateAuthorSandbox) { // create a sandbox for the user with permissions based on role @@ -1068,7 +1158,7 @@ public class WebProjectServiceImpl extends WCMUtil implements WebProjectService if (WCMUtil.ROLE_CONTENT_MANAGER.equals(role)) { managers.add(userAuth); - sandboxFactory.updateSandboxManagers(wpStoreId, wpNodeRef, managers); + sandboxFactory.updateSandboxManagers(wpStoreId, managers); } sandboxFactory.addStagingAreaUser(wpStoreId, userAuth, role); @@ -1076,9 +1166,6 @@ public class WebProjectServiceImpl extends WCMUtil implements WebProjectService // create an app:webuser instance for the user and assoc to the web project node createWebUser(wpNodeRef, userAuth, role); - // get permissions and roles for a web project folder type - Set perms = permissionService.getSettablePermissions(WCMAppModel.TYPE_AVMWEBFOLDER); - // set permissions for the user for (String permission : perms) {