Exemple #1
0
 /// <summary>
 /// Generic function to redirect a put request for properties of a blob
 /// </summary>
 public static async Task<HandlerResult> BasicBlobAsync(IHttpRequestWrapper requestWrapper, string container, string blob, bool servePrimaryOnly, bool operationCanReplicateBlob)
 {
     return await WebOperationRunner.DoHandlerAsync("BlobHandler.BasicBlobAsync", async () =>
         {
             var namespaceBlob = await NamespaceHandler.FetchNamespaceBlobAsync(container, blob);
             if (!await namespaceBlob.ExistsAsync())
             {
                 return new HandlerResult
                 {
                     StatusCode = HttpStatusCode.NotFound,
                 };
             }
             string accountName = namespaceBlob.SelectDataAccount(servePrimaryOnly);
             if (operationCanReplicateBlob)
             {
                 if (namespaceBlob.IsReplicated || 
                     BlobReplicationHandler.ShouldReplicateBlob(requestWrapper.Headers, container, blob))
                 {
                     accountName = namespaceBlob.PrimaryAccountName;
                     await BlobReplicationHandler.EnqueueBlobReplicationAsync(namespaceBlob, false);
                 }
             }
             Uri redirect = ControllerOperations.GetRedirectUri(HttpContextFactory.Current.Request,
                 DashConfiguration.GetDataAccountByAccountName(accountName),
                 namespaceBlob.Container,
                 namespaceBlob.BlobName,
                 false);
             return HandlerResult.Redirect(requestWrapper, redirect);
         });
 }
        public static DashClientCapabilities DetectClient(IHttpRequestWrapper requestWrapper)
        {
            DashClientCapabilities retval = DashClientCapabilities.None;
            string agent     = requestWrapper.Headers.Value("User-Agent", String.Empty).ToLower();
            bool   expect100 = requestWrapper.Headers.Contains("Expect");

            if (expect100)
            {
                // Expect: 100-Continue trumps everything
                retval = DashClientCapabilities.FullSupport;
            }
            else if (agent.Contains("dash"))
            {
                // Modified client
                retval = DashClientCapabilities.FullSupport;
            }
            else if (agent.StartsWith("wa-storage/2.0.6") ||
                     agent.Contains(".net") ||
                     agent.Contains("windowspowershell"))
            {
                // .NET clients can handle redirects seamlessly (they omit the Authorization header as they follow the redirect)
                retval = DashClientCapabilities.FollowRedirects;
            }
            return(retval);
        }
Exemple #3
0
        public static async Task <bool> IsAuthorizedAsync(IHttpRequestWrapper request)
        {
            bool retval           = false;
            var  requestUriParts  = request.UriParts;
            var  requestOperation = StorageOperations.GetBlobOperation(request.HttpMethod, requestUriParts, request.QueryParameters, request.Headers);

            if (request.HttpMethod == HttpMethod.Options.ToString())
            {
                return(true);
            }
            switch (requestOperation)
            {
            case StorageOperationTypes.GetContainerProperties:
            case StorageOperationTypes.GetContainerMetadata:
            case StorageOperationTypes.ListBlobs:
                retval = await GetContainerPublicAccessAsync(requestUriParts.Container) == BlobContainerPublicAccessType.Container;

                break;

            case StorageOperationTypes.GetBlob:
            case StorageOperationTypes.GetBlobProperties:
            case StorageOperationTypes.GetBlobMetadata:
            case StorageOperationTypes.GetBlockList:
                retval = await GetContainerPublicAccessAsync(requestUriParts.Container) != BlobContainerPublicAccessType.Off;

                break;
            }
            return(retval);
        }
 public static DashClientCapabilities DetectClient(IHttpRequestWrapper requestWrapper)
 {
     DashClientCapabilities retval = DashClientCapabilities.None;
     string agent = requestWrapper.Headers.Value("User-Agent", String.Empty).ToLower();
     bool expect100 = requestWrapper.Headers.Contains("Expect");
     if (expect100)
     {
         // Expect: 100-Continue trumps everything
         retval = DashClientCapabilities.FullSupport;
     }
     else if (agent.Contains("dash"))
     {
         // Modified client
         retval = DashClientCapabilities.FullSupport;
     }
     else if (agent.StartsWith("wa-storage/2.0.6") || 
         agent.StartsWith("services_batch_storage_client") ||
         agent.Contains(".net") ||
         agent.Contains("windowspowershell"))
     {
         // .NET clients can handle redirects seamlessly (they omit the Authorization header as they follow the redirect)
         retval = DashClientCapabilities.FollowRedirects;
     }
     return retval;
 }
Exemple #5
0
 /// <summary>
 /// Generic function to redirect a put request for properties of a blob
 /// </summary>
 public static async Task <HandlerResult> BasicBlobAsync(IHttpRequestWrapper requestWrapper, string container, string blob, bool servePrimaryOnly, bool operationCanReplicateBlob)
 {
     return(await WebOperationRunner.DoHandlerAsync("BlobHandler.BasicBlobAsync", async() =>
     {
         var namespaceBlob = await NamespaceHandler.FetchNamespaceBlobAsync(container, blob);
         if (!await namespaceBlob.ExistsAsync())
         {
             return new HandlerResult
             {
                 StatusCode = HttpStatusCode.NotFound,
             };
         }
         string accountName = namespaceBlob.SelectDataAccount(servePrimaryOnly);
         if (operationCanReplicateBlob)
         {
             if (namespaceBlob.IsReplicated ||
                 BlobReplicationHandler.ShouldReplicateBlob(requestWrapper.Headers, container, blob))
             {
                 accountName = namespaceBlob.PrimaryAccountName;
                 await BlobReplicationHandler.EnqueueBlobReplicationAsync(namespaceBlob, false);
             }
         }
         Uri redirect = ControllerOperations.GetRedirectUri(HttpContextFactory.Current.Request,
                                                            DashConfiguration.GetDataAccountByAccountName(accountName),
                                                            namespaceBlob.Container,
                                                            namespaceBlob.BlobName,
                                                            false);
         return HandlerResult.Redirect(requestWrapper, redirect);
     }));
 }
Exemple #6
0
        public static async Task<bool> IsAuthorizedAsync(IHttpRequestWrapper request)
        {
            bool retval = false;
            var requestUriParts = request.UriParts;
            var requestOperation = StorageOperations.GetBlobOperation(request.HttpMethod, requestUriParts, request.QueryParameters, request.Headers);
            if (request.HttpMethod == HttpMethod.Options.ToString())
            {
                return true;
            }
            switch (requestOperation)
            {
                case StorageOperationTypes.GetContainerProperties:
                case StorageOperationTypes.GetContainerMetadata:
                case StorageOperationTypes.ListBlobs:
                    retval = await GetContainerPublicAccessAsync(requestUriParts.Container) == BlobContainerPublicAccessType.Container;
                    break;

                case StorageOperationTypes.GetBlob:
                case StorageOperationTypes.GetBlobProperties:
                case StorageOperationTypes.GetBlobMetadata:
                case StorageOperationTypes.GetBlockList:
                    retval = await GetContainerPublicAccessAsync(requestUriParts.Container) != BlobContainerPublicAccessType.Off;
                    break;
            }
            return retval;
        }
Exemple #7
0
 public LoginAmplaSessionUsingQueryString(IHttpRequestWrapper requestWrapper, IHttpResponseWrapper responseWrapper, IAmplaUserService amplaUserService, IFormsAuthenticationService formsAuthenticationService, IAmplaSessionStorage amplaSessionStorage)
 {
     this.requestWrapper             = requestWrapper;
     this.responseWrapper            = responseWrapper;
     this.amplaUserService           = amplaUserService;
     this.formsAuthenticationService = formsAuthenticationService;
     this.amplaSessionStorage        = amplaSessionStorage;
 }
Exemple #8
0
 public static HandlerResult Redirect(IHttpRequestWrapper requestWrapper, string location)
 {
     return new HandlerResult
     {
         StatusCode = HttpStatusCode.Redirect,
         Location = location,
         Request = requestWrapper,
     };
 }
