aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-webserver-webapi
diff options
context:
space:
mode:
authorAntoine Vigneau <antoine.vigneau@sonarsource.com>2023-07-25 21:07:09 +0200
committersonartech <sonartech@sonarsource.com>2023-07-28 20:03:15 +0000
commitc43733d1db5a9aa18b9b8985ad2a19972421987d (patch)
tree512938ea09fc2bc2d7453d6c9abbf969ee8fc112 /server/sonar-webserver-webapi
parent3994545c5d4313228716935ec752c0eb1253a077 (diff)
downloadsonarqube-c43733d1db5a9aa18b9b8985ad2a19972421987d.tar.gz
sonarqube-c43733d1db5a9aa18b9b8985ad2a19972421987d.zip
SONAR-19969 Support apiv2 POST /users to create a user
Diffstat (limited to 'server/sonar-webserver-webapi')
-rw-r--r--server/sonar-webserver-webapi/src/it/java/org/sonar/server/user/ws/CreateActionIT.java12
-rw-r--r--server/sonar-webserver-webapi/src/it/java/org/sonar/server/user/ws/DeactivateActionIT.java4
-rw-r--r--server/sonar-webserver-webapi/src/it/java/org/sonar/server/user/ws/SearchActionIT.java4
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/CreateAction.java80
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/UpdateAction.java7
5 files changed, 40 insertions, 67 deletions
diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/user/ws/CreateActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/user/ws/CreateActionIT.java
index 5071be725cd..430dc0e30c7 100644
--- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/user/ws/CreateActionIT.java
+++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/user/ws/CreateActionIT.java
@@ -33,10 +33,14 @@ import org.sonar.db.audit.NoOpAuditPersister;
import org.sonar.db.user.GroupDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.authentication.CredentialsLocalAuthentication;
+import org.sonar.server.common.avatar.AvatarResolverImpl;
+import org.sonar.server.common.management.ManagedInstanceChecker;
+import org.sonar.server.common.user.UserDeactivator;
+import org.sonar.server.common.user.service.UserService;
import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.exceptions.UnauthorizedException;
-import org.sonar.server.common.management.ManagedInstanceChecker;
+import org.sonar.server.management.ManagedInstanceService;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.user.NewUserNotifier;
import org.sonar.server.user.UserUpdater;
@@ -72,8 +76,10 @@ public class CreateActionIT {
private final CredentialsLocalAuthentication localAuthentication = new CredentialsLocalAuthentication(db.getDbClient(), settings.asConfig());
private final ManagedInstanceChecker managedInstanceChecker = mock(ManagedInstanceChecker.class);
- private final WsActionTester tester = new WsActionTester(new CreateAction(db.getDbClient(), new UserUpdater(mock(NewUserNotifier.class),
- db.getDbClient(), new DefaultGroupFinder(db.getDbClient()), settings.asConfig(), new NoOpAuditPersister(), localAuthentication), userSessionRule, managedInstanceChecker));
+ private final ManagedInstanceService managedInstanceService = mock(ManagedInstanceService.class);
+ private final UserService userService = new UserService(db.getDbClient(), new AvatarResolverImpl(), managedInstanceService, managedInstanceChecker, mock(UserDeactivator.class),
+ new UserUpdater(mock(NewUserNotifier.class), db.getDbClient(), new DefaultGroupFinder(db.getDbClient()), settings.asConfig(), new NoOpAuditPersister(), localAuthentication));
+ private final WsActionTester tester = new WsActionTester(new CreateAction(userSessionRule, managedInstanceChecker, userService));
@Before
public void setUp() {
diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/user/ws/DeactivateActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/user/ws/DeactivateActionIT.java
index 129f5561d38..87eebebcc4e 100644
--- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/user/ws/DeactivateActionIT.java
+++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/user/ws/DeactivateActionIT.java
@@ -56,6 +56,7 @@ import org.sonar.server.exceptions.UnauthorizedException;
import org.sonar.server.management.ManagedInstanceService;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.user.ExternalIdentity;
+import org.sonar.server.user.UserUpdater;
import org.sonar.server.ws.TestRequest;
import org.sonar.server.ws.TestResponse;
import org.sonar.server.ws.WsActionTester;
@@ -85,7 +86,8 @@ public class DeactivateActionIT {
private final UserDeactivator userDeactivator = new UserDeactivator(dbClient, userAnonymizer);
private final ManagedInstanceChecker managedInstanceChecker = mock(ManagedInstanceChecker.class);
- private final UserService userService = new UserService(dbClient, mock(AvatarResolver.class), mock(ManagedInstanceService.class), managedInstanceChecker, userDeactivator);
+ private final UserService userService = new UserService(dbClient, mock(AvatarResolver.class), mock(ManagedInstanceService.class), managedInstanceChecker, userDeactivator,
+ mock(UserUpdater.class));
private final WsActionTester ws = new WsActionTester(new DeactivateAction(dbClient, userSession, new UserJsonWriter(userSession), userService));
@Test
diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/user/ws/SearchActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/user/ws/SearchActionIT.java
index 732d219d5a0..091a47fd5ac 100644
--- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/user/ws/SearchActionIT.java
+++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/user/ws/SearchActionIT.java
@@ -44,6 +44,7 @@ import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.exceptions.ServerException;
import org.sonar.server.management.ManagedInstanceService;
import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.user.UserUpdater;
import org.sonar.server.ws.TestRequest;
import org.sonar.server.ws.WsActionTester;
import org.sonarqube.ws.Common.Paging;
@@ -81,7 +82,8 @@ public class SearchActionIT {
new AvatarResolverImpl(),
managedInstanceService,
mock(ManagedInstanceChecker.class),
- mock(UserDeactivator.class));
+ mock(UserDeactivator.class),
+ mock(UserUpdater.class));
private final SearchWsReponseGenerator searchWsReponseGenerator = new SearchWsReponseGenerator(userSession);
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/CreateAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/CreateAction.java
index 839e2697918..e6c65a5fa59 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/CreateAction.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/CreateAction.java
@@ -19,23 +19,19 @@
*/
package org.sonar.server.user.ws;
-import java.util.HashSet;
import java.util.List;
-import java.util.Set;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.sonar.api.server.ws.Change;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
import org.sonar.db.user.UserDto;
import org.sonar.server.common.management.ManagedInstanceChecker;
-import org.sonar.server.user.ExternalIdentity;
-import org.sonar.server.user.NewUser;
+import org.sonar.server.common.user.service.UserCreateRequest;
+import org.sonar.server.common.user.service.UserSearchResult;
+import org.sonar.server.common.user.service.UserService;
import org.sonar.server.user.UserSession;
-import org.sonar.server.user.UserUpdater;
import org.sonarqube.ws.Users.CreateWsResponse;
import static com.google.common.base.Preconditions.checkArgument;
@@ -43,7 +39,6 @@ import static com.google.common.base.Strings.emptyToNull;
import static com.google.common.base.Strings.isNullOrEmpty;
import static java.util.Collections.emptyList;
import static java.util.Optional.ofNullable;
-import static org.sonar.server.user.ExternalIdentity.SQ_AUTHORITY;
import static org.sonar.server.user.UserUpdater.EMAIL_MAX_LENGTH;
import static org.sonar.server.user.UserUpdater.LOGIN_MAX_LENGTH;
import static org.sonar.server.user.UserUpdater.LOGIN_MIN_LENGTH;
@@ -60,16 +55,14 @@ import static org.sonarqube.ws.client.user.UsersWsParameters.PARAM_SCM_ACCOUNT;
public class CreateAction implements UsersWsAction {
- private final DbClient dbClient;
- private final UserUpdater userUpdater;
private final UserSession userSession;
private final ManagedInstanceChecker managedInstanceChecker;
+ private final UserService userService;
- public CreateAction(DbClient dbClient, UserUpdater userUpdater, UserSession userSession, ManagedInstanceChecker managedInstanceService) {
- this.dbClient = dbClient;
- this.userUpdater = userUpdater;
+ public CreateAction(UserSession userSession, ManagedInstanceChecker managedInstanceService, UserService userService) {
this.userSession = userSession;
this.managedInstanceChecker = managedInstanceService;
+ this.userService = userService;
}
@Override
@@ -124,32 +117,17 @@ public class CreateAction implements UsersWsAction {
public void handle(Request request, Response response) throws Exception {
userSession.checkLoggedIn().checkIsSystemAdministrator();
managedInstanceChecker.throwIfInstanceIsManaged();
- CreateRequest createRequest = toWsRequest(request);
- checkArgument(isValidIfPresent(createRequest.getEmail()), "Email '%s' is not valid", createRequest.getEmail());
- writeProtobuf(doHandle(createRequest), request, response);
+
+ UserCreateRequest userCreateRequest = toUserCreateRequest(request);
+ String email = userCreateRequest.getEmail().orElse(null);
+ checkArgument(isValidIfPresent(email), "Email '%s' is not valid", email);
+
+ writeProtobuf(doHandle(userCreateRequest), request, response);
}
- private CreateWsResponse doHandle(CreateRequest request) {
- try (DbSession dbSession = dbClient.openSession(false)) {
- String login = request.getLogin();
- NewUser.Builder newUser = NewUser.builder()
- .setLogin(login)
- .setName(request.getName())
- .setEmail(request.getEmail())
- .setScmAccounts(request.getScmAccounts())
- .setPassword(request.getPassword());
- if (!request.isLocal()) {
- newUser.setExternalIdentity(new ExternalIdentity(SQ_AUTHORITY, login, login));
- }
- UserDto existingUser = dbClient.userDao().selectByLogin(dbSession, login);
- if (existingUser == null) {
- return buildResponse(userUpdater.createAndCommit(dbSession, newUser.build(), u -> {
- }));
- }
- checkArgument(!existingUser.isActive(), "An active user with login '%s' already exists", login);
- return buildResponse(userUpdater.reactivateAndCommit(dbSession, existingUser, newUser.build(), u -> {
- }));
- }
+ private CreateWsResponse doHandle(UserCreateRequest userCreateRequest) {
+ UserSearchResult userSearchResult = userService.createUser(userCreateRequest);
+ return buildResponse(userSearchResult.userDto());
}
private static CreateWsResponse buildResponse(UserDto userDto) {
@@ -163,42 +141,24 @@ public class CreateAction implements UsersWsAction {
return CreateWsResponse.newBuilder().setUser(userBuilder).build();
}
- private static CreateRequest toWsRequest(Request request) {
- return CreateRequest.builder()
+ private static UserCreateRequest toUserCreateRequest(Request request) {
+ return UserCreateRequest.builder()
+ .setEmail(request.param(PARAM_EMAIL))
+ .setLocal(request.mandatoryParamAsBoolean(PARAM_LOCAL))
.setLogin(request.mandatoryParam(PARAM_LOGIN))
.setName(request.mandatoryParam(PARAM_NAME))
.setPassword(request.param(PARAM_PASSWORD))
- .setEmail(request.param(PARAM_EMAIL))
.setScmAccounts(parseScmAccounts(request))
- .setLocal(request.mandatoryParamAsBoolean(PARAM_LOCAL))
.build();
}
public static List<String> parseScmAccounts(Request request) {
if (request.hasParam(PARAM_SCM_ACCOUNT)) {
- List<String> scmAccounts = request.multiParam(PARAM_SCM_ACCOUNT);
- validateScmAccounts(scmAccounts);
- return scmAccounts;
+ return request.multiParam(PARAM_SCM_ACCOUNT);
}
return emptyList();
}
- private static void validateScmAccounts(List<String> scmAccounts) {
- scmAccounts.forEach(CreateAction::validateScmAccountFormat);
- validateNoDuplicates(scmAccounts);
- }
-
- private static void validateScmAccountFormat(String scmAccount) {
- checkArgument(scmAccount.equals(scmAccount.strip()), "SCM account cannot start or end with whitespace: '%s'", scmAccount);
- }
-
- private static void validateNoDuplicates(List<String> scmAccounts) {
- Set<String> duplicateCheck = new HashSet<>();
- for (String account : scmAccounts) {
- checkArgument(duplicateCheck.add(account), "Duplicate SCM account: '%s'", account);
- }
- }
-
static class CreateRequest {
private final String login;
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/UpdateAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/UpdateAction.java
index 08cba756840..4603ecae29e 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/UpdateAction.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/UpdateAction.java
@@ -33,8 +33,9 @@ import org.sonar.api.utils.text.JsonWriter;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.user.UserDto;
-import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.common.management.ManagedInstanceChecker;
+import org.sonar.server.common.user.service.UserService;
+import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.user.UpdateUser;
import org.sonar.server.user.UserSession;
import org.sonar.server.user.UserUpdater;
@@ -160,11 +161,13 @@ public class UpdateAction implements UsersWsAction {
}
private static UpdateRequest toWsRequest(Request request) {
+ List<String> scmAccounts = parseScmAccounts(request);
+ UserService.validateScmAccounts(scmAccounts);
return UpdateRequest.builder()
.setLogin(request.mandatoryParam(PARAM_LOGIN))
.setName(request.param(PARAM_NAME))
.setEmail(request.param(PARAM_EMAIL))
- .setScmAccounts(parseScmAccounts(request))
+ .setScmAccounts(scmAccounts)
.build();
}