RM-600: A user with the "EditDocumentMetaData" capability can only edit the properties of the records content.

* record service extended with isPropertyEditable method .. indicates, based on the capabilities of the current user and the nature of the property, whether it can be edited
  * the record service monitors record aspect property updates and throws ModelExceptions if a user tries to update a record or non-record property if they don't have the appropriate capability
  * form filter updated to protect record properties the current user may not be able to edit
  * unit tests

So what does this all mean?  

It means we can control the write permissions of individual properties on a record based on capabilities.  In this case we slice the capability to edit a record in two .. you can either edit the content properties (ie the normal meta-data) or the record properties .. or both of course!



git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/HEAD@49041 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Roy Wetherall
2013-04-10 07:26:45 +00:00
parent 90a9bd99c0
commit f041d32b21
16 changed files with 816 additions and 280 deletions

View File

@@ -58,12 +58,6 @@
<property name="index" value="80"/> <property name="index" value="80"/>
</bean> </bean>
<bean id="metadataControlGroup"
parent="groupBase">
<property name="id" value="metadataControl"/>
<property name="index" value="90"/>
</bean>
<bean id="referencesGroup" <bean id="referencesGroup"
parent="groupBase"> parent="groupBase">
<property name="id" value="references"/> <property name="id" value="references"/>

View File

@@ -183,35 +183,38 @@
<property name="conditions"> <property name="conditions">
<map> <map>
<entry key="capabilityCondition.filling" value="true"/> <entry key="capabilityCondition.filling" value="true"/>
<entry key="capabilityCondition.cutoff" value="false"/>
<entry key="capabilityCondition.frozen" value="false"/> <entry key="capabilityCondition.frozen" value="false"/>
<entry key="capabilityCondition.declared" value="true"/> <entry key="capabilityCondition.declared" value="true"/>
</map> </map>
</property> </property>
<property name="group"><ref bean="metadataControlGroup"/></property> <property name="group"><ref bean="recordsGroup"/></property>
<property name="index" value="10" /> <property name="index" value="10" />
</bean> </bean>
<!-- TODO .. this is being used for the wrong thing! ... should indicate that the use can edit the non-record meta-data of a record -->
<!-- TODO .. may need some kind of 'prep' record capability you get with file? .. I think this is what this is being used for atm -->
<bean id="rmEditNonRecordMetadataCapability" <bean id="rmEditNonRecordMetadataCapability"
parent="declarativeCapability" parent="declarativeCapability">
class="org.alfresco.module.org_alfresco_module_rm.capability.impl.EditCapability">
<property name="name" value="EditNonRecordMetadata"/> <property name="name" value="EditNonRecordMetadata"/>
<property name="permission" value="EditNonRecordMetadata"/> <property name="permission" value="EditNonRecordMetadata"/>
<property name="kinds">
<list>
<value>RECORD</value>
</list>
</property>
<property name="conditions"> <property name="conditions">
<map> <map>
<entry key="capabilityCondition.fileable" value="true"/> <entry key="capabilityCondition.filling" value="true"/>
<entry key="capabilityCondition.cutoff" value="false"/>
<entry key="capabilityCondition.frozen" value="false"/> <entry key="capabilityCondition.frozen" value="false"/>
<entry key="capabilityCondition.closed" value="false"/>
<entry key="capabilityCondition.declared" value="false"/>
</map> </map>
</property> </property>
<property name="group"><ref bean="metadataControlGroup"/></property> <property name="group"><ref bean="recordsGroup"/></property>
<property name="index" value="20" /> <property name="index" value="20" />
</bean> </bean>
<bean id="rmEditRecordMetadataCapability" <bean id="rmEditRecordMetadataCapability"
parent="rmBaseCapability" parent="declarativeCapability">
class="org.alfresco.module.org_alfresco_module_rm.capability.impl.EditCapability">
<property name="name" value="EditRecordMetadata"/> <property name="name" value="EditRecordMetadata"/>
<property name="permission" value="EditRecordMetadata"/> <property name="permission" value="EditRecordMetadata"/>
<property name="kinds"> <property name="kinds">
@@ -228,7 +231,7 @@
<entry key="capabilityCondition.declared" value="false"/> <entry key="capabilityCondition.declared" value="false"/>
</map> </map>
</property> </property>
<property name="group"><ref bean="metadataControlGroup"/></property> <property name="group"><ref bean="recordsGroup"/></property>
<property name="index" value="30" /> <property name="index" value="30" />
</bean> </bean>
@@ -246,7 +249,7 @@
</map> </map>
</property> </property>
<property name="targetCapability" ref="rmFileRecordsCapability"/> <property name="targetCapability" ref="rmFileRecordsCapability"/>
<property name="group"><ref bean="metadataControlGroup"/></property> <property name="group"><ref bean="recordsGroup"/></property>
<property name="index" value="40" /> <property name="index" value="40" />
</bean> </bean>

View File

@@ -255,7 +255,6 @@
<aspect>rma:recordComponentIdentifier</aspect> <aspect>rma:recordComponentIdentifier</aspect>
<aspect>rma:commonRecordDetails</aspect> <aspect>rma:commonRecordDetails</aspect>
<aspect>rma:filePlanComponent</aspect> <aspect>rma:filePlanComponent</aspect>
<!-- <aspect>rma:vitalRecordDefinition</aspect> -->
</mandatory-aspects> </mandatory-aspects>
</type> </type>
@@ -590,6 +589,7 @@
<property name="rma:rootNodeRef"> <property name="rma:rootNodeRef">
<title>Root node reference</title> <title>Root node reference</title>
<type>d:noderef</type> <type>d:noderef</type>
<protected>true</protected>
</property> </property>
</properties> </properties>
</aspect> </aspect>
@@ -659,6 +659,7 @@
<property name="rma:identifier"> <property name="rma:identifier">
<title>Record Component Identifier</title> <title>Record Component Identifier</title>
<type>d:text</type> <type>d:text</type>
<protected>true</protected>
<mandatory>true</mandatory> <mandatory>true</mandatory>
<index enabled="true"> <index enabled="true">
<atomic>true</atomic> <atomic>true</atomic>
@@ -837,9 +838,11 @@
<properties> <properties>
<property name="rma:readers"> <property name="rma:readers">
<type>d:any</type> <type>d:any</type>
<protected>true</protected>
</property> </property>
<property name="rma:writers"> <property name="rma:writers">
<type>d:any</type> <type>d:any</type>
<protected>true</protected>
</property> </property>
</properties> </properties>
</aspect> </aspect>
@@ -857,6 +860,7 @@
<property name="rma:notificationIssued"> <property name="rma:notificationIssued">
<title>Indicates whether a notification that this record is due for review has been issued</title> <title>Indicates whether a notification that this record is due for review has been issued</title>
<type>d:boolean</type> <type>d:boolean</type>
<protected>true</protected>
<mandatory>false</mandatory> <mandatory>false</mandatory>
<default>false</default> <default>false</default>
</property> </property>
@@ -1025,9 +1029,11 @@
<properties> <properties>
<property name="rma:recordSearchHasDispositionSchedule"> <property name="rma:recordSearchHasDispositionSchedule">
<type>d:boolean</type> <type>d:boolean</type>
<protected>true</protected>
</property> </property>
<property name="rma:recordSearchDispositionActionName"> <property name="rma:recordSearchDispositionActionName">
<type>d:text</type> <type>d:text</type>
<protected>true</protected>
<index enabled="true"> <index enabled="true">
<atomic>true</atomic> <atomic>true</atomic>
<stored>false</stored> <stored>false</stored>
@@ -1036,37 +1042,43 @@
</property> </property>
<property name="rma:recordSearchDispositionActionAsOf"> <property name="rma:recordSearchDispositionActionAsOf">
<type>d:date</type> <type>d:date</type>
<protected>true</protected>
</property> </property>
<property name="rma:recordSearchDispositionPeriod"> <property name="rma:recordSearchDispositionPeriod">
<type>d:text</type> <type>d:text</type>
<index enabled="true"> <protected>true</protected>
<index enabled="true">
<atomic>true</atomic> <atomic>true</atomic>
<stored>false</stored> <stored>false</stored>
<tokenised>false</tokenised> <tokenised>false</tokenised>
</index> </index>
</property> </property>
<property name="rma:recordSearchDispositionPeriodExpression"> <property name="rma:recordSearchDispositionPeriodExpression">
<type>d:text</type> <type>d:text</type>
<index enabled="true"> <protected>true</protected>
<index enabled="true">
<atomic>true</atomic> <atomic>true</atomic>
<stored>false</stored> <stored>false</stored>
<tokenised>false</tokenised> <tokenised>false</tokenised>
</index> </index>
</property> </property>
<property name="rma:recordSearchDispositionEventsEligible"> <property name="rma:recordSearchDispositionEventsEligible">
<type>d:boolean</type> <type>d:boolean</type>
<protected>true</protected>
</property> </property>
<property name="rma:recordSearchDispositionEvents"> <property name="rma:recordSearchDispositionEvents">
<type>d:text</type> <type>d:text</type>
<multiple>true</multiple> <protected>true</protected>
<index enabled="true"> <multiple>true</multiple>
<index enabled="true">
<atomic>true</atomic> <atomic>true</atomic>
<stored>false</stored> <stored>false</stored>
<tokenised>false</tokenised> <tokenised>false</tokenised>
</index> </index>
</property> </property>
<property name="rma:recordSearchDispositionAuthority"> <property name="rma:recordSearchDispositionAuthority">
<type>d:text</type> <type>d:text</type>
<protected>true</protected>
<index enabled="true"> <index enabled="true">
<atomic>true</atomic> <atomic>true</atomic>
<stored>false</stored> <stored>false</stored>
@@ -1075,12 +1087,15 @@
</property> </property>
<property name="rma:recordSearchDispositionInstructions"> <property name="rma:recordSearchDispositionInstructions">
<type>d:text</type> <type>d:text</type>
<protected>true</protected>
</property> </property>
<property name="rma:recordSearchHoldReason"> <property name="rma:recordSearchHoldReason">
<type>d:text</type> <type>d:text</type>
<protected>true</protected>
</property> </property>
<property name="rma:recordSearchVitalRecordReviewPeriod"> <property name="rma:recordSearchVitalRecordReviewPeriod">
<type>d:text</type> <type>d:text</type>
<protected>true</protected>
<index enabled="true"> <index enabled="true">
<atomic>true</atomic> <atomic>true</atomic>
<stored>false</stored> <stored>false</stored>
@@ -1089,6 +1104,7 @@
</property> </property>
<property name="rma:recordSearchVitalRecordReviewPeriodExpression"> <property name="rma:recordSearchVitalRecordReviewPeriodExpression">
<type>d:text</type> <type>d:text</type>
<protected>true</protected>
<index enabled="true"> <index enabled="true">
<atomic>true</atomic> <atomic>true</atomic>
<stored>false</stored> <stored>false</stored>
@@ -1147,13 +1163,16 @@
<title>The originating details of a record</title> <title>The originating details of a record</title>
<properties> <properties>
<property name="rma:recordOriginatingUserId"> <property name="rma:recordOriginatingUserId">
<type>d:text</type> <type>d:text</type>
<protected>true</protected>
</property> </property>
<property name="rma:recordOriginatingCreationDate"> <property name="rma:recordOriginatingCreationDate">
<type>d:date</type> <type>d:date</type>
<protected>true</protected>
</property> </property>
<property name="rma:recordOriginatingLocation"> <property name="rma:recordOriginatingLocation">
<type>d:any</type> <type>d:any</type>
<protected>true</protected>
</property> </property>
</properties> </properties>
</aspect> </aspect>
@@ -1163,13 +1182,16 @@
<title>The rejection details of a record</title> <title>The rejection details of a record</title>
<properties> <properties>
<property name="rma:recordRejectionUserId"> <property name="rma:recordRejectionUserId">
<type>d:text</type> <type>d:text</type>
<protected>true</protected>
</property> </property>
<property name="rma:recordRejectionDate"> <property name="rma:recordRejectionDate">
<type>d:date</type> <type>d:date</type>
<protected>true</protected>
</property> </property>
<property name="rma:recordRejectionReason"> <property name="rma:recordRejectionReason">
<type>d:text</type> <type>d:text</type>
<protected>true</protected>
</property> </property>
</properties> </properties>
</aspect> </aspect>

