api-presigned.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. /*
  2. * MinIO Go Library for Amazon S3 Compatible Cloud Storage
  3. * Copyright 2015-2017 MinIO, Inc.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. package s3cli
  18. import (
  19. "errors"
  20. "net/http"
  21. "net/url"
  22. "time"
  23. "github.com/minio/minio-go/v6/pkg/s3signer"
  24. "github.com/minio/minio-go/v6/pkg/s3utils"
  25. )
  26. // presignURL - Returns a presigned URL for an input 'method'.
  27. // Expires maximum is 7days - ie. 604800 and minimum is 1.
  28. func (c Client) presignURL(method string, bucketName string, objectName string, expires time.Duration, reqParams url.Values) (u *url.URL, err error) {
  29. // Input validation.
  30. if method == "" {
  31. return nil, ErrInvalidArgument("method cannot be empty.")
  32. }
  33. if err = s3utils.CheckValidBucketName(bucketName); err != nil {
  34. return nil, err
  35. }
  36. if err = isValidExpiry(expires); err != nil {
  37. return nil, err
  38. }
  39. // Convert expires into seconds.
  40. expireSeconds := int64(expires / time.Second)
  41. reqMetadata := requestMetadata{
  42. presignURL: true,
  43. bucketName: bucketName,
  44. objectName: objectName,
  45. expires: expireSeconds,
  46. queryValues: reqParams,
  47. }
  48. // Instantiate a new request.
  49. // Since expires is set newRequest will presign the request.
  50. var req *http.Request
  51. if req, err = c.newRequest(method, reqMetadata); err != nil {
  52. return nil, err
  53. }
  54. return req.URL, nil
  55. }
  56. // PresignedGetObject - Returns a presigned URL to access an object
  57. // data without credentials. URL can have a maximum expiry of
  58. // upto 7days or a minimum of 1sec. Additionally you can override
  59. // a set of response headers using the query parameters.
  60. func (c Client) PresignedGetObject(bucketName string, objectName string, expires time.Duration, reqParams url.Values) (u *url.URL, err error) {
  61. if err = s3utils.CheckValidObjectName(objectName); err != nil {
  62. return nil, err
  63. }
  64. return c.presignURL("GET", bucketName, objectName, expires, reqParams)
  65. }
  66. // PresignedHeadObject - Returns a presigned URL to access object
  67. // metadata without credentials. URL can have a maximum expiry of
  68. // upto 7days or a minimum of 1sec. Additionally you can override
  69. // a set of response headers using the query parameters.
  70. func (c Client) PresignedHeadObject(bucketName string, objectName string, expires time.Duration, reqParams url.Values) (u *url.URL, err error) {
  71. if err = s3utils.CheckValidObjectName(objectName); err != nil {
  72. return nil, err
  73. }
  74. return c.presignURL("HEAD", bucketName, objectName, expires, reqParams)
  75. }
  76. // PresignedPutObject - Returns a presigned URL to upload an object
  77. // without credentials. URL can have a maximum expiry of upto 7days
  78. // or a minimum of 1sec.
  79. func (c Client) PresignedPutObject(bucketName string, objectName string, expires time.Duration) (u *url.URL, err error) {
  80. if err = s3utils.CheckValidObjectName(objectName); err != nil {
  81. return nil, err
  82. }
  83. return c.presignURL("PUT", bucketName, objectName, expires, nil)
  84. }
  85. // Presign - returns a presigned URL for any http method of your choice
  86. // along with custom request params. URL can have a maximum expiry of
  87. // upto 7days or a minimum of 1sec.
  88. func (c Client) Presign(method string, bucketName string, objectName string, expires time.Duration, reqParams url.Values) (u *url.URL, err error) {
  89. return c.presignURL(method, bucketName, objectName, expires, reqParams)
  90. }
  91. // PresignedPostPolicy - Returns POST urlString, form data to upload an object.
  92. func (c Client) PresignedPostPolicy(p *PostPolicy) (u *url.URL, formData map[string]string, err error) {
  93. // Validate input arguments.
  94. if p.expiration.IsZero() {
  95. return nil, nil, errors.New("Expiration time must be specified")
  96. }
  97. if _, ok := p.formData["key"]; !ok {
  98. return nil, nil, errors.New("object key must be specified")
  99. }
  100. if _, ok := p.formData["bucket"]; !ok {
  101. return nil, nil, errors.New("bucket name must be specified")
  102. }
  103. bucketName := p.formData["bucket"]
  104. // Fetch the bucket location.
  105. location, err := c.getBucketLocation(bucketName)
  106. if err != nil {
  107. return nil, nil, err
  108. }
  109. isVirtualHost := c.isVirtualHostStyleRequest(*c.endpointURL, bucketName)
  110. u, err = c.makeTargetURL(bucketName, "", location, isVirtualHost, nil)
  111. if err != nil {
  112. return nil, nil, err
  113. }
  114. // Get credentials from the configured credentials provider.
  115. credValues, err := c.credsProvider.Get()
  116. if err != nil {
  117. return nil, nil, err
  118. }
  119. var (
  120. signerType = credValues.SignerType
  121. sessionToken = credValues.SessionToken
  122. accessKeyID = credValues.AccessKeyID
  123. secretAccessKey = credValues.SecretAccessKey
  124. )
  125. if signerType.IsAnonymous() {
  126. return nil, nil, ErrInvalidArgument("Presigned operations are not supported for anonymous credentials")
  127. }
  128. // Keep time.
  129. t := time.Now().UTC()
  130. // For signature version '2' handle here.
  131. if signerType.IsV2() {
  132. policyBase64 := p.base64()
  133. p.formData["policy"] = policyBase64
  134. // For Google endpoint set this value to be 'GoogleAccessId'.
  135. if s3utils.IsGoogleEndpoint(*c.endpointURL) {
  136. p.formData["GoogleAccessId"] = accessKeyID
  137. } else {
  138. // For all other endpoints set this value to be 'AWSAccessKeyId'.
  139. p.formData["AWSAccessKeyId"] = accessKeyID
  140. }
  141. // Sign the policy.
  142. p.formData["signature"] = s3signer.PostPresignSignatureV2(policyBase64, secretAccessKey)
  143. return u, p.formData, nil
  144. }
  145. // Add date policy.
  146. if err = p.addNewPolicy(policyCondition{
  147. matchType: "eq",
  148. condition: "$x-amz-date",
  149. value: t.Format(iso8601DateFormat),
  150. }); err != nil {
  151. return nil, nil, err
  152. }
  153. // Add algorithm policy.
  154. if err = p.addNewPolicy(policyCondition{
  155. matchType: "eq",
  156. condition: "$x-amz-algorithm",
  157. value: signV4Algorithm,
  158. }); err != nil {
  159. return nil, nil, err
  160. }
  161. // Add a credential policy.
  162. credential := s3signer.GetCredential(accessKeyID, location, t)
  163. if err = p.addNewPolicy(policyCondition{
  164. matchType: "eq",
  165. condition: "$x-amz-credential",
  166. value: credential,
  167. }); err != nil {
  168. return nil, nil, err
  169. }
  170. if sessionToken != "" {
  171. if err = p.addNewPolicy(policyCondition{
  172. matchType: "eq",
  173. condition: "$x-amz-security-token",
  174. value: sessionToken,
  175. }); err != nil {
  176. return nil, nil, err
  177. }
  178. }
  179. // Get base64 encoded policy.
  180. policyBase64 := p.base64()
  181. // Fill in the form data.
  182. p.formData["policy"] = policyBase64
  183. p.formData["x-amz-algorithm"] = signV4Algorithm
  184. p.formData["x-amz-credential"] = credential
  185. p.formData["x-amz-date"] = t.Format(iso8601DateFormat)
  186. if sessionToken != "" {
  187. p.formData["x-amz-security-token"] = sessionToken
  188. }
  189. p.formData["x-amz-signature"] = s3signer.PostPresignSignatureV4(policyBase64, t, secretAccessKey, location)
  190. return u, p.formData, nil
  191. }