Merge release/V2.3.0.x into release/V2.3

This commit is contained in:
Ana Bozianu
2016-09-20 14:00:59 +03:00
52 changed files with 5154 additions and 1262 deletions

14
pom.xml
View File

@@ -78,6 +78,7 @@
<!-- Maven specific build properties -->
<maven.alfresco.includeDependencies>false</maven.alfresco.includeDependencies>
<maven.build.sourceVersion>1.7</maven.build.sourceVersion>
<maven.build.testSourceVersion>1.8</maven.build.testSourceVersion>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<argLine>-Xmx1024m -XX:MaxPermSize=256m -Duser.language=en -Dcom.sun.management.jmxremote</argLine>
</properties>
@@ -148,6 +149,19 @@
<source>${maven.build.sourceVersion}</source>
<target>${maven.build.sourceVersion}</target>
</configuration>
<executions>
<execution>
<id>default-testCompile</id>
<phase>process-test-sources</phase>
<goals>
<goal>testCompile</goal>
</goals>
<configuration>
<source>${maven.build.testSourceVersion}</source>
<target>${maven.build.testSourceVersion}</target>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>

View File

@@ -58,6 +58,7 @@
org.alfresco.service.cmr.security.PermissionService.hasPermission=ACL_ALLOW
org.alfresco.service.cmr.security.PermissionService.getReaders=ACL_METHOD.ROLE_ADMINISTRATOR
org.alfresco.repo.security.permissions.impl.ExtendedPermissionService.getWriters=ACL_METHOD.ROLE_ADMINISTRATOR
org.alfresco.repo.security.permissions.impl.ExtendedPermissionService.getReadersAndWriters=ACL_METHOD.ROLE_ADMINISTRATOR
org.alfresco.service.cmr.security.PermissionService.deletePermissions=ACL_NODE.0.sys:base.ChangePermissions
org.alfresco.service.cmr.security.PermissionService.deletePermission=ACL_NODE.0.sys:base.ChangePermissions
org.alfresco.service.cmr.security.PermissionService.setPermission=ACL_NODE.0.sys:base.ChangePermissions

View File

@@ -56,3 +56,4 @@ log4j.logger.org.alfresco.module.org_alfresco_module_rm.patch=info
# Job debug
#
#log4j.logger.org.alfresco.module.org_alfresco_module_rm.job=debug
log4j.logger.org.alfresco.repo.web.scripts.roles.DynamicAuthoritiesGet=info

View File

@@ -574,6 +574,9 @@
<property name="nodeService" ref="nodeService"/>
<property name="filePlanService" ref="filePlanService" />
<property name="filePlanRoleService" ref="filePlanRoleService" />
<property name="authorityService" ref="authorityService"/>
<property name="permissionService" ref="permissionService"/>
<property name="transactionService" ref="transactionService"/>
</bean>
<bean id="ExtendedSecurityService" class="org.springframework.aop.framework.ProxyFactoryBean">

View File

@@ -28,7 +28,6 @@
<property name="cmObjectType" ref="cm.object" />
<property name="extendedPermissionService" ref="ExtendedPermissionService" />
<property name="extendedSecurityService" ref="ExtendedSecurityService" />
<property name="ownableService" ref="OwnableService" />
</bean>
<bean class="org.alfresco.util.BeanExtender">
<property name="beanName" value="versionService" />

View File

@@ -587,6 +587,19 @@
</property>
</bean>
<!-- Remove Dynamic Authorities GET webscript -->
<bean id="webscript.org.alfresco.repository.roles.rm-dynamicauthorities.get"
class="org.alfresco.repo.web.scripts.roles.DynamicAuthoritiesGet"
parent="webscript">
<property name="patchDAO" ref="patchDAO"/>
<property name="nodeDAO" ref="nodeDAO"/>
<property name="qnameDAO" ref="qnameDAO"/>
<property name="nodeService" ref="nodeService"/>
<property name="permissionService" ref="permissionService"/>
<property name="extendedSecurityService" ref="extendedSecurityService"/>
<property name="transactionService" ref="transactionService"/>
</bean>
<!-- REST impl for GET Holds -->
<bean id="webscript.org.alfresco.rma.holds.get"
class="org.alfresco.module.org_alfresco_module_rm.script.hold.HoldsGet"

View File

@@ -0,0 +1,13 @@
<webscript>
<shortname>Removes dynamic authorities</shortname>
<description><![CDATA[
Removes dynamic authorities from in place records created in previous verssions.<br/>
URL parameter batchsize is mandatory, and represents the number of records that are processed in one transaction.<br/>
URL parameter maxProcessedRecords is optional, and represents the maximum number of records that will be processed in one request.<br/>
]]>
</description>
<url>/api/rm/rm-dynamicauthorities?batchsize={batchsize}&amp;maxProcessedRecords={maxProcessedRecords?}</url>
<format default="json">argument</format>
<authentication>admin</authentication>
<transaction allow="readonly">required</transaction>
</webscript>

View File

@@ -0,0 +1,4 @@
{
"responsestatus" : "${responsestatus?json_string}",
"message" : "${message?json_string}"
}

View File

@@ -0,0 +1,148 @@
/*
* #%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.security;
import java.util.Set;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* Deprecated extended security service for compatibility.
*
* @author Roy Wetherall
*/
public interface DeprecatedExtendedSecurityService
{
/**
* Gets the set of authorities that are extended readers for the given node.
*
* @param nodeRef node reference
* @return {@link Set}<{@link String}> set of extended readers
*
* @deprecated as of 2.5, use {@link ExtendedSecurityService#getReaders(NodeRef)}
*/
Set<String> getExtendedReaders(NodeRef nodeRef);
/**
* Get the set of authorities that are extended writers for the given node.
*
* @param nodeRef node reference
* @return {@link Set}<{@link String}> set of extended writers
*
* @deprecated as of 2.5, use {@link ExtendedSecurityService#getWriters(NodeRef)}
*/
Set<String> getExtendedWriters(NodeRef nodeRef);
/**
* Add extended security for the specified authorities to a node.
*
* As of, 2.5 this method no longer applies the extended security to parents.
*
* @param nodeRef node reference
* @param readers set of authorities to add extended read permissions
* @param writers set of authorities to add extended write permissions
*
* @deprecated as of 2.5, use {@link ExtendedSecurityService#set(NodeRef, Set, Set)}
*/
@Deprecated
void addExtendedSecurity(NodeRef nodeRef, Set<String> readers, Set<String> writers);
/**
* Add extended security for the specified authorities to a node.
* <p>
* If specified, the read and write extended permissions are applied to all parents up to the file plan as
* extended read. This ensures parental read, but not parental write.
*
* @param nodeRef node reference
* @param readers set of authorities to add extended read permissions
* @param writers set of authorities to add extended write permissions
* @param applyToParents true if extended security applied to parents (read only) false otherwise.
*
* @deprecated as of 2.5, because extended security is no longer applied to parents. Note that calling this method will
* only apply the extended security to the node and the applyToParents parameter value will be ignored.
*
* @see ExtendedSecurityService#set(NodeRef, Set, Set)
*/
@Deprecated void addExtendedSecurity(NodeRef nodeRef, Set<String> readers, Set<String> writers, boolean applyToParents);
/**
* Remove all extended readers and writers from the given node reference.
*
* @param nodeRef node reference
*
* @deprecated as of 2.5, see {@link ExtendedSecurityService#remove(NodeRef)}
*/
@Deprecated void removeAllExtendedSecurity(NodeRef nodeRef);
/**
* Remove the extended security for the specified authorities from a node.
*
* @param nodeRef node reference
* @param readers set of authorities to remove as extended readers
* @param writers set of authorities to remove as extended writers
*
* @deprecated as of 2.5, because partial removal of readers and writers from node or parents is no longer supported.
* Note that calling this method will now remove all extended security from the node and never applied to parents.
*
* @see {@link ExtendedSecurityService#remove(NodeRef)}
*/
@Deprecated void removeExtendedSecurity(NodeRef nodeRef, Set<String> readers, Set<String> writers);
/**
* Remove the extended security for the specified authorities from a node.
* <p>
* If specified, extended security will also be removed from the parent hierarchy.(read only). Note that
* extended security is records as a reference count, so security will only be utterly removed from the parent
* hierarchy if all references to the authority are removed.
*
* @param nodeRef node reference
* @param readers set of authorities to remove as extended readers
* @param writers set of authorities to remove as extedned writers
* @param applyToParents true if removal of extended security is applied to parent hierarchy (read only), false
* otherwise
*
* @deprecated as of 2.5, because partial removal of readers and writers from node or parents is no longer supported.
* Note that calling this method will now remove all extended security from the node and never applied to parents.
*
* @see {@link ExtendedSecurityService#remove(NodeRef)}
*/
@Deprecated void removeExtendedSecurity(NodeRef nodeRef, Set<String> readers, Set<String> writers, boolean applyToParents);
/**
* Remove all extended readers and writers from the given node reference.
*
* @param nodeRef node reference
* @param applyToParents if true then apply removal to parent hierarchy (read only) false otherwise.
*
* @deprecated as of 2.5, because partial removal of readers and writers from node or parents is no longer supported.
* Note that calling this method will now remove all extended security from the node and never applied to parents.
*
* @see {@link ExtendedSecurityService#remove(NodeRef)}
*/
@Deprecated void removeAllExtendedSecurity(NodeRef nodeRef, boolean applyToParents);
}

View File

@@ -1,21 +1,30 @@
/*
* Copyright (C) 2005-2014 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* #%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.security;
import java.util.Collections;
@@ -32,6 +41,7 @@ import org.alfresco.service.cmr.repository.NodeRef;
* @author Roy Wetherall
* @since 2.1
*/
@Deprecated
public class ExtendedReaderDynamicAuthority extends ExtendedSecurityBaseDynamicAuthority
{
/** Extended reader role */

View File

@@ -1,21 +1,30 @@
/*
* Copyright (C) 2005-2014 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* #%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.security;
import java.util.Map;
@@ -39,6 +48,7 @@ import org.springframework.context.ApplicationContextAware;
* @author Roy Wetherall
* @since 2.1
*/
@Deprecated
public abstract class ExtendedSecurityBaseDynamicAuthority implements DynamicAuthority,
RecordsManagementModel,
ApplicationContextAware

View File

@@ -1,21 +1,30 @@
/*
* Copyright (C) 2005-2014 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* #%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.security;
import java.util.Collections;
@@ -33,6 +42,7 @@ import org.alfresco.service.cmr.repository.NodeRef;
* @author Roy Wetherall
* @since 2.1
*/
@Deprecated
public class ExtendedWriterDynamicAuthority extends ExtendedSecurityBaseDynamicAuthority
{
/** Extended writer role */

View File

@@ -1,21 +1,30 @@
/*
* Copyright (C) 2005-2014 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* #%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.security;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
@@ -26,6 +35,7 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
*
* @author Roy Wetherall
* @since 2.1
* @deprecated as of 2.2, use {@link AuthenticationUtil}.
*/
public interface FilePlanAuthenticationService
{

View File

@@ -1,21 +1,30 @@
/*
* Copyright (C) 2005-2014 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* #%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.security;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
@@ -25,6 +34,7 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
* @author Roy Wetherall
* @since 2.1
*/
@Deprecated
public class FilePlanAuthenticationServiceImpl implements FilePlanAuthenticationService
{
/** Default rm admin user values */

View File

@@ -188,14 +188,12 @@ public abstract class CopyMoveLinkFileToBaseAction extends RMActionExecuterAbstr
}
catch (FileNotFoundException fileNotFound)
{
throw new AlfrescoRuntimeException(
"Unable to execute file to action, because the " + (mode == CopyMoveLinkFileToActionMode.MOVE ? "move" : "copy") + " operation failed.",
fileNotFound
);
throw new AlfrescoRuntimeException("Unable to execute file to action, because the " + (mode == CopyMoveLinkFileToActionMode.MOVE ? "move" : "copy") + " operation failed.", fileNotFound);
}
return null;
}
});
}
}

View File

@@ -48,6 +48,9 @@ import org.springframework.extensions.surf.util.I18NUtil;
*/
public class DeclareRecordAction extends RMActionExecuterAbstractBase
{
/** action name */
public static final String NAME = "declareRecord";
/** I18N */
private static final String MSG_UNDECLARED_ONLY_RECORDS = "rm.action.undeclared-only-records";
private static final String MSG_NO_DECLARE_MAND_PROP = "rm.action.no-declare-mand-prop";

View File

@@ -33,11 +33,10 @@ import org.alfresco.model.ContentModel;
import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel;
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
import org.alfresco.module.org_alfresco_module_rm.role.FilePlanRoleService;
import org.alfresco.module.org_alfresco_module_rm.security.ExtendedReaderDynamicAuthority;
import org.alfresco.module.org_alfresco_module_rm.security.ExtendedWriterDynamicAuthority;
import org.alfresco.module.org_alfresco_module_rm.util.ServiceBaseImpl;
import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.repo.domain.node.NodeDAO;
import org.alfresco.repo.rule.RuleModel;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
@@ -347,23 +346,12 @@ public class FilePlanServiceImpl extends ServiceBaseImpl
containerType,
properties).getChildRef();
// if (!inheritPermissions)
// {
// set inheritance to false
getPermissionService().setInheritParentPermissions(container, false);
getPermissionService().setPermission(container, allRoles, RMPermissionModel.READ_RECORDS, true);
getPermissionService().setPermission(container, ExtendedReaderDynamicAuthority.EXTENDED_READER, RMPermissionModel.READ_RECORDS, true);
getPermissionService().setPermission(container, ExtendedWriterDynamicAuthority.EXTENDED_WRITER, RMPermissionModel.FILING, true);
// TODO set the admin users to have filing permissions on the unfiled container!!!
// TODO we will need to be able to get a list of the admin roles from the service
// }
// else
// {
// just inherit eveything
// TODO will change this when we are able to set permissions on holds and transfers!
// getPermissionService().setInheritParentPermissions(container, true);
// }
// prevent inheritance of rules
nodeService.addAspect(container, RuleModel.ASPECT_IGNORE_INHERITED_RULES, null);
return container;
}

View File

@@ -242,9 +242,10 @@ public interface RecordsManagementModel extends RecordsManagementCustomModel
QName PROP_LOADED_DATA_SET_IDS = QName.createQName(RM_URI, "loadedDataSetIds");
// Extended security aspect
QName ASPECT_EXTENDED_SECURITY = QName.createQName(RM_URI, "extendedSecurity");
QName PROP_READERS = QName.createQName(RM_URI, "readers");
QName PROP_WRITERS = QName.createQName(RM_URI, "writers");
// @deprecated as of 2.5, because of performance issues
@Deprecated QName ASPECT_EXTENDED_SECURITY = QName.createQName(RM_URI, "extendedSecurity");
@Deprecated QName PROP_READERS = QName.createQName(RM_URI, "readers");
@Deprecated QName PROP_WRITERS = QName.createQName(RM_URI, "writers");
// Originating details of a record
QName ASPECT_RECORD_ORIGINATING_DETAILS = QName.createQName(RM_URI, "recordOriginatingDetails");

View File

@@ -30,6 +30,7 @@ import org.alfresco.module.org_alfresco_module_rm.record.RecordService;
import org.alfresco.module.org_alfresco_module_rm.security.ExtendedSecurityService;
import org.alfresco.repo.copy.CopyBehaviourCallback;
import org.alfresco.repo.copy.CopyDetails;
import org.alfresco.repo.copy.CopyServicePolicies;
import org.alfresco.repo.copy.DefaultCopyBehaviourCallback;
import org.alfresco.repo.node.NodeServicePolicies;
import org.alfresco.repo.policy.Behaviour.NotificationFrequency;
@@ -56,7 +57,8 @@ public class RecordAspect extends AbstractDisposableItem
implements NodeServicePolicies.OnCreateChildAssociationPolicy,
RecordsManagementPolicies.OnCreateReference,
RecordsManagementPolicies.OnRemoveReference,
NodeServicePolicies.OnMoveNodePolicy
NodeServicePolicies.OnMoveNodePolicy,
CopyServicePolicies.OnCopyCompletePolicy
{
/** Well-known location of the scripts folder. */
// TODO make configurable
@@ -123,11 +125,11 @@ public class RecordAspect extends AbstractDisposableItem
// manage any extended readers
NodeRef parent = childAssocRef.getParentRef();
Set<String> readers = extendedSecurityService.getExtendedReaders(parent);
Set<String> writers = extendedSecurityService.getExtendedWriters(parent);
Set<String> readers = extendedSecurityService.getReaders(parent);
Set<String> writers = extendedSecurityService.getWriters(parent);
if (readers != null && readers.size() != 0)
{
extendedSecurityService.addExtendedSecurity(thumbnail, readers, writers, false);
extendedSecurityService.set(thumbnail, readers, writers);
}
}
@@ -287,4 +289,33 @@ public class RecordAspect extends AbstractDisposableItem
scriptService.executeScript(scriptNodeRef, null, objectModel);
}
}
/**
* On copy complete behaviour for record aspect.
*
* @param classRef
* @param sourceNodeRef
* @param targetNodeRef
* @param copyToNewNode
* @param copyMap
*/
@Override
@Behaviour
(
kind = BehaviourKind.CLASS
)
public void onCopyComplete(QName classRef,
NodeRef sourceNodeRef,
NodeRef targetNodeRef,
boolean copyToNewNode,
Map<NodeRef, NodeRef> copyMap)
{
// given the node exists and is a record
if (nodeService.exists(targetNodeRef) &&
nodeService.hasAspect(targetNodeRef, ASPECT_RECORD))
{
// then remove any extended security from the newly copied record
extendedSecurityService.remove(targetNodeRef);
}
}
}

View File

@@ -25,13 +25,10 @@ import java.util.Set;
import org.alfresco.model.ContentModel;
import org.alfresco.module.org_alfresco_module_rm.capability.Capability;
import org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService;
import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel;
import org.alfresco.module.org_alfresco_module_rm.dod5015.DOD5015Model;
import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService;
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
import org.alfresco.module.org_alfresco_module_rm.role.FilePlanRoleService;
import org.alfresco.module.org_alfresco_module_rm.security.ExtendedReaderDynamicAuthority;
import org.alfresco.module.org_alfresco_module_rm.security.ExtendedWriterDynamicAuthority;
import org.alfresco.module.org_alfresco_module_rm.security.FilePlanPermissionService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
@@ -155,10 +152,6 @@ public class RMv21InPlacePatch extends RMv21PatchComponent
ruleService.disableRules();
try
{
// set permissions
filePlanPermissionService.setPermission(filePlan, ExtendedReaderDynamicAuthority.EXTENDED_READER, RMPermissionModel.READ_RECORDS);
filePlanPermissionService.setPermission(filePlan, ExtendedWriterDynamicAuthority.EXTENDED_WRITER, RMPermissionModel.FILING);
// create fileplan containers
filePlanService.createHoldContainer(filePlan);
filePlanService.createTransferContainer(filePlan);

View File

@@ -21,7 +21,6 @@ package org.alfresco.module.org_alfresco_module_rm.record;
import static org.alfresco.model.ContentModel.ASPECT_PENDING_DELETE;
import java.util.List;
import java.util.Set;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
@@ -95,6 +94,9 @@ public class InplaceRecordServiceImpl extends ServiceBaseImpl implements Inplace
{
// remove the child association
NodeRef originatingLocation = (NodeRef) nodeService.getProperty(nodeRef, PROP_RECORD_ORIGINATING_LOCATION);
if (originatingLocation != null)
{
List<ChildAssociationRef> parentAssocs = nodeService.getParentAssocs(nodeRef);
for (ChildAssociationRef childAssociationRef : parentAssocs)
{
@@ -109,7 +111,8 @@ public class InplaceRecordServiceImpl extends ServiceBaseImpl implements Inplace
// remove the extended security from the node
// this prevents the users from continuing to see the record in searchs and other linked locations
extendedSecurityService.removeAllExtendedSecurity(nodeRef);
extendedSecurityService.remove(nodeRef);
}
return null;
}
@@ -164,18 +167,11 @@ public class InplaceRecordServiceImpl extends ServiceBaseImpl implements Inplace
{
try
{
// Get the extended readers/writers
Set<String> extendedReaders = extendedSecurityService.getExtendedReaders(nodeRef);
Set<String> extendedWriters = extendedSecurityService.getExtendedWriters(nodeRef);
// Move the record
fileFolderService.moveFrom(nodeRef, source, targetNodeRef, null);
// Update the originating location property
nodeService.setProperty(nodeRef, PROP_RECORD_ORIGINATING_LOCATION, targetNodeRef);
// Set the extended readers/writers
extendedSecurityService.addExtendedSecurity(nodeRef, extendedReaders, extendedWriters);
}
catch (FileExistsException | FileNotFoundException ex)
{

View File

@@ -96,6 +96,7 @@ import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern;
import org.alfresco.util.EqualsHelper;
import org.alfresco.util.Pair;
import org.alfresco.util.ParameterCheck;
import org.alfresco.util.PropertyMap;
import org.apache.commons.lang.ArrayUtils;
@@ -240,10 +241,6 @@ public class RecordServiceImpl extends BaseBehaviourBean
this,
"onCreateChildAssociation",
NotificationFrequency.FIRST_EVENT);
private JavaBehaviour onDeleteDeclaredRecordLink = new JavaBehaviour(
this,
"onDeleteDeclaredRecordLink",
NotificationFrequency.FIRST_EVENT);
/**
* @param identifierService identifier service
@@ -404,11 +401,6 @@ public class RecordServiceImpl extends BaseBehaviourBean
TYPE_RECORD_FOLDER,
ContentModel.ASSOC_CONTAINS,
onCreateChildAssociation);
policyComponent.bindAssociationBehaviour(
NodeServicePolicies.BeforeDeleteChildAssociationPolicy.QNAME,
ContentModel.TYPE_FOLDER,
ContentModel.ASSOC_CONTAINS,
onDeleteDeclaredRecordLink);
}
/**
@@ -579,27 +571,6 @@ public class RecordServiceImpl extends BaseBehaviourBean
}, AuthenticationUtil.getSystemUserName());
}
/**
* Looking specifically at linked content that was declared a record from a non-rm site.
* When the site or the folder that the link was declared in is deleted we need to remove
* the extended security property accounts in the tree
*
* @param childAssocRef
*/
public void onDeleteDeclaredRecordLink(ChildAssociationRef childAssocRef)
{
// Is the deleted child association not a primary association?
// Does the deleted child association have the rma:recordOriginatingDetails aspect?
// Is the parent of the deleted child association a folder (cm:folder)?
if (!childAssocRef.isPrimary() &&
nodeService.hasAspect(childAssocRef.getChildRef(), ASPECT_RECORD_ORIGINATING_DETAILS) &&
nodeService.getType(childAssocRef.getParentRef()).equals(ContentModel.TYPE_FOLDER))
{
// ..then remove the extended readers and writers up the tree for this remaining node
extendedSecurityService.removeExtendedSecurity(childAssocRef.getChildRef(), extendedSecurityService.getExtendedReaders(childAssocRef.getChildRef()), extendedSecurityService.getExtendedWriters(childAssocRef.getChildRef()), true);
}
}
/**
* @see org.alfresco.module.org_alfresco_module_rm.record.RecordService#disablePropertyEditableCheck()
*/
@@ -893,12 +864,10 @@ public class RecordServiceImpl extends BaseBehaviourBean
throw new AlfrescoRuntimeException("Unable to create record, because new record container could not be found.");
}
// get the documents readers
Long aclId = nodeService.getNodeAclId(nodeRef);
Set<String> readers = extendedPermissionService.getReaders(aclId);
Set<String> writers = extendedPermissionService.getWriters(aclId);
// get the documents readers and writers
Pair<Set<String>, Set<String>> readersAndWriters = extendedPermissionService.getReadersAndWriters(nodeRef);
// add the current owner to the list of extended writers
// get the current owner
String owner = ownableService.getOwner(nodeRef);
// get the documents primary parent assoc
@@ -950,13 +919,7 @@ public class RecordServiceImpl extends BaseBehaviourBean
nodeService.addChild(parentAssoc.getParentRef(), nodeRef, parentAssoc.getTypeQName(), parentAssoc.getQName());
// set the extended security
Set<String> combinedWriters = new HashSet<String>(writers);
if (owner != null && !owner.isEmpty() && !owner.equals(OwnableService.NO_OWNER))
{
combinedWriters.add(owner);
}
combinedWriters.add(AuthenticationUtil.getFullyAuthenticatedUser());
extendedSecurityService.addExtendedSecurity(nodeRef, readers, combinedWriters);
extendedSecurityService.set(nodeRef, readersAndWriters);
}
finally
{
@@ -988,24 +951,8 @@ public class RecordServiceImpl extends BaseBehaviourBean
// get the unfiled record folder
final NodeRef unfiledRecordFolder = filePlanService.getUnfiledContainer(filePlan);
// get the documents readers
Long aclId = nodeService.getNodeAclId(nodeRef);
Set<String> readers = extendedPermissionService.getReaders(aclId);
Set<String> writers = extendedPermissionService.getWriters(aclId);
// add the current owner to the list of extended writers
Set<String> modifiedWrtiers = new HashSet<String>(writers);
if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_OWNABLE))
{
String owner = ownableService.getOwner(nodeRef);
if (owner != null && !owner.isEmpty() && !owner.equals(OwnableService.NO_OWNER))
{
modifiedWrtiers.add(owner);
}
}
// add the current user as extended writer
modifiedWrtiers.add(authenticationUtil.getFullyAuthenticatedUser());
// get the documents readers and writers
Pair<Set<String>, Set<String>> readersAndWriters = extendedPermissionService.getReadersAndWriters(nodeRef);
// copy version state and create record
NodeRef record = null;
@@ -1074,7 +1021,7 @@ public class RecordServiceImpl extends BaseBehaviourBean
}
// set extended security on record
extendedSecurityService.addExtendedSecurity(record, readers, writers);
extendedSecurityService.set(record, readersAndWriters);
return record;
}

View File

@@ -36,8 +36,6 @@ import org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService;
import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel;
import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService;
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.ExtendedWriterDynamicAuthority;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authority.RMAuthority;
import org.alfresco.service.cmr.repository.DuplicateChildNodeNameException;
@@ -186,8 +184,6 @@ public class FilePlanRoleServiceImpl implements FilePlanRoleService,
// Set the permissions
permissionService.setInheritParentPermissions(filePlan, false);
permissionService.setPermission(filePlan, allRoles, RMPermissionModel.READ_RECORDS, true);
permissionService.setPermission(filePlan, ExtendedReaderDynamicAuthority.EXTENDED_READER, RMPermissionModel.READ_RECORDS, true);
permissionService.setPermission(filePlan, ExtendedWriterDynamicAuthority.EXTENDED_WRITER, RMPermissionModel.FILING, true);
// Create the transfer and hold containers
systemContainers.add(filePlanService.createHoldContainer(filePlan));

View File

@@ -1,26 +1,37 @@
/*
* Copyright (C) 2005-2014 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* #%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.security;
import java.util.Set;
import org.alfresco.api.AlfrescoPublicApi;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.util.Pair;
/**
* Extended security service.
@@ -28,13 +39,17 @@ import org.alfresco.service.cmr.repository.NodeRef;
* @author Roy Wetherall
* @since 2.1
*/
public interface ExtendedSecurityService
@AlfrescoPublicApi
public interface ExtendedSecurityService extends DeprecatedExtendedSecurityService
{
/** IPR group prefix */
static final String IPR_GROUP_PREFIX = "IPR";
/**
* Indicates whether a node has extended security.
*
* @param nodeRef node reference
* @return boolean true if the node has extedned security, false otherwise
* @return boolean true if the node has extended security, false otherwise
*/
boolean hasExtendedSecurity(NodeRef nodeRef);
@@ -44,7 +59,7 @@ public interface ExtendedSecurityService
* @param nodeRef node reference
* @return {@link Set}<{@link String}> set of extended readers
*/
Set<String> getExtendedReaders(NodeRef nodeRef);
Set<String> getReaders(NodeRef nodeRef);
/**
* Get the set of authorities that are extended writers for the given node.
@@ -52,66 +67,39 @@ public interface ExtendedSecurityService
* @param nodeRef node reference
* @return {@link Set}<{@link String}> set of extended writers
*/
Set<String> getExtendedWriters(NodeRef nodeRef);
Set<String> getWriters(NodeRef nodeRef);
/**
* Add extended security for the specified authorities to a node.
* Helper to allow caller to provide authority sets as a pair where the
* first is the readers and the second is the writers.
*
* @see #set(NodeRef, Set, Set)
*
* @param nodeRef node reference
* @param readers set of authorities to add extended read permissions
* @param writers set of authorities to add extended write permissions
* @param readersAndWriters pair where first is the set of readers and the
* second is the set of writers
*/
void addExtendedSecurity(NodeRef nodeRef, Set<String> readers, Set<String> writers);
void set(NodeRef nodeRef, Pair<Set<String>, Set<String>> readersAndWriters);
/**
* Add extended security for the specified authorities to a node.
* Set extended security for a node, where the readers will be granted ReadRecord
* permission and ViewRecord capability to the node and where the writers will be
* granted Filling permission and Filling capability to the node.
* <p>
* If specified, the read and write extended permissions are applied to all parents up to the file plan as
* extended read. This ensures parental read, but not parental write.
* Note it is vaild to provide 'null' values for readers and/or writers.
*
* @param nodeRef node reference
* @param readers set of authorities to add extended read permissions
* @param writers set of authorities to add extended write permissions
* @param applyToParents true if extended security applied to parents (read only) false otherwise.
* @param readers set of readers
* @param writers set of writers
*
* @since 2.5
*/
void addExtendedSecurity(NodeRef nodeRef, Set<String> readers, Set<String> writers, boolean applyToParents);
void set(NodeRef nodeRef, Set<String> readers, Set<String> writers);
/**
* Remove the extended security for the specified authorities from a node.
*
* @param nodeRef node reference
* @param readers set of authorities to remove as extended readers
* @param writers set of authorities to remove as extended writers
*/
void removeExtendedSecurity(NodeRef nodeRef, Set<String> readers, Set<String> writers);
/**
* Remove the extended security for the specified authorities from a node.
* <p>
* If specified, extended security will also be removed from the parent hierarchy.(read only). Note that
* extended security is records as a reference count, so security will only be utterly removed from the parent
* hierarchy if all references to the authority are removed.
*
* @param nodeRef node reference
* @param readers set of authorities to remove as extended readers
* @param writers set of authorities to remove as extedned writers
* @param applyToParents true if removal of extended security is applied to parent hierarchy (read only), false
* otherwise
*/
void removeExtendedSecurity(NodeRef nodeRef, Set<String> readers, Set<String> writers, boolean applyToParents);
/**
* Remove all extended readers and writers from the given node reference.
* Removes all extended security from a node.
*
* @param nodeRef node reference
*/
void removeAllExtendedSecurity(NodeRef nodeRef);
/**
* Remove all extended readers and writers from the given node reference.
*
* @param nodeRef node reference
* @param applyToParents if true then apply removal to parent hierarchy (read only) false otherwise.
*/
void removeAllExtendedSecurity(NodeRef nodeRef, boolean applyToParents);
void remove(NodeRef nodeRef);
}

View File

@@ -1,40 +1,63 @@
/*
* Copyright (C) 2005-2014 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* #%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.security;
import java.io.Serializable;
import java.util.HashMap;
import static org.alfresco.service.cmr.security.PermissionService.GROUP_PREFIX;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.alfresco.model.RenditionModel;
import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel;
import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService;
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
import org.alfresco.module.org_alfresco_module_rm.role.FilePlanRoleService;
import org.alfresco.module.org_alfresco_module_rm.util.ServiceBaseImpl;
import org.alfresco.query.PagingRequest;
import org.alfresco.query.PagingResults;
import org.alfresco.repo.security.authority.RMAuthority;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.security.AccessPermission;
import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.cmr.security.AuthorityType;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.Pair;
import org.alfresco.util.ParameterCheck;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.extensions.webscripts.ui.common.StringUtils;
/**
* Extended security service implementation.
@@ -44,14 +67,32 @@ import org.alfresco.util.ParameterCheck;
*/
public class ExtendedSecurityServiceImpl extends ServiceBaseImpl
implements ExtendedSecurityService,
RecordsManagementModel
RecordsManagementModel,
ApplicationListener<ContextRefreshedEvent>
{
/** ipr group names */
static final String ROOT_IPR_GROUP = "INPLACE_RECORD_MANAGEMENT";
static final String READER_GROUP_PREFIX = ExtendedSecurityService.IPR_GROUP_PREFIX + "R";
static final String WRITER_GROUP_PREFIX = ExtendedSecurityService.IPR_GROUP_PREFIX + "W";
/** max page size for authority query */
private static final int MAX_ITEMS = 50;
/** File plan service */
private FilePlanService filePlanService;
/** File plan role service */
private FilePlanRoleService filePlanRoleService;
/** authority service */
private AuthorityService authorityService;
/** permission service */
private PermissionService permissionService;
/** transaction service */
private TransactionService transactionService;
/**
* @param filePlanService file plan service
*/
@@ -68,116 +109,150 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl
this.filePlanRoleService = filePlanRoleService;
}
/**
* @param authorityService authority service
*/
public void setAuthorityService(AuthorityService authorityService)
{
this.authorityService = authorityService;
}
/**
* @param permissionService permission service
*/
public void setPermissionService(PermissionService permissionService)
{
this.permissionService = permissionService;
}
/**
* @param transactionService transaction service
*/
public void setTransactionService(TransactionService transactionService)
{
this.transactionService = transactionService;
}
/**
* Application context refresh event handler
*/
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent)
{
transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
// if the root group doesn't exist then create it
if (!authorityService.authorityExists(getRootIRPGroup()))
{
authorityService.createAuthority(AuthorityType.GROUP, ROOT_IPR_GROUP, ROOT_IPR_GROUP, Collections.singleton(RMAuthority.ZONE_APP_RM));
}
return null;
}
});
}
/**
* Get root IPR group name
*/
private String getRootIRPGroup()
{
return GROUP_PREFIX + ROOT_IPR_GROUP;
}
/**
* @see org.alfresco.module.org_alfresco_module_rm.security.ExtendedSecurityService#hasExtendedSecurity(org.alfresco.service.cmr.repository.NodeRef)
*/
@Override
public boolean hasExtendedSecurity(NodeRef nodeRef)
{
return nodeService.hasAspect(nodeRef, ASPECT_EXTENDED_SECURITY);
return (getIPRGroups(nodeRef) != null);
}
/**
* @see org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService#getExtendedReaders(org.alfresco.service.cmr.repository.NodeRef)
* @see org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService#getReaders(org.alfresco.service.cmr.repository.NodeRef)
*/
@SuppressWarnings("unchecked")
@Override
public Set<String> getExtendedReaders(NodeRef nodeRef)
{
Set<String> result = null;
Map<String, Integer> readerMap = (Map<String, Integer>)nodeService.getProperty(nodeRef, PROP_READERS);
if (readerMap != null)
{
result = readerMap.keySet();
}
return result;
}
/**
* @see org.alfresco.module.org_alfresco_module_rm.security.ExtendedSecurityService#getExtendedWriters(org.alfresco.service.cmr.repository.NodeRef)
*/
@SuppressWarnings("unchecked")
@Override
public Set<String> getExtendedWriters(NodeRef nodeRef)
{
Set<String> result = null;
Map<String, Integer> map = (Map<String, Integer>)nodeService.getProperty(nodeRef, PROP_WRITERS);
if (map != null)
{
result = map.keySet();
}
return result;
}
/**
* @see org.alfresco.module.org_alfresco_module_rm.security.ExtendedSecurityService#addExtendedSecurity(org.alfresco.service.cmr.repository.NodeRef, java.util.Set, java.util.Set)
*/
@Override
public void addExtendedSecurity(NodeRef nodeRef, Set<String> readers, Set<String> writers)
{
addExtendedSecurity(nodeRef, readers, writers, true);
}
/**
* @see org.alfresco.module.org_alfresco_module_rm.security.ExtendedSecurityService#addExtendedSecurity(org.alfresco.service.cmr.repository.NodeRef, java.util.Set, java.util.Set, boolean)
*/
@Override
public void addExtendedSecurity(NodeRef nodeRef, Set<String> readers, Set<String> writers, boolean applyToParents)
public Set<String> getReaders(NodeRef nodeRef)
{
ParameterCheck.mandatory("nodeRef", nodeRef);
ParameterCheck.mandatory("applyToParents", applyToParents);
if (nodeRef != null)
Set<String> result = Collections.EMPTY_SET;
Pair<String, String> iprGroups = getIPRGroups(nodeRef);
if (iprGroups != null)
{
addExtendedSecurityImpl(nodeRef, readers, writers, applyToParents);
// add to the extended security roles
addExtendedSecurityRoles(nodeRef, readers, writers);
result = getAuthorities(iprGroups.getFirst());
}
return result;
}
/**
* Add extended security implementation method
* @see org.alfresco.module.org_alfresco_module_rm.security.ExtendedSecurityService#getWriters(org.alfresco.service.cmr.repository.NodeRef)
*/
@SuppressWarnings("unchecked")
@Override
public Set<String> getWriters(NodeRef nodeRef)
{
ParameterCheck.mandatory("nodeRef", nodeRef);
Set<String> result = Collections.EMPTY_SET;
Pair<String, String> iprGroups = getIPRGroups(nodeRef);
if (iprGroups != null)
{
result = getAuthorities(iprGroups.getSecond());
}
return result;
}
/**
* Helper to get authorities for a given group
*
* @param nodeRef
* @param readers
* @param writers
* @param applyToParents
* @param group group name
* @return Set<String> immediate authorities
*/
@SuppressWarnings("unchecked")
private void addExtendedSecurityImpl(final NodeRef nodeRef, Set<String> readers, Set<String> writers, boolean applyToParents)
private Set<String> getAuthorities(String group)
{
Set<String> result = new HashSet<String>();
result.addAll(authorityService.getContainedAuthorities(null, group, true));
return result;
}
/**
* @see org.alfresco.module.org_alfresco_module_rm.security.ExtendedSecurityService#set(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.util.Pair)
*/
@Override
public void set(NodeRef nodeRef, Pair<Set<String>, Set<String>> readersAndWriters)
{
ParameterCheck.mandatory("nodeRef", nodeRef);
ParameterCheck.mandatory("applyToParents", applyToParents);
// get the properties
final Map<QName, Serializable> properties = nodeService.getProperties(nodeRef);
// update the readers map
if (readers != null && readers.size() != 0)
{
// get reader map
Map<String, Integer> readersMap = (Map<String, Integer>)properties.get(PROP_READERS);
// set the readers property (this will in turn apply the aspect if required)
properties.put(PROP_READERS, (Serializable)addToMap(readersMap, readers));
set(nodeRef, readersAndWriters.getFirst(), readersAndWriters.getSecond());
}
// update the writers map
if (writers != null && writers.size() != 0)
/**
* @see org.alfresco.module.org_alfresco_module_rm.security.ExtendedSecurityService#set(org.alfresco.service.cmr.repository.NodeRef, java.util.Set, java.util.Set)
*/
@Override
public void set(NodeRef nodeRef, Set<String> readers, Set<String> writers)
{
// get writer map
Map<String, Integer> writersMap = (Map<String, Integer>)properties.get(PROP_WRITERS);
ParameterCheck.mandatory("nodeRef", nodeRef);
// set the writers property (this will in turn apply the aspect if required)
properties.put(PROP_WRITERS, (Serializable)addToMap(writersMap, writers));
}
// remove existing extended security, assuming there is any
remove(nodeRef);
// set properties
nodeService.setProperties(nodeRef, properties);
// find groups
Pair<String, String> iprGroups = createOrFindIPRGroups(readers, writers);
// assign groups to correct fileplan roles
NodeRef filePlan = filePlanService.getFilePlan(nodeRef);
filePlanRoleService.assignRoleToAuthority(filePlan, FilePlanRoleService.ROLE_EXTENDED_READERS, iprGroups.getFirst());
filePlanRoleService.assignRoleToAuthority(filePlan, FilePlanRoleService.ROLE_EXTENDED_WRITERS, iprGroups.getSecond());
// assign groups to node
assignIPRGroupsToNode(iprGroups, nodeRef);
// apply the readers to any renditions of the content
if (isRecord(nodeRef))
@@ -186,100 +261,299 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl
for (ChildAssociationRef assoc : assocs)
{
NodeRef child = assoc.getChildRef();
addExtendedSecurityImpl(child, readers, writers, false);
assignIPRGroupsToNode(iprGroups, child);
}
}
}
/**
* Get the IPR groups associated with a given node reference.
* <p>
* Return null if none found.
*
* @param nodeRef
* @param readers
* @param writers
* @param nodeRef node reference
* @return Pair<String, String> where first is the read group and second if the write group, null if none found
*/
private void addExtendedSecurityRoles(NodeRef nodeRef, Set<String> readers, Set<String> writers)
private Pair<String, String> getIPRGroups(NodeRef nodeRef)
{
NodeRef filePlan = filePlanService.getFilePlan(nodeRef);
Pair<String, String> result = null;
String iprReaderGroup = null;
String iprWriterGroup = null;
addExtendedSecurityRolesImpl(filePlan, readers, FilePlanRoleService.ROLE_EXTENDED_READERS);
addExtendedSecurityRolesImpl(filePlan, writers, FilePlanRoleService.ROLE_EXTENDED_WRITERS);
// get all the set permissions
Set<AccessPermission> permissions = permissionService.getAllSetPermissions(nodeRef);
for (AccessPermission permission : permissions)
{
// look for the presence of the reader group
if (permission.getAuthority().startsWith(GROUP_PREFIX + READER_GROUP_PREFIX))
{
iprReaderGroup = permission.getAuthority();
}
// look for the presence of the writer group
else if (permission.getAuthority().startsWith(GROUP_PREFIX + WRITER_GROUP_PREFIX))
{
iprWriterGroup = permission.getAuthority();
}
}
// assuming the are both present then return
if (iprReaderGroup != null && iprWriterGroup != null)
{
result = new Pair<String, String>(iprReaderGroup, iprWriterGroup);
}
return result;
}
/**
* Add extended security roles implementation
* Given a set of readers and writers find or create the appropriate IPR groups.
* <p>
* The IPR groups are named with hashes of the authority lists in order to reduce
* the set of groups that require exact match. A further index is used to handle
* a situation where there is a hash clash, but a difference in the authority lists.
* <p>
* When no match is found the groups are created. Once created
*
* @param filePlan file plan
* @param authorities authorities
* @param roleName role name
* @param readers authorities with read
* @param writers authorities with write
* @return Pair<String, String> where first is the full name of the read group and
* second is the full name of the write group
*/
private void addExtendedSecurityRolesImpl(NodeRef filePlan, Set<String> authorities, String roleName)
private Pair<String, String> createOrFindIPRGroups(Set<String> readers, Set<String> writers)
{
if (authorities != null)
{
for (String authority : authorities)
{
if ((!authority.equals(PermissionService.ALL_AUTHORITIES) && !authority.equals(PermissionService.OWNER_AUTHORITY)))
{
// add the authority to the role
filePlanRoleService.assignRoleToAuthority(filePlan, roleName, authority);
}
}
}
return new Pair<String, String>(
createOrFindIPRGroup(READER_GROUP_PREFIX, readers),
createOrFindIPRGroup(WRITER_GROUP_PREFIX, writers));
}
/**
* Create or find an IPR group based on the provided prefix and authorities.
*
* @param map
* @param keys
* @return
* @param groupPrefix group prefix
* @param authorities authorities
* @return String full group name
*/
private Map<String, Integer> addToMap(Map<String, Integer> map, Set<String> keys)
private String createOrFindIPRGroup(String groupPrefix, Set<String> authorities)
{
if (map == null)
{
// create map
map = new HashMap<String, Integer>(7);
}
String group = null;
for (String key : keys)
// find group or determine what the next index is if no group exists or there is a clash
Pair<String, Integer> groupResult = findIPRGroup(groupPrefix, authorities);
if (groupResult.getFirst() == null)
{
if (!key.equals(PermissionService.ALL_AUTHORITIES))
{
if (map.containsKey(key))
{
// increment reference count
Integer count = map.get(key);
map.put(key, Integer.valueOf(count.intValue()+1));
group = createIPRGroup(groupPrefix, authorities, groupResult.getSecond());
}
else
{
// add key with initial count
map.put(key, Integer.valueOf(1));
}
}
group = groupResult.getFirst();
}
return map;
return group;
}
/**
* @see org.alfresco.module.org_alfresco_module_rm.security.ExtendedSecurityService#removeExtendedSecurity(org.alfresco.service.cmr.repository.NodeRef, java.util.Set, java.util.Set)
* Given a group name prefix and the authorities, finds the exact match existing group.
* <p>
* If the group does not exist then the group returned is null and the index shows the next available
* group index for creation.
*
* @param groupPrefix group name prefix
* @param authorities authorities
* @return Pair<String, Integer> where first is the name of the found group, null if none found and second
* if the next available create index
*/
@Override
public void removeExtendedSecurity(NodeRef nodeRef, Set<String> readers, Set<String> writers)
private Pair<String, Integer> findIPRGroup(String groupPrefix, Set<String> authorities)
{
removeExtendedSecurity(nodeRef, readers, writers, true);
String iprGroup = null;
int nextGroupIndex = 0;
boolean hasMoreItems = true;
int pageCount = 0;
// determine the short name prefix
String groupShortNamePrefix = getIPRGroupPrefixShortName(groupPrefix, authorities);
// iterate over the authorities to find a match
while (hasMoreItems == true)
{
// get matching authorities
PagingResults<String> results = authorityService.getAuthorities(AuthorityType.GROUP,
RMAuthority.ZONE_APP_RM,
groupShortNamePrefix,
false,
false,
new PagingRequest(MAX_ITEMS*pageCount, MAX_ITEMS));
// record the total count
nextGroupIndex = nextGroupIndex + results.getPage().size();
// see if any of the matching groups exactly match
for (String group : results.getPage())
{
// if exists and matches we have found our group
if (isIPRGroupTrueMatch(group, authorities))
{
iprGroup = group;
break;
}
}
// determine if there are any more pages to inspect
hasMoreItems = results.hasMoreItems();
pageCount ++;
}
return new Pair<String, Integer>(iprGroup, nextGroupIndex);
}
/**
* @see org.alfresco.module.org_alfresco_module_rm.security.ExtendedSecurityService#removeExtendedSecurity(org.alfresco.service.cmr.repository.NodeRef, java.util.Set, java.util.Set, boolean)
* Determines whether a group exactly matches a list of authorities.
*
* @param authorities list of authorities
* @param group group
* @return
*/
private boolean isIPRGroupTrueMatch(String group, Set<String> authorities)
{
Set<String> contained = authorityService.getContainedAuthorities(null, group, true);
return contained.equals(authorities);
}
/**
* Get IPR group prefix short name.
* <p>
* 'package' scope to help testing.
*
* @param prefix prefix
* @param authorities authorities
* @return String group prefix short name
*/
/*package*/ String getIPRGroupPrefixShortName(String prefix, Set<String> authorities)
{
StringBuilder builder = new StringBuilder(128)
.append(prefix)
.append(getAuthoritySetHashCode(authorities));
return builder.toString();
}
/**
* Get IPR group short name.
* <p>
* Note this excludes the "GROUP_" prefix.
* <p>
* 'package' scope to help testing.
*
* @param prefix prefix
* @param readers read authorities
* @param writers write authorities
* @param index group index
* @return String group short name
*/
/*package*/ String getIPRGroupShortName(String prefix, Set<String> authorities, int index)
{
return getIPRGroupShortName(prefix, authorities, Integer.toString(index));
}
/**
* Get IPR group short name.
* <p>
* Note this excludes the "GROUP_" prefix.
*
* @param prefix prefix
* @param readers read authorities
* @param writers write authorities
* @param index group index
* @return String group short name
*/
private String getIPRGroupShortName(String prefix, Set<String> authorities, String index)
{
StringBuilder builder = new StringBuilder(128)
.append(getIPRGroupPrefixShortName(prefix, authorities))
.append(index);
return builder.toString();
}
/**
* Gets the hashcode value of a set of authorities.
*
* @param authorities set of authorities
* @return int hash code
*/
private int getAuthoritySetHashCode(Set<String> authorities)
{
int result = 0;
if (authorities != null && !authorities.isEmpty())
{
result = StringUtils.join(authorities.toArray(), "").hashCode();
}
return result;
}
/**
* Creates a new IPR group.
*
* @param groupNamePrefix group name prefix
* @param children child authorities
* @param index group index
* @return String full name of created group
*/
private String createIPRGroup(String groupNamePrefix, Set<String> children, int index)
{
ParameterCheck.mandatory("groupNamePrefix", groupNamePrefix);
// get the group name
String groupShortName = getIPRGroupShortName(groupNamePrefix, children, index);
// create group
String group = authorityService.createAuthority(AuthorityType.GROUP, groupShortName, groupShortName, Collections.singleton(RMAuthority.ZONE_APP_RM));
// add root parent
authorityService.addAuthority(getRootIRPGroup(), group);
// add children if provided
if (children != null)
{
for (String child : children)
{
if (authorityService.authorityExists(child) &&
!PermissionService.ALL_AUTHORITIES.equals(child))
{
authorityService.addAuthority(group, child);
}
}
}
return group;
}
/**
* Assign IPR groups to a node reference with the correct permissions.
*
* @param iprGroups iprGroups, first read and second write
* @param nodeRef node reference
*/
private void assignIPRGroupsToNode(Pair<String, String> iprGroups, NodeRef nodeRef)
{
permissionService.setPermission(nodeRef, iprGroups.getFirst(), RMPermissionModel.READ_RECORDS, true);
permissionService.setPermission(nodeRef, iprGroups.getSecond(), RMPermissionModel.FILING, true);
}
/**
* @see org.alfresco.module.org_alfresco_module_rm.security.ExtendedSecurityService#remove(org.alfresco.service.cmr.repository.NodeRef)
*/
@Override
public void removeExtendedSecurity(NodeRef nodeRef, Set<String> readers, Set<String>writers, boolean applyToParents)
public void remove(NodeRef nodeRef)
{
if (hasExtendedSecurity(nodeRef))
ParameterCheck.mandatory("nodeRef", nodeRef);
Pair<String, String> iprGroups = getIPRGroups(nodeRef);
if (iprGroups != null)
{
removeExtendedSecurityImpl(nodeRef, readers, writers);
// remove any extended security that might be present
clearPermissions(nodeRef, iprGroups);
// remove the readers from any renditions of the content
if (isRecord(nodeRef))
@@ -288,110 +562,85 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl
for (ChildAssociationRef assoc : assocs)
{
NodeRef child = assoc.getChildRef();
removeExtendedSecurityImpl(child, readers, writers);
}
}
if (applyToParents)
{
// apply the extended readers up the file plan primary hierarchy
NodeRef parent = nodeService.getPrimaryParent(nodeRef).getParentRef();
if (parent != null &&
filePlanService.isFilePlanComponent(parent))
{
removeExtendedSecurity(parent, readers, null, applyToParents);
removeExtendedSecurity(parent, writers, null, applyToParents);
clearPermissions(child, iprGroups);
}
}
}
}
/**
* Removes a set of readers and writers from a node reference.
* <p>
* Removes the aspect and resets the property to null if all readers and writers are removed.
* Clear the nodes IPR permissions
*
* @param nodeRef node reference
* @param readers {@link Set} of readers
* @param writers {@link Set} of writers
*/
@SuppressWarnings("unchecked")
private void removeExtendedSecurityImpl(NodeRef nodeRef, Set<String> readers, Set<String> writers)
private void clearPermissions(NodeRef nodeRef, Pair<String, String> iprGroups)
{
Map<String, Integer> readersMap = (Map<String, Integer>)nodeService.getProperty(nodeRef, PROP_READERS);
nodeService.setProperty(nodeRef, PROP_READERS, (Serializable)removeFromMap(readersMap, readers));
Map<String, Integer> writersMap = (Map<String, Integer>)nodeService.getProperty(nodeRef, PROP_WRITERS);
nodeService.setProperty(nodeRef, PROP_WRITERS, (Serializable)removeFromMap(writersMap, writers));
if (readersMap == null && writersMap == null)
{
// remove the aspect
nodeService.removeAspect(nodeRef, ASPECT_EXTENDED_SECURITY);
}
// remove group permissions from node
permissionService.clearPermission(nodeRef, iprGroups.getFirst());
permissionService.clearPermission(nodeRef, iprGroups.getSecond());
}
/**
* Helper method to remove items from map or reduce reference count
*
* @param map ref count map
* @param keys keys
* @return Map<String, Integer> ref count map
* @see org.alfresco.module.org_alfresco_module_rm.security.DeprecatedExtendedSecurityService#getExtendedReaders(org.alfresco.service.cmr.repository.NodeRef)
*/
private Map<String, Integer> removeFromMap(Map<String, Integer> map, Set<String> keys)
@Override @Deprecated public Set<String> getExtendedReaders(NodeRef nodeRef)
{
if (map != null && keys != null && keys.size() != 0)
{
// remove the keys
for (String key : keys)
{
if (!key.equals(PermissionService.ALL_AUTHORITIES))
{
Integer count = map.get(key);
if (count != null)
{
if (count == 1)
{
// remove entry all together if the reference count is now 0
map.remove(key);
}
else
{
// decrement the reference count by 1
map.put(key, Integer.valueOf(count.intValue()-1));
}
}
}
}
}
// reset the map to null if now empty
if (map != null && map.isEmpty())
{
map = null;
}
return map;
return getReaders(nodeRef);
}
/**
* @see org.alfresco.module.org_alfresco_module_rm.security.ExtendedSecurityService#removeAllExtendedSecurity(org.alfresco.service.cmr.repository.NodeRef)
* @see org.alfresco.module.org_alfresco_module_rm.security.DeprecatedExtendedSecurityService#getExtendedWriters(org.alfresco.service.cmr.repository.NodeRef)
*/
@Override
public void removeAllExtendedSecurity(NodeRef nodeRef)
@Override @Deprecated public Set<String> getExtendedWriters(NodeRef nodeRef)
{
removeAllExtendedSecurity(nodeRef, true);
return getWriters(nodeRef);
}
/**
* @see org.alfresco.module.org_alfresco_module_rm.security.ExtendedSecurityService#removeAllExtendedSecurity(org.alfresco.service.cmr.repository.NodeRef, boolean)
* @see org.alfresco.module.org_alfresco_module_rm.security.DeprecatedExtendedSecurityService#addExtendedSecurity(org.alfresco.service.cmr.repository.NodeRef, java.util.Set, java.util.Set)
*/
@Override
public void removeAllExtendedSecurity(NodeRef nodeRef, boolean applyToParents)
@Override @Deprecated public void addExtendedSecurity(NodeRef nodeRef, Set<String> readers, Set<String> writers)
{
if (hasExtendedSecurity(nodeRef))
set(nodeRef, readers, writers);
}
/**
* @see org.alfresco.module.org_alfresco_module_rm.security.DeprecatedExtendedSecurityService#addExtendedSecurity(org.alfresco.service.cmr.repository.NodeRef, java.util.Set, java.util.Set, boolean)
*/
@Override @Deprecated public void addExtendedSecurity(NodeRef nodeRef, Set<String> readers, Set<String> writers, boolean applyToParents)
{
removeExtendedSecurity(nodeRef, getExtendedReaders(nodeRef), getExtendedWriters(nodeRef));
}
set(nodeRef, readers, writers);
}
/**
* @see org.alfresco.module.org_alfresco_module_rm.security.DeprecatedExtendedSecurityService#removeAllExtendedSecurity(org.alfresco.service.cmr.repository.NodeRef)
*/
@Override @Deprecated public void removeAllExtendedSecurity(NodeRef nodeRef)
{
remove(nodeRef);
}
/**
* @see org.alfresco.module.org_alfresco_module_rm.security.DeprecatedExtendedSecurityService#removeExtendedSecurity(org.alfresco.service.cmr.repository.NodeRef, java.util.Set, java.util.Set)
*/
@Override @Deprecated public void removeExtendedSecurity(NodeRef nodeRef, Set<String> readers, Set<String> writers)
{
remove(nodeRef);
}
/**
* @see org.alfresco.module.org_alfresco_module_rm.security.DeprecatedExtendedSecurityService#removeExtendedSecurity(org.alfresco.service.cmr.repository.NodeRef, java.util.Set, java.util.Set, boolean)
*/
@Override @Deprecated public void removeExtendedSecurity(NodeRef nodeRef, Set<String> readers, Set<String>writers, boolean applyToParents)
{
remove(nodeRef);
}
/**
* @see org.alfresco.module.org_alfresco_module_rm.security.DeprecatedExtendedSecurityService#removeAllExtendedSecurity(org.alfresco.service.cmr.repository.NodeRef, boolean)
*/
@Override @Deprecated public void removeAllExtendedSecurity(NodeRef nodeRef, boolean applyToParents)
{
remove(nodeRef);
}
}

View File

@@ -18,8 +18,6 @@
*/
package org.alfresco.module.org_alfresco_module_rm.security;
import static org.alfresco.module.org_alfresco_module_rm.security.ExtendedReaderDynamicAuthority.EXTENDED_READER;
import static org.alfresco.module.org_alfresco_module_rm.security.ExtendedWriterDynamicAuthority.EXTENDED_WRITER;
import static org.alfresco.repo.policy.Behaviour.NotificationFrequency.TRANSACTION_COMMIT;
import static org.alfresco.repo.policy.annotation.BehaviourKind.CLASS;
import static org.alfresco.repo.security.authentication.AuthenticationUtil.getSystemUserName;
@@ -374,13 +372,29 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl
boolean inheritanceAllowed = isInheritanceAllowed(nodeRef, isParentNodeFilePlan);
getPermissionService().setInheritParentPermissions(nodeRef, inheritanceAllowed);
// clear all existing permissions
Set<AccessPermission> keepPerms = new HashSet<AccessPermission>(5);
Set<AccessPermission> origionalPerms= getPermissionService().getAllSetPermissions(nodeRef);
for (AccessPermission perm : origionalPerms)
{
if (perm.getAuthority().startsWith(PermissionService.GROUP_PREFIX + ExtendedSecurityService.IPR_GROUP_PREFIX))
{
// then we can assume this is a permission we want to preserve
keepPerms.add(perm);
}
}
// clear all existing permissions and start again
getPermissionService().clearPermission(nodeRef, null);
// re-add keep'er permissions
for (AccessPermission keeper : keepPerms)
{
setPermission(nodeRef, keeper.getAuthority(), keeper.getPermission());
}
if (!inheritanceAllowed)
{
getPermissionService().setPermission(nodeRef, EXTENDED_READER, READ_RECORDS, true);
getPermissionService().setPermission(nodeRef, EXTENDED_WRITER, FILING, true);
String adminRole = getAdminRole(nodeRef);
getPermissionService().setPermission(nodeRef, adminRole, RMPermissionModel.FILING, true);
}
@@ -426,9 +440,20 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl
return authorityService.getName(AuthorityType.GROUP, FilePlanRoleService.ROLE_ADMIN + filePlan.getId());
}
/**
* Indicates whether the default behaviour is to inherit permissions or not.
*
* @param nodeRef node reference
* @param isParentNodeFilePlan true if parent node is a file plan, false otherwise
* @return boolean true if inheritance true, false otherwise
*/
private boolean isInheritanceAllowed(NodeRef nodeRef, Boolean isParentNodeFilePlan)
{
return !(isFilePlan(nodeRef) || isTransfer(nodeRef) || isHold(nodeRef) || isUnfiledRecordsContainer(nodeRef) || (isRecordCategory(nodeRef) && isTrue(isParentNodeFilePlan)));
return !(isFilePlan(nodeRef) ||
isTransfer(nodeRef) ||
isHold(nodeRef) ||
isUnfiledRecordsContainer(nodeRef) ||
(isRecordCategory(nodeRef) && isTrue(isParentNodeFilePlan)));
}
/**
@@ -485,20 +510,14 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl
for (AccessPermission recordPermission : origionalRecordPerms)
{
String permission = recordPermission.getPermission();
String authority = recordPermission.getAuthority();
if ((RMPermissionModel.FILING.equals(permission) || RMPermissionModel.READ_RECORDS.equals(permission)) &&
recordPermission.isSetDirectly() &&
!ExtendedReaderDynamicAuthority.EXTENDED_READER.equals(authority) &&
!ExtendedWriterDynamicAuthority.EXTENDED_WRITER.equals(authority))
recordPermission.isSetDirectly())
{
// then we can assume this is a permission we want to preserve
keepPerms.add(recordPermission);
}
}
// clear all existing permissions and start again
permissionService.deletePermissions(record);
// re-setup the records permissions
setupPermissions(destinationAssocRef.getParentRef(), record);
@@ -580,7 +599,7 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl
private boolean canPerformPermissionAction(NodeRef nodeRef)
{
return isFilePlanContainer(nodeRef) || isRecordFolder(nodeRef) || isRecord(nodeRef) || isTransfer(nodeRef);
return isFilePlanContainer(nodeRef) || isRecordFolder(nodeRef) || isRecord(nodeRef) || isTransfer(nodeRef) || isHold(nodeRef);
}
/**

View File

@@ -25,7 +25,6 @@ import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@@ -47,12 +46,12 @@ import org.alfresco.repo.version.VersionModel;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.security.OwnableService;
import org.alfresco.service.cmr.version.ReservedVersionNameException;
import org.alfresco.service.cmr.version.Version;
import org.alfresco.service.cmr.version.VersionHistory;
import org.alfresco.service.cmr.version.VersionType;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.Pair;
import org.alfresco.util.ParameterCheck;
import org.alfresco.util.PropertyMap;
import org.apache.commons.lang.StringUtils;
@@ -121,9 +120,6 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
/** extended security service */
private ExtendedSecurityService extendedSecurityService;
/** ownable service */
private OwnableService ownableService;
/**
* @param filePlanService file plan service
*/
@@ -188,14 +184,6 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
this.extendedSecurityService = extendedSecurityService;
}
/**
* @param ownableService ownable service
*/
public void setOwnableService(OwnableService ownableService)
{
this.ownableService = ownableService;
}
/**
* @see org.alfresco.repo.version.Version2ServiceImpl#createVersion(org.alfresco.service.cmr.repository.NodeRef, java.util.Map, int)
*/
@@ -688,13 +676,8 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
{
public NodeRef doWork() throws Exception
{
// get the documents readers
Long aclId = nodeService.getNodeAclId(nodeRef);
Set<String> readers = extendedPermissionService.getReaders(aclId);
Set<String> writers = extendedPermissionService.getWriters(aclId);
// add the current owner to the list of extended writers
String owner = ownableService.getOwner(nodeRef);
// get the documents readers and writers
Pair<Set<String>, Set<String>> readersAndWriters = extendedPermissionService.getReadersAndWriters(nodeRef);
// grab the frozen state
NodeRef currentFrozenState = currentVersion.getFrozenStateNodeRef();
@@ -734,13 +717,7 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
linkToPreviousVersionRecord(nodeRef, record);
// set the extended security
Set<String> combinedWriters = new HashSet<String>(writers);
if (owner != null && !owner.isEmpty() && !owner.equals(OwnableService.NO_OWNER))
{
combinedWriters.add(owner);
}
combinedWriters.add(authenticationUtil.getFullyAuthenticatedUser());
extendedSecurityService.addExtendedSecurity(record, readers, combinedWriters);
extendedSecurityService.set(record, readersAndWriters);
return record;
}

View File

@@ -20,7 +20,9 @@ package org.alfresco.repo.security.permissions.impl;
import java.util.Set;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.util.Pair;
/**
* Extended Permission Service Interface used in RM.
@@ -31,4 +33,18 @@ import org.alfresco.service.cmr.security.PermissionService;
public interface ExtendedPermissionService extends PermissionService
{
Set<String> getWriters(Long aclId);
/**
* Get the readers and writers for a given node.
* <p>
* The writers list includes the owner for the node.
*
* @param nodeRef node reference
* @return Pair<Set<String>, Set<String>> first is a set containing all the authorities that have read permission on the
* document and second is a set containing all the authorities that have write
* permission on the document, including the owner.
*
* @since 2.5
*/
Pair<Set<String>, Set<String>> getReadersAndWriters(NodeRef nodeRef);
}

View File

@@ -29,16 +29,17 @@ import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel;
import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService;
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
import org.alfresco.module.org_alfresco_module_rm.role.FilePlanRoleService;
import org.alfresco.module.org_alfresco_module_rm.security.ExtendedReaderDynamicAuthority;
import org.alfresco.module.org_alfresco_module_rm.security.ExtendedWriterDynamicAuthority;
import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.repo.security.permissions.AccessControlEntry;
import org.alfresco.repo.security.permissions.AccessControlList;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.security.AccessStatus;
import org.alfresco.service.cmr.security.AuthorityType;
import org.alfresco.service.cmr.security.OwnableService;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.util.Pair;
import org.alfresco.util.PropertyCheck;
import org.apache.commons.lang.StringUtils;
import org.springframework.context.ApplicationEvent;
/**
@@ -303,13 +304,17 @@ public class RMPermissionServiceImpl extends PermissionServiceImpl
final String adminRole = getAdminRole(nodeRef);
if (nodeService.hasAspect(nodeRef, RecordsManagementModel.ASPECT_FILE_PLAN_COMPONENT) && isNotBlank(adminRole) && !inheritParentPermissions)
{
setPermission(nodeRef, ExtendedReaderDynamicAuthority.EXTENDED_READER, RMPermissionModel.READ_RECORDS, true);
setPermission(nodeRef, ExtendedWriterDynamicAuthority.EXTENDED_WRITER, RMPermissionModel.FILING, true);
setPermission(nodeRef, adminRole, RMPermissionModel.FILING, true);
}
super.setInheritParentPermissions(nodeRef, inheritParentPermissions);
}
/**
* Helper method to the RM admin role scoped by the correct file plan.
*
* @param nodeRef node reference
* @return String RM admin role
*/
private String getAdminRole(NodeRef nodeRef)
{
String adminRole = null;
@@ -320,4 +325,28 @@ public class RMPermissionServiceImpl extends PermissionServiceImpl
}
return adminRole;
}
/**
* @see org.alfresco.repo.security.permissions.impl.ExtendedPermissionService#getReadersAndWriters(org.alfresco.service.cmr.repository.NodeRef)
*/
@Override
public Pair<Set<String>, Set<String>> getReadersAndWriters(NodeRef nodeRef)
{
// get the documents readers
Long aclId = nodeService.getNodeAclId(nodeRef);
Set<String> readers = getReaders(aclId);
Set<String> writers = getWriters(aclId);
// add the current owner to the list of extended writers
Set<String> modifiedWrtiers = new HashSet<String>(writers);
String owner = ownableService.getOwner(nodeRef);
if (StringUtils.isNotBlank(owner) &&
!owner.equals(OwnableService.NO_OWNER) &&
authorityService.authorityExists(owner))
{
modifiedWrtiers.add(owner);
}
return new Pair<Set<String>, Set<String>> (readers, modifiedWrtiers);
}
}

View File

@@ -0,0 +1,241 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
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.lang.StringUtils;
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_PARAMETER_BATCHSIZE_GREATER_THAN_ZERO = "Parameter batchsize should be a number greater than 0.";
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<String, Object> executeImpl(WebScriptRequest req, Status status, Cache cache)
{
Map<String, Object> model = new HashMap<String, Object>();
String batchSizeStr = req.getParameter(BATCH_SIZE);
String totalToBeProcessedRecordsStr = req.getParameter(TOTAL_NUMBER_TO_PROCESS);
Long size = 0L;
if (StringUtils.isBlank(batchSizeStr))
{
model.put(MODEL_STATUS, FAILED_STATUS);
model.put(MODEL_MESSAGE, MESSAGE_BATCHSIZE_IS_MANDATORY);
logger.info(MESSAGE_BATCHSIZE_IS_MANDATORY);
return model;
}
try
{
size = Long.parseLong(batchSizeStr);
if(size <= 0)
{
model.put(MODEL_STATUS, FAILED_STATUS);
model.put(MODEL_MESSAGE, MESSAGE_PARAMETER_BATCHSIZE_GREATER_THAN_ZERO);
logger.info(MESSAGE_PARAMETER_BATCHSIZE_GREATER_THAN_ZERO);
return model;
}
}
catch(NumberFormatException ex)
{
model.put(MODEL_STATUS, FAILED_STATUS);
model.put(MODEL_MESSAGE, MESSAGE_BATCHSIZE_IS_INVALID);
logger.info(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<Long, QName> recordAspectPair = qnameDAO.getQName(ASPECT_EXTENDED_SECURITY);
if(recordAspectPair == null)
{
model.put(MODEL_STATUS, SUCCESS_STATUS);
model.put(MODEL_MESSAGE, MESSAGE_NO_RECORDS_TO_PROCESS);
logger.info(MESSAGE_NO_RECORDS_TO_PROCESS);
return model;
}
//default total number of records to be processed to batch size value
Long totalNumberOfRecordsToProcess = batchSize;
if (StringUtils.isNotBlank(totalToBeProcessedRecordsStr))
{
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<NodeRef> processedNodes = new ArrayList<NodeRef>();
logger.info(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<Void>()
{
public Void execute() throws Throwable
{
// get the nodes with the extended security aspect applied
List<Long> 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();
String recordName = (String) nodeService.getProperty(record, ContentModel.PROP_NAME);
logger.info(MessageFormat.format(MESSAGE_PROCESSING_RECORD_BEGIN_TEMPLATE, recordName));
processNode(record);
logger.info(MessageFormat.format(MESSAGE_PROCESSING_RECORD_END_TEMPLATE, recordName));
processedNodes.add(record);
}
return null;
}
},
false, // read only
true); // requires new
}
logger.info(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);
logger.info(message);
return model;
}
/**
* Process each node
*
* @param nodeRef
*/
@SuppressWarnings({ "unchecked"})
private void processNode(NodeRef nodeRef)
{
// get the reader/writer data
Map<String, Integer> readers = (Map<String, Integer>)nodeService.getProperty(nodeRef, PROP_READERS);
Map<String, Integer> writers = (Map<String, Integer>)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());
}
}
}

View File

