Signed-off-by: Anatolii Bazko <abazko@redhat.com>
pull/452/head
Anatolii Bazko 2023-02-27 10:31:38 +02:00
parent 426718a495
commit a9507244a3
8 changed files with 54 additions and 14 deletions

View File

@ -123,7 +123,7 @@ che.oauth2.azure.devops.clientsecret_filepath=NULL
# Azure DevOps Service OAuth2 scopes.
# Separate multiple values with comma, for example: scope,scope,scope
# The full list of scopes: https://learn.microsoft.com/en-us/azure/devops/integrate/get-started/authentication/oauth?view=azure-devops#scopes
che.integration.azure.devops.application_scopes=vso.code_full
che.integration.azure.devops.application_scopes=vso.code_write
# Azure DevOps Service OAuth2 authorization URI.
che.oauth.azure.devops.authuri=https://app.vssps.visualstudio.com/oauth2/authorize
@ -132,10 +132,10 @@ che.oauth.azure.devops.authuri=https://app.vssps.visualstudio.com/oauth2/authori
che.oauth.azure.devops.tokenuri=https://app.vssps.visualstudio.com/oauth2/token
# Azure DevOps Service API server address.
che.integration.azure.devops.api_endpoint=https://vssps.dev.azure.com/
che.integration.azure.devops.api_endpoint=https://vssps.dev.azure.com
# Azure DevOps SCM API server address.
che.integration.azure.devops.scm.api_endpoint=https://dev.azure.com/
che.integration.azure.devops.scm.api_endpoint=https://dev.azure.com
# Azure DevOps Service redirect URIs.
# Separate multiple values with comma, for example: URI,URI,URI.

View File

@ -19,7 +19,10 @@ import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/** TODO replace this class with URLEncodedUtils */
public class UrlUtils {

View File

@ -126,7 +126,11 @@ public class AzureDevOpsApiClient {
});
}
/** Returns the scopes of the OAuth token. */
/**
* Returns the scopes of the OAuth token. Consider using the REST API:
*
* <p>https://learn.microsoft.com/en-us/rest/api/azure/devops/tokens/pats/get?view=azure-devops-rest-7.0&tabs=HTTP
*/
public String[] getTokenScopes(String authenticationToken)
throws ScmItemNotFoundException, ScmCommunicationException, ScmBadRequestException {
return scopes;

View File

@ -11,6 +11,7 @@
*/
package org.eclipse.che.api.factory.server.azure.devops;
import static com.google.common.base.Strings.isNullOrEmpty;
import static java.lang.String.format;
import static java.util.regex.Pattern.compile;
import static org.eclipse.che.commons.lang.StringUtils.trimEnd;
@ -81,6 +82,16 @@ public class AzureDevOpsURLParser {
String branch = matcher.group("branch");
String tag = matcher.group("tag");
// The url might have the following formats:
// - https://<organization>@<host>/<organization>/<project>/_git/<repoName>
// - https://<credentials>@<host>/<organization>/<project>/_git/<repoName>
// For the first case we need to remove the `organization` from the url to distinguish it from
// `credentials`
String organizationCanIgnore = matcher.group("organizationCanIgnore");
if (!isNullOrEmpty(organization) && organization.equals(organizationCanIgnore)) {
url = url.replace(organizationCanIgnore + "@", "");
}
return new AzureDevOpsUrl()
.withHostName(azureDevOpsScmApiEndpointHost)
.withProject(project)
@ -88,6 +99,7 @@ public class AzureDevOpsURLParser {
.withOrganization(organization)
.withBranch(branch)
.withTag(tag)
.withDevfileFilenames(devfileFilenamesProvider.getConfiguredDevfileFilenames());
.withDevfileFilenames(devfileFilenamesProvider.getConfiguredDevfileFilenames())
.withUrl(url);
}
}

View File

@ -18,7 +18,7 @@ import java.util.List;
import java.util.Optional;
import java.util.StringJoiner;
import java.util.stream.Collectors;
import org.eclipse.che.api.factory.server.urlfactory.RemoteFactoryUrl;
import org.eclipse.che.api.factory.server.urlfactory.DefaultFactoryUrl;
/**
* Representation of Azure DevOps URL, allowing to get details from it.
@ -28,7 +28,7 @@ import org.eclipse.che.api.factory.server.urlfactory.RemoteFactoryUrl;
*
* @author Anatolii Bazko
*/
public class AzureDevOpsUrl implements RemoteFactoryUrl {
public class AzureDevOpsUrl extends DefaultFactoryUrl {
private String hostName;

View File

@ -18,6 +18,8 @@ import org.testng.annotations.DataProvider;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import java.util.Optional;
import static org.mockito.Mockito.mock;
import static org.testng.Assert.assertEquals;
@ -97,4 +99,20 @@ public class AzureDevOpsURLParserTest {
},
};
}
@Test(dataProvider = "url")
public void testCredentials(String url, String organization, Optional<String> credentials) {
AzureDevOpsUrl azureDevOpsUrl = azureDevOpsURLParser.parse(url);
assertEquals(azureDevOpsUrl.getOrganization(), organization);
assertEquals(azureDevOpsUrl.getCredentials(), credentials);
}
@DataProvider(name = "url")
public Object[][] url() {
return new Object[][]{
{"https://MyOrg@dev.azure.com/MyOrg/MyProject/_git/MyRepo", "MyOrg", Optional.empty()},
{"https://user:pwd@dev.azure.com/MyOrg/MyProject/_git/MyRepo", "MyOrg", Optional.of("user:pwd")},
};
}
}

View File

@ -11,9 +11,8 @@
*/
package org.eclipse.che.api.factory.server.scm;
import static org.eclipse.che.api.factory.server.scm.PersonalAccessTokenFetcher.OAUTH_2_PREFIX;
import static com.google.common.base.Strings.isNullOrEmpty;
import static org.eclipse.che.api.factory.server.scm.PersonalAccessTokenFetcher.OAUTH_2_PREFIX;
import java.io.FileNotFoundException;
import java.io.IOException;
@ -80,11 +79,13 @@ public class AuthorizingFileContentProvider<T extends RemoteFactoryUrl>
// try to authenticate for the given URL
String authorization;
if (isNullOrEmpty(credentials)) {
PersonalAccessToken token =
personalAccessTokenManager.getAndStore(remoteFactoryUrl.getHostName());
authorization =
formatAuthorization(
personalAccessTokenManager
.getAndStore(remoteFactoryUrl.getHostName())
.getToken());
token.getToken(),
token.getScmTokenName() == null
|| !token.getScmTokenName().startsWith(OAUTH_2_PREFIX));
} else {
authorization = getCredentialsAuthorization(credentials);
}

View File

@ -12,6 +12,7 @@
package org.eclipse.che.api.factory.server.scm;
import com.google.common.base.Objects;
import org.eclipse.che.commons.annotation.Nullable;
import org.eclipse.che.commons.env.EnvironmentContext;
/**
@ -23,7 +24,7 @@ public class PersonalAccessToken {
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. */
private final String scmOrganization;
@Nullable private final String scmOrganization;
private final String scmUserId;
private final String scmTokenName;
@ -101,6 +102,7 @@ public class PersonalAccessToken {
return cheUserId;
}
@Nullable
public String getScmOrganization() {
return scmOrganization;
}