RM-1440: Edit Non Record Metadata capability is required to file a record

* can now file an electronic record with only CreateRecord capability
  * tidied up some of the policy code as I went
  * integration test
  * non-electronic records to follow



git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/HEAD@73961 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Roy Wetherall
2014-06-17 06:44:19 +00:00
parent 901a8fd8c8
commit c9223cb510
18 changed files with 361 additions and 142 deletions

View File

@@ -61,7 +61,7 @@ public class ChangeOrDeleteReferencesCapability extends DeclarativeCapability
else
{
if (checkConditions(source) &&
checkPermissions(source))
checkPermissions(source))
{
return AccessDecisionVoter.ACCESS_GRANTED;
}

View File

@@ -67,19 +67,18 @@ public class CreateCapability extends DeclarativeCapability
@Override
public int evaluate(NodeRef nodeRef)
{
return evaluate(nodeRef, null, null, null);
return evaluate(nodeRef, null, null);
}
/**
* Evaluate capability.
*
* @param destination
* @param linkee
* @param type
* @param assocType
* @param destination destination node reference
* @param linkee linkee node reference, can be null
* @param assocType association type, can be null
* @return
*/
public int evaluate(NodeRef destination, NodeRef linkee, QName type, QName assocType)
public int evaluate(NodeRef destination, NodeRef linkee, QName assocType)
{
if (linkee != null)
{
@@ -95,8 +94,9 @@ public class CreateCapability extends DeclarativeCapability
{
if (linkee == null)
{
if (recordService.isRecord(destination) && !recordService.isDeclared(destination) &&
permissionService.hasPermission(destination, RMPermissionModel.FILE_RECORDS) == AccessStatus.ALLOWED)
if (recordService.isRecord(destination) &&
!recordService.isDeclared(destination) &&
permissionService.hasPermission(destination, RMPermissionModel.FILE_RECORDS) == AccessStatus.ALLOWED)
{
return AccessDecisionVoter.ACCESS_GRANTED;
}

View File

@@ -1,50 +0,0 @@
/*
* Copyright (C) 2005-2014 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.module.org_alfresco_module_rm.capability.impl;
import java.io.Serializable;
import java.util.Map;
import org.alfresco.module.org_alfresco_module_rm.capability.declarative.DeclarativeCompositeCapability;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName;
/**
* Update properties capability.
*
* @author andyh
*/
public class UpdatePropertiesCapability extends DeclarativeCompositeCapability
{
/**
* Evaluate capability, taking into account the protected properties.
*
* @param nodeRef node reference
* @param properties updated properties, if no null
*/
public int evaluate(NodeRef nodeRef, Map<QName, Serializable> properties)
{
// if ((properties != null) && (voter.includesProtectedPropertyChange(nodeRef, properties)))
// {
// return AccessDecisionVoter.ACCESS_DENIED;
// }
return evaluate(nodeRef);
}
}

View File

@@ -30,11 +30,14 @@ import org.alfresco.service.namespace.NamespacePrefixResolver;
import org.alfresco.service.namespace.QName;
/**
* RM security configuration definition.
*
* @author Roy Wetherall
* @since 2.1
*/
public class ConfigAttributeDefinition
{
/** allowable RM security configurations */
public static final String RM = "RM";
public static final String RM_ALLOW = "RM_ALLOW";
public static final String RM_DENY = "RM_DENY";
@@ -42,18 +45,30 @@ public class ConfigAttributeDefinition
public static final String RM_ABSTAIN = "RM_ABSTAIN";
public static final String RM_QUERY = "RM_QUERY";
/** security type */
private String typeString;
/** policy name */
private String policyName;
/** simple permission reference */
private SimplePermissionReference required;
/** parameter position map */
private Map<Integer, Integer> parameters = new HashMap<Integer, Integer>(2, 1.0f);
/** is parent */
private boolean parent = false;
/**
* Default constructor
*
* @param attr configuration attribute instance
* @param namespacePrefixResolver namespace prefix resolver
*/
public ConfigAttributeDefinition(ConfigAttribute attr, NamespacePrefixResolver namespacePrefixResolver)
{
// tokenize configuration string
StringTokenizer st = new StringTokenizer(attr.getAttribute(), ".", false);
if (st.countTokens() < 1)
{
@@ -61,8 +76,13 @@ public class ConfigAttributeDefinition
}
typeString = st.nextToken();
if (!(typeString.equals(RM) || typeString.equals(RM_ALLOW) || typeString.equals(RM_CAP) || typeString.equals(RM_DENY) || typeString.equals(RM_QUERY) || typeString
.equals(RM_ABSTAIN)))
// check that the configuration is valid
if (!(typeString.equals(RM) ||
typeString.equals(RM_ALLOW) ||
typeString.equals(RM_CAP) ||
typeString.equals(RM_DENY) ||
typeString.equals(RM_QUERY) ||
typeString.equals(RM_ABSTAIN)))
{
throw new ACLEntryVoterException("Invalid type: must be ACL_NODE, ACL_PARENT or ACL_ALLOW");
}

View File

@@ -31,18 +31,25 @@ public class CreatePolicy extends AbstractBasePolicy
Class[] params,
ConfigAttributeDefinition cad)
{
NodeRef destination = getTestNode(invocation, params, cad.getParameters().get(0), cad.isParent());
QName type = getType(invocation, params, cad.getParameters().get(1), cad.isParent());
// linkee is not null for creating secondary child assocs
NodeRef linkee = getTestNode(invocation, params, cad.getParameters().get(1), cad.isParent());
NodeRef linkee = null;
QName assocType = null;
if(cad.getParameters().size() > 2)
// get the destination node
NodeRef destination = getTestNode(invocation, params, cad.getParameters().get(0), cad.isParent());
if (cad.getParameters().size() > 1)
{
assocType = getType(invocation, params, cad.getParameters().get(2), cad.isParent());
// get the linkee when present
linkee = getTestNode(invocation, params, cad.getParameters().get(1), cad.isParent());
// get the assoc type
if(cad.getParameters().size() > 2)
{
assocType = getType(invocation, params, cad.getParameters().get(2), cad.isParent());
}
}
return ((CreateCapability)capabilityService.getCapability("Create")).evaluate(destination, linkee, type, assocType);
return ((CreateCapability)capabilityService.getCapability("Create")).evaluate(destination, linkee, assocType);
}
}

View File

@@ -18,13 +18,7 @@
*/
package org.alfresco.module.org_alfresco_module_rm.capability.policy;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import org.alfresco.module.org_alfresco_module_rm.capability.impl.UpdatePropertiesCapability;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName;
import org.aopalliance.intercept.MethodInvocation;
public class UpdatePropertiesPolicy extends AbstractBasePolicy
@@ -35,29 +29,8 @@ public class UpdatePropertiesPolicy extends AbstractBasePolicy
Class[] params,
ConfigAttributeDefinition cad)
{
NodeRef updatee = getTestNode(invocation, params, cad.getParameters().get(0), cad.isParent());
Map<QName, Serializable> properties;
if (QName.class.isAssignableFrom(params[cad.getParameters().get(1)]))
{
// single update/delete
// We have a specific property
QName propertyQName = getQName(invocation, params, cad.getParameters().get(1));
properties = new HashMap<QName, Serializable>(1, 1.0f);
if (cad.getParameters().size() > 2)
{
properties.put(propertyQName, getProperty(invocation, params, cad.getParameters().get(2)));
}
else
{
properties.put(propertyQName, null);
}
}
else
{
properties = getProperties(invocation, params, cad.getParameters().get(1));
}
return ((UpdatePropertiesCapability)capabilityService.getCapability("UpdateProperties")).evaluate(updatee, properties);
NodeRef nodeRef = getTestNode(invocation, params, cad.getParameters().get(0), cad.isParent());
return capabilityService.getCapability("UpdateProperties").evaluate(nodeRef);
}
}

View File

@@ -56,6 +56,14 @@ public interface RecordService
* Disables the property editable check.
*/
void disablePropertyEditableCheck();
/**
* Disables the property editable check for a given node in this transaction only.
*
* @param nodeRef node reference
* @since 2.2
*/
void disablePropertyEditableCheck(NodeRef nodeRef);
/**
* Enables the property editable check. By default this is always enabled.
@@ -138,12 +146,13 @@ public interface RecordService
* Creates a new document in the unfiled records container if the given node reference is a file plan
* node reference otherwise the node reference will be used as the destination for the new record.
*
* @param nodeRef
* @param name
* @param type
* @param properties
* @param parent parent node reference
* @param name name of the new record
* @param type content type, cm:content if null
* @param properties properties
* @param reader content reader
*/
NodeRef createRecord(NodeRef nodeRef, String name, QName type, Map<QName, Serializable> properties, ContentReader reader);
NodeRef createRecordFromContent(NodeRef parent, String name, QName type, Map<QName, Serializable> properties, ContentReader reader);
/**
* Indicates whether the record is filed or not

View File

@@ -109,6 +109,9 @@ public class RecordServiceImpl extends BaseBehaviourBean
{
/** Logger */
private static Log logger = LogFactory.getLog(RecordServiceImpl.class);
/** transation data key */
private static final String IGNORE_ON_UPDATE = "ignoreOnUpdate";
/** I18N */
private static final String MSG_NODE_HAS_ASPECT = "rm.service.node-has-aspect";
@@ -335,8 +338,6 @@ public class RecordServiceImpl extends BaseBehaviourBean
onDeleteDeclaredRecordLink);
}
/**
* @see org.alfresco.repo.node.NodeServicePolicies.OnRemoveAspectPolicy#onRemoveAspect(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName)
*/
@@ -510,6 +511,15 @@ public class RecordServiceImpl extends BaseBehaviourBean
{
getBehaviour("onUpdateProperties").disable();
}
/**
* @see org.alfresco.module.org_alfresco_module_rm.record.RecordService#disablePropertyEditableCheck(org.alfresco.service.cmr.repository.NodeRef)
*/
public void disablePropertyEditableCheck(NodeRef nodeRef)
{
Set<NodeRef> ignoreOnUpdate = TransactionalResourceHelper.getSet(IGNORE_ON_UPDATE);
ignoreOnUpdate.add(nodeRef);
}
/**
* @see org.alfresco.module.org_alfresco_module_rm.record.RecordService#enablePropertyEditableCheck()
@@ -537,7 +547,8 @@ public class RecordServiceImpl extends BaseBehaviourBean
if (AuthenticationUtil.getFullyAuthenticatedUser() != null &&
!AuthenticationUtil.isRunAsUserTheSystemUser() &&
nodeService.exists(nodeRef) &&
isRecord(nodeRef))
isRecord(nodeRef) &&
!TransactionalResourceHelper.getSet(IGNORE_ON_UPDATE).contains(nodeRef))
{
for (Map.Entry<QName, Serializable> entry : after.entrySet())
{
@@ -560,9 +571,10 @@ public class RecordServiceImpl extends BaseBehaviourBean
// otherwise
propertyUnchanged = EqualsHelper.nullSafeEquals(beforeValue, afterValue);
}
if (!propertyUnchanged &&
!isPropertyEditable(nodeRef, property))
!(ContentModel.PROP_CONTENT.equals(property) && beforeValue == null) &&
!isPropertyEditable(nodeRef, property))
{
// the user can't edit the record property
throw new ModelAccessDeniedException(
@@ -814,19 +826,21 @@ public class RecordServiceImpl extends BaseBehaviourBean
}
/**
* @see org.alfresco.module.org_alfresco_module_rm.record.RecordService#createRecord(org.alfresco.service.cmr.repository.NodeRef, java.lang.String, org.alfresco.service.namespace.QName, java.util.Map, org.alfresco.service.cmr.repository.ContentReader)
* @see org.alfresco.module.org_alfresco_module_rm.record.RecordService#createNewRecord(org.alfresco.service.cmr.repository.NodeRef, java.lang.String, org.alfresco.service.namespace.QName, java.util.Map, org.alfresco.service.cmr.repository.ContentReader)
*/
@Override
public NodeRef createRecord(NodeRef nodeRef, String name, QName type, Map<QName, Serializable> properties, ContentReader reader)
public NodeRef createRecordFromContent(NodeRef parent, String name, QName type, Map<QName, Serializable> properties, ContentReader reader)
{
ParameterCheck.mandatory("nodeRef", nodeRef);
ParameterCheck.mandatory("nodeRef", parent);
ParameterCheck.mandatory("name", name);
NodeRef destination = nodeRef;
if (isFilePlan(nodeRef))
NodeRef record = null;
NodeRef destination = parent;
if (isFilePlan(parent))
{
// get the unfiled record container for the file plan
destination = filePlanService.getUnfiledContainer(nodeRef);
destination = filePlanService.getUnfiledContainer(parent);
if (destination == null)
{
throw new AlfrescoRuntimeException("Unable to create record, because unfiled container could not be found.");
@@ -838,24 +852,35 @@ public class RecordServiceImpl extends BaseBehaviourBean
{
type = ContentModel.TYPE_CONTENT;
}
// TODO ensure that the type is a sub-type of cm:content
// create the new record
NodeRef record = fileFolderService.create(destination, name, type).getNodeRef();
// set the properties
if (properties != null)
else if (!dictionaryService.isSubClass(type, ContentModel.TYPE_CONTENT))
{
nodeService.addProperties(record, properties);
throw new AlfrescoRuntimeException("Record can only be created from a sub-type of cm:content.");
}
// set the content
if (reader != null)
disablePropertyEditableCheck();
try
{
ContentWriter writer = fileFolderService.getWriter(record);
writer.setEncoding(reader.getEncoding());
writer.setMimetype(reader.getMimetype());
writer.putContent(reader);
// create the new record
record = fileFolderService.create(destination, name, type).getNodeRef();
// set the properties
if (properties != null)
{
nodeService.addProperties(record, properties);
}
// set the content
if (reader != null)
{
ContentWriter writer = fileFolderService.getWriter(record);
writer.setEncoding(reader.getEncoding());
writer.setMimetype(reader.getMimetype());
writer.putContent(reader);
}
}
finally
{
enablePropertyEditableCheck();
}
// Check if the "record" aspect has been applied already.
@@ -882,6 +907,7 @@ public class RecordServiceImpl extends BaseBehaviourBean
ParameterCheck.mandatory("document", document);
ruleService.disableRules();
disablePropertyEditableCheck();
try
{
// get the record id
@@ -921,6 +947,7 @@ public class RecordServiceImpl extends BaseBehaviourBean
finally
{
ruleService.enableRules();
enablePropertyEditableCheck();
}
}

View File

@@ -116,10 +116,10 @@ public class ReportServiceImpl extends ServiceBaseImpl
ParameterCheck.mandatory("nodeRef", nodeRef);
ParameterCheck.mandatory("report", report);
return recordService.createRecord(nodeRef,
report.getReportName(),
report.getReportType(),
report.getReportProperties(),
report.getReportContent());
return recordService.createRecordFromContent(nodeRef,
report.getReportName(),
report.getReportType(),
report.getReportProperties(),
report.getReportContent());
}
}

View File

@@ -0,0 +1,61 @@
/**
*
*/
package org.alfresco.repo.model.filefolder;
import org.alfresco.module.org_alfresco_module_rm.record.RecordService;
import org.alfresco.service.cmr.model.FileExistsException;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName;
/**
* @author Roy Wetherall
*/
public class ExtendedFileFolderServiceImpl extends FileFolderServiceImpl
{
protected RecordService recordService;
public void setRecordService(RecordService recordService)
{
this.recordService = recordService;
}
@Override
public FileInfo create(NodeRef parentNodeRef, String name, QName typeQName) throws FileExistsException
{
FileInfo result = null;
recordService.disablePropertyEditableCheck();
try
{
result = super.create(parentNodeRef, name, typeQName);
}
finally
{
recordService.enablePropertyEditableCheck();
recordService.disablePropertyEditableCheck(result.getNodeRef());
}
return result;
}
@Override
public FileInfo create(NodeRef parentNodeRef, String name, QName typeQName, QName assocQName) throws FileExistsException
{
FileInfo result = null;
recordService.disablePropertyEditableCheck();
try
{
result = super.create(parentNodeRef, name, typeQName, assocQName);
}
finally
{
recordService.enablePropertyEditableCheck();
recordService.disablePropertyEditableCheck(result.getNodeRef());
}
return result;
}
}