@@ -35,14 +35,14 @@ public class RM1429Test extends DeleteHoldTest
{
public void testDeleteHoldWithoutPermissionsOnChildren()
{
// Create the test hold
final NodeRef hold = createAndCheckHold();
doTestInTransaction(new Test<Void>()
final NodeRef hold = doTestInTransaction(new Test<NodeRef>()
{
@Override
public Void run()
public NodeRef run()
{
// Create the test hold
NodeRef hold = createAndCheckHold();
// Add the user to the RM Manager role
filePlanRoleService.assignRoleToAuthority(filePlan, FilePlanRoleService.ROLE_RECORDS_MANAGER, userName);
@@ -55,7 +55,7 @@ public class RM1429Test extends DeleteHoldTest
// Add record folder to the hold
holdService.addToHold(hold, rmFolder);
return null;
return hold;
}
});

View File

@@ -35,14 +35,14 @@ public class RM1463Test extends DeleteHoldTest
{
public void testAddRecordFolderToHoldWithoutFilingPermissionOnRecordFolder()
{
// Create hold
final NodeRef hold = createAndCheckHold();
doTestInTransaction(new Test<Void>()
final NodeRef hold = doTestInTransaction(new Test<NodeRef>()
{
@Override
public Void run()
public NodeRef run()
{
// Create hold
NodeRef hold = createAndCheckHold();
// Add the user to the RM Manager role
filePlanRoleService.assignRoleToAuthority(filePlan, FilePlanRoleService.ROLE_RECORDS_MANAGER, userName);
@@ -52,7 +52,7 @@ public class RM1463Test extends DeleteHoldTest
// Give the user only read permissions on the record folder
permissionService.setPermission(rmFolder, userName, RMPermissionModel.READ_RECORDS, true);
return null;
return hold;
}
});

View File

@@ -35,14 +35,14 @@ public class RM1464Test extends DeleteHoldTest
{
public void testAddRecordFolderToHoldWithoutFilingPermissionOnHold()
{
// Create hold
final NodeRef hold = createAndCheckHold();
doTestInTransaction(new Test<Void>()
final NodeRef hold = doTestInTransaction(new Test<NodeRef>()
{
@Override
public Void run()
public NodeRef run()
{
// Create hold
NodeRef hold = createAndCheckHold();
// Add the user to the RM Manager role
filePlanRoleService.assignRoleToAuthority(filePlan, FilePlanRoleService.ROLE_RECORDS_MANAGER, userName);
@@ -52,7 +52,7 @@ public class RM1464Test extends DeleteHoldTest
// Give the user filing permissions on the record folder
permissionService.setPermission(rmFolder, userName, RMPermissionModel.FILING, true);
return null;
return hold;
}
});

View File

@@ -0,0 +1,211 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.alfresco.module.org_alfresco_module_rm.test.integration.issue;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import org.alfresco.model.ContentModel;
import org.alfresco.module.org_alfresco_module_rm.action.dm.CreateRecordAction;
import org.alfresco.module.org_alfresco_module_rm.action.impl.FileToAction;
import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.rule.Rule;
import org.alfresco.service.cmr.rule.RuleService;
import org.alfresco.service.cmr.rule.RuleType;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
/**
* System test for RM-3993: Exceptions thrown when concurrently creating identical folder structure
*
* @author Silviu Dinuta
* @since 2.3.0.7
*/
public class RM3993Test extends BaseRMTestCase
{
private static final int NUMBER_OF_BATCHES = 4;
private static final int NUMBER_IN_BATCH = 500;
private RuleService ruleService;
private NodeRef ruleFolder;
private NodeRef nodeRefCategory1;
@Override
protected void initServices()
{
super.initServices();
ruleService = (RuleService) applicationContext.getBean("RuleService");
}
@Override
protected boolean isCollaborationSiteTest()
{
return true;
}
@Override
protected boolean isRecordTest()
{
return true;
}
/**
* Given that I have auto declare configured And that I have auto file configured to a path where only the record
* folder needs to be created When I add lots of documents in the same transaction Then the rules should fire And
* the documents should be filed in the new record folder
*/
public void testAutoDeclareAutoFileCreateRecordFolderOnly() throws Exception
{
doTestInTransaction(new Test<Void>()
{
@Override
public Void run()
{
// create the folder
ruleFolder = fileFolderService.create(documentLibrary, "mytestfolder", ContentModel.TYPE_FOLDER)
.getNodeRef();
// create record category
nodeRefCategory1 = filePlanService.createRecordCategory(filePlan, "category1");
Action action = actionService.createAction(CreateRecordAction.NAME);
action.setParameterValue(CreateRecordAction.PARAM_FILE_PLAN, filePlan);
Rule rule = new Rule();
rule.setRuleType(RuleType.INBOUND);
rule.setTitle("my rule");
rule.setAction(action);
rule.setExecuteAsynchronously(true);
ruleService.saveRule(ruleFolder, rule);
Action fileAction = actionService.createAction(FileToAction.NAME);
fileAction.setParameterValue(FileToAction.PARAM_PATH,
"/category1/{node.cm:description}");
fileAction.setParameterValue(FileToAction.PARAM_CREATE_RECORD_PATH, true);
Rule fileRule = new Rule();
fileRule.setRuleType(RuleType.INBOUND);
fileRule.setTitle("my rule");
fileRule.setAction(fileAction);
fileRule.setExecuteAsynchronously(true);
ruleService.saveRule(filePlanService.getUnfiledContainer(filePlan), fileRule);
return null;
}
@Override
public void test(Void result) throws Exception
{
assertFalse(ruleService.getRules(ruleFolder).isEmpty());
}
});
List<NodeRef> records = new ArrayList<NodeRef>(NUMBER_OF_BATCHES * NUMBER_IN_BATCH);
for (int i = 0; i < NUMBER_OF_BATCHES; i++)
{
final int finali = i;
records.addAll(doTestInTransaction(new Test<List<NodeRef>>()
{
@Override
public List<NodeRef> run() throws Exception
{
List<NodeRef> records = new ArrayList<NodeRef>(NUMBER_IN_BATCH);
for (int j = 0; j < NUMBER_IN_BATCH; j++)
{
int count = (finali)* NUMBER_IN_BATCH + (j + 1);
String name = "content" + count + ".txt";
System.out.println(name + " - creating");
Random rand = new Random();
int descInt = rand.nextInt(2)+1;
NodeRef record = createFile(ruleFolder, name, Integer.toString(descInt), ContentModel.TYPE_CONTENT);
records.add(record);
}
return records;
}
}));
}
try
{
while (!records.isEmpty())
{
Thread.sleep(1000);
final Iterator<NodeRef> temp = records.iterator();
doTestInTransaction(new Test<Void>()
{
@Override
public Void run() throws Exception
{
while (temp.hasNext())
{
NodeRef record = temp.next();
if (nodeService.hasAspect(record, ASPECT_RECORD) && recordService.isFiled(record))
{
String name = (String) nodeService.getProperty(record, ContentModel.PROP_NAME);
System.out.println(name + " - complete");
temp.remove();
}
}
return null;
}
});
}
}
catch (Exception exception)
{
exception.printStackTrace();
throw exception;
}
List<NodeRef> allContained = filePlanService.getAllContained(nodeRefCategory1, true);
assertTrue(allContained.size() == 2002);
}
private NodeRef createFile(NodeRef parentNodeRef, String name, String descrption, QName typeQName)
{
Map<QName, Serializable> properties = new HashMap<QName, Serializable>(11);
properties.put(ContentModel.PROP_NAME, (Serializable) name);
properties.put(ContentModel.PROP_DESCRIPTION, (Serializable) descrption);
QName assocQName = QName.createQName(
NamespaceService.CONTENT_MODEL_1_0_URI,
QName.createValidLocalName(name));
ChildAssociationRef assocRef = nodeService.createNode(
parentNodeRef,
ContentModel.ASSOC_CONTAINS,
assocQName,
typeQName,
properties);
NodeRef nodeRef = assocRef.getChildRef();
return nodeRef;
}
}

View File

@@ -0,0 +1,188 @@
/*
* #%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.test.integration.record;
import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel;
import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.service.cmr.model.FileExistsException;
import org.alfresco.service.cmr.model.FileNotFoundException;
import org.alfresco.service.cmr.security.AccessStatus;
/**
* Create Inplace Record Test
*
* @author Roy Wetherall
*/
public class CreateInplaceRecordTest extends BaseRMTestCase
{
@Override
protected boolean isCollaborationSiteTest()
{
return true;
}
/**
* Given a document in a collaboration site
* When the document is declared by a site collaborator
* Then the document becomes a record
* And the site users have the appropriate in-place permissions on the record
*/
public void testCreateInplaceRecordFromCollabSite()
{
doBehaviourDrivenTest(new BehaviourDrivenTest()
{
public void given()
{
// Check that the document is not a record
assertFalse("The document should not be a record", recordService.isRecord(dmDocument));
}
public void when()
{
// Declare the document as a record
AuthenticationUtil.runAs(new RunAsWork<Void>()
{
public Void doWork() throws Exception
{
// Declare record
recordService.createRecord(filePlan, dmDocument);
return null;
}
}, dmCollaborator);
}
public void then()
{
// Check that the document is a record now
assertTrue("The document should now be a record", recordService.isRecord(dmDocument));
// Check that the record is in the unfiled container
// Check that the record is still a child of the collaboration folder
// Check that the collaborator has filling permissions on the record
AuthenticationUtil.runAs(new RunAsWork<Void>()
{
public Void doWork() throws Exception
{
assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(dmDocument, RMPermissionModel.FILING));
assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(dmDocument, RMPermissionModel.READ_RECORDS));
return null;
}
}, dmCollaborator);
// Check that the consumer has read permissions on the record
AuthenticationUtil.runAs(new RunAsWork<Void>()
{
public Void doWork() throws Exception
{
assertEquals(AccessStatus.DENIED, permissionService.hasPermission(dmDocument, RMPermissionModel.FILING));
assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(dmDocument, RMPermissionModel.READ_RECORDS));
return null;
}
}, dmConsumer);
}
});
}
public void testFileInplaceRecordFromCollabSite()
{
doBehaviourDrivenTest(new BehaviourDrivenTest()
{
public void given()
{
// Check that the document is not a record
assertFalse("The document should not be a record", recordService.isRecord(dmDocument));
// Declare the document as a record
AuthenticationUtil.runAs(new RunAsWork<Void>()
{
public Void doWork() throws Exception
{
// Declare record
recordService.createRecord(filePlan, dmDocument);
return null;
}
}, dmCollaborator);
// Check that the document is a record
assertTrue("The document should be a record", recordService.isRecord(dmDocument));
assertFalse("The record should not be filed", recordService.isFiled(dmDocument));
}
public void when() throws FileExistsException, FileNotFoundException
{
// file the document to a location in the file plan
fileFolderService.move(dmDocument, rmFolder, null);
}
public void then()
{
// Check that the document is a record now
assertTrue("The document should be a record", recordService.isRecord(dmDocument));
assertTrue("The record hsould be filed", recordService.isFiled(dmDocument));
// Check that the record is in the unfiled container
// TODO
// Check that the record is still a child of the collaboration folder
// TODO
// Check that the collaborator has filling permissions on the record
AuthenticationUtil.runAs(new RunAsWork<Void>()
{
public Void doWork() throws Exception
{
assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(dmDocument, RMPermissionModel.FILING));
assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(dmDocument, RMPermissionModel.READ_RECORDS));
return null;
}
}, dmCollaborator);
// Check that the consumer has read permissions on the record
AuthenticationUtil.runAs(new RunAsWork<Void>()
{
public Void doWork() throws Exception
{
assertEquals(AccessStatus.DENIED, permissionService.hasPermission(dmDocument, RMPermissionModel.FILING));
assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(dmDocument, RMPermissionModel.READ_RECORDS));
return null;
}
}, dmConsumer);
}
});
}
}

View File

@@ -0,0 +1,951 @@
/*
* #%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.test.integration.record;
import static org.alfresco.module.org_alfresco_module_rm.test.util.bdt.BehaviourTest.test;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.alfresco.model.ContentModel;
import org.alfresco.module.org_alfresco_module_rm.action.impl.CutOffAction;
import org.alfresco.module.org_alfresco_module_rm.action.impl.DeclareRecordAction;
import org.alfresco.module.org_alfresco_module_rm.action.impl.DestroyAction;
import org.alfresco.module.org_alfresco_module_rm.capability.Capability;
import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel;
import org.alfresco.module.org_alfresco_module_rm.role.FilePlanRoleService;
import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase;
import org.alfresco.module.org_alfresco_module_rm.test.util.CommonRMTestUtils;
import org.alfresco.module.org_alfresco_module_rm.test.util.bdt.BehaviourTest;
import org.alfresco.repo.security.permissions.AccessDeniedException;
import org.alfresco.repo.site.SiteModel;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.security.AccessStatus;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.util.GUID;
/**
* In-place record permission integration test.
*
* @author Roy Wetherall
* @since 2.5
*/
public class InplaceRecordPermissionTest extends BaseRMTestCase
{
/** capability list */
private static final List<String> CAPABILITIES = Stream
.of(RMPermissionModel.VIEW_RECORDS,
RMPermissionModel.EDIT_NON_RECORD_METADATA,
RMPermissionModel.EDIT_RECORD_METADATA)
.collect(Collectors.toList());
/** test data */
private NodeRef contribDoc;
private NodeRef deleteUserDoc;
private NodeRef copiedDoc;
private NodeRef copyDoc;
private String deletedUser;
/** services */
private NodeService dbNodeService;
/** capabilities */
private Capability viewRecordsCapability;
private Capability editNonRecordMetadataCapability;
private Capability editRecordMetadataCapability;
/** test characteristics */
@Override protected boolean isCollaborationSiteTest() { return true; }
@Override protected boolean isUserTest() { return true; }
/**
* @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase#initServices()
*/
@Override
protected void initServices()
{
super.initServices();
// initialise behaviour tests
BehaviourTest.initBehaviourTests(retryingTransactionHelper);
// get services
dbNodeService = (NodeService)applicationContext.getBean("dbNodeService");
// get capability references
viewRecordsCapability = capabilityService.getCapability(RMPermissionModel.VIEW_RECORDS);
editNonRecordMetadataCapability = capabilityService.getCapability(RMPermissionModel.EDIT_NON_RECORD_METADATA);
editRecordMetadataCapability = capabilityService.getCapability(RMPermissionModel.EDIT_RECORD_METADATA);
}
/**
* Given a document in a collaboration site
* When a user without write permissions on the document tries to declare it as a record
* Then the declaration fails
* And the document does not become a record
*/
public void testUserWithOutWriteCantDeclareInPlaceRecord()
{
test()
.given()
// Given a document in a collaboration site that is not a record
.expect(false)
.from(() -> recordService.isRecord(dmDocument))
.because("The document is not a record.")
// And a user with no write permission on the document
.as(userName)
.expect(AccessStatus.DENIED.toString())
.from(() -> permissionService.hasPermission(dmDocument, PermissionService.WRITE).toString())
.because("User does not have write access to document.")
// When the user tries to declare the record
// Then we expect this to fail
.when()
.as(userName)
.expectException(AccessDeniedException.class)
.from(() -> recordService.createRecord(filePlan, dmDocument))
.because("The user does not have write permission on the document.");
}
/**
* Given a document in a collaboration site that is not a record
* And a contributor the didn't create the document
* When the contributor tries to declare the document as a record
* Then the document does not become a record
*/
public void testContributorThatIsntOwnerDeclareInPlaceRecord()
{
test()
.given()
// Given a document in a collaboration site that is not a record
.expect(false)
.from(() -> recordService.isRecord(dmDocument))
.because("The document is not a record.")
// And a contributor the didn't create the document
.as(dmContributor)
.expect(AccessStatus.DENIED.toString())
.from(() -> permissionService.hasPermission(dmDocument, PermissionService.WRITE).toString())
.because("Contributor does not have write access to document.")
// When the user tries to declare the record
// When the contributor tries to declare the document as a record
.when()
.as(dmContributor)
.expectException(AccessDeniedException.class)
.from(() -> recordService.createRecord(filePlan, dmDocument))
.because("The contributor does not have write permission on the document.");
}
/**
* Given a document in a collaboration site is not a record
* When the document is declared by a site collaborator
* Then the document becomes a record
* And the site users have the appropriate in-place permissions on the record
*/
public void testCreateInplaceRecordFromCollabSite()
{
test()
// Given that a document in a collaboration site is not a record
.given()
.asAdmin()
.expect(false)
.from(() -> recordService.isRecord(dmDocument))
.because("The document is not a record")
// When it is declared as an inplace record
.when()
.as(dmCollaborator)
.perform(() -> recordService.createRecord(filePlan, dmDocument))
.then()
.asAdmin()
// Then it becomes a record
.expect(true)
.from(() -> recordService.isRecord(dmDocument))
.because("The document is a record")
// And it isn't filed
.expect(false)
.from(() -> recordService.isFiled(dmDocument))
.because("The record is not filed")
// And a site collaborator has filling permissions and filling capability on the record
.as(dmCollaborator)
.perform(() ->
checkInPlaceAccess(dmDocument,
AccessStatus.ALLOWED, // read record permission
AccessStatus.ALLOWED, // filing permission
AccessStatus.ALLOWED, // view record capability
AccessStatus.ALLOWED, // edit non record metadata capability
AccessStatus.DENIED)) // edit record metadata capability
// And a site contributor has read and view
.as(dmContributor)
.perform(() ->
checkInPlaceAccess(dmDocument,
AccessStatus.ALLOWED, // read record permission
AccessStatus.DENIED, // filing permission
AccessStatus.ALLOWED, // view record capability
AccessStatus.DENIED, // edit non record metadata capability
AccessStatus.DENIED)) // edit record metadata capability
// And a site consumer has read permissions and view record capability on the record
.as(dmConsumer)
.perform(() ->
checkInPlaceAccess(dmDocument,
AccessStatus.ALLOWED, // read record permission
AccessStatus.DENIED, // filing permission
AccessStatus.ALLOWED, // view record capability
AccessStatus.DENIED, // edit non record metadata capability
AccessStatus.DENIED)) // edit record metadata capability
// And a user that is not a member of the site has no access to the inplace record
.as(userName)
.perform(() ->
checkInPlaceAccess(dmDocument,
AccessStatus.DENIED, // read record permission
AccessStatus.DENIED, // filing permission
AccessStatus.DENIED, // view record capability
AccessStatus.DENIED, // edit non record metadata capability
AccessStatus.DENIED)); // edit record metadata capability
}
/**
* Helper method to check in place access for a user on a record.
*/
private void checkInPlaceAccess(NodeRef nodeRef, AccessStatus ... accessStatus)
{
// check permission access
assertEquals("Incorrect read record permission access.", accessStatus[0], permissionService.hasPermission(nodeRef, RMPermissionModel.READ_RECORDS));
assertEquals("Incorrect filling permission access.", accessStatus[1], permissionService.hasPermission(nodeRef, RMPermissionModel.FILING));
// check capability access
Map<Capability, AccessStatus> access = capabilityService.getCapabilitiesAccessState(nodeRef, CAPABILITIES);
assertEquals("Incorrect view records capability access", accessStatus[2], access.get(viewRecordsCapability));
assertEquals("Incorrect edit non record metadata capability access", accessStatus[3], access.get(editNonRecordMetadataCapability));
assertEquals("Incorrect edit record metadata capability access", accessStatus[4], access.get(editRecordMetadataCapability));
}
/**
* Given that a document is created by contributor
* When it is declared as an inplace record
* Then it becomes a record
* And it isn't filed
* And a site collaborator has filling permissions and filling capability on the record
* And a site contributor has filling capability and permissions
* And a site consumer has read permissions and view record capability on the record
* And a user that is not a member of the site has no access to the inplace record
*/
public void testCreateInplaceRecordFromCollabSiteWhenContribIsCreatorOfDocument()
{
test()
// Given that a document is created by contributor
.given()
.as(dmContributor)
.perform(() ->
{
contribDoc = fileFolderService.create(dmFolder, "contrib.txt" , ContentModel.TYPE_CONTENT).getNodeRef();
dbNodeService.addAspect(contribDoc, ContentModel.ASPECT_AUDITABLE, null);
})
.expect(false)
.from(() -> recordService.isRecord(contribDoc))
.because("It is not a record.")
.asAdmin()
.expect(dmContributor)
.from(() -> ownableService.getOwner(contribDoc))
.because("As the creator of the document the contributor is also the owner")
.as(dmContributor)
.expect(AccessStatus.ALLOWED.toString())
.from(() -> permissionService.hasPermission(contribDoc, PermissionService.WRITE).toString())
.because("Contrib user has write permissions on created document as the owner.")
// When it is declared as an inplace record
.when()
.as(dmContributor)
.perform(() -> recordService.createRecord(filePlan, contribDoc))
.then()
.asAdmin()
// Then it becomes a record
.expect(true)
.from(() -> recordService.isRecord(contribDoc))
.because("The document is a record")
// And it isn't filed
.expect(false)
.from(() -> recordService.isFiled(contribDoc))
.because("The record is not filed")
// And a site collaborator has filling permissions and filling capability on the record
.as(dmCollaborator)
.perform(() ->
checkInPlaceAccess(contribDoc,
AccessStatus.ALLOWED, // read record permission
AccessStatus.ALLOWED, // filing permission
AccessStatus.ALLOWED, // view record capability
AccessStatus.ALLOWED, // edit non record metadata capability
AccessStatus.DENIED)) // edit record metadata capability
// And a site contributor has filling capability and permissions
.as(dmContributor)
.perform(() ->
checkInPlaceAccess(contribDoc,
AccessStatus.ALLOWED, // read record permission
AccessStatus.ALLOWED, // filing permission
AccessStatus.ALLOWED, // view record capability
AccessStatus.ALLOWED, // edit non record metadata capability
AccessStatus.DENIED)) // edit record metadata capability
// And a site consumer has read permissions and view record capability on the record
.as(dmConsumer)
.perform(() ->
checkInPlaceAccess(contribDoc,
AccessStatus.ALLOWED, // read record permission
AccessStatus.DENIED, // filing permission
AccessStatus.ALLOWED, // view record capability
AccessStatus.DENIED, // edit non record metadata capability
AccessStatus.DENIED)) // edit record metadata capability
// And a user that is not a member of the site has no access to the inplace record
.as(userName)
.perform(() ->
checkInPlaceAccess(contribDoc,
AccessStatus.DENIED, // read record permission
AccessStatus.DENIED, // filing permission
AccessStatus.DENIED, // view record capability
AccessStatus.DENIED, // edit non record metadata capability
AccessStatus.DENIED)); // edit record metadata capability
}
/**
* Given an unfiled in-place record
* When the record is moved to the file plan (ie filed)
* Then the site users still have the appropriate in-place permissions on the record
*/
public void testFileInplaceRecordFromCollabSite() throws Exception
{
test()
// Given an unfiled inplace record
.given()
.as(dmCollaborator)
.perform(() -> recordService.createRecord(filePlan, dmDocument))
.expect(true)
.from(() -> recordService.isRecord(dmDocument))
.because("The document is a record.")
.expect(false)
.from(() -> recordService.isFiled(dmDocument))
.because("The record is not filed")
// When the record is filed
.when()
.asAdmin()
.perform(() -> fileFolderService.move(dmDocument, rmFolder, null))
.then()
// Then the record is filed
.asAdmin()
.expect(true)
.from(() -> recordService.isFiled(dmDocument))
.because("The record is filed.")
// And the collaborator has filling permissions and filling capability on the record
.as(dmCollaborator)
.perform(() ->
checkInPlaceAccess(dmDocument,
AccessStatus.ALLOWED, // read record permission
AccessStatus.ALLOWED, // filing permission
AccessStatus.ALLOWED, // view record capability
AccessStatus.ALLOWED, // edit non record metadata capability
AccessStatus.DENIED)) // edit record metadata capability
// And a site contributor has read and view
.as(dmContributor)
.perform(() ->
checkInPlaceAccess(dmDocument,
AccessStatus.ALLOWED, // read record permission
AccessStatus.DENIED, // filing permission
AccessStatus.ALLOWED, // view record capability
AccessStatus.DENIED, // edit non record metadata capability
AccessStatus.DENIED)) // edit record metadata capability
// And the consumer has read permissions and view record capability on the record
.as(dmConsumer)
.perform(() ->
checkInPlaceAccess(dmDocument,
AccessStatus.ALLOWED, // read record permission
AccessStatus.DENIED, // filing permission
AccessStatus.ALLOWED, // view record capability
AccessStatus.DENIED, // edit non record metadata capability
AccessStatus.DENIED)) // edit record metadata capability
// And a user that is not in the site has no permissions on the record
.as(userName)
.perform(() ->
checkInPlaceAccess(dmDocument,
AccessStatus.DENIED, // read record permission
AccessStatus.DENIED, // filing permission
AccessStatus.DENIED, // view record capability
AccessStatus.DENIED, // edit non record metadata capability
AccessStatus.DENIED)); // edit record metadata capability
}
/**
* Given an incomplete inplace record
* When it is completed
* Then the inplace users still have access to the record
* And can't edit the records meta-data
*/
public void testCompletedInPlaceRecord()
{
test()
// Given an incomplete record
.given()
.as(dmCollaborator)
.perform(() -> recordService.createRecord(filePlan, dmDocument))
.expect(false)
.from(() -> recordService.isDeclared(dmDocument))
.because("Record is not complete.")
// When it is completed
.when()
.asAdmin()
.perform(() -> rmActionService.executeRecordsManagementAction(dmDocument, DeclareRecordAction.NAME))
.expect(true)
.from(() -> recordService.isDeclared(dmDocument))
.because("Record is complete.")
.then()
// Then the collaborator has filling permissions, view record capability, but not edit non-record metadata
.as(dmCollaborator)
.perform(() ->
checkInPlaceAccess(dmDocument,
AccessStatus.ALLOWED, // read record permission
AccessStatus.ALLOWED, // filing permission
AccessStatus.ALLOWED, // view record capability
AccessStatus.DENIED, // edit non record metadata capability
AccessStatus.DENIED)) // edit record metadata capability
// And a site contributor has read and view
.as(dmContributor)
.perform(() ->
checkInPlaceAccess(dmDocument,
AccessStatus.ALLOWED, // read record permission
AccessStatus.DENIED, // filing permission
AccessStatus.ALLOWED, // view record capability
AccessStatus.DENIED, // edit non record metadata capability
AccessStatus.DENIED)) // edit record metadata capability
// And the consumer has read permissions and view record capability on the record
.as(dmConsumer)
.perform(() ->
checkInPlaceAccess(dmDocument,
AccessStatus.ALLOWED, // read record permission
AccessStatus.DENIED, // filing permission
AccessStatus.ALLOWED, // view record capability
AccessStatus.DENIED, // edit non record metadata capability
AccessStatus.DENIED)) // edit record metadata capability
// And a user that is not in the site has no permissions on the record
.as(userName)
.perform(() ->
checkInPlaceAccess(dmDocument,
AccessStatus.DENIED, // read record permission
AccessStatus.DENIED, // filing permission
AccessStatus.DENIED, // view record capability
AccessStatus.DENIED, // edit non record metadata capability
AccessStatus.DENIED)); // edit record metadata capability
}
/**
* Given an inplace record ready for destruction
* When it is destroyed
* And it's metadata is maintained
* Then the inplace users will no longer see the record
*/
public void testDestroyedRecordInplacePermissions()
{
test()
.given()
// Given that a record is declared by a collaborator
.as(dmCollaborator)
.perform(() -> recordService.createRecord(filePlan, dmDocument))
.expect(true)
.from(() -> recordService.isRecord(dmDocument))
.because("Document is a record.")
// And it is filed into the file plan
// And eligible for destruction
.asAdmin()
.perform(() ->
{
// create record category and disposition schedule
NodeRef recordCategory = filePlanService.createRecordCategory(filePlan, GUID.generate());
utils.createBasicDispositionSchedule(recordCategory, GUID.generate(), GUID.generate(), true, true);
// create record folder and file record
NodeRef recordFolder = recordFolderService.createRecordFolder(recordCategory, GUID.generate());
fileFolderService.move(dmDocument, recordFolder, null);
// cut off record
rmActionService.executeRecordsManagementAction(dmDocument, DeclareRecordAction.NAME);
utils.completeEvent(dmDocument, CommonRMTestUtils.DEFAULT_EVENT_NAME);
rmActionService.executeRecordsManagementAction(dmDocument, CutOffAction.NAME);
})
.expect("destroy")
.from(() -> dispositionService.getNextDispositionAction(dmDocument).getName())
.because("The next action is destroy.")
.expect(true)
.from(() -> dispositionService.isNextDispositionActionEligible(dmDocument))
.because("The next action is eligible.")
// When the record is destroyed
.when(() -> rmActionService.executeRecordsManagementAction(dmDocument, DestroyAction.NAME))
.then()
.expect(true)
.from(() -> recordService.isMetadataStub(dmDocument))
.because("The record has been destroyed and the meta-stub remains.")
// Then the collaborator has no permissions or capabilities
.as(dmCollaborator)
.perform(() ->
checkInPlaceAccess(dmDocument,
AccessStatus.DENIED, // read record permission
AccessStatus.DENIED, // filing permission
AccessStatus.DENIED, // view record capability
AccessStatus.DENIED, // edit non record metadata capability
AccessStatus.DENIED)) // edit record metadata capability
// And a site contributor has no permissions or capabilities
.as(dmContributor)
.perform(() ->
checkInPlaceAccess(dmDocument,
AccessStatus.DENIED, // read record permission
AccessStatus.DENIED, // filing permission
AccessStatus.DENIED, // view record capability
AccessStatus.DENIED, // edit non record metadata capability
AccessStatus.DENIED)) // edit record metadata capability
// And the consumer has no permissions or capabilities
.as(dmConsumer)
.perform(() ->
checkInPlaceAccess(dmDocument,
AccessStatus.DENIED, // read record permission
AccessStatus.DENIED, // filing permission
AccessStatus.DENIED, // view record capability
AccessStatus.DENIED, // edit non record metadata capability
AccessStatus.DENIED)) // edit record metadata capability
// And a user that is not in the site has no permissions or capabilities
.as(userName)
.perform(() ->
checkInPlaceAccess(dmDocument,
AccessStatus.DENIED, // read record permission
AccessStatus.DENIED, // filing permission
AccessStatus.DENIED, // view record capability
AccessStatus.DENIED, // edit non record metadata capability
AccessStatus.DENIED)); // edit record metadata capability
}
/**
* Given an inplace record
* And the collaborator has view and edit non-record capability
* And doesn't have edit record capability
* When we add edit record metadata capability to the extended writer role
* Then the collaborator now has edit record metadata capability
*/
public void testAddUserToRole()
{
test()
.given()
.as(dmCollaborator)
// Given an inplace record
.perform(() -> recordService.createRecord(filePlan, dmDocument))
.expect(true)
.from(() -> recordService.isRecord(dmDocument))
.because("Document is a record.")
// And the collaborator has view and edit non-record capability
// And doesn't have edit record capability
.perform(() ->
checkInPlaceAccess(dmDocument,
AccessStatus.ALLOWED, // read record permission
AccessStatus.ALLOWED, // filing permission
AccessStatus.ALLOWED, // view record capability
AccessStatus.ALLOWED, // edit non record metadata capability
AccessStatus.DENIED)) // edit record metadata capability
.when()
.asAdmin()
// When we add edit record metadata capability to the extended writer role
.perform(() -> filePlanRoleService.updateRole(filePlan,
FilePlanRoleService.ROLE_EXTENDED_WRITERS,
"",
Stream
.of(viewRecordsCapability, editNonRecordMetadataCapability, editRecordMetadataCapability)
.collect(Collectors.toSet())))
.then()
.as(dmCollaborator)
// Then the collaborator now has edit record metadata capability
.perform(() ->
checkInPlaceAccess(dmDocument,
AccessStatus.ALLOWED, // read record permission
AccessStatus.ALLOWED, // filing permission
AccessStatus.ALLOWED, // view record capability
AccessStatus.ALLOWED, // edit non record metadata capability
AccessStatus.ALLOWED)) // edit record metadata capability
;
}
/**
* Given an inplace record
* When the record is hidden
* Then the collaborator has no access to the record
* And the consumer has no access to the record
* And a user that is not in the site has no permissions or capabilities
*/
public void testNoPermissionsAfterHide()
{
test()
.given()
.as(dmCollaborator)
// Given an inplace record
.perform(() -> recordService.createRecord(filePlan, dmDocument))
.expect(true)
.from(() -> recordService.isRecord(dmDocument))
.because("Document is a record.")
.when()
.asAdmin()
// When the record is hidden
.perform(() -> inplaceRecordService.hideRecord(dmDocument))
.then()
// Then the collaborator has no access to the record
.as(dmCollaborator)
.perform(() ->
checkInPlaceAccess(dmDocument,
AccessStatus.DENIED, // read record permission
AccessStatus.DENIED, // filing permission
AccessStatus.DENIED, // view record capability
AccessStatus.DENIED, // edit non record metadata capability
AccessStatus.DENIED)) // edit record metadata capability
// And a site contributor has read and view
.as(dmContributor)
.perform(() ->
checkInPlaceAccess(dmDocument,
AccessStatus.DENIED, // read record permission
AccessStatus.DENIED, // filing permission
AccessStatus.DENIED, // view record capability
AccessStatus.DENIED, // edit non record metadata capability
AccessStatus.DENIED)) // edit record metadata capability
// And the consumer has no access to the record
.as(dmConsumer)
.perform(() ->
checkInPlaceAccess(dmDocument,
AccessStatus.DENIED, // read record permission
AccessStatus.DENIED, // filing permission
AccessStatus.DENIED, // view record capability
AccessStatus.DENIED, // edit non record metadata capability
AccessStatus.DENIED)) // edit record metadata capability
// And a user that is not in the site has no permissions or capabilities
.as(userName)
.perform(() ->
checkInPlaceAccess(dmDocument,
AccessStatus.DENIED, // read record permission
AccessStatus.DENIED, // filing permission
AccessStatus.DENIED, // view record capability
AccessStatus.DENIED, // edit non record metadata capability
AccessStatus.DENIED)); // edit record metadata capability
;
}
/**
* Given an inplace record
* When the record is rejected
* Then the collaborator has no access to the record
* And the consumer has no access to the record
* And a user that is not in the site has no permissions or capabilities
*/
public void testNoPermissionsAfterReject()
{
test()
.given()
.as(dmCollaborator)
// Given an inplace record
.perform(() -> recordService.createRecord(filePlan, dmDocument))
.expect(true)
.from(() -> recordService.isRecord(dmDocument))
.because("Document is a record.")
.when()
.asAdmin()
// When the record is rejected
.perform(() -> recordService.rejectRecord(dmDocument, GUID.generate()))
.then()
// Then the collaborator has no access to the record
.as(dmCollaborator)
.perform(() ->
checkInPlaceAccess(dmDocument,
AccessStatus.DENIED, // read record permission
AccessStatus.DENIED, // filing permission
AccessStatus.DENIED, // view record capability
AccessStatus.DENIED, // edit non record metadata capability
AccessStatus.DENIED)) // edit record metadata capability
// And a site contributor has read and view
.as(dmContributor)
.perform(() ->
checkInPlaceAccess(dmDocument,
AccessStatus.DENIED, // read record permission
AccessStatus.DENIED, // filing permission
AccessStatus.DENIED, // view record capability
AccessStatus.DENIED, // edit non record metadata capability
AccessStatus.DENIED)) // edit record metadata capability
// And the consumer has no access to the record
.as(dmConsumer)
.perform(() ->
checkInPlaceAccess(dmDocument,
AccessStatus.DENIED, // read record permission
AccessStatus.DENIED, // filing permission
AccessStatus.DENIED, // view record capability
AccessStatus.DENIED, // edit non record metadata capability
AccessStatus.DENIED)) // edit record metadata capability
// And a user that is not in the site has no permissions or capabilities
.as(userName)
.perform(() ->
checkInPlaceAccess(dmDocument,
AccessStatus.DENIED, // read record permission
AccessStatus.DENIED, // filing permission
AccessStatus.DENIED, // view record capability
AccessStatus.DENIED, // edit non record metadata capability
AccessStatus.DENIED)); // edit record metadata capability
;
}
/**
* Given a user is the cm:creator of a document
* And the user is deleted
* When the document is declared as a record by a manager
* Then it successfully becomes a record
*/
public void testCmCreatorDeletedBeforeRecordDeclaration()
{
test()
.given()
.asAdmin()
.perform(() ->
{
deletedUser = GUID.generate();
createPerson(deletedUser);
siteService.setMembership(collabSiteId, deletedUser, SiteModel.SITE_CONTRIBUTOR);
})
.as(deletedUser)
.perform(() ->
{
deleteUserDoc = fileFolderService.create(dmFolder, "deleteUserDoc.txt" , ContentModel.TYPE_CONTENT).getNodeRef();
dbNodeService.addAspect(deleteUserDoc, ContentModel.ASPECT_AUDITABLE, null);
})
.asAdmin()
.perform(() -> personService.deletePerson(deletedUser))
.when()
.as(dmCollaborator)
.perform(() -> recordService.createRecord(filePlan, deleteUserDoc))
.then()
.expect(true)
.from(() -> recordService.isRecord(deleteUserDoc))
.because("The document is now a record.")
;
}
/**
* Given a document created by the collaborator
* And declared as a record by the collaborator
* And filed by the records manager
* When the records manager copies the record
* Then the collaborator has no access to the record copy
* And a site contributor has no access to the record copy
* And the consumer has no access to the record copy
* And a user that is not in the site has no access to the record copy
*/
public void testNoPermissionsOnCopy()
{
test()
.given()
.as(dmCollaborator)
.perform(() ->
{
// Given a document created by the collaborator
copiedDoc = fileFolderService.create(dmFolder, "copiedDoc.txt" , ContentModel.TYPE_CONTENT).getNodeRef();
dbNodeService.addAspect(copiedDoc, ContentModel.ASPECT_AUDITABLE, null);
// And declared as a record by the collaborator
recordService.createRecord(filePlan, copiedDoc);
})
.asAdmin()
// And filed by the records manager
.perform(() -> fileFolderService.move(copiedDoc, rmFolder, null))
.as(dmCollaborator)
.perform(() ->
checkInPlaceAccess(copiedDoc,
AccessStatus.ALLOWED, // read record permission
AccessStatus.ALLOWED, // filing permission
AccessStatus.ALLOWED, // view record capability
AccessStatus.ALLOWED, // edit non record metadata capability
AccessStatus.DENIED)) // edit record metadata capability
.when()
.asAdmin()
// When the records manager copies the record
.perform(() -> copyDoc = fileFolderService.copy(copiedDoc, rmFolder, "newRecord.txt").getNodeRef())
.then()
// Then the collaborator has no access to the record copy
.as(dmCollaborator)
.perform(() ->
checkInPlaceAccess(copyDoc,
AccessStatus.DENIED, // read record permission
AccessStatus.DENIED, // filing permission
AccessStatus.DENIED, // view record capability
AccessStatus.DENIED, // edit non record metadata capability
AccessStatus.DENIED)) // edit record metadata capability
.perform(() ->
checkInPlaceAccess(copiedDoc,
AccessStatus.ALLOWED, // read record permission
AccessStatus.ALLOWED, // filing permission
AccessStatus.ALLOWED, // view record capability
AccessStatus.ALLOWED, // edit non record metadata capability
AccessStatus.DENIED)) // edit record metadata capability
// And a site contributor has no access to the record copy
.as(dmContributor)
.perform(() ->
checkInPlaceAccess(copyDoc,
AccessStatus.DENIED, // read record permission
AccessStatus.DENIED, // filing permission
AccessStatus.DENIED, // view record capability
AccessStatus.DENIED, // edit non record metadata capability
AccessStatus.DENIED)) // edit record metadata capability
// And the consumer has no access to the record copy
.as(dmConsumer)
.perform(() ->
checkInPlaceAccess(copyDoc,
AccessStatus.DENIED, // read record permission
AccessStatus.DENIED, // filing permission
AccessStatus.DENIED, // view record capability
AccessStatus.DENIED, // edit non record metadata capability
AccessStatus.DENIED)) // edit record metadata capability
// And a user that is not in the site has no access to the record copy
.as(userName)
.perform(() ->
checkInPlaceAccess(copyDoc,
AccessStatus.DENIED, // read record permission
AccessStatus.DENIED, // filing permission
AccessStatus.DENIED, // view record capability
AccessStatus.DENIED, // edit non record metadata capability
AccessStatus.DENIED)); // edit record metadata capability
;
}
/**
* Test group reuse
*/
public void testGroupReuse()
{
test()
.when()
.as(dmCollaborator)
.perform(50, () ->
{
NodeRef newDocument = fileFolderService.create(dmFolder, GUID.generate(), ContentModel.TYPE_CONTENT).getNodeRef();
recordService.createRecord(filePlan, newDocument);
})
.as(dmContributor)
.perform(50, () ->
{
NodeRef newDocument = fileFolderService.create(dmFolder, GUID.generate(), ContentModel.TYPE_CONTENT).getNodeRef();
recordService.createRecord(filePlan, newDocument);
})
.then()
.asAdmin()
.expect(101)
.from(() -> nodeService.getChildAssocs(dmFolder).size())
.because("One hundred inplace records have been created.")
.expect(3)
.from(() -> authorityService.getContainedAuthorities(null, "GROUP_INPLACE_RECORD_MANAGEMENT", true).size())
.because("The read and write groups are reused.");
;
}
/**
* Test tear down
*/
@Override
protected void tearDownImpl()
{
super.tearDownImpl();
// clear up groups
authorityService.getContainedAuthorities(null, "GROUP_INPLACE_RECORD_MANAGEMENT", true)
.stream()
.forEach((group) -> authorityService.deleteAuthority(group));
}
}

View File

@@ -61,6 +61,9 @@ public class MoveInplaceRecordTest extends BaseRMTestCase
private Set<String> extendedReadersBeforeMove;
private Set<String> extendedWritersBeforeMove;
// primary parent of record
private NodeRef primaryParentBeforeMove;
public void given()
{
// Create the destination folder
@@ -84,8 +87,14 @@ public class MoveInplaceRecordTest extends BaseRMTestCase
// Check that the document is a record now
assertTrue(recordService.isRecord(dmDocument));
extendedReadersBeforeMove = extendedSecurityService.getExtendedReaders(dmDocument);
extendedWritersBeforeMove = extendedSecurityService.getExtendedWriters(dmDocument);
extendedReadersBeforeMove = extendedSecurityService.getReaders(dmDocument);
extendedWritersBeforeMove = extendedSecurityService.getWriters(dmDocument);
// get the primary parent and assert that it's a record management artifact
primaryParentBeforeMove = nodeService.getPrimaryParent(dmDocument).getParentRef();
assertTrue("Primary parent of newly created should be a records management artifact.",
filePlanService.isFilePlanComponent(primaryParentBeforeMove));
}
public void when()
@@ -105,15 +114,27 @@ public class MoveInplaceRecordTest extends BaseRMTestCase
public void then()
{
// assert that the document is still a record
assertTrue("After move the document should still be a record.",
recordService.isRecord(dmDocument));
// Check that the source folder is empty now and the destination folder has the document
assertEquals(0, nodeService.getChildAssocs(dmFolder).size());
List<ChildAssociationRef> destinationFolderChildAssocs = nodeService.getChildAssocs(destinationDmFolder);
assertEquals(1, destinationFolderChildAssocs.size());
assertEquals(dmDocument, destinationFolderChildAssocs.get(0).getChildRef());
// Check that the primary parent of the record has remained unchanged
NodeRef primaryParentAfterMove = nodeService.getPrimaryParent(dmDocument).getParentRef();
assertTrue("Primary parent of record after inplace move should be a records management artifact.",
filePlanService.isFilePlanComponent(primaryParentAfterMove));
assertEquals("Primary parent of record after inplace move should remain the same.",
primaryParentBeforeMove,
primaryParentAfterMove);
// Check extended readers/writers
Set<String> extendedReadersAfterMove = extendedSecurityService.getExtendedReaders(dmDocument);
Set<String> extendedWritersAfterMove = extendedSecurityService.getExtendedWriters(dmDocument);
Set<String> extendedReadersAfterMove = extendedSecurityService.getReaders(dmDocument);
Set<String> extendedWritersAfterMove = extendedSecurityService.getWriters(dmDocument);
assertEquals(extendedReadersBeforeMove.size(), extendedReadersAfterMove.size());
assertEquals(extendedWritersBeforeMove.size(), extendedWritersAfterMove.size());

View File

@@ -21,6 +21,7 @@ package org.alfresco.module.org_alfresco_module_rm.test.legacy.action;
import org.alfresco.module.org_alfresco_module_rm.action.impl.FileReportAction;
import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.repository.NodeRef;
import org.apache.commons.lang.StringUtils;
@@ -53,6 +54,8 @@ public class FileReportActionTest extends BaseRMTestCase
private void fileReport(final String mimeType)
{
AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser();
// create record folder
final NodeRef recordFolder = recordFolderService.createRecordFolder(rmContainer, GUID.generate());

View File

@@ -110,24 +110,27 @@ public class FileToActionTest extends BaseRMTestCase
// create record from document
recordService.createRecord(filePlan, dmDocument);
return null;
}
@Override
public void test(Void result) throws Exception
{
AuthenticationUtil.runAs(() ->
{
// check things have gone according to plan
assertTrue(recordService.isRecord(dmDocument));
assertFalse(recordService.isFiled(dmDocument));
AuthenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork<Void>()
{
public Void doWork() throws Exception
{
// is the unfiled container the primary parent of the filed record
NodeRef parent = nodeService.getPrimaryParent(dmDocument).getParentRef();
assertEquals(filePlanService.getUnfiledContainer(filePlan), parent);
// TODO Auto-generated method stub
return null;
}});
return null;
},
AuthenticationUtil.getAdminUserName());
}
}, dmCollaborator);
}

View File

@@ -104,7 +104,8 @@ public class RejectActionTest extends BaseRMTestCase
assertTrue(nodeService.getParentAssocs(dmDocument).size() == 1);
// The extended reader information should be removed
assertNull(extendedSecurityService.getExtendedReaders(dmDocument));
assertFalse(extendedSecurityService.hasExtendedSecurity(dmDocument));
assertTrue(extendedSecurityService.getReaders(dmDocument).isEmpty());
return null;
}

View File

@@ -1,26 +1,33 @@
/*
* Copyright (C) 2005-2014 Alfresco Software Limited.
*
* This file is part of Alfresco
*
it st * #%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.test.legacy.service;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.alfresco.model.ContentModel;
@@ -92,64 +99,29 @@ public class ExtendedSecurityServiceImplTest extends BaseRMTestCase
assertFalse(extendedSecurityService.hasExtendedSecurity(rmFolder));
assertFalse(extendedSecurityService.hasExtendedSecurity(record));
assertNull(extendedSecurityService.getExtendedReaders(record));
assertNull(extendedSecurityService.getExtendedWriters(record));
assertTrue(extendedSecurityService.getReaders(record).isEmpty());
assertTrue(extendedSecurityService.getWriters(record).isEmpty());
Set<String> extendedReaders = new HashSet<String>(2);
extendedReaders.add(monkey);
extendedReaders.add(elephant);
extendedSecurityService.addExtendedSecurity(record, extendedReaders, null);
Map<String, Integer> testMap = new HashMap<String, Integer>(2);
testMap.put(monkey, Integer.valueOf(1));
testMap.put(elephant, Integer.valueOf(1));
checkExtendedReaders(record, testMap);
extendedSecurityService.set(record, extendedReaders, null);
checkExtendedReaders(record, extendedReaders);
Set<String> extendedReadersToo = new HashSet<String>(2);
extendedReadersToo.add(monkey);
extendedReadersToo.add(snake);
extendedSecurityService.addExtendedSecurity(recordToo, extendedReadersToo, null);
extendedSecurityService.set(recordToo, extendedReadersToo, null);
checkExtendedReaders(recordToo, extendedReadersToo);
Map<String, Integer> testMapToo = new HashMap<String, Integer>(2);
testMapToo.put(monkey, Integer.valueOf(1));
testMapToo.put(snake, Integer.valueOf(1));
// test remove
extendedSecurityService.remove(recordToo);
Map<String, Integer> testMapThree = new HashMap<String, Integer>(3);
testMapThree.put(monkey, Integer.valueOf(2));
testMapThree.put(elephant, Integer.valueOf(1));
testMapThree.put(snake, Integer.valueOf(1));
checkExtendedReaders(recordToo, testMapToo);
// test remove (with no parent inheritance)
Set<String> removeMap1 = new HashSet<String>(2);
removeMap1.add(elephant);
removeMap1.add(monkey);
extendedSecurityService.removeExtendedSecurity(rmFolder, removeMap1, null, false);
Map<String, Integer> testMapFour = new HashMap<String, Integer>(2);
testMapFour.put(monkey, Integer.valueOf(1));
testMapFour.put(snake, Integer.valueOf(1));
checkExtendedReaders(recordToo, testMapToo);
// test remove (apply to parents)
Set<String> removeMap2 = new HashSet<String>(1);
removeMap2.add(snake);
extendedSecurityService.removeExtendedSecurity(recordToo, removeMap2, null, true);
testMapThree.remove(snake);
testMapFour.remove(snake);
testMapToo.remove(snake);
checkExtendedReaders(recordToo, testMapToo);
assertFalse(extendedSecurityService.hasExtendedSecurity(recordToo));
assertTrue(extendedSecurityService.getReaders(recordToo).isEmpty());
assertTrue(extendedSecurityService.getWriters(recordToo).isEmpty());
return null;
}
@@ -163,12 +135,12 @@ public class ExtendedSecurityServiceImplTest extends BaseRMTestCase
doTestInTransaction(new Test<Void>()
{
Map<String, Integer> testMap = new HashMap<String, Integer>(2);
Set<String> extendedReaders = new HashSet<String>(2);;
public Void run() throws Exception
{
testMap.put(monkey, Integer.valueOf(1));
testMap.put(elephant, Integer.valueOf(1));
extendedReaders.add(monkey);
extendedReaders.add(elephant);
assertFalse(extendedSecurityService.hasExtendedSecurity(filePlan));
assertFalse(extendedSecurityService.hasExtendedSecurity(rmContainer));
@@ -177,15 +149,12 @@ public class ExtendedSecurityServiceImplTest extends BaseRMTestCase
assertFalse(extendedSecurityService.hasExtendedSecurity(moveRecordCategory));
assertFalse(extendedSecurityService.hasExtendedSecurity(moveRecordFolder));
assertNull(extendedSecurityService.getExtendedReaders(record));
assertTrue(extendedSecurityService.getReaders(record).isEmpty());
Set<String> extendedReaders = new HashSet<String>(2);
extendedReaders.add(monkey);
extendedReaders.add(elephant);
extendedSecurityService.set(record, extendedReaders, null);
extendedSecurityService.addExtendedSecurity(record, extendedReaders, null);
checkExtendedReaders(record, extendedReaders);
checkExtendedReaders(record, testMap);
assertFalse(extendedSecurityService.hasExtendedSecurity(moveRecordCategory));
assertFalse(extendedSecurityService.hasExtendedSecurity(moveRecordFolder));
@@ -197,31 +166,21 @@ public class ExtendedSecurityServiceImplTest extends BaseRMTestCase
@Override
public void test(Void result) throws Exception
{
checkExtendedReaders(record, testMap);
checkExtendedReaders(record, extendedReaders);
}
});
}
@SuppressWarnings("unchecked")
private void checkExtendedReaders(NodeRef nodeRef, Map<String, Integer> testMap)
/**
* Check extended readers helper method
*/
private void checkExtendedReaders(NodeRef nodeRef, Set<String> testReaders)
{
assertTrue(extendedSecurityService.hasExtendedSecurity(nodeRef));
Map<String, Integer> readersMap = (Map<String,Integer>)nodeService.getProperty(nodeRef, PROP_READERS);
assertNotNull(readersMap);
assertEquals(testMap.size(), readersMap.size());
for (Map.Entry<String, Integer> entry: testMap.entrySet())
{
assertTrue(readersMap.containsKey(entry.getKey()));
assertEquals(entry.getKey(), entry.getValue(), readersMap.get(entry.getKey()));
}
Set<String> readers = extendedSecurityService.getExtendedReaders(nodeRef);
Set<String> readers = extendedSecurityService.getReaders(nodeRef);
assertNotNull(readers);
assertEquals(testMap.size(), readers.size());
assertEquals(testReaders, readers);
}
public void testDifferentUsersDifferentPermissions()

View File

@@ -24,8 +24,6 @@ import java.util.Set;
import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel;
import org.alfresco.module.org_alfresco_module_rm.role.FilePlanRoleService;
import org.alfresco.module.org_alfresco_module_rm.security.ExtendedReaderDynamicAuthority;
import org.alfresco.module.org_alfresco_module_rm.security.ExtendedWriterDynamicAuthority;
import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.service.cmr.repository.NodeRef;
@@ -1232,10 +1230,6 @@ public class FilePlanPermissionServiceImplTest extends BaseRMTestCase
accessPermissions.put(permission.getAuthority(), permission.getPermission());
}
assertTrue(accessPermissions.containsKey(ExtendedReaderDynamicAuthority.EXTENDED_READER));
assertEquals(RMPermissionModel.READ_RECORDS, accessPermissions.get(ExtendedReaderDynamicAuthority.EXTENDED_READER));
assertTrue(accessPermissions.containsKey(ExtendedWriterDynamicAuthority.EXTENDED_WRITER));
assertEquals(RMPermissionModel.FILING, accessPermissions.get(ExtendedWriterDynamicAuthority.EXTENDED_WRITER));
String adminRole = authorityService.getName(AuthorityType.GROUP, FilePlanRoleService.ROLE_ADMIN + filePlan.getId());
assertTrue(accessPermissions.containsKey(adminRole));
assertEquals(RMPermissionModel.FILING, accessPermissions.get(adminRole));

View File

@@ -29,8 +29,6 @@ import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel;
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
import org.alfresco.module.org_alfresco_module_rm.record.RecordService;
import org.alfresco.module.org_alfresco_module_rm.role.Role;
import org.alfresco.module.org_alfresco_module_rm.security.ExtendedReaderDynamicAuthority;
import org.alfresco.module.org_alfresco_module_rm.security.ExtendedWriterDynamicAuthority;
import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
@@ -172,22 +170,13 @@ public class RecordServiceImplTest extends BaseRMTestCase
public void testExtendedWriters() throws Exception
{
final ExtendedReaderDynamicAuthority readerDy = (ExtendedReaderDynamicAuthority)applicationContext.getBean("extendedReaderDynamicAuthority");
final ExtendedWriterDynamicAuthority writerDy = (ExtendedWriterDynamicAuthority)applicationContext.getBean("extendedWriterDynamicAuthority");
doTestInTransaction(new Test<Void>()
{
@Override
public Void run()
{
assertNull(extendedSecurityService.getExtendedReaders(recordOne));
assertNull(extendedSecurityService.getExtendedWriters(recordOne));
assertFalse(readerDy.hasAuthority(recordOne, dmCollaborator));
assertFalse(writerDy.hasAuthority(recordOne, dmCollaborator));
assertFalse(readerDy.hasAuthority(filePlan, dmCollaborator));
assertFalse(writerDy.hasAuthority(filePlan, dmCollaborator));
assertTrue(extendedSecurityService.getReaders(recordOne).isEmpty());
assertTrue(extendedSecurityService.getWriters(recordOne).isEmpty());
return null;
}
@@ -200,16 +189,9 @@ public class RecordServiceImplTest extends BaseRMTestCase
{
assertEquals(AccessStatus.DENIED, permissionService.hasPermission(recordOne, RMPermissionModel.READ_RECORDS));
assertEquals(AccessStatus.DENIED, permissionService.hasPermission(recordOne, RMPermissionModel.FILING));
assertFalse(readerDy.hasAuthority(recordOne, dmCollaborator));
assertFalse(writerDy.hasAuthority(recordOne, dmCollaborator));
assertEquals(AccessStatus.DENIED, permissionService.hasPermission(filePlan, RMPermissionModel.VIEW_RECORDS));
assertEquals(AccessStatus.DENIED, permissionService.hasPermission(filePlan, RMPermissionModel.EDIT_NON_RECORD_METADATA));
assertFalse(readerDy.hasAuthority(filePlan, dmCollaborator));
assertFalse(writerDy.hasAuthority(filePlan, dmCollaborator));
return null;
}
}, dmCollaborator);
@@ -221,10 +203,10 @@ public class RecordServiceImplTest extends BaseRMTestCase
{
Set<String> writers = new HashSet<String>(1);
writers.add(dmCollaborator);
extendedSecurityService.addExtendedSecurity(recordOne, null, writers);
extendedSecurityService.set(recordOne, null, writers);
assertNull(extendedSecurityService.getExtendedReaders(recordOne));
assertFalse(extendedSecurityService.getExtendedWriters(recordOne).isEmpty());
assertTrue(extendedSecurityService.getReaders(recordOne).isEmpty());
assertFalse(extendedSecurityService.getWriters(recordOne).isEmpty());
return null;
}
@@ -238,9 +220,7 @@ public class RecordServiceImplTest extends BaseRMTestCase
assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(recordOne, RMPermissionModel.READ_RECORDS));
assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(recordOne, RMPermissionModel.FILING));
assertFalse(readerDy.hasAuthority(recordOne, dmCollaborator));
assertTrue(writerDy.hasAuthority(recordOne, dmCollaborator));
// ALLOWED, becuase users have been added to the in-place roles
assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(filePlan, RMPermissionModel.VIEW_RECORDS));
assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(filePlan, RMPermissionModel.EDIT_NON_RECORD_METADATA));
@@ -302,7 +282,8 @@ public class RecordServiceImplTest extends BaseRMTestCase
public void test(Void result)
{
checkPermissions(READ_RECORDS, AccessStatus.DENIED, // file plan
checkPermissions(READ_RECORDS,
AccessStatus.DENIED, // file plan
AccessStatus.DENIED, // unfiled container
AccessStatus.DENIED, // record category
AccessStatus.DENIED, // record folder
@@ -311,7 +292,8 @@ public class RecordServiceImplTest extends BaseRMTestCase
assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(filePlan,
RMPermissionModel.VIEW_RECORDS));
checkPermissions(FILING, AccessStatus.DENIED, // file plan
checkPermissions(FILING,
AccessStatus.DENIED, // file plan
AccessStatus.DENIED, // unfiled container
AccessStatus.DENIED, // record category
AccessStatus.DENIED, // record folder

View File

@@ -262,6 +262,8 @@ public abstract class BaseRMTestCase extends RetryingTransactionHelperTestCase
/** collaboration site users */
protected String dmConsumer;
protected NodeRef dmConsumerNodeRef;
protected String dmContributor;
protected NodeRef dmContributorNodeRef;
protected String dmCollaborator;
protected NodeRef dmCollaboratorNodeRef;
@@ -412,11 +414,12 @@ public abstract class BaseRMTestCase extends RetryingTransactionHelperTestCase
@Override
public Object execute() throws Throwable
{
// As system user
AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName());
// Do the tear down
AuthenticationUtil.runAsSystem(() ->
{
tearDownImpl();
return null;
});
return null;
}
@@ -590,17 +593,22 @@ public abstract class BaseRMTestCase extends RetryingTransactionHelperTestCase
}
protected void setupTestUsers(final NodeRef filePlan)
{
AuthenticationUtil.runAs(() ->
{
retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback<Object>()
{
@Override
public Object execute() throws Throwable
{
AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());
setupTestUsersImpl(filePlan);
return null;
}
});
return null;
},
AuthenticationUtil.getAdminUserName());
}
/**
@@ -676,19 +684,15 @@ public abstract class BaseRMTestCase extends RetryingTransactionHelperTestCase
*/
protected void setupMultiHierarchyTestData()
{
retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback<Object>()
AuthenticationUtil.runAsSystem(() ->
{
@Override
public Object execute() throws Throwable
return retryingTransactionHelper.doInTransaction(() ->
{
// As system user
AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName());
// Do setup
setupMultiHierarchyTestDataImpl();
return null;
}
});
});
}
@@ -764,6 +768,10 @@ public abstract class BaseRMTestCase extends RetryingTransactionHelperTestCase
dmConsumerNodeRef = createPerson(dmConsumer);
siteService.setMembership(collabSiteId, dmConsumer, SiteModel.SITE_CONSUMER);
dmContributor = GUID.generate();
dmContributorNodeRef = createPerson(dmContributor);
siteService.setMembership(collabSiteId, dmContributor, SiteModel.SITE_CONTRIBUTOR);
dmCollaborator = GUID.generate();
dmCollaboratorNodeRef = createPerson(dmCollaborator);
siteService.setMembership(collabSiteId, dmCollaborator, SiteModel.SITE_COLLABORATOR);