Exemple #9
0
 public static HandlerResult Redirect(IHttpRequestWrapper requestWrapper, string location)
 {
     return(new HandlerResult
     {
         StatusCode = HttpStatusCode.Redirect,
         Location = location,
         Request = requestWrapper,
     });
 }
Exemple #10
0
        public static async Task <HandlerResult> AbortCopyBlobAsync(IHttpRequestWrapper requestWrapper, string destContainer, string destBlob, string copyId)
        {
            return(await WebOperationRunner.DoHandlerAsync("BlobHandler.AbortCopyBlobAsync", async() =>
            {
                var destNamespaceBlob = await NamespaceHandler.FetchNamespaceBlobAsync(destContainer, destBlob);

                var destCloudBlob = NamespaceHandler.GetBlobByName(
                    DashConfiguration.GetDataAccountByAccountName(destNamespaceBlob.PrimaryAccountName),
                    destContainer,
                    destBlob);
                await destCloudBlob.AbortCopyAsync(copyId);
                return new HandlerResult
                {
                    StatusCode = HttpStatusCode.NoContent,
                };
            }));
        }
Exemple #11
0
        public override async Task <byte[]> CreateResponseAsync(IHttpRequestWrapper request, byte[] body, IHttpResponseWrapper response, Endpoint endpoint)
        {
            var requestPath = request.Path.ToString();

            if (StripPath != null)
            {
                requestPath = Regex.Replace(requestPath, StripPath, "");
            }
            var httpMsg = new HttpRequestMessage(HttpMethod.Post, Url + requestPath);

            Debug.Assert(request.Headers != null);
            foreach (var header in request.Headers)
            {
                if (!HEADERS_TO_SKIP.Contains(header.Key.ToLower()))
                {
                    httpMsg.Headers.Add(header.Key, header.Value.ToArray());
                }
            }
            httpMsg.Content = new ByteArrayContent(body);
            if ((string)request.Headers["Content-Type"] != null)
            {
                httpMsg.Content.Headers.ContentType = MediaTypeHeaderValue.Parse(request.Headers["Content-Type"]);
            }

            var httpClient =
                ProxyUrl == null ?
                new HttpClient() :
                new HttpClient(new HttpClientHandler {
                UseProxy = true, Proxy = new NetApp11WebProxy(new Uri(ProxyUrl))
            });

            var responseMessage = await httpClient.SendAsync(httpMsg);

            response.ContentType    = responseMessage.Content.Headers.ContentType.ToString();
            response.HttpStatusCode = responseMessage.StatusCode;
            var responseBodyStream = await responseMessage.Content.ReadAsStreamAsync();

            var memoryStream = new MemoryStream();

            responseBodyStream.CopyTo(memoryStream);
            memoryStream.Seek(0, SeekOrigin.Begin);
            memoryStream.CopyTo(response.Body);
            return(memoryStream.ToArray());
        }
Exemple #12
0
        public override async Task <byte[]> CreateResponseAsync(IHttpRequestWrapper request, byte[] requestBody, IHttpResponseWrapper response, Endpoint endpoint)
        {
            var requestInfo = new RequestInfo
            {
                RequestPath = request.Path.ToString(),
                QueryString = request.QueryString.ToString(),
                RequestBody = Encoding.UTF8.GetString(requestBody),
                Headers     = request.Headers,
                Endpoint    = endpoint
            };
            var responseBody = await GetBodyAndExecuteReplacementsAsync(requestInfo);

            SetContentType(requestInfo, response);
            SetStatusCode(requestInfo, response);

            await response.WriteAsync(responseBody, Encoding);

            return(Encoding.GetBytes(responseBody));
        }
Exemple #13
0
 public static async Task<HandlerResult> PutBlobAsync(IHttpRequestWrapper requestWrapper, string container, string blob, bool operationCanReplicateBlob)
 {
     return await WebOperationRunner.DoHandlerAsync("BlobHandler.PutBlobAsync", async () =>
         {
             var namespaceBlob = await NamespaceHandler.CreateNamespaceBlobAsync(container, blob);
             if (operationCanReplicateBlob)
             {
                 if (BlobReplicationHandler.ShouldReplicateBlob(requestWrapper.Headers, container, blob))
                 {
                     await BlobReplicationHandler.EnqueueBlobReplicationAsync(namespaceBlob, false);
                 }
             }
             Uri redirect = ControllerOperations.GetRedirectUri(HttpContextFactory.Current.Request,
                 DashConfiguration.GetDataAccountByAccountName(namespaceBlob.PrimaryAccountName),
                 container,
                 blob,
                 false);
             return HandlerResult.Redirect(requestWrapper, redirect);
         });
 }
Exemple #14
0
 public static async Task <HandlerResult> PutBlobAsync(IHttpRequestWrapper requestWrapper, string container, string blob, bool operationCanReplicateBlob)
 {
     return(await WebOperationRunner.DoHandlerAsync("BlobHandler.PutBlobAsync", async() =>
     {
         var namespaceBlob = await NamespaceHandler.CreateNamespaceBlobAsync(container, blob);
         if (operationCanReplicateBlob)
         {
             if (BlobReplicationHandler.ShouldReplicateBlob(requestWrapper.Headers, container, blob))
             {
                 await BlobReplicationHandler.EnqueueBlobReplicationAsync(namespaceBlob, false);
             }
         }
         Uri redirect = ControllerOperations.GetRedirectUri(HttpContextFactory.Current.Request,
                                                            DashConfiguration.GetDataAccountByAccountName(namespaceBlob.PrimaryAccountName),
                                                            container,
                                                            blob,
                                                            false);
         return HandlerResult.Redirect(requestWrapper, redirect);
     }));
 }
Exemple #15
0
        public override async Task <byte[]> CreateResponseAsync(IHttpRequestWrapper request, byte[] requestBody, IHttpResponseWrapper response, string endpointDirectory)
        {
            var responseBody = GetBodyAndExecuteReplacements(new RequestInfo
            {
                RequestPath       = request.Path.ToString(),
                QueryString       = request.QueryString.ToString(),
                RequestBody       = Encoding.UTF8.GetString(requestBody),
                Headers           = request.Headers,
                EndpointDirectory = endpointDirectory
            });

            if (ContentType != null)
            {
                var contenttype = ContentType;
                contenttype         += $"; charset={Encoding.WebName}";
                response.ContentType = contenttype;
            }
            await response.WriteAsync(responseBody, Encoding);

            return(Encoding.GetBytes(responseBody));
        }
Exemple #16
0
 public static async Task<bool> IsRequestAuthorizedAsync(IHttpRequestWrapper request, bool ignoreRequestAge = false)
 {
     var headers = request.Headers;
     var queryParams = request.QueryParameters;
     // See what type of auth scheme is applied to this request
     bool sharedKeyRequest = SharedKey.IsRequestType(headers);
     bool sasRequest = SharedAccessSignature.IsRequestType(queryParams);
     if (sharedKeyRequest && sasRequest)
     {
         // Can't be a SAS & SharedKey Authorization together
         return false;
     }
     else if (sasRequest)
     {
         return await SharedAccessSignature.IsAuthorizedAsync(request, headers, queryParams, ignoreRequestAge);
     }
     else if (sharedKeyRequest)
     {
         return SharedKey.IsAuthorized(request, headers, queryParams, ignoreRequestAge);
     }
     // Anonymous
     return await Anonymous.IsAuthorizedAsync(request);
 }
Exemple #17
0
        public static async Task <bool> IsRequestAuthorizedAsync(IHttpRequestWrapper request, bool ignoreRequestAge = false)
        {
            var headers     = request.Headers;
            var queryParams = request.QueryParameters;
            // See what type of auth scheme is applied to this request
            bool sharedKeyRequest = SharedKey.IsRequestType(headers);
            bool sasRequest       = SharedAccessSignature.IsRequestType(queryParams);

            if (sharedKeyRequest && sasRequest)
            {
                // Can't be a SAS & SharedKey Authorization together
                return(false);
            }
            else if (sasRequest)
            {
                return(await SharedAccessSignature.IsAuthorizedAsync(request, headers, queryParams, ignoreRequestAge));
            }
            else if (sharedKeyRequest)
            {
                return(SharedKey.IsAuthorized(request, headers, queryParams, ignoreRequestAge));
            }
            // Anonymous
            return(await Anonymous.IsAuthorizedAsync(request));
        }