View File

@@ -481,8 +481,11 @@
</property> </property>
</bean> </bean>
<bean id="broadcastVitalRecordDefinition" class="org.alfresco.module.org_alfresco_module_rm.vital.BroadcastVitalRecordDefinitionAction" <bean id="broadcastVitalRecordDefinition"
parent="rmAction" /> class="org.alfresco.module.org_alfresco_module_rm.vital.BroadcastVitalRecordDefinitionAction"
parent="rmAction">
<property name="filePlanAuthenticationService" ref="FilePlanAuthenticationService"/>
</bean>
<!-- broadcast disposition action definition update --> <!-- broadcast disposition action definition update -->
<!-- bound to policy: allow --> <!-- bound to policy: allow -->

View File

@@ -1101,6 +1101,7 @@
<property name="filePlanService" ref="FilePlanService" /> <property name="filePlanService" ref="FilePlanService" />
<property name="ownableService" ref="OwnableService" /> <property name="ownableService" ref="OwnableService" />
<property name="notificationHelper" ref="recordsManagementNotificationHelper"/> <property name="notificationHelper" ref="recordsManagementNotificationHelper"/>
<property name="capabilityService" ref="CapabilityService" />
</bean> </bean>
<bean id="RecordService" class="org.springframework.aop.framework.ProxyFactoryBean"> <bean id="RecordService" class="org.springframework.aop.framework.ProxyFactoryBean">
@@ -1149,6 +1150,7 @@
org.alfresco.module.org_alfresco_module_rm.record.RecordService.isFiled=RM.Read.0 org.alfresco.module.org_alfresco_module_rm.record.RecordService.isFiled=RM.Read.0
org.alfresco.module.org_alfresco_module_rm.record.RecordService.createRecord=RM_ALLOW org.alfresco.module.org_alfresco_module_rm.record.RecordService.createRecord=RM_ALLOW
org.alfresco.module.org_alfresco_module_rm.record.RecordService.hideRecord=RM_ALLOW org.alfresco.module.org_alfresco_module_rm.record.RecordService.hideRecord=RM_ALLOW
org.alfresco.module.org_alfresco_module_rm.record.RecordService.isPropertyEditable=RM.Read.0
org.alfresco.module.org_alfresco_module_rm.record.RecordService.*=RM_DENY org.alfresco.module.org_alfresco_module_rm.record.RecordService.*=RM_DENY
]]> ]]>
</value> </value>

View File

