diff --git a/.run/azure-ad [hpi_run].run.xml b/.run/azure-ad [hpi_run].run.xml index 347e336a..8259292b 100644 --- a/.run/azure-ad [hpi_run].run.xml +++ b/.run/azure-ad [hpi_run].run.xml @@ -12,7 +12,7 @@ + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 1df7e66a..f30f913b 100644 --- a/pom.xml +++ b/pom.xml @@ -157,7 +157,18 @@ com.github.scribejava scribejava-apis - 4.2.0 + 8.3.1 + + + + com.fasterxml.jackson.datatype + * + + + com.fasterxml.jackson.core + * + + org.bitbucket.b_c diff --git a/src/main/java/com/microsoft/jenkins/azuread/AzureEnvironment.java b/src/main/java/com/microsoft/jenkins/azuread/AzureEnvironment.java index 537310b3..2781e45d 100644 --- a/src/main/java/com/microsoft/jenkins/azuread/AzureEnvironment.java +++ b/src/main/java/com/microsoft/jenkins/azuread/AzureEnvironment.java @@ -20,8 +20,6 @@ static String getAuthorityHost(String azureEnvironmentName) { switch (azureEnvironmentName) { case AZURE_CHINA: return AzureAuthorityHosts.AZURE_CHINA; - case AZURE_GERMANY: - return AzureAuthorityHosts.AZURE_GERMANY; case AZURE_US_GOVERNMENT_L4: case AZURE_US_GOVERNMENT_L5: return AzureAuthorityHosts.AZURE_GOVERNMENT; @@ -35,8 +33,6 @@ static String getGraphResource(String azureEnv) { switch (azureEnv) { case AZURE_CHINA: return "https://microsoftgraph.chinacloudapi.cn/"; - case AZURE_GERMANY: - return "https://graph.microsoft.de/"; case AZURE_US_GOVERNMENT_L4: return "https://graph.microsoft.us/"; case AZURE_US_GOVERNMENT_L5: diff --git a/src/main/java/com/microsoft/jenkins/azuread/AzureSecurityRealm.java b/src/main/java/com/microsoft/jenkins/azuread/AzureSecurityRealm.java index f9404985..56d54d6d 100644 --- a/src/main/java/com/microsoft/jenkins/azuread/AzureSecurityRealm.java +++ b/src/main/java/com/microsoft/jenkins/azuread/AzureSecurityRealm.java @@ -24,8 +24,7 @@ import com.microsoft.graph.options.QueryOption; import com.microsoft.graph.requests.GraphServiceClient; import com.microsoft.graph.requests.GroupCollectionPage; -import com.microsoft.jenkins.azuread.scribe.AzureApi; -import com.microsoft.jenkins.azuread.scribe.AzureOAuthService; +import com.microsoft.jenkins.azuread.scribe.AzureAdApi; import com.microsoft.jenkins.azuread.utils.UUIDValidator; import com.thoughtworks.xstream.converters.Converter; import com.thoughtworks.xstream.converters.MarshallingContext; @@ -98,7 +97,6 @@ import static com.microsoft.jenkins.azuread.AzureEnvironment.AZURE_US_GOVERNMENT_L4; import static com.microsoft.jenkins.azuread.AzureEnvironment.AZURE_US_GOVERNMENT_L5; import static com.microsoft.jenkins.azuread.AzureEnvironment.getAuthorityHost; -import static com.microsoft.jenkins.azuread.AzureEnvironment.getGraphResource; import static com.microsoft.jenkins.azuread.AzureEnvironment.getServiceRoot; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; @@ -121,6 +119,7 @@ public class AzureSecurityRealm extends SecurityRealm { private static final int NOT_FOUND = 404; private static final int BAD_REQUEST = 400; public static final String CONVERTER_DISABLE_GRAPH_INTEGRATION = "disableGraphIntegration"; + public static final String CONVERTER_SINGLE_LOGOUT = "singleLogout"; public static final String CONVERTER_ENVIRONMENT_NAME = "environmentName"; private Cache caches; @@ -311,15 +310,13 @@ public JwtConsumer getJwtConsumer() { return jwtConsumer.get(); } - AzureOAuthService getOAuthService() { - return (AzureOAuthService) new ServiceBuilder(clientId.getPlainText()) + OAuth20Service getOAuthService() { + return new ServiceBuilder(clientId.getPlainText()) .apiSecret(clientSecret.getPlainText()) .responseType("id_token") - .scope("openid profile email") + .defaultScope("openid profile email") .callback(getRootUrl() + CALLBACK_URL) - .build(AzureApi.instance(getGraphResource(getAzureEnvironmentName()), - this.getTenant(), - getAuthorityHost(getAzureEnvironmentName()))); + .build(AzureAdApi.custom(getTenant(), getAuthorityHost(getAzureEnvironmentName()))); } GraphServiceClient getAzureClient() { @@ -469,7 +466,7 @@ protected String getPostLogOutUrl2(StaplerRequest req, Authentication auth) { // Ensure single sign-out if (singleLogout) { - return getOAuthService().getLogoutUrl(); + return ((AzureAdApi) getOAuthService().getApi()).getLogoutUrl(); } return req.getContextPath() + "/" + AzureAdLogoutAction.POST_LOGOUT_URL; } @@ -658,6 +655,10 @@ public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingC writer.startNode(CONVERTER_DISABLE_GRAPH_INTEGRATION); writer.setValue(String.valueOf(realm.isDisableGraphIntegration())); writer.endNode(); + + writer.startNode(CONVERTER_SINGLE_LOGOUT); + writer.setValue(String.valueOf(realm.isSingleLogout())); + writer.endNode(); } @Override @@ -689,6 +690,9 @@ public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext co case CONVERTER_DISABLE_GRAPH_INTEGRATION: realm.setDisableGraphIntegration(Boolean.parseBoolean(value)); break; + case CONVERTER_SINGLE_LOGOUT: + realm.setSingleLogout(Boolean.parseBoolean(value)); + break; default: break; } diff --git a/src/main/java/com/microsoft/jenkins/azuread/scribe/AzureAdApi.java b/src/main/java/com/microsoft/jenkins/azuread/scribe/AzureAdApi.java new file mode 100644 index 00000000..d32d21bd --- /dev/null +++ b/src/main/java/com/microsoft/jenkins/azuread/scribe/AzureAdApi.java @@ -0,0 +1,34 @@ +package com.microsoft.jenkins.azuread.scribe; + +import com.github.scribejava.apis.MicrosoftAzureActiveDirectory20Api; + +public class AzureAdApi extends MicrosoftAzureActiveDirectory20Api { + + private final String tenant; + private String authorityHost; + private static final String OAUTH_2 = "/oauth2"; + + AzureAdApi(String tenant, String authorityHost) { + super(tenant); + this.authorityHost = authorityHost; + this.tenant = tenant; + } + + public static AzureAdApi custom(String tenant, String authorityHost) { + return new AzureAdApi(tenant, authorityHost); + } + + @Override + public String getAccessTokenEndpoint() { + return authorityHost + tenant + OAUTH_2 + getEndpointVersionPath() + "/token"; + } + + @Override + protected String getAuthorizationBaseUrl() { + return authorityHost + tenant + OAUTH_2 + getEndpointVersionPath() + "/authorize"; + } + + public String getLogoutUrl() { + return authorityHost + tenant + OAUTH_2 + "/logout"; + } +} diff --git a/src/main/java/com/microsoft/jenkins/azuread/scribe/AzureApi.java b/src/main/java/com/microsoft/jenkins/azuread/scribe/AzureApi.java deleted file mode 100644 index c1c9de8c..00000000 --- a/src/main/java/com/microsoft/jenkins/azuread/scribe/AzureApi.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See LICENSE file in the project root for license information. - */ - -package com.microsoft.jenkins.azuread.scribe; - -import com.azure.identity.AzureAuthorityHosts; -import com.github.scribejava.core.extractors.TokenExtractor; -import com.github.scribejava.core.model.OAuth2AccessToken; -import com.github.scribejava.core.model.OAuthConfig; -import com.github.scribejava.core.model.ParameterList; -import org.apache.commons.lang.StringUtils; -import com.github.scribejava.core.builder.api.DefaultApi20; - -public class AzureApi extends DefaultApi20 { - - public static final String DEFAULT_TENANT = "common"; - - private String tenant; - private final String loginEndpoint; - - private String resource; - - protected AzureApi(String resource, String tenant, String loginEndpoint) { - this.resource = resource; - this.tenant = tenant; - this.loginEndpoint = loginEndpoint; - } - - public static AzureApi instance(String resource) { - return instance(resource, DEFAULT_TENANT, AzureAuthorityHosts.AZURE_PUBLIC_CLOUD); - } - - public static AzureApi instance(String resource, String tenant, String loginEndpoint) { - return new AzureApi(resource, tenant, loginEndpoint); - } - - private String getBaseEndpoint() { - StringBuilder url = new StringBuilder(); - url.append(loginEndpoint); - if (StringUtils.isNotEmpty(tenant)) { - url.append(tenant); - } else { - url.append(DEFAULT_TENANT); - } - url.append("/oauth2"); - return url.toString(); - } - - @Override - public String getAccessTokenEndpoint() { - return getBaseEndpoint() + "/v2.0/token"; - } - @Override - protected String getAuthorizationBaseUrl() { - return getBaseEndpoint() + "/v2.0/authorize"; - } - - @Override - public TokenExtractor getAccessTokenExtractor() { - return AzureJsonTokenExtractor.instance(); - } - - @Override - public AzureOAuthService createService(OAuthConfig config) { - return new AzureOAuthService(this, config); - } - - public String getTenant() { - return tenant; - } - - public String getResource() { - return resource; - } - - protected String getLogoutBaseUrl() { - return getBaseEndpoint() + "/logout"; - } - - public String getLogoutUrl(String postLogoutUrl) { - final ParameterList parameters = new ParameterList(); - if (postLogoutUrl != null) { - parameters.add("post_logout_redirect_uri", postLogoutUrl); - } - return parameters.appendTo(getLogoutBaseUrl()); - } -} diff --git a/src/main/java/com/microsoft/jenkins/azuread/scribe/AzureJsonTokenExtractor.java b/src/main/java/com/microsoft/jenkins/azuread/scribe/AzureJsonTokenExtractor.java deleted file mode 100644 index 9fc7baac..00000000 --- a/src/main/java/com/microsoft/jenkins/azuread/scribe/AzureJsonTokenExtractor.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See LICENSE file in the project root for license information. - */ - -package com.microsoft.jenkins.azuread.scribe; - -import com.github.scribejava.core.exceptions.OAuthException; -import com.github.scribejava.core.extractors.TokenExtractor; -import com.github.scribejava.core.model.OAuth2AccessToken; -import com.github.scribejava.core.model.Response; -import com.github.scribejava.core.utils.Preconditions; -import com.microsoft.jenkins.azuread.Utils; - -import java.io.IOException; - -public class AzureJsonTokenExtractor implements TokenExtractor { - - private static class InstanceHolder { - private static final AzureJsonTokenExtractor INSTANCE = new AzureJsonTokenExtractor(); - } - - public static AzureJsonTokenExtractor instance() { - return InstanceHolder.INSTANCE; - } - - protected AzureJsonTokenExtractor() { - } - - @Override - public OAuth2AccessToken extract(Response response) throws IOException, OAuthException { - Preconditions.checkEmptyString(response.getBody(), - "Response body is incorrect. Can't extract a token from an empty string"); - return Utils.JsonUtil.fromJson(response.getBody(), AzureToken.class); - } -} diff --git a/src/main/java/com/microsoft/jenkins/azuread/scribe/AzureOAuthService.java b/src/main/java/com/microsoft/jenkins/azuread/scribe/AzureOAuthService.java deleted file mode 100644 index d57229bf..00000000 --- a/src/main/java/com/microsoft/jenkins/azuread/scribe/AzureOAuthService.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See LICENSE file in the project root for license information. - */ - -package com.microsoft.jenkins.azuread.scribe; - -import com.github.scribejava.core.model.OAuth2AccessToken; -import com.github.scribejava.core.model.OAuthConfig; -import com.github.scribejava.core.model.OAuthConstants; -import com.github.scribejava.core.model.OAuthRequest; -import com.github.scribejava.core.oauth.OAuth20Service; - -import java.io.IOException; -import java.util.concurrent.ExecutionException; - -public class AzureOAuthService extends OAuth20Service { - - public AzureOAuthService(AzureApi api, OAuthConfig config) { - super(api, config); - } - - @Override - protected OAuthRequest createAccessTokenRequest(String code) { - OAuthRequest request = super.createAccessTokenRequest(code); - request.addParameter("resource", getApi().getResource()); - return request; - } - - protected OAuthRequest createAccessTokenCredentialGrantRequest() { - final OAuthRequest request = new OAuthRequest(getApi().getAccessTokenVerb(), getApi().getAccessTokenEndpoint()); - final OAuthConfig config = getConfig(); - request.addParameter(OAuthConstants.CLIENT_ID, config.getApiKey()); - request.addParameter(OAuthConstants.CLIENT_SECRET, config.getApiSecret()); - request.addParameter(OAuthConstants.SCOPE, config.getScope()); - request.addParameter(OAuthConstants.GRANT_TYPE, "client_credentials"); - return request; - } - - public OAuth2AccessToken getAccessTokenCredentialGrant() - throws InterruptedException, ExecutionException, IOException { - final OAuthRequest request = createAccessTokenCredentialGrantRequest(); - return sendAccessTokenRequestSync(request); - } - - @Override - public AzureApi getApi() { - return (AzureApi) super.getApi(); - } - - public final String getLogoutUrl() { - return getLogoutUrl(null); - } - - public String getLogoutUrl(String postLogoutUrl) { - return getApi().getLogoutUrl(postLogoutUrl); - } -} diff --git a/tsconfig.json b/tsconfig.json index 1cb4ded3..2dd5d087 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,5 +10,6 @@ "allowJs": true, "resolveJsonModule": true, "moduleResolution": "node", - } + }, + "include": ["src/main/frontend/index.ts"], }