diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws.sln b/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws.sln new file mode 100644 index 0000000..7e2a480 --- /dev/null +++ b/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.40629.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elasticsearch.Net.Aws", "Elasticsearch.Net.Aws\Elasticsearch.Net.Aws.csproj", "{17DB4A76-0442-4020-8659-13645AEEE722}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{4C55D18B-FD05-424C-8E22-C37F511FD5DA}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {17DB4A76-0442-4020-8659-13645AEEE722}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {17DB4A76-0442-4020-8659-13645AEEE722}.Debug|Any CPU.Build.0 = Debug|Any CPU + {17DB4A76-0442-4020-8659-13645AEEE722}.Release|Any CPU.ActiveCfg = Release|Any CPU + {17DB4A76-0442-4020-8659-13645AEEE722}.Release|Any CPU.Build.0 = Release|Any CPU + {4C55D18B-FD05-424C-8E22-C37F511FD5DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4C55D18B-FD05-424C-8E22-C37F511FD5DA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4C55D18B-FD05-424C-8E22-C37F511FD5DA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4C55D18B-FD05-424C-8E22-C37F511FD5DA}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/AwsHttpConnection.cs b/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/AwsHttpConnection.cs new file mode 100644 index 0000000..14f0462 --- /dev/null +++ b/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/AwsHttpConnection.cs @@ -0,0 +1,40 @@ +using System; +using System.Linq; +using System.Net; +using Elasticsearch.Net.Connection; +using Elasticsearch.Net.Connection.Configuration; + +namespace Elasticsearch.Net.Aws +{ + /// + /// Encapsulates an IConnection that works with AWS's Elasticsearch service. + /// + public class AwsHttpConnection : HttpConnection + { + readonly string _accessKey; + readonly string _secretKey; + readonly string _region; + + /// + /// Initializes a new instance of the AwsHttpConnection class. + /// + /// The NEST/Elasticsearch.Net settings. + /// AWS specific settings required for signing requests. + public AwsHttpConnection(IConnectionConfigurationValues settings, AwsSettings awsSettings) : base(settings) + { + if (awsSettings == null) throw new ArgumentNullException("awsSettings"); + if (string.IsNullOrWhiteSpace(awsSettings.SecretKey)) throw new ArgumentException("awsSettings.SecretKey is invalid.", "awsSettings"); + if (string.IsNullOrWhiteSpace(awsSettings.Region)) throw new ArgumentException("awsSettings.Region is invalid.", "awsSettings"); + _accessKey = awsSettings.AccessKey; + _secretKey = awsSettings.SecretKey; + _region = awsSettings.Region.ToLowerInvariant(); + } + + protected override HttpWebRequest CreateHttpWebRequest(Uri uri, string method, byte[] data, IRequestConfiguration requestSpecificConfig) + { + var request = base.CreateHttpWebRequest(uri, method, data, requestSpecificConfig); + SignV4Util.SignRequest(request, data, _accessKey, _secretKey, _region, "es"); + return request; + } + } +} diff --git a/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/AwsSettings.cs b/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/AwsSettings.cs new file mode 100644 index 0000000..0bfb9cc --- /dev/null +++ b/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/AwsSettings.cs @@ -0,0 +1,27 @@ +using System; +using System.Linq; + +namespace Elasticsearch.Net.Aws +{ + /// + /// Encapsulates + /// + public class AwsSettings + { + /// + /// Gets or sets the region. e.g. us-east-1. Required. + /// + public string Region { get; set; } + + /// + /// Gets or sets the AWS access key. Required. + /// + public string AccessKey { get; set; } + + /// + /// Gets or sets the AWS secret key. e.g. wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY + /// Required. + /// + public string SecretKey { get; set; } + } +} diff --git a/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws.csproj b/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws.csproj new file mode 100644 index 0000000..0759dac --- /dev/null +++ b/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws.csproj @@ -0,0 +1,71 @@ + + + + + Debug + AnyCPU + {17DB4A76-0442-4020-8659-13645AEEE722} + Library + Properties + Elasticsearch.Net.Aws + Elasticsearch.Net.Aws + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Elasticsearch.Net.1.7.1\lib\net45\Elasticsearch.Net.dll + True + + + ..\packages\NEST.1.7.1\lib\net45\Nest.dll + True + + + ..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll + True + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/Properties/AssemblyInfo.cs b/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..f19947e --- /dev/null +++ b/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/Properties/AssemblyInfo.cs @@ -0,0 +1,37 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Elasticsearch.Net.Aws")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Elasticsearch.Net.Aws")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("4571fc2e-a85c-49ce-b8bb-b509f78829b4")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: InternalsVisibleTo("Tests")] diff --git a/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/SignV4Util.cs b/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/SignV4Util.cs new file mode 100644 index 0000000..2efa69d --- /dev/null +++ b/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/SignV4Util.cs @@ -0,0 +1,170 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Net; +using System.Security.Cryptography; +using System.Text; +using System.Text.RegularExpressions; + +namespace Elasticsearch.Net.Aws +{ + internal static class SignV4Util + { + static readonly char[] _datePartSplitChars = { 'T' }; + + public static void SignRequest(HttpWebRequest request, byte[] body, string accessKey, string secretKey, string region, string service) + { + var date = DateTime.UtcNow; + var dateStamp = date.ToString("yyyyMMdd"); + var amzDate = date.ToString("yyyyMMddTHHmmssZ"); + request.Headers["X-Amz-Date"] = amzDate; + var signingKey = GetSigningKey(secretKey, dateStamp, region, service); + var signature = signingKey.GetHmacSha256Hash(GetStringToSign(request, body, region, service)).ToLowercaseHex(); + var auth = string.Format( + "AWS4-HMAC-SHA256 Credential={0}/{1}, SignedHeaders={2}, Signature={3}", + accessKey, + GetCredentialScope(dateStamp, region, service), + GetSignedHeaders(request), + signature); + request.Headers[HttpRequestHeader.Authorization] = auth; + } + + public static byte[] GetSigningKey(string secretKey, string dateStamp, string region, string service) + { + return _encoding.GetBytes("AWS4" + secretKey) + .GetHmacSha256Hash(dateStamp) + .GetHmacSha256Hash(region) + .GetHmacSha256Hash(service) + .GetHmacSha256Hash("aws4_request"); + } + + private static byte[] GetHmacSha256Hash(this byte[] key, string data) + { + using (var kha = KeyedHashAlgorithm.Create("HmacSHA256")) + { + kha.Key = key; + return kha.ComputeHash(_encoding.GetBytes(data)); + } + } + + public static string GetStringToSign(HttpWebRequest request, byte[] data, string region, string service) + { + var canonicalRequest = GetCanonicalRequest(request, data); + var awsDate = request.Headers["x-amz-date"]; + Debug.Assert(Regex.IsMatch(awsDate, @"\d{8}T\d{6}Z")); + var datePart = awsDate.Split(_datePartSplitChars, 2)[0]; + return string.Join("\n", + "AWS4-HMAC-SHA256", + awsDate, + GetCredentialScope(datePart, region, service), + GetHash(canonicalRequest).ToLowercaseHex() + ); + } + + private static string GetCredentialScope(string date, string region, string service) + { + return string.Format("{0}/{1}/{2}/aws4_request", date, region, service); + } + + public static string GetCanonicalRequest(HttpWebRequest request, byte[] data) + { + var canonicalHeaders = request.GetCanonicalHeaders(); + var result = new StringBuilder(); + result.Append(request.Method); + result.Append('\n'); + result.Append(request.RequestUri.AbsolutePath); + result.Append('\n'); + result.Append(request.RequestUri.Query); + result.Append('\n'); + WriteCanonicalHeaders(canonicalHeaders, result); + result.Append('\n'); + WriteSignedHeaders(canonicalHeaders, result); + result.Append('\n'); + WriteRequestPayloadHash(data, result); + return result.ToString(); + } + + private static Dictionary GetCanonicalHeaders(this HttpWebRequest request) + { + var q = from string key in request.Headers + let headerName = key.ToLowerInvariant() + let headerValues = string.Join(",", + request.Headers + .GetValues(key) ?? Enumerable.Empty() + .Select(v => v.Trimall()) + ) + select new { headerName, headerValues }; + var result = q.ToDictionary(v => v.headerName, v => v.headerValues); + result["host"] = request.RequestUri.Host.ToLowerInvariant(); + return result; + } + + private static void WriteCanonicalHeaders(Dictionary canonicalHeaders, StringBuilder output) + { + var q = from pair in canonicalHeaders + orderby pair.Key ascending + select string.Format("{0}:{1}\n", pair.Key, pair.Value); + foreach (var line in q) + { + output.Append(line); + } + } + + private static string GetSignedHeaders(HttpWebRequest request) + { + var canonicalHeaders = request.GetCanonicalHeaders(); + var result = new StringBuilder(); + WriteSignedHeaders(canonicalHeaders, result); + return result.ToString(); + } + + private static void WriteSignedHeaders(Dictionary canonicalHeaders, StringBuilder output) + { + bool started = false; + foreach (var pair in canonicalHeaders.OrderBy(v => v.Key)) + { + if (started) output.Append(';'); + output.Append(pair.Key.ToLowerInvariant()); + started = true; + } + } + + static readonly byte[] _emptyBytes = new byte[0]; + + private static void WriteRequestPayloadHash(byte[] data, StringBuilder output) + { + data = data ?? _emptyBytes; + var hash = GetHash(data); + foreach (var b in hash) + { + output.AppendFormat("{0:x2}", b); + } + } + + private static string ToLowercaseHex(this byte[] data) + { + var result = new StringBuilder(); + foreach (var b in data) + { + result.AppendFormat("{0:x2}", b); + } + return result.ToString(); + } + + static readonly UTF8Encoding _encoding = new UTF8Encoding(false); + + private static byte[] GetHash(string data) + { + return GetHash(_encoding.GetBytes(data)); + } + + private static byte[] GetHash(this byte[] data) + { + using (var algo = HashAlgorithm.Create("SHA256")) + { + return algo.ComputeHash(data); + } + } + } +} diff --git a/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/StringUtil.cs b/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/StringUtil.cs new file mode 100644 index 0000000..baa5245 --- /dev/null +++ b/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/StringUtil.cs @@ -0,0 +1,141 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Net; +using System.Security.Cryptography; +using System.Text; + +namespace Elasticsearch.Net.Aws +{ + internal static class StringUtil + { + public static string Trimall(this string value) + { + value = value.Trim(); + var output = new StringBuilder(); + using (var reader = new StringReader(value)) + { + while (true) + { + var next = reader.Peek(); + if (next < 0) break; + var c = (char)next; + if (c == '"') + { + ReadQuotedString(output, reader); + continue; + } + if (char.IsWhiteSpace(c)) + { + ReadWhitespace(output, reader); + continue; + } + output.Append((char)reader.Read()); + } + } + return output.ToString(); + } + + private static void ReadQuotedString(StringBuilder output, StringReader reader) + { + var start = reader.Read(); + Debug.Assert(start == '"'); + output.Append('"'); + bool escape = false; + while (true) + { + var next = reader.Read(); + if (next < 0) break; + var c = (char)next; + output.Append(c); + if (escape) + { + escape = false; + } + else + { + if (c == '"') break; + if (c == '\\') escape = true; + } + } + } + + private static void ReadWhitespace(StringBuilder output, StringReader reader) + { + var lastWhitespace = (char)reader.Read(); + Debug.Assert(char.IsWhiteSpace(lastWhitespace)); + while (true) + { + var next = reader.Peek(); + if (next < 0) break; + var c = (char)next; + if (!char.IsWhiteSpace(c)) break; + lastWhitespace = c; + reader.Read(); + } + output.Append(lastWhitespace); + } + + private static string GetCanonicalRequest(HttpWebRequest request, byte[] data) + { + var result = new StringBuilder(); + result.Append(request.Method); + result.Append('\n'); + result.Append(request.RequestUri.AbsolutePath); + result.Append('\n'); + result.Append(request.RequestUri.Query); + result.Append('\n'); + WriteCanonicalHeaders(request, result); + result.Append('\n'); + WriteSignedHeaders(request, result); + result.Append('\n'); + WriteRequestPayloadHash(data, result); + return result.ToString(); + } + + private static void WriteCanonicalHeaders(HttpWebRequest request, StringBuilder output) + { + var q = from string key in request.Headers + let headerName = key.ToLowerInvariant() + let headerValues = string.Join(",", + request.Headers + .GetValues(key) ?? Enumerable.Empty() + .Select(v => v.Trimall()) + ) + orderby headerName ascending + select string.Format("{0}:{1}\n", headerName, headerValues); + foreach (var line in q) + { + output.Append(line); + } + } + + private static void WriteSignedHeaders(HttpWebRequest request, StringBuilder output) + { + bool started = false; + foreach (string headerName in request.Headers) + { + if (started) output.Append(';'); + output.Append(headerName.ToLowerInvariant()); + started = true; + } + } + + static readonly byte[] _emptyBytes = new byte[0]; + + private static void WriteRequestPayloadHash(byte[] data, StringBuilder output) + { + data = data ?? _emptyBytes; + byte[] hash; + using (var algo = HashAlgorithm.Create("SHA256")) + { + hash = algo.ComputeHash(data); + } + foreach (var b in hash) + { + output.AppendFormat("{0:x2}", b); + } + } + } +} diff --git a/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/packages.config b/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/packages.config new file mode 100644 index 0000000..273a18c --- /dev/null +++ b/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/Elasticsearch.Net.Aws/Tests/Properties/AssemblyInfo.cs b/src/Elasticsearch.Net.Aws/Tests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..5ed36c2 --- /dev/null +++ b/src/Elasticsearch.Net.Aws/Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Tests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Tests")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("3eec6e0d-be36-4993-b4ab-9f302a797e75")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/Elasticsearch.Net.Aws/Tests/SignUtilTests.cs b/src/Elasticsearch.Net.Aws/Tests/SignUtilTests.cs new file mode 100644 index 0000000..bb30fab --- /dev/null +++ b/src/Elasticsearch.Net.Aws/Tests/SignUtilTests.cs @@ -0,0 +1,87 @@ +using System; +using System.Diagnostics; +using System.Linq; +using System.Net; +using System.Text; +using Elasticsearch.Net.Aws; +using NUnit.Framework; + +namespace Tests +{ + [TestFixture] + public class SignUtilTests + { + HttpWebRequest _sampleRequest; + byte[] _sampleBody; + + [SetUp] + public void SetUp() + { + _sampleRequest = WebRequest.CreateHttp("https://iam.amazonaws.com/"); + _sampleRequest.Method = "POST"; + _sampleRequest.ContentType = "application/x-www-form-urlencoded; charset=utf-8"; + _sampleRequest.Headers["X-Amz-Date"] = "20110909T233600Z"; + var encoding = new UTF8Encoding(false); + _sampleBody = encoding.GetBytes("Action=ListUsers&Version=2010-05-08"); + } + + [Test] + public void GetCanonicalRequest_should_match_sample() + { + var canonicalRequest = SignV4Util.GetCanonicalRequest(_sampleRequest, _sampleBody); + Trace.WriteLine("=== Actual ==="); + Trace.Write(canonicalRequest); + + const string expected = "POST\n/\n\ncontent-type:application/x-www-form-urlencoded; charset=utf-8\nhost:iam.amazonaws.com\nx-amz-date:20110909T233600Z\n\ncontent-type;host;x-amz-date\nb6359072c78d70ebee1e81adcbab4f01bf2c23245fa365ef83fe8f1f955085e2"; + Trace.WriteLine("=== Expected ==="); + Trace.Write(expected); + + Assert.AreEqual(expected, canonicalRequest); + } + + [Test] + public void GetStringToSign_should_match_sample() + { + var stringToSign = SignV4Util.GetStringToSign(_sampleRequest, _sampleBody, "us-east-1", "iam"); + Trace.WriteLine("=== Actual ==="); + Trace.Write(stringToSign); + + const string expected = "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/iam/aws4_request\n3511de7e95d28ecd39e9513b642aee07e54f4941150d8df8bf94b328ef7e55e2"; + Trace.WriteLine("=== Expected ==="); + Trace.Write(expected); + + Assert.AreEqual(expected, stringToSign); + } + + [Test] + public void GetSigningKey_should_match_sample() + { + // sample comes from - http://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html + var secretKey = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY"; + var date = "20120215"; + var region = "us-east-1"; + var service = "iam"; + var expectedSigningKey = "f4780e2d9f65fa895f9c67b32ce1baf0b0d8a43505a000a1a9e090d414db404d"; + var actualSigningKeyBytes = SignV4Util.GetSigningKey(secretKey, date, region, service); + var actualSigningKey = BitConverter.ToString(actualSigningKeyBytes).Replace("-", "").ToLowerInvariant(); + Trace.WriteLine("Expected: " + expectedSigningKey); + Trace.WriteLine("Actual: " + actualSigningKey); + Assert.AreEqual(expectedSigningKey, actualSigningKey); + } + + [Test] + public void SignRequest_should_apply_signature_to_request() + { + var secretKey = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY"; + SignV4Util.SignRequest(_sampleRequest, _sampleBody, "ExampleKey", secretKey, "us-east-1", "iam"); + var amzDate = _sampleRequest.Headers["X-Amz-Date"]; + + Assert.IsNotNullOrEmpty(amzDate); + Trace.WriteLine("X-Amz-Date: " + amzDate); + + var auth = _sampleRequest.Headers[HttpRequestHeader.Authorization]; + Assert.IsNotNullOrEmpty(auth); + Trace.WriteLine("Authorize: " + auth); + } + } +} diff --git a/src/Elasticsearch.Net.Aws/Tests/StringUtilTests.cs b/src/Elasticsearch.Net.Aws/Tests/StringUtilTests.cs new file mode 100644 index 0000000..b8f1431 --- /dev/null +++ b/src/Elasticsearch.Net.Aws/Tests/StringUtilTests.cs @@ -0,0 +1,19 @@ +using System; +using System.Linq; +using Elasticsearch.Net.Aws; +using NUnit.Framework; + +namespace Tests +{ + [TestFixture] + public class StringUtilTests + { + [TestCase(" leading whitespace", "leading whitespace")] + [TestCase("trailing whitespace ", "trailing whitespace")] + [TestCase(" whitespace\t\r \tin the middle \r\n", "whitespace\tin the middle")] + public void Trimall_should_follow_rfc_2616(string original, string expected) + { + Assert.AreEqual(original.Trimall(), expected.Trimall()); + } + } +} diff --git a/src/Elasticsearch.Net.Aws/Tests/Tests.csproj b/src/Elasticsearch.Net.Aws/Tests/Tests.csproj new file mode 100644 index 0000000..271b7fb --- /dev/null +++ b/src/Elasticsearch.Net.Aws/Tests/Tests.csproj @@ -0,0 +1,67 @@ + + + + + Debug + AnyCPU + {4C55D18B-FD05-424C-8E22-C37F511FD5DA} + Library + Properties + Tests + Tests + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\NUnit.2.6.4\lib\nunit.framework.dll + True + + + + + + + + + + + + + + + + + {17db4a76-0442-4020-8659-13645aeee722} + Elasticsearch.Net.Aws + + + + + + + + \ No newline at end of file diff --git a/src/Elasticsearch.Net.Aws/Tests/packages.config b/src/Elasticsearch.Net.Aws/Tests/packages.config new file mode 100644 index 0000000..c714ef3 --- /dev/null +++ b/src/Elasticsearch.Net.Aws/Tests/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file