Exemple #18
0
        public static bool IsAuthorized(IHttpRequestWrapper request, RequestHeaders headers, RequestQueryParameters queryParams, bool ignoreRequestAge)
        {
            // Quick request age check
            var    authHeader        = headers.Value <string>("Authorization");
            string requestDateHeader = headers.Value <string>("x-ms-date");
            string dateHeader        = String.Empty;

            if (String.IsNullOrWhiteSpace(requestDateHeader))
            {
                requestDateHeader = headers.Value <string>("Date");
                dateHeader        = requestDateHeader;
            }
            if (String.IsNullOrWhiteSpace(requestDateHeader))
            {
                // One of the date headers is mandatory
                return(false);
            }
            if (!ignoreRequestAge)
            {
                DateTime requestDate;
                if (!DateTime.TryParse(requestDateHeader, out requestDate))
                {
                    return(false);
                }
                else if (requestDate < DateTime.Now.AddMinutes(-15))
                {
                    return(false);
                }
            }
            var parts = authHeader.Split(' ', ':');

            if (parts.Length != 3)
            {
                return(false);
            }
            else if (parts[0] != SharedKeySignature.AlgorithmSharedKey && parts[0] != SharedKeySignature.AlgorithmSharedKeyLite)
            {
                return(false);
            }
            var account   = parts[1];
            var signature = parts[2];

            if (!String.Equals(account, SharedKeySignature.AccountName, StringComparison.OrdinalIgnoreCase))
            {
                return(false);
            }
            // We have to deal with multiple encodings (the spec is a bit ambiguous on what an 'encoded' path actually is).
            // Only run the validation if the encodings result in different strings
            var requestUriParts = request.UriParts;
            var pathsToCheck    = new List <string>()
            {
                requestUriParts.OriginalUriPath
            };
            var unencodedPath = requestUriParts.PublicUriPath;

            if (unencodedPath != pathsToCheck[0])
            {
                pathsToCheck.Add(unencodedPath);
            }
            var alternateEncodingPaths = AlternateEncodeString(pathsToCheck[0]);

            if (alternateEncodingPaths != null)
            {
                pathsToCheck.AddRange(alternateEncodingPaths);
            }
            // For some verbs we can't tell if the Content-Length header was specified as 0 or that IIS/UrlRewrite/ASP.NET has constructed
            // the header value for us. The difference is significant to the signature as content length is included for SharedKey
            bool   fullKeyAlgorithm = parts[0] == SharedKeySignature.AlgorithmSharedKey;
            bool   runBlankContentLengthComparison = false;
            string method        = request.HttpMethod.ToUpper();
            var    contentLength = headers.Value("Content-Length", String.Empty);

            if (fullKeyAlgorithm)
            {
                int length;
                if (!int.TryParse(contentLength, out length) || length <= 0)
                {
                    // Preserve a Content-Length: 0 header for PUT methods
                    runBlankContentLengthComparison = !method.Equals(WebRequestMethods.Http.Put, StringComparison.OrdinalIgnoreCase);
                }
            }
            var validationChecks = pathsToCheck.SelectMany(uriPath => new[] {
                runBlankContentLengthComparison?Tuple.Create(true, uriPath, String.Empty) : null,
                    Tuple.Create(true, uriPath, contentLength),
                    runBlankContentLengthComparison ? Tuple.Create(false, uriPath, String.Empty) : null,
                    Tuple.Create(false, uriPath, contentLength),
            })
                                   .Where(check => check != null)
                                   .ToArray();
            var evaluationResult = validationChecks
                                   .FirstOrDefault(validatationCheck =>
                                                   VerifyRequestAuthorization(signature, validatationCheck.Item1, !fullKeyAlgorithm, method, validatationCheck.Item2, headers, queryParams, dateHeader, validatationCheck.Item3));

            if (evaluationResult != null)
            {
                // Remember the Auth Scheme & Key for when we have to sign the response
                request.AuthenticationScheme = parts[0];
                request.AuthenticationKey    = evaluationResult.Item1 ? SharedKeySignature.PrimaryAccountKey : SharedKeySignature.SecondaryAccountKey;
                return(true);
            }
            DashTrace.TraceWarning("Failed to authenticate SharedKey request: {0}:{1}:{2}:{3}:{4}", parts[0], account, method, request.Url, signature);

            return(false);
        }
Exemple #19
0
 static bool IsRequestAuthorized(IHttpRequestWrapper request)
 {
     return RequestAuthorization.IsRequestAuthorizedAsync(request, true).Result;
 }
Exemple #20
0
 /// <summary>
 /// Initializes a new instance of the <see cref="FormsAuthenticationService"/> class.
 /// </summary>
 /// <param name="request">The request.</param>
 /// <param name="response">The response.</param>
 public FormsAuthenticationService(IHttpRequestWrapper request, IHttpResponseWrapper response)
 {
     this.request  = request;
     this.response = response;
 }
 public BrowserCapabilitiesService(IHttpRequestWrapper httpRequestWrapper)
 {
     _httpRequestWrapper = httpRequestWrapper;
 }
Exemple #22
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);
         });
 }
Exemple #23
0
 static bool IsRequestAuthorized(IHttpRequestWrapper request)
 {
     return(RequestAuthorization.IsRequestAuthorizedAsync(request, true).Result);
 }
