Skip to content

Commit f3ab813

Browse files
fix: filter user groups by env scope in query parameter
1 parent d1480b2 commit f3ab813

File tree

7 files changed

+294
-22
lines changed

7 files changed

+294
-22
lines changed

gravitee-apim-rest-api/gravitee-apim-rest-api-management/gravitee-apim-rest-api-management-rest/src/main/java/io/gravitee/rest/api/management/rest/resource/organization/UserResource.java

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@
4747
import java.util.HashMap;
4848
import java.util.List;
4949
import java.util.Set;
50+
import org.slf4j.Logger;
51+
import org.slf4j.LoggerFactory;
5052

5153
/**
5254
* Defines the REST resources to manage Users.
@@ -60,6 +62,8 @@
6062
@Tag(name = "Users")
6163
public class UserResource extends AbstractResource {
6264

65+
private static final Logger LOGGER = LoggerFactory.getLogger(UserResource.class);
66+
6367
@Context
6468
private ResourceContext resourceContext;
6569

@@ -120,31 +124,31 @@ public Response deleteUser() {
120124
@ApiResponse(responseCode = "404", description = "User not found")
121125
@ApiResponse(responseCode = "500", description = "Internal server error")
122126
@Permissions(@Permission(value = RolePermission.ORGANIZATION_USERS, acls = RolePermissionAction.READ))
123-
public List<UserGroupEntity> getUserGroups() {
127+
public List<UserGroupEntity> getUserGroups(@QueryParam("environmentId") String environmentId) {
124128
// Check that user belongs to current organization
125129
userService.findById(GraviteeContext.getExecutionContext(), userId);
130+
LOGGER.debug("Search groups for user: {} with query: {}", userId, environmentId);
131+
Set<GroupEntity> groups = groupService.findByUserAndEnvironment(userId, environmentId);
132+
List<UserGroupEntity> result = new ArrayList<>();
133+
groups.forEach(groupEntity -> {
134+
UserGroupEntity userGroupEntity = new UserGroupEntity();
135+
userGroupEntity.setId(groupEntity.getId());
136+
userGroupEntity.setName(groupEntity.getName());
137+
userGroupEntity.setRoles(new HashMap<>());
138+
userGroupEntity.setEnvironmentId(groupEntity.getEnvironmentId());
139+
Set<RoleEntity> roles = membershipService.getRoles(
140+
MembershipReferenceType.GROUP,
141+
groupEntity.getId(),
142+
MembershipMemberType.USER,
143+
userId
144+
);
145+
if (!roles.isEmpty()) {
146+
roles.forEach(role -> userGroupEntity.getRoles().put(role.getScope().name(), role.getName()));
147+
}
148+
result.add(userGroupEntity);
149+
});
126150

127-
List<UserGroupEntity> groups = new ArrayList<>();
128-
groupService
129-
.findByUser(userId)
130-
.forEach(groupEntity -> {
131-
UserGroupEntity userGroupEntity = new UserGroupEntity();
132-
userGroupEntity.setId(groupEntity.getId());
133-
userGroupEntity.setName(groupEntity.getName());
134-
userGroupEntity.setRoles(new HashMap<>());
135-
Set<RoleEntity> roles = membershipService.getRoles(
136-
MembershipReferenceType.GROUP,
137-
groupEntity.getId(),
138-
MembershipMemberType.USER,
139-
userId
140-
);
141-
if (!roles.isEmpty()) {
142-
roles.forEach(role -> userGroupEntity.getRoles().put(role.getScope().name(), role.getName()));
143-
}
144-
groups.add(userGroupEntity);
145-
});
146-
147-
return groups;
151+
return result;
148152
}
149153

150154
@GET
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
* Copyright © 2015 The Gravitee team (http://gravitee.io)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.gravitee.rest.api.management.rest.resource;
17+
18+
import static org.junit.Assert.*;
19+
import static org.mockito.Mockito.*;
20+
21+
import io.gravitee.common.http.HttpStatusCode;
22+
import io.gravitee.rest.api.model.*;
23+
import jakarta.ws.rs.core.GenericType;
24+
import jakarta.ws.rs.core.Response;
25+
import java.util.*;
26+
import org.junit.Before;
27+
import org.junit.Test;
28+
import org.mockito.Mockito;
29+
30+
/**
31+
* Test for UserResource.getUserGroups() method
32+
*/
33+
public class UserResourceTest extends AbstractResourceTest {
34+
35+
private static final String USER_ID = "test-user";
36+
private static final String ENV_ID = "test-env";
37+
38+
@Before
39+
public void resetMocks() {
40+
Mockito.reset(groupService, membershipService);
41+
}
42+
43+
@Override
44+
protected String contextPath() {
45+
// Matches the REST path you’re testing, e.g. /users/{userId}/groups?environmentId=ENV_ID
46+
return "users/" + USER_ID + "/groups/";
47+
}
48+
49+
@Test
50+
public void shouldReturnAllGroupsIncludingEnvGroups() {
51+
GroupEntity group1 = new GroupEntity();
52+
group1.setId("group1");
53+
group1.setName("Global Group");
54+
GroupEntity group2 = new GroupEntity();
55+
group2.setId("group2");
56+
group2.setName("Env Group");
57+
58+
when(groupService.findByUserAndEnvironment(USER_ID, null)).thenReturn(Set.of(group1, group2));
59+
60+
final Response response = orgTarget().request().get();
61+
assertEquals(HttpStatusCode.OK_200, response.getStatus());
62+
63+
List<UserGroupEntity> result = response.readEntity(new GenericType<>() {});
64+
assertNotNull(result);
65+
assertEquals(2, result.size());
66+
assertTrue(result.stream().map(UserGroupEntity::getId).toList().containsAll(List.of("group1", "group2")));
67+
68+
verify(groupService, atLeastOnce()).findByUserAndEnvironment(USER_ID, null);
69+
}
70+
71+
@Test
72+
public void shouldReturnGroupsByEnvironment() {
73+
GroupEntity group = new GroupEntity();
74+
group.setId("env-group");
75+
group.setName("Env Group");
76+
when(groupService.findByUserAndEnvironment(USER_ID, ENV_ID)).thenReturn(Set.of(group));
77+
78+
final Response response = orgTarget().queryParam("environmentId", ENV_ID).request().get();
79+
assertEquals(HttpStatusCode.OK_200, response.getStatus());
80+
81+
List<UserGroupEntity> result = response.readEntity(new GenericType<>() {});
82+
assertNotNull(result);
83+
assertEquals(1, result.size());
84+
assertEquals("env-group", result.get(0).getId());
85+
assertEquals("Env Group", result.get(0).getName());
86+
87+
verify(groupService, atLeastOnce()).findByUserAndEnvironment(USER_ID, ENV_ID);
88+
}
89+
90+
@Test
91+
public void shouldReturnEmptyListWhenNoGroups() {
92+
when(groupService.findByUserAndEnvironment(USER_ID, null)).thenReturn(Collections.emptySet());
93+
94+
final Response response = orgTarget().request().get();
95+
assertEquals(HttpStatusCode.OK_200, response.getStatus());
96+
97+
List<UserGroupEntity> result = response.readEntity(new GenericType<>() {});
98+
assertNotNull(result);
99+
assertTrue(result.isEmpty());
100+
101+
verify(groupService, atLeastOnce()).findByUserAndEnvironment(USER_ID, null);
102+
verify(membershipService, never()).getRoles(any(), any(), any(), any());
103+
}
104+
}

