JCLOUDS-1148: Fix token caches in OAuth flows

This commit is contained in:
Daniel Haeser Rech 2016-08-03 22:54:22 -03:00 committed by Ignasi Barrera
parent 9662edf903
commit 913fdeb168
4 changed files with 58 additions and 37 deletions

View File

@ -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() {

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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() {
}
}
}