/* * #%L * Alfresco Records Management Module * %% * Copyright (C) 2005 - 2016 Alfresco Software Limited * %% * This file is part of the Alfresco software. * - * If the software was purchased under a paid Alfresco license, the terms of * the paid license agreement will prevail. Otherwise, the software is * provided under the following open source license terms: * - * Alfresco is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * - * Alfresco 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 Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * #L% */ package org.alfresco.module.org_alfresco_module_rm.caveat; import static org.apache.commons.lang.exception.ExceptionUtils.getFullStackTrace; import java.io.File; import java.io.InputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.caveat.RMListOfValuesConstraint.MatchLogic; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; import org.alfresco.repo.cache.SimpleCache; import org.alfresco.repo.content.ContentServicePolicies; import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.node.NodeServicePolicies; import org.alfresco.repo.policy.Behaviour.NotificationFrequency; import org.alfresco.repo.policy.annotation.Behaviour; import org.alfresco.repo.policy.annotation.BehaviourBean; import org.alfresco.repo.policy.annotation.BehaviourKind; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.version.VersionModel; import org.alfresco.service.cmr.dictionary.Constraint; import org.alfresco.service.cmr.dictionary.ConstraintDefinition; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.dictionary.PropertyDefinition; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.security.AuthorityService; import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.util.JSONtoFmModel; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.json.JSONException; import org.json.JSONObject; import net.sf.acegisecurity.AccessDeniedException; /** * RM Caveat Config component impl * * @author janv */ @BehaviourBean(defaultType = "rma:caveatConfig") public class RMCaveatConfigComponentImpl implements ContentServicePolicies.OnContentUpdatePolicy, NodeServicePolicies.BeforeDeleteNodePolicy, NodeServicePolicies.OnCreateNodePolicy, RMCaveatConfigComponent { private static Log logger = LogFactory.getLog(RMCaveatConfigComponentImpl.class); private ContentService contentService; private DictionaryService dictionaryService; private NamespaceService namespaceService; private AuthorityService authorityService; private PersonService personService; private NodeService nodeService; // Default private StoreRef storeRef = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore"); private List<String> caveatAspectURINames = new ArrayList<String>(0); private List<QName> caveatAspectQNames = new ArrayList<QName>(0); private List<String> caveatModelURINames = new ArrayList<String>(0); private List<QName> caveatModelQNames = new ArrayList<QName>(0); private static final String CAVEAT_CONFIG_NAME = "caveatConfig.json"; private static final QName DATATYPE_TEXT = DataTypeDefinition.TEXT; /** * Lock objects */ private ReadWriteLock lock = new ReentrantReadWriteLock(); private Lock readLock = lock.readLock(); private Lock writeLock = lock.writeLock(); /* * Caveat Config (Shared) config * first string is property name * second string is authority name (user or group full name) * third string is list of values of property */ private SimpleCache<String, Map<String, List<String>>> caveatConfig; public void setCaveatConfig(SimpleCache<String, Map<String, List<String>>> caveatConfig) { this.caveatConfig = caveatConfig; } public void setNodeService(NodeService nodeService) { this.nodeService = nodeService; } public void setContentService(ContentService contentService) { this.contentService = contentService; } public void setDictionaryService(DictionaryService dictionaryService) { this.dictionaryService = dictionaryService; } public void setNamespaceService(NamespaceService namespaceService) { this.namespaceService = namespaceService; } public void setAuthorityService(AuthorityService authorityService) { this.authorityService = authorityService; } public void setPersonService(PersonService personService) { this.personService = personService; } public void setStoreRef(String storeRef) { this.storeRef = new StoreRef(storeRef); } public void setCaveatAspects(List<String> caveatAspectNames) { this.caveatAspectURINames = caveatAspectNames; } public void setCaveatModels(List<String> caveatModelNames) { this.caveatModelURINames = caveatModelNames; } /** * Initialise behaviours and caveat config cache */ public void init() { if (caveatAspectURINames.size() > 0) { for (String caveatAspectURIName : caveatAspectURINames) { caveatAspectQNames.add(QName.createQName(caveatAspectURIName)); } if (logger.isInfoEnabled()) { logger.info("Caveat aspects configured "+caveatAspectQNames); } } else { logger.warn("No caveat aspects configured - caveats will not be applied"); } if (caveatModelURINames.size() > 0) { for (String caveatModelURIName : caveatModelURINames) { caveatModelQNames.add(QName.createQName(caveatModelURIName)); } if (logger.isInfoEnabled()) { logger.info("Caveat models configured "+caveatModelQNames); } } else { logger.info("No caveat models configured - all models will be checked"); } NodeRef caveatConfigNodeRef = getCaveatConfigNode(); if (caveatConfigNodeRef != null) { validateAndReset(caveatConfigNodeRef); } } /** * @see org.alfresco.repo.content.ContentServicePolicies.OnContentUpdatePolicy#onContentUpdate(org.alfresco.service.cmr.repository.NodeRef, boolean) * RM-2770 - this method has to be fired on transaction commit to be able to validate the content when the content store is encrypted */ @Override @Behaviour ( kind = BehaviourKind.CLASS, notificationFrequency = NotificationFrequency.TRANSACTION_COMMIT ) public void onContentUpdate(NodeRef nodeRef, boolean newContent) { if (logger.isInfoEnabled()) { logger.info("onContentUpdate: "+nodeRef+", "+newContent); } validateAndReset(nodeRef); } /** * @see org.alfresco.repo.node.NodeServicePolicies.BeforeDeleteNodePolicy#beforeDeleteNode(org.alfresco.service.cmr.repository.NodeRef) */ @Override @Behaviour(kind = BehaviourKind.CLASS) public void beforeDeleteNode(NodeRef nodeRef) { if (logger.isInfoEnabled()) { logger.info("beforeDeleteNode: "+nodeRef); } validateAndReset(nodeRef); } /** * @see org.alfresco.repo.node.NodeServicePolicies.OnCreateNodePolicy#onCreateNode(org.alfresco.service.cmr.repository.ChildAssociationRef) */ @Override @Behaviour(kind = BehaviourKind.CLASS) public void onCreateNode(ChildAssociationRef childAssocRef) { if (logger.isInfoEnabled()) { logger.info("onCreateNode: "+childAssocRef); } validateAndReset(childAssocRef.getChildRef()); } /** * Validate the caveat config and optionally update the cache. * * @param nodeRef The nodeRef of the config * @param updateCache Set to <code>true</code> to update the cache */ @SuppressWarnings("unchecked") protected void validateAndReset(NodeRef nodeRef) { ContentReader cr = contentService.getReader(nodeRef, ContentModel.PROP_CONTENT); if (cr != null) { // TODO - check who can change caveat config ! // TODO - locking (or checkout/checkin) String caveatConfigData = cr.getContentString(); if (caveatConfigData != null) { NodeRef existing = getCaveatConfigNode(); if ((existing != null && (! existing.equals(nodeRef)))) { throw new AlfrescoRuntimeException("Cannot create more than one caveat config (existing="+existing+", new="+nodeRef+")"); } try { if (logger.isTraceEnabled()) { logger.trace(caveatConfigData); } Set<QName> models = new HashSet<QName>(1); Set<QName> props = new HashSet<QName>(10); Set<String> expectedPrefixes = new HashSet<String>(10); if (caveatModelQNames.size() > 0) { models.addAll(caveatModelQNames); } else { models.addAll(dictionaryService.getAllModels()); } if (logger.isTraceEnabled()) { logger.trace("validateAndReset: models to check "+models); } for (QName model : models) { props.addAll(dictionaryService.getProperties(model, DATATYPE_TEXT)); expectedPrefixes.addAll(namespaceService.getPrefixes(model.getNamespaceURI())); } if (props.size() == 0) { logger.warn("validateAndReset: no caveat properties found"); } else { if (logger.isTraceEnabled()) { logger.trace("validateAndReset: properties to check "+props); } } Map<String, Object> caveatConfigMap = JSONtoFmModel.convertJSONObjectToMap(caveatConfigData); for (Map.Entry<String, Object> conEntry : caveatConfigMap.entrySet()) { String conStr = conEntry.getKey(); QName conQName = QName.resolveToQName(namespaceService, conStr); // check prefix String conPrefix = QName.splitPrefixedQName(conStr)[0]; boolean prefixFound = false; for (String expectedPrefix : expectedPrefixes) { if (conPrefix.equals(expectedPrefix)) { prefixFound = true; } } if (! prefixFound) { throw new AlfrescoRuntimeException("Unexpected prefix: "+ conPrefix + " (" + conStr +") expected one of "+expectedPrefixes+")"); } Map<String, List<String>> caveatMap = (Map<String, List<String>>)conEntry.getValue(); List<String> allowedValues = null; @SuppressWarnings("unused") boolean found = false; for (QName propertyName : props) { PropertyDefinition propDef = dictionaryService.getProperty(propertyName); List<ConstraintDefinition> conDefs = propDef.getConstraints(); for (ConstraintDefinition conDef : conDefs) { final Constraint con = conDef.getConstraint(); if (con instanceof RMListOfValuesConstraint) { String conName = ((RMListOfValuesConstraint)con).getShortName(); if (conName.equals(conStr)) { // note: assumes only one caveat/LOV against a given property allowedValues = AuthenticationUtil.runAs(new RunAsWork<List<String>>() { public List<String> doWork() { return ((RMListOfValuesConstraint)con).getAllowedValues(); } }, AuthenticationUtil.getSystemUserName()); found = true; break; } } } } if (allowedValues != null) { if (logger.isInfoEnabled()) { logger.info("Processing constraint: "+conQName); } for (Map.Entry<String, List<String>> caveatEntry : caveatMap.entrySet()) { String authorityName = caveatEntry.getKey(); List<String> caveatList = caveatEntry.getValue(); // validate authority (user or group) - note: groups are configured with fullname (ie. GROUP_xxx) if ((! authorityService.authorityExists(authorityName) && ! personService.personExists(authorityName))) { // TODO - review warnings (& I18N) String msg = "User/group does not exist: "+authorityName+" (constraint="+conStr+")"; logger.warn(msg); } // validate caveat list for (String value : caveatList) { if (! allowedValues.contains(value)) { // TODO - review warnings (& add I18N) String msg = "Invalid value in list: "+value+" (authority="+authorityName+", constraint="+conStr+")"; logger.warn(msg); } } } } } try { writeLock.lock(); // we can't just clear the cache, as all puts to the cache afterwards in this transaction will be ignored // first delete all keys that are now not in the config caveatConfig.getKeys().retainAll(caveatConfigMap.keySet()); for (Map.Entry<String, Object> conEntry : caveatConfigMap.entrySet()) { String conStr = conEntry.getKey(); Map<String, List<String>> caveatMap = (Map<String, List<String>>)conEntry.getValue(); Map<String, List<String>> cacheValue = caveatConfig.get(conStr); if (cacheValue == null || !cacheValue.equals(caveatMap)) { // update the cache caveatConfig.put(conStr, caveatMap); } } } finally { writeLock.unlock(); } } catch (JSONException e) { throw new AlfrescoRuntimeException("Invalid caveat config syntax: "+e); } } } } private NodeRef getCaveatConfigNode() { NodeRef rootNode = nodeService.getRootNode(storeRef); return nodeService.getChildByName(rootNode, RecordsManagementModel.ASSOC_CAVEAT_CONFIG, CAVEAT_CONFIG_NAME); } public NodeRef updateOrCreateCaveatConfig(InputStream is) { NodeRef caveatConfig = getOrCreateCaveatConfig(); // Update the content ContentWriter writer = this.contentService.getWriter(caveatConfig, ContentModel.PROP_CONTENT, true); writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); writer.setEncoding("UTF-8"); writer.putContent(is); return caveatConfig; } public NodeRef updateOrCreateCaveatConfig(File jsonFile) { NodeRef caveatConfig = getOrCreateCaveatConfig(); // Update the content ContentWriter writer = this.contentService.getWriter(caveatConfig, ContentModel.PROP_CONTENT, true); writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); writer.setEncoding("UTF-8"); writer.putContent(jsonFile); return caveatConfig; } public NodeRef updateOrCreateCaveatConfig(String jsonString) { NodeRef caveatConfig = getOrCreateCaveatConfig(); // Update the content ContentWriter writer = this.contentService.getWriter(caveatConfig, ContentModel.PROP_CONTENT, true); writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); writer.setEncoding("UTF-8"); writer.putContent(jsonString); return caveatConfig; } private NodeRef getOrCreateCaveatConfig() { NodeRef caveatConfig = getCaveatConfigNode(); if (caveatConfig == null) { NodeRef rootNode = nodeService.getRootNode(storeRef); nodeService.addAspect(rootNode, VersionModel.ASPECT_VERSION_STORE_ROOT, null); // Create caveat config caveatConfig = nodeService.createNode(rootNode, RecordsManagementModel.ASSOC_CAVEAT_CONFIG, QName.createQName(RecordsManagementModel.RM_URI, CAVEAT_CONFIG_NAME), RecordsManagementModel.TYPE_CAVEAT_CONFIG).getChildRef(); nodeService.setProperty(caveatConfig, ContentModel.PROP_NAME, CAVEAT_CONFIG_NAME); } return caveatConfig; } // Get list of all caveat qualified names public Collection<String> getRMConstraintNames() { Collection<String> rmConstraintNames = Collections.emptySet(); try { readLock.lock(); rmConstraintNames = caveatConfig.getKeys(); } finally { readLock.unlock(); } return Collections.unmodifiableCollection(rmConstraintNames); } // Get allowed values for given caveat (for current user) public List<String> getRMAllowedValues(String constraintName) { List<String> allowedValues = new ArrayList<String>(0); String userName = AuthenticationUtil.getRunAsUser(); if (userName != null && !(AuthenticationUtil.isMtEnabled() && AuthenticationUtil.isRunAsUserTheSystemUser())) { // note: userName and userGroupNames must not be null caveatConfig.get(constraintName); Set<String> userGroupFullNames = authorityService.getAuthoritiesForUser(userName); allowedValues = getRMAllowedValues(userName, userGroupFullNames, constraintName); } return allowedValues; } private List<String> getRMAllowedValues(String userName, Set<String> userGroupFullNames, String constraintName) { Set<String>allowedValues = new HashSet<String>(); // note: userName and userGroupNames must not be null Map<String, List<String>> caveatConstraintDef = null; try { readLock.lock(); caveatConstraintDef = caveatConfig.get(constraintName); } finally { readLock.unlock(); } if (caveatConstraintDef != null) { List<String> direct = caveatConstraintDef.get(userName); if(direct != null) { allowedValues.addAll(direct); } for (String group : userGroupFullNames) { List<String> values = caveatConstraintDef.get(group); if(values != null) { allowedValues.addAll(values); } } } List<String>ret = new ArrayList<String>(); ret.addAll(allowedValues); return Collections.unmodifiableList(ret); } /** * Check whether access to 'record component' node is vetoed for current user due to caveat(s) * * @param nodeRef * @return false, if caveat(s) veto access otherwise return true */ @SuppressWarnings("unchecked") public boolean hasAccess(NodeRef nodeRef) { try { if ((! nodeService.exists(nodeRef)) || (caveatAspectQNames.size() == 0)) { return true; } boolean found = false; for (QName caveatAspectQName : caveatAspectQNames) { if (nodeService.hasAspect(nodeRef, caveatAspectQName)) { found = true; break; } } if (! found) { // no caveat aspect return true; } else { // check for caveats String userName = AuthenticationUtil.getRunAsUser(); if (userName != null) { // check all text properties Map<QName, Serializable> props = nodeService.getProperties(nodeRef); for (Map.Entry<QName, Serializable> entry : props.entrySet()) { QName propName = entry.getKey(); PropertyDefinition propDef = dictionaryService.getProperty(propName); if ((propDef != null) && (propDef.getDataType().getName().equals(DATATYPE_TEXT))) { List<ConstraintDefinition> conDefs = propDef.getConstraints(); for (ConstraintDefinition conDef : conDefs) { Constraint con = conDef.getConstraint(); if (con instanceof RMListOfValuesConstraint) { RMListOfValuesConstraint rmCon = ((RMListOfValuesConstraint)con); String conName = rmCon.getShortName(); MatchLogic matchLogic = rmCon.getMatchLogicEnum(); Map<String, List<String>> caveatConstraintDef = caveatConfig.get(conName); if (caveatConstraintDef == null) { continue; } else { Set<String> userGroupNames = authorityService.getAuthoritiesForUser(userName); List<String> allowedValues = getRMAllowedValues(userName, userGroupNames, conName); List<String> propValues = null; Object val = entry.getValue(); if (val instanceof String) { propValues = new ArrayList<String>(1); propValues.add((String)val); } else if (val instanceof List) { propValues = (List<String>)val; } if (propValues != null && !isAllowed(propValues, allowedValues, matchLogic)) { if (logger.isDebugEnabled()) { logger.debug("Veto access: caveat="+conName+", userName="+userName+", nodeRef="+nodeRef+", propName="+propName+", propValues="+propValues+", allowedValues="+allowedValues); } return false; } } } } } } } return true; } } catch (AccessDeniedException ade) { return false; } } private boolean isAllowed(List<String> propValues, List<String> userGroupValues, MatchLogic matchLogic) { if (matchLogic.equals(MatchLogic.AND)) { // check user/group values match all values on node for (String propValue : propValues) { if (! userGroupValues.contains(propValue)) { if (logger.isTraceEnabled()) { logger.trace("Not allowed: "+propValues+", "+userGroupValues+", "+matchLogic); } return false; } } return true; } else if (matchLogic.equals(MatchLogic.OR)) { // check user/group values match at least one value on node for (String propValue : propValues) { if (userGroupValues.contains(propValue)) { return true; } } if (logger.isTraceEnabled()) { logger.trace("Not allowed: "+propValues+", "+userGroupValues+", "+matchLogic); } return false; } logger.error("Unexpected match logic type: "+matchLogic); return false; } /** * Add a single value to an authority in a list. The existing values of the list remain. * * @param listName the name of the RMConstraintList * @param authorityName * @param value * @throws AlfrescoRuntimeException if either the list or the authority do not already exist. */ public void addRMConstraintListValue(String listName, String authorityName, String value) { Map<String, List<String>> members = null; try { readLock.lock(); members = caveatConfig.get(listName); if(members == null) { throw new AlfrescoRuntimeException("unable to add to list, list not defined:"+ listName); } try { readLock.unlock(); writeLock.lock(); // check again members = caveatConfig.get(listName); if(members == null) { throw new AlfrescoRuntimeException("unable to add to list, list not defined:"+ listName); } List<String> values = members.get(authorityName); if(values == null) { throw new AlfrescoRuntimeException("Unable to add to authority in list. Authority not member listName: "+ listName + " authorityName:" +authorityName); } values.add(value); caveatConfig.put(listName, members); updateOrCreateCaveatConfig(convertToJSONString(caveatConfig)); } finally { readLock.lock(); writeLock.unlock(); } } finally { readLock.unlock(); } } /** * Get the member details of the specified list * @param listName * @return the details of the specified list */ public Map<String, List<String>> getListDetails(String listName) { Map<String, List<String>> listDetails = null; try { readLock.lock(); listDetails = caveatConfig.get(listName); } finally { readLock.unlock(); } if (listDetails == null) { return Collections.emptyMap(); } else { return Collections.unmodifiableMap(listDetails); } } public List<QName> getRMCaveatModels() { return caveatModelQNames; } /** * Replace the values for an authority in a list. * The existing values are removed. * * If the authority does not already exist in the list, it will be added * * @param listName the name of the RMConstraintList * @param authorityName * @param values */ public void updateRMConstraintListAuthority(String listName, String authorityName, List<String>values) { Map<String, List<String>> members = null; try { writeLock.lock(); members = caveatConfig.get(listName); if(members == null) { // Create the new list, with the authority name Map<String, List<String>> constraint = new HashMap<String, List<String>>(0); constraint.put(authorityName, new ArrayList<String>(values)); members = constraint; } else { members.put(authorityName, new ArrayList<String>(values)); } caveatConfig.put(listName, members); updateOrCreateCaveatConfig(convertToJSONString(caveatConfig)); } finally { writeLock.unlock(); } } /** * Replace the authorities for a value in a list * * @param listName * @param valueName * @param authorities */ public void updateRMConstraintListValue(String listName, String valueName, List<String>authorities) { Map<String, List<String>> members = caveatConfig.get(listName); try { writeLock.lock(); if(members == null) { // Members List does not exist Map<String, List<String>> emptyConstraint = new HashMap<String, List<String>>(0); caveatConfig.put(listName, emptyConstraint); members = emptyConstraint; } // authorities contains authority, values[] // pivot contains value, members[] Map<String, List<String>> pivot = PivotUtil.getPivot(members); // remove all authorities which have this value List<String> existingAuthorities = pivot.get(valueName); if(existingAuthorities != null) { for(String authority : existingAuthorities) { List<String> vals = members.get(authority); vals.remove(valueName); } } // add the new authorities for this value for(String authority : authorities) { List<String> vals = members.get(authority); if(vals == null) { vals= new ArrayList<String>(); members.put(authority, vals); } vals.add(valueName); } caveatConfig.put(listName, members); updateOrCreateCaveatConfig(convertToJSONString(caveatConfig)); } finally { writeLock.unlock(); } } public void removeRMConstraintListValue(String listName, String valueName) { Map<String, List<String>> members = null; try { readLock.lock(); members = caveatConfig.get(listName); if (members != null) { try { readLock.unlock(); writeLock.lock(); // check again members = caveatConfig.get(listName); if (members != null) { // authorities contains authority, values[] // pivot contains value, members[] Map<String, List<String>> pivot = PivotUtil.getPivot(members); // remove all authorities which have this value List<String> existingAuthorities = pivot.get(valueName); if(existingAuthorities != null) { for(String authority : existingAuthorities) { List<String> vals = members.get(authority); vals.remove(valueName); } caveatConfig.put(listName, members); } } updateOrCreateCaveatConfig(convertToJSONString(caveatConfig)); } finally { readLock.lock(); writeLock.unlock(); } } } finally { readLock.unlock(); } } /** * Remove an authority from a list * * @param listName the name of the RMConstraintList * @param authorityName * @param values */ public void removeRMConstraintListAuthority(String listName, String authorityName) { Map<String, List<String>> members = null; try { writeLock.lock(); members = caveatConfig.get(listName); if(members != null) { members.remove(listName); } caveatConfig.put(listName, members); updateOrCreateCaveatConfig(convertToJSONString(caveatConfig)); } finally { writeLock.unlock(); } } /** * @param config the configuration to convert * @return a String containing the JSON representation of the configuration. */ private String convertToJSONString(SimpleCache<String, Map<String, List<String>>> config) { JSONObject configJSONObject = new JSONObject(); Collection<String> listNames = config.getKeys(); for (String listName : listNames) { Map<String, List<String>> members = config.get(listName); Set<String> authorityNames = members.keySet(); JSONObject listMembers = new JSONObject(); for (String authorityName : authorityNames) { List<String> authorities = members.get(authorityName); try { listMembers.put(authorityName, authorities); } catch (JSONException error) { StringBuilder sb = new StringBuilder(); sb.append("Cannot add the key '"); sb.append(authorityName); sb.append("' with the value '"); sb.append(authorities); sb.append("' to the JSONObject 'listMembers' '"); sb.append(listMembers); sb.append("': "); sb.append(getFullStackTrace(error)); throw new AlfrescoRuntimeException(sb.toString()); } } try { configJSONObject.put(listName, listMembers); } catch (JSONException error) { StringBuilder sb = new StringBuilder(); sb.append("Cannot add the key '"); sb.append(listName); sb.append("' with the value '"); sb.append(listMembers); sb.append("' to the JSONObject 'configJSONObject' '"); sb.append(configJSONObject); sb.append("': "); sb.append(getFullStackTrace(error)); throw new AlfrescoRuntimeException(sb.toString()); } } return configJSONObject.toString(); } /** * Get an RMConstraintInfo * @param listQName * @return the constraint or null if it does not exist */ public RMConstraintInfo getRMConstraint(QName listQName) { ConstraintDefinition dictionaryDef = dictionaryService.getConstraint(listQName); if(dictionaryDef != null) { Constraint con = dictionaryDef.getConstraint(); if (con instanceof RMListOfValuesConstraint) { final RMListOfValuesConstraint def = (RMListOfValuesConstraint)con; RMConstraintInfo info = new RMConstraintInfo(); info.setName(listQName.toPrefixString()); info.setTitle(con.getTitle()); List<String> allowedValues = AuthenticationUtil.runAs(new RunAsWork<List<String>>() { public List<String> doWork() { return def.getAllowedValues(); } }, AuthenticationUtil.getSystemUserName()); info.setAllowedValues(allowedValues.toArray(new String[allowedValues.size()])); info.setCaseSensitive(def.isCaseSensitive()); return info; } } return null; } /** * Get RM Constraint detail. * * @return the constraintInfo or null */ public RMConstraintInfo getRMConstraint(String listName) { QName listQName = QName.createQName(listName, namespaceService); return getRMConstraint(listQName); } public void deleteRMConstraint(String listName) { try { writeLock.lock(); caveatConfig.remove(listName); updateOrCreateCaveatConfig(convertToJSONString(caveatConfig)); } finally { writeLock.unlock(); } } public void addRMConstraint(String listName) { try { writeLock.lock(); Map<String, List<String>> emptyConstraint = new HashMap<String, List<String>>(0); caveatConfig.put(listName, emptyConstraint); updateOrCreateCaveatConfig(convertToJSONString(caveatConfig)); } finally { writeLock.unlock(); } } }