mirror of https://github.com/apache/jclouds.git
JCLOUDS-750 At the cost of fiddling with type hierarchy adapters, remove lots of junk with google auto.
This commit is contained in:
parent
f94d0caf3b
commit
07d7a5a749
|
@ -48,6 +48,12 @@
|
|||
<artifactId>jclouds-core</artifactId>
|
||||
<version>${jclouds.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.auto.value</groupId>
|
||||
<artifactId>auto-value</artifactId>
|
||||
<version>1.0-rc2</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.jclouds</groupId>
|
||||
<artifactId>jclouds-core</artifactId>
|
||||
|
@ -61,6 +67,11 @@
|
|||
<version>${jclouds.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp</groupId>
|
||||
<artifactId>mockwebserver</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
|
|
|
@ -57,5 +57,4 @@ public interface OAuthApi extends Closeable {
|
|||
@POST
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
Token authenticate(@BinderParam(OAuthTokenBinder.class) TokenRequest tokenRequest) throws AuthorizationException;
|
||||
|
||||
}
|
||||
|
|
|
@ -22,16 +22,17 @@ import static org.jclouds.oauth.v2.config.OAuthProperties.SIGNATURE_OR_MAC_ALGOR
|
|||
import java.net.URI;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.jclouds.apis.ApiMetadata;
|
||||
import org.jclouds.oauth.v2.config.OAuthHttpApiModule;
|
||||
import org.jclouds.oauth.v2.config.OAuthModule;
|
||||
import org.jclouds.oauth.v2.config.OAuthParserModule;
|
||||
import org.jclouds.rest.internal.BaseHttpApiMetadata;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.inject.Module;
|
||||
|
||||
/**
|
||||
* Implementation of {@link ApiMetadata} for OAuth 2 API
|
||||
*/
|
||||
@AutoService(ApiMetadata.class)
|
||||
public class OAuthApiMetadata extends BaseHttpApiMetadata<OAuthApi> {
|
||||
|
||||
@Override
|
||||
|
@ -64,7 +65,8 @@ public class OAuthApiMetadata extends BaseHttpApiMetadata<OAuthApi> {
|
|||
.documentation(URI.create("TODO"))
|
||||
.version("2")
|
||||
.defaultProperties(OAuthApiMetadata.defaultProperties())
|
||||
.defaultModules(ImmutableSet.<Class<? extends Module>>of(OAuthModule.class, OAuthHttpApiModule.class));
|
||||
.defaultModules(ImmutableSet
|
||||
.<Class<? extends Module>>of(OAuthModule.class, OAuthParserModule.class, OAuthHttpApiModule.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,21 +16,10 @@
|
|||
*/
|
||||
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.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Provides;
|
||||
import com.google.inject.Singleton;
|
||||
import com.google.inject.TypeLiteral;
|
||||
import com.google.inject.name.Named;
|
||||
import org.jclouds.oauth.v2.domain.ClaimSet;
|
||||
import org.jclouds.oauth.v2.domain.Header;
|
||||
import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.jclouds.oauth.v2.domain.OAuthCredentials;
|
||||
import org.jclouds.oauth.v2.domain.Token;
|
||||
import org.jclouds.oauth.v2.domain.TokenRequest;
|
||||
|
@ -41,28 +30,25 @@ 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 java.lang.reflect.Type;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
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.inject.AbstractModule;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Provides;
|
||||
import com.google.inject.Singleton;
|
||||
import com.google.inject.TypeLiteral;
|
||||
import com.google.inject.name.Named;
|
||||
|
||||
import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
|
||||
|
||||
/**
|
||||
* Base OAuth module
|
||||
*/
|
||||
public class OAuthModule extends AbstractModule {
|
||||
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
@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(CredentialType.class).toProvider(CredentialTypeFromPropertyOrDefault.class);
|
||||
bind(new TypeLiteral<Supplier<OAuthCredentials>>() {}).to(OAuthCredentialsSupplier.class);
|
||||
bind(new TypeLiteral<Function<GeneratedHttpRequest, TokenRequest>>() {}).to(BuildTokenRequest.class);
|
||||
|
|
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jclouds.oauth.v2.config;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.oauth.v2.domain.ClaimSet;
|
||||
import org.jclouds.oauth.v2.domain.Header;
|
||||
import org.jclouds.oauth.v2.domain.Token;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Provides;
|
||||
|
||||
/** Configures type adapter factories for {@link Header}, {@link ClaimSet}, and {@link Token}. */
|
||||
public final class OAuthParserModule extends AbstractModule {
|
||||
@Override protected void configure() {
|
||||
}
|
||||
|
||||
// TODO: change jclouds core to use collaborative set bindings
|
||||
@Provides @Singleton public Set<TypeAdapterFactory> typeAdapterFactories() {
|
||||
return ImmutableSet
|
||||
.<TypeAdapterFactory>of(new HeaderTypeAdapter(), new ClaimSetTypeAdapter(), new TokenAdapter());
|
||||
}
|
||||
|
||||
private static final class HeaderTypeAdapter extends SubtypeAdapterFactory<Header> {
|
||||
HeaderTypeAdapter() {
|
||||
super(Header.class);
|
||||
}
|
||||
|
||||
@Override public void write(JsonWriter out, Header value) throws IOException {
|
||||
out.beginObject();
|
||||
out.name("alg");
|
||||
out.value(value.signerAlgorithm());
|
||||
out.name("typ");
|
||||
out.value(value.type());
|
||||
out.endObject();
|
||||
}
|
||||
|
||||
@Override public Header read(JsonReader in) throws IOException {
|
||||
in.beginObject();
|
||||
in.nextName();
|
||||
String signerAlgorithm = in.nextString();
|
||||
in.nextName();
|
||||
String type = in.nextString();
|
||||
in.endObject();
|
||||
return Header.create(signerAlgorithm, type);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ClaimSetTypeAdapter extends SubtypeAdapterFactory<ClaimSet> {
|
||||
ClaimSetTypeAdapter() {
|
||||
super(ClaimSet.class);
|
||||
}
|
||||
|
||||
@Override public void write(JsonWriter out, ClaimSet value) throws IOException {
|
||||
out.beginObject();
|
||||
for (Map.Entry<String, String> entry : value.claims().entrySet()) {
|
||||
out.name(entry.getKey());
|
||||
out.value(entry.getValue());
|
||||
}
|
||||
out.name("exp");
|
||||
out.value(value.expirationTime());
|
||||
out.name("iat");
|
||||
out.value(value.emissionTime());
|
||||
out.endObject();
|
||||
}
|
||||
|
||||
@Override public ClaimSet read(JsonReader in) throws IOException {
|
||||
Map<String, String> claims = new LinkedHashMap<String, String>();
|
||||
in.beginObject();
|
||||
while (in.hasNext()) {
|
||||
claims.put(in.nextName(), in.nextString());
|
||||
}
|
||||
in.endObject();
|
||||
return ClaimSet.create(0, 0, Collections.unmodifiableMap(claims));
|
||||
}
|
||||
}
|
||||
|
||||
/** OAuth is used in apis that may not default to snake case. Explicity control case format. */
|
||||
private static final class TokenAdapter extends SubtypeAdapterFactory<Token> {
|
||||
TokenAdapter() {
|
||||
super(Token.class);
|
||||
}
|
||||
|
||||
@Override public void write(JsonWriter out, Token value) throws IOException {
|
||||
out.beginObject();
|
||||
out.name("access_token");
|
||||
out.value(value.accessToken());
|
||||
out.name("token_type");
|
||||
out.value(value.tokenType());
|
||||
out.name("expires_in");
|
||||
out.value(value.expiresIn());
|
||||
out.endObject();
|
||||
}
|
||||
|
||||
@Override public Token read(JsonReader in) throws IOException {
|
||||
String accessToken = null;
|
||||
String tokenType = null;
|
||||
int expiresIn = 0;
|
||||
in.beginObject();
|
||||
while (in.hasNext()) {
|
||||
String name = in.nextName();
|
||||
if (name.equals("access_token")) {
|
||||
accessToken = in.nextString();
|
||||
} else if (name.equals("token_type")) {
|
||||
tokenType = in.nextString();
|
||||
} else if (name.equals("expires_in")) {
|
||||
expiresIn = in.nextInt();
|
||||
} else {
|
||||
in.skipValue();
|
||||
}
|
||||
}
|
||||
in.endObject();
|
||||
return Token.create(accessToken, tokenType, expiresIn);
|
||||
}
|
||||
}
|
||||
|
||||
private abstract static class SubtypeAdapterFactory<T> extends TypeAdapter<T> implements TypeAdapterFactory {
|
||||
private final Class<T> baseClass;
|
||||
|
||||
private SubtypeAdapterFactory(Class<T> baseClass) {
|
||||
this.baseClass = baseClass;
|
||||
}
|
||||
|
||||
@Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
|
||||
if (!(baseClass.isAssignableFrom(typeToken.getRawType()))) {
|
||||
return null;
|
||||
}
|
||||
return (TypeAdapter<T>) this;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,176 +16,27 @@
|
|||
*/
|
||||
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;
|
||||
import com.google.auto.value.AutoValue;
|
||||
|
||||
/**
|
||||
* The claimset for the token.
|
||||
* The claimset for the {@linkplain Token}.
|
||||
*
|
||||
* @see <a
|
||||
* href="https://developers.google.com/accounts/docs/OAuth2ServiceAccount"
|
||||
* >doc</a>
|
||||
* @see <a href="https://developers.google.com/accounts/docs/OAuth2ServiceAccount">doc</a>
|
||||
*/
|
||||
public class ClaimSet extends ForwardingMap<String, String> {
|
||||
@AutoValue
|
||||
public abstract class ClaimSet {
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
/** The emission time, in seconds since the epoch. */
|
||||
public abstract long emissionTime();
|
||||
|
||||
public Builder toBuilder() {
|
||||
return builder().fromClaimSet(this);
|
||||
}
|
||||
/** The expiration time, in seconds since the emission time. */
|
||||
public abstract long expirationTime();
|
||||
|
||||
public static class Builder {
|
||||
public abstract Map<String, String> claims();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return string().toString();
|
||||
}
|
||||
|
||||
protected ToStringHelper string() {
|
||||
return toStringHelper(this).omitNullValues().add("claims", claims)
|
||||
.add("emissionTime", emissionTime).add("expirationTIme", expirationTime);
|
||||
public static ClaimSet create(long emissionTime, long expirationTime, Map<String, String> claims) {
|
||||
return new AutoValue_ClaimSet(emissionTime, expirationTime, claims);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,113 +16,23 @@
|
|||
*/
|
||||
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;
|
||||
import com.google.auto.value.AutoValue;
|
||||
|
||||
/**
|
||||
* The header for the OAuth token, contains the signer algorithm's name and the type of the token
|
||||
*
|
||||
* @see <a href="https://developers.google.com/accounts/docs/OAuth2ServiceAccount">doc</a>
|
||||
*/
|
||||
public class Header {
|
||||
@AutoValue
|
||||
public abstract class Header {
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
/** The name of the algorithm used to compute the signature, e.g., {@code RS256}. */
|
||||
public abstract String signerAlgorithm();
|
||||
|
||||
public Builder toBuilder() {
|
||||
return builder().fromHeader(this);
|
||||
}
|
||||
/** The type of the token, e.g., {@code JWT}. */
|
||||
public abstract String type();
|
||||
|
||||
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);
|
||||
public static Header create(String signerAlgorithm, String type){
|
||||
return new AutoValue_Header(signerAlgorithm, type);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,134 +16,24 @@
|
|||
*/
|
||||
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;
|
||||
import com.google.auto.value.AutoValue;
|
||||
|
||||
/**
|
||||
* The oauth token, obtained upon a successful token request and ready to embed in requests.
|
||||
*/
|
||||
public class Token {
|
||||
@AutoValue
|
||||
public abstract class Token {
|
||||
/** The access token obtained from the OAuth server. */
|
||||
public abstract String accessToken();
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
/** The type of the token, e.g., {@code Bearer}. */
|
||||
public abstract String tokenType();
|
||||
|
||||
/** In how many seconds this token expires. */
|
||||
public abstract long expiresIn();
|
||||
|
||||
public static Token
|
||||
create(String accessToken, String tokenType, long expiresIn) {
|
||||
return new AutoValue_Token(accessToken, tokenType, expiresIn);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,116 +16,14 @@
|
|||
*/
|
||||
package org.jclouds.oauth.v2.domain;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.auto.value.AutoValue;
|
||||
|
||||
import static com.google.common.base.Objects.equal;
|
||||
import static com.google.common.base.Objects.toStringHelper;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
@AutoValue
|
||||
public abstract class TokenRequest {
|
||||
public abstract Header header();
|
||||
public abstract ClaimSet claimSet();
|
||||
|
||||
/**
|
||||
* A complete token request.
|
||||
*/
|
||||
public class TokenRequest {
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
public static TokenRequest create(Header header, ClaimSet claimSet) {
|
||||
return new AutoValue_TokenRequest(header, claimSet);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ public interface TokenRequestFormat {
|
|||
/**
|
||||
* The name of the type of the token request, e.g., "JWT"
|
||||
*/
|
||||
String getTypeName();
|
||||
String type();
|
||||
|
||||
/**
|
||||
* The claims that must be present in the token request for it to be valid.
|
||||
|
|
|
@ -54,7 +54,7 @@ public class OAuthAuthenticator implements OAuthAuthenticationFilter {
|
|||
TokenRequest tokenRequest = tokenRequestBuilder.apply(generatedHttpRequest);
|
||||
Token token = tokenFetcher.apply(tokenRequest);
|
||||
return request.toBuilder().addHeader("Authorization", String.format("%s %s",
|
||||
token.getTokenType(), token.getAccessToken())).build();
|
||||
token.tokenType(), token.accessToken())).build();
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,8 @@ 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 java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
@ -38,7 +40,6 @@ import org.jclouds.rest.internal.GeneratedHttpRequest;
|
|||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.reflect.Invokable;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.name.Named;
|
||||
|
@ -61,7 +62,7 @@ public class BuildTokenRequest implements Function<GeneratedHttpRequest, TokenRe
|
|||
|
||||
@Inject(optional = true)
|
||||
@Named(ADDITIONAL_CLAIMS)
|
||||
protected Map<String, String> additionalClaims = ImmutableMap.of();
|
||||
protected Map<String, String> additionalClaims = Collections.emptyMap();
|
||||
|
||||
@Inject(optional = true)
|
||||
@Named(SCOPES)
|
||||
|
@ -93,24 +94,20 @@ public class BuildTokenRequest implements Function<GeneratedHttpRequest, TokenRe
|
|||
long now = timeSourceMillisSinceEpoch.get() / 1000;
|
||||
|
||||
// fetch the token
|
||||
Header header = new Header.Builder()
|
||||
.signerAlgorithm(signatureAlgorithm)
|
||||
.type(tokenRequestFormat.getTypeName())
|
||||
.build();
|
||||
Header header = Header.create(signatureAlgorithm, tokenRequestFormat.type());
|
||||
|
||||
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();
|
||||
Map<String, String> claims = new LinkedHashMap<String, String>();
|
||||
claims.put("iss", credentialsSupplier.get().identity);
|
||||
claims.put("scope", getOAuthScopes(request));
|
||||
claims.put("aud", assertionTargetDescription);
|
||||
claims.putAll(additionalClaims);
|
||||
|
||||
return new TokenRequest.Builder()
|
||||
.header(header)
|
||||
.claimSet(claimSet)
|
||||
.build();
|
||||
checkState(claims.keySet().containsAll(tokenRequestFormat.requiredClaims()),
|
||||
"not all required claims were present");
|
||||
|
||||
ClaimSet claimSet = ClaimSet.create(now, now + tokenDuration, Collections.unmodifiableMap(claims));
|
||||
|
||||
return TokenRequest.create(header, claimSet);
|
||||
}
|
||||
|
||||
protected String getOAuthScopes(GeneratedHttpRequest request) {
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jclouds.oauth.v2.internal;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
/**
|
||||
* Type adapter used to serialize all subtypes of a value. This can be used to force serialization for an {@link
|
||||
* com.google.auto.value.AutoValue} generated class.
|
||||
*/
|
||||
public abstract class SubtypeAdapterFactory<T> extends TypeAdapter<T> implements TypeAdapterFactory {
|
||||
private final Class<T> baseClass;
|
||||
|
||||
protected SubtypeAdapterFactory(Class<T> baseClass) {
|
||||
this.baseClass = baseClass;
|
||||
}
|
||||
|
||||
/** Accepts duty for any subtype. When using AutoValue properly, this will be the generated form. */
|
||||
@Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
|
||||
if (!(baseClass.isAssignableFrom(typeToken.getRawType()))) {
|
||||
return null;
|
||||
}
|
||||
return (TypeAdapter<T>) this;
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jclouds.oauth.v2.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.
|
||||
*/
|
||||
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();
|
||||
}
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jclouds.oauth.v2.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();
|
||||
}
|
||||
}
|
|
@ -24,7 +24,6 @@ import static org.jclouds.io.Payloads.newUrlEncodedFormPayload;
|
|||
import java.util.Set;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.io.Payload;
|
||||
|
@ -45,7 +44,6 @@ import com.google.common.collect.ImmutableSet;
|
|||
* https://developers.google.com/accounts/docs/OAuth2ServiceAccount
|
||||
* <p/>
|
||||
*/
|
||||
@Singleton
|
||||
public class JWTTokenRequestFormat implements TokenRequestFormat {
|
||||
|
||||
private static final String ASSERTION_FORM_PARAM = "assertion";
|
||||
|
@ -55,18 +53,15 @@ public class JWTTokenRequestFormat implements TokenRequestFormat {
|
|||
private final Function<byte[], byte[]> signer;
|
||||
private final Json json;
|
||||
|
||||
@Inject
|
||||
public JWTTokenRequestFormat(Function<byte[], byte[]> signer, Json json) {
|
||||
@Inject JWTTokenRequestFormat(Function<byte[], byte[]> signer, Json json) {
|
||||
this.signer = signer;
|
||||
this.json = json;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <R extends HttpRequest> R formatRequest(R request, TokenRequest tokenRequest) {
|
||||
@Override public <R extends HttpRequest> R formatRequest(R request, TokenRequest tokenRequest) {
|
||||
|
||||
String encodedHeader = json.toJson(tokenRequest.getHeader());
|
||||
String encodedClaimSet = json.toJson(tokenRequest.getClaimSet());
|
||||
String encodedHeader = json.toJson(tokenRequest.header());
|
||||
String encodedClaimSet = json.toJson(tokenRequest.claimSet());
|
||||
|
||||
encodedHeader = base64Url().omitPadding().encode(encodedHeader.getBytes(UTF_8));
|
||||
encodedClaimSet = base64Url().omitPadding().encode(encodedClaimSet.getBytes(UTF_8));
|
||||
|
@ -83,13 +78,11 @@ public class JWTTokenRequestFormat implements TokenRequestFormat {
|
|||
return (R) request.toBuilder().payload(payload).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeName() {
|
||||
@Override public String type() {
|
||||
return "JWT";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> requiredClaims() {
|
||||
@Override public Set<String> requiredClaims() {
|
||||
// exp and ist (expiration and emission times) are assumed mandatory already
|
||||
return ImmutableSet.of("iss", "scope", "aud");
|
||||
}
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
org.jclouds.oauth.v2.OAuthApiMetadata
|
|
@ -16,35 +16,33 @@
|
|||
*/
|
||||
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.oauth.v2.config.CredentialType;
|
||||
import org.jclouds.oauth.v2.config.OAuthProperties;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Throwables.propagate;
|
||||
import static org.jclouds.oauth.v2.config.OAuthProperties.AUDIENCE;
|
||||
import static org.jclouds.util.Strings2.toStringAndClose;
|
||||
|
||||
import java.io.File;
|
||||
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;
|
||||
import org.jclouds.oauth.v2.config.CredentialType;
|
||||
import org.jclouds.oauth.v2.config.OAuthProperties;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.io.Files;
|
||||
|
||||
/**
|
||||
* Utils for OAuth tests.
|
||||
*/
|
||||
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",
|
||||
Files.asCharSource(new File("src/test/resources/testpk.pem"), Charsets.UTF_8).read());
|
||||
properties.put("oauth.credential", toStringAndClose(OAuthTestUtils.class.getResourceAsStream("/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);
|
||||
throw propagate(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,7 +73,7 @@ public class OAuthTestUtils {
|
|||
try {
|
||||
credentialFromFile = Files.toString(new File(val), Charsets.UTF_8);
|
||||
} catch (IOException e) {
|
||||
throw Throwables.propagate(e);
|
||||
throw propagate(e);
|
||||
}
|
||||
overrides.setProperty(key, credentialFromFile);
|
||||
return credentialFromFile;
|
||||
|
|
|
@ -1,99 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jclouds.oauth.v2.features;
|
||||
|
||||
import static com.google.common.base.Charsets.UTF_8;
|
||||
import static com.google.common.io.BaseEncoding.base64Url;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* Tests that a token requess is well formed.
|
||||
*/
|
||||
@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().omitPadding().encode(header.getBytes(UTF_8)) + "." +
|
||||
// Base64 Encoded Claims
|
||||
base64Url().omitPadding().encode(claims.getBytes(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);
|
||||
}
|
||||
}
|
|
@ -34,6 +34,8 @@ import org.jclouds.oauth.v2.domain.TokenRequest;
|
|||
import org.jclouds.oauth.v2.internal.BaseOAuthApiLiveTest;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
/**
|
||||
* A live test for authentication. Requires the following properties to be set:
|
||||
* - test.oauth.endpoint
|
||||
|
@ -62,17 +64,17 @@ public class OAuthApiLiveTest extends BaseOAuthApiLiveTest {
|
|||
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();
|
||||
Header header = Header.create(signatureAlgorithm, "JWT");
|
||||
|
||||
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();
|
||||
ClaimSet claimSet = ClaimSet.create(now, now + 3600,
|
||||
ImmutableMap.of("aud", audience, "scope", scopes, "iss", identity));
|
||||
|
||||
TokenRequest tokenRequest = TokenRequest.builder().header(header).claimSet(claimSet).build();
|
||||
TokenRequest tokenRequest = TokenRequest.create(header, claimSet);
|
||||
Token token = api.authenticate(tokenRequest);
|
||||
|
||||
assertNotNull(token, "no token when authenticating " + tokenRequest);
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jclouds.oauth.v2.features;
|
||||
|
||||
import static com.google.common.base.Charsets.UTF_8;
|
||||
import static com.google.common.io.BaseEncoding.base64Url;
|
||||
import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor;
|
||||
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
|
||||
import static org.jclouds.Constants.PROPERTY_MAX_RETRIES;
|
||||
import static org.jclouds.oauth.v2.config.OAuthProperties.AUDIENCE;
|
||||
import static org.jclouds.util.Strings2.toStringAndClose;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.jclouds.ContextBuilder;
|
||||
import org.jclouds.concurrent.config.ExecutorServiceModule;
|
||||
import org.jclouds.oauth.v2.OAuthApi;
|
||||
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.Token;
|
||||
import org.jclouds.oauth.v2.domain.TokenRequest;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.io.BaseEncoding;
|
||||
import com.google.inject.Module;
|
||||
import com.squareup.okhttp.mockwebserver.MockResponse;
|
||||
import com.squareup.okhttp.mockwebserver.MockWebServer;
|
||||
import com.squareup.okhttp.mockwebserver.RecordedRequest;
|
||||
|
||||
@Test(groups = "unit", testName = "OAuthApiMockTest")
|
||||
public class OAuthApiMockTest {
|
||||
|
||||
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 = Token.create("1/8xbJqaOZXSUZbHLl5EOtu1pxz3fmmetKx9W8CV4t79M", "Bearer", 3600);
|
||||
|
||||
private static final ClaimSet CLAIM_SET = ClaimSet.create(1328569781, 1328573381, ImmutableMap
|
||||
.of("iss", "761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer.gserviceaccount.com", "scope",
|
||||
"https://www.googleapis.com/auth/prediction", "aud", "https://accounts.google.com/o/oauth2/token"));
|
||||
|
||||
private static final Header HEADER = Header.create("RS256", "JWT");
|
||||
|
||||
public void testGenerateJWTRequest() throws Exception {
|
||||
MockWebServer server = new MockWebServer();
|
||||
server.enqueue(new MockResponse().setBody("{\n" +
|
||||
" \"access_token\" : \"1/8xbJqaOZXSUZbHLl5EOtu1pxz3fmmetKx9W8CV4t79M\",\n" +
|
||||
" \"token_type\" : \"Bearer\",\n" +
|
||||
" \"expires_in\" : 3600\n" +
|
||||
"}"));
|
||||
server.play();
|
||||
|
||||
OAuthApi api = api(server.getUrl("/"));
|
||||
|
||||
assertEquals(api.authenticate(TokenRequest.create(HEADER, CLAIM_SET)), TOKEN);
|
||||
|
||||
RecordedRequest request = server.takeRequest();
|
||||
assertEquals(request.getMethod(), "POST");
|
||||
assertEquals(request.getHeader("Accept"), APPLICATION_JSON);
|
||||
assertEquals(request.getHeader("Content-Type"), "application/x-www-form-urlencoded");
|
||||
|
||||
assertEquals(new String(request.getBody(), UTF_8), //
|
||||
"grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&" +
|
||||
// Base64 Encoded Header
|
||||
"assertion=" +
|
||||
Joiner.on('.').join(encoding.encode(header.getBytes(UTF_8)), encoding.encode(claims.getBytes(UTF_8)),
|
||||
// Base64 encoded {header}.{claims} signature (using SHA256)
|
||||
"W2Lesr_98AzVYiMbzxFqmwcOjpIWlwqkC6pNn1fXND9oSDNNnFhy-AAR6DKH-x9ZmxbY80" +
|
||||
"R5fH-OCeWumXlVgceKN8Z2SmgQsu8ElTpypQA54j_5j8vUImJ5hsOUYPeyF1U2BUzZ3L5g" +
|
||||
"03PXBA0YWwRU9E1ChH28dQBYuGiUmYw"));
|
||||
|
||||
}
|
||||
|
||||
private final BaseEncoding encoding = base64Url().omitPadding();
|
||||
|
||||
private OAuthApi api(URL url) throws IOException {
|
||||
Properties overrides = new Properties();
|
||||
overrides.put(AUDIENCE, "https://accounts.google.com/o/oauth2/token");
|
||||
overrides.put(PROPERTY_MAX_RETRIES, "1");
|
||||
|
||||
return ContextBuilder.newBuilder(new OAuthApiMetadata())
|
||||
.credentials("foo", toStringAndClose(OAuthTestUtils.class.getResourceAsStream("/testpk.pem")))
|
||||
.endpoint(url.toString())
|
||||
.overrides(overrides)
|
||||
.modules(ImmutableSet.<Module>of(new ExecutorServiceModule(sameThreadExecutor())))
|
||||
.buildApi(OAuthApi.class);
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jclouds.oauth.v2.internal;
|
||||
|
||||
import org.jclouds.oauth.v2.OAuthApi;
|
||||
|
||||
public class BaseOAuthApiExpectTest extends BaseOAuthExpectTest<OAuthApi> {
|
||||
|
||||
}
|
|
@ -37,6 +37,7 @@ import org.jclouds.oauth.v2.domain.TokenRequest;
|
|||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Key;
|
||||
|
@ -75,18 +76,14 @@ public abstract class BaseOAuthAuthenticatedApiLiveTest<A extends Closeable> ext
|
|||
// obtain the scopes from the subclass
|
||||
String scopes = getScopes();
|
||||
|
||||
Header header = Header.builder().signerAlgorithm(signatureAlgorithm).type("JWT").build();
|
||||
Header header = Header.create(signatureAlgorithm, "JWT");
|
||||
|
||||
long now = SECONDS.convert(System.currentTimeMillis(), MILLISECONDS);
|
||||
|
||||
ClaimSet claimSet = ClaimSet.builder()
|
||||
.addClaim("aud", audience)
|
||||
.addClaim("scope", scopes)
|
||||
.addClaim("iss", identity)
|
||||
.emissionTime(now)
|
||||
.expirationTime(now + 3600).build();
|
||||
ClaimSet claimSet = ClaimSet.create(now, now + 3600,
|
||||
ImmutableMap.of("aud", audience, "scope", scopes, "iss", identity));
|
||||
|
||||
TokenRequest tokenRequest = TokenRequest.builder().header(header).claimSet(claimSet).build();
|
||||
TokenRequest tokenRequest = TokenRequest.create(header, claimSet);
|
||||
|
||||
Token token = oauthApi.authenticate(tokenRequest);
|
||||
|
||||
|
|
|
@ -16,11 +16,15 @@
|
|||
*/
|
||||
package org.jclouds.oauth.v2.internal;
|
||||
|
||||
import org.jclouds.rest.internal.BaseRestApiExpectTest;
|
||||
import org.jclouds.json.BaseItemParserTest;
|
||||
import org.jclouds.json.config.GsonModule;
|
||||
import org.jclouds.oauth.v2.config.OAuthParserModule;
|
||||
|
||||
public class BaseOAuthExpectTest<T> extends BaseRestApiExpectTest<T> {
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
public BaseOAuthExpectTest() {
|
||||
provider = "oauth";
|
||||
public abstract class BaseOAuthParseTest<T> extends BaseItemParserTest<T> {
|
||||
@Override protected Injector injector() {
|
||||
return Guice.createInjector(new GsonModule(), new OAuthParserModule());
|
||||
}
|
||||
}
|
|
@ -34,6 +34,7 @@ import org.jclouds.util.Strings2;
|
|||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
@Test(groups = "unit")
|
||||
|
@ -47,10 +48,10 @@ public class JWTTokenRequestFormatTest {
|
|||
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();
|
||||
Header header = Header.create("a", "b");
|
||||
ClaimSet claimSet = ClaimSet.create(0, 0,
|
||||
ImmutableMap.of("ist", STRING_THAT_GENERATES_URL_UNSAFE_BASE64_ENCODING));
|
||||
TokenRequest tokenRequest = TokenRequest.create(header, claimSet);
|
||||
HttpRequest request = tokenRequestFormat.formatRequest(HttpRequest.builder().method("GET").endpoint
|
||||
("http://localhost").build(), tokenRequest);
|
||||
|
||||
|
|
|
@ -16,15 +16,16 @@
|
|||
*/
|
||||
package org.jclouds.oauth.v2.parse;
|
||||
|
||||
import org.jclouds.json.BaseItemParserTest;
|
||||
import org.jclouds.oauth.v2.domain.Token;
|
||||
import org.testng.annotations.Test;
|
||||
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
@Test(groups = "unit")
|
||||
public class ParseTokenTest extends BaseItemParserTest<Token> {
|
||||
import org.jclouds.oauth.v2.domain.Token;
|
||||
import org.jclouds.oauth.v2.internal.BaseOAuthParseTest;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test(groups = "unit", testName = "ParseTokenTest")
|
||||
public class ParseTokenTest extends BaseOAuthParseTest<Token> {
|
||||
|
||||
@Override
|
||||
public String resource() {
|
||||
|
@ -32,9 +33,8 @@ public class ParseTokenTest extends BaseItemParserTest<Token> {
|
|||
}
|
||||
|
||||
@Override
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Consumes(APPLICATION_JSON)
|
||||
public Token expected() {
|
||||
return Token.builder().expiresIn(3600).tokenType("Bearer").accessToken
|
||||
("1/8xbJqaOZXSUZbHLl5EOtu1pxz3fmmetKx9W8CV4t79M").build();
|
||||
return Token.create("1/8xbJqaOZXSUZbHLl5EOtu1pxz3fmmetKx9W8CV4t79M", "Bearer", 3600);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue