SHA-1892 : Error appears when Consumer or Contributor is trying to create a link on a document

- After creating the link for a node, add the marker aspect (LINKED) to the original node as System user. 
   - Added test for case 
   - Added a new test for DocumentLinkServiceImplTest for updated fucntionality

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/DEV/5.2.N/root@132612 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Ramona Neamtu
2016-11-10 09:39:15 +00:00
parent da4cb3dc21
commit dbe2c44536
2 changed files with 111 additions and 38 deletions

View File

@@ -39,6 +39,7 @@ import org.alfresco.repo.policy.BehaviourFilter;
import org.alfresco.repo.policy.JavaBehaviour; import org.alfresco.repo.policy.JavaBehaviour;
import org.alfresco.repo.policy.PolicyComponent; import org.alfresco.repo.policy.PolicyComponent;
import org.alfresco.repo.search.QueryParameterDefImpl; import org.alfresco.repo.search.QueryParameterDefImpl;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.permissions.AccessDeniedException; import org.alfresco.repo.security.permissions.AccessDeniedException;
import org.alfresco.repo.site.SiteModel; import org.alfresco.repo.site.SiteModel;
import org.alfresco.service.cmr.coci.CheckOutCheckInService; import org.alfresco.service.cmr.coci.CheckOutCheckInService;
@@ -60,8 +61,8 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.extensions.surf.util.I18NUtil; import org.springframework.extensions.surf.util.I18NUtil;
/** /**
* Implementation of the document link service * Implementation of the document link service In addition to the document link
* In addition to the document link service, this class also provides a BeforeDeleteNodePolicy * service, this class also provides a BeforeDeleteNodePolicy
* *
* @author Ana Bozianu * @author Ana Bozianu
* @since 5.1 * @since 5.1
@@ -80,7 +81,7 @@ public class DocumentLinkServiceImpl implements DocumentLinkService, NodeService
/** Shallow search for nodes with a name pattern */ /** Shallow search for nodes with a name pattern */
private static final String XPATH_QUERY_NODE_NAME_MATCH = "./*[like(@cm:name, $cm:name, false)]"; private static final String XPATH_QUERY_NODE_NAME_MATCH = "./*[like(@cm:name, $cm:name, false)]";
/** Shallow search for links with a destination pattern */ /** Shallow search for links with a destination pattern */
private static final String XPATH_QUERY_LINK_DEST_MATCH = ".//*[like(@cm:destination, $cm:destination, false)]"; private static final String XPATH_QUERY_LINK_DEST_MATCH = ".//*[like(@cm:destination, $cm:destination, false)]";
@@ -88,7 +89,7 @@ public class DocumentLinkServiceImpl implements DocumentLinkService, NodeService
/* I18N labels */ /* I18N labels */
private static final String LINK_TO_LABEL = "doclink_service.link_to_label"; private static final String LINK_TO_LABEL = "doclink_service.link_to_label";
/** /**
* The initialise method. Register our policies. * The initialise method. Register our policies.
*/ */
@@ -124,13 +125,13 @@ public class DocumentLinkServiceImpl implements DocumentLinkService, NodeService
} }
@Override @Override
public NodeRef createDocumentLink(NodeRef source, NodeRef destination) public NodeRef createDocumentLink(final NodeRef source, NodeRef destination)
{ {
if(logger.isDebugEnabled()) if (logger.isDebugEnabled())
{ {
logger.debug("Creating document link. source: " + source + ", destination: " + destination); logger.debug("Creating document link. source: " + source + ", destination: " + destination);
} }
/* Validate input */ /* Validate input */
PropertyCheck.mandatory(this, "source", source); PropertyCheck.mandatory(this, "source", source);
PropertyCheck.mandatory(this, "destination", destination); PropertyCheck.mandatory(this, "destination", destination);
@@ -172,9 +173,9 @@ public class DocumentLinkServiceImpl implements DocumentLinkService, NodeService
ChildAssociationRef childRef = null; ChildAssociationRef childRef = null;
QName sourceType = nodeService.getType(source); QName sourceType = nodeService.getType(source);
if( checkOutCheckInService.isWorkingCopy(source) || nodeService.hasAspect(source, ContentModel.ASPECT_LOCKABLE)) if (checkOutCheckInService.isWorkingCopy(source) || nodeService.hasAspect(source, ContentModel.ASPECT_LOCKABLE))
{ {
throw new IllegalArgumentException( "Cannot perform operation since the node (id:" + source.getId() + ") is locked."); throw new IllegalArgumentException("Cannot perform operation since the node (id:" + source.getId() + ") is locked.");
} }
if (dictionaryService.isSubClass(sourceType, ContentModel.TYPE_CONTENT)) if (dictionaryService.isSubClass(sourceType, ContentModel.TYPE_CONTENT))
@@ -183,7 +184,8 @@ public class DocumentLinkServiceImpl implements DocumentLinkService, NodeService
childRef = nodeService.createNode(destination, ContentModel.ASSOC_CONTAINS, assocQName, ApplicationModel.TYPE_FILELINK, props); childRef = nodeService.createNode(destination, ContentModel.ASSOC_CONTAINS, assocQName, ApplicationModel.TYPE_FILELINK, props);
} }
else if (!dictionaryService.isSubClass(sourceType, SiteModel.TYPE_SITE) && dictionaryService.isSubClass(nodeService.getType(source), ContentModel.TYPE_FOLDER)) else if (!dictionaryService.isSubClass(sourceType, SiteModel.TYPE_SITE)
&& dictionaryService.isSubClass(nodeService.getType(source), ContentModel.TYPE_FOLDER))
{ {
// create Folder link node // create Folder link node
childRef = nodeService.createNode(destination, ContentModel.ASSOC_CONTAINS, assocQName, ApplicationModel.TYPE_FOLDERLINK, props); childRef = nodeService.createNode(destination, ContentModel.ASSOC_CONTAINS, assocQName, ApplicationModel.TYPE_FOLDERLINK, props);
@@ -193,16 +195,24 @@ public class DocumentLinkServiceImpl implements DocumentLinkService, NodeService
throw new IllegalArgumentException("unsupported source node type : " + nodeService.getType(source)); throw new IllegalArgumentException("unsupported source node type : " + nodeService.getType(source));
} }
//add linked aspect to the sourceNode // add linked aspect to the sourceNode - run as System
behaviourFilter.disableBehaviour(source, ContentModel.ASPECT_AUDITABLE); AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Object>()
try
{ {
nodeService.addAspect(source, ApplicationModel.ASPECT_LINKED, null); public Void doWork() throws Exception
} {
finally behaviourFilter.disableBehaviour(source, ContentModel.ASPECT_AUDITABLE);
{ try
behaviourFilter.enableBehaviour(source, ContentModel.ASPECT_AUDITABLE); {
} nodeService.addAspect(source, ApplicationModel.ASPECT_LINKED, null);
}
finally
{
behaviourFilter.enableBehaviour(source, ContentModel.ASPECT_AUDITABLE);
}
return null;
}
}, AuthenticationUtil.getSystemUserName());
return childRef.getChildRef(); return childRef.getChildRef();
} }
@@ -246,13 +256,13 @@ public class DocumentLinkServiceImpl implements DocumentLinkService, NodeService
return (NodeRef) nodeService.getProperty(linkNodeRef, ContentModel.PROP_LINK_DESTINATION); return (NodeRef) nodeService.getProperty(linkNodeRef, ContentModel.PROP_LINK_DESTINATION);
} }
@Override @Override
public List<NodeRef> getNodeLinks(NodeRef nodeRef) public List<NodeRef> getNodeLinks(NodeRef nodeRef)
{ {
/* Validate input */ /* Validate input */
PropertyCheck.mandatory(this, "nodeRef", nodeRef); PropertyCheck.mandatory(this, "nodeRef", nodeRef);
/* Get all links of the given nodeRef */ /* Get all links of the given nodeRef */
QueryParameterDefinition[] params = new QueryParameterDefinition[1]; QueryParameterDefinition[] params = new QueryParameterDefinition[1];
params[0] = new QueryParameterDefImpl(ContentModel.PROP_LINK_DESTINATION, dictionaryService.getDataType(DataTypeDefinition.NODE_REF), true, nodeRef.toString()); params[0] = new QueryParameterDefImpl(ContentModel.PROP_LINK_DESTINATION, dictionaryService.getDataType(DataTypeDefinition.NODE_REF), true, nodeRef.toString());
@@ -260,12 +270,12 @@ public class DocumentLinkServiceImpl implements DocumentLinkService, NodeService
List<NodeRef> nodeLinks = new ArrayList<NodeRef>(); List<NodeRef> nodeLinks = new ArrayList<NodeRef>();
List<NodeRef> nodeRefs; List<NodeRef> nodeRefs;
/* Search for links in all stores */ /* Search for links in all stores */
for(StoreRef store : nodeService.getStores()) for (StoreRef store : nodeService.getStores())
{ {
/* Get the root node */ /* Get the root node */
NodeRef rootNodeRef = nodeService.getRootNode(store); NodeRef rootNodeRef = nodeService.getRootNode(store);
/* Execute the query, retrieve links to the document*/ /* Execute the query, retrieve links to the document */
nodeRefs = searchService.selectNodes(rootNodeRef, XPATH_QUERY_LINK_DEST_MATCH, params, namespaceService, true); nodeRefs = searchService.selectNodes(rootNodeRef, XPATH_QUERY_LINK_DEST_MATCH, params, namespaceService, true);
nodeLinks.addAll(nodeRefs); nodeLinks.addAll(nodeRefs);
} }
@@ -337,25 +347,31 @@ public class DocumentLinkServiceImpl implements DocumentLinkService, NodeService
public void beforeDeleteLinkNode(NodeRef linkNodeRef) public void beforeDeleteLinkNode(NodeRef linkNodeRef)
{ {
// NodeRef linkNodeRef = childAssocRef.getChildRef(); final NodeRef nodeRef = getLinkDestination(linkNodeRef);
NodeRef nodeRef = getLinkDestination(linkNodeRef);
List<NodeRef> nodeRefLinks = getNodeLinks(nodeRef); List<NodeRef> nodeRefLinks = getNodeLinks(nodeRef);
if (nodeRefLinks.size() == 1 && nodeRefLinks.contains(linkNodeRef)) if (nodeRefLinks.size() == 1 && nodeRefLinks.contains(linkNodeRef))
{ {
behaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE); AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Object>()
behaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_LOCKABLE);
try
{ {
// remove linked aspect to the sourceNode public Void doWork() throws Exception
nodeService.removeAspect(nodeRef, ApplicationModel.ASPECT_LINKED); {
} behaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE);
finally behaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_LOCKABLE);
{ try
behaviourFilter.enableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE); {
behaviourFilter.enableBehaviour(nodeRef, ContentModel.ASPECT_LOCKABLE); nodeService.removeAspect(nodeRef, ApplicationModel.ASPECT_LINKED);
} }
finally
{
behaviourFilter.enableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE);
behaviourFilter.enableBehaviour(nodeRef, ContentModel.ASPECT_LOCKABLE);
}
return null;
}
}, AuthenticationUtil.getSystemUserName());
} }
} }
@@ -393,4 +409,4 @@ public class DocumentLinkServiceImpl implements DocumentLinkService, NodeService
{ {
this.behaviourFilter = behaviourFilter; this.behaviourFilter = behaviourFilter;
} }
} }

