fixup! Fetch oauth tokens from kubernetes secrets
parent
71b21e37f5
commit
ab873284e6
|
|
@ -14,7 +14,6 @@ package org.eclipse.che.api.factory.server.scm.kubernetes;
|
|||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import static org.eclipse.che.commons.lang.StringUtils.trimEnd;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import io.fabric8.kubernetes.api.model.LabelSelector;
|
||||
import io.fabric8.kubernetes.api.model.LabelSelectorBuilder;
|
||||
|
|
@ -87,8 +86,8 @@ public class KubernetesPersonalAccessTokenManager implements PersonalAccessToken
|
|||
this.gitCredentialManager = gitCredentialManager;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void save(PersonalAccessToken personalAccessToken)
|
||||
@Override
|
||||
public void store(PersonalAccessToken personalAccessToken)
|
||||
throws UnsatisfiedScmPreconditionException, ScmConfigurationPersistenceException {
|
||||
try {
|
||||
String namespace = getFirstNamespace();
|
||||
|
|
@ -136,7 +135,7 @@ public class KubernetesPersonalAccessTokenManager implements PersonalAccessToken
|
|||
ScmUnauthorizedException, ScmCommunicationException, UnknownScmProviderException {
|
||||
PersonalAccessToken personalAccessToken =
|
||||
scmPersonalAccessTokenFetcher.fetchPersonalAccessToken(cheUser, scmServerUrl);
|
||||
save(personalAccessToken);
|
||||
store(personalAccessToken);
|
||||
return personalAccessToken;
|
||||
}
|
||||
|
||||
|
|
@ -291,7 +290,7 @@ public class KubernetesPersonalAccessTokenManager implements PersonalAccessToken
|
|||
}
|
||||
|
||||
@Override
|
||||
public void store(String scmServerUrl)
|
||||
public void storeGitCredentials(String scmServerUrl)
|
||||
throws UnsatisfiedScmPreconditionException, ScmConfigurationPersistenceException,
|
||||
ScmCommunicationException, ScmUnauthorizedException {
|
||||
Subject subject = EnvironmentContext.getCurrent().getSubject();
|
||||
|
|
|
|||
|
|
@ -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/
|
||||
|
|
@ -142,7 +142,7 @@ public class KubernetesPersonalAccessTokenManagerTest {
|
|||
"https://bitbucket.com", "cheUser", "username", "token-name", "tid-24", "token123");
|
||||
|
||||
// when
|
||||
personalAccessTokenManager.save(token);
|
||||
personalAccessTokenManager.store(token);
|
||||
|
||||
// then
|
||||
verify(nonNamespaceOperation).createOrReplace(captor.capture());
|
||||
|
|
|
|||
|
|
@ -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/
|
||||
|
|
@ -73,7 +73,7 @@ public class CredentialsSecretConfigurator implements NamespaceConfigurator {
|
|||
.forEach(
|
||||
s -> {
|
||||
try {
|
||||
personalAccessTokenManager.store(
|
||||
personalAccessTokenManager.storeGitCredentials(
|
||||
s.getMetadata().getAnnotations().get(ANNOTATION_SCM_URL));
|
||||
} catch (ScmCommunicationException
|
||||
| ScmConfigurationPersistenceException
|
||||
|
|
|
|||
|
|
@ -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/
|
||||
|
|
@ -91,7 +91,7 @@ public class CredentialsSecretConfiguratorTest {
|
|||
configurator.configure(namespaceResolutionContext, TEST_NAMESPACE_NAME);
|
||||
|
||||
// then
|
||||
verify(personalAccessTokenManager).store(eq("test-url"));
|
||||
verify(personalAccessTokenManager).storeGitCredentials(eq("test-url"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -129,6 +129,6 @@ public class CredentialsSecretConfiguratorTest {
|
|||
configurator.configure(namespaceResolutionContext, TEST_NAMESPACE_NAME);
|
||||
|
||||
// then
|
||||
verify(personalAccessTokenManager, never()).store(anyString());
|
||||
verify(personalAccessTokenManager, never()).storeGitCredentials(anyString());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ package org.eclipse.che.security.oauth;
|
|||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static java.util.Collections.emptyList;
|
||||
import static org.eclipse.che.api.factory.server.scm.PersonalAccessTokenFetcher.OAUTH_2_PREFIX;
|
||||
import static org.eclipse.che.commons.lang.UrlUtils.*;
|
||||
import static org.eclipse.che.commons.lang.UrlUtils.getParameter;
|
||||
import static org.eclipse.che.dto.server.DtoFactory.newDto;
|
||||
|
|
@ -43,7 +44,10 @@ import org.eclipse.che.api.core.util.LinksHelper;
|
|||
import org.eclipse.che.api.factory.server.scm.PersonalAccessToken;
|
||||
import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenManager;
|
||||
import org.eclipse.che.api.factory.server.scm.exception.ScmConfigurationPersistenceException;
|
||||
import org.eclipse.che.api.factory.server.scm.exception.UnsatisfiedScmPreconditionException;
|
||||
import org.eclipse.che.commons.annotation.Nullable;
|
||||
import org.eclipse.che.commons.env.EnvironmentContext;
|
||||
import org.eclipse.che.commons.lang.NameGenerator;
|
||||
import org.eclipse.che.commons.subject.Subject;
|
||||
import org.eclipse.che.security.oauth.shared.dto.OAuthAuthenticatorDescriptor;
|
||||
import org.slf4j.Logger;
|
||||
|
|
@ -84,7 +88,8 @@ public class EmbeddedOAuthAPI implements OAuthAPI {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Response callback(UriInfo uriInfo, List<String> errorValues) throws NotFoundException {
|
||||
public Response callback(UriInfo uriInfo, @Nullable List<String> errorValues)
|
||||
throws NotFoundException {
|
||||
URL requestUrl = getRequestUrl(uriInfo);
|
||||
Map<String, List<String>> params = getQueryParametersFromState(getState(requestUrl));
|
||||
errorValues = errorValues == null ? uriInfo.getQueryParameters().get("error") : errorValues;
|
||||
|
|
@ -97,13 +102,25 @@ public class EmbeddedOAuthAPI implements OAuthAPI {
|
|||
OAuthAuthenticator oauth = getAuthenticator(providerName);
|
||||
final List<String> scopes = params.get("scope");
|
||||
try {
|
||||
oauth.callback(requestUrl, scopes == null ? emptyList() : scopes);
|
||||
String token = oauth.callback(requestUrl, scopes == null ? emptyList() : scopes);
|
||||
personalAccessTokenManager.store(
|
||||
new PersonalAccessToken(
|
||||
oauth.getEndpointUrl(),
|
||||
EnvironmentContext.getCurrent().getSubject().getUserId(),
|
||||
null,
|
||||
null,
|
||||
NameGenerator.generate(OAUTH_2_PREFIX, 5),
|
||||
NameGenerator.generate("id-", 5),
|
||||
token));
|
||||
} catch (OAuthAuthenticationException e) {
|
||||
return Response.temporaryRedirect(
|
||||
URI.create(
|
||||
getParameter(params, "redirect_after_login")
|
||||
+ String.format("&%s=access_denied", ERROR_QUERY_NAME)))
|
||||
.build();
|
||||
} catch (UnsatisfiedScmPreconditionException | ScmConfigurationPersistenceException e) {
|
||||
// Skip exception, the token will be stored in the next request.
|
||||
LOG.error(e.getMessage(), e);
|
||||
}
|
||||
final String redirectAfterLogin = getParameter(params, "redirect_after_login");
|
||||
return Response.temporaryRedirect(URI.create(redirectAfterLogin)).build();
|
||||
|
|
|
|||
|
|
@ -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/
|
||||
|
|
@ -174,7 +174,7 @@ public abstract class OAuthAuthenticator {
|
|||
* server
|
||||
* @param scopes specify exactly what type of access needed. This list must be exactly the same as
|
||||
* list passed to the method {@link #getAuthenticateUrl(URL, java.util.List)}
|
||||
* @return id of authenticated user
|
||||
* @return access token
|
||||
* @throws OAuthAuthenticationException if authentication failed or <code>requestUrl</code> does
|
||||
* not contain required parameters, e.g. 'code'
|
||||
*/
|
||||
|
|
@ -202,7 +202,7 @@ public abstract class OAuthAuthenticator {
|
|||
userId = EnvironmentContext.getCurrent().getSubject().getUserId();
|
||||
}
|
||||
flow.createAndStoreCredential(tokenResponse, userId);
|
||||
return userId;
|
||||
return tokenResponse.getAccessToken();
|
||||
} catch (IOException ioe) {
|
||||
throw new OAuthAuthenticationException(ioe.getMessage());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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/
|
||||
|
|
@ -11,23 +11,33 @@
|
|||
*/
|
||||
package org.eclipse.che.security.oauth;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static org.eclipse.che.api.factory.server.scm.PersonalAccessTokenFetcher.OAUTH_2_PREFIX;
|
||||
import static org.eclipse.che.dto.server.DtoFactory.newDto;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyList;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import jakarta.ws.rs.core.UriBuilder;
|
||||
import jakarta.ws.rs.core.UriInfo;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.util.Set;
|
||||
import org.eclipse.che.api.auth.shared.dto.OAuthToken;
|
||||
import org.eclipse.che.api.core.NotFoundException;
|
||||
import org.eclipse.che.api.factory.server.scm.PersonalAccessToken;
|
||||
import org.eclipse.che.api.factory.server.scm.PersonalAccessTokenManager;
|
||||
import org.eclipse.che.security.oauth.shared.dto.OAuthAuthenticatorDescriptor;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.testng.MockitoTestNGListener;
|
||||
|
|
@ -40,6 +50,7 @@ public class EmbeddedOAuthAPITest {
|
|||
|
||||
@Mock OAuthAuthenticatorProvider oauth2Providers;
|
||||
@Mock org.eclipse.che.security.oauth1.OAuthAuthenticatorProvider oauth1Providers;
|
||||
@Mock PersonalAccessTokenManager personalAccessTokenManager;
|
||||
|
||||
@InjectMocks EmbeddedOAuthAPI embeddedOAuthAPI;
|
||||
|
||||
|
|
@ -101,4 +112,32 @@ public class EmbeddedOAuthAPITest {
|
|||
callback.getLocation().toString(),
|
||||
"http://eclipse.che?quary%3Dparam%26error_code%3Daccess_denied");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldStoreTokenOnCallback() throws Exception {
|
||||
// given
|
||||
UriInfo uriInfo = mock(UriInfo.class);
|
||||
OAuthAuthenticator authenticator = mock(OAuthAuthenticator.class);
|
||||
when(authenticator.getEndpointUrl()).thenReturn("http://eclipse.che");
|
||||
when(authenticator.callback(any(URL.class), anyList())).thenReturn("token");
|
||||
when(uriInfo.getRequestUri())
|
||||
.thenReturn(
|
||||
new URI(
|
||||
"http://eclipse.che?state=oauth_provider%3Dgithub%26redirect_after_login%3DredirectUrl"));
|
||||
when(oauth2Providers.getAuthenticator("github")).thenReturn(authenticator);
|
||||
ArgumentCaptor<PersonalAccessToken> tokenCapture =
|
||||
ArgumentCaptor.forClass(PersonalAccessToken.class);
|
||||
|
||||
// when
|
||||
embeddedOAuthAPI.callback(uriInfo, emptyList());
|
||||
|
||||
// then
|
||||
verify(personalAccessTokenManager).store(tokenCapture.capture());
|
||||
PersonalAccessToken token = tokenCapture.getValue();
|
||||
assertEquals(token.getScmProviderUrl(), "http://eclipse.che");
|
||||
assertEquals(token.getCheUserId(), "0000-00-0000");
|
||||
assertTrue(token.getScmTokenId().startsWith("id-"));
|
||||
assertTrue(token.getScmTokenName().startsWith(OAUTH_2_PREFIX));
|
||||
assertEquals(token.getToken(), "token");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,7 +105,18 @@ public interface PersonalAccessTokenManager {
|
|||
* @throws ScmCommunicationException - problem occurred during communication with scm provider.
|
||||
* @throws ScmUnauthorizedException - scm authorization required.
|
||||
*/
|
||||
void store(String scmServerUrl)
|
||||
void storeGitCredentials(String scmServerUrl)
|
||||
throws UnsatisfiedScmPreconditionException, ScmConfigurationPersistenceException,
|
||||
ScmCommunicationException, ScmUnauthorizedException;
|
||||
|
||||
/**
|
||||
* Store {@link PersonalAccessToken} in permanent storage.
|
||||
*
|
||||
* @param token personal access token
|
||||
* @throws UnsatisfiedScmPreconditionException - storage preconditions aren't met.
|
||||
* @throws ScmConfigurationPersistenceException - problem occurred during communication with
|
||||
* permanent storage.
|
||||
*/
|
||||
void store(PersonalAccessToken token)
|
||||
throws UnsatisfiedScmPreconditionException, ScmConfigurationPersistenceException;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue