Skip to content

Commit

Permalink
Use hardcoded string as username for Azure git credentials (#529)
Browse files Browse the repository at this point in the history
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:<token>@dev.azure.com
  • Loading branch information
vinokurig authored Jul 6, 2023
1 parent 21323d1 commit aee2079
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, String> 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<Secret> 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");
Expand Down

0 comments on commit aee2079

Please sign in to comment.