@@ -30,6 +30,7 @@ import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase; import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase;
import org.alfresco.repo.action.executer.ActionExecuterAbstractBase; import org.alfresco.repo.action.executer.ActionExecuterAbstractBase;
import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.dictionary.AspectDefinition; import org.alfresco.service.cmr.dictionary.AspectDefinition;
import org.alfresco.service.cmr.dictionary.PropertyDefinition; import org.alfresco.service.cmr.dictionary.PropertyDefinition;
@@ -59,7 +60,7 @@ public class DeclareRecordAction extends RMActionExecuterAbstractBase
* @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef) * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef)
*/ */
@Override @Override
protected void executeImpl(Action action, NodeRef actionedUponNodeRef) protected void executeImpl(final Action action, final NodeRef actionedUponNodeRef)
{ {
if (recordService.isRecord(actionedUponNodeRef) == true) if (recordService.isRecord(actionedUponNodeRef) == true)
{ {
@@ -75,8 +76,16 @@ public class DeclareRecordAction extends RMActionExecuterAbstractBase
declaredProps.put(PROP_DECLARED_BY, AuthenticationUtil.getRunAsUser()); declaredProps.put(PROP_DECLARED_BY, AuthenticationUtil.getRunAsUser());
this.nodeService.addAspect(actionedUponNodeRef, ASPECT_DECLARED_RECORD, declaredProps); this.nodeService.addAspect(actionedUponNodeRef, ASPECT_DECLARED_RECORD, declaredProps);
// remove all owner related rights AuthenticationUtil.runAsSystem(new RunAsWork<Void>()
this.ownableService.setOwner(actionedUponNodeRef, OwnableService.NO_OWNER); {
@Override
public Void doWork() throws Exception
{
// remove all owner related rights
ownableService.setOwner(actionedUponNodeRef, OwnableService.NO_OWNER);
return null;
}
});
} }
else else
{ {

View File

@@ -1,100 +0,0 @@
/*
* Copyright (C) 2005-2011 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.capability.impl;
import net.sf.acegisecurity.vote.AccessDecisionVoter;
import org.alfresco.module.org_alfresco_module_rm.capability.declarative.DeclarativeCapability;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.security.OwnableService;
/**
* Edit capability, checks the permission and whether the current user is the owner of the
* object.
*
* @author Roy Wetherall
*/
public class EditCapability extends DeclarativeCapability
{
private OwnableService ownableService;
private OwnableService getOwnableService()
{
if (ownableService == null)
{
ownableService = (OwnableService)applicationContext.getBean("OwnableService");
}
return ownableService;
}
/**
* @see org.alfresco.module.org_alfresco_module_rm.capability.declarative.DeclarativeCapability#evaluate(org.alfresco.service.cmr.repository.NodeRef)
*/
public int evaluate(final NodeRef nodeRef)
{
// The default state is not knowing anything
int result = AccessDecisionVoter.ACCESS_ABSTAIN;
// Check we are dealing with a file plan component
if (rmService.isFilePlanComponent(nodeRef) == true)
{
// Now the default state is denied
result = AccessDecisionVoter.ACCESS_DENIED;
// Check the kind of the object, the permissions and the conditions
if (checkKinds(nodeRef) == true && checkConditions(nodeRef) == true)
{
if (checkPermissions(nodeRef) == true)
{
result = AccessDecisionVoter.ACCESS_GRANTED;
}
else
{
result = AuthenticationUtil.runAs(new RunAsWork<Integer>()
{
@Override
public Integer doWork() throws Exception
{
Integer result = Integer.valueOf(AccessDecisionVoter.ACCESS_DENIED);
// Since we know this is undeclared if you are the owner then you should be able to
// edit the records meta-data (otherwise how can it be declared by the user?)
if (getOwnableService().hasOwner(nodeRef) == true)
{
String user = AuthenticationUtil.getFullyAuthenticatedUser();
if (user != null &&
getOwnableService().getOwner(nodeRef).equals(user) == true)
{
result = Integer.valueOf(AccessDecisionVoter.ACCESS_GRANTED);
}
}
return result;
}
}, AuthenticationUtil.getSystemUserName()).intValue();
}
}
}
return result;
}
}

View File

@@ -38,6 +38,7 @@ import org.alfresco.repo.forms.Form;
import org.alfresco.repo.forms.PropertyFieldDefinition; import org.alfresco.repo.forms.PropertyFieldDefinition;
import org.alfresco.repo.forms.processor.node.FieldUtils; import org.alfresco.repo.forms.processor.node.FieldUtils;
import org.alfresco.repo.forms.processor.node.FormFieldConstants; import org.alfresco.repo.forms.processor.node.FormFieldConstants;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.dictionary.PropertyDefinition; import org.alfresco.service.cmr.dictionary.PropertyDefinition;
@@ -91,9 +92,10 @@ public class RecordsManagementNodeFormFilter extends RecordsManagementFormFilter
this.dispositionService = dispositionService; this.dispositionService = dispositionService;
} }
/* /**
* @see org.alfresco.repo.forms.processor.Filter#afterGenerate(java.lang.Object, java.util.List, java.util.List, org.alfresco.repo.forms.Form, java.util.Map) * @see org.alfresco.repo.forms.processor.Filter#afterGenerate(java.lang.Object, java.util.List, java.util.List, org.alfresco.repo.forms.Form, java.util.Map)
*/ */
@Override
public void afterGenerate( public void afterGenerate(
NodeRef nodeRef, NodeRef nodeRef,
List<String> fields, List<String> fields,
@@ -118,6 +120,9 @@ public class RecordsManagementNodeFormFilter extends RecordsManagementFormFilter
// add the supplemental marking list property // add the supplemental marking list property
forceSupplementalMarkingListProperty(form, nodeRef); forceSupplementalMarkingListProperty(form, nodeRef);
// protect uneditable properties
protectRecordProperties(form, nodeRef);
// if the record is the result of an email we need to 'protect' some fields // if the record is the result of an email we need to 'protect' some fields
if (this.nodeService.hasAspect(nodeRef, ImapModel.ASPECT_IMAP_CONTENT)) if (this.nodeService.hasAspect(nodeRef, ImapModel.ASPECT_IMAP_CONTENT))
{ {
@@ -147,6 +152,11 @@ public class RecordsManagementNodeFormFilter extends RecordsManagementFormFilter
} }
} }
/**
*
* @param form
* @param nodeRef
*/
protected void addCustomPropertyFieldsToGroup(Form form, NodeRef nodeRef) protected void addCustomPropertyFieldsToGroup(Form form, NodeRef nodeRef)
{ {
Set<QName> customisables = rmAdminService.getCustomisable(nodeRef); Set<QName> customisables = rmAdminService.getCustomisable(nodeRef);
@@ -165,6 +175,11 @@ public class RecordsManagementNodeFormFilter extends RecordsManagementFormFilter
} }
} }
/**
*
* @param form
* @param nodeRef
*/
protected void addRecordMetadataPropertyFieldsToGroup(Form form, NodeRef nodeRef) protected void addRecordMetadataPropertyFieldsToGroup(Form form, NodeRef nodeRef)
{ {
Set<QName> aspects = recordService.getRecordMetaDataAspects(); Set<QName> aspects = recordService.getRecordMetaDataAspects();
@@ -210,7 +225,11 @@ public class RecordsManagementNodeFormFilter extends RecordsManagementFormFilter
} }
} }
/**
*
* @param form
* @param nodeRef
*/
protected void addTransientProperties(Form form, NodeRef nodeRef) protected void addTransientProperties(Form form, NodeRef nodeRef)
{ {
if (recordService.isRecord(nodeRef) == true) if (recordService.isRecord(nodeRef) == true)
@@ -239,6 +258,13 @@ public class RecordsManagementNodeFormFilter extends RecordsManagementFormFilter
} }
} }
/**
*
* @param form
* @param name
* @param type
* @param value
*/
protected void addTransientPropertyField(Form form, String name, QName type, Object value) protected void addTransientPropertyField(Form form, String name, QName type, Object value)
{ {
String dataKeyName = FormFieldConstants.PROP_DATA_PREFIX + name; String dataKeyName = FormFieldConstants.PROP_DATA_PREFIX + name;
@@ -251,6 +277,47 @@ public class RecordsManagementNodeFormFilter extends RecordsManagementFormFilter
form.addData(dataKeyName, value); form.addData(dataKeyName, value);
} }
/**
*
* @param form
* @param nodeRef
*/
protected void protectRecordProperties(Form form, NodeRef nodeRef)
{
List<FieldDefinition> fieldDefs = form.getFieldDefinitions();
for (FieldDefinition fieldDef : fieldDefs)
{
if (fieldDef.isProtectedField() == false)
{
String name = fieldDef.getName();
String prefixName = null;
if ("size".equals(name) || "mimetype".equals(name) || "encoding".equals(name))
{
prefixName = "cm:content";
}
else
{
prefixName = fieldDef.getName();
}
if (logger.isDebugEnabled() == true)
{
logger.debug("Checking property " + prefixName + " is editable by user " + AuthenticationUtil.getFullyAuthenticatedUser());
}
QName qname = QName.createQName(prefixName, namespaceService);
if (recordService.isPropertyEditable(nodeRef, qname) == false)
{
if (logger.isDebugEnabled() == true)
{
logger.debug(" ... protected property");
}
fieldDef.setProtectedField(true);
}
}
}
}
/** /**
* Marks all the fields that contain data extracted from an email * Marks all the fields that contain data extracted from an email
* as protected fields. * as protected fields.

View File

@@ -128,7 +128,7 @@ public class RecordContainerType implements RecordsManagementModel,
@Override @Override
public void onCreateChildAssociation(final ChildAssociationRef childAssocRef, boolean isNewNode) public void onCreateChildAssociation(final ChildAssociationRef childAssocRef, boolean isNewNode)
{ {
filePlanAuthenticationService.runAsRmAdmin(new RunAsWork<Void>() AuthenticationUtil.runAsSystem(new RunAsWork<Void>()
{ {
@Override @Override
public Void doWork() throws Exception public Void doWork() throws Exception

View File

@@ -45,44 +45,69 @@ public abstract class ProtectedModelArtifact
/** Set of capabilities */ /** Set of capabilities */
private Set<Capability> capabilities; private Set<Capability> capabilities;
/** Capability names */
private Set<String> capabilityNames; private Set<String> capabilityNames;
/**
* @param namespaceService namespace service
*/
public void setNamespaceService(NamespaceService namespaceService) public void setNamespaceService(NamespaceService namespaceService)
{ {
this.namespaceService = namespaceService; this.namespaceService = namespaceService;
} }
/**
* @param modelSecurityService model security service
*/
public void setModelSecurityService(ModelSecurityService modelSecurityService) public void setModelSecurityService(ModelSecurityService modelSecurityService)
{ {
this.modelSecurityService = modelSecurityService; this.modelSecurityService = modelSecurityService;
} }
/**
* Init method
*/
public void init() public void init()
{ {
modelSecurityService.register(this); modelSecurityService.register(this);
} }
/**
* @param name artifact name (in cm:content form)
*/
public void setName(String name) public void setName(String name)
{ {
QName qname = QName.createQName(name, namespaceService); QName qname = QName.createQName(name, namespaceService);
this.name = qname; this.name = qname;
} }
/**
* @return artifact QName
*/
public QName getQName() public QName getQName()
{ {
return name; return name;
} }
/**
* @param capabilities capabilities
*/
public void setCapabilities(Set<Capability> capabilities) public void setCapabilities(Set<Capability> capabilities)
{ {
this.capabilities = capabilities; this.capabilities = capabilities;
} }
/**
* @return capabilities
*/
public Set<Capability> getCapabilities() public Set<Capability> getCapabilities()
{ {
return capabilities; return capabilities;
} }
/**
* @return capability names
*/
public Set<String> getCapilityNames() public Set<String> getCapilityNames()
{ {
if (capabilityNames == null && capabilities != null) if (capabilityNames == null && capabilities != null)

View File

@@ -96,4 +96,13 @@ public interface RecordService
* @param reason The reason for rejection * @param reason The reason for rejection
*/ */
void rejectRecord(NodeRef nodeRef, String reason); void rejectRecord(NodeRef nodeRef, String reason);
/**
* Indicates whether a property of a record is editable for the current user or not.
*
* @param record record
* @param property property
* @return boolean true if editable, false otherwise.
*/
boolean isPropertyEditable(NodeRef record, QName property);
} }

View File

