diff --git a/pom.xml b/pom.xml index c1c25427..c17bc47e 100644 --- a/pom.xml +++ b/pom.xml @@ -3,8 +3,7 @@ 4.0.0 com.microsoft.azure adal4j - - 1.1.1 + 1.1.2 jar adal4j diff --git a/src/main/java/com/microsoft/aad/adal4j/AdalAuthorizatonGrant.java b/src/main/java/com/microsoft/aad/adal4j/AdalAuthorizatonGrant.java index 77431ba8..17a66d5b 100644 --- a/src/main/java/com/microsoft/aad/adal4j/AdalAuthorizatonGrant.java +++ b/src/main/java/com/microsoft/aad/adal4j/AdalAuthorizatonGrant.java @@ -31,7 +31,7 @@ class AdalAuthorizatonGrant { private final AuthorizationGrant grant; private final Map params; - + /** * * @param grant @@ -62,16 +62,15 @@ Map toParameters() { if (this.params != null) { outParams.putAll(this.params); } - + outParams.put("scope", "openid"); outParams.putAll(grant.toParameters()); return outParams; } - - AuthorizationGrant getAuthorizationGrant(){ + + AuthorizationGrant getAuthorizationGrant() { return this.grant; } - Map getCustomParameters() { return params; diff --git a/src/main/java/com/microsoft/aad/adal4j/AdalJWTClaimsSet.java b/src/main/java/com/microsoft/aad/adal4j/AdalJWTClaimsSet.java index a6885757..410e9a93 100644 --- a/src/main/java/com/microsoft/aad/adal4j/AdalJWTClaimsSet.java +++ b/src/main/java/com/microsoft/aad/adal4j/AdalJWTClaimsSet.java @@ -42,7 +42,8 @@ public JSONObject toJSONObject() { final JSONArray arr = (JSONArray) jo.get(AUDIENCE_CLAIM); if (!arr.isEmpty()) { jo.put(AUDIENCE_CLAIM, arr.get(0)); - } else { + } + else { jo.remove(AUDIENCE_CLAIM); } } diff --git a/src/main/java/com/microsoft/aad/adal4j/AdalOAuthRequest.java b/src/main/java/com/microsoft/aad/adal4j/AdalOAuthRequest.java index b98fd779..55c5a19b 100644 --- a/src/main/java/com/microsoft/aad/adal4j/AdalOAuthRequest.java +++ b/src/main/java/com/microsoft/aad/adal4j/AdalOAuthRequest.java @@ -26,10 +26,14 @@ import java.io.OutputStreamWriter; import java.io.Reader; import java.net.HttpURLConnection; +import java.net.Proxy; import java.net.URL; import java.util.Collections; import java.util.Map; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLSocketFactory; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,6 +50,8 @@ class AdalOAuthRequest extends HTTPRequest { private final Map extraHeaderParams; private final Logger log = LoggerFactory.getLogger(AdalOAuthRequest.class); + private final Proxy proxy; + private final SSLSocketFactory sslSocketFactory; /** * @@ -54,9 +60,12 @@ class AdalOAuthRequest extends HTTPRequest { * @param correlationId */ AdalOAuthRequest(final Method method, final URL url, - final Map extraHeaderParams) { + final Map extraHeaderParams, final Proxy proxy, + final SSLSocketFactory sslSocketFactory) { super(method, url); this.extraHeaderParams = extraHeaderParams; + this.proxy = proxy; + this.sslSocketFactory = sslSocketFactory; } Map getReadOnlyExtraHeaderParameters() { @@ -69,7 +78,8 @@ Map getReadOnlyExtraHeaderParameters() { @Override public HTTPResponse send() throws IOException { - final HttpURLConnection conn = HttpHelper.openConnection(this.getURL()); + final HttpsURLConnection conn = HttpHelper.openConnection(this.getURL(), + this.proxy, this.sslSocketFactory); this.configureHeaderAndExecuteOAuthCall(conn); final String out = this.processAndReadResponse(conn); HttpHelper.verifyReturnedCorrelationId(log, conn, @@ -88,7 +98,8 @@ HTTPResponse createResponse(final HttpURLConnection conn, final String out) try { response.setContentType(conn.getContentType()); - } catch (final ParseException e) { + } + catch (final ParseException e) { throw new IOException("Couldn't parse Content-Type header: " + e.getMessage(), e); } @@ -102,7 +113,7 @@ HTTPResponse createResponse(final HttpURLConnection conn, final String out) return response; } - void configureHeaderAndExecuteOAuthCall(final HttpURLConnection conn) + void configureHeaderAndExecuteOAuthCall(final HttpsURLConnection conn) throws IOException { if (this.getAuthorization() != null) { @@ -111,14 +122,15 @@ void configureHeaderAndExecuteOAuthCall(final HttpURLConnection conn) Map params = new java.util.HashMap<>(); if (this.extraHeaderParams != null && !this.extraHeaderParams.isEmpty()) { - for (java.util.Map.Entry entry : this.extraHeaderParams.entrySet()) { + for (java.util.Map.Entry entry : this.extraHeaderParams + .entrySet()) { if (entry.getValue() == null || entry.getValue().isEmpty()) { continue; } params.put(entry.getKey(), entry.getValue()); } } - + HttpHelper.configureAdditionalHeaders(conn, params); conn.setDoOutput(true); conn.setRequestProperty("Content-Type", @@ -139,13 +151,13 @@ String processAndReadResponse(final HttpURLConnection conn) final int responseCode = conn.getResponseCode(); if (responseCode == 200) { inReader = new InputStreamReader(conn.getInputStream()); - } else { - InputStream stream = conn.getErrorStream(); - if(stream == null && responseCode == 404) - { - stream = conn.getInputStream(); - } - + } + else { + InputStream stream = conn.getErrorStream(); + if (stream == null && responseCode == 404) { + stream = conn.getInputStream(); + } + inReader = new InputStreamReader(stream); } final BufferedReader reader = new BufferedReader(inReader); @@ -159,7 +171,8 @@ String processAndReadResponse(final HttpURLConnection conn) } out.append(buffer, 0, rsz); } - } finally { + } + finally { reader.close(); } return out.toString(); diff --git a/src/main/java/com/microsoft/aad/adal4j/AdalTokenRequest.java b/src/main/java/com/microsoft/aad/adal4j/AdalTokenRequest.java index 7e1e965b..5d5dcbf8 100644 --- a/src/main/java/com/microsoft/aad/adal4j/AdalTokenRequest.java +++ b/src/main/java/com/microsoft/aad/adal4j/AdalTokenRequest.java @@ -20,9 +20,12 @@ package com.microsoft.aad.adal4j; import java.io.IOException; +import java.net.Proxy; import java.net.URL; import java.util.Map; +import javax.net.ssl.SSLSocketFactory; + import com.nimbusds.oauth2.sdk.ParseException; import com.nimbusds.oauth2.sdk.SerializeException; import com.nimbusds.oauth2.sdk.TokenErrorResponse; @@ -42,14 +45,19 @@ class AdalTokenRequest { private final ClientAuthentication clientAuth; private final AdalAuthorizatonGrant authzGrant; private final Map headerMap; + private final Proxy proxy; + private final SSLSocketFactory sslSocketFactory; AdalTokenRequest(final URL uri, final ClientAuthentication clientAuth, final AdalAuthorizatonGrant authzGrant, - final Map headerMap) { + final Map headerMap, final Proxy proxy, + final SSLSocketFactory sslSocketFactory) { this.clientAuth = clientAuth; this.authzGrant = authzGrant; this.uri = uri; this.headerMap = headerMap; + this.proxy = proxy; + this.sslSocketFactory = sslSocketFactory; } /** @@ -86,15 +94,14 @@ AuthenticationResult executeOAuthRequestAndProcessResponse() .getJWTClaimsSet()); } - result = new AuthenticationResult( - response.getAccessToken().getType().getValue(), - response.getAccessToken().getValue(), - refreshToken, - response.getAccessToken().getLifetime(), - response.getIDTokenString(), - info, - !StringHelper.isBlank(response.getResource())); - } else { + result = new AuthenticationResult(response.getAccessToken() + .getType().getValue(), + response.getAccessToken().getValue(), refreshToken, + response.getAccessToken().getLifetime(), + response.getIDTokenString(), info, + !StringHelper.isBlank(response.getResource())); + } + else { final TokenErrorResponse errorResponse = TokenErrorResponse .parse(httpResponse); throw new AuthenticationException(errorResponse.toJSONObject() @@ -116,7 +123,8 @@ AdalOAuthRequest toOAuthRequest() throws SerializeException { } final AdalOAuthRequest httpRequest = new AdalOAuthRequest( - HTTPRequest.Method.POST, this.uri, headerMap); + HTTPRequest.Method.POST, this.uri, headerMap, this.proxy, + this.sslSocketFactory); httpRequest.setContentType(CommonContentTypes.APPLICATION_URLENCODED); final Map params = this.authzGrant.toParameters(); httpRequest.setQuery(URLUtils.serializeParameters(params)); diff --git a/src/main/java/com/microsoft/aad/adal4j/AsymmetricKeyCredential.java b/src/main/java/com/microsoft/aad/adal4j/AsymmetricKeyCredential.java index 7c96c112..11f17861 100644 --- a/src/main/java/com/microsoft/aad/adal4j/AsymmetricKeyCredential.java +++ b/src/main/java/com/microsoft/aad/adal4j/AsymmetricKeyCredential.java @@ -108,11 +108,11 @@ public String getPublicCertificateHash() * @throws CertificateEncodingException * @throws NoSuchAlgorithmException */ - public String getPublicCertificate() - throws CertificateEncodingException, NoSuchAlgorithmException { + public String getPublicCertificate() throws CertificateEncodingException, + NoSuchAlgorithmException { return Base64.encodeBase64String(this.publicCertificate.getEncoded()); } - + /** * Returns private key of the credential. * diff --git a/src/main/java/com/microsoft/aad/adal4j/AuthenticationAuthority.java b/src/main/java/com/microsoft/aad/adal4j/AuthenticationAuthority.java index 82ac4b54..a9574874 100644 --- a/src/main/java/com/microsoft/aad/adal4j/AuthenticationAuthority.java +++ b/src/main/java/com/microsoft/aad/adal4j/AuthenticationAuthority.java @@ -24,6 +24,8 @@ import java.util.Arrays; import java.util.Map; +import javax.net.ssl.SSLSocketFactory; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,7 +37,8 @@ class AuthenticationAuthority { .getLogger(AuthenticationAuthority.class); private final static String[] TRUSTED_HOST_LIST = { "login.windows.net", - "login.chinacloudapi.cn", "login.cloudgovapi.us", "login.microsoftonline.com" }; + "login.chinacloudapi.cn", "login.cloudgovapi.us", + "login.microsoftonline.com" }; private final static String TENANTLESS_TENANT_NAME = "common"; private final static String AUTHORIZE_ENDPOINT_TEMPLATE = "https://{host}/{tenant}/oauth2/authorize"; private final static String DISCOVERY_ENDPOINT = "common/discovery/instance"; @@ -63,8 +66,6 @@ class AuthenticationAuthority { private final URL authorityUrl; private final boolean validateAuthority; - private Proxy proxy; - AuthenticationAuthority(final URL authorityUrl, final boolean validateAuthority) { @@ -75,14 +76,6 @@ class AuthenticationAuthority { setupAuthorityProperties(); } - public Proxy getProxy() { - return proxy; - } - - public void setProxy(Proxy proxy) { - this.proxy = proxy; - } - String getHost() { return host; } @@ -98,11 +91,11 @@ String getAuthority() { String getTokenEndpoint() { return tokenEndpoint; } - + String getUserRealmEndpoint(String username) { return String.format(userRealmEndpointFormat, host, username); } - + AuthorityType getAuthorityType() { return authorityType; } @@ -123,7 +116,8 @@ void setSelfSignedJwtAudience(final String selfSignedJwtAudience) { this.selfSignedJwtAudience = selfSignedJwtAudience; } - void doInstanceDiscovery(final Map headers) + void doInstanceDiscovery(final Map headers, + final Proxy proxy, final SSLSocketFactory sslSocketFactory) throws Exception { // instance discovery should be executed only once per context instance. @@ -132,7 +126,9 @@ void doInstanceDiscovery(final Map headers) if (!doStaticInstanceDiscovery()) { // if authority must be validated and dynamic discovery request // as a fall back is success - if (validateAuthority && !doDynamicInstanceDiscovery(headers)) { + if (validateAuthority + && !doDynamicInstanceDiscovery(headers, proxy, + sslSocketFactory)) { throw new AuthenticationException( AuthenticationErrorMessage.AUTHORITY_NOT_IN_VALID_LIST); } @@ -144,9 +140,11 @@ void doInstanceDiscovery(final Map headers) } } - boolean doDynamicInstanceDiscovery(final Map headers) + boolean doDynamicInstanceDiscovery(final Map headers, + final Proxy proxy, final SSLSocketFactory sslSocketFactory) throws Exception { - final String json = HttpHelper.executeHttpGet(log, instanceDiscoveryEndpoint, headers, proxy); + final String json = HttpHelper.executeHttpGet(log, + instanceDiscoveryEndpoint, headers, proxy, sslSocketFactory); final InstanceDiscoveryResponse discoveryResponse = JsonHelper .convertJsonToObject(json, InstanceDiscoveryResponse.class); return !StringHelper.isBlank(discoveryResponse diff --git a/src/main/java/com/microsoft/aad/adal4j/AuthenticationCallback.java b/src/main/java/com/microsoft/aad/adal4j/AuthenticationCallback.java index 55ca4791..85d60012 100644 --- a/src/main/java/com/microsoft/aad/adal4j/AuthenticationCallback.java +++ b/src/main/java/com/microsoft/aad/adal4j/AuthenticationCallback.java @@ -19,7 +19,6 @@ ******************************************************************************/ package com.microsoft.aad.adal4j; - /** * Authentication callback Interface that can be implemented by the developer. */ diff --git a/src/main/java/com/microsoft/aad/adal4j/AuthenticationContext.java b/src/main/java/com/microsoft/aad/adal4j/AuthenticationContext.java index 8fed5869..e8cf8b00 100644 --- a/src/main/java/com/microsoft/aad/adal4j/AuthenticationContext.java +++ b/src/main/java/com/microsoft/aad/adal4j/AuthenticationContext.java @@ -32,6 +32,8 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; +import javax.net.ssl.SSLSocketFactory; + import org.apache.commons.codec.binary.Base64; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -72,6 +74,7 @@ public class AuthenticationContext { private final ExecutorService service; private final boolean validateAuthority; private Proxy proxy; + private SSLSocketFactory sslSocketFactory; /** * Constructor to create the context with the address of the authority. @@ -88,7 +91,7 @@ public class AuthenticationContext { * thrown if URL is invalid */ public AuthenticationContext(final String authority, - final boolean validateAuthority, final ExecutorService service) + final boolean validateAuthority, final ExecutorService service) throws MalformedURLException { if (StringHelper.isBlank(authority)) { @@ -106,13 +109,43 @@ public AuthenticationContext(final String authority, this.getAuthority()), this.shouldValidateAuthority()); } + /** + * Returns Proxy configuration + * + * @return Proxy Object + */ public Proxy getProxy() { return proxy; } + /** + * Sets Proxy configuration to be used by the context for all network + * communication. Default is null and system defined properties if any, + * would be used. + * + * @param proxy + * Proxy configuration object + */ public void setProxy(Proxy proxy) { this.proxy = proxy; - authenticationAuthority.setProxy(proxy); + } + + /** + * Returns SSLSocketFactory configuration object. + * + * @return SSLSocketFactory object + */ + public SSLSocketFactory getSslSocketFactory() { + return sslSocketFactory; + } + + /** + * Sets SSLSocketFactory object to be used by the context. + * + * @param sslSocketFactory + */ + public void setSslSocketFactory(SSLSocketFactory sslSocketFactory) { + this.sslSocketFactory = sslSocketFactory; } private String canonicalizeUri(String authority) { @@ -144,13 +177,15 @@ public AuthenticationResult call() throws Exception { if (callback != null) { callback.onSuccess(result); } - } catch (final Exception ex) { + } + catch (final Exception ex) { log.error(LogHelper.createMessage( "Request to acquire token failed.", this.headers.getHeaderCorrelationIdValue()), ex); if (callback != null) { callback.onFailure(ex); - } else { + } + else { throw ex; } } @@ -194,8 +229,8 @@ private Callable init( * Token, Refresh Token and the Access Token's expiration time. */ public Future acquireToken(final String resource, - final String clientId, final String username, - final String password, final AuthenticationCallback callback) { + final String clientId, final String username, + final String password, final AuthenticationCallback callback) { if (StringHelper.isBlank(resource)) { throw new IllegalArgumentException("resource is null or empty"); } @@ -213,9 +248,9 @@ public Future acquireToken(final String resource, } return this.acquireToken(new AdalAuthorizatonGrant( - new ResourceOwnerPasswordCredentialsGrant(username, new Secret( - password)), resource), new ClientAuthenticationPost( - ClientAuthenticationMethod.NONE, new ClientID(clientId)), + new ResourceOwnerPasswordCredentialsGrant(username, new Secret( + password)), resource), new ClientAuthenticationPost( + ClientAuthenticationMethod.NONE, new ClientID(clientId)), callback); } @@ -235,8 +270,8 @@ ClientAuthenticationMethod.NONE, new ClientID(clientId)), * property will be null for this overload. */ public Future acquireToken(final String resource, - final ClientAssertion credential, - final AuthenticationCallback callback) { + final ClientAssertion credential, + final AuthenticationCallback callback) { this.validateInput(resource, credential, true); final ClientAuthentication clientAuth = createClientAuthFromClientAssertion(credential); @@ -246,7 +281,7 @@ public Future acquireToken(final String resource, } private void validateInput(final String resource, final Object credential, - final boolean validateResource) { + final boolean validateResource) { if (validateResource && StringHelper.isBlank(resource)) { throw new IllegalArgumentException("resource is null or empty"); } @@ -275,8 +310,8 @@ private void validateInput(final String resource, final Object credential, * @throws AuthenticationException */ public Future acquireToken(final String resource, - final ClientAssertion assertion, final ClientCredential credential, - final AuthenticationCallback callback) { + final ClientAssertion assertion, final ClientCredential credential, + final AuthenticationCallback callback) { this.validateInput(resource, credential, true); Map params = new HashMap(); @@ -289,9 +324,10 @@ public Future acquireToken(final String resource, final ClientAuthentication clientAuth = new ClientSecretPost( new ClientID(credential.getClientId()), new Secret( - credential.getClientSecret())); + credential.getClientSecret())); return this.acquireToken(grant, clientAuth, callback); - } catch (final Exception e) { + } + catch (final Exception e) { throw new AuthenticationException(e); } } @@ -312,12 +348,12 @@ public Future acquireToken(final String resource, * property will be null for this overload. */ public Future acquireToken(final String resource, - final ClientCredential credential, - final AuthenticationCallback callback) { + final ClientCredential credential, + final AuthenticationCallback callback) { this.validateInput(resource, credential, true); final ClientAuthentication clientAuth = new ClientSecretPost( new ClientID(credential.getClientId()), new Secret( - credential.getClientSecret())); + credential.getClientSecret())); final AdalAuthorizatonGrant authGrant = new AdalAuthorizatonGrant( new ClientCredentialsGrant(), resource); return this.acquireToken(authGrant, clientAuth, callback); @@ -340,11 +376,11 @@ public Future acquireToken(final String resource, * @throws AuthenticationException */ public Future acquireToken(final String resource, - final AsymmetricKeyCredential credential, - final AuthenticationCallback callback) + final AsymmetricKeyCredential credential, + final AuthenticationCallback callback) throws AuthenticationException { return this.acquireToken(resource, JwtHelper.buildJwt(credential, - this.authenticationAuthority.getSelfSignedJwtAudience()), + this.authenticationAuthority.getSelfSignedJwtAudience()), callback); } @@ -500,7 +536,7 @@ public Future acquireTokenByAuthorizationCode( credential, resource); final ClientAuthentication clientAuth = new ClientSecretPost( new ClientID(credential.getClientId()), new Secret( - credential.getClientSecret())); + credential.getClientSecret())); final AdalAuthorizatonGrant authGrant = new AdalAuthorizatonGrant( new AuthorizationCodeGrant(new AuthorizationCode( authorizationCode), redirectUri), resource); @@ -682,7 +718,7 @@ public Future acquireTokenByRefreshToken( final ClientAuthentication clientAuth = new ClientSecretPost( new ClientID(credential.getClientId()), new Secret( - credential.getClientSecret())); + credential.getClientSecret())); final AdalAuthorizatonGrant authGrant = new AdalAuthorizatonGrant( new RefreshTokenGrant(new RefreshToken(refreshToken)), resource); return this.acquireToken(authGrant, clientAuth, callback); @@ -753,7 +789,7 @@ public Future acquireTokenByRefreshToken( } private void validateRefreshTokenRequestInput(final String refreshToken, - final String clientId, final Object credential) { + final String clientId, final Object credential) { if (StringHelper.isBlank(refreshToken)) { throw new IllegalArgumentException("refreshToken is null or empty"); @@ -772,11 +808,13 @@ private AuthenticationResult acquireTokenCommon( log.debug(LogHelper.createMessage( String.format("Using Client Http Headers: %s", headers), headers.getHeaderCorrelationIdValue())); - this.authenticationAuthority.doInstanceDiscovery(headers - .getReadonlyHeaderMap()); + this.authenticationAuthority.doInstanceDiscovery( + headers.getReadonlyHeaderMap(), this.proxy, + this.sslSocketFactory); final URL url = new URL(this.authenticationAuthority.getTokenUri()); final AdalTokenRequest request = new AdalTokenRequest(url, clientAuth, - authGrant, headers.getReadonlyHeaderMap()); + authGrant, headers.getReadonlyHeaderMap(), this.proxy, + this.sslSocketFactory); AuthenticationResult result = request .executeOAuthRequestAndProcessResponse(); return result; @@ -788,26 +826,30 @@ private AuthenticationResult acquireTokenCommon( */ private AdalAuthorizatonGrant processPasswordGrant( AdalAuthorizatonGrant authGrant) throws Exception { + if (!(authGrant.getAuthorizationGrant() instanceof ResourceOwnerPasswordCredentialsGrant)) { return authGrant; } - + ResourceOwnerPasswordCredentialsGrant grant = (ResourceOwnerPasswordCredentialsGrant) authGrant .getAuthorizationGrant(); - UserDiscoveryResponse discoveryResponse = - UserDiscoveryRequest.execute(this.authenticationAuthority.getUserRealmEndpoint(grant.getUsername()), proxy); + UserDiscoveryResponse discoveryResponse = UserDiscoveryRequest.execute( + this.authenticationAuthority.getUserRealmEndpoint(grant + .getUsername()), this.proxy, this.sslSocketFactory); if (discoveryResponse.isAccountFederated()) { - WSTrustResponse response = WSTrustRequest.execute(discoveryResponse - .getFederationMetadataUrl(), grant.getUsername(), - grant.getPassword().getValue(), proxy); + WSTrustResponse response = WSTrustRequest.execute( + discoveryResponse.getFederationMetadataUrl(), + grant.getUsername(), grant.getPassword().getValue(), + this.proxy, this.sslSocketFactory); AuthorizationGrant updatedGrant = null; if (response.isTokenSaml2()) { updatedGrant = new SAML2BearerGrant(new Base64URL( Base64.encodeBase64String(response.getToken().getBytes( "UTF-8")))); - } else { + } + else { updatedGrant = new SAML11BearerGrant(new Base64URL( Base64.encodeBase64String(response.getToken() .getBytes()))); @@ -821,7 +863,7 @@ private AdalAuthorizatonGrant processPasswordGrant( } private void logResult(AuthenticationResult result, - ClientDataHttpHeaders headers) throws NoSuchAlgorithmException, + ClientDataHttpHeaders headers) throws NoSuchAlgorithmException, UnsupportedEncodingException { if (!StringHelper.isBlank(result.getAccessToken())) { String logMessage = ""; @@ -833,7 +875,8 @@ private void logResult(AuthenticationResult result, logMessage = String .format("Access Token with hash '%s' and Refresh Token with hash '%s' returned", accessTokenHash, refreshTokenHash); - } else { + } + else { logMessage = String .format("Access Token with hash '%s' returned", accessTokenHash); @@ -860,7 +903,8 @@ private ClientAuthentication createClientAuthFromClientAssertion( JWTAuthentication.CLIENT_ASSERTION_TYPE); map.put("client_assertion", credential.getAssertion()); return PrivateKeyJWT.parse(map); - } catch (final ParseException e) { + } + catch (final ParseException e) { throw new AuthenticationException(e); } } @@ -907,8 +951,8 @@ public String getAuthority() { } private void validateAuthCodeRequestInput(final String authorizationCode, - final URI redirectUri, final Object credential, - final String resource) { + final URI redirectUri, final Object credential, + final String resource) { if (StringHelper.isBlank(authorizationCode)) { throw new IllegalArgumentException( "authorization code is null or empty"); diff --git a/src/main/java/com/microsoft/aad/adal4j/AuthenticationResult.java b/src/main/java/com/microsoft/aad/adal4j/AuthenticationResult.java index 35d83185..63cb42ad 100644 --- a/src/main/java/com/microsoft/aad/adal4j/AuthenticationResult.java +++ b/src/main/java/com/microsoft/aad/adal4j/AuthenticationResult.java @@ -85,7 +85,7 @@ public Date getExpiresOnDate() { public String getIdToken() { return idToken; } - + public UserInfo getUserInfo() { return userInfo; } diff --git a/src/main/java/com/microsoft/aad/adal4j/BindingPolicy.java b/src/main/java/com/microsoft/aad/adal4j/BindingPolicy.java index e9a03477..7d341cb4 100644 --- a/src/main/java/com/microsoft/aad/adal4j/BindingPolicy.java +++ b/src/main/java/com/microsoft/aad/adal4j/BindingPolicy.java @@ -24,11 +24,11 @@ class BindingPolicy { private String value; private String url; private WSTrustVersion version; - + public BindingPolicy(String value) { this.value = value; } - + public BindingPolicy(String url, WSTrustVersion version) { this.url = url; this.version = version; @@ -48,13 +48,13 @@ public String getUrl() { public void setUrl(String url) { this.url = url; - } - - public void setVersion(WSTrustVersion version){ - this.version = version; } - public WSTrustVersion getVersion(){ + public void setVersion(WSTrustVersion version) { + this.version = version; + } + + public WSTrustVersion getVersion() { return this.version; } } \ No newline at end of file diff --git a/src/main/java/com/microsoft/aad/adal4j/ClientAssertion.java b/src/main/java/com/microsoft/aad/adal4j/ClientAssertion.java index 97821216..f9455508 100644 --- a/src/main/java/com/microsoft/aad/adal4j/ClientAssertion.java +++ b/src/main/java/com/microsoft/aad/adal4j/ClientAssertion.java @@ -19,15 +19,14 @@ ******************************************************************************/ package com.microsoft.aad.adal4j; - /*** * Credential type containing an assertion of type * "urn:ietf:params:oauth:token-type:jwt". */ public final class ClientAssertion { - + private final String assertion; - + /** * Constructor to create credential with a jwt token encoded as a base64 url * encoded string. diff --git a/src/main/java/com/microsoft/aad/adal4j/ClientDataHttpHeaders.java b/src/main/java/com/microsoft/aad/adal4j/ClientDataHttpHeaders.java index 73f27672..9af56aee 100644 --- a/src/main/java/com/microsoft/aad/adal4j/ClientDataHttpHeaders.java +++ b/src/main/java/com/microsoft/aad/adal4j/ClientDataHttpHeaders.java @@ -52,7 +52,8 @@ final class ClientDataHttpHeaders { ClientDataHttpHeaders(final String correlationId) { if (!StringHelper.isBlank(correlationId)) { this.correlationIdHeaderValue = correlationId; - } else { + } + else { this.correlationIdHeaderValue = UUID.randomUUID().toString(); } this.headerValues = initHeaderMap(); diff --git a/src/main/java/com/microsoft/aad/adal4j/HttpHelper.java b/src/main/java/com/microsoft/aad/adal4j/HttpHelper.java index d2d315b3..79d72ab1 100644 --- a/src/main/java/com/microsoft/aad/adal4j/HttpHelper.java +++ b/src/main/java/com/microsoft/aad/adal4j/HttpHelper.java @@ -24,64 +24,49 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; -import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.Proxy; import java.net.URL; import java.util.Map; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLSocketFactory; + import org.slf4j.Logger; class HttpHelper { - static String executeHttpGet(final Logger log, final String url) - throws Exception { - return executeHttpGet(log, url, null, null); - } - - static String executeHttpGet(final Logger log, final String url, final Proxy proxy) - throws Exception { - return executeHttpGet(log, url, null, proxy); - } - static String executeHttpGet(final Logger log, final String url, - final Map headers) throws Exception { - final HttpURLConnection conn = HttpHelper.openConnection(url); - return executeGetRequest(log, headers, conn); + final Proxy proxy, final SSLSocketFactory sslSocketFactory) + throws Exception { + return executeHttpGet(log, url, null, proxy, sslSocketFactory); } static String executeHttpGet(final Logger log, final String url, - final Map headers, final Proxy proxy) throws Exception { - final HttpURLConnection conn = - proxy == null ? - HttpHelper.openConnection(url) : - HttpHelper.openConnection(url, proxy); + final Map headers, final Proxy proxy, + final SSLSocketFactory sslSocketFactory) throws Exception { + final HttpsURLConnection conn = HttpHelper.openConnection(url, proxy, + sslSocketFactory); return executeGetRequest(log, headers, conn); } static String executeHttpPost(final Logger log, final String url, - String postData) throws Exception { - return executeHttpPost(log, url, postData, null); - } - - static String executeHttpPost(final Logger log, final String url, - String postData, final Map headers) - throws Exception { - final HttpURLConnection conn = HttpHelper.openConnection(url); - return executePostRequest(log, postData, headers, conn); + String postData, final Proxy proxy, + final SSLSocketFactory sslSocketFactory) throws Exception { + return executeHttpPost(log, url, postData, null, proxy, + sslSocketFactory); } static String executeHttpPost(final Logger log, final String url, - String postData, final Map headers, final Proxy proxy) + String postData, final Map headers, + final Proxy proxy, final SSLSocketFactory sslSocketFactory) throws Exception { - final HttpURLConnection conn = - proxy == null ? - HttpHelper.openConnection(url) : - HttpHelper.openConnection(url, proxy); + final HttpsURLConnection conn = HttpHelper.openConnection(url, proxy, + sslSocketFactory); return executePostRequest(log, postData, headers, conn); } - static String readResponseFromConnection(final HttpURLConnection conn) + static String readResponseFromConnection(final HttpsURLConnection conn) throws IOException { final Reader inReader = new InputStreamReader(conn.getInputStream()); final BufferedReader reader = new BufferedReader(inReader); @@ -97,35 +82,40 @@ static String readResponseFromConnection(final HttpURLConnection conn) while ((rsz = reader.read(buffer, 0, buffer.length)) > -1) { out.append(buffer, 0, rsz); } - } finally { + } + finally { reader.close(); } return out.toString(); } - static HttpURLConnection openConnection(final URL finalURL, final Proxy proxy) - throws IOException { - return (HttpURLConnection) finalURL.openConnection(proxy); - } - - static HttpURLConnection openConnection(final URL finalURL) + static HttpsURLConnection openConnection(final URL finalURL, + final Proxy proxy, final SSLSocketFactory sslSocketFactory) throws IOException { - return (HttpURLConnection) finalURL.openConnection(); - } + HttpsURLConnection connection = null; + if (proxy != null) { + connection = (HttpsURLConnection) finalURL.openConnection(proxy); + } + else { + connection = (HttpsURLConnection) finalURL.openConnection(); + } - static HttpURLConnection openConnection(final String url, final Proxy proxy) - throws IOException { - return openConnection(new URL(url), proxy); + if (sslSocketFactory != null) { + connection.setSSLSocketFactory(sslSocketFactory); + } + + return connection; } - static HttpURLConnection openConnection(final String url) + static HttpsURLConnection openConnection(final String url, + final Proxy proxy, final SSLSocketFactory sslSocketFactory) throws IOException { - return openConnection(new URL(url)); + return openConnection(new URL(url), proxy, sslSocketFactory); } - static HttpURLConnection configureAdditionalHeaders( - final HttpURLConnection conn, final Map headers) + static HttpsURLConnection configureAdditionalHeaders( + final HttpsURLConnection conn, final Map headers) throws MalformedURLException, IOException { if (headers != null) { for (final String key : headers.keySet()) { @@ -135,8 +125,8 @@ static HttpURLConnection configureAdditionalHeaders( return conn; } - static void verifyReturnedCorrelationId(Logger log, HttpURLConnection conn, - String sentCorrelationId) { + static void verifyReturnedCorrelationId(Logger log, + HttpsURLConnection conn, String sentCorrelationId) { if (StringHelper .isBlank(conn .getHeaderField(ClientDataHttpHeaders.CORRELATION_ID_HEADER_NAME)) @@ -152,14 +142,16 @@ static void verifyReturnedCorrelationId(Logger log, HttpURLConnection conn, } } - private static String executeGetRequest(Logger log, Map headers, HttpURLConnection conn) + private static String executeGetRequest(Logger log, + Map headers, HttpsURLConnection conn) throws IOException { configureAdditionalHeaders(conn, headers); return getResponse(log, headers, conn); } - private static String executePostRequest(Logger log, String postData, Map headers, - HttpURLConnection conn) throws IOException { + private static String executePostRequest(Logger log, String postData, + Map headers, HttpsURLConnection conn) + throws IOException { configureAdditionalHeaders(conn, headers); conn.setRequestMethod("POST"); conn.setDoOutput(true); @@ -170,15 +162,16 @@ private static String executePostRequest(Logger log, String postData, Map headers, HttpURLConnection conn) - throws IOException { + private static String getResponse(Logger log, Map headers, + HttpsURLConnection conn) throws IOException { String response = readResponseFromConnection(conn); if (headers != null) { HttpHelper.verifyReturnedCorrelationId(log, conn, headers diff --git a/src/main/java/com/microsoft/aad/adal4j/JsonHelper.java b/src/main/java/com/microsoft/aad/adal4j/JsonHelper.java index 0a68d596..5cc7654b 100644 --- a/src/main/java/com/microsoft/aad/adal4j/JsonHelper.java +++ b/src/main/java/com/microsoft/aad/adal4j/JsonHelper.java @@ -28,15 +28,14 @@ /** * */ - class JsonHelper { +class JsonHelper { /** * * @param json * @param clazz * @return */ - static T convertJsonToObject(final String json, - final Class clazz) { + static T convertJsonToObject(final String json, final Class clazz) { final Reader reader = new StringReader(json); final Gson gson = new GsonBuilder().create(); return gson.fromJson(reader, clazz); diff --git a/src/main/java/com/microsoft/aad/adal4j/JwtHelper.java b/src/main/java/com/microsoft/aad/adal4j/JwtHelper.java index 12a6b8fd..e1d8d970 100644 --- a/src/main/java/com/microsoft/aad/adal4j/JwtHelper.java +++ b/src/main/java/com/microsoft/aad/adal4j/JwtHelper.java @@ -70,13 +70,14 @@ static ClientAssertion buildJwt(final AsymmetricKeyCredential credential, certs.add(new Base64(credential.getPublicCertificate())); builder.x509CertChain(certs); builder.x509CertThumbprint(new Base64URL(credential - .getPublicCertificateHash())); + .getPublicCertificateHash())); jwt = new SignedJWT(builder.build(), claimsSet); final RSASSASigner signer = new RSASSASigner( (RSAPrivateKey) credential.getKey()); jwt.sign(signer); - } catch (final Exception e) { + } + catch (final Exception e) { throw new AuthenticationException(e); } diff --git a/src/main/java/com/microsoft/aad/adal4j/MexParser.java b/src/main/java/com/microsoft/aad/adal4j/MexParser.java index 1712e704..2dd2c563 100644 --- a/src/main/java/com/microsoft/aad/adal4j/MexParser.java +++ b/src/main/java/com/microsoft/aad/adal4j/MexParser.java @@ -27,6 +27,7 @@ import java.util.Map; import java.util.Map.Entry; +import javax.net.ssl.SSLSocketFactory; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.xpath.XPath; @@ -67,26 +68,31 @@ static BindingPolicy getWsTrustEndpointFromMexResponse(String mexResponse) NamespaceContextImpl nameSpace = new NamespaceContextImpl(); xPath.setNamespaceContext(nameSpace); String xpathExpression = "//wsdl:definitions/wsp:Policy/wsp:ExactlyOne/wsp:All/" - + "sp:SignedEncryptedSupportingTokens/wsp:Policy/sp:UsernameToken/" - + "wsp:Policy/sp:WssUsernameToken10"; - Map policies = selectUsernamePasswordPoliciesWithExpression(xmlDocument, xPath, xpathExpression); - nameSpace.modifyNameSpace("sp", "http://schemas.xmlsoap.org/ws/2005/07/securitypolicy"); + + "sp:SignedEncryptedSupportingTokens/wsp:Policy/sp:UsernameToken/" + + "wsp:Policy/sp:WssUsernameToken10"; + Map policies = selectUsernamePasswordPoliciesWithExpression( + xmlDocument, xPath, xpathExpression); + nameSpace.modifyNameSpace("sp", + "http://schemas.xmlsoap.org/ws/2005/07/securitypolicy"); xpathExpression = "//wsdl:definitions/wsp:Policy/wsp:ExactlyOne/wsp:All/" + "sp:SignedSupportingTokens/wsp:Policy/sp:UsernameToken/" + "wsp:Policy/sp:WssUsernameToken10"; - policies.putAll(selectUsernamePasswordPoliciesWithExpression(xmlDocument, xPath, xpathExpression)); - + policies.putAll(selectUsernamePasswordPoliciesWithExpression( + xmlDocument, xPath, xpathExpression)); + if (policies.isEmpty()) { log.debug("No matching policies"); return null; - } else { - Map bindings = getMatchingBindings(xmlDocument, - xPath, policies); + } + else { + Map bindings = getMatchingBindings( + xmlDocument, xPath, policies); if (bindings.isEmpty()) { log.debug("No matching bindings"); return null; - } else { + } + else { getPortsForPolicyBindings(xmlDocument, xPath, bindings, policies); return selectSingleMatchingPolicy(policies); @@ -94,9 +100,11 @@ static BindingPolicy getWsTrustEndpointFromMexResponse(String mexResponse) } } - static BindingPolicy getWsTrustEndpointFromMexEndpoint(String metadataEndpoint, Proxy proxy) - throws Exception { - String mexResponse = HttpHelper.executeHttpGet(log, metadataEndpoint, proxy); + static BindingPolicy getWsTrustEndpointFromMexEndpoint( + String metadataEndpoint, Proxy proxy, + SSLSocketFactory sslSocketFactory) throws Exception { + String mexResponse = HttpHelper.executeHttpGet(log, metadataEndpoint, + proxy, sslSocketFactory); return getWsTrustEndpointFromMexResponse(mexResponse); } @@ -106,13 +114,15 @@ private static BindingPolicy selectSingleMatchingPolicy( BindingPolicy wstrust13 = null, wstrust2005 = null; // Select wstrust13 first if wstrust13 available - Iterator> it = policies.entrySet().iterator(); + Iterator> it = policies.entrySet() + .iterator(); while (it.hasNext()) { Map.Entry pair = it.next(); if (pair.getValue().getUrl() != null) { - if(pair.getValue().getVersion() == WSTrustVersion.WSTRUST13){ + if (pair.getValue().getVersion() == WSTrustVersion.WSTRUST13) { wstrust13 = pair.getValue(); - } else if(pair.getValue().getVersion() == WSTrustVersion.WSTRUST2005){ + } + else if (pair.getValue().getVersion() == WSTrustVersion.WSTRUST2005) { wstrust2005 = pair.getValue(); } } @@ -126,8 +136,8 @@ private static BindingPolicy selectSingleMatchingPolicy( return wstrust13 != null ? wstrust13 : wstrust2005; } - private static void getPortsForPolicyBindings( - Document xmlDocument, XPath xPath, Map bindings, + private static void getPortsForPolicyBindings(Document xmlDocument, + XPath xPath, Map bindings, Map policies) throws Exception { NodeList portNodes = (NodeList) xPath.compile(PORT_XPATH).evaluate( @@ -135,7 +145,8 @@ private static void getPortsForPolicyBindings( if (portNodes.getLength() == 0) { log.warn("no ports found"); - } else { + } + else { for (int i = 0; i < portNodes.getLength(); i++) { Node portNode = portNodes.item(i); String bindingId = portNode.getAttributes() @@ -143,8 +154,9 @@ private static void getPortsForPolicyBindings( String[] bindingIdParts = bindingId.split(":"); bindingId = bindingIdParts[bindingIdParts.length - 1]; BindingPolicy trustPolicy = bindings.get(bindingId); - if(trustPolicy != null) { - BindingPolicy bindingPolicy = policies.get(trustPolicy.getUrl()); + if (trustPolicy != null) { + BindingPolicy bindingPolicy = policies.get(trustPolicy + .getUrl()); if (bindingPolicy != null && StringHelper.isBlank(bindingPolicy.getUrl())) { bindingPolicy.setVersion(trustPolicy.getVersion()); @@ -152,14 +164,19 @@ private static void getPortsForPolicyBindings( ADDRESS_XPATH).evaluate(portNode, XPathConstants.NODESET); if (addressNodes.getLength() > 0) { - String address = addressNodes.item(0).getTextContent(); + String address = addressNodes.item(0) + .getTextContent(); if (address != null - && address.toLowerCase().startsWith("https://")) { + && address.toLowerCase().startsWith( + "https://")) { bindingPolicy.setUrl(address.trim()); - } else { - log.warn("skipping insecure endpoint: " + address); } - } else { + else { + log.warn("skipping insecure endpoint: " + + address); + } + } + else { throw new Exception("no address nodes on port"); } } @@ -185,7 +202,8 @@ private static Map getMatchingBindings( String bindingName = bindingNode.getAttributes() .getNamedItem("name").getNodeValue(); - WSTrustVersion version = checkSoapActionAndTransport(xPath, bindingNode); + WSTrustVersion version = checkSoapActionAndTransport(xPath, + bindingNode); if (version != WSTrustVersion.UNDEFINED) { BindingPolicy policy = new BindingPolicy(""); policy.setUrl(uri); @@ -198,10 +216,9 @@ private static Map getMatchingBindings( } private static WSTrustVersion checkSoapActionAndTransport(XPath xPath, - Node bindingNode) throws XPathExpressionException { + Node bindingNode) throws XPathExpressionException { NodeList soapTransportAttributes = null; String soapAction = null; - String soapTransport = null; String bindingName = bindingNode.getAttributes().getNamedItem("name") .getNodeValue(); NodeList soapActionAttributes = (NodeList) xPath.compile( @@ -215,13 +232,14 @@ private static WSTrustVersion checkSoapActionAndTransport(XPath xPath, if (soapTransportAttributes != null && soapTransportAttributes.getLength() > 0 && soapTransportAttributes.item(0).getNodeValue() - .equalsIgnoreCase(SOAP_HTTP_TRANSPORT_VALUE)) { + .equalsIgnoreCase(SOAP_HTTP_TRANSPORT_VALUE)) { if (soapAction.equalsIgnoreCase(RST_SOAP_ACTION)) { log.debug("Found binding matching Action and Transport: " + bindingName); return WSTrustVersion.WSTRUST13; - } else if (soapAction.equalsIgnoreCase(RST_SOAP_ACTION_2005)) { + } + else if (soapAction.equalsIgnoreCase(RST_SOAP_ACTION_2005)) { log.debug("Binding node did not match soap Action or Transport: " + bindingName); return WSTrustVersion.WSTRUST2005; @@ -233,8 +251,9 @@ private static WSTrustVersion checkSoapActionAndTransport(XPath xPath, } private static Map selectUsernamePasswordPoliciesWithExpression( - Document xmlDocument, XPath xPath, String xpathExpression) throws XPathExpressionException { - + Document xmlDocument, XPath xPath, String xpathExpression) + throws XPathExpressionException { + Map policies = new HashMap(); NodeList nodeList = (NodeList) xPath.compile(xpathExpression).evaluate( @@ -274,7 +293,8 @@ private static String checkPolicy(XPath xPath, Node node) if (transportBindingNodes.getLength() > 0 && id != null) { policyId = id.getNodeValue(); log.debug("found matching policy id: " + policyId); - } else { + } + else { log.debug("potential policy did not match required transport binding: " + id.getNodeValue()); } diff --git a/src/main/java/com/microsoft/aad/adal4j/NamespaceContextImpl.java b/src/main/java/com/microsoft/aad/adal4j/NamespaceContextImpl.java index c05abe0d..df7e70eb 100644 --- a/src/main/java/com/microsoft/aad/adal4j/NamespaceContextImpl.java +++ b/src/main/java/com/microsoft/aad/adal4j/NamespaceContextImpl.java @@ -47,13 +47,15 @@ public class NamespaceContextImpl implements NamespaceContext { PREF_MAP.put("wst", "http://docs.oasis-open.org/ws-sx/ws-trust/200512"); PREF_MAP.put("t", "http://schemas.xmlsoap.org/ws/2005/02/trust"); PREF_MAP.put("a", "http://www.w3.org/2005/08/addressing"); - PREF_MAP.put("q", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"); + PREF_MAP.put( + "q", + "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"); } - public void modifyNameSpace(String key, String value){ + public void modifyNameSpace(String key, String value) { PREF_MAP.put(key, value); } - + public String getNamespaceURI(String prefix) { return PREF_MAP.get(prefix); } diff --git a/src/main/java/com/microsoft/aad/adal4j/SAML11BearerGrant.java b/src/main/java/com/microsoft/aad/adal4j/SAML11BearerGrant.java index 18f90634..dfdc69fb 100644 --- a/src/main/java/com/microsoft/aad/adal4j/SAML11BearerGrant.java +++ b/src/main/java/com/microsoft/aad/adal4j/SAML11BearerGrant.java @@ -30,17 +30,18 @@ class SAML11BearerGrant extends SAML2BearerGrant { /** * The grant type. */ - public static GrantType grantType = new GrantType("urn:ietf:params:oauth:grant-type:saml1_1-bearer"); - + public static GrantType grantType = new GrantType( + "urn:ietf:params:oauth:grant-type:saml1_1-bearer"); + public SAML11BearerGrant(Base64URL assertion) { super(assertion); } @Override - public Map toParameters() { + public Map toParameters() { - Map params = super.toParameters(); - params.put("grant_type", grantType.getValue()); - return params; + Map params = super.toParameters(); + params.put("grant_type", grantType.getValue()); + return params; } } diff --git a/src/main/java/com/microsoft/aad/adal4j/UserDiscoveryRequest.java b/src/main/java/com/microsoft/aad/adal4j/UserDiscoveryRequest.java index 5ea6d7f2..b091274f 100644 --- a/src/main/java/com/microsoft/aad/adal4j/UserDiscoveryRequest.java +++ b/src/main/java/com/microsoft/aad/adal4j/UserDiscoveryRequest.java @@ -23,6 +23,8 @@ import java.util.HashMap; import java.util.Map; +import javax.net.ssl.SSLSocketFactory; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,13 +33,18 @@ class UserDiscoveryRequest { private final static Logger log = LoggerFactory .getLogger(UserDiscoveryRequest.class); private final static Map HEADERS; - static{ + static { HEADERS = new HashMap(); HEADERS.put("Accept", "application/json, text/javascript, */*"); - + } - static UserDiscoveryResponse execute(final String uri, final Proxy proxy) throws Exception { - String response = HttpHelper.executeHttpGet(log, uri, HEADERS, proxy); - return JsonHelper.convertJsonToObject(response, UserDiscoveryResponse.class); + + static UserDiscoveryResponse execute(final String uri, final Proxy proxy, + final SSLSocketFactory sslSocketFactory) throws Exception { + + String response = HttpHelper.executeHttpGet(log, uri, HEADERS, proxy, + sslSocketFactory); + return JsonHelper.convertJsonToObject(response, + UserDiscoveryResponse.class); } } diff --git a/src/main/java/com/microsoft/aad/adal4j/UserDiscoveryResponse.java b/src/main/java/com/microsoft/aad/adal4j/UserDiscoveryResponse.java index f9406585..6ef94194 100644 --- a/src/main/java/com/microsoft/aad/adal4j/UserDiscoveryResponse.java +++ b/src/main/java/com/microsoft/aad/adal4j/UserDiscoveryResponse.java @@ -22,7 +22,7 @@ import com.google.gson.annotations.SerializedName; class UserDiscoveryResponse { - + @SerializedName("ver") private float version; @@ -34,28 +34,28 @@ class UserDiscoveryResponse { @SerializedName("federation_protocol") private String federationProtocol; - + @SerializedName("federation_active_auth_url") private String federationActiveAuthUrl; - - + float getVersion() { return version; } - - boolean isAccountFederated(){ - return !StringHelper.isBlank(this.accountType) && this.accountType.equalsIgnoreCase("Federated"); + + boolean isAccountFederated() { + return !StringHelper.isBlank(this.accountType) + && this.accountType.equalsIgnoreCase("Federated"); } - - String getFederationProtocol(){ + + String getFederationProtocol() { return federationProtocol; } - String getFederationMetadataUrl(){ + String getFederationMetadataUrl() { return federationMetadataUrl; } - String getFederationActiveAuthUrl(){ + String getFederationActiveAuthUrl() { return federationActiveAuthUrl; } } diff --git a/src/main/java/com/microsoft/aad/adal4j/UserInfo.java b/src/main/java/com/microsoft/aad/adal4j/UserInfo.java index a5e4af5b..2970e28d 100644 --- a/src/main/java/com/microsoft/aad/adal4j/UserInfo.java +++ b/src/main/java/com/microsoft/aad/adal4j/UserInfo.java @@ -105,7 +105,8 @@ static UserInfo createFromIdTokenClaims(final ReadOnlyJWTClaimsSet claims) .getStringClaim(AuthenticationConstants.ID_TOKEN_OBJECT_ID))) { uniqueId = claims .getStringClaim(AuthenticationConstants.ID_TOKEN_OBJECT_ID); - } else if (!StringHelper.isBlank(claims + } + else if (!StringHelper.isBlank(claims .getStringClaim(AuthenticationConstants.ID_TOKEN_SUBJECT))) { uniqueId = claims .getStringClaim(AuthenticationConstants.ID_TOKEN_SUBJECT); @@ -115,7 +116,8 @@ static UserInfo createFromIdTokenClaims(final ReadOnlyJWTClaimsSet claims) .getStringClaim(AuthenticationConstants.ID_TOKEN_UPN))) { displayableId = claims .getStringClaim(AuthenticationConstants.ID_TOKEN_UPN); - } else if (!StringHelper.isBlank(claims + } + else if (!StringHelper.isBlank(claims .getStringClaim(AuthenticationConstants.ID_TOKEN_EMAIL))) { displayableId = claims .getStringClaim(AuthenticationConstants.ID_TOKEN_EMAIL); diff --git a/src/main/java/com/microsoft/aad/adal4j/WSTrustRequest.java b/src/main/java/com/microsoft/aad/adal4j/WSTrustRequest.java index 5594fce7..bd22acfa 100644 --- a/src/main/java/com/microsoft/aad/adal4j/WSTrustRequest.java +++ b/src/main/java/com/microsoft/aad/adal4j/WSTrustRequest.java @@ -29,6 +29,8 @@ import java.util.TimeZone; import java.util.UUID; +import javax.net.ssl.SSLSocketFactory; + import org.apache.commons.lang3.StringEscapeUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,26 +41,35 @@ class WSTrustRequest { .getLogger(WSTrustRequest.class); private final static int MAX_EXPECTED_MESSAGE_SIZE = 1024; - private final static String DEFAULT_APPLIES_TO = "urn:federation:MicrosoftOnline"; - - static WSTrustResponse execute(String url, String username, String password, Proxy proxy) + private final static String DEFAULT_APPLIES_TO = "urn:federation:MicrosoftOnline"; + + static WSTrustResponse execute(String url, String username, + String password, Proxy proxy, SSLSocketFactory sslSocketFactory) throws Exception { + Map headers = new HashMap(); headers.put("Content-Type", "application/soap+xml; charset=utf-8"); - - BindingPolicy policy = MexParser.getWsTrustEndpointFromMexEndpoint(url, proxy); - String soapAction = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue"; // default value (WSTrust 1.3) - - // only change it if version is wsTrust2005, otherwise default to wsTrust13 - if (policy.getVersion() == WSTrustVersion.WSTRUST2005) - { - soapAction = "http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue"; // wsTrust2005 soap value + BindingPolicy policy = MexParser.getWsTrustEndpointFromMexEndpoint(url, + proxy, sslSocketFactory); + String soapAction = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue"; // default + // value + // (WSTrust + // 1.3) + + // only change it if version is wsTrust2005, otherwise default to + // wsTrust13 + if (policy.getVersion() == WSTrustVersion.WSTRUST2005) { + soapAction = "http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue"; // wsTrust2005 + // soap + // value } headers.put("SOAPAction", soapAction); - String body = buildMessage(policy.getUrl(), username, password, policy.getVersion()).toString(); - String response = HttpHelper.executeHttpPost(log, policy.getUrl(), body, headers, proxy); + String body = buildMessage(policy.getUrl(), username, password, + policy.getVersion()).toString(); + String response = HttpHelper.executeHttpPost(log, policy.getUrl(), + body, headers, proxy, sslSocketFactory); return WSTrustResponse.parse(response, policy.getVersion()); } @@ -67,83 +78,95 @@ private static StringBuilder buildMessage(String address, String username, StringBuilder securityHeaderBuilder = new StringBuilder( MAX_EXPECTED_MESSAGE_SIZE); - buildSecurityHeader(securityHeaderBuilder, username, password, addressVersion); + buildSecurityHeader(securityHeaderBuilder, username, password, + addressVersion); String guid = UUID.randomUUID().toString(); StringBuilder messageBuilder = new StringBuilder( MAX_EXPECTED_MESSAGE_SIZE); - + String schemaLocation = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"; String soapAction = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue"; String rstTrustNamespace = "http://docs.oasis-open.org/ws-sx/ws-trust/200512"; String keyType = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/Bearer"; String requestType = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue"; - - if (addressVersion == WSTrustVersion.WSTRUST2005) - { + + if (addressVersion == WSTrustVersion.WSTRUST2005) { soapAction = "http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue"; rstTrustNamespace = "http://schemas.xmlsoap.org/ws/2005/02/trust"; keyType = "http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey"; requestType = "http://schemas.xmlsoap.org/ws/2005/02/trust/Issue"; } - // Example WSTrust 1.3 request - // - // - // http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue - // https://corp.sts.microsoft.com:443/adfs/services/trust/2005/windowstransport - // 1303795308--2011-04-26T05:21:50Z2011-04-26T05:26:50Z--http://schemas.xmlsoap.org/ws/2005/02/trust/Issue--urn:federation:MicrosoftOnlinehttp://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey + // + // + // http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue + // https://corp.sts.microsoft.com:443/adfs/services/trust/2005/windowstransport + // 1303795308--2011-04-26T05:21:50Z2011-04-26T05:26:50Z--http://schemas.xmlsoap.org/ws/2005/02/trust/Issue--urn:federation:MicrosoftOnlinehttp://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey messageBuilder .append(String .format("" - + "" - + "%s" - + "urn:uuid:" - + "%s" - + // guid - "" - + "" - + "http://www.w3.org/2005/08/addressing/anonymous" - + "" - + "" - + "%s" - + // resource - "" - + "%s" - + // securityHeader - "" - + "" - + "" - + "" - + "" - + "" - + "%s" - + // appliesTo like - // urn:federation:MicrosoftOnline. Either - // wst:TokenType or wst:AppliesTo should be - // defined in the token request message. If - // both are specified, the wst:AppliesTo field - // takes precedence. - "" - + "" - + "" - + "%s" - + "%s" - + // If we dont specify tokentype, it will - // return samlv1.1 - "" - + "" - + "", schemaLocation, soapAction, - guid, address, securityHeaderBuilder.toString(), + + "" + + "%s" + + "urn:uuid:" + + "%s" + + // guid + "" + + "" + + "http://www.w3.org/2005/08/addressing/anonymous" + + "" + + "" + + "%s" + + // resource + "" + + "%s" + + // securityHeader + "" + + "" + + "" + + "" + + "" + + "" + + "%s" + + // appliesTo like + // urn:federation:MicrosoftOnline. Either + // wst:TokenType or wst:AppliesTo should be + // defined in the token request message. If + // both are specified, the wst:AppliesTo field + // takes precedence. + "" + + "" + + "" + + "%s" + + "%s" + + // If we dont specify tokentype, it will + // return samlv1.1 + "" + + "" + + "", schemaLocation, soapAction, + guid, address, + securityHeaderBuilder.toString(), rstTrustNamespace, DEFAULT_APPLIES_TO, keyType, requestType)); return messageBuilder; } - private static StringBuilder buildSecurityHeader(StringBuilder securityHeaderBuilder, String username, String password, WSTrustVersion version) { + private static StringBuilder buildSecurityHeader( + StringBuilder securityHeaderBuilder, String username, + String password, WSTrustVersion version) { StringBuilder messageCredentialsBuilder = new StringBuilder( MAX_EXPECTED_MESSAGE_SIZE); @@ -163,22 +186,17 @@ private static StringBuilder buildSecurityHeader(StringBuilder securityHeaderBui String expiryTimeString = dateFormat.format(date); messageCredentialsBuilder.append(String.format( - "" + // guid - "%s" + //username - "%s" + //password - "", - guid, - username, - password)); - - securityHeaderBuilder.append(""); - securityHeaderBuilder.append(String.format( - "" + "" + // guid + "%s" + // username + "%s" + // password + "", guid, username, password)); + + securityHeaderBuilder + .append(""); + securityHeaderBuilder.append(String.format("" + "%s" + // created "%s" + // Expires - "", - currentTimeString, expiryTimeString)); + "", currentTimeString, expiryTimeString)); securityHeaderBuilder.append(messageCredentialsBuilder.toString()); securityHeaderBuilder.append(""); diff --git a/src/main/java/com/microsoft/aad/adal4j/WSTrustResponse.java b/src/main/java/com/microsoft/aad/adal4j/WSTrustResponse.java index 1815d41f..b5b0c6e3 100644 --- a/src/main/java/com/microsoft/aad/adal4j/WSTrustResponse.java +++ b/src/main/java/com/microsoft/aad/adal4j/WSTrustResponse.java @@ -80,7 +80,8 @@ boolean isTokenSaml2() { && !SAML1_ASSERTION.equalsIgnoreCase(tokenType); } - static WSTrustResponse parse(String response, WSTrustVersion version) throws Exception { + static WSTrustResponse parse(String response, WSTrustVersion version) + throws Exception { WSTrustResponse responseValue = new WSTrustResponse(); DocumentBuilderFactory builderFactory = DocumentBuilderFactory .newInstance(); @@ -91,7 +92,7 @@ static WSTrustResponse parse(String response, WSTrustVersion version) throws Exc XPath xPath = XPathFactory.newInstance().newXPath(); NamespaceContextImpl namespace = new NamespaceContextImpl(); xPath.setNamespaceContext(namespace); - + if (parseError(responseValue, xmlDocument, xPath)) { if (StringHelper.isBlank(responseValue.errorCode)) { responseValue.errorCode = "NONE"; @@ -102,7 +103,8 @@ static WSTrustResponse parse(String response, WSTrustVersion version) throws Exc throw new Exception("Server returned error in RSTR - ErrorCode: " + responseValue.errorCode + " : FaultMessage: " + responseValue.faultMessage.trim()); - } else { + } + else { parseToken(responseValue, xmlDocument, xPath, version); } @@ -110,11 +112,12 @@ static WSTrustResponse parse(String response, WSTrustVersion version) throws Exc } private static void parseToken(WSTrustResponse responseValue, - Document xmlDocument, XPath xPath, WSTrustVersion version) throws Exception { + Document xmlDocument, XPath xPath, WSTrustVersion version) + throws Exception { - NodeList tokenTypeNodes = (NodeList) xPath - .compile(version.getResponseTokenTypePath()) - .evaluate(xmlDocument, XPathConstants.NODESET); + NodeList tokenTypeNodes = (NodeList) xPath.compile( + version.getResponseTokenTypePath()).evaluate(xmlDocument, + XPathConstants.NODESET); if (tokenTypeNodes.getLength() == 0) { log.warn("No TokenType elements found in RSTR"); } @@ -145,7 +148,6 @@ private static void parseToken(WSTrustResponse responseValue, continue; } - responseValue.token = innerXml(requestedTokenNodes.item(0)); if (StringHelper.isBlank(responseValue.token)) { log.warn("Unable to find token associated with TokenType element: " @@ -216,7 +218,8 @@ static String innerXml(Node node) { resultBuilder.append(sw.toString()); } - } catch (Exception ex) { + } + catch (Exception ex) { ex.printStackTrace(); } diff --git a/src/main/java/com/microsoft/aad/adal4j/WsTrustVersion.java b/src/main/java/com/microsoft/aad/adal4j/WSTrustVersion.java similarity index 78% rename from src/main/java/com/microsoft/aad/adal4j/WsTrustVersion.java rename to src/main/java/com/microsoft/aad/adal4j/WSTrustVersion.java index 21831f56..316e4791 100644 --- a/src/main/java/com/microsoft/aad/adal4j/WsTrustVersion.java +++ b/src/main/java/com/microsoft/aad/adal4j/WSTrustVersion.java @@ -23,10 +23,11 @@ enum WSTrustVersion { - WSTRUST13("//s:Envelope/s:Body/wst:RequestSecurityTokenResponseCollection/wst:RequestSecurityTokenResponse/wst:TokenType", - "wst:RequestedSecurityToken"), - WSTRUST2005("//s:Envelope/s:Body/t:RequestSecurityTokenResponse/t:TokenType", - "t:RequestedSecurityToken"), UNDEFINED("", ""); + WSTRUST13( + "//s:Envelope/s:Body/wst:RequestSecurityTokenResponseCollection/wst:RequestSecurityTokenResponse/wst:TokenType", + "wst:RequestedSecurityToken"), WSTRUST2005( + "//s:Envelope/s:Body/t:RequestSecurityTokenResponse/t:TokenType", + "t:RequestedSecurityToken"), UNDEFINED("", ""); private String responseTokenTypePath = ""; private String responseSecurityTokenPath = ""; @@ -38,8 +39,8 @@ private WSTrustVersion(String tokenType, String responseSecurityToken) { public String getResponseTokenTypePath() { return this.responseTokenTypePath; } - - public String getResponseSecurityTokenPath(){ + + public String getResponseSecurityTokenPath() { return this.responseSecurityTokenPath; } } diff --git a/src/samples/public-client-app-sample/pom.xml b/src/samples/public-client-app-sample/pom.xml index a85c0581..f237d54e 100644 --- a/src/samples/public-client-app-sample/pom.xml +++ b/src/samples/public-client-app-sample/pom.xml @@ -14,9 +14,8 @@ com.microsoft.azure - adal4j - - 1.1.1 + adal4j + 1.1.2 com.nimbusds oauth2-oidc-sdk diff --git a/src/samples/web-app-samples-for-adal4j/pom.xml b/src/samples/web-app-samples-for-adal4j/pom.xml index 005a4106..1b224759 100644 --- a/src/samples/web-app-samples-for-adal4j/pom.xml +++ b/src/samples/web-app-samples-for-adal4j/pom.xml @@ -15,7 +15,7 @@ com.microsoft.azure adal4j - 1.1.1 + 1.1.2 com.nimbusds oauth2-oidc-sdk diff --git a/src/test/java/com/microsoft/aad/adal4j/AdalOAuthRequestTest.java b/src/test/java/com/microsoft/aad/adal4j/AdalOAuthRequestTest.java index addeb37d..e2fa817c 100644 --- a/src/test/java/com/microsoft/aad/adal4j/AdalOAuthRequestTest.java +++ b/src/test/java/com/microsoft/aad/adal4j/AdalOAuthRequestTest.java @@ -45,7 +45,7 @@ public class AdalOAuthRequestTest extends AbstractAdalTests { @Test public void testConstructor() throws MalformedURLException { final AdalOAuthRequest request = new AdalOAuthRequest(Method.POST, - new URL("http://login.windows.net"), null); + new URL("http://login.windows.net"), null, null, null); assertNotNull(request); } @@ -55,7 +55,7 @@ public void testCreateResponseContentTypeParsingFailure() throws Exception { final AdalOAuthRequest request = new AdalOAuthRequest(Method.GET, - new URL("https://" + TestConfiguration.AAD_HOST_NAME), null); + new URL("https://" + TestConfiguration.AAD_HOST_NAME), null, null, null); final HttpURLConnection conn = PowerMock .createMock(HttpURLConnection.class); EasyMock.expect(conn.getResponseCode()).andReturn(200).times(1); @@ -72,7 +72,7 @@ public void testCreateResponseContentTypeParsingFailure() public void testCreateResponseLocationNull() throws Exception { final AdalOAuthRequest request = new AdalOAuthRequest(Method.GET, - new URL("https://" + TestConfiguration.AAD_HOST_NAME), null); + new URL("https://" + TestConfiguration.AAD_HOST_NAME), null, null, null); final HttpURLConnection conn = PowerMock .createMock(HttpURLConnection.class); EasyMock.expect(conn.getResponseCode()).andReturn(200).times(1); @@ -101,7 +101,7 @@ public void testCreateResponseLocationNull() @Test public void testCreateResponse() throws Exception { final AdalOAuthRequest request = new AdalOAuthRequest(Method.GET, - new URL("https://" + TestConfiguration.AAD_HOST_NAME), null); + new URL("https://" + TestConfiguration.AAD_HOST_NAME), null, null, null); final HttpURLConnection conn = PowerMock .createMock(HttpURLConnection.class); EasyMock.expect(conn.getResponseCode()).andReturn(200).times(1); @@ -132,7 +132,7 @@ public void testCreateResponse() throws Exception { @Test public void testCreateResponseFor404() throws Exception { final AdalOAuthRequest request = new AdalOAuthRequest(Method.GET, - new URL("https://" + TestConfiguration.AAD_HOST_NAME), null); + new URL("https://" + TestConfiguration.AAD_HOST_NAME), null, null, null); final HttpURLConnection conn = PowerMock .createMock(HttpURLConnection.class); EasyMock.expect(conn.getResponseCode()).andReturn(404); diff --git a/src/test/java/com/microsoft/aad/adal4j/AdalTokenRequestTest.java b/src/test/java/com/microsoft/aad/adal4j/AdalTokenRequestTest.java index cddc398c..b7ca6f0a 100644 --- a/src/test/java/com/microsoft/aad/adal4j/AdalTokenRequestTest.java +++ b/src/test/java/com/microsoft/aad/adal4j/AdalTokenRequestTest.java @@ -63,7 +63,7 @@ public void testNullUri() throws SerializeException, ParseException, (String) null); final ClientDataHttpHeaders cdhh = new ClientDataHttpHeaders("corr-id"); final AdalTokenRequest request = new AdalTokenRequest(null, ca, grant, - cdhh.getReadonlyHeaderMap()); + cdhh.getReadonlyHeaderMap(), null, null); Assert.assertNotNull(request); request.executeOAuthRequestAndProcessResponse(); } @@ -81,7 +81,7 @@ public void testConstructor() throws MalformedURLException, final ClientDataHttpHeaders cdhh = new ClientDataHttpHeaders("corr-id"); final AdalTokenRequest request = new AdalTokenRequest(new URL( "http://login.windows.net"), ca, grant, - cdhh.getReadonlyHeaderMap()); + cdhh.getReadonlyHeaderMap(), null, null); Assert.assertNotNull(request); } @@ -99,7 +99,7 @@ public void testToOAuthRequestNonEmptyCorrelationId() final ClientDataHttpHeaders cdhh = new ClientDataHttpHeaders("corr-id"); final AdalTokenRequest request = new AdalTokenRequest(new URL( "http://login.windows.net"), ca, grant, - cdhh.getReadonlyHeaderMap()); + cdhh.getReadonlyHeaderMap(), null, null); Assert.assertNotNull(request); final AdalOAuthRequest req = request.toOAuthRequest(); Assert.assertNotNull(req); @@ -119,7 +119,7 @@ public void testToOAuthRequestNullCorrelationId_NullClientAuth() final AdalAuthorizatonGrant grant = new AdalAuthorizatonGrant(ag, (String) null); final AdalTokenRequest request = new AdalTokenRequest(new URL( - "http://login.windows.net"), null, grant, null); + "http://login.windows.net"), null, grant, null, null, null); Assert.assertNotNull(request); final AdalOAuthRequest req = request.toOAuthRequest(); Assert.assertNotNull(req); @@ -137,7 +137,7 @@ public void testExecuteOAuth_Success() throws SerializeException, final AdalTokenRequest request = PowerMock.createPartialMock( AdalTokenRequest.class, new String[] { "toOAuthRequest" }, - new URL("http://login.windows.net"), null, grant, null); + new URL("http://login.windows.net"), null, grant, null, null, null); final AdalOAuthRequest adalOAuthHttpRequest = PowerMock .createMock(AdalOAuthRequest.class); final HTTPResponse httpResponse = PowerMock @@ -186,7 +186,7 @@ public void testExecuteOAuth_Failure() throws SerializeException, final AdalTokenRequest request = PowerMock.createPartialMock( AdalTokenRequest.class, new String[] { "toOAuthRequest" }, - new URL("http://login.windows.net"), null, grant, null); + new URL("http://login.windows.net"), null, grant, null, null, null); final AdalOAuthRequest adalOAuthHttpRequest = PowerMock .createMock(AdalOAuthRequest.class); final HTTPResponse httpResponse = PowerMock @@ -216,7 +216,8 @@ public void testExecuteOAuth_Failure() throws SerializeException, try { request.executeOAuthRequestAndProcessResponse(); PowerMock.verifyAll(); - } finally { + } + finally { PowerMock.reset(request, adalOAuthHttpRequest, httpResponse, TokenErrorResponse.class, jsonObj, errorResponse); } diff --git a/src/test/java/com/microsoft/aad/adal4j/AuthenticationContextTest.java b/src/test/java/com/microsoft/aad/adal4j/AuthenticationContextTest.java index 71b604e4..cd614582 100644 --- a/src/test/java/com/microsoft/aad/adal4j/AuthenticationContextTest.java +++ b/src/test/java/com/microsoft/aad/adal4j/AuthenticationContextTest.java @@ -24,8 +24,8 @@ import static org.testng.Assert.assertTrue; import java.io.FileInputStream; -import java.io.IOException; import java.net.MalformedURLException; +import java.net.Proxy; import java.net.URI; import java.security.KeyStore; import java.security.MessageDigest; @@ -39,6 +39,8 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; +import javax.net.ssl.SSLSocketFactory; + import org.easymock.EasyMock; import org.powermock.api.easymock.PowerMock; import org.powermock.core.classloader.annotations.PrepareForTest; @@ -51,289 +53,271 @@ @Test(groups = { "checkin" }) @PrepareForTest({ AuthenticationContext.class, AuthenticationCallback.class, - AsymmetricKeyCredential.class }) + AsymmetricKeyCredential.class, UserDiscoveryRequest.class }) public class AuthenticationContextTest extends AbstractAdalTests { - private AuthenticationContext ctx = null; - private ExecutorService service = null; - - @BeforeTest - public void setup() { - service = Executors.newFixedThreadPool(1); - } - - @AfterTest - public void cleanup() { - if (service != null) { - service.shutdown(); - } - } - - @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "authority is null or empty") - public void testConstructor_NullAuthority() throws MalformedURLException { - ctx = new AuthenticationContext(null, true, service); - } - - @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "service is null") - public void testConstructor_NullService() throws MalformedURLException { - ctx = new AuthenticationContext(TestConfiguration.AAD_TENANT_ENDPOINT, - true, null); - } - - @Test - public void testCorrelationId() throws MalformedURLException { - ctx = new AuthenticationContext(TestConfiguration.AAD_TENANT_ENDPOINT, - true, service); - ctx.setCorrelationId("correlationId"); - Assert.assertEquals(ctx.getCorrelationId(), "correlationId"); - } - - @Test - public void testAcquireTokenAuthCode_ClientCredential() throws Exception { - ctx = PowerMock.createPartialMock(AuthenticationContext.class, - new String[] { "acquireTokenCommon" }, - TestConfiguration.AAD_TENANT_ENDPOINT, true, service); - PowerMock.expectPrivate(ctx, "acquireTokenCommon", - EasyMock.isA(AdalAuthorizatonGrant.class), - EasyMock.isA(ClientAuthentication.class), - EasyMock.isA(ClientDataHttpHeaders.class)).andReturn( - new AuthenticationResult("bearer", "accessToken", - "refreshToken", new Date().getTime(), "idToken", null, - false)); - PowerMock.replay(ctx); - Future result = ctx - .acquireTokenByAuthorizationCode("auth_code", new URI( - TestConfiguration.AAD_DEFAULT_REDIRECT_URI), - new ClientCredential("clientId", "clientSecret"), null); - AuthenticationResult ar = result.get(); - Assert.assertNotNull(ar); - PowerMock.verifyAll(); - } - - @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "authorization code is null or empty") - public void testcAquireTokenAuthCode_AuthCodeNull() throws Exception { - ctx = new AuthenticationContext(TestConfiguration.AAD_TENANT_ENDPOINT, - true, service); - ctx.acquireTokenByAuthorizationCode(null, new URI( - TestConfiguration.AAD_DEFAULT_REDIRECT_URI), - new ClientCredential("clientId", "clientSecret"), null); - } - - @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "redirect uri is null") - public void testAcquireTokenAuthCode_RedirectUriNull() throws Exception { - ctx = new AuthenticationContext(TestConfiguration.AAD_TENANT_ENDPOINT, - true, service); - ctx.acquireTokenByAuthorizationCode("auth_code", null, - new ClientCredential("clientId", "clientSecret"), null); - } - - @Test - public void testAcquireTokenAuthCode_KeyCredential() throws Exception { - ctx = PowerMock.createPartialMock(AuthenticationContext.class, - new String[] { "acquireTokenCommon" }, - TestConfiguration.AAD_TENANT_ENDPOINT, true, service); - PowerMock.expectPrivate(ctx, "acquireTokenCommon", - EasyMock.isA(AdalAuthorizatonGrant.class), - EasyMock.isA(ClientAuthentication.class), - EasyMock.isA(ClientDataHttpHeaders.class)).andReturn( - new AuthenticationResult("bearer", "accessToken", - "refreshToken", new Date().getTime(), "idToken", null, - false)); - final KeyStore keystore = KeyStore.getInstance("PKCS12", "SunJSSE"); - keystore.load( - new FileInputStream(this.getClass() - .getResource(TestConfiguration.AAD_CERTIFICATE_PATH) - .getFile()), - TestConfiguration.AAD_CERTIFICATE_PASSWORD.toCharArray()); - final String alias = keystore.aliases().nextElement(); - final PrivateKey key = (PrivateKey) keystore.getKey(alias, - TestConfiguration.AAD_CERTIFICATE_PASSWORD.toCharArray()); - final X509Certificate cert = (X509Certificate) keystore - .getCertificate(alias); - - PowerMock.replay(ctx); - Future result = ctx - .acquireTokenByAuthorizationCode("auth_code", new URI( - TestConfiguration.AAD_DEFAULT_REDIRECT_URI), - AsymmetricKeyCredential.create( - TestConfiguration.AAD_CLIENT_ID, key, cert), - null); - AuthenticationResult ar = result.get(); - Assert.assertNotNull(ar); - PowerMock.verifyAll(); - PowerMock.resetAll(ctx); - } - - @Test - public void testAcquireToken_Username_Password() throws Exception { - ctx = PowerMock.createPartialMock(AuthenticationContext.class, - new String[] { "acquireTokenCommon" }, - TestConfiguration.AAD_TENANT_ENDPOINT, true, service); - PowerMock.expectPrivate(ctx, "acquireTokenCommon", - EasyMock.isA(AdalAuthorizatonGrant.class), - EasyMock.isA(ClientAuthentication.class), - EasyMock.isA(ClientDataHttpHeaders.class)).andReturn( - new AuthenticationResult("bearer", "accessToken", - "refreshToken", new Date().getTime(), null, null, false)); - - PowerMock.replay(ctx); - Future result = ctx.acquireToken("resource", - "clientId", "username", - "password", null); - - AuthenticationResult ar = result.get(); - Assert.assertNotNull(ar); - PowerMock.verifyAll(); - PowerMock.resetAll(ctx); - } - - @Test(groups = { "end-to-end" }) - public void testAcquireToken_KeyCred() throws Exception { - final KeyStore keystore = KeyStore.getInstance("PKCS12", "SunJSSE"); - keystore.load( - new FileInputStream(this.getClass() - .getResource(TestConfiguration.AAD_CERTIFICATE_PATH) - .getFile()), - TestConfiguration.AAD_CERTIFICATE_PASSWORD.toCharArray()); - final String alias = keystore.aliases().nextElement(); - final PrivateKey key = (PrivateKey) keystore.getKey(alias, - TestConfiguration.AAD_CERTIFICATE_PASSWORD.toCharArray()); - final X509Certificate cert = (X509Certificate) keystore - .getCertificate(alias); - ctx = new AuthenticationContext(TestConfiguration.AAD_TENANT_ENDPOINT, - true, service); - final Future result = ctx.acquireToken( - TestConfiguration.AAD_RESOURCE_ID, AsymmetricKeyCredential - .create(TestConfiguration.AAD_CLIENT_ID, key, cert), - null); - final AuthenticationResult ar = result.get(); - assertNotNull(ar); - assertFalse(StringHelper.isBlank(result.get().getAccessToken())); - assertTrue(StringHelper.isBlank(result.get().getRefreshToken())); - } - - @Test(groups = { "end-to-end" }) - public void testAcquireToken_RefreshToken() throws Exception { - final KeyStore keystore = KeyStore.getInstance("PKCS12", "SunJSSE"); - keystore.load( - new FileInputStream(this.getClass() - .getResource(TestConfiguration.AAD_CERTIFICATE_PATH) - .getFile()), - TestConfiguration.AAD_CERTIFICATE_PASSWORD.toCharArray()); - final String alias = keystore.aliases().nextElement(); - final PrivateKey key = (PrivateKey) keystore.getKey(alias, - TestConfiguration.AAD_CERTIFICATE_PASSWORD.toCharArray()); - final X509Certificate cert = (X509Certificate) keystore - .getCertificate(alias); - ctx = new AuthenticationContext(TestConfiguration.AAD_TENANT_ENDPOINT, - true, service); - final Future result = ctx.acquireToken( - TestConfiguration.AAD_RESOURCE_ID, AsymmetricKeyCredential - .create(TestConfiguration.AAD_CLIENT_ID, key, cert), - null); - final AuthenticationResult ar = result.get(); - assertNotNull(ar); - assertFalse(StringHelper.isBlank(result.get().getAccessToken())); - assertTrue(StringHelper.isBlank(result.get().getRefreshToken())); - } - - @Test(expectedExceptions = AuthenticationException.class) - public void testInvalidClientAssertion() throws MalformedURLException { - ctx = new AuthenticationContext(TestConfiguration.AAD_TENANT_ENDPOINT, - true, service); - ctx.acquireToken(TestConfiguration.AAD_RESOURCE_ID, - new ClientAssertion("invalid_assertion"), null); - } - - @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "resource is null or empty") - public void testValidateInput_ValidateNullResource() - throws MalformedURLException { - ctx = new AuthenticationContext(TestConfiguration.AAD_TENANT_ENDPOINT, - true, service); - ctx.acquireToken(null, new ClientAssertion("invalid_assertion"), null); - } - - @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "credential is null") - public void testValidateInput_NullCredential() throws MalformedURLException { - ctx = new AuthenticationContext(TestConfiguration.AAD_TENANT_ENDPOINT, - true, service); - ctx.acquireToken(TestConfiguration.AAD_RESOURCE_ID, - (ClientAssertion) null, null); - } - - @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "refreshToken is null or empty") - public void testValidateRefreshTokenRequestInput_NullRefreshToken() - throws MalformedURLException { - ctx = new AuthenticationContext(TestConfiguration.AAD_TENANT_ENDPOINT, - true, service); - ctx.acquireTokenByRefreshToken(null, "client_id", new ClientAssertion( - "invalid_assertion"), null); - } - - @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "clientId is null or empty") - public void testValidateRefreshTokenRequestInput_NullClientId() - throws MalformedURLException { - ctx = new AuthenticationContext(TestConfiguration.AAD_TENANT_ENDPOINT, - true, service); - ctx.acquireTokenByRefreshToken("refresh_token", null, - new ClientAssertion("invalid_assertion"), null); - } - - @Test(expectedExceptions = IOException.class, expectedExceptionsMessageRegExp = "lgn.windows.net") - public void testFailedAcquireTokenRequest() throws Throwable { - ctx = new AuthenticationContext( - TestConfiguration.AAD_UNKNOWN_TENANT_ENDPOINT, true, service); - Future result = ctx.acquireTokenByRefreshToken( - "refresh", new ClientCredential("clientId", "clientSecret"), - "resource", null); - try { - result.get(); - } catch (ExecutionException ee) { - throw ee.getCause(); - } - } - - @Test - public void testFailedAcquireTokenRequest_ExecuteCallback() - throws Throwable { - ctx = new AuthenticationContext( - TestConfiguration.AAD_UNKNOWN_TENANT_ENDPOINT, true, service); - AuthenticationCallback ac = PowerMock - .createMock(AuthenticationCallback.class); - ac.onFailure(EasyMock.isA(Throwable.class)); - EasyMock.expectLastCall(); - PowerMock.replay(ac); - Future result = ctx.acquireTokenByRefreshToken( - "refresh", new ClientCredential("clientId", "clientSecret"), - "resource", ac); - try { - result.get(); - } catch (ExecutionException ee) { - throw ee.getCause(); - } - } - - static String getThumbPrint(final byte[] der) - throws NoSuchAlgorithmException, CertificateEncodingException { - final MessageDigest md = MessageDigest.getInstance("SHA-1"); - md.update(der); - final byte[] digest = md.digest(); - return hexify(digest); - - } - - static String hexify(final byte bytes[]) { - - final char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', - '9', 'a', 'b', 'c', 'd', 'e', 'f' }; - - final StringBuffer buf = new StringBuffer(bytes.length * 2); - - for (int i = 0; i < bytes.length; ++i) { - buf.append(hexDigits[(bytes[i] & 0xf0) >> 4]); - buf.append(hexDigits[bytes[i] & 0x0f]); - } - - return buf.toString(); - } + private AuthenticationContext ctx = null; + private ExecutorService service = null; + + @BeforeTest + public void setup() { + service = Executors.newFixedThreadPool(1); + } + + @AfterTest + public void cleanup() { + if (service != null) { + service.shutdown(); + } + } + + @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "authority is null or empty") + public void testConstructor_NullAuthority() throws MalformedURLException { + ctx = new AuthenticationContext(null, true, service); + } + + @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "service is null") + public void testConstructor_NullService() throws MalformedURLException { + ctx = new AuthenticationContext(TestConfiguration.AAD_TENANT_ENDPOINT, + true, null); + } + + @Test + public void testCorrelationId() throws MalformedURLException { + ctx = new AuthenticationContext(TestConfiguration.AAD_TENANT_ENDPOINT, + true, service); + ctx.setCorrelationId("correlationId"); + Assert.assertEquals(ctx.getCorrelationId(), "correlationId"); + } + + @Test + public void testAcquireTokenAuthCode_ClientCredential() throws Exception { + ctx = PowerMock.createPartialMock(AuthenticationContext.class, + new String[] { "acquireTokenCommon" }, + TestConfiguration.AAD_TENANT_ENDPOINT, true, service); + PowerMock.expectPrivate(ctx, "acquireTokenCommon", + EasyMock.isA(AdalAuthorizatonGrant.class), + EasyMock.isA(ClientAuthentication.class), + EasyMock.isA(ClientDataHttpHeaders.class)).andReturn( + new AuthenticationResult("bearer", "accessToken", + "refreshToken", new Date().getTime(), "idToken", null, + false)); + PowerMock.replay(ctx); + Future result = ctx + .acquireTokenByAuthorizationCode("auth_code", new URI( + TestConfiguration.AAD_DEFAULT_REDIRECT_URI), + new ClientCredential("clientId", "clientSecret"), null); + AuthenticationResult ar = result.get(); + Assert.assertNotNull(ar); + PowerMock.verifyAll(); + } + + @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "authorization code is null or empty") + public void testcAquireTokenAuthCode_AuthCodeNull() throws Exception { + ctx = new AuthenticationContext(TestConfiguration.AAD_TENANT_ENDPOINT, + true, service); + ctx.acquireTokenByAuthorizationCode(null, new URI( + TestConfiguration.AAD_DEFAULT_REDIRECT_URI), + new ClientCredential("clientId", "clientSecret"), null); + } + + @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "redirect uri is null") + public void testAcquireTokenAuthCode_RedirectUriNull() throws Exception { + ctx = new AuthenticationContext(TestConfiguration.AAD_TENANT_ENDPOINT, + true, service); + ctx.acquireTokenByAuthorizationCode("auth_code", null, + new ClientCredential("clientId", "clientSecret"), null); + } + + public void testAcquireToken_Username_Password() throws Exception { + ctx = PowerMock.createPartialMock(AuthenticationContext.class, + new String[] { "acquireTokenCommon" }, + TestConfiguration.AAD_TENANT_ENDPOINT, true, service); + PowerMock.expectPrivate(ctx, "acquireTokenCommon", + EasyMock.isA(AdalAuthorizatonGrant.class), + EasyMock.isA(ClientAuthentication.class), + EasyMock.isA(ClientDataHttpHeaders.class)) + .andReturn( + new AuthenticationResult("bearer", "accessToken", + "refreshToken", new Date().getTime(), null, + null, false)); + + UserDiscoveryResponse response = EasyMock + .createMock(UserDiscoveryResponse.class); + EasyMock.expect(response.isAccountFederated()).andReturn(false); + + PowerMock.mockStatic(UserDiscoveryRequest.class); + EasyMock.expect( + UserDiscoveryRequest.execute(EasyMock.isA(String.class), EasyMock.isNull(Proxy.class), + EasyMock.isNull(SSLSocketFactory.class))).andReturn(response); + + PowerMock.replay(ctx, response, UserDiscoveryRequest.class); + Future result = ctx.acquireToken("resource", + "clientId", "username", "password", null); + + AuthenticationResult ar = result.get(); + Assert.assertNotNull(ar); + PowerMock.verifyAll(); + PowerMock.resetAll(ctx); + } + + @Test + public void testAcquireTokenAuthCode_KeyCredential() throws Exception { + ctx = PowerMock.createPartialMock(AuthenticationContext.class, + new String[] { "acquireTokenCommon" }, + TestConfiguration.AAD_TENANT_ENDPOINT, true, service); + PowerMock.expectPrivate(ctx, "acquireTokenCommon", + EasyMock.isA(AdalAuthorizatonGrant.class), + EasyMock.isA(ClientAuthentication.class), + EasyMock.isA(ClientDataHttpHeaders.class)).andReturn( + new AuthenticationResult("bearer", "accessToken", + "refreshToken", new Date().getTime(), "idToken", null, + false)); + final KeyStore keystore = KeyStore.getInstance("PKCS12", "SunJSSE"); + keystore.load( + new FileInputStream(this.getClass() + .getResource(TestConfiguration.AAD_CERTIFICATE_PATH) + .getFile()), + TestConfiguration.AAD_CERTIFICATE_PASSWORD.toCharArray()); + final String alias = keystore.aliases().nextElement(); + final PrivateKey key = (PrivateKey) keystore.getKey(alias, + TestConfiguration.AAD_CERTIFICATE_PASSWORD.toCharArray()); + final X509Certificate cert = (X509Certificate) keystore + .getCertificate(alias); + + PowerMock.replay(ctx); + Future result = ctx + .acquireTokenByAuthorizationCode("auth_code", new URI( + TestConfiguration.AAD_DEFAULT_REDIRECT_URI), + AsymmetricKeyCredential.create( + TestConfiguration.AAD_CLIENT_ID, key, cert), + null); + AuthenticationResult ar = result.get(); + Assert.assertNotNull(ar); + PowerMock.verifyAll(); + PowerMock.resetAll(ctx); + } + + public void testAcquireToken_KeyCred() throws Exception { + + ctx = PowerMock.createPartialMock(AuthenticationContext.class, + new String[] { "acquireTokenCommon" }, + TestConfiguration.AAD_TENANT_ENDPOINT, true, service); + PowerMock.expectPrivate(ctx, "acquireTokenCommon", + EasyMock.isA(AdalAuthorizatonGrant.class), + EasyMock.isA(ClientAuthentication.class), + EasyMock.isA(ClientDataHttpHeaders.class)).andReturn( + new AuthenticationResult("bearer", "accessToken", null, + new Date().getTime(), null, null, false)); + + final KeyStore keystore = KeyStore.getInstance("PKCS12", "SunJSSE"); + keystore.load( + new FileInputStream(this.getClass() + .getResource(TestConfiguration.AAD_CERTIFICATE_PATH) + .getFile()), + TestConfiguration.AAD_CERTIFICATE_PASSWORD.toCharArray()); + final String alias = keystore.aliases().nextElement(); + final PrivateKey key = (PrivateKey) keystore.getKey(alias, + TestConfiguration.AAD_CERTIFICATE_PASSWORD.toCharArray()); + final X509Certificate cert = (X509Certificate) keystore + .getCertificate(alias); + + PowerMock.replay(ctx); + final Future result = ctx.acquireToken( + TestConfiguration.AAD_RESOURCE_ID, AsymmetricKeyCredential + .create(TestConfiguration.AAD_CLIENT_ID, key, cert), + null); + final AuthenticationResult ar = result.get(); + assertNotNull(ar); + assertFalse(StringHelper.isBlank(result.get().getAccessToken())); + assertTrue(StringHelper.isBlank(result.get().getRefreshToken())); + PowerMock.verifyAll(); + PowerMock.resetAll(ctx); + } + + @Test(expectedExceptions = AuthenticationException.class) + public void testInvalidClientAssertion() throws MalformedURLException { + ctx = new AuthenticationContext(TestConfiguration.AAD_TENANT_ENDPOINT, + true, service); + ctx.acquireToken(TestConfiguration.AAD_RESOURCE_ID, + new ClientAssertion("invalid_assertion"), null); + } + + @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "resource is null or empty") + public void testValidateInput_ValidateNullResource() + throws MalformedURLException { + ctx = new AuthenticationContext(TestConfiguration.AAD_TENANT_ENDPOINT, + true, service); + ctx.acquireToken(null, new ClientAssertion("invalid_assertion"), null); + } + + @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "credential is null") + public void testValidateInput_NullCredential() throws MalformedURLException { + ctx = new AuthenticationContext(TestConfiguration.AAD_TENANT_ENDPOINT, + true, service); + ctx.acquireToken(TestConfiguration.AAD_RESOURCE_ID, + (ClientAssertion) null, null); + } + + @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "refreshToken is null or empty") + public void testValidateRefreshTokenRequestInput_NullRefreshToken() + throws MalformedURLException { + ctx = new AuthenticationContext(TestConfiguration.AAD_TENANT_ENDPOINT, + true, service); + ctx.acquireTokenByRefreshToken(null, "client_id", new ClientAssertion( + "invalid_assertion"), null); + } + + @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "clientId is null or empty") + public void testValidateRefreshTokenRequestInput_NullClientId() + throws MalformedURLException { + ctx = new AuthenticationContext(TestConfiguration.AAD_TENANT_ENDPOINT, + true, service); + ctx.acquireTokenByRefreshToken("refresh_token", null, + new ClientAssertion("invalid_assertion"), null); + } + + @Test + public void testFailedAcquireTokenRequest_ExecuteCallback() + throws Throwable { + ctx = new AuthenticationContext( + TestConfiguration.AAD_UNKNOWN_TENANT_ENDPOINT, true, service); + AuthenticationCallback ac = PowerMock + .createMock(AuthenticationCallback.class); + ac.onFailure(EasyMock.isA(Throwable.class)); + EasyMock.expectLastCall(); + PowerMock.replay(ac); + Future result = ctx.acquireTokenByRefreshToken( + "refresh", new ClientCredential("clientId", "clientSecret"), + "resource", ac); + try { + result.get(); + } catch (ExecutionException ee) { + throw ee.getCause(); + } + } + + static String getThumbPrint(final byte[] der) + throws NoSuchAlgorithmException, CertificateEncodingException { + final MessageDigest md = MessageDigest.getInstance("SHA-1"); + md.update(der); + final byte[] digest = md.digest(); + return hexify(digest); + + } + + static String hexify(final byte bytes[]) { + + final char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', + '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + + final StringBuffer buf = new StringBuffer(bytes.length * 2); + + for (int i = 0; i < bytes.length; ++i) { + buf.append(hexDigits[(bytes[i] & 0xf0) >> 4]); + buf.append(hexDigits[bytes[i] & 0x0f]); + } + + return buf.toString(); + } } \ No newline at end of file diff --git a/src/test/java/com/microsoft/aad/adal4j/HttpHelperTest.java b/src/test/java/com/microsoft/aad/adal4j/HttpHelperTest.java index 2038800f..e59caf0f 100644 --- a/src/test/java/com/microsoft/aad/adal4j/HttpHelperTest.java +++ b/src/test/java/com/microsoft/aad/adal4j/HttpHelperTest.java @@ -21,7 +21,8 @@ import java.io.IOException; import java.io.InputStream; -import java.net.HttpURLConnection; + +import javax.net.ssl.HttpsURLConnection; import org.easymock.EasyMock; import org.powermock.api.easymock.PowerMock; @@ -37,8 +38,8 @@ public class HttpHelperTest extends AbstractAdalTests { @Test(expectedExceptions = IOException.class, expectedExceptionsMessageRegExp = "Failed: HTTP error code 403") public void testReadResponseFromConnection_ResponseCodeNot200() throws Exception { - final HttpURLConnection connection = PowerMock - .createMock(HttpURLConnection.class); + final HttpsURLConnection connection = PowerMock + .createMock(HttpsURLConnection.class); EasyMock.expect(connection.getResponseCode()).andReturn(403).times(2); final InputStream is = PowerMock.createMock(InputStream.class); is.close();