View File

@@ -30,6 +30,7 @@ import java.util.Set;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService;
import org.alfresco.module.org_alfresco_module_rm.action.impl.CompleteEventAction;
import org.alfresco.module.org_alfresco_module_rm.action.impl.CutOffAction;
import org.alfresco.module.org_alfresco_module_rm.action.impl.DestroyAction;
import org.alfresco.module.org_alfresco_module_rm.action.impl.TransferAction;
@@ -292,4 +293,20 @@ public class CommonRMTestUtils implements RecordsManagementModel
return filePlanRoleService.createRole(filePlan, roleName, roleName, capabilities);
}
/**
* Helper method to complete event on disposable item
*
* @param disposableItem disposable item (record or record folder)
* @param eventName event name
*/
public void completeEvent(NodeRef disposableItem, String eventName)
{
// build action properties
Map<String, Serializable> params = new HashMap<String, Serializable>(1);
params.put(CompleteEventAction.PARAM_EVENT_NAME, eventName);
// complete event
actionService.executeRecordsManagementAction(disposableItem, CompleteEventAction.NAME, params);
}
}

View File

@@ -0,0 +1,276 @@
/*
* #%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.test.util.bdt;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
/**
* Helper class that provides an simple way to write behaviour integration tests.
* <p>
* Note that initBehaviourTest() must be called before given() is called.
*
* @author Roy Wetherall
* @since 2.5
*/
public class BehaviourTest
{
/** retrying transaction helper */
private static RetryingTransactionHelper retryingTransactionHelper;
/** current execution user */
private String asUser = AuthenticationUtil.getAdminUserName();
/**
* Initialise behaviour tests for execution with retrying transaction helper
*
* @param retryingTransactionHelper retrying transaction helper
*/
public static void initBehaviourTests(RetryingTransactionHelper retryingTransactionHelper)
{
BehaviourTest.retryingTransactionHelper = retryingTransactionHelper;
}
/**
* Start a test
*
* @return BehaviourTest new test instance
*/
public static BehaviourTest test()
{
return new BehaviourTest();
}
/**
* Helper method to get the retrying transaction helper
*
* @return RetryingTransactionHelper retrying transaction helper
*/
/*package*/ RetryingTransactionHelper getRetryingTransactionHelper()
{
return retryingTransactionHelper;
}
/**
* Helper method to get the execution user
*
* @return String execution user
*/
/* package*/ String getAsUser()
{
return asUser;
}
/**
* Helper method to switch the current execution user to admin.
*
* @return BehaviourTest test instance
*/
public BehaviourTest asAdmin()
{
return as(AuthenticationUtil.getAdminUserName());
}
/**
* Set execution user
*
* @param asUser execution user
* @return BehaviourTest test instance
*/
public BehaviourTest as(String asUser)
{
this.asUser = asUser;
return this;
}
/**
* Given.
* <p>
* Used to group together given conditions.
*
* @return BehaviourTest test instance
*/
public BehaviourTest given()
{
return this;
}
/**
* Given.
* <p>
* Performs work.
*
* @param given work to do
* @return BehaviourTest test instance
*/
public BehaviourTest given(Work given)
{
return perform(given);
}
/**
* When.
* <p>
* Used to group together when actions.
*
* @return BehaviourTest test instance
*/
public BehaviourTest when()
{
return this;
}
/**
* When.
* <p>
* Performs work.
*
* @param when work to do
* @return BehaviourTest test instance
*/
public BehaviourTest when(Work when)
{
return perform(when);
}
/**
* Then.
* <p>
* Used to group together then actions.
*
* @return BehaviourTest test instance
*/
public BehaviourTest then()
{
return this;
}
/**
* Then.
* <p>
* Performs work.
*
* @param then work to do
* @return BehaviourTest test instance
*/
public BehaviourTest then(Work then)
{
return perform(then);
}
/**
* Expect a value.
*
* @param value value
* @return ExpectedValue expected value evaluator
*/
public ExpectedValue<Boolean> expect(boolean value)
{
return new ExpectedValue<Boolean>(this, value);
}
/**
* Expect a value.
*
* @param value value
* @return ExpectedValue expected value evaluator
*/
public ExpectedValue<String> expect(String value)
{
return new ExpectedValue<String>(this, value);
}
/**
* Expect a value.
*
* @param value value
* @return ExpectedValue expected value evaluator
*/
public ExpectedValue<Object> expect(Object value)
{
return new ExpectedValue<Object>(this, value);
}
/**
* Expect a failure.
*
* @param exceptionClass expected exception
* @return ExpectedFailure expected failure evaluator
*/
public ExpectedFailure expectException(Class<? extends Exception> exceptionClass)
{
return new ExpectedFailure(this, exceptionClass);
}
/**
* Perform work a number of times
*
* @param count number of times to perform the work
* @param work work to perform
* @return BehaviourTest test instance
*/
public BehaviourTest perform(int count, Work work)
{
for (int i = 0; i < count; i++)
{
perform(work);
}
return this;
}
/**
* Perform work
*
* @param work work to perform
* @return BehaviourTest test instance
*/
public BehaviourTest perform(Work work)
{
return AuthenticationUtil.runAs(() ->
{
return retryingTransactionHelper.doInTransaction(() ->
{
work.doIt();
return this;
});
},
this.asUser);
}
/**
* Work Interface
*/
public interface Work
{
/**
* Do the work.
*/
void doIt() throws Exception;
}
}

View File

@@ -0,0 +1,92 @@
/*
* #%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.test.util.bdt;
import static org.junit.Assert.fail;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Set;
import java.util.stream.Collectors;
import org.alfresco.module.org_alfresco_module_rm.test.util.bdt.BehaviourTest.Work;
/**
* Expected failure.
*
* @author Roy Wetherall
* @since 2.5
*/
public class ExpectedFailure
{
private static final String MESSAGE = "Expected failure \"{0}\" was not observed.";
private BehaviourTest test;
private Set<Class<? extends Exception>> exceptionClasses;
private Work work;
@SafeVarargs
public ExpectedFailure(BehaviourTest test, Class<? extends Exception> ...exceptionClasses)
{
this.test = test;
this.exceptionClasses = Arrays.stream(exceptionClasses).collect(Collectors.toSet());
}
public ExpectedFailure from(Work work)
{
this.work = work;
return this;
}
public BehaviourTest because(String message)
{
try
{
test.perform(work);
}
catch(Exception actualException)
{
boolean found = false;
for (Class<? extends Exception> exceptionClass : exceptionClasses)
{
if (exceptionClass.isAssignableFrom(actualException.getClass()))
{
found = true;
}
}
if (!found)
{
fail(MessageFormat.format(MESSAGE, message));
}
}
return test;
}
}

View File

@@ -0,0 +1,86 @@
/*
* #%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.test.util.bdt;
import static org.junit.Assert.assertEquals;
import java.text.MessageFormat;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
/**
* Expected value.
*
* @author Roy Wetherall
* @since 2.5
*/
public class ExpectedValue<T>
{
private static final String MESSAGE = "Expected value outcome \"{0}\" was not observed.";
private T expectedValue;
private Evaluation<T> evaluation;
private BehaviourTest test;
public ExpectedValue(BehaviourTest test, T value)
{
this.expectedValue = value;
this.test = test;
}
public ExpectedValue<T> from(Evaluation<T> evaluation)
{
this.evaluation = evaluation;
return this;
}
public BehaviourTest because(String message)
{
T actualValue = (T)AuthenticationUtil.runAs(() ->
{
return test.getRetryingTransactionHelper().doInTransaction(() ->
{
return evaluation.eval();
});
},
test.getAsUser());
if (message != null)
{
message = MessageFormat.format(MESSAGE, message);
}
assertEquals(message, expectedValue, actualValue);
return test;
}
public interface Evaluation<T>
{
T eval() throws Exception;
}
}

View File

@@ -0,0 +1,928 @@
/*
* #%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.security;
import static org.alfresco.module.org_alfresco_module_rm.security.ExtendedSecurityServiceImpl.READER_GROUP_PREFIX;
import static org.alfresco.module.org_alfresco_module_rm.security.ExtendedSecurityServiceImpl.ROOT_IPR_GROUP;
import static org.alfresco.module.org_alfresco_module_rm.security.ExtendedSecurityServiceImpl.WRITER_GROUP_PREFIX;
import static org.alfresco.module.org_alfresco_module_rm.test.util.AlfMock.generateText;
import static org.alfresco.service.cmr.security.PermissionService.GROUP_PREFIX;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anySet;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.alfresco.model.RenditionModel;
import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel;
import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService;
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
import org.alfresco.module.org_alfresco_module_rm.role.FilePlanRoleService;
import org.alfresco.module.org_alfresco_module_rm.test.util.AlfMock;
import org.alfresco.query.PagingRequest;
import org.alfresco.query.PagingResults;
import org.alfresco.repo.security.authority.RMAuthority;
import org.alfresco.repo.security.permissions.impl.AccessPermissionImpl;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.security.AccessPermission;
import org.alfresco.service.cmr.security.AccessStatus;
import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.cmr.security.AuthorityType;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.namespace.RegexQNamePattern;
import org.alfresco.service.transaction.TransactionService;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.context.ApplicationContext;
import org.springframework.context.event.ContextRefreshedEvent;
/**
* Extended security service implementation unit test.
*
* @author Roy Wetherall
* @since 2.5
*/
public class ExtendedSecurityServiceImplUnitTest
{
/** service mocks*/
@Mock private FilePlanService mockedFilePlanService;
@Mock private FilePlanRoleService mockedFilePlanRoleService;
@Mock private AuthorityService mockedAuthorityService;
@Mock private PermissionService mockedPermissionService;
@Mock private TransactionService mockedTransactionService;
@Mock private RetryingTransactionHelper mockedRetryingTransactionHelper;
@Mock private NodeService mockedNodeService;
@Mock private PagingResults<String> mockedReadPagingResults;
@Mock private PagingResults<String> mockedWritePagingResults;
@Mock private ApplicationContext mockedApplicationContext;
@Mock private ChildAssociationRef mockedChildAssociationRef;
/** test component */
@InjectMocks private ExtendedSecurityServiceImpl extendedSecurityService;
/** read/write group full names */
private static final String READER_GROUP_FULL_PREFIX = GROUP_PREFIX + READER_GROUP_PREFIX;
private static final String WRITER_GROUP_FULL_PREFIX = GROUP_PREFIX + WRITER_GROUP_PREFIX;
/** test authorities */
private static final String USER = "USER";
private static final String GROUP = GROUP_PREFIX + "GROUP";
private static final String USER_W = "USER_W";
private static final String GROUP_W = GROUP_PREFIX + "GROUP_W";
private static final Set<String> READERS = Stream.of(USER, GROUP).collect(Collectors.toSet());
private static final Set<String> WRITERS = Stream.of(USER_W, GROUP_W).collect(Collectors.toSet());
/** has extended security permission set */
private static final Set<AccessPermission> HAS_EXTENDED_SECURITY = Stream
.of(new AccessPermissionImpl(AlfMock.generateText(), AccessStatus.ALLOWED, READER_GROUP_FULL_PREFIX, 0),
new AccessPermissionImpl(AlfMock.generateText(), AccessStatus.ALLOWED, AlfMock.generateText(), 1),
new AccessPermissionImpl(AlfMock.generateText(), AccessStatus.ALLOWED, WRITER_GROUP_FULL_PREFIX, 2))
.collect(Collectors.toSet());
/** has no extended security permission set */
private static final Set<AccessPermission> HAS_NO_EXTENDED_SECURITY = Stream
.of(new AccessPermissionImpl(AlfMock.generateText(), AccessStatus.ALLOWED, AlfMock.generateText(), 0),
new AccessPermissionImpl(AlfMock.generateText(), AccessStatus.ALLOWED, AlfMock.generateText(), 1),
new AccessPermissionImpl(AlfMock.generateText(), AccessStatus.ALLOWED, AlfMock.generateText(), 2))
.collect(Collectors.toSet());
/** test data */
private NodeRef nodeRef;
private NodeRef filePlan;
private String readGroupPrefix;
private String writeGroupPrefix;
/**
* Before tests
*/
@SuppressWarnings("unchecked")
@Before public void before()
{
// initialise mocks
MockitoAnnotations.initMocks(this);
// setup node
nodeRef = AlfMock.generateNodeRef(mockedNodeService);
// setup file plan
filePlan = AlfMock.generateNodeRef(mockedNodeService);
when(mockedFilePlanService.getFilePlan(any(NodeRef.class)))
.thenReturn(filePlan);
// set-up application context
when(mockedApplicationContext.getBean("dbNodeService"))
.thenReturn(mockedNodeService);
// setup retrying transaction helper
Answer<Object> doInTransactionAnswer = new Answer<Object>()
{
@SuppressWarnings("rawtypes")
@Override
public Object answer(InvocationOnMock invocation) throws Throwable
{
RetryingTransactionCallback callback = (RetryingTransactionCallback)invocation.getArguments()[0];
return callback.execute();
}
};
doAnswer(doInTransactionAnswer)
.when(mockedRetryingTransactionHelper)
.<Object>doInTransaction(any(RetryingTransactionCallback.class));
when(mockedTransactionService.getRetryingTransactionHelper())
.thenReturn(mockedRetryingTransactionHelper);
// setup create authority
Answer<String> createAuthorityAnswer = new Answer<String>()
{
public String answer(InvocationOnMock invocation) throws Throwable
{
return PermissionService.GROUP_PREFIX + (String)invocation.getArguments()[1];
}
};
when(mockedAuthorityService.createAuthority(any(AuthorityType.class), anyString(), anyString(), anySet()))
.thenAnswer(createAuthorityAnswer);
// setup group prefixes
readGroupPrefix = extendedSecurityService.getIPRGroupPrefixShortName(READER_GROUP_PREFIX, READERS);
writeGroupPrefix = extendedSecurityService.getIPRGroupPrefixShortName(WRITER_GROUP_PREFIX, WRITERS);
// make sure the users and groups exist
Stream
.of(USER, USER_W, GROUP, GROUP_W)
.forEach((a) ->
when(mockedAuthorityService.authorityExists(a))
.thenReturn(true));
}
/**
* Given that the root authority does not exist
* When the application context is refreshed
* Then the root authority is created
*/
@Test public void rootAuthorityDoesNotExist()
{
// group doesn't exist
when(mockedAuthorityService.authorityExists(GROUP_PREFIX + ExtendedSecurityServiceImpl.ROOT_IPR_GROUP))
.thenReturn(false);
// refresh context
extendedSecurityService.onApplicationEvent(mock(ContextRefreshedEvent.class));
// verify group is created
verify(mockedAuthorityService).createAuthority(AuthorityType.GROUP, ROOT_IPR_GROUP, ROOT_IPR_GROUP, Collections.singleton(RMAuthority.ZONE_APP_RM));
}
/**
* Given that the root authority exists
* When the application context is refreshed
* Then nothing happens
*/
@Test public void rootAuthorityDoesExist()
{
// group doesn't exist
when(mockedAuthorityService.authorityExists(GROUP_PREFIX + ROOT_IPR_GROUP))
.thenReturn(true);
// refresh context
extendedSecurityService.onApplicationEvent(mock(ContextRefreshedEvent.class));
// verify group is created
verify(mockedAuthorityService, never()).createAuthority(AuthorityType.GROUP, ROOT_IPR_GROUP, ROOT_IPR_GROUP, Collections.singleton(RMAuthority.ZONE_APP_RM));
}
/**
* Given that an IPR read group has read on a node
* And that an IPR write group has filling on a node
* When checking for the existence of extended permissions on that node
* Then it will be confirmed
*/
@Test public void hasExtendedSecurityWithReadAndWriteGroups()
{
// setup permissions
when(mockedPermissionService.getAllSetPermissions(nodeRef))
.thenReturn(HAS_EXTENDED_SECURITY);
// has extended security
assertTrue(extendedSecurityService.hasExtendedSecurity(nodeRef));
}
/**
* Given that there is no IPR read group has read on a node
* When checking for the existence of extended permissions on that node
* Then it will be denied
*/
@Test public void hasExtendedSecurityWithNoReadGroup()
{
// setup permissions
Set<AccessPermission> permissions = Stream
.of(new AccessPermissionImpl(AlfMock.generateText(), AccessStatus.ALLOWED, AlfMock.generateText(), 0),
new AccessPermissionImpl(AlfMock.generateText(), AccessStatus.ALLOWED, AlfMock.generateText(), 1),
new AccessPermissionImpl(AlfMock.generateText(), AccessStatus.ALLOWED, GROUP_PREFIX + WRITER_GROUP_PREFIX, 2))
.collect(Collectors.toSet());
when(mockedPermissionService.getAllSetPermissions(nodeRef))
.thenReturn(permissions);
// has extended security
assertFalse(extendedSecurityService.hasExtendedSecurity(nodeRef));
}
/**
* Given that there is no IPR write group has read on a node
* When checking for the existence of extended permissions on that node
* Then it will be denied
*/
@Test public void hasExtendedSecurityWithNoWriteGroup()
{
// setup permissions
Set<AccessPermission> permissions = Stream
.of(new AccessPermissionImpl(AlfMock.generateText(), AccessStatus.ALLOWED, GROUP_PREFIX + READER_GROUP_PREFIX, 0),
new AccessPermissionImpl(AlfMock.generateText(), AccessStatus.ALLOWED, AlfMock.generateText(), 1),
new AccessPermissionImpl(AlfMock.generateText(), AccessStatus.ALLOWED, AlfMock.generateText(), 2))
.collect(Collectors.toSet());
when(mockedPermissionService.getAllSetPermissions(nodeRef))
.thenReturn(permissions);
// has extended security
assertFalse(extendedSecurityService.hasExtendedSecurity(nodeRef));
}
/**
* Given that an IPR read group has no groups assigned permission
* When checking for the existence of extended permissions on that node
* Then it will be denied
*/
@Test public void hasExtendedSecurityWithNoAssignedGroups()
{
// setup permissions
when(mockedPermissionService.getAllSetPermissions(nodeRef))
.thenReturn(HAS_NO_EXTENDED_SECURITY);
// has extended security
assertFalse(extendedSecurityService.hasExtendedSecurity(nodeRef));
}
/**
* Given that there are no IPR groups assigned
* When I try and get the extended readers
* The I will get an empty set
*/
@Test public void getExtendedReadersNoIPRGroupsAssigned()
{
// setup permissions
when(mockedPermissionService.getAllSetPermissions(nodeRef))
.thenReturn(HAS_NO_EXTENDED_SECURITY);
// get extended readers
assertTrue(extendedSecurityService.getReaders(nodeRef).isEmpty());
}
/**
* Given that there are IPR groups assigned
* When I try and get the extended readers
* The I will get the set of readers
*/
@Test public void getExtendedReaders()
{
// setup permissions
when(mockedPermissionService.getAllSetPermissions(nodeRef))
.thenReturn(HAS_EXTENDED_SECURITY);
when(mockedAuthorityService.getContainedAuthorities(null, READER_GROUP_FULL_PREFIX, true))
.thenReturn(Stream
.of(USER, GROUP)
.collect(Collectors.toSet()));
// get extended readers
Set<String> extendedReaders = extendedSecurityService.getReaders(nodeRef);
assertEquals(Stream
.of(USER, GROUP)
.collect(Collectors.toSet()),
extendedReaders);
}
/**
* Given that there are no IPR groups assigned
* When I try and get the extended writers
* The I will get an empty set
*/
@Test public void getExtendedWritersNoIPRGroupsAssigned()
{
// setup permissions
when(mockedPermissionService.getAllSetPermissions(nodeRef))
.thenReturn(HAS_NO_EXTENDED_SECURITY);
// get extended writers
assertTrue(extendedSecurityService.getWriters(nodeRef).isEmpty());
}
/**
* Given that there are IPR groups assigned
* When I try and get the extended writers
* The I will get the set of writers
*/
@Test public void getExtendedWriters()
{
// setup permissions
when(mockedPermissionService.getAllSetPermissions(nodeRef))
.thenReturn(HAS_EXTENDED_SECURITY);
when(mockedAuthorityService.getContainedAuthorities(null, WRITER_GROUP_FULL_PREFIX, true))
.thenReturn(Stream
.of(USER, GROUP)
.collect(Collectors.toSet()));
// get extended writers
Set<String> extendedWriters = extendedSecurityService.getWriters(nodeRef);
assertEquals(Stream
.of(USER, GROUP)
.collect(Collectors.toSet()),
extendedWriters);
}
/**
* Given a node with no previous IPR groups assigned
* And no IPR group matching authorities
* When I add some read and write authorities
* Then new IPR groups are created
* And they are assigned to the node
* And they are added to the RM roles
*/
@Test public void addExtendedSecurityForTheFirstTimeAndCreateGroups()
{
// group names
String readGroup = extendedSecurityService.getIPRGroupShortName(READER_GROUP_PREFIX, READERS, 0);
String writeGroup = extendedSecurityService.getIPRGroupShortName(WRITER_GROUP_PREFIX, WRITERS, 0);
// setup query results
when(mockedReadPagingResults.getPage())
.thenReturn(Collections.emptyList());
when(mockedAuthorityService.getAuthorities(
eq(AuthorityType.GROUP),
eq(RMAuthority.ZONE_APP_RM),
any(String.class),
eq(false),
eq(false),
any(PagingRequest.class)))
.thenReturn(mockedReadPagingResults);
// add extended security
extendedSecurityService.set(nodeRef, READERS, WRITERS);
// verify no old permissions needing to be cleared
verify(mockedPermissionService, never()).clearPermission(eq(nodeRef), anyString());
// verify read group created correctly
verify(mockedAuthorityService).createAuthority(AuthorityType.GROUP, readGroup, readGroup, Collections.singleton(RMAuthority.ZONE_APP_RM));
readGroup = GROUP_PREFIX + readGroup;
verify(mockedAuthorityService).addAuthority(GROUP_PREFIX + ROOT_IPR_GROUP, readGroup);
verify(mockedAuthorityService).addAuthority(readGroup, USER);
verify(mockedAuthorityService).addAuthority(readGroup, GROUP);
// verify write group created correctly
verify(mockedAuthorityService).createAuthority(AuthorityType.GROUP, writeGroup, writeGroup, Collections.singleton(RMAuthority.ZONE_APP_RM));
writeGroup = GROUP_PREFIX + writeGroup;
verify(mockedAuthorityService).addAuthority(GROUP_PREFIX + ROOT_IPR_GROUP, writeGroup);
verify(mockedAuthorityService).addAuthority(writeGroup, USER_W);
verify(mockedAuthorityService).addAuthority(writeGroup, GROUP_W);
// verify groups assigned to RM roles
verify(mockedFilePlanRoleService).assignRoleToAuthority(filePlan, FilePlanRoleService.ROLE_EXTENDED_READERS, readGroup);
verify(mockedFilePlanRoleService).assignRoleToAuthority(filePlan, FilePlanRoleService.ROLE_EXTENDED_WRITERS, writeGroup);
// verify permissions are assigned to node
verify(mockedPermissionService).setPermission(nodeRef, readGroup, RMPermissionModel.READ_RECORDS, true);
verify(mockedPermissionService).setPermission(nodeRef, writeGroup, RMPermissionModel.FILING, true);
}
/**
* Given a node with no previous IPR groups assigned
* And existing IPR groups matching
* When I add some read and write authorities
* Then the existing IPR groups are used
* And they are assigned to the node
* And do not not need to be re-added to the RM roles
*/
@Test public void addExtendedSecurityForTheFirstTimeAndReuseGroups()
{
// group names
String readGroup = readGroupPrefix + "0";
String writeGroup = writeGroupPrefix + "0";
// setup query results
when(mockedReadPagingResults.getPage())
.thenReturn(Stream.of(GROUP_PREFIX + readGroup).collect(Collectors.toList()));
when(mockedAuthorityService.getAuthorities(
eq(AuthorityType.GROUP),
eq(RMAuthority.ZONE_APP_RM),
eq(readGroupPrefix),
eq(false),
eq(false),
any(PagingRequest.class)))
.thenReturn(mockedReadPagingResults);
when(mockedWritePagingResults.getPage())
.thenReturn(Stream.of(GROUP_PREFIX + writeGroup).collect(Collectors.toList()));
when(mockedAuthorityService.getAuthorities(
eq(AuthorityType.GROUP),
eq(RMAuthority.ZONE_APP_RM),
eq(writeGroupPrefix),
eq(false),
eq(false),
any(PagingRequest.class)))
.thenReturn(mockedWritePagingResults);
// setup exact match
when(mockedAuthorityService.authorityExists(GROUP_PREFIX + writeGroup))
.thenReturn(true);
when(mockedAuthorityService.getContainedAuthorities(null, GROUP_PREFIX + readGroup, true))
.thenReturn(Stream
.of(USER, GROUP)
.collect(Collectors.toSet()));
when(mockedAuthorityService.getContainedAuthorities(null, GROUP_PREFIX + writeGroup, true))
.thenReturn(Stream
.of(USER_W, GROUP_W)
.collect(Collectors.toSet()));
// add extended security
extendedSecurityService.set(nodeRef, READERS, WRITERS);
// verify no old permissions needing to be cleared
verify(mockedPermissionService, never()).clearPermission(eq(nodeRef), anyString());
// verify read group is not recreated
verify(mockedAuthorityService, never()).createAuthority(AuthorityType.GROUP, readGroup, readGroup, Collections.singleton(RMAuthority.ZONE_APP_RM));
readGroup = GROUP_PREFIX + readGroup;
verify(mockedAuthorityService, never()).addAuthority(GROUP_PREFIX + ROOT_IPR_GROUP, readGroup);
verify(mockedAuthorityService, never()).addAuthority(readGroup, USER);
verify(mockedAuthorityService, never()).addAuthority(readGroup, GROUP);
// verify write group is not recreated
verify(mockedAuthorityService, never()).createAuthority(AuthorityType.GROUP, writeGroup, writeGroup, Collections.singleton(RMAuthority.ZONE_APP_RM));
writeGroup = GROUP_PREFIX + writeGroup;
verify(mockedAuthorityService, never()).addAuthority(readGroup, writeGroup);
verify(mockedAuthorityService, never()).addAuthority(writeGroup, USER_W);
verify(mockedAuthorityService, never()).addAuthority(writeGroup, GROUP_W);
// verify groups assigned to RM roles
verify(mockedFilePlanRoleService).assignRoleToAuthority(filePlan, FilePlanRoleService.ROLE_EXTENDED_READERS, readGroup);
verify(mockedFilePlanRoleService).assignRoleToAuthority(filePlan, FilePlanRoleService.ROLE_EXTENDED_WRITERS, writeGroup);
// verify permissions are assigned to node
verify(mockedPermissionService).setPermission(nodeRef, readGroup, RMPermissionModel.READ_RECORDS, true);
verify(mockedPermissionService).setPermission(nodeRef, writeGroup, RMPermissionModel.FILING, true);
}
/**
* Given a node with no previous IPR groups assigned
* And existing IPR groups matches existing has, but not exact match
* When I add some read and write authorities
* Then new groups are created
* And they are assigned to the node
* And added to the RM roles
*/
@Test public void addExtendedSecurityForTheFirstTimeAndCreateGroupsAfterClash()
{
// group names
String readGroup = readGroupPrefix + "0";
String writeGroup = writeGroupPrefix + "0";
// setup query results
when(mockedReadPagingResults.getPage())
.thenReturn(Stream.of(GROUP_PREFIX + readGroup).collect(Collectors.toList()));
when(mockedAuthorityService.getAuthorities(
eq(AuthorityType.GROUP),
eq(RMAuthority.ZONE_APP_RM),
eq(readGroupPrefix),
eq(false),
eq(false),
any(PagingRequest.class)))
.thenReturn(mockedReadPagingResults);
when(mockedWritePagingResults.getPage())
.thenReturn(Stream.of(GROUP_PREFIX + writeGroup).collect(Collectors.toList()));
when(mockedAuthorityService.getAuthorities(
eq(AuthorityType.GROUP),
eq(RMAuthority.ZONE_APP_RM),
eq(writeGroupPrefix),
eq(false),
eq(false),
any(PagingRequest.class)))
.thenReturn(mockedWritePagingResults);
// setup exact match
when(mockedAuthorityService.authorityExists(GROUP_PREFIX + writeGroup))
.thenReturn(true);
when(mockedAuthorityService.getContainedAuthorities(null, GROUP_PREFIX + readGroup, true))
.thenReturn(Stream
.of(USER, GROUP, AlfMock.generateText())
.collect(Collectors.toSet()));
when(mockedAuthorityService.getContainedAuthorities(null, GROUP_PREFIX + writeGroup, true))
.thenReturn(Stream
.of(USER_W, AlfMock.generateText())
.collect(Collectors.toSet()));
// add extended security
extendedSecurityService.set(nodeRef, READERS, WRITERS);
// verify no old permissions needing to be cleared
verify(mockedPermissionService, never()).clearPermission(eq(nodeRef), anyString());
// new group names
readGroup = extendedSecurityService.getIPRGroupShortName(READER_GROUP_PREFIX, READERS, 1);
writeGroup = extendedSecurityService.getIPRGroupShortName(WRITER_GROUP_PREFIX, WRITERS, 1);
// verify read group created correctly
verify(mockedAuthorityService).createAuthority(AuthorityType.GROUP, readGroup, readGroup, Collections.singleton(RMAuthority.ZONE_APP_RM));
readGroup = GROUP_PREFIX + readGroup;
verify(mockedAuthorityService).addAuthority(GROUP_PREFIX + ROOT_IPR_GROUP, readGroup);
verify(mockedAuthorityService).addAuthority(readGroup, USER);
verify(mockedAuthorityService).addAuthority(readGroup, GROUP);
// verify write group created correctly
verify(mockedAuthorityService).createAuthority(AuthorityType.GROUP, writeGroup, writeGroup, Collections.singleton(RMAuthority.ZONE_APP_RM));
writeGroup = GROUP_PREFIX + writeGroup;
verify(mockedAuthorityService).addAuthority(GROUP_PREFIX + ROOT_IPR_GROUP, writeGroup);
verify(mockedAuthorityService).addAuthority(writeGroup, USER_W);
verify(mockedAuthorityService).addAuthority(writeGroup, GROUP_W);
// verify groups assigned to RM roles
verify(mockedFilePlanRoleService).assignRoleToAuthority(filePlan, FilePlanRoleService.ROLE_EXTENDED_READERS, readGroup);
verify(mockedFilePlanRoleService).assignRoleToAuthority(filePlan, FilePlanRoleService.ROLE_EXTENDED_WRITERS, writeGroup);
// verify permissions are assigned to node
verify(mockedPermissionService).setPermission(nodeRef, readGroup, RMPermissionModel.READ_RECORDS, true);
verify(mockedPermissionService).setPermission(nodeRef, writeGroup, RMPermissionModel.FILING, true);
}
/**
* Given a node with no previous IPR groups assigned
* And existing IPR groups matching, but found on second page of find results
* When I add some read and write authorities
* Then the existing IPR groups are used
* And they are assigned to the node
* And do not not need to be re-added to the RM roles
*/
@Test public void addExtendedSecurityWithResultPaging()
{
// group names
String readGroup = readGroupPrefix + "0";
String writeGroup = writeGroupPrefix + "0";
// create fity results
List<String> fiftyResults = new ArrayList<String>(50);
for (int i = 0; i < 50; i++)
{
fiftyResults.add(AlfMock.generateText());
}
// setup query results
when(mockedReadPagingResults.getPage())
.thenReturn(fiftyResults)
.thenReturn(Stream.of(GROUP_PREFIX + readGroup).collect(Collectors.toList()));
when(mockedAuthorityService.getAuthorities(
eq(AuthorityType.GROUP),
eq(RMAuthority.ZONE_APP_RM),
eq(readGroupPrefix),
eq(false),
eq(false),
any(PagingRequest.class)))
.thenReturn(mockedReadPagingResults);
when(mockedWritePagingResults.getPage())
.thenReturn(fiftyResults)
.thenReturn(Stream.of(GROUP_PREFIX + writeGroup).collect(Collectors.toList()));
when(mockedAuthorityService.getAuthorities(
eq(AuthorityType.GROUP),
eq(RMAuthority.ZONE_APP_RM),
eq(writeGroupPrefix),
eq(false),
eq(false),
any(PagingRequest.class)))
.thenReturn(mockedWritePagingResults);
// setup exact match
when(mockedAuthorityService.authorityExists(GROUP_PREFIX + writeGroup))
.thenReturn(true);
when(mockedAuthorityService.getContainedAuthorities(null, GROUP_PREFIX + readGroup, true))
.thenReturn(Stream
.of(USER, GROUP)
.collect(Collectors.toSet()));
when(mockedAuthorityService.getContainedAuthorities(null, GROUP_PREFIX + writeGroup, true))
.thenReturn(Stream
.of(USER_W, GROUP_W)
.collect(Collectors.toSet()));
// add extended security
extendedSecurityService.set(nodeRef, READERS, WRITERS);
// verify no old permissions needing to be cleared
verify(mockedPermissionService, never()).clearPermission(eq(nodeRef), anyString());
// verify read group is not recreated
verify(mockedAuthorityService, never()).createAuthority(AuthorityType.GROUP, readGroup, readGroup, Collections.singleton(RMAuthority.ZONE_APP_RM));
readGroup = GROUP_PREFIX + readGroup;
verify(mockedAuthorityService, never()).addAuthority(GROUP_PREFIX + ROOT_IPR_GROUP, readGroup);
verify(mockedAuthorityService, never()).addAuthority(readGroup, USER);
verify(mockedAuthorityService, never()).addAuthority(readGroup, GROUP);
// verify write group is not recreated
verify(mockedAuthorityService, never()).createAuthority(AuthorityType.GROUP, writeGroup, writeGroup, Collections.singleton(RMAuthority.ZONE_APP_RM));
writeGroup = GROUP_PREFIX + writeGroup;
verify(mockedAuthorityService, never()).addAuthority(readGroup, writeGroup);
verify(mockedAuthorityService, never()).addAuthority(writeGroup, USER_W);
verify(mockedAuthorityService, never()).addAuthority(writeGroup, GROUP_W);
// verify groups assigned to RM roles
verify(mockedFilePlanRoleService).assignRoleToAuthority(filePlan, FilePlanRoleService.ROLE_EXTENDED_READERS, readGroup);
verify(mockedFilePlanRoleService).assignRoleToAuthority(filePlan, FilePlanRoleService.ROLE_EXTENDED_WRITERS, writeGroup);
// verify permissions are assigned to node
verify(mockedPermissionService).setPermission(nodeRef, readGroup, RMPermissionModel.READ_RECORDS, true);
verify(mockedPermissionService).setPermission(nodeRef, writeGroup, RMPermissionModel.FILING, true);
}
/**
* Given that a node already has extended security
* When I add extended security
* Then the existing extended security is replaced with the new extended security
*/
@Test public void addExtendedSecurityToNodeWithExtendedSecurity()
{
// group names
String readGroup = extendedSecurityService.getIPRGroupShortName(READER_GROUP_FULL_PREFIX, READERS, 0);
String writeGroup = extendedSecurityService.getIPRGroupShortName(WRITER_GROUP_FULL_PREFIX, WRITERS, 0);
// setup permissions
Set<AccessPermission> permissions = Stream
.of(new AccessPermissionImpl(AlfMock.generateText(), AccessStatus.ALLOWED, readGroup, 0),
new AccessPermissionImpl(AlfMock.generateText(), AccessStatus.ALLOWED, AlfMock.generateText(), 1),
new AccessPermissionImpl(AlfMock.generateText(), AccessStatus.ALLOWED, writeGroup, 2))
.collect(Collectors.toSet());
when(mockedPermissionService.getAllSetPermissions(nodeRef))
.thenReturn(permissions);
// set revised reader and writers
String user = generateText();
String group = generateText();
String userW = generateText();
String groupW = generateText();
Set<String> newReaders = Stream.of(user, group).collect(Collectors.toSet());
Set<String> newWriters = Stream.of(userW, groupW).collect(Collectors.toSet());
// new group names
String newReadGroup = extendedSecurityService.getIPRGroupShortName(READER_GROUP_PREFIX, newReaders, 0);
String newWriteGroup = extendedSecurityService.getIPRGroupShortName(WRITER_GROUP_PREFIX, newWriters, 0);
// make sure users and groups exist
Stream
.of(user, group, userW, groupW)
.forEach((a) ->
when(mockedAuthorityService.authorityExists(a))
.thenReturn(true));
// setup query results for no group matches
when(mockedReadPagingResults.getPage())
.thenReturn(Collections.emptyList());
when(mockedAuthorityService.getAuthorities(
eq(AuthorityType.GROUP),
eq(RMAuthority.ZONE_APP_RM),
any(String.class),
eq(false),
eq(false),
any(PagingRequest.class)))
.thenReturn(mockedReadPagingResults);
// set extended security
extendedSecurityService.set(nodeRef, newReaders, newWriters);
// verify that the old permissions are cleared
verify(mockedPermissionService).clearPermission(nodeRef, readGroup);
verify(mockedPermissionService).clearPermission(nodeRef, writeGroup);
// verify read group created correctly
verify(mockedAuthorityService).createAuthority(AuthorityType.GROUP, newReadGroup, newReadGroup, Collections.singleton(RMAuthority.ZONE_APP_RM));
newReadGroup = GROUP_PREFIX + newReadGroup;
verify(mockedAuthorityService).addAuthority(GROUP_PREFIX + ROOT_IPR_GROUP, newReadGroup);
verify(mockedAuthorityService).addAuthority(newReadGroup, user);
verify(mockedAuthorityService).addAuthority(newReadGroup, group);
// verify write group created correctly
verify(mockedAuthorityService).createAuthority(AuthorityType.GROUP, newWriteGroup, newWriteGroup, Collections.singleton(RMAuthority.ZONE_APP_RM));
newWriteGroup = GROUP_PREFIX + newWriteGroup;
verify(mockedAuthorityService).addAuthority(GROUP_PREFIX + ROOT_IPR_GROUP, newWriteGroup);
verify(mockedAuthorityService).addAuthority(newWriteGroup, userW);
verify(mockedAuthorityService).addAuthority(newWriteGroup, groupW);
// verify groups assigned to RM roles
verify(mockedFilePlanRoleService).assignRoleToAuthority(filePlan, FilePlanRoleService.ROLE_EXTENDED_READERS, newReadGroup);
verify(mockedFilePlanRoleService).assignRoleToAuthority(filePlan, FilePlanRoleService.ROLE_EXTENDED_WRITERS, newWriteGroup);
// verify permissions are assigned to node
verify(mockedPermissionService).setPermission(nodeRef, newReadGroup, RMPermissionModel.READ_RECORDS, true);
verify(mockedPermissionService).setPermission(nodeRef, newWriteGroup, RMPermissionModel.FILING, true);
}
/**
* Given that a node has renditions
* When I add extended security
* Then they are applied to the renditions
*/
@Test public void extendedSecurityAddedToRenditions()
{
// group names
String readGroup = extendedSecurityService.getIPRGroupShortName(READER_GROUP_PREFIX, READERS, 0);
String writeGroup = extendedSecurityService.getIPRGroupShortName(WRITER_GROUP_PREFIX, WRITERS, 0);
// setup query results
when(mockedReadPagingResults.getPage())
.thenReturn(Collections.emptyList());
when(mockedAuthorityService.getAuthorities(
eq(AuthorityType.GROUP),
eq(RMAuthority.ZONE_APP_RM),
any(String.class),
eq(false),
eq(false),
any(PagingRequest.class)))
.thenReturn(mockedReadPagingResults);
// setup renditions
NodeRef renditionNodeRef = AlfMock.generateNodeRef(mockedNodeService);
when(mockedNodeService.hasAspect(nodeRef, RecordsManagementModel.ASPECT_RECORD))
.thenReturn(true);
when(mockedChildAssociationRef.getChildRef())
.thenReturn(renditionNodeRef);
when(mockedNodeService.getChildAssocs(nodeRef, RenditionModel.ASSOC_RENDITION, RegexQNamePattern.MATCH_ALL))
.thenReturn(Collections.singletonList(mockedChildAssociationRef));
// add extended security
extendedSecurityService.set(nodeRef, READERS, WRITERS);
// verify no old permissions needing to be cleared
verify(mockedPermissionService, never()).clearPermission(eq(nodeRef), anyString());
// verify read group created correctly
verify(mockedAuthorityService).createAuthority(AuthorityType.GROUP, readGroup, readGroup, Collections.singleton(RMAuthority.ZONE_APP_RM));
readGroup = GROUP_PREFIX + readGroup;
verify(mockedAuthorityService).addAuthority(GROUP_PREFIX + ROOT_IPR_GROUP, readGroup);
verify(mockedAuthorityService).addAuthority(readGroup, USER);
verify(mockedAuthorityService).addAuthority(readGroup, GROUP);
// verify write group created correctly
verify(mockedAuthorityService).createAuthority(AuthorityType.GROUP, writeGroup, writeGroup, Collections.singleton(RMAuthority.ZONE_APP_RM));
writeGroup = GROUP_PREFIX + writeGroup;
verify(mockedAuthorityService).addAuthority(GROUP_PREFIX + ROOT_IPR_GROUP, writeGroup);
verify(mockedAuthorityService).addAuthority(writeGroup, USER_W);
verify(mockedAuthorityService).addAuthority(writeGroup, GROUP_W);
// verify groups assigned to RM roles
verify(mockedFilePlanRoleService).assignRoleToAuthority(filePlan, FilePlanRoleService.ROLE_EXTENDED_READERS, readGroup);
verify(mockedFilePlanRoleService).assignRoleToAuthority(filePlan, FilePlanRoleService.ROLE_EXTENDED_WRITERS, writeGroup);
// verify permissions are assigned to node
verify(mockedPermissionService).setPermission(nodeRef, readGroup, RMPermissionModel.READ_RECORDS, true);
verify(mockedPermissionService).setPermission(nodeRef, writeGroup, RMPermissionModel.FILING, true);
// verify permissions are assigned to the rendition
verify(mockedPermissionService).setPermission(renditionNodeRef, readGroup, RMPermissionModel.READ_RECORDS, true);
verify(mockedPermissionService).setPermission(renditionNodeRef, writeGroup, RMPermissionModel.FILING, true);
}
/**
* Given that a node has extended security
* When I remove the extended security
* Then the inplace groups permissions are removed
*/
@Test public void removeAllExtendedSecurity()
{
// group names
String readGroup = extendedSecurityService.getIPRGroupShortName(READER_GROUP_FULL_PREFIX, READERS, 0);
String writeGroup = extendedSecurityService.getIPRGroupShortName(WRITER_GROUP_FULL_PREFIX, WRITERS, 0);
// setup permissions
Set<AccessPermission> permissions = Stream
.of(new AccessPermissionImpl(AlfMock.generateText(), AccessStatus.ALLOWED, readGroup, 0),
new AccessPermissionImpl(AlfMock.generateText(), AccessStatus.ALLOWED, AlfMock.generateText(), 1),
new AccessPermissionImpl(AlfMock.generateText(), AccessStatus.ALLOWED, writeGroup, 2))
.collect(Collectors.toSet());
when(mockedPermissionService.getAllSetPermissions(nodeRef))
.thenReturn(permissions);
// remove extended security
extendedSecurityService.remove(nodeRef);
// verify that the groups permissions have been removed
verify(mockedPermissionService).clearPermission(nodeRef, readGroup);
verify(mockedPermissionService).clearPermission(nodeRef, writeGroup);
}
/**
* Given that a node has no extended security
* When I remove the extended security
* Then nothing happens
*/
@Test public void noExtendedSecurityToRemove()
{
when(mockedPermissionService.getAllSetPermissions(nodeRef))
.thenReturn(HAS_NO_EXTENDED_SECURITY);
// remove extended security
extendedSecurityService.remove(nodeRef);
// verify that the groups permissions have been removed
verify(mockedPermissionService, never()).clearPermission(eq(nodeRef), anyString());
}
/**
* Given that node has renditions
* When I remove the extended security for a node
* Then the extended security is also removed from the renditions
*/
@Test public void removeExtendedSecurityFromRenditions()
{
// group names
String readGroup = extendedSecurityService.getIPRGroupShortName(READER_GROUP_FULL_PREFIX, READERS, 0);
String writeGroup = extendedSecurityService.getIPRGroupShortName(WRITER_GROUP_FULL_PREFIX, WRITERS, 0);
// setup renditions
NodeRef renditionNodeRef = AlfMock.generateNodeRef(mockedNodeService);
when(mockedNodeService.hasAspect(nodeRef, RecordsManagementModel.ASPECT_RECORD))
.thenReturn(true);
when(mockedChildAssociationRef.getChildRef())
.thenReturn(renditionNodeRef);
when(mockedNodeService.getChildAssocs(nodeRef, RenditionModel.ASSOC_RENDITION, RegexQNamePattern.MATCH_ALL))
.thenReturn(Collections.singletonList(mockedChildAssociationRef));
// setup permissions
Set<AccessPermission> permissions = Stream
.of(new AccessPermissionImpl(AlfMock.generateText(), AccessStatus.ALLOWED, readGroup, 0),
new AccessPermissionImpl(AlfMock.generateText(), AccessStatus.ALLOWED, AlfMock.generateText(), 1),
new AccessPermissionImpl(AlfMock.generateText(), AccessStatus.ALLOWED, writeGroup, 2))
.collect(Collectors.toSet());
when(mockedPermissionService.getAllSetPermissions(nodeRef))
.thenReturn(permissions);
// remove extended security
extendedSecurityService.remove(nodeRef);
// verify that the groups permissions have been removed
verify(mockedPermissionService).clearPermission(nodeRef, readGroup);
verify(mockedPermissionService).clearPermission(nodeRef, writeGroup);
// verify that the groups permissions have been removed from the rendition
verify(mockedPermissionService).clearPermission(renditionNodeRef, readGroup);
verify(mockedPermissionService).clearPermission(renditionNodeRef, writeGroup);
}
}

View File