View File

@@ -39,6 +39,7 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; 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.service.ServiceRegistry; import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.coci.CheckOutCheckInService;
import org.alfresco.service.cmr.model.FileFolderService; import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.repository.DeleteLinksStatusReport; import org.alfresco.service.cmr.repository.DeleteLinksStatusReport;
import org.alfresco.service.cmr.repository.DocumentLinkService; import org.alfresco.service.cmr.repository.DocumentLinkService;
@@ -79,8 +80,10 @@ public class DocumentLinkServiceImplTest extends TestCase
private SiteService siteService; private SiteService siteService;
private FileFolderService fileFolderService; private FileFolderService fileFolderService;
private NodeService nodeService; private NodeService nodeService;
private CheckOutCheckInService cociService;
// nodes the test user has read/write permission to // nodes the test user has read/write permission to
private NodeRef site1;
private NodeRef site1File1; private NodeRef site1File1;
private NodeRef site1File2; // do not create links of this file private NodeRef site1File2; // do not create links of this file
private NodeRef site1Folder1; private NodeRef site1Folder1;
@@ -108,6 +111,7 @@ public class DocumentLinkServiceImplTest extends TestCase
siteService = serviceRegistry.getSiteService(); siteService = serviceRegistry.getSiteService();
fileFolderService = serviceRegistry.getFileFolderService(); fileFolderService = serviceRegistry.getFileFolderService();
nodeService = serviceRegistry.getNodeService(); nodeService = serviceRegistry.getNodeService();
cociService = serviceRegistry.getCheckOutCheckInService();
// Start the transaction // Start the transaction
txn = transactionService.getUserTransaction(); txn = transactionService.getUserTransaction();
@@ -125,7 +129,7 @@ public class DocumentLinkServiceImplTest extends TestCase
* Create the working test root 1 to which the user has read/write * Create the working test root 1 to which the user has read/write
* permission * permission
*/ */
NodeRef site1 = siteService.createSite("site1", GUID.generate(), "myTitle", "myDescription", SiteVisibility.PUBLIC).getNodeRef(); site1 = siteService.createSite("site1", GUID.generate(), "myTitle", "myDescription", SiteVisibility.PUBLIC).getNodeRef();
permissionService.setPermission(site1, TEST_USER, PermissionService.ALL_PERMISSIONS, true); permissionService.setPermission(site1, TEST_USER, PermissionService.ALL_PERMISSIONS, true);
site1Folder1 = fileFolderService.create(site1, site1Folder1Name, ContentModel.TYPE_FOLDER).getNodeRef(); site1Folder1 = fileFolderService.create(site1, site1Folder1Name, ContentModel.TYPE_FOLDER).getNodeRef();
site1File1 = fileFolderService.create(site1Folder1, site1File1Name, ContentModel.TYPE_CONTENT).getNodeRef(); site1File1 = fileFolderService.create(site1Folder1, site1File1Name, ContentModel.TYPE_CONTENT).getNodeRef();
@@ -336,4 +340,57 @@ public class DocumentLinkServiceImplTest extends TestCase
assertEquals(ex.getClass(), AccessDeniedException.class); assertEquals(ex.getClass(), AccessDeniedException.class);
} }
/**
* Tests the creation of a Site link, an locked node or a checked out node
*
* @throws Exception
*/
public void testCreateLinksNotAllowed() throws Exception
{
NodeRef invalidLinkNodeRef;
// Create link for Site
try
{
invalidLinkNodeRef = documentLinkService.createDocumentLink(site1, site2Folder1);
fail("unsupported source node type : " + nodeService.getType(site1));
}
catch (IllegalArgumentException e)
{
// Expected
}
NodeRef firstLinkNodeRef = documentLinkService.createDocumentLink(site1File1, site1Folder1);
assertEquals(true, nodeService.hasAspect(site1File1, ApplicationModel.ASPECT_LINKED));
// Create link for working copy
NodeRef workingCopyNodeRef = cociService.checkout(site1File1);
try
{
invalidLinkNodeRef = documentLinkService.createDocumentLink(workingCopyNodeRef, site1Folder1);
fail("Cannot perform operation since the node (id:" + workingCopyNodeRef.getId() + ") is locked.");
}
catch (IllegalArgumentException e)
{
// Expected
}
// Create link for locked node (original)
try
{
invalidLinkNodeRef = documentLinkService.createDocumentLink(site1File1, site1Folder1);
fail("Cannot perform operation since the node (id:" + site1File1.getId() + ") is locked.");
}
catch (IllegalArgumentException e)
{
// Expected
}
// Even the node is locked, user can delete previous created links
nodeService.deleteNode(firstLinkNodeRef);
assertEquals(false, nodeService.hasAspect(site1File1, ApplicationModel.ASPECT_LINKED));
cociService.cancelCheckout(workingCopyNodeRef);
}
} }