123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175 |
- package storage
- // Copyright 2017 Microsoft Corporation
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- import (
- "errors"
- "fmt"
- "net/url"
- "strings"
- "time"
- )
- // OverrideHeaders defines overridable response heaedrs in
- // a request using a SAS URI.
- // See https://docs.microsoft.com/en-us/rest/api/storageservices/constructing-a-service-sas
- type OverrideHeaders struct {
- CacheControl string
- ContentDisposition string
- ContentEncoding string
- ContentLanguage string
- ContentType string
- }
- // BlobSASOptions are options to construct a blob SAS
- // URI.
- // See https://docs.microsoft.com/en-us/rest/api/storageservices/constructing-a-service-sas
- type BlobSASOptions struct {
- BlobServiceSASPermissions
- OverrideHeaders
- SASOptions
- }
- // BlobServiceSASPermissions includes the available permissions for
- // blob service SAS URI.
- type BlobServiceSASPermissions struct {
- Read bool
- Add bool
- Create bool
- Write bool
- Delete bool
- }
- func (p BlobServiceSASPermissions) buildString() string {
- permissions := ""
- if p.Read {
- permissions += "r"
- }
- if p.Add {
- permissions += "a"
- }
- if p.Create {
- permissions += "c"
- }
- if p.Write {
- permissions += "w"
- }
- if p.Delete {
- permissions += "d"
- }
- return permissions
- }
- // GetSASURI creates an URL to the blob which contains the Shared
- // Access Signature with the specified options.
- //
- // See https://docs.microsoft.com/en-us/rest/api/storageservices/constructing-a-service-sas
- func (b *Blob) GetSASURI(options BlobSASOptions) (string, error) {
- uri := b.GetURL()
- signedResource := "b"
- canonicalizedResource, err := b.Container.bsc.client.buildCanonicalizedResource(uri, b.Container.bsc.auth, true)
- if err != nil {
- return "", err
- }
- permissions := options.BlobServiceSASPermissions.buildString()
- return b.Container.bsc.client.blobAndFileSASURI(options.SASOptions, uri, permissions, canonicalizedResource, signedResource, options.OverrideHeaders)
- }
- func (c *Client) blobAndFileSASURI(options SASOptions, uri, permissions, canonicalizedResource, signedResource string, headers OverrideHeaders) (string, error) {
- start := ""
- if options.Start != (time.Time{}) {
- start = options.Start.UTC().Format(time.RFC3339)
- }
- expiry := options.Expiry.UTC().Format(time.RFC3339)
- // We need to replace + with %2b first to avoid being treated as a space (which is correct for query strings, but not the path component).
- canonicalizedResource = strings.Replace(canonicalizedResource, "+", "%2b", -1)
- canonicalizedResource, err := url.QueryUnescape(canonicalizedResource)
- if err != nil {
- return "", err
- }
- protocols := ""
- if options.UseHTTPS {
- protocols = "https"
- }
- stringToSign, err := blobSASStringToSign(permissions, start, expiry, canonicalizedResource, options.Identifier, options.IP, protocols, c.apiVersion, headers)
- if err != nil {
- return "", err
- }
- sig := c.computeHmac256(stringToSign)
- sasParams := url.Values{
- "sv": {c.apiVersion},
- "se": {expiry},
- "sr": {signedResource},
- "sp": {permissions},
- "sig": {sig},
- }
- if start != "" {
- sasParams.Add("st", start)
- }
- if c.apiVersion >= "2015-04-05" {
- if protocols != "" {
- sasParams.Add("spr", protocols)
- }
- if options.IP != "" {
- sasParams.Add("sip", options.IP)
- }
- }
- // Add override response hedaers
- addQueryParameter(sasParams, "rscc", headers.CacheControl)
- addQueryParameter(sasParams, "rscd", headers.ContentDisposition)
- addQueryParameter(sasParams, "rsce", headers.ContentEncoding)
- addQueryParameter(sasParams, "rscl", headers.ContentLanguage)
- addQueryParameter(sasParams, "rsct", headers.ContentType)
- sasURL, err := url.Parse(uri)
- if err != nil {
- return "", err
- }
- sasURL.RawQuery = sasParams.Encode()
- return sasURL.String(), nil
- }
- func blobSASStringToSign(signedPermissions, signedStart, signedExpiry, canonicalizedResource, signedIdentifier, signedIP, protocols, signedVersion string, headers OverrideHeaders) (string, error) {
- rscc := headers.CacheControl
- rscd := headers.ContentDisposition
- rsce := headers.ContentEncoding
- rscl := headers.ContentLanguage
- rsct := headers.ContentType
- if signedVersion >= "2015-02-21" {
- canonicalizedResource = "/blob" + canonicalizedResource
- }
- // https://msdn.microsoft.com/en-us/library/azure/dn140255.aspx#Anchor_12
- if signedVersion >= "2015-04-05" {
- return fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s", signedPermissions, signedStart, signedExpiry, canonicalizedResource, signedIdentifier, signedIP, protocols, signedVersion, rscc, rscd, rsce, rscl, rsct), nil
- }
- // reference: http://msdn.microsoft.com/en-us/library/azure/dn140255.aspx
- if signedVersion >= "2013-08-15" {
- return fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s", signedPermissions, signedStart, signedExpiry, canonicalizedResource, signedIdentifier, signedVersion, rscc, rscd, rsce, rscl, rsct), nil
- }
- return "", errors.New("storage: not implemented SAS for versions earlier than 2013-08-15")
- }
|