@@ -31,11 +31,16 @@ import java.util.Set;
import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService;
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.disposition.DispositionSchedule; import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService;
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.fileplan.FilePlanService;
import org.alfresco.module.org_alfresco_module_rm.identifier.IdentifierService; import org.alfresco.module.org_alfresco_module_rm.identifier.IdentifierService;
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementCustomModel;
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
import org.alfresco.module.org_alfresco_module_rm.model.security.ModelAccessDeniedException;
import org.alfresco.module.org_alfresco_module_rm.notification.RecordsManagementNotificationHelper; import org.alfresco.module.org_alfresco_module_rm.notification.RecordsManagementNotificationHelper;
import org.alfresco.module.org_alfresco_module_rm.security.ExtendedSecurityService; import org.alfresco.module.org_alfresco_module_rm.security.ExtendedSecurityService;
import org.alfresco.module.org_alfresco_module_rm.vital.VitalRecordServiceImpl; import org.alfresco.module.org_alfresco_module_rm.vital.VitalRecordServiceImpl;
@@ -48,31 +53,80 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.security.permissions.AccessDeniedException; import org.alfresco.repo.security.permissions.AccessDeniedException;
import org.alfresco.repo.security.permissions.impl.ExtendedPermissionService; import org.alfresco.repo.security.permissions.impl.ExtendedPermissionService;
import org.alfresco.service.cmr.dictionary.AspectDefinition; import org.alfresco.service.cmr.dictionary.AspectDefinition;
import org.alfresco.service.cmr.dictionary.ClassDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.security.AccessStatus; import org.alfresco.service.cmr.security.AccessStatus;
import org.alfresco.service.cmr.security.OwnableService; import org.alfresco.service.cmr.security.OwnableService;
import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
import org.alfresco.util.EqualsHelper;
import org.alfresco.util.ParameterCheck; import org.alfresco.util.ParameterCheck;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationContextAware;
/** /**
* Record service implementation * Record service implementation.
* *
* @author Roy Wetherall * @author Roy Wetherall
* @since 2.1 * @since 2.1
*/ */
public class RecordServiceImpl implements RecordService, public class RecordServiceImpl implements RecordService,
RecordsManagementModel, RecordsManagementModel,
RecordsManagementCustomModel,
NodeServicePolicies.OnCreateChildAssociationPolicy, NodeServicePolicies.OnCreateChildAssociationPolicy,
NodeServicePolicies.OnUpdatePropertiesPolicy,
ApplicationContextAware ApplicationContextAware
{ {
/** Logger */
private static Log logger = LogFactory.getLog(RecordServiceImpl.class);
/** Always edit property array */
private static final QName[] ALWAYS_EDIT_PROPERTIES = new QName[]
{
ContentModel.PROP_LAST_THUMBNAIL_MODIFICATION_DATA
};
private static final String[] ALWAYS_EDIT_URIS = new String[]
{
NamespaceService.SECURITY_MODEL_1_0_URI,
NamespaceService.SYSTEM_MODEL_1_0_URI,
NamespaceService.WORKFLOW_MODEL_1_0_URI,
NamespaceService.APP_MODEL_1_0_URI,
NamespaceService.DATALIST_MODEL_1_0_URI,
NamespaceService.DICTIONARY_MODEL_1_0_URI,
NamespaceService.BPM_MODEL_1_0_URI,
NamespaceService.RENDITION_MODEL_1_0_URI
};
private static final String[] RECORD_MODEL_URIS = new String[]
{
RM_URI,
RM_CUSTOM_URI,
DOD5015Model.DOD_URI
};
private static final String[] NON_RECORD_MODEL_URIS = new String[]
{
NamespaceService.AUDIO_MODEL_1_0_URI,
NamespaceService.CONTENT_MODEL_1_0_URI,
NamespaceService.EMAILSERVER_MODEL_URI,
NamespaceService.EXIF_MODEL_1_0_URI,
NamespaceService.FORUMS_MODEL_1_0_URI,
NamespaceService.LINKS_MODEL_1_0_URI,
NamespaceService.REPOSITORY_VIEW_1_0_URI
};
/** Application context */ /** Application context */
private ApplicationContext applicationContext; private ApplicationContext applicationContext;
@@ -108,15 +162,22 @@ public class RecordServiceImpl implements RecordService,
/** Ownable service */ /** Ownable service */
private OwnableService ownableService; private OwnableService ownableService;
/** Capability service */
private CapabilityService capabilityService;
/** List of available record meta-data aspects */ /** List of available record meta-data aspects */
private Set<QName> recordMetaDataAspects; private Set<QName> recordMetaDataAspects;
/** Behaviours */ /** Behaviours */
private JavaBehaviour onCreateChildAssociation = new JavaBehaviour( private JavaBehaviour onCreateChildAssociation = new JavaBehaviour(
this, this,
"onCreateChildAssociation", "onCreateChildAssociation",
NotificationFrequency.FIRST_EVENT); NotificationFrequency.FIRST_EVENT);
private JavaBehaviour onUpdateProperties = new JavaBehaviour(
this,
"onUpdateProperties",
NotificationFrequency.EVERY_EVENT);
@Override @Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
@@ -211,6 +272,14 @@ public class RecordServiceImpl implements RecordService,
{ {
this.ownableService = ownableService; this.ownableService = ownableService;
} }
/**
* @param capabilityService capability service
*/
public void setCapabilityService(CapabilityService capabilityService)
{
this.capabilityService = capabilityService;
}
/** /**
* Init method * Init method
@@ -222,6 +291,10 @@ public class RecordServiceImpl implements RecordService,
TYPE_RECORD_FOLDER, TYPE_RECORD_FOLDER,
ContentModel.ASSOC_CONTAINS, ContentModel.ASSOC_CONTAINS,
onCreateChildAssociation); onCreateChildAssociation);
policyComponent.bindClassBehaviour(
NodeServicePolicies.OnUpdatePropertiesPolicy.QNAME,
ASPECT_RECORD,
onUpdateProperties);
} }
/** /**
@@ -239,6 +312,56 @@ public class RecordServiceImpl implements RecordService,
file(nodeRef); file(nodeRef);
} }
} }
/**
* Ensure that the user only updates record properties that they have permission to.
*
* @see org.alfresco.repo.node.NodeServicePolicies.OnUpdatePropertiesPolicy#onUpdateProperties(org.alfresco.service.cmr.repository.NodeRef, java.util.Map, java.util.Map)
*/
@Override
public void onUpdateProperties(final NodeRef nodeRef, final Map<QName, Serializable> before, final Map<QName, Serializable> after)
{
onUpdateProperties.disable();
try
{
if (AuthenticationUtil.getFullyAuthenticatedUser() != null &&
AuthenticationUtil.isRunAsUserTheSystemUser() == false &&
nodeService.exists(nodeRef) == true)
{
if (isRecord(nodeRef) == true)
{
for (QName property : after.keySet())
{
Serializable beforeValue = null;
if (before != null)
{
beforeValue = before.get(property);
}
Serializable afterValue = null;
if (after != null)
{
afterValue = after.get(property);
}
if (EqualsHelper.nullSafeEquals(beforeValue, afterValue) == false &&
isPropertyEditable(nodeRef, property) == false)
{
// the user can't edit the record property
throw new ModelAccessDeniedException(
"The user " + AuthenticationUtil.getFullyAuthenticatedUser() +
" does not have the permission to edit the record property " + property.toString() +
" on the node " + nodeRef.toString());
}
}
}
}
}
finally
{
onUpdateProperties.enable();
}
}
/** /**
* @see org.alfresco.module.org_alfresco_module_rm.record.RecordService#getRecordMetaDataAspects() * @see org.alfresco.module.org_alfresco_module_rm.record.RecordService#getRecordMetaDataAspects()
@@ -579,4 +702,120 @@ public class RecordServiceImpl implements RecordService,
} }
}); });
} }
/**
* @see org.alfresco.module.org_alfresco_module_rm.record.RecordService#isPropertyEditable(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName)
*/
@Override
public boolean isPropertyEditable(NodeRef record, QName property)
{
ParameterCheck.mandatory("record", record);
ParameterCheck.mandatory("property", property);
if (isRecord(record) == false)
{
throw new AlfrescoRuntimeException("Can not check if the property " + property.toString() + " is editable, because node reference is not a record.");
}
if (logger.isDebugEnabled() == true)
{
logger.debug("Checking whether property " + property.toString() + " is editable for user " + AuthenticationUtil.getRunAsUser());
}
boolean result = alwaysEditProperty(property);
if (result == false)
{
boolean allowRecordEdit = false;
boolean allowNonRecordEdit = false;
AccessStatus accessNonRecord = capabilityService.getCapabilityAccessState(record, RMPermissionModel.EDIT_NON_RECORD_METADATA);
AccessStatus accessDeclaredRecord = capabilityService.getCapabilityAccessState(record, RMPermissionModel.EDIT_DECLARED_RECORD_METADATA);
AccessStatus accessRecord = capabilityService.getCapabilityAccessState(record, RMPermissionModel.EDIT_RECORD_METADATA);
if (AccessStatus.ALLOWED.equals(accessNonRecord) == true)
{
allowNonRecordEdit = true;
}
if (AccessStatus.ALLOWED.equals(accessRecord) == true ||
AccessStatus.ALLOWED.equals(accessDeclaredRecord) == true)
{
allowRecordEdit = true;
}
if (allowNonRecordEdit == true && allowRecordEdit == true)
{
result = true;
}
else if (allowNonRecordEdit == true && allowRecordEdit == false)
{
// can only edit non record properties
if (isRecordMetadata(property) == false)
{
result = true;
}
}
else if (allowNonRecordEdit == false && allowRecordEdit == true)
{
// can only edit record properties
if (isRecordMetadata(property) == true)
{
result = true;
}
}
// otherwise we can't edit any properties so just return the empty set
}
return result;
}
private boolean isRecordMetadata(QName property)
{
boolean result = ArrayUtils.contains(RECORD_MODEL_URIS, property.getNamespaceURI());
if (result == false && ArrayUtils.contains(NON_RECORD_MODEL_URIS, property.getNamespaceURI()) == false)
{
PropertyDefinition def = dictionaryService.getProperty(property);
if (def != null)
{
ClassDefinition parent = def.getContainerClass();
if (parent != null && parent.isAspect() == true)
{
result = getRecordMetaDataAspects().contains(parent.getName());
}
}
}
return result;
}
/**
* Determines whether the property should always be allowed to be edited or not.
*
* @param property
* @return
*/
private boolean alwaysEditProperty(QName property)
{
return (ArrayUtils.contains(ALWAYS_EDIT_URIS, property.getNamespaceURI()) ||
ArrayUtils.contains(ALWAYS_EDIT_PROPERTIES, property) ||
isProtectedProperty(property));
}
/**
* Helper method to determine whether a property is protected at a dictionary definition
* level.
*
* @param property property qualified name
* @return booelan true if protected, false otherwise
*/
private boolean isProtectedProperty(QName property)
{
boolean result = false;
PropertyDefinition def = dictionaryService.getProperty(property);
if (def != null)
{
result = def.isProtected();
}
return result;
}
} }

