Repository: jclouds Updated Branches: refs/heads/master 9662edf90 -> 0fd013da0
JCLOUDS-1148: Fix token caches in OAuth flows Project: http://git-wip-us.apache.org/repos/asf/jclouds/repo Commit: http://git-wip-us.apache.org/repos/asf/jclouds/commit/913fdeb1 Tree: http://git-wip-us.apache.org/repos/asf/jclouds/tree/913fdeb1 Diff: http://git-wip-us.apache.org/repos/asf/jclouds/diff/913fdeb1 Branch: refs/heads/master Commit: 913fdeb168db948d59c58531efa0a413332b3f71 Parents: 9662edf Author: Daniel Haeser Rech <[email protected]> Authored: Wed Aug 3 22:54:22 2016 -0300 Committer: Ignasi Barrera <[email protected]> Committed: Thu Sep 1 09:55:23 2016 +0200 ---------------------------------------------------------------------- .../jclouds/oauth/v2/domain/ClientSecret.java | 9 ++-- .../ClientCredentialsJWTBearerTokenFlow.java | 32 ++++++++------ .../v2/filters/ClientCredentialsSecretFlow.java | 10 +---- .../oauth/v2/filters/JWTBearerTokenFlow.java | 44 +++++++++++++++----- 4 files changed, 58 insertions(+), 37 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/jclouds/blob/913fdeb1/apis/oauth/src/main/java/org/jclouds/oauth/v2/domain/ClientSecret.java ---------------------------------------------------------------------- diff --git a/apis/oauth/src/main/java/org/jclouds/oauth/v2/domain/ClientSecret.java b/apis/oauth/src/main/java/org/jclouds/oauth/v2/domain/ClientSecret.java index c006ca1..2ec45a5 100644 --- a/apis/oauth/src/main/java/org/jclouds/oauth/v2/domain/ClientSecret.java +++ b/apis/oauth/src/main/java/org/jclouds/oauth/v2/domain/ClientSecret.java @@ -38,12 +38,9 @@ public abstract class ClientSecret { /** The scope(s) to authorize against. **/ @Nullable public abstract String scope(); - /** When does the token expire. **/ - public abstract long expire(); - - @SerializedNames({ "client_id", "client_secret", "resource", "scope", "expire" }) - public static ClientSecret create(String clientId, String clientSecret, String resource, String scope, long expire) { - return new AutoValue_ClientSecret(clientId, clientSecret, resource, scope, expire); + @SerializedNames({ "client_id", "client_secret", "resource", "scope" }) + public static ClientSecret create(String clientId, String clientSecret, String resource, String scope) { + return new AutoValue_ClientSecret(clientId, clientSecret, resource, scope); } ClientSecret() { http://git-wip-us.apache.org/repos/asf/jclouds/blob/913fdeb1/apis/oauth/src/main/java/org/jclouds/oauth/v2/filters/ClientCredentialsJWTBearerTokenFlow.java ---------------------------------------------------------------------- diff --git a/apis/oauth/src/main/java/org/jclouds/oauth/v2/filters/ClientCredentialsJWTBearerTokenFlow.java b/apis/oauth/src/main/java/org/jclouds/oauth/v2/filters/ClientCredentialsJWTBearerTokenFlow.java index dd11940..dccb7f5 100644 --- a/apis/oauth/src/main/java/org/jclouds/oauth/v2/filters/ClientCredentialsJWTBearerTokenFlow.java +++ b/apis/oauth/src/main/java/org/jclouds/oauth/v2/filters/ClientCredentialsJWTBearerTokenFlow.java @@ -58,7 +58,6 @@ public class ClientCredentialsJWTBearerTokenFlow implements OAuthFilter { private final String audience; private final Supplier<Credentials> credentialsSupplier; private final OAuthScopes scopes; - private final long tokenDuration; private final LoadingCache<ClientCredentialsAuthArgs, Token> tokenCache; @Inject @@ -71,7 +70,6 @@ public class ClientCredentialsJWTBearerTokenFlow implements OAuthFilter { this.scopes = scopes; this.audience = audience; this.resource = resource; - this.tokenDuration = tokenDuration; // 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 long cacheExpirationSeconds = tokenDuration > 30 ? tokenDuration - 30 : tokenDuration; @@ -80,26 +78,40 @@ public class ClientCredentialsJWTBearerTokenFlow implements OAuthFilter { static final class AuthorizeToken extends CacheLoader<ClientCredentialsAuthArgs, Token> { private final AuthorizationApi api; + private final long tokenDuration; - @Inject AuthorizeToken(AuthorizationApi api) { + @Inject AuthorizeToken(AuthorizationApi api, @Named(PROPERTY_SESSION_INTERVAL) long tokenDuration) { this.api = api; + this.tokenDuration = tokenDuration; + } + + long currentTimeSeconds() { + return System.currentTimeMillis() / 1000; } @Override public Token load(ClientCredentialsAuthArgs key) throws Exception { - return api.authorize(key.clientId(), key.claims(), key.resource(), key.scope()); + final long now = currentTimeSeconds(); + final ClientCredentialsClaims claims = ClientCredentialsClaims.create( + key.claims().iss(), + key.claims().sub(), + key.claims().aud(), + now + tokenDuration, + now, + UUID.randomUUID().toString() + ); + return api.authorize(key.clientId(), claims, key.resource(), key.scope()); } } @Override public HttpRequest filter(HttpRequest request) throws HttpException { - long now = currentTimeSeconds(); List<String> configuredScopes = scopes.forRequest(request); ClientCredentialsClaims claims = ClientCredentialsClaims.create( // credentialsSupplier.get().identity, // iss credentialsSupplier.get().identity, // sub audience, // aud - now + tokenDuration, // exp - now, // nbf - UUID.randomUUID().toString() // jti + -1, // placeholder exp for the cache + -1, // placeholder nbf for the cache + null // placeholder jti for the cache ); ClientCredentialsAuthArgs authArgs = ClientCredentialsAuthArgs.create( credentialsSupplier.get().identity, @@ -112,9 +124,5 @@ public class ClientCredentialsJWTBearerTokenFlow implements OAuthFilter { String authorization = String.format("%s %s", token.tokenType(), token.accessToken()); return request.toBuilder().addHeader("Authorization", authorization).build(); } - - long currentTimeSeconds() { - return System.currentTimeMillis() / 1000; - } } http://git-wip-us.apache.org/repos/asf/jclouds/blob/913fdeb1/apis/oauth/src/main/java/org/jclouds/oauth/v2/filters/ClientCredentialsSecretFlow.java ---------------------------------------------------------------------- diff --git a/apis/oauth/src/main/java/org/jclouds/oauth/v2/filters/ClientCredentialsSecretFlow.java b/apis/oauth/src/main/java/org/jclouds/oauth/v2/filters/ClientCredentialsSecretFlow.java index f6e3534..d721865 100644 --- a/apis/oauth/src/main/java/org/jclouds/oauth/v2/filters/ClientCredentialsSecretFlow.java +++ b/apis/oauth/src/main/java/org/jclouds/oauth/v2/filters/ClientCredentialsSecretFlow.java @@ -55,7 +55,6 @@ public class ClientCredentialsSecretFlow implements OAuthFilter { private static final Joiner ON_SPACE = Joiner.on(" "); private final Supplier<Credentials> credentialsSupplier; - private final long tokenDuration; private final LoadingCache<ClientSecret, Token> tokenCache; private final String resource; private final OAuthScopes scopes; @@ -67,7 +66,6 @@ public class ClientCredentialsSecretFlow implements OAuthFilter { this.credentialsSupplier = credentialsSupplier; this.scopes = scopes; this.resource = resource; - this.tokenDuration = tokenDuration; // 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 long cacheExpirationSeconds = tokenDuration > 30 ? tokenDuration - 30 : tokenDuration; @@ -87,21 +85,15 @@ public class ClientCredentialsSecretFlow implements OAuthFilter { } @Override public HttpRequest filter(HttpRequest request) throws HttpException { - long now = currentTimeSeconds(); List<String> configuredScopes = scopes.forRequest(request); ClientSecret client = ClientSecret.create( credentialsSupplier.get().identity, credentialsSupplier.get().credential, resource == null ? "" : resource, - configuredScopes.isEmpty() ? null : ON_SPACE.join(configuredScopes), - now + tokenDuration + configuredScopes.isEmpty() ? null : ON_SPACE.join(configuredScopes) ); Token token = tokenCache.getUnchecked(client); String authorization = String.format("%s %s", token.tokenType(), token.accessToken()); return request.toBuilder().addHeader("Authorization", authorization).build(); } - - long currentTimeSeconds() { - return System.currentTimeMillis() / 1000; - } } http://git-wip-us.apache.org/repos/asf/jclouds/blob/913fdeb1/apis/oauth/src/main/java/org/jclouds/oauth/v2/filters/JWTBearerTokenFlow.java ---------------------------------------------------------------------- diff --git a/apis/oauth/src/main/java/org/jclouds/oauth/v2/filters/JWTBearerTokenFlow.java b/apis/oauth/src/main/java/org/jclouds/oauth/v2/filters/JWTBearerTokenFlow.java index 7f2729f..43ec549 100644 --- a/apis/oauth/src/main/java/org/jclouds/oauth/v2/filters/JWTBearerTokenFlow.java +++ b/apis/oauth/src/main/java/org/jclouds/oauth/v2/filters/JWTBearerTokenFlow.java @@ -32,6 +32,7 @@ import org.jclouds.oauth.v2.config.OAuthScopes; import org.jclouds.oauth.v2.domain.Claims; import org.jclouds.oauth.v2.domain.Token; +import com.google.auto.value.AutoValue; import com.google.common.base.Joiner; import com.google.common.base.Supplier; import com.google.common.cache.CacheBuilder; @@ -53,30 +54,36 @@ public class JWTBearerTokenFlow implements OAuthFilter { private final String audience; private final Supplier<Credentials> credentialsSupplier; private final OAuthScopes scopes; - private final long tokenDuration; - private final LoadingCache<Claims, Token> tokenCache; + private final LoadingCache<TokenCacheKey, Token> tokenCache; @Inject JWTBearerTokenFlow(AuthorizeToken loader, @Named(PROPERTY_SESSION_INTERVAL) long tokenDuration, @Provider Supplier<Credentials> credentialsSupplier, OAuthScopes scopes, @Named(AUDIENCE) String audience) { this.credentialsSupplier = credentialsSupplier; this.scopes = scopes; this.audience = audience; - this.tokenDuration = tokenDuration; // 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 long cacheExpirationSeconds = tokenDuration > 30 ? tokenDuration - 30 : tokenDuration; this.tokenCache = CacheBuilder.newBuilder().expireAfterWrite(cacheExpirationSeconds, SECONDS).build(loader); } - static final class AuthorizeToken extends CacheLoader<Claims, Token> { + static final class AuthorizeToken extends CacheLoader<TokenCacheKey, Token> { private final AuthorizationApi api; + private final long tokenDuration; - @Inject AuthorizeToken(AuthorizationApi api) { + @Inject AuthorizeToken(AuthorizationApi api, @Named(PROPERTY_SESSION_INTERVAL) long tokenDuration) { this.api = api; + this.tokenDuration = tokenDuration; } - @Override public Token load(Claims key) throws Exception { - return api.authorize(key); + @Override public Token load(TokenCacheKey tokenCacheKey) throws Exception { + final Claims claims = Claims.create( + tokenCacheKey.claims().iss(), + tokenCacheKey.claims().scope(), + tokenCacheKey.claims().aud(), + tokenCacheKey.startTime + tokenDuration, + tokenCacheKey.startTime); + return api.authorize(claims); } } @@ -86,10 +93,11 @@ public class JWTBearerTokenFlow implements OAuthFilter { credentialsSupplier.get().identity, // iss ON_COMMA.join(scopes.forRequest(request)), // scope audience, // aud - now + tokenDuration, // exp - now // iat + -1, // placeholder exp for the cache + -1 // placeholder iat for the cache ); - Token token = tokenCache.getUnchecked(claims); + final TokenCacheKey tokenCacheKey = TokenCacheKey.create(claims, now); + Token token = tokenCache.getUnchecked(tokenCacheKey); String authorization = String.format("%s %s", token.tokenType(), token.accessToken()); return request.toBuilder().addHeader("Authorization", authorization).build(); } @@ -97,4 +105,20 @@ public class JWTBearerTokenFlow implements OAuthFilter { long currentTimeSeconds() { return System.currentTimeMillis() / 1000; } + + @AutoValue + abstract static class TokenCacheKey { + public abstract Claims claims(); + + long startTime; + + public static TokenCacheKey create(Claims claims, long startTime) { + final AutoValue_JWTBearerTokenFlow_TokenCacheKey tokenCacheKey = new AutoValue_JWTBearerTokenFlow_TokenCacheKey(claims); + tokenCacheKey.startTime = startTime; + return tokenCacheKey; + } + + TokenCacheKey() { + } + } }
