From 0d077586dabc4b8f60182d577228e54c4c39605f Mon Sep 17 00:00:00 2001 From: Ancuta Morarasu Date: Wed, 11 May 2016 11:18:35 +0000 Subject: [PATCH] Merged HEAD (5.2) to 5.2.N (5.2.1) 126433 jkaabimofrad: Merged FILE-FOLDER-API (5.2.0) to HEAD (5.2) 121874 jvonka: (Quick) Shared Links API - improvements - permission checks, return sharedByUser etc - updated design notes & JIRAs to be in-sync - TODO review api/impl (& add api tests) RA-775, RA-773, RA-750, RA-708, RA-776 git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/DEV/5.2.N/root@126779 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- config/alfresco/public-rest-context.xml | 2 + .../rest/api/impl/QuickShareLinksImpl.java | 173 +++++++++++++----- .../rest/api/model/QuickShareLink.java | 13 ++ 3 files changed, 145 insertions(+), 43 deletions(-) diff --git a/config/alfresco/public-rest-context.xml b/config/alfresco/public-rest-context.xml index 229420494d..5a543416e3 100644 --- a/config/alfresco/public-rest-context.xml +++ b/config/alfresco/public-rest-context.xml @@ -486,6 +486,8 @@ + + diff --git a/source/java/org/alfresco/rest/api/impl/QuickShareLinksImpl.java b/source/java/org/alfresco/rest/api/impl/QuickShareLinksImpl.java index e2ee7da22f..50b9db2a10 100644 --- a/source/java/org/alfresco/rest/api/impl/QuickShareLinksImpl.java +++ b/source/java/org/alfresco/rest/api/impl/QuickShareLinksImpl.java @@ -19,6 +19,8 @@ package org.alfresco.rest.api.impl; import org.alfresco.model.QuickShareModel; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.permissions.AccessDeniedException; import org.alfresco.repo.tenant.TenantUtil; import org.alfresco.rest.api.Nodes; import org.alfresco.rest.api.QuickShareLinks; @@ -26,6 +28,8 @@ import org.alfresco.rest.api.model.ContentInfo; import org.alfresco.rest.api.model.QuickShareLink; import org.alfresco.rest.api.model.UserInfo; import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException; +import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException; +import org.alfresco.rest.framework.core.exceptions.NotFoundException; import org.alfresco.rest.framework.core.exceptions.PermissionDeniedException; import org.alfresco.rest.framework.resource.content.BinaryResource; import org.alfresco.rest.framework.resource.parameters.Parameters; @@ -36,6 +40,8 @@ import org.alfresco.service.cmr.repository.InvalidNodeRefException; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.cmr.security.PersonService; import org.alfresco.util.Pair; import org.alfresco.util.ParameterCheck; import org.apache.commons.logging.Log; @@ -50,6 +56,8 @@ import java.util.Map; /** * Centralises access to quick share services and maps between representations. * + * TODO - if QuickShare is disabled should we return 403 (as below) or 404 (eg. when accessing a link) ? + * * @author janv * * @since publicapi1.0 @@ -64,6 +72,8 @@ public class QuickShareLinksImpl implements QuickShareLinks, InitializingBean private QuickShareService quickShareService; private Nodes nodes; private NodeService nodeService; + private PersonService personService; + private AuthorityService authorityService; public void setQuickShareService(QuickShareService quickShareService) { @@ -80,6 +90,16 @@ public class QuickShareLinksImpl implements QuickShareLinks, InitializingBean this.nodeService = nodeService; } + public void setPersonService(PersonService personService) + { + this.personService = personService; + } + + public void setAuthorityService(AuthorityService authorityService) + { + this.authorityService = authorityService; + } + public void setEnabled(boolean enabled) { this.enabled = enabled; @@ -91,26 +111,25 @@ public class QuickShareLinksImpl implements QuickShareLinks, InitializingBean ParameterCheck.mandatory("quickShareService", this.quickShareService); ParameterCheck.mandatory("nodes", this.nodes); ParameterCheck.mandatory("nodeService", this.nodeService); + ParameterCheck.mandatory("personService", this.personService); + ParameterCheck.mandatory("authorityService", this.authorityService); } /** * Returns limited metadata regarding the shared (content) link. - * + *

* Note: does *not* require authenticated access for (public) shared link. */ public QuickShareLink readById(String sharedId, Parameters parameters) { - if (! enabled) - { - throw new PermissionDeniedException(DISABLED); - } + checkEnabled(); return getQuickShareInfo(sharedId); } /** * Download content via shared link. - * + *

* Note: does *not* require authenticated access for (public) shared link. * * @param sharedId @@ -120,10 +139,8 @@ public class QuickShareLinksImpl implements QuickShareLinks, InitializingBean */ public BinaryResource readProperty(String sharedId, final Parameters parameters) throws EntityNotFoundException { - if (! enabled) - { - throw new PermissionDeniedException(DISABLED); - } + checkEnabled(); + checkValidShareId(sharedId); try { @@ -137,7 +154,7 @@ public class QuickShareLinksImpl implements QuickShareLinks, InitializingBean public BinaryResource doWork() throws Exception { // belt-and-braces (similar to QuickShareContentGet) - if (! nodeService.hasAspect(nodeRef, QuickShareModel.ASPECT_QSHARE)) + if (!nodeService.hasAspect(nodeRef, QuickShareModel.ASPECT_QSHARE)) { throw new InvalidNodeRefException(nodeRef); } @@ -148,30 +165,42 @@ public class QuickShareLinksImpl implements QuickShareLinks, InitializingBean } catch (InvalidSharedIdException ex) { - logger.warn("Unable to find: "+sharedId); - throw new EntityNotFoundException("Unable to find: "+sharedId); + logger.warn("Unable to find: " + sharedId); + throw new EntityNotFoundException("Unable to find: " + sharedId); } - catch (InvalidNodeRefException inre){ - logger.warn("Unable to find: "+sharedId+" ["+inre.getNodeRef()+"]"); - throw new EntityNotFoundException("Unable to find: "+sharedId); + catch (InvalidNodeRefException inre) + { + logger.warn("Unable to find: " + sharedId + " [" + inre.getNodeRef() + "]"); + throw new EntityNotFoundException("Unable to find: " + sharedId); } } /** * Delete the shared link. - * + *

* Once deleted, the shared link will no longer exist hence get/download will no longer work (ie. return 404). * If the link is later re-created then a new unique shared id will be generated. - * + *

* Requires authenticated access. * * @param sharedId String id of the quick share */ public void delete(String sharedId, Parameters parameters) { - if (! enabled) + checkEnabled(); + checkValidShareId(sharedId); + + NodeRef nodeRef = quickShareService.getTenantNodeRefFromSharedId(sharedId).getSecond(); + String currentUser = AuthenticationUtil.getFullyAuthenticatedUser(); + + // TODO site check - see ACE-XXX + //String siteName = getSiteName(nodeRef); + + String sharedBy = (String) nodeService.getProperty(nodeRef, QuickShareModel.PROP_QSHARE_SHAREDBY); + + if ((!currentUser.equals(sharedBy)) && (!authorityService.isAdminAuthority(currentUser))) { - throw new PermissionDeniedException(DISABLED); + throw new PermissionDeniedException("Can't perform unshare action: " + sharedId); } try @@ -180,18 +209,19 @@ public class QuickShareLinksImpl implements QuickShareLinks, InitializingBean } catch (InvalidSharedIdException ex) { - logger.warn("Unable to find: "+sharedId); - throw new EntityNotFoundException("Unable to find: "+sharedId); + logger.warn("Unable to find: " + sharedId); + throw new EntityNotFoundException("Unable to find: " + sharedId); } - catch (InvalidNodeRefException inre){ - logger.warn("Unable to find: "+sharedId+" ["+inre.getNodeRef()+"]"); - throw new EntityNotFoundException("Unable to find: "+sharedId); + catch (InvalidNodeRefException inre) + { + logger.warn("Unable to find: " + sharedId + " [" + inre.getNodeRef() + "]"); + throw new EntityNotFoundException("Unable to find: " + sharedId); } } /** * Create quick share. - * + *

* Requires authenticated access. * * @param nodeIds @@ -200,15 +230,37 @@ public class QuickShareLinksImpl implements QuickShareLinks, InitializingBean */ public List create(List nodeIds, Parameters parameters) { + checkEnabled(); + List result = new ArrayList<>(nodeIds.size()); for (QuickShareLink qs : nodeIds) { String nodeId = qs.getNodeId(); - QuickShareDTO qsDto = quickShareService.shareContent(new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, nodeId)); - // TODO should we skip errors (eg. broken share) ? - result.add(getQuickShareInfo(qsDto.getId())); + if (nodeId == null) + { + throw new InvalidArgumentException("A valid nodeId must be specified !"); + } + + NodeRef nodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, nodeId); + + try + { + // Note: this throws AccessDeniedException (=> 403) via QuickShareService (when NodeService tries to getAspects) + QuickShareDTO qsDto = quickShareService.shareContent(nodeRef); + result.add(getQuickShareInfo(qsDto.getId())); + } + catch (AccessDeniedException ade) + { + logger.warn("Unable to create shared link: [" + nodeRef + "]"); + throw new PermissionDeniedException("Unable to create shared link: " + nodeId); + } + catch (InvalidNodeRefException inre) + { + logger.warn("Unable to create shared link: [" + nodeRef + "]"); + throw new EntityNotFoundException("Unable to create shared link: " + nodeId); + } } return result; @@ -216,35 +268,70 @@ public class QuickShareLinksImpl implements QuickShareLinks, InitializingBean private QuickShareLink getQuickShareInfo(String sharedId) { + checkValidShareId(sharedId); + try { - Map map = (Map)quickShareService.getMetaData(sharedId).get("item"); + Map map = (Map) quickShareService.getMetaData(sharedId).get("item"); - String nodeId = new NodeRef((String)map.get("nodeRef")).getId(); + NodeRef nodeRef = new NodeRef((String) map.get("nodeRef")); - ContentInfo contentInfo = new ContentInfo((String)map.get("mimetype"), null, (Long)map.get("size"), null); + ContentInfo contentInfo = new ContentInfo((String) map.get("mimetype"), null, (Long) map.get("size"), null); - // note: we do not return modifier user id (to be consistent with v0 internal - limited disclosure) - UserInfo modifier = new UserInfo(null,(String)map.get("modifierFirstName"), (String)map.get("modifierLastName")); + // note: we do not currently return userids (to be consistent with v0 internal - limited disclosure) + UserInfo modifiedByUser = new UserInfo(null, (String) map.get("modifierFirstName"), (String) map.get("modifierLastName")); + // TODO review - limit to authenticated users ? (not exposed by V0 but needed for "find") + UserInfo sharedByUser = null; + String sharedByUserId = (String) nodeService.getProperty(nodeRef, QuickShareModel.PROP_QSHARE_SHAREDBY); + if (sharedByUserId != null) + { + NodeRef pRef = personService.getPerson(sharedByUserId); + if (pRef != null) + { + PersonService.PersonInfo pInfo = personService.getPerson(pRef); + if (pInfo != null) + { + sharedByUser = new UserInfo(null, pInfo.getFirstName(), pInfo.getLastName()); + } + } + } // TODO other "properties" (if needed) - eg. cm:title, cm:lastThumbnailModificationData, ... thumbnail info ... - QuickShareLink qs = new QuickShareLink(sharedId, nodeId); - qs.setName((String)map.get("name")); + QuickShareLink qs = new QuickShareLink(sharedId, nodeRef.getId()); + qs.setName((String) map.get("name")); qs.setContent(contentInfo); - qs.setModifiedAt((Date)map.get("modified")); - qs.setModifiedByUser(modifier); + qs.setModifiedAt((Date) map.get("modified")); + qs.setModifiedByUser(modifiedByUser); + qs.setSharedByUser(sharedByUser); return qs; } catch (InvalidSharedIdException ex) { - logger.warn("Unable to find: "+sharedId); - throw new EntityNotFoundException("Unable to find: "+sharedId); + logger.warn("Unable to find: " + sharedId); + throw new EntityNotFoundException("Unable to find: " + sharedId); } - catch (InvalidNodeRefException inre){ - logger.warn("Unable to find: "+sharedId+" ["+inre.getNodeRef()+"]"); - throw new EntityNotFoundException("Unable to find: "+sharedId); + catch (InvalidNodeRefException inre) + { + logger.warn("Unable to find: " + sharedId + " [" + inre.getNodeRef() + "]"); + throw new EntityNotFoundException("Unable to find: " + sharedId); + } + } + + private void checkEnabled() + { + if (!enabled) + { + throw new NotFoundException(DISABLED); + } + } + + private void checkValidShareId(String sharedId) + { + if (sharedId==null) + { + throw new InvalidArgumentException("A valid sharedId must be specified !"); } } } \ No newline at end of file diff --git a/source/java/org/alfresco/rest/api/model/QuickShareLink.java b/source/java/org/alfresco/rest/api/model/QuickShareLink.java index 6f2b52b632..95d84cf4b2 100644 --- a/source/java/org/alfresco/rest/api/model/QuickShareLink.java +++ b/source/java/org/alfresco/rest/api/model/QuickShareLink.java @@ -46,6 +46,8 @@ public class QuickShareLink protected Date modifiedAt; protected UserInfo modifiedByUser; + protected UserInfo sharedByUser; + public QuickShareLink() { } @@ -112,6 +114,16 @@ public class QuickShareLink this.modifiedByUser = modifiedByUser; } + public UserInfo getSharedByUser() + { + return sharedByUser; + } + + public void setSharedByUser(UserInfo sharedByUser) + { + this.sharedByUser = sharedByUser; + } + // eg. for debug logging etc @Override public String toString() @@ -122,6 +134,7 @@ public class QuickShareLink sb.append(", name=").append(getName()); sb.append(", modifiedAt=").append(getModifiedAt()); sb.append(", modifiedByUser=").append(getModifiedByUser()); + sb.append(", sharedByUser=").append(getSharedByUser()); sb.append(", content=").append(getContent()); sb.append("]"); return sb.toString();