View File

@@ -26,6 +26,8 @@ import java.util.Map;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase; import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase;
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
import org.alfresco.module.org_alfresco_module_rm.security.FilePlanAuthenticationService;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
@@ -43,14 +45,29 @@ import org.alfresco.service.namespace.RegexQNamePattern;
*/ */
public class BroadcastVitalRecordDefinitionAction extends RMActionExecuterAbstractBase public class BroadcastVitalRecordDefinitionAction extends RMActionExecuterAbstractBase
{ {
private FilePlanAuthenticationService filePlanAuthenticationService;
public void setFilePlanAuthenticationService(FilePlanAuthenticationService filePlanAuthenticationService)
{
this.filePlanAuthenticationService = filePlanAuthenticationService;
}
/** /**
* @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action,
* org.alfresco.service.cmr.repository.NodeRef) * org.alfresco.service.cmr.repository.NodeRef)
*/ */
@Override @Override
protected void executeImpl(Action action, NodeRef actionedUponNodeRef) protected void executeImpl(Action action, final NodeRef actionedUponNodeRef)
{ {
this.propagateChangeToChildrenOf(actionedUponNodeRef); filePlanAuthenticationService.runAsRmAdmin(new RunAsWork<Void>()
{
@Override
public Void doWork() throws Exception
{
propagateChangeToChildrenOf(actionedUponNodeRef);
return null;
}
});
} }
/** /**

View File

@@ -22,10 +22,12 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Set; 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.Capability;
import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel; 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.dod5015.DOD5015Model;
import org.alfresco.module.org_alfresco_module_rm.record.RecordService; 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.ExtendedSecurityService; import org.alfresco.module.org_alfresco_module_rm.security.ExtendedSecurityService;
import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase; import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase;
import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.content.MimetypeMap;
@@ -37,10 +39,11 @@ import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.security.AccessStatus; import org.alfresco.service.cmr.security.AccessStatus;
import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
import org.alfresco.util.GUID;
/** /**
* Records Service Implementation Test * Records Service Implementation Test
* *
* @author Roy Wetherall * @author Roy Wetherall
* @author Tuna Aksoy * @author Tuna Aksoy
* @since 2.1 * @since 2.1
@@ -49,7 +52,9 @@ public class RecordServiceImplTest extends BaseRMTestCase
{ {
/** Services */ /** Services */
protected ActionService dmActionService; protected ActionService dmActionService;
protected PermissionService dmPermissionService; protected PermissionService dmPermissionService;
protected ExtendedSecurityService extendedSecurityService; protected ExtendedSecurityService extendedSecurityService;
/** /**
@@ -67,7 +72,7 @@ public class RecordServiceImplTest extends BaseRMTestCase
/** /**
* This is a user test * This is a user test
* *
* @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase#isUserTest() * @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase#isUserTest()
*/ */
@Override @Override
@@ -78,7 +83,7 @@ public class RecordServiceImplTest extends BaseRMTestCase
/** /**
* This is a record test * This is a record test
* *
* @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase#isRecordTest() * @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase#isRecordTest()
*/ */
@Override @Override
@@ -116,19 +121,14 @@ public class RecordServiceImplTest extends BaseRMTestCase
/** /**
* Helper method for getting a list of record meta data aspects * Helper method for getting a list of record meta data aspects
* *
* @return Record meta data aspects as list * @return Record meta data aspects as list
*/ */
private List<QName> getAspectList() private List<QName> getAspectList()
{ {
QName[] aspects = new QName[] QName[] aspects = new QName[] { DOD5015Model.ASPECT_DIGITAL_PHOTOGRAPH_RECORD,
{ DOD5015Model.ASPECT_PDF_RECORD, DOD5015Model.ASPECT_WEB_RECORD,
DOD5015Model.ASPECT_DIGITAL_PHOTOGRAPH_RECORD, DOD5015Model.ASPECT_SCANNED_RECORD, ASPECT_RECORD_META_DATA };
DOD5015Model.ASPECT_PDF_RECORD,
DOD5015Model.ASPECT_WEB_RECORD,
DOD5015Model.ASPECT_SCANNED_RECORD,
ASPECT_RECORD_META_DATA
};
return Arrays.asList(aspects); return Arrays.asList(aspects);
} }
@@ -190,22 +190,21 @@ public class RecordServiceImplTest extends BaseRMTestCase
} }
/** /**
* @see RecordService#createRecord(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef) * @see RecordService#createRecord(org.alfresco.service.cmr.repository.NodeRef,
* org.alfresco.service.cmr.repository.NodeRef)
*/ */
public void testCreateRecord() throws Exception public void testCreateRecord() throws Exception
{ {
// show that users without WRITE can not create a record from a document // show that users without WRITE can not create a record from a document
doTestInTransaction(new FailureTest doTestInTransaction(new FailureTest(
( "Can not create a record from a document if you do not have WRITE permissions.",
"Can not create a record from a document if you do not have WRITE permissions.", AccessDeniedException.class)
AccessDeniedException.class
)
{ {
public void run() throws Exception public void run() throws Exception
{ {
recordService.createRecord(filePlan, dmDocument); recordService.createRecord(filePlan, dmDocument);
} }
}, dmConsumer); }, dmConsumer);
// create record from document // create record from document
doTestInTransaction(new Test<Void>() doTestInTransaction(new Test<Void>()
@@ -218,107 +217,108 @@ public class RecordServiceImplTest extends BaseRMTestCase
assertFalse(recordService.isRecord(dmDocument)); assertFalse(recordService.isRecord(dmDocument));
assertFalse(extendedSecurityService.hasExtendedSecurity(dmDocument)); assertFalse(extendedSecurityService.hasExtendedSecurity(dmDocument));
checkPermissions(READ_RECORDS, checkPermissions(READ_RECORDS, AccessStatus.DENIED, // file plan
AccessStatus.DENIED, // file plan AccessStatus.DENIED, // unfiled container
AccessStatus.DENIED, // unfiled container AccessStatus.DENIED, // record category
AccessStatus.DENIED, // record category AccessStatus.DENIED, // record folder
AccessStatus.DENIED, // record folder AccessStatus.DENIED); // doc/record
AccessStatus.DENIED); // doc/record
assertEquals(AccessStatus.DENIED, assertEquals(AccessStatus.DENIED, dmPermissionService.hasPermission(filePlan,
dmPermissionService.hasPermission(filePlan, RMPermissionModel.VIEW_RECORDS)); RMPermissionModel.VIEW_RECORDS));
checkPermissions(FILING, checkPermissions(FILING, AccessStatus.DENIED, // file plan
AccessStatus.DENIED, // file plan AccessStatus.DENIED, // unfiled container
AccessStatus.DENIED, // unfiled container AccessStatus.DENIED, // record category
AccessStatus.DENIED, // record category AccessStatus.DENIED, // record folder
AccessStatus.DENIED, // record folder AccessStatus.DENIED); // doc/record
AccessStatus.DENIED); // doc/record
recordService.createRecord(filePlan, dmDocument); recordService.createRecord(filePlan, dmDocument);
checkPermissions(READ_RECORDS, checkPermissions(READ_RECORDS, AccessStatus.ALLOWED, // file
AccessStatus.ALLOWED, // file plan // plan
AccessStatus.ALLOWED, // unfiled container AccessStatus.ALLOWED, // unfiled container
AccessStatus.DENIED, // record category AccessStatus.DENIED, // record category
AccessStatus.DENIED, // record folder AccessStatus.DENIED, // record folder
AccessStatus.ALLOWED); // doc/record AccessStatus.ALLOWED); // doc/record
assertEquals(AccessStatus.ALLOWED, assertEquals(AccessStatus.ALLOWED, dmPermissionService.hasPermission(filePlan,
dmPermissionService.hasPermission(filePlan, RMPermissionModel.VIEW_RECORDS)); RMPermissionModel.VIEW_RECORDS));
checkPermissions(FILING, checkPermissions(FILING, AccessStatus.DENIED, // file plan
AccessStatus.DENIED, // file plan AccessStatus.DENIED, // unfiled container
AccessStatus.DENIED, // unfiled container AccessStatus.DENIED, // record category
AccessStatus.DENIED, // record category AccessStatus.DENIED, // record folder
AccessStatus.DENIED, // record folder AccessStatus.ALLOWED); // doc/record
AccessStatus.ALLOWED); // doc/record
assertTrue(recordService.isRecord(dmDocument)); assertTrue(recordService.isRecord(dmDocument));
assertTrue(extendedSecurityService.hasExtendedSecurity(dmDocument)); assertTrue(extendedSecurityService.hasExtendedSecurity(dmDocument));
assertFalse(recordService.isFiled(dmDocument)); assertFalse(recordService.isFiled(dmDocument));
// show that the record has meta-data about it's original location // show that the record has meta-data about it's original
// location
assertTrue(nodeService.hasAspect(dmDocument, ASPECT_RECORD_ORIGINATING_DETAILS)); assertTrue(nodeService.hasAspect(dmDocument, ASPECT_RECORD_ORIGINATING_DETAILS));
assertEquals(originalLocation, nodeService.getProperty(dmDocument, PROP_RECORD_ORIGINATING_LOCATION)); assertEquals(originalLocation, nodeService.getProperty(dmDocument, PROP_RECORD_ORIGINATING_LOCATION));
assertFalse(originalLocation == nodeService.getPrimaryParent(dmDocument).getParentRef()); assertFalse(originalLocation == nodeService.getPrimaryParent(dmDocument).getParentRef());
// show that the record is linked to it's original location // show that the record is linked to it's original location
assertEquals(2, nodeService.getParentAssocs(dmDocument).size()); assertEquals(2, nodeService.getParentAssocs(dmDocument).size());
// **** // ****
// Capability Tests // Capability Tests
// **** // ****
assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(filePlan, RMPermissionModel.VIEW_RECORDS)); assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(filePlan,
assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(filePlan, RMPermissionModel.EDIT_RECORD_METADATA)); RMPermissionModel.VIEW_RECORDS));
assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(filePlan,
RMPermissionModel.EDIT_RECORD_METADATA));
Capability filling = capabilityService.getCapability("FileRecords"); Capability filling = capabilityService.getCapability("FileRecords");
assertEquals(AccessStatus.DENIED, filling.hasPermission(dmDocument)); assertEquals(AccessStatus.DENIED, filling.hasPermission(dmDocument));
Capability editRecordMetadata = capabilityService.getCapability("EditRecordMetadata"); Capability editRecordMetadata = capabilityService.getCapability("EditRecordMetadata");
assertEquals(AccessStatus.ALLOWED, editRecordMetadata.hasPermission(dmDocument)); assertEquals(AccessStatus.ALLOWED, editRecordMetadata.hasPermission(dmDocument));
Capability updateProperties = capabilityService.getCapability("UpdateProperties"); Capability updateProperties = capabilityService.getCapability("UpdateProperties");
assertEquals(AccessStatus.ALLOWED, updateProperties.hasPermission(dmDocument)); assertEquals(AccessStatus.ALLOWED, updateProperties.hasPermission(dmDocument));
return null; return null;
} }
}, dmCollaborator); }, dmCollaborator);
// check the consumer's permissions are correct for the newly created document // check the consumer's permissions are correct for the newly created
// document
doTestInTransaction(new Test<Void>() doTestInTransaction(new Test<Void>()
{ {
@Override @Override
public Void run() public Void run()
{ {
checkPermissions(READ_RECORDS, checkPermissions(READ_RECORDS, AccessStatus.ALLOWED, // file
AccessStatus.ALLOWED, // file plan // plan
AccessStatus.ALLOWED, // unfiled container AccessStatus.ALLOWED, // unfiled container
AccessStatus.DENIED, // record category AccessStatus.DENIED, // record category
AccessStatus.DENIED, // record folder AccessStatus.DENIED, // record folder
AccessStatus.ALLOWED); // doc/record AccessStatus.ALLOWED); // doc/record
checkPermissions(FILING, AccessStatus.DENIED, // file plan
AccessStatus.DENIED, // unfiled container
AccessStatus.DENIED, // record category
AccessStatus.DENIED, // record folder
AccessStatus.DENIED); // doc/record
assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(filePlan,
RMPermissionModel.VIEW_RECORDS));
assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(filePlan,
RMPermissionModel.EDIT_RECORD_METADATA));
checkPermissions(FILING,
AccessStatus.DENIED, // file plan
AccessStatus.DENIED, // unfiled container
AccessStatus.DENIED, // record category
AccessStatus.DENIED, // record folder
AccessStatus.DENIED); // doc/record
assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(filePlan, RMPermissionModel.VIEW_RECORDS));
assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(filePlan, RMPermissionModel.EDIT_RECORD_METADATA));
Capability filling = capabilityService.getCapability("FileRecords"); Capability filling = capabilityService.getCapability("FileRecords");
assertEquals(AccessStatus.DENIED, filling.hasPermission(dmDocument)); assertEquals(AccessStatus.DENIED, filling.hasPermission(dmDocument));
Capability editRecordMetadata = capabilityService.getCapability("EditRecordMetadata"); Capability editRecordMetadata = capabilityService.getCapability("EditRecordMetadata");
assertEquals(AccessStatus.DENIED, editRecordMetadata.hasPermission(dmDocument)); assertEquals(AccessStatus.DENIED, editRecordMetadata.hasPermission(dmDocument));
Capability updateProperties = capabilityService.getCapability("UpdateProperties"); Capability updateProperties = capabilityService.getCapability("UpdateProperties");
assertEquals(AccessStatus.DENIED, updateProperties.hasPermission(dmDocument)); assertEquals(AccessStatus.DENIED, updateProperties.hasPermission(dmDocument));
return null; return null;
} }
}, dmConsumer); }, dmConsumer);
@@ -327,17 +327,15 @@ public class RecordServiceImplTest extends BaseRMTestCase
public void testCreateRecordNoLink() throws Exception public void testCreateRecordNoLink() throws Exception
{ {
// show that users without WRITE can not create a record from a document // show that users without WRITE can not create a record from a document
doTestInTransaction(new FailureTest doTestInTransaction(new FailureTest(
( "Can not create a record from a document if you do not have WRITE permissions.",
"Can not create a record from a document if you do not have WRITE permissions.", AccessDeniedException.class)
AccessDeniedException.class
)
{ {
public void run() throws Exception public void run() throws Exception
{ {
recordService.createRecord(filePlan, dmDocument, false); recordService.createRecord(filePlan, dmDocument, false);
} }
}, dmConsumer); }, dmConsumer);
// create record from document // create record from document
final NodeRef originalLocation = doTestInTransaction(new Test<NodeRef>() final NodeRef originalLocation = doTestInTransaction(new Test<NodeRef>()
@@ -347,44 +345,40 @@ public class RecordServiceImplTest extends BaseRMTestCase
{ {
NodeRef originalLocation = nodeService.getPrimaryParent(dmDocument).getParentRef(); NodeRef originalLocation = nodeService.getPrimaryParent(dmDocument).getParentRef();
assertFalse(recordService.isRecord(dmDocument)); //assertFalse(recordService.isRecord(dmDocument));
assertFalse(extendedSecurityService.hasExtendedSecurity(dmDocument)); //assertFalse(extendedSecurityService.hasExtendedSecurity(dmDocument));
checkPermissions(READ_RECORDS, checkPermissions(READ_RECORDS, AccessStatus.DENIED, // file plan
AccessStatus.DENIED, // file plan AccessStatus.DENIED, // unfiled container
AccessStatus.DENIED, // unfiled container AccessStatus.DENIED, // record category
AccessStatus.DENIED, // record category AccessStatus.DENIED, // record folder
AccessStatus.DENIED, // record folder AccessStatus.DENIED); // doc/record
AccessStatus.DENIED); // doc/record
assertEquals(AccessStatus.DENIED, assertEquals(AccessStatus.DENIED, dmPermissionService.hasPermission(filePlan,
dmPermissionService.hasPermission(filePlan, RMPermissionModel.VIEW_RECORDS)); RMPermissionModel.VIEW_RECORDS));
checkPermissions(FILING, checkPermissions(FILING, AccessStatus.DENIED, // file plan
AccessStatus.DENIED, // file plan AccessStatus.DENIED, // unfiled container
AccessStatus.DENIED, // unfiled container AccessStatus.DENIED, // record category
AccessStatus.DENIED, // record category AccessStatus.DENIED, // record folder
AccessStatus.DENIED, // record folder AccessStatus.DENIED); // doc/record
AccessStatus.DENIED); // doc/record
recordService.createRecord(filePlan, dmDocument, false); recordService.createRecord(filePlan, dmDocument, false);
checkPermissions(READ_RECORDS, checkPermissions(READ_RECORDS, AccessStatus.DENIED, // file plan
AccessStatus.DENIED, // file plan AccessStatus.DENIED, // unfiled container
AccessStatus.DENIED, // unfiled container AccessStatus.DENIED, // record category
AccessStatus.DENIED, // record category AccessStatus.DENIED, // record folder
AccessStatus.DENIED, // record folder AccessStatus.DENIED); // doc/record
AccessStatus.DENIED); // doc/record
assertEquals(AccessStatus.DENIED, assertEquals(AccessStatus.DENIED, dmPermissionService.hasPermission(filePlan,
dmPermissionService.hasPermission(filePlan, RMPermissionModel.VIEW_RECORDS)); RMPermissionModel.VIEW_RECORDS));
checkPermissions(FILING, checkPermissions(FILING, AccessStatus.DENIED, // file plan
AccessStatus.DENIED, // file plan AccessStatus.DENIED, // unfiled container
AccessStatus.DENIED, // unfiled container AccessStatus.DENIED, // record category
AccessStatus.DENIED, // record category AccessStatus.DENIED, // record folder
AccessStatus.DENIED, // record folder AccessStatus.DENIED); // doc/record
AccessStatus.DENIED); // doc/record
return originalLocation; return originalLocation;
} }
@@ -399,7 +393,8 @@ public class RecordServiceImplTest extends BaseRMTestCase
assertFalse(extendedSecurityService.hasExtendedSecurity(dmDocument)); assertFalse(extendedSecurityService.hasExtendedSecurity(dmDocument));
assertFalse(recordService.isFiled(dmDocument)); assertFalse(recordService.isFiled(dmDocument));
// show that the record has meta-data about it's original location // show that the record has meta-data about it's original
// location
assertTrue(nodeService.hasAspect(dmDocument, ASPECT_RECORD_ORIGINATING_DETAILS)); assertTrue(nodeService.hasAspect(dmDocument, ASPECT_RECORD_ORIGINATING_DETAILS));
assertEquals(originalLocation, nodeService.getProperty(dmDocument, PROP_RECORD_ORIGINATING_LOCATION)); assertEquals(originalLocation, nodeService.getProperty(dmDocument, PROP_RECORD_ORIGINATING_LOCATION));
assertFalse(originalLocation == nodeService.getPrimaryParent(dmDocument).getParentRef()); assertFalse(originalLocation == nodeService.getPrimaryParent(dmDocument).getParentRef());
@@ -419,7 +414,7 @@ public class RecordServiceImplTest extends BaseRMTestCase
@Override @Override
public NodeRef run() public NodeRef run()
{ {
NodeRef record = fileFolderService.create(rmFolder, "test101.txt" , TYPE_CONTENT).getNodeRef(); NodeRef record = fileFolderService.create(rmFolder, "test101.txt", TYPE_CONTENT).getNodeRef();
ContentWriter writer = contentService.getWriter(record, PROP_CONTENT, true); ContentWriter writer = contentService.getWriter(record, PROP_CONTENT, true);
writer.setEncoding("UTF-8"); writer.setEncoding("UTF-8");
@@ -495,21 +490,245 @@ public class RecordServiceImplTest extends BaseRMTestCase
}, AuthenticationUtil.getSystemUserName()); }, AuthenticationUtil.getSystemUserName());
} }
private void checkPermissions(String permission, AccessStatus filePlanExpected, private void checkPermissions(String permission, AccessStatus filePlanExpected, AccessStatus unfiledExpected,
AccessStatus unfiledExpected, AccessStatus recordCatExpected, AccessStatus recordFolderExpected, AccessStatus recordExpected)
AccessStatus recordCatExpected,
AccessStatus recordFolderExpected,
AccessStatus recordExpected)
{ {
assertEquals(filePlanExpected, assertEquals(filePlanExpected, dmPermissionService.hasPermission(filePlan, permission));
dmPermissionService.hasPermission(filePlan, permission)); assertEquals(unfiledExpected, dmPermissionService.hasPermission(unfiledContainer, permission));
assertEquals(unfiledExpected, assertEquals(recordCatExpected, dmPermissionService.hasPermission(rmContainer, permission));
dmPermissionService.hasPermission(unfiledContainer, permission)); assertEquals(recordFolderExpected, dmPermissionService.hasPermission(rmFolder, permission));
assertEquals(recordCatExpected, assertEquals(recordExpected, dmPermissionService.hasPermission(dmDocument, permission));
dmPermissionService.hasPermission(rmContainer, permission)); }
assertEquals(recordFolderExpected,
dmPermissionService.hasPermission(rmFolder, permission)); private String createUserWithCapabilties(final String... capabiltyNames)
assertEquals(recordExpected, {
dmPermissionService.hasPermission(dmDocument, permission)); return doTestInTransaction(new Test<String>()
{
@Override
public String run() throws Exception
{
Role role = utils.createRole(filePlan, GUID.generate(), capabiltyNames);
String userName = GUID.generate();
createPerson(userName);
filePlanRoleService.assignRoleToAuthority(filePlan, role.getName(), userName);
return userName;
}
}, AuthenticationUtil.getSystemUserName());
}
/**
* Test {@link RecordService#isPropertyEditable(NodeRef, QName)}
*/
public void testIsPropertyEditable() throws Exception
{
final String nonRecordMetadata = createUserWithCapabilties(
RMPermissionModel.VIEW_RECORDS,
RMPermissionModel.EDIT_NON_RECORD_METADATA);
final String recordMetadata = createUserWithCapabilties(
RMPermissionModel.VIEW_RECORDS,
RMPermissionModel.EDIT_RECORD_METADATA);
final String declaredRecordMetadata = createUserWithCapabilties(
RMPermissionModel.VIEW_RECORDS,
RMPermissionModel.EDIT_DECLARED_RECORD_METADATA);
doTestInTransaction(new VoidTest()
{
@Override
public void runImpl() throws Exception
{
filePlanPermissionService.setPermission(rmFolder, rmUserName, RMPermissionModel.FILING);
filePlanPermissionService.setPermission(rmFolder, nonRecordMetadata, RMPermissionModel.FILING);
filePlanPermissionService.setPermission(rmFolder, recordMetadata, RMPermissionModel.FILING);
filePlanPermissionService.setPermission(rmFolder, declaredRecordMetadata, RMPermissionModel.FILING);
}
});
// test rmadmin
doTestInTransaction(new VoidTest()
{
@Override
public void runImpl() throws Exception
{
assertTrue(recordService.isPropertyEditable(recordOne, PROP_ORIGINATING_ORGANIZATION));
assertTrue(recordService.isPropertyEditable(recordOne, PROP_DESCRIPTION));
assertTrue(recordService.isPropertyEditable(recordDeclaredOne, PROP_ORIGINATING_ORGANIZATION));
assertFalse(recordService.isPropertyEditable(recordDeclaredOne, PROP_DESCRIPTION));
}
});
// test normal user
doTestInTransaction(new VoidTest()
{
@Override
public void runImpl() throws Exception
{
assertFalse(recordService.isPropertyEditable(recordOne, PROP_ORIGINATING_ORGANIZATION));
assertFalse(recordService.isPropertyEditable(recordOne, PROP_DESCRIPTION));
assertFalse(recordService.isPropertyEditable(recordDeclaredOne, PROP_ORIGINATING_ORGANIZATION));
assertFalse(recordService.isPropertyEditable(recordDeclaredOne, PROP_DESCRIPTION));
}
}, rmUserName);
// test undeclared record with edit non-record metadata capability
// test declared record with edit non-record metadata capability
doTestInTransaction(new VoidTest()
{
@Override
public void runImpl() throws Exception
{
assertFalse(recordService.isPropertyEditable(recordOne, PROP_ORIGINATING_ORGANIZATION));
assertTrue(recordService.isPropertyEditable(recordOne, PROP_DESCRIPTION));
assertFalse(recordService.isPropertyEditable(recordDeclaredOne, PROP_ORIGINATING_ORGANIZATION));
assertFalse(recordService.isPropertyEditable(recordDeclaredOne, PROP_DESCRIPTION));
}
}, nonRecordMetadata);
// test undeclared record with edit record metadata capability
// test declared record with edit record metadata capability
doTestInTransaction(new VoidTest()
{
@Override
public void runImpl() throws Exception
{
assertTrue(recordService.isPropertyEditable(recordOne, PROP_ORIGINATING_ORGANIZATION));
assertFalse(recordService.isPropertyEditable(recordOne, PROP_DESCRIPTION));
assertFalse(recordService.isPropertyEditable(recordDeclaredOne, PROP_ORIGINATING_ORGANIZATION));
assertFalse(recordService.isPropertyEditable(recordDeclaredOne, PROP_DESCRIPTION));
}
}, recordMetadata);
// test undeclared record with edit declared record metadata capability
// test declared record with edit declared record metadata capability
doTestInTransaction(new VoidTest()
{
@Override
public void runImpl() throws Exception
{
assertFalse(recordService.isPropertyEditable(recordOne, PROP_ORIGINATING_ORGANIZATION));
assertFalse(recordService.isPropertyEditable(recordOne, PROP_DESCRIPTION));
assertTrue(recordService.isPropertyEditable(recordDeclaredOne, PROP_ORIGINATING_ORGANIZATION));
assertFalse(recordService.isPropertyEditable(recordDeclaredOne, PROP_DESCRIPTION));
}
}, declaredRecordMetadata);
}
public void testRecordPropertiesUpdate() throws Exception
{
final String nonRecordMetadata = createUserWithCapabilties(
RMPermissionModel.VIEW_RECORDS,
RMPermissionModel.EDIT_NON_RECORD_METADATA);
final String recordMetadata = createUserWithCapabilties(
RMPermissionModel.VIEW_RECORDS,
RMPermissionModel.EDIT_RECORD_METADATA);
final String declaredRecordMetadata = createUserWithCapabilties(
RMPermissionModel.VIEW_RECORDS,
RMPermissionModel.EDIT_DECLARED_RECORD_METADATA);
doTestInTransaction(new VoidTest()
{
@Override
public void runImpl() throws Exception
{
filePlanPermissionService.setPermission(rmFolder, rmUserName, RMPermissionModel.FILING);
filePlanPermissionService.setPermission(rmFolder, nonRecordMetadata, RMPermissionModel.FILING);
filePlanPermissionService.setPermission(rmFolder, recordMetadata, RMPermissionModel.FILING);
filePlanPermissionService.setPermission(rmFolder, declaredRecordMetadata, RMPermissionModel.FILING);
}
});
// test rmadmin
canEditProperty(recordOne, ContentModel.PROP_DESCRIPTION, rmAdminName);
canEditProperty(recordOne, PROP_ORIGINATING_ORGANIZATION, rmAdminName);
cantEditProperty(recordDeclaredOne, ContentModel.PROP_DESCRIPTION, rmAdminName);
canEditProperty(recordDeclaredOne, PROP_ORIGINATING_ORGANIZATION, rmAdminName);
// test normal user
cantEditProperty(recordOne, ContentModel.PROP_DESCRIPTION, rmUserName);
cantEditProperty(recordOne, PROP_ORIGINATING_ORGANIZATION, rmUserName);
cantEditProperty(recordDeclaredOne, ContentModel.PROP_DESCRIPTION, rmUserName);
cantEditProperty(recordDeclaredOne, PROP_ORIGINATING_ORGANIZATION, rmUserName);
// test undeclared record with edit non-record metadata capability
canEditProperty(recordOne, ContentModel.PROP_DESCRIPTION, nonRecordMetadata);
cantEditProperty(recordOne, PROP_ORIGINATING_ORGANIZATION, nonRecordMetadata);
// test declared record with edit non-record metadata capability
cantEditProperty(recordDeclaredOne, ContentModel.PROP_DESCRIPTION, nonRecordMetadata);
cantEditProperty(recordDeclaredOne, PROP_ORIGINATING_ORGANIZATION, nonRecordMetadata);
// test undeclared record with edit record metadata capability
cantEditProperty(recordOne, ContentModel.PROP_DESCRIPTION, recordMetadata);
canEditProperty(recordOne, PROP_ORIGINATING_ORGANIZATION, recordMetadata);
// test declared record with edit record metadata capability
cantEditProperty(recordDeclaredOne, ContentModel.PROP_DESCRIPTION, recordMetadata);
cantEditProperty(recordDeclaredOne, PROP_ORIGINATING_ORGANIZATION, recordMetadata);
// test undeclared record with edit declared record metadata capability
cantEditProperty(recordOne, ContentModel.PROP_DESCRIPTION, declaredRecordMetadata);
cantEditProperty(recordOne, PROP_ORIGINATING_ORGANIZATION, declaredRecordMetadata);
// test declared record with edit declared record metadata capability
cantEditProperty(recordDeclaredOne, ContentModel.PROP_DESCRIPTION, declaredRecordMetadata);
canEditProperty(recordDeclaredOne, PROP_ORIGINATING_ORGANIZATION, declaredRecordMetadata);
}
public abstract class CommitPropertyFailTest extends Test<Void>
{
@Override
public Void run() throws Exception
{
// TODO Auto-generated method stub
return null;
}
@Override
public void test(Void result) throws Exception
{
// TODO Auto-generated method stub
super.test(result);
}
}
private void cantEditProperty(final NodeRef nodeRef, final QName property, String user) throws Exception
{
boolean failure = false;
try
{
doTestInTransaction(new VoidTest()
{
@Override
public void runImpl() throws Exception
{
nodeService.setProperty(nodeRef, property, GUID.generate());
}
}, user);
}
catch (Throwable exception)
{
// expected
failure = true;
}
// assert fail not failure
if (failure == false)
{
fail("Property should not have been editable.");
}
}
private void canEditProperty(final NodeRef nodeRef, final QName property, String user) throws Exception
{
doTestInTransaction(new VoidTest()
{
@Override
public void runImpl() throws Exception
{
nodeService.setProperty(nodeRef, property, GUID.generate());
}
}, user);
} }
} }

View File

@@ -454,8 +454,7 @@ public abstract class BaseRMTestCase extends RetryingTransactionHelperTestCase
unfiledContainer = filePlanService.getUnfiledContainer(filePlan); unfiledContainer = filePlanService.getUnfiledContainer(filePlan);
assertNotNull(unfiledContainer); assertNotNull(unfiledContainer);
} }
}, }, AuthenticationUtil.getSystemUserName());
AuthenticationUtil.getAdminUserName());
} }
/** /**
@@ -566,6 +565,7 @@ public abstract class BaseRMTestCase extends RetryingTransactionHelperTestCase
{ {
filePlanPermissionService.setPermission(filePlan, user, FILING); filePlanPermissionService.setPermission(filePlan, user, FILING);
filePlanPermissionService.setPermission(rmContainer, user, FILING); filePlanPermissionService.setPermission(rmContainer, user, FILING);
filePlanPermissionService.setPermission(rmFolder, user, FILING);
filePlanPermissionService.setPermission(unfiledContainer, user, FILING); filePlanPermissionService.setPermission(unfiledContainer, user, FILING);
} }
} }

View File

@@ -7,16 +7,23 @@ import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService; import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService;
import org.alfresco.module.org_alfresco_module_rm.action.impl.FreezeAction; import org.alfresco.module.org_alfresco_module_rm.action.impl.FreezeAction;
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.disposition.DispositionSchedule; import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService;
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
import org.alfresco.module.org_alfresco_module_rm.model.security.ModelSecurityService; import org.alfresco.module.org_alfresco_module_rm.model.security.ModelSecurityService;
import org.alfresco.module.org_alfresco_module_rm.role.FilePlanRoleService;
import org.alfresco.module.org_alfresco_module_rm.role.Role;
import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
@@ -38,6 +45,8 @@ public class CommonRMTestUtils implements RecordsManagementModel
private ContentService contentService; private ContentService contentService;
private RecordsManagementActionService actionService; private RecordsManagementActionService actionService;
private ModelSecurityService modelSecurityService; private ModelSecurityService modelSecurityService;
private FilePlanRoleService filePlanRoleService;
private CapabilityService capabilityService;
/** test values */ /** test values */
public static final String DEFAULT_DISPOSITION_AUTHORITY = "disposition authority"; public static final String DEFAULT_DISPOSITION_AUTHORITY = "disposition authority";
@@ -54,6 +63,8 @@ public class CommonRMTestUtils implements RecordsManagementModel
contentService = (ContentService)applicationContext.getBean("ContentService"); contentService = (ContentService)applicationContext.getBean("ContentService");
actionService = (RecordsManagementActionService)applicationContext.getBean("RecordsManagementActionService"); actionService = (RecordsManagementActionService)applicationContext.getBean("RecordsManagementActionService");
modelSecurityService = (ModelSecurityService)applicationContext.getBean("ModelSecurityService"); modelSecurityService = (ModelSecurityService)applicationContext.getBean("ModelSecurityService");
filePlanRoleService = (FilePlanRoleService)applicationContext.getBean("FilePlanRoleService");
capabilityService = (CapabilityService)applicationContext.getBean("CapabilityService");
} }
/** /**
@@ -231,4 +242,20 @@ public class CommonRMTestUtils implements RecordsManagementModel
}, AuthenticationUtil.getSystemUserName()); }, AuthenticationUtil.getSystemUserName());
} }
public Role createRole(NodeRef filePlan, String roleName, String ... capabilityNames)
{
Set<Capability> capabilities = new HashSet<Capability>(capabilityNames.length);
for (String name : capabilityNames)
{
Capability capability = capabilityService.getCapability(name);
if (capability == null)
{
throw new AlfrescoRuntimeException("capability " + name + " not found.");
}
capabilities.add(capability);
}
return filePlanRoleService.createRole(filePlan, roleName, roleName, capabilities);
}
} }