/*
* 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;
using Amazon.Runtime;
using Amazon.S3;
using Amazon.S3.Model;
using Amazon.S3.Util;
using Amazon.Util;
using AWSSDK_DotNet.IntegrationTests.Utils;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
namespace AWSSDK_DotNet.IntegrationTests.Tests.S3
{
///
/// Summary description for PutObjectTest
///
[TestClass]
public class GeneratePresignedUrlTests : TestBase
{
private const string TestContent = "This is the content body!";
private const string TestKey = "key";
private const long MegSize = 1048576;
[TestMethod]
[TestCategory("S3")]
[TestCategory("RequiresIAMUser")]
public void USEastUnder7Days()
{
TestPreSignedUrl(RegionEndpoint.USEast1, AWSSDKUtils.CorrectedUtcNow.AddDays(7).AddHours(-2), true, true);
TestPreSignedUrlWithSessionToken(RegionEndpoint.USEast1, AWSSDKUtils.CorrectedUtcNow.AddDays(7).AddHours(-2), true, true);
}
[TestMethod]
[TestCategory("S3")]
[TestCategory("RequiresIAMUser")]
public void USEastOver7Days()
{
// us-east-1 allows Sigv2 so it should fall back to it since the expiration is > 7 days
TestPreSignedUrl(RegionEndpoint.USEast1, AWSSDKUtils.CorrectedUtcNow.AddDays(7).AddHours(2), true, false);
TestPreSignedUrlWithSessionToken(RegionEndpoint.USEast1, AWSSDKUtils.CorrectedUtcNow.AddDays(7).AddHours(2), true, false);
}
[TestMethod]
[TestCategory("S3")]
[TestCategory("RequiresIAMUser")]
public void EUCentral1Under7Days()
{
TestPreSignedUrl(RegionEndpoint.EUCentral1, AWSSDKUtils.CorrectedUtcNow.AddDays(7).AddHours(-2), true, true);
TestPreSignedUrlWithSessionToken(RegionEndpoint.EUCentral1, AWSSDKUtils.CorrectedUtcNow.AddDays(7).AddHours(-2), true, true);
}
[TestMethod]
[TestCategory("S3")]
[TestCategory("RequiresIAMUser")]
public void EUCentral1Over7Days()
{
// EUCentral1 doesn't allow SigV2 so we expect an error since the expiration > 7 days
AssertExtensions.ExpectException(()=>{
TestPreSignedUrl(RegionEndpoint.EUCentral1, AWSSDKUtils.CorrectedUtcNow.AddDays(7).AddHours(2), true, true);
}, typeof(ArgumentException), "The maximum expiry period for a presigned url using AWS4 signing is 604800 seconds");
AssertExtensions.ExpectException(() => {
TestPreSignedUrlWithSessionToken(RegionEndpoint.EUCentral1, AWSSDKUtils.CorrectedUtcNow.AddDays(7).AddHours(2), true, true);
}, typeof(ArgumentException), "The maximum expiry period for a presigned url using AWS4 signing is 604800 seconds");
}
[TestMethod]
[TestCategory("S3")]
public void USEastSignedParameters() {
TestSignedUrlParameters(RegionEndpoint.USEast1, DateTime.Now.AddDays(1));
}
private void TestPreSignedUrl(RegionEndpoint region, DateTime expires, bool useSigV4, bool expectSigV4Url)
{
var client = new AmazonS3Client(region);
var originalUseSigV4 = AWSConfigsS3.UseSignatureVersion4;
string bucketName = null;
try
{
AWSConfigsS3.UseSignatureVersion4 = true;
bucketName = CreateBucketAndObject(client);
AssertPreSignedUrl(client, bucketName, expires, expectSigV4Url);
}
finally
{
AWSConfigsS3.UseSignatureVersion4 = originalUseSigV4;
if (bucketName != null)
DeleteBucket(client, bucketName);
}
}
private void TestPreSignedUrlWithSessionToken(RegionEndpoint region, DateTime expires, bool useSigV4, bool expectSigV4Url)
{
using (var sts = new Amazon.SecurityToken.AmazonSecurityTokenServiceClient())
{
AWSCredentials credentials = sts.GetSessionToken().Credentials;
var client = new AmazonS3Client(credentials, region);
var originalUseSigV4 = AWSConfigsS3.UseSignatureVersion4;
AWSConfigsS3.UseSignatureVersion4 = true;
string bucketName = null;
try
{
AWSConfigsS3.UseSignatureVersion4 = true;
bucketName = CreateBucketAndObject(client);
AssertPreSignedUrl(client, bucketName, expires, expectSigV4Url);
}
finally
{
AWSConfigsS3.UseSignatureVersion4 = originalUseSigV4;
if (bucketName != null)
DeleteBucket(client, bucketName);
}
}
}
private void TestSignedUrlParameters(RegionEndpoint region, DateTime expires) {
var client = new AmazonS3Client(region);
var originalUseSigV4 = AWSConfigsS3.UseSignatureVersion4;
string bucketName = null;
try {
AWSConfigsS3.UseSignatureVersion4 = true;
bucketName = CreateBucketAndObject(client);
AssertSignedUrlParameters(client, bucketName, expires, true);
}
finally {
AWSConfigsS3.UseSignatureVersion4 = originalUseSigV4;
if (bucketName != null)
DeleteBucket(client, bucketName);
}
}
private string CreateBucketAndObject(AmazonS3Client client)
{
var bucketName = S3TestUtils.CreateBucketWithWait(client);
client.PutObject(new PutObjectRequest
{
BucketName = bucketName,
Key = TestKey,
ContentBody = TestContent
});
S3TestUtils.WaitForObject(client, bucketName, TestKey, 30);
return bucketName;
}
private void AssertPreSignedUrl(AmazonS3Client client, string bucketName, DateTime expires, bool expectSigV4Url)
{
// generate url
var url = client.GetPreSignedURL(new GetPreSignedUrlRequest
{
BucketName = bucketName,
Key = TestKey,
Expires = expires
});
// make sure we used the correct signtaure version
var urlIsSigV4 = url.Contains("aws4_request");
Assert.AreEqual(expectSigV4Url, urlIsSigV4);
// use independent web client make sure the URL actually works
var wc = new WebClient();
Assert.AreEqual(wc.DownloadString(url), TestContent);
}
private void AssertSignedUrlParameters(AmazonS3Client client, string bucketName, DateTime expires, bool expectSigV4Url) {
const string paramKey = "x-test-param";
const string paramValue = "TestParamValue";
const string badParamKey = "x-test-param2";
const string badParamValue = "TestParamValue2";
var preSignedRequest = new GetPreSignedUrlRequest
{
BucketName = bucketName,
Key = TestKey,
Expires = expires
};
// Add a parameter & value to be signed
preSignedRequest.Parameters.Add(paramKey, paramValue);
// generate url
var url = client.GetPreSignedURL(preSignedRequest);
// make sure we used the correct signtaure version
var urlIsSigV4 = url.Contains("aws4_request");
Assert.AreEqual(expectSigV4Url, urlIsSigV4);
// use independent web client make sure the URL actually works
var wc = new WebClient();
Assert.AreEqual(wc.DownloadString(url), TestContent);
// change parameter and we should get a 403 response
string badParamURL = url.Replace(paramKey, badParamKey);
// Using a modified parameter name should throw an exception
WebException wex = Assert.ThrowsException(() => wc.DownloadString(badParamURL));
// And that exception should be permission denied:
Assert.IsTrue(wex.Message.Contains("403"));
// change value and we should get a 403 response
string badValueURL = url.Replace(paramValue, badParamValue);
// Using a modified parameter value should throw an exception
wex = Assert.ThrowsException(() => wc.DownloadString(badValueURL));
// And that exception should be permission denied:
Assert.IsTrue(wex.Message.Contains("403"));
}
[TestMethod]
[TestCategory("S3")]
public void MultipartUploadPresignedUrl()
{
var key = "multipart";
var client = new AmazonS3Client(RegionEndpoint.USEast1);
var bucketName = CreateBucketAndObject(client);
var totalMegs = 15;
var initiateMultipartResponse = client.InitiateMultipartUpload(new InitiateMultipartUploadRequest()
{
BucketName = bucketName,
Key = key,
ContentType = "text/plain"
});
var abortedMessage = "";
var partETags = new List();
try
{
for (var part = 1; part <= totalMegs / 5; part++)
{
var url = client.GetPreSignedURL(new GetPreSignedUrlRequest
{
BucketName = bucketName,
Key = key,
Expires = DateTime.Now.AddDays(1),
PartNumber = part,
UploadId = initiateMultipartResponse.UploadId,
Verb = HttpVerb.PUT,
ContentType = "text/plain",
Protocol = Protocol.HTTPS
});
WebRequest request = WebRequest.Create(url);
request.ContentLength = MegSize * 5;
request.Method = "PUT";
request.ContentType = "text/plain";
using (var dataStream = request.GetRequestStream())
{
var random = new Random();
var buffer = new byte[MegSize * 5];
random.NextBytes(buffer);
dataStream.Write(buffer, 0, (int)(MegSize * 5));
}
WebResponse response = request.GetResponse();
partETags.Add(new PartETag(part, response.Headers["ETag"]));
}
client.CompleteMultipartUpload(new CompleteMultipartUploadRequest()
{
BucketName = bucketName,
Key = key,
UploadId = initiateMultipartResponse.UploadId,
PartETags = partETags
});
}
catch (Exception e)
{
abortedMessage = e.StackTrace;
client.AbortMultipartUpload(new AbortMultipartUploadRequest()
{
BucketName = bucketName,
Key = key,
UploadId = initiateMultipartResponse.UploadId
});
}
finally
{
DeleteBucket(client, bucketName);
if (!string.IsNullOrEmpty(abortedMessage))
{
Assert.Inconclusive(abortedMessage);
}
}
}
private void DeleteBucket(AmazonS3Client client, string bucketName)
{
AmazonS3Util.DeleteS3BucketWithObjects(client, bucketName);
}
}
}