che-22776_1
ivinokur 2024-01-30 11:49:24 +02:00
parent 04bd0521dc
commit f2296b651f
20 changed files with 115 additions and 105 deletions

View File

@ -51,10 +51,6 @@
<groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-auth</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-core</artifactId>

View File

@ -14,7 +14,6 @@ package org.eclipse.che.api.factory.server.scm.kubernetes;
import static com.google.common.base.Strings.isNullOrEmpty;
import static java.lang.String.format;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.eclipse.che.api.factory.server.scm.PersonalAccessTokenFetcher.OAUTH_2_SUFFIX;
import static org.eclipse.che.workspace.infrastructure.kubernetes.provision.secret.KubernetesSecretAnnotationNames.ANNOTATION_AUTOMOUNT;
import static org.eclipse.che.workspace.infrastructure.kubernetes.provision.secret.KubernetesSecretAnnotationNames.ANNOTATION_DEV_WORKSPACE_MOUNT_PATH;
import static org.eclipse.che.workspace.infrastructure.kubernetes.provision.secret.KubernetesSecretAnnotationNames.ANNOTATION_GIT_CREDENTIALS;
@ -176,7 +175,7 @@ public class KubernetesGitCredentialManager implements GitCredentialManager {
private String getUsernameSegment(PersonalAccessToken personalAccessToken) {
// Special characters are not allowed in URL username segment, so we need to escape them.
PercentEscaper percentEscaper = new PercentEscaper("", false);
return personalAccessToken.getScmProviderName().startsWith(OAUTH_2_SUFFIX)
return personalAccessToken.isOAuthToken()
? "oauth2"
: isNullOrEmpty(personalAccessToken.getScmOrganization())
? percentEscaper.escape(personalAccessToken.getScmUserName())

View File

@ -12,6 +12,7 @@
package org.eclipse.che.api.factory.server.scm.kubernetes;
import static com.google.common.base.Strings.isNullOrEmpty;
import static java.lang.Boolean.parseBoolean;
import static org.eclipse.che.commons.lang.StringUtils.trimEnd;
import com.google.common.annotations.VisibleForTesting;
@ -62,6 +63,7 @@ public class KubernetesPersonalAccessTokenManager implements PersonalAccessToken
public static final String NAME_PATTERN = "personal-access-token-";
public static final String ANNOTATION_CHE_USERID = "che.eclipse.org/che-userid";
public static final String ANNOTATION_SCM_IS_OAUTH_TOKEN = "che.eclipse.org/scm-is-oauth-token";
public static final String ANNOTATION_SCM_ORGANIZATION = "che.eclipse.org/scm-organization";
public static final String ANNOTATION_SCM_PERSONAL_ACCESS_TOKEN_ID =
"che.eclipse.org/scm-personal-access-token-id";
@ -96,6 +98,9 @@ public class KubernetesPersonalAccessTokenManager implements PersonalAccessToken
.withName(NameGenerator.generate(NAME_PATTERN, 5))
.withAnnotations(
new ImmutableMap.Builder<String, String>()
.put(
ANNOTATION_SCM_IS_OAUTH_TOKEN,
Boolean.toString(personalAccessToken.isOAuthToken()))
.put(ANNOTATION_CHE_USERID, personalAccessToken.getCheUserId())
.put(ANNOTATION_SCM_URL, personalAccessToken.getScmProviderUrl())
.put(
@ -194,6 +199,7 @@ public class KubernetesPersonalAccessTokenManager implements PersonalAccessToken
PersonalAccessToken personalAccessToken =
new PersonalAccessToken(
parseBoolean(secretAnnotations.get(ANNOTATION_SCM_IS_OAUTH_TOKEN)),
personalAccessTokenParams.getScmProviderUrl(),
secretAnnotations.get(ANNOTATION_CHE_USERID),
personalAccessTokenParams.getOrganization(),
@ -278,6 +284,7 @@ public class KubernetesPersonalAccessTokenManager implements PersonalAccessToken
private PersonalAccessTokenParams secret2PersonalAccessTokenParams(Secret secret) {
Map<String, String> secretAnnotations = secret.getMetadata().getAnnotations();
boolean isOauth = parseBoolean(secretAnnotations.get(ANNOTATION_SCM_IS_OAUTH_TOKEN));
String token = new String(Base64.getDecoder().decode(secret.getData().get("token"))).trim();
String configuredOAuthProviderName = secretAnnotations.get(ANNOTATION_SCM_PROVIDER_NAME);
String configuredTokenId = secretAnnotations.get(ANNOTATION_SCM_PERSONAL_ACCESS_TOKEN_ID);
@ -285,6 +292,7 @@ public class KubernetesPersonalAccessTokenManager implements PersonalAccessToken
String configuredScmServerUrl = secretAnnotations.get(ANNOTATION_SCM_URL);
return new PersonalAccessTokenParams(
isOauth,
trimEnd(configuredScmServerUrl, '/'),
configuredOAuthProviderName,
configuredTokenId,

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012-2023 Red Hat, Inc.
* Copyright (c) 2012-2024 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/
@ -92,8 +92,14 @@ public class KubernetesGitCredentialManagerTest {
PersonalAccessToken token =
new PersonalAccessToken(
"https://bitbucket.com", "cheUser", "username", "token-name", "tid-23434", "token123");
false,
"https://bitbucket.com",
"cheUser",
"cheOrganization",
"username",
"bitbucket",
"tid-23434",
"token123");
// when
kubernetesGitCredentialManager.createOrReplace(token);
// then
@ -113,6 +119,7 @@ public class KubernetesGitCredentialManagerTest {
KubernetesNamespaceMeta namespaceMeta = new KubernetesNamespaceMetaImpl("test");
PersonalAccessToken token =
new PersonalAccessToken(
false,
"https://bitbucket-server.com:5648",
"cheUser",
"cheOrganization",
@ -198,10 +205,12 @@ public class KubernetesGitCredentialManagerTest {
KubernetesNamespaceMeta namespaceMeta = new KubernetesNamespaceMetaImpl("test");
PersonalAccessToken token =
new PersonalAccessToken(
false,
"https://bitbucket.com:5648",
"cheUser",
"cheOrganization",
"username",
"token-name",
"bitbucket",
"tid-23434",
"token123");

View File

@ -11,11 +11,12 @@
*/
package org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator;
import static java.lang.Boolean.parseBoolean;
import com.google.common.collect.ImmutableMap;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenFetcher;
import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenManager;
import org.eclipse.che.api.factory.server.scm.exception.ScmCommunicationException;
import org.eclipse.che.api.factory.server.scm.exception.ScmConfigurationPersistenceException;
@ -40,6 +41,7 @@ public class OAuthTokenSecretsConfigurator implements NamespaceConfigurator {
private static final String ANNOTATION_SCM_URL = "che.eclipse.org/scm-url";
private static final String ANNOTATION_SCM_PERSONAL_ACCESS_TOKEN_NAME =
"che.eclipse.org/scm-personal-access-token-name";
public static final String ANNOTATION_SCM_IS_OAUTH_TOKEN = "che.eclipse.org/scm-is-oauth-token";
private static final Map<String, String> SEARCH_LABELS =
ImmutableMap.of(
@ -66,10 +68,8 @@ public class OAuthTokenSecretsConfigurator implements NamespaceConfigurator {
&& s.getMetadata()
.getAnnotations()
.containsKey(ANNOTATION_SCM_PERSONAL_ACCESS_TOKEN_NAME)
&& s.getMetadata()
.getAnnotations()
.get(ANNOTATION_SCM_PERSONAL_ACCESS_TOKEN_NAME)
.startsWith(PersonalAccessTokenFetcher.OAUTH_2_SUFFIX))
&& parseBoolean(
s.getMetadata().getAnnotations().get(ANNOTATION_SCM_IS_OAUTH_TOKEN)))
.forEach(
s -> {
try {

View File

@ -184,35 +184,31 @@ public class EmbeddedOAuthAPI implements OAuthAPI {
if (token != null) {
return token;
}
Optional<PersonalAccessToken> tokenOptional =
personalAccessTokenManager.get(subject, oauthProvider, null);
if (tokenOptional.isPresent()) {
PersonalAccessToken tokenDto = tokenOptional.get();
return newDto(OAuthToken.class).withToken(tokenDto.getToken());
}
throw new UnauthorizedException(
"OAuth token for user " + subject.getUserId() + " was not found");
} catch (IOException e) {
throw new ServerException(e.getLocalizedMessage(), e);
} catch (ScmCommunicationException
| ScmUnauthorizedException
| ScmConfigurationPersistenceException e) {
throw new RuntimeException(e);
}
}
@Override
public void invalidateToken(String oauthProvider)
throws NotFoundException, UnauthorizedException, ServerException {
throws NotFoundException, UnauthorizedException {
OAuthAuthenticator oauth = getAuthenticator(oauthProvider);
OAuthToken oauthToken = getToken(oauthProvider);
try {
if (!oauth.invalidateToken(oauthToken.getToken())) {
Optional<PersonalAccessToken> tokenOptional =
personalAccessTokenManager.get(
EnvironmentContext.getCurrent().getSubject(), oauthProvider, null);
if (tokenOptional.isPresent() && !oauth.invalidateToken(tokenOptional.get().getToken())) {
throw new UnauthorizedException(
"OAuth token for provider " + oauthProvider + " was not found");
}
} catch (IOException e) {
throw new ServerException(e.getMessage());
} catch (ScmConfigurationPersistenceException
| ScmUnauthorizedException
| ScmCommunicationException
| IOException e) {
throw new UnauthorizedException(
"OAuth token for provider " + oauthProvider + " was not found");
}
}

View File

@ -82,12 +82,11 @@ public class AzureDevOpsPersonalAccessTokenFetcher implements PersonalAccessToke
try {
oAuthToken = oAuthAPI.getToken(AzureDevOps.PROVIDER_NAME);
String tokenName = NameGenerator.generate(OAUTH_2_SUFFIX, 5);
String tokenId = NameGenerator.generate("id-", 5);
Optional<Pair<Boolean, String>> valid =
isValid(
new PersonalAccessTokenParams(
scmServerUrl, tokenName, tokenId, oAuthToken.getToken(), null));
true, scmServerUrl, "azure-devops", tokenId, oAuthToken.getToken(), null));
if (valid.isEmpty()) {
throw buildScmUnauthorizedException(cheSubject);
} else if (!valid.get().first) {
@ -99,7 +98,7 @@ public class AzureDevOpsPersonalAccessTokenFetcher implements PersonalAccessToke
scmServerUrl,
cheSubject.getUserId(),
valid.get().second,
tokenName,
"azure-devops",
tokenId,
oAuthToken.getToken());
} catch (UnauthorizedException e) {
@ -132,8 +131,7 @@ public class AzureDevOpsPersonalAccessTokenFetcher implements PersonalAccessToke
try {
AzureDevOpsUser user;
if (personalAccessToken.getScmProviderName() != null
&& personalAccessToken.getScmProviderName().startsWith(OAUTH_2_SUFFIX)) {
if (personalAccessToken.getScmProviderName() != null && personalAccessToken.isOAuthToken()) {
user = azureDevOpsApiClient.getUserWithOAuthToken(personalAccessToken.getToken());
} else {
user =
@ -155,8 +153,7 @@ public class AzureDevOpsPersonalAccessTokenFetcher implements PersonalAccessToke
try {
AzureDevOpsUser user;
if (params.getScmProviderName() != null
&& params.getScmProviderName().startsWith(OAUTH_2_SUFFIX)) {
if (params.isOAuthToken()) {
user = azureDevOpsApiClient.getUserWithOAuthToken(params.getToken());
} else {
user = azureDevOpsApiClient.getUserWithPAT(params.getToken(), params.getOrganization());

View File

@ -95,11 +95,12 @@ public class BitbucketServerPersonalAccessTokenFetcher implements PersonalAccess
bitbucketServerApiClient.createPersonalAccessTokens(tokenName, DEFAULT_TOKEN_SCOPE);
LOG.debug("Token created = {} for {}", token.getId(), token.getUser());
return new PersonalAccessToken(
true,
scmServerUrl,
EnvironmentContext.getCurrent().getSubject().getUserId(),
user.getName(),
user.getSlug(),
token.getName(),
"bitbucket-server",
valueOf(token.getId()),
token.getToken());
} catch (ScmBadRequestException | ScmItemNotFoundException e) {

View File

@ -97,7 +97,7 @@ public class BitbucketPersonalAccessTokenFetcher implements PersonalAccessTokenF
Optional<Pair<Boolean, String>> valid =
isValid(
new PersonalAccessTokenParams(
scmServerUrl, tokenName, tokenId, oAuthToken.getToken(), null));
true, scmServerUrl, tokenName, tokenId, oAuthToken.getToken(), null));
if (valid.isEmpty()) {
throw buildScmUnauthorizedException(cheSubject);
} else if (!valid.get().first) {

View File

@ -18,7 +18,6 @@ import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
import static java.net.HttpURLConnection.HTTP_FORBIDDEN;
import static org.eclipse.che.api.factory.server.scm.PersonalAccessTokenFetcher.OAUTH_2_SUFFIX;
import static org.eclipse.che.dto.server.DtoFactory.newDto;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;
@ -87,7 +86,12 @@ public class BitbucketPersonalAccessTokenFetcherTest {
.withBodyFile("bitbucket/rest/user/response.json")));
PersonalAccessTokenParams personalAccessTokenParams =
new PersonalAccessTokenParams(
"https://bitbucket.org/", "scmTokenName", "scmTokenId", bitbucketOauthToken, null);
true,
"https://bitbucket.org/",
"scmTokenName",
"scmTokenId",
bitbucketOauthToken,
null);
assertTrue(
bitbucketPersonalAccessTokenFetcher.isValid(personalAccessTokenParams).isEmpty(),
"Should not validate SCM server with trailing /");
@ -165,7 +169,7 @@ public class BitbucketPersonalAccessTokenFetcherTest {
PersonalAccessTokenParams params =
new PersonalAccessTokenParams(
"https://bitbucket.org", "params-name", "tid-23434", bitbucketOauthToken, null);
false, "https://bitbucket.org", "params-name", "tid-23434", bitbucketOauthToken, null);
Optional<Pair<Boolean, String>> valid = bitbucketPersonalAccessTokenFetcher.isValid(params);
assertTrue(valid.isPresent());
@ -187,11 +191,7 @@ public class BitbucketPersonalAccessTokenFetcherTest {
PersonalAccessTokenParams params =
new PersonalAccessTokenParams(
"https://bitbucket.org",
OAUTH_2_SUFFIX + "-params-name",
"tid-23434",
bitbucketOauthToken,
null);
true, "https://bitbucket.org", "azure-devops", "tid-23434", bitbucketOauthToken, null);
Optional<Pair<Boolean, String>> valid = bitbucketPersonalAccessTokenFetcher.isValid(params);
assertTrue(valid.isPresent());
@ -204,11 +204,7 @@ public class BitbucketPersonalAccessTokenFetcherTest {
PersonalAccessTokenParams params =
new PersonalAccessTokenParams(
"https://bitbucket.org",
OAUTH_2_SUFFIX + "-token-name",
"tid-23434",
bitbucketOauthToken,
null);
true, "https://bitbucket.org", "azure-devops", "tid-23434", bitbucketOauthToken, null);
assertFalse(bitbucketPersonalAccessTokenFetcher.isValid(params).isPresent());
}

View File

@ -139,7 +139,7 @@ public abstract class AbstractGithubPersonalAccessTokenFetcher
Optional<Pair<Boolean, String>> valid =
isValid(
new PersonalAccessTokenParams(
scmServerUrl, providerName, tokenId, oAuthToken.getToken(), null));
true, scmServerUrl, providerName, tokenId, oAuthToken.getToken(), null));
if (valid.isEmpty()) {
throw buildScmUnauthorizedException(cheSubject);
} else if (!valid.get().first) {
@ -184,8 +184,7 @@ public abstract class AbstractGithubPersonalAccessTokenFetcher
}
try {
if (personalAccessToken.getScmProviderName() != null
&& personalAccessToken.getScmProviderName().startsWith(OAUTH_2_SUFFIX)) {
if (personalAccessToken.isOAuthToken()) {
String[] scopes = githubApiClient.getTokenScopes(personalAccessToken.getToken()).second;
return Optional.of(containsScopes(scopes, DEFAULT_TOKEN_SCOPES));
} else {
@ -217,8 +216,7 @@ public abstract class AbstractGithubPersonalAccessTokenFetcher
}
}
try {
if (params.getScmProviderName() != null
&& params.getScmProviderName().startsWith(OAUTH_2_SUFFIX)) {
if (params.getScmProviderName() != null && params.isOAuthToken()) {
Pair<String, String[]> pair = apiClient.getTokenScopes(params.getToken());
return Optional.of(
Pair.of(

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012-2023 Red Hat, Inc.
* Copyright (c) 2012-2024 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/
@ -17,8 +17,9 @@ import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
import static org.eclipse.che.dto.server.DtoFactory.newDto;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
@ -26,9 +27,11 @@ import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import com.github.tomakehurst.wiremock.common.Slf4jNotifier;
import com.google.common.net.HttpHeaders;
import org.eclipse.che.api.auth.shared.dto.OAuthToken;
import java.util.Optional;
import org.eclipse.che.api.factory.server.scm.GitUserData;
import org.eclipse.che.api.factory.server.scm.PersonalAccessToken;
import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenManager;
import org.eclipse.che.commons.subject.Subject;
import org.eclipse.che.security.oauth.OAuthAPI;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
@ -80,8 +83,11 @@ public class GithubGitUserDataFetcherTest {
@Test
public void shouldFetchGitUserData() throws Exception {
OAuthToken oAuthToken = newDto(OAuthToken.class).withToken(githubOauthToken).withScope("repo");
when(oAuthTokenFetcher.getToken(anyString())).thenReturn(oAuthToken);
PersonalAccessToken token = mock(PersonalAccessToken.class);
when(token.getToken()).thenReturn(githubOauthToken);
when(token.getScmProviderUrl()).thenReturn(wireMockServer.url("/"));
when(personalAccessTokenManager.get(any(Subject.class), eq("github"), eq(null)))
.thenReturn(Optional.of(token));
GitUserData gitUserData = githubGUDFetcher.fetchGitUserData();

View File

@ -19,7 +19,6 @@ import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
import static java.net.HttpURLConnection.HTTP_FORBIDDEN;
import static org.eclipse.che.api.factory.server.github.GithubPersonalAccessTokenFetcher.DEFAULT_TOKEN_SCOPES;
import static org.eclipse.che.api.factory.server.scm.PersonalAccessTokenFetcher.OAUTH_2_SUFFIX;
import static org.eclipse.che.dto.server.DtoFactory.newDto;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;
@ -91,7 +90,7 @@ public class GithubPersonalAccessTokenFetcherTest {
.withBodyFile("github/rest/user/response.json")));
PersonalAccessTokenParams personalAccessTokenParams =
new PersonalAccessTokenParams(
"https://github.com/", "scmTokenName", "scmTokenId", githubOauthToken, null);
true, "https://github.com/", "scmTokenName", "scmTokenId", githubOauthToken, null);
assertTrue(
githubPATFetcher.isValid(personalAccessTokenParams).isEmpty(),
"Should not validate SCM server with trailing /");
@ -213,7 +212,7 @@ public class GithubPersonalAccessTokenFetcherTest {
PersonalAccessTokenParams params =
new PersonalAccessTokenParams(
wireMockServer.url("/"), "token-name", "tid-23434", githubOauthToken, null);
false, wireMockServer.url("/"), "token-name", "tid-23434", githubOauthToken, null);
Optional<Pair<Boolean, String>> valid = githubPATFetcher.isValid(params);
assertTrue(valid.isPresent());
@ -235,11 +234,7 @@ public class GithubPersonalAccessTokenFetcherTest {
PersonalAccessTokenParams params =
new PersonalAccessTokenParams(
wireMockServer.url("/"),
OAUTH_2_SUFFIX + "-params-name",
"tid-23434",
githubOauthToken,
null);
true, wireMockServer.url("/"), "github", "tid-23434", githubOauthToken, null);
Optional<Pair<Boolean, String>> valid = githubPATFetcher.isValid(params);
assertTrue(valid.isPresent());
@ -252,11 +247,7 @@ public class GithubPersonalAccessTokenFetcherTest {
PersonalAccessTokenParams params =
new PersonalAccessTokenParams(
wireMockServer.url("/"),
OAUTH_2_SUFFIX + "-token-name",
"tid-23434",
githubOauthToken,
null);
true, wireMockServer.url("/"), "github", "tid-23434", githubOauthToken, null);
assertFalse(githubPATFetcher.isValid(params).isPresent());
}

View File

@ -106,12 +106,11 @@ public class GitlabOAuthTokenFetcher implements PersonalAccessTokenFetcher {
OAuthToken oAuthToken;
try {
oAuthToken = oAuthAPI.getToken(OAUTH_PROVIDER_NAME);
String tokenName = NameGenerator.generate(OAUTH_2_SUFFIX, 5);
String tokenId = NameGenerator.generate("id-", 5);
Optional<Pair<Boolean, String>> valid =
isValid(
new PersonalAccessTokenParams(
scmServerUrl, tokenName, tokenId, oAuthToken.getToken(), null));
true, scmServerUrl, OAUTH_PROVIDER_NAME, tokenId, oAuthToken.getToken(), null));
if (valid.isEmpty()) {
throw buildScmUnauthorizedException(cheSubject);
} else if (!valid.get().first) {
@ -123,7 +122,7 @@ public class GitlabOAuthTokenFetcher implements PersonalAccessTokenFetcher {
scmServerUrl,
cheSubject.getUserId(),
valid.get().second,
tokenName,
OAUTH_PROVIDER_NAME,
tokenId,
oAuthToken.getToken());
} catch (UnauthorizedException e) {
@ -160,8 +159,7 @@ public class GitlabOAuthTokenFetcher implements PersonalAccessTokenFetcher {
return Optional.empty();
}
}
if (personalAccessToken.getScmProviderName() != null
&& personalAccessToken.getScmProviderName().startsWith(OAUTH_2_SUFFIX)) {
if (personalAccessToken.getScmProviderName() != null && personalAccessToken.isOAuthToken()) {
// validation OAuth token by special API call
try {
GitlabOauthTokenInfo info =
@ -199,8 +197,7 @@ public class GitlabOAuthTokenFetcher implements PersonalAccessTokenFetcher {
}
try {
GitlabUser user = gitlabApiClient.getUser(params.getToken());
if (params.getScmProviderName() != null
&& params.getScmProviderName().startsWith(OAUTH_2_SUFFIX)) {
if (params.getScmProviderName() != null && params.isOAuthToken()) {
// validation OAuth token by special API call
GitlabOauthTokenInfo info = gitlabApiClient.getOAuthTokenInfo(params.getToken());
return Optional.of(

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012-2023 Red Hat, Inc.
* Copyright (c) 2012-2024 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/
@ -179,7 +179,7 @@ public class GitlabOAuthTokenFetcherTest {
PersonalAccessTokenParams params =
new PersonalAccessTokenParams(
wireMockServer.baseUrl(), "oauth2-token-name", "tid-23434", "token123", null);
true, wireMockServer.baseUrl(), "oauth2-token-name", "tid-23434", "token123", null);
Optional<Pair<Boolean, String>> valid = oAuthTokenFetcher.isValid(params);

View File

@ -17,8 +17,9 @@ import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
import static org.eclipse.che.dto.server.DtoFactory.newDto;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
@ -26,9 +27,11 @@ import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import com.github.tomakehurst.wiremock.common.Slf4jNotifier;
import com.google.common.net.HttpHeaders;
import org.eclipse.che.api.auth.shared.dto.OAuthToken;
import java.util.Optional;
import org.eclipse.che.api.factory.server.scm.GitUserData;
import org.eclipse.che.api.factory.server.scm.PersonalAccessToken;
import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenManager;
import org.eclipse.che.commons.subject.Subject;
import org.eclipse.che.security.oauth.OAuthAPI;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
@ -78,9 +81,11 @@ public class GitlabUserDataFetcherTest {
@Test
public void shouldFetchGitUserData() throws Exception {
OAuthToken oAuthToken =
newDto(OAuthToken.class).withToken("oauthtoken").withScope("api write_repository openid");
when(oAuthTokenFetcher.getToken(anyString())).thenReturn(oAuthToken);
PersonalAccessToken token = mock(PersonalAccessToken.class);
when(token.getToken()).thenReturn("oauthtoken");
when(token.getScmProviderUrl()).thenReturn(wireMockServer.url("/"));
when(personalAccessTokenManager.get(any(Subject.class), eq("gitlab"), eq(null)))
.thenReturn(Optional.of(token));
GitUserData gitUserData = gitlabUserDataFetcher.fetchGitUserData();
assertEquals(gitUserData.getScmUsername(), "John Smith");

View File

@ -12,7 +12,6 @@
package org.eclipse.che.api.factory.server.scm;
import static com.google.common.base.Strings.isNullOrEmpty;
import static org.eclipse.che.api.factory.server.scm.PersonalAccessTokenFetcher.OAUTH_2_SUFFIX;
import static org.eclipse.che.api.factory.server.scm.exception.ExceptionMessages.getDevfileConnectionErrorMessage;
import java.io.FileNotFoundException;
@ -82,11 +81,7 @@ public class AuthorizingFileContentProvider<T extends RemoteFactoryUrl>
if (isNullOrEmpty(credentials)) {
PersonalAccessToken token =
personalAccessTokenManager.getAndStore(remoteFactoryUrl.getProviderUrl());
authorization =
formatAuthorization(
token.getToken(),
token.getScmProviderName() == null
|| !token.getScmProviderName().startsWith(OAUTH_2_SUFFIX));
authorization = formatAuthorization(token.getToken(), token.isOAuthToken());
} else {
authorization = getCredentialsAuthorization(credentials);
}

View File

@ -21,6 +21,7 @@ import org.eclipse.che.commons.env.EnvironmentContext;
*/
public class PersonalAccessToken {
private final boolean isOAuthToken;
private final String scmProviderUrl;
private final String scmUserName;
/** Organization that user belongs to. Can be null if user is not a member of any organization. */
@ -32,6 +33,7 @@ public class PersonalAccessToken {
private final String cheUserId;
public PersonalAccessToken(
boolean isOAuthToken,
String scmProviderUrl,
String cheUserId,
String scmOrganization,
@ -39,6 +41,7 @@ public class PersonalAccessToken {
String scmProviderName,
String scmTokenId,
String token) {
this.isOAuthToken = isOAuthToken;
this.scmProviderUrl = scmProviderUrl;
this.scmOrganization = scmOrganization;
this.scmUserName = scmUserName;
@ -55,11 +58,12 @@ public class PersonalAccessToken {
String scmProviderName,
String scmTokenId,
String token) {
this(scmProviderUrl, cheUserId, null, scmUserName, scmProviderName, scmTokenId, token);
this(true, scmProviderUrl, cheUserId, null, scmUserName, scmProviderName, scmTokenId, token);
}
public PersonalAccessToken(String scmProviderUrl, String scmUserName, String token) {
this(
true,
scmProviderUrl,
EnvironmentContext.getCurrent().getSubject().getUserId(),
null,
@ -93,6 +97,10 @@ public class PersonalAccessToken {
return cheUserId;
}
public boolean isOAuthToken() {
return isOAuthToken;
}
@Nullable
public String getScmOrganization() {
return scmOrganization;
@ -103,7 +111,8 @@ public class PersonalAccessToken {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PersonalAccessToken that = (PersonalAccessToken) o;
return Objects.equal(scmProviderUrl, that.scmProviderUrl)
return Objects.equal(isOAuthToken, that.isOAuthToken)
&& Objects.equal(scmProviderUrl, that.scmProviderUrl)
&& Objects.equal(scmUserName, that.scmUserName)
&& Objects.equal(scmOrganization, that.scmOrganization)
&& Objects.equal(scmProviderName, that.scmProviderName)
@ -115,6 +124,7 @@ public class PersonalAccessToken {
@Override
public int hashCode() {
return Objects.hashCode(
isOAuthToken,
scmProviderUrl,
scmUserName,
scmOrganization,
@ -127,7 +137,9 @@ public class PersonalAccessToken {
@Override
public String toString() {
return "PersonalAccessToken{"
+ "scmProviderUrl='"
+ "isOAuthToken="
+ isOAuthToken
+ ", scmProviderUrl='"
+ scmProviderUrl
+ '\''
+ ", scmUserName='"
@ -136,7 +148,7 @@ public class PersonalAccessToken {
+ ", scmOrganization='"
+ scmOrganization
+ '\''
+ ", scmTokenName='"
+ ", scmProviderName='"
+ scmProviderName
+ '\''
+ ", scmTokenId='"
@ -147,6 +159,7 @@ public class PersonalAccessToken {
+ '\''
+ ", cheUserId='"
+ cheUserId
+ '\''
+ '}';
}
}

View File

@ -19,10 +19,6 @@ import org.eclipse.che.commons.lang.Pair;
import org.eclipse.che.commons.subject.Subject;
public interface PersonalAccessTokenFetcher {
/** Prefix for token names indication it is OAuth token (to differentiate from PAT-s) */
String OAUTH_2_SUFFIX = "-oauth2";
/**
* Retrieve new PersonalAccessToken from concrete scm provider
*

View File

@ -13,6 +13,7 @@ package org.eclipse.che.api.factory.server.scm;
/** An object to hold parameters for creating a personal access token. */
public class PersonalAccessTokenParams {
private final boolean isOAuthToken;
private final String scmProviderUrl;
private final String scmProviderName;
private final String scmTokenId;
@ -20,11 +21,13 @@ public class PersonalAccessTokenParams {
private final String organization;
public PersonalAccessTokenParams(
boolean isOAuthToken,
String scmProviderUrl,
String scmProviderName,
String scmTokenId,
String token,
String organization) {
this.isOAuthToken = isOAuthToken;
this.scmProviderUrl = scmProviderUrl;
this.scmProviderName = scmProviderName;
this.scmTokenId = scmTokenId;
@ -51,4 +54,8 @@ public class PersonalAccessTokenParams {
public String getOrganization() {
return organization;
}
public boolean isOAuthToken() {
return isOAuthToken;
}
}