/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file 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.
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using Amazon.Runtime;
using Amazon.Runtime.Internal;
using Amazon.Runtime.SharedInterfaces;
using Amazon.SecurityToken;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Amazon.SecurityToken.Model;
#if ASYNC_AWAIT
using System.Threading.Tasks;
#endif
namespace AWSSDK.UnitTests
{
///
/// This class contains Fallback Credentials Factory tests that must be completed wihtin the service assembly
///
[TestClass]
public class FallbackCredentialsFactorySTSTests
{
public void TestRetriesOnException(T exception) where T : AmazonServiceException
{
var webIdentityToken = "Dummy.OIDC.Token";
var roleArn = "someRoleArn";
var roleSessionName = "someRoleSessionName";
var currentDirectory = Directory.GetCurrentDirectory();
var webIdentityTokenFilePath = Path.Combine(currentDirectory, "my-token.jwt");
File.WriteAllText(webIdentityTokenFilePath, webIdentityToken);
var envVariables = new Dictionary()
{
{ AssumeRoleWithWebIdentityCredentials.WebIdentityTokenFileEnvVariable, webIdentityTokenFilePath },
{ AssumeRoleWithWebIdentityCredentials.RoleArnEnvVariable, roleArn },
{ AssumeRoleWithWebIdentityCredentials.RoleSessionNameEnvVariable, roleSessionName },
};
AWSCredentials awsCredentials;
using (new FallbackFactoryTestFixture(envVariables))
{
awsCredentials = FallbackCredentialsFactory.GetCredentials();
}
var webIdentityCredentials = new AssumeRoleWithWebIdentityTestCredentials((AssumeRoleWithWebIdentityCredentials)awsCredentials, null);
var retries = 0;
var client = webIdentityCredentials.Client as AmazonSecurityTokenServiceClient;
var pipeline = client
.GetType()
.GetProperty("RuntimePipeline", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)
.GetValue(client, null)
as RuntimePipeline;
// Setup STS failures
var credentialsRetriever = new Mock(awsCredentials);
credentialsRetriever.Setup(cr => cr.InvokeSync(It.IsAny())).Throws(exception); // Setting up the exception here
pipeline.ReplaceHandler(credentialsRetriever.Object);
// Setup retry count notifications
var retryHandler = pipeline.Handlers.Find(h => h is RetryHandler) as RetryHandler;
var notifyingRetryHandler = new NotifyingRetryHandler(client.Config);
notifyingRetryHandler.AddHandler((object sender, RetryEventArgs e) => { retries++; });
pipeline.ReplaceHandler(notifyingRetryHandler);
var defaultRetryPolicy = new DefaultRetryPolicy(client.Config);
using (new FallbackFactoryTestFixture(envVariables))
{
// Act and Assert
Assert.ThrowsException(() => webIdentityCredentials.GetCredentials());
}
Assert.AreEqual(defaultRetryPolicy.MaxRetries, retries);
webIdentityCredentials.Dispose();
}
[TestMethod]
public void TestAssumeRoleWithWebIdentity_RetriesInvalidIdentityToken()
{
TestRetriesOnException(new InvalidIdentityTokenException("InvalidIdentityToken"));
}
[TestMethod]
public void TestAssumeRoleWithWebIdentity_RetriesIDPCommunicationErrors()
{
TestRetriesOnException(new IDPCommunicationErrorException("IDPCommunicationError"));
}
#if ASYNC_AWAIT
public async Task TestRetriesOnExceptionAsync(T excpetion) where T : AmazonServiceException
{
var webIdentityToken = "Dummy.OIDC.Token";
var roleArn = "someRoleArn";
var roleSessionName = "someRoleSessionName";
var currentDirectory = Directory.GetCurrentDirectory();
var webIdentityTokenFilePath = Path.Combine(currentDirectory, "my-token.jwt");
File.WriteAllText(webIdentityTokenFilePath, webIdentityToken);
var envVariables = new Dictionary()
{
{ AssumeRoleWithWebIdentityCredentials.WebIdentityTokenFileEnvVariable, webIdentityTokenFilePath },
{ AssumeRoleWithWebIdentityCredentials.RoleArnEnvVariable, roleArn },
{ AssumeRoleWithWebIdentityCredentials.RoleSessionNameEnvVariable, roleSessionName },
};
AWSCredentials awsCredentials;
using (new FallbackFactoryTestFixture(envVariables))
{
awsCredentials = FallbackCredentialsFactory.GetCredentials();
}
var webIdentityCredentials = new AssumeRoleWithWebIdentityTestCredentials((AssumeRoleWithWebIdentityCredentials)awsCredentials, null);
var retries = 0;
var client = webIdentityCredentials.Client as AmazonSecurityTokenServiceClient;
var pipeline = client
.GetType()
.GetProperty("RuntimePipeline", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)
.GetValue(client, null)
as RuntimePipeline;
// Setup STS failures
var credentialsRetriever = new Mock(awsCredentials);
credentialsRetriever.Setup(cr => cr.InvokeAsync(It.IsAny())).ThrowsAsync(excpetion); // Setting up the exception here
pipeline.ReplaceHandler(credentialsRetriever.Object);
// Setup retry count notifications
var retryHandler = pipeline.Handlers.Find(h => h is RetryHandler) as RetryHandler;
var notifyingRetryHandler = new NotifyingRetryHandler(client.Config);
notifyingRetryHandler.AddHandler((object sender, RetryEventArgs e) => { retries++; });
pipeline.ReplaceHandler(notifyingRetryHandler);
var defaultRetryPolicy = new DefaultRetryPolicy(client.Config);
using (new FallbackFactoryTestFixture(envVariables))
{
// Act and Assert
await Assert.ThrowsExceptionAsync(async () => await webIdentityCredentials.GetCredentialsAsync());
}
Assert.AreEqual(defaultRetryPolicy.MaxRetries, retries);
webIdentityCredentials.Dispose();
}
[TestMethod]
public async Task TestAssumeRoleWithWebIdentityAsync_RetriesInvalidIdentityToken()
{
await TestRetriesOnExceptionAsync(new InvalidIdentityTokenException("InvalidIdentityToken"));
}
[TestMethod]
public async Task TestAssumeRoleWithWebIdentityAsync_RetriesIDPCommunicationErrors()
{
await TestRetriesOnExceptionAsync(new IDPCommunicationErrorException("IDPCommunicationError"));
}
#endif
}
public class RetryEventArgs
{
public RetryEventArgs(IExecutionContext executionContext)
{
RetryExecutionContext = executionContext;
}
public IExecutionContext RetryExecutionContext { get; set; }
}
public class NotifyingDefaultRetryPolicy : SecurityTokenServiceRetryPolicy
{
public delegate void RetryEventHandler(object sender, RetryEventArgs e);
public event RetryEventHandler EventHandler;
public NotifyingDefaultRetryPolicy(IClientConfig config) : base(config)
{
MaxBackoffInMilliseconds = 1;
}
public override bool RetryForException(IExecutionContext executionContext, Exception exception)
{
EventHandler?.Invoke(this, new RetryEventArgs(executionContext));
return base.RetryForException(executionContext, exception);
}
#if ASYNC_AWAIT
public override Task RetryForExceptionAsync(IExecutionContext executionContext, Exception exception)
{
EventHandler?.Invoke(this, new RetryEventArgs(executionContext));
return base.RetryForExceptionAsync(executionContext, exception);
}
#endif
}
public class NotifyingRetryHandler : RetryHandler
{
public NotifyingRetryHandler(IClientConfig config) : base(new NotifyingDefaultRetryPolicy(config)) { }
public void AddHandler(NotifyingDefaultRetryPolicy.RetryEventHandler eventHandler)
{
(RetryPolicy as NotifyingDefaultRetryPolicy).EventHandler += eventHandler;
}
public void RemoveHandler(NotifyingDefaultRetryPolicy.RetryEventHandler eventHandler)
{
(RetryPolicy as NotifyingDefaultRetryPolicy).EventHandler -= eventHandler;
}
}
public class AssumeRoleWithWebIdentityTestCredentials : AssumeRoleWithWebIdentityCredentials
{
public ICoreAmazonSTS_WebIdentity Client { get; set; }
public AssumeRoleWithWebIdentityTestCredentials(string webIdentityTokenFile, string roleArn, string roleSessionName, AssumeRoleWithWebIdentityCredentialsOptions options)
: base(webIdentityTokenFile, roleArn, roleSessionName, options)
{
Client = base.CreateClient();
}
public AssumeRoleWithWebIdentityTestCredentials(AssumeRoleWithWebIdentityCredentials baseCreds, AssumeRoleWithWebIdentityCredentialsOptions options)
: base(baseCreds.WebIdentityTokenFile, baseCreds.RoleArn, baseCreds.RoleSessionName, options)
{
Client = base.CreateClient();
}
protected override ICoreAmazonSTS_WebIdentity CreateClient()
{
return Client;
}
}
public class FallbackFactoryTestFixture : IDisposable
{
private readonly Dictionary originalEnvironmentVariables = new Dictionary();
public FallbackFactoryTestFixture(Dictionary newEnvironmentVariables = null)
{
if (newEnvironmentVariables != null)
{
foreach (KeyValuePair envVariable in newEnvironmentVariables)
{
var originalValue = Environment.GetEnvironmentVariable(envVariable.Key);
Environment.SetEnvironmentVariable(envVariable.Key, envVariable.Value);
originalEnvironmentVariables.Add(envVariable.Key, originalValue);
}
}
// reset before use to ensure the new credentialProfileChains are used.
FallbackCredentialsFactory.Reset();
FallbackRegionFactory.Reset();
FallbackEndpointDiscoveryEnabledFactory.Reset();
FallbackInternalConfigurationFactory.Reset();
}
public void Dispose()
{
foreach (KeyValuePair envVariable in originalEnvironmentVariables)
{
Environment.SetEnvironmentVariable(envVariable.Key, envVariable.Value);
}
FallbackCredentialsFactory.Reset();
FallbackRegionFactory.Reset();
FallbackEndpointDiscoveryEnabledFactory.Reset();
FallbackInternalConfigurationFactory.Reset();
}
}
}