public static bool DoesOperationTriggerReplication(StorageOperationTypes operation)
 {
     return _replicationTriggerOperations.Contains(operation);
 }
Beispiel #2
0
 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);
         });
 }
Beispiel #3
0
        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;
        }
Beispiel #4
0
        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);
        }
Beispiel #5
0
 /// <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);
         });
 }
Beispiel #6
0
        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));
 }
Beispiel #8
0
 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);
     }));
 }
Beispiel #9
0
 /// <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);
     }));
 }
Beispiel #10
0
 public OperationDefinition(OperationGroup group, HttpMethod method1, HttpMethod method2, string comp, StorageOperationTypes operation)
     : this(group, method1, method2, comp, (attribs) => operation)
 {
 }
Beispiel #11
0
        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);
        }
Beispiel #12
0
 public OperationDefinition(OperationGroup group, HttpMethod method1, HttpMethod method2, string comp, StorageOperationTypes operation)
     : this(group, method1, method2, comp, (attribs) => operation)
 {
 }