Merge pull request #2035 from Alfresco/feature/ACS-5506-be-handling-a-description-of-the-group-and-flag-for-subgroups

ACS-5506 Add description and hasSubgroups to groups API
This commit is contained in:
MichalKinas
2024-02-08 13:35:24 +01:00
committed by GitHub
14 changed files with 509 additions and 135 deletions

View File

@@ -25,7 +25,7 @@
*/ */
package org.alfresco.rest.model; package org.alfresco.rest.model;
import java.util.ArrayList; import java.util.List;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
@@ -38,13 +38,17 @@ public class RestGroupsModel extends TestModel implements IRestModel<RestGroupsM
private String id; private String id;
@JsonProperty(required = true) @JsonProperty(required = true)
private String displayName; private String displayName;
@JsonProperty()
private String description;
@JsonProperty(required = true) @JsonProperty(required = true)
private Boolean isRoot; private Boolean isRoot;
@JsonProperty()
private Boolean hasSubgroups;
@JsonProperty("parentIds") @JsonProperty("parentIds")
private ArrayList<String> parentIds; private List<String> parentIds;
@JsonProperty("zones") @JsonProperty("zones")
private ArrayList<String> zones; private List<String> zones;
@JsonProperty(value = "entry") @JsonProperty(value = "entry")
RestGroupsModel model; RestGroupsModel model;
@@ -75,6 +79,22 @@ public class RestGroupsModel extends TestModel implements IRestModel<RestGroupsM
this.displayName = displayName; this.displayName = displayName;
} }
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Boolean getHasSubgroups() {
return hasSubgroups;
}
public void setHasSubgroups(Boolean hasSubgroups) {
this.hasSubgroups = hasSubgroups;
}
public Boolean getIsRoot() public Boolean getIsRoot()
{ {
return isRoot; return isRoot;
@@ -85,22 +105,22 @@ public class RestGroupsModel extends TestModel implements IRestModel<RestGroupsM
this.isRoot = isRoot; this.isRoot = isRoot;
} }
public ArrayList<String> getParentIds() public List<String> getParentIds()
{ {
return parentIds; return parentIds;
} }
public void setParentIds(ArrayList<String> parentIds) public void setParentIds(List<String> parentIds)
{ {
this.parentIds = parentIds; this.parentIds = parentIds;
} }
public ArrayList<String> getZones() public List<String> getZones()
{ {
return zones; return zones;
} }
public void setZones(ArrayList<String> zones) public void setZones(List<String> zones)
{ {
this.zones = zones; this.zones = zones;
} }

View File

