*/
package org.sonar.server.management;
+import com.google.common.collect.MoreCollectors;
+import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import javax.annotation.Priority;
import org.sonar.api.server.ServerSide;
+import org.sonar.db.DbSession;
+
+import static java.util.function.Function.identity;
+import static java.util.stream.Collectors.toMap;
+import static java.util.stream.Collectors.toSet;
+import static org.sonar.api.utils.Preconditions.checkState;
@ServerSide
@Priority(ManagedInstanceService.DELEGATING_INSTANCE_PRIORITY)
public final boolean isInstanceExternallyManaged() {
return delegates.stream().anyMatch(ManagedInstanceService::isInstanceExternallyManaged);
}
+
+ @Override
+ public Map<String, Boolean> getUserUuidToManaged(DbSession dbSession, Set<String> userUuids) {
+ return findManagedInstanceService()
+ .map(managedInstanceService -> managedInstanceService.getUserUuidToManaged(dbSession, userUuids))
+ .orElse(returnNonManagedForAllGroups(userUuids));
+ }
+
+ @Override
+ public Map<String, Boolean> getGroupUuidToManaged(DbSession dbSession, Set<String> groupUuids) {
+ return findManagedInstanceService()
+ .map(managedInstanceService -> managedInstanceService.getGroupUuidToManaged(dbSession, groupUuids))
+ .orElse(returnNonManagedForAllGroups(groupUuids));
+ }
+
+ private Optional<ManagedInstanceService> findManagedInstanceService() {
+ Set<ManagedInstanceService> managedInstanceServices = delegates.stream()
+ .filter(ManagedInstanceService::isInstanceExternallyManaged)
+ .collect(toSet());
+
+ checkState(managedInstanceServices.size() < 2,
+ "The instance can't be managed by more than one identity provider and %s were found.", managedInstanceServices.size());
+ return managedInstanceServices.stream().collect(MoreCollectors.toOptional());
+ }
+
+ private static Map<String, Boolean> returnNonManagedForAllGroups(Set<String> resourcesUuid) {
+ return resourcesUuid.stream().collect(toMap(identity(), any -> false));
+ }
}
*/
package org.sonar.server.management;
+import java.util.Map;
+import java.util.Set;
+import org.sonar.db.DbSession;
+
public interface ManagedInstanceService {
int DELEGATING_INSTANCE_PRIORITY = 1;
boolean isInstanceExternallyManaged();
+
+ Map<String, Boolean> getUserUuidToManaged(DbSession dbSession, Set<String> userUuids);
+
+ Map<String, Boolean> getGroupUuidToManaged(DbSession dbSession, Set<String> groupUuids);
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.management;
*/
package org.sonar.server.management;
-import java.util.Collections;
+import java.util.Map;
import java.util.Set;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
+import org.sonar.db.DbSession;
+import static java.util.Collections.emptySet;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class DelegatingManagedInstanceServiceTest {
+ @Mock
+ private DbSession dbSession;
+
+ @Test
+ public void isInstanceExternallyManaged_whenNoManagedInstanceService_returnsFalse() {
+ DelegatingManagedInstanceService managedInstanceService = new DelegatingManagedInstanceService(emptySet());
+ assertThat(managedInstanceService.isInstanceExternallyManaged()).isFalse();
+ }
+
+ @Test
+ public void isInstanceExternallyManaged_whenAllManagedInstanceServiceReturnsFalse_returnsFalse() {
+ Set<ManagedInstanceService> delegates = Set.of(new NeverManagedInstanceService(), new NeverManagedInstanceService());
+ DelegatingManagedInstanceService managedInstanceService = new DelegatingManagedInstanceService(delegates);
+
+ assertThat(managedInstanceService.isInstanceExternallyManaged()).isFalse();
+ }
+
+ @Test
+ public void isInstanceExternallyManaged_whenOneManagedInstanceServiceReturnsTrue_returnsTrue() {
+ Set<ManagedInstanceService> delegates = Set.of(new NeverManagedInstanceService(), new AlwaysManagedInstanceService());
+ DelegatingManagedInstanceService managedInstanceService = new DelegatingManagedInstanceService(delegates);
+
+ assertThat(managedInstanceService.isInstanceExternallyManaged()).isTrue();
+ }
+
+ @Test
+ public void getUserUuidToManaged_whenNoDelegates_setAllUsersAsNonManaged() {
+ Set<String> userUuids = Set.of("a", "b");
+ DelegatingManagedInstanceService managedInstanceService = new DelegatingManagedInstanceService(emptySet());
+
+ Map<String, Boolean> userUuidToManaged = managedInstanceService.getUserUuidToManaged(dbSession, userUuids);
+
+ assertThat(userUuidToManaged).containsExactlyInAnyOrderEntriesOf(Map.of("a", false, "b", false));
+ }
+
+ @Test
+ public void getUserUuidToManaged_delegatesToRightService_andPropagateAnswer() {
+ Set<String> userUuids = Set.of("a", "b");
+ Map<String, Boolean> serviceResponse = Map.of("a", false, "b", true);
+
+ ManagedInstanceService anotherManagedInstanceService = getManagedInstanceService(userUuids, serviceResponse);
+ DelegatingManagedInstanceService managedInstanceService = new DelegatingManagedInstanceService(Set.of(new NeverManagedInstanceService(), anotherManagedInstanceService));
+
+ Map<String, Boolean> userUuidToManaged = managedInstanceService.getUserUuidToManaged(dbSession, userUuids);
+
+ assertThat(userUuidToManaged).containsExactlyInAnyOrderEntriesOf(serviceResponse);
+ }
+
@Test
- public void isInstanceExternallyManaged_whenNoChecksDefined_returnsFalse() {
- DelegatingManagedInstanceService checker = new DelegatingManagedInstanceService(Collections.emptySet());
- assertThat(checker.isInstanceExternallyManaged()).isFalse();
+ public void getGroupUuidToManaged_whenNoDelegates_setAllUsersAsNonManaged() {
+ Set<String> groupUuids = Set.of("a", "b");
+ DelegatingManagedInstanceService managedInstanceService = new DelegatingManagedInstanceService(emptySet());
+
+ Map<String, Boolean> groupUuidToManaged = managedInstanceService.getGroupUuidToManaged(dbSession, groupUuids);
+
+ assertThat(groupUuidToManaged).containsExactlyInAnyOrderEntriesOf(Map.of("a", false, "b", false));
}
@Test
- public void isInstanceExternallyManaged_whenAllChecksReturnFalse_returnsFalse() {
+ public void getGroupUuidToManaged_delegatesToRightService_andPropagateAnswer() {
+ Set<String> groupUuids = Set.of("a", "b");
+ Map<String, Boolean> serviceResponse = Map.of("a", false, "b", true);
+
+ ManagedInstanceService anotherManagedInstanceService = getManagedInstanceService(groupUuids, serviceResponse);
+ DelegatingManagedInstanceService managedInstanceService = new DelegatingManagedInstanceService(Set.of(new NeverManagedInstanceService(), anotherManagedInstanceService));
+
+ Map<String, Boolean> groupUuidToManaged = managedInstanceService.getGroupUuidToManaged(dbSession, groupUuids);
- DelegatingManagedInstanceService checker = new DelegatingManagedInstanceService(Set.of(() -> false, () -> false));
- assertThat(checker.isInstanceExternallyManaged()).isFalse();
+ assertThat(groupUuidToManaged).containsExactlyInAnyOrderEntriesOf(serviceResponse);
}
@Test
- public void isInstanceExternallyManaged_whenOneCheckReturnsTrue_returnsTrue() {
- DelegatingManagedInstanceService checker = new DelegatingManagedInstanceService(Set.of(() -> false, () -> true, () -> false));
- assertThat(checker.isInstanceExternallyManaged()).isTrue();
+ public void getGroupUuidToManaged_ifMoreThanOneDelegatesActivated_throws() {
+ Set<ManagedInstanceService> managedInstanceServices = Set.of(new AlwaysManagedInstanceService(), new AlwaysManagedInstanceService());
+ DelegatingManagedInstanceService delegatingManagedInstanceService = new DelegatingManagedInstanceService(managedInstanceServices);
+ assertThatIllegalStateException()
+ .isThrownBy(() -> delegatingManagedInstanceService.getGroupUuidToManaged(dbSession, Set.of("a")))
+ .withMessage("The instance can't be managed by more than one identity provider and 2 were found.");
+ }
+
+ @Test
+ public void getUserUuidToManaged_ifMoreThanOneDelegatesActivated_throws() {
+ Set<ManagedInstanceService> managedInstanceServices = Set.of(new AlwaysManagedInstanceService(), new AlwaysManagedInstanceService());
+ DelegatingManagedInstanceService delegatingManagedInstanceService = new DelegatingManagedInstanceService(managedInstanceServices);
+ assertThatIllegalStateException()
+ .isThrownBy(() -> delegatingManagedInstanceService.getUserUuidToManaged(dbSession, Set.of("a")))
+ .withMessage("The instance can't be managed by more than one identity provider and 2 were found.");
+ }
+
+ private ManagedInstanceService getManagedInstanceService(Set<String> userUuids, Map<String, Boolean> uuidToManaged) {
+ ManagedInstanceService anotherManagedInstanceService = mock(ManagedInstanceService.class);
+ when(anotherManagedInstanceService.isInstanceExternallyManaged()).thenReturn(true);
+ when(anotherManagedInstanceService.getGroupUuidToManaged(dbSession, userUuids)).thenReturn(uuidToManaged);
+ when(anotherManagedInstanceService.getUserUuidToManaged(dbSession, userUuids)).thenReturn(uuidToManaged);
+ return anotherManagedInstanceService;
+ }
+
+ private static class NeverManagedInstanceService implements ManagedInstanceService {
+
+ @Override
+ public boolean isInstanceExternallyManaged() {
+ return false;
+ }
+
+ @Override
+ public Map<String, Boolean> getUserUuidToManaged(DbSession dbSession, Set<String> userUuids) {
+ return null;
+ }
+
+ @Override
+ public Map<String, Boolean> getGroupUuidToManaged(DbSession dbSession, Set<String> groupUuids) {
+ return null;
+ }
+ }
+
+ private static class AlwaysManagedInstanceService implements ManagedInstanceService {
+
+ @Override
+ public boolean isInstanceExternallyManaged() {
+ return true;
+ }
+
+ @Override
+ public Map<String, Boolean> getUserUuidToManaged(DbSession dbSession, Set<String> userUuids) {
+ return null;
+ }
+
+ @Override
+ public Map<String, Boolean> getGroupUuidToManaged(DbSession dbSession, Set<String> groupUuids) {
+ return null;
+ }
}
}
*/
package org.sonar.server.permission.ws;
+import java.util.Set;
import org.junit.Before;
import org.junit.Test;
import org.sonar.api.resources.Qualifiers;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.server.management.ManagedInstanceService;
import org.sonar.server.permission.PermissionService;
import org.sonar.server.permission.PermissionServiceImpl;
import static java.lang.String.format;
+import static java.util.function.Function.identity;
+import static java.util.stream.Collectors.toMap;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.Assertions.tuple;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import static org.sonar.api.server.ws.WebService.Param.PAGE;
import static org.sonar.api.server.ws.WebService.Param.PAGE_SIZE;
import static org.sonar.api.server.ws.WebService.Param.TEXT_QUERY;
+import static org.sonar.api.web.UserRole.ISSUE_ADMIN;
import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
import static org.sonar.test.JsonAssert.assertJson;
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PERMISSION;
private final ResourceTypes resourceTypes = new ResourceTypesRule().setRootQualifiers(Qualifiers.PROJECT);
private final PermissionService permissionService = new PermissionServiceImpl(resourceTypes);
private final WsParameters wsParameters = new WsParameters(permissionService);
+ private final ManagedInstanceService managedInstanceService = mock(ManagedInstanceService.class);
@Override
protected GroupsAction buildWsAction() {
return new GroupsAction(
db.getDbClient(),
userSession,
- newPermissionWsSupport(), wsParameters);
+ newPermissionWsSupport(), wsParameters, managedInstanceService);
}
@Before
assertThat(wsDef.since()).isEqualTo("5.2");
assertThat(wsDef.isPost()).isFalse();
assertThat(wsDef.changelog()).extracting(Change::getVersion, Change::getDescription).containsExactlyInAnyOrder(
+ tuple("10.0", "Response includes 'managed' field."),
tuple("8.4", "Field 'id' in the response is deprecated. Format changes from integer to string."),
tuple("7.4", "The response list is returning all groups even those without permissions, the groups with permission are at the top of the list."));
}
" \"description\": \"" + group1.getDescription() + "\",\n" +
" \"permissions\": [\n" +
" \"scan\"\n" +
- " ]\n" +
+ " ],\n" +
+ " \"managed\": false\n" +
" },\n" +
" {\n" +
" \"name\": \"group-2-name\",\n" +
" \"description\": \"" + group2.getDescription() + "\",\n" +
" \"permissions\": [\n" +
" \"scan\"\n" +
- " ]\n" +
+ " ],\n" +
+ " \"managed\": false\n" +
" }\n" +
" ]\n" +
"}\n");
public void search_groups_with_project_permissions() {
ComponentDto project = db.components().insertPrivateProject();
GroupDto group = db.users().insertGroup("project-group-name");
- db.users().insertProjectPermissionOnGroup(group, UserRole.ISSUE_ADMIN, project);
+ db.users().insertProjectPermissionOnGroup(group, ISSUE_ADMIN, project);
ComponentDto anotherProject = db.components().insertPrivateProject();
GroupDto anotherGroup = db.users().insertGroup("another-project-group-name");
- db.users().insertProjectPermissionOnGroup(anotherGroup, UserRole.ISSUE_ADMIN, anotherProject);
+ db.users().insertProjectPermissionOnGroup(anotherGroup, ISSUE_ADMIN, anotherProject);
GroupDto groupWithoutPermission = db.users().insertGroup("group-without-permission");
userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
String result = newRequest()
- .setParam(PARAM_PERMISSION, UserRole.ISSUE_ADMIN)
+ .setParam(PARAM_PERMISSION, ISSUE_ADMIN)
.setParam(PARAM_PROJECT_ID, project.uuid())
.execute()
.getInput();
public void return_also_groups_without_permission_when_search_query() {
ComponentDto project = db.components().insertPrivateProject();
GroupDto group = db.users().insertGroup("group-with-permission");
- db.users().insertProjectPermissionOnGroup(group, UserRole.ISSUE_ADMIN, project);
+ db.users().insertProjectPermissionOnGroup(group, ISSUE_ADMIN, project);
GroupDto groupWithoutPermission = db.users().insertGroup("group-without-permission");
GroupDto anotherGroup = db.users().insertGroup("another-group");
loginAsAdmin();
String result = newRequest()
- .setParam(PARAM_PERMISSION, UserRole.ISSUE_ADMIN)
+ .setParam(PARAM_PERMISSION, ISSUE_ADMIN)
.setParam(PARAM_PROJECT_ID, project.uuid())
.setParam(TEXT_QUERY, "group-with")
.execute()
public void return_only_groups_with_permission_when_no_search_query() {
ComponentDto project = db.components().insertComponent(newPrivateProjectDto("project-uuid"));
GroupDto group = db.users().insertGroup("project-group-name");
- db.users().insertProjectPermissionOnGroup(group, UserRole.ISSUE_ADMIN, project);
+ db.users().insertProjectPermissionOnGroup(group, ISSUE_ADMIN, project);
GroupDto groupWithoutPermission = db.users().insertGroup("group-without-permission");
loginAsAdmin();
String result = newRequest()
- .setParam(PARAM_PERMISSION, UserRole.ISSUE_ADMIN)
+ .setParam(PARAM_PERMISSION, ISSUE_ADMIN)
.setParam(PARAM_PROJECT_ID, project.uuid())
.execute()
.getInput();
public void return_anyone_group_when_search_query_and_no_param_permission() {
ComponentDto project = db.components().insertPrivateProject();
GroupDto group = db.users().insertGroup("group-with-permission");
- db.users().insertProjectPermissionOnGroup(group, UserRole.ISSUE_ADMIN, project);
+ db.users().insertProjectPermissionOnGroup(group, ISSUE_ADMIN, project);
loginAsAdmin();
String result = newRequest()
public void search_groups_on_views() {
ComponentDto view = db.components().insertComponent(ComponentTesting.newPortfolio("view-uuid").setKey("view-key"));
GroupDto group = db.users().insertGroup("project-group-name");
- db.users().insertProjectPermissionOnGroup(group, UserRole.ISSUE_ADMIN, view);
+ db.users().insertProjectPermissionOnGroup(group, ISSUE_ADMIN, view);
loginAsAdmin();
String result = newRequest()
- .setParam(PARAM_PERMISSION, UserRole.ISSUE_ADMIN)
+ .setParam(PARAM_PERMISSION, ISSUE_ADMIN)
.setParam(PARAM_PROJECT_ID, "view-uuid")
.execute()
.getInput();
.doesNotContain("group-3");
}
+ @Test
+ public void return_isManaged() {
+ ComponentDto view = db.components().insertComponent(ComponentTesting.newPortfolio("view-uuid").setKey("view-key"));
+ GroupDto managedGroup = db.users().insertGroup("managed-group");
+ GroupDto localGroup = db.users().insertGroup("local-group");
+ db.users().insertProjectPermissionOnGroup(managedGroup, ISSUE_ADMIN, view);
+ db.users().insertProjectPermissionOnGroup(localGroup, ISSUE_ADMIN, view);
+ mockGroupsAsManaged(managedGroup.getUuid());
+
+ loginAsAdmin();
+ String result = newRequest()
+ .setParam(PARAM_PERMISSION, ISSUE_ADMIN)
+ .setParam(PARAM_PROJECT_ID, "view-uuid")
+ .execute()
+ .getInput();
+
+ assertJson(result).isSimilarTo(
+ "{\n"
+ + " \"paging\": {\n"
+ + " \"pageIndex\": 1,\n"
+ + " \"pageSize\": 20,\n"
+ + " \"total\": 2\n"
+ + " },\n"
+ + " \"groups\": [\n"
+ + " {\n"
+ + " \"name\": \"local-group\",\n"
+ + " \"managed\": false\n"
+ + " },\n"
+ + " {\n"
+ + " \"name\": \"managed-group\",\n"
+ + " \"managed\": true\n"
+ + " }\n"
+ + " ]\n"
+ + "}"
+ );
+ }
+
@Test
public void fail_if_not_logged_in() {
- assertThatThrownBy(() -> {
+ assertThatThrownBy(() -> {
userSession.anonymous();
newRequest()
@Test
public void fail_if_insufficient_privileges() {
- assertThatThrownBy(() -> {
+ assertThatThrownBy(() -> {
userSession.logIn("login");
newRequest()
.setParam(PARAM_PERMISSION, GlobalPermission.SCAN.getKey())
public void fail_if_project_uuid_and_project_key_are_provided() {
ComponentDto project = db.components().insertPrivateProject();
- assertThatThrownBy(() -> {
+ assertThatThrownBy(() -> {
loginAsAdmin();
newRequest()
.setParam(PARAM_PERMISSION, GlobalPermission.SCAN.getKey())
ComponentDto project = db.components().insertPublicProject();
ComponentDto branch = db.components().insertProjectBranch(project);
GroupDto group = db.users().insertGroup();
- db.users().insertProjectPermissionOnGroup(group, UserRole.ISSUE_ADMIN, project);
+ db.users().insertProjectPermissionOnGroup(group, ISSUE_ADMIN, project);
loginAsAdmin();
- assertThatThrownBy(() -> {
+ assertThatThrownBy(() -> {
newRequest()
- .setParam(PARAM_PERMISSION, UserRole.ISSUE_ADMIN)
+ .setParam(PARAM_PERMISSION, ISSUE_ADMIN)
.setParam(PARAM_PROJECT_ID, branch.uuid())
.execute();
})
.isInstanceOf(NotFoundException.class)
.hasMessage(format("Project id '%s' not found", branch.uuid()));
}
+
+ private void mockGroupsAsManaged(String... groupUuids) {
+ when(managedInstanceService.getGroupUuidToManaged(any(), any())).thenAnswer(invocation ->
+ {
+ Set<?> allGroupUuids = invocation.getArgument(1, Set.class);
+ return allGroupUuids.stream()
+ .map(groupUuid -> (String) groupUuid)
+ .collect(toMap(identity(), userUuid -> Set.of(groupUuids).contains(userUuid)));
+ }
+ );
+ }
}
*/
package org.sonar.server.permission.ws;
+import java.util.Set;
import org.junit.Test;
import org.sonar.api.resources.Qualifiers;
import org.sonar.api.resources.ResourceTypes;
import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.exceptions.UnauthorizedException;
import org.sonar.server.issue.AvatarResolverImpl;
+import org.sonar.server.management.ManagedInstanceService;
import org.sonar.server.permission.PermissionService;
import org.sonar.server.permission.PermissionServiceImpl;
import org.sonar.server.permission.RequestValidator;
import static java.lang.String.format;
+import static java.util.function.Function.identity;
+import static java.util.stream.Collectors.toMap;
import static org.apache.commons.lang.StringUtils.countMatches;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import static org.sonar.api.server.ws.WebService.Param.PAGE;
import static org.sonar.api.server.ws.WebService.Param.PAGE_SIZE;
import static org.sonar.api.server.ws.WebService.Param.TEXT_QUERY;
private PermissionService permissionService = new PermissionServiceImpl(resourceTypes);
private WsParameters wsParameters = new WsParameters(permissionService);
private RequestValidator requestValidator = new RequestValidator(permissionService);
+ private ManagedInstanceService managedInstanceService = mock(ManagedInstanceService.class);
@Override
protected UsersAction buildWsAction() {
- return new UsersAction(db.getDbClient(), userSession, newPermissionWsSupport(), new AvatarResolverImpl(), wsParameters, requestValidator);
+ return new UsersAction(db.getDbClient(), userSession, newPermissionWsSupport(), new AvatarResolverImpl(), wsParameters, requestValidator, managedInstanceService);
}
@Test
db.users().insertGlobalPermissionOnUser(user1, GlobalPermission.ADMINISTER);
db.users().insertGlobalPermissionOnUser(user1, GlobalPermission.ADMINISTER_QUALITY_GATES);
db.users().insertGlobalPermissionOnUser(user3, GlobalPermission.SCAN);
+ mockUsersAsManaged(user3.getUuid());
loginAsAdmin();
String result = newRequest().execute().getInput();
db.users().insertGlobalPermissionOnUser(user1, GlobalPermission.SCAN);
db.users().insertGlobalPermissionOnUser(user2, GlobalPermission.SCAN);
db.users().insertGlobalPermissionOnUser(user3, GlobalPermission.ADMINISTER);
+ mockUsersAsManaged(user1.getUuid());
}
+ private void mockUsersAsManaged(String... userUuids) {
+ when(managedInstanceService.getUserUuidToManaged(any(), any())).thenAnswer(invocation ->
+ {
+ Set<?> allUsersUuids = invocation.getArgument(1, Set.class);
+ return allUsersUuids.stream()
+ .map(userUuid -> (String) userUuid)
+ .collect(toMap(identity(), userUuid -> Set.of(userUuids).contains(userUuid)));
+ }
+ );
+ }
}
*/
package org.sonar.server.user.ws;
+import java.util.Set;
import java.util.stream.IntStream;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.db.user.UserDto;
import org.sonar.server.es.EsTester;
import org.sonar.server.issue.AvatarResolverImpl;
+import org.sonar.server.management.ManagedInstanceService;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.user.index.UserIndex;
import org.sonar.server.user.index.UserIndexer;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
+import static java.util.function.Function.identity;
+import static java.util.stream.Collectors.toMap;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.tuple;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import static org.sonar.api.utils.DateUtils.formatDateTime;
import static org.sonar.test.JsonAssert.assertJson;
@Rule
public DbTester db = DbTester.create();
+ private ManagedInstanceService managedInstanceService = mock(ManagedInstanceService.class);
private UserIndex index = new UserIndex(es.client(), System2.INSTANCE);
private UserIndexer userIndexer = new UserIndexer(db.getDbClient(), es.client());
- private WsActionTester ws = new WsActionTester(new SearchAction(userSession, index, db.getDbClient(), new AvatarResolverImpl()));
+ private WsActionTester ws = new WsActionTester(new SearchAction(userSession, index, db.getDbClient(), new AvatarResolverImpl(), managedInstanceService));
@Test
public void search_for_all_active_users() {
.containsExactlyInAnyOrder(tuple(user.getLogin(), "6a6c19fea4a3676970167ce51f39e6ee"));
}
+ @Test
+ public void return_isManaged() {
+ UserDto nonManagedUser = db.users().insertUser(u -> u.setEmail("john@doe.com"));
+ UserDto managedUser = db.users().insertUser(u -> u.setEmail("externalUser@doe.com"));
+ mockUsersAsManaged(managedUser.getUuid());
+ userIndexer.indexAll();
+ userSession.logIn().setSystemAdministrator();
+
+ SearchWsResponse response = ws.newRequest()
+ .executeProtobuf(SearchWsResponse.class);
+
+ assertThat(response.getUsersList())
+ .extracting(User::getLogin, User::getManaged)
+ .containsExactlyInAnyOrder(
+ tuple(managedUser.getLogin(), true),
+ tuple(nonManagedUser.getLogin(), false)
+ );
+ }
+
@Test
public void return_scm_accounts() {
UserDto user = db.users().insertUser(u -> u.setScmAccounts(asList("john1", "john2")));
.executeProtobuf(SearchWsResponse.class);
assertThat(response.getUsersList())
- .extracting(User::getLogin, User::getName, User::hasTokensCount, User::hasScmAccounts, User::hasAvatar, User::hasGroups)
- .containsExactlyInAnyOrder(tuple(user.getLogin(), user.getName(), false, false, false, false));
+ .extracting(User::getLogin, User::getName, User::hasTokensCount, User::hasScmAccounts, User::hasAvatar, User::hasGroups, User::hasManaged)
+ .containsExactlyInAnyOrder(tuple(user.getLogin(), user.getName(), false, false, false, false, false));
}
@Test
assertThat(ws.newRequest().setParam("q", user.getLogin())
.executeProtobuf(SearchWsResponse.class).getUsersList())
.extracting(User::getLogin, User::getName, User::getEmail, User::getExternalIdentity, User::getExternalProvider,
- User::hasScmAccounts, User::hasAvatar, User::hasGroups, User::getTokensCount, User::hasLastConnectionDate)
+ User::hasScmAccounts, User::hasAvatar, User::hasGroups, User::getTokensCount, User::hasLastConnectionDate, User::hasManaged)
.containsExactlyInAnyOrder(
- tuple(user.getLogin(), user.getName(), user.getEmail(), user.getExternalLogin(), user.getExternalIdentityProvider(), true, true, true, 2, true));
+ tuple(user.getLogin(), user.getName(), user.getEmail(), user.getExternalLogin(), user.getExternalIdentityProvider(), true, true, true, 2, true, true));
userSession.logIn(otherUser);
assertThat(ws.newRequest().setParam("q", user.getLogin())
.setExternalLogin("sbrandhof@ldap.com")
.setExternalIdentityProvider("sonarqube")
.setScmAccounts(asList("simon.brandhof", "s.brandhof@company.tld")));
+ mockUsersAsManaged(simon.getUuid());
+
GroupDto sonarUsers = db.users().insertGroup("sonar-users");
GroupDto sonarAdministrators = db.users().insertGroup("sonar-administrators");
db.users().insertMember(sonarUsers, simon);
assertThat(action.params()).hasSize(4);
}
+ private void mockUsersAsManaged(String... userUuids) {
+ when(managedInstanceService.getUserUuidToManaged(any(), any())).thenAnswer(invocation ->
+ {
+ Set<?> allUsersUuids = invocation.getArgument(1, Set.class);
+ return allUsersUuids.stream()
+ .map(userUuid -> (String) userUuid)
+ .collect(toMap(identity(), userUuid -> Set.of(userUuids).contains(userUuid)));
+ }
+ );
+ }
+
}
*/
package org.sonar.server.usergroups.ws;
+import java.util.Set;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.api.server.ws.Change;
import org.sonar.db.user.GroupDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.server.management.ManagedInstanceService;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.usergroups.DefaultGroupFinder;
import org.sonar.server.ws.TestRequest;
import org.sonarqube.ws.Common.Paging;
import org.sonarqube.ws.MediaTypes;
+import static java.util.function.Function.identity;
+import static java.util.stream.Collectors.toMap;
import static org.apache.commons.lang.StringUtils.capitalize;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.Assertions.tuple;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import static org.sonar.api.server.ws.WebService.Param.FIELDS;
import static org.sonar.api.server.ws.WebService.Param.PAGE;
import static org.sonar.api.server.ws.WebService.Param.PAGE_SIZE;
@Rule
public UserSessionRule userSession = UserSessionRule.standalone();
+ private final ManagedInstanceService managedInstanceService = mock(ManagedInstanceService.class);
private final WsActionTester ws = new WsActionTester(new SearchAction(db.getDbClient(), userSession,
- new DefaultGroupFinder(db.getDbClient())));
+ new DefaultGroupFinder(db.getDbClient()), managedInstanceService));
@Test
public void define_search_action() {
assertThat(action.responseExampleAsString()).isNotEmpty();
assertThat(action.params()).hasSize(4);
assertThat(action.changelog()).extracting(Change::getVersion, Change::getDescription).containsOnly(
+ tuple("10.0", "Response includes 'managed' field."),
tuple("8.4", "Field 'id' in the response is deprecated. Format changes from integer to string."),
tuple("6.4", "Paging response fields moved to a Paging object"),
tuple("6.4", "'default' response field has been added"));
tuple("sonar-users", "Users", 0));
}
+ @Test
+ public void search_returnsCorrectlyIsManagedFlag() {
+ insertDefaultGroup(0);
+ insertGroup("admins", 0);
+ insertGroup("customer1", 0);
+ GroupDto customer2group = insertGroup("customer2", 0);
+ GroupDto customer3group = insertGroup("customer3", 0);
+ mockGroupAsManaged(customer2group.getUuid(), customer3group.getUuid());
+ loginAsAdmin();
+
+ SearchWsResponse response = call(ws.newRequest());
+
+ assertThat(response.getGroupsList()).extracting(Group::getName, Group::getManaged).containsOnly(
+ tuple("admins", false),
+ tuple("customer1", false),
+ tuple("customer2", true),
+ tuple("customer3", true),
+ tuple("sonar-users", false));
+ }
+
@Test
public void search_with_members() {
insertDefaultGroup(5);
insertDefaultGroup(0);
loginAsAdmin();
- assertThat(call(ws.newRequest()).getGroupsList()).extracting(Group::hasId, Group::hasName, Group::hasDescription, Group::hasMembersCount)
- .containsOnly(tuple(true, true, true, true));
- assertThat(call(ws.newRequest().setParam(FIELDS, "")).getGroupsList()).extracting(Group::hasId, Group::hasName, Group::hasDescription, Group::hasMembersCount)
- .containsOnly(tuple(true, true, true, true));
- assertThat(call(ws.newRequest().setParam(FIELDS, "name")).getGroupsList()).extracting(Group::hasId, Group::hasName, Group::hasDescription, Group::hasMembersCount)
- .containsOnly(tuple(true, true, false, false));
- assertThat(call(ws.newRequest().setParam(FIELDS, "description")).getGroupsList()).extracting(Group::hasId, Group::hasName, Group::hasDescription, Group::hasMembersCount)
- .containsOnly(tuple(true, false, true, false));
- assertThat(call(ws.newRequest().setParam(FIELDS, "membersCount")).getGroupsList()).extracting(Group::hasId, Group::hasName, Group::hasDescription, Group::hasMembersCount)
- .containsOnly(tuple(true, false, false, true));
+ assertThat(call(ws.newRequest()).getGroupsList())
+ .extracting(Group::hasId, Group::hasName, Group::hasDescription, Group::hasMembersCount, Group::hasManaged)
+ .containsOnly(tuple(true, true, true, true, true));
+ assertThat(call(ws.newRequest().setParam(FIELDS, "")).getGroupsList())
+ .extracting(Group::hasId, Group::hasName, Group::hasDescription, Group::hasMembersCount, Group::hasManaged)
+ .containsOnly(tuple(true, true, true, true, true));
+ assertThat(call(ws.newRequest().setParam(FIELDS, "name")).getGroupsList())
+ .extracting(Group::hasId, Group::hasName, Group::hasDescription, Group::hasMembersCount, Group::hasManaged)
+ .containsOnly(tuple(true, true, false, false, false));
+ assertThat(call(ws.newRequest().setParam(FIELDS, "description")).getGroupsList())
+ .extracting(Group::hasId, Group::hasName, Group::hasDescription, Group::hasMembersCount, Group::hasManaged)
+ .containsOnly(tuple(true, false, true, false, false));
+ assertThat(call(ws.newRequest().setParam(FIELDS, "membersCount")).getGroupsList())
+ .extracting(Group::hasId, Group::hasName, Group::hasDescription, Group::hasMembersCount, Group::hasManaged)
+ .containsOnly(tuple(true, false, false, true, false));
+ assertThat(call(ws.newRequest().setParam(FIELDS, "managed")).getGroupsList())
+ .extracting(Group::hasId, Group::hasName, Group::hasDescription, Group::hasMembersCount, Group::hasManaged)
+ .containsOnly(tuple(true, false, false, false, true));
}
@Test
@Test
public void test_json_example() {
insertDefaultGroup(17);
- insertGroup("administrators", 2);
+ GroupDto groupDto = insertGroup("administrators", 2);
+ mockGroupAsManaged(groupDto.getUuid());
loginAsAdmin();
String response = ws.newRequest().setMediaType(MediaTypes.JSON).execute().getInput();
assertThat(action.params()).extracting(WebService.Param::key).containsOnly("p", "q", "ps", "f");
- assertThat(action.param("f").possibleValues()).containsOnly("name", "description", "membersCount");
+ assertThat(action.param("f").possibleValues()).containsOnly("name", "description", "membersCount", "managed");
}
private SearchWsResponse call(TestRequest request) {
addMembers(group, numberOfMembers);
}
- private void insertGroup(String name, int numberOfMembers) {
+ private GroupDto insertGroup(String name, int numberOfMembers) {
GroupDto group = newGroupDto().setName(name).setDescription(capitalize(name));
db.users().insertGroup(group);
addMembers(group, numberOfMembers);
+ return group;
+ }
+
+ private void mockGroupAsManaged(String... groupUuids) {
+ when(managedInstanceService.getGroupUuidToManaged(any(), any())).thenAnswer(invocation ->
+ {
+ @SuppressWarnings("unchecked")
+ Set<String> allGroupUuids = (Set<String>) invocation.getArgument(1, Set.class);
+ return allGroupUuids.stream()
+ .collect(toMap(identity(), userUuid -> Set.of(groupUuids).contains(userUuid)));
+ }
+ );
}
private void addMembers(GroupDto group, int numberOfMembers) {
*/
package org.sonar.server.usergroups.ws;
+import java.util.Set;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.api.server.ws.Change;
import org.sonar.db.user.UserDto;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.management.ManagedInstanceService;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.usergroups.DefaultGroupFinder;
import org.sonar.server.ws.TestRequest;
import org.sonar.server.ws.WsActionTester;
+import static java.util.function.Function.identity;
+import static java.util.stream.Collectors.toMap;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.Assertions.tuple;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import static org.sonar.db.permission.GlobalPermission.ADMINISTER;
import static org.sonar.db.user.UserTesting.newUserDto;
import static org.sonar.server.usergroups.ws.GroupWsSupport.PARAM_GROUP_NAME;
public DbTester db = DbTester.create(System2.INSTANCE);
@Rule
public UserSessionRule userSession = UserSessionRule.standalone();
+
+ private final ManagedInstanceService managedInstanceService = mock(ManagedInstanceService.class);
+
private final WsActionTester ws = new WsActionTester(
- new UsersAction(db.getDbClient(), userSession, new GroupWsSupport(db.getDbClient(), new DefaultGroupFinder(db.getDbClient()))));
+ new UsersAction(db.getDbClient(), userSession, managedInstanceService, new GroupWsSupport(db.getDbClient(), new DefaultGroupFinder(db.getDbClient()))));
@Test
public void verify_definition() {
assertThat(wsDef.since()).isEqualTo("5.2");
assertThat(wsDef.isPost()).isFalse();
assertThat(wsDef.changelog()).extracting(Change::getVersion, Change::getDescription).containsOnly(
+ tuple("10.0", "Field 'managed' added to the payload."),
tuple("10.0", "Parameter 'id' is removed. Use 'name' instead."),
tuple("9.8", "response fields 'total', 's', 'ps' have been deprecated, please use 'paging' object instead."),
tuple("9.8", "The field 'paging' has been added to the response."),
""");
}
+ @Test
+ public void test_isManagedFlag() {
+ GroupDto group = db.users().insertGroup();
+ UserDto lovelace = db.users().insertUser(newUserDto().setLogin("ada.login").setName("Ada Lovelace"));
+ UserDto hopper = db.users().insertUser(newUserDto().setLogin("grace").setName("Grace Hopper"));
+ mockUsersAsManaged(hopper.getUuid());
+ db.users().insertMember(group, hopper);
+ db.users().insertMember(group, lovelace);
+ loginAsAdmin();
+
+ String result = newUsersRequest()
+ .setParam(PARAM_GROUP_NAME, group.getName())
+ .execute()
+ .getInput();
+
+ assertJson(result).isSimilarTo("""
+ {
+ "users": [
+ {"login": "ada.login", "name": "Ada Lovelace", "managed": false},
+ {"login": "grace", "name": "Grace Hopper", "managed": true}
+ ]
+ }
+ """);
+ }
+
@Test
public void filter_members_by_name() {
GroupDto group = db.users().insertGroup("a group");
db.users().insertMember(group, admin);
UserDto george = db.users().insertUser(newUserDto().setLogin("george.orwell").setName("George Orwell"));
db.users().insertMember(group, george);
+ mockUsersAsManaged(george.getUuid());
loginAsAdmin();
String result = newUsersRequest()
userSession.logIn().addPermission(ADMINISTER);
}
+ private void mockUsersAsManaged(String... userUuids) {
+ when(managedInstanceService.getUserUuidToManaged(any(), any())).thenAnswer(invocation ->
+ {
+ Set<?> allUsersUuids = invocation.getArgument(1, Set.class);
+ return allUsersUuids.stream()
+ .map(userUuid -> (String) userUuid)
+ .collect(toMap(identity(), userUuid -> Set.of(userUuids).contains(userUuid)));
+ }
+ );
+ }
+
}
"email": "email-1",
"permissions": [
"scan"
- ]
+ ],
+ "managed": true
},
{
"login": "login-2",
"email": "email-2",
"permissions": [
"scan"
- ]
+ ],
+ "managed": false
}
]
}
import com.google.common.collect.TreeMultimap;
import com.google.common.io.Resources;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
+import java.util.Set;
import javax.annotation.Nullable;
import org.sonar.api.security.DefaultGroups;
import org.sonar.api.server.ws.Change;
import org.sonar.db.permission.GroupPermissionDto;
import org.sonar.db.permission.PermissionQuery;
import org.sonar.db.user.GroupDto;
+import org.sonar.server.management.ManagedInstanceService;
import org.sonar.server.user.UserSession;
import org.sonarqube.ws.Permissions.Group;
import org.sonarqube.ws.Permissions.WsGroupsResponse;
import static java.util.Collections.emptyList;
import static java.util.Optional.ofNullable;
+import static java.util.stream.Collectors.toSet;
import static org.sonar.db.permission.PermissionQuery.DEFAULT_PAGE_SIZE;
import static org.sonar.db.permission.PermissionQuery.RESULTS_MAX_SIZE;
import static org.sonar.db.permission.PermissionQuery.SEARCH_QUERY_MIN_LENGTH;
private final UserSession userSession;
private final PermissionWsSupport wsSupport;
private final WsParameters wsParameters;
+ private final ManagedInstanceService managedInstanceService;
- public GroupsAction(DbClient dbClient, UserSession userSession, PermissionWsSupport wsSupport, WsParameters wsParameters) {
+ public GroupsAction(DbClient dbClient, UserSession userSession, PermissionWsSupport wsSupport, WsParameters wsParameters,
+ ManagedInstanceService managedInstanceService) {
this.dbClient = dbClient;
this.userSession = userSession;
this.wsSupport = wsSupport;
this.wsParameters = wsParameters;
+ this.managedInstanceService = managedInstanceService;
}
@Override
"</ul>")
.addPagingParams(DEFAULT_PAGE_SIZE, RESULTS_MAX_SIZE)
.setChangelog(
+ new Change("10.0", "Response includes 'managed' field."),
new Change("8.4", "Field 'id' in the response is deprecated. Format changes from integer to string."),
new Change("7.4", "The response list is returning all groups even those without permissions, the groups with permission are at the top of the list."))
.setResponseExample(Resources.getResource(getClass(), "groups-example.json"))
List<GroupDto> groups = findGroups(dbSession, query);
int total = dbClient.groupPermissionDao().countGroupsByQuery(dbSession, query);
List<GroupPermissionDto> groupsWithPermission = findGroupPermissions(dbSession, groups, project.orElse(null));
+ Map<String, Boolean> groupUuidToIsManaged = managedInstanceService.getGroupUuidToManaged(dbSession, getUserUuids(groups));
Paging paging = Paging.forPageIndex(request.mandatoryParamAsInt(Param.PAGE)).withPageSize(query.getPageSize()).andTotal(total);
- WsGroupsResponse groupsResponse = buildResponse(groups, groupsWithPermission, paging);
+ WsGroupsResponse groupsResponse = buildResponse(groups, groupsWithPermission, groupUuidToIsManaged, paging);
writeProtobuf(groupsResponse, request, response);
}
}
+ private static Set<String> getUserUuids(List<GroupDto> groups) {
+ return groups.stream().map(GroupDto::getUuid).collect(toSet());
+ }
+
private static PermissionQuery buildPermissionQuery(Request request, @Nullable ComponentDto project) {
String textQuery = request.param(Param.TEXT_QUERY);
PermissionQuery.Builder permissionQuery = PermissionQuery.builder()
return permissionQuery.build();
}
- private static WsGroupsResponse buildResponse(List<GroupDto> groups, List<GroupPermissionDto> groupPermissions, Paging paging) {
+ private static WsGroupsResponse buildResponse(List<GroupDto> groups, List<GroupPermissionDto> groupPermissions,
+ Map<String, Boolean> groupUuidToIsManaged, Paging paging) {
Multimap<String, String> permissionsByGroupUuid = TreeMultimap.create();
groupPermissions.forEach(groupPermission -> permissionsByGroupUuid.put(groupPermission.getGroupUuid(), groupPermission.getRole()));
WsGroupsResponse.Builder response = WsGroupsResponse.newBuilder();
}
ofNullable(group.getDescription()).ifPresent(wsGroup::setDescription);
wsGroup.addAllPermissions(permissionsByGroupUuid.get(group.getUuid()));
+ wsGroup.setManaged(groupUuidToIsManaged.getOrDefault(group.getUuid(), false));
});
response.getPagingBuilder()
import com.google.common.collect.Ordering;
import com.google.common.collect.TreeMultimap;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
+import java.util.Set;
import javax.annotation.Nullable;
import org.sonar.api.server.ws.Change;
import org.sonar.api.server.ws.Request;
import org.sonar.db.permission.UserPermissionDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.issue.AvatarResolver;
+import org.sonar.server.management.ManagedInstanceService;
import org.sonar.server.permission.RequestValidator;
import org.sonar.server.user.UserSession;
import org.sonarqube.ws.Permissions;
import static com.google.common.base.Strings.emptyToNull;
import static java.util.Collections.emptyList;
import static java.util.Optional.ofNullable;
+import static java.util.stream.Collectors.toSet;
import static org.sonar.db.permission.PermissionQuery.DEFAULT_PAGE_SIZE;
import static org.sonar.db.permission.PermissionQuery.RESULTS_MAX_SIZE;
import static org.sonar.db.permission.PermissionQuery.SEARCH_QUERY_MIN_LENGTH;
private final AvatarResolver avatarResolver;
private final WsParameters wsParameters;
private final RequestValidator requestValidator;
+ private final ManagedInstanceService managedInstanceService;
public UsersAction(DbClient dbClient, UserSession userSession, PermissionWsSupport wsSupport, AvatarResolver avatarResolver, WsParameters wsParameters,
- RequestValidator requestValidator) {
+ RequestValidator requestValidator, ManagedInstanceService managedInstanceService) {
this.dbClient = dbClient;
this.userSession = userSession;
this.wsSupport = wsSupport;
this.avatarResolver = avatarResolver;
this.wsParameters = wsParameters;
this.requestValidator = requestValidator;
+ this.managedInstanceService = managedInstanceService;
}
@Override
"</ul>")
.addPagingParams(DEFAULT_PAGE_SIZE, RESULTS_MAX_SIZE)
.setChangelog(
+ new Change("10.0", "Response includes 'managed' field."),
new Change("7.4", "The response list is returning all users even those without permissions, the users with permission are at the top of the list."))
.setInternal(true)
.setResponseExample(getClass().getResource("users-example.json"))
int total = dbClient.userPermissionDao().countUsersByQuery(dbSession, query);
List<UserPermissionDto> userPermissions = findUserPermissions(dbSession, users, project.orElse(null));
Paging paging = Paging.forPageIndex(request.mandatoryParamAsInt(Param.PAGE)).withPageSize(query.getPageSize()).andTotal(total);
- UsersWsResponse usersWsResponse = buildResponse(users, userPermissions, paging);
+ Map<String, Boolean> userUuidToIsManaged = managedInstanceService.getUserUuidToManaged(dbSession, getUserUuids(users));
+ UsersWsResponse usersWsResponse = buildResponse(users, userPermissions, userUuidToIsManaged, paging);
writeProtobuf(usersWsResponse, request, response);
}
}
+ private static Set<String> getUserUuids(List<UserDto> users) {
+ return users.stream().map(UserDto::getUuid).collect(toSet());
+ }
+
private PermissionQuery buildPermissionQuery(Request request, @Nullable ComponentDto project) {
String textQuery = request.param(Param.TEXT_QUERY);
String permission = request.param(PARAM_PERMISSION);
return permissionQuery.build();
}
- private UsersWsResponse buildResponse(List<UserDto> users, List<UserPermissionDto> userPermissions, Paging paging) {
+ private UsersWsResponse buildResponse(List<UserDto> users, List<UserPermissionDto> userPermissions, Map<String, Boolean> userUuidToIsManaged,
+ Paging paging) {
Multimap<String, String> permissionsByUserUuid = TreeMultimap.create();
userPermissions.forEach(userPermission -> permissionsByUserUuid.put(userPermission.getUserUuid(), userPermission.getPermission()));
ofNullable(user.getEmail()).ifPresent(userResponse::setEmail);
ofNullable(emptyToNull(user.getEmail())).ifPresent(u -> userResponse.setAvatar(avatarResolver.create(user)));
ofNullable(user.getName()).ifPresent(userResponse::setName);
+ ofNullable(userUuidToIsManaged.get(user.getUuid())).ifPresent(userResponse::setManaged);
});
response.getPagingBuilder()
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.sonar.api.server.ws.Change;
import org.sonar.server.es.SearchOptions;
import org.sonar.server.es.SearchResult;
import org.sonar.server.issue.AvatarResolver;
+import org.sonar.server.management.ManagedInstanceService;
import org.sonar.server.user.UserSession;
import org.sonar.server.user.index.UserDoc;
import org.sonar.server.user.index.UserIndex;
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Strings.emptyToNull;
+import static java.lang.Boolean.TRUE;
import static java.util.Optional.ofNullable;
import static org.sonar.api.server.ws.WebService.Param.PAGE;
import static org.sonar.api.server.ws.WebService.Param.PAGE_SIZE;
private final UserIndex userIndex;
private final DbClient dbClient;
private final AvatarResolver avatarResolver;
+ private final ManagedInstanceService managedInstanceService;
- public SearchAction(UserSession userSession, UserIndex userIndex, DbClient dbClient, AvatarResolver avatarResolver) {
+ public SearchAction(UserSession userSession, UserIndex userIndex, DbClient dbClient, AvatarResolver avatarResolver,
+ ManagedInstanceService managedInstanceService) {
this.userSession = userSession;
this.userIndex = userIndex;
this.dbClient = dbClient;
this.avatarResolver = avatarResolver;
+ this.managedInstanceService = managedInstanceService;
}
@Override
"Field 'lastConnectionDate' is only updated every hour, so it may not be accurate, for instance when a user authenticates many times in less than one hour.")
.setSince("3.6")
.setChangelog(
+ new Change("10.0", "Response includes 'managed' field."),
new Change("9.7", "New parameter 'deactivated' to optionally search for deactivated users"),
new Change("7.7", "New field 'lastConnectionDate' is added to response"),
new Change("7.4", "External identity is only returned to system administrators"),
Multimap<String, String> groupsByLogin = dbClient.groupMembershipDao().selectGroupsByLogins(dbSession, logins);
List<UserDto> users = dbClient.userDao().selectByOrderedLogins(dbSession, logins);
Map<String, Integer> tokenCountsByLogin = dbClient.userTokenDao().countTokensByUsers(dbSession, users);
+ Map<String, Boolean> userUuidToIsManaged = managedInstanceService.getUserUuidToManaged(dbSession, getUserUuids(users));
Paging paging = forPageIndex(request.getPage()).withPageSize(request.getPageSize()).andTotal((int) result.getTotal());
- return buildResponse(users, groupsByLogin, tokenCountsByLogin, paging);
+ return buildResponse(users, groupsByLogin, tokenCountsByLogin, userUuidToIsManaged, paging);
}
}
- private SearchWsResponse buildResponse(List<UserDto> users, Multimap<String, String> groupsByLogin, Map<String, Integer> tokenCountsByLogin, Paging paging) {
+ private static Set<String> getUserUuids(List<UserDto> users) {
+ return users.stream().map(UserDto::getUuid).collect(Collectors.toSet());
+ }
+
+ private SearchWsResponse buildResponse(List<UserDto> users, Multimap<String, String> groupsByLogin, Map<String, Integer> tokenCountsByLogin,
+ Map<String, Boolean> userUuidToIsManaged, Paging paging) {
SearchWsResponse.Builder responseBuilder = newBuilder();
- users.forEach(user -> responseBuilder.addUsers(towsUser(user, firstNonNull(tokenCountsByLogin.get(user.getUuid()), 0), groupsByLogin.get(user.getLogin()))));
+ users.forEach(user -> responseBuilder.addUsers(
+ towsUser(user, firstNonNull(tokenCountsByLogin.get(user.getUuid()), 0), groupsByLogin.get(user.getLogin()), userUuidToIsManaged.get(user.getUuid()))
+ ));
responseBuilder.getPagingBuilder()
.setPageIndex(paging.pageIndex())
.setPageSize(paging.pageSize())
return responseBuilder.build();
}
- private User towsUser(UserDto user, @Nullable Integer tokensCount, Collection<String> groups) {
+ private User towsUser(UserDto user, @Nullable Integer tokensCount, Collection<String> groups, Boolean isManaged) {
User.Builder userBuilder = User.newBuilder().setLogin(user.getLogin());
ofNullable(user.getName()).ifPresent(userBuilder::setName);
if (userSession.isLoggedIn()) {
ofNullable(user.getExternalLogin()).ifPresent(userBuilder::setExternalIdentity);
ofNullable(tokensCount).ifPresent(userBuilder::setTokensCount);
ofNullable(user.getLastConnectionDate()).ifPresent(date -> userBuilder.setLastConnectionDate(formatDateTime(date)));
+ userBuilder.setManaged(TRUE.equals(isManaged));
}
return userBuilder.build();
}
import org.sonar.db.DbSession;
import org.sonar.db.user.GroupDto;
import org.sonar.server.es.SearchOptions;
+import org.sonar.server.management.ManagedInstanceService;
import org.sonar.server.user.UserSession;
import org.sonar.server.usergroups.DefaultGroupFinder;
+import static java.lang.Boolean.TRUE;
import static java.util.Optional.ofNullable;
import static org.apache.commons.lang.StringUtils.defaultIfBlank;
import static org.sonar.api.utils.Paging.forPageIndex;
private static final String FIELD_NAME = "name";
private static final String FIELD_DESCRIPTION = "description";
private static final String FIELD_MEMBERS_COUNT = "membersCount";
- private static final List<String> ALL_FIELDS = Arrays.asList(FIELD_NAME, FIELD_DESCRIPTION, FIELD_MEMBERS_COUNT);
+ private static final String FIELD_IS_MANAGED = "managed";
+ private static final List<String> ALL_FIELDS = Arrays.asList(FIELD_NAME, FIELD_DESCRIPTION, FIELD_MEMBERS_COUNT, FIELD_IS_MANAGED);
private final DbClient dbClient;
private final UserSession userSession;
private final DefaultGroupFinder defaultGroupFinder;
+ private final ManagedInstanceService managedInstanceService;
- public SearchAction(DbClient dbClient, UserSession userSession, DefaultGroupFinder defaultGroupFinder) {
+ public SearchAction(DbClient dbClient, UserSession userSession, DefaultGroupFinder defaultGroupFinder, ManagedInstanceService managedInstanceService) {
this.dbClient = dbClient;
this.userSession = userSession;
this.defaultGroupFinder = defaultGroupFinder;
+ this.managedInstanceService = managedInstanceService;
}
@Override
.addPagingParams(100, MAX_PAGE_SIZE)
.addSearchQuery("sonar-users", "names")
.setChangelog(
+ new Change("10.0", "Response includes 'managed' field."),
new Change("8.4", "Field 'id' in the response is deprecated. Format changes from integer to string."),
new Change("6.4", "Paging response fields moved to a Paging object"),
new Change("6.4", "'default' response field has been added"));
Paging paging = forPageIndex(page).withPageSize(pageSize).andTotal(limit);
List<GroupDto> groups = dbClient.groupDao().selectByQuery(dbSession, query, options.getOffset(), pageSize);
List<String> groupUuids = groups.stream().map(GroupDto::getUuid).collect(MoreCollectors.toList(groups.size()));
+ Map<String, Boolean> groupUuidToIsManaged = managedInstanceService.getGroupUuidToManaged(dbSession, new HashSet<>(groupUuids));
Map<String, Integer> userCountByGroup = dbClient.groupMembershipDao().countUsersByGroups(dbSession, groupUuids);
- writeProtobuf(buildResponse(groups, userCountByGroup, fields, paging, defaultGroup), request, response);
+ writeProtobuf(buildResponse(groups, userCountByGroup, groupUuidToIsManaged, fields, paging, defaultGroup), request, response);
}
}
return fields;
}
- private static SearchWsResponse buildResponse(List<GroupDto> groups, Map<String, Integer> userCountByGroup, Set<String> fields, Paging paging, GroupDto defaultGroup) {
+ private static SearchWsResponse buildResponse(List<GroupDto> groups, Map<String, Integer> userCountByGroup,
+ Map<String, Boolean> groupUuidToIsManaged, Set<String> fields, Paging paging, GroupDto defaultGroup) {
SearchWsResponse.Builder responseBuilder = SearchWsResponse.newBuilder();
groups.forEach(group -> responseBuilder
- .addGroups(toWsGroup(group, userCountByGroup.get(group.getName()), fields, defaultGroup.getUuid().equals(group.getUuid()))));
+ .addGroups(toWsGroup(group, userCountByGroup.get(group.getName()), groupUuidToIsManaged.get(group.getUuid()), fields, defaultGroup.getUuid().equals(group.getUuid()))));
responseBuilder.getPagingBuilder()
.setPageIndex(paging.pageIndex())
.setPageSize(paging.pageSize())
return responseBuilder.build();
}
- private static Group toWsGroup(GroupDto group, Integer memberCount, Set<String> fields, boolean isDefault) {
+ private static Group toWsGroup(GroupDto group, Integer memberCount, Boolean isManaged, Set<String> fields, boolean isDefault) {
Group.Builder groupBuilder = Group.newBuilder()
.setId(group.getUuid())
.setDefault(isDefault);
if (fields.contains(FIELD_MEMBERS_COUNT)) {
groupBuilder.setMembersCount(memberCount);
}
+ if (fields.contains(FIELD_IS_MANAGED)) {
+ groupBuilder.setManaged(TRUE.equals(isManaged));
+ }
return groupBuilder.build();
}
package org.sonar.server.usergroups.ws;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
import org.sonar.api.server.ws.Change;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.db.permission.GlobalPermission;
import org.sonar.db.user.UserMembershipDto;
import org.sonar.db.user.UserMembershipQuery;
+import org.sonar.server.management.ManagedInstanceService;
import org.sonar.server.permission.GroupUuid;
import org.sonar.server.user.UserSession;
+import static java.lang.Boolean.TRUE;
+import static java.util.stream.Collectors.toSet;
import static org.sonar.api.utils.Paging.forPageIndex;
import static org.sonar.server.usergroups.ws.GroupWsSupport.defineGroupWsParameters;
private static final String FIELD_SELECTED = "selected";
private static final String FIELD_NAME = "name";
private static final String FIELD_LOGIN = "login";
+ private static final String FIELD_MANAGED = "managed";
private final DbClient dbClient;
private final UserSession userSession;
+ private final ManagedInstanceService managedInstanceService;
private final GroupWsSupport support;
- public UsersAction(DbClient dbClient, UserSession userSession, GroupWsSupport support) {
+ public UsersAction(DbClient dbClient, UserSession userSession, ManagedInstanceService managedInstanceService, GroupWsSupport support) {
this.dbClient = dbClient;
this.userSession = userSession;
+ this.managedInstanceService = managedInstanceService;
this.support = support;
}
.addSearchQuery("freddy", "names", "logins")
.addPagingParams(25)
.setChangelog(
+ new Change("10.0", "Field 'managed' added to the payload."),
new Change("10.0", "Parameter 'id' is removed. Use 'name' instead."),
new Change("9.8", "response fields 'total', 's', 'ps' have been deprecated, please use 'paging' object instead."),
new Change("9.8", "The field 'paging' has been added to the response."),
int total = dbClient.groupMembershipDao().countMembers(dbSession, query);
Paging paging = forPageIndex(page).withPageSize(pageSize).andTotal(total);
List<UserMembershipDto> users = dbClient.groupMembershipDao().selectMembers(dbSession, query, paging.offset(), paging.pageSize());
-
+ Map<String, Boolean> userUuidToIsManaged = managedInstanceService.getUserUuidToManaged(dbSession, getUserUuids(users));
try (JsonWriter json = response.newJsonWriter()) {
json.beginObject();
- writeMembers(json, users);
+ writeMembers(json, users, userUuidToIsManaged);
writePaging(json, paging);
json.name("paging").beginObject()
.prop("pageIndex", page)
}
}
- private static void writeMembers(JsonWriter json, List<UserMembershipDto> users) {
+ private static Set<String> getUserUuids(List<UserMembershipDto> users) {
+ return users.stream().map(UserMembershipDto::getUuid).collect(toSet());
+ }
+
+ private static void writeMembers(JsonWriter json, List<UserMembershipDto> users, Map<String, Boolean> userUuidToIsManaged) {
json.name("users").beginArray();
for (UserMembershipDto user : users) {
json.beginObject()
.prop(FIELD_LOGIN, user.getLogin())
.prop(FIELD_NAME, user.getName())
.prop(FIELD_SELECTED, user.getGroupUuid() != null)
+ .prop(FIELD_MANAGED, TRUE.equals(userUuidToIsManaged.get(user.getUuid())))
.endObject();
}
json.endArray();
"id": "AU-Tpxb--iU5OvuD2FLy",
"name": "sonar-administrators",
"description": "System administrators",
- "permissions": []
+ "permissions": [],
+ "managed": false
},
{
"id": "AU-Tpxb--iU5OvuD2FLz",
"name": "sonar-users",
"description": "Every authenticated user automatically belongs to this group",
- "permissions": []
+ "permissions": [],
+ "managed": true
}
]
}
"name": "Administrator",
"email": "admin@admin.com",
"avatar": "64e1b8d34f425d19e1ee2ea7236d3028",
- "permissions": ["admin", "gateadmin", "profileadmin"]
+ "permissions": ["admin", "gateadmin", "profileadmin"],
+ "managed": false
},
{
"login": "george.orwell",
"name": "George Orwell",
"email": "george.orwell@1984.net",
"avatar": "583af86a274c1027ef078cada831babf",
- "permissions": ["scan"]
+ "permissions": ["scan"],
+ "managed": true
},
{
"login": "adam.west",
"name": "Adam West",
"email": "adamwest@adamwest.com",
"avatar": "9b55aba24cc5ee533294334bd20abb34",
- "permissions": []
+ "permissions": [],
+ "managed": false
}
]
}
"local": true,
"externalIdentity": "fmallet",
"externalProvider": "sonarqube",
- "avatar": "2f9dff586d3f74f825b059e3798a3bbb"
+ "avatar": "2f9dff586d3f74f825b059e3798a3bbb",
+ "managed": false
},
{
"login": "sbrandhof",
"local": false,
"externalIdentity": "sbrandhof@ldap.com",
"externalProvider": "sonarqube",
- "avatar": "3930ad855bc7fe48db8e9a663174cdd3"
+ "avatar": "3930ad855bc7fe48db8e9a663174cdd3",
+ "managed": true
}
]
}
"name": "sonar-users",
"description": "Users",
"membersCount": 17,
- "default": true
+ "default": true,
+ "managed": false
},
{
"id": "AU-Tpxb--iU5OvuD2FLz",
"name": "administrators",
"description": "Administrators",
"membersCount": 2,
- "default": false
+ "default": false,
+ "managed": true
}
]
}
{
"login": "admin",
"name": "Administrator",
- "selected": true
+ "selected": true,
+ "managed": false
},
{
"login": "george.orwell",
"name": "George Orwell",
- "selected": true
+ "selected": true,
+ "managed": true
}
],
"paging": {
--- /dev/null
+{
+ "paging": {
+ "pageIndex": 1,
+ "pageSize": 20,
+ "total": 2
+ },
+ "groups": [
+ {
+ "name": "local-group",
+ "managed": false
+ },
+ {
+ "name": "managed-group",
+ "managed": true
+ }
+ ]
+}
optional string email = 3;
repeated string permissions = 4;
optional string avatar = 5;
+ optional bool managed = 6;
}
message OldGroup {
optional string name = 2;
optional string description = 3;
repeated string permissions = 4;
+ optional bool managed = 5;
}
optional string description = 4;
optional int32 membersCount = 5;
optional bool default = 6;
+ optional bool managed = 7;
}
optional string externalProvider = 10;
optional string avatar = 11;
optional string lastConnectionDate = 12;
+ optional bool managed = 13;
}
message Groups {