/* * 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; using Amazon.Runtime.Internal; using Amazon.Runtime.Internal.Util; using Amazon.S3Control.Model; using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Text; using System.Xml; namespace Amazon.S3Control.Internal { /// /// Shared helper methods for S3 ARNs /// public static class S3ArnUtils { private static readonly char[] separators = new char[] { '/', '?' }; /// /// An array of the characters which split sections of an ARN /// public static readonly char[] ArnSplit = new char[] { '/', ':' }; /// /// Service name of S3 outposts /// public const string S3OutpostsService = "s3-outposts"; /// /// Service name of S3 /// public const string S3Service = "s3"; /// /// If the ARN contains an account ID and the request does not, then return it. /// Throw an exception if there is a mismatch, or if neither the ARN or the /// request contain an account Id. /// /// The account ID in the request /// The account ID in the ARN /// A string Account ID internal static string GetAccountIdBasedOnArn(string requestAccountId, string arnAccountId) { if (!string.IsNullOrEmpty(arnAccountId) && !string.IsNullOrEmpty(requestAccountId) && !string.Equals(arnAccountId, requestAccountId)) { throw new AmazonClientException("Account ID mismatch, the Account ID cannot be specified in an ARN and in the accountId field"); } if (string.IsNullOrEmpty(arnAccountId) && string.IsNullOrEmpty(requestAccountId)) { throw new AmazonClientException("Invalid ARN, Account ID not set"); } if (string.IsNullOrEmpty(requestAccountId) && !(arnAccountId.Length == 12 && arnAccountId.ToCharArray().All(x => char.IsDigit(x)))) { throw new AmazonAccountIdException(); } return !string.IsNullOrEmpty(requestAccountId) ? requestAccountId : arnAccountId; } /// /// Get the endpoint for an outpost request which doesn't use /// an ARN. /// /// The client config object for this request /// The Uri Endpoint for the outpost request internal static Uri GetNonStandardOutpostIdEndpoint(IClientConfig config) { return new UriBuilder($"{(config.UseHttp ? "http" : "https")}" + $"://s3-outposts.{config.RegionEndpoint.SystemName}.{config.RegionEndpoint.PartitionDnsSuffix}").Uri; } /// /// Check if the request resource is an outpost resource. /// /// The S3 request object /// A boolean value of whether or not the resource path contains an outpost resource internal static bool ResourcePathContainsOutpostsResource(IRequest request) { Arn arn; return RequestContainsArn(request, out arn) && arn.IsOutpostArn(); } /// /// Check the outpost id against certain criteria. /// /// The outpost id to be checked /// A boolean whether or not the string is an outpost Id public static bool IsValidOutpostId(string outpostId) { return outpostId.Length <= 63 && outpostId.Length >= 1 && outpostId.All(x => char.IsLetterOrDigit(x) || x == '-'); } /// /// Check the many places that the ARN could be in an S3 Control request /// for an ARN. /// /// /// The request being sent to S3Control /// Any ARN found in the request /// A boolean of whether or not the request contains an ARN internal static bool RequestContainsArn(IRequest request, out Arn arn) { var createAccessPointRequest = request.OriginalRequest as CreateAccessPointRequest; if (createAccessPointRequest != null && createAccessPointRequest.IsSetBucket() && Arn.TryParse(createAccessPointRequest.Bucket.Trim().Trim(separators), out arn)) { return true; } return DictionaryContainsArn(request.PathResources, out arn) || DictionaryContainsArn(request.Parameters, out arn); } /// /// Check if the value of any dictionary item /// is of type ARN. /// Note: this assumes that there is just 0-1 ARN in the path resource dictionary /// /// The dictionary /// ARN found in dictionary /// A boolean whether or not the dictionary contains an ARN in the values internal static bool DictionaryContainsArn(IDictionary dictValues, out Arn arn) { Arn outArn = null; var matchingKvp = dictValues.FirstOrDefault(kvp => Arn.TryParse(kvp.Value, out outArn)); arn = matchingKvp.Value != null ? outArn : null; return arn != null; } /// /// Replace the ARNs in the dictionary /// with the extracted bucket or access point name. /// Note: this assumes that there is just 0-1 ARN in the path resource dictionary /// /// The dictionary of values /// bucket or access point name public static void ReplacePathResourceArns(IDictionary dictValues, string bucketOrAccessPointName) { var matchingKvp = dictValues.FirstOrDefault(kvp => Arn.TryParse(kvp.Value, out _)); if (!matchingKvp.Equals(default(KeyValuePair))) { dictValues[matchingKvp.Key] = bucketOrAccessPointName; } } /// /// Need to hardcode special cases where the outpost ID can be set /// but there is no ARN in the request. /// /// An AmazonWebServiceRequest object /// The outpost id found in the request /// A boolean of whether or not the request contains a non-null outpost ID public static bool DoesRequestHaveOutpostId(AmazonWebServiceRequest request, out string outpostId) { var createBucketRequest = request as CreateBucketRequest; var listRegionalBucketsRequest = request as ListRegionalBucketsRequest; var createAccessPointRequest = request as CreateAccessPointRequest; outpostId = createBucketRequest?.OutpostId ?? listRegionalBucketsRequest?.OutpostId ?? createAccessPointRequest?.OutpostId; return outpostId != null; } } }