Exemple #24
0
        public static async Task<BlobListResults> GetBlobListing(string container, IHttpRequestWrapper request)
        {
            // Extract query parameters
            var queryParams = request.QueryParameters;
            var prefix = queryParams.Value<string>("prefix");
            var delim = queryParams.Value<string>("delimiter");
            var marker = queryParams.Value<string>("marker");
            var indicatedMaxResults = queryParams.Value<string>("maxresults");
            var maxResults = queryParams.Value("maxresults", 5000);
            var includedDataSets = String.Join(",", queryParams.Values<string>("include"));
            bool isHierarchicalListing = !String.IsNullOrWhiteSpace(delim);

            var blobTasks = DashConfiguration.DataAccounts
                .Select(account => ChildBlobListAsync(account, container, prefix, delim, includedDataSets))
                .ToArray();
            var namespaceTask = ChildBlobListAsync(DashConfiguration.NamespaceAccount, container, prefix, delim, BlobListingDetails.Metadata.ToString());
            var blobs = await Task.WhenAll(blobTasks);
            var namespaceBlobs = await namespaceTask;
            Func<string, IListBlobItem, string> makeBlobKeyWithAccount = (account, blob) => String.Format("{0}|{1}", account, blob.Uri.AbsolutePath);
            Func<IListBlobItem, string> makeBlobKey = (blob) => makeBlobKeyWithAccount(blob.Uri.Host.Substring(0, blob.Uri.Host.IndexOf('.')), blob);
            Func<NamespaceListWrapper, string> makeWrapperKey = (wrapper) => makeBlobKeyWithAccount(wrapper.IsDirectory ? String.Empty : wrapper.NamespaceBlob.PrimaryAccountName, wrapper.SourceItem);
            var sortedDataBlobs = blobs
                .SelectMany(blobList => blobList)
                .Select(blob => new {
                    Blob = blob, 
                    SortMarker = GetRawMarkerForBlob(blob),
                })
                .OrderBy(blob => blob.SortMarker, StringComparer.Ordinal)
                .SkipWhile(blob => !String.IsNullOrWhiteSpace(marker) && GetMarker(blob.SortMarker) != marker);
            // We're creating a lookup list to merge with the ns blobs. We do 2 passes here - one for actual blobs & a second for directories
            // (if applicable) as we don't have a specific account for directories - we just care they exist in the data space somewhere.
            // We also have to handle the snapshot case where there will only be 1 ns entry but multiple blobs (the current one & n snapshots)
            var dataBlobsLookup = sortedDataBlobs
                .Where(blob => blob.Blob is ICloudBlob)
                .ToLookup(blob => makeBlobKey(blob.Blob), StringComparer.OrdinalIgnoreCase)
                .ToDictionary(group => group.Key, group => group.Select(blob => blob.Blob), StringComparer.OrdinalIgnoreCase);
            if (isHierarchicalListing)
            {
                foreach (var directory in sortedDataBlobs
                                            .Select(blob => blob.Blob)
                                            .Where(blob => blob is CloudBlobDirectory)
                                            .ToLookup(directory => ((CloudBlobDirectory)directory).Prefix, StringComparer.OrdinalIgnoreCase))
                {
                    dataBlobsLookup[makeBlobKeyWithAccount(String.Empty, directory.First())] = Enumerable.Repeat(directory.First(), 1);
                }
            }
            var joinedList = namespaceBlobs
                .Select(nsBlob => new
                {
                    BlobWrapper = new NamespaceListWrapper(nsBlob),
                    SortMarker = GetRawMarkerForBlob(nsBlob),
                })
                .Where(nsBlob => nsBlob.BlobWrapper.IsDirectory || !nsBlob.BlobWrapper.NamespaceBlob.IsMarkedForDeletion)
                .OrderBy(nsBlob => nsBlob.SortMarker, StringComparer.Ordinal)
                .Where(nsBlob => dataBlobsLookup.ContainsKey(makeWrapperKey(nsBlob.BlobWrapper)))
                .SelectMany(nsBlob => dataBlobsLookup[makeWrapperKey(nsBlob.BlobWrapper)]
                                                .Select(dataBlob => new
                                                {
                                                    DataBlob = dataBlob,
                                                    NamespaceWrapper = nsBlob.BlobWrapper,
                                                }))
                .SkipWhile(dataBlob => !String.IsNullOrWhiteSpace(marker) && GetMarkerForBlob(dataBlob.DataBlob) != marker);
            if (isHierarchicalListing)
            {
                // For a hierarchical listing we have to deal with a corner case in that we may have orphaned replicated data blobs
                // included in the data accounts listing as well as being included in the namespace listing. However, if there's
                // nothing but deleted blobs in the namespace for that directory, we should omit the directory.
                // This is required to mitigate our eventual consistency model for replication.
                var validDirectoryTasks = joinedList
                    .Where(blobPair => blobPair.NamespaceWrapper.IsDirectory)
                    .Select(blobPair => IsValidNamespaceDirectoryAsync((CloudBlobDirectory)blobPair.NamespaceWrapper.SourceItem))
                    .ToArray();
                var validDirectories = new HashSet<string>((await Task.WhenAll(validDirectoryTasks))
                        .Where(directory => directory.Item2)
                        .Select(directory => directory.Item1.Prefix),
                    StringComparer.OrdinalIgnoreCase);
                joinedList = joinedList
                    .Where(blobPair => !blobPair.NamespaceWrapper.IsDirectory || validDirectories.Contains(blobPair.NamespaceWrapper.Prefix));
            }
            var resultsList = joinedList
                .Take(maxResults + 1)                  // Get an extra listing so that we can generate the nextMarker
                .Select(blobPair => blobPair.DataBlob);
            return new BlobListResults
            {
                RequestVersion = request.Headers.RequestVersion,
                ServiceEndpoint = request.Url.GetComponents(UriComponents.SchemeAndServer, UriFormat.Unescaped),
                ContainerName = container,
                MaxResults = maxResults,
                IndicatedMaxResults = indicatedMaxResults,
                Delimiter = delim,
                Marker = marker,
                Prefix = prefix,
                Blobs = resultsList,
                IncludeDetails = String.IsNullOrWhiteSpace(includedDataSets) ? BlobListingDetails.None : (BlobListingDetails)Enum.Parse(typeof(BlobListingDetails), includedDataSets, true),
            };
        }
Exemple #25
0
 public abstract Task <byte[]> CreateResponseAsync(IHttpRequestWrapper request, byte[] requestBody, IHttpResponseWrapper response, Endpoint endpoint);
Exemple #26
0
 public CorsAccessRequest(IHttpRequestWrapper httpRequest)
 {
     this.httpRequest = httpRequest;
 }
 public CorsAccessRequest(IHttpRequestWrapper httpRequest)
 {
     this.httpRequest = httpRequest;
 }
Exemple #28
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);
     }));
 }
Exemple #29
0
        public static async Task<bool> IsAuthorizedAsync(IHttpRequestWrapper request, RequestHeaders headers, RequestQueryParameters queryParams, bool ignoreRequestAge)
        {
            var requestUriParts = request.UriParts;
            string resourceType = String.Empty;
            DateTimeOffset? start = queryParams.Value(ParamStartTime, DateTimeOffset.UtcNow);
            DateTimeOffset? expiry = queryParams.Value(ParamExpiryTime, DateTimeOffset.MinValue);
            DateTimeOffset sasVersion = queryParams.Value(ParamVersion, StorageServiceVersions.Version_2009_09_19);
            bool accountSas = queryParams.Contains(ParamResourceTypeEx);
            if (accountSas)
            {
                resourceType = queryParams.Value<string>(ParamResourceTypeEx).ToLowerInvariant();
            }
            else
            {
                resourceType = queryParams.Value<string>(ParamResourceType).ToLowerInvariant();
            }
            if (expiry == DateTimeOffset.MinValue)
            {
                expiry = null;
            }
            SharedAccessBlobPermissions permissions = PermissionsFromString(queryParams.Value(ParamPermissions, String.Empty)); 
            // Determine validity of the structure first
            if (requestUriParts.IsAccountRequest)
            {
                if (sasVersion < StorageServiceVersions.Version_2015_04_05 || !resourceType.Contains('s'))
                {
                    // SAS keys are not valid for account operations before 2015-04-05
                    return false;
                }
            }
            else if (requestUriParts.IsContainerRequest)
            {
                if (resourceType != "c")
                {
                    return false;
                }
            }
            else if (requestUriParts.IsBlobRequest)
            {
                if (resourceType.IndexOfAny(new[] { 'c', 'b', 'o' }) == -1)
                {
                    return false;
                }
            }
            if (!accountSas)
            {
                var storedPolicyId = queryParams.Value<string>(ParamStoredPolicy);
                if (!String.IsNullOrWhiteSpace(storedPolicyId))
                {
                    // Validate that we're not duplicating values for both stored access policy & url
                    // Allow stored policy to the specified from a different container for test purposes - this isn't a security violation as it must come from the same account.
                    var aclContainer = headers.Value("StoredPolicyContainer", String.Empty);
                    if (String.IsNullOrEmpty(aclContainer))
                    {
                        aclContainer = requestUriParts.Container;
                    }
                    var storedPolicy = await GetStoredPolicyForContainer(aclContainer, 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;
                }
            }
            else
            {
                if (!queryParams.Value<string>(ParamServices, String.Empty).Contains('b'))
                {
                    // SAS must include blob service
                    return false;
                }
            }
            if (!expiry.HasValue || permissions == SharedAccessBlobPermissions.None)
            {
                return false;
            }
            else if (!ignoreRequestAge && (start.Value > DateTimeOffset.UtcNow || expiry.Value < DateTimeOffset.UtcNow))
            {
                return false;
            }
            else if (!IsProtocolMatched(request, queryParams.Value<string>(ParamProtocol)))
            {
                return false;
            }
            else if (!IsSourceAddressInRange(request, queryParams.Value<string>(ParamSourceIP)))
            {
                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:
                case StorageOperationTypes.GetBlobServiceProperties:
                case StorageOperationTypes.GetBlobServiceStats:
                case StorageOperationTypes.GetContainerProperties:
                case StorageOperationTypes.GetContainerMetadata:
                    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:
                case StorageOperationTypes.SetBlobServiceProperties:
                case StorageOperationTypes.CreateContainer:
                case StorageOperationTypes.SetContainerMetadata:
                case StorageOperationTypes.LeaseContainer:
                    if (!permissions.IsFlagSet(SharedAccessBlobPermissions.Write))
                    {
                        return false;
                    }
                    break;

                case StorageOperationTypes.DeleteBlob:
                case StorageOperationTypes.DeleteContainer:
                    if (!permissions.IsFlagSet(SharedAccessBlobPermissions.Delete))
                    {
                        return false;
                    }
                    break;

                case StorageOperationTypes.ListBlobs:
                case StorageOperationTypes.ListContainers:
                    if (!permissions.IsFlagSet(SharedAccessBlobPermissions.List))
                    {
                        return false;
                    }
                    break;

                default:
                    // All other operations are not supported by SAS uris
                    return false;
            }
            Func<string> stringToSignFactory = null;
            if (!accountSas)
            {
                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, sasVersion),
                                                                    queryParams.Value<string>(ParamStoredPolicy));
                Func<string> ipAndProtocolSnippet = () => String.Format("{0}\n{1}\n",
                                                                    queryParams.Value<string>(ParamSourceIP),
                                                                    queryParams.Value<string>(ParamProtocol));
                Func<string> v2012_02_12StringToSign = () => String.Format("{0}\n{1}{2}",
                                                                            baseStringToSign(),
                                                                            sasVersion >= StorageServiceVersions.Version_2015_04_05 ? ipAndProtocolSnippet() : String.Empty,
                                                                            sasVersion.ToVersionString());

                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));
                }
            }
            else
            {
                stringToSignFactory = () => String.Format("{0}\n{1}\n{2}\n{3}\n{4}\n{5}\n{6}\n{7}\n{8}\n",
                                                                SharedKeySignature.AccountName,
                                                                queryParams.Value<string>(ParamPermissions),
                                                                queryParams.Value<string>(ParamServices),
                                                                queryParams.Value<string>(ParamResourceTypeEx),
                                                                queryParams.Value<string>(ParamStartTime),
                                                                queryParams.Value<string>(ParamExpiryTime),
                                                                queryParams.Value<string>(ParamSourceIP),
                                                                queryParams.Value<string>(ParamProtocol),
                                                                sasVersion.ToVersionString());
            }
            string signature = queryParams.Value<string>(ParamSignature);
            var usingPrimaryKey = new[] { true, false };
            string stringToSign = stringToSignFactory();
            int matchIndex = Array.FindIndex(usingPrimaryKey, usePrimaryKey => VerifySignature(signature, usePrimaryKey, stringToSign));
            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;
        }
