blob: f75585228c8ffd59f32e7d3b104f6bf57c46c0a4 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jclouds.oauth.v2;
import static com.google.common.base.Charsets.UTF_8;
import static com.google.common.io.BaseEncoding.base64Url;
import static com.google.common.util.concurrent.MoreExecutors.newDirectExecutorService;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static org.jclouds.Constants.PROPERTY_MAX_RETRIES;
import static org.jclouds.oauth.v2.config.CredentialType.P12_PRIVATE_KEY_CREDENTIALS;
import static org.jclouds.oauth.v2.config.CredentialType.CLIENT_CREDENTIALS_SECRET;
import static org.jclouds.oauth.v2.config.CredentialType.CLIENT_CREDENTIALS_P12_AND_CERTIFICATE;
import static org.jclouds.oauth.v2.config.OAuthProperties.CERTIFICATE;
import static org.jclouds.oauth.v2.config.OAuthProperties.AUDIENCE;
import static org.jclouds.oauth.v2.config.OAuthProperties.CREDENTIAL_TYPE;
import static org.jclouds.oauth.v2.config.OAuthProperties.JWS_ALG;
import static org.jclouds.oauth.v2.config.OAuthProperties.RESOURCE;
import static org.jclouds.util.Strings2.toStringAndClose;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.fail;
import java.io.IOException;
import java.net.URL;
import java.util.Properties;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;
import org.jclouds.ContextBuilder;
import org.jclouds.concurrent.config.ExecutorServiceModule;
import org.jclouds.oauth.v2.config.CredentialType;
import org.jclouds.oauth.v2.config.OAuthModule;
import org.jclouds.oauth.v2.config.OAuthScopes;
import org.jclouds.oauth.v2.config.OAuthScopes.SingleScope;
import org.jclouds.oauth.v2.domain.Claims;
import org.jclouds.oauth.v2.domain.ClientCredentialsClaims;
import org.jclouds.oauth.v2.domain.Token;
import org.jclouds.rest.AnonymousHttpApiMetadata;
import org.jclouds.rest.AuthorizationException;
import org.testng.annotations.Test;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.BaseEncoding;
import com.google.inject.Binder;
import com.google.inject.Module;
@Test(groups = "unit", testName = "OAuthApiMockTest")
public class AuthorizationApiMockTest {
private static final String SCOPE = "https://www.googleapis.com/auth/prediction";
private static final String header = "{\"alg\":\"RS256\",\"typ\":\"JWT\"}";
private static final String claims = "{\"iss\":\"761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer"
+ ".gserviceaccount.com\",\"scope\":\"" + SCOPE + "\",\"aud\":\"https://accounts.google"
+ ".com/o/oauth2/token\",\"exp\":1328573381,\"iat\":1328569781}";
private static final Token TOKEN = Token.create("1/8xbJqaOZXSUZbHLl5EOtu1pxz3fmmetKx9W8CV4t79M", "Bearer", 3600);
private static final Claims CLAIMS = Claims.create(
"761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer.gserviceaccount.com", // iss
SCOPE, // scope
"https://accounts.google.com/o/oauth2/token", // aud
1328573381, // exp
1328569781 // iat
);
private static final String clientCredentialsHeader = "{\"alg\":\"RS256\",\"typ\":\"JWT\",\"x5t\":\"RZk6mx4gGECvF6XWZWkK9qaGdHk=\"}";
private static final String clientCredentialsClaims = "{\"iss\":\"a242b44e-2c2a-3bdd-b094-6152da263c54\"," +
"\"sub\":\"a242b44e-2c2a-3bdd-b094-6152da263c54\",\"aud\":" +
"\"https://login.microsoftonline.com/a242ccee-1a1a-3bdd-b094-6152da263c54/oauth2/token\"" +
",\"exp\":1328573381,\"nbf\":1328569781,\"jti\":\"abcdefgh\"}";
private static final ClientCredentialsClaims CLIENT_CREDENTIALS_CLAIMS = ClientCredentialsClaims.create(
"a242b44e-2c2a-3bdd-b094-6152da263c54", // iss
"a242b44e-2c2a-3bdd-b094-6152da263c54", // sub
"https://login.microsoftonline.com/a242ccee-1a1a-3bdd-b094-6152da263c54/oauth2/token", //aud
1328573381, // exp
1328569781, // nbf
"abcdefgh" // jti
);
public void testGenerateJWTRequest() throws Exception {
MockWebServer server = new MockWebServer();
try {
server.enqueue(new MockResponse().setBody("{\n"
+ " \"access_token\" : \"1/8xbJqaOZXSUZbHLl5EOtu1pxz3fmmetKx9W8CV4t79M\",\n"
+ " \"token_type\" : \"Bearer\",\n" + " \"expires_in\" : 3600\n" + "}"));
server.start();
AuthorizationApi api = api(server.url("/").url(), P12_PRIVATE_KEY_CREDENTIALS);
assertEquals(api.authorize(CLAIMS), TOKEN);
RecordedRequest request = server.takeRequest();
assertEquals(request.getMethod(), "POST");
assertEquals(request.getHeader("Accept"), APPLICATION_JSON);
assertEquals(request.getHeader("Content-Type"), "application/x-www-form-urlencoded");
assertEquals(
request.getBody().readUtf8(), //
"grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&"
+
// Base64 Encoded Header
"assertion="
+ Joiner.on('.').join(encoding.encode(header.getBytes(UTF_8)),
encoding.encode(claims.getBytes(UTF_8)),
// Base64 encoded {header}.{claims} signature (using
// SHA256)
"W2Lesr_98AzVYiMbzxFqmwcOjpIWlwqkC6pNn1fXND9oSDNNnFhy-AAR6DKH-x9ZmxbY80"
+ "R5fH-OCeWumXlVgceKN8Z2SmgQsu8ElTpypQA54j_5j8vUImJ5hsOUYPeyF1U2BUzZ3L5g"
+ "03PXBA0YWwRU9E1ChH28dQBYuGiUmYw"));
} finally {
server.shutdown();
}
}
public void testAuthorizationExceptionIsPopulatedOn4xx() throws Exception {
MockWebServer server = new MockWebServer();
try {
server.enqueue(new MockResponse().setResponseCode(400));
server.start();
AuthorizationApi api = api(server.url("/").url(), P12_PRIVATE_KEY_CREDENTIALS);
api.authorize(CLAIMS);
fail("An AuthorizationException should have been raised");
} catch (AuthorizationException ex) {
// Success
} finally {
server.shutdown();
}
}
public void testGenerateClientSecretRequest() throws Exception {
MockWebServer server = new MockWebServer();
String credential = "password";
String identity = "user";
String resource = "http://management.azure.com/";
String encoded_resource = "http%3A//management.azure.com/";
try {
server.enqueue(new MockResponse().setBody("{\n"
+ " \"access_token\" : \"1/8xbJqaOZXSUZbHLl5EOtu1pxz3fmmetKx9W8CV4t79M\",\n"
+ " \"token_type\" : \"Bearer\",\n" + " \"expires_in\" : 3600\n" + "}"));
server.start();
AuthorizationApi api = api(server.url("/").url(), CLIENT_CREDENTIALS_SECRET);
assertEquals(api.authorizeClientSecret(identity, credential, resource, null), TOKEN);
RecordedRequest request = server.takeRequest();
assertEquals(request.getMethod(), "POST");
assertEquals(request.getHeader("Accept"), APPLICATION_JSON);
assertEquals(request.getHeader("Content-Type"), "application/x-www-form-urlencoded");
assertEquals(
request.getBody().readUtf8(), //
"grant_type=client_credentials&client_id=" + identity
+ "&client_secret=" + credential
+ "&resource=" + encoded_resource);
} finally {
server.shutdown();
}
}
public void testGenerateClientCredentialsJWTRequest() throws Exception {
MockWebServer server = new MockWebServer();
String identity = "user";
String resource = "http://management.azure.com/";
String encoded_resource = "http%3A//management.azure.com/";
try {
server.enqueue(new MockResponse().setBody("{\n"
+ " \"access_token\" : \"1/8xbJqaOZXSUZbHLl5EOtu1pxz3fmmetKx9W8CV4t79M\",\n"
+ " \"token_type\" : \"Bearer\",\n" + " \"expires_in\" : 3600\n" + "}"));
server.start();
AuthorizationApi api = api(server.url("/").url(), CLIENT_CREDENTIALS_P12_AND_CERTIFICATE);
assertEquals(api.authorize(identity, CLIENT_CREDENTIALS_CLAIMS, resource, null), TOKEN);
RecordedRequest request = server.takeRequest();
assertEquals(request.getMethod(), "POST");
assertEquals(request.getHeader("Accept"), APPLICATION_JSON);
assertEquals(request.getHeader("Content-Type"), "application/x-www-form-urlencoded");
assertEquals(
request.getBody().readUtf8(),
"grant_type=client_credentials&" +
"client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&" +
"client_id=" + identity + "&" +
"client_assertion=" +
Joiner.on(".").join(encoding.encode(clientCredentialsHeader.getBytes(UTF_8)),
encoding.encode(clientCredentialsClaims.getBytes(UTF_8)),
"ip3i0YLlunb4iq8sUMlpYDKnEuzmvlLpF4NQvn_aiysO5cuT5QHuGREq" +
"gyEa-UMhfZoW49ggUWjS7YBT00r64cFE3dovaNMiZYZuVWu_" +
"FpqO2QlwV7uXqhaRIE0cyabbKG44YJwA-NE4rtFZedFMo94F" +
"6aOz2FN3en8zS9UVqmM"
) + "&" +
"resource=" + encoded_resource);
} finally {
server.shutdown();
}
}
private final BaseEncoding encoding = base64Url().omitPadding();
private AuthorizationApi api(URL url, CredentialType credentialType) throws IOException {
Properties overrides = new Properties();
overrides.setProperty("oauth.endpoint", url.toString());
overrides.setProperty(JWS_ALG, "RS256");
overrides.setProperty(CREDENTIAL_TYPE, credentialType.toString());
overrides.setProperty(AUDIENCE, "https://accounts.google.com/o/oauth2/token");
overrides.setProperty(RESOURCE, "https://management.azure.com/");
overrides.setProperty(PROPERTY_MAX_RETRIES, "1");
overrides.setProperty(CERTIFICATE, toStringAndClose(OAuthTestUtils.class.getResourceAsStream("/testcert.pem")));
return ContextBuilder.newBuilder(AnonymousHttpApiMetadata.forApi(AuthorizationApi.class))
.credentials("foo", toStringAndClose(OAuthTestUtils.class.getResourceAsStream("/testpk.pem")))
.endpoint(url.toString())
.overrides(overrides)
.modules(ImmutableSet.of(new ExecutorServiceModule(newDirectExecutorService()), new OAuthModule(), new Module() {
@Override public void configure(Binder binder) {
binder.bind(OAuthScopes.class).toInstance(SingleScope.create(SCOPE));
}
}))
.buildApi(AuthorizationApi.class);
}
}