From f48b0a35640b0c377d269b4154c98c153f9fe838 Mon Sep 17 00:00:00 2001 From: Meena Peri Date: Sun, 1 Oct 2023 18:16:11 -0500 Subject: [PATCH] CSharp Auth and auth lib changes --- .../sellingpartner-api-aa-csharp/README.md | 44 +-- .../SellingPartnerAPIAuthAndAuthCSharp.sln | 49 +-- .../AWSAuthenticationCredentials.cs | 20 -- .../AWSSigV4Signer.cs | 82 ----- .../AWSSignerHelper.cs | 263 ---------------- .../Amazon.SellingPartnerAPIAA.csproj | 36 ++- .../Amazon.SellingPartnerAPIAA/LWAClient.cs | 26 +- .../LWAException.cs | 46 +++ .../LWAExceptionErrorCode.cs | 17 + .../templates/ApiClient.mustache | 4 +- .../templates/Configuration.mustache | 6 - .../templates/IReadableConfiguration.mustache | 6 - .../swagger-codegen/templates/api.mustache | 12 - .../AWSSigV4SignerTest.cs | 88 ------ .../AWSSignerHelperTest.cs | 291 ------------------ .../Amazon.SellingPartnerAPIAATests.csproj | 41 +-- .../LWAClientTest.cs | 82 ++++- 17 files changed, 249 insertions(+), 864 deletions(-) delete mode 100644 clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/AWSAuthenticationCredentials.cs delete mode 100644 clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/AWSSigV4Signer.cs delete mode 100644 clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/AWSSignerHelper.cs create mode 100644 clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/LWAException.cs create mode 100644 clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/LWAExceptionErrorCode.cs delete mode 100644 clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/AWSSigV4SignerTest.cs delete mode 100644 clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/AWSSignerHelperTest.cs diff --git a/clients/sellingpartner-api-aa-csharp/README.md b/clients/sellingpartner-api-aa-csharp/README.md index 7ae8977..be5d771 100644 --- a/clients/sellingpartner-api-aa-csharp/README.md +++ b/clients/sellingpartner-api-aa-csharp/README.md @@ -38,36 +38,9 @@ restRequest = new LWAAuthorizationSigner(lwaAuthorizationCredentials).Sign(restR ``` Note the IRestRequest reference is treated as **mutable** when signed. -## AWSSigV4Signer -Signs a request with [AWS Signature Version 4](https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html) -using the provided AWS developer account credentials. - -*Example* -``` -using RestSharp; -using Amazon.SellingPartnerAPIAA; - -string resource = "/my/api/path"; -RestClient restClient = new RestClient("https://..."); -IRestRequest restRequest = new RestRequest(resource, Method.GET); - -AWSAuthenticationCredentials awsAuthenticationCredentials = new AWSAuthenticationCredentials -{ - AccessKeyId = "..." - SecretKey = "..." - Region = "..." -}; - -restRequest = new AWSSigV4Signer(awsAuthenticationCredentials) - .Sign(restRequest, restClient.BaseUrl.Host); -``` -Note the IRestRequest reference is treated as **mutable** when signed. - ## RateLimitConfiguration - Interface to set and get rateLimit configurations that are used with RateLimiter. RateLimiter is used on client side to restrict the rate at which requests are made. RateLimiter Configuration takes Permit, rate which requests are made and TimeOut - *Example* ``` RateLimitConfiguration rateLimitConfig = new RateLimitConfigurationOnRequests @@ -78,6 +51,23 @@ RateLimitConfiguration rateLimitConfig = new RateLimitConfigurationOnRequests ``` +## Exception +This package returns a custom LWAException when there is an error returned during LWA authorization. LWAException provides additional details like errorCode and errorDescription to help fix the issue. + +*Example* +``` +catch (LWAException e) + { + Console.WriteLine("LWA Exception when calling Selling partner API"); + Console.WriteLine(e.getErrorCode()); + Console.WriteLine(e.getErrorMessage()); + Console.WriteLine(e.Message); + } +``` + +## Version +Selling Partner API Authentication/Authorization Library version 2.0. + ## Resources This package features Mustache templates designed for use with [swagger codegen](https://swagger.io/tools/swagger-codegen/). When you build Selling Partner API Swagger models with these templates, they help generate a rich SDK with functionality to invoke Selling Partner APIs built in. The templates are located in *resources/swagger-codegen*. diff --git a/clients/sellingpartner-api-aa-csharp/SellingPartnerAPIAuthAndAuthCSharp.sln b/clients/sellingpartner-api-aa-csharp/SellingPartnerAPIAuthAndAuthCSharp.sln index bdbc902..1eec521 100644 --- a/clients/sellingpartner-api-aa-csharp/SellingPartnerAPIAuthAndAuthCSharp.sln +++ b/clients/sellingpartner-api-aa-csharp/SellingPartnerAPIAuthAndAuthCSharp.sln @@ -1,23 +1,26 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Amazon.SellingPartnerAPIAA", "src\Amazon.SellingPartnerAPIAA\Amazon.SellingPartnerAPIAA.csproj", "{64339397-3AAB-49D3-8B50-7A467B16D545}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Amazon.SellingPartnerAPIAATests", "test\Amazon.SellingPartnerAPIAATests\Amazon.SellingPartnerAPIAATests.csproj", "{12B130EB-1087-4F88-BDFA-3088868C0A46}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {64339397-3AAB-49D3-8B50-7A467B16D545}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {64339397-3AAB-49D3-8B50-7A467B16D545}.Debug|Any CPU.Build.0 = Debug|Any CPU - {64339397-3AAB-49D3-8B50-7A467B16D545}.Release|Any CPU.ActiveCfg = Release|Any CPU - {64339397-3AAB-49D3-8B50-7A467B16D545}.Release|Any CPU.Build.0 = Release|Any CPU - {12B130EB-1087-4F88-BDFA-3088868C0A46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {12B130EB-1087-4F88-BDFA-3088868C0A46}.Debug|Any CPU.Build.0 = Debug|Any CPU - {12B130EB-1087-4F88-BDFA-3088868C0A46}.Release|Any CPU.ActiveCfg = Release|Any CPU - {12B130EB-1087-4F88-BDFA-3088868C0A46}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Amazon.SellingPartnerAPIAA", "src\Amazon.SellingPartnerAPIAA\Amazon.SellingPartnerAPIAA.csproj", "{64339397-3AAB-49D3-8B50-7A467B16D545}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Amazon.SellingPartnerAPIAATests", "test\Amazon.SellingPartnerAPIAATests\Amazon.SellingPartnerAPIAATests.csproj", "{12B130EB-1087-4F88-BDFA-3088868C0A46}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {64339397-3AAB-49D3-8B50-7A467B16D545}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {64339397-3AAB-49D3-8B50-7A467B16D545}.Debug|Any CPU.Build.0 = Debug|Any CPU + {64339397-3AAB-49D3-8B50-7A467B16D545}.Release|Any CPU.ActiveCfg = Release|Any CPU + {64339397-3AAB-49D3-8B50-7A467B16D545}.Release|Any CPU.Build.0 = Release|Any CPU + {12B130EB-1087-4F88-BDFA-3088868C0A46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {12B130EB-1087-4F88-BDFA-3088868C0A46}.Debug|Any CPU.Build.0 = Debug|Any CPU + {12B130EB-1087-4F88-BDFA-3088868C0A46}.Release|Any CPU.ActiveCfg = Release|Any CPU + {12B130EB-1087-4F88-BDFA-3088868C0A46}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + version = 2.0 + EndGlobalSection +EndGlobal diff --git a/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/AWSAuthenticationCredentials.cs b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/AWSAuthenticationCredentials.cs deleted file mode 100644 index b415738..0000000 --- a/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/AWSAuthenticationCredentials.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Amazon.SellingPartnerAPIAA -{ - public class AWSAuthenticationCredentials - { - /** - * AWS IAM User Access Key Id - */ - public string AccessKeyId { get; set; } - - /** - * AWS IAM User Secret Key - */ - public string SecretKey { get; set; } - - /** - * AWS Region - */ - public string Region { get; set; } - } -} diff --git a/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/AWSSigV4Signer.cs b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/AWSSigV4Signer.cs deleted file mode 100644 index 3937efa..0000000 --- a/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/AWSSigV4Signer.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System; -using System.Text; -using RestSharp; - -namespace Amazon.SellingPartnerAPIAA -{ - public class AWSSigV4Signer - - { - public virtual AWSSignerHelper AwsSignerHelper { get; set; } - private AWSAuthenticationCredentials awsCredentials; - - /// - /// Constructor for AWSSigV4Signer - /// - /// AWS Developer Account Credentials - public AWSSigV4Signer(AWSAuthenticationCredentials awsAuthenticationCredentials) - { - awsCredentials = awsAuthenticationCredentials; - AwsSignerHelper = new AWSSignerHelper(); - } - - /// - /// Signs a Request with AWS Signature Version 4 - /// - /// RestRequest which needs to be signed - /// Request endpoint - /// RestRequest with AWS Signature - public IRestRequest Sign(IRestRequest request, string host) - { - DateTime signingDate = AwsSignerHelper.InitializeHeaders(request, host); - string signedHeaders = AwsSignerHelper.ExtractSignedHeaders(request); - - string hashedCanonicalRequest = CreateCanonicalRequest(request, signedHeaders); - - string stringToSign = AwsSignerHelper.BuildStringToSign(signingDate, - hashedCanonicalRequest, - awsCredentials.Region); - - string signature = AwsSignerHelper.CalculateSignature(stringToSign, - signingDate, - awsCredentials.SecretKey, - awsCredentials.Region); - - AwsSignerHelper.AddSignature(request, - awsCredentials.AccessKeyId, - signedHeaders, - signature, - awsCredentials.Region, - signingDate); - - return request; - } - - private string CreateCanonicalRequest(IRestRequest restRequest, string signedHeaders) - { - var canonicalizedRequest = new StringBuilder(); - //Request Method - canonicalizedRequest.AppendFormat("{0}\n", restRequest.Method); - - //CanonicalURI - canonicalizedRequest.AppendFormat("{0}\n", AwsSignerHelper.ExtractCanonicalURIParameters(restRequest)); - - //CanonicalQueryString - canonicalizedRequest.AppendFormat("{0}\n", AwsSignerHelper.ExtractCanonicalQueryString(restRequest)); - - //CanonicalHeaders - canonicalizedRequest.AppendFormat("{0}\n", AwsSignerHelper.ExtractCanonicalHeaders(restRequest)); - - //SignedHeaders - canonicalizedRequest.AppendFormat("{0}\n", signedHeaders); - - // Hash(digest) the payload in the body - canonicalizedRequest.AppendFormat(AwsSignerHelper.HashRequestBody(restRequest)); - - string canonicalRequest = canonicalizedRequest.ToString(); - - //Create a digest(hash) of the canonical request - return Utils.ToHex(Utils.Hash(canonicalRequest)); - } - } -} \ No newline at end of file diff --git a/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/AWSSignerHelper.cs b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/AWSSignerHelper.cs deleted file mode 100644 index 6cd10d4..0000000 --- a/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/AWSSignerHelper.cs +++ /dev/null @@ -1,263 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using RestSharp; -using System.Text.RegularExpressions; -using System.Globalization; - -namespace Amazon.SellingPartnerAPIAA -{ - public class AWSSignerHelper - { - public const string ISO8601BasicDateTimeFormat = "yyyyMMddTHHmmssZ"; - public const string ISO8601BasicDateFormat = "yyyyMMdd"; - - public const string XAmzDateHeaderName = "X-Amz-Date"; - public const string AuthorizationHeaderName = "Authorization"; - public const string CredentialSubHeaderName = "Credential"; - public const string SignatureSubHeaderName = "Signature"; - public const string SignedHeadersSubHeaderName = "SignedHeaders"; - public const string HostHeaderName = "host"; - - public const string Scheme = "AWS4"; - public const string Algorithm = "HMAC-SHA256"; - public const string TerminationString = "aws4_request"; - public const string ServiceName = "execute-api"; - public const string Slash = "/"; - - private readonly static Regex CompressWhitespaceRegex = new Regex("\\s+"); - - public virtual IDateHelper DateHelper { get; set; } - - public AWSSignerHelper() - { - DateHelper = new SigningDateHelper(); - } - - /// - /// Returns URI encoded version of absolute path - /// - /// RestRequest - /// URI encoded version of absolute path - public virtual string ExtractCanonicalURIParameters(IRestRequest request) - { - string resource = request.Resource; - string canonicalUri = string.Empty; - - if (string.IsNullOrEmpty(resource)) - { - canonicalUri = Slash; - } - else - { - if (!resource.StartsWith(Slash)) - { - canonicalUri = Slash; - } - IDictionary pathParameters = request.Parameters - .Where(parameter => ParameterType.UrlSegment.Equals(parameter.Type)) - .ToDictionary(parameter => parameter.Name.Trim().ToString(), parameter => parameter.Value.ToString()); - - // Replace path parameter with actual value. - // Ex: /products/pricing/v0/items/{Asin}/offers -> /products/pricing/v0/items/AB12CD3E4Z/offers - foreach (string parameter in pathParameters.Keys) - { - resource = resource.Replace("{" + parameter + "}", pathParameters[parameter]); - } - - //Split path at / into segments - IEnumerable encodedSegments = resource.Split(new char[] { '/' }, StringSplitOptions.None); - - // Encode twice - encodedSegments = encodedSegments.Select(segment => Utils.UrlEncode(segment)); - encodedSegments = encodedSegments.Select(segment => Utils.UrlEncode(segment)); - - canonicalUri += string.Join(Slash, encodedSegments.ToArray()); - } - - return canonicalUri; - } - - /// - /// Returns query parameters in canonical order with URL encoding - /// - /// RestRequest - /// Query parameters in canonical order with URL encoding - public virtual string ExtractCanonicalQueryString(IRestRequest request) - { - IDictionary queryParameters = request.Parameters - .Where(parameter => ParameterType.QueryString.Equals(parameter.Type)) - .ToDictionary(parameter => parameter.Name.Trim().ToString(), parameter => parameter.Value.ToString()); - - SortedDictionary sortedQueryParameters = new SortedDictionary(queryParameters); - - StringBuilder canonicalQueryString = new StringBuilder(); - foreach (var key in sortedQueryParameters.Keys) - { - if (canonicalQueryString.Length > 0) - { - canonicalQueryString.Append("&"); - } - canonicalQueryString.AppendFormat("{0}={1}", - Utils.UrlEncode(key), - Utils.UrlEncode(sortedQueryParameters[key])); - } - - return canonicalQueryString.ToString(); - } - - /// - /// Returns Http headers in canonical order with all header names to lowercase - /// - /// RestRequest - /// Returns Http headers in canonical order - public virtual string ExtractCanonicalHeaders(IRestRequest request) - { - IDictionary headers = request.Parameters - .Where(parameter => ParameterType.HttpHeader.Equals(parameter.Type)) - .ToDictionary(header => header.Name.Trim().ToLowerInvariant(), header => header.Value.ToString()); - - SortedDictionary sortedHeaders = new SortedDictionary(headers); - - StringBuilder headerString = new StringBuilder(); - - foreach (string headerName in sortedHeaders.Keys) - { - headerString.AppendFormat("{0}:{1}\n", - headerName, - CompressWhitespaceRegex.Replace(sortedHeaders[headerName].Trim(), " ")); - } - - return headerString.ToString(); - } - - /// - /// Returns list(as string) of Http headers in canonical order - /// - /// RestRequest - /// List of Http headers in canonical order - public virtual string ExtractSignedHeaders(IRestRequest request) - { - List rawHeaders = request.Parameters.Where(parameter => ParameterType.HttpHeader.Equals(parameter.Type)) - .Select(header => header.Name.Trim().ToLowerInvariant()) - .ToList(); - rawHeaders.Sort(StringComparer.OrdinalIgnoreCase); - - return string.Join(";", rawHeaders); - } - - /// - /// Returns hexadecimal hashed value(using SHA256) of payload in the body of request - /// - /// RestRequest - /// Hexadecimal hashed value of payload in the body of request - public virtual string HashRequestBody(IRestRequest request) - { - Parameter body = request.Parameters.FirstOrDefault(parameter => ParameterType.RequestBody.Equals(parameter.Type)); - string value = body != null ? body.Value.ToString() : string.Empty; - return Utils.ToHex(Utils.Hash(value)); - } - - /// - /// Builds the string for signing using signing date, hashed canonical request and region - /// - /// Signing Date - /// Hashed Canonical Request - /// Region - /// String to be used for signing - public virtual string BuildStringToSign(DateTime signingDate, string hashedCanonicalRequest, string region) - { - string scope = BuildScope(signingDate, region); - string stringToSign = string.Format(CultureInfo.InvariantCulture, "{0}-{1}\n{2}\n{3}\n{4}", - Scheme, - Algorithm, - signingDate.ToString(ISO8601BasicDateTimeFormat, CultureInfo.InvariantCulture), - scope, - hashedCanonicalRequest); - - return stringToSign; - } - - /// - /// Sets AWS4 mandated 'x-amz-date' header, returning the date/time that will - /// be used throughout the signing process. - /// - /// RestRequest - /// Request endpoint - /// Date and time used for x-amz-date, in UTC - public virtual DateTime InitializeHeaders(IRestRequest restRequest, string host) - { - restRequest.Parameters.RemoveAll(parameter => ParameterType.HttpHeader.Equals(parameter.Type) - && parameter.Name == XAmzDateHeaderName); - restRequest.Parameters.RemoveAll(parameter => ParameterType.HttpHeader.Equals(parameter.Type) - && parameter.Name == HostHeaderName); - - DateTime signingDate = DateHelper.GetUtcNow(); - - restRequest.AddHeader(XAmzDateHeaderName, signingDate.ToString(ISO8601BasicDateTimeFormat, CultureInfo.InvariantCulture)); - restRequest.AddHeader(HostHeaderName, host); - - return signingDate; - } - - /// - /// Calculates AWS4 signature for the string, prepared for signing - /// - /// String to be signed - /// Signing Date - /// Secret Key - /// Region - /// AWS4 Signature - public virtual string CalculateSignature(string stringToSign, - DateTime signingDate, - string secretKey, - string region) - { - string date = signingDate.ToString(ISO8601BasicDateFormat, CultureInfo.InvariantCulture); - byte[] kSecret = Encoding.UTF8.GetBytes(Scheme + secretKey); - byte[] kDate = Utils.GetKeyedHash(kSecret, date); - byte[] kRegion = Utils.GetKeyedHash(kDate, region); - byte[] kService = Utils.GetKeyedHash(kRegion, ServiceName); - byte[] kSigning = Utils.GetKeyedHash(kService, TerminationString); - - // Calculate the signature - return Utils.ToHex(Utils.GetKeyedHash(kSigning, stringToSign)); - } - - /// - /// Add a signature to a request in the form of an 'Authorization' header - /// - /// Request to be signed - /// Access Key Id - /// Signed Headers - /// The signature to add - /// AWS region for the request - /// Signature date - public virtual void AddSignature(IRestRequest restRequest, - string accessKeyId, - string signedHeaders, - string signature, - string region, - DateTime signingDate) - { - string scope = BuildScope(signingDate, region); - StringBuilder authorizationHeaderValueBuilder = new StringBuilder(); - authorizationHeaderValueBuilder.AppendFormat("{0}-{1}", Scheme, Algorithm); - authorizationHeaderValueBuilder.AppendFormat(" {0}={1}/{2},", CredentialSubHeaderName, accessKeyId, scope); - authorizationHeaderValueBuilder.AppendFormat(" {0}={1},", SignedHeadersSubHeaderName, signedHeaders); - authorizationHeaderValueBuilder.AppendFormat(" {0}={1}", SignatureSubHeaderName, signature); - - restRequest.AddHeader(AuthorizationHeaderName, authorizationHeaderValueBuilder.ToString()); - } - - private static string BuildScope(DateTime signingDate, string region) - { - return string.Format("{0}/{1}/{2}/{3}", - signingDate.ToString(ISO8601BasicDateFormat, CultureInfo.InvariantCulture), - region, - ServiceName, - TerminationString); - } - } -} diff --git a/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/Amazon.SellingPartnerAPIAA.csproj b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/Amazon.SellingPartnerAPIAA.csproj index ebeccfd..cf23bc7 100644 --- a/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/Amazon.SellingPartnerAPIAA.csproj +++ b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/Amazon.SellingPartnerAPIAA.csproj @@ -1,16 +1,20 @@ - - - - netstandard2.0 - - - - - - - - - - - - + + + + net461 + 2.0 + 2.0 + 2.0 + 2.0 + + + + + + + + + + + + diff --git a/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/LWAClient.cs b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/LWAClient.cs index dfcd4d4..3ea63ff 100644 --- a/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/LWAClient.cs +++ b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/LWAClient.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -9,6 +10,8 @@ namespace Amazon.SellingPartnerAPIAA public class LWAClient { public const string AccessTokenKey = "access_token"; + public const string ErrorCodeKey = "error"; + public const string ErrorDescKey = "error_description"; public const string JsonMediaType = "application/json; charset=utf-8"; public IRestClient RestClient { get; set; } @@ -38,19 +41,34 @@ namespace Amazon.SellingPartnerAPIAA accessTokenRequest.AddParameter(JsonMediaType, jsonRequestBody, ParameterType.RequestBody); string accessToken; + + LWAExceptionErrorCode errorCode; + try { var response = RestClient.Execute(accessTokenRequest); + JObject responseJson = !String.IsNullOrEmpty(response.Content) ? JObject.Parse(response.Content) : null; if (!IsSuccessful(response)) { - throw new IOException("Unsuccessful LWA token exchange", response.ErrorException); + if (responseJson != null) + { + // If error code is present check error code if its one of the defined LWAExceptionErrorCode values + var parsedErrorCode = responseJson.ContainsKey(ErrorCodeKey) ? Enum.TryParse(responseJson.GetValue(ErrorCodeKey).ToString(), out errorCode) : false; + + if (parsedErrorCode) // throw error code and description if matches defined values + { + throw new LWAException(responseJson.GetValue(ErrorCodeKey).ToString(), responseJson.GetValue(ErrorDescKey).ToString(), "Error getting LWA Access Token"); + } + } //else throw general LWA exception + throw new LWAException(LWAExceptionErrorCode.other.ToString(), "Other LWA Exception", "Error getting LWA Access Token"); } - - JObject responseJson = JObject.Parse(response.Content); - accessToken = responseJson.GetValue(AccessTokenKey).ToString(); } + catch (LWAException e) + { + throw new LWAException(e.getErrorCode(), e.getErrorMessage(), e.Message); + } catch (Exception e) { throw new SystemException("Error getting LWA Access Token", e); diff --git a/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/LWAException.cs b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/LWAException.cs new file mode 100644 index 0000000..e2d8edf --- /dev/null +++ b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/LWAException.cs @@ -0,0 +1,46 @@ +using System; +namespace Amazon.SellingPartnerAPIAA +{ + public class LWAException:Exception + { + private String errorMessage; + + private String errorCode; + + public LWAException():base() + { + + } + + public LWAException(String errorCode, String errorMessage) + { + this.errorCode = errorCode; + this.errorMessage = errorMessage; + } + + public LWAException(String errorCode, String errorMessage, String message): base(message) + { + this.errorCode = errorCode; + this.errorMessage = errorMessage; + } + + public LWAException(String errorCode, String errorMessage, String message, Exception innerException) : base(message,innerException) + { + this.errorCode = errorCode; + this.errorMessage = errorMessage; + } + + + public String getErrorCode() + { + return this.errorCode; + } + + public String getErrorMessage() + { + return this.errorMessage; + } + + } +} + diff --git a/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/LWAExceptionErrorCode.cs b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/LWAExceptionErrorCode.cs new file mode 100644 index 0000000..eed8d0f --- /dev/null +++ b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/LWAExceptionErrorCode.cs @@ -0,0 +1,17 @@ +using System; +namespace Amazon.SellingPartnerAPIAA +{ + public enum LWAExceptionErrorCode + { + access_denied, + invalid_grant, + invalid_request, + invalid_scope, + server_error, + temporarily_unavailable, + unauthorized_client, + invalid_client, + other + } +} + diff --git a/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/resources/swagger-codegen/templates/ApiClient.mustache b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/resources/swagger-codegen/templates/ApiClient.mustache index faad469..52a4e56 100644 --- a/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/resources/swagger-codegen/templates/ApiClient.mustache +++ b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/resources/swagger-codegen/templates/ApiClient.mustache @@ -33,7 +33,6 @@ namespace {{packageName}}.Client {{>visibility}} partial class ApiClient { private LWAAuthorizationSigner lwaAuthorizationSigner; - private AWSSigV4Signer awsSigV4Signer; private RateLimitConfiguration rateLimitConfig; private TimeLimiter rateLimiter; @@ -46,10 +45,10 @@ namespace {{packageName}}.Client /// Allows for extending request processing for generated code. /// /// The RestSharp request object + /// Thrown when there is an error during LWA Authorization private void InterceptRequest(IRestRequest request) { lwaAuthorizationSigner.Sign(request); - awsSigV4Signer.Sign(request, RestClient.BaseUrl.Host); } /// @@ -74,7 +73,6 @@ namespace {{packageName}}.Client {{/netStandard}} lwaAuthorizationSigner = new LWAAuthorizationSigner(Configuration.AuthorizationCredentials); - awsSigV4Signer = new AWSSigV4Signer(Configuration.AuthenticationCredentials); rateLimitConfig = Configuration.RateLimitConfig; if(rateLimitConfig != null) { diff --git a/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/resources/swagger-codegen/templates/Configuration.mustache b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/resources/swagger-codegen/templates/Configuration.mustache index 247498a..3038689 100644 --- a/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/resources/swagger-codegen/templates/Configuration.mustache +++ b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/resources/swagger-codegen/templates/Configuration.mustache @@ -268,12 +268,6 @@ namespace {{packageName}}.Client /// /// The LWAAuthorizationCredentials public virtual LWAAuthorizationCredentials AuthorizationCredentials { get; set; } - - /// - /// Gets or sets the AWSAuthenticationCredentials for Amazon Selling Partner API Authentication - /// - /// The AWSAuthenticationCredentials - public virtual AWSAuthenticationCredentials AuthenticationCredentials { get; set; } /// /// Gets or sets the RateLimitConfiguration for Amazon Selling Partner API Authentication diff --git a/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/resources/swagger-codegen/templates/IReadableConfiguration.mustache b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/resources/swagger-codegen/templates/IReadableConfiguration.mustache index aa890fc..cae6955 100644 --- a/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/resources/swagger-codegen/templates/IReadableConfiguration.mustache +++ b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/resources/swagger-codegen/templates/IReadableConfiguration.mustache @@ -88,12 +88,6 @@ namespace {{packageName}}.Client /// /// AuthorizationCredentials LWAAuthorizationCredentials AuthorizationCredentials { get; } - - /// - /// Gets the AWSAuthenticationCredentials for Amazon Selling Partner API Authentication - /// - /// AuthenticationCredentials - AWSAuthenticationCredentials AuthenticationCredentials { get; } /// /// Gets the RateLimitConfigurationOnRequests for Amazon Selling Partner API RateLimit diff --git a/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/resources/swagger-codegen/templates/api.mustache b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/resources/swagger-codegen/templates/api.mustache index 32b375e..17644f2 100644 --- a/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/resources/swagger-codegen/templates/api.mustache +++ b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/resources/swagger-codegen/templates/api.mustache @@ -435,7 +435,6 @@ namespace {{packageName}}.{{apiPackage}} public class Builder { private LWAAuthorizationCredentials lwaAuthorizationCredentials; - private AWSAuthenticationCredentials awsAuthenticationCredentials; private RateLimitConfiguration rateLimitConfiguration; public Builder SetLWAAuthorizationCredentials(LWAAuthorizationCredentials lwaAuthorizationCredentials) @@ -443,12 +442,6 @@ namespace {{packageName}}.{{apiPackage}} this.lwaAuthorizationCredentials = lwaAuthorizationCredentials; return this; } - - public Builder SetAWSAuthenticationCredentials(AWSAuthenticationCredentials awsAuthenticationCredentials) - { - this.awsAuthenticationCredentials = awsAuthenticationCredentials; - return this; - } public Builder SetRateLimitConfiguration(RateLimitConfiguration rateLimitConfiguration) @@ -464,15 +457,10 @@ namespace {{packageName}}.{{apiPackage}} throw new NullReferenceException("LWAAuthoriztionCredentials not set"); } - if (awsAuthenticationCredentials == null) - { - throw new NullReferenceException("AWSAuthenticationCredentials not set"); - } {{packageName}}.Client.Configuration configuration = new {{packageName}}.Client.Configuration() { AuthorizationCredentials = lwaAuthorizationCredentials, - AuthenticationCredentials = awsAuthenticationCredentials, RateLimitConfig = rateLimitConfiguration }; diff --git a/clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/AWSSigV4SignerTest.cs b/clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/AWSSigV4SignerTest.cs deleted file mode 100644 index 20d6a3f..0000000 --- a/clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/AWSSigV4SignerTest.cs +++ /dev/null @@ -1,88 +0,0 @@ -using Moq; -using Xunit; -using RestSharp; -using Amazon.SellingPartnerAPIAA; -using System; - -namespace Amazon.SellingPartnerAPIAATests -{ - public class AWSSigV4SignerTest - { - private const string TestAccessKeyId = "aKey"; - private const string TestSecretKey = "sKey"; - private const string TestRegion = "us-east-1"; - private const string TestResourcePath = "iam/user"; - private const string TestHost = "sellingpartnerapi.amazon.com"; - - private RestRequest request; - private AWSSigV4Signer sigV4SignerUnderTest; - private Mock mockAWSSignerHelper; - - public AWSSigV4SignerTest() - { - request = new RestRequest(TestResourcePath, Method.GET); - - AWSAuthenticationCredentials authenticationCredentials = new AWSAuthenticationCredentials - { - AccessKeyId = TestAccessKeyId, - SecretKey = TestSecretKey, - Region = TestRegion - }; - mockAWSSignerHelper = new Mock(); - sigV4SignerUnderTest = new AWSSigV4Signer(authenticationCredentials); - sigV4SignerUnderTest.AwsSignerHelper = mockAWSSignerHelper.Object; - } - - [Fact] - public void TestSignRequest() - { - DateTime signingDate = DateTime.UtcNow; - string expectedHashedCanonicalRequest = "b7a5ea4c3179fcebed77f19ccd7d85795d4b7a1810709b55fa7ad3fd79ab6adc"; - string expectedSignedHeaders = "testSignedHeaders"; - string expectedSignature = "testSignature"; - string expectedStringToSign = "testStringToSign"; - mockAWSSignerHelper.Setup(signerHelper => signerHelper.InitializeHeaders(request, TestHost)) - .Returns(signingDate); - mockAWSSignerHelper.Setup(signerHelper => signerHelper.ExtractCanonicalURIParameters(request)) - .Returns("testURIParameters"); - mockAWSSignerHelper.Setup(signerHelper => signerHelper.ExtractCanonicalQueryString(request)) - .Returns("testCanonicalQueryString"); - mockAWSSignerHelper.Setup(signerHelper => signerHelper.ExtractCanonicalHeaders(request)) - .Returns("testCanonicalHeaders"); - mockAWSSignerHelper.Setup(signerHelper => signerHelper.ExtractSignedHeaders(request)) - .Returns(expectedSignedHeaders); - mockAWSSignerHelper.Setup(signerHelper => signerHelper.HashRequestBody(request)) - .Returns("testHashRequestBody"); - mockAWSSignerHelper.Setup(signerHelper => signerHelper.BuildStringToSign(signingDate, - expectedHashedCanonicalRequest, TestRegion)) - .Returns(expectedStringToSign); - mockAWSSignerHelper.Setup(signerHelper => signerHelper.CalculateSignature(expectedStringToSign, - signingDate, TestSecretKey, TestRegion)) - .Returns(expectedSignature); - - IRestRequest actualRestRequest = sigV4SignerUnderTest.Sign(request, TestHost); - - mockAWSSignerHelper.Verify(signerHelper => signerHelper.InitializeHeaders(request, TestHost)); - mockAWSSignerHelper.Verify(signerHelper => signerHelper.ExtractCanonicalURIParameters(request)); - mockAWSSignerHelper.Verify(signerHelper => signerHelper.ExtractCanonicalQueryString(request)); - mockAWSSignerHelper.Verify(signerHelper => signerHelper.ExtractCanonicalHeaders(request)); - mockAWSSignerHelper.Verify(signerHelper => signerHelper.ExtractSignedHeaders(request)); - mockAWSSignerHelper.Verify(signerHelper => signerHelper.HashRequestBody(request)); - mockAWSSignerHelper.Verify(signerHelper => signerHelper.BuildStringToSign(signingDate, - expectedHashedCanonicalRequest, - TestRegion)); - mockAWSSignerHelper.Verify(signerHelper => signerHelper.CalculateSignature(expectedStringToSign, - signingDate, - TestSecretKey, - TestRegion)); - mockAWSSignerHelper.Verify(signerHelper => signerHelper.AddSignature(request, - TestAccessKeyId, - expectedSignedHeaders, - expectedSignature, - TestRegion, - signingDate)); - - Assert.Equal(request, actualRestRequest); - } - } -} diff --git a/clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/AWSSignerHelperTest.cs b/clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/AWSSignerHelperTest.cs deleted file mode 100644 index 665eb3d..0000000 --- a/clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/AWSSignerHelperTest.cs +++ /dev/null @@ -1,291 +0,0 @@ -using System; -using Xunit; -using RestSharp; -using Amazon.SellingPartnerAPIAA; -using System.Text; -using Moq; - -namespace Amazon.SellingPartnerAPIAATests -{ - public class AWSSignerHelperTest - { - private const string Slash = "/"; - private const string ISOSigningDateTime = "20200504T121212Z"; - private const string ISOSigningDate = "20200504"; - private const string TestAccessKeyId = "aKey"; - private const string TestSecretKey = "sKey"; - private const string TestRegion = "us-east-1"; - private const string TestResourcePath = "iam/user"; - private const string TestHost = "sellingpartnerapi.amazon.com"; - private const string JsonMediaType = "application/json; charset=utf-8"; - - private static readonly DateTime SigningDate = DateTime.Parse("2020-05-04 12:12:12"); - - private AWSSignerHelper awsSignerHelperUnderTest; - - public AWSSignerHelperTest() - { - var mockDateHelper = new Mock(); - mockDateHelper.Setup(dateHelper => dateHelper.GetUtcNow()).Returns(SigningDate); - awsSignerHelperUnderTest = new AWSSignerHelper() { DateHelper = mockDateHelper.Object }; - } - - [Fact] - public void TestExtractCanonicalURIParameters() - { - IRestRequest request = new RestRequest(TestResourcePath, Method.GET); - string result = awsSignerHelperUnderTest.ExtractCanonicalURIParameters(request); - Assert.Equal(Slash + TestResourcePath, result); - } - - [Fact] - public void TestExtractCanonicalURIParameters_UrlSegments() - { - IRestRequest request = new RestRequest("products/pricing/v0/items/{Asin}/offers/{SellerSKU}", Method.GET); - request.AddUrlSegment("Asin", "AB12CD3E4Z"); - request.AddUrlSegment("SellerSKU", "1234567890"); - string result = awsSignerHelperUnderTest.ExtractCanonicalURIParameters(request); - Assert.Equal("/products/pricing/v0/items/AB12CD3E4Z/offers/1234567890", result); - } - - [Fact] - public void TestExtractCanonicalURIParameters_IncorrectUrlSegment() - { - IRestRequest request = new RestRequest("products/pricing/v0/items/{Asin}/offers", Method.GET); - request.AddUrlSegment("asin", "AB12CD3E4Z"); - string result = awsSignerHelperUnderTest.ExtractCanonicalURIParameters(request); - Assert.Equal("/products/pricing/v0/items/%257BAsin%257D/offers", result); - } - - [Fact] - public void TestExtractCanonicalURIParameters_ResourcePathWithSpace() - { - IRestRequest request = new RestRequest("iam/ user", Method.GET); - string result = awsSignerHelperUnderTest.ExtractCanonicalURIParameters(request); - Assert.Equal("/iam/%2520user", result); - } - - [Fact] - public void TestExtractCanonicalURIParameters_EmptyResourcePath() - { - IRestRequest request = new RestRequest(string.Empty, Method.GET); - string result = awsSignerHelperUnderTest.ExtractCanonicalURIParameters(request); - Assert.Equal(Slash, result); - } - - [Fact] - public void TestExtractCanonicalURIParameters_NullResourcePath() - { - string result = awsSignerHelperUnderTest.ExtractCanonicalURIParameters(new RestRequest()); - Assert.Equal(Slash, result); - } - - [Fact] - public void TestExtractCanonicalURIParameters_SlashPath() - { - IRestRequest request = new RestRequest(Slash, Method.GET); - string result = awsSignerHelperUnderTest.ExtractCanonicalURIParameters(request); - Assert.Equal(Slash, result); - } - - [Fact] - public void TestExtractCanonicalQueryString() - { - IRestRequest request = new RestRequest(); - request.AddQueryParameter("Version", "2010-05-08"); - request.AddQueryParameter("Action", "ListUsers"); - request.AddQueryParameter("RequestId", "1"); - - string result = awsSignerHelperUnderTest.ExtractCanonicalQueryString(request); - //Query parameters in canonical order - Assert.Equal("Action=ListUsers&RequestId=1&Version=2010-05-08", result); - } - - [Fact] - public void TestExtractCanonicalQueryString_EmptyQueryParameters() - { - string result = awsSignerHelperUnderTest.ExtractCanonicalQueryString(new RestRequest()); - Assert.Empty(result); - } - - [Fact] - public void TestExtractCanonicalQueryString_WithUrlEncoding() - { - IRestRequest request = new RestRequest(); - request.AddQueryParameter("Action^", "ListUsers$Roles"); - string result = awsSignerHelperUnderTest.ExtractCanonicalQueryString(request); - Assert.Equal("Action%5E=ListUsers%24Roles", result); - } - - [Fact] - public void TestExtractCanonicalHeaders() - { - IRestRequest request = new RestRequest(); - request.AddHeader("X-Amz-Date", "20150830T123600Z"); - request.AddHeader("Host", "iam.amazonaws.com"); - request.AddHeader("Content-Type", JsonMediaType); - - string result = awsSignerHelperUnderTest.ExtractCanonicalHeaders(request); - Assert.Equal("content-type:application/json; charset=utf-8\nhost:iam.amazonaws.com\n" + - "x-amz-date:20150830T123600Z\n", result); - } - - [Fact] - public void TestExtractCanonicalHeaders_NoHeader() - { - string result = awsSignerHelperUnderTest.ExtractCanonicalHeaders(new RestRequest()); - Assert.Empty(result); - } - - [Fact] - public void TestExtractSignedHeaders() - { - IRestRequest request = new RestRequest(); - request.AddHeader("X-Amz-Date", "20150830T123600Z"); - request.AddHeader("Host", "iam.amazonaws.com"); - request.AddHeader("Content-Type", JsonMediaType); - - string result = awsSignerHelperUnderTest.ExtractSignedHeaders(request); - Assert.Equal("content-type;host;x-amz-date", result); - } - - [Fact] - public void TestExtractSignedHeaders_NoHeader() - { - string result = awsSignerHelperUnderTest.ExtractSignedHeaders(new RestRequest()); - Assert.Empty(result); - } - - [Fact] - public void TestHashRequestBody() - { - IRestRequest request = new RestRequest(TestResourcePath, Method.POST); - request.AddJsonBody("{\"test\":\"payload\"}"); - - string result = awsSignerHelperUnderTest.HashRequestBody(request); - Assert.NotEmpty(result); - } - - [Fact] - public void TestHashRequestBody_NoBody() - { - string result = awsSignerHelperUnderTest.HashRequestBody(new RestRequest()); - Assert.NotEmpty(result); - } - - [Fact] - public void TestBuildStringToSign() - { - string expectedCanonicalHash = "foo"; - StringBuilder expectedStringBuilder = new StringBuilder(); - expectedStringBuilder.AppendLine("AWS4-HMAC-SHA256"); - expectedStringBuilder.AppendLine(ISOSigningDateTime); - expectedStringBuilder.AppendFormat("{0}/{1}/execute-api/aws4_request\n", ISOSigningDate, TestRegion); - expectedStringBuilder.Append(expectedCanonicalHash); - - string result = awsSignerHelperUnderTest.BuildStringToSign(SigningDate, expectedCanonicalHash, TestRegion); - - Assert.Equal(expectedStringBuilder.ToString(), result); - } - - [Fact] - public void TestInitializeHeadersReturnsUtcNow() - { - Assert.Equal(SigningDate, awsSignerHelperUnderTest.InitializeHeaders(new RestRequest(), TestHost)); - } - - [Fact] - public void TestInitializeHeadersSetsUtcNowXAmzDateHeader() - { - IRestRequest request = new RestRequest(); - awsSignerHelperUnderTest.InitializeHeaders(request, TestHost); - - Parameter actualParameter = request.Parameters.Find(parameter => - ParameterType.HttpHeader.Equals(parameter.Type) && parameter.Name == AWSSignerHelper.XAmzDateHeaderName); - - Assert.Equal(ISOSigningDateTime, actualParameter.Value); - } - - [Fact] - public void TestInitializeHeadersOverwritesXAmzDateHeader() - { - IRestRequest request = new RestRequest(); - request.AddHeader(AWSSignerHelper.XAmzDateHeaderName, "foobar"); - - awsSignerHelperUnderTest.InitializeHeaders(request, TestHost); - - Parameter actualParameter = request.Parameters.Find(parameter => - ParameterType.HttpHeader.Equals(parameter.Type) && parameter.Name == AWSSignerHelper.XAmzDateHeaderName); - - Assert.Equal(ISOSigningDateTime, actualParameter.Value); - } - - [Fact] - public void TestAddSignatureToRequest() - { - IRestRequest restRequest = new RestRequest(); - string expectedAccessKeyId = TestAccessKeyId; - string expectedRegion = TestRegion; - string expectedSignature = "testCalculatedSignature"; - string expectedSignedHeaders = "header1;header2"; - - string expectedAuthorizationHeaderValue = string.Format("AWS4-HMAC-SHA256 " + - "Credential={0}/{1}/{2}/execute-api/aws4_request, SignedHeaders={3}, Signature={4}", - expectedAccessKeyId, - ISOSigningDate, - expectedRegion, - expectedSignedHeaders, - expectedSignature); - - awsSignerHelperUnderTest.AddSignature(restRequest, - expectedAccessKeyId, - expectedSignedHeaders, - expectedSignature, - expectedRegion, - SigningDate); - - Parameter actualParameter = restRequest.Parameters.Find(parameter => - ParameterType.HttpHeader.Equals(parameter.Type) && parameter.Name == AWSSignerHelper.AuthorizationHeaderName); - - Assert.Equal(expectedAuthorizationHeaderValue, actualParameter.Value); - } - - [Fact] - public void TestCalculateSignature() - { - string signature = awsSignerHelperUnderTest.CalculateSignature("testString", - SigningDate, - TestSecretKey, - TestRegion); - Assert.Equal("7e2c7c2e330123ef7468b41d8ddaf3841e6ef56959b9116b44ded5466cf96405", signature); - } - - [Fact] - public void TestInitializeHeadersSetsHostHeader() - { - IRestRequest restRequest = new RestRequest(); - - awsSignerHelperUnderTest.InitializeHeaders(restRequest, TestHost); - - Parameter actualParamter = restRequest.Parameters.Find(parameter => - ParameterType.HttpHeader.Equals(parameter.Type) && parameter.Name == AWSSignerHelper.HostHeaderName); - - Assert.Equal(TestHost, actualParamter.Value); - } - - [Fact] - public void TestInitializeHeadersOverwritesHostHeader() - { - IRestRequest restRequest = new RestRequest(); - - restRequest.AddHeader(AWSSignerHelper.HostHeaderName, "foobar"); - - awsSignerHelperUnderTest.InitializeHeaders(restRequest, TestHost); - - Parameter actualParamter = restRequest.Parameters.Find(parameter => - ParameterType.HttpHeader.Equals(parameter.Type) && parameter.Name == AWSSignerHelper.HostHeaderName); - - Assert.Equal(TestHost, actualParamter.Value); - } - } -} diff --git a/clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/Amazon.SellingPartnerAPIAATests.csproj b/clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/Amazon.SellingPartnerAPIAATests.csproj index 4a0bd06..5036ffd 100644 --- a/clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/Amazon.SellingPartnerAPIAATests.csproj +++ b/clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/Amazon.SellingPartnerAPIAATests.csproj @@ -1,20 +1,21 @@ - - - - netcoreapp3.1 - - false - - - - - - - - - - - - - - + + + + netcoreapp3.1 + + false + 2.0 + + + + + + + + + + + + + + diff --git a/clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/LWAClientTest.cs b/clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/LWAClientTest.cs index cf4fbc2..230475d 100644 --- a/clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/LWAClientTest.cs +++ b/clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/LWAClientTest.cs @@ -15,6 +15,9 @@ namespace Amazon.SellingPartnerAPIAATests private const string TestClientSecret = "cSecret"; private const string TestClientId = "cId"; private const string TestRefreshGrantType = "rToken"; + private const string LWAExceptionMessage = "Error getting LWA Access Token"; + private const string LWAClientAuthMessage = "Client authentication failed"; + private const string LWAAccessDeniedMessage = "The customer or authorization server denied the request"; private Mock mockRestClient; private Mock mockLWAAccessTokenRequestMetaBuilder; @@ -101,7 +104,7 @@ namespace Amazon.SellingPartnerAPIAATests } [Fact] - public void UnsuccessfulPostThrowsException() + public void UnsuccessfulPostMissingContentThrowsOtherException() { IRestResponse response = new RestResponse { @@ -119,8 +122,81 @@ namespace Amazon.SellingPartnerAPIAATests LWAClient lwaClientUnderTest = new LWAClient(LWAAuthorizationCredentials); lwaClientUnderTest.RestClient = mockRestClient.Object; - SystemException systemException = Assert.Throws(() => lwaClientUnderTest.GetAccessToken()); - Assert.IsType(systemException.GetBaseException()); + LWAException lwaException = Assert.Throws(() => lwaClientUnderTest.GetAccessToken()); + Assert.Equal(LWAExceptionErrorCode.other.ToString(), lwaException.getErrorCode()); + Assert.Equal(LWAExceptionMessage, lwaException.Message); + } + + [Fact] + public void UnsuccessfulPostThrowsInvalidClientException() + { + IRestResponse response = new RestResponse + { + StatusCode = HttpStatusCode.BadRequest, + ResponseStatus = ResponseStatus.Completed, + Content = @"{error:'invalid_client', error_description:'Client authentication failed'}" + }; + + IRestRequest request = new RestRequest(); + + mockRestClient.Setup(client => client.Execute(It.IsAny())) + .Callback((IRestRequest req) => { request = req; }) + .Returns(response); + + LWAClient lwaClientUnderTest = new LWAClient(LWAAuthorizationCredentials); + lwaClientUnderTest.RestClient = mockRestClient.Object; + + LWAException lwaException = Assert.Throws(() => lwaClientUnderTest.GetAccessToken()); + Assert.Equal(LWAExceptionErrorCode.invalid_client.ToString(), lwaException.getErrorCode()); + Assert.Equal(LWAClientAuthMessage, lwaException.getErrorMessage()); + } + + [Fact] + public void UnsuccessfulPostThrowsAccessDeniedException() + { + IRestResponse response = new RestResponse + { + StatusCode = HttpStatusCode.BadRequest, + ResponseStatus = ResponseStatus.Completed, + Content = @"{error:'access_denied', error_description:'The customer or authorization server denied the request'}" + }; + + IRestRequest request = new RestRequest(); + + mockRestClient.Setup(client => client.Execute(It.IsAny())) + .Callback((IRestRequest req) => { request = req; }) + .Returns(response); + + LWAClient lwaClientUnderTest = new LWAClient(LWAAuthorizationCredentials); + lwaClientUnderTest.RestClient = mockRestClient.Object; + + LWAException lwaException = Assert.Throws(() => lwaClientUnderTest.GetAccessToken()); + Assert.Equal(LWAExceptionErrorCode.access_denied.ToString(), lwaException.getErrorCode()); + Assert.Equal(LWAAccessDeniedMessage, lwaException.getErrorMessage()); + } + + [Fact] + public void UnsuccessfulPostThrowsOtherException() + { + IRestResponse response = new RestResponse + { + StatusCode = HttpStatusCode.BadRequest, + ResponseStatus = ResponseStatus.Completed, + Content = @"{error:'invalid_token'}" + }; + + IRestRequest request = new RestRequest(); + + mockRestClient.Setup(client => client.Execute(It.IsAny())) + .Callback((IRestRequest req) => { request = req; }) + .Returns(response); + + LWAClient lwaClientUnderTest = new LWAClient(LWAAuthorizationCredentials); + lwaClientUnderTest.RestClient = mockRestClient.Object; + + LWAException lwaException = Assert.Throws(() => lwaClientUnderTest.GetAccessToken()); + Assert.Equal(LWAExceptionErrorCode.other.ToString(), lwaException.getErrorCode()); + Assert.Equal(LWAExceptionMessage, lwaException.Message); } [Fact]