diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/capability/rm-capabilities-record-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/capability/rm-capabilities-record-context.xml
index afa426d881..8b82723be3 100644
--- a/rm-server/config/alfresco/module/org_alfresco_module_rm/capability/rm-capabilities-record-context.xml
+++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/capability/rm-capabilities-record-context.xml
@@ -245,7 +245,8 @@
+ parent="declarativeCapability"
+ class="org.alfresco.module.org_alfresco_module_rm.capability.impl.EditNonRecordMetadataCapability">
@@ -263,6 +264,7 @@
+
+
diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/EditNonRecordMetadataCapability.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/EditNonRecordMetadataCapability.java
new file mode 100644
index 0000000000..fa1cc976c6
--- /dev/null
+++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/EditNonRecordMetadataCapability.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2005-2014 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+package org.alfresco.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.module.org_alfresco_module_rm.record.RecordServiceImpl;
+import org.alfresco.module.org_alfresco_module_rm.util.TransactionalResourceHelper;
+import org.alfresco.service.cmr.repository.NodeRef;
+
+/**
+ * Edit non record metadata capability
+ *
+ * @author Roy Wetherall
+ * @since 2.3
+ */
+public class EditNonRecordMetadataCapability extends DeclarativeCapability
+{
+ /** transaction resource helper */
+ private TransactionalResourceHelper transactionalResourceHelper;
+
+ /**
+ * @param transactionalResourceHelper transaction resource helper
+ */
+ public void setTransactionalResourceHelper(TransactionalResourceHelper transactionalResourceHelper)
+ {
+ this.transactionalResourceHelper = transactionalResourceHelper;
+ }
+
+ @Override
+ public int evaluate(NodeRef nodeRef)
+ {
+ // check if this node is a new record
+ if (transactionalResourceHelper.getSet(RecordServiceImpl.KEY_NEW_RECORDS).contains(nodeRef))
+ {
+ // since this is a new record created within this transaction, ignore the usual capability check
+ // under the assumption that the user has CreateRecord
+ // @see https://issues.alfresco.com/jira/browse/RM-1956
+ return AccessDecisionVoter.ACCESS_GRANTED;
+ }
+
+ return super.evaluate(nodeRef);
+ }
+
+
+}
\ No newline at end of file
diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/UpdateCapability.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/UpdateCapability.java
index be66403728..701a1da03b 100644
--- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/UpdateCapability.java
+++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/UpdateCapability.java
@@ -19,16 +19,10 @@
package org.alfresco.module.org_alfresco_module_rm.capability.impl;
import java.io.Serializable;
-import java.util.Arrays;
-import java.util.List;
import java.util.Map;
-import net.sf.acegisecurity.vote.AccessDecisionVoter;
-
-import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel;
import org.alfresco.module.org_alfresco_module_rm.capability.declarative.DeclarativeCompositeCapability;
import org.alfresco.service.cmr.repository.NodeRef;
-import org.alfresco.service.cmr.security.AccessStatus;
import org.alfresco.service.namespace.QName;
/**
@@ -48,36 +42,6 @@ public class UpdateCapability extends DeclarativeCompositeCapability
*/
public int evaluate(NodeRef nodeRef, QName aspectQName, Map properties)
{
- int result = evaluate(nodeRef);
-
- if (AccessDecisionVoter.ACCESS_GRANTED != result)
- {
- if (checkEligablePermissions(nodeRef))
- {
- result = AccessDecisionVoter.ACCESS_GRANTED;
- }
- }
-
- return result;
- }
-
- private boolean checkEligablePermissions(NodeRef nodeRef)
- {
- boolean result = false;
- List permissions = Arrays.asList(
- RMPermissionModel.CREATE_RECORDS
- );
-
- NodeRef filePlan = getFilePlanService().getFilePlan(nodeRef);
- for (String permission : permissions)
- {
- if (permissionService.hasPermission(filePlan, permission) == AccessStatus.ALLOWED)
- {
- result = true;
- break;
- }
- }
-
- return result;
+ return evaluate(nodeRef);
}
}
diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/policy/UpdatePropertiesPolicy.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/policy/UpdatePropertiesPolicy.java
index cb64dc36ef..02f30dbc1b 100644
--- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/policy/UpdatePropertiesPolicy.java
+++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/policy/UpdatePropertiesPolicy.java
@@ -18,14 +18,7 @@
*/
package org.alfresco.module.org_alfresco_module_rm.capability.policy;
-import java.util.Arrays;
-import java.util.List;
-
-import net.sf.acegisecurity.vote.AccessDecisionVoter;
-
-import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel;
import org.alfresco.service.cmr.repository.NodeRef;
-import org.alfresco.service.cmr.security.AccessStatus;
import org.aopalliance.intercept.MethodInvocation;
public class UpdatePropertiesPolicy extends AbstractBasePolicy
@@ -37,38 +30,6 @@ public class UpdatePropertiesPolicy extends AbstractBasePolicy
ConfigAttributeDefinition cad)
{
NodeRef nodeRef = getTestNode(invocation, params, cad.getParameters().get(0), cad.isParent());
- int result = getCapabilityService().getCapability("UpdateProperties").evaluate(nodeRef);
-
- if (AccessDecisionVoter.ACCESS_GRANTED != result)
- {
- if (checkEligablePermissions(nodeRef))
- {
- result = AccessDecisionVoter.ACCESS_GRANTED;
- }
- }
-
- return result;
- }
-
- private boolean checkEligablePermissions(NodeRef nodeRef)
- {
- boolean result = false;
- List permissions = Arrays.asList(
- RMPermissionModel.CREATE_RECORDS,
- RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS,
- RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA
- );
-
- NodeRef filePlan = getFilePlanService().getFilePlan(nodeRef);
- for (String permission : permissions)
- {
- if (permissionService.hasPermission(filePlan, permission) == AccessStatus.ALLOWED)
- {
- result = true;
- break;
- }
- }
-
- return result;
+ return getCapabilityService().getCapability("UpdateProperties").evaluate(nodeRef);
}
}
\ No newline at end of file
diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java
index e5976bd634..a6e54acba8 100644
--- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java
+++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java
@@ -72,7 +72,6 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.security.permissions.AccessDeniedException;
import org.alfresco.repo.security.permissions.impl.ExtendedPermissionService;
-import org.alfresco.repo.transaction.TransactionalResourceHelper;
import org.alfresco.service.cmr.dictionary.AspectDefinition;
import org.alfresco.service.cmr.dictionary.ClassDefinition;
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
@@ -128,7 +127,9 @@ public class RecordServiceImpl extends BaseBehaviourBean
private static Log logger = LogFactory.getLog(RecordServiceImpl.class);
/** transation data key */
- private static final String IGNORE_ON_UPDATE = "ignoreOnUpdate";
+ private static final String KEY_IGNORE_ON_UPDATE = "ignoreOnUpdate";
+ private static final String KEY_PENDING_FILLING = "pendingFilling";
+ public static final String KEY_NEW_RECORDS = "newRecords";
/** I18N */
private static final String MSG_NODE_HAS_ASPECT = "rm.service.node-has-aspect";
@@ -413,7 +414,7 @@ public class RecordServiceImpl extends BaseBehaviourBean
else
{
// check whether filling is pending aspect removal
- Set pendingFilling = TransactionalResourceHelper.getSet("pendingFilling");
+ Set pendingFilling = transactionalResourceHelper.getSet(KEY_PENDING_FILLING);
if (pendingFilling.contains(nodeRef))
{
file(nodeRef);
@@ -504,11 +505,20 @@ public class RecordServiceImpl extends BaseBehaviourBean
if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_NO_CONTENT))
{
// we need to postpone filling until the NO_CONTENT aspect is removed
- Set pendingFilling = TransactionalResourceHelper.getSet("pendingFilling");
+ Set pendingFilling = transactionalResourceHelper.getSet(KEY_PENDING_FILLING);
pendingFilling.add(nodeRef);
}
else
{
+ // store information about the 'new' record in the transaction
+ // @since 2.3
+ // @see https://issues.alfresco.com/jira/browse/RM-1956
+ if (bNew)
+ {
+ Set newRecords = transactionalResourceHelper.getSet(KEY_NEW_RECORDS);
+ newRecords.add(nodeRef);
+ }
+
// create and file the content as a record
file(nodeRef);
}
@@ -567,7 +577,7 @@ public class RecordServiceImpl extends BaseBehaviourBean
*/
public void disablePropertyEditableCheck(NodeRef nodeRef)
{
- Set ignoreOnUpdate = TransactionalResourceHelper.getSet(IGNORE_ON_UPDATE);
+ Set ignoreOnUpdate = transactionalResourceHelper.getSet(KEY_IGNORE_ON_UPDATE);
ignoreOnUpdate.add(nodeRef);
}
@@ -598,7 +608,7 @@ public class RecordServiceImpl extends BaseBehaviourBean
!AuthenticationUtil.isRunAsUserTheSystemUser() &&
nodeService.exists(nodeRef) &&
isRecord(nodeRef) &&
- !TransactionalResourceHelper.getSet(IGNORE_ON_UPDATE).contains(nodeRef))
+ !transactionalResourceHelper.getSet(KEY_IGNORE_ON_UPDATE).contains(nodeRef))
{
for (Map.Entry entry : after.entrySet())
{
@@ -637,8 +647,7 @@ public class RecordServiceImpl extends BaseBehaviourBean
if (!propertyUnchanged &&
!(ContentModel.PROP_CONTENT.equals(property) && beforeValue == null) &&
- !isPropertyEditable(nodeRef, property) &&
- !checkEligablePermissions(nodeRef))
+ !isPropertyEditable(nodeRef, property))
{
// the user can't edit the record property
throw new ModelAccessDeniedException(
@@ -650,28 +659,6 @@ public class RecordServiceImpl extends BaseBehaviourBean
}
}
- private boolean checkEligablePermissions(NodeRef nodeRef)
- {
- boolean result = false;
- List permissions = Arrays.asList(
- RMPermissionModel.CREATE_RECORDS,
- RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS,
- RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA
- );
-
- NodeRef filePlan = getFilePlan(nodeRef);
- for (String permission : permissions)
- {
- if (permissionService.hasPermission(filePlan, permission) == AccessStatus.ALLOWED)
- {
- result = true;
- break;
- }
- }
-
- return result;
- }
-
/**
* Get map containing record metadata aspects.
*
@@ -1711,7 +1698,7 @@ public class RecordServiceImpl extends BaseBehaviourBean
// we can not link a record to the same location more than once
throw new AlfrescoRuntimeException("Can not link a record to the same record folder more than once");
}
- }
+ }
// get the current name of the record
String name = nodeService.getProperty(record, ContentModel.PROP_NAME).toString();
diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/util/ServiceBaseImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/util/ServiceBaseImpl.java
index 4f5c6f9b9c..84ae926006 100644
--- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/util/ServiceBaseImpl.java
+++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/util/ServiceBaseImpl.java
@@ -26,7 +26,6 @@ import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanComponentKind
import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService;
import org.alfresco.module.org_alfresco_module_rm.hold.HoldService;
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
-import org.alfresco.repo.transaction.TransactionalResourceHelper;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.rendition.RenditionService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
@@ -63,6 +62,9 @@ public class ServiceBaseImpl implements RecordsManagementModel, ApplicationConte
/** authentication helper */
protected AuthenticationUtil authenticationUtil;
+
+ /** transactional resource helper */
+ protected TransactionalResourceHelper transactionalResourceHelper;
/**
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
@@ -104,6 +106,14 @@ public class ServiceBaseImpl implements RecordsManagementModel, ApplicationConte
{
this.authenticationUtil = authenticationUtil;
}
+
+ /**
+ * @param transactionalResourceHelper transactional resource helper
+ */
+ public void setTransactionalResourceHelper(TransactionalResourceHelper transactionalResourceHelper)
+ {
+ this.transactionalResourceHelper = transactionalResourceHelper;
+ }
/**
* Helper to get internal node service.
@@ -129,7 +139,7 @@ public class ServiceBaseImpl implements RecordsManagementModel, ApplicationConte
{
FilePlanComponentKind result = null;
- Map map = TransactionalResourceHelper.getMap("rm.transaction.filePlanComponentByNodeRef");
+ Map map = transactionalResourceHelper.getMap("rm.transaction.filePlanComponentByNodeRef");
if (map.containsKey(nodeRef))
{
result = map.get(nodeRef);
@@ -387,7 +397,7 @@ public class ServiceBaseImpl implements RecordsManagementModel, ApplicationConte
NodeRef result = null;
if (nodeRef != null)
{
- Map transactionCache = TransactionalResourceHelper.getMap("rm.servicebase.getFilePlan");
+ Map transactionCache = transactionalResourceHelper.getMap("rm.servicebase.getFilePlan");
if (transactionCache.containsKey(nodeRef))
{
result = transactionCache.get(nodeRef);
diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/record/CreateRecordTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/record/CreateRecordTest.java
index 1f410afb6a..fa9162ea48 100644
--- a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/record/CreateRecordTest.java
+++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/record/CreateRecordTest.java
@@ -28,6 +28,8 @@ 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.repo.security.authentication.AuthenticationUtil.RunAsWork;
+import org.alfresco.repo.security.permissions.AccessDeniedException;
+import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.util.GUID;
@@ -92,7 +94,7 @@ public class CreateRecordTest extends BaseRMTestCase
}
/**
- * @see
+ *
*/
public void testCreateRecordCapabilityOnlyFromFileFolderService() throws Exception
{
@@ -201,4 +203,85 @@ public class CreateRecordTest extends BaseRMTestCase
}
});
}
+
+ /**
+ * Given I have ViewRecord and CreateRecord capabilities
+ * And I have filling on a record folder
+ * When I create content via ScriptNode (simulated)
+ * Then the record is successfully created
+ *
+ * @see https://issues.alfresco.com/jira/browse/RM-1956
+ */
+ public void testCreateRecordViaCoreServices() throws Exception
+ {
+ doBehaviourDrivenTest(new BehaviourDrivenTest()
+ {
+ /** test data */
+ String roleName = GUID.generate();
+ String user = GUID.generate();
+ NodeRef recordFolder;
+ NodeRef record;
+
+ public void given()
+ {
+ // create a role with view and create capabilities
+ Set capabilities = new HashSet(2);
+ capabilities.add(capabilityService.getCapability("ViewRecords"));
+ capabilities.add(capabilityService.getCapability("CreateRecords"));
+ filePlanRoleService.createRole(filePlan, roleName, roleName, capabilities);
+
+ // create user and assign to role
+ createPerson(user, true);
+ filePlanRoleService.assignRoleToAuthority(filePlan, roleName, user);
+
+ // create file plan structure
+ NodeRef rc = filePlanService.createRecordCategory(filePlan, GUID.generate());
+ recordFolder = recordFolderService.createRecordFolder(rc, GUID.generate());
+ }
+
+ public void when()
+ {
+ // give read and file permissions to user
+ filePlanPermissionService.setPermission(recordFolder, user, RMPermissionModel.FILING);
+
+ record = AuthenticationUtil.runAs(new RunAsWork()
+ {
+ public NodeRef doWork() throws Exception
+ {
+ NodeRef record = fileFolderService.create(recordFolder, "testRecord.txt", ContentModel.TYPE_CONTENT).getNodeRef();
+ ContentData content = (ContentData)nodeService.getProperty(record, PROP_CONTENT);
+ nodeService.setProperty(record, PROP_CONTENT, ContentData.setMimetype(content, MimetypeMap.MIMETYPE_TEXT_PLAIN));
+ return record;
+ }
+ }, user);
+ }
+
+ public void then()
+ {
+ // check the details of the record
+ assertTrue(recordService.isRecord(record));
+
+ AuthenticationUtil.runAs(new RunAsWork()
+ {
+ public Void doWork() throws Exception
+ {
+ // we are expecting an expception here
+ try
+ {
+ ContentData content = (ContentData)nodeService.getProperty(record, PROP_CONTENT);
+ nodeService.setProperty(record, PROP_CONTENT, ContentData.setMimetype(content, MimetypeMap.MIMETYPE_TEXT_PLAIN));
+ fail("Expecting access denied exception");
+ }
+ catch (AccessDeniedException exception)
+ {
+ // expceted
+ }
+
+ return null;
+ }
+ }, user);
+ }
+
+ });
+ }
}
diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/service/RecordServiceImplTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/service/RecordServiceImplTest.java
index 3353cc3763..14f5ffa379 100644
--- a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/service/RecordServiceImplTest.java
+++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/service/RecordServiceImplTest.java
@@ -746,8 +746,7 @@ public class RecordServiceImplTest extends BaseRMTestCase
// test rmadmin
canEditProperty(recordOne, ContentModel.PROP_DESCRIPTION, ADMIN_USER);
canEditProperty(recordOne, RecordsManagementModel.PROP_LOCATION, ADMIN_USER);
- // FIXME: Why can a admin user edit the location property of a declared record but not the desc?
- //cantEditProperty(recordDeclaredOne, ContentModel.PROP_DESCRIPTION, ADMIN_USER);
+ cantEditProperty(recordDeclaredOne, ContentModel.PROP_DESCRIPTION, ADMIN_USER);
canEditProperty(recordDeclaredOne, RecordsManagementModel.PROP_LOCATION, ADMIN_USER);
// test normal user
diff --git a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/EditNonRecordsMetadataCapabilityUnitTest.java b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/EditNonRecordsMetadataCapabilityUnitTest.java
new file mode 100644
index 0000000000..e622588d5c
--- /dev/null
+++ b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/EditNonRecordsMetadataCapabilityUnitTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2005-2014 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+package org.alfresco.module.org_alfresco_module_rm.capability.impl;
+
+import static org.mockito.Mockito.when;
+
+import java.util.Set;
+
+import net.sf.acegisecurity.vote.AccessDecisionVoter;
+
+import org.alfresco.module.org_alfresco_module_rm.record.RecordServiceImpl;
+import org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+
+/**
+ * Edit non records metadata capability unit test
+ *
+ * @author Roy Wetherall
+ * @since 2.3
+ */
+public class EditNonRecordsMetadataCapabilityUnitTest extends BaseUnitTest
+{
+ /** mocked set */
+ @Mock private Set