/// <summary> /// Helper method for AuthorizeS3ToPublishAsync() /// </summary> /// <param name="attributes"></param> /// <param name="topicArn"></param> /// <param name="bucket"></param> /// <param name="policy"></param> /// <param name="statement"></param> private static void GetNewPolicyAndStatementForTopicAttributes(Dictionary <string, string> attributes, string topicArn, string bucket, out Policy policy, out Statement statement) { if (attributes.ContainsKey("Policy") && !string.IsNullOrEmpty(attributes["Policy"])) { policy = Policy.FromJson(attributes["Policy"]); } else { policy = new Policy(); } var sourceArn = string.Format(CultureInfo.InvariantCulture, "arn:aws:s3:*:*:{0}", bucket); statement = new Statement(Statement.StatementEffect.Allow); statement.Actions.Add(SNSActionIdentifiers.Publish); statement.Resources.Add(new Resource(topicArn)); statement.Principals.Add(new Principal("*")); statement.Conditions.Add(ConditionFactory.NewSourceArnCondition(sourceArn)); // If the arn doesn't have the required tokens then it is most likely be called from a mock or fake AWS service. // Since this is an existing method we don't want to introduce a new exception. So if there is no account id then // don't add the extra condition. if (Arn.TryParse(topicArn, out Arn arn) && !string.IsNullOrEmpty(arn.AccountId)) { statement.Conditions.Add(ConditionFactory.NewCondition(ConditionFactory.StringComparisonType.StringEquals, ConditionFactory.SOURCE_ACCOUNT_KEY, arn.AccountId)); } }
private static void ProcessPreRequestHandlers(IExecutionContext executionContext) { var request = executionContext.RequestContext.OriginalRequest; // For CreateAccessPoint, the bucket needs to be extracted from the Arn before // it is added to the request payload. Setting outpost id so that the header isn't // lost in the case of an outpost request var createAccessPointRequest = request as CreateAccessPointRequest; Arn arn; if (createAccessPointRequest != null && createAccessPointRequest.IsSetBucket() && Arn.TryParse(createAccessPointRequest.Bucket, out arn)) { IS3Resource s3Resource = null; if (arn.IsOutpostArn()) { s3Resource = arn.ParseOutpost(); } if (s3Resource != null) { createAccessPointRequest.OutpostId = ((S3OutpostResource)s3Resource).OutpostId; createAccessPointRequest.Bucket = s3Resource.Name; createAccessPointRequest.AccountId = createAccessPointRequest.AccountId ?? arn.AccountId; } } }
/// <summary> /// Check if the value of any dictionary item /// is of type ARN. /// <b>Note:</b> this assumes that there is just 0-1 ARN in the path resource dictionary /// </summary> /// <param name="dictValues">The dictionary</param> /// <param name="arn">ARN found in dictionary</param> /// <returns>A boolean whether or not the dictionary contains an ARN in the values</returns> internal static bool DictionaryContainsArn(IDictionary <string, string> 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); }
/// <summary> /// Replace the ARNs in the dictionary /// with the extracted bucket or access point name. /// <b>Note:</b> this assumes that there is just 0-1 ARN in the path resource dictionary /// </summary> /// <param name="dictValues">The dictionary of values</param> /// <param name="bucketOrAccessPointName">bucket or access point name</param> public static void ReplacePathResourceArns(IDictionary <string, string> dictValues, string bucketOrAccessPointName) { var matchingKvp = dictValues.FirstOrDefault(kvp => Arn.TryParse(kvp.Value, out _)); if (!matchingKvp.Equals(default(KeyValuePair <string, string>))) { dictValues[matchingKvp.Key] = bucketOrAccessPointName; } }
private static bool IsS3AccessPointsArn(string bucket) { Arn arn; if (Arn.TryParse(bucket, out arn)) { string accessPointString; return(arn.TryParseAccessPoint(out accessPointString)); } return(false); }
/// <summary> /// Check the many places that the ARN could be in an S3 Control request /// for an ARN. /// <ul> /// <li> CreateAccessPoint can contain the ARN in the payload </li> /// <li> ListAccessPoints can contain an ARN in the query params </li> /// <li> Most other requests can contain an ARN in the resource path </li> /// </ul> /// </summary> /// <param name="request">The request being sent to S3Control</param> /// <param name="arn">Any ARN found in the request</param> /// <returns>A boolean of whether or not the request contains an ARN</returns> 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)); }
/// <summary> /// Creates a <see cref="TopicAddress"/> from a topic ARN. /// </summary> /// <param name="topicArn">The SNS topic ARN.</param> /// <returns>A <see cref="TopicAddress"/> created from the ARN.</returns> /// <exception cref="ArgumentException"></exception> public static TopicAddress FromArn(string topicArn) { if (!Arn.IsArn(topicArn) || !Arn.TryParse(topicArn, out var arn)) { throw new ArgumentException("Must be a valid ARN.", nameof(topicArn)); } if (!string.Equals(arn.Service, "sns", StringComparison.OrdinalIgnoreCase)) { throw new ArgumentException("Must be an ARN for an SNS topic.", nameof(topicArn)); } return(new TopicAddress { TopicArn = topicArn }); }
/// <summary> /// Create a signed URL allowing access to a resource that would /// usually require authentication. /// </summary> /// <remarks> /// <para> /// When using query string authentication you create a query, /// specify an expiration time for the query, sign it with your /// signature, place the data in an HTTP request, and distribute /// the request to a user or embed the request in a web page. /// </para> /// <para> /// A PreSigned URL can be generated for GET, PUT, DELETE and HEAD /// operations on your bucketName, keys, and versions. /// </para> /// </remarks> /// <param name="request">The GetPreSignedUrlRequest that defines the /// parameters of the operation.</param> /// <param name="useSigV2Fallback">determines if signing will fall back to SigV2 if the /// signing region is us-east-1</param> /// <returns>A string that is the signed http request.</returns> /// <exception cref="T:System.ArgumentException" /> /// <exception cref="T:System.ArgumentNullException" /> internal string GetPreSignedURLInternal(GetPreSignedUrlRequest request, bool useSigV2Fallback = true) { if (Credentials == null) { throw new AmazonS3Exception("Credentials must be specified, cannot call method anonymously"); } if (request == null) { throw new ArgumentNullException("request", "The PreSignedUrlRequest specified is null!"); } if (!request.IsSetExpires()) { throw new InvalidOperationException("The Expires specified is null!"); } var aws4Signing = AWSConfigsS3.UseSignatureVersion4; Arn arn; string accessPoint; if (Arn.TryParse(request.BucketName, out arn) && (arn.TryParseAccessPoint(out accessPoint) || arn.IsOutpostArn())) { aws4Signing = true; } else { var region = AWS4Signer.DetermineSigningRegion(Config, "s3", alternateEndpoint: null, request: null); if (aws4Signing && string.IsNullOrEmpty(region)) { throw new InvalidOperationException("To use AWS4 signing, a region must be specified in the client configuration using the AuthenticationRegion or Region properties, or be determinable from the service URL."); } RegionEndpoint endpoint = RegionEndpoint.GetBySystemName(region); var s3SignatureVersionOverride = endpoint.GetEndpointForService("s3").SignatureVersionOverride; if (s3SignatureVersionOverride == "4" || s3SignatureVersionOverride == null) { aws4Signing = true; } var fallbackToSigV2 = useSigV2Fallback && !AWSConfigsS3.UseSigV4SetExplicitly; if (endpoint?.SystemName == RegionEndpoint.USEast1.SystemName && fallbackToSigV2) { aws4Signing = false; } // If the expiration is longer than SigV4 will allow then automatically use SigV2 instead. // But only if the region we're signing for allows SigV2. if (aws4Signing) { var secondsUntilExpiration = GetSecondsUntilExpiration(this.Config, request, aws4Signing); if (secondsUntilExpiration > AWS4PreSignedUrlSigner.MaxAWS4PreSignedUrlExpiry && s3SignatureVersionOverride == "2") { aws4Signing = false; } } } var immutableCredentials = Credentials.GetCredentials(); var irequest = Marshall(this.Config, request, immutableCredentials.AccessKey, immutableCredentials.Token, aws4Signing); irequest.Endpoint = EndpointResolver.DetermineEndpoint(this.Config, irequest); var context = new Amazon.Runtime.Internal.ExecutionContext(new Amazon.Runtime.Internal.RequestContext(true, new NullSigner()) { Request = irequest, ClientConfig = this.Config }, null); AmazonS3PostMarshallHandler.ProcessRequestHandlers(context); var metrics = new RequestMetrics(); string authorization; if (aws4Signing) { var aws4Signer = new AWS4PreSignedUrlSigner(); var signingResult = aws4Signer.SignRequest(irequest, this.Config, metrics, immutableCredentials.AccessKey, immutableCredentials.SecretKey); authorization = "&" + signingResult.ForQueryParameters; } else { Amazon.S3.Internal.S3Signer.SignRequest(irequest, metrics, immutableCredentials.AccessKey, immutableCredentials.SecretKey); authorization = irequest.Headers[HeaderKeys.AuthorizationHeader]; authorization = authorization.Substring(authorization.IndexOf(":", StringComparison.Ordinal) + 1); authorization = "&Signature=" + AmazonS3Util.UrlEncode(authorization, false); } Uri url = AmazonServiceClient.ComposeUrl(irequest); string result = url.AbsoluteUri + authorization; Protocol protocol = DetermineProtocol(); if (request.Protocol != protocol) { switch (protocol) { case Protocol.HTTP: result = result.Replace("http://", "https://"); break; case Protocol.HTTPS: result = result.Replace("https://", "http://"); break; } } return(result); }
internal static bool IsS3OutpostsArn(string bucket) { Arn arn; return(Arn.TryParse(bucket, out arn) && arn.IsOutpostArn()); }