diff --git a/config/alfresco/activities/activities-feed-context.xml b/config/alfresco/activities/activities-feed-context.xml index e6d0a14511..2882decd3d 100644 --- a/config/alfresco/activities/activities-feed-context.xml +++ b/config/alfresco/activities/activities-feed-context.xml @@ -82,6 +82,8 @@ + + alfresco/extension/templates/activities diff --git a/source/java/org/alfresco/repo/activities/ActivityPostServiceImpl.java b/source/java/org/alfresco/repo/activities/ActivityPostServiceImpl.java index 3189f4f4fc..76ce293a3d 100644 --- a/source/java/org/alfresco/repo/activities/ActivityPostServiceImpl.java +++ b/source/java/org/alfresco/repo/activities/ActivityPostServiceImpl.java @@ -22,6 +22,7 @@ import java.sql.SQLException; import java.util.Date; import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.repo.activities.post.lookup.PostLookup; import org.alfresco.repo.domain.activities.ActivityPostDAO; import org.alfresco.repo.domain.activities.ActivityPostEntity; import org.alfresco.repo.security.authentication.AuthenticationUtil; @@ -29,9 +30,9 @@ import org.alfresco.repo.tenant.TenantService; import org.alfresco.service.cmr.activities.ActivityPostService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; -import org.springframework.extensions.surf.util.ParameterCheck; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.surf.util.ParameterCheck; /** * Activity Post Service Implementation @@ -84,7 +85,7 @@ public class ActivityPostServiceImpl implements ActivityPostService ParameterCheck.mandatory("nodeRef", nodeRef); StringBuffer sb = new StringBuffer(); - sb.append("{").append("\"nodeRef\":\"").append(nodeRef.toString()).append("\"").append("}"); + sb.append("{").append("\""+PostLookup.JSON_NODEREF_LOOKUP+"\":\"").append(nodeRef.toString()).append("\"").append("}"); postActivity(activityType, siteId, appTool, sb.toString(), ActivityPostEntity.STATUS.PENDING); } @@ -97,7 +98,7 @@ public class ActivityPostServiceImpl implements ActivityPostService ParameterCheck.mandatory("nodeRef", nodeRef); StringBuffer sb = new StringBuffer(); - sb.append("{").append("\"nodeRef\":\"").append(nodeRef.toString()).append("\"").append(",") + sb.append("{").append("\""+PostLookup.JSON_NODEREF_LOOKUP+"\":\"").append(nodeRef.toString()).append("\"").append(",") .append("\"name\":\"").append(name).append("\"") .append("}"); @@ -116,10 +117,10 @@ public class ActivityPostServiceImpl implements ActivityPostService ParameterCheck.mandatory("parentNodeRef", parentNodeRef); StringBuffer sb = new StringBuffer(); - sb.append("{").append("\"nodeRef\":\"").append(nodeRef.toString()).append("\"").append(",") - .append("\"name\":\"").append(name).append("\"").append(",") - .append("\"typeQName\":\"").append(typeQName.toPrefixString()).append("\"").append(",") // TODO toPrefixString does not return prefix ??!! - .append("\"parentNodeRef\":\"").append(parentNodeRef.toString()).append("\"") + sb.append("{").append("\""+PostLookup.JSON_NODEREF_LOOKUP+"\":\"").append(nodeRef.toString()).append("\"").append(",") + .append("\""+PostLookup.JSON_NAME+"\":\"").append(name).append("\"").append(",") + .append("\""+PostLookup.JSON_TYPEQNAME+"\":\"").append(typeQName.toPrefixString()).append("\"").append(",") // TODO toPrefixString does not return prefix ??!! + .append("\""+PostLookup.JSON_NODEREF_PARENT+"\":\"").append(parentNodeRef.toString()).append("\"") .append("}"); postActivity(activityType, siteId, appTool, sb.toString(), ActivityPostEntity.STATUS.PENDING); diff --git a/source/java/org/alfresco/repo/activities/feed/FeedTaskProcessor.java b/source/java/org/alfresco/repo/activities/feed/FeedTaskProcessor.java index 68e8827260..db8902be63 100644 --- a/source/java/org/alfresco/repo/activities/feed/FeedTaskProcessor.java +++ b/source/java/org/alfresco/repo/activities/feed/FeedTaskProcessor.java @@ -257,7 +257,14 @@ public abstract class FeedTaskProcessor excludedConnections++; } else - { + { + // read permission check + if (! canRead(ctx, connectedUser, model)) + { + excludedConnections++; + continue; + } + for (String fmTemplate : fmTemplates) { // determine format - based on template naming convention @@ -448,6 +455,11 @@ public abstract class FeedTaskProcessor return members; } + protected boolean canRead(RepoCtx ctx, final String connectedUser, Map model) throws Exception + { + throw new UnsupportedOperationException("FeedTaskProcessor: Remote callback for 'canRead' not implemented"); + } + protected Map> getActivityTypeTemplates(String repoEndPoint, String ticket, String subPath) throws Exception { StringBuffer sbUrl = new StringBuffer(); diff --git a/source/java/org/alfresco/repo/activities/feed/local/LocalFeedTaskProcessor.java b/source/java/org/alfresco/repo/activities/feed/local/LocalFeedTaskProcessor.java index 361b5d9128..aab6ad4ab2 100644 --- a/source/java/org/alfresco/repo/activities/feed/local/LocalFeedTaskProcessor.java +++ b/source/java/org/alfresco/repo/activities/feed/local/LocalFeedTaskProcessor.java @@ -28,6 +28,7 @@ import java.util.Set; import org.alfresco.repo.activities.feed.FeedTaskProcessor; import org.alfresco.repo.activities.feed.RepoCtx; +import org.alfresco.repo.activities.post.lookup.PostLookup; import org.alfresco.repo.domain.activities.ActivityFeedDAO; import org.alfresco.repo.domain.activities.ActivityFeedEntity; import org.alfresco.repo.domain.activities.ActivityPostDAO; @@ -37,7 +38,13 @@ import org.alfresco.repo.domain.activities.FeedControlEntity; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.template.ClassPathRepoTemplateLoader; import org.alfresco.service.cmr.repository.ContentService; +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.AccessPermission; +import org.alfresco.service.cmr.security.AccessStatus; +import org.alfresco.service.cmr.security.AuthorityType; +import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.site.SiteService; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -68,6 +75,8 @@ public class LocalFeedTaskProcessor extends FeedTaskProcessor implements Applica private SiteService siteService; private NodeService nodeService; private ContentService contentService; + private PermissionService permissionService; + private String defaultEncoding; private List templateSearchPaths; private boolean useRemoteCallbacks; @@ -107,6 +116,11 @@ public class LocalFeedTaskProcessor extends FeedTaskProcessor implements Applica this.contentService = contentService; } + public void setPermissionService(PermissionService permissionService) + { + this.permissionService = permissionService; + } + public void setDefaultEncoding(String defaultEncoding) { this.defaultEncoding = defaultEncoding; @@ -206,6 +220,105 @@ public class LocalFeedTaskProcessor extends FeedTaskProcessor implements Applica } } + protected boolean canRead(RepoCtx ctx, final String connectedUser, Map model) throws Exception + { + if (useRemoteCallbacks) + { + // note: not implemented + return super.canRead(ctx, connectedUser, model); + } + else + { + if (permissionService == null) + { + // if permission service not configured then fallback (ie. no read permission check) + return true; + } + + String nodeRefStr = (String)model.get(PostLookup.JSON_NODEREF); + if (nodeRefStr == null) + { + nodeRefStr = (String)model.get(PostLookup.JSON_NODEREF_PARENT); + } + + if (nodeRefStr != null) + { + final NodeRef nodeRef = new NodeRef(nodeRefStr); + + return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public Boolean doWork() throws Exception + { + return canReadImpl(connectedUser, nodeRef); + } + }, AuthenticationUtil.getSystemUserName()); + } + + return true; + } + } + + private boolean canReadImpl(final String connectedUser, final NodeRef nodeRef) throws Exception + { + // check for read permission + long start = System.currentTimeMillis(); + + try + { + // note: deleted node does not exist (hence no permission, although default permission check would return true which is problematic) + final NodeRef checkNodeRef; + if (nodeService.exists(nodeRef)) + { + checkNodeRef = nodeRef; + } + else + { + // TODO: require ghosting - this is temp workaround (we should not rely on archive - may be permanently deleted, ie. not archived or already purged) + NodeRef archiveNodeRef = new NodeRef(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE, nodeRef.getId()); + if (! nodeService.exists(archiveNodeRef)) + { + return false; + } + checkNodeRef = archiveNodeRef; + } + + if (connectedUser.equals("")) + { + // site feed (public site) + Set perms = permissionService.getAllSetPermissions(checkNodeRef); + for (AccessPermission perm : perms) + { + if (perm.getAuthority().equals(PermissionService.ALL_AUTHORITIES) && + perm.getAuthorityType().equals(AuthorityType.EVERYONE) && + perm.getPermission().equals(PermissionService.READ_PERMISSIONS) && + perm.getAccessStatus().equals(AccessStatus.ALLOWED)) + { + return true; + } + } + return false; + } + else + { + // user feed + return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public Boolean doWork() throws Exception + { + return (permissionService.hasPermission(checkNodeRef, PermissionService.READ) == AccessStatus.ALLOWED); + } + }, connectedUser); + } + } + finally + { + if (logger.isDebugEnabled()) + { + logger.debug("canRead: " + nodeRef + " in "+(System.currentTimeMillis()-start)+" msecs"); + } + } + } + @Override protected Map> getActivityTypeTemplates(String repoEndPoint, String ticket, String subPath) throws Exception { diff --git a/source/java/org/alfresco/repo/activities/post/lookup/PostLookup.java b/source/java/org/alfresco/repo/activities/post/lookup/PostLookup.java index 646e004ec6..13bf58e35c 100644 --- a/source/java/org/alfresco/repo/activities/post/lookup/PostLookup.java +++ b/source/java/org/alfresco/repo/activities/post/lookup/PostLookup.java @@ -62,6 +62,20 @@ public class PostLookup private PersonService personService; private TenantService tenantService; + public static final String JSON_NODEREF_LOOKUP = "nodeRefL"; // requires additional lookup + + public static final String JSON_NODEREF = "nodeRef"; + public static final String JSON_NODEREF_PARENT = "parentNodeRef"; + + public static final String JSON_FIRSTNAME = "firstName"; + public static final String JSON_LASTNAME = "lastName"; + + public static final String JSON_NAME = "name"; + public static final String JSON_TYPEQNAME = "typeQName"; + public static final String JSON_PARENT_NODEREF = "parentNodeRef"; + public static final String JSON_DISPLAY_PATH = "displayPath"; + + public void setPostDAO(ActivityPostDAO postDAO) { this.postDAO = postDAO; @@ -139,9 +153,9 @@ public class PostLookup String activityDataStr = null; - if (! jo.isNull("nodeRef")) + if (! jo.isNull(JSON_NODEREF_LOOKUP)) { - String nodeRefStr = jo.getString("nodeRef"); + String nodeRefStr = jo.getString(JSON_NODEREF_LOOKUP); NodeRef nodeRef = new NodeRef(nodeRefStr); // lookup additional node data @@ -154,8 +168,8 @@ public class PostLookup Pair firstLastName = lookupPerson(postUserId); if (firstLastName != null) { - jo.put("firstName", firstLastName.getFirst()); - jo.put("lastName", firstLastName.getSecond()); + jo.put(JSON_FIRSTNAME, firstLastName.getFirst()); + jo.put(JSON_LASTNAME, firstLastName.getSecond()); activityDataStr = jo.toString(); } @@ -283,22 +297,22 @@ public class PostLookup public JSONObject execute() throws Throwable { String name = ""; - if (! jo.isNull("name")) + if (! jo.isNull(JSON_NAME)) { - name = jo.getString("name"); + name = jo.getString(JSON_NAME); } NodeRef parentNodeRef = null; - if (! jo.isNull("parentNodeRef")) + if (! jo.isNull(JSON_PARENT_NODEREF)) { - parentNodeRef = new NodeRef(jo.getString("parentNodeRef")); + parentNodeRef = new NodeRef(jo.getString(JSON_PARENT_NODEREF)); } String typeQName = ""; - if (! jo.isNull("typeQName")) + if (! jo.isNull(JSON_TYPEQNAME)) { - typeQName = jo.getString("typeQName"); + typeQName = jo.getString(JSON_TYPEQNAME); } String displayPath = ""; @@ -341,25 +355,25 @@ public class PostLookup // parent node exists, lookup parent node path path = nodeService.getPath(parentNodeRef); } - + if (path != null) { // lookup display path displayPath = path.toDisplayPath(nodeService, permissionService); - + // note: for now, also tack on the node name displayPath += "/" + name; } // merge with existing activity data - jo.put("name", name); - jo.put("nodeRef", nodeRef.toString()); - jo.put("typeQName", typeQName); - jo.put("parentNodeRef", (parentNodeRef != null ? parentNodeRef.toString() : null)); - jo.put("displayPath", displayPath); - jo.put("firstName", firstName); - jo.put("lastName", lastName); - + jo.put(JSON_NAME, name); + jo.put(JSON_NODEREF, nodeRef.toString()); + jo.put(JSON_TYPEQNAME, typeQName); + jo.put(JSON_PARENT_NODEREF, (parentNodeRef != null ? parentNodeRef.toString() : null)); + jo.put(JSON_DISPLAY_PATH, displayPath); + jo.put(JSON_FIRSTNAME, firstName); + jo.put(JSON_LASTNAME, lastName); + return jo; } };