mirror of https://github.com/apache/jclouds.git
oauth v2 implementation
This commit is contained in:
parent
6352cbccfd
commit
fa27c74373
|
@ -0,0 +1,22 @@
|
|||
In order to use oauth applications must specify the following properties:
|
||||
|
||||
Mandatory:
|
||||
<myprovider>.identity - the oauth identity (e.g., service account email in Google Api's)
|
||||
<myprovider>.credential - the private key used to sign requests, in pem format
|
||||
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\
|
||||
-Dtest.oauth.identity=<accout email>\
|
||||
-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
|
|
@ -0,0 +1,123 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. jclouds 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.
|
||||
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.jclouds</groupId>
|
||||
<artifactId>jclouds-project</artifactId>
|
||||
<version>1.6.0-SNAPSHOT</version>
|
||||
<relativePath>../../project/pom.xml</relativePath>
|
||||
</parent>
|
||||
<groupId>org.jclouds.labs</groupId>
|
||||
<artifactId>oauth</artifactId>
|
||||
<name>jclouds OAuth core</name>
|
||||
<description>jclouds components to access OAuth</description>
|
||||
|
||||
<properties>
|
||||
<jclouds.version>1.6.0-SNAPSHOT</jclouds.version>
|
||||
<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.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>
|
||||
<test.oauth.build-version></test.oauth.build-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jclouds</groupId>
|
||||
<artifactId>jclouds-core</artifactId>
|
||||
<version>${jclouds.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jclouds</groupId>
|
||||
<artifactId>jclouds-core</artifactId>
|
||||
<version>${jclouds.version}</version>
|
||||
<type>test-jar</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jclouds.driver</groupId>
|
||||
<artifactId>jclouds-slf4j</artifactId>
|
||||
<version>${jclouds.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>test-jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>live</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>integration</id>
|
||||
<phase>integration-test</phase>
|
||||
<goals>
|
||||
<goal>test</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<systemPropertyVariables>
|
||||
<test.oauth.identity>${test.oauth.identity}</test.oauth.identity>
|
||||
<test.oauth.credential>${test.oauth.credential}</test.oauth.credential>
|
||||
<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.audience>${test.jclouds.oauth.audience}</test.jclouds.oauth.audience>
|
||||
<test.jclouds.oauth.scopes>${test.jclouds.oauth.scopes}</test.jclouds.oauth.scopes>
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
|
@ -0,0 +1,49 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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 org.jclouds.concurrent.Timeout;
|
||||
import org.jclouds.oauth.v2.domain.Token;
|
||||
import org.jclouds.oauth.v2.domain.TokenRequest;
|
||||
import org.jclouds.rest.AuthorizationException;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Provides synchronous access to OAuth.
|
||||
* <p/>
|
||||
*
|
||||
* @author David Alves
|
||||
* @see OAuthAsyncApi
|
||||
*/
|
||||
@Timeout(duration = 60, timeUnit = TimeUnit.SECONDS)
|
||||
public interface OAuthApi {
|
||||
|
||||
/**
|
||||
* Authenticates/Authorizes access to a resource defined in TokenRequest against an OAuth v2
|
||||
* authentication/authorization server.
|
||||
*
|
||||
* @param tokenRequest specified the principal and the required permissions
|
||||
* @return a Token object with the token required to access the resource along with its expiration time
|
||||
* @throws AuthorizationException if the principal cannot be authenticated or has no permissions for the specifed
|
||||
* resources.
|
||||
*/
|
||||
public Token authenticate(TokenRequest tokenRequest) throws AuthorizationException;
|
||||
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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.ImmutableSet;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import com.google.inject.Module;
|
||||
import org.jclouds.apis.ApiMetadata;
|
||||
import org.jclouds.oauth.v2.config.OAuthModule;
|
||||
import org.jclouds.oauth.v2.config.OAuthRestClientModule;
|
||||
import org.jclouds.rest.RestContext;
|
||||
import org.jclouds.rest.internal.BaseRestApiMetadata;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Properties;
|
||||
|
||||
import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
|
||||
import static org.jclouds.oauth.v2.config.OAuthProperties.SIGNATURE_OR_MAC_ALGORITHM;
|
||||
|
||||
/**
|
||||
* Implementation of {@link ApiMetadata} for OAuth 2 API
|
||||
*
|
||||
* @author David Alves
|
||||
*/
|
||||
public class OAuthApiMetadata extends BaseRestApiMetadata {
|
||||
|
||||
public static final TypeToken<RestContext<OAuthApi, OAuthAsyncApi>> CONTEXT_TOKEN = new
|
||||
TypeToken<RestContext<OAuthApi, OAuthAsyncApi>>() {};
|
||||
|
||||
@Override
|
||||
public Builder toBuilder() {
|
||||
return new Builder().fromApiMetadata(this);
|
||||
}
|
||||
|
||||
public OAuthApiMetadata() {
|
||||
this(new Builder());
|
||||
}
|
||||
|
||||
protected OAuthApiMetadata(Builder builder) {
|
||||
super(builder);
|
||||
}
|
||||
|
||||
public static Properties defaultProperties() {
|
||||
Properties properties = BaseRestApiMetadata.defaultProperties();
|
||||
properties.put(SIGNATURE_OR_MAC_ALGORITHM, "RS256");
|
||||
properties.put(PROPERTY_SESSION_INTERVAL, 3600);
|
||||
return properties;
|
||||
}
|
||||
|
||||
public static class Builder extends BaseRestApiMetadata.Builder {
|
||||
|
||||
protected Builder() {
|
||||
super(OAuthApi.class, OAuthAsyncApi.class);
|
||||
id("oauth").name("OAuth API")
|
||||
.identityName("service_account")
|
||||
.credentialName("service_key")
|
||||
.documentation(URI.create("TODO"))
|
||||
.version("2")
|
||||
.defaultProperties(OAuthApiMetadata.defaultProperties())
|
||||
.defaultModules(ImmutableSet.<Class<? extends Module>>of(OAuthModule.class, OAuthRestClientModule
|
||||
.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuthApiMetadata build() {
|
||||
return new OAuthApiMetadata(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder fromApiMetadata(ApiMetadata in) {
|
||||
super.fromApiMetadata(in);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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.util.concurrent.ListenableFuture;
|
||||
import org.jclouds.oauth.v2.config.Authentication;
|
||||
import org.jclouds.oauth.v2.domain.Token;
|
||||
import org.jclouds.oauth.v2.domain.TokenRequest;
|
||||
import org.jclouds.oauth.v2.handlers.OAuthTokenBinder;
|
||||
import org.jclouds.rest.AuthorizationException;
|
||||
import org.jclouds.rest.annotations.BinderParam;
|
||||
import org.jclouds.rest.annotations.Endpoint;
|
||||
import org.jclouds.rest.annotations.SkipEncoding;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
/**
|
||||
* Provides asynchronous access to OAuth via REST api.
|
||||
* <p/>
|
||||
* Usually this is not directly used by a client, which instead specifies OAuthAuthenticator as a request filter,
|
||||
* which in turn uses this class to perform token requests.
|
||||
*
|
||||
* @author David Alves
|
||||
* @see OAuthAsyncApi
|
||||
*/
|
||||
@SkipEncoding({'/', '='})
|
||||
@Endpoint(Authentication.class)
|
||||
public interface OAuthAsyncApi {
|
||||
|
||||
/**
|
||||
* @see OAuthApi#authenticate(TokenRequest)
|
||||
*/
|
||||
@POST
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
public ListenableFuture<Token> authenticate(@BinderParam(OAuthTokenBinder.class) TokenRequest tokenRequest)
|
||||
throws AuthorizationException;
|
||||
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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 \
|
||||
*
|
||||
* @author David Alves
|
||||
*/
|
||||
public 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";
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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.
|
||||
*
|
||||
* @author David Alves
|
||||
* @see org.jclouds.oauth.v2.OAuthAsyncApi
|
||||
*/
|
||||
@Retention(value = RetentionPolicy.RUNTIME)
|
||||
@Target(value = {ElementType.TYPE, ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
|
||||
@Qualifier
|
||||
public @interface Authentication {
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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 com.google.common.base.Supplier;
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Provides;
|
||||
import org.jclouds.oauth.v2.OAuthApi;
|
||||
import org.jclouds.oauth.v2.OAuthAsyncApi;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import java.net.URI;
|
||||
|
||||
import static org.jclouds.rest.config.BinderUtils.bindClientAndAsyncClient;
|
||||
|
||||
/**
|
||||
* An OAuth module to be used form other providers.
|
||||
*
|
||||
* @author David Alves
|
||||
*/
|
||||
public class OAuthAuthenticationModule extends AbstractModule {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bindClientAndAsyncClient(binder(), OAuthApi.class, OAuthAsyncApi.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) {
|
||||
return Suppliers.ofInstance(URI.create(endpoint));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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 com.google.common.base.Function;
|
||||
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.collect.ImmutableMap;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Provides;
|
||||
import com.google.inject.TypeLiteral;
|
||||
import org.jclouds.oauth.v2.domain.ClaimSet;
|
||||
import org.jclouds.oauth.v2.domain.Header;
|
||||
import org.jclouds.oauth.v2.domain.OAuthCredentials;
|
||||
import org.jclouds.oauth.v2.domain.Token;
|
||||
import org.jclouds.oauth.v2.domain.TokenRequest;
|
||||
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.SignOrProduceMacForToken;
|
||||
import org.jclouds.oauth.v2.json.ClaimSetTypeAdapter;
|
||||
import org.jclouds.oauth.v2.json.HeaderTypeAdapter;
|
||||
import org.jclouds.rest.internal.GeneratedHttpRequest;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
|
||||
|
||||
/**
|
||||
* Base OAuth module
|
||||
*
|
||||
* @author David Alves
|
||||
*/
|
||||
public class OAuthModule extends AbstractModule {
|
||||
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(new TypeLiteral<Function<byte[], byte[]>>() {}).to(SignOrProduceMacForToken.class);
|
||||
bind(new TypeLiteral<Map<Type, Object>>() {}).toInstance(ImmutableMap.<Type, Object>of(
|
||||
Header.class, new HeaderTypeAdapter(),
|
||||
ClaimSet.class, new ClaimSetTypeAdapter()));
|
||||
bind(new TypeLiteral<Supplier<OAuthCredentials>>() {}).to(OAuthCredentialsSupplier.class);
|
||||
bind(new TypeLiteral<Function<GeneratedHttpRequest, TokenRequest>>() {}).to(BuildTokenRequest.class);
|
||||
bind(new TypeLiteral<Function<TokenRequest, Token>>() {}).to(FetchToken.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a cache for tokens. Cache is time based and expires after 59 minutes (the maximum time a token is
|
||||
* valid is 60 minutes)
|
||||
*/
|
||||
@Provides
|
||||
@Singleton
|
||||
public LoadingCache<TokenRequest, Token> provideAccessCache(Function<TokenRequest, Token> getAccess,
|
||||
@Named(PROPERTY_SESSION_INTERVAL) long
|
||||
sessionIntervalInSeconds) {
|
||||
// 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.MINUTES).build(CacheLoader
|
||||
.from(getAccess));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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;
|
||||
|
||||
/**
|
||||
* Configurable properties for jclouds OAuth
|
||||
*
|
||||
* @author David Alves
|
||||
*/
|
||||
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 oauth audience, who this token is intended for. For instance in JWT and for
|
||||
* google API's this property maps to: {"aud","https://accounts.google.com/o/oauth2/token"}
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/draft-jones-json-web-token-04">doc</a>
|
||||
*/
|
||||
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";
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package org.jclouds.oauth.v2.config;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import com.google.inject.Provides;
|
||||
import org.jclouds.oauth.v2.OAuthApi;
|
||||
import org.jclouds.oauth.v2.OAuthAsyncApi;
|
||||
import org.jclouds.providers.ProviderMetadata;
|
||||
import org.jclouds.rest.ConfiguresRestClient;
|
||||
import org.jclouds.rest.config.RestClientModule;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
import java.net.URI;
|
||||
|
||||
/**
|
||||
* OAuth module to when accessing OAuth stand-alone.
|
||||
*
|
||||
* @author David Alves
|
||||
*/
|
||||
@ConfiguresRestClient
|
||||
public class OAuthRestClientModule extends RestClientModule<OAuthApi, OAuthAsyncApi> {
|
||||
|
||||
public OAuthRestClientModule() {
|
||||
super(TypeToken.class.cast(TypeToken.of(OAuthApi.class)), TypeToken.class.cast(TypeToken.of(OAuthAsyncApi
|
||||
.class)));
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Authentication
|
||||
protected Supplier<URI> provideAuthenticationEndpoint(ProviderMetadata providerMetadata) {
|
||||
return Suppliers.ofInstance(URI.create(providerMetadata.getEndpoint()));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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;
|
||||
|
||||
/**
|
||||
* Used to annotate REST methods/ifaces that use OAuthAuthentication.
|
||||
* <p/>
|
||||
* Sets the scopes for the token request for that particular method.
|
||||
*
|
||||
* @author David Alves
|
||||
*/
|
||||
@Retention(value = RetentionPolicy.RUNTIME)
|
||||
@Target(value = {ElementType.TYPE, ElementType.METHOD})
|
||||
@Qualifier
|
||||
public @interface OAuthScopes {
|
||||
|
||||
/**
|
||||
* @return the OAuth scopes required to access the resource.
|
||||
*/
|
||||
String[] value();
|
||||
|
||||
}
|
|
@ -0,0 +1,195 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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 com.google.common.base.Splitter;
|
||||
import com.google.common.collect.ForwardingMap;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.google.common.base.Objects.ToStringHelper;
|
||||
import static com.google.common.base.Objects.equal;
|
||||
import static com.google.common.base.Objects.toStringHelper;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
|
||||
/**
|
||||
* The claimset for the token.
|
||||
*
|
||||
* @author David Alves
|
||||
* @see <a
|
||||
* href="https://developers.google.com/accounts/docs/OAuth2ServiceAccount"
|
||||
* >doc</a>
|
||||
*/
|
||||
public class ClaimSet extends ForwardingMap<String, String> {
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public Builder toBuilder() {
|
||||
return builder().fromClaimSet(this);
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private Set<String> requiredClaims;
|
||||
private ImmutableMap.Builder<String, String> claims = new ImmutableMap.Builder<String, String>();
|
||||
private long emissionTime;
|
||||
private long expirationTime;
|
||||
|
||||
public Builder() {
|
||||
this(ImmutableSet.<String>of());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor that allows to predefine a mandatory set of claims as a comma-separated string, e.g, "iss,iat".
|
||||
*/
|
||||
public Builder(String commaSeparatedRequiredClaims) {
|
||||
this(ImmutableSet.copyOf(Splitter.on(",").split(checkNotNull(commaSeparatedRequiredClaims))));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor that allows to predefine a mandatory set of claims as a set of strings.
|
||||
*/
|
||||
public Builder(Set<String> requiredClaims) {
|
||||
this.requiredClaims = ImmutableSet.copyOf(checkNotNull(requiredClaims));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a Claim, i.e. key/value pair, e.g., "scope":"all_permissions".
|
||||
*/
|
||||
public Builder addClaim(String name, String value) {
|
||||
claims.put(checkNotNull(name), checkNotNull(value, "value of %s", name));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ClaimSet#getEmissionTime()
|
||||
*/
|
||||
public Builder emissionTime(long emmissionTime) {
|
||||
this.emissionTime = emmissionTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ClaimSet#getExpirationTime()
|
||||
*/
|
||||
public Builder expirationTime(long expirationTime) {
|
||||
this.expirationTime = expirationTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a map containing multiple claims
|
||||
*/
|
||||
public Builder addAllClaims(Map<String, String> claims) {
|
||||
this.claims.putAll(checkNotNull(claims));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClaimSet build() {
|
||||
Map<String, String> claimsMap = claims.build();
|
||||
checkState(Sets.intersection(claimsMap.keySet(), requiredClaims).size() == requiredClaims.size(),
|
||||
"not all required claims were present");
|
||||
if (expirationTime == 0) {
|
||||
expirationTime = emissionTime + 3600;
|
||||
}
|
||||
return new ClaimSet(claimsMap, emissionTime, expirationTime);
|
||||
}
|
||||
|
||||
public Builder fromClaimSet(ClaimSet claimSet) {
|
||||
return new Builder().addAllClaims(claimSet.claims).expirationTime(expirationTime).emissionTime(emissionTime);
|
||||
}
|
||||
}
|
||||
|
||||
private final Map<String, String> claims;
|
||||
private final long emissionTime;
|
||||
private final long expirationTime;
|
||||
|
||||
private ClaimSet(Map<String, String> claims, long emissionTime, long expirationTime) {
|
||||
this.claims = claims;
|
||||
this.emissionTime = emissionTime;
|
||||
this.expirationTime = expirationTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* The emission time, in seconds since the epoch.
|
||||
*/
|
||||
public long getEmissionTime() {
|
||||
return emissionTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* The expiration time, in seconds since the emission time.
|
||||
*/
|
||||
public long getExpirationTime() {
|
||||
return expirationTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns the claims.
|
||||
*/
|
||||
@Override
|
||||
protected Map<String, String> delegate() {
|
||||
return claims;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(claims, emissionTime, expirationTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
ClaimSet other = (ClaimSet) obj;
|
||||
return equal(claims, other.claims) && equal(this.emissionTime,
|
||||
other.emissionTime) && equal(this.expirationTime, other.expirationTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return string().toString();
|
||||
}
|
||||
|
||||
protected ToStringHelper string() {
|
||||
return toStringHelper(this).omitNullValues().add("claims", claims)
|
||||
.add("emissionTime", emissionTime).add("expirationTIme", expirationTime);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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 static com.google.common.base.Objects.equal;
|
||||
import static com.google.common.base.Objects.toStringHelper;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* The header for the OAuth token, contains the signer algorithm's name and the type of the token
|
||||
*
|
||||
* @author David Alves
|
||||
* @see <a href="https://developers.google.com/accounts/docs/OAuth2ServiceAccount">doc</a>
|
||||
*/
|
||||
public class Header {
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public Builder toBuilder() {
|
||||
return builder().fromHeader(this);
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private String signerAlgorithm;
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* @see Header#getSignerAlgorithm()
|
||||
*/
|
||||
public Builder signerAlgorithm(String signerAlgorithm) {
|
||||
this.signerAlgorithm = checkNotNull(signerAlgorithm);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Header#getType()
|
||||
*/
|
||||
public Builder type(String type) {
|
||||
this.type = checkNotNull(type);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Header build() {
|
||||
return new Header(signerAlgorithm, type);
|
||||
}
|
||||
|
||||
public Builder fromHeader(Header header) {
|
||||
return new Builder().signerAlgorithm(header.signerAlgorithm).type(header.type);
|
||||
}
|
||||
}
|
||||
|
||||
private final String signerAlgorithm;
|
||||
private final String type;
|
||||
|
||||
protected Header(String signerAlgorithm, String type) {
|
||||
this.signerAlgorithm = checkNotNull(signerAlgorithm);
|
||||
this.type = checkNotNull(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the algorithm used to compute the signature, e.g., "RS256"
|
||||
*/
|
||||
public String getSignerAlgorithm() {
|
||||
return signerAlgorithm;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of the token, e.g., "JWT"
|
||||
*/
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
Header other = (Header) obj;
|
||||
return equal(this.signerAlgorithm, other.signerAlgorithm) && equal(this.type,
|
||||
other.type);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(signerAlgorithm, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return string().toString();
|
||||
}
|
||||
|
||||
protected Objects.ToStringHelper string() {
|
||||
return toStringHelper(this).omitNullValues().add("signerAlgorithm", signerAlgorithm)
|
||||
.add("type", type);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
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());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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 java.beans.ConstructorProperties;
|
||||
|
||||
import static com.google.common.base.Objects.equal;
|
||||
import static com.google.common.base.Objects.toStringHelper;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* The oauth token, obtained upon a successful token request and ready to embed in requests.
|
||||
*
|
||||
* @author David Alves
|
||||
*/
|
||||
public class Token {
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public Builder toBuilder() {
|
||||
return builder().fromToken(this);
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private String accessToken;
|
||||
private String tokenType;
|
||||
private long expiresIn;
|
||||
|
||||
/**
|
||||
* @see Token#getAccessToken()
|
||||
*/
|
||||
public Builder accessToken(String accessToken) {
|
||||
this.accessToken = checkNotNull(accessToken);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Token#getTokenType()
|
||||
*/
|
||||
public Builder tokenType(String tokenType) {
|
||||
this.tokenType = checkNotNull(tokenType);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Token#getExpiresIn()
|
||||
*/
|
||||
public Builder expiresIn(long expiresIn) {
|
||||
this.expiresIn = expiresIn;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Token build() {
|
||||
return new Token(accessToken, tokenType, expiresIn);
|
||||
}
|
||||
|
||||
public Builder fromToken(Token token) {
|
||||
return new Builder().accessToken(token.accessToken).tokenType(token.tokenType).expiresIn(token.expiresIn);
|
||||
}
|
||||
}
|
||||
|
||||
private final String accessToken;
|
||||
private final String tokenType;
|
||||
private final long expiresIn;
|
||||
|
||||
@ConstructorProperties({"access_token", "token_type", "expires_in"})
|
||||
protected Token(String accessToken, String tokenType, long expiresIn) {
|
||||
this.accessToken = accessToken;
|
||||
this.tokenType = tokenType;
|
||||
this.expiresIn = expiresIn;
|
||||
}
|
||||
|
||||
/**
|
||||
* The access token obtained from the OAuth server.
|
||||
*/
|
||||
public String getAccessToken() {
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of the token, e.g., "Bearer"
|
||||
*/
|
||||
public String getTokenType() {
|
||||
return tokenType;
|
||||
}
|
||||
|
||||
/**
|
||||
* In how many seconds this token expires.
|
||||
*/
|
||||
public long getExpiresIn() {
|
||||
return expiresIn;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
Token other = (Token) obj;
|
||||
return equal(this.accessToken, other.accessToken) && equal(this.tokenType,
|
||||
other.tokenType) && equal(this.expiresIn,
|
||||
other.expiresIn);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(accessToken, tokenType, expiresIn);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return string().toString();
|
||||
}
|
||||
|
||||
protected Objects.ToStringHelper string() {
|
||||
return toStringHelper(this).omitNullValues().add("accessToken", accessToken)
|
||||
.add("tokenType", tokenType).add("expiresIn", expiresIn);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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 static com.google.common.base.Objects.equal;
|
||||
import static com.google.common.base.Objects.toStringHelper;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* A complete token request.
|
||||
*
|
||||
* @author David Alves
|
||||
*/
|
||||
public class TokenRequest {
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public Builder toBuilder() {
|
||||
return builder().fromTokenRequest(this);
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private Header header;
|
||||
private ClaimSet claimSet;
|
||||
|
||||
/**
|
||||
* @see TokenRequest#getClaimSet()
|
||||
*/
|
||||
public Builder header(Header header) {
|
||||
this.header = header;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see TokenRequest#getHeader()
|
||||
*/
|
||||
public Builder claimSet(ClaimSet claimSet) {
|
||||
this.claimSet = claimSet;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TokenRequest build() {
|
||||
return new TokenRequest(header, claimSet);
|
||||
}
|
||||
|
||||
public Builder fromTokenRequest(TokenRequest tokeRequest) {
|
||||
return new Builder().header(tokeRequest.header).claimSet(tokeRequest.claimSet);
|
||||
}
|
||||
}
|
||||
|
||||
private final Header header;
|
||||
private final ClaimSet claimSet;
|
||||
|
||||
public TokenRequest(Header header, ClaimSet claimSet) {
|
||||
this.header = checkNotNull(header);
|
||||
this.claimSet = checkNotNull(claimSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* The header of this token request.
|
||||
*
|
||||
* @see Header
|
||||
*/
|
||||
public Header getHeader() {
|
||||
return header;
|
||||
}
|
||||
|
||||
/**
|
||||
* The claim set of this token request.
|
||||
*
|
||||
* @see ClaimSet
|
||||
*/
|
||||
public ClaimSet getClaimSet() {
|
||||
return claimSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
TokenRequest other = (TokenRequest) obj;
|
||||
return equal(this.header, other.header) && equal(this.claimSet,
|
||||
other.claimSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(header, claimSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return string().toString();
|
||||
}
|
||||
|
||||
protected Objects.ToStringHelper string() {
|
||||
return toStringHelper(this).omitNullValues().add("header", header)
|
||||
.add("claimSet", claimSet);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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.inject.ImplementedBy;
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.oauth.v2.json.JWTTokenRequestFormat;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Transforms a TokenRequest into a specific format (e.g. JWT token)
|
||||
*
|
||||
* @author David Alves
|
||||
*/
|
||||
@ImplementedBy(JWTTokenRequestFormat.class)
|
||||
public interface TokenRequestFormat {
|
||||
|
||||
/**
|
||||
* Transforms the provided HttpRequest into a particular token request with a specific format.
|
||||
*/
|
||||
public <R extends HttpRequest> R formatRequest(R httpRequest, TokenRequest tokenRequest);
|
||||
|
||||
/**
|
||||
* The name of the type of the token request, e.g., "JWT"
|
||||
*/
|
||||
public String getTypeName();
|
||||
|
||||
/**
|
||||
* The claims that must be present in the token request for it to be valid.
|
||||
*/
|
||||
public Set<String> requiredClaims();
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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.filters;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import org.jclouds.http.HttpException;
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.http.HttpRequestFilter;
|
||||
import org.jclouds.oauth.v2.domain.Token;
|
||||
import org.jclouds.oauth.v2.domain.TokenRequest;
|
||||
import org.jclouds.rest.internal.GeneratedHttpRequest;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
|
||||
/**
|
||||
* To be used by client applications to embed an OAuth authentication in their REST requests.
|
||||
* <p/>
|
||||
* TODO when we're able to use the OAuthAuthentication an this should be used automatically
|
||||
*
|
||||
* @author David Alves
|
||||
*/
|
||||
@Singleton
|
||||
public class OAuthAuthenticator implements HttpRequestFilter {
|
||||
|
||||
private Function<GeneratedHttpRequest, TokenRequest> tokenRequestBuilder;
|
||||
private Function<TokenRequest, Token> tokenFetcher;
|
||||
|
||||
@Inject
|
||||
OAuthAuthenticator(Function<GeneratedHttpRequest, 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) request;
|
||||
TokenRequest tokenRequest = tokenRequestBuilder.apply(generatedHttpRequest);
|
||||
Token token = tokenFetcher.apply(tokenRequest);
|
||||
return request.toBuilder().addHeader("Authorization", String.format("%s %s",
|
||||
token.getTokenType(), token.getAccessToken())).build();
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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 com.google.common.base.Function;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.base.Ticker;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.name.Named;
|
||||
import org.jclouds.Constants;
|
||||
import org.jclouds.oauth.v2.config.OAuthScopes;
|
||||
import org.jclouds.oauth.v2.domain.ClaimSet;
|
||||
import org.jclouds.oauth.v2.domain.Header;
|
||||
import org.jclouds.oauth.v2.domain.OAuthCredentials;
|
||||
import org.jclouds.oauth.v2.domain.TokenRequest;
|
||||
import org.jclouds.oauth.v2.domain.TokenRequestFormat;
|
||||
import org.jclouds.rest.internal.GeneratedHttpRequest;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static org.jclouds.oauth.v2.OAuthConstants.ADDITIONAL_CLAIMS;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @author David Alves
|
||||
*/
|
||||
@Singleton
|
||||
public class BuildTokenRequest implements Function<GeneratedHttpRequest, TokenRequest> {
|
||||
|
||||
private final String assertionTargetDescription;
|
||||
private final String signatureAlgorithm;
|
||||
private final TokenRequestFormat tokenRequestFormat;
|
||||
private final Supplier<OAuthCredentials> credentialsSupplier;
|
||||
private final long tokenDuration;
|
||||
|
||||
@Inject(optional = true)
|
||||
@Named(ADDITIONAL_CLAIMS)
|
||||
protected Map<String, String> additionalClaims = ImmutableMap.of();
|
||||
|
||||
@Inject(optional = true)
|
||||
@Named(SCOPES)
|
||||
protected String globalScopes = null;
|
||||
|
||||
@Inject(optional = true)
|
||||
public Ticker ticker = Ticker.systemTicker();
|
||||
|
||||
|
||||
@Inject
|
||||
public BuildTokenRequest(@Named(AUDIENCE) String assertionTargetDescription,
|
||||
@Named(SIGNATURE_OR_MAC_ALGORITHM) String signatureAlgorithm,
|
||||
TokenRequestFormat tokenRequestFormat, Supplier<OAuthCredentials> credentialsSupplier,
|
||||
@Named(Constants.PROPERTY_SESSION_INTERVAL) long tokenDuration) {
|
||||
this.assertionTargetDescription = assertionTargetDescription;
|
||||
this.signatureAlgorithm = signatureAlgorithm;
|
||||
this.tokenRequestFormat = tokenRequestFormat;
|
||||
this.credentialsSupplier = credentialsSupplier;
|
||||
this.tokenDuration = tokenDuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenRequest apply(GeneratedHttpRequest request) {
|
||||
long now = TimeUnit.SECONDS.convert(ticker.read(), TimeUnit.NANOSECONDS);
|
||||
|
||||
// fetch the token
|
||||
Header header = new Header.Builder()
|
||||
.signerAlgorithm(signatureAlgorithm)
|
||||
.type(tokenRequestFormat.getTypeName())
|
||||
.build();
|
||||
|
||||
ClaimSet claimSet = new ClaimSet.Builder(this.tokenRequestFormat.requiredClaims())
|
||||
.addClaim("iss", credentialsSupplier.get().identity)
|
||||
.addClaim("scope", getOAuthScopes(request))
|
||||
.addClaim("aud", assertionTargetDescription)
|
||||
.emissionTime(now)
|
||||
.expirationTime(now + tokenDuration)
|
||||
.addAllClaims(additionalClaims)
|
||||
.build();
|
||||
|
||||
return new TokenRequest.Builder()
|
||||
.header(header)
|
||||
.claimSet(claimSet)
|
||||
.build();
|
||||
}
|
||||
|
||||
protected String getOAuthScopes(GeneratedHttpRequest request) {
|
||||
OAuthScopes classScopes = request.getDeclaring().getAnnotation(OAuthScopes.class);
|
||||
OAuthScopes methodScopes = request.getJavaMethod().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",
|
||||
request.getDeclaring().getName(),
|
||||
request.getJavaMethod().getName()));
|
||||
return globalScopes;
|
||||
}
|
||||
|
||||
OAuthScopes scopes = methodScopes != null ? methodScopes : classScopes;
|
||||
return Joiner.on(",").join(scopes.value());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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 com.google.common.base.Function;
|
||||
import org.jclouds.oauth.v2.OAuthApi;
|
||||
import org.jclouds.oauth.v2.domain.Token;
|
||||
import org.jclouds.oauth.v2.domain.TokenRequest;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
/**
|
||||
* @author David Alves
|
||||
*/
|
||||
@Singleton
|
||||
public class FetchToken implements Function<TokenRequest, Token> {
|
||||
|
||||
private OAuthApi oAuthApi;
|
||||
|
||||
@Inject
|
||||
public FetchToken(OAuthApi oAuthApi) {
|
||||
this.oAuthApi = oAuthApi;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Token apply(TokenRequest input) {
|
||||
return this.oAuthApi.authenticate(input);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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 com.google.common.base.Supplier;
|
||||
import org.jclouds.crypto.Pems;
|
||||
import org.jclouds.io.Payloads;
|
||||
import org.jclouds.oauth.v2.domain.OAuthCredentials;
|
||||
import org.jclouds.rest.annotations.Credential;
|
||||
import org.jclouds.rest.annotations.Identity;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import java.io.IOException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static java.lang.String.format;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @author David Alves
|
||||
* @see org.jclouds.oauth.v2.OAuthConstants#OAUTH_ALGORITHM_NAMES_TO_KEYFACTORY_ALGORITHM_NAMES
|
||||
*/
|
||||
@Singleton
|
||||
public class OAuthCredentialsSupplier implements Supplier<OAuthCredentials> {
|
||||
|
||||
|
||||
private final String identity;
|
||||
private final String privateKeyInPemFormat;
|
||||
private final String keyFactoryAlgorithm;
|
||||
private OAuthCredentials credentials;
|
||||
|
||||
@Inject
|
||||
public OAuthCredentialsSupplier(@Identity String identity,
|
||||
@Credential String privateKeyInPemFormat,
|
||||
@Named(SIGNATURE_OR_MAC_ALGORITHM) String signatureOrMacAlgorithm) {
|
||||
this.identity = identity;
|
||||
this.privateKeyInPemFormat = privateKeyInPemFormat;
|
||||
checkState(OAUTH_ALGORITHM_NAMES_TO_KEYFACTORY_ALGORITHM_NAMES.containsKey(signatureOrMacAlgorithm),
|
||||
format("No mapping for key factory for algorithm: %s", signatureOrMacAlgorithm));
|
||||
this.keyFactoryAlgorithm = OAUTH_ALGORITHM_NAMES_TO_KEYFACTORY_ALGORITHM_NAMES.get(signatureOrMacAlgorithm);
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void loadPrivateKey() throws NoSuchAlgorithmException, IOException, InvalidKeySpecException {
|
||||
if (keyFactoryAlgorithm.equals(NO_ALGORITHM)) {
|
||||
this.credentials = new OAuthCredentials.Builder().identity(identity).credential
|
||||
(privateKeyInPemFormat).build();
|
||||
return;
|
||||
}
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(keyFactoryAlgorithm);
|
||||
PrivateKey privateKey = keyFactory.generatePrivate(Pems.privateKeySpec(Payloads.newStringPayload
|
||||
(privateKeyInPemFormat)));
|
||||
this.credentials = new OAuthCredentials.Builder().identity(identity).credential
|
||||
(privateKeyInPemFormat).privateKey(privateKey).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuthCredentials get() {
|
||||
return this.credentials;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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 com.google.common.base.Function;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.base.Throwables;
|
||||
import org.jclouds.oauth.v2.domain.OAuthCredentials;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.crypto.Mac;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.Signature;
|
||||
import java.security.SignatureException;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static java.lang.String.format;
|
||||
import static org.jclouds.oauth.v2.OAuthConstants.NO_ALGORITHM;
|
||||
import static org.jclouds.oauth.v2.OAuthConstants.OAUTH_ALGORITHM_NAMES_TO_SIGNATURE_ALGORITHM_NAMES;
|
||||
import static org.jclouds.oauth.v2.config.OAuthProperties.SIGNATURE_OR_MAC_ALGORITHM;
|
||||
|
||||
/**
|
||||
* Function that signs/produces mac's for OAuth tokens, provided a {@link Signature} or a {@link Mac} algorithm and
|
||||
* {@link PrivateKey}
|
||||
*
|
||||
* @author David Alves
|
||||
*/
|
||||
public class SignOrProduceMacForToken implements Function<byte[], byte[]> {
|
||||
|
||||
private final Supplier<OAuthCredentials> credentials;
|
||||
private final String signatureOrMacAlgorithm;
|
||||
private Function<byte[], byte[]> signatureOrMacFunction;
|
||||
|
||||
|
||||
@Inject
|
||||
public SignOrProduceMacForToken(@Named(SIGNATURE_OR_MAC_ALGORITHM) String signatureOrMacAlgorithm,
|
||||
Supplier<OAuthCredentials> credentials) {
|
||||
checkState(OAUTH_ALGORITHM_NAMES_TO_SIGNATURE_ALGORITHM_NAMES.containsKey(signatureOrMacAlgorithm),
|
||||
format("the signature algorithm %s is not supported", signatureOrMacAlgorithm));
|
||||
this.signatureOrMacAlgorithm = OAUTH_ALGORITHM_NAMES_TO_SIGNATURE_ALGORITHM_NAMES.get(signatureOrMacAlgorithm);
|
||||
this.credentials = credentials;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void loadSignatureOrMacOrNone() throws InvalidKeyException, NoSuchAlgorithmException {
|
||||
if (signatureOrMacAlgorithm.equals(NO_ALGORITHM)) {
|
||||
this.signatureOrMacFunction = new Function<byte[], byte[]>() {
|
||||
@Override
|
||||
public byte[] apply(byte[] input) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
} else if (signatureOrMacAlgorithm.startsWith("SHA")) {
|
||||
this.signatureOrMacFunction = new SignatureGenerator(signatureOrMacAlgorithm, credentials.get().privateKey);
|
||||
} else {
|
||||
this.signatureOrMacFunction = new MessageAuthenticationCodeGenerator(signatureOrMacAlgorithm,
|
||||
credentials.get().privateKey);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] apply(byte[] input) {
|
||||
return signatureOrMacFunction.apply(input);
|
||||
}
|
||||
|
||||
private static class MessageAuthenticationCodeGenerator implements Function<byte[], byte[]> {
|
||||
|
||||
private Mac mac;
|
||||
|
||||
private MessageAuthenticationCodeGenerator(String macAlgorithm, PrivateKey privateKey) throws
|
||||
NoSuchAlgorithmException, InvalidKeyException {
|
||||
this.mac = Mac.getInstance(macAlgorithm);
|
||||
this.mac.init(privateKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] apply(byte[] input) {
|
||||
this.mac.update(input);
|
||||
return this.mac.doFinal();
|
||||
}
|
||||
}
|
||||
|
||||
private static class SignatureGenerator implements Function<byte[], byte[]> {
|
||||
|
||||
private Signature signature;
|
||||
|
||||
private SignatureGenerator(String signatureAlgorithm, PrivateKey privateKey) throws NoSuchAlgorithmException,
|
||||
InvalidKeyException {
|
||||
this.signature = Signature.getInstance(signatureAlgorithm);
|
||||
this.signature.initSign(privateKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] apply(byte[] input) {
|
||||
try {
|
||||
signature.update(input);
|
||||
return signature.sign();
|
||||
} catch (SignatureException e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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.handlers;
|
||||
|
||||
import org.jclouds.http.HttpCommand;
|
||||
import org.jclouds.http.HttpErrorHandler;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
import org.jclouds.http.HttpResponseException;
|
||||
import org.jclouds.rest.AuthorizationException;
|
||||
import org.jclouds.rest.ResourceNotFoundException;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import static javax.ws.rs.core.Response.Status;
|
||||
import static org.jclouds.http.HttpUtils.closeClientButKeepContentStream;
|
||||
|
||||
/**
|
||||
* This will parse and set an appropriate exception on the command object.
|
||||
*
|
||||
* @author David Alves
|
||||
*/
|
||||
@Singleton
|
||||
public class OAuthErrorHandler implements HttpErrorHandler {
|
||||
public void handleError(HttpCommand command, HttpResponse response) {
|
||||
// it is important to always read fully and close streams
|
||||
byte[] data = closeClientButKeepContentStream(response);
|
||||
String message = data != null ? new String(data) : null;
|
||||
|
||||
Exception exception = message != null ? new HttpResponseException(command, response, message)
|
||||
: new HttpResponseException(command, response);
|
||||
message = message != null ? message : String.format("%s -> %s", command.getCurrentRequest().getRequestLine(),
|
||||
response.getStatusLine());
|
||||
Status status = Status.fromStatusCode(response.getStatusCode());
|
||||
switch (status) {
|
||||
case BAD_REQUEST:
|
||||
break;
|
||||
case UNAUTHORIZED:
|
||||
case FORBIDDEN:
|
||||
exception = new AuthorizationException(message, exception);
|
||||
break;
|
||||
case NOT_FOUND:
|
||||
if (!command.getCurrentRequest().getMethod().equals("DELETE")) {
|
||||
exception = new ResourceNotFoundException(message, exception);
|
||||
}
|
||||
break;
|
||||
case CONFLICT:
|
||||
exception = new IllegalStateException(message, exception);
|
||||
break;
|
||||
}
|
||||
command.setException(exception);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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.handlers;
|
||||
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.oauth.v2.domain.TokenRequest;
|
||||
import org.jclouds.oauth.v2.domain.TokenRequestFormat;
|
||||
import org.jclouds.rest.Binder;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
/**
|
||||
* Generic implementation of a token binder. Uses a provided {@link TokenRequestFormat} to actually bind tokens to
|
||||
* requests.
|
||||
*
|
||||
* @author David Alves
|
||||
*/
|
||||
@Singleton
|
||||
public class OAuthTokenBinder implements Binder {
|
||||
|
||||
private final TokenRequestFormat tokenRequestFormat;
|
||||
|
||||
@Inject
|
||||
OAuthTokenBinder(TokenRequestFormat tokenRequestFormat) {
|
||||
this.tokenRequestFormat = tokenRequestFormat;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R extends HttpRequest> R bindToRequest(R request, Object input) {
|
||||
return tokenRequestFormat.formatRequest(request, (TokenRequest) input);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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.json;
|
||||
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import org.jclouds.oauth.v2.domain.ClaimSet;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* JSON TypeAdapter for the ClaimSet type. Pull the claims maps to the root level and adds two properties for the
|
||||
* expiration time and issuing time.
|
||||
*
|
||||
* @author David Alves
|
||||
*/
|
||||
public class ClaimSetTypeAdapter extends TypeAdapter<ClaimSet> {
|
||||
|
||||
@Override
|
||||
public void write(JsonWriter out, ClaimSet value) throws IOException {
|
||||
out.beginObject();
|
||||
for (Map.Entry<String, String> entry : value.entrySet()) {
|
||||
out.name(entry.getKey());
|
||||
out.value(entry.getValue());
|
||||
}
|
||||
out.name("exp");
|
||||
out.value(value.getExpirationTime());
|
||||
out.name("iat");
|
||||
out.value(value.getEmissionTime());
|
||||
out.endObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClaimSet read(JsonReader in) throws IOException {
|
||||
ClaimSet.Builder builder = new ClaimSet.Builder();
|
||||
in.beginObject();
|
||||
while (in.hasNext()) {
|
||||
String claimName = in.nextName();
|
||||
String claimValue = in.nextString();
|
||||
builder.addClaim(claimName, claimValue);
|
||||
}
|
||||
in.endObject();
|
||||
return builder.build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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.json;
|
||||
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import org.jclouds.oauth.v2.domain.Header;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* JSON TypeAdapter for the Header type. Simply transforms the field names.
|
||||
*/
|
||||
public class HeaderTypeAdapter extends TypeAdapter<Header> {
|
||||
|
||||
@Override
|
||||
public void write(JsonWriter out, Header value) throws IOException {
|
||||
out.beginObject();
|
||||
out.name("alg");
|
||||
out.value(value.getSignerAlgorithm());
|
||||
out.name("typ");
|
||||
out.value(value.getType());
|
||||
out.endObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Header read(JsonReader in) throws IOException {
|
||||
Header.Builder builder = new Header.Builder();
|
||||
in.beginObject();
|
||||
in.nextName();
|
||||
builder.signerAlgorithm(in.nextString());
|
||||
in.nextName();
|
||||
builder.type(in.nextString());
|
||||
in.endObject();
|
||||
return builder.build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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.json;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import org.jclouds.crypto.CryptoStreams;
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.io.Payloads;
|
||||
import org.jclouds.json.Json;
|
||||
import org.jclouds.oauth.v2.domain.TokenRequest;
|
||||
import org.jclouds.oauth.v2.domain.TokenRequestFormat;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.google.common.base.Joiner.on;
|
||||
|
||||
/**
|
||||
* Formats a token request into JWT format namely:
|
||||
* - transforms the token request to json
|
||||
* - creates the base64 header.claimset portions of the payload.
|
||||
* - uses the provided signer function to create a signature
|
||||
* - creates the full url encoded payload as described in:
|
||||
* https://developers.google.com/accounts/docs/OAuth2ServiceAccount
|
||||
* <p/>
|
||||
*
|
||||
* @author David Alves
|
||||
*/
|
||||
@Singleton
|
||||
public class JWTTokenRequestFormat implements TokenRequestFormat {
|
||||
|
||||
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
|
||||
public JWTTokenRequestFormat(Function<byte[], byte[]> signer, Json json) {
|
||||
this.signer = signer;
|
||||
this.json = json;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R extends HttpRequest> R formatRequest(R httpRequest, TokenRequest tokenRequest) {
|
||||
HttpRequest.Builder builder = httpRequest.toBuilder();
|
||||
|
||||
// transform to json and encode in base 64
|
||||
// use commons-codec base 64 which properly encodes urls (jclouds's Base64 does not)
|
||||
String encodedHeader = json.toJson(tokenRequest.getHeader());
|
||||
String encodedClaimSet = json.toJson(tokenRequest.getClaimSet());
|
||||
|
||||
String encodedSignature = null;
|
||||
|
||||
encodedHeader = CryptoStreams.base64Url(encodedHeader.getBytes(Charsets.UTF_8));
|
||||
encodedClaimSet = CryptoStreams.base64Url(encodedClaimSet.getBytes(Charsets.UTF_8));
|
||||
|
||||
byte[] signature = signer.apply(on(".").join(encodedHeader, encodedClaimSet).getBytes(Charsets.UTF_8));
|
||||
encodedSignature = signature != null ? CryptoStreams.base64Url(signature) : "";
|
||||
|
||||
// the final assertion in base 64 encoded {header}.{claimSet}.{signature} format
|
||||
String assertion = on(".").join(encodedHeader, encodedClaimSet, encodedSignature);
|
||||
|
||||
builder.payload(Payloads.newUrlEncodedFormPayload(ImmutableMultimap.of(GRANT_TYPE_FORM_PARAM,
|
||||
GRANT_TYPE_JWT_BEARER, ASSERTION_FORM_PARAM, assertion)));
|
||||
|
||||
return (R) builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeName() {
|
||||
return "JWT";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> requiredClaims() {
|
||||
// exp and ist (expiration and emission times) are assumed mandatory already
|
||||
return ImmutableSet.of("iss", "scope", "aud");
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
org.jclouds.oauth.v2.OAuthApiMetadata
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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.base.Ticker;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.name.Names;
|
||||
import org.jclouds.apis.BaseContextLiveTest;
|
||||
import org.jclouds.oauth.v2.domain.ClaimSet;
|
||||
import org.jclouds.oauth.v2.domain.Header;
|
||||
import org.jclouds.oauth.v2.domain.Token;
|
||||
import org.jclouds.oauth.v2.domain.TokenRequest;
|
||||
import org.jclouds.rest.RestContext;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static org.jclouds.oauth.v2.OAuthTestUtils.setCredentialFromPemFile;
|
||||
import static org.jclouds.oauth.v2.config.OAuthProperties.AUDIENCE;
|
||||
import static org.jclouds.oauth.v2.config.OAuthProperties.SIGNATURE_OR_MAC_ALGORITHM;
|
||||
import static org.testng.Assert.assertNotNull;
|
||||
|
||||
/**
|
||||
* A base test of oauth authenticated rest providers. Providers must set the following properties:
|
||||
* <p/>
|
||||
* - oauth.endpoint
|
||||
* - oauth.audience
|
||||
* - oauth.signature-or-mac-algorithm
|
||||
* <p/>
|
||||
* - oauth.scopes is provided by the subclass
|
||||
* <p/>
|
||||
* This test asserts that a provider can authenticate with oauth for a given scope, or more simply
|
||||
* that authentication/authorization is working.
|
||||
*
|
||||
* @author David Alves
|
||||
*/
|
||||
|
||||
@Test(groups = "live")
|
||||
public abstract class BaseOauthAuthenticatedRestContextLiveTest<S, A> extends BaseContextLiveTest<RestContext<S, A>> {
|
||||
|
||||
|
||||
private OAuthApi oauthApi;
|
||||
|
||||
@Override
|
||||
protected Properties setupProperties() {
|
||||
Properties props = super.setupProperties();
|
||||
setCredentialFromPemFile(props, provider + ".credential");
|
||||
return props;
|
||||
}
|
||||
|
||||
public void testAuthenticate() {
|
||||
|
||||
try {
|
||||
oauthApi = context.utils().injector().getInstance(OAuthApi.class);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Provider has no OAuthApi bound. Was the OAuthAuthenticationModule added?");
|
||||
}
|
||||
|
||||
// obtain the necessary properties from the context
|
||||
String signatureAlgorithm = getContextPropertyOrFail(SIGNATURE_OR_MAC_ALGORITHM);
|
||||
checkState(OAuthConstants.OAUTH_ALGORITHM_NAMES_TO_SIGNATURE_ALGORITHM_NAMES.containsKey(signatureAlgorithm)
|
||||
, String.format("Algorithm not supported: " + signatureAlgorithm));
|
||||
|
||||
String audience = getContextPropertyOrFail(AUDIENCE);
|
||||
|
||||
// obtain the scopes from the subclass
|
||||
String scopes = getScopes();
|
||||
|
||||
Header header = Header.builder().signerAlgorithm(signatureAlgorithm).type("JWT").build();
|
||||
|
||||
long now = TimeUnit.SECONDS.convert(Ticker.systemTicker().read(), TimeUnit.NANOSECONDS);
|
||||
|
||||
ClaimSet claimSet = ClaimSet.builder().addClaim("aud", audience).addClaim("scope", scopes).addClaim("iss",
|
||||
identity).emissionTime(now).expirationTime(now + 3600).build();
|
||||
|
||||
TokenRequest tokenRequest = TokenRequest.builder().header(header).claimSet(claimSet).build();
|
||||
|
||||
Token token = oauthApi.authenticate(tokenRequest);
|
||||
|
||||
assertNotNull(token);
|
||||
}
|
||||
|
||||
public abstract String getScopes();
|
||||
|
||||
private String getContextPropertyOrFail(String property) {
|
||||
try {
|
||||
return context.utils().injector().getInstance(Key.get(String.class, Names.named(property)));
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Provider " + provider + " must have a named property: " + property + " for " +
|
||||
"oauth to work");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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.ImmutableSet;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import org.jclouds.View;
|
||||
import org.jclouds.apis.internal.BaseApiMetadataTest;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/**
|
||||
* Tests that OAuthApiMetadata is properly registered in ServiceLoader
|
||||
* <p/>
|
||||
* <pre>
|
||||
* META-INF/services/org.jclouds.apis.ApiMetadata
|
||||
* </pre>
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(groups = "unit")
|
||||
public class OAuthApiMetadataTest extends BaseApiMetadataTest {
|
||||
|
||||
public OAuthApiMetadataTest() {
|
||||
super(new OAuthApiMetadata(), ImmutableSet.<TypeToken<? extends View>>of());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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.base.Charsets;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.io.Files;
|
||||
import org.jclouds.util.Strings2;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Properties;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static org.jclouds.oauth.v2.config.OAuthProperties.AUDIENCE;
|
||||
|
||||
/**
|
||||
* Utils for OAuth tests.
|
||||
*
|
||||
* @author David Alves
|
||||
*/
|
||||
public class OAuthTestUtils {
|
||||
|
||||
public static Properties defaultProperties(Properties properties) {
|
||||
try {
|
||||
properties = properties == null ? new Properties() : properties;
|
||||
properties.put("oauth.identity", "foo");
|
||||
properties.put("oauth.credential", Strings2.toStringAndClose(new FileInputStream("src/test/resources/testpk" +
|
||||
".pem")));
|
||||
properties.put("oauth.endpoint", "http://localhost:5000/o/oauth2/token");
|
||||
properties.put(AUDIENCE, "https://accounts.google.com/o/oauth2/token");
|
||||
return properties;
|
||||
} catch (IOException e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static String setCredentialFromPemFile(Properties overrides, String key) {
|
||||
String val = null;
|
||||
String credentialFromFile = null;
|
||||
String testKey = "test." + key;
|
||||
|
||||
if (System.getProperties().containsKey(testKey)) {
|
||||
val = System.getProperty(testKey);
|
||||
}
|
||||
checkNotNull(val, String.format("the property %s must be set (pem private key path)", testKey));
|
||||
|
||||
try {
|
||||
credentialFromFile = Files.toString(new File(val), Charsets.UTF_8);
|
||||
} catch (IOException e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
overrides.setProperty(key, credentialFromFile);
|
||||
return credentialFromFile;
|
||||
}
|
||||
|
||||
public static String getMandatoryProperty(Properties properties, String key) {
|
||||
checkNotNull(properties);
|
||||
checkNotNull(key);
|
||||
String value = properties.getProperty(key);
|
||||
return checkNotNull(value, String.format("mandatory property %s or test.%s was not present", key, key));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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.features;
|
||||
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
import org.jclouds.oauth.v2.OAuthApi;
|
||||
import org.jclouds.oauth.v2.OAuthTestUtils;
|
||||
import org.jclouds.oauth.v2.domain.ClaimSet;
|
||||
import org.jclouds.oauth.v2.domain.Header;
|
||||
import org.jclouds.oauth.v2.domain.Token;
|
||||
import org.jclouds.oauth.v2.domain.TokenRequest;
|
||||
import org.jclouds.oauth.v2.internal.BaseOAuthApiExpectTest;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Properties;
|
||||
|
||||
import static org.jclouds.crypto.CryptoStreams.base64Url;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Tests that a token requess is well formed.
|
||||
*
|
||||
* @author David Alves
|
||||
*/
|
||||
@Test(groups = "unit")
|
||||
public class OAuthApiExpectTest extends BaseOAuthApiExpectTest {
|
||||
|
||||
private static final String header = "{\"alg\":\"RS256\",\"typ\":\"JWT\"}";
|
||||
|
||||
private static final String claims = "{\"iss\":\"761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer" +
|
||||
".gserviceaccount.com\"," +
|
||||
"\"scope\":\"https://www.googleapis.com/auth/prediction\",\"aud\":\"https://accounts.google" +
|
||||
".com/o/oauth2/token\",\"exp\":1328573381,\"iat\":1328569781}";
|
||||
|
||||
private static final Token TOKEN = new Token.Builder().accessToken
|
||||
("1/8xbJqaOZXSUZbHLl5EOtu1pxz3fmmetKx9W8CV4t79M").tokenType("Bearer").expiresIn(3600).build();
|
||||
|
||||
private static final ClaimSet CLAIM_SET = new ClaimSet.Builder().addClaim("iss",
|
||||
"761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer" +
|
||||
".gserviceaccount.com")
|
||||
.addClaim("scope", "https://www.googleapis.com/auth/prediction")
|
||||
.addClaim("aud", "https://accounts.google.com/o/oauth2/token")
|
||||
.expirationTime(1328573381)
|
||||
.emissionTime(1328569781).build();
|
||||
|
||||
private static final Header HEADER = new Header.Builder().signerAlgorithm("RS256").type("JWT").build();
|
||||
|
||||
private static final String URL_ENCODED_TOKEN_REQUEST =
|
||||
"grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&" +
|
||||
// Base64 Encoded Header
|
||||
"assertion=" + base64Url(header.getBytes(Charset.forName("UTF-8"))) + "." +
|
||||
// Base64 Encoded Claims
|
||||
base64Url(claims.getBytes(Charset.forName("UTF-8"))) + "." +
|
||||
// Base64 encoded {header}.{claims} signature (using SHA256)
|
||||
"W2Lesr_98AzVYiMbzxFqmwcOjpIWlwqkC6pNn1fXND9oSDNNnFhy-AAR6DKH-x9ZmxbY80" +
|
||||
"R5fH-OCeWumXlVgceKN8Z2SmgQsu8ElTpypQA54j_5j8vUImJ5hsOUYPeyF1U2BUzZ3L5g" +
|
||||
"03PXBA0YWwRU9E1ChH28dQBYuGiUmYw";
|
||||
|
||||
private static final HttpRequest TOKEN_REQUEST = HttpRequest.builder()
|
||||
.method("POST")
|
||||
.endpoint(URI.create("http://localhost:5000/o/oauth2/token"))
|
||||
.addHeader("Accept", MediaType.APPLICATION_JSON)
|
||||
.payload(payloadFromStringWithContentType(URL_ENCODED_TOKEN_REQUEST, "application/x-www-form-urlencoded"))
|
||||
.build();
|
||||
|
||||
private static final HttpResponse TOKEN_RESPONSE = HttpResponse.builder().statusCode(200).payload(
|
||||
payloadFromString("{\n" +
|
||||
" \"access_token\" : \"1/8xbJqaOZXSUZbHLl5EOtu1pxz3fmmetKx9W8CV4t79M\",\n" +
|
||||
" \"token_type\" : \"Bearer\",\n" +
|
||||
" \"expires_in\" : 3600\n" +
|
||||
"}")).build();
|
||||
|
||||
@Override
|
||||
protected Properties setupProperties() {
|
||||
return OAuthTestUtils.defaultProperties(super.setupProperties());
|
||||
}
|
||||
|
||||
public void testGenerateJWTRequest() {
|
||||
OAuthApi api = requestSendsResponse(TOKEN_REQUEST, TOKEN_RESPONSE);
|
||||
assertEquals(api.authenticate(new TokenRequest(HEADER, CLAIM_SET)), TOKEN);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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.features;
|
||||
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import org.jclouds.oauth.v2.OAuthApi;
|
||||
import org.jclouds.oauth.v2.OAuthApiMetadata;
|
||||
import org.jclouds.oauth.v2.OAuthAsyncApi;
|
||||
import org.jclouds.oauth.v2.OAuthConstants;
|
||||
import org.jclouds.oauth.v2.domain.ClaimSet;
|
||||
import org.jclouds.oauth.v2.domain.Header;
|
||||
import org.jclouds.oauth.v2.domain.Token;
|
||||
import org.jclouds.oauth.v2.domain.TokenRequest;
|
||||
import org.jclouds.oauth.v2.internal.BaseOAuthApiLiveTest;
|
||||
import org.jclouds.rest.RestContext;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static org.jclouds.oauth.v2.OAuthTestUtils.getMandatoryProperty;
|
||||
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.testng.Assert.assertNotNull;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* A live test for authentication. Requires the following properties to be set:
|
||||
* - test.oauth.endpoint
|
||||
* - test.oauth.identity
|
||||
* - test.oauth.credential
|
||||
* - test.jclouds.oauth.audience
|
||||
* - test.jclouds.oauth.scopes
|
||||
* - test.jclouds.oauth.signature-or-mac-algorithm
|
||||
*
|
||||
* @author David Alves
|
||||
*/
|
||||
@Test(groups = "live", singleThreaded = true)
|
||||
public class OAuthApiLiveTest extends BaseOAuthApiLiveTest {
|
||||
|
||||
private Properties properties;
|
||||
|
||||
@Override
|
||||
protected Properties setupProperties() {
|
||||
properties = super.setupProperties();
|
||||
return properties;
|
||||
|
||||
}
|
||||
|
||||
@Test(groups = "live", singleThreaded = true)
|
||||
public void testAuthenticateJWTToken() throws Exception {
|
||||
assertTrue(properties != null, "properties were not set");
|
||||
String signatureAlgorithm = getMandatoryProperty(properties, SIGNATURE_OR_MAC_ALGORITHM);
|
||||
checkState(OAuthConstants.OAUTH_ALGORITHM_NAMES_TO_SIGNATURE_ALGORITHM_NAMES.containsKey(signatureAlgorithm)
|
||||
, String.format("Algorithm not supported: " + signatureAlgorithm));
|
||||
|
||||
Header header = Header.builder().signerAlgorithm(signatureAlgorithm).type("JWT").build();
|
||||
|
||||
String scopes = getMandatoryProperty(properties, SCOPES);
|
||||
String audience = getMandatoryProperty(properties, AUDIENCE);
|
||||
|
||||
long now = nowInSeconds();
|
||||
|
||||
ClaimSet claimSet = ClaimSet.builder().addClaim("aud", audience).addClaim("scope", scopes).addClaim("iss",
|
||||
identity).emissionTime(now).expirationTime(now + 3600).build();
|
||||
|
||||
TokenRequest tokenRequest = TokenRequest.builder().header(header).claimSet(claimSet).build();
|
||||
Token token = context.getApi().authenticate(tokenRequest);
|
||||
|
||||
assertNotNull(token);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TypeToken<RestContext<OAuthApi, OAuthAsyncApi>> contextType() {
|
||||
return OAuthApiMetadata.CONTEXT_TOKEN;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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 org.jclouds.oauth.v2.domain.OAuthCredentials;
|
||||
import org.jclouds.util.Strings2;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.UnrecoverableKeyException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertNotNull;
|
||||
|
||||
/**
|
||||
* Test loading the credentials by extracting a pk from a PKCS12 keystore.
|
||||
*/
|
||||
@Test(groups = "unit")
|
||||
public class OAuthCredentialsFromPKTest {
|
||||
|
||||
public static OAuthCredentials loadOAuthCredentials() throws IOException, NoSuchAlgorithmException,
|
||||
CertificateException, InvalidKeySpecException {
|
||||
OAuthCredentialsSupplier loader = new OAuthCredentialsSupplier("foo",
|
||||
Strings2.toStringAndClose(new FileInputStream("src/test/resources/testpk.pem")), "RS256");
|
||||
loader.loadPrivateKey();
|
||||
return loader.get();
|
||||
}
|
||||
|
||||
|
||||
public void testLoadPKString() throws IOException, NoSuchAlgorithmException, KeyStoreException,
|
||||
CertificateException, UnrecoverableKeyException, InvalidKeySpecException {
|
||||
OAuthCredentials creds = loadOAuthCredentials();
|
||||
assertNotNull(creds);
|
||||
assertEquals(creds.identity, "foo");
|
||||
assertNotNull(creds.privateKey);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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 org.jclouds.crypto.CryptoStreams;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
|
||||
import static com.google.common.base.Suppliers.ofInstance;
|
||||
import static org.testng.Assert.assertNotNull;
|
||||
import static org.testng.AssertJUnit.assertEquals;
|
||||
|
||||
|
||||
/**
|
||||
* Tests the SignOrProduceMacForToken
|
||||
*
|
||||
* @author David Alves
|
||||
*/
|
||||
@Test(groups = "unit")
|
||||
public class SignerFunctionTest {
|
||||
|
||||
private static final String PAYLOAD = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.\n" +
|
||||
"eyJpc3MiOiI3NjEzMjY3OTgwNjktcjVtbGpsbG4xcmQ0bHJiaGc3NWVmZ2lncDM2bTc4ajVAZ" +
|
||||
"GV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJzY29wZSI6Imh0dHBzOi8vd3d3Lmdvb2ds" +
|
||||
"ZWFwaXMuY29tL2F1dGgvcHJlZGljdGlvbiIsImF1ZCI6Imh0dHBzOi8vYWNjb3VudHMuZ29vZ2x" +
|
||||
"lLmNvbS9vL29hdXRoMi90b2tlbiIsImV4cCI6MTMyODU1NDM4NSwiaWF0IjoxMzI4NTUwNzg1fQ";
|
||||
|
||||
private static final String SHA256withRSA_PAYLOAD_SIGNATURE_RESULT =
|
||||
"bmQrCv4gjkLWDK1JNJni74_kPiSDUMF_FImgqKJMUIgkDX1m2Sg3bH1yjF-cjBN7CvfAscnageo" +
|
||||
"GtL2TGbwoTjJgUO5Yy0esavUUF-mBQHQtSw-2nL-9TNyM4SNi6fHPbgr83GGKOgA86r" +
|
||||
"I9-nj3oUGd1fQty2k4Lsd-Zdkz6es";
|
||||
|
||||
|
||||
public void testSignPayload() throws InvalidKeyException, IOException, NoSuchAlgorithmException,
|
||||
CertificateException, InvalidKeySpecException {
|
||||
SignOrProduceMacForToken signer = new SignOrProduceMacForToken("RS256",
|
||||
ofInstance(OAuthCredentialsFromPKTest
|
||||
.loadOAuthCredentials()));
|
||||
signer.loadSignatureOrMacOrNone();
|
||||
byte[] payloadSignature = signer.apply(PAYLOAD.getBytes("UTF-8"));
|
||||
assertNotNull(payloadSignature);
|
||||
assertEquals(CryptoStreams.base64Url(payloadSignature), SHA256withRSA_PAYLOAD_SIGNATURE_RESULT);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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.handlers;
|
||||
|
||||
import org.easymock.IArgumentMatcher;
|
||||
import org.jclouds.http.HttpCommand;
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import static org.easymock.EasyMock.createMock;
|
||||
import static org.easymock.EasyMock.expect;
|
||||
import static org.easymock.EasyMock.replay;
|
||||
import static org.easymock.EasyMock.reportMatcher;
|
||||
import static org.easymock.EasyMock.verify;
|
||||
|
||||
/**
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(groups = "unit")
|
||||
public class OAuthErrorHandlerTest {
|
||||
|
||||
@Test
|
||||
public void test409MakesIllegalStateException() {
|
||||
assertCodeMakes(
|
||||
"POST",
|
||||
URI.create("http://oauth.org"),
|
||||
409,
|
||||
"HTTP/1.1 409 Conflict",
|
||||
"\"{\"code\":\"InvalidState\",\"message\":\"An incompatible transition has already been queued for this" +
|
||||
" resource\"}\"",
|
||||
IllegalStateException.class);
|
||||
}
|
||||
|
||||
private void assertCodeMakes(String method, URI uri, int statusCode, String message, String content,
|
||||
Class<? extends Exception> expected) {
|
||||
assertCodeMakes(method, uri, statusCode, message, "application/json", content, expected);
|
||||
}
|
||||
|
||||
private void assertCodeMakes(String method, URI uri, int statusCode, String message, String contentType,
|
||||
String content, Class<? extends Exception> expected) {
|
||||
|
||||
OAuthErrorHandler function = new OAuthErrorHandler();
|
||||
|
||||
HttpCommand command = createMock(HttpCommand.class);
|
||||
HttpRequest request = HttpRequest.builder().method(method).endpoint(uri).build();
|
||||
HttpResponse response = HttpResponse.builder().statusCode(statusCode).message(message).payload(content).build();
|
||||
response.getPayload().getContentMetadata().setContentType(contentType);
|
||||
|
||||
expect(command.getCurrentRequest()).andReturn(request).atLeastOnce();
|
||||
command.setException(classEq(expected));
|
||||
|
||||
replay(command);
|
||||
|
||||
function.handleError(command, response);
|
||||
|
||||
verify(command);
|
||||
}
|
||||
|
||||
public static Exception classEq(final Class<? extends Exception> in) {
|
||||
reportMatcher(new IArgumentMatcher() {
|
||||
|
||||
@Override
|
||||
public void appendTo(StringBuffer buffer) {
|
||||
buffer.append("classEq(");
|
||||
buffer.append(in);
|
||||
buffer.append(")");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(Object arg) {
|
||||
return arg.getClass() == in;
|
||||
}
|
||||
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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.internal;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.sun.jersey.core.util.Base64;
|
||||
import org.jclouds.crypto.CryptoStreams;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Tests that the Base64 implementations used to Base64 encode the tokens are Url safe.
|
||||
*
|
||||
* @author David Alves
|
||||
*/
|
||||
@Test(groups = "unit")
|
||||
public class Base64UrlSafeTest {
|
||||
|
||||
public static final String STRING_THAT_GENERATES_URL_UNSAFE_BASE64_ENCODING = "§1234567890'+±!\"#$%&/()" +
|
||||
"=?*qwertyuiopº´WERTYUIOPªàsdfghjklç~ASDFGHJKLÇ^<zxcvbnm," +
|
||||
".->ZXCVBNM;:_@€";
|
||||
|
||||
|
||||
public void testJcloudsCoreBase64IsNotUrlSafe() {
|
||||
String encoded = new String(Base64.encode(STRING_THAT_GENERATES_URL_UNSAFE_BASE64_ENCODING.getBytes(Charsets
|
||||
.UTF_8)), Charsets.UTF_8);
|
||||
assertTrue(encoded.contains("+"), encoded);
|
||||
assertTrue(encoded.contains("/"), encoded);
|
||||
}
|
||||
|
||||
public void testUsedBase64IsUrlSafe() {
|
||||
String encoded = CryptoStreams.base64Url(
|
||||
STRING_THAT_GENERATES_URL_UNSAFE_BASE64_ENCODING.getBytes(Charsets.UTF_8));
|
||||
assertTrue(!encoded.contains("+"));
|
||||
assertTrue(!encoded.contains("/"));
|
||||
assertTrue(!encoded.endsWith("="));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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.internal;
|
||||
|
||||
import org.jclouds.oauth.v2.OAuthApi;
|
||||
|
||||
/**
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
public class BaseOAuthApiExpectTest extends BaseOAuthExpectTest<OAuthApi> {
|
||||
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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.internal;
|
||||
|
||||
import com.google.common.base.Ticker;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import org.jclouds.apis.BaseContextLiveTest;
|
||||
import org.jclouds.oauth.v2.OAuthApi;
|
||||
import org.jclouds.oauth.v2.OAuthApiMetadata;
|
||||
import org.jclouds.oauth.v2.OAuthAsyncApi;
|
||||
import org.jclouds.rest.RestContext;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static org.jclouds.oauth.v2.OAuthTestUtils.setCredentialFromPemFile;
|
||||
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;
|
||||
|
||||
|
||||
/**
|
||||
* @author David Alves
|
||||
*/
|
||||
@Test(groups = "live")
|
||||
public class BaseOAuthApiLiveTest extends BaseContextLiveTest<RestContext<OAuthApi, OAuthAsyncApi>> {
|
||||
|
||||
public BaseOAuthApiLiveTest() {
|
||||
provider = "oauth";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TypeToken<RestContext<OAuthApi, OAuthAsyncApi>> contextType() {
|
||||
return OAuthApiMetadata.CONTEXT_TOKEN;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Properties setupProperties() {
|
||||
Properties props = super.setupProperties();
|
||||
setCredentialFromPemFile(props, "oauth.credential");
|
||||
checkNotNull(setIfTestSystemPropertyPresent(props, "oauth.endpoint"), "test.oauth.endpoint must be set");
|
||||
checkNotNull(setIfTestSystemPropertyPresent(props, AUDIENCE), "test.jclouds.oauth.audience must be set");
|
||||
setIfTestSystemPropertyPresent(props, SCOPES);
|
||||
setIfTestSystemPropertyPresent(props, SIGNATURE_OR_MAC_ALGORITHM);
|
||||
return props;
|
||||
}
|
||||
|
||||
protected long nowInSeconds() {
|
||||
return TimeUnit.SECONDS.convert(Ticker.systemTicker().read(), TimeUnit.NANOSECONDS);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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.internal;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.inject.Module;
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
import org.jclouds.oauth.v2.OAuthAsyncApi;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
public class BaseOAuthAsyncApiExpectTest extends BaseOAuthExpectTest<OAuthAsyncApi> {
|
||||
public OAuthAsyncApi createClient(Function<HttpRequest, HttpResponse> fn, Module module, Properties props) {
|
||||
return createInjector(fn, module, props).getInstance(OAuthAsyncApi.class);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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.internal;
|
||||
|
||||
import org.jclouds.rest.internal.BaseRestApiExpectTest;
|
||||
|
||||
/**
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
public class BaseOAuthExpectTest<T> extends BaseRestApiExpectTest<T> {
|
||||
|
||||
public BaseOAuthExpectTest() {
|
||||
provider = "oauth";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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.json;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.Iterables;
|
||||
import org.jclouds.ContextBuilder;
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.oauth.v2.OAuthApiMetadata;
|
||||
import org.jclouds.oauth.v2.OAuthTestUtils;
|
||||
import org.jclouds.oauth.v2.domain.ClaimSet;
|
||||
import org.jclouds.oauth.v2.domain.Header;
|
||||
import org.jclouds.oauth.v2.domain.TokenRequest;
|
||||
import org.jclouds.oauth.v2.domain.TokenRequestFormat;
|
||||
import org.jclouds.util.Strings2;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.jclouds.oauth.v2.internal.Base64UrlSafeTest.STRING_THAT_GENERATES_URL_UNSAFE_BASE64_ENCODING;
|
||||
import static org.testng.Assert.assertNotNull;
|
||||
import static org.testng.Assert.assertSame;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author David Alves
|
||||
*/
|
||||
@Test(groups = "unit")
|
||||
public class JWTTokenRequestFormatTest {
|
||||
|
||||
public void testPayloadIsUrlSafe() throws IOException {
|
||||
|
||||
|
||||
TokenRequestFormat tokenRequestFormat = ContextBuilder.newBuilder(new OAuthApiMetadata()).overrides
|
||||
(OAuthTestUtils.defaultProperties(null)).build().utils()
|
||||
.injector().getInstance(TokenRequestFormat.class);
|
||||
Header header = new Header.Builder().signerAlgorithm("a").type("b").build();
|
||||
ClaimSet claimSet = new ClaimSet.Builder().addClaim("ist", STRING_THAT_GENERATES_URL_UNSAFE_BASE64_ENCODING)
|
||||
.build();
|
||||
TokenRequest tokenRequest = new TokenRequest.Builder().claimSet(claimSet).header(header).build();
|
||||
HttpRequest request = tokenRequestFormat.formatRequest(HttpRequest.builder().method("GET").endpoint
|
||||
("http://localhost").build(), tokenRequest);
|
||||
|
||||
assertNotNull(request.getPayload());
|
||||
|
||||
String payload = Strings2.toStringAndClose(request.getPayload().getInput());
|
||||
|
||||
// make sure the paylod is in the format {header}.{claims}.{signature}
|
||||
Iterable<String> parts = Splitter.on(".").split(payload);
|
||||
|
||||
assertSame(Iterables.size(parts), 3);
|
||||
|
||||
assertTrue(!payload.contains("+"));
|
||||
assertTrue(!payload.contains("/"));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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.parse;
|
||||
|
||||
import org.jclouds.json.BaseItemParserTest;
|
||||
import org.jclouds.oauth.v2.domain.Token;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
/**
|
||||
* @author David Alves
|
||||
*/
|
||||
@Test(groups = "unit")
|
||||
public class ParseTokenTest extends BaseItemParserTest<Token> {
|
||||
|
||||
@Override
|
||||
public String resource() {
|
||||
return "/tokenResponse.json";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
public Token expected() {
|
||||
return Token.builder().expiresIn(3600).tokenType("Bearer").accessToken
|
||||
("1/8xbJqaOZXSUZbHLl5EOtu1pxz3fmmetKx9W8CV4t79M").build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0"?>
|
||||
<configuration scan="false">
|
||||
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
|
||||
<file>target/test-data/jclouds.log</file>
|
||||
|
||||
<encoder>
|
||||
<Pattern>%d %-5p [%c] [%thread] %m%n</Pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<appender name="WIREFILE" class="ch.qos.logback.core.FileAppender">
|
||||
<file>target/test-data/jclouds-wire.log</file>
|
||||
|
||||
<encoder>
|
||||
<Pattern>%d %-5p [%c] [%thread] %m%n</Pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root>
|
||||
<level value="warn" />
|
||||
</root>
|
||||
|
||||
<logger name="org.jclouds">
|
||||
<level value="DEBUG" />
|
||||
<appender-ref ref="FILE" />
|
||||
</logger>
|
||||
|
||||
<logger name="jclouds.wire">
|
||||
<level value="DEBUG" />
|
||||
<appender-ref ref="WIREFILE" />
|
||||
</logger>
|
||||
|
||||
<logger name="jclouds.headers">
|
||||
<level value="DEBUG" />
|
||||
<appender-ref ref="WIREFILE" />
|
||||
</logger>
|
||||
|
||||
</configuration>
|
|
@ -0,0 +1,15 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICXgIBAAKBgQCwqwzakEPP+U9vx9JCuMHebFIVQZ4Sjaj2RU9dJ6YT2s3u7dC6
|
||||
/0fGM5xm4fXmSHqyGC6PC8weQSkxnSpbU+R4jMWPM8ML4TIr5wP0avbg+wy3+WWI
|
||||
of0MN7YHkCfqpaaiKiMw7niK1y07YvxJN8LX1xLpE7aXgIpn6L/qtJdHnQIDAQAB
|
||||
AoGBAIAHlcsW3W3smPrC7sdXqXeNPHcXFH0RmC7Qz9EMmLiuyqqqQagitFsYr/GH
|
||||
M3Ltd611BNi5jfUm97ly0m1ZAKp/nkTMVhfKfRIVsBDHtjQHcUOR9tr0LslptmaN
|
||||
TG0bovbUohe5KwOqMK4YOjUQbInChVBrf7VrNQtv8e0eShdpAkEA3lzLP9QYfP1i
|
||||
C4iYXqS7cgMDrs3qujC7PoyB54maen+Uvgyau1ZJpKMzXYkORPcYk+b71bl9jF80
|
||||
U+7LDnJjPwJBAMtksvL1V8DC5DYL43497JW4KBN4YZ3K7YWx/9gkvc3Q9VdXiUGu
|
||||
6WKjmcbmsPI/jFdeO71uy934N8qEXLJcyiMCQQCTNKcxWD3l8PCJZiJI9ZFKBwjX
|
||||
Hmb4X+51mBsfpw7nbbKQplOBFbynC4ujrmoN6e8RaubpNGUTGqvPrNQsejmNAkEA
|
||||
lUDEAH4BczaQ+QgoXI9ceVG2NvNzzrMHMcC5Ggd8MPhR0VIvKsAMC5I6WjcXSe1Q
|
||||
Mxy3gf84Ix7u8fHHhCuLOQJAQRhrlXiQUk4cJumNhjza5/+KtaV4FPbEQi+qcWE6
|
||||
tGoHPEBfbXyUdcUD4ae8X1W0yri0BuyVNaOCpGCBRIhPZA==
|
||||
-----END RSA PRIVATE KEY-----
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"access_token" : "1/8xbJqaOZXSUZbHLl5EOtu1pxz3fmmetKx9W8CV4t79M",
|
||||
"token_type" : "Bearer",
|
||||
"expires_in" : 3600
|
||||
}
|
|
@ -18,8 +18,7 @@
|
|||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
--><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>jclouds-project</artifactId>
|
||||
|
@ -61,5 +60,6 @@
|
|||
<module>fgcp-au</module>
|
||||
<module>fgcp-de</module>
|
||||
<module>abiquo</module>
|
||||
</modules>
|
||||
<module>oauth</module>
|
||||
</modules>
|
||||
</project>
|
||||
|
|
Loading…
Reference in New Issue