diff options
author | Jacek <jacek.poreda@sonarsource.com> | 2020-12-22 16:39:32 +0100 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2021-01-11 20:20:38 +0000 |
commit | a915585e9181af099ee293977126dc117ffdc15a (patch) | |
tree | 03c6a6b7a5b5cb5cba0eb16a97942ba6637738a0 | |
parent | 177ce9bc1e0e79ea6fb76f798ebbef7e787a6078 (diff) | |
download | sonarqube-a915585e9181af099ee293977126dc117ffdc15a.tar.gz sonarqube-a915585e9181af099ee293977126dc117ffdc15a.zip |
SONAR-13930 Allow migration of auth system
9 files changed, 482 insertions, 4 deletions
diff --git a/server/sonar-docs/src/pages/instance-administration/delegated-auth.md b/server/sonar-docs/src/pages/instance-administration/delegated-auth.md index c0bc8d4e6b7..f0c3ac36248 100644 --- a/server/sonar-docs/src/pages/instance-administration/delegated-auth.md +++ b/server/sonar-docs/src/pages/instance-administration/delegated-auth.md @@ -230,6 +230,9 @@ Authentication will be tried on each server, in the order they are listed in the Note that all the LDAP servers must be available while (re)starting the SonarQube server. +### Migrate users to a new authentication method +If you are changing your delegated authentication method and migrating existing users from your previous authentication method, you can use the `api/users/update_identity_provider` web API to update your users' identity provider. + ### Troubleshooting * Detailed connection logs (and potential error codes received from LDAP server) are output to SonarQube's _$SONARQUBE_HOME/logs/web.log_, when logging is in `DEBUG` mode. diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/UpdateIdentityProviderAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/UpdateIdentityProviderAction.java new file mode 100644 index 00000000000..e68dbf8fea3 --- /dev/null +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/UpdateIdentityProviderAction.java @@ -0,0 +1,193 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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.user.ws; + +import java.util.List; +import java.util.stream.Collectors; +import javax.annotation.Nullable; +import org.sonar.api.server.authentication.IdentityProvider; +import org.sonar.api.server.ws.Request; +import org.sonar.api.server.ws.Response; +import org.sonar.api.server.ws.WebService; +import org.sonar.api.server.ws.WebService.NewController; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.user.UserDto; +import org.sonar.server.authentication.IdentityProviderRepository; +import org.sonar.server.exceptions.NotFoundException; +import org.sonar.server.user.ExternalIdentity; +import org.sonar.server.user.UpdateUser; +import org.sonar.server.user.UserSession; +import org.sonar.server.user.UserUpdater; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Strings.isNullOrEmpty; +import static java.lang.String.format; +import static org.sonar.server.user.ExternalIdentity.SQ_AUTHORITY; +import static org.sonarqube.ws.client.user.UsersWsParameters.ACTION_UPDATE_IDENTITY_PROVIDER; +import static org.sonarqube.ws.client.user.UsersWsParameters.PARAM_LOGIN; +import static org.sonarqube.ws.client.user.UsersWsParameters.PARAM_NEW_EXTERNAL_IDENTITY; +import static org.sonarqube.ws.client.user.UsersWsParameters.PARAM_NEW_EXTERNAL_PROVIDER; + +public class UpdateIdentityProviderAction implements UsersWsAction { + + private final DbClient dbClient; + private final IdentityProviderRepository identityProviderRepository; + private final UserUpdater userUpdater; + private final UserSession userSession; + + public UpdateIdentityProviderAction(DbClient dbClient, IdentityProviderRepository identityProviderRepository, + UserUpdater userUpdater, UserSession userSession) { + this.dbClient = dbClient; + this.identityProviderRepository = identityProviderRepository; + this.userUpdater = userUpdater; + this.userSession = userSession; + } + + @Override + public void define(NewController controller) { + WebService.NewAction action = controller.createAction(ACTION_UPDATE_IDENTITY_PROVIDER) + .setDescription("Update identity provider information. <br/>" + + "It's only possible to migrate to an installed identity provider. " + + "Be careful that as soon as this information has been updated for a user, " + + "the user will only be able to authenticate on the new identity provider. " + + "It is not possible to migrate external user to local one.<br/>" + + "Requires Administer System permission.") + .setSince("8.7") + .setInternal(false) + .setPost(true) + .setHandler(this); + + action.createParam(PARAM_LOGIN) + .setDescription("User login") + .setRequired(true); + action.createParam(PARAM_NEW_EXTERNAL_PROVIDER) + .setRequired(true) + .setDescription("New external provider. Only authentication system installed are available. Use 'sonarqube' identity provider for LDAP."); + + action.createParam(PARAM_NEW_EXTERNAL_IDENTITY) + .setDescription("New external identity, usually the login used in the authentication system. " + + "If not provided previous identity will be used."); + } + + @Override + public void handle(Request request, Response response) throws Exception { + userSession.checkLoggedIn().checkIsSystemAdministrator(); + UpdateIdentityProviderRequest wsRequest = toWsRequest(request); + doHandle(wsRequest); + response.noContent(); + } + + private void doHandle(UpdateIdentityProviderRequest request) { + checkEnabledIdentityProviders(request.newExternalProvider); + try (DbSession dbSession = dbClient.openSession(false)) { + UserDto user = getUser(dbSession, request.login); + ExternalIdentity externalIdentity = getExternalIdentity(request, user); + userUpdater.updateAndCommit(dbSession, user, new UpdateUser().setExternalIdentity(externalIdentity), u -> { + }); + } + } + + private void checkEnabledIdentityProviders(String newExternalProvider) { + List<String> availableIdentityProviders = getAvailableIdentityProviders(); + checkArgument(availableIdentityProviders.contains(newExternalProvider), "Value of parameter 'newExternalProvider' (%s) must be one of: [%s]", newExternalProvider, + String.join(", ", availableIdentityProviders)); + } + + private List<String> getAvailableIdentityProviders() { + List<String> discoveredProviders = identityProviderRepository.getAllEnabledAndSorted() + .stream() + .map(IdentityProvider::getKey) + .collect(Collectors.toList()); + discoveredProviders.add(SQ_AUTHORITY); + return discoveredProviders; + } + + private UserDto getUser(DbSession dbSession, String login) { + UserDto user = dbClient.userDao().selectByLogin(dbSession, login); + if (user == null || !user.isActive()) { + throw new NotFoundException(format("User '%s' doesn't exist", login)); + } + return user; + } + + private static ExternalIdentity getExternalIdentity(UpdateIdentityProviderRequest request, UserDto user) { + return new ExternalIdentity( + request.newExternalProvider, + request.newExternalIdentity != null ? request.newExternalIdentity : user.getExternalLogin(), + null); + } + + private static UpdateIdentityProviderRequest toWsRequest(Request request) { + return UpdateIdentityProviderRequest.builder() + .setLogin(request.mandatoryParam(PARAM_LOGIN)) + .setNewExternalProvider(request.mandatoryParam(PARAM_NEW_EXTERNAL_PROVIDER)) + .setNewExternalIdentity(request.param(PARAM_NEW_EXTERNAL_IDENTITY)) + .build(); + } + + static class UpdateIdentityProviderRequest { + private final String login; + private final String newExternalProvider; + private final String newExternalIdentity; + + public UpdateIdentityProviderRequest(Builder builder) { + this.login = builder.login; + this.newExternalProvider = builder.newExternalProvider; + this.newExternalIdentity = builder.newExternalIdentity; + } + + public static UpdateIdentityProviderRequest.Builder builder() { + return new UpdateIdentityProviderRequest.Builder(); + } + + static class Builder { + private String login; + private String newExternalProvider; + private String newExternalIdentity; + + private Builder() { + // enforce factory method use + } + + public Builder setLogin(String login) { + this.login = login; + return this; + } + + public Builder setNewExternalProvider(String newExternalProvider) { + this.newExternalProvider = newExternalProvider; + return this; + } + + public Builder setNewExternalIdentity(@Nullable String newExternalIdentity) { + this.newExternalIdentity = newExternalIdentity; + return this; + } + + public UpdateIdentityProviderRequest build() { + checkArgument(!isNullOrEmpty(login), "Login is mandatory and must not be empty"); + checkArgument(!isNullOrEmpty(newExternalProvider), "New External Provider is mandatory and must not be empty"); + return new UpdateIdentityProviderRequest(this); + } + } + } + +} diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/UsersWsModule.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/UsersWsModule.java index 23056c8079d..c7ecb05366d 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/UsersWsModule.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/UsersWsModule.java @@ -46,7 +46,8 @@ public class UsersWsModule extends Module { UserJsonWriter.class, SetHomepageAction.class, HomepageTypesImpl.class, - SetSettingAction.class); + SetSettingAction.class, + UpdateIdentityProviderAction.class); if (configuration.getBoolean(ProcessProperties.Property.SONARCLOUD_ENABLED.getKey()).orElse(false)) { // onboarding tutorial is available only in SonarCloud diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/user/ws/UpdateIdentityProviderActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/user/ws/UpdateIdentityProviderActionTest.java new file mode 100644 index 00000000000..59831550531 --- /dev/null +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/user/ws/UpdateIdentityProviderActionTest.java @@ -0,0 +1,189 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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.user.ws; + +import org.junit.Rule; +import org.junit.Test; +import org.sonar.api.config.internal.MapSettings; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.DbTester; +import org.sonar.db.user.UserDto; +import org.sonar.server.authentication.CredentialsLocalAuthentication; +import org.sonar.server.authentication.IdentityProviderRepositoryRule; +import org.sonar.server.authentication.TestIdentityProvider; +import org.sonar.server.es.EsTester; +import org.sonar.server.exceptions.ForbiddenException; +import org.sonar.server.exceptions.NotFoundException; +import org.sonar.server.exceptions.UnauthorizedException; +import org.sonar.server.tester.UserSessionRule; +import org.sonar.server.user.NewUserNotifier; +import org.sonar.server.user.UserUpdater; +import org.sonar.server.user.index.UserIndexer; +import org.sonar.server.usergroups.DefaultGroupFinder; +import org.sonar.server.ws.TestRequest; +import org.sonar.server.ws.WsActionTester; + +import static com.google.common.collect.Lists.newArrayList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.mock; +import static org.sonar.db.user.UserTesting.newUserDto; + +public class UpdateIdentityProviderActionTest { + private final static String SQ_AUTHORITY = "sonarqube"; + + @Rule + public IdentityProviderRepositoryRule identityProviderRepository = new IdentityProviderRepositoryRule() + .addIdentityProvider(new TestIdentityProvider().setName("Gitlab").setKey("gitlab").setEnabled(true)) + .addIdentityProvider(new TestIdentityProvider().setName("Github").setKey("github").setEnabled(true)); + + @Rule + public DbTester db = DbTester.create(); + @Rule + public EsTester es = EsTester.create(); + @Rule + public UserSessionRule userSession = UserSessionRule.standalone().logIn().setSystemAdministrator(); + + private final MapSettings settings = new MapSettings(); + private final DbClient dbClient = db.getDbClient(); + private final DbSession dbSession = db.getSession(); + private final UserIndexer userIndexer = new UserIndexer(dbClient, es.client()); + private final CredentialsLocalAuthentication localAuthentication = new CredentialsLocalAuthentication(dbClient); + + private final WsActionTester underTest = new WsActionTester(new UpdateIdentityProviderAction(dbClient, identityProviderRepository, + new UserUpdater(mock(NewUserNotifier.class), dbClient, userIndexer, new DefaultGroupFinder(db.getDbClient()), settings.asConfig(), localAuthentication), + userSession)); + + @Test + public void change_identity_provider_of_a_local_user_all_params() { + String userLogin = "login-1"; + String newExternalLogin = "login@github.com"; + String newExternalIdentityProvider = "github"; + createUser(true, userLogin, userLogin, SQ_AUTHORITY); + TestRequest request = underTest.newRequest() + .setParam("login", userLogin) + .setParam("newExternalProvider", newExternalIdentityProvider) + .setParam("newExternalIdentity", newExternalLogin); + + request.execute(); + assertThat(dbClient.userDao().selectByExternalLoginAndIdentityProvider(dbSession, newExternalLogin, newExternalIdentityProvider)) + .isNotNull() + .extracting(UserDto::isLocal, UserDto::getExternalLogin, UserDto::getExternalIdentityProvider) + .contains(false, newExternalLogin, newExternalIdentityProvider); + } + + @Test + public void change_identity_provider_of_a_local_user_mandatory_params_only_provider_login_stays_same() { + String userLogin = "login-1"; + String newExternalIdentityProvider = "github"; + createUser(true, userLogin, userLogin, SQ_AUTHORITY); + TestRequest request = underTest.newRequest() + .setParam("login", userLogin) + .setParam("newExternalProvider", newExternalIdentityProvider); + + request.execute(); + assertThat(dbClient.userDao().selectByExternalLoginAndIdentityProvider(dbSession, userLogin, newExternalIdentityProvider)) + .isNotNull() + .extracting(UserDto::isLocal, UserDto::getExternalLogin, UserDto::getExternalIdentityProvider) + .contains(false, userLogin, newExternalIdentityProvider); + } + + @Test + public void change_identity_provider_of_a_external_user_to_new_one() { + String userLogin = "login-1"; + String oldExternalIdentityProvider = "gitlab"; + String oldExternalIdentity = "john@gitlab.com"; + createUser(false, userLogin, oldExternalIdentity, oldExternalIdentityProvider); + + String newExternalIdentityProvider = "github"; + String newExternalIdentity = "john@github.com"; + TestRequest request = underTest.newRequest() + .setParam("login", userLogin) + .setParam("newExternalProvider", newExternalIdentityProvider) + .setParam("newExternalIdentity", newExternalIdentity); + + request.execute(); + assertThat(dbClient.userDao().selectByExternalLoginAndIdentityProvider(dbSession, newExternalIdentity, newExternalIdentityProvider)) + .isNotNull() + .extracting(UserDto::isLocal, UserDto::getExternalLogin, UserDto::getExternalIdentityProvider) + .contains(false, newExternalIdentity, newExternalIdentityProvider); + } + + @Test + public void fail_if_user_not_exist() { + TestRequest request = underTest.newRequest() + .setParam("login", "not-existing") + .setParam("newExternalProvider", "gitlab"); + + assertThatThrownBy(request::execute) + .isInstanceOf(NotFoundException.class) + .hasMessage("User 'not-existing' doesn't exist"); + } + + @Test + public void fail_if_identity_provider_not_exist() { + createUser(true, "login-1", "login-1", SQ_AUTHORITY); + TestRequest request = underTest.newRequest() + .setParam("login", "login-1") + .setParam("newExternalProvider", "not-existing"); + + assertThatThrownBy(request::execute) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Value of parameter 'newExternalProvider' (not-existing) must be one of: [github, gitlab, sonarqube]"); + } + + @Test + public void fail_if_anonymous() { + userSession.anonymous(); + TestRequest request = underTest.newRequest() + .setParam("login", "not-existing") + .setParam("newExternalProvider", "something"); + + assertThatThrownBy(request::execute) + .isInstanceOf(UnauthorizedException.class); + } + + @Test + public void fail_if_not_admin() { + userSession.logIn("some-user"); + TestRequest request = underTest.newRequest() + .setParam("login", "not-existing") + .setParam("newExternalProvider", "something"); + + assertThatThrownBy(request::execute) + .isInstanceOf(ForbiddenException.class); + } + + private void createUser(boolean local, String login, String externalLogin, String externalIdentityProvider) { + UserDto userDto = newUserDto() + .setEmail("john@email.com") + .setLogin(login) + .setName("John") + .setScmAccounts(newArrayList("jn")) + .setActive(true) + .setLocal(local) + .setExternalLogin(externalLogin) + .setExternalIdentityProvider(externalIdentityProvider); + dbClient.userDao().insert(dbSession, userDto); + userIndexer.commitAndIndex(dbSession, userDto); + } + +} diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/user/ws/UsersWsModuleTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/user/ws/UsersWsModuleTest.java index db34976dcd2..9e961b12313 100644 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/user/ws/UsersWsModuleTest.java +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/user/ws/UsersWsModuleTest.java @@ -34,7 +34,7 @@ public class UsersWsModuleTest { public void verify_count_of_added_components() { ComponentContainer container = new ComponentContainer(); new UsersWsModule(new ConfigurationBridge(settings)).configure(container); - assertThat(container.size()).isEqualTo(2 + 14); + assertThat(container.size()).isEqualTo(2 + 15); } @Test @@ -43,6 +43,6 @@ public class UsersWsModuleTest { ComponentContainer container = new ComponentContainer(); new UsersWsModule(new ConfigurationBridge(settings)).configure(container); - assertThat(container.size()).isEqualTo(2 + 15); + assertThat(container.size()).isEqualTo(2 + 16); } } diff --git a/sonar-ws-generator/src/main/java/org/sonarqube/wsgenerator/ApiDefinitionDownloader.java b/sonar-ws-generator/src/main/java/org/sonarqube/wsgenerator/ApiDefinitionDownloader.java index 4c25869f801..b998af934dc 100644 --- a/sonar-ws-generator/src/main/java/org/sonarqube/wsgenerator/ApiDefinitionDownloader.java +++ b/sonar-ws-generator/src/main/java/org/sonarqube/wsgenerator/ApiDefinitionDownloader.java @@ -43,6 +43,7 @@ public class ApiDefinitionDownloader { Orchestrator orchestrator = builder // Enable organizations ws .setServerProperty("sonar.sonarcloud.enabled", "true") + .setServerProperty("sonar.forceAuthentication", "false") .build(); orchestrator.start(); diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/user/UsersWsParameters.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/user/UsersWsParameters.java index 434814810ca..fd15902dec0 100644 --- a/sonar-ws/src/main/java/org/sonarqube/ws/client/user/UsersWsParameters.java +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/user/UsersWsParameters.java @@ -27,9 +27,9 @@ public class UsersWsParameters { public static final String ACTION_CREATE = "create"; public static final String ACTION_DEACTIVATE = "deactivate"; public static final String ACTION_UPDATE = "update"; - public static final String ACTION_GROUPS = "groups"; public static final String ACTION_SKIP_ONBOARDING_TUTORIAL = "skip_onboarding_tutorial"; public static final String ACTION_CURRENT = "current"; + public static final String ACTION_UPDATE_IDENTITY_PROVIDER = "update_identity_provider"; public static final String PARAM_LOGIN = "login"; public static final String PARAM_PASSWORD = "password"; @@ -40,6 +40,8 @@ public class UsersWsParameters { public static final String PARAM_SCM_ACCOUNT = "scmAccount"; public static final String PARAM_LOCAL = "local"; public static final String PARAM_SELECTED = "selected"; + public static final String PARAM_NEW_EXTERNAL_PROVIDER = "newExternalProvider"; + public static final String PARAM_NEW_EXTERNAL_IDENTITY = "newExternalIdentity"; private UsersWsParameters() { // Only static stuff diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/users/UpdateIdentityProviderRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/users/UpdateIdentityProviderRequest.java new file mode 100644 index 00000000000..31c40d1bcb4 --- /dev/null +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/users/UpdateIdentityProviderRequest.java @@ -0,0 +1,72 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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.sonarqube.ws.client.users; + +import java.util.List; +import javax.annotation.Generated; + +/** + * This is part of the internal API. + * This is a POST request. + * @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/users/update_identity_provider">Further information about this action online (including a response example)</a> + * @since 8.7 + */ +@Generated("sonar-ws-generator") +public class UpdateIdentityProviderRequest { + + private String login; + private String newExternalIdentity; + private String newExternalProvider; + + /** + * This is a mandatory parameter. + */ + public UpdateIdentityProviderRequest setLogin(String login) { + this.login = login; + return this; + } + + public String getLogin() { + return login; + } + + /** + */ + public UpdateIdentityProviderRequest setNewExternalIdentity(String newExternalIdentity) { + this.newExternalIdentity = newExternalIdentity; + return this; + } + + public String getNewExternalIdentity() { + return newExternalIdentity; + } + + /** + * This is a mandatory parameter. + */ + public UpdateIdentityProviderRequest setNewExternalProvider(String newExternalProvider) { + this.newExternalProvider = newExternalProvider; + return this; + } + + public String getNewExternalProvider() { + return newExternalProvider; + } +} diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/users/UsersService.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/users/UsersService.java index 694d3037b5a..edcad0df8be 100644 --- a/sonar-ws/src/main/java/org/sonarqube/ws/client/users/UsersService.java +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/users/UsersService.java @@ -227,4 +227,21 @@ public class UsersService extends BaseService { .setParam("newLogin", request.getNewLogin()) .setMediaType(MediaTypes.JSON)).content(); } + + /** + * + * This is part of the internal API. + * This is a POST request. + * @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/users/update_identity_provider">Further information about this action online (including a response example)</a> + * @since 8.7 + */ + public void updateIdentityProvider(UpdateIdentityProviderRequest request) { + call( + new PostRequest(path("update_identity_provider")) + .setParam("login", request.getLogin()) + .setParam("newExternalIdentity", request.getNewExternalIdentity()) + .setParam("newExternalProvider", request.getNewExternalProvider()) + .setMediaType(MediaTypes.JSON) + ).content(); + } } |