/*
* 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 Amazon.Runtime.Internal.Auth;
using Amazon.Runtime.Internal.Util;
using Amazon.Util;
using System;
using System.IO;
#if AWS_ASYNC_API
using System.Threading.Tasks;
#endif
namespace Amazon.Runtime.Internal
{
///
/// This handler signs the request.
///
public class Signer : PipelineHandler
{
///
/// Calls pre invoke logic before calling the next handler
/// in the pipeline.
///
/// The execution context which contains both the
/// requests and response context.
public override void InvokeSync(IExecutionContext executionContext)
{
PreInvoke(executionContext);
base.InvokeSync(executionContext);
}
#if AWS_ASYNC_API
///
/// Calls pre invoke logic before calling the next handler
/// in the pipeline.
///
/// The response type for the current request.
/// The execution context, it contains the
/// request and response context.
/// A task that represents the asynchronous operation.
public override async System.Threading.Tasks.Task InvokeAsync(IExecutionContext executionContext)
{
await PreInvokeAsync(executionContext).ConfigureAwait(false);
return await base.InvokeAsync(executionContext).ConfigureAwait(false);
}
#elif AWS_APM_API
///
/// Calls pre invoke logic before calling the next handler
/// in the pipeline.
///
/// The execution context which contains both the
/// requests and response context.
/// IAsyncResult which represent an async operation.
public override IAsyncResult InvokeAsync(IAsyncExecutionContext executionContext)
{
PreInvoke(ExecutionContext.CreateFromAsyncContext(executionContext));
return base.InvokeAsync(executionContext);
}
#endif
///
/// Signs the request before invoking the next handler.
///
///
/// The execution context, it contains the request and response context.
///
protected static void PreInvoke(IExecutionContext executionContext)
{
if (ShouldSign(executionContext.RequestContext))
{
SignRequest(executionContext.RequestContext);
executionContext.RequestContext.IsSigned = true;
}
}
#if AWS_ASYNC_API
protected static async System.Threading.Tasks.Task PreInvokeAsync(IExecutionContext executionContext)
{
if (ShouldSign(executionContext.RequestContext))
{
await SignRequestAsync(executionContext.RequestContext).ConfigureAwait(false);
executionContext.RequestContext.IsSigned = true;
}
}
#endif
///
/// Determines if the request should be signed.
///
/// The request context.
/// A boolean value that indicated if the request should be signed.
private static bool ShouldSign(IRequestContext requestContext)
{
return !requestContext.IsSigned ||
requestContext.ClientConfig.ResignRetries;
}
///
/// Signs the request.
///
/// The request context.
public static void SignRequest(IRequestContext requestContext)
{
ImmutableCredentials immutableCredentials = requestContext.ImmutableCredentials;
// credentials would be null in the case of anonymous users getting public resources from S3
if (immutableCredentials == null && requestContext.Signer.RequiresCredentials)
return;
using (requestContext.Metrics.StartEvent(Metric.RequestSigningTime))
{
if (immutableCredentials?.UseToken == true &&
!(requestContext.Signer is NullSigner) &&
!(requestContext.Signer is BearerTokenSigner))
{
ClientProtocol protocol = requestContext.Signer.Protocol;
switch (protocol)
{
case ClientProtocol.QueryStringProtocol:
requestContext.Request.Parameters["SecurityToken"] = immutableCredentials.Token;
break;
case ClientProtocol.RestProtocol:
requestContext.Request.Headers[HeaderKeys.XAmzSecurityTokenHeader] = immutableCredentials.Token;
break;
default:
throw new InvalidDataException("Cannot determine protocol");
}
}
requestContext.Signer.Sign(requestContext.Request, requestContext.ClientConfig, requestContext.Metrics, immutableCredentials);
}
}
#if AWS_ASYNC_API
///
/// Signs the request.
///
/// The request context.
private static async Task SignRequestAsync(IRequestContext requestContext)
{
ImmutableCredentials immutableCredentials = requestContext.ImmutableCredentials;
// credentials would be null in the case of anonymous users getting public resources from S3
if (immutableCredentials == null && requestContext.Signer.RequiresCredentials)
return;
using (requestContext.Metrics.StartEvent(Metric.RequestSigningTime))
{
if (immutableCredentials?.UseToken == true &&
!(requestContext.Signer is NullSigner) &&
!(requestContext.Signer is BearerTokenSigner))
{
ClientProtocol protocol = requestContext.Signer.Protocol;
switch (protocol)
{
case ClientProtocol.QueryStringProtocol:
requestContext.Request.Parameters["SecurityToken"] = immutableCredentials.Token;
break;
case ClientProtocol.RestProtocol:
requestContext.Request.Headers[HeaderKeys.XAmzSecurityTokenHeader] = immutableCredentials.Token;
break;
default:
throw new InvalidDataException("Cannot determine protocol");
}
}
await requestContext.Signer
.SignAsync(
requestContext.Request,
requestContext.ClientConfig,
requestContext.Metrics,
immutableCredentials)
.ConfigureAwait(false);
}
}
#endif
}
}