gravitee-apim-rest-api/gravitee-apim-rest-api-model/src/main/java/io/gravitee/rest/api/model/GroupEntity.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,4 +83,6 @@ public class GroupEntity {
8383
private boolean primaryOwner;
8484

8585
private String origin = OriginContext.Origin.MANAGEMENT.name();
86+
87+
private String environmentId;
8688
}

gravitee-apim-rest-api/gravitee-apim-rest-api-model/src/main/java/io/gravitee/rest/api/model/UserGroupEntity.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public class UserGroupEntity {
2626
private String id;
2727
private String name;
2828
private Map<String, String> roles;
29+
private String environmentId;
2930

3031
public String getId() {
3132
return id;
@@ -50,4 +51,12 @@ public Map<String, String> getRoles() {
5051
public void setRoles(Map<String, String> roles) {
5152
this.roles = roles;
5253
}
54+
55+
public String getEnvironmentId() {
56+
return environmentId;
57+
}
58+
59+
public void setEnvironmentId(String environmentId) {
60+
this.environmentId = environmentId;
61+
}
5362
}

gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/rest/api/service/GroupService.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ public interface GroupService {
4949
List<GroupEntity> findByName(final String environmentId, String name);
5050

5151
Set<GroupEntity> findByUser(String username);
52+
Set<GroupEntity> findByUserAndEnvironment(String username, String environmentId);
5253
List<ApiEntity> getApis(final String environmentId, String groupId);
5354
List<ApplicationEntity> getApplications(String groupId);
5455
int getNumberOfMembers(ExecutionContext executionContext, String groupId);

gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/rest/api/service/impl/GroupServiceImpl.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -907,6 +907,34 @@ public Set<GroupEntity> findByUser(String user) {
907907
}
908908
}
909909

910+
@Override
911+
public Set<GroupEntity> findByUserAndEnvironment(String user, String environmentId) {
912+
Set<String> userGroups = membershipService
913+
.getMembershipsByMemberAndReference(MembershipMemberType.USER, user, MembershipReferenceType.GROUP)
914+
.stream()
915+
.map(MembershipEntity::getReferenceId)
916+
.collect(Collectors.toSet());
917+
918+
try {
919+
Set<Group> allGroups = groupRepository.findByIds(userGroups);
920+
Set<GroupEntity> filteredGroups = allGroups
921+
.stream()
922+
.filter(group -> environmentId == null || environmentId.equals(group.getEnvironmentId()))
923+
.map(this::map)
924+
.collect(Collectors.toSet());
925+
926+
logger.debug(
927+
"After filtering by environment '{}': {} groups remain: {}",
928+
environmentId,
929+
filteredGroups.size(),
930+
filteredGroups.stream().map(GroupEntity::getName).collect(Collectors.toList())
931+
);
932+
return filteredGroups;
933+
} catch (TechnicalException ex) {
934+
throw new TechnicalManagementException("An error occurs while trying to find user groups for environment " + environmentId, ex);
935+
}
936+
}
937+
910938
@Override
911939
public List<ApiEntity> getApis(final String environmentId, String groupId) {
912940
return apiRepository
@@ -1060,6 +1088,7 @@ private GroupEntity map(ExecutionContext executionContext, Group group) {
10601088
entity.setEmailInvitation(group.isEmailInvitation());
10611089
entity.setDisableMembershipNotifications(group.isDisableMembershipNotifications());
10621090
entity.setOrigin(group.getOrigin());
1091+
entity.setEnvironmentId(group.getEnvironmentId());
10631092

10641093
return entity;
10651094
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/*
2+
* Copyright © 2015 The Gravitee team (http://gravitee.io)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.gravitee.rest.api.service.impl;
17+
18+
import static org.assertj.core.api.Assertions.assertThat;
19+
import static org.mockito.Mockito.when;
20+
21+
import io.gravitee.repository.exceptions.TechnicalException;
22+
import io.gravitee.repository.management.api.GroupRepository;
23+
import io.gravitee.repository.management.model.Group;
24+
import io.gravitee.rest.api.model.GroupEntity;
25+
import io.gravitee.rest.api.model.MembershipEntity;
26+
import io.gravitee.rest.api.model.MembershipMemberType;
27+
import io.gravitee.rest.api.model.MembershipReferenceType;
28+
import io.gravitee.rest.api.service.MembershipService;
29+
import java.util.Arrays;
30+
import java.util.Collections;
31+
import java.util.HashSet;
32+
import java.util.Set;
33+
import org.junit.Test;
34+
import org.junit.runner.RunWith;
35+
import org.mockito.InjectMocks;
36+
import org.mockito.Mock;
37+
import org.mockito.junit.MockitoJUnitRunner;
38+
39+
@RunWith(MockitoJUnitRunner.class)
40+
public class GroupService_FindByUserAndEnvironmentTest {
41+
42+
@InjectMocks
43+
private final GroupServiceImpl groupService = new GroupServiceImpl();
44+
45+
@Mock
46+
private GroupRepository groupRepository;
47+
48+
@Mock
49+
private MembershipService membershipService;
50+
51+
@Test
52+
public void shouldReturnGroupsForUserInEnvironment() throws TechnicalException {
53+
String userId = "user1";
54+
String envId = "env1";
55+
Group group1 = Group.builder().id("g1").environmentId(envId).name("Group1").build();
56+
Group group2 = Group.builder().id("g2").environmentId("env2").name("Group2").build();
57+
Group group3 = Group.builder().id("g3").environmentId(envId).name("Group3").build();
58+
Set<String> userGroups = new HashSet<>(Arrays.asList("g1", "g2", "g3"));
59+
when(
60+
membershipService.getMembershipsByMemberAndReference(MembershipMemberType.USER, userId, MembershipReferenceType.GROUP)
61+
).thenReturn(
62+
new HashSet<>(
63+
Arrays.asList(
64+
MembershipEntity.builder().id("m1").referenceId("g1").build(),
65+
MembershipEntity.builder().id("m2").referenceId("g2").build(),
66+
MembershipEntity.builder().id("m3").referenceId("g3").build()
67+
)
68+
)
69+
);
70+
when(groupRepository.findByIds(userGroups)).thenReturn(Set.of(group1, group2, group3));
71+
Set<GroupEntity> result = groupService.findByUserAndEnvironment(userId, envId);
72+
assertThat(result).hasSize(2);
73+
assertThat(result.stream().map(GroupEntity::getName)).containsExactlyInAnyOrder("Group1", "Group3");
74+
}
75+
76+
@Test
77+
public void shouldReturnEmptyForUserWithNoGroupsInEnv() throws TechnicalException {
78+
String userId = "user2";
79+
String envId = "env1";
80+
Group group1 = Group.builder().id("g1").environmentId("env2").name("Group2").build();
81+
Set<String> userGroups = Collections.singleton("g1");
82+
when(
83+
membershipService.getMembershipsByMemberAndReference(MembershipMemberType.USER, userId, MembershipReferenceType.GROUP)
84+
).thenReturn(new HashSet<>(Collections.singletonList(MembershipEntity.builder().id("m4").referenceId("g1").build())));
85+
when(groupRepository.findByIds(userGroups)).thenReturn(Set.of(group1));
86+
Set<GroupEntity> result = groupService.findByUserAndEnvironment(userId, envId);
87+
assertThat(result).isEmpty();
88+
}
89+
90+
@Test
91+
public void shouldReturnEmptyForNonExistentUserOrEnv() throws TechnicalException {
92+
String userId = "nouser";
93+
String envId = "noenv";
94+
when(
95+
membershipService.getMembershipsByMemberAndReference(MembershipMemberType.USER, userId, MembershipReferenceType.GROUP)
96+
).thenReturn(new HashSet<>());
97+
when(groupRepository.findByIds(Collections.emptySet())).thenReturn(Set.of());
98+
Set<GroupEntity> result = groupService.findByUserAndEnvironment(userId, envId);
99+
assertThat(result).isEmpty();
100+
}
101+
102+
@Test
103+
public void shouldReturnAllGroupsIfEnvIdIsNull() throws TechnicalException {
104+
String userId = "user3";
105+
Group group1 = Group.builder().id("g1").environmentId("env1").name("Group1").build();
106+
Group group2 = Group.builder().id("g2").environmentId("env2").name("Group2").build();
107+
Set<String> userGroups = new HashSet<>(Arrays.asList("g1", "g2"));
108+
when(
109+
membershipService.getMembershipsByMemberAndReference(MembershipMemberType.USER, userId, MembershipReferenceType.GROUP)
110+
).thenReturn(
111+
new HashSet<>(
112+
Arrays.asList(
113+
MembershipEntity.builder().id("m5").referenceId("g1").build(),
114+
MembershipEntity.builder().id("m6").referenceId("g2").build()
115+
)
116+
)
117+
);
118+
when(groupRepository.findByIds(userGroups)).thenReturn(Set.of(group1, group2));
119+
Set<GroupEntity> result = groupService.findByUserAndEnvironment(userId, null);
120+
assertThat(result).hasSize(2);
121+
assertThat(result.stream().map(GroupEntity::getName)).containsExactlyInAnyOrder("Group1", "Group2");
122+
}
123+
}

0 commit comments

Comments
 (0)