Exemple #30
0
 static bool IsSourceAddressInRange(IHttpRequestWrapper request, string ipAddressOrRange)
 {
     // We don't have a reliable way of determining the client address (headers can be spoofed),
     // therefore we only do structural checks here without actually verifying the address.
     if (String.IsNullOrWhiteSpace(ipAddressOrRange))
     {
         return true;
     }
     IPAddress parseCheck;
     var parts = ipAddressOrRange.Split('-');
     if (parts.Length <= 2)
     {
         return parts.All(address => IPAddress.TryParse(address, out parseCheck));
     }
     return false;
 }
Exemple #31
0
 static bool IsProtocolMatched(IHttpRequestWrapper request, string allowedProtocols)
 {
     if (String.IsNullOrWhiteSpace(allowedProtocols))
     {
         return true;
     }
     string requestScheme = request.Url.Scheme.ToLowerInvariant();
     return allowedProtocols.Split(',')
         .Any(protocol => String.Equals(requestScheme, protocol, StringComparison.OrdinalIgnoreCase));
 }
Exemple #32
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);
        }
 public MatchController()
 {
     _httpRequestWrapper = new HttpRequestWrapper(new TokenBasedRequestWrapper());
 }
Exemple #34
0
 public static StorageOperationTypes GetBlobOperation(IHttpRequestWrapper request)
 {
     return(GetBlobOperation(request.HttpMethod, request.UriParts, request.QueryParameters, request.Headers));
 }
        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;
        }
Exemple #36
0
 public StaticDataController()
 {
     _httpRequestWrapper = new HttpRequestWrapper(new TokenBasedRequestWrapper());
 }
Exemple #37
0
 public abstract Task <byte[]> CreateResponseAsync(IHttpRequestWrapper request, byte[] requestBody, IHttpResponseWrapper response, string endpointDirectory);
Exemple #38
0
        public static bool IsAuthorized(IHttpRequestWrapper request, RequestHeaders headers, RequestQueryParameters queryParams, bool ignoreRequestAge)
        {
            // Quick request age check
            var authHeader = headers.Value<string>("Authorization");
            DateTimeOffset requestVersion = headers.RequestVersion;
            string requestDateHeader = headers.Value<string>("x-ms-date");
            string dateHeader = String.Empty;
            if (String.IsNullOrWhiteSpace(requestDateHeader))
            {
                requestDateHeader = headers.Value<string>("Date");
                dateHeader = requestDateHeader;
            }
            if (String.IsNullOrWhiteSpace(requestDateHeader))
            {
                // One of the date headers is mandatory
                return false;
            }
            if (!ignoreRequestAge)
            {
                DateTime requestDate;
                if (!DateTime.TryParse(requestDateHeader, out requestDate))
                {
                    return false;
                }
                else if (requestDate < DateTime.Now.AddMinutes(-15))
                {
                    return false;
                }
            }
            var parts = authHeader.Split(' ', ':');
            if (parts.Length != 3)
            {
                return false;
            }
            else if (parts[0] != SharedKeySignature.AlgorithmSharedKey && parts[0] != SharedKeySignature.AlgorithmSharedKeyLite)
            {
                return false;
            }
            var account = parts[1];
            var signature = parts[2];

            if (!String.Equals(account, SharedKeySignature.AccountName, StringComparison.OrdinalIgnoreCase))
            {
                return false;
            }
            // We have to deal with multiple encodings (the spec is a bit ambiguous on what an 'encoded' path actually is).
            // Only run the validation if the encodings result in different strings
            var requestUriParts = request.UriParts;
            var pathsToCheck = new List<string>() { requestUriParts.OriginalUriPath };
            var unencodedPath = requestUriParts.PublicUriPath;
            if (unencodedPath != pathsToCheck[0])
            {
                pathsToCheck.Add(unencodedPath);
            }
            var alternateEncodingPaths = AlternateEncodeString(pathsToCheck[0]);
            if (alternateEncodingPaths != null)
            {
                pathsToCheck.AddRange(alternateEncodingPaths);
            }
            // For some verbs we can't tell if the Content-Length header was specified as 0 or that IIS/UrlRewrite/ASP.NET has constructed
            // the header value for us. The difference is significant to the signature as content length is included for SharedKey.
            // The ambiguity has been resolved in version 2015-02-21
            bool fullKeyAlgorithm = parts[0] == SharedKeySignature.AlgorithmSharedKey;
            bool runBlankContentLengthComparison = false;
            string method = request.HttpMethod.ToUpper();
            var contentLength = headers.Value("Content-Length", String.Empty);
            if (fullKeyAlgorithm)
            {
                int length;
                if (!int.TryParse(contentLength, out length) || length <= 0)
                {
                    if (requestVersion >= StorageServiceVersions.Version_2015_02_21)
                    {
                        // This version made it explicit what to do with 0 content-length
                        contentLength = String.Empty;
                        runBlankContentLengthComparison = false;
                    }
                    else
                    {
                        // Preserve a Content-Length: 0 header for PUT methods
                        runBlankContentLengthComparison = !method.Equals(WebRequestMethods.Http.Put, StringComparison.OrdinalIgnoreCase);
                    }
                }
            }
            var stringsToCheck = pathsToCheck.SelectMany(uriPath => new[] {
                    runBlankContentLengthComparison ? Tuple.Create(uriPath, String.Empty) : null,
                    Tuple.Create(uriPath, contentLength),
                })
                .Where(check => check != null)
                .Select(pathWithContentLength => GetStringToSign(!fullKeyAlgorithm, method, pathWithContentLength.Item1, headers, queryParams, dateHeader, pathWithContentLength.Item2))
                .SelectMany(stringToSign => new[] { Tuple.Create(true, stringToSign), Tuple.Create(false, stringToSign) });
            var evaluationResult = stringsToCheck
                .FirstOrDefault(validatationCheck => VerifyRequestAuthorization(signature, validatationCheck.Item1, validatationCheck.Item2));
            if (evaluationResult != null)
            {
                // Remember the Auth Scheme & Key for when we have to sign the response
                request.AuthenticationScheme = parts[0];
                request.AuthenticationKey = evaluationResult.Item1 ? SharedKeySignature.PrimaryAccountKey : SharedKeySignature.SecondaryAccountKey;
                return true;
            }
            DashTrace.TraceWarning("Failed to authenticate SharedKey request: {0}:{1}:{2}:{3}:{4}", parts[0], account, method, request.Url, signature);

            return false;
        }
