public static bool DoesOperationTriggerReplication(StorageOperationTypes operation) { return _replicationTriggerOperations.Contains(operation); }
private async Task<HttpResponseMessage> PutBlobHandler(string container, string blob, IHttpRequestWrapper requestWrapper, StorageOperationTypes operation) { return await DoHandlerAsync(String.Format("BlobController.PutBlobHandler: {0} {1}/{2}", operation, container, blob), async () => { var namespaceBlob = await NamespaceHandler.CreateNamespaceBlobAsync(container, blob); if (BlobReplicationOperations.DoesOperationTriggerReplication(operation) && BlobReplicationHandler.ShouldReplicateBlob(requestWrapper.Headers, namespaceBlob)) { await BlobReplicationHandler.EnqueueBlobReplicationAsync(namespaceBlob, false); } return await ForwardRequestHandler(namespaceBlob, operation); }); }
private async Task<HttpResponseMessage> ForwardRequestHandler(NamespaceBlob namespaceBlob, StorageOperationTypes operation) { // Clone the inbound request var sourceRequest = this.Request; // We always target the primary data account for forwarded messages. If the operation invalidates the replicas, then // separate logic will enqueue the new blob to be replicated. var clonedRequest = new HttpRequestMessage(sourceRequest.Method, ControllerOperations.GetRedirectUri(sourceRequest.RequestUri, sourceRequest.Method.Method, DashConfiguration.GetDataAccountByAccountName(namespaceBlob.PrimaryAccountName), namespaceBlob.Container, namespaceBlob.BlobName, false)); clonedRequest.Version = sourceRequest.Version; foreach (var property in sourceRequest.Properties) { clonedRequest.Properties.Add(property); } foreach (var header in sourceRequest.Headers) { if (!_noCopyHeaders.Contains(header.Key)) { clonedRequest.Headers.TryAddWithoutValidation(header.Key, header.Value); } } // Depending on the operation, we have to do some fixup to unwind HttpRequestMessage a bit - we also have to fixup some responses switch (operation) { case StorageOperationTypes.GetBlob: case StorageOperationTypes.GetBlobMetadata: case StorageOperationTypes.GetBlobProperties: case StorageOperationTypes.GetBlockList: case StorageOperationTypes.GetPageRanges: case StorageOperationTypes.LeaseBlob: case StorageOperationTypes.SetBlobMetadata: case StorageOperationTypes.SetBlobProperties: case StorageOperationTypes.SnapshotBlob: // Push any headers that are assigned to Content onto the request itself as these operations do not have any body if (sourceRequest.Content != null) { foreach (var header in sourceRequest.Content.Headers) { clonedRequest.Headers.TryAddWithoutValidation(header.Key, header.Value); } } break; default: clonedRequest.Content = sourceRequest.Content; break; } var client = new HttpClient(); var response = await client.SendAsync(clonedRequest, HttpCompletionOption.ResponseHeadersRead); // Fixup response for HEAD requests switch (operation) { case StorageOperationTypes.GetBlobProperties: var content = response.Content; if (response.IsSuccessStatusCode && content != null) { string mediaType = null; string dummyContent = String.Empty; if (content.Headers.ContentType != null) { mediaType = content.Headers.ContentType.MediaType; } // For some reason, a HEAD request requires some content otherwise the Content-Length is set to 0 dummyContent = "A"; response.Content = new StringContent(dummyContent, null, mediaType); foreach (var header in content.Headers) { response.Content.Headers.TryAddWithoutValidation(header.Key, header.Value); } response.Content.Headers.ContentLength = content.Headers.ContentLength; content.Dispose(); } break; } return response; }
public static async Task <bool> IsAuthorizedAsync(IHttpRequestWrapper request, RequestHeaders headers, RequestQueryParameters queryParams, bool ignoreRequestAge) { var requestUriParts = request.UriParts; var resourceType = queryParams.Value <string>(ParamResourceType, String.Empty).ToLowerInvariant(); DateTimeOffset?start = queryParams.Value(ParamStartTime, DateTimeOffset.UtcNow); DateTimeOffset?expiry = queryParams.Value(ParamExpiryTime, DateTimeOffset.MinValue); if (expiry == DateTimeOffset.MinValue) { expiry = null; } SharedAccessBlobPermissions permissions = SharedAccessBlobPolicy.PermissionsFromString(queryParams.Value(ParamPermissions, String.Empty)); // Determine validity of the structure first if (requestUriParts.IsAccountRequest) { // SAS keys are not valid for account operations return(false); } else if (requestUriParts.IsContainerRequest && resourceType != "c") { return(false); } else if (requestUriParts.IsBlobRequest && resourceType.IndexOfAny(new [] { 'c', 'b' }) == -1) { return(false); } var storedPolicyId = queryParams.Value <string>(ParamStoredPolicy); if (!String.IsNullOrWhiteSpace(storedPolicyId)) { // Validate that we're not duplicating values for both stored access policy & url var storedPolicy = await GetStoredPolicyForContainer(requestUriParts.Container, storedPolicyId); if (storedPolicy == null) { return(false); } if (storedPolicy.SharedAccessStartTime.HasValue) { start = storedPolicy.SharedAccessStartTime; } if (storedPolicy.SharedAccessExpiryTime.HasValue) { if (expiry.HasValue) { return(false); } expiry = storedPolicy.SharedAccessExpiryTime; } if (queryParams.Contains(ParamPermissions)) { return(false); } permissions = storedPolicy.Permissions; } if (!expiry.HasValue || permissions == SharedAccessBlobPermissions.None) { return(false); } else if (!ignoreRequestAge && (start.Value > DateTimeOffset.UtcNow || expiry.Value < DateTimeOffset.UtcNow)) { return(false); } // Verify the assigned permissions line up with the requested operation StorageOperationTypes requestOperation = StorageOperations.GetBlobOperation(request); switch (requestOperation) { case StorageOperationTypes.GetBlob: case StorageOperationTypes.GetBlobMetadata: case StorageOperationTypes.GetBlobProperties: case StorageOperationTypes.GetBlockList: case StorageOperationTypes.GetPageRanges: if (!permissions.IsFlagSet(SharedAccessBlobPermissions.Read)) { return(false); } break; case StorageOperationTypes.AbortCopyBlob: case StorageOperationTypes.CopyBlob: case StorageOperationTypes.LeaseBlob: case StorageOperationTypes.PutBlob: case StorageOperationTypes.PutBlock: case StorageOperationTypes.PutBlockList: case StorageOperationTypes.PutPage: case StorageOperationTypes.SetBlobMetadata: case StorageOperationTypes.SetBlobProperties: case StorageOperationTypes.SnapshotBlob: if (!permissions.IsFlagSet(SharedAccessBlobPermissions.Write)) { return(false); } break; case StorageOperationTypes.DeleteBlob: if (!permissions.IsFlagSet(SharedAccessBlobPermissions.Delete)) { return(false); } break; case StorageOperationTypes.ListBlobs: if (!permissions.IsFlagSet(SharedAccessBlobPermissions.List)) { return(false); } break; default: // All other operations are not supported by SAS uris return(false); } DateTimeOffset sasVersion = queryParams.Value(ParamVersion, StorageServiceVersions.Version_2009_09_19); Func <string> stringToSignFactory = null; Func <string> baseStringToSign = () => String.Format("{0}\n{1}\n{2}\n{3}\n{4}", queryParams.Value <string>(ParamPermissions), queryParams.Value <string>(ParamStartTime), queryParams.Value <string>(ParamExpiryTime), GetCanonicalizedResource(requestUriParts, resourceType), queryParams.Value <string>(ParamStoredPolicy)); Func <string> v2012_02_12StringToSign = () => String.Format("{0}\n{1}", baseStringToSign(), queryParams.Value <string>(ParamVersion)); if (sasVersion < StorageServiceVersions.Version_2012_02_12) { stringToSignFactory = baseStringToSign; } else if (sasVersion == StorageServiceVersions.Version_2012_02_12) { stringToSignFactory = v2012_02_12StringToSign; } else { stringToSignFactory = () => String.Format("{0}\n{1}\n{2}\n{3}\n{4}\n{5}", v2012_02_12StringToSign(), queryParams.Value <string>(ParamCacheControl), queryParams.Value <string>(ParamContentDisposition), queryParams.Value <string>(ParamContentEncoding), queryParams.Value <string>(ParamContentLang), queryParams.Value <string>(ParamContentType)); } string signature = queryParams.Value <string>(ParamSignature); var usingPrimaryKey = new[] { true, false }; int matchIndex = Array.FindIndex(usingPrimaryKey, usePrimaryKey => VerifySignature(signature, usePrimaryKey, stringToSignFactory)); if (matchIndex != -1) { // We can't sign the redirection response when the request uses a SAS key - preserve the matching key, however request.AuthenticationScheme = String.Empty; request.AuthenticationKey = usingPrimaryKey[matchIndex] ? SharedKeySignature.PrimaryAccountKey : SharedKeySignature.SecondaryAccountKey; return(true); } return(false); }
/// <summary> /// Generic function to forward blob request. Target blob must already exist. /// </summary> private async Task<HttpResponseMessage> BasicBlobHandler(string container, string blob, IHttpRequestWrapper requestWrapper, StorageOperationTypes operation) { return await DoHandlerAsync(String.Format("BlobController.BasicBlobHandler: {0} {1}/{2}", operation, container, blob), async () => { var namespaceBlob = await NamespaceHandler.FetchNamespaceBlobAsync(container, blob); if (!await namespaceBlob.ExistsAsync()) { return this.CreateResponse(HttpStatusCode.NotFound, (RequestHeaders)null); } if (BlobReplicationOperations.DoesOperationTriggerReplication(operation) && (namespaceBlob.IsReplicated || BlobReplicationHandler.ShouldReplicateBlob(requestWrapper.Headers, namespaceBlob))) { await BlobReplicationHandler.EnqueueBlobReplicationAsync(namespaceBlob, false); } return await ForwardRequestHandler(namespaceBlob, operation); }); }
private async Task <HttpResponseMessage> ForwardRequestHandler(NamespaceBlob namespaceBlob, StorageOperationTypes operation) { // Clone the inbound request var sourceRequest = this.Request; // We always target the primary data account for forwarded messages. If the operation invalidates the replicas, then // separate logic will enqueue the new blob to be replicated. var clonedRequest = new HttpRequestMessage(sourceRequest.Method, ControllerOperations.GetRedirectUri(sourceRequest.RequestUri, sourceRequest.Method.Method, DashConfiguration.GetDataAccountByAccountName(namespaceBlob.PrimaryAccountName), namespaceBlob.Container, namespaceBlob.BlobName, false)); clonedRequest.Version = sourceRequest.Version; foreach (var property in sourceRequest.Properties) { clonedRequest.Properties.Add(property); } foreach (var header in sourceRequest.Headers) { if (!_noCopyHeaders.Contains(header.Key)) { clonedRequest.Headers.TryAddWithoutValidation(header.Key, header.Value); } } // Depending on the operation, we have to do some fixup to unwind HttpRequestMessage a bit - we also have to fixup some responses switch (operation) { case StorageOperationTypes.GetBlob: case StorageOperationTypes.GetBlobMetadata: case StorageOperationTypes.GetBlobProperties: case StorageOperationTypes.GetBlockList: case StorageOperationTypes.GetPageRanges: case StorageOperationTypes.LeaseBlob: case StorageOperationTypes.SetBlobMetadata: case StorageOperationTypes.SetBlobProperties: case StorageOperationTypes.SnapshotBlob: // Push any headers that are assigned to Content onto the request itself as these operations do not have any body if (sourceRequest.Content != null) { foreach (var header in sourceRequest.Content.Headers) { clonedRequest.Headers.TryAddWithoutValidation(header.Key, header.Value); } } break; default: clonedRequest.Content = sourceRequest.Content; break; } var client = new HttpClient(); var response = await client.SendAsync(clonedRequest, HttpCompletionOption.ResponseHeadersRead); // Fixup response for HEAD requests switch (operation) { case StorageOperationTypes.GetBlobProperties: var content = response.Content; if (response.IsSuccessStatusCode && content != null) { string mediaType = null; string dummyContent = String.Empty; if (content.Headers.ContentType != null) { mediaType = content.Headers.ContentType.MediaType; } // For some reason, a HEAD request requires some content otherwise the Content-Length is set to 0 dummyContent = "A"; response.Content = new StringContent(dummyContent, null, mediaType); foreach (var header in content.Headers) { response.Content.Headers.TryAddWithoutValidation(header.Key, header.Value); } response.Content.Headers.ContentLength = content.Headers.ContentLength; content.Dispose(); } break; } return(response); }
public static bool DoesOperationTriggerReplication(StorageOperationTypes operation) { return(_replicationTriggerOperations.Contains(operation)); }
private async Task <HttpResponseMessage> PutBlobHandler(string container, string blob, IHttpRequestWrapper requestWrapper, StorageOperationTypes operation) { return(await DoHandlerAsync(String.Format("BlobController.PutBlobHandler: {0} {1}/{2}", operation, container, blob), async() => { var namespaceBlob = await NamespaceHandler.CreateNamespaceBlobAsync(container, blob); if (BlobReplicationOperations.DoesOperationTriggerReplication(operation) && BlobReplicationHandler.ShouldReplicateBlob(requestWrapper.Headers, namespaceBlob)) { await BlobReplicationHandler.EnqueueBlobReplicationAsync(namespaceBlob, false); } return await ForwardRequestHandler(namespaceBlob, operation); })); }
/// <summary> /// Generic function to forward blob request. Target blob must already exist. /// </summary> private async Task <HttpResponseMessage> BasicBlobHandler(string container, string blob, IHttpRequestWrapper requestWrapper, StorageOperationTypes operation) { return(await DoHandlerAsync(String.Format("BlobController.BasicBlobHandler: {0} {1}/{2}", operation, container, blob), async() => { var namespaceBlob = await NamespaceHandler.FetchNamespaceBlobAsync(container, blob); if (!await namespaceBlob.ExistsAsync()) { return this.CreateResponse(HttpStatusCode.NotFound, (RequestHeaders)null); } if (BlobReplicationOperations.DoesOperationTriggerReplication(operation) && (namespaceBlob.IsReplicated || BlobReplicationHandler.ShouldReplicateBlob(requestWrapper.Headers, namespaceBlob))) { await BlobReplicationHandler.EnqueueBlobReplicationAsync(namespaceBlob, false); } return await ForwardRequestHandler(namespaceBlob, operation); })); }
public OperationDefinition(OperationGroup group, HttpMethod method1, HttpMethod method2, string comp, StorageOperationTypes operation) : this(group, method1, method2, comp, (attribs) => operation) { }
public static async Task <HandlerResult> HandlePrePipelineOperationAsync(IHttpRequestWrapper requestWrapper) { string containerName = requestWrapper.UriParts.Container; string blobName = requestWrapper.UriParts.BlobName; StorageOperationTypes requestOperation = StorageOperations.GetBlobOperation(requestWrapper); DashClientCapabilities client = DashClientDetector.DetectClient(requestWrapper); HandlerResult result = null; bool servePrimaryOnly = false; switch (requestOperation) { case StorageOperationTypes.GetBlobProperties: case StorageOperationTypes.SetBlobProperties: case StorageOperationTypes.GetBlobMetadata: case StorageOperationTypes.SetBlobMetadata: case StorageOperationTypes.LeaseBlob: case StorageOperationTypes.GetBlockList: servePrimaryOnly = true; // Fall through goto case StorageOperationTypes.GetBlob; case StorageOperationTypes.GetBlob: case StorageOperationTypes.SnapshotBlob: case StorageOperationTypes.GetPageRanges: if (client.HasFlag(DashClientCapabilities.FollowRedirects)) { // If the client has specified a concurrent operation, then we always serve the primary account // as that is where the concurrency controls exist if (!servePrimaryOnly && requestWrapper.Headers.IsConcurrentRequest()) { servePrimaryOnly = true; } result = await BlobHandler.BasicBlobAsync(requestWrapper, containerName, blobName, servePrimaryOnly, BlobReplicationOperations.DoesOperationTriggerReplication(requestOperation)); } break; case StorageOperationTypes.PutPage: if (client.HasFlag(DashClientCapabilities.NoPayloadToDash)) { result = await BlobHandler.BasicBlobAsync(requestWrapper, containerName, blobName, true, BlobReplicationOperations.DoesOperationTriggerReplication(requestOperation)); } break; case StorageOperationTypes.PutBlob: case StorageOperationTypes.PutBlock: case StorageOperationTypes.PutBlockList: if (client.HasFlag(DashClientCapabilities.NoPayloadToDash)) { result = await BlobHandler.PutBlobAsync(requestWrapper, containerName, blobName, BlobReplicationOperations.DoesOperationTriggerReplication(requestOperation)); } break; default: // All other operations flow through to the controller action break; } DashTrace.TraceInformation("Operation: {0}, client capability: {1}, action: {2}", requestOperation, client, result == null ? "Forward" : "Redirect"); return(result); }