@@ -31,44 +31,73 @@ public class GroupsTests extends RestTest
@Test(groups = { TestGroup.REST_API, TestGroup.GROUPS, TestGroup.SANITY }) @Test(groups = { TestGroup.REST_API, TestGroup.GROUPS, TestGroup.SANITY })
@TestRail(section = { TestGroup.REST_API, TestGroup.NODES }, executionType = ExecutionType.SANITY, @TestRail(section = { TestGroup.REST_API, TestGroup.NODES }, executionType = ExecutionType.SANITY,
description = "Verify creation, listing, updating and deletion of groups.") description = "Verify creation, listing, updating and deletion of groups.")
public void createListUpdateAndDeleteGroup() throws Exception public void createListUpdateAndDeleteGroup()
{ {
String groupName = "ZtestGroup" + UUID.randomUUID().toString(); String groupName = "ZtestGroup" + UUID.randomUUID();
JsonObject groupBody = Json.createObjectBuilder().add("id", groupName).add("displayName", groupName).build(); String subGroupName = "ZtestSubgroup" + UUID.randomUUID();
String groupDescription = "ZtestGroup description" + UUID.randomUUID();
JsonObject groupBody = Json.createObjectBuilder().add("id", groupName).add("displayName", groupName).add("description", groupDescription).build();
JsonObject subgroupBody = Json.createObjectBuilder().add("id", subGroupName).add("displayName", subGroupName).build();
String groupBodyCreate = groupBody.toString(); String groupBodyCreate = groupBody.toString();
String subgroupBodyCreate = subgroupBody.toString();
//GroupCreation: //GroupCreation:
//-ve //-ve
restClient.authenticateUser(userModel).withCoreAPI().usingGroups().createGroup(groupBodyCreate); restClient.authenticateUser(userModel).withCoreAPI().usingGroups().createGroup(groupBodyCreate);
restClient.assertStatusCodeIs(HttpStatus.FORBIDDEN); restClient.assertStatusCodeIs(HttpStatus.FORBIDDEN);
//+ve //+ve
restClient.authenticateUser(adminUser).withCoreAPI().usingParams("include=zones").usingGroups().createGroup(groupBodyCreate) restClient.authenticateUser(adminUser).withCoreAPI().usingParams("include=zones,hasSubgroups,description").usingGroups().createGroup(groupBodyCreate)
.assertThat().field("zones").contains("APP.DEFAULT") .assertThat().field("zones").contains("APP.DEFAULT")
.and().field("isRoot").is(true) .and().field("isRoot").is(true)
.and().field("displayName").is(groupName); .and().field("displayName").is(groupName)
.and().field("description").is(groupDescription)
.and().field("hasSubgroups").is(false);
restClient.assertStatusCodeIs(HttpStatus.CREATED);
//AddChildGroup
restClient.authenticateUser(adminUser).withCoreAPI().usingParams("include=zones").usingGroups().createGroup(subgroupBodyCreate);
restClient.assertStatusCodeIs(HttpStatus.CREATED);
//LinkChildGroupToParent
JsonObject groupMembershipGroupBody = Json.createObjectBuilder().add("id", "GROUP_"+subGroupName).add("memberType", "GROUP").build();
String groupMembershipGroupBodyCreate = groupMembershipGroupBody.toString();
restClient.authenticateUser(adminUser).withCoreAPI().usingGroups().createGroupMembership("GROUP_"+groupName, groupMembershipGroupBodyCreate);
restClient.assertStatusCodeIs(HttpStatus.CREATED); restClient.assertStatusCodeIs(HttpStatus.CREATED);
//ListGroups: //ListGroups:
restClient.withCoreAPI().usingParams("orderBy=displayName DESC&maxItems=10").usingGroups().listGroups() restClient.withCoreAPI().usingParams("orderBy=displayName DESC&maxItems=10").usingGroups().listGroups()
.assertThat().entriesListContains("id", "GROUP_"+groupName) .assertThat().entriesListContains("id", "GROUP_"+groupName)
.and().entriesListContains("id", "GROUP_"+subGroupName)
.and().entriesListDoesNotContain("zones") .and().entriesListDoesNotContain("zones")
.and().paginationField("maxItems").is("10"); .and().paginationField("maxItems").is("10");
restClient.assertStatusCodeIs(HttpStatus.OK); restClient.assertStatusCodeIs(HttpStatus.OK);
groupBody = Json.createObjectBuilder().add("displayName", "Z"+groupName).build(); groupBody = Json.createObjectBuilder().add("displayName", "Z"+groupName).add("description", "Z"+groupDescription).build();
String groupBodyUpdate = groupBody.toString(); String groupBodyUpdate = groupBody.toString();
//UpdateGroup: //UpdateGroup:
restClient.withCoreAPI().usingGroups().updateGroupDetails("GROUP_"+groupName, groupBodyUpdate) restClient.withCoreAPI().usingParams("include=description").usingGroups().updateGroupDetails("GROUP_"+groupName, groupBodyUpdate)
.assertThat().field("displayName").is("Z"+groupName) .assertThat().field("displayName").is("Z"+groupName)
.and().field("description").is("Z"+groupDescription)
.and().field("id").is("GROUP_"+groupName) .and().field("id").is("GROUP_"+groupName)
.and().field("zones").isNull(); .and().field("zones").isNull();
restClient.assertStatusCodeIs(HttpStatus.OK); restClient.assertStatusCodeIs(HttpStatus.OK);
//GetGroupDetails: //GetGroupDetails:
restClient.withCoreAPI().usingParams("include=zones").usingGroups().getGroupDetail("GROUP_"+groupName) restClient.withCoreAPI().usingParams("include=zones,hasSubgroups").usingGroups().getGroupDetail("GROUP_"+groupName)
.assertThat().field("id").is("GROUP_"+groupName) .assertThat().field("id").is("GROUP_"+groupName)
.and().field("zones").contains("APP.DEFAULT") .and().field("zones").contains("APP.DEFAULT")
.and().field("isRoot").is(true); .and().field("isRoot").is(true)
.and().field("hasSubgroups").is(true);
restClient.assertStatusCodeIs(HttpStatus.OK);
//DeleteChildGroup:
restClient.authenticateUser(adminUser).withCoreAPI().usingGroups().deleteGroup("GROUP_"+subGroupName);
restClient.assertStatusCodeIs(HttpStatus.NO_CONTENT);
//VerifyIfParentHasNoSubgroups:
restClient.withCoreAPI().usingParams("include=zones,hasSubgroups").usingGroups().getGroupDetail("GROUP_"+groupName)
.assertThat().field("id").is("GROUP_"+groupName)
.and().field("hasSubgroups").is(false);
restClient.assertStatusCodeIs(HttpStatus.OK); restClient.assertStatusCodeIs(HttpStatus.OK);
//DeleteGroup: //DeleteGroup:
@@ -83,9 +112,9 @@ public class GroupsTests extends RestTest
@Test(groups = { TestGroup.REST_API, TestGroup.GROUPS, TestGroup.SANITY }) @Test(groups = { TestGroup.REST_API, TestGroup.GROUPS, TestGroup.SANITY })
@TestRail(section = { TestGroup.REST_API, TestGroup.NODES }, executionType = ExecutionType.SANITY, @TestRail(section = { TestGroup.REST_API, TestGroup.NODES }, executionType = ExecutionType.SANITY,
description = "Verify creation, listing(only for person) and deletion of group memberships. ") description = "Verify creation, listing(only for person) and deletion of group memberships. ")
public void createListDeleteGroupMembership() throws Exception public void createListDeleteGroupMembership()
{ {
String groupName = "ZtestGroup" + UUID.randomUUID().toString(); String groupName = "ZtestGroup" + UUID.randomUUID();
JsonObject groupBody = Json.createObjectBuilder().add("id", groupName).add("displayName", groupName).build(); JsonObject groupBody = Json.createObjectBuilder().add("id", groupName).add("displayName", groupName).build();
String groupBodyCreate = groupBody.toString(); String groupBodyCreate = groupBody.toString();
@@ -95,6 +124,7 @@ public class GroupsTests extends RestTest
JsonObject groupMembershipBody = Json.createObjectBuilder().add("id", userModel.getUsername()).add("memberType", "PERSON").build(); JsonObject groupMembershipBody = Json.createObjectBuilder().add("id", userModel.getUsername()).add("memberType", "PERSON").build();
String groupMembershipBodyCreate = groupMembershipBody.toString(); String groupMembershipBodyCreate = groupMembershipBody.toString();
//MembershipCreation: //MembershipCreation:
//-ve //-ve
restClient.authenticateUser(userModel).withCoreAPI().usingGroups().createGroupMembership("GROUP_"+groupName, groupMembershipBodyCreate); restClient.authenticateUser(userModel).withCoreAPI().usingGroups().createGroupMembership("GROUP_"+groupName, groupMembershipBodyCreate);
@@ -127,7 +157,7 @@ public class GroupsTests extends RestTest
description = "Verify listing of group memberships.") description = "Verify listing of group memberships.")
public void listGroupMembership() throws Exception public void listGroupMembership() throws Exception
{ {
String groupName = "testGroup" + UUID.randomUUID().toString(); String groupName = "testGroup" + UUID.randomUUID();
JsonObject groupBody = Json.createObjectBuilder().add("id", groupName).add("displayName", groupName).build(); JsonObject groupBody = Json.createObjectBuilder().add("id", groupName).add("displayName", groupName).build();
String groupBodyCreate = groupBody.toString(); String groupBodyCreate = groupBody.toString();
@@ -146,12 +176,10 @@ public class GroupsTests extends RestTest
restClient.assertStatusCodeIs(HttpStatus.CREATED); restClient.assertStatusCodeIs(HttpStatus.CREATED);
//ListGroupMembership //ListGroupMembership
RetryOperation op = new RetryOperation(){ RetryOperation op = () -> {
public void execute() throws Exception{ restClient.withCoreAPI().usingGroups().listGroupMemberships("GROUP_"+groupName)
restClient.withCoreAPI().usingGroups().listGroupMemberships("GROUP_"+groupName) .assertThat().entriesListContains("id", userModel.getUsername());
.assertThat().entriesListContains("id", userModel.getUsername()); restClient.assertStatusCodeIs(HttpStatus.OK);
restClient.assertStatusCodeIs(HttpStatus.OK);
}
}; };
Utility.sleep(500, 35000, op);// Allow indexing to complete. Utility.sleep(500, 35000, op);// Allow indexing to complete.
} }

View File

@@ -40,8 +40,10 @@ public interface Groups
{ {
String PARAM_ID = "id"; String PARAM_ID = "id";
String PARAM_DISPLAY_NAME = "displayName"; String PARAM_DISPLAY_NAME = "displayName";
String PARAM_INCLUDE_DESCRIPTION = "description";
String PARAM_INCLUDE_PARENT_IDS = "parentIds"; String PARAM_INCLUDE_PARENT_IDS = "parentIds";
String PARAM_INCLUDE_ZONES = "zones"; String PARAM_INCLUDE_ZONES = "zones";
String PARAM_INCLUDE_HAS_SUBGROUPS = "hasSubgroups";
String PARAM_IS_ROOT = "isRoot"; String PARAM_IS_ROOT = "isRoot";
String PARAM_CASCADE = "cascade"; String PARAM_CASCADE = "cascade";
String PARAM_MEMBER_TYPE = "memberType"; String PARAM_MEMBER_TYPE = "memberType";

View File

@@ -27,6 +27,7 @@ package org.alfresco.rest.api.impl;
import static org.alfresco.repo.security.authentication.AuthenticationUtil.runAsSystem; import static org.alfresco.repo.security.authentication.AuthenticationUtil.runAsSystem;
import java.io.Serializable;
import java.text.Collator; import java.text.Collator;
import java.util.AbstractList; import java.util.AbstractList;
import java.util.ArrayList; import java.util.ArrayList;
@@ -40,6 +41,7 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.alfresco.model.ContentModel;
import org.alfresco.query.CannedQueryPageDetails; import org.alfresco.query.CannedQueryPageDetails;
import org.alfresco.query.EmptyPagingResults; import org.alfresco.query.EmptyPagingResults;
import org.alfresco.query.PagingRequest; import org.alfresco.query.PagingRequest;
@@ -71,8 +73,10 @@ import org.alfresco.rest.workflow.api.impl.MapBasedQueryWalkerOrSupported;
import org.alfresco.service.cmr.security.AuthorityService; import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.cmr.security.AuthorityType; import org.alfresco.service.cmr.security.AuthorityType;
import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.AlfrescoCollator; import org.alfresco.util.AlfrescoCollator;
import org.alfresco.util.Pair; import org.alfresco.util.Pair;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.extensions.surf.util.I18NUtil; import org.springframework.extensions.surf.util.I18NUtil;
@@ -101,9 +105,9 @@ public class GroupsImpl implements Groups
} }
// List groups filtering (via where clause) // List groups filtering (via where clause)
private final static Set<String> LIST_GROUPS_EQUALS_QUERY_PROPERTIES = new HashSet<>(Arrays.asList(new String[] { PARAM_IS_ROOT })); private final static Set<String> LIST_GROUPS_EQUALS_QUERY_PROPERTIES = new HashSet<>(List.of(PARAM_IS_ROOT));
private final static Set<String> LIST_GROUP_MEMBERS_QUERY_PROPERTIES = new HashSet<>(Arrays.asList(new String[] { PARAM_MEMBER_TYPE })); private final static Set<String> LIST_GROUP_MEMBERS_QUERY_PROPERTIES = new HashSet<>(List.of(PARAM_MEMBER_TYPE));
protected AuthorityService authorityService; protected AuthorityService authorityService;
private AuthorityDAO authorityDAO; private AuthorityDAO authorityDAO;
@@ -142,7 +146,12 @@ public class GroupsImpl implements Groups
authorityDisplayName = group.getDisplayName(); authorityDisplayName = group.getDisplayName();
} }
String authority = authorityService.createAuthority(AuthorityType.GROUP, group.getId(), authorityDisplayName, authorityZones); Map<QName, Serializable> props = new HashMap<>();
if (StringUtils.isNotEmpty(group.getDescription()))
{
props.put(ContentModel.PROP_DESCRIPTION, group.getDescription());
}
String authority = authorityService.createAuthority(AuthorityType.GROUP, group.getId(), authorityDisplayName, authorityZones, props);
// Set a given child authority to be included by the given parent // Set a given child authority to be included by the given parent
// authorities. // authorities.
@@ -161,7 +170,14 @@ public class GroupsImpl implements Groups
try try
{ {
authorityService.setAuthorityDisplayName(groupId, group.getDisplayName()); if (StringUtils.isNotEmpty(group.getDescription()))
{
authorityService.setAuthorityDisplayNameAndDescription(groupId, group.getDisplayName(), group.getDescription());
}
else
{
authorityService.setAuthorityDisplayName(groupId, group.getDisplayName());
}
} }
catch (AuthorityException ae) catch (AuthorityException ae)
{ {
@@ -173,10 +189,10 @@ public class GroupsImpl implements Groups
public Group getGroup(String groupId, Parameters parameters) throws EntityNotFoundException public Group getGroup(String groupId, Parameters parameters) throws EntityNotFoundException
{ {
AuthorityInfo authorityInfo = getAuthorityInfo(groupId); final List<String> includeParam = parameters.getInclude();
AuthorityInfo authorityInfo = getAuthorityInfo(groupId, includeParam.contains(PARAM_INCLUDE_DESCRIPTION));
final Set<String> rootAuthorities = getAllRootAuthorities(AuthorityType.GROUP); final Set<String> rootAuthorities = getAllRootAuthorities(AuthorityType.GROUP);
final List<String> includeParam = parameters.getInclude();
return getGroup(authorityInfo, includeParam, rootAuthorities); return getGroup(authorityInfo, includeParam, rootAuthorities);
} }
@@ -196,7 +212,7 @@ public class GroupsImpl implements Groups
PagingResults<AuthorityInfo> pagingResult; PagingResults<AuthorityInfo> pagingResult;
try try
{ {
pagingResult = getAuthoritiesInfo(authorityType, groupsFilters, rootAuthorities, sortProp, paging); pagingResult = getAuthoritiesInfo(authorityType, groupsFilters, rootAuthorities, sortProp, paging, parameters.getInclude().contains(PARAM_INCLUDE_DESCRIPTION));
} }
catch (UnknownAuthorityException e) catch (UnknownAuthorityException e)
{ {
@@ -213,7 +229,7 @@ public class GroupsImpl implements Groups
private List<Group> createGroupsResponse(final List<AuthorityInfo> page, final List<String> includeParam, final Set<String> rootAuthorities) private List<Group> createGroupsResponse(final List<AuthorityInfo> page, final List<String> includeParam, final Set<String> rootAuthorities)
{ {
List<Group> groups = new AbstractList<Group>() List<Group> groups = new AbstractList<>()
{ {
@Override @Override
public Group get(int index) public Group get(int index)
@@ -336,7 +352,7 @@ public class GroupsImpl implements Groups
filter(a -> a.startsWith(AuthorityType.GROUP.getPrefixString())). filter(a -> a.startsWith(AuthorityType.GROUP.getPrefixString())).
filter(a -> isRootPredicate(finalIsRootParam, rootAuthorities, a)). filter(a -> isRootPredicate(finalIsRootParam, rootAuthorities, a)).
filter(a -> zonePredicate(a, finalZoneFilter)). filter(a -> zonePredicate(a, finalZoneFilter)).
map(this::getAuthorityInfo). map(a -> getAuthorityInfo(a, includeParam.contains(PARAM_INCLUDE_DESCRIPTION))).
sorted(new AuthorityInfoComparator(sortProp.getFirst(), sortProp.getSecond())). sorted(new AuthorityInfoComparator(sortProp.getFirst(), sortProp.getSecond())).
collect(Collectors.toList()); collect(Collectors.toList());
@@ -355,23 +371,25 @@ public class GroupsImpl implements Groups
} }
private PagingResults<AuthorityInfo> getAuthoritiesInfo(AuthorityType authorityType, GroupsFilter groupsFilter, Set<String> rootAuthorities, private PagingResults<AuthorityInfo> getAuthoritiesInfo(AuthorityType authorityType, GroupsFilter groupsFilter, Set<String> rootAuthorities,
Pair<String, Boolean> sortProp, Paging paging) Pair<String, Boolean> sortProp, Paging paging, boolean includeDescription)
{ {
Boolean isRootParam = groupsFilter.getIsRoot(); Boolean isRootParam = groupsFilter.getIsRoot();
String zoneFilter = groupsFilter.getZoneFilter(); String zoneFilter = groupsFilter.getZoneFilter();
String displayNameFilter = groupsFilter.getDisplayNameFilter(); String displayNameFilter = groupsFilter.getDisplayNameFilter();
PagingResults<AuthorityInfo> pagingResult; PagingResults<AuthorityInfo> pagingResult;
if (isRootParam != null || displayNameFilter != null) // Don't use canned queries when fetching authorities with description
// if better performance is requested for loading descriptions we can add canned queries in the future
if (isRootParam != null || displayNameFilter != null || includeDescription)
{ {
List<AuthorityInfo> groupList; List<AuthorityInfo> groupList;
if (isRootParam != null && isRootParam) if ((isRootParam != null && isRootParam) || includeDescription)
{ {
// Limit the post processing work by using the already loaded // Limit the post processing work by using the already loaded
// list of root authorities. // list of root authorities.
List<AuthorityInfo> authorities = rootAuthorities.stream(). List<AuthorityInfo> authorities = rootAuthorities.stream().
map(this::getAuthorityInfo). map(auth -> getAuthorityInfo(auth, includeDescription)).
filter(auth -> zonePredicate(auth.getAuthorityName(), zoneFilter)). filter(auth -> zonePredicate(auth.getAuthorityName(), zoneFilter)).
filter(auth -> displayNamePredicate(auth.getAuthorityDisplayName(), displayNameFilter)). filter(auth -> displayNamePredicate(auth.getAuthorityDisplayName(), displayNameFilter)).
collect(Collectors.toList()); collect(Collectors.toList());
@@ -526,9 +544,9 @@ public class GroupsImpl implements Groups
* The authority name. * The authority name.
* @return The authority info. * @return The authority info.
*/ */
private AuthorityInfo getAuthorityInfo(String id) private AuthorityInfo getAuthorityInfo(String id, boolean includeDescription)
{ {
return getAuthorityInfo(id, false); return getAuthorityInfo(id, includeDescription, false);
} }
/** /**
@@ -537,11 +555,13 @@ public class GroupsImpl implements Groups
* *
* @param id * @param id
* The authority name. * The authority name.
* @param includeDescription
* True if description should be loaded
* @param defaultDisplayNameIfNull * @param defaultDisplayNameIfNull
* True if we would like to get a default value (e.g. shortName of the authority) if the authority display name is null. * True if we would like to get a default value (e.g. shortName of the authority) if the authority display name is null.
* @return The authority info. * @return The authority info.
*/ */
private AuthorityInfo getAuthorityInfo(String id, boolean defaultDisplayNameIfNull) private AuthorityInfo getAuthorityInfo(String id, boolean includeDescription, boolean defaultDisplayNameIfNull)
{ {
if (id == null || id.isEmpty()) if (id == null || id.isEmpty())
{ {
@@ -554,9 +574,20 @@ public class GroupsImpl implements Groups
throw new EntityNotFoundException(id); throw new EntityNotFoundException(id);
} }
String authorityDisplayName = getAuthorityDisplayName(id, defaultDisplayNameIfNull); String authorityDisplayName;
String description = null;
return new AuthorityInfo(null, authorityDisplayName, id); if (includeDescription)
{
Pair<String, String> displayNameAndDescription = getAuthorityDisplayNameAndDescription(id, defaultDisplayNameIfNull);
authorityDisplayName = displayNameAndDescription.getFirst();
description = displayNameAndDescription.getSecond();
}
else
{
authorityDisplayName = getAuthorityDisplayName(id, defaultDisplayNameIfNull);
}
return new AuthorityInfo(null, authorityDisplayName, id, description);
} }
private String getAuthorityDisplayName(String id, boolean defaultDisplayNameIfNull) private String getAuthorityDisplayName(String id, boolean defaultDisplayNameIfNull)
@@ -564,6 +595,11 @@ public class GroupsImpl implements Groups
return defaultDisplayNameIfNull ? authorityService.getAuthorityDisplayName(id) : authorityDAO.getAuthorityDisplayName(id); return defaultDisplayNameIfNull ? authorityService.getAuthorityDisplayName(id) : authorityDAO.getAuthorityDisplayName(id);
} }
private Pair<String, String> getAuthorityDisplayNameAndDescription(String id, boolean defaultDisplayNameIfNull)
{
return defaultDisplayNameIfNull ? authorityService.getAuthorityDisplayNameAndDescription(id) : authorityDAO.getAuthorityDisplayNameAndDescription(id);
}
private Group getGroup(AuthorityInfo authorityInfo, List<String> includeParam, Set<String> rootAuthorities) private Group getGroup(AuthorityInfo authorityInfo, List<String> includeParam, Set<String> rootAuthorities)
{ {
if (authorityInfo == null) if (authorityInfo == null)
@@ -576,13 +612,23 @@ public class GroupsImpl implements Groups
// REPO-1743 // REPO-1743
String authorityDisplayName = authorityInfo.getAuthorityDisplayName(); String authorityDisplayName = authorityInfo.getAuthorityDisplayName();
String description = authorityInfo.getDescription();
if (authorityDisplayName == null || authorityDisplayName.isEmpty()) if (authorityDisplayName == null || authorityDisplayName.isEmpty())
{ {
authorityDisplayName = authorityService.getAuthorityDisplayName(authorityInfo.getAuthorityName()); if (includeParam != null && includeParam.contains(PARAM_INCLUDE_DESCRIPTION))
{
Pair<String, String> displayNameAndDescription = authorityService.getAuthorityDisplayNameAndDescription(authorityInfo.getAuthorityName());
authorityDisplayName = displayNameAndDescription.getFirst();
description = displayNameAndDescription.getSecond();
}
else
{
authorityDisplayName = authorityService.getAuthorityDisplayName(authorityInfo.getAuthorityName());
}
} }
group.setDisplayName(authorityDisplayName); group.setDisplayName(authorityDisplayName);
group.setDescription(description);
group.setIsRoot(isRootAuthority(rootAuthorities, authorityInfo.getAuthorityName())); group.setIsRoot(isRootAuthority(rootAuthorities, authorityInfo.getAuthorityName()));
// Optionally include // Optionally include
@@ -606,6 +652,19 @@ public class GroupsImpl implements Groups
Set<String> authorityZones = authorityService.getAuthorityZones(authorityInfo.getAuthorityName()); Set<String> authorityZones = authorityService.getAuthorityZones(authorityInfo.getAuthorityName());
group.setZones(authorityZones); group.setZones(authorityZones);
} }
if (includeParam.contains(PARAM_INCLUDE_HAS_SUBGROUPS))
{
Set<String> containedAuthorities;
try
{
containedAuthorities = authorityService.getContainedAuthorities(AuthorityType.GROUP, authorityInfo.getAuthorityName(), true);
} catch (UnknownAuthorityException e)
{
containedAuthorities = Collections.emptySet();
}
group.setHasSubgroups(CollectionUtils.isNotEmpty(containedAuthorities));
}
} }
return group; return group;
@@ -621,7 +680,7 @@ public class GroupsImpl implements Groups
Pair<String, Boolean> sortProp; Pair<String, Boolean> sortProp;
List<SortColumn> sortCols = parameters.getSorting(); List<SortColumn> sortCols = parameters.getSorting();
if ((sortCols != null) && (sortCols.size() > 0)) if (sortCols != null && !sortCols.isEmpty())
{ {
if (sortCols.size() > 1) if (sortCols.size() > 1)
{ {
@@ -636,7 +695,7 @@ public class GroupsImpl implements Groups
throw new InvalidArgumentException("Invalid sort field: " + sortCol.column); throw new InvalidArgumentException("Invalid sort field: " + sortCol.column);
} }
sortProp = new Pair<>(sortPropName, (sortCol.asc ? Boolean.TRUE : Boolean.FALSE)); sortProp = new Pair<>(sortPropName, sortCol.asc ? Boolean.TRUE : Boolean.FALSE);
} }
else else
{ {
@@ -851,7 +910,6 @@ public class GroupsImpl implements Groups
validateGroupMemberId(groupMemberId); validateGroupMemberId(groupMemberId);
// Verify if groupMemberId is member of groupId // Verify if groupMemberId is member of groupId
AuthorityType authorityType = AuthorityType.getAuthorityType(groupMemberId);
Set<String> parents = authorityService.getContainingAuthorities(AuthorityType.GROUP, groupMemberId, true); Set<String> parents = authorityService.getContainingAuthorities(AuthorityType.GROUP, groupMemberId, true);
if (!parents.contains(groupId)) if (!parents.contains(groupId))
{ {
@@ -894,7 +952,7 @@ public class GroupsImpl implements Groups
} }
List<AuthorityInfo> authorityInfoList = new ArrayList<>(authorities.size()); List<AuthorityInfo> authorityInfoList = new ArrayList<>(authorities.size());
authorityInfoList.addAll(authorities.stream().map(this::getAuthorityInfo).collect(Collectors.toList())); authorityInfoList.addAll(authorities.stream().map(auth -> getAuthorityInfo(auth, false)).collect(Collectors.toList()));
// Post process sorting - this should be moved to service // Post process sorting - this should be moved to service
// layer. It is done here because sorting is not supported at // layer. It is done here because sorting is not supported at
@@ -943,7 +1001,7 @@ public class GroupsImpl implements Groups
private GroupMember getGroupMember(String authorityId) private GroupMember getGroupMember(String authorityId)
{ {
AuthorityInfo authorityInfo = getAuthorityInfo(authorityId); AuthorityInfo authorityInfo = getAuthorityInfo(authorityId, false);
return getGroupMember(authorityInfo); return getGroupMember(authorityInfo);
} }
@@ -1014,6 +1072,10 @@ public class GroupsImpl implements Groups
{ {
throw new InvalidArgumentException("Group update does not support field: zones"); throw new InvalidArgumentException("Group update does not support field: zones");
} }
if (group.wasSet(Group.HAS_SUBGROUPS))
{
throw new InvalidArgumentException("Group update does not support field: hasSubgroups");
}
} }
} }
@@ -1054,7 +1116,7 @@ public class GroupsImpl implements Groups
{ {
String name = inferPrefix ? authorityService.getName(authorityType, authorityName) : authorityName; String name = inferPrefix ? authorityService.getName(authorityType, authorityName) : authorityName;
return (name != null && authorityService.authorityExists(name)); return name != null && authorityService.authorityExists(name);
} }
private boolean isGroupAuthority(String authorityName) private boolean isGroupAuthority(String authorityName)

View File

@@ -42,7 +42,9 @@ public class Group implements Comparable<Group>
protected String id; // group id (aka authority name) protected String id; // group id (aka authority name)
protected String displayName; protected String displayName;
protected String description;
protected Boolean isRoot; protected Boolean isRoot;
protected Boolean hasSubgroups;
protected Set<String> parentIds; protected Set<String> parentIds;
protected Set<String> zones; protected Set<String> zones;
@@ -50,7 +52,9 @@ public class Group implements Comparable<Group>
public static final String ID = "id"; public static final String ID = "id";
public static final String DISPLAY_NAME = "displayName"; public static final String DISPLAY_NAME = "displayName";
public static final String DESCRIPTION = "description";
public static final String IS_ROOT = "isRoot"; public static final String IS_ROOT = "isRoot";
public static final String HAS_SUBGROUPS = "hasSubgroups";
public static final String PARENT_IDS = "parentIds"; public static final String PARENT_IDS = "parentIds";
public static final String ZONES = "zones"; public static final String ZONES = "zones";
@@ -81,6 +85,14 @@ public class Group implements Comparable<Group>
setFields.put(DISPLAY_NAME, true); setFields.put(DISPLAY_NAME, true);
} }
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Boolean getIsRoot() public Boolean getIsRoot()
{ {
return isRoot; return isRoot;
@@ -92,6 +104,14 @@ public class Group implements Comparable<Group>
setFields.put(IS_ROOT, true); setFields.put(IS_ROOT, true);
} }
public Boolean getHasSubgroups() {
return hasSubgroups;
}
public void setHasSubgroups(Boolean hasSubgroups) {
this.hasSubgroups = hasSubgroups;
}
public Set<String> getParentIds() public Set<String> getParentIds()
{ {
return parentIds; return parentIds;
@@ -154,12 +174,13 @@ public class Group implements Comparable<Group>
@Override @Override
public String toString() public String toString()
{ {
return "Group [id=" + id + ", displayName=" + displayName + ", isRoot=" + isRoot + "]"; return "Group [id=" + id + ", displayName=" + displayName + ", description=" + description
+ ", isRoot=" + isRoot + ", hasSubgroups=" + hasSubgroups + "]";
} }
public boolean wasSet(String fieldName) public boolean wasSet(String fieldName)
{ {
Boolean b = setFields.get(fieldName); Boolean b = setFields.get(fieldName);
return (b != null ? b : false); return b != null && b;
} }
} }

View File

@@ -43,17 +43,20 @@ import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.cmr.security.AuthorityType; import org.alfresco.service.cmr.security.AuthorityType;
import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.util.GUID; import org.alfresco.util.GUID;
import org.alfresco.util.testing.category.LuceneTests;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.mockito.Mock; import org.mockito.Mock;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import java.util.*; import java.util.*;
import static org.junit.Assert.*; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
/** /**
@@ -69,13 +72,13 @@ public class GroupsTest extends AbstractSingleNetworkSiteTest
protected AuthorityService authorityService; protected AuthorityService authorityService;
private String rootGroupName = null; private String rootGroupName;
private Group rootGroup = null; private Group rootGroup;
private Group groupA = null; private Group groupA;
private Group groupB = null; private Group groupB;
private GroupMember groupMemberA = null; private GroupMember groupMemberA;
private GroupMember groupMemberB = null; private GroupMember groupMemberB;
private GroupMember personMember = null; private GroupMember personMember;
@Mock @Mock
private ResultSetRow groupAResultSetRow; private ResultSetRow groupAResultSetRow;
@Mock @Mock
@@ -670,6 +673,7 @@ public class GroupsTest extends AbstractSingleNetworkSiteTest
// Optionally included. // Optionally included.
assertNull(group.getParentIds()); assertNull(group.getParentIds());
assertNull(group.getZones()); assertNull(group.getZones());
assertNull(group.getHasSubgroups());
} }
} }
@@ -956,7 +960,7 @@ public class GroupsTest extends AbstractSingleNetworkSiteTest
expected.retainAll(respPostProcess.getList()); expected.retainAll(respPostProcess.getList());
// If this assertion fails, then the tests aren't providing any value - change them! // If this assertion fails, then the tests aren't providing any value - change them!
assertTrue("List doesn't contain enough items for test to be conclusive.", expected.size() > 0); assertTrue("List doesn't contain enough items for test to be conclusive.", !expected.isEmpty());
checkList(expected, respPostProcess.getPaging(), respPostProcess); checkList(expected, respPostProcess.getPaging(), respPostProcess);
} }
@@ -977,7 +981,7 @@ public class GroupsTest extends AbstractSingleNetworkSiteTest
expected.retainAll(respPostProcess.getList()); expected.retainAll(respPostProcess.getList());
// If this assertion fails, then the tests aren't providing any value - change them! // If this assertion fails, then the tests aren't providing any value - change them!
assertTrue("List doesn't contain enough items for test to be conclusive.", expected.size() > 0); assertTrue("List doesn't contain enough items for test to be conclusive.", !expected.isEmpty());
checkList(expected, respPostProcess.getPaging(), respPostProcess); checkList(expected, respPostProcess.getPaging(), respPostProcess);
} }
@@ -1154,7 +1158,6 @@ public class GroupsTest extends AbstractSingleNetworkSiteTest
// -ve test: invalid zones clause // -ve test: invalid zones clause
{ {
Paging paging = getPaging(0, Integer.MAX_VALUE);
Map<String, String> otherParams = new HashMap<>(); Map<String, String> otherParams = new HashMap<>();
otherParams.put("include", org.alfresco.rest.api.Groups.PARAM_INCLUDE_ZONES); otherParams.put("include", org.alfresco.rest.api.Groups.PARAM_INCLUDE_ZONES);
@@ -1418,16 +1421,17 @@ public class GroupsTest extends AbstractSingleNetworkSiteTest
setRequestContext(networkOne.getId(), networkAdmin, DEFAULT_ADMIN_PWD); setRequestContext(networkOne.getId(), networkAdmin, DEFAULT_ADMIN_PWD);
Map<String, String> otherParams = new HashMap<>(); Map<String, String> otherParams = new HashMap<>();
otherParams.put("include", org.alfresco.rest.api.Groups.PARAM_INCLUDE_PARENT_IDS); otherParams.put("include", org.alfresco.rest.api.Groups.PARAM_INCLUDE_HAS_SUBGROUPS);
Group group = generateGroup(); Group group = generateGroup();
Group createdGroup01 = groupsProxy.createGroup(group, null, HttpServletResponse.SC_CREATED); Group createdGroup01 = groupsProxy.createGroup(group, otherParams, HttpServletResponse.SC_CREATED);
assertNotNull(createdGroup01); assertNotNull(createdGroup01);
assertNotNull(createdGroup01.getId()); assertNotNull(createdGroup01.getId());
assertTrue(createdGroup01.getIsRoot()); assertTrue(createdGroup01.getIsRoot());
assertNull(createdGroup01.getParentIds()); assertNull(createdGroup01.getParentIds());
assertFalse(createdGroup01.getHasSubgroups());
Set<String> subGroup01Parents = new HashSet<>(); Set<String> subGroup01Parents = new HashSet<>();
subGroup01Parents.add(createdGroup01.getId()); subGroup01Parents.add(createdGroup01.getId());
@@ -1435,12 +1439,18 @@ public class GroupsTest extends AbstractSingleNetworkSiteTest
Group subGroup01 = generateGroup(); Group subGroup01 = generateGroup();
subGroup01.setParentIds(subGroup01Parents); subGroup01.setParentIds(subGroup01Parents);
otherParams.put("include", org.alfresco.rest.api.Groups.PARAM_INCLUDE_PARENT_IDS + "," + org.alfresco.rest.api.Groups.PARAM_INCLUDE_HAS_SUBGROUPS);
Group createdSubGroup01 = groupsProxy.createGroup(subGroup01, otherParams, HttpServletResponse.SC_CREATED); Group createdSubGroup01 = groupsProxy.createGroup(subGroup01, otherParams, HttpServletResponse.SC_CREATED);
assertNotNull(createdSubGroup01); assertNotNull(createdSubGroup01);
assertNotNull(createdSubGroup01.getId()); assertNotNull(createdSubGroup01.getId());
assertFalse(createdSubGroup01.getIsRoot()); assertFalse(createdSubGroup01.getIsRoot());
assertNotNull(createdSubGroup01.getParentIds()); assertNotNull(createdSubGroup01.getParentIds());
assertEquals(subGroup01Parents, createdSubGroup01.getParentIds()); assertEquals(subGroup01Parents, createdSubGroup01.getParentIds());
assertFalse(createdSubGroup01.getHasSubgroups());
//validate if parent group now has any subgroup
Group group01 = groupsProxy.getGroup(createdGroup01.getId(), otherParams, HttpServletResponse.SC_OK);
assertTrue(group01.getHasSubgroups());
} }
// Group id is missing. // Group id is missing.

View File

@@ -58,7 +58,9 @@ public class Group extends org.alfresco.rest.api.model.Group implements Serializ
AssertUtil.assertEquals("id", getId(), other.getId()); AssertUtil.assertEquals("id", getId(), other.getId());
AssertUtil.assertEquals("displayName", getDisplayName(), other.getDisplayName()); AssertUtil.assertEquals("displayName", getDisplayName(), other.getDisplayName());
AssertUtil.assertEquals("description", getDescription(), other.getDescription());
AssertUtil.assertEquals("isRoot", getIsRoot(), other.getIsRoot()); AssertUtil.assertEquals("isRoot", getIsRoot(), other.getIsRoot());
AssertUtil.assertEquals("hasSubgroups", getHasSubgroups(), other.getHasSubgroups());
AssertUtil.assertEquals("parentIds", getParentIds(), other.getParentIds()); AssertUtil.assertEquals("parentIds", getParentIds(), other.getParentIds());
AssertUtil.assertEquals("zones", getZones(), other.getZones()); AssertUtil.assertEquals("zones", getZones(), other.getZones());
} }
@@ -73,11 +75,21 @@ public class Group extends org.alfresco.rest.api.model.Group implements Serializ
groupJson.put("displayName", getDisplayName()); groupJson.put("displayName", getDisplayName());
if (getDescription() != null)
{
groupJson.put("description", getDescription());
}
if (getIsRoot() != null) if (getIsRoot() != null)
{ {
groupJson.put("isRoot", getIsRoot()); groupJson.put("isRoot", getIsRoot());
} }
if (getHasSubgroups() != null)
{
groupJson.put("hasSubgroups", getHasSubgroups());
}
if (getParentIds() != null) if (getParentIds() != null)
{ {
groupJson.put("parentIds", new ArrayList(getParentIds())); groupJson.put("parentIds", new ArrayList(getParentIds()));
@@ -95,16 +107,19 @@ public class Group extends org.alfresco.rest.api.model.Group implements Serializ
{ {
String id = (String) jsonObject.get("id"); String id = (String) jsonObject.get("id");
String displayName = (String) jsonObject.get("displayName"); String displayName = (String) jsonObject.get("displayName");
String description = (String) jsonObject.get("description");
Boolean isRoot = (Boolean) jsonObject.get("isRoot"); Boolean isRoot = (Boolean) jsonObject.get("isRoot");
Boolean hasSubgroups = (Boolean) jsonObject.get("hasSubgroups");
List<String> parentIds = (List<String>) jsonObject.get("parentIds"); List<String> parentIds = (List<String>) jsonObject.get("parentIds");
List<String> zones = (List<String>) jsonObject.get("zones"); List<String> zones = (List<String>) jsonObject.get("zones");
Group group = new Group(); Group group = new Group();
group.setId(id); group.setId(id);
group.setDisplayName(displayName); group.setDisplayName(displayName);
group.setDescription(description);
group.setIsRoot(isRoot); group.setIsRoot(isRoot);
group.setParentIds(parentIds != null ? new HashSet<String>(parentIds) : null); group.setHasSubgroups(hasSubgroups);
group.setZones(zones != null ? new HashSet<String>(zones) : null); group.setParentIds(parentIds != null ? new HashSet<>(parentIds) : null);
group.setZones(zones != null ? new HashSet<>(zones) : null);
return group; return group;
} }

View File

@@ -1,31 +1,33 @@
/* /*
* #%L * #%L
* Alfresco Repository * Alfresco Repository
* %% * %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited * Copyright (C) 2005 - 2016 Alfresco Software Limited
* %% * %%
* This file is part of the Alfresco software. * This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of * If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is * the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms: * provided under the following open source license terms:
* *
* Alfresco is free software: you can redistribute it and/or modify * 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 * 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 * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* Alfresco is distributed in the hope that it will be useful, * Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L% * #L%
*/ */
package org.alfresco.repo.security.authority; package org.alfresco.repo.security.authority;
import java.io.Serializable;
import java.util.Collection; import java.util.Collection;
import java.util.Map;
import java.util.Set; import java.util.Set;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
@@ -34,6 +36,8 @@ import org.alfresco.query.PagingResults;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.security.AuthorityType; import org.alfresco.service.cmr.security.AuthorityType;
import org.alfresco.service.cmr.security.AuthorityService.AuthorityFilter; import org.alfresco.service.cmr.security.AuthorityService.AuthorityFilter;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.Pair;
public interface AuthorityDAO public interface AuthorityDAO
{ {
@@ -61,6 +65,11 @@ public interface AuthorityDAO
*/ */
void createAuthority(String name, String authorityDisplayName, Set<String> authorityZones); void createAuthority(String name, String authorityDisplayName, Set<String> authorityZones);
/**
* Create an authority with properties.
*/
void createAuthority(String name, String authorityDisplayName, Set<String> authorityZones, Map<QName, Serializable> properties);
/** /**
* Delete an authority. * Delete an authority.
*/ */
@@ -142,7 +151,19 @@ public interface AuthorityDAO
* Set the display name for an authority * Set the display name for an authority
*/ */
void setAuthorityDisplayName(String authorityName, String authorityDisplayName); void setAuthorityDisplayName(String authorityName, String authorityDisplayName);
/**
* Get the display name and description for an authority
*
* @return the display name and description
*/
Pair<String, String> getAuthorityDisplayNameAndDescription(String authorityName);
/**
* Set the display name and description for an authority
*/
void setAuthorityDisplayNameAndDescription(String authorityName, String authorityDisplayName, String description);
/** /**
* Get root authorities * Get root authorities
*/ */

View File

@@ -92,6 +92,7 @@ import org.alfresco.util.PropertyCheck;
import org.alfresco.util.SearchLanguageConversion; import org.alfresco.util.SearchLanguageConversion;
import org.alfresco.util.registry.NamedObjectRegistry; import org.alfresco.util.registry.NamedObjectRegistry;
import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.logging.Log; 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;
@@ -378,27 +379,38 @@ public class AuthorityDAOImpl implements AuthorityDAO, NodeServicePolicies.Befor
} }
} }
public void createAuthority(String name, String authorityDisplayName, Set<String> authorityZones) @Override
public void createAuthority(String name, String authorityDisplayName, Set<String> authorityZones) {
createAuthority(name, authorityDisplayName, authorityZones, null);
}
@Override
public void createAuthority(String name, String authorityDisplayName, Set<String> authorityZones, Map<QName, Serializable> properties)
{ {
HashMap<QName, Serializable> props = new HashMap<QName, Serializable>(); Map<QName, Serializable> props = new HashMap<>();
/* MNT-11749 : Alfresco allows to create authorities with different char cases, but disallow duplicates */ /* MNT-11749 : Alfresco allows to create authorities with different char cases, but disallow duplicates */
props.put(ContentModel.PROP_NAME, DigestUtils.md5Hex(name)); props.put(ContentModel.PROP_NAME, DigestUtils.md5Hex(name));
props.put(ContentModel.PROP_AUTHORITY_NAME, name); props.put(ContentModel.PROP_AUTHORITY_NAME, name);
props.put(ContentModel.PROP_AUTHORITY_DISPLAY_NAME, authorityDisplayName); props.put(ContentModel.PROP_AUTHORITY_DISPLAY_NAME, authorityDisplayName);
if (properties != null)
{
props.putAll(properties);
}
NodeRef childRef; NodeRef childRef;
NodeRef authorityContainerRef = getAuthorityContainer(); NodeRef authorityContainerRef = getAuthorityContainer();
logger.info("AuthorityDAO before create node");
childRef = nodeService.createNode(authorityContainerRef, ContentModel.ASSOC_CHILDREN, QName.createQName("cm", name, namespacePrefixResolver), childRef = nodeService.createNode(authorityContainerRef, ContentModel.ASSOC_CHILDREN, QName.createQName("cm", name, namespacePrefixResolver),
ContentModel.TYPE_AUTHORITY_CONTAINER, props).getChildRef(); ContentModel.TYPE_AUTHORITY_CONTAINER, props).getChildRef();
if (authorityZones != null) if (authorityZones != null)
{ {
Set<NodeRef> zoneRefs = new HashSet<NodeRef>(authorityZones.size() * 2); Set<NodeRef> zoneRefs = new HashSet<>(authorityZones.size() * 2);
String currentUserDomain = tenantService.getCurrentUserDomain(); String currentUserDomain = tenantService.getCurrentUserDomain();
for (String authorityZone : authorityZones) for (String authorityZone : authorityZones)
{ {
zoneRefs.add(getOrCreateZone(authorityZone)); zoneRefs.add(getOrCreateZone(authorityZone));
zoneAuthorityCache.remove(new Pair<String, String>(currentUserDomain, authorityZone)); zoneAuthorityCache.remove(new Pair<>(currentUserDomain, authorityZone));
} }
zoneAuthorityCache.remove(new Pair<String, String>(currentUserDomain, null)); zoneAuthorityCache.remove(new Pair<>(currentUserDomain, null));
nodeService.addChild(zoneRefs, childRef, ContentModel.ASSOC_IN_ZONE, QName.createQName("cm", name, namespacePrefixResolver)); nodeService.addChild(zoneRefs, childRef, ContentModel.ASSOC_IN_ZONE, QName.createQName("cm", name, namespacePrefixResolver));
} }
authorityLookupCache.put(cacheKey(name), childRef); authorityLookupCache.put(cacheKey(name), childRef);
@@ -1431,7 +1443,33 @@ public class AuthorityDAOImpl implements AuthorityDAO, NodeServicePolicies.Befor
return; return;
} }
nodeService.setProperty(ref, ContentModel.PROP_AUTHORITY_DISPLAY_NAME, authorityDisplayName); nodeService.setProperty(ref, ContentModel.PROP_AUTHORITY_DISPLAY_NAME, authorityDisplayName);
}
@Override
public Pair<String, String> getAuthorityDisplayNameAndDescription(String authorityName)
{
NodeRef ref = getAuthorityOrNull(authorityName);
if (ref == null)
{
return Pair.nullPair();
}
String displayName = getAuthorityDisplayName(authorityName);
Serializable description = nodeService.getProperty(ref, ContentModel.PROP_DESCRIPTION);
return new Pair<>(displayName, DefaultTypeConverter.INSTANCE.convert(String.class, description));
}
@Override
public void setAuthorityDisplayNameAndDescription(String authorityName, String authorityDisplayName, String description)
{
NodeRef ref = getAuthorityOrNull(authorityName);
if (ref == null)
{
return;
}
Map<QName, Serializable> properties = nodeService.getProperties(ref);
properties.put(ContentModel.PROP_AUTHORITY_DISPLAY_NAME, authorityDisplayName);
properties.put(ContentModel.PROP_DESCRIPTION, description);
nodeService.setProperties(ref, properties);
} }
public NodeRef getOrCreateZone(String zoneName) public NodeRef getOrCreateZone(String zoneName)