Exemple #39
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);
         });
 }
Exemple #40
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);
     }));
 }
 /// <summary>
 /// Initializes a new instance of the <see cref="AlignSessionWithFormsAuthentication"/> class.
 /// </summary>
 /// <param name="httpRequest">The HTTP request.</param>
 /// <param name="amplaSessionStorage">The ampla session storage.</param>
 /// <param name="formsAuthenticationService">The forms authentication service.</param>
 public AlignSessionWithFormsAuthentication(IHttpRequestWrapper httpRequest, IAmplaSessionStorage amplaSessionStorage, IFormsAuthenticationService formsAuthenticationService)
 {
     this.httpRequest                = httpRequest;
     this.amplaSessionStorage        = amplaSessionStorage;
     this.formsAuthenticationService = formsAuthenticationService;
 }
Exemple #42
0
        public static async Task<HandlerResult> AbortCopyBlobAsync(IHttpRequestWrapper requestWrapper, string destContainer, string destBlob, string copyId)
        {
            return await WebOperationRunner.DoHandlerAsync("BlobHandler.AbortCopyBlobAsync", async () =>
                {
                    var destNamespaceBlob = await NamespaceHandler.FetchNamespaceBlobAsync(destContainer, destBlob);

                    var destCloudBlob = NamespaceHandler.GetBlobByName(
                        DashConfiguration.GetDataAccountByAccountName(destNamespaceBlob.PrimaryAccountName), 
                        destContainer, 
                        destBlob);
                    await destCloudBlob.AbortCopyAsync(copyId);
                    return new HandlerResult
                    {
                        StatusCode = HttpStatusCode.NoContent,
                    };
                });
        }
Exemple #43
0
 public static StorageOperationTypes GetBlobOperation(IHttpRequestWrapper request)
 {
     return GetBlobOperation(request.HttpMethod, request.UriParts, request.QueryParameters, request.Headers);
 }
Exemple #44
0
 public static async Task<HandlerResult> CopyBlobAsync(IHttpRequestWrapper requestWrapper, string destContainer, string destBlob, string source)
 {
     return await WebOperationRunner.DoHandlerAsync(String.Format("BlobHandler.CopyBlobAsync: {0}/{1} from {2}", destContainer, destBlob, source), async () =>
         {
             // source is a naked URI supplied by client
             Uri sourceUri;
             if (Uri.TryCreate(source, UriKind.RelativeOrAbsolute, out sourceUri))
             {
                 string sourceContainer = String.Empty;
                 string sourceBlobName = String.Empty;
                 string sourceQuery = String.Empty;
                 BlobType sourceBlobType = BlobType.BlockBlob;
                 var requestVersion = requestWrapper.Headers.RequestVersion;
                 bool processRelativeSource = false;
                 if (!sourceUri.IsAbsoluteUri)
                 {
                     if (requestVersion >= StorageServiceVersions.Version_2012_02_12)
                     {
                         // 2012-02-12 onwards doesn't accept relative URIs
                         return new HandlerResult
                         {
                             StatusCode = HttpStatusCode.BadRequest,
                         };
                     }
                     // Make sourceUri absolute here because a bunch of Uri functionality fails for relative URIs
                     sourceUri = new Uri(new Uri("http://dummyhost"), sourceUri);
                     processRelativeSource = true;
                 }
                 if (processRelativeSource ||
                     Uri.Compare(sourceUri, requestWrapper.Url, UriComponents.StrongAuthority, UriFormat.Unescaped, StringComparison.OrdinalIgnoreCase) == 0)
                 {
                     var segments = PathUtils.GetPathSegments(sourceUri);
                     if (processRelativeSource)
                     {
                         // Blob in named container: /accountName/containerName/blobName
                         // Snapshot in named container: /accountName/containerName/blobName?snapshot=<DateTime>
                         // Blob in root container: /accountName/blobName
                         // Snapshot in root container: /accountName/blobName?snapshot=<DateTime>
                         if (!String.Equals(segments.FirstOrDefault(), DashConfiguration.AccountName))
                         {
                             return new HandlerResult
                             {
                                 StatusCode = HttpStatusCode.BadRequest,
                                 ErrorInformation = new DashErrorInformation
                                 {
                                     ErrorCode = "CopyAcrossAccountsNotSupported",
                                     ErrorMessage = "The copy source account and destination account must be the same.",
                                 },
                             };
                         }
                         if (segments.Count() == 2)
                         {
                             sourceContainer = "root";
                             sourceBlobName = segments.ElementAt(1);
                         }
                         else if (segments.Count() > 2)
                         {
                             sourceContainer = segments.ElementAt(1);
                             sourceBlobName = PathUtils.CombinePathSegments(segments.Skip(2));
                         }
                     }
                     else
                     {
                         sourceContainer = segments.FirstOrDefault();
                         sourceBlobName = PathUtils.CombinePathSegments(segments.Skip(1));
                     }
                 }
                 var destNamespaceBlob = await NamespaceHandler.FetchNamespaceBlobAsync(destContainer, destBlob);
                 string destAccount = String.Empty;
                 if (!String.IsNullOrEmpty(sourceContainer) && !String.IsNullOrEmpty(sourceBlobName))
                 {
                     var sourceQueryParams = HttpUtility.ParseQueryString(sourceUri.Query);
                     var sourceNamespaceBlob = await NamespaceHandler.FetchNamespaceBlobAsync(sourceContainer, sourceBlobName, sourceQueryParams["snapshot"]);
                     if (!await sourceNamespaceBlob.ExistsAsync())
                     {
                         // This isn't actually documented (what happens when the source doesn't exist), but by obervation the service emits 404
                         return new HandlerResult
                         {
                             StatusCode = HttpStatusCode.NotFound,
                         };
                     }
                     var sourceCloudContainer = NamespaceHandler.GetContainerByName(
                         DashConfiguration.GetDataAccountByAccountName(sourceNamespaceBlob.PrimaryAccountName), sourceContainer);
                     sourceBlobType = sourceCloudContainer.GetBlobReferenceFromServer(sourceBlobName).BlobType;
                     // This is effectively an intra-account copy which is expected to be atomic. Therefore, even if the destination already
                     // exists, we need to place the destination in the same data account as the source.
                     // If the destination blob already exists, we delete it below to prevent an orphaned data blob
                     destAccount = sourceNamespaceBlob.PrimaryAccountName;
                     var sourceUriBuilder = ControllerOperations.GetRedirectUriBuilder(HttpMethod.Get.Method,
                         requestWrapper.Url.Scheme,
                         DashConfiguration.GetDataAccountByAccountName(destAccount),
                         sourceContainer,
                         sourceBlobName,
                         false,
                         String.Empty);
                     sourceUri = sourceUriBuilder.Uri;
                 }
                 else if (await destNamespaceBlob.ExistsAsync())
                 {
                     destAccount = destNamespaceBlob.PrimaryAccountName;
                 }
                 else
                 {
                     destAccount = NamespaceHandler.GetDataStorageAccountForBlob(destBlob).Credentials.AccountName;
                 }
                 bool replicateDestination = false;
                 if (await destNamespaceBlob.ExistsAsync() && destNamespaceBlob.PrimaryAccountName != destAccount)
                 {
                     // Delete the existing blob to prevent orphaning it
                     replicateDestination = destNamespaceBlob.IsReplicated;
                     var dataBlob = NamespaceHandler.GetBlobByName(
                         DashConfiguration.GetDataAccountByAccountName(destNamespaceBlob.PrimaryAccountName), destContainer, destBlob);
                     await dataBlob.DeleteIfExistsAsync();
                 }
                 destNamespaceBlob.PrimaryAccountName = destAccount;
                 destNamespaceBlob.Container = destContainer;
                 destNamespaceBlob.BlobName = destBlob;
                 destNamespaceBlob.IsMarkedForDeletion = false;
                 await destNamespaceBlob.SaveAsync();
                 // Now that we've got the metadata tucked away - do the actual copy
                 var destCloudContainer = NamespaceHandler.GetContainerByName(DashConfiguration.GetDataAccountByAccountName(destAccount), destContainer);
                 ICloudBlob destCloudBlob = null;
                 if (sourceBlobType == BlobType.PageBlob)
                 {
                     destCloudBlob = destCloudContainer.GetPageBlobReference(destBlob);
                 }
                 else
                 {
                     destCloudBlob = destCloudContainer.GetBlockBlobReference(destBlob);
                 }
                 // Storage client will retry failed copy. Let our clients decide that.
                 var copyId = await destCloudBlob.StartCopyFromBlobAsync(sourceUri,
                     AccessCondition.GenerateEmptyCondition(),
                     AccessCondition.GenerateEmptyCondition(),
                     new BlobRequestOptions
                     {
                         RetryPolicy = new NoRetry(),
                     },
                     new OperationContext());
                 // Check if we should replicate the copied destination blob
                 if (replicateDestination || BlobReplicationHandler.ShouldReplicateBlob(requestWrapper.Headers, destContainer, destBlob))
                 {
                     await BlobReplicationHandler.EnqueueBlobReplicationAsync(destNamespaceBlob, false);
                 }
                 return new HandlerResult
                 {
                     StatusCode = requestVersion >= StorageServiceVersions.Version_2012_02_12 ? HttpStatusCode.Accepted : HttpStatusCode.Created,
                     Headers = new ResponseHeaders(new[] 
                     {
                         new KeyValuePair<string, string>("x-ms-copy-id", copyId),
                         new KeyValuePair<string, string>("x-ms-copy-status", destCloudBlob.CopyState.Status == CopyStatus.Success ? "success" : "pending"),
                     })
                 };
             }
             return new HandlerResult
             {
                 StatusCode = HttpStatusCode.BadRequest,
             };
         });
 }
Exemple #45
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);
        }
Exemple #46
0
        public static async Task <BlobListResults> GetBlobListing(string container, IHttpRequestWrapper request)
        {
            // Extract query parameters
            var  queryParams           = request.QueryParameters;
            var  prefix                = queryParams.Value <string>("prefix");
            var  delim                 = queryParams.Value <string>("delimiter");
            var  marker                = queryParams.Value <string>("marker");
            var  indicatedMaxResults   = queryParams.Value <string>("maxresults");
            var  maxResults            = queryParams.Value("maxresults", 5000);
            var  includedDataSets      = String.Join(",", queryParams.Values <string>("include"));
            bool isHierarchicalListing = !String.IsNullOrWhiteSpace(delim);

            var blobTasks = DashConfiguration.DataAccounts
                            .Select(account => ChildBlobListAsync(account, container, prefix, delim, includedDataSets));
            var namespaceTask = ChildBlobListAsync(DashConfiguration.NamespaceAccount, container, prefix, delim, BlobListingDetails.Metadata.ToString());
            var blobs         = await Task.WhenAll(blobTasks);

            var namespaceBlobs = await namespaceTask;
            Func <string, IListBlobItem, string> makeBlobKeyWithAccount = (account, blob) => String.Format("{0}|{1}", account, blob.Uri.AbsolutePath);
            Func <IListBlobItem, string>         makeBlobKey            = (blob) => makeBlobKeyWithAccount(blob.Uri.Host.Substring(0, blob.Uri.Host.IndexOf('.')), blob);
            Func <NamespaceListWrapper, string>  makeWrapperKey         = (wrapper) => makeBlobKeyWithAccount(wrapper.IsDirectory ? String.Empty : wrapper.NamespaceBlob.PrimaryAccountName, wrapper.SourceItem);
            var sortedDataBlobs = blobs
                                  .SelectMany(blobList => blobList)
                                  .Select(blob => new {
                Blob       = blob,
                SortMarker = GetRawMarkerForBlob(blob),
            })
                                  .OrderBy(blob => blob.SortMarker, StringComparer.Ordinal)
                                  .SkipWhile(blob => !String.IsNullOrWhiteSpace(marker) && GetMarker(blob.SortMarker) != marker);
            // We're creating a lookup list to merge with the ns blobs. We do 2 passes here - one for actual blobs & a second for directories
            // (if applicable) as we don't have a specific account for directories - we just care they exist in the data space somewhere.
            // We also have to handle the snapshot case where there will only be 1 ns entry but multiple blobs (the current one & n snapshots)
            var dataBlobsLookup = sortedDataBlobs
                                  .Where(blob => blob.Blob is ICloudBlob)
                                  .ToLookup(blob => makeBlobKey(blob.Blob), StringComparer.OrdinalIgnoreCase)
                                  .ToDictionary(group => group.Key, group => group.Select(blob => blob.Blob), StringComparer.OrdinalIgnoreCase);

            if (isHierarchicalListing)
            {
                foreach (var directory in sortedDataBlobs
                         .Select(blob => blob.Blob)
                         .Where(blob => blob is CloudBlobDirectory)
                         .ToLookup(directory => ((CloudBlobDirectory)directory).Prefix, StringComparer.OrdinalIgnoreCase))
                {
                    dataBlobsLookup[makeBlobKeyWithAccount(String.Empty, directory.First())] = Enumerable.Repeat(directory.First(), 1);
                }
            }
            var joinedList = namespaceBlobs
                             .Select(nsBlob => new {
                BlobWrapper = new NamespaceListWrapper(nsBlob),
                SortMarker  = GetRawMarkerForBlob(nsBlob),
            })
                             .Where(nsBlob => nsBlob.BlobWrapper.IsDirectory || !nsBlob.BlobWrapper.NamespaceBlob.IsMarkedForDeletion)
                             .OrderBy(nsBlob => nsBlob.SortMarker, StringComparer.Ordinal)
                             .SkipWhile(nsBlob => !String.IsNullOrWhiteSpace(marker) && GetMarker(nsBlob.SortMarker) != marker)
                             .Where(nsBlob => dataBlobsLookup.ContainsKey(makeWrapperKey(nsBlob.BlobWrapper)))
                             .SelectMany(nsBlob => dataBlobsLookup[makeWrapperKey(nsBlob.BlobWrapper)]
                                         .Select(dataBlob => new {
                DataBlob         = dataBlob,
                NamespaceWrapper = nsBlob.BlobWrapper,
            }));

            if (isHierarchicalListing)
            {
                // For a hierarchical listing we have to deal with a corner case in that we may have orphaned replicated data blobs
                // included in the data accounts listing as well as being included in the namespace listing. However, if there's
                // nothing but deleted blobs in the namespace for that directory, we should omit the directory.
                // This is required to mitigate our eventual consistency model for replication.
                var validDirectoryTasks = joinedList
                                          .Where(blobPair => blobPair.NamespaceWrapper.IsDirectory)
                                          .Select(blobPair => IsValidNamespaceDirectoryAsync((CloudBlobDirectory)blobPair.NamespaceWrapper.SourceItem));
                var validDirectories = new HashSet <string>((await Task.WhenAll(validDirectoryTasks))
                                                            .Where(directory => directory.Item2)
                                                            .Select(directory => directory.Item1.Prefix),
                                                            StringComparer.OrdinalIgnoreCase);
                joinedList = joinedList
                             .Where(blobPair => !blobPair.NamespaceWrapper.IsDirectory || validDirectories.Contains(blobPair.NamespaceWrapper.Prefix));
            }
            var resultsList = joinedList
                              .Take(maxResults + 1)    // Get an extra listing so that we can generate the nextMarker
                              .Select(blobPair => blobPair.DataBlob);

            return(new BlobListResults
            {
                RequestVersion = request.Headers.Value("x-ms-version", StorageServiceVersions.Version_2009_09_19),
                ServiceEndpoint = request.Url.GetComponents(UriComponents.SchemeAndServer, UriFormat.Unescaped),
                ContainerName = container,
                MaxResults = maxResults,
                IndicatedMaxResults = indicatedMaxResults,
                Delimiter = delim,
                Marker = marker,
                Prefix = prefix,
                Blobs = resultsList,
                IncludeDetails = String.IsNullOrWhiteSpace(includedDataSets) ? BlobListingDetails.None : (BlobListingDetails)Enum.Parse(typeof(BlobListingDetails), includedDataSets, true),
            });
        }
Exemple #47
0
 public static async Task <HandlerResult> CopyBlobAsync(IHttpRequestWrapper requestWrapper, string destContainer, string destBlob, string source)
 {
     return(await WebOperationRunner.DoHandlerAsync(String.Format("BlobHandler.CopyBlobAsync: {0}/{1} from {2}", destContainer, destBlob, source), async() =>
     {
         // source is a naked URI supplied by client
         Uri sourceUri;
         if (Uri.TryCreate(source, UriKind.RelativeOrAbsolute, out sourceUri))
         {
             string sourceContainer = String.Empty;
             string sourceBlobName = String.Empty;
             string sourceQuery = String.Empty;
             BlobType sourceBlobType = BlobType.BlockBlob;
             var requestVersion = requestWrapper.Headers.Value("x-ms-version", StorageServiceVersions.Version_2009_09_19);
             bool processRelativeSource = false;
             if (!sourceUri.IsAbsoluteUri)
             {
                 if (requestVersion >= StorageServiceVersions.Version_2012_02_12)
                 {
                     // 2012-02-12 onwards doesn't accept relative URIs
                     return new HandlerResult
                     {
                         StatusCode = HttpStatusCode.BadRequest,
                     };
                 }
                 // Make sourceUri absolute here because a bunch of Uri functionality fails for relative URIs
                 sourceUri = new Uri(new Uri("http://dummyhost"), sourceUri);
                 processRelativeSource = true;
             }
             if (processRelativeSource ||
                 (String.Equals(sourceUri.Host, requestWrapper.Url.Host, StringComparison.OrdinalIgnoreCase) &&
                  ((sourceUri.IsDefaultPort && requestWrapper.Url.IsDefaultPort) || (sourceUri.Port == requestWrapper.Url.Port))))
             {
                 var segments = PathUtils.GetPathSegments(sourceUri.AbsolutePath);
                 if (processRelativeSource)
                 {
                     // Blob in named container: /accountName/containerName/blobName
                     // Snapshot in named container: /accountName/containerName/blobName?snapshot=<DateTime>
                     // Blob in root container: /accountName/blobName
                     // Snapshot in root container: /accountName/blobName?snapshot=<DateTime>
                     if (!String.Equals(segments.FirstOrDefault(), DashConfiguration.AccountName))
                     {
                         return new HandlerResult
                         {
                             StatusCode = HttpStatusCode.BadRequest,
                             ErrorInformation = new DashErrorInformation
                             {
                                 ErrorCode = "CopyAcrossAccountsNotSupported",
                                 ErrorMessage = "The copy source account and destination account must be the same.",
                             },
                         };
                     }
                     if (segments.Count() == 2)
                     {
                         sourceContainer = "root";
                         sourceBlobName = segments[1];
                     }
                     else if (segments.Count() > 2)
                     {
                         sourceContainer = segments[1];
                         sourceBlobName = PathUtils.CombinePathSegments(segments.Skip(2));
                     }
                 }
                 else
                 {
                     sourceContainer = segments.FirstOrDefault();
                     sourceBlobName = PathUtils.CombinePathSegments(segments.Skip(1));
                 }
             }
             var destNamespaceBlob = await NamespaceHandler.FetchNamespaceBlobAsync(destContainer, destBlob);
             string destAccount = String.Empty;
             if (!String.IsNullOrEmpty(sourceContainer) && !String.IsNullOrEmpty(sourceBlobName))
             {
                 var sourceQueryParams = HttpUtility.ParseQueryString(sourceUri.Query);
                 var sourceNamespaceBlob = await NamespaceHandler.FetchNamespaceBlobAsync(sourceContainer, sourceBlobName, sourceQueryParams["snapshot"]);
                 if (!await sourceNamespaceBlob.ExistsAsync())
                 {
                     // This isn't actually documented (what happens when the source doesn't exist), but by obervation the service emits 404
                     return new HandlerResult
                     {
                         StatusCode = HttpStatusCode.NotFound,
                     };
                 }
                 var sourceCloudContainer = NamespaceHandler.GetContainerByName(
                     DashConfiguration.GetDataAccountByAccountName(sourceNamespaceBlob.PrimaryAccountName), sourceContainer);
                 sourceBlobType = sourceCloudContainer.GetBlobReferenceFromServer(sourceBlobName).BlobType;
                 // This is effectively an intra-account copy which is expected to be atomic. Therefore, even if the destination already
                 // exists, we need to place the destination in the same data account as the source.
                 // If the destination blob already exists, we delete it below to prevent an orphaned data blob
                 destAccount = sourceNamespaceBlob.PrimaryAccountName;
                 var sourceUriBuilder = ControllerOperations.GetRedirectUriBuilder("GET",
                                                                                   requestWrapper.Url.Scheme,
                                                                                   DashConfiguration.GetDataAccountByAccountName(destAccount),
                                                                                   sourceContainer,
                                                                                   sourceBlobName,
                                                                                   false,
                                                                                   String.Empty);
                 sourceUri = sourceUriBuilder.Uri;
             }
             else if (await destNamespaceBlob.ExistsAsync())
             {
                 destAccount = destNamespaceBlob.PrimaryAccountName;
             }
             else
             {
                 destAccount = NamespaceHandler.GetDataStorageAccountForBlob(destBlob).Credentials.AccountName;
             }
             bool replicateDestination = false;
             if (await destNamespaceBlob.ExistsAsync() && destNamespaceBlob.PrimaryAccountName != destAccount)
             {
                 // Delete the existing blob to prevent orphaning it
                 replicateDestination = destNamespaceBlob.IsReplicated;
                 var dataBlob = NamespaceHandler.GetBlobByName(
                     DashConfiguration.GetDataAccountByAccountName(destNamespaceBlob.PrimaryAccountName), destContainer, destBlob);
                 await dataBlob.DeleteIfExistsAsync();
             }
             destNamespaceBlob.PrimaryAccountName = destAccount;
             destNamespaceBlob.Container = destContainer;
             destNamespaceBlob.BlobName = destBlob;
             destNamespaceBlob.IsMarkedForDeletion = false;
             await destNamespaceBlob.SaveAsync();
             // Now that we've got the metadata tucked away - do the actual copy
             var destCloudContainer = NamespaceHandler.GetContainerByName(DashConfiguration.GetDataAccountByAccountName(destAccount), destContainer);
             ICloudBlob destCloudBlob = null;
             if (sourceBlobType == BlobType.PageBlob)
             {
                 destCloudBlob = destCloudContainer.GetPageBlobReference(destBlob);
             }
             else
             {
                 destCloudBlob = destCloudContainer.GetBlockBlobReference(destBlob);
             }
             // Storage client will retry failed copy. Let our clients decide that.
             var copyId = await destCloudBlob.StartCopyFromBlobAsync(sourceUri,
                                                                     AccessCondition.GenerateEmptyCondition(),
                                                                     AccessCondition.GenerateEmptyCondition(),
                                                                     new BlobRequestOptions
             {
                 RetryPolicy = new NoRetry(),
             },
                                                                     new OperationContext());
             // Check if we should replicate the copied destination blob
             if (replicateDestination || BlobReplicationHandler.ShouldReplicateBlob(requestWrapper.Headers, destContainer, destBlob))
             {
                 await BlobReplicationHandler.EnqueueBlobReplicationAsync(destNamespaceBlob, false);
             }
             return new HandlerResult
             {
                 StatusCode = requestVersion >= StorageServiceVersions.Version_2012_02_12 ? HttpStatusCode.Accepted : HttpStatusCode.Created,
                 Headers = new ResponseHeaders(new[]
                 {
                     new KeyValuePair <string, string>("x-ms-copy-id", copyId),
                     new KeyValuePair <string, string>("x-ms-copy-status", destCloudBlob.CopyState.Status == CopyStatus.Success ? "success" : "pending"),
                 })
             };
         }
         return new HandlerResult
         {
             StatusCode = HttpStatusCode.BadRequest,
         };
     }));
 }
Exemple #48
0
 public static HandlerResult Redirect(IHttpRequestWrapper requestWrapper, Uri location)
 {
     return(Redirect(requestWrapper, location.ToString()));
 }
Exemple #49
0
 public static HandlerResult Redirect(IHttpRequestWrapper requestWrapper, Uri location)
 {
     return Redirect(requestWrapper, location.ToString());
 }
        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;
        }