diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-webscript-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-webscript-context.xml index 7a31fd1b76..da41dd3d0e 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-webscript-context.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-webscript-context.xml @@ -585,6 +585,19 @@ + + + + + + + + + + + + Get webscript for removing dynamic authorities from RM + Gets the result after removing dynamic authorities from RM. + /api/rm/rm-dynamicauthorities?batchsize={batchsize} + argument + user + required + \ No newline at end of file diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/repository/roles/rm-dynamicauthorities.get.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/repository/roles/rm-dynamicauthorities.get.json.ftl new file mode 100644 index 0000000000..10971542cb --- /dev/null +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/repository/roles/rm-dynamicauthorities.get.json.ftl @@ -0,0 +1,4 @@ +{ + "responsestatus" : "${responsestatus?json_string}", + "message" : "${message?json_string}" +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/repo/web/scripts/roles/DynamicAuthoritiesGet.java b/rm-server/source/java/org/alfresco/repo/web/scripts/roles/DynamicAuthoritiesGet.java new file mode 100644 index 0000000000..67e77045c9 --- /dev/null +++ b/rm-server/source/java/org/alfresco/repo/web/scripts/roles/DynamicAuthoritiesGet.java @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2005-2014 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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 . + */ +package org.alfresco.repo.web.scripts.roles; + +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.module.org_alfresco_module_rm.security.ExtendedReaderDynamicAuthority; +import org.alfresco.module.org_alfresco_module_rm.security.ExtendedSecurityService; +import org.alfresco.module.org_alfresco_module_rm.security.ExtendedWriterDynamicAuthority; +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.patch.PatchDAO; +import org.alfresco.repo.domain.qname.QNameDAO; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.Pair; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * Webscript used for removing dynamic authorities from the records. + * + * @author Silviu Dinuta + * @since 2.3.0.7 + */ +@SuppressWarnings("deprecation") +public class DynamicAuthoritiesGet extends DeclarativeWebScript implements RecordsManagementModel +{ + + private static final String MESSAGE_PROCESSING_BEGIN = "Processing - BEGIN"; + private static final String MESSAGE_PROCESSING_END = "Processing - END"; + private static final String MESSAGE_PROCESSING_RECORD_END_TEMPLATE = "Processing record {0} - END"; + private static final String MESSAGE_PROCESSING_RECORD_BEGIN_TEMPLATE = "Processing record {0} - BEGIN"; + private static final String MESSAGE_BATCHSIZE_IS_INVALID = "Parameter batchsize is invalid."; + private static final String MESSAGE_BATCHSIZE_IS_MANDATORY = "Parameter batchsize is mandatory"; + private static final String SUCCESS_STATUS = "success"; + private static final String FAILED_STATUS = "failed"; + /** + * The logger + */ + private static Log logger = LogFactory.getLog(DynamicAuthoritiesGet.class); + private static final String BATCH_SIZE = "batchsize"; + private static final String TOTAL_NUMBER_TO_PROCESS = "maxProcessedRecords"; + private static final String MODEL_STATUS = "responsestatus"; + private static final String MODEL_MESSAGE = "message"; + private static final String MESSAGE_ALL_TEMPLATE = "Processed {0} records."; + private static final String MESSAGE_PARTIAL_TEMPLATE = "Processed first {0} records."; + private static final String MESSAGE_NO_RECORDS_TO_PROCESS = "There where no records to be processed."; + + + /** services */ + private PatchDAO patchDAO; + private NodeDAO nodeDAO; + private QNameDAO qnameDAO; + private NodeService nodeService; + private PermissionService permissionService; + private ExtendedSecurityService extendedSecurityService; + private TransactionService transactionService; + + /** service setters */ + public void setPatchDAO(PatchDAO patchDAO) { this.patchDAO = patchDAO; } + public void setNodeDAO(NodeDAO nodeDAO) { this.nodeDAO = nodeDAO; } + public void setQnameDAO(QNameDAO qnameDAO) { this.qnameDAO = qnameDAO; } + public void setNodeService(NodeService nodeService) { this.nodeService = nodeService; } + public void setPermissionService(PermissionService permissionService) { this.permissionService = permissionService; } + public void setExtendedSecurityService(ExtendedSecurityService extendedSecurityService) { this.extendedSecurityService = extendedSecurityService; } + public void setTransactionService(TransactionService transactionService) { this.transactionService = transactionService; } + + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + Map model = new HashMap(); + String batchSizeStr = req.getParameter(BATCH_SIZE); + String totalToBeProcessedRecordsStr = req.getParameter(TOTAL_NUMBER_TO_PROCESS); + + Long size = 0L; + if (batchSizeStr == null || batchSizeStr.length() == 0) + { + model.put(MODEL_STATUS, FAILED_STATUS); + model.put(MODEL_MESSAGE, MESSAGE_BATCHSIZE_IS_MANDATORY); + if (logger.isDebugEnabled()) + { + logger.debug(MESSAGE_BATCHSIZE_IS_MANDATORY); + } + return model; + } + try + { + size = Long.parseLong(batchSizeStr); + } + catch(NumberFormatException ex) + { + model.put(MODEL_STATUS, FAILED_STATUS); + model.put(MODEL_MESSAGE, MESSAGE_BATCHSIZE_IS_INVALID); + if (logger.isDebugEnabled()) + { + logger.debug(MESSAGE_BATCHSIZE_IS_INVALID); + } + return model; + } + final Long batchSize = size; + // get the max node id and the extended security aspect + Long maxNodeId = patchDAO.getMaxAdmNodeID(); + final Pair recordAspectPair = qnameDAO.getQName(ASPECT_EXTENDED_SECURITY); + if(recordAspectPair == null) + { + model.put(MODEL_STATUS, SUCCESS_STATUS); + model.put(MODEL_MESSAGE, MESSAGE_NO_RECORDS_TO_PROCESS); + if (logger.isDebugEnabled()) + { + logger.debug(MESSAGE_NO_RECORDS_TO_PROCESS); + } + return model; + } + + Long totalNumberOfRecordsToProcess = 0L; + if (totalToBeProcessedRecordsStr != null && totalToBeProcessedRecordsStr.length() != 0) + { + try + { + totalNumberOfRecordsToProcess = Long.parseLong(totalToBeProcessedRecordsStr); + } + catch(NumberFormatException ex) + { + //do nothing here, the value will remain 0L in this case + } + } + + final Long maxRecordsToProcess = totalNumberOfRecordsToProcess; + final List processedNodes = new ArrayList(); + if (logger.isDebugEnabled()) + { + logger.debug(MESSAGE_PROCESSING_BEGIN); + } + // by batch size + for (Long i = 0L; i < maxNodeId; i+=batchSize) + { + if(maxRecordsToProcess != 0 && processedNodes.size() >= maxRecordsToProcess) + { + break; + } + final Long currentIndex = i; + + transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + // get the nodes with the extended security aspect applied + List nodeIds = patchDAO.getNodesByAspectQNameId(recordAspectPair.getFirst(), currentIndex, currentIndex + batchSize); + + // process each one + for (Long nodeId : nodeIds) + { + if(maxRecordsToProcess != 0 && processedNodes.size() >= maxRecordsToProcess) + { + break; + } + NodeRef record = nodeDAO.getNodePair(nodeId).getSecond(); + if (logger.isDebugEnabled()) + { + logger.debug(MessageFormat.format(MESSAGE_PROCESSING_RECORD_BEGIN_TEMPLATE, (String) nodeService.getProperty(record, ContentModel.PROP_NAME))); + } + processNode(record); + if (logger.isDebugEnabled()) + { + logger.debug(MessageFormat.format(MESSAGE_PROCESSING_RECORD_END_TEMPLATE, (String) nodeService.getProperty(record, ContentModel.PROP_NAME))); + } + processedNodes.add(record); + } + + return null; + } + }, + false, // read only + true); // requires new + } + if (logger.isDebugEnabled()) + { + logger.debug(MESSAGE_PROCESSING_END); + } + int processedNodesSize = processedNodes.size(); + String message = ""; + if(totalNumberOfRecordsToProcess == 0 || (totalNumberOfRecordsToProcess > 0 && processedNodesSize < totalNumberOfRecordsToProcess)) + { + message = MessageFormat.format(MESSAGE_ALL_TEMPLATE, processedNodesSize); + } + if (totalNumberOfRecordsToProcess > 0 && totalNumberOfRecordsToProcess == processedNodesSize) + { + message = MessageFormat.format(MESSAGE_PARTIAL_TEMPLATE, totalNumberOfRecordsToProcess); + } + model.put(MODEL_STATUS, SUCCESS_STATUS); + model.put(MODEL_MESSAGE, message); + if (logger.isDebugEnabled()) + { + logger.debug(message); + } + return model; + } + + /** + * Process each node + * + * @param nodeRef + */ + @SuppressWarnings({ "unchecked"}) + private void processNode(NodeRef nodeRef) + { + // get the reader/writer data + Map readers = (Map)nodeService.getProperty(nodeRef, PROP_READERS); + Map writers = (Map)nodeService.getProperty(nodeRef, PROP_WRITERS); + + // remove extended security aspect + nodeService.removeAspect(nodeRef, ASPECT_EXTENDED_SECURITY); + + // remove dynamic authority permissions + permissionService.clearPermission(nodeRef, ExtendedReaderDynamicAuthority.EXTENDED_READER); + permissionService.clearPermission(nodeRef, ExtendedWriterDynamicAuthority.EXTENDED_WRITER); + + // if record then ... + if (nodeService.hasAspect(nodeRef, ASPECT_RECORD)) + { + // re-set extended security via API + extendedSecurityService.set(nodeRef, readers.keySet(), writers.keySet()); + } + } +}