* Refactor OAuth so that it doesn't require private keys when we aren't signing anything. * Rename constant serviceAccountCredentials to p12PrivateKeyCredentials as this better describes the credential value.
Project: http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/repo Commit: http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/commit/6c839f13 Tree: http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/tree/6c839f13 Diff: http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/diff/6c839f13 Branch: refs/heads/master Commit: 6c839f135a0756afb8ed5c72c43ceabda3d5cdcf Parents: 37e0397 Author: Adrian Cole <[email protected]> Authored: Tue Nov 11 10:40:47 2014 -0800 Committer: Adrian Cole <[email protected]> Committed: Tue Nov 11 16:20:58 2014 -0800 ---------------------------------------------------------------------- .../GoogleCloudStorageApiMetadata.java | 4 +- .../BaseGoogleCloudStorageExpectTest.java | 15 ++- google-compute-engine/pom.xml | 2 +- .../GoogleComputeEngineApiMetadata.java | 4 +- .../config/GoogleComputeEngineProperties.java | 2 +- .../BaseGoogleComputeEngineApiMockTest.java | 3 - .../BaseGoogleComputeEngineExpectTest.java | 6 +- .../internal/GoogleComputeEngineTestModule.java | 6 +- .../internal/TestProperties.java | 13 +- oauth/README | 6 - oauth/pom.xml | 4 +- .../org/jclouds/oauth/v2/JWSAlgorithms.java | 75 +++++++++++ .../java/org/jclouds/oauth/v2/OAuthApi.java | 8 +- .../org/jclouds/oauth/v2/OAuthApiMetadata.java | 8 +- .../org/jclouds/oauth/v2/OAuthConstants.java | 78 ----------- .../oauth/v2/binders/OAuthTokenBinder.java | 76 ----------- .../jclouds/oauth/v2/binders/TokenBinder.java | 77 +++++++++++ .../jclouds/oauth/v2/config/Authentication.java | 35 ----- .../jclouds/oauth/v2/config/CredentialType.java | 20 ++- .../java/org/jclouds/oauth/v2/config/OAuth.java | 35 +++++ .../v2/config/OAuthAuthenticationModule.java | 10 +- .../oauth/v2/config/OAuthHttpApiModule.java | 2 +- .../jclouds/oauth/v2/config/OAuthModule.java | 45 ++++--- .../oauth/v2/config/OAuthProperties.java | 18 +-- .../jclouds/oauth/v2/config/OAuthScopes.java | 3 +- .../org/jclouds/oauth/v2/domain/Header.java | 2 +- .../oauth/v2/domain/OAuthCredentials.java | 129 ------------------ .../v2/filters/BearerTokenAuthenticator.java | 7 +- .../oauth/v2/filters/OAuthAuthenticator.java | 15 +-- .../oauth/v2/functions/BuildTokenRequest.java | 92 ++++++------- .../v2/functions/OAuthCredentialsSupplier.java | 130 ------------------- .../oauth/v2/functions/PrivateKeySupplier.java | 105 +++++++++++++++ .../v2/functions/SignOrProduceMacForToken.java | 59 +++------ .../jclouds/oauth/v2/OAuthApiMetadataTest.java | 5 +- .../org/jclouds/oauth/v2/OAuthTestUtils.java | 7 +- .../oauth/v2/binders/OAuthTokenBinderTest.java | 75 ----------- .../oauth/v2/binders/TokenBinderTest.java | 74 +++++++++++ .../oauth/v2/features/OAuthApiLiveTest.java | 17 +-- .../functions/OAuthCredentialsFromPKTest.java | 62 --------- .../functions/OAuthCredentialsSupplierTest.java | 56 -------- .../v2/functions/PrivateKeySupplierTest.java | 83 ++++++++++++ .../oauth/v2/functions/SignerFunctionTest.java | 9 +- .../v2/handlers/OAuthErrorHandlerTest.java | 16 +-- .../oauth/v2/internal/BaseOAuthApiLiveTest.java | 7 +- .../BaseOAuthAuthenticatedApiLiveTest.java | 14 +- 45 files changed, 630 insertions(+), 889 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/6c839f13/google-cloud-storage/src/main/java/org/jclouds/googlecloudstorage/GoogleCloudStorageApiMetadata.java ---------------------------------------------------------------------- diff --git a/google-cloud-storage/src/main/java/org/jclouds/googlecloudstorage/GoogleCloudStorageApiMetadata.java b/google-cloud-storage/src/main/java/org/jclouds/googlecloudstorage/GoogleCloudStorageApiMetadata.java index 4cf6a49..604e4ed 100644 --- a/google-cloud-storage/src/main/java/org/jclouds/googlecloudstorage/GoogleCloudStorageApiMetadata.java +++ b/google-cloud-storage/src/main/java/org/jclouds/googlecloudstorage/GoogleCloudStorageApiMetadata.java @@ -21,7 +21,7 @@ import static org.jclouds.googlecloudstorage.reference.GoogleCloudStorageConstan import static org.jclouds.googlecloudstorage.reference.GoogleCloudStorageConstants.OPERATION_COMPLETE_INTERVAL; import static org.jclouds.googlecloudstorage.reference.GoogleCloudStorageConstants.OPERATION_COMPLETE_TIMEOUT; import static org.jclouds.oauth.v2.config.OAuthProperties.AUDIENCE; -import static org.jclouds.oauth.v2.config.OAuthProperties.SIGNATURE_OR_MAC_ALGORITHM; +import static org.jclouds.oauth.v2.config.OAuthProperties.JWS_ALG; import static org.jclouds.reflect.Reflection2.typeToken; import java.net.URI; @@ -60,7 +60,7 @@ public class GoogleCloudStorageApiMetadata extends BaseHttpApiMetadata<GoogleClo Properties properties = BaseHttpApiMetadata.defaultProperties(); properties.put("oauth.endpoint", "https://accounts.google.com/o/oauth2/token"); properties.put(AUDIENCE, "https://accounts.google.com/o/oauth2/token"); - properties.put(SIGNATURE_OR_MAC_ALGORITHM, "RS256"); + properties.put(JWS_ALG, "RS256"); properties.put(PROPERTY_SESSION_INTERVAL, 3600); properties.put(OPERATION_COMPLETE_INTERVAL, 2000); properties.put(OPERATION_COMPLETE_TIMEOUT, 600000); http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/6c839f13/google-cloud-storage/src/test/java/org/jclouds/googlecloudstorage/internal/BaseGoogleCloudStorageExpectTest.java ---------------------------------------------------------------------- diff --git a/google-cloud-storage/src/test/java/org/jclouds/googlecloudstorage/internal/BaseGoogleCloudStorageExpectTest.java b/google-cloud-storage/src/test/java/org/jclouds/googlecloudstorage/internal/BaseGoogleCloudStorageExpectTest.java index bf09591..d0976c7 100644 --- a/google-cloud-storage/src/test/java/org/jclouds/googlecloudstorage/internal/BaseGoogleCloudStorageExpectTest.java +++ b/google-cloud-storage/src/test/java/org/jclouds/googlecloudstorage/internal/BaseGoogleCloudStorageExpectTest.java @@ -27,6 +27,8 @@ import static org.jclouds.crypto.Pems.privateKeySpec; import static org.jclouds.crypto.Pems.publicKeySpec; import static org.jclouds.crypto.PemsTest.PRIVATE_KEY; import static org.jclouds.crypto.PemsTest.PUBLIC_KEY; +import static org.jclouds.oauth.v2.JWSAlgorithms.NONE; +import static org.jclouds.oauth.v2.config.OAuthProperties.JWS_ALG; import java.io.IOException; import java.net.URI; @@ -49,13 +51,13 @@ import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpResponse; import org.jclouds.io.Payload; import org.jclouds.io.payloads.ByteSourcePayload; -import org.jclouds.oauth.v2.OAuthConstants; -import org.jclouds.oauth.v2.config.OAuthProperties; +import org.jclouds.oauth.v2.functions.BuildTokenRequest; +import org.jclouds.oauth.v2.functions.BuildTokenRequest.TestBuildTokenRequest; import org.jclouds.rest.internal.BaseRestApiExpectTest; import org.jclouds.ssh.SshKeys; + import com.google.common.base.Joiner; import com.google.common.base.Supplier; -import com.google.common.base.Suppliers; import com.google.common.io.ByteSource; import com.google.common.io.Resources; import com.google.inject.Binder; @@ -89,9 +91,8 @@ public class BaseGoogleCloudStorageExpectTest<T> extends BaseRestApiExpectTest<T return new Module() { @Override public void configure(Binder binder) { - // Predicatable time - binder.bind(new TypeLiteral<Supplier<Long>>() { - }).toInstance(Suppliers.ofInstance(0L)); + // Predictable time + binder.bind(BuildTokenRequest.class).to(TestBuildTokenRequest.class); try { KeyFactory keyfactory = KeyFactory.getInstance("RSA"); PrivateKey privateKey = keyfactory.generatePrivate(privateKeySpec(ByteSource.wrap(PRIVATE_KEY @@ -135,7 +136,7 @@ public class BaseGoogleCloudStorageExpectTest<T> extends BaseRestApiExpectTest<T protected Properties setupProperties() { Properties props = super.setupProperties(); // use no sig algorithm for expect tests (means no credential is required either) - props.put(OAuthProperties.SIGNATURE_OR_MAC_ALGORITHM, OAuthConstants.NO_ALGORITHM); + props.put(JWS_ALG, NONE); return props; } http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/6c839f13/google-compute-engine/pom.xml ---------------------------------------------------------------------- diff --git a/google-compute-engine/pom.xml b/google-compute-engine/pom.xml index 38de821..dd9a379 100644 --- a/google-compute-engine/pom.xml +++ b/google-compute-engine/pom.xml @@ -37,7 +37,7 @@ <test.google-compute-engine.credential>Private key (PEM encoded PKCS12 file or literal) associated with the client_email</test.google-compute-engine.credential> <!-- Add this property to use a different project, or avoid looking up the project for each test. --> <test.jclouds.google-compute-engine.project-name></test.jclouds.google-compute-engine.project-name> - <test.jclouds.oauth.credential-type>serviceAccountCredentials</test.jclouds.oauth.credential-type> + <test.jclouds.oauth.credential-type>p12PrivateKeyCredentials</test.jclouds.oauth.credential-type> <test.google-compute-engine.api-version>v1</test.google-compute-engine.api-version> <test.google-compute-engine.build-version/> <test.google-compute-engine.template>imageNameMatches=debian-7-wheezy-v[0-9]*,locationId=us-central1-a,minRam=2048</test.google-compute-engine.template> http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/6c839f13/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/GoogleComputeEngineApiMetadata.java ---------------------------------------------------------------------- diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/GoogleComputeEngineApiMetadata.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/GoogleComputeEngineApiMetadata.java index 32da0eb..4ddea1b 100644 --- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/GoogleComputeEngineApiMetadata.java +++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/GoogleComputeEngineApiMetadata.java @@ -23,7 +23,7 @@ import static org.jclouds.googlecomputeengine.config.GoogleComputeEngineProperti import static org.jclouds.googlecomputeengine.config.GoogleComputeEngineProperties.OPERATION_COMPLETE_TIMEOUT; import static org.jclouds.googlecomputeengine.config.GoogleComputeEngineProperties.PROJECT_NAME; import static org.jclouds.oauth.v2.config.OAuthProperties.AUDIENCE; -import static org.jclouds.oauth.v2.config.OAuthProperties.SIGNATURE_OR_MAC_ALGORITHM; +import static org.jclouds.oauth.v2.config.OAuthProperties.JWS_ALG; import static org.jclouds.reflect.Reflection2.typeToken; import java.net.URI; @@ -58,7 +58,7 @@ public class GoogleComputeEngineApiMetadata extends BaseHttpApiMetadata<GoogleCo Properties properties = BaseHttpApiMetadata.defaultProperties(); properties.put("oauth.endpoint", "https://accounts.google.com/o/oauth2/token"); properties.put(AUDIENCE, "https://accounts.google.com/o/oauth2/token"); - properties.put(SIGNATURE_OR_MAC_ALGORITHM, "RS256"); + properties.put(JWS_ALG, "RS256"); properties.put(PROPERTY_SESSION_INTERVAL, 3600); properties.put(OPERATION_COMPLETE_INTERVAL, 500); properties.put(OPERATION_COMPLETE_TIMEOUT, 600000); http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/6c839f13/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/config/GoogleComputeEngineProperties.java ---------------------------------------------------------------------- diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/config/GoogleComputeEngineProperties.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/config/GoogleComputeEngineProperties.java index 4f2be3d..e554fc0 100644 --- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/config/GoogleComputeEngineProperties.java +++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/config/GoogleComputeEngineProperties.java @@ -24,7 +24,7 @@ import com.google.common.annotations.Beta; public final class GoogleComputeEngineProperties { /** - * How requests are authorized using OAuth. Defaults to {@link org.jclouds.oauth.v2.config.CredentialType#SERVICE_ACCOUNT_CREDENTIALS}. + * How requests are authorized using OAuth. Defaults to {@link org.jclouds.oauth.v2.config.CredentialType#P12_PRIVATE_KEY_CREDENTIALS}. * * @see org.jclouds.oauth.v2.config.CredentialType */ http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/6c839f13/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/internal/BaseGoogleComputeEngineApiMockTest.java ---------------------------------------------------------------------- diff --git a/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/internal/BaseGoogleComputeEngineApiMockTest.java b/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/internal/BaseGoogleComputeEngineApiMockTest.java index ab691e8..0f53fba 100644 --- a/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/internal/BaseGoogleComputeEngineApiMockTest.java +++ b/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/internal/BaseGoogleComputeEngineApiMockTest.java @@ -23,9 +23,7 @@ import static javax.ws.rs.core.MediaType.APPLICATION_JSON; import static org.jclouds.googlecomputeengine.config.GoogleComputeEngineProperties.CREDENTIAL_TYPE; import static org.jclouds.googlecomputeengine.config.GoogleComputeEngineProperties.IMAGE_PROJECTS; import static org.jclouds.googlecomputeengine.config.GoogleComputeEngineProperties.PROJECT_NAME; -import static org.jclouds.oauth.v2.OAuthConstants.NO_ALGORITHM; import static org.jclouds.oauth.v2.config.CredentialType.BEARER_TOKEN_CREDENTIALS; -import static org.jclouds.oauth.v2.config.OAuthProperties.SIGNATURE_OR_MAC_ALGORITHM; import static org.jclouds.util.Strings2.toStringAndClose; import static org.testng.Assert.assertEquals; @@ -74,7 +72,6 @@ public class BaseGoogleComputeEngineApiMockTest { overrides.put(PROJECT_NAME, "party"); overrides.put(IMAGE_PROJECTS, "debian-cloud"); overrides.put(CREDENTIAL_TYPE, BEARER_TOKEN_CREDENTIALS.toString()); - overrides.put(SIGNATURE_OR_MAC_ALGORITHM, NO_ALGORITHM); // TODO: this should be implied by the above. return ContextBuilder.newBuilder(new GoogleComputeEngineProviderMetadata()) .credentials(identity, credential) .endpoint(url("")) http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/6c839f13/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/internal/BaseGoogleComputeEngineExpectTest.java ---------------------------------------------------------------------- diff --git a/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/internal/BaseGoogleComputeEngineExpectTest.java b/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/internal/BaseGoogleComputeEngineExpectTest.java index bf9bead..220f5f0 100644 --- a/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/internal/BaseGoogleComputeEngineExpectTest.java +++ b/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/internal/BaseGoogleComputeEngineExpectTest.java @@ -21,8 +21,8 @@ import static com.google.common.base.Throwables.propagate; import static com.google.common.io.BaseEncoding.base64Url; import static javax.ws.rs.core.MediaType.APPLICATION_JSON; import static org.jclouds.googlecomputeengine.config.GoogleComputeEngineProperties.PROJECT_NAME; -import static org.jclouds.oauth.v2.OAuthConstants.NO_ALGORITHM; -import static org.jclouds.oauth.v2.config.OAuthProperties.SIGNATURE_OR_MAC_ALGORITHM; +import static org.jclouds.oauth.v2.JWSAlgorithms.NONE; +import static org.jclouds.oauth.v2.config.OAuthProperties.JWS_ALG; import static org.jclouds.util.Strings2.toStringAndClose; import java.io.IOException; @@ -78,7 +78,7 @@ public class BaseGoogleComputeEngineExpectTest<T> extends BaseRestApiExpectTest< Properties props = super.setupProperties(); props.put(PROJECT_NAME, "party"); // use no sig algorithm for expect tests (means no credential is required either) - props.put(SIGNATURE_OR_MAC_ALGORITHM, NO_ALGORITHM); + props.put(JWS_ALG, NONE); return props; } http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/6c839f13/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/internal/GoogleComputeEngineTestModule.java ---------------------------------------------------------------------- diff --git a/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/internal/GoogleComputeEngineTestModule.java b/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/internal/GoogleComputeEngineTestModule.java index 219b716..8541fc5 100644 --- a/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/internal/GoogleComputeEngineTestModule.java +++ b/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/internal/GoogleComputeEngineTestModule.java @@ -37,10 +37,11 @@ import java.security.interfaces.RSAPublicKey; import java.util.concurrent.atomic.AtomicInteger; import org.jclouds.crypto.Crypto; +import org.jclouds.oauth.v2.functions.BuildTokenRequest; +import org.jclouds.oauth.v2.functions.BuildTokenRequest.TestBuildTokenRequest; import org.jclouds.ssh.SshKeys; import com.google.common.base.Supplier; -import com.google.common.base.Suppliers; import com.google.common.io.ByteSource; import com.google.inject.Binder; import com.google.inject.Module; @@ -67,8 +68,7 @@ enum GoogleComputeEngineTestModule implements Module { @Override public void configure(Binder binder) { // Predictable time - binder.bind(new TypeLiteral<Supplier<Long>>() { - }).toInstance(Suppliers.ofInstance(0L)); + binder.bind(BuildTokenRequest.class).to(TestBuildTokenRequest.class); // Predictable ssh keys Crypto crypto = createMock(Crypto.class); http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/6c839f13/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/internal/TestProperties.java ---------------------------------------------------------------------- diff --git a/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/internal/TestProperties.java b/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/internal/TestProperties.java index a9a3efd..7c43910 100644 --- a/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/internal/TestProperties.java +++ b/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/internal/TestProperties.java @@ -18,10 +18,8 @@ package org.jclouds.googlecomputeengine.internal; import static org.jclouds.googlecomputeengine.config.GoogleComputeEngineProperties.CREDENTIAL_TYPE; import static org.jclouds.googlecomputeengine.config.GoogleComputeEngineProperties.PROJECT_NAME; -import static org.jclouds.oauth.v2.OAuthConstants.NO_ALGORITHM; import static org.jclouds.oauth.v2.OAuthTestUtils.setCredential; -import static org.jclouds.oauth.v2.config.CredentialType.BEARER_TOKEN_CREDENTIALS; -import static org.jclouds.oauth.v2.config.OAuthProperties.SIGNATURE_OR_MAC_ALGORITHM; +import static org.jclouds.oauth.v2.config.CredentialType.P12_PRIVATE_KEY_CREDENTIALS; import java.util.Properties; @@ -33,12 +31,9 @@ public final class TestProperties { public static Properties apply(Properties props) { setIfTestSystemPropertyPresent(props, PROJECT_NAME); setIfTestSystemPropertyPresent(props, CREDENTIAL_TYPE); - if (props.containsKey(CREDENTIAL_TYPE)) { - if (CredentialType.fromValue(props.getProperty(CREDENTIAL_TYPE)) == BEARER_TOKEN_CREDENTIALS) { - props.put(SIGNATURE_OR_MAC_ALGORITHM, NO_ALGORITHM); // TODO: this should be implied by the above. - } else { - setCredential(props, "google-compute-engine.credential"); - } + if (props.containsKey(CREDENTIAL_TYPE) + && CredentialType.fromValue(props.getProperty(CREDENTIAL_TYPE)) == P12_PRIVATE_KEY_CREDENTIALS) { + setCredential(props, "google-compute-engine.credential"); } return props; } http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/6c839f13/oauth/README ---------------------------------------------------------------------- diff --git a/oauth/README b/oauth/README index 32b571a..1c5f73b 100644 --- a/oauth/README +++ b/oauth/README @@ -6,11 +6,6 @@ Mandatory: oauth.endpoint - the endpoint to use for authentication (e.g., "http://accounts.google.com/o/oauth2/token" in Google Api's) oauth.audience - the "audience" of the token request (e.g., "http://accounts.google.com/o/oauth2/token" in Google Api's) -Optional: -- each application may expose a Map<String,String> of additional claims to be added to the token request, -these should be annotated/named with "oauth.additional-claims" -oauth.signature-or-mac-algorithm - the algorithms to use when signing the token request. - Running the live test: mvn clean install -Plive\ @@ -18,5 +13,4 @@ mvn clean install -Plive\ -Dtest.oauth.credential=<accout pk in pem format>\ -Dtest.oauth.endpoint=https://accounts.google.com/o/oauth2/token\ -Dtest.jclouds.oauth.audience=https://accounts.google.com/o/oauth2/token\ - -Dtest.jclouds.oauth.signature-or-mac-algorithm=RS256\ -Dtest.jclouds.oauth.scopes=https://www.googleapis.com/auth/prediction \ No newline at end of file http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/6c839f13/oauth/pom.xml ---------------------------------------------------------------------- diff --git a/oauth/pom.xml b/oauth/pom.xml index a13d15b..275ce4b 100644 --- a/oauth/pom.xml +++ b/oauth/pom.xml @@ -35,7 +35,7 @@ <test.oauth.identity>FIX_ME</test.oauth.identity> <test.oauth.credential>FIX_ME</test.oauth.credential> <test.oauth.endpoint>FIX_ME</test.oauth.endpoint> - <test.jclouds.oauth.signature-or-mac-algorithm>FIX_ME</test.jclouds.oauth.signature-or-mac-algorithm> + <test.jclouds.oauth.jws-alg>RS256</test.jclouds.oauth.jws-alg> <test.jclouds.oauth.audience>FIX_ME</test.jclouds.oauth.audience> <test.jclouds.oauth.scopes>FIX_ME</test.jclouds.oauth.scopes> <test.oauth.api-version>2</test.oauth.api-version> @@ -121,7 +121,7 @@ <test.oauth.endpoint>${test.oauth.endpoint}</test.oauth.endpoint> <test.oauth.api-version>${test.oauth.api-version}</test.oauth.api-version> <test.oauth.build-version>${test.oauth.build-version}</test.oauth.build-version> - <test.jclouds.oauth.signature-or-mac-algorithm>${test.jclouds.oauth.signature-or-mac-algorithm}</test.jclouds.oauth.signature-or-mac-algorithm> + <test.jclouds.oauth.jws-alg>${test.jclouds.oauth.jws-alg}</test.jclouds.oauth.jws-alg> <test.jclouds.oauth.audience>${test.jclouds.oauth.audience}</test.jclouds.oauth.audience> <test.jclouds.oauth.scopes>${test.jclouds.oauth.scopes}</test.jclouds.oauth.scopes> </systemPropertyVariables> http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/6c839f13/oauth/src/main/java/org/jclouds/oauth/v2/JWSAlgorithms.java ---------------------------------------------------------------------- diff --git a/oauth/src/main/java/org/jclouds/oauth/v2/JWSAlgorithms.java b/oauth/src/main/java/org/jclouds/oauth/v2/JWSAlgorithms.java new file mode 100644 index 0000000..072fc53 --- /dev/null +++ b/oauth/src/main/java/org/jclouds/oauth/v2/JWSAlgorithms.java @@ -0,0 +1,75 @@ +/* + * 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.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.util.List; + +import com.google.common.collect.ImmutableList; + +/** + * JSON Web Signature Algorithms + * <p/> + * We only support <a href="http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-36#section-3.1">required + * or recommended algorithms</a>, with the exception of {@code none}, which is only supported in tests. + */ +public final class JWSAlgorithms { + /** This is a marker algorithm only supported in tests. */ + public static final String NONE = "none"; + + private static final List<String> SUPPORTED_ALGS = ImmutableList.of("ES256", "RS256", "HS256", NONE); + + /** + * Static mapping between the oauth algorithm name and the Crypto provider signature algorithm name and KeyFactory. + */ + private static final List<List<String>> ALG_TO_SIGNATURE_ALG_AND_KEY_FACTORY = ImmutableList.<List<String>>of( // + ImmutableList.of(SUPPORTED_ALGS.get(0), "SHA256withECDSA", "EC"), // ECDSA using P-256 and SHA-256 + ImmutableList.of(SUPPORTED_ALGS.get(1), "SHA256withRSA", "RSA"), // RSASSA-PKCS-v1_5 using SHA-256 + ImmutableList.of(SUPPORTED_ALGS.get(2), "HmacSHA256", "DiffieHellman") // HMAC using SHA-256 + ); + + /** Ordered list of supported algorithms by recommendation. */ + public static List<String> supportedAlgs() { + return SUPPORTED_ALGS; + } + + public static String macOrSignature(String jwsAlg) { + return ALG_TO_SIGNATURE_ALG_AND_KEY_FACTORY.get(indexOf(jwsAlg)).get(1); + } + + public static KeyFactory keyFactory(String jwsAlg) { + String keyFactoryAlgorithm = ALG_TO_SIGNATURE_ALG_AND_KEY_FACTORY.get(indexOf(jwsAlg)).get(2); + try { + return KeyFactory.getInstance(keyFactoryAlgorithm); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError("Invalid contents in JWSAlgorithms! " + e.getMessage()); + } + } + + private static int indexOf(String jwsAlg) { + int result = SUPPORTED_ALGS.indexOf(checkNotNull(jwsAlg, "jwsAlg")); + checkArgument(result != -1, "JSON Web Signature alg %s is not in the supported list %s", jwsAlg, SUPPORTED_ALGS); + return result; + } + + private JWSAlgorithms() { + } +} http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/6c839f13/oauth/src/main/java/org/jclouds/oauth/v2/OAuthApi.java ---------------------------------------------------------------------- diff --git a/oauth/src/main/java/org/jclouds/oauth/v2/OAuthApi.java b/oauth/src/main/java/org/jclouds/oauth/v2/OAuthApi.java index d3082fc..770e5e8 100644 --- a/oauth/src/main/java/org/jclouds/oauth/v2/OAuthApi.java +++ b/oauth/src/main/java/org/jclouds/oauth/v2/OAuthApi.java @@ -24,8 +24,8 @@ import javax.inject.Named; import javax.ws.rs.Consumes; import javax.ws.rs.POST; -import org.jclouds.oauth.v2.binders.OAuthTokenBinder; -import org.jclouds.oauth.v2.config.Authentication; +import org.jclouds.oauth.v2.binders.TokenBinder; +import org.jclouds.oauth.v2.config.OAuth; import org.jclouds.oauth.v2.domain.Token; import org.jclouds.oauth.v2.domain.TokenRequest; import org.jclouds.rest.AuthorizationException; @@ -39,7 +39,7 @@ import org.jclouds.rest.annotations.Endpoint; * OAuthAuthenticator as a request filter, which in turn uses this class to * perform token requests. */ -@Endpoint(Authentication.class) +@Endpoint(OAuth.class) public interface OAuthApi extends Closeable { /** @@ -57,5 +57,5 @@ public interface OAuthApi extends Closeable { @Named("authenticate") @POST @Consumes(APPLICATION_JSON) - Token authenticate(@BinderParam(OAuthTokenBinder.class) TokenRequest tokenRequest) throws AuthorizationException; + Token authenticate(@BinderParam(TokenBinder.class) TokenRequest tokenRequest) throws AuthorizationException; } http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/6c839f13/oauth/src/main/java/org/jclouds/oauth/v2/OAuthApiMetadata.java ---------------------------------------------------------------------- diff --git a/oauth/src/main/java/org/jclouds/oauth/v2/OAuthApiMetadata.java b/oauth/src/main/java/org/jclouds/oauth/v2/OAuthApiMetadata.java index 58714e0..66f61bb 100644 --- a/oauth/src/main/java/org/jclouds/oauth/v2/OAuthApiMetadata.java +++ b/oauth/src/main/java/org/jclouds/oauth/v2/OAuthApiMetadata.java @@ -17,9 +17,9 @@ package org.jclouds.oauth.v2; import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL; -import static org.jclouds.oauth.v2.config.CredentialType.SERVICE_ACCOUNT_CREDENTIALS; +import static org.jclouds.oauth.v2.config.CredentialType.P12_PRIVATE_KEY_CREDENTIALS; import static org.jclouds.oauth.v2.config.OAuthProperties.CREDENTIAL_TYPE; -import static org.jclouds.oauth.v2.config.OAuthProperties.SIGNATURE_OR_MAC_ALGORITHM; +import static org.jclouds.oauth.v2.config.OAuthProperties.JWS_ALG; import java.net.URI; import java.util.Properties; @@ -51,8 +51,8 @@ public class OAuthApiMetadata extends BaseHttpApiMetadata<OAuthApi> { public static Properties defaultProperties() { Properties properties = BaseHttpApiMetadata.defaultProperties(); - properties.put(SIGNATURE_OR_MAC_ALGORITHM, "RS256"); - properties.put(CREDENTIAL_TYPE, SERVICE_ACCOUNT_CREDENTIALS); + properties.put(JWS_ALG, "RS256"); + properties.put(CREDENTIAL_TYPE, P12_PRIVATE_KEY_CREDENTIALS); properties.put(PROPERTY_SESSION_INTERVAL, 3600); return properties; } http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/6c839f13/oauth/src/main/java/org/jclouds/oauth/v2/OAuthConstants.java ---------------------------------------------------------------------- diff --git a/oauth/src/main/java/org/jclouds/oauth/v2/OAuthConstants.java b/oauth/src/main/java/org/jclouds/oauth/v2/OAuthConstants.java deleted file mode 100644 index 9b140d4..0000000 --- a/oauth/src/main/java/org/jclouds/oauth/v2/OAuthConstants.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * 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 com.google.common.collect.ImmutableMap; - -import java.util.Map; - -/** - * The constants for OAuth \ - */ -public final class OAuthConstants { - - /** - * Selected algorithm when a signature or mac isn't required. - */ - public static final String NO_ALGORITHM = "none"; - - /** - * Static mapping between the oauth algorithm name and the Crypto provider signature algorithm name. - * - * @see <a href="http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-06#section-3.1">doc</a> - * @see org.jclouds.oauth.v2.json.JWTTokenRequestFormat - */ - public static final Map<String, String> OAUTH_ALGORITHM_NAMES_TO_SIGNATURE_ALGORITHM_NAMES = ImmutableMap - .<String, String>builder() - .put("RS256", "SHA256withRSA") - .put("RS384", "SHA384withRSA") - .put("RS512", "SHA512withRSA") - .put("HS256", "HmacSHA256") - .put("HS384", "HmacSHA384") - .put("HS512", "HmacSHA512") - .put("ES256", "SHA256withECDSA") - .put("ES384", "SHA384withECDSA") - .put("ES512", "SHA512withECDSA") - .put(NO_ALGORITHM, NO_ALGORITHM).build(); - - /** - * Static mapping between the oauth algorithm name and the Crypto provider KeyFactory algorithm name. - * - * @see <a href="http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-06#section-3.1">doc</a> - */ - public static final Map<String, String> OAUTH_ALGORITHM_NAMES_TO_KEYFACTORY_ALGORITHM_NAMES = ImmutableMap - .<String, String>builder() - .put("RS256", "RSA") - .put("RS384", "RSA") - .put("RS512", "RSA") - .put("HS256", "DiffieHellman") - .put("HS384", "DiffieHellman") - .put("HS512", "DiffieHellman") - .put("ES256", "EC") - .put("ES384", "EC") - .put("ES512", "EC") - .put(NO_ALGORITHM, NO_ALGORITHM).build(); - - /** - * The (optional) set of additional claims to use, provided in Map<String,String> form - */ - public static final String ADDITIONAL_CLAIMS = "jclouds.oauth.additional-claims"; - - private OAuthConstants() { - throw new AssertionError("intentionally unimplemented"); - } -} http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/6c839f13/oauth/src/main/java/org/jclouds/oauth/v2/binders/OAuthTokenBinder.java ---------------------------------------------------------------------- diff --git a/oauth/src/main/java/org/jclouds/oauth/v2/binders/OAuthTokenBinder.java b/oauth/src/main/java/org/jclouds/oauth/v2/binders/OAuthTokenBinder.java deleted file mode 100644 index e62d9c7..0000000 --- a/oauth/src/main/java/org/jclouds/oauth/v2/binders/OAuthTokenBinder.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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.binders; - -import static com.google.common.base.Charsets.UTF_8; -import static com.google.common.base.Joiner.on; -import static com.google.common.io.BaseEncoding.base64Url; -import static org.jclouds.io.Payloads.newUrlEncodedFormPayload; - -import javax.inject.Inject; - -import org.jclouds.http.HttpRequest; -import org.jclouds.io.Payload; -import org.jclouds.json.Json; -import org.jclouds.oauth.v2.domain.TokenRequest; -import org.jclouds.rest.Binder; - -import com.google.common.base.Function; -import com.google.common.collect.ImmutableMultimap; - -/** - * Formats a token request into JWT format namely: - * <ol> - * <li>Transforms the token request to json.</li> - * <li>Creates the base64 header.claimset portions of the payload.</li> - * <li>Uses the provided signer function to create a signature.</li> - * <li>Creates the full url encoded payload as described in: <a href="https://developers.google.com/accounts/docs/OAuth2ServiceAccount">OAuth2ServiceAccount</a></li> - * </ol> - */ -public final class OAuthTokenBinder implements Binder { - private static final String ASSERTION_FORM_PARAM = "assertion"; - private static final String GRANT_TYPE_FORM_PARAM = "grant_type"; - private static final String GRANT_TYPE_JWT_BEARER = "urn:ietf:params:oauth:grant-type:jwt-bearer"; - - private final Function<byte[], byte[]> signer; - private final Json json; - - @Inject OAuthTokenBinder(Function<byte[], byte[]> signer, Json json) { - this.signer = signer; - this.json = json; - } - - @Override public <R extends HttpRequest> R bindToRequest(R request, Object input) { - TokenRequest tokenRequest = (TokenRequest) input; - String encodedHeader = json.toJson(tokenRequest.header()); - String encodedClaimSet = json.toJson(tokenRequest.claimSet()); - - encodedHeader = base64Url().omitPadding().encode(encodedHeader.getBytes(UTF_8)); - encodedClaimSet = base64Url().omitPadding().encode(encodedClaimSet.getBytes(UTF_8)); - - byte[] signature = signer.apply(on(".").join(encodedHeader, encodedClaimSet).getBytes(UTF_8)); - String encodedSignature = signature != null ? base64Url().omitPadding().encode(signature) : ""; - - // the final assertion in base 64 encoded {header}.{claimSet}.{signature} format - String assertion = on(".").join(encodedHeader, encodedClaimSet, encodedSignature); - Payload payload = newUrlEncodedFormPayload(ImmutableMultimap.<String, String> builder() - .put(GRANT_TYPE_FORM_PARAM, GRANT_TYPE_JWT_BEARER) - .put(ASSERTION_FORM_PARAM, assertion).build()); - - return (R) request.toBuilder().payload(payload).build(); - } -} http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/6c839f13/oauth/src/main/java/org/jclouds/oauth/v2/binders/TokenBinder.java ---------------------------------------------------------------------- diff --git a/oauth/src/main/java/org/jclouds/oauth/v2/binders/TokenBinder.java b/oauth/src/main/java/org/jclouds/oauth/v2/binders/TokenBinder.java new file mode 100644 index 0000000..0782000 --- /dev/null +++ b/oauth/src/main/java/org/jclouds/oauth/v2/binders/TokenBinder.java @@ -0,0 +1,77 @@ +/* + * 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.binders; + +import static com.google.common.base.Charsets.UTF_8; +import static com.google.common.base.Joiner.on; +import static com.google.common.io.BaseEncoding.base64Url; +import static org.jclouds.io.Payloads.newUrlEncodedFormPayload; + +import javax.inject.Inject; + +import org.jclouds.http.HttpRequest; +import org.jclouds.io.Payload; +import org.jclouds.json.Json; +import org.jclouds.oauth.v2.domain.TokenRequest; +import org.jclouds.rest.Binder; + +import com.google.common.base.Function; +import com.google.common.base.Supplier; +import com.google.common.collect.ImmutableMultimap; + +/** + * Formats a token request into JWT format namely: + * <ol> + * <li>Transforms the token request to json.</li> + * <li>Creates the base64 header.claimset portions of the payload.</li> + * <li>Uses the provided signer function to create a signature.</li> + * <li>Creates the full url encoded payload as described in: <a href="https://developers.google.com/accounts/docs/OAuth2ServiceAccount">OAuth2ServiceAccount</a></li> + * </ol> + */ +public final class TokenBinder implements Binder { + private static final String ASSERTION_FORM_PARAM = "assertion"; + private static final String GRANT_TYPE_FORM_PARAM = "grant_type"; + private static final String GRANT_TYPE_JWT_BEARER = "urn:ietf:params:oauth:grant-type:jwt-bearer"; + + private final Supplier<Function<byte[], byte[]>> signer; + private final Json json; + + @Inject TokenBinder(Supplier<Function<byte[], byte[]>> signer, Json json) { + this.signer = signer; + this.json = json; + } + + @Override public <R extends HttpRequest> R bindToRequest(R request, Object input) { + TokenRequest tokenRequest = (TokenRequest) input; + String encodedHeader = json.toJson(tokenRequest.header()); + String encodedClaimSet = json.toJson(tokenRequest.claimSet()); + + encodedHeader = base64Url().omitPadding().encode(encodedHeader.getBytes(UTF_8)); + encodedClaimSet = base64Url().omitPadding().encode(encodedClaimSet.getBytes(UTF_8)); + + byte[] signature = signer.get().apply(on(".").join(encodedHeader, encodedClaimSet).getBytes(UTF_8)); + String encodedSignature = signature != null ? base64Url().omitPadding().encode(signature) : ""; + + // the final assertion in base 64 encoded {header}.{claimSet}.{signature} format + String assertion = on(".").join(encodedHeader, encodedClaimSet, encodedSignature); + Payload payload = newUrlEncodedFormPayload(ImmutableMultimap.<String, String> builder() + .put(GRANT_TYPE_FORM_PARAM, GRANT_TYPE_JWT_BEARER) + .put(ASSERTION_FORM_PARAM, assertion).build()); + + return (R) request.toBuilder().payload(payload).build(); + } +} http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/6c839f13/oauth/src/main/java/org/jclouds/oauth/v2/config/Authentication.java ---------------------------------------------------------------------- diff --git a/oauth/src/main/java/org/jclouds/oauth/v2/config/Authentication.java b/oauth/src/main/java/org/jclouds/oauth/v2/config/Authentication.java deleted file mode 100644 index 24f6851..0000000 --- a/oauth/src/main/java/org/jclouds/oauth/v2/config/Authentication.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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.config; - - -import javax.inject.Qualifier; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Qualifies OAuth related resources, such as Endpoint. - * - * @see org.jclouds.oauth.v2.OAuthApi - */ -@Retention(value = RetentionPolicy.RUNTIME) -@Target(value = {ElementType.TYPE, ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD}) -@Qualifier -public @interface Authentication { -} http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/6c839f13/oauth/src/main/java/org/jclouds/oauth/v2/config/CredentialType.java ---------------------------------------------------------------------- diff --git a/oauth/src/main/java/org/jclouds/oauth/v2/config/CredentialType.java b/oauth/src/main/java/org/jclouds/oauth/v2/config/CredentialType.java index 24a658b..141564b 100644 --- a/oauth/src/main/java/org/jclouds/oauth/v2/config/CredentialType.java +++ b/oauth/src/main/java/org/jclouds/oauth/v2/config/CredentialType.java @@ -16,27 +16,23 @@ */ package org.jclouds.oauth.v2.config; +import static com.google.common.base.CaseFormat.LOWER_CAMEL; +import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE; import static com.google.common.base.Preconditions.checkNotNull; -import com.google.common.base.CaseFormat; - -/** - * Decides what type of credentials createContext is supplied with. - */ +/** Defines the contents of the credential field in {@link org.jclouds.ContextBuilder#credentials(String, String)}. */ public enum CredentialType { BEARER_TOKEN_CREDENTIALS, - SERVICE_ACCOUNT_CREDENTIALS; + /** Contents are a PEM-encoded P12 Private Key. */ + P12_PRIVATE_KEY_CREDENTIALS; - @Override - public String toString() { - return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, name()); + @Override public String toString() { + return UPPER_UNDERSCORE.to(LOWER_CAMEL, name()); } public static CredentialType fromValue(String credentialType) { - return valueOf(CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, checkNotNull(credentialType, - "credentialType"))); + return valueOf(LOWER_CAMEL.to(UPPER_UNDERSCORE, checkNotNull(credentialType, "credentialType"))); } - } http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/6c839f13/oauth/src/main/java/org/jclouds/oauth/v2/config/OAuth.java ---------------------------------------------------------------------- diff --git a/oauth/src/main/java/org/jclouds/oauth/v2/config/OAuth.java b/oauth/src/main/java/org/jclouds/oauth/v2/config/OAuth.java new file mode 100644 index 0000000..6255e50 --- /dev/null +++ b/oauth/src/main/java/org/jclouds/oauth/v2/config/OAuth.java @@ -0,0 +1,35 @@ +/* + * 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.config; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import javax.inject.Qualifier; + +/** + * Qualifies OAuth related resources, such as Endpoint. + * + * @see org.jclouds.oauth.v2.OAuthApi + */ +@Retention(value = RetentionPolicy.RUNTIME) +@Target(value = {ElementType.TYPE, ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD}) +@Qualifier +public @interface OAuth { +} http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/6c839f13/oauth/src/main/java/org/jclouds/oauth/v2/config/OAuthAuthenticationModule.java ---------------------------------------------------------------------- diff --git a/oauth/src/main/java/org/jclouds/oauth/v2/config/OAuthAuthenticationModule.java b/oauth/src/main/java/org/jclouds/oauth/v2/config/OAuthAuthenticationModule.java index fb2d413..4d238f1 100644 --- a/oauth/src/main/java/org/jclouds/oauth/v2/config/OAuthAuthenticationModule.java +++ b/oauth/src/main/java/org/jclouds/oauth/v2/config/OAuthAuthenticationModule.java @@ -30,9 +30,6 @@ import com.google.common.base.Suppliers; import com.google.inject.AbstractModule; import com.google.inject.Provides; -/** - * An OAuth module to be used form other providers. - */ public class OAuthAuthenticationModule extends AbstractModule { @Override @@ -40,13 +37,10 @@ public class OAuthAuthenticationModule extends AbstractModule { bindHttpApi(binder(), OAuthApi.class); } - /** - * When oauth is used as a module the oauth endpoint is a normal property - */ @Provides @Singleton - @Authentication - protected Supplier<URI> provideAuthenticationEndpoint(@Named("oauth.endpoint") String endpoint) { + @OAuth + protected Supplier<URI> oauthEndpoint(@Named("oauth.endpoint") String endpoint) { return Suppliers.ofInstance(URI.create(endpoint)); } } http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/6c839f13/oauth/src/main/java/org/jclouds/oauth/v2/config/OAuthHttpApiModule.java ---------------------------------------------------------------------- diff --git a/oauth/src/main/java/org/jclouds/oauth/v2/config/OAuthHttpApiModule.java b/oauth/src/main/java/org/jclouds/oauth/v2/config/OAuthHttpApiModule.java index 6b4fdf9..3bb7c1a 100644 --- a/oauth/src/main/java/org/jclouds/oauth/v2/config/OAuthHttpApiModule.java +++ b/oauth/src/main/java/org/jclouds/oauth/v2/config/OAuthHttpApiModule.java @@ -37,7 +37,7 @@ public class OAuthHttpApiModule extends HttpApiModule<OAuthApi> { @Provides @Singleton - @Authentication + @OAuth protected Supplier<URI> provideAuthenticationEndpoint(ProviderMetadata providerMetadata) { return Suppliers.ofInstance(URI.create(providerMetadata.getEndpoint())); } http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/6c839f13/oauth/src/main/java/org/jclouds/oauth/v2/config/OAuthModule.java ---------------------------------------------------------------------- diff --git a/oauth/src/main/java/org/jclouds/oauth/v2/config/OAuthModule.java b/oauth/src/main/java/org/jclouds/oauth/v2/config/OAuthModule.java index 788dac4..e831c0f 100644 --- a/oauth/src/main/java/org/jclouds/oauth/v2/config/OAuthModule.java +++ b/oauth/src/main/java/org/jclouds/oauth/v2/config/OAuthModule.java @@ -16,11 +16,14 @@ */ package org.jclouds.oauth.v2.config; +import static java.util.concurrent.TimeUnit.SECONDS; import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL; +import static org.jclouds.oauth.v2.JWSAlgorithms.NONE; +import static org.jclouds.oauth.v2.config.OAuthProperties.JWS_ALG; -import java.util.concurrent.TimeUnit; +import java.security.PrivateKey; -import org.jclouds.oauth.v2.domain.OAuthCredentials; +import org.jclouds.http.HttpRequest; import org.jclouds.oauth.v2.domain.Token; import org.jclouds.oauth.v2.domain.TokenRequest; import org.jclouds.oauth.v2.filters.BearerTokenAuthenticator; @@ -28,12 +31,12 @@ import org.jclouds.oauth.v2.filters.OAuthAuthenticationFilter; import org.jclouds.oauth.v2.filters.OAuthAuthenticator; import org.jclouds.oauth.v2.functions.BuildTokenRequest; import org.jclouds.oauth.v2.functions.FetchToken; -import org.jclouds.oauth.v2.functions.OAuthCredentialsSupplier; +import org.jclouds.oauth.v2.functions.PrivateKeySupplier; import org.jclouds.oauth.v2.functions.SignOrProduceMacForToken; -import org.jclouds.rest.internal.GeneratedHttpRequest; import com.google.common.base.Function; import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; @@ -48,11 +51,10 @@ import com.google.inject.name.Named; public class OAuthModule extends AbstractModule { @Override protected void configure() { - bind(new TypeLiteral<Function<byte[], byte[]>>() {}).to(SignOrProduceMacForToken.class); bind(CredentialType.class).toProvider(CredentialTypeFromPropertyOrDefault.class); - bind(new TypeLiteral<Supplier<OAuthCredentials>>() {}).to(OAuthCredentialsSupplier.class); - bind(new TypeLiteral<Function<GeneratedHttpRequest, TokenRequest>>() {}).to(BuildTokenRequest.class); + bind(new TypeLiteral<Function<HttpRequest, TokenRequest>>() {}).to(BuildTokenRequest.class); bind(new TypeLiteral<Function<TokenRequest, Token>>() {}).to(FetchToken.class); + bind(new TypeLiteral<Supplier<PrivateKey>>() {}).annotatedWith(OAuth.class).to(PrivateKeySupplier.class); } /** @@ -65,21 +67,34 @@ public class OAuthModule extends AbstractModule { @Provides @Singleton public LoadingCache<TokenRequest, Token> provideAccessCache(Function<TokenRequest, Token> getAccess, - @Named(PROPERTY_SESSION_INTERVAL) long - sessionIntervalInSeconds) { + @Named(PROPERTY_SESSION_INTERVAL) long expirationSeconds) { // since the session interval is also the token expiration time requested to the server make the token expire a // bit before the deadline to make sure there aren't session expiration exceptions - sessionIntervalInSeconds = sessionIntervalInSeconds > 30 ? sessionIntervalInSeconds - 30 : - sessionIntervalInSeconds; - return CacheBuilder.newBuilder().expireAfterWrite(sessionIntervalInSeconds, TimeUnit.SECONDS).build(CacheLoader - .from(getAccess)); + expirationSeconds = expirationSeconds > 30 ? expirationSeconds - 30 : expirationSeconds; + return CacheBuilder.newBuilder().expireAfterWrite(expirationSeconds, SECONDS).build(CacheLoader.from(getAccess)); + } + + /** + * Defers instantiation of {@linkplain SignOrProduceMacForToken} so as to avoid requiring private keys when the alg + * is set to {@linkplain org.jclouds.oauth.v2.JWSAlgorithms#NONE}. + */ + @Provides @Singleton Supplier<Function<byte[], byte[]>> signOrProduceMacForToken(@Named(JWS_ALG) String jwsAlg, + Provider<SignOrProduceMacForToken> in) { + if (jwsAlg.equals(NONE)) { // Current implementation requires we return null on none. + return Suppliers.<Function<byte[], byte[]>>ofInstance(new Function<byte[], byte[]>() { + @Override public byte[] apply(byte[] input) { + return null; + } + }); + } + return Suppliers.memoize(in.get()); } @Singleton public static class CredentialTypeFromPropertyOrDefault implements Provider<CredentialType> { @Inject(optional = true) @Named(OAuthProperties.CREDENTIAL_TYPE) - String credentialType = CredentialType.SERVICE_ACCOUNT_CREDENTIALS.toString(); + String credentialType = CredentialType.P12_PRIVATE_KEY_CREDENTIALS.toString(); @Override public CredentialType get() { @@ -93,7 +108,7 @@ public class OAuthModule extends AbstractModule { OAuthAuthenticator serviceAccountAuth, BearerTokenAuthenticator bearerTokenAuth) { switch (credentialType) { - case SERVICE_ACCOUNT_CREDENTIALS: + case P12_PRIVATE_KEY_CREDENTIALS: return serviceAccountAuth; case BEARER_TOKEN_CREDENTIALS: return bearerTokenAuth; http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/6c839f13/oauth/src/main/java/org/jclouds/oauth/v2/config/OAuthProperties.java ---------------------------------------------------------------------- diff --git a/oauth/src/main/java/org/jclouds/oauth/v2/config/OAuthProperties.java b/oauth/src/main/java/org/jclouds/oauth/v2/config/OAuthProperties.java index 9394cad..197bc31 100644 --- a/oauth/src/main/java/org/jclouds/oauth/v2/config/OAuthProperties.java +++ b/oauth/src/main/java/org/jclouds/oauth/v2/config/OAuthProperties.java @@ -16,17 +16,12 @@ */ package org.jclouds.oauth.v2.config; -/** - * Configurable properties for jclouds OAuth - */ +import org.jclouds.oauth.v2.JWSAlgorithms; + public class OAuthProperties { - /** - * The selected signature algorithm to use to sign the requests. - * <p/> - * This refers to the name the oauth provider expects, i.e., "RSA - */ - public static final String SIGNATURE_OR_MAC_ALGORITHM = "jclouds.oauth.signature-or-mac-algorithm"; + /** The JSON Web Signature alg, from the {@link JWSAlgorithms#supportedAlgs() supported list}. */ + public static final String JWS_ALG = "jclouds.oauth.jws-alg"; /** * The oauth audience, who this token is intended for. For instance in JWT and for @@ -37,11 +32,6 @@ public class OAuthProperties { public static final String AUDIENCE = "jclouds.oauth.audience"; /** - * Optional list of comma-separated scopes to use when no OAuthScopes annotation is present. - */ - public static final String SCOPES = "jclouds.oauth.scopes"; - - /** * Specify if credentials are id + private key or if you are reusing an oauth2 token. * * @see org.jclouds.oauth.v2.config.CredentialType http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/6c839f13/oauth/src/main/java/org/jclouds/oauth/v2/config/OAuthScopes.java ---------------------------------------------------------------------- diff --git a/oauth/src/main/java/org/jclouds/oauth/v2/config/OAuthScopes.java b/oauth/src/main/java/org/jclouds/oauth/v2/config/OAuthScopes.java index 57ffd29..d4fe7d4 100644 --- a/oauth/src/main/java/org/jclouds/oauth/v2/config/OAuthScopes.java +++ b/oauth/src/main/java/org/jclouds/oauth/v2/config/OAuthScopes.java @@ -16,12 +16,13 @@ */ package org.jclouds.oauth.v2.config; -import javax.inject.Qualifier; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import javax.inject.Qualifier; + /** * Used to annotate REST methods/ifaces that use OAuthAuthentication. * <p/> http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/6c839f13/oauth/src/main/java/org/jclouds/oauth/v2/domain/Header.java ---------------------------------------------------------------------- diff --git a/oauth/src/main/java/org/jclouds/oauth/v2/domain/Header.java b/oauth/src/main/java/org/jclouds/oauth/v2/domain/Header.java index ea83a91..9cdd924 100644 --- a/oauth/src/main/java/org/jclouds/oauth/v2/domain/Header.java +++ b/oauth/src/main/java/org/jclouds/oauth/v2/domain/Header.java @@ -28,7 +28,7 @@ import com.google.auto.value.AutoValue; @AutoValue public abstract class Header { - /** The name of the algorithm used to compute the signature, e.g., {@code RS256}. */ + /** The name of the algorithm used to compute the signature, e.g., {@code ES256}. */ public abstract String signerAlgorithm(); /** The type of the token, e.g., {@code JWT}. */ http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/6c839f13/oauth/src/main/java/org/jclouds/oauth/v2/domain/OAuthCredentials.java ---------------------------------------------------------------------- diff --git a/oauth/src/main/java/org/jclouds/oauth/v2/domain/OAuthCredentials.java b/oauth/src/main/java/org/jclouds/oauth/v2/domain/OAuthCredentials.java deleted file mode 100644 index 78cb402..0000000 --- a/oauth/src/main/java/org/jclouds/oauth/v2/domain/OAuthCredentials.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * 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.domain; - -import com.google.common.base.Objects; -import org.jclouds.domain.Credentials; - -import java.security.PrivateKey; - -import static com.google.common.base.Objects.equal; -import static com.google.common.base.Objects.toStringHelper; -import static com.google.common.base.Preconditions.checkNotNull; - -/** - * Special kind credentials for oauth authentication that includes {@link java.security.PrivateKey} to sign - * requests. - */ -public class OAuthCredentials extends Credentials { - - public static Builder builder() { - return new Builder(); - } - - public Builder toBuilder() { - return builder().fromOauthCredentials(this); - } - - public static class Builder extends Credentials.Builder<OAuthCredentials> { - - protected PrivateKey privateKey; - - /** - * @see OAuthCredentials#privateKey - */ - public Builder privateKey(PrivateKey privateKey) { - this.privateKey = checkNotNull(privateKey); - return this; - } - - /** - * @see Credentials#identity - */ - public Builder identity(String identity) { - this.identity = checkNotNull(identity); - return this; - } - - /** - * @see Credentials#credential - */ - public Builder credential(String credential) { - this.credential = credential; - return this; - } - - public OAuthCredentials build() { - return new OAuthCredentials(checkNotNull(identity), credential, privateKey); - } - - public Builder fromOauthCredentials(OAuthCredentials credentials) { - return new Builder().privateKey(credentials.privateKey).identity(credentials.identity) - .credential(credentials.credential); - } - } - - /** - * The private key associated with Credentials#identity. - * Used to sign token requests. - */ - public final PrivateKey privateKey; - - public OAuthCredentials(String identity, String credential, PrivateKey privateKey) { - super(identity, credential); - this.privateKey = privateKey; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - OAuthCredentials other = (OAuthCredentials) obj; - return equal(this.identity, other.identity) && equal(this.credential, - other.credential) && equal(this.privateKey, - other.privateKey); - } - - /** - * {@inheritDoc} - */ - @Override - public int hashCode() { - return Objects.hashCode(identity, credential, privateKey); - } - - /** - * {@inheritDoc} - */ - @Override - public String toString() { - return string().toString(); - } - - protected Objects.ToStringHelper string() { - return toStringHelper(this).omitNullValues().add("identity", identity) - .add("credential", credential != null ? credential.hashCode() : null).add("privateKey", - privateKey.hashCode()); - } -} http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/6c839f13/oauth/src/main/java/org/jclouds/oauth/v2/filters/BearerTokenAuthenticator.java ---------------------------------------------------------------------- diff --git a/oauth/src/main/java/org/jclouds/oauth/v2/filters/BearerTokenAuthenticator.java b/oauth/src/main/java/org/jclouds/oauth/v2/filters/BearerTokenAuthenticator.java index c5d944c..3d7d92d 100644 --- a/oauth/src/main/java/org/jclouds/oauth/v2/filters/BearerTokenAuthenticator.java +++ b/oauth/src/main/java/org/jclouds/oauth/v2/filters/BearerTokenAuthenticator.java @@ -20,16 +20,17 @@ import static java.lang.String.format; import javax.inject.Inject; +import org.jclouds.domain.Credentials; import org.jclouds.http.HttpException; import org.jclouds.http.HttpRequest; -import org.jclouds.oauth.v2.domain.OAuthCredentials; +import org.jclouds.location.Provider; import com.google.common.base.Supplier; public final class BearerTokenAuthenticator implements OAuthAuthenticationFilter { - private final Supplier<OAuthCredentials> creds; + private final Supplier<Credentials> creds; - @Inject BearerTokenAuthenticator(Supplier<OAuthCredentials> creds) { + @Inject BearerTokenAuthenticator(@Provider Supplier<Credentials> creds) { this.creds = creds; } http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/6c839f13/oauth/src/main/java/org/jclouds/oauth/v2/filters/OAuthAuthenticator.java ---------------------------------------------------------------------- diff --git a/oauth/src/main/java/org/jclouds/oauth/v2/filters/OAuthAuthenticator.java b/oauth/src/main/java/org/jclouds/oauth/v2/filters/OAuthAuthenticator.java index 28a2c15..cb858b5 100644 --- a/oauth/src/main/java/org/jclouds/oauth/v2/filters/OAuthAuthenticator.java +++ b/oauth/src/main/java/org/jclouds/oauth/v2/filters/OAuthAuthenticator.java @@ -16,15 +16,12 @@ */ package org.jclouds.oauth.v2.filters; -import static com.google.common.base.Preconditions.checkState; - import javax.inject.Inject; import org.jclouds.http.HttpException; import org.jclouds.http.HttpRequest; import org.jclouds.oauth.v2.domain.Token; import org.jclouds.oauth.v2.domain.TokenRequest; -import org.jclouds.rest.internal.GeneratedHttpRequest; import com.google.common.base.Function; import com.google.common.cache.LoadingCache; @@ -36,21 +33,19 @@ import com.google.common.cache.LoadingCache; */ public final class OAuthAuthenticator implements OAuthAuthenticationFilter { - private Function<GeneratedHttpRequest, TokenRequest> tokenRequestBuilder; + private Function<HttpRequest, TokenRequest> tokenRequestBuilder; private Function<TokenRequest, Token> tokenFetcher; - @Inject OAuthAuthenticator(Function<GeneratedHttpRequest, TokenRequest> tokenRequestBuilder, + @Inject OAuthAuthenticator(Function<HttpRequest, TokenRequest> tokenRequestBuilder, LoadingCache<TokenRequest, Token> tokenFetcher) { this.tokenRequestBuilder = tokenRequestBuilder; this.tokenFetcher = tokenFetcher; } @Override public HttpRequest filter(HttpRequest request) throws HttpException { - checkState(request instanceof GeneratedHttpRequest, "request must be an instance of GeneratedHttpRequest"); - GeneratedHttpRequest generatedHttpRequest = GeneratedHttpRequest.class.cast(request); - TokenRequest tokenRequest = tokenRequestBuilder.apply(generatedHttpRequest); + TokenRequest tokenRequest = tokenRequestBuilder.apply(request); Token token = tokenFetcher.apply(tokenRequest); - return request.toBuilder().addHeader("Authorization", String.format("%s %s", - token.tokenType(), token.accessToken())).build(); + String authorization = String.format("%s %s", token.tokenType(), token.accessToken()); + return request.toBuilder().addHeader("Authorization", authorization).build(); } } http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/6c839f13/oauth/src/main/java/org/jclouds/oauth/v2/functions/BuildTokenRequest.java ---------------------------------------------------------------------- diff --git a/oauth/src/main/java/org/jclouds/oauth/v2/functions/BuildTokenRequest.java b/oauth/src/main/java/org/jclouds/oauth/v2/functions/BuildTokenRequest.java index 7d94349..4a51954 100644 --- a/oauth/src/main/java/org/jclouds/oauth/v2/functions/BuildTokenRequest.java +++ b/oauth/src/main/java/org/jclouds/oauth/v2/functions/BuildTokenRequest.java @@ -17,21 +17,23 @@ package org.jclouds.oauth.v2.functions; import static com.google.common.base.Preconditions.checkState; -import static org.jclouds.oauth.v2.OAuthConstants.ADDITIONAL_CLAIMS; +import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL; import static org.jclouds.oauth.v2.config.OAuthProperties.AUDIENCE; -import static org.jclouds.oauth.v2.config.OAuthProperties.SCOPES; -import static org.jclouds.oauth.v2.config.OAuthProperties.SIGNATURE_OR_MAC_ALGORITHM; +import static org.jclouds.oauth.v2.config.OAuthProperties.JWS_ALG; import static org.jclouds.oauth.v2.domain.Claims.EXPIRATION_TIME; import static org.jclouds.oauth.v2.domain.Claims.ISSUED_AT; -import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; -import org.jclouds.Constants; +import javax.inject.Inject; +import javax.inject.Named; + +import org.jclouds.domain.Credentials; +import org.jclouds.http.HttpRequest; +import org.jclouds.location.Provider; import org.jclouds.oauth.v2.config.OAuthScopes; import org.jclouds.oauth.v2.domain.Header; -import org.jclouds.oauth.v2.domain.OAuthCredentials; import org.jclouds.oauth.v2.domain.TokenRequest; import org.jclouds.rest.internal.GeneratedHttpRequest; @@ -39,83 +41,65 @@ import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.base.Supplier; import com.google.common.reflect.Invokable; -import com.google.inject.Inject; -import com.google.inject.name.Named; - -/** - * The default authenticator. - * <p/> - * Builds the default token request with the following claims: iss,scope,aud,iat,exp. - * <p/> - * TODO scopes etc should come from the REST method and not from a global property - */ -public final class BuildTokenRequest implements Function<GeneratedHttpRequest, TokenRequest> { + +/** Builds the default token request with the following claims: {@code iss,scope,aud,iat,exp}. */ +public class BuildTokenRequest implements Function<HttpRequest, TokenRequest> { + private static final Joiner ON_COMMA = Joiner.on(","); + private final String assertionTargetDescription; private final String signatureAlgorithm; - private final Supplier<OAuthCredentials> credentialsSupplier; + private final Supplier<Credentials> credentialsSupplier; private final long tokenDuration; - @Inject(optional = true) - @Named(ADDITIONAL_CLAIMS) - private Map<String, String> additionalClaims = Collections.emptyMap(); - - @Inject(optional = true) - @Named(SCOPES) - private String globalScopes = null; + public static class TestBuildTokenRequest extends BuildTokenRequest { + @Inject TestBuildTokenRequest(@Named(AUDIENCE) String assertionTargetDescription, + @Named(JWS_ALG) String signatureAlgorithm, @Provider Supplier<Credentials> credentialsSupplier, + @Named(PROPERTY_SESSION_INTERVAL) long tokenDuration) { + super(assertionTargetDescription, signatureAlgorithm, credentialsSupplier, tokenDuration); + } - // injectable so expect tests can override with a predictable value - @Inject(optional = true) - private Supplier<Long> timeSourceMillisSinceEpoch = new Supplier<Long>() { - @Override - public Long get() { - return System.currentTimeMillis(); + public long currentTimeSeconds() { + return 0; } - }; + } @Inject BuildTokenRequest(@Named(AUDIENCE) String assertionTargetDescription, - @Named(SIGNATURE_OR_MAC_ALGORITHM) String signatureAlgorithm, - Supplier<OAuthCredentials> credentialsSupplier, - @Named(Constants.PROPERTY_SESSION_INTERVAL) long tokenDuration) { + @Named(JWS_ALG) String signatureAlgorithm, @Provider Supplier<Credentials> credentialsSupplier, + @Named(PROPERTY_SESSION_INTERVAL) long tokenDuration) { this.assertionTargetDescription = assertionTargetDescription; this.signatureAlgorithm = signatureAlgorithm; this.credentialsSupplier = credentialsSupplier; this.tokenDuration = tokenDuration; } - @Override public TokenRequest apply(GeneratedHttpRequest request) { - long now = timeSourceMillisSinceEpoch.get() / 1000; - - // fetch the token + @Override public TokenRequest apply(HttpRequest request) { Header header = Header.create(signatureAlgorithm, "JWT"); Map<String, Object> claims = new LinkedHashMap<String, Object>(); claims.put("iss", credentialsSupplier.get().identity); - claims.put("scope", getOAuthScopes(request)); + claims.put("scope", getOAuthScopes((GeneratedHttpRequest) request)); claims.put("aud", assertionTargetDescription); + + long now = currentTimeSeconds(); claims.put(EXPIRATION_TIME, now + tokenDuration); claims.put(ISSUED_AT, now); - claims.putAll(additionalClaims); return TokenRequest.create(header, claims); } + //TODO: Remove and switch to a request function. private String getOAuthScopes(GeneratedHttpRequest request) { Invokable<?, ?> invokable = request.getInvocation().getInvokable(); - OAuthScopes classScopes = invokable.getOwnerType().getRawType().getAnnotation(OAuthScopes.class); OAuthScopes methodScopes = invokable.getAnnotation(OAuthScopes.class); - - // if no annotations are present the rely on globally set scopes - if (classScopes == null && methodScopes == null) { - checkState(globalScopes != null, String.format("REST class or method should be annotated " + - "with OAuthScopes specifying required permissions. Alternatively a global property " + - "\"oauth.scopes\" may be set to define scopes globally. REST Class: %s, Method: %s", - invokable.getOwnerType(), - invokable.getName())); - return globalScopes; - } - + checkState(classScopes != null || methodScopes != null, "Api interface or method should be annotated " // + + "with OAuthScopes specifying required permissions. Api interface: %s, Method: %s", // + invokable.getOwnerType(), invokable.getName()); OAuthScopes scopes = methodScopes != null ? methodScopes : classScopes; - return Joiner.on(",").join(scopes.value()); + return ON_COMMA.join(scopes.value()); + } + + long currentTimeSeconds() { + return System.currentTimeMillis() / 1000; } } http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/6c839f13/oauth/src/main/java/org/jclouds/oauth/v2/functions/OAuthCredentialsSupplier.java ---------------------------------------------------------------------- diff --git a/oauth/src/main/java/org/jclouds/oauth/v2/functions/OAuthCredentialsSupplier.java b/oauth/src/main/java/org/jclouds/oauth/v2/functions/OAuthCredentialsSupplier.java deleted file mode 100644 index 3ec907e..0000000 --- a/oauth/src/main/java/org/jclouds/oauth/v2/functions/OAuthCredentialsSupplier.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * 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.functions; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Throwables.propagate; -import static java.lang.String.format; -import static org.jclouds.crypto.Pems.privateKeySpec; -import static org.jclouds.oauth.v2.OAuthConstants.NO_ALGORITHM; -import static org.jclouds.oauth.v2.OAuthConstants.OAUTH_ALGORITHM_NAMES_TO_KEYFACTORY_ALGORITHM_NAMES; -import static org.jclouds.oauth.v2.config.OAuthProperties.SIGNATURE_OR_MAC_ALGORITHM; -import static org.jclouds.util.Throwables2.getFirstThrowableOfType; - -import java.io.IOException; -import java.security.GeneralSecurityException; -import java.security.KeyFactory; -import java.security.PrivateKey; -import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Singleton; - -import org.jclouds.domain.Credentials; -import org.jclouds.location.Provider; -import org.jclouds.oauth.v2.config.CredentialType; -import org.jclouds.oauth.v2.domain.OAuthCredentials; -import org.jclouds.rest.AuthorizationException; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Charsets; -import com.google.common.base.Supplier; -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; -import com.google.common.io.ByteSource; -import com.google.common.util.concurrent.UncheckedExecutionException; - -/** - * Loads {@link OAuthCredentials} from a pem private key using the KeyFactory obtained from the JWT Algorithm - * Name<->KeyFactory name mapping in OAuthConstants. The pem pk algorithm must match the KeyFactory algorithm. - * - * @see org.jclouds.oauth.v2.OAuthConstants#OAUTH_ALGORITHM_NAMES_TO_KEYFACTORY_ALGORITHM_NAMES - */ -@Singleton // due to cache -public final class OAuthCredentialsSupplier implements Supplier<OAuthCredentials> { - - private final Supplier<Credentials> creds; - private final LoadingCache<Credentials, OAuthCredentials> keyCache; - - @Inject OAuthCredentialsSupplier(@Provider Supplier<Credentials> creds, OAuthCredentialsForCredentials loader, - @Named(SIGNATURE_OR_MAC_ALGORITHM) String signatureOrMacAlgorithm) { - this.creds = creds; - checkArgument(OAUTH_ALGORITHM_NAMES_TO_KEYFACTORY_ALGORITHM_NAMES.containsKey(signatureOrMacAlgorithm), - format("No mapping for key factory for algorithm: %s", signatureOrMacAlgorithm)); - // throw out the private key related to old credentials - this.keyCache = CacheBuilder.newBuilder().maximumSize(2).build(checkNotNull(loader, "loader")); - } - - /** - * it is relatively expensive to extract a private key from a PEM. cache the relationship between current credentials - * so that the private key is only recalculated once. - */ - @VisibleForTesting - static final class OAuthCredentialsForCredentials extends CacheLoader<Credentials, OAuthCredentials> { - private final String keyFactoryAlgorithm; - private final CredentialType credentialType; - - @Inject OAuthCredentialsForCredentials(@Named(SIGNATURE_OR_MAC_ALGORITHM) String signatureOrMacAlgorithm, - CredentialType credentialType) { - this.keyFactoryAlgorithm = OAUTH_ALGORITHM_NAMES_TO_KEYFACTORY_ALGORITHM_NAMES.get(checkNotNull( - signatureOrMacAlgorithm, "signatureOrMacAlgorithm")); - this.credentialType = credentialType; - } - - @Override public OAuthCredentials load(Credentials in) { - try { - String identity = in.identity; - String privateKeyInPemFormat = in.credential; - - // If passing Bearer tokens, simply create and pass it along - if (credentialType == CredentialType.BEARER_TOKEN_CREDENTIALS) { - return new OAuthCredentials.Builder().identity(identity).credential(in.credential).build(); - } - - if (keyFactoryAlgorithm.equals(NO_ALGORITHM)) { - return new OAuthCredentials.Builder().identity(identity).credential(privateKeyInPemFormat).build(); - } - KeyFactory keyFactory = KeyFactory.getInstance(keyFactoryAlgorithm); - PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec(ByteSource.wrap( - privateKeyInPemFormat.getBytes(Charsets.UTF_8)))); - return new OAuthCredentials.Builder().identity(identity).credential(privateKeyInPemFormat) - .privateKey(privateKey).build(); - } catch (IOException e) { - throw propagate(e); - // catch security exceptions InvalidKeySpecException and NoSuchAlgorithmException as GSE - } catch (GeneralSecurityException e) { - throw new AuthorizationException("security exception loading credentials. " + e.getMessage(), e); - // catch IAE that is thrown when parsing the pk fails - } catch (IllegalArgumentException e) { - throw new AuthorizationException("cannot parse pk. " + e.getMessage(), e); - } - } - } - - @Override public OAuthCredentials get() { - try { - // loader always throws UncheckedExecutionException so no point in using get() - return keyCache.getUnchecked(checkNotNull(creds.get(), "credential supplier returned null")); - } catch (UncheckedExecutionException e) { - AuthorizationException authorizationException = getFirstThrowableOfType(e, AuthorizationException.class); - if (authorizationException != null) { - throw authorizationException; - } - throw e; - } - } -} http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/6c839f13/oauth/src/main/java/org/jclouds/oauth/v2/functions/PrivateKeySupplier.java ---------------------------------------------------------------------- diff --git a/oauth/src/main/java/org/jclouds/oauth/v2/functions/PrivateKeySupplier.java b/oauth/src/main/java/org/jclouds/oauth/v2/functions/PrivateKeySupplier.java new file mode 100644 index 0000000..cbdad5e --- /dev/null +++ b/oauth/src/main/java/org/jclouds/oauth/v2/functions/PrivateKeySupplier.java @@ -0,0 +1,105 @@ +/* + * 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.functions; + +import static com.google.common.base.Charsets.UTF_8; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Throwables.propagate; +import static org.jclouds.crypto.Pems.privateKeySpec; +import static org.jclouds.oauth.v2.config.OAuthProperties.JWS_ALG; +import static org.jclouds.util.Throwables2.getFirstThrowableOfType; + +import java.io.IOException; +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.spec.InvalidKeySpecException; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.jclouds.domain.Credentials; +import org.jclouds.location.Provider; +import org.jclouds.oauth.v2.JWSAlgorithms; +import org.jclouds.rest.AuthorizationException; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Supplier; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.io.ByteSource; +import com.google.common.util.concurrent.UncheckedExecutionException; + +/** + * Loads {@link PrivateKey} from a pem private key using the KeyFactory obtained vi {@link + * JWSAlgorithms#keyFactory(String)}. The pem pk algorithm must match the KeyFactory algorithm. + */ +@Singleton // due to cache +public final class PrivateKeySupplier implements Supplier<PrivateKey> { + + private final Supplier<Credentials> creds; + private final LoadingCache<Credentials, PrivateKey> keyCache; + + @Inject PrivateKeySupplier(@Provider Supplier<Credentials> creds, PrivateKeyForCredentials loader) { + this.creds = creds; + // throw out the private key related to old credentials + this.keyCache = CacheBuilder.newBuilder().maximumSize(2).build(checkNotNull(loader, "loader")); + } + + /** + * it is relatively expensive to extract a private key from a PEM. cache the relationship between current + * credentials + * so that the private key is only recalculated once. + */ + @VisibleForTesting + static final class PrivateKeyForCredentials extends CacheLoader<Credentials, PrivateKey> { + private final String jwsAlg; + + @Inject PrivateKeyForCredentials(@Named(JWS_ALG) String jwsAlg) { + this.jwsAlg = jwsAlg; + } + + @Override public PrivateKey load(Credentials in) { + try { + String privateKeyInPemFormat = in.credential; + KeyFactory keyFactory = JWSAlgorithms.keyFactory(jwsAlg); + return keyFactory.generatePrivate(privateKeySpec(ByteSource.wrap(privateKeyInPemFormat.getBytes(UTF_8)))); + } catch (IOException e) { + throw propagate(e); + } catch (InvalidKeySpecException e) { + throw new AuthorizationException("security exception loading credentials. " + e.getMessage(), e); + // catch IAE that is thrown when parsing the pk fails + } catch (IllegalArgumentException e) { + throw new AuthorizationException("cannot parse pk. " + e.getMessage(), e); + } + } + } + + @Override public PrivateKey get() { + try { + // loader always throws UncheckedExecutionException so no point in using get() + return keyCache.getUnchecked(checkNotNull(creds.get(), "credential supplier returned null")); + } catch (UncheckedExecutionException e) { + AuthorizationException authorizationException = getFirstThrowableOfType(e, AuthorizationException.class); + if (authorizationException != null) { + throw authorizationException; + } + throw e; + } + } +}
