1 package storage
2
3
4
5
6 import (
7 "errors"
8 "fmt"
9 "net/url"
10 "strings"
11 "time"
12 )
13
14
15
16
17 type OverrideHeaders struct {
18 CacheControl string
19 ContentDisposition string
20 ContentEncoding string
21 ContentLanguage string
22 ContentType string
23 }
24
25
26
27
28 type BlobSASOptions struct {
29 BlobServiceSASPermissions
30 OverrideHeaders
31 SASOptions
32 }
33
34
35
36 type BlobServiceSASPermissions struct {
37 Read bool
38 Add bool
39 Create bool
40 Write bool
41 Delete bool
42 }
43
44 func (p BlobServiceSASPermissions) buildString() string {
45 permissions := ""
46 if p.Read {
47 permissions += "r"
48 }
49 if p.Add {
50 permissions += "a"
51 }
52 if p.Create {
53 permissions += "c"
54 }
55 if p.Write {
56 permissions += "w"
57 }
58 if p.Delete {
59 permissions += "d"
60 }
61 return permissions
62 }
63
64
65
66
67
68 func (b *Blob) GetSASURI(options BlobSASOptions) (string, error) {
69 uri := b.GetURL()
70 signedResource := "b"
71 canonicalizedResource, err := b.Container.bsc.client.buildCanonicalizedResource(uri, b.Container.bsc.auth, true)
72 if err != nil {
73 return "", err
74 }
75
76 permissions := options.BlobServiceSASPermissions.buildString()
77 return b.Container.bsc.client.blobAndFileSASURI(options.SASOptions, uri, permissions, canonicalizedResource, signedResource, options.OverrideHeaders)
78 }
79
80 func (c *Client) blobAndFileSASURI(options SASOptions, uri, permissions, canonicalizedResource, signedResource string, headers OverrideHeaders) (string, error) {
81 start := ""
82 if options.Start != (time.Time{}) {
83 start = options.Start.UTC().Format(time.RFC3339)
84 }
85
86 expiry := options.Expiry.UTC().Format(time.RFC3339)
87
88
89 canonicalizedResource = strings.Replace(canonicalizedResource, "+", "%2b", -1)
90 canonicalizedResource, err := url.QueryUnescape(canonicalizedResource)
91 if err != nil {
92 return "", err
93 }
94
95 protocols := ""
96 if options.UseHTTPS {
97 protocols = "https"
98 }
99 stringToSign, err := blobSASStringToSign(permissions, start, expiry, canonicalizedResource, options.Identifier, options.IP, protocols, c.apiVersion, signedResource, "", headers)
100 if err != nil {
101 return "", err
102 }
103
104 sig := c.computeHmac256(stringToSign)
105 sasParams := url.Values{
106 "sv": {c.apiVersion},
107 "se": {expiry},
108 "sr": {signedResource},
109 "sp": {permissions},
110 "sig": {sig},
111 }
112
113 if start != "" {
114 sasParams.Add("st", start)
115 }
116
117 if c.apiVersion >= "2015-04-05" {
118 if protocols != "" {
119 sasParams.Add("spr", protocols)
120 }
121 if options.IP != "" {
122 sasParams.Add("sip", options.IP)
123 }
124 }
125
126
127 addQueryParameter(sasParams, "rscc", headers.CacheControl)
128 addQueryParameter(sasParams, "rscd", headers.ContentDisposition)
129 addQueryParameter(sasParams, "rsce", headers.ContentEncoding)
130 addQueryParameter(sasParams, "rscl", headers.ContentLanguage)
131 addQueryParameter(sasParams, "rsct", headers.ContentType)
132
133 sasURL, err := url.Parse(uri)
134 if err != nil {
135 return "", err
136 }
137 sasURL.RawQuery = sasParams.Encode()
138 return sasURL.String(), nil
139 }
140
141 func blobSASStringToSign(signedPermissions, signedStart, signedExpiry, canonicalizedResource, signedIdentifier, signedIP, protocols, signedVersion, signedResource, signedSnapshotTime string, headers OverrideHeaders) (string, error) {
142 rscc := headers.CacheControl
143 rscd := headers.ContentDisposition
144 rsce := headers.ContentEncoding
145 rscl := headers.ContentLanguage
146 rsct := headers.ContentType
147
148 if signedVersion >= "2015-02-21" {
149 canonicalizedResource = "/blob" + canonicalizedResource
150 }
151
152
153 if signedVersion >= "2018-11-09" {
154 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\n%s\n%s", signedPermissions, signedStart, signedExpiry, canonicalizedResource, signedIdentifier, signedIP, protocols, signedVersion, signedResource, signedSnapshotTime, rscc, rscd, rsce, rscl, rsct), nil
155 }
156
157
158 if signedVersion >= "2015-04-05" {
159 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
160 }
161
162
163 if signedVersion >= "2013-08-15" {
164 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
165 }
166
167 return "", errors.New("storage: not implemented SAS for versions earlier than 2013-08-15")
168 }
169
View as plain text