blobsasuri.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. package storage
  2. // Copyright 2017 Microsoft Corporation
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. import (
  16. "errors"
  17. "fmt"
  18. "net/url"
  19. "strings"
  20. "time"
  21. )
  22. // OverrideHeaders defines overridable response heaedrs in
  23. // a request using a SAS URI.
  24. // See https://docs.microsoft.com/en-us/rest/api/storageservices/constructing-a-service-sas
  25. type OverrideHeaders struct {
  26. CacheControl string
  27. ContentDisposition string
  28. ContentEncoding string
  29. ContentLanguage string
  30. ContentType string
  31. }
  32. // BlobSASOptions are options to construct a blob SAS
  33. // URI.
  34. // See https://docs.microsoft.com/en-us/rest/api/storageservices/constructing-a-service-sas
  35. type BlobSASOptions struct {
  36. BlobServiceSASPermissions
  37. OverrideHeaders
  38. SASOptions
  39. }
  40. // BlobServiceSASPermissions includes the available permissions for
  41. // blob service SAS URI.
  42. type BlobServiceSASPermissions struct {
  43. Read bool
  44. Add bool
  45. Create bool
  46. Write bool
  47. Delete bool
  48. }
  49. func (p BlobServiceSASPermissions) buildString() string {
  50. permissions := ""
  51. if p.Read {
  52. permissions += "r"
  53. }
  54. if p.Add {
  55. permissions += "a"
  56. }
  57. if p.Create {
  58. permissions += "c"
  59. }
  60. if p.Write {
  61. permissions += "w"
  62. }
  63. if p.Delete {
  64. permissions += "d"
  65. }
  66. return permissions
  67. }
  68. // GetSASURI creates an URL to the blob which contains the Shared
  69. // Access Signature with the specified options.
  70. //
  71. // See https://docs.microsoft.com/en-us/rest/api/storageservices/constructing-a-service-sas
  72. func (b *Blob) GetSASURI(options BlobSASOptions) (string, error) {
  73. uri := b.GetURL()
  74. signedResource := "b"
  75. canonicalizedResource, err := b.Container.bsc.client.buildCanonicalizedResource(uri, b.Container.bsc.auth, true)
  76. if err != nil {
  77. return "", err
  78. }
  79. permissions := options.BlobServiceSASPermissions.buildString()
  80. return b.Container.bsc.client.blobAndFileSASURI(options.SASOptions, uri, permissions, canonicalizedResource, signedResource, options.OverrideHeaders)
  81. }
  82. func (c *Client) blobAndFileSASURI(options SASOptions, uri, permissions, canonicalizedResource, signedResource string, headers OverrideHeaders) (string, error) {
  83. start := ""
  84. if options.Start != (time.Time{}) {
  85. start = options.Start.UTC().Format(time.RFC3339)
  86. }
  87. expiry := options.Expiry.UTC().Format(time.RFC3339)
  88. // 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).
  89. canonicalizedResource = strings.Replace(canonicalizedResource, "+", "%2b", -1)
  90. canonicalizedResource, err := url.QueryUnescape(canonicalizedResource)
  91. if err != nil {
  92. return "", err
  93. }
  94. protocols := ""
  95. if options.UseHTTPS {
  96. protocols = "https"
  97. }
  98. stringToSign, err := blobSASStringToSign(permissions, start, expiry, canonicalizedResource, options.Identifier, options.IP, protocols, c.apiVersion, headers)
  99. if err != nil {
  100. return "", err
  101. }
  102. sig := c.computeHmac256(stringToSign)
  103. sasParams := url.Values{
  104. "sv": {c.apiVersion},
  105. "se": {expiry},
  106. "sr": {signedResource},
  107. "sp": {permissions},
  108. "sig": {sig},
  109. }
  110. if start != "" {
  111. sasParams.Add("st", start)
  112. }
  113. if c.apiVersion >= "2015-04-05" {
  114. if protocols != "" {
  115. sasParams.Add("spr", protocols)
  116. }
  117. if options.IP != "" {
  118. sasParams.Add("sip", options.IP)
  119. }
  120. }
  121. // Add override response hedaers
  122. addQueryParameter(sasParams, "rscc", headers.CacheControl)
  123. addQueryParameter(sasParams, "rscd", headers.ContentDisposition)
  124. addQueryParameter(sasParams, "rsce", headers.ContentEncoding)
  125. addQueryParameter(sasParams, "rscl", headers.ContentLanguage)
  126. addQueryParameter(sasParams, "rsct", headers.ContentType)
  127. sasURL, err := url.Parse(uri)
  128. if err != nil {
  129. return "", err
  130. }
  131. sasURL.RawQuery = sasParams.Encode()
  132. return sasURL.String(), nil
  133. }
  134. func blobSASStringToSign(signedPermissions, signedStart, signedExpiry, canonicalizedResource, signedIdentifier, signedIP, protocols, signedVersion string, headers OverrideHeaders) (string, error) {
  135. rscc := headers.CacheControl
  136. rscd := headers.ContentDisposition
  137. rsce := headers.ContentEncoding
  138. rscl := headers.ContentLanguage
  139. rsct := headers.ContentType
  140. if signedVersion >= "2015-02-21" {
  141. canonicalizedResource = "/blob" + canonicalizedResource
  142. }
  143. // https://msdn.microsoft.com/en-us/library/azure/dn140255.aspx#Anchor_12
  144. if signedVersion >= "2015-04-05" {
  145. 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
  146. }
  147. // reference: http://msdn.microsoft.com/en-us/library/azure/dn140255.aspx
  148. if signedVersion >= "2013-08-15" {
  149. 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
  150. }
  151. return "", errors.New("storage: not implemented SAS for versions earlier than 2013-08-15")
  152. }