blob: 7b869dcfc0343d3296f44f2988b9ef07efec8f4a [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jclouds.oauth.v2.functions;
import static com.google.common.base.Preconditions.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;
import java.util.Map;
import javax.inject.Singleton;
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 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;
/**
* 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
*/
@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;
// injectable so expect tests can override with a predictable value
@Inject(optional = true)
protected Supplier<Long> timeSourceMillisSinceEpoch = new Supplier<Long>() {
@Override
public Long get() {
return System.currentTimeMillis();
}
};
@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 = timeSourceMillisSinceEpoch.get() / 1000;
// 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) {
Invokable<?, ?> invokable = request.getInvocation().getInvokable();
OAuthScopes classScopes = invokable.getOwnerType().getRawType().getAnnotation(OAuthScopes.class);
OAuthScopes methodScopes = invokable.getAnnotation(OAuthScopes.class);
// if no annotations are present the rely on globally set scopes
if (classScopes == null && methodScopes == null) {
checkState(globalScopes != null, String.format("REST class or method should be annotated " +
"with OAuthScopes specifying required permissions. Alternatively a global property " +
"\"oauth.scopes\" may be set to define scopes globally. REST Class: %s, Method: %s",
invokable.getOwnerType(),
invokable.getName()));
return globalScopes;
}
OAuthScopes scopes = methodScopes != null ? methodScopes : classScopes;
return Joiner.on(",").join(scopes.value());
}
}