queuesasuri.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  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. // QueueSASOptions are options to construct a blob SAS
  23. // URI.
  24. // See https://docs.microsoft.com/en-us/rest/api/storageservices/constructing-a-service-sas
  25. type QueueSASOptions struct {
  26. QueueSASPermissions
  27. SASOptions
  28. }
  29. // QueueSASPermissions includes the available permissions for
  30. // a queue SAS URI.
  31. type QueueSASPermissions struct {
  32. Read bool
  33. Add bool
  34. Update bool
  35. Process bool
  36. }
  37. func (q QueueSASPermissions) buildString() string {
  38. permissions := ""
  39. if q.Read {
  40. permissions += "r"
  41. }
  42. if q.Add {
  43. permissions += "a"
  44. }
  45. if q.Update {
  46. permissions += "u"
  47. }
  48. if q.Process {
  49. permissions += "p"
  50. }
  51. return permissions
  52. }
  53. // GetSASURI creates an URL to the specified queue which contains the Shared
  54. // Access Signature with specified permissions and expiration time.
  55. //
  56. // See https://docs.microsoft.com/en-us/rest/api/storageservices/constructing-a-service-sas
  57. func (q *Queue) GetSASURI(options QueueSASOptions) (string, error) {
  58. canonicalizedResource, err := q.qsc.client.buildCanonicalizedResource(q.buildPath(), q.qsc.auth, true)
  59. if err != nil {
  60. return "", err
  61. }
  62. // "The canonicalizedresouce portion of the string is a canonical path to the signed resource.
  63. // It must include the service name (blob, table, queue or file) for version 2015-02-21 or
  64. // later, the storage account name, and the resource name, and must be URL-decoded.
  65. // -- https://msdn.microsoft.com/en-us/library/azure/dn140255.aspx
  66. // 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).
  67. canonicalizedResource = strings.Replace(canonicalizedResource, "+", "%2b", -1)
  68. canonicalizedResource, err = url.QueryUnescape(canonicalizedResource)
  69. if err != nil {
  70. return "", err
  71. }
  72. signedStart := ""
  73. if options.Start != (time.Time{}) {
  74. signedStart = options.Start.UTC().Format(time.RFC3339)
  75. }
  76. signedExpiry := options.Expiry.UTC().Format(time.RFC3339)
  77. protocols := "https,http"
  78. if options.UseHTTPS {
  79. protocols = "https"
  80. }
  81. permissions := options.QueueSASPermissions.buildString()
  82. stringToSign, err := queueSASStringToSign(q.qsc.client.apiVersion, canonicalizedResource, signedStart, signedExpiry, options.IP, permissions, protocols, options.Identifier)
  83. if err != nil {
  84. return "", err
  85. }
  86. sig := q.qsc.client.computeHmac256(stringToSign)
  87. sasParams := url.Values{
  88. "sv": {q.qsc.client.apiVersion},
  89. "se": {signedExpiry},
  90. "sp": {permissions},
  91. "sig": {sig},
  92. }
  93. if q.qsc.client.apiVersion >= "2015-04-05" {
  94. sasParams.Add("spr", protocols)
  95. addQueryParameter(sasParams, "sip", options.IP)
  96. }
  97. uri := q.qsc.client.getEndpoint(queueServiceName, q.buildPath(), nil)
  98. sasURL, err := url.Parse(uri)
  99. if err != nil {
  100. return "", err
  101. }
  102. sasURL.RawQuery = sasParams.Encode()
  103. return sasURL.String(), nil
  104. }
  105. func queueSASStringToSign(signedVersion, canonicalizedResource, signedStart, signedExpiry, signedIP, signedPermissions, protocols, signedIdentifier string) (string, error) {
  106. if signedVersion >= "2015-02-21" {
  107. canonicalizedResource = "/queue" + canonicalizedResource
  108. }
  109. // https://msdn.microsoft.com/en-us/library/azure/dn140255.aspx#Anchor_12
  110. if signedVersion >= "2015-04-05" {
  111. return fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s",
  112. signedPermissions,
  113. signedStart,
  114. signedExpiry,
  115. canonicalizedResource,
  116. signedIdentifier,
  117. signedIP,
  118. protocols,
  119. signedVersion), nil
  120. }
  121. // reference: http://msdn.microsoft.com/en-us/library/azure/dn140255.aspx
  122. if signedVersion >= "2013-08-15" {
  123. return fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s", signedPermissions, signedStart, signedExpiry, canonicalizedResource, signedIdentifier, signedVersion), nil
  124. }
  125. return "", errors.New("storage: not implemented SAS for versions earlier than 2013-08-15")
  126. }