Merged HEAD (5.2) to 5.2.N (5.2.1)

126518 jkaabimofrad: Merged FILE-FOLDER-API (5.2.0) to HEAD (5.2)
      122954 jvonka: (Quick) Shared Links API - add optional select/include of "allowableOperations" when authenticated and getting shared link info or find links (or even after create)
      RA-750, RA-777


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/DEV/5.2.N/root@126862 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Ancuta Morarasu
2016-05-11 11:56:47 +00:00
parent eb78ee21a1
commit a59b24b576
4 changed files with 135 additions and 33 deletions

View File

@@ -102,4 +102,6 @@ public interface QuickShareLinks
* API Constants - query parameters, etc * API Constants - query parameters, etc
*/ */
String PARAM_SHAREDBY = "sharedByUser"; String PARAM_SHAREDBY = "sharedByUser";
String PARAM_SELECT_ISLINK = "allowableOperations";
} }

View File

@@ -25,6 +25,7 @@ import org.alfresco.repo.quickshare.QuickShareServiceImpl.QuickShareEmailRequest
import org.alfresco.repo.search.QueryParameterDefImpl; import org.alfresco.repo.search.QueryParameterDefImpl;
import org.alfresco.repo.security.authentication.AuthenticationUtil; 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.tenant.TenantUtil; import org.alfresco.repo.tenant.TenantUtil;
import org.alfresco.rest.antlr.WhereClauseParser; import org.alfresco.rest.antlr.WhereClauseParser;
import org.alfresco.rest.api.Nodes; import org.alfresco.rest.api.Nodes;
@@ -67,6 +68,7 @@ import org.alfresco.service.cmr.search.SearchParameters;
import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.AuthorityService; import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.cmr.site.SiteService;
import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
import org.alfresco.util.Pair; import org.alfresco.util.Pair;
@@ -76,10 +78,13 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.extensions.surf.util.I18NUtil; import org.springframework.extensions.surf.util.I18NUtil;
import org.springframework.extensions.webscripts.WebScriptException;
import javax.servlet.http.HttpServletResponse;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
@@ -114,6 +119,7 @@ public class QuickShareLinksImpl implements QuickShareLinks, InitializingBean
private SearchService searchService; private SearchService searchService;
private DictionaryService dictionaryService; private DictionaryService dictionaryService;
private NamespaceService namespaceService; private NamespaceService namespaceService;
private SiteService siteService;
public void setServiceRegistry(ServiceRegistry sr) public void setServiceRegistry(ServiceRegistry sr)
{ {
@@ -155,6 +161,7 @@ public class QuickShareLinksImpl implements QuickShareLinks, InitializingBean
this.searchService = sr.getSearchService(); this.searchService = sr.getSearchService();
this.dictionaryService = sr.getDictionaryService(); this.dictionaryService = sr.getDictionaryService();
this.namespaceService = sr.getNamespaceService(); this.namespaceService = sr.getNamespaceService();
this.siteService = sr.getSiteService();
} }
/** /**
@@ -162,7 +169,7 @@ public class QuickShareLinksImpl implements QuickShareLinks, InitializingBean
* <p> * <p>
* Note: does *not* require authenticated access for (public) shared link. * Note: does *not* require authenticated access for (public) shared link.
*/ */
public QuickShareLink readById(final String sharedId, Parameters parameters) public QuickShareLink readById(final String sharedId, final Parameters parameters)
{ {
checkEnabled(); checkEnabled();
@@ -177,7 +184,7 @@ public class QuickShareLinksImpl implements QuickShareLinks, InitializingBean
{ {
public QuickShareLink doWork() throws Exception public QuickShareLink doWork() throws Exception
{ {
return getQuickShareInfo(sharedId, noAuth); return getQuickShareInfo(sharedId, noAuth, parameters.getSelectedProperties());
} }
}, networkTenantDomain); }, networkTenantDomain);
} }
@@ -264,14 +271,9 @@ public class QuickShareLinksImpl implements QuickShareLinks, InitializingBean
try try
{ {
NodeRef nodeRef = quickShareService.getTenantNodeRefFromSharedId(sharedId).getSecond(); NodeRef nodeRef = quickShareService.getTenantNodeRefFromSharedId(sharedId).getSecond();
String currentUser = AuthenticationUtil.getFullyAuthenticatedUser();
// TODO site check - see ACE-XXX String sharedByUserId = (String)nodeService.getProperty(nodeRef, QuickShareModel.PROP_QSHARE_SHAREDBY);
//String siteName = getSiteName(nodeRef); if (! canDeleteSharedLink(nodeRef, sharedByUserId))
String sharedBy = (String) nodeService.getProperty(nodeRef, QuickShareModel.PROP_QSHARE_SHAREDBY);
if ((!currentUser.equals(sharedBy)) && (!authorityService.isAdminAuthority(currentUser)))
{ {
throw new PermissionDeniedException("Can't perform unshare action: " + sharedId); throw new PermissionDeniedException("Can't perform unshare action: " + sharedId);
} }
@@ -307,6 +309,8 @@ public class QuickShareLinksImpl implements QuickShareLinks, InitializingBean
boolean noAuth = (AuthenticationUtil.getRunAsUser() == null); boolean noAuth = (AuthenticationUtil.getRunAsUser() == null);
List<String> selectParam = parameters.getSelectedProperties();
for (QuickShareLink qs : nodeIds) for (QuickShareLink qs : nodeIds)
{ {
String nodeId = qs.getNodeId(); String nodeId = qs.getNodeId();
@@ -332,7 +336,7 @@ public class QuickShareLinksImpl implements QuickShareLinks, InitializingBean
try try
{ {
QuickShareDTO qsDto = quickShareService.shareContent(nodeRef); QuickShareDTO qsDto = quickShareService.shareContent(nodeRef);
result.add(getQuickShareInfo(qsDto.getId(), noAuth)); result.add(getQuickShareInfo(qsDto.getId(), noAuth, selectParam));
} }
catch (InvalidNodeRefException inre) catch (InvalidNodeRefException inre)
{ {
@@ -434,17 +438,18 @@ public class QuickShareLinksImpl implements QuickShareLinks, InitializingBean
sp.setSkipCount(pagingRequest.getSkipCount()); sp.setSkipCount(pagingRequest.getSkipCount());
sp.setMaxItems(pagingRequest.getMaxItems()); sp.setMaxItems(pagingRequest.getMaxItems());
// TODO is perf ok with Solr 4 paging/sorting ? (otherwise make this optional via orderBy and leave default sort as undefined)
sp.addSort("@" + ContentModel.PROP_MODIFIED, false); sp.addSort("@" + ContentModel.PROP_MODIFIED, false);
ResultSet results = searchService.query(sp); ResultSet results = searchService.query(sp);
List<QuickShareLink> qsLinks = new ArrayList<>(results.length()); List<QuickShareLink> qsLinks = new ArrayList<>(results.length());
List<String> selectParam = parameters.getSelectedProperties();
for (ResultSetRow row : results) for (ResultSetRow row : results)
{ {
NodeRef nodeRef = row.getNodeRef(); NodeRef nodeRef = row.getNodeRef();
qsLinks.add(getQuickShareInfo(nodeRef, false)); qsLinks.add(getQuickShareInfo(nodeRef, false, selectParam));
} }
results.close(); results.close();
@@ -452,23 +457,23 @@ public class QuickShareLinksImpl implements QuickShareLinks, InitializingBean
return CollectionWithPagingInfo.asPaged(paging, qsLinks, results.hasMore(), new Long(results.getNumberFound()).intValue()); return CollectionWithPagingInfo.asPaged(paging, qsLinks, results.hasMore(), new Long(results.getNumberFound()).intValue());
} }
private QuickShareLink getQuickShareInfo(String sharedId, boolean noAuth) private QuickShareLink getQuickShareInfo(String sharedId, boolean noAuth, List<String> selectParam)
{ {
checkValidShareId(sharedId); checkValidShareId(sharedId);
Map<String, Object> map = (Map<String, Object>) quickShareService.getMetaData(sharedId).get("item"); Map<String, Object> map = (Map<String, Object>) quickShareService.getMetaData(sharedId).get("item");
NodeRef nodeRef = new NodeRef((String) map.get("nodeRef")); NodeRef nodeRef = new NodeRef((String) map.get("nodeRef"));
return getQuickShareInfo(nodeRef, map, noAuth); return getQuickShareInfo(nodeRef, map, noAuth, selectParam);
} }
private QuickShareLink getQuickShareInfo(NodeRef nodeRef, boolean noAuth) private QuickShareLink getQuickShareInfo(NodeRef nodeRef, boolean noAuth, List<String> selectParam)
{ {
Map<String, Object> map = (Map<String, Object>) quickShareService.getMetaData(nodeRef).get("item"); Map<String, Object> map = (Map<String, Object>) quickShareService.getMetaData(nodeRef).get("item");
return getQuickShareInfo(nodeRef, map , noAuth); return getQuickShareInfo(nodeRef, map , noAuth, selectParam);
} }
private QuickShareLink getQuickShareInfo(NodeRef nodeRef, Map<String, Object> map, boolean noAuth) private QuickShareLink getQuickShareInfo(NodeRef nodeRef, Map<String, Object> map, boolean noAuth, List<String> selectParam)
{ {
String sharedId = (String)map.get("sharedId"); String sharedId = (String)map.get("sharedId");
@@ -489,17 +494,24 @@ public class QuickShareLinksImpl implements QuickShareLinks, InitializingBean
UserInfo modifiedByUser = Node.lookupUserInfo((String)nodeProps.get(ContentModel.PROP_MODIFIER), mapUserInfo, personService, displayNameOnly); UserInfo modifiedByUser = Node.lookupUserInfo((String)nodeProps.get(ContentModel.PROP_MODIFIER), mapUserInfo, personService, displayNameOnly);
// TODO review - should we return sharedByUser for authenticated users only ?? (not exposed by V0 but needed for "find") // TODO review - should we return sharedByUser for authenticated users only ?? (not exposed by V0 but needed for "find")
UserInfo sharedByUser = Node.lookupUserInfo((String)nodeProps.get(QuickShareModel.PROP_QSHARE_SHAREDBY), mapUserInfo, personService, displayNameOnly); String sharedByUserId = (String)nodeProps.get(QuickShareModel.PROP_QSHARE_SHAREDBY);
UserInfo sharedByUser = Node.lookupUserInfo(sharedByUserId, mapUserInfo, personService, displayNameOnly);
// TODO other "properties" (if needed) - eg. cm:title, cm:lastThumbnailModificationData, ... thumbnail info ... QuickShareLink qs = new QuickShareLink(sharedId, (noAuth ? null : nodeRef.getId()));
QuickShareLink qs = new QuickShareLink(sharedId, nodeRef.getId());
qs.setName((String) map.get("name")); qs.setName((String) map.get("name"));
qs.setContent(contentInfo); qs.setContent(contentInfo);
qs.setModifiedAt((Date) map.get("modified")); qs.setModifiedAt((Date) map.get("modified"));
qs.setModifiedByUser(modifiedByUser); qs.setModifiedByUser(modifiedByUser);
qs.setSharedByUser(sharedByUser); qs.setSharedByUser(sharedByUser);
if ((! noAuth) && selectParam.contains(PARAM_SELECT_ISLINK))
{
if (canDeleteSharedLink(nodeRef, sharedByUserId))
{
qs.setAllowableOperations(Collections.singletonList("delete"));
}
}
return qs; return qs;
} }
catch (InvalidSharedIdException ex) catch (InvalidSharedIdException ex)
@@ -514,6 +526,54 @@ public class QuickShareLinksImpl implements QuickShareLinks, InitializingBean
} }
} }
// TODO push down to QuickShareService and also update v0 webscript (UnshareContentDelete)
private boolean canDeleteSharedLink(NodeRef nodeRef, String sharedByUserId)
{
boolean canDeleteSharedLink = false;
String currentUser = AuthenticationUtil.getFullyAuthenticatedUser();
String siteName = getSiteName(nodeRef);
if (siteName != null)
{
// node belongs to a site - current user must be a manager or collaborator (irrespective of whether they shared the link or not)
String role = siteService.getMembersRole(siteName, currentUser);
if (role.equals(SiteModel.SITE_MANAGER) || role.equals(SiteModel.SITE_COLLABORATOR))
{
canDeleteSharedLink = true;
}
}
else if ((currentUser.equals(sharedByUserId)) || (authorityService.isAdminAuthority(currentUser)))
{
// node does not belongs to a site - current user must be the person who shared the link or an admin
canDeleteSharedLink = true;
}
return canDeleteSharedLink;
}
private String getSiteName(NodeRef nodeRef)
{
NodeRef parent = nodeService.getPrimaryParent(nodeRef).getParentRef();
while (parent != null && !nodeService.getType(parent).equals(SiteModel.TYPE_SITE))
{
// check that we can read parent name
String parentName = (String) nodeService.getProperty(parent, ContentModel.PROP_NAME);
if (nodeService.getPrimaryParent(nodeRef) != null)
{
parent = nodeService.getPrimaryParent(parent).getParentRef();
}
}
if (parent == null)
{
return null;
}
return nodeService.getProperty(parent, ContentModel.PROP_NAME).toString();
}
private void checkEnabled() private void checkEnabled()
{ {
if (!enabled) if (!enabled)

View File

@@ -19,6 +19,7 @@
package org.alfresco.rest.api.model; package org.alfresco.rest.api.model;
import java.util.Date; import java.util.Date;
import java.util.List;
/** /**
* Representation of quick share link * Representation of quick share link
@@ -45,10 +46,13 @@ public class QuickShareLink
private String name; private String name;
private ContentInfo content; private ContentInfo content;
protected Date modifiedAt; private Date modifiedAt;
protected UserInfo modifiedByUser; private UserInfo modifiedByUser;
private UserInfo sharedByUser;
private List<String> allowableOperations;
protected UserInfo sharedByUser;
public QuickShareLink() public QuickShareLink()
{ {
@@ -126,6 +130,16 @@ public class QuickShareLink
this.sharedByUser = sharedByUser; this.sharedByUser = sharedByUser;
} }
public List<String> getAllowableOperations()
{
return allowableOperations;
}
public void setAllowableOperations(List<String> allowableOperations)
{
this.allowableOperations = allowableOperations;
}
// eg. for debug logging etc // eg. for debug logging etc
@Override @Override
public String toString() public String toString()
@@ -138,6 +152,7 @@ public class QuickShareLink
sb.append(", modifiedByUser=").append(getModifiedByUser()); sb.append(", modifiedByUser=").append(getModifiedByUser());
sb.append(", sharedByUser=").append(getSharedByUser()); sb.append(", sharedByUser=").append(getSharedByUser());
sb.append(", content=").append(getContent()); sb.append(", content=").append(getContent());
sb.append(", allowableOperations=").append(getAllowableOperations());
sb.append("]"); sb.append("]");
return sb.toString(); return sb.toString();
} }

View File

@@ -21,7 +21,6 @@ package org.alfresco.rest.api.tests;
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.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.rest.api.Nodes;
import org.alfresco.rest.api.People; import org.alfresco.rest.api.People;
import org.alfresco.rest.api.QuickShareLinks; import org.alfresco.rest.api.QuickShareLinks;
import org.alfresco.rest.api.impl.QuickShareLinksImpl; import org.alfresco.rest.api.impl.QuickShareLinksImpl;
@@ -41,6 +40,7 @@ import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@@ -182,29 +182,54 @@ public class SharedLinkApiTest extends AbstractBaseApiTest
post(URL_SHARED_LINKS, user2, toJsonAsStringNonNull(body), 409); post(URL_SHARED_LINKS, user2, toJsonAsStringNonNull(body), 409);
// auth access to get shared link info // auth access to get shared link info - as user1
response = getSingle(QuickShareLinkEntityResource.class, user1, sharedId, null, 200); Map<String, String> params = Collections.singletonMap("select", "allowableOperations");
response = getSingle(QuickShareLinkEntityResource.class, user1, sharedId, params, 200);
resp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), QuickShareLink.class); resp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), QuickShareLink.class);
assertEquals(sharedId, resp.getId()); assertEquals(sharedId, resp.getId());
assertEquals(d1Id, resp.getNodeId());
assertEquals(docName1, resp.getName()); assertEquals(docName1, resp.getName());
assertEquals(d1Id, resp.getNodeId());
assertEquals(user1, resp.getModifiedByUser().getId()); // returned if authenticated assertEquals(user1, resp.getModifiedByUser().getId()); // returned if authenticated
assertEquals(user2, resp.getSharedByUser().getId()); // returned if authenticated assertEquals(user2, resp.getSharedByUser().getId()); // returned if authenticated
assertNull(resp.getAllowableOperations());
// unauth access to get shared link info // auth access to get shared link info - as user2
response = getSingle(QuickShareLinkEntityResource.class, null, sharedId, null, 200); params = Collections.singletonMap("select", "allowableOperations");
response = getSingle(QuickShareLinkEntityResource.class, user2, sharedId, params, 200);
resp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), QuickShareLink.class); resp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), QuickShareLink.class);
assertEquals(sharedId, resp.getId()); assertEquals(sharedId, resp.getId());
assertEquals(d1Id, resp.getNodeId());
assertEquals(docName1, resp.getName()); assertEquals(docName1, resp.getName());
assertEquals(d1Id, resp.getNodeId());
assertNull(resp.getModifiedByUser().getId()); assertEquals(user1, resp.getModifiedByUser().getId()); // returned if authenticated
assertEquals(user2, resp.getSharedByUser().getId()); // returned if authenticated
assertEquals(1, resp.getAllowableOperations().size());
assertEquals("delete", resp.getAllowableOperations().get(0));
// allowable operations not selected
response = getSingle(QuickShareLinkEntityResource.class, user2, sharedId, null, 200);
resp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), QuickShareLink.class);
assertNull(resp.getAllowableOperations());
// unauth access to get shared link info
params = Collections.singletonMap("select", "allowableOperations"); // note: this will be ignore for unauth access
response = getSingle(QuickShareLinkEntityResource.class, null, sharedId, params, 200);
resp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), QuickShareLink.class);
assertEquals(sharedId, resp.getId());
assertEquals(docName1, resp.getName());
assertNull(resp.getNodeId()); // nodeId not returned
assertNull(resp.getAllowableOperations()); // select is ignored
assertNull(resp.getModifiedByUser().getId()); // userId not returned
assertEquals(user1+" "+user1, resp.getModifiedByUser().getDisplayName()); assertEquals(user1+" "+user1, resp.getModifiedByUser().getDisplayName());
assertNull(resp.getSharedByUser().getId()); assertNull(resp.getSharedByUser().getId()); // userId not returned
assertEquals(user2+" "+user2, resp.getSharedByUser().getDisplayName()); assertEquals(user2+" "+user2, resp.getSharedByUser().getDisplayName());