/*************************************************************************
* Copyright 2009-2016 Eucalyptus Systems, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*
* Please contact Eucalyptus Systems, Inc., 6755 Hollister Ave., Goleta
* CA 93117, USA or visit http://www.eucalyptus.com/licenses/ if you need
* additional information or have any questions.
************************************************************************/
package com.eucalyptus.tokens;
import static com.eucalyptus.auth.AccessKeys.accessKeyIdentifier;
import static com.eucalyptus.auth.login.AccountUsernamePasswordCredentials.AccountUsername;
import com.eucalyptus.auth.principal.AccessKeyCredential;
import static com.eucalyptus.auth.policy.PolicySpec.IAM_RESOURCE_USER;
import static com.eucalyptus.auth.policy.PolicySpec.VENDOR_STS;
import static com.eucalyptus.util.CollectionUtils.propertyPredicate;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.cert.Certificate;
import java.util.Collections;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
import javax.security.auth.Subject;
import com.eucalyptus.auth.Accounts;
import com.eucalyptus.auth.Permissions;
import com.eucalyptus.auth.PolicyEvaluationContext;
import com.eucalyptus.auth.euare.common.oidc.OIDCIssuerIdentifier;
import com.eucalyptus.auth.euare.common.oidc.OIDCUtils;
import com.eucalyptus.auth.euare.identity.region.RegionConfigurations;
import com.eucalyptus.auth.euare.policy.OpenIDConnectAudKey;
import com.eucalyptus.auth.euare.policy.OpenIDConnectSubKey;
import com.eucalyptus.auth.policy.PolicySpec;
import com.eucalyptus.auth.policy.ern.Ern;
import com.eucalyptus.auth.policy.ern.EuareResourceName;
import com.eucalyptus.auth.principal.AccessKey;
import com.eucalyptus.auth.AuthException;
import com.eucalyptus.auth.principal.AccountFullName;
import com.eucalyptus.auth.principal.BaseRole;
import com.eucalyptus.auth.principal.HasRole;
import com.eucalyptus.auth.principal.OpenIdConnectProvider;
import com.eucalyptus.auth.principal.Principal.PrincipalType;
import com.eucalyptus.auth.principal.Principals;
import com.eucalyptus.auth.principal.TemporaryAccessKey;
import com.eucalyptus.auth.principal.User;
import com.eucalyptus.auth.principal.UserPrincipal;
import com.eucalyptus.auth.tokens.RoleSecurityTokenAttributes;
import com.eucalyptus.auth.tokens.SecurityToken;
import com.eucalyptus.auth.tokens.SecurityTokenManager;
import com.eucalyptus.auth.tokens.SecurityTokenValidationException;
import com.eucalyptus.component.annotation.ComponentNamed;
import com.eucalyptus.context.Context;
import com.eucalyptus.context.Contexts;
import com.eucalyptus.crypto.Digest;
import com.eucalyptus.records.Logs;
import com.eucalyptus.tokens.TokensException.Code;
import com.eucalyptus.tokens.oidc.JsonWebKey;
import com.eucalyptus.tokens.oidc.JsonWebKeySet;
import com.eucalyptus.tokens.oidc.JsonWebSignatureVerifier;
import com.eucalyptus.tokens.oidc.OidcDiscoveryCache;
import com.eucalyptus.tokens.oidc.OidcIdentityToken;
import com.eucalyptus.tokens.oidc.OidcParseException;
import com.eucalyptus.tokens.oidc.OidcProviderConfiguration;
import com.eucalyptus.tokens.policy.ExternalIdKey;
import com.eucalyptus.util.EucalyptusCloudException;
import com.eucalyptus.util.Pair;
import com.eucalyptus.util.RestrictedTypes;
import com.google.common.base.Ascii;
import com.google.common.base.Function;
import com.google.common.base.MoreObjects;
import com.google.common.base.Optional;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
import com.google.common.io.BaseEncoding;
import javaslang.collection.Stream;
import javaslang.control.Option;
import org.apache.log4j.Logger;
/**
* Service component for temporary access tokens
*/
@SuppressWarnings( { "UnusedDeclaration", "Guava", "StaticPseudoFunctionalStyleMethod" } )
@ComponentNamed
public class TokensService {
private static final Logger LOG = Logger.getLogger( TokensService.class );
private static final Pattern ROLE_ARN_PATTERN = Pattern.compile( "arn:aws:iam::([0-9\\p{javaLowerCase}-]{1,63}):role/.+" );
private static final int ROLE_ARN_PATTERN_ACCOUNT_GROUP = 1;
private static final OidcDiscoveryCache oidcDiscoveryCache = new OidcDiscoveryCache( );
public GetCallerIdentityResponseType getCallerIdentity(
final GetCallerIdentityType request
) throws EucalyptusCloudException {
final GetCallerIdentityResponseType reply = request.getReply( );
final Context ctx = Contexts.lookup( );
final UserPrincipal user = ctx.getUser( );
final String arn;
final String userId = user.getAuthenticatedId( );
final String account = ctx.getAccountNumber( );
try {
final Optional<RoleSecurityTokenAttributes> roleAttributes = RoleSecurityTokenAttributes.forUser( user );
if ( roleAttributes.isPresent( ) ) {
arn = assumedRoleArn( ((HasRole) user).getRole( ), roleAttributes.get( ).getSessionName( ) );
} else {
arn = Accounts.getUserArn( user );
}
} catch ( final AuthException e ) {
throw new EucalyptusCloudException( e.getMessage( ), e );
}
reply.getResponseMetadata().setRequestId( reply.getCorrelationId( ) );
reply.setResult( new GetCallerIdentityResultType( arn, userId, account ) );
return reply;
}
public GetSessionTokenResponseType getSessionToken( final GetSessionTokenType request ) throws EucalyptusCloudException {
final GetSessionTokenResponseType reply = request.getReply();
reply.getResponseMetadata().setRequestId( reply.getCorrelationId( ) );
final Context ctx = Contexts.lookup();
final Subject subject = ctx.getSubject();
final User requestUser = ctx.getUser( );
final Set<AccessKeyCredential> accessKeyCredentials = subject == null ?
Collections.emptySet( ) :
subject.getPublicCredentials( AccessKeyCredential.class );
if ( accessKeyCredentials.isEmpty( ) ) {
throw new TokensException( Code.MissingAuthenticationToken, "Missing credential." );
}
final String accessKeyId = Iterables.getOnlyElement( accessKeyCredentials ).getAccessKeyId( );
final AccessKey accessKey;
try {
accessKey = Iterables.find( requestUser.getKeys( ), propertyPredicate( accessKeyId, accessKeyIdentifier( ) ) );
} catch ( final AuthException | NoSuchElementException e ) {
throw new TokensException( Code.MissingAuthenticationToken, "Invalid credential: " + accessKeyId );
}
try {
final int durationSeconds =
MoreObjects.firstNonNull( request.getDurationSeconds(), (int) TimeUnit.HOURS.toSeconds( 12 ) );
final SecurityToken token = SecurityTokenManager.issueSecurityToken(
requestUser,
accessKey,
requestUser.isAccountAdmin() ? (int) TimeUnit.HOURS.toSeconds( 1 ) : 0,
durationSeconds );
reply.setResult( GetSessionTokenResultType.forCredentials(
token.getAccessKeyId(),
token.getSecretKey(),
token.getToken(),
token.getExpires()
) );
} catch ( final SecurityTokenValidationException e ) {
throw new TokensException( Code.ValidationError, e.getMessage( ) );
} catch ( final AuthException e ) {
throw new EucalyptusCloudException( e.getMessage(), e );
}
return reply;
}
public AssumeRoleResponseType assumeRole( final AssumeRoleType request ) throws EucalyptusCloudException {
final AssumeRoleResponseType reply = request.getReply( );
reply.getResponseMetadata().setRequestId( reply.getCorrelationId( ) );
// Verify that access is not via a role or password credentials.
//
// It is not currently safe to allow access via a role (see EUCA-8416).
// Other forms of temporary credential are not forbidden by the pipeline at
// the time of authentication.
final Context ctx = Contexts.lookup( );
final Subject subject = ctx.getSubject( );
final Set<AccessKeyCredential> accessKeyCredentials = subject == null ?
Collections.emptySet( ) :
subject.getPublicCredentials( AccessKeyCredential.class );
//noinspection OptionalGetWithoutIsPresent
if ( accessKeyCredentials.size( ) == 1 &&
Iterables.get( accessKeyCredentials, 0 ).getType( ).isDefined( ) &&
Iterables.get( accessKeyCredentials, 0 ).getType( ).get( ) != TemporaryAccessKey.TemporaryKeyType.Access ) {
throw new TokensException( Code.MissingAuthenticationToken, "Temporary credential not permitted." );
}
rejectPasswordCredentials( );
// basic parameter validation
if ( request.getRoleSessionName( ) == null || !request.getRoleSessionName( ).matches( "[\\w+=,.@-]{2,64}" ) ) {
throw new TokensException( Code.ValidationError, "Invalid role session name" );
}
final BaseRole role = lookupRole( request.getRoleArn( ), "AssumeRole" );
try {
// check for ec2 principal (instance profile)
final PrincipalType principalType;
final String principalName;
final RoleSecurityTokenAttributes tokenAttributes;
if ( request.getRoleSessionName( ).matches( "i-[0-9a-fA-F]{8,32}" ) &&
Principals.isSameUser( Contexts.lookup( ).getUser( ), Principals.systemUser( ) ) ) {
principalType = PrincipalType.Service;
principalName = "ec2.amazon.com";
tokenAttributes = RoleSecurityTokenAttributes.instance(
request.getRoleSessionName( ),
"arn:aws:ec2:" + RegionConfigurations.getRegionName( ).or( "" ) + ":" +
role.getAccountNumber( ) + ":instance/" + request.getRoleSessionName( )
);
} else {
principalType = null;
principalName = null;
tokenAttributes = RoleSecurityTokenAttributes.basic( request.getRoleSessionName( ) );
}
try {
PolicyEvaluationContext.builder( )
.attrIfNotNull( ExternalIdKey.CONTEXT_KEY, request.getExternalId( ) )
.attrIfNotNull( RestrictedTypes.principalTypeContextKey, principalType )
.attrIfNotNull( RestrictedTypes.principalNameContextKey, principalName )
.build( ).doWithContext( () ->
RestrictedTypes.doPrivileged(
Accounts.getRoleFullName( role ),
new RoleResolver( role ) ) );
} catch ( final AuthException e ) {
throw new TokensException( Code.AccessDenied, "Not authorized to perform sts:AssumeRole" );
} catch ( final Exception e ) {
throw new TokensException( Code.AccessDenied, e.getMessage( ) );
}
final SecurityToken token = SecurityTokenManager.issueSecurityToken(
role,
tokenAttributes,
MoreObjects.firstNonNull( request.getDurationSeconds(), (int) TimeUnit.HOURS.toSeconds( 1 ) ) );
reply.getAssumeRoleResult().setCredentials( new CredentialsType(
token.getAccessKeyId(),
token.getSecretKey(),
token.getToken(),
token.getExpires()
) );
reply.getAssumeRoleResult().setAssumedRoleUser( new AssumedRoleUserType(
role.getRoleId() + ":" + request.getRoleSessionName(),
assumedRoleArn( role, request.getRoleSessionName() )
) );
} catch ( final SecurityTokenValidationException e ) {
throw new TokensException( Code.ValidationError, e.getMessage( ) );
} catch ( final AuthException e ) {
throw new EucalyptusCloudException( e.getMessage(), e );
}
return reply;
}
public AssumeRoleWithWebIdentityResponseType assumeRoleWithWebIdentity(
final AssumeRoleWithWebIdentityType request
) throws EucalyptusCloudException {
final AssumeRoleWithWebIdentityResponseType reply = request.getReply( );
reply.getResponseMetadata().setRequestId( reply.getCorrelationId( ) );
// verify credentials were not used in the request
final Context ctx = Contexts.lookup( );
final Subject subject = ctx.getSubject( );
final Set<Object> creds = subject == null ? Collections.emptySet( ) : subject.getPublicCredentials( );
if ( !creds.isEmpty( ) ) {
throw new TokensException( Code.AccessDenied, "Credentials not acceptable for action" );
}
// basic parameter validation
if ( request.getRoleSessionName( ) == null || !request.getRoleSessionName( ).matches( "[\\w+=,.@-]{2,64}" ) ) {
throw new TokensException( Code.ValidationError, "Invalid role session name" );
}
if ( request.getWebIdentityToken( ) == null ||
request.getWebIdentityToken( ).length( ) < 4 ||
request.getWebIdentityToken( ).length( ) > 2048 ) {
throw new TokensException( Code.ValidationError, "Token invalid" );
}
final BaseRole role = lookupRole( request.getRoleArn( ), "AssumeRoleWithWebIdentity" );
try {
final String identityToken = request.getWebIdentityToken( );
// parse JWT
final String[] jwtParts = identityToken.split( "\\." );
if ( jwtParts.length != 3 ) {
throw new TokensException( Code.InvalidIdentityToken,
"The ID Token provided is not a valid JWT. (You may see this error if you sent an Access Token)" );
}
final String issuerUrl;
final List<String> audList;
final String sub;
try {
final OidcIdentityToken idToken = OidcIdentityToken.parse(
new String( BaseEncoding.base64Url( ).decode( jwtParts[ 1 ] ), StandardCharsets.UTF_8 ) );
issuerUrl = idToken.getIss( );
audList = idToken.getAud( );
sub = idToken.getSub( );
if ( Strings.isNullOrEmpty( sub ) ) {
throw new TokensException( Code.InvalidIdentityToken, "Invalid token subject" );
}
final long now = System.currentTimeMillis( );
final long skew = TimeUnit.SECONDS.toMillis( TokensServiceConfiguration.getWebIdentityTokenTimeSkew( ) );
final long issued = idToken.getIat( ) * 1000L;
if ( ( issued - skew ) > now ) {
throw new TokensException( Code.InvalidIdentityToken, "Token not yet valid" );
}
final long notBefore = idToken.getNbf( ).getOrElse( 0L ) * 1000L;
if ( ( notBefore - skew ) > now ) {
throw new TokensException( Code.InvalidIdentityToken, "Token not yet valid" );
}
final long expiration = idToken.getExp( ) * 1000L;
if ( ( expiration + skew ) < now ) {
throw new TokensException( Code.ExpiredTokenException, "Token has expired" );
}
} catch ( IllegalArgumentException | OidcParseException e ) {
throw new TokensException( Code.InvalidIdentityToken, "Token invalid: " + e.getMessage( ) );
}
// fetch oidc provider
final String accountId = role.getAccountNumber( );
final OIDCIssuerIdentifier issuerIdentifier = OIDCUtils.parseIssuerIdentifier( issuerUrl );
final OpenIdConnectProvider provider =
lookupOpenIdConnectProvider( accountId, issuerIdentifier.getHost( ) + issuerIdentifier.getPath( ) );
final String providerArn = provider.getArn( );
// verify aud from token
final String aud = Stream.ofAll( audList ).find( provider.getClientIds( )::contains )
.getOrElseThrow( ( ) -> new TokensException( Code.InvalidIdentityToken, "Incorrect token audience" ) );
// oidc discovery
final String configJson = resolveUrl( OidcProviderConfiguration.buildDiscoveryUrl( provider ) ).getLeft( );
final OidcProviderConfiguration providerConfiguration;
try {
providerConfiguration = OidcProviderConfiguration.parse( configJson );
} catch ( final OidcParseException e ) {
LOG.warn( "Error performing discovery for oidc provider: " + e.getMessage( ) );
throw new TokensException( Code.IDPCommunicationError, "Error discovering OIDC provider configuration" );
}
if ( !providerConfiguration.getIssuer( ).equals( OIDCUtils.buildIssuerIdentifier( provider ) ) ) {
LOG.warn( "OIDC provider discovery error, issuer mismatch: " + providerConfiguration.getIssuer( ) );
throw new TokensException( Code.IDPCommunicationError, "Error discovering OIDC provider configuration" );
}
final Pair<String, Certificate[]> readResult = resolveUrl( providerConfiguration.getJwksUri( ) );
final byte[] thumbprint = Digest.SHA1.digestBinary( readResult.getRight( )[ 0 ].getEncoded( ) );
if ( !Stream.ofAll( provider.getThumbprints( ) ).find( providerThumb ->
MessageDigest.isEqual(
thumbprint,
BaseEncoding.base16( ).decode( Ascii.toUpperCase( providerThumb ) ) )
).isDefined( ) ) {
throw new TokensException( Code.ValidationError, "SSL Certificate thumbprint does not match" );
}
// verify JWT signature
final String keysJson = readResult.getLeft( );
if ( !isSignatureVerified(
jwtParts,
keysJson,
alg -> TokensServiceConfiguration.getWebIdSignatureAlgorithmPattern( ).matcher( alg ).matches( ) )
) {
throw new TokensException( Code.InvalidIdentityToken, "Token signature invalid" );
}
// verify assume role policy
try {
PolicyEvaluationContext.builder( )
.attr( RestrictedTypes.principalTypeContextKey, PrincipalType.Federated )
.attr( RestrictedTypes.principalNameContextKey, providerArn )
.attr( OpenIDConnectAudKey.CONTEXT_KEY, Pair.pair( provider.getUrl( ), aud ) )
.attr( OpenIDConnectSubKey.CONTEXT_KEY, Pair.pair( provider.getUrl( ), sub ) )
.build( ).doWithContext( () ->
RestrictedTypes.doPrivileged(
Accounts.getRoleFullName( role ),
new RoleResolver( role ) ) );
} catch ( final AuthException e ) {
throw new TokensException( Code.AccessDenied, "Not authorized to perform sts:AssumeRoleWithWebIdentity" );
} catch ( final Exception e ) {
throw new TokensException( Code.AccessDenied, e.getMessage( ) );
}
// issue credentials
final SecurityToken token = SecurityTokenManager.issueSecurityToken(
role,
RoleSecurityTokenAttributes.webIdentity(
request.getRoleSessionName( ),
provider.getUrl( ),
aud,
sub
),
MoreObjects.firstNonNull( request.getDurationSeconds( ), (int) TimeUnit.HOURS.toSeconds( 1 ) ) );
// populate result
final AssumeRoleWithWebIdentityResultType result = reply.getAssumeRoleWithWebIdentityResult( );
result.setProvider( providerArn );
result.setAudience( aud );
result.setSubjectFromWebIdentityToken( sub );
result.setCredentials( new CredentialsType(
token.getAccessKeyId( ),
token.getSecretKey( ),
token.getToken( ),
token.getExpires( )
) );
result.setAssumedRoleUser( new AssumedRoleUserType(
role.getRoleId( ) + ":" + request.getRoleSessionName( ),
assumedRoleArn( role, request.getRoleSessionName( ) )
) );
} catch ( final SecurityTokenValidationException e ) {
throw new TokensException( Code.ValidationError, e.getMessage( ) );
} catch ( final IOException e ) {
LOG.warn( "Error performing discovery for oidc provider: " + e.getMessage( ) );
Logs.exhaust( ).info( "Error performing discovery for oidc provider: " + e.getMessage( ), e );
throw new TokensException( Code.IDPCommunicationError, e.getMessage( ) );
} catch ( GeneralSecurityException e ) {
LOG.error( "Error assuming role with web identity", e );
throw new EucalyptusCloudException( e.getMessage(), e );
} catch ( final AuthException e ) {
throw new EucalyptusCloudException( e.getMessage(), e );
}
return reply;
}
private static Pair<String, Certificate[]> resolveUrl( final String url ) throws IOException {
return oidcDiscoveryCache.get(
TokensServiceConfiguration.webIdentityOidcDiscoveryCache,
TokensServiceConfiguration.webIdentityOidcDiscoveryRefresh * 1000L,
System.currentTimeMillis( ),
url );
}
static Boolean isSignatureVerified(
final String[] jwtParts,
final String jwkText,
final Predicate<String> signatureAlgorithmPredicate
) throws GeneralSecurityException {
try {
final JsonWebKeySet webKeySet = JsonWebKeySet.parse( jwkText );
return JsonWebSignatureVerifier.isValid(
jwtParts[ 0 ],
jwtParts[ 1 ],
jwtParts[ 2 ],
new JsonWebSignatureVerifier.KeyResolver( ) {
@Override
public <K extends JsonWebKey> Option<K> resolve( final Option<String> kid, final Class<K> keyType ) {
return webKeySet.findKey( kid, keyType, "sig", "verify" );
}
},
signatureAlgorithmPredicate
);
} catch ( GeneralSecurityException e ) {
throw e;
} catch ( Throwable e ) {
throw new GeneralSecurityException( e.getMessage( ), e );
}
}
public GetAccessTokenResponseType getAccessToken( final GetAccessTokenType request ) throws EucalyptusCloudException {
final GetAccessTokenResponseType reply = request.getReply();
reply.getResponseMetadata().setRequestId( reply.getCorrelationId( ) );
final Context ctx = Contexts.lookup();
final Subject subject = ctx.getSubject();
final User requestUser = ctx.getUser( );
final AccountUsername accountUsername = subject == null ?
null :
Iterables.getFirst( subject.getPublicCredentials( AccountUsername.class ), null );
try {
if ( accountUsername == null ||
!accountUsername.getAccount( ).equals( Accounts.lookupAccountAliasById( ctx.getAccountNumber( ) ) ) ||
!accountUsername.getUsername( ).equals( requestUser.getName( ) ) ) {
throw new EucalyptusCloudException( "Invalid authentication" );
}
final SecurityToken token = SecurityTokenManager.issueSecurityToken(
requestUser,
requestUser.isAccountAdmin() ? (int) TimeUnit.DAYS.toSeconds( 1 ) : 0,
MoreObjects.firstNonNull( request.getDurationSeconds(), (int) TimeUnit.HOURS.toSeconds( 12 ) ) );
reply.setResult( GetAccessTokenResultType.forCredentials(
token.getAccessKeyId(),
token.getSecretKey(),
token.getToken(),
token.getExpires()
) );
} catch ( final SecurityTokenValidationException e ) {
throw new TokensException( Code.ValidationError, e.getMessage( ) );
} catch ( final AuthException e ) {
throw new EucalyptusCloudException( e.getMessage(), e );
}
return reply;
}
public GetImpersonationTokenResponseType getImpersonationToken( final GetImpersonationTokenType request ) throws EucalyptusCloudException {
final GetImpersonationTokenResponseType reply = request.getReply();
reply.getResponseMetadata().setRequestId( reply.getCorrelationId( ) );
final Context ctx = Contexts.lookup();
// Verify that access is not via password credentials.
rejectPasswordCredentials( );
final User impersonated;
final AccountFullName impersonatedAccount;
try {
if ( !Strings.isNullOrEmpty( request.getImpersonatedUserId( ) ) ) {
impersonated = Accounts.lookupPrincipalByUserId( request.getImpersonatedUserId( ) );
} else {
String accountNumber = Accounts.lookupAccountIdByAlias( request.getAccountAlias( ) );
impersonated = Accounts.lookupPrincipalByAccountNumberAndUsername( accountNumber, request.getUserName( ) );
}
impersonatedAccount = AccountFullName.getInstance( impersonated.getAccountNumber( ) );
} catch ( AuthException e ) {
throw new TokensException( Code.ValidationError, e.getMessage( ) );
}
try {
if ( !ctx.isAdministrator() || !Permissions.isAuthorized(
VENDOR_STS,
IAM_RESOURCE_USER,
Accounts.getUserFullName( impersonated ),
impersonatedAccount,
PolicySpec.STS_GETIMPERSONATIONTOKEN,
ctx.getAuthContext( ) ) ) {
throw new EucalyptusCloudException( "Permission denied" );
}
final SecurityToken token = SecurityTokenManager.issueSecurityToken(
impersonated,
impersonated.isAccountAdmin() ? (int) TimeUnit.DAYS.toSeconds( 1 ) : 0,
MoreObjects.firstNonNull( request.getDurationSeconds(), (int) TimeUnit.HOURS.toSeconds( 12 ) ) );
reply.setResult( GetImpersonationTokenResultType.forCredentials(
token.getAccessKeyId(),
token.getSecretKey(),
token.getToken(),
token.getExpires()
) );
} catch ( final SecurityTokenValidationException e ) {
throw new TokensException( Code.ValidationError, e.getMessage( ) );
} catch ( final AuthException e ) {
throw new EucalyptusCloudException( e.getMessage(), e );
}
return reply;
}
private static void rejectPasswordCredentials( ) throws TokensException {
final Context context = Contexts.lookup( );
final Subject subject = context.getSubject( );
final AccountUsername accountUsername = subject == null ?
null :
Iterables.getFirst( subject.getPublicCredentials( AccountUsername.class ), null );
if ( accountUsername != null ) {
throw new TokensException( Code.MissingAuthenticationToken, "Missing credential." );
}
}
private static String assumedRoleArn( final BaseRole role,
final String roleSessionName ) throws AuthException {
return Accounts.getAssumedRoleArn( role, roleSessionName );
}
private static BaseRole lookupRole( final String roleArnStringWithAlias,
final String action ) throws TokensException {
try {
final Matcher matcher = ROLE_ARN_PATTERN.matcher( roleArnStringWithAlias );
if ( !matcher.matches( ) ) {
throw new IllegalArgumentException();
}
final String accountNumberOrAlias = matcher.group( ROLE_ARN_PATTERN_ACCOUNT_GROUP );
final String roleArnString = Accounts.isAccountNumber( accountNumberOrAlias ) ||
!TokensServiceConfiguration.getRoleArnAliasPattern( ).matcher( accountNumberOrAlias ).matches( ) ?
roleArnStringWithAlias :
roleArnStringWithAlias.substring( 0, matcher.start( ROLE_ARN_PATTERN_ACCOUNT_GROUP ) ) +
Accounts.lookupAccountIdentifiersByAlias( accountNumberOrAlias ).getAccountNumber( ) +
roleArnStringWithAlias.substring( matcher.end( ROLE_ARN_PATTERN_ACCOUNT_GROUP ) );
final Ern roleArn = Ern.parse( roleArnString );
if ( !( roleArn instanceof EuareResourceName ) ||
!PolicySpec.IAM_RESOURCE_ROLE.equals( ( (EuareResourceName) roleArn ).getType( ) ) ||
roleArn.getAccount( ) == null ) {
throw new IllegalArgumentException( );
}
final String roleAccountId = roleArn.getAccount( );
final String roleName = ( (EuareResourceName) roleArn ).getName( );
return Accounts.lookupRoleByName( roleAccountId, roleName );
} catch ( Exception e ) {
throw new TokensException( Code.AccessDenied, "Not authorized to perform sts:" + action );
}
}
private static class RoleResolver implements Function<String,BaseRole> {
private final BaseRole role;
private RoleResolver( final BaseRole role ) {
this.role = role;
}
@Override
public BaseRole apply( @Nullable final String roleFullName ) {
return role;
}
}
private static OpenIdConnectProvider lookupOpenIdConnectProvider( String accountId, final String url ) throws TokensException {
try {
return Accounts.lookupOidcProviderByUrl( accountId, url );
} catch ( Exception e ) {
throw new TokensException( Code.InvalidParameterValue, "Invalid openid connect provider: " + url + ", account: " + accountId );
}
}
}