parent
426718a495
commit
a9507244a3
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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")},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue