Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

private static final Logger LOGGER = LoggerFactory.getLogger(UserResource.class);

@Context
private ResourceContext resourceContext;

Expand Down Expand Up @@ -120,31 +124,31 @@ public Response deleteUser() {
@ApiResponse(responseCode = "404", description = "User not found")
@ApiResponse(responseCode = "500", description = "Internal server error")
@Permissions(@Permission(value = RolePermission.ORGANIZATION_USERS, acls = RolePermissionAction.READ))
public List<UserGroupEntity> getUserGroups() {
public List<UserGroupEntity> getUserGroups(@QueryParam("environmentId") String environmentId) {
// Check that user belongs to current organization
userService.findById(GraviteeContext.getExecutionContext(), userId);
LOGGER.debug("Search groups for user: {} with query: {}", userId, environmentId);
Set<GroupEntity> groups = groupService.findByUserAndEnvironment(userId, environmentId);
List<UserGroupEntity> result = new ArrayList<>();
groups.forEach(groupEntity -> {
UserGroupEntity userGroupEntity = new UserGroupEntity();
userGroupEntity.setId(groupEntity.getId());
userGroupEntity.setName(groupEntity.getName());
userGroupEntity.setRoles(new HashMap<>());
userGroupEntity.setEnvironmentId(groupEntity.getEnvironmentId());
Set<RoleEntity> roles = membershipService.getRoles(
MembershipReferenceType.GROUP,
groupEntity.getId(),
MembershipMemberType.USER,
userId
);
if (!roles.isEmpty()) {
roles.forEach(role -> userGroupEntity.getRoles().put(role.getScope().name(), role.getName()));
}
result.add(userGroupEntity);
});

List<UserGroupEntity> groups = new ArrayList<>();
groupService
.findByUser(userId)
.forEach(groupEntity -> {
UserGroupEntity userGroupEntity = new UserGroupEntity();
userGroupEntity.setId(groupEntity.getId());
userGroupEntity.setName(groupEntity.getName());
userGroupEntity.setRoles(new HashMap<>());
Set<RoleEntity> roles = membershipService.getRoles(
MembershipReferenceType.GROUP,
groupEntity.getId(),
MembershipMemberType.USER,
userId
);
if (!roles.isEmpty()) {
roles.forEach(role -> userGroupEntity.getRoles().put(role.getScope().name(), role.getName()));
}
groups.add(userGroupEntity);
});

return groups;
return result;
}

@GET
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* Copyright © 2015 The Gravitee team (http://gravitee.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.gravitee.rest.api.management.rest.resource;

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

import io.gravitee.common.http.HttpStatusCode;
import io.gravitee.rest.api.model.*;
import jakarta.ws.rs.core.GenericType;
import jakarta.ws.rs.core.Response;
import java.util.*;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;

/**
* Test for UserResource.getUserGroups() method
*/
public class UserResourceTest extends AbstractResourceTest {

private static final String USER_ID = "test-user";
private static final String ENV_ID = "test-env";

@Before
public void resetMocks() {
Mockito.reset(groupService, membershipService);
}

@Override
protected String contextPath() {
// Matches the REST path you’re testing, e.g. /users/{userId}/groups?environmentId=ENV_ID
return "users/" + USER_ID + "/groups/";
}

@Test
public void shouldReturnAllGroupsIncludingEnvGroups() {
GroupEntity group1 = new GroupEntity();
group1.setId("group1");
group1.setName("Global Group");
GroupEntity group2 = new GroupEntity();
group2.setId("group2");
group2.setName("Env Group");

when(groupService.findByUserAndEnvironment(USER_ID, null)).thenReturn(Set.of(group1, group2));

final Response response = orgTarget().request().get();
assertEquals(HttpStatusCode.OK_200, response.getStatus());

List<UserGroupEntity> result = response.readEntity(new GenericType<>() {});
assertNotNull(result);
assertEquals(2, result.size());
assertTrue(result.stream().map(UserGroupEntity::getId).toList().containsAll(List.of("group1", "group2")));

verify(groupService, atLeastOnce()).findByUserAndEnvironment(USER_ID, null);
}

@Test
public void shouldReturnGroupsByEnvironment() {
GroupEntity group = new GroupEntity();
group.setId("env-group");
group.setName("Env Group");
when(groupService.findByUserAndEnvironment(USER_ID, ENV_ID)).thenReturn(Set.of(group));

final Response response = orgTarget().queryParam("environmentId", ENV_ID).request().get();
assertEquals(HttpStatusCode.OK_200, response.getStatus());

List<UserGroupEntity> result = response.readEntity(new GenericType<>() {});
assertNotNull(result);
assertEquals(1, result.size());
assertEquals("env-group", result.get(0).getId());
assertEquals("Env Group", result.get(0).getName());

verify(groupService, atLeastOnce()).findByUserAndEnvironment(USER_ID, ENV_ID);
}

@Test
public void shouldReturnEmptyListWhenNoGroups() {
when(groupService.findByUserAndEnvironment(USER_ID, null)).thenReturn(Collections.emptySet());

final Response response = orgTarget().request().get();
assertEquals(HttpStatusCode.OK_200, response.getStatus());

List<UserGroupEntity> result = response.readEntity(new GenericType<>() {});
assertNotNull(result);
assertTrue(result.isEmpty());

verify(groupService, atLeastOnce()).findByUserAndEnvironment(USER_ID, null);
verify(membershipService, never()).getRoles(any(), any(), any(), any());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,6 @@ public class GroupEntity {
private boolean primaryOwner;

private String origin = OriginContext.Origin.MANAGEMENT.name();

private String environmentId;
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public class UserGroupEntity {
private String id;
private String name;
private Map<String, String> roles;
private String environmentId;

public String getId() {
return id;
Expand All @@ -50,4 +51,12 @@ public Map<String, String> getRoles() {
public void setRoles(Map<String, String> roles) {
this.roles = roles;
}

public String getEnvironmentId() {
return environmentId;
}

public void setEnvironmentId(String environmentId) {
this.environmentId = environmentId;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public interface GroupService {
List<GroupEntity> findByName(final String environmentId, String name);

Set<GroupEntity> findByUser(String username);
Set<GroupEntity> findByUserAndEnvironment(String username, String environmentId);
List<ApiEntity> getApis(final String environmentId, String groupId);
List<ApplicationEntity> getApplications(String groupId);
int getNumberOfMembers(ExecutionContext executionContext, String groupId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -907,6 +907,34 @@ public Set<GroupEntity> findByUser(String user) {
}
}

@Override
public Set<GroupEntity> findByUserAndEnvironment(String user, String environmentId) {
Set<String> userGroups = membershipService
.getMembershipsByMemberAndReference(MembershipMemberType.USER, user, MembershipReferenceType.GROUP)
.stream()
.map(MembershipEntity::getReferenceId)
.collect(Collectors.toSet());

try {
Set<Group> allGroups = groupRepository.findByIds(userGroups);
Set<GroupEntity> filteredGroups = allGroups
.stream()
.filter(group -> environmentId == null || environmentId.equals(group.getEnvironmentId()))
.map(this::map)
.collect(Collectors.toSet());

logger.debug(
"After filtering by environment '{}': {} groups remain: {}",
environmentId,
filteredGroups.size(),
filteredGroups.stream().map(GroupEntity::getName).collect(Collectors.toList())
);
return filteredGroups;
} catch (TechnicalException ex) {
throw new TechnicalManagementException("An error occurs while trying to find user groups for environment " + environmentId, ex);
}
}

@Override
public List<ApiEntity> getApis(final String environmentId, String groupId) {
return apiRepository
Expand Down Expand Up @@ -1060,6 +1088,7 @@ private GroupEntity map(ExecutionContext executionContext, Group group) {
entity.setEmailInvitation(group.isEmailInvitation());
entity.setDisableMembershipNotifications(group.isDisableMembershipNotifications());
entity.setOrigin(group.getOrigin());
entity.setEnvironmentId(group.getEnvironmentId());

return entity;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/*
* Copyright © 2015 The Gravitee team (http://gravitee.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.gravitee.rest.api.service.impl;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;

import io.gravitee.repository.exceptions.TechnicalException;
import io.gravitee.repository.management.api.GroupRepository;
import io.gravitee.repository.management.model.Group;
import io.gravitee.rest.api.model.GroupEntity;
import io.gravitee.rest.api.model.MembershipEntity;
import io.gravitee.rest.api.model.MembershipMemberType;
import io.gravitee.rest.api.model.MembershipReferenceType;
import io.gravitee.rest.api.service.MembershipService;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)
public class GroupService_FindByUserAndEnvironmentTest {

@InjectMocks
private final GroupServiceImpl groupService = new GroupServiceImpl();

@Mock
private GroupRepository groupRepository;

@Mock
private MembershipService membershipService;

@Test
public void shouldReturnGroupsForUserInEnvironment() throws TechnicalException {
String userId = "user1";
String envId = "env1";
Group group1 = Group.builder().id("g1").environmentId(envId).name("Group1").build();
Group group2 = Group.builder().id("g2").environmentId("env2").name("Group2").build();
Group group3 = Group.builder().id("g3").environmentId(envId).name("Group3").build();
Set<String> userGroups = new HashSet<>(Arrays.asList("g1", "g2", "g3"));
when(
membershipService.getMembershipsByMemberAndReference(MembershipMemberType.USER, userId, MembershipReferenceType.GROUP)
).thenReturn(
new HashSet<>(
Arrays.asList(
MembershipEntity.builder().id("m1").referenceId("g1").build(),
MembershipEntity.builder().id("m2").referenceId("g2").build(),
MembershipEntity.builder().id("m3").referenceId("g3").build()
)
)
);
when(groupRepository.findByIds(userGroups)).thenReturn(Set.of(group1, group2, group3));
Set<GroupEntity> result = groupService.findByUserAndEnvironment(userId, envId);
assertThat(result).hasSize(2);
assertThat(result.stream().map(GroupEntity::getName)).containsExactlyInAnyOrder("Group1", "Group3");
}

@Test
public void shouldReturnEmptyForUserWithNoGroupsInEnv() throws TechnicalException {
String userId = "user2";
String envId = "env1";
Group group1 = Group.builder().id("g1").environmentId("env2").name("Group2").build();
Set<String> userGroups = Collections.singleton("g1");
when(
membershipService.getMembershipsByMemberAndReference(MembershipMemberType.USER, userId, MembershipReferenceType.GROUP)
).thenReturn(new HashSet<>(Collections.singletonList(MembershipEntity.builder().id("m4").referenceId("g1").build())));
when(groupRepository.findByIds(userGroups)).thenReturn(Set.of(group1));
Set<GroupEntity> result = groupService.findByUserAndEnvironment(userId, envId);
assertThat(result).isEmpty();
}

@Test
public void shouldReturnEmptyForNonExistentUserOrEnv() throws TechnicalException {
String userId = "nouser";
String envId = "noenv";
when(
membershipService.getMembershipsByMemberAndReference(MembershipMemberType.USER, userId, MembershipReferenceType.GROUP)
).thenReturn(new HashSet<>());
when(groupRepository.findByIds(Collections.emptySet())).thenReturn(Set.of());
Set<GroupEntity> result = groupService.findByUserAndEnvironment(userId, envId);
assertThat(result).isEmpty();
}

@Test
public void shouldReturnAllGroupsIfEnvIdIsNull() throws TechnicalException {
String userId = "user3";
Group group1 = Group.builder().id("g1").environmentId("env1").name("Group1").build();
Group group2 = Group.builder().id("g2").environmentId("env2").name("Group2").build();
Set<String> userGroups = new HashSet<>(Arrays.asList("g1", "g2"));
when(
membershipService.getMembershipsByMemberAndReference(MembershipMemberType.USER, userId, MembershipReferenceType.GROUP)
).thenReturn(
new HashSet<>(
Arrays.asList(
MembershipEntity.builder().id("m5").referenceId("g1").build(),
MembershipEntity.builder().id("m6").referenceId("g2").build()
)
)
);
when(groupRepository.findByIds(userGroups)).thenReturn(Set.of(group1, group2));
Set<GroupEntity> result = groupService.findByUserAndEnvironment(userId, null);
assertThat(result).hasSize(2);
assertThat(result.stream().map(GroupEntity::getName)).containsExactlyInAnyOrder("Group1", "Group2");
}
}