diff --git a/config/alfresco/public-services-security-context.xml b/config/alfresco/public-services-security-context.xml index e2c0dfce60..1e74305a36 100644 --- a/config/alfresco/public-services-security-context.xml +++ b/config/alfresco/public-services-security-context.xml @@ -975,6 +975,7 @@ org.alfresco.service.cmr.site.SiteService.hasCreateSitePermissions=ACL_ALLOW org.alfresco.service.cmr.site.SiteService.isMember=ACL_ALLOW org.alfresco.service.cmr.site.SiteService.listMembers=ACL_ALLOW + org.alfresco.service.cmr.site.SiteService.listMembersInfo=ACL_ALLOW org.alfresco.service.cmr.site.SiteService.listSites=ACL_ALLOW,AFTER_ACL_NODE.sys:base.ReadProperties org.alfresco.service.cmr.site.SiteService.removeMembership=ACL_ALLOW org.alfresco.service.cmr.site.SiteService.setMembership=ACL_ALLOW diff --git a/config/alfresco/site-services-context.xml b/config/alfresco/site-services-context.xml index c1a1519d22..5409b4f5f1 100644 --- a/config/alfresco/site-services-context.xml +++ b/config/alfresco/site-services-context.xml @@ -35,6 +35,7 @@ listSites getSite listMembers + listMembersInfo getMembersRole isMember getContainer diff --git a/source/java/org/alfresco/repo/site/SiteMemberInfoImpl.java b/source/java/org/alfresco/repo/site/SiteMemberInfoImpl.java new file mode 100644 index 0000000000..2613d863ee --- /dev/null +++ b/source/java/org/alfresco/repo/site/SiteMemberInfoImpl.java @@ -0,0 +1,89 @@ + +package org.alfresco.repo.site; + +import java.io.Serializable; + +import org.alfresco.service.cmr.site.SiteMemberInfo; + +/** + * Site member's information class + * + * @author Jamal Kaabi-Mofrad + * @since Odin + */ +public class SiteMemberInfoImpl implements SiteMemberInfo, Serializable +{ + private static final long serialVersionUID = -5902865692214513762L; + + private String memberName; + + private String memberRole; + + private boolean memberOfGroup; + + /** + * Constructor + * + * @param memberName The name of an individual or a group + * @param memberRole The role of the individual or group + * @param isMemberOfGroup Whether a member belongs to a group with access + * rights to the site or not + */ + public SiteMemberInfoImpl(String memberName, String memberRole, boolean isMemberOfGroup) + { + this.memberName = memberName; + this.memberRole = memberRole; + this.memberOfGroup = isMemberOfGroup; + } + + /** + * @see org.alfresco.service.cmr.site.SiteMemberInfo#getMemberName() + */ + @Override + public String getMemberName() + { + return this.memberName; + } + + /** + * @see org.alfresco.service.cmr.site.SiteMemberInfo#getMemberRole() + */ + @Override + public String getMemberRole() + { + return memberRole; + } + + /** + * @see org.alfresco.service.cmr.site.SiteMemberInfo#isMemberOfGroup() + */ + @Override + public boolean isMemberOfGroup() + { + return memberOfGroup; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + ((this.memberName == null) ? 0 : this.memberName.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) { return true; } + if (obj == null) { return false; } + if (!(obj instanceof SiteMemberInfoImpl)) { return false; } + SiteMemberInfoImpl other = (SiteMemberInfoImpl) obj; + if (this.memberName == null) + { + if (other.memberName != null) { return false; } + } + else if (!this.memberName.equals(other.memberName)) { return false; } + return true; + } +} diff --git a/source/java/org/alfresco/repo/site/SiteServiceImpl.java b/source/java/org/alfresco/repo/site/SiteServiceImpl.java index 921b72fd28..116e3ed815 100644 --- a/source/java/org/alfresco/repo/site/SiteServiceImpl.java +++ b/source/java/org/alfresco/repo/site/SiteServiceImpl.java @@ -47,9 +47,9 @@ import org.alfresco.repo.node.NodeServicePolicies; import org.alfresco.repo.node.NodeServicePolicies.OnRestoreNodePolicy; import org.alfresco.repo.node.getchildren.FilterProp; import org.alfresco.repo.node.getchildren.FilterPropString; +import org.alfresco.repo.node.getchildren.FilterPropString.FilterTypeString; import org.alfresco.repo.node.getchildren.GetChildrenCannedQuery; import org.alfresco.repo.node.getchildren.GetChildrenCannedQueryFactory; -import org.alfresco.repo.node.getchildren.FilterPropString.FilterTypeString; import org.alfresco.repo.policy.BehaviourFilter; import org.alfresco.repo.policy.JavaBehaviour; import org.alfresco.repo.policy.PolicyComponent; @@ -78,13 +78,14 @@ import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.cmr.security.AccessPermission; import org.alfresco.service.cmr.security.AccessStatus; import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.cmr.security.AuthorityService.AuthorityFilter; import org.alfresco.service.cmr.security.AuthorityType; import org.alfresco.service.cmr.security.NoSuchPersonException; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.cmr.security.PublicServiceAccessService; -import org.alfresco.service.cmr.security.AuthorityService.AuthorityFilter; import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.site.SiteMemberInfo; import org.alfresco.service.cmr.site.SiteService; import org.alfresco.service.cmr.site.SiteVisibility; import org.alfresco.service.cmr.tagging.TaggingService; @@ -1511,36 +1512,77 @@ public class SiteServiceImpl extends AbstractLifecycleBean implements SiteServic } } + /** + * @see org.alfresco.service.cmr.site.SiteService#listMembersInfo(String, + * String, String, int, boolean) + */ + public List listMembersInfo(String shortName, final String nameFilter, final String roleFilter, final int size, final boolean collapseGroups) + { + // MT share - for activity service system callback + if (tenantService.isEnabled() + && (AuthenticationUtil.SYSTEM_USER_NAME.equals(AuthenticationUtil + .getRunAsUser())) && tenantService.isTenantName(shortName)) + { + final String tenantDomain = tenantService.getDomain(shortName); + final String sName = tenantService.getBaseName(shortName, true); + + return AuthenticationUtil.runAs( + new AuthenticationUtil.RunAsWork>() + { + public List doWork() throws Exception + { + return listMembersInfoImpl(sName, nameFilter, roleFilter, size, + collapseGroups); + } + }, tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), + tenantDomain)); + } + else + { + return listMembersInfoImpl(shortName, nameFilter, roleFilter, size, collapseGroups); + } + } + private Map listMembersImpl(String shortName, String nameFilter, String roleFilter, int size, boolean collapseGroups) + { + Map members = new HashMap(32); + + List list = listMembersInfoImpl(shortName, nameFilter, roleFilter, size, + collapseGroups); + for (SiteMemberInfo info : list) + members.put(info.getMemberName(), info.getMemberRole()); + + return members; + } + + private List listMembersInfoImpl(String shortName, String nameFilter, + String roleFilter, int size, boolean collapseGroups) { NodeRef siteNodeRef = getSiteNodeRef(shortName); - if (siteNodeRef == null) - { - throw new SiteDoesNotExistException(shortName); - } - + if (siteNodeRef == null) { throw new SiteDoesNotExistException(shortName); } + // Build an array of name filter tokens pre lowercased to test against person properties // We require that matching people have at least one match against one of these on - // either their firstname or last name + // either their firstname or last name String nameFilterLower = null; String[] nameFilters = new String[0]; if (nameFilter != null && nameFilter.length() != 0) { StringTokenizer t = new StringTokenizer(nameFilter, " "); nameFilters = new String[t.countTokens()]; - for (int i=0; t.hasMoreTokens(); i++) + for (int i = 0; t.hasMoreTokens(); i++) { nameFilters[i] = t.nextToken().toLowerCase(); } nameFilterLower = nameFilter.toLowerCase(); } - - Map members = new HashMap(32); + + List members = new ArrayList(32); QName siteType = directNodeService.getType(siteNodeRef); Set permissions = this.permissionService.getSettablePermissions(siteType); Map groupsToExpand = new HashMap(32); - + AUTHORITY_FIND: for (String permission : permissions) { if (roleFilter == null || roleFilter.length() == 0 || roleFilter.equals(permission)) @@ -1551,68 +1593,68 @@ public class SiteServiceImpl extends AbstractLifecycleBean implements SiteServic { switch (AuthorityType.getAuthorityType(authority)) { - case USER: - boolean addUser = true; - if (nameFilter != null && nameFilter.length() != 0 && !nameFilter.equals(authority)) - { - // found a filter - does it match person first/last name? - addUser = matchPerson(nameFilters, authority); - } - if (addUser) - { - // Add the user and their permission to the returned map - members.put(authority, permission); - - // break on max size limit reached - if (members.size() == size) break AUTHORITY_FIND; - } - break; - case GROUP: - if (collapseGroups) - { - if (!groupsToExpand.containsKey(authority)) + case USER: + boolean addUser = true; + if (nameFilter != null && nameFilter.length() != 0 && !nameFilter.equals(authority)) { - groupsToExpand.put(authority, permission); + // found a filter - does it match person first/last name? + addUser = matchPerson(nameFilters, authority); } - } - else - { - if (nameFilter != null && nameFilter.length() != 0) + if (addUser) { - // found a filter - does it match Group name part? - if (matchByFilter(authority.substring(GROUP_PREFIX_LENGTH).toLowerCase(), nameFilterLower)) + // Add the user and their permission to the returned map + members.add(new SiteMemberInfoImpl(authority, permission, false)); + + // break on max size limit reached + if (members.size() == size) break AUTHORITY_FIND; + } + break; + case GROUP: + if (collapseGroups) + { + if (!groupsToExpand.containsKey(authority)) { - members.put(authority, permission); - } - else - { - // Does it match on the Group Display Name part instead? - String displayName = authorityService.getAuthorityDisplayName(authority); - if(displayName != null && matchByFilter(displayName.toLowerCase(), nameFilterLower)) - { - members.put(authority, permission); - } + groupsToExpand.put(authority, permission); } } else { - // No name filter add this group - members.put(authority, permission); + if (nameFilter != null && nameFilter.length() != 0) + { + // found a filter - does it match Group name part? + if (matchByFilter(authority.substring(GROUP_PREFIX_LENGTH).toLowerCase(), nameFilterLower)) + { + members.add(new SiteMemberInfoImpl(authority, permission, false)); + } + else + { + // Does it match on the Group Display Name part instead? + String displayName = authorityService.getAuthorityDisplayName(authority); + if (displayName != null && matchByFilter(displayName.toLowerCase(), nameFilterLower)) + { + members.add(new SiteMemberInfoImpl(authority, permission, false)); + } + } + } + else + { + // No name filter add this group + members.add(new SiteMemberInfoImpl(authority, permission, false)); + } + + // break on max size limit reached + if (members.size() == size) break AUTHORITY_FIND; } - - // break on max size limit reached - if (members.size() == size) break AUTHORITY_FIND; - } - break; + break; } } } } - + if (collapseGroups) { - for (Map.Entry entry : groupsToExpand.entrySet()) - { + for (Map.Entry entry : groupsToExpand.entrySet()) + { Set subUsers = this.authorityService.getContainedAuthorities(AuthorityType.USER, entry.getKey(), false); for (String subUser : subUsers) { @@ -1624,19 +1666,19 @@ public class SiteServiceImpl extends AbstractLifecycleBean implements SiteServic } if (addUser) { - // Add the collapsed user into the members list if they do not already appear in the list - if (members.containsKey(subUser) == false) + SiteMemberInfo memberInfo = new SiteMemberInfoImpl(subUser,entry.getValue(), true); + // Add the collapsed user into the members list if they do not already appear in the list + if (members.contains(memberInfo) == false) { - members.put(subUser, entry.getValue()); + members.add(memberInfo); } - + // break on max size limit reached if (members.size() == size) break; } } - } + } } - return members; } diff --git a/source/java/org/alfresco/repo/site/SiteServiceImplTest.java b/source/java/org/alfresco/repo/site/SiteServiceImplTest.java index 5d36579bf8..3d8b3e12b7 100644 --- a/source/java/org/alfresco/repo/site/SiteServiceImplTest.java +++ b/source/java/org/alfresco/repo/site/SiteServiceImplTest.java @@ -61,6 +61,7 @@ import org.alfresco.service.cmr.security.AuthorityType; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.site.SiteMemberInfo; import org.alfresco.service.cmr.site.SiteService; import org.alfresco.service.cmr.site.SiteVisibility; import org.alfresco.service.cmr.tagging.TaggingService; @@ -2180,4 +2181,59 @@ public class SiteServiceImplTest extends BaseAlfrescoSpringTest this.scriptService.executeScript(location, model); } + public void testListMembersInfo() + { + String siteShortName = "testMemberInfo"; + + // Create a site as user one + this.siteService.createSite(TEST_SITE_PRESET, siteShortName, TEST_TITLE, TEST_DESCRIPTION, + SiteVisibility.PRIVATE); + + // Get the members of the site and check that user one is a manager + List members = this.siteService.listMembersInfo(siteShortName, null, null, 0, false); + assertNotNull(members); + assertEquals(1, members.size()); + SiteMemberInfo user = members.get(0); + assertNotNull(user); + assertTrue(user.getMemberName().equals(USER_ONE)); + assertEquals(SiteModel.SITE_MANAGER, user.getMemberRole()); + assertFalse("USER_ONE is NOT member of any group", user.isMemberOfGroup()); + + // GROUP_TWO - USER_TWO, USER_THREE + this.siteService.setMembership(siteShortName, this.groupTwo, SiteModel.SITE_COLLABORATOR); + this.siteService.setMembership(siteShortName, USER_FOUR, SiteModel.SITE_CONSUMER); + + // Get the members of the site in expanded list + members = this.siteService.listMembersInfo(siteShortName, null, null, 0, true); + assertNotNull(members); + assertEquals(4, members.size()); + // Get USER_TWO who is a member of group two + user = lookupMemberInfoByUserName(members, USER_TWO); + assertNotNull(user); + assertEquals(SiteModel.SITE_COLLABORATOR, user.getMemberRole()); + assertTrue("USER_TWO is member of group two", user.isMemberOfGroup()); + // Get USER_THREE who is a member of group two + user = lookupMemberInfoByUserName(members, USER_THREE); + assertNotNull(user); + assertEquals(SiteModel.SITE_COLLABORATOR, user.getMemberRole()); + assertTrue("USER_THREE is member of group two", user.isMemberOfGroup()); + // Get USER_FOUR + user = lookupMemberInfoByUserName(members, USER_FOUR); + assertNotNull(user); + assertEquals(SiteModel.SITE_CONSUMER, user.getMemberRole()); + assertFalse("USER_FOUR is NOT member of any group", user.isMemberOfGroup()); + } + + private SiteMemberInfo lookupMemberInfoByUserName(List members, String name) + { + for (SiteMemberInfo info : members) + { + if (name.equals(info.getMemberName())) + { + return info; + } + } + return null; + } + } diff --git a/source/java/org/alfresco/service/cmr/site/SiteMemberInfo.java b/source/java/org/alfresco/service/cmr/site/SiteMemberInfo.java new file mode 100644 index 0000000000..ed0de35fec --- /dev/null +++ b/source/java/org/alfresco/service/cmr/site/SiteMemberInfo.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2005-2012 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 . + */ + +package org.alfresco.service.cmr.site; + +/** + * Site member's information. The member can either be an individual or a group. + * + * @author Jamal Kaabi-Mofrad + * @since odin + */ +public interface SiteMemberInfo +{ + + /** + * Get the member's name. The name can either be the name of an individual + * or a group + * + * @return String member's name + */ + public String getMemberName(); + + + /** + * Get the member's role + * + * @return String member's role + */ + public String getMemberRole(); + + + /** + * Indicates whether a member belongs to a group with access rights to the + * site or not + * + * @return true if the member belongs to a group with access + * rights, otherwise false + */ + public boolean isMemberOfGroup(); + +} diff --git a/source/java/org/alfresco/service/cmr/site/SiteService.java b/source/java/org/alfresco/service/cmr/site/SiteService.java index a5f7c56437..1466c75aaf 100644 --- a/source/java/org/alfresco/service/cmr/site/SiteService.java +++ b/source/java/org/alfresco/service/cmr/site/SiteService.java @@ -246,6 +246,21 @@ public interface SiteService @NotAuditable Map listMembers(String shortName, String nameFilter, String roleFilter, int size, boolean collapseGroups); + /** + * List the members of the site. This includes both users and groups if collapseGroups is set to false, otherwise all + * groups that are members are collapsed into their component users and listed. + * + * + * @param shortName site short name + * @param nameFilter name filter + * @param roleFilter role filter + * @param size max results size crop if >0 + * @param collapseGroups true if collapse member groups into user list, false otherwise + * @return List of site authorities’ information objects + */ + @NotAuditable + List listMembersInfo(String shortName, String nameFilter, String roleFilter, int size, boolean collapseGroups); + /** * Gets the role of the specified user. *