diff --git a/infrastructures/kubernetes/pom.xml b/infrastructures/kubernetes/pom.xml
index 77509eccdd..4406dde172 100644
--- a/infrastructures/kubernetes/pom.xml
+++ b/infrastructures/kubernetes/pom.xml
@@ -125,6 +125,10 @@
org.eclipse.che.core
che-core-api-dto
+
+ org.eclipse.che.core
+ che-core-api-factory
+
org.eclipse.che.core
che-core-api-model
diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/KubernetesInfraModule.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/KubernetesInfraModule.java
index e1636584e2..8f6245159f 100644
--- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/KubernetesInfraModule.java
+++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/KubernetesInfraModule.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2021 Red Hat, Inc.
+ * Copyright (c) 2012-2022 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
@@ -49,6 +49,7 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.environment.Kubernete
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironmentFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.RemoveNamespaceOnWorkspaceRemove;
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.CredentialsSecretConfigurator;
+import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.GitconfigUserDataConfigurator;
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.NamespaceConfigurator;
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.PreferencesConfigMapConfigurator;
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.SshKeysConfigurator;
@@ -118,6 +119,7 @@ public class KubernetesInfraModule extends AbstractModule {
namespaceConfigurators.addBinding().to(UserProfileConfigurator.class);
namespaceConfigurators.addBinding().to(UserPreferencesConfigurator.class);
namespaceConfigurators.addBinding().to(SshKeysConfigurator.class);
+ namespaceConfigurators.addBinding().to(GitconfigUserDataConfigurator.class);
bind(KubernetesNamespaceService.class);
diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/configurator/GitconfigUserDataConfigurator.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/configurator/GitconfigUserDataConfigurator.java
new file mode 100644
index 0000000000..053ffac8c1
--- /dev/null
+++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/configurator/GitconfigUserDataConfigurator.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2012-2022 Red Hat, Inc.
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Red Hat, Inc. - initial API and implementation
+ */
+package org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator;
+
+import com.google.common.collect.ImmutableMap;
+import io.fabric8.kubernetes.api.model.ConfigMap;
+import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
+import java.util.Map;
+import java.util.Set;
+import javax.inject.Inject;
+import org.eclipse.che.api.factory.server.scm.GitUserData;
+import org.eclipse.che.api.factory.server.scm.GitUserDataFetcher;
+import org.eclipse.che.api.factory.server.scm.exception.ScmCommunicationException;
+import org.eclipse.che.api.factory.server.scm.exception.ScmUnauthorizedException;
+import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
+import org.eclipse.che.api.workspace.server.spi.NamespaceResolutionContext;
+import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientFactory;
+
+public class GitconfigUserDataConfigurator implements NamespaceConfigurator {
+ private final KubernetesClientFactory clientFactory;
+ private final Set gitUserDataFetchers;
+ private static final String CONFIGMAP_NAME = "workspace-userdata-gitconfig";
+ private static final String CONFIGMAP_DATA_KEY = "gitconfig";
+
+ @Inject
+ public GitconfigUserDataConfigurator(
+ KubernetesClientFactory clientFactory, Set gitUserDataFetchers) {
+ this.clientFactory = clientFactory;
+ this.gitUserDataFetchers = gitUserDataFetchers;
+ }
+
+ @Override
+ public void configure(NamespaceResolutionContext namespaceResolutionContext, String namespaceName)
+ throws InfrastructureException {
+ var client = clientFactory.create();
+ GitUserData gitUserData = null;
+ for (GitUserDataFetcher fetcher : gitUserDataFetchers) {
+ try {
+ gitUserData = fetcher.fetchGitUserData();
+ break;
+ } catch (ScmUnauthorizedException | ScmCommunicationException ignored) {
+ }
+ }
+ Map annotations =
+ ImmutableMap.of(
+ "controller.devfile.io/mount-as",
+ "subpath",
+ "controller.devfile.io/mount-path",
+ "/etc/");
+ Map labels =
+ ImmutableMap.of(
+ "controller.devfile.io/mount-to-devworkspace",
+ "true",
+ "controller.devfile.io/watch-configmap",
+ "true");
+ if (gitUserData != null
+ && client.configMaps().inNamespace(namespaceName).withName(CONFIGMAP_NAME).get() == null
+ && client
+ .configMaps()
+ .inNamespace(namespaceName)
+ .withLabels(labels)
+ .list()
+ .getItems()
+ .stream()
+ .noneMatch(
+ configMap ->
+ configMap
+ .getMetadata()
+ .getAnnotations()
+ .entrySet()
+ .containsAll(annotations.entrySet())
+ && configMap.getData().containsKey(CONFIGMAP_DATA_KEY))) {
+ ConfigMap configMap =
+ new ConfigMapBuilder()
+ .withNewMetadata()
+ .withName(CONFIGMAP_NAME)
+ .withLabels(labels)
+ .withAnnotations(annotations)
+ .endMetadata()
+ .build();
+ configMap.setData(
+ ImmutableMap.of(
+ CONFIGMAP_DATA_KEY,
+ String.format(
+ "[user]\n\tname = %1$s\n\temail = %2$s",
+ gitUserData.getScmUsername(), gitUserData.getScmUserEmail())));
+ client.configMaps().inNamespace(namespaceName).create(configMap);
+ }
+ }
+}
diff --git a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInfraModule.java b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInfraModule.java
index 488abb5519..f112e05376 100644
--- a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInfraModule.java
+++ b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInfraModule.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2021 Red Hat, Inc.
+ * Copyright (c) 2012-2022 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
@@ -54,6 +54,7 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.environment.Kubernete
import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironmentFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesNamespaceFactory;
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.CredentialsSecretConfigurator;
+import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.GitconfigUserDataConfigurator;
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.NamespaceConfigurator;
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.PreferencesConfigMapConfigurator;
import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.SshKeysConfigurator;
@@ -127,6 +128,7 @@ public class OpenShiftInfraModule extends AbstractModule {
namespaceConfigurators.addBinding().to(OpenShiftWorkspaceServiceAccountConfigurator.class);
namespaceConfigurators.addBinding().to(OpenShiftStopWorkspaceRoleConfigurator.class);
namespaceConfigurators.addBinding().to(SshKeysConfigurator.class);
+ namespaceConfigurators.addBinding().to(GitconfigUserDataConfigurator.class);
bind(KubernetesNamespaceService.class);
diff --git a/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerModule.java b/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerModule.java
index 96a7b739f8..4ae3b5656b 100644
--- a/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerModule.java
+++ b/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerModule.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2021 Red Hat, Inc.
+ * Copyright (c) 2012-2022 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
@@ -14,6 +14,7 @@ package org.eclipse.che.api.factory.server.bitbucket;
import com.google.inject.AbstractModule;
import com.google.inject.multibindings.Multibinder;
import org.eclipse.che.api.factory.server.bitbucket.server.BitbucketServerApiClient;
+import org.eclipse.che.api.factory.server.scm.GitUserDataFetcher;
import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenFetcher;
import org.eclipse.che.security.oauth1.BitbucketServerApiProvider;
@@ -24,5 +25,8 @@ public class BitbucketServerModule extends AbstractModule {
Multibinder.newSetBinder(binder(), PersonalAccessTokenFetcher.class);
tokenFetcherMultibinder.addBinding().to(BitbucketServerPersonalAccessTokenFetcher.class);
bind(BitbucketServerApiClient.class).toProvider(BitbucketServerApiProvider.class);
+ Multibinder gitUserDataMultibinder =
+ Multibinder.newSetBinder(binder(), GitUserDataFetcher.class);
+ gitUserDataMultibinder.addBinding().to(BitbucketServerUserDataFetcher.class);
}
}
diff --git a/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerUserDataFetcher.java b/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerUserDataFetcher.java
new file mode 100644
index 0000000000..f6ba682832
--- /dev/null
+++ b/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerUserDataFetcher.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2012-2022 Red Hat, Inc.
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Red Hat, Inc. - initial API and implementation
+ */
+package org.eclipse.che.api.factory.server.bitbucket;
+
+import static java.util.stream.Collectors.toList;
+
+import com.google.common.base.Splitter;
+import java.util.Collections;
+import java.util.List;
+import javax.inject.Inject;
+import javax.inject.Named;
+import org.eclipse.che.api.factory.server.bitbucket.server.BitbucketServerApiClient;
+import org.eclipse.che.api.factory.server.bitbucket.server.BitbucketUser;
+import org.eclipse.che.api.factory.server.scm.GitUserData;
+import org.eclipse.che.api.factory.server.scm.GitUserDataFetcher;
+import org.eclipse.che.api.factory.server.scm.exception.ScmCommunicationException;
+import org.eclipse.che.api.factory.server.scm.exception.ScmItemNotFoundException;
+import org.eclipse.che.api.factory.server.scm.exception.ScmUnauthorizedException;
+import org.eclipse.che.commons.annotation.Nullable;
+import org.eclipse.che.commons.env.EnvironmentContext;
+import org.eclipse.che.commons.lang.StringUtils;
+import org.eclipse.che.commons.subject.Subject;
+
+/** Bitbucket git user data retriever. */
+public class BitbucketServerUserDataFetcher implements GitUserDataFetcher {
+
+ /** Bitbucket API client. */
+ private final BitbucketServerApiClient bitbucketServerApiClient;
+
+ private final List registeredBitbucketEndpoints;
+
+ @Inject
+ public BitbucketServerUserDataFetcher(
+ BitbucketServerApiClient bitbucketServerApiClient,
+ @Nullable @Named("che.integration.bitbucket.server_endpoints") String bitbucketEndpoints) {
+ if (bitbucketEndpoints != null) {
+ this.registeredBitbucketEndpoints =
+ Splitter.on(",")
+ .splitToStream(bitbucketEndpoints)
+ .map(e -> StringUtils.trimEnd(e, '/'))
+ .collect(toList());
+ } else {
+ this.registeredBitbucketEndpoints = Collections.emptyList();
+ }
+ this.bitbucketServerApiClient = bitbucketServerApiClient;
+ }
+
+ @Override
+ public GitUserData fetchGitUserData() throws ScmUnauthorizedException, ScmCommunicationException {
+ GitUserData gitUserData = null;
+ for (String bitbucketServerEndpoint : this.registeredBitbucketEndpoints) {
+ if (bitbucketServerApiClient.isConnected(bitbucketServerEndpoint)) {
+ Subject cheSubject = EnvironmentContext.getCurrent().getSubject();
+ try {
+ BitbucketUser user = bitbucketServerApiClient.getUser(cheSubject);
+ gitUserData = new GitUserData(user.getName(), user.getEmailAddress());
+ } catch (ScmItemNotFoundException e) {
+ throw new ScmCommunicationException(e.getMessage(), e);
+ }
+ break;
+ }
+ }
+ if (gitUserData == null) {
+ throw new ScmCommunicationException("Failed to retrieve git user data from Bitbucket");
+ }
+ return gitUserData;
+ }
+}
diff --git a/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubModule.java b/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubModule.java
index 0525e3104c..a34ce968b0 100644
--- a/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubModule.java
+++ b/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubModule.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2021 Red Hat, Inc.
+ * Copyright (c) 2012-2022 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
@@ -13,6 +13,7 @@ package org.eclipse.che.api.factory.server.github;
import com.google.inject.AbstractModule;
import com.google.inject.multibindings.Multibinder;
+import org.eclipse.che.api.factory.server.scm.GitUserDataFetcher;
import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenFetcher;
public class GithubModule extends AbstractModule {
@@ -22,5 +23,8 @@ public class GithubModule extends AbstractModule {
Multibinder tokenFetcherMultibinder =
Multibinder.newSetBinder(binder(), PersonalAccessTokenFetcher.class);
tokenFetcherMultibinder.addBinding().to(GithubPersonalAccessTokenFetcher.class);
+ Multibinder gitUserDataMultibinder =
+ Multibinder.newSetBinder(binder(), GitUserDataFetcher.class);
+ gitUserDataMultibinder.addBinding().to(GithubUserDataFetcher.class);
}
}
diff --git a/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubUserDataFetcher.java b/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubUserDataFetcher.java
new file mode 100644
index 0000000000..ebfda1b18e
--- /dev/null
+++ b/wsmaster/che-core-api-factory-github/src/main/java/org/eclipse/che/api/factory/server/github/GithubUserDataFetcher.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2012-2022 Red Hat, Inc.
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Red Hat, Inc. - initial API and implementation
+ */
+package org.eclipse.che.api.factory.server.github;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableSet;
+import java.util.Set;
+import javax.inject.Inject;
+import javax.inject.Named;
+import org.eclipse.che.api.auth.shared.dto.OAuthToken;
+import org.eclipse.che.api.core.BadRequestException;
+import org.eclipse.che.api.core.ConflictException;
+import org.eclipse.che.api.core.ForbiddenException;
+import org.eclipse.che.api.core.NotFoundException;
+import org.eclipse.che.api.core.ServerException;
+import org.eclipse.che.api.core.UnauthorizedException;
+import org.eclipse.che.api.factory.server.scm.GitUserData;
+import org.eclipse.che.api.factory.server.scm.GitUserDataFetcher;
+import org.eclipse.che.api.factory.server.scm.exception.ScmBadRequestException;
+import org.eclipse.che.api.factory.server.scm.exception.ScmCommunicationException;
+import org.eclipse.che.api.factory.server.scm.exception.ScmItemNotFoundException;
+import org.eclipse.che.api.factory.server.scm.exception.ScmUnauthorizedException;
+import org.eclipse.che.commons.env.EnvironmentContext;
+import org.eclipse.che.commons.subject.Subject;
+import org.eclipse.che.security.oauth.OAuthAPI;
+
+/** GitHub user data retriever. */
+public class GithubUserDataFetcher implements GitUserDataFetcher {
+ private final String apiEndpoint;
+ private final OAuthAPI oAuthAPI;
+
+ /** GitHub API client. */
+ private final GithubApiClient githubApiClient;
+
+ /** Name of this OAuth provider as found in OAuthAPI. */
+ private static final String OAUTH_PROVIDER_NAME = "github";
+
+ /** Collection of OAuth scopes required to make integration with GitHub work. */
+ public static final Set DEFAULT_TOKEN_SCOPES = ImmutableSet.of("repo");
+
+ @Inject
+ public GithubUserDataFetcher(@Named("che.api") String apiEndpoint, OAuthAPI oAuthAPI) {
+ this.apiEndpoint = apiEndpoint;
+ this.oAuthAPI = oAuthAPI;
+ this.githubApiClient = new GithubApiClient();
+ }
+
+ @Override
+ public GitUserData fetchGitUserData() throws ScmUnauthorizedException, ScmCommunicationException {
+ OAuthToken oAuthToken;
+ try {
+ oAuthToken = oAuthAPI.getToken(OAUTH_PROVIDER_NAME);
+ // Find the user associated to the OAuth token by querying the GitHub API.
+ GithubUser user = githubApiClient.getUser(oAuthToken.getToken());
+ return new GitUserData(user.getName(), user.getEmail());
+ } catch (UnauthorizedException e) {
+ Subject cheSubject = EnvironmentContext.getCurrent().getSubject();
+ throw new ScmUnauthorizedException(
+ cheSubject.getUserName()
+ + " is not authorized in "
+ + OAUTH_PROVIDER_NAME
+ + " OAuth provider.",
+ OAUTH_PROVIDER_NAME,
+ "2.0",
+ getLocalAuthenticateUrl());
+ } catch (NotFoundException
+ | ServerException
+ | ForbiddenException
+ | BadRequestException
+ | ScmItemNotFoundException
+ | ScmBadRequestException
+ | ConflictException e) {
+ throw new ScmCommunicationException(e.getMessage(), e);
+ }
+ }
+
+ private String getLocalAuthenticateUrl() {
+ return apiEndpoint
+ + "/oauth/authenticate?oauth_provider="
+ + OAUTH_PROVIDER_NAME
+ + "&scope="
+ + Joiner.on(',').join(DEFAULT_TOKEN_SCOPES)
+ + "&request_method=POST&signature_method=rsa";
+ }
+}
diff --git a/wsmaster/che-core-api-factory-gitlab/src/main/java/org/eclipse/che/api/factory/server/gitlab/GitlabModule.java b/wsmaster/che-core-api-factory-gitlab/src/main/java/org/eclipse/che/api/factory/server/gitlab/GitlabModule.java
index 8b3a52223d..bba050bf0e 100644
--- a/wsmaster/che-core-api-factory-gitlab/src/main/java/org/eclipse/che/api/factory/server/gitlab/GitlabModule.java
+++ b/wsmaster/che-core-api-factory-gitlab/src/main/java/org/eclipse/che/api/factory/server/gitlab/GitlabModule.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2021 Red Hat, Inc.
+ * Copyright (c) 2012-2022 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
@@ -13,6 +13,7 @@ package org.eclipse.che.api.factory.server.gitlab;
import com.google.inject.AbstractModule;
import com.google.inject.multibindings.Multibinder;
+import org.eclipse.che.api.factory.server.scm.GitUserDataFetcher;
import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenFetcher;
public class GitlabModule extends AbstractModule {
@@ -22,5 +23,8 @@ public class GitlabModule extends AbstractModule {
Multibinder tokenFetcherMultibinder =
Multibinder.newSetBinder(binder(), PersonalAccessTokenFetcher.class);
tokenFetcherMultibinder.addBinding().to(GitlabOAuthTokenFetcher.class);
+ Multibinder gitUserDataMultibinder =
+ Multibinder.newSetBinder(binder(), GitUserDataFetcher.class);
+ gitUserDataMultibinder.addBinding().to(GitlabUserDataFetcher.class);
}
}
diff --git a/wsmaster/che-core-api-factory-gitlab/src/main/java/org/eclipse/che/api/factory/server/gitlab/GitlabUserDataFetcher.java b/wsmaster/che-core-api-factory-gitlab/src/main/java/org/eclipse/che/api/factory/server/gitlab/GitlabUserDataFetcher.java
new file mode 100644
index 0000000000..a666eb479c
--- /dev/null
+++ b/wsmaster/che-core-api-factory-gitlab/src/main/java/org/eclipse/che/api/factory/server/gitlab/GitlabUserDataFetcher.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2012-2022 Red Hat, Inc.
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Red Hat, Inc. - initial API and implementation
+ */
+package org.eclipse.che.api.factory.server.gitlab;
+
+import static java.lang.String.format;
+import static java.util.stream.Collectors.toList;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableSet;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import javax.inject.Inject;
+import javax.inject.Named;
+import org.eclipse.che.api.auth.shared.dto.OAuthToken;
+import org.eclipse.che.api.core.BadRequestException;
+import org.eclipse.che.api.core.ConflictException;
+import org.eclipse.che.api.core.ForbiddenException;
+import org.eclipse.che.api.core.NotFoundException;
+import org.eclipse.che.api.core.ServerException;
+import org.eclipse.che.api.core.UnauthorizedException;
+import org.eclipse.che.api.factory.server.scm.GitUserData;
+import org.eclipse.che.api.factory.server.scm.GitUserDataFetcher;
+import org.eclipse.che.api.factory.server.scm.exception.ScmBadRequestException;
+import org.eclipse.che.api.factory.server.scm.exception.ScmCommunicationException;
+import org.eclipse.che.api.factory.server.scm.exception.ScmItemNotFoundException;
+import org.eclipse.che.api.factory.server.scm.exception.ScmUnauthorizedException;
+import org.eclipse.che.commons.annotation.Nullable;
+import org.eclipse.che.commons.env.EnvironmentContext;
+import org.eclipse.che.commons.lang.StringUtils;
+import org.eclipse.che.commons.subject.Subject;
+import org.eclipse.che.inject.ConfigurationException;
+import org.eclipse.che.security.oauth.OAuthAPI;
+
+/** Gitlab OAuth token retriever. */
+public class GitlabUserDataFetcher implements GitUserDataFetcher {
+ private final String apiEndpoint;
+ private final OAuthAPI oAuthAPI;
+
+ /** Name of this OAuth provider as found in OAuthAPI. */
+ private static final String OAUTH_PROVIDER_NAME = "gitlab";
+
+ private final List registeredGitlabEndpoints;
+
+ public static final Set DEFAULT_TOKEN_SCOPES =
+ ImmutableSet.of("api", "write_repository", "openid");
+
+ @Inject
+ public GitlabUserDataFetcher(
+ @Named("che.api") String apiEndpoint,
+ @Nullable @Named("che.integration.gitlab.server_endpoints") String gitlabEndpoints,
+ @Nullable @Named("che.integration.gitlab.oauth_endpoint") String oauthEndpoint,
+ OAuthAPI oAuthAPI) {
+ this.apiEndpoint = apiEndpoint;
+ if (gitlabEndpoints != null) {
+ this.registeredGitlabEndpoints =
+ Splitter.on(",")
+ .splitToStream(gitlabEndpoints)
+ .map(e -> StringUtils.trimEnd(e, '/'))
+ .collect(toList());
+ } else {
+ this.registeredGitlabEndpoints = Collections.emptyList();
+ }
+ if (oauthEndpoint != null) {
+ if (!registeredGitlabEndpoints.contains(StringUtils.trimEnd(oauthEndpoint, '/'))) {
+ throw new ConfigurationException(
+ "GitLab OAuth integration endpoint must be present in registered GitLab endpoints list.");
+ }
+ this.oAuthAPI = oAuthAPI;
+ } else {
+ this.oAuthAPI = null;
+ }
+ }
+
+ @Override
+ public GitUserData fetchGitUserData() throws ScmUnauthorizedException, ScmCommunicationException {
+ if (oAuthAPI == null) {
+ throw new ScmCommunicationException(
+ format(
+ "OAuth 2 is not configured for SCM provider [%s]. For details, refer "
+ + "the documentation in section of SCM providers configuration.",
+ OAUTH_PROVIDER_NAME));
+ }
+ OAuthToken oAuthToken;
+ try {
+ oAuthToken = oAuthAPI.getToken(OAUTH_PROVIDER_NAME);
+ } catch (UnauthorizedException e) {
+ Subject cheSubject = EnvironmentContext.getCurrent().getSubject();
+ throw new ScmUnauthorizedException(
+ cheSubject.getUserName()
+ + " is not authorized in "
+ + OAUTH_PROVIDER_NAME
+ + " OAuth provider.",
+ OAUTH_PROVIDER_NAME,
+ "2.0",
+ getLocalAuthenticateUrl());
+ } catch (NotFoundException
+ | ServerException
+ | ForbiddenException
+ | BadRequestException
+ | ConflictException e) {
+ throw new ScmCommunicationException(e.getMessage(), e);
+ }
+ GitUserData gitUserData = null;
+ for (String gitlabServerEndpoint : this.registeredGitlabEndpoints) {
+ try {
+ GitlabUser user = new GitlabApiClient(gitlabServerEndpoint).getUser(oAuthToken.getToken());
+ gitUserData = new GitUserData(user.getName(), user.getEmail());
+ break;
+ } catch (ScmItemNotFoundException | ScmBadRequestException e) {
+ throw new ScmCommunicationException(e.getMessage(), e);
+ }
+ }
+ if (gitUserData == null) {
+ throw new ScmCommunicationException("Failed to retrieve git user data from Gitlab");
+ }
+ return gitUserData;
+ }
+
+ private String getLocalAuthenticateUrl() {
+ return apiEndpoint
+ + "/oauth/authenticate?oauth_provider="
+ + OAUTH_PROVIDER_NAME
+ + "&scope="
+ + Joiner.on('+').join(DEFAULT_TOKEN_SCOPES)
+ + "&request_method=POST&signature_method=rsa";
+ }
+}
diff --git a/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/scm/GitUserData.java b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/scm/GitUserData.java
new file mode 100644
index 0000000000..516486096f
--- /dev/null
+++ b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/scm/GitUserData.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2012-2022 Red Hat, Inc.
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Red Hat, Inc. - initial API and implementation
+ */
+package org.eclipse.che.api.factory.server.scm;
+
+import java.util.Objects;
+
+/** Personal SCM user data such as `username` and `email`. Is used to sign git commits. */
+public class GitUserData {
+ private final String scmUsername;
+ private final String scmUserEmail;
+
+ public GitUserData(String scmUsername, String scmUserEmail) {
+ this.scmUsername = scmUsername;
+ this.scmUserEmail = scmUserEmail;
+ }
+
+ public String getScmUsername() {
+ return scmUsername;
+ }
+
+ public String getScmUserEmail() {
+ return scmUserEmail;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ GitUserData that = (GitUserData) o;
+ return Objects.equals(scmUsername, that.scmUsername)
+ && Objects.equals(scmUserEmail, that.scmUserEmail);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(scmUsername, scmUserEmail);
+ }
+
+ @Override
+ public String toString() {
+ return "GitUserData{"
+ + ", scmUsername='"
+ + scmUsername
+ + '\''
+ + ", scmUserEmail='"
+ + scmUserEmail
+ + '\''
+ + '}';
+ }
+}
diff --git a/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/scm/GitUserDataFetcher.java b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/scm/GitUserDataFetcher.java
new file mode 100644
index 0000000000..dccd7951da
--- /dev/null
+++ b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/scm/GitUserDataFetcher.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2012-2022 Red Hat, Inc.
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Red Hat, Inc. - initial API and implementation
+ */
+package org.eclipse.che.api.factory.server.scm;
+
+import org.eclipse.che.api.factory.server.scm.exception.ScmCommunicationException;
+import org.eclipse.che.api.factory.server.scm.exception.ScmUnauthorizedException;
+
+public interface GitUserDataFetcher {
+
+ /**
+ * Retrieve a {@link GitUserData} object from concrete scm provider
+ *
+ * @return - {@link GitUserData} object.
+ * @throws ScmUnauthorizedException - in case if user is not authorized che server to create a new
+ * token. Further user interaction is needed before calling this method next time.
+ * @throws ScmCommunicationException - Some unexpected problem occurred during communication with
+ * scm provider.
+ */
+ GitUserData fetchGitUserData() throws ScmUnauthorizedException, ScmCommunicationException;
+}