@@ -18,13 +18,10 @@
*/
package org.alfresco.module.org_alfresco_module_rm.security;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import java.util.HashSet;
import java.util.Set;
@@ -164,15 +161,6 @@ public class FilePlanPermissionServiceImplUnitTest extends BaseUnitTest
// verify permission set on target node
verify(mockedPermissionService, times(1)).setPermission(unfiledRecordFolder, AUTHORITY, RMPermissionModel.READ_RECORDS, true);
// verify READ permission set up hierarchy
verify(mockedPermissionService, times(1)).setPermission(unfiledRecordContainer, AUTHORITY, RMPermissionModel.READ_RECORDS, true);
verify(mockedPermissionService, times(1)).setPermission(filePlan, AUTHORITY, RMPermissionModel.READ_RECORDS, true);
// verify READ permission set down hierarchy
verify(mockedPermissionService, times(1)).setPermission(unfiledRecordFolderChild, AUTHORITY, RMPermissionModel.READ_RECORDS, true);
verify(mockedPermissionService, times(1)).setPermission(unfiledRecord, AUTHORITY, RMPermissionModel.READ_RECORDS, true);
}
/**
@@ -186,14 +174,6 @@ public class FilePlanPermissionServiceImplUnitTest extends BaseUnitTest
// verify permission set on target node
verify(mockedPermissionService, times(1)).setPermission(unfiledRecordFolder, AUTHORITY, RMPermissionModel.FILING, true);
// verify READ permission set up hierarchy
verify(mockedPermissionService, times(1)).setPermission(unfiledRecordContainer, AUTHORITY, RMPermissionModel.READ_RECORDS, true);
verify(mockedPermissionService, times(1)).setPermission(filePlan, AUTHORITY, RMPermissionModel.READ_RECORDS, true);
// verify FILING permission set down hierarchy
verify(mockedPermissionService, times(1)).setPermission(unfiledRecordFolderChild, AUTHORITY, RMPermissionModel.FILING, true);
verify(mockedPermissionService, times(1)).setPermission(unfiledRecord, AUTHORITY, RMPermissionModel.FILING, true);
}
/**
@@ -207,14 +187,6 @@ public class FilePlanPermissionServiceImplUnitTest extends BaseUnitTest
// verify permission deleted on target node
verify(mockedPermissionService, times(1)).deletePermission(unfiledRecordFolder, AUTHORITY, RMPermissionModel.READ_RECORDS);
// verify no permissions deleted up the hierarchy
verify(mockedPermissionService, never()).deletePermission(eq(unfiledRecordContainer), eq(AUTHORITY), anyString());
verify(mockedPermissionService, never()).deletePermission(eq(filePlan), eq(AUTHORITY), anyString());
// verify READ permission removed down hierarchy
verify(mockedPermissionService, times(1)).deletePermission(unfiledRecordFolderChild, AUTHORITY, RMPermissionModel.READ_RECORDS);
verify(mockedPermissionService, times(1)).deletePermission(unfiledRecord, AUTHORITY, RMPermissionModel.READ_RECORDS);
}
/**
@@ -228,16 +200,6 @@ public class FilePlanPermissionServiceImplUnitTest extends BaseUnitTest
// verify permission set on target node
verify(mockedPermissionService, times(1)).setPermission(holdContainer, AUTHORITY, RMPermissionModel.READ_RECORDS, true);
// verify READ permission set up hierarchy
verify(mockedPermissionService, times(1)).setPermission(filePlan, AUTHORITY, RMPermissionModel.READ_RECORDS, true);
// verify READ permission set on hold
verify(mockedPermissionService, times(1)).setPermission(hold, AUTHORITY, RMPermissionModel.READ_RECORDS, true);
// verify permission not set on child of hold
verify(mockedPermissionService, never()).setPermission(eq(heldRecord), eq(AUTHORITY), anyString(), eq(true));
}
/**
@@ -251,16 +213,6 @@ public class FilePlanPermissionServiceImplUnitTest extends BaseUnitTest
// verify permission set on target node
verify(mockedPermissionService, times(1)).setPermission(holdContainer, AUTHORITY, RMPermissionModel.FILING, true);
// verify READ permission set up hierarchy
verify(mockedPermissionService, times(1)).setPermission(filePlan, AUTHORITY, RMPermissionModel.READ_RECORDS, true);
// verify FILING permission set on hold
verify(mockedPermissionService, times(1)).setPermission(hold, AUTHORITY, RMPermissionModel.FILING, true);
// verify permission not set on child of hold
verify(mockedPermissionService, never()).setPermission(eq(heldRecord), eq(AUTHORITY), anyString(), eq(true));
}
/**
@@ -274,13 +226,6 @@ public class FilePlanPermissionServiceImplUnitTest extends BaseUnitTest
// verify permission set on target node
verify(mockedPermissionService, times(1)).setPermission(hold, AUTHORITY, RMPermissionModel.READ_RECORDS, true);
// verify READ permission set up hierarchy
verify(mockedPermissionService, times(1)).setPermission(holdContainer, AUTHORITY, RMPermissionModel.READ_RECORDS, true);
verify(mockedPermissionService, times(1)).setPermission(filePlan, AUTHORITY, RMPermissionModel.READ_RECORDS, true);
// verify permission not set on child of hold
verify(mockedPermissionService, never()).setPermission(eq(heldRecord), eq(AUTHORITY), anyString(), eq(true));
}
/**
@@ -294,13 +239,6 @@ public class FilePlanPermissionServiceImplUnitTest extends BaseUnitTest
// verify permission set on target node
verify(mockedPermissionService, times(1)).setPermission(hold, AUTHORITY, RMPermissionModel.FILING, true);
// verify READ permission set up hierarchy
verify(mockedPermissionService, times(1)).setPermission(holdContainer, AUTHORITY, RMPermissionModel.READ_RECORDS, true);
verify(mockedPermissionService, times(1)).setPermission(filePlan, AUTHORITY, RMPermissionModel.READ_RECORDS, true);
// verify permission not set on child of hold
verify(mockedPermissionService, never()).setPermission(eq(heldRecord), eq(AUTHORITY), anyString(), eq(true));
}
/**
@@ -314,23 +252,18 @@ public class FilePlanPermissionServiceImplUnitTest extends BaseUnitTest
perms.add(new AccessPermissionImpl(RMPermissionModel.READ_RECORDS, AccessStatus.ALLOWED, AUTHORITY, 0));
perms.add(new AccessPermissionImpl(RMPermissionModel.FILING, AccessStatus.ALLOWED, AUTHORITY2, 1));
// setup in-place readers and writers
perms.add(new AccessPermissionImpl(RMPermissionModel.READ_RECORDS, AccessStatus.ALLOWED, ExtendedReaderDynamicAuthority.EXTENDED_READER, 2));
perms.add(new AccessPermissionImpl(RMPermissionModel.FILING, AccessStatus.ALLOWED, ExtendedWriterDynamicAuthority.EXTENDED_WRITER, 3));
doReturn(perms).when(mockedPermissionService).getAllSetPermissions(nodeRef);
}
/**
* Helper to verify the core permissions have been initialized correctly
*/
private void verifyInitPermissions(NodeRef nodeRef)
private void verifyInitPermissions(NodeRef nodeRef, boolean isInherited)
{
verify(mockedPermissionService, times(1)).setInheritParentPermissions(nodeRef, false);
verify(mockedPermissionService, times(1)).clearPermission(nodeRef, null);
verify(mockedPermissionService, times(1)).setPermission(nodeRef, ExtendedReaderDynamicAuthority.EXTENDED_READER, RMPermissionModel.READ_RECORDS, true);
verify(mockedPermissionService, times(1)).setPermission(nodeRef, ExtendedWriterDynamicAuthority.EXTENDED_WRITER, RMPermissionModel.FILING, true);
verify(mockedOwnableService, times(1)).setOwner(nodeRef, OwnableService.NO_OWNER);
verify(mockedPermissionService).getAllSetPermissions(nodeRef);
verify(mockedPermissionService).setInheritParentPermissions(nodeRef, isInherited);
verify(mockedPermissionService).clearPermission(nodeRef, null);
verify(mockedOwnableService).setOwner(nodeRef, OwnableService.NO_OWNER);
}
/**
@@ -341,21 +274,20 @@ public class FilePlanPermissionServiceImplUnitTest extends BaseUnitTest
* @param read verification mode relating to setting read on the child
* @param filling verification mode relating to setting filling on the child
*/
private void verifyInitPermissions(NodeRef parent, NodeRef child, VerificationMode read, VerificationMode filling)
private void verifyInitPermissions(NodeRef parent, NodeRef child, VerificationMode read, VerificationMode filling, boolean isParentFilePlan, boolean isInherited)
{
// verify the core permissions are set-up correctly
verifyInitPermissions(child);
verifyInitPermissions(child, isInherited);
if (isParentFilePlan)
{
// verify the permissions came from the correct parent
verify(mockedPermissionService).getAllSetPermissions(parent);
// verify all the inherited permissions are set correctly (note read are not inherited from fileplan)
verify(mockedPermissionService, filling).setPermission(child, AUTHORITY2, RMPermissionModel.FILING, true);
verify(mockedPermissionService, read).setPermission(child, AUTHORITY, RMPermissionModel.READ_RECORDS, true);
// verify that there are no unaccounted for interactions with the permission service
verifyNoMoreInteractions(mockedPermissionService);
}
}
/**
@@ -371,7 +303,7 @@ public class FilePlanPermissionServiceImplUnitTest extends BaseUnitTest
filePlanPermissionService.setupRecordCategoryPermissions(rootRecordCategory);
// verify permission init
verifyInitPermissions(filePlan, rootRecordCategory, never(), times(1));
verifyInitPermissions(filePlan, rootRecordCategory, never(), times(1), true, false);
}
/**
@@ -387,7 +319,7 @@ public class FilePlanPermissionServiceImplUnitTest extends BaseUnitTest
filePlanPermissionService.setupRecordCategoryPermissions(recordCategory);
// verify permission init
verifyInitPermissions(rootRecordCategory, recordCategory, times(1), times(1));
verifyInitPermissions(rootRecordCategory, recordCategory, times(1), times(1), false, true);
}
/**
@@ -403,7 +335,7 @@ public class FilePlanPermissionServiceImplUnitTest extends BaseUnitTest
filePlanPermissionService.setupPermissions(recordCategory, newRecordFolder);
// verify permission init
verifyInitPermissions(recordCategory, newRecordFolder, times(1), times(1));
verifyInitPermissions(recordCategory, newRecordFolder, times(1), times(1), false, true);
}
@@ -420,7 +352,7 @@ public class FilePlanPermissionServiceImplUnitTest extends BaseUnitTest
filePlanPermissionService.setupPermissions(newRecordFolder, newRecord);
// verify permission init
verifyInitPermissions(newRecordFolder, newRecord, times(1), times(1));
verifyInitPermissions(newRecordFolder, newRecord, times(1), times(1), false, true);
}
/**
@@ -436,7 +368,7 @@ public class FilePlanPermissionServiceImplUnitTest extends BaseUnitTest
filePlanPermissionService.setupPermissions(filePlan, holdContainer);
// verify permissions are set-up correctly
verifyInitPermissions(filePlan, holdContainer, times(1), times(1));
verifyInitPermissions(filePlan, holdContainer, times(1), times(1), false, true);
}
/**
@@ -452,7 +384,7 @@ public class FilePlanPermissionServiceImplUnitTest extends BaseUnitTest
filePlanPermissionService.setupPermissions(holdContainer, hold);
// verify permissions are set-up correctly
verifyInitPermissions(holdContainer, hold, never(), times(1));
verifyInitPermissions(holdContainer, hold, never(), times(1), false, false);
}
@@ -469,7 +401,7 @@ public class FilePlanPermissionServiceImplUnitTest extends BaseUnitTest
filePlanPermissionService.setupPermissions(filePlan, unfiledRecordContainer);
// verify permissions are set-up correctly
verifyInitPermissions(filePlan, unfiledRecordContainer, times(1), times(1));
verifyInitPermissions(filePlan, unfiledRecordContainer, times(1), times(1), false, false);
}
/**
@@ -485,7 +417,7 @@ public class FilePlanPermissionServiceImplUnitTest extends BaseUnitTest
filePlanPermissionService.setupPermissions(unfiledRecordContainer, unfiledRecordFolder);
// verify permissions are set-up correctly
verifyInitPermissions(unfiledRecordContainer, unfiledRecordFolder, never(), times(1));
verifyInitPermissions(unfiledRecordContainer, unfiledRecordFolder, never(), times(1), false, true);
}
@@ -502,7 +434,7 @@ public class FilePlanPermissionServiceImplUnitTest extends BaseUnitTest
filePlanPermissionService.setupPermissions(unfiledRecordFolder, unfiledRecord);
// verify permission init
verifyInitPermissions(unfiledRecordFolder, unfiledRecord, times(1), times(1));
verifyInitPermissions(unfiledRecordFolder, unfiledRecord, times(1), times(1), false, true);
}
}

View File

@@ -0,0 +1,130 @@
/*
* #%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.test.util;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;
import java.util.UUID;
import org.alfresco.model.ContentModel;
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.namespace.QName;
import org.alfresco.util.GUID;
/**
* Utilities helpful when mocking Alfresco constructs.
*
* @author Roy Wetherall
* @since 2.4.a
*/
public class AlfMock
{
/**
* Helper to generate random text value suitable for a property
* value or node name
*/
public static String generateText()
{
return UUID.randomUUID().toString();
}
/**
* Helper method to generate a qname.
*/
public static QName generateQName()
{
return generateQName(GUID.generate());
}
/**
* Helper method to generate a qname.
*/
public static QName generateQName(String uri)
{
return QName.createQName(uri, GUID.generate());
}
/**
* Helper method to generate a node reference.
*
* @return {@link NodeRef} node reference that behaves like a node that exists in the spaces store
*/
public static NodeRef generateNodeRef(NodeService mockedNodeService)
{
return generateNodeRef(mockedNodeService, null);
}
/**
* Helper method to generate a node reference of a particular type.
*
* @param type content type qualified name
* @return {@link NodeRef} node reference that behaves like a node that exists in the spaces store with
* the content type provided
*/
public static NodeRef generateNodeRef(NodeService mockedNodeService, QName type)
{
return generateNodeRef(mockedNodeService, type, true);
}
/**
* Helper method to generate a cm:content node reference with a given name.
*
* @param name content name
* @return NodeRef node reference
*/
public static NodeRef generateCmContent(NodeService mockedNodeService, String name)
{
NodeRef nodeRef = generateNodeRef(mockedNodeService, ContentModel.TYPE_CONTENT, true);
doReturn(name).when(mockedNodeService).getProperty(nodeRef, ContentModel.PROP_NAME);
return nodeRef;
}
/**
* Helper method to generate a node reference of a particular type with a given existence characteristic.
*
* @param type content type qualified name
* @param exists indicates whether this node should behave like a node that exists or not
* @return {@link NodeRef} node reference that behaves like a node that exists (or not) in the spaces store with
* the content type provided
*/
public static NodeRef generateNodeRef(NodeService mockedNodeService, QName type, boolean exists)
{
NodeRef nodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, GUID.generate());
when(mockedNodeService.exists(eq(nodeRef))).thenReturn(exists);
if (type != null)
{
when(mockedNodeService.getType(eq(nodeRef))).thenReturn(type);
}
return nodeRef;
}
}

View File

@@ -0,0 +1,392 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.web.scripts.roles;
import static java.util.Collections.emptyMap;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.io.Serializable;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.ImmutableMap;
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.module.org_alfresco_module_rm.test.util.AlfMock;
import org.alfresco.module.org_alfresco_module_rm.test.util.BaseWebScriptUnitTest;
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;
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.json.JSONObject;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.extensions.webscripts.DeclarativeWebScript;
/**
* DynamicAuthoritiesGet Unit Test
*
* @author Silviu Dinuta
*/
@SuppressWarnings("deprecation")
public class DynamicAuthoritiesGetUnitTest extends BaseWebScriptUnitTest implements RecordsManagementModel
{
/** test data */
private static final Long ASPECT_ID = 123l;
private static final QName ASPECT = AlfMock.generateQName();
/** mocks */
@Mock
private PatchDAO mockedPatchDAO;
@Mock
private NodeDAO mockedNodeDAO;
@Mock
private QNameDAO mockedQnameDAO;
@Mock
private NodeService mockedNodeService;
@Mock
private PermissionService mockedPermissionService;
@Mock
private ExtendedSecurityService mockedExtendedSecurityService;
@Mock
private TransactionService mockedTransactionService;
@Mock
private RetryingTransactionHelper mockedRetryingTransactionHelper;
/** test component */
@InjectMocks
private DynamicAuthoritiesGet webScript;
@Override
protected DeclarativeWebScript getWebScript()
{
return webScript;
}
@Override
protected String getWebScriptTemplate()
{
return "alfresco/templates/webscripts/org/alfresco/repository/roles/rm-dynamicauthorities.get.json.ftl";
}
/**
* Before test
*/
@SuppressWarnings("unchecked")
@Before
public void before()
{
MockitoAnnotations.initMocks(this);
webScript.setNodeService(mockedNodeService);
webScript.setPermissionService(mockedPermissionService);
webScript.setExtendedSecurityService(mockedExtendedSecurityService);
// setup retrying transaction helper
Answer<Object> doInTransactionAnswer = new Answer<Object>()
{
@SuppressWarnings("rawtypes")
@Override
public Object answer(InvocationOnMock invocation) throws Throwable
{
RetryingTransactionCallback callback = (RetryingTransactionCallback) invocation.getArguments()[0];
return callback.execute();
}
};
doAnswer(doInTransactionAnswer).when(mockedRetryingTransactionHelper)
.<Object> doInTransaction(any(RetryingTransactionCallback.class), anyBoolean(), anyBoolean());
when(mockedTransactionService.getRetryingTransactionHelper()).thenReturn(mockedRetryingTransactionHelper);
// max node id
when(mockedPatchDAO.getMaxAdmNodeID()).thenReturn(500000L);
// aspect
when(mockedQnameDAO.getQName(ASPECT_EXTENDED_SECURITY)).thenReturn(new Pair<Long, QName>(ASPECT_ID, ASPECT));
}
/**
* Given that there are no nodes with the extended security aspect When the action is executed Nothing happens
* @throws Exception
*/
@SuppressWarnings({ "unchecked" })
@Test
public void noNodesWithExtendedSecurity() throws Exception
{
when(mockedPatchDAO.getNodesByAspectQNameId(eq(ASPECT_ID), anyLong(), anyLong()))
.thenReturn(Collections.emptyList());
// Set up parameters.
Map<String, String> parameters = ImmutableMap.of("batchsize", "10", "maxProcessedRecords", "3");
JSONObject json = executeJSONWebScript(parameters);
assertNotNull(json);
String actualJSONString = json.toString();
// Check the JSON result using Jackson to allow easy equality testing.
ObjectMapper mapper = new ObjectMapper();
String expectedJSONString = "{\"responsestatus\":\"success\",\"message\":\"Processed 0 records.\"}";
assertEquals(mapper.readTree(expectedJSONString), mapper.readTree(actualJSONString));
verify(mockedNodeService, never()).getProperty(any(NodeRef.class), eq(PROP_READERS));
verify(mockedNodeService, never()).getProperty(any(NodeRef.class), eq(PROP_WRITERS));
verify(mockedNodeService, never()).removeAspect(any(NodeRef.class), eq(ASPECT_EXTENDED_SECURITY));
verify(mockedPermissionService, never()).clearPermission(any(NodeRef.class),
eq(ExtendedReaderDynamicAuthority.EXTENDED_READER));
verify(mockedPermissionService, never()).clearPermission(any(NodeRef.class),
eq(ExtendedWriterDynamicAuthority.EXTENDED_WRITER));
verify(mockedExtendedSecurityService, never()).set(any(NodeRef.class), any(Set.class), any(Set.class));
}
/**
* Given that there are records with the extended security aspect When the action is executed Then the aspect is
* removed And the dynamic authorities permissions are cleared And extended security is set via the updated API
* @throws Exception
*/
@SuppressWarnings("unchecked")
@Test
public void recordsWithExtendedSecurityAspect() throws Exception
{
List<Long> ids = Stream.of(1l, 2l, 3l).collect(Collectors.toList());
when(mockedPatchDAO.getNodesByAspectQNameId(eq(ASPECT_ID), anyLong(), anyLong()))
.thenReturn(ids)
.thenReturn(Collections.emptyList());
ids.stream().forEach((i) -> {
NodeRef nodeRef = AlfMock.generateNodeRef(mockedNodeService);
when(mockedNodeDAO.getNodePair(i)).thenReturn(new Pair<Long, NodeRef>(i, nodeRef));
when(mockedNodeService.hasAspect(nodeRef, ASPECT_RECORD)).thenReturn(true);
when(mockedNodeService.getProperty(nodeRef, PROP_READERS))
.thenReturn((Serializable) Collections.emptyMap());
when(mockedNodeService.getProperty(nodeRef, PROP_WRITERS))
.thenReturn((Serializable) Collections.emptyMap());
});
// Set up parameters.
Map<String, String> parameters = ImmutableMap.of("batchsize", "10", "maxProcessedRecords", "4");
JSONObject json = executeJSONWebScript(parameters);
assertNotNull(json);
String actualJSONString = json.toString();
ObjectMapper mapper = new ObjectMapper();
String expectedJSONString = "{\"responsestatus\":\"success\",\"message\":\"Processed 3 records.\"}";
assertEquals(mapper.readTree(expectedJSONString), mapper.readTree(actualJSONString));
verify(mockedNodeService, times(3)).getProperty(any(NodeRef.class), eq(PROP_READERS));
verify(mockedNodeService, times(3)).getProperty(any(NodeRef.class), eq(PROP_WRITERS));
verify(mockedNodeService, times(3)).removeAspect(any(NodeRef.class), eq(ASPECT_EXTENDED_SECURITY));
verify(mockedPermissionService, times(3)).clearPermission(any(NodeRef.class),
eq(ExtendedReaderDynamicAuthority.EXTENDED_READER));
verify(mockedPermissionService, times(3)).clearPermission(any(NodeRef.class),
eq(ExtendedWriterDynamicAuthority.EXTENDED_WRITER));
verify(mockedExtendedSecurityService, times(3)).set(any(NodeRef.class), any(Set.class), any(Set.class));
}
/**
* Given that there are non-records with the extended security aspect When the web script is executed Then the aspect is
* removed And the dynamic authorities permissions are cleared
* @throws Exception
*/
@SuppressWarnings("unchecked")
@Test
public void nonRecordsWithExtendedSecurityAspect() throws Exception
{
List<Long> ids = Stream.of(1l, 2l, 3l).collect(Collectors.toList());
when(mockedPatchDAO.getNodesByAspectQNameId(eq(ASPECT_ID), anyLong(), anyLong()))
.thenReturn(ids)
.thenReturn(Collections.emptyList());
ids.stream().forEach((i) -> {
NodeRef nodeRef = AlfMock.generateNodeRef(mockedNodeService);
when(mockedNodeDAO.getNodePair(i)).thenReturn(new Pair<Long, NodeRef>(i, nodeRef));
when(mockedNodeService.hasAspect(nodeRef, ASPECT_RECORD)).thenReturn(false);
when(mockedNodeService.getProperty(nodeRef, PROP_READERS))
.thenReturn((Serializable) Collections.emptyMap());
when(mockedNodeService.getProperty(nodeRef, PROP_WRITERS))
.thenReturn((Serializable) Collections.emptyMap());
});
// Set up parameters.
Map<String, String> parameters = ImmutableMap.of("batchsize", "10", "maxProcessedRecords", "4");
JSONObject json = executeJSONWebScript(parameters);
assertNotNull(json);
String actualJSONString = json.toString();
ObjectMapper mapper = new ObjectMapper();
String expectedJSONString = "{\"responsestatus\":\"success\",\"message\":\"Processed 3 records.\"}";
assertEquals(mapper.readTree(expectedJSONString), mapper.readTree(actualJSONString));
verify(mockedNodeService, times(3)).getProperty(any(NodeRef.class), eq(PROP_READERS));
verify(mockedNodeService, times(3)).getProperty(any(NodeRef.class), eq(PROP_WRITERS));
verify(mockedNodeService, times(3)).removeAspect(any(NodeRef.class), eq(ASPECT_EXTENDED_SECURITY));
verify(mockedPermissionService, times(3)).clearPermission(any(NodeRef.class),
eq(ExtendedReaderDynamicAuthority.EXTENDED_READER));
verify(mockedPermissionService, times(3)).clearPermission(any(NodeRef.class),
eq(ExtendedWriterDynamicAuthority.EXTENDED_WRITER));
verify(mockedExtendedSecurityService, never()).set(any(NodeRef.class), any(Set.class), any(Set.class));
}
@Test
public void missingBatchSizeParameter() throws Exception
{
JSONObject json = executeJSONWebScript(emptyMap());
assertNotNull(json);
String actualJSONString = json.toString();
ObjectMapper mapper = new ObjectMapper();
String expectedJSONString = "{\"responsestatus\":\"failed\",\"message\":\"Parameter batchsize is mandatory\"}";
assertEquals(mapper.readTree(expectedJSONString), mapper.readTree(actualJSONString));
}
@Test
public void invalidBatchSizeParameter() throws Exception
{
// Set up parameters.
Map<String, String> parameters = ImmutableMap.of("batchsize", "dd");
JSONObject json = executeJSONWebScript(parameters);
assertNotNull(json);
String actualJSONString = json.toString();
ObjectMapper mapper = new ObjectMapper();
String expectedJSONString = "{\"responsestatus\":\"failed\",\"message\":\"Parameter batchsize is invalid.\"}";
assertEquals(mapper.readTree(expectedJSONString), mapper.readTree(actualJSONString));
}
@Test
public void batchSizeShouldBeGraterThanZero() throws Exception
{
when(mockedQnameDAO.getQName(ASPECT_EXTENDED_SECURITY)).thenReturn(null);
// Set up parameters.
Map<String, String> parameters = ImmutableMap.of("batchsize", "0");
JSONObject json = executeJSONWebScript(parameters);
assertNotNull(json);
String actualJSONString = json.toString();
ObjectMapper mapper = new ObjectMapper();
String expectedJSONString = "{\"responsestatus\":\"failed\",\"message\":\"Parameter batchsize should be a number greater than 0.\"}";
assertEquals(mapper.readTree(expectedJSONString), mapper.readTree(actualJSONString));
}
@Test
public void extendedSecurityAspectNotCreated() throws Exception
{
when(mockedQnameDAO.getQName(ASPECT_EXTENDED_SECURITY)).thenReturn(null);
// Set up parameters.
Map<String, String> parameters = ImmutableMap.of("batchsize", "3");
JSONObject json = executeJSONWebScript(parameters);
assertNotNull(json);
String actualJSONString = json.toString();
ObjectMapper mapper = new ObjectMapper();
String expectedJSONString = "{\"responsestatus\":\"success\",\"message\":\"There where no records to be processed.\"}";
assertEquals(mapper.readTree(expectedJSONString), mapper.readTree(actualJSONString));
}
@Test
public void processAllRecordsWhenMaxProcessedRecordsIsZero() throws Exception
{
List<Long> ids = Stream.of(1l, 2l, 3l,4l).collect(Collectors.toList());
when(mockedPatchDAO.getNodesByAspectQNameId(eq(ASPECT_ID), anyLong(), anyLong()))
.thenReturn(ids)
.thenReturn(Collections.emptyList());
ids.stream().forEach((i) -> {
NodeRef nodeRef = AlfMock.generateNodeRef(mockedNodeService);
when(mockedNodeDAO.getNodePair(i)).thenReturn(new Pair<Long, NodeRef>(i, nodeRef));
when(mockedNodeService.hasAspect(nodeRef, ASPECT_RECORD)).thenReturn(false);
when(mockedNodeService.getProperty(nodeRef, PROP_READERS))
.thenReturn((Serializable) Collections.emptyMap());
when(mockedNodeService.getProperty(nodeRef, PROP_WRITERS))
.thenReturn((Serializable) Collections.emptyMap());
});
// Set up parameters.
Map<String, String> parameters = ImmutableMap.of("batchsize", "10", "maxProcessedRecords", "0");
JSONObject json = executeJSONWebScript(parameters);
assertNotNull(json);
String actualJSONString = json.toString();
ObjectMapper mapper = new ObjectMapper();
String expectedJSONString = "{\"responsestatus\":\"success\",\"message\":\"Processed 4 records.\"}";
assertEquals(mapper.readTree(expectedJSONString), mapper.readTree(actualJSONString));
}
@Test
public void whenMaxProcessedRecordsIsMissingItDefaultsToBatchSize() throws Exception
{
List<Long> ids = Stream.of(1l, 2l, 3l, 4l, 5l).collect(Collectors.toList());
when(mockedPatchDAO.getNodesByAspectQNameId(eq(ASPECT_ID), anyLong(), anyLong()))
.thenReturn(ids)
.thenReturn(Collections.emptyList());
ids.stream().forEach((i) -> {
NodeRef nodeRef = AlfMock.generateNodeRef(mockedNodeService);
when(mockedNodeDAO.getNodePair(i)).thenReturn(new Pair<Long, NodeRef>(i, nodeRef));
when(mockedNodeService.hasAspect(nodeRef, ASPECT_RECORD)).thenReturn(false);
when(mockedNodeService.getProperty(nodeRef, PROP_READERS))
.thenReturn((Serializable) Collections.emptyMap());
when(mockedNodeService.getProperty(nodeRef, PROP_WRITERS))
.thenReturn((Serializable) Collections.emptyMap());
});
// Set up parameters.
Map<String, String> parameters = ImmutableMap.of("batchsize", "4");
JSONObject json = executeJSONWebScript(parameters);
assertNotNull(json);
String actualJSONString = json.toString();
ObjectMapper mapper = new ObjectMapper();
String expectedJSONString = "{\"responsestatus\":\"success\",\"message\":\"Processed first 4 records.\"}";
assertEquals(mapper.readTree(expectedJSONString), mapper.readTree(actualJSONString));
}
}