commit
parent
04bd0521dc
commit
f2296b651f
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
+ '\''
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue