/// <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); }
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; }
/// <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 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 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; }
public static HandlerResult Redirect(IHttpRequestWrapper requestWrapper, string location) { return new HandlerResult { StatusCode = HttpStatusCode.Redirect, Location = location, Request = requestWrapper, }; }
public static HandlerResult Redirect(IHttpRequestWrapper requestWrapper, string location) { return(new HandlerResult { StatusCode = HttpStatusCode.Redirect, Location = location, Request = requestWrapper, }); }
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, }; })); }
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()); }
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)); }
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); }); }
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); })); }
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)); }
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); }
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)); }
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); }
static bool IsRequestAuthorized(IHttpRequestWrapper request) { return RequestAuthorization.IsRequestAuthorizedAsync(request, true).Result; }
/// <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; }
/// <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); }); }
static bool IsRequestAuthorized(IHttpRequestWrapper request) { return(RequestAuthorization.IsRequestAuthorizedAsync(request, true).Result); }
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), }; }
public abstract Task <byte[]> CreateResponseAsync(IHttpRequestWrapper request, byte[] requestBody, IHttpResponseWrapper response, Endpoint endpoint);
public CorsAccessRequest(IHttpRequestWrapper httpRequest) { this.httpRequest = httpRequest; }
public CorsAccessRequest(IHttpRequestWrapper httpRequest) { this.httpRequest = httpRequest; }
/// <summary> /// Generic function to forward blob request. Target blob must already exist. /// </summary> private async Task <HttpResponseMessage> BasicBlobHandler(string container, string blob, IHttpRequestWrapper requestWrapper, StorageOperationTypes operation) { return(await DoHandlerAsync(String.Format("BlobController.BasicBlobHandler: {0} {1}/{2}", operation, container, blob), async() => { var namespaceBlob = await NamespaceHandler.FetchNamespaceBlobAsync(container, blob); if (!await namespaceBlob.ExistsAsync()) { return this.CreateResponse(HttpStatusCode.NotFound, (RequestHeaders)null); } if (BlobReplicationOperations.DoesOperationTriggerReplication(operation) && (namespaceBlob.IsReplicated || BlobReplicationHandler.ShouldReplicateBlob(requestWrapper.Headers, namespaceBlob))) { await BlobReplicationHandler.EnqueueBlobReplicationAsync(namespaceBlob, false); } return await ForwardRequestHandler(namespaceBlob, operation); })); }
public 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; }
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; }
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)); }
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()); }
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; }
public StaticDataController() { _httpRequestWrapper = new HttpRequestWrapper(new TokenBasedRequestWrapper()); }
public abstract Task <byte[]> CreateResponseAsync(IHttpRequestWrapper request, byte[] requestBody, IHttpResponseWrapper response, string endpointDirectory);
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; }
private async Task<HttpResponseMessage> PutBlobHandler(string container, string blob, IHttpRequestWrapper requestWrapper, StorageOperationTypes operation) { return await DoHandlerAsync(String.Format("BlobController.PutBlobHandler: {0} {1}/{2}", operation, container, blob), async () => { var namespaceBlob = await NamespaceHandler.CreateNamespaceBlobAsync(container, blob); if (BlobReplicationOperations.DoesOperationTriggerReplication(operation) && BlobReplicationHandler.ShouldReplicateBlob(requestWrapper.Headers, namespaceBlob)) { await BlobReplicationHandler.EnqueueBlobReplicationAsync(namespaceBlob, false); } return await ForwardRequestHandler(namespaceBlob, operation); }); }
private async Task <HttpResponseMessage> 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; }
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, }; }); }
public static StorageOperationTypes GetBlobOperation(IHttpRequestWrapper request) { return GetBlobOperation(request.HttpMethod, request.UriParts, request.QueryParameters, request.Headers); }
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, }; }); }
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); }
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), }); }
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, }; })); }
public static HandlerResult Redirect(IHttpRequestWrapper requestWrapper, Uri location) { return(Redirect(requestWrapper, location.ToString())); }
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; }