From aee207932219a054b4b3459eb0eec2bfa28e038e Mon Sep 17 00:00:00 2001 From: Igor Vinokur Date: Thu, 6 Jul 2023 09:37:33 +0300 Subject: [PATCH] Use hardcoded string as username for Azure git credentials (#529) Since Azure git provider do not support username for its user object, use username string as username in the git credentials file if the PAT is related to Azure Devops: https://username:@dev.azure.com --- .../KubernetesGitCredentialManager.java | 20 +++++-- .../KubernetesGitCredentialManagerTest.java | 52 +++++++++++++++++++ 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/infrastructures/infrastructure-factory/src/main/java/org/eclipse/che/api/factory/server/scm/kubernetes/KubernetesGitCredentialManager.java b/infrastructures/infrastructure-factory/src/main/java/org/eclipse/che/api/factory/server/scm/kubernetes/KubernetesGitCredentialManager.java index 908ee290e8..b5c3a2fd83 100644 --- a/infrastructures/infrastructure-factory/src/main/java/org/eclipse/che/api/factory/server/scm/kubernetes/KubernetesGitCredentialManager.java +++ b/infrastructures/infrastructure-factory/src/main/java/org/eclipse/che/api/factory/server/scm/kubernetes/KubernetesGitCredentialManager.java @@ -11,6 +11,7 @@ */ 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_PREFIX; @@ -156,9 +157,7 @@ public void createOrReplace(PersonalAccessToken personalAccessToken) format( "%s://%s:%s@%s%s", scmUrl.getProtocol(), - personalAccessToken.getScmTokenName().startsWith(OAUTH_2_PREFIX) - ? "oauth2" - : personalAccessToken.getScmUserName(), + getUsernameSegment(personalAccessToken), URLEncoder.encode(personalAccessToken.getToken(), UTF_8), scmUrl.getHost(), scmUrl.getPort() != 80 && scmUrl.getPort() != -1 @@ -171,6 +170,21 @@ public void createOrReplace(PersonalAccessToken personalAccessToken) } } + /** + * Returns username URL segment for git credentials. For OAuth2 tokens it is "oauth2", for others + * - {@param personalAccessToken#getScmUserName()} or just "username" string if the token has a + * non-null {@param personalAccessToken#getScmOrganization()}. This is needed to support providers + * that do not have username in their user object. Such providers have an additional organization + * field. + */ + private String getUsernameSegment(PersonalAccessToken personalAccessToken) { + return personalAccessToken.getScmTokenName().startsWith(OAUTH_2_PREFIX) + ? "oauth2" + : isNullOrEmpty(personalAccessToken.getScmOrganization()) + ? personalAccessToken.getScmUserName() + : "username"; + } + /** * It is not guaranteed that this code will always return same namespace, but since credentials * are now added into manually pre-created user namespace, we can expect always the same result diff --git a/infrastructures/infrastructure-factory/src/test/java/org/eclipse/che/api/factory/server/scm/kubernetes/KubernetesGitCredentialManagerTest.java b/infrastructures/infrastructure-factory/src/test/java/org/eclipse/che/api/factory/server/scm/kubernetes/KubernetesGitCredentialManagerTest.java index 9f9bf8c8b7..5ac8e79bde 100644 --- a/infrastructures/infrastructure-factory/src/test/java/org/eclipse/che/api/factory/server/scm/kubernetes/KubernetesGitCredentialManagerTest.java +++ b/infrastructures/infrastructure-factory/src/test/java/org/eclipse/che/api/factory/server/scm/kubernetes/KubernetesGitCredentialManagerTest.java @@ -108,6 +108,58 @@ public void testCreateAndSaveNewPATGitCredential() throws Exception { assertFalse(createdSecret.getMetadata().getName().contains(token.getScmUserName())); } + @Test + public void shouldUseHardcodedUsernameIfScmOrganizationIsDefined() throws Exception { + // given + KubernetesNamespaceMeta namespaceMeta = new KubernetesNamespaceMetaImpl("test"); + PersonalAccessToken token = + new PersonalAccessToken( + "https://bitbucket-server.com:5648", + "cheUser", + "cheOrganization", + "username", + "token-name", + "tid-23434", + "token123"); + + Map annotations = new HashMap<>(DEFAULT_SECRET_ANNOTATIONS); + + annotations.put(ANNOTATION_SCM_URL, token.getScmProviderUrl() + "/"); + annotations.put(ANNOTATION_SCM_USERNAME, token.getScmUserName()); + annotations.put(ANNOTATION_CHE_USERID, token.getCheUserId()); + ObjectMeta objectMeta = + new ObjectMetaBuilder() + .withName(NameGenerator.generate(NAME_PATTERN, 5)) + .withAnnotations(annotations) + .build(); + Secret existing = + new SecretBuilder() + .withMetadata(objectMeta) + .withData(Map.of("credentials", "foo 123")) + .build(); + + when(namespaceFactory.list()).thenReturn(Collections.singletonList(namespaceMeta)); + + when(cheServerKubernetesClientFactory.create()).thenReturn(kubeClient); + when(kubeClient.secrets()).thenReturn(secretsMixedOperation); + when(secretsMixedOperation.inNamespace(eq(namespaceMeta.getName()))) + .thenReturn(nonNamespaceOperation); + when(nonNamespaceOperation.withLabels(anyMap())).thenReturn(filterWatchDeletable); + when(filterWatchDeletable.list()).thenReturn(secretList); + when(secretList.getItems()).thenReturn(singletonList(existing)); + + // when + kubernetesGitCredentialManager.createOrReplace(token); + // then + ArgumentCaptor captor = ArgumentCaptor.forClass(Secret.class); + verify(nonNamespaceOperation).createOrReplace(captor.capture()); + Secret createdSecret = captor.getValue(); + assertNotNull(createdSecret); + assertEquals( + new String(Base64.getDecoder().decode(createdSecret.getData().get("credentials"))), + "https://username:token123@bitbucket-server.com:5648"); + } + @Test public void testCreateAndSaveNewOAuthGitCredential() throws Exception { KubernetesNamespaceMeta meta = new KubernetesNamespaceMetaImpl("test");