View File

@@ -1,28 +1,28 @@
/* /*
* #%L * #%L
* Alfresco Repository * Alfresco Repository
* %% * %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited * Copyright (C) 2005 - 2016 Alfresco Software Limited
* %% * %%
* This file is part of the Alfresco software. * This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of * If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is * the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms: * provided under the following open source license terms:
* *
* Alfresco is free software: you can redistribute it and/or modify * 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 * 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 * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* Alfresco is distributed in the hope that it will be useful, * Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L% * #L%
*/ */
package org.alfresco.repo.security.authority; package org.alfresco.repo.security.authority;
import org.alfresco.api.AlfrescoPublicApi; import org.alfresco.api.AlfrescoPublicApi;
@@ -39,15 +39,24 @@ import org.alfresco.service.cmr.security.AuthorityType;
public class AuthorityInfo public class AuthorityInfo
{ {
private Long nodeId; private Long nodeId;
private String authorityDisplayName; // eg. My Group, My Role private String authorityDisplayName; // eg. My Group, My Role
private String authorityName; // eg. GROUP_my1, ROLE_myA private String authorityName; // eg. GROUP_my1, ROLE_myA
private String description;
public AuthorityInfo(Long nodeId, String authorityDisplayName, String authorityName, String description)
{
this.nodeId = nodeId;
this.authorityDisplayName = authorityDisplayName;
this.authorityName = authorityName;
this.description = description;
}
public AuthorityInfo(Long nodeId, String authorityDisplayName, String authorityName) public AuthorityInfo(Long nodeId, String authorityDisplayName, String authorityName)
{ {
this.nodeId = nodeId; this.nodeId = nodeId;
this.authorityDisplayName = authorityDisplayName; this.authorityDisplayName = authorityDisplayName;
this.authorityName = authorityName; this.authorityName = authorityName;
this.description = null;
} }
public Long getNodeId() public Long getNodeId()
@@ -65,6 +74,10 @@ public class AuthorityInfo
return authorityName; return authorityName;
} }
public String getDescription() {
return description;
}
public String getShortName() public String getShortName()
{ {
AuthorityType type = AuthorityType.getAuthorityType(authorityName); AuthorityType type = AuthorityType.getAuthorityType(authorityName);

View File

@@ -25,6 +25,7 @@
*/ */
package org.alfresco.repo.security.authority; package org.alfresco.repo.security.authority;
import java.io.Serializable;
import java.util.AbstractSet; import java.util.AbstractSet;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
@@ -32,6 +33,7 @@ import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.TreeSet; import java.util.TreeSet;
@@ -53,6 +55,7 @@ import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.cmr.security.AuthorityType; import org.alfresco.service.cmr.security.AuthorityType;
import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.Pair; import org.alfresco.util.Pair;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.extensions.surf.util.ParameterCheck; import org.springframework.extensions.surf.util.ParameterCheck;
@@ -65,7 +68,6 @@ import org.springframework.extensions.surf.util.ParameterCheck;
public class AuthorityServiceImpl implements AuthorityService, InitializingBean public class AuthorityServiceImpl implements AuthorityService, InitializingBean
{ {
public static final String GROUP_ALFRESCO_SYSTEM_ADMINISTRATORS_AUTHORITY = PermissionService.GROUP_PREFIX + "ALFRESCO_SYSTEM_ADMINISTRATORS"; public static final String GROUP_ALFRESCO_SYSTEM_ADMINISTRATORS_AUTHORITY = PermissionService.GROUP_PREFIX + "ALFRESCO_SYSTEM_ADMINISTRATORS";
private static Set<String> DEFAULT_ZONES = new HashSet<String>(); private static Set<String> DEFAULT_ZONES = new HashSet<String>();
static static
@@ -543,6 +545,15 @@ public class AuthorityServiceImpl implements AuthorityService, InitializingBean
{ {
return createAuthority(type, shortName, shortName, getDefaultZones()); return createAuthority(type, shortName, shortName, getDefaultZones());
} }
/**
* {@inheritDoc}
*/
@Override
public String createAuthority(AuthorityType type, String shortName, Map<QName, Serializable> properties)
{
return createAuthority(type, shortName, shortName, getDefaultZones(), properties);
}
/** /**
* {@inheritDoc} * {@inheritDoc}
@@ -643,12 +654,21 @@ public class AuthorityServiceImpl implements AuthorityService, InitializingBean
*/ */
public String createAuthority(AuthorityType type, String shortName, String authorityDisplayName, public String createAuthority(AuthorityType type, String shortName, String authorityDisplayName,
Set<String> authorityZones) Set<String> authorityZones)
{
return createAuthority(type, shortName, authorityDisplayName, authorityZones, null);
}
/**
* {@inheritDoc}
*/
@Override
public String createAuthority(AuthorityType type, String shortName, String authorityDisplayName,
Set<String> authorityZones, Map<QName, Serializable> properties)
{ {
checkTypeIsMutable(type); checkTypeIsMutable(type);
String name = getName(type, shortName); String name = getName(type, shortName);
authorityDAO.createAuthority(name, authorityDisplayName, authorityZones, properties);
authorityDAO.createAuthority(name, authorityDisplayName, authorityZones);
return name; return name;
} }
@@ -674,6 +694,31 @@ public class AuthorityServiceImpl implements AuthorityService, InitializingBean
checkTypeIsMutable(type); checkTypeIsMutable(type);
authorityDAO.setAuthorityDisplayName(authorityName, authorityDisplayName); authorityDAO.setAuthorityDisplayName(authorityName, authorityDisplayName);
} }
/**
* {@inheritDoc}
*/
@Override
public Pair<String, String> getAuthorityDisplayNameAndDescription(String name)
{
Pair<String, String> displayNameAndDescription = authorityDAO.getAuthorityDisplayNameAndDescription(name);
if(displayNameAndDescription.getFirst() == null)
{
displayNameAndDescription.setFirst(getShortName(name));
}
return displayNameAndDescription;
}
/**
* {@inheritDoc}
*/
@Override
public void setAuthorityDisplayNameAndDescription(String authorityName, String authorityDisplayName, String description)
{
AuthorityType type = AuthorityType.getAuthorityType(authorityName);
checkTypeIsMutable(type);
authorityDAO.setAuthorityDisplayNameAndDescription(authorityName, authorityDisplayName, description);
}
/** /**
* {@inheritDoc} * {@inheritDoc}

View File

@@ -25,7 +25,9 @@
*/ */
package org.alfresco.service.cmr.security; package org.alfresco.service.cmr.security;
import java.io.Serializable;
import java.util.Collection; import java.util.Collection;
import java.util.Map;
import java.util.Set; import java.util.Set;
import org.alfresco.api.AlfrescoPublicApi; import org.alfresco.api.AlfrescoPublicApi;
@@ -35,6 +37,8 @@ import org.alfresco.repo.security.authority.AuthorityInfo;
import org.alfresco.service.Auditable; import org.alfresco.service.Auditable;
import org.alfresco.service.NotAuditable; import org.alfresco.service.NotAuditable;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.Pair;
/** /**
* The service that encapsulates authorities granted to users. * The service that encapsulates authorities granted to users.
@@ -225,6 +229,24 @@ public interface AuthorityService
@Auditable(parameters = {"type", "shortName"}) @Auditable(parameters = {"type", "shortName"})
public String createAuthority(AuthorityType type, String shortName); public String createAuthority(AuthorityType type, String shortName);
/**
* Create an authority with properties.
*
* @param type -
* the type of the authority
* @param shortName -
* the short name of the authority to create
* this will also be set as the default display name for the authority
*
* @param properties -
* properties that will be added to authority
*
* @return the name of the authority (this will be the prefix, if any
* associated with the type appended with the short name)
*/
@Auditable(parameters = {"type", "shortName"})
String createAuthority(AuthorityType type, String shortName, Map<QName, Serializable> properties);
/** /**
* Create an authority with a display name and zone. * Create an authority with a display name and zone.
* *
@@ -242,6 +264,27 @@ public interface AuthorityService
@Auditable(parameters = {"type", "shortName", "authorityDisplayName", "authorityZones"}) @Auditable(parameters = {"type", "shortName", "authorityDisplayName", "authorityZones"})
public String createAuthority(AuthorityType type, String shortName, String authorityDisplayName, Set<String> authorityZones); public String createAuthority(AuthorityType type, String shortName, String authorityDisplayName, Set<String> authorityZones);
/**
* Create an authority with a display name and zone.
*
* @param type
* the type of the authority
* @param shortName
* the short name of the authority to create
* @param authorityDisplayName
* the display name for the authority
* @param authorityZones
* identifier for external user registry owning the authority or <code>null</code> if not applicable
*
* @param properties -
* properties that will be added to authority
*
* @return the full name of the authority (this will be the prefix, if any associated with the type appended with
* the short name)
*/
@Auditable(parameters = {"type", "shortName", "authorityDisplayName", "authorityZones"})
String createAuthority(AuthorityType type, String shortName, String authorityDisplayName, Set<String> authorityZones, Map<QName, Serializable> properties);
/** /**
* Set an authority to include another authority. For example, adding a * Set an authority to include another authority. For example, adding a
* group to a group or adding a user to a group. * group to a group or adding a user to a group.
@@ -399,7 +442,16 @@ public interface AuthorityService
* @return - the display name * @return - the display name
*/ */
@Auditable(parameters = {"name"}) @Auditable(parameters = {"name"})
public String getAuthorityDisplayName(String name); String getAuthorityDisplayName(String name);
/**
* Get the display name and description for the given authority.
*
* @param name - the full authority string including any prefix (e.g. GROUP_woof)
* @return - pair containing display name and description
*/
@Auditable(parameters = {"name"})
Pair<String, String> getAuthorityDisplayNameAndDescription(String name);
/** /**
* Set the display name for the given authority. * Set the display name for the given authority.
@@ -409,7 +461,18 @@ public interface AuthorityService
* @param authorityDisplayName String * @param authorityDisplayName String
*/ */
@Auditable(parameters = {"authorityName", "authorityDisplayName"}) @Auditable(parameters = {"authorityName", "authorityDisplayName"})
public void setAuthorityDisplayName(String authorityName, String authorityDisplayName); void setAuthorityDisplayName(String authorityName, String authorityDisplayName);
/**
* Set the display name and description for the given authority.
* Setting the display name is only supported for authorities of type group
*
* @param authorityName String
* @param authorityDisplayName String
* @param description String
*/
@Auditable(parameters = {"authorityName", "authorityDisplayName", "description"})
void setAuthorityDisplayNameAndDescription(String authorityName, String authorityDisplayName, String description);
/** /**
* Gets the authority node for the specified name * Gets the authority node for the specified name

View File

@@ -821,6 +821,8 @@
org.alfresco.service.cmr.security.AuthorityService.authorityExists=ACL_ALLOW org.alfresco.service.cmr.security.AuthorityService.authorityExists=ACL_ALLOW
org.alfresco.service.cmr.security.AuthorityService.setAuthorityDisplayName=ACL_METHOD.ROLE_ADMINISTRATOR org.alfresco.service.cmr.security.AuthorityService.setAuthorityDisplayName=ACL_METHOD.ROLE_ADMINISTRATOR
org.alfresco.service.cmr.security.AuthorityService.getAuthorityDisplayName=ACL_ALLOW org.alfresco.service.cmr.security.AuthorityService.getAuthorityDisplayName=ACL_ALLOW
org.alfresco.service.cmr.security.AuthorityService.setAuthorityDisplayNameAndDescription=ACL_METHOD.ROLE_ADMINISTRATOR
org.alfresco.service.cmr.security.AuthorityService.getAuthorityDisplayNameAndDescription=ACL_ALLOW
org.alfresco.service.cmr.security.AuthorityService.getOrCreateZone=ACL_METHOD.ROLE_ADMINISTRATOR org.alfresco.service.cmr.security.AuthorityService.getOrCreateZone=ACL_METHOD.ROLE_ADMINISTRATOR
org.alfresco.service.cmr.security.AuthorityService.getZone=ACL_ALLOW org.alfresco.service.cmr.security.AuthorityService.getZone=ACL_ALLOW
org.alfresco.service.cmr.security.AuthorityService.getAuthorityZones=ACL_ALLOW org.alfresco.service.cmr.security.AuthorityService.getAuthorityZones=ACL_ALLOW

View File

@@ -83,9 +83,11 @@ import org.alfresco.service.namespace.RegexQNamePattern;
import org.alfresco.service.transaction.TransactionService; import org.alfresco.service.transaction.TransactionService;
import org.alfresco.test_category.OwnJVMTestsCategory; import org.alfresco.test_category.OwnJVMTestsCategory;
import org.alfresco.util.ApplicationContextHelper; import org.alfresco.util.ApplicationContextHelper;
import org.alfresco.util.Pair;
import org.alfresco.util.testing.category.LuceneTests; import org.alfresco.util.testing.category.LuceneTests;
import org.alfresco.util.testing.category.RedundantTests; import org.alfresco.util.testing.category.RedundantTests;
import org.junit.FixMethodOrder; import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.experimental.categories.Category; import org.junit.experimental.categories.Category;
import org.junit.runners.MethodSorters; import org.junit.runners.MethodSorters;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
@@ -581,7 +583,40 @@ public class AuthorityServiceTest extends TestCase
// Ignore since we where expecting this // Ignore since we where expecting this
} }
} }
@Test
public void testCreateGroupAuthWithProperties()
{
String auth;
String groupName = "TESTGROUP";
String prefixedGroupName = "GROUP_TESTGROUP";
String description = "testDesc";
String title = "testTitle";
Map<QName, Serializable> props = new HashMap<>();
props.put(ContentModel.PROP_DESCRIPTION, description);
props.put(ContentModel.PROP_TITLE, title);
// create authority with properties and default zones
auth = pubAuthorityService.createAuthority(AuthorityType.GROUP, groupName, props);
assertTrue(pubAuthorityService.authorityExists(prefixedGroupName));
NodeRef nodeRef = pubAuthorityService.getAuthorityNodeRef(auth);
assertEquals(nodeService.getProperty(nodeRef, ContentModel.PROP_DESCRIPTION), description);
assertEquals(nodeService.getProperty(nodeRef, ContentModel.PROP_TITLE), title);
pubAuthorityService.deleteAuthority(auth);
// create authority with zones and properties
Set<String> zones = new HashSet<>();
zones.add("Test1");
zones.add("Test2");
auth = pubAuthorityService.createAuthority(AuthorityType.GROUP, groupName, prefixedGroupName, zones, props);
assertTrue(pubAuthorityService.authorityExists(prefixedGroupName));
nodeRef = pubAuthorityService.getAuthorityNodeRef(auth);
assertEquals(nodeService.getProperty(nodeRef, ContentModel.PROP_DESCRIPTION), description);
assertEquals(nodeService.getProperty(nodeRef, ContentModel.PROP_TITLE), title);
assertEquals(2, pubAuthorityService.getAuthorityZones(auth).size());
pubAuthorityService.deleteAuthority(auth);
}
public void testCreateOwnerAuth() public void testCreateOwnerAuth()
{ {
try try
@@ -1373,7 +1408,6 @@ public class AuthorityServiceTest extends TestCase
properties.put(ContentModel.PROP_ORGID, orgId); properties.put(ContentModel.PROP_ORGID, orgId);
return properties; return properties;
} }
public void testAuthorityDisplayNames() public void testAuthorityDisplayNames()
{ {
String authOne = pubAuthorityService.createAuthority(AuthorityType.GROUP, "One"); String authOne = pubAuthorityService.createAuthority(AuthorityType.GROUP, "One");