api-presigned.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  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 minio
  18. import (
  19. "context"
  20. "errors"
  21. "net/http"
  22. "net/url"
  23. "time"
  24. "github.com/minio/minio-go/v7/pkg/s3utils"
  25. "github.com/minio/minio-go/v7/pkg/signer"
  26. )
  27. // presignURL - Returns a presigned URL for an input 'method'.
  28. // Expires maximum is 7days - ie. 604800 and minimum is 1.
  29. func (c Client) presignURL(ctx context.Context, method string, bucketName string, objectName string, expires time.Duration, reqParams url.Values) (u *url.URL, err error) {
  30. // Input validation.
  31. if method == "" {
  32. return nil, errInvalidArgument("method cannot be empty.")
  33. }
  34. if err = s3utils.CheckValidBucketName(bucketName); err != nil {
  35. return nil, err
  36. }
  37. if err = isValidExpiry(expires); err != nil {
  38. return nil, err
  39. }
  40. // Convert expires into seconds.
  41. expireSeconds := int64(expires / time.Second)
  42. reqMetadata := requestMetadata{
  43. presignURL: true,
  44. bucketName: bucketName,
  45. objectName: objectName,
  46. expires: expireSeconds,
  47. queryValues: reqParams,
  48. }
  49. // Instantiate a new request.
  50. // Since expires is set newRequest will presign the request.
  51. var req *http.Request
  52. if req, err = c.newRequest(ctx, method, reqMetadata); err != nil {
  53. return nil, err
  54. }
  55. return req.URL, nil
  56. }
  57. // PresignedGetObject - Returns a presigned URL to access an object
  58. // data without credentials. URL can have a maximum expiry of
  59. // upto 7days or a minimum of 1sec. Additionally you can override
  60. // a set of response headers using the query parameters.
  61. func (c Client) PresignedGetObject(ctx context.Context, bucketName string, objectName string, expires time.Duration, reqParams url.Values) (u *url.URL, err error) {
  62. if err = s3utils.CheckValidObjectName(objectName); err != nil {
  63. return nil, err
  64. }
  65. return c.presignURL(ctx, http.MethodGet, bucketName, objectName, expires, reqParams)
  66. }
  67. // PresignedHeadObject - Returns a presigned URL to access
  68. // object metadata without credentials. URL can have a maximum expiry
  69. // of upto 7days or a minimum of 1sec. Additionally you can override
  70. // a set of response headers using the query parameters.
  71. func (c Client) PresignedHeadObject(ctx context.Context, bucketName string, objectName string, expires time.Duration, reqParams url.Values) (u *url.URL, err error) {
  72. if err = s3utils.CheckValidObjectName(objectName); err != nil {
  73. return nil, err
  74. }
  75. return c.presignURL(ctx, http.MethodHead, bucketName, objectName, expires, reqParams)
  76. }
  77. // PresignedPutObject - Returns a presigned URL to upload an object
  78. // without credentials. URL can have a maximum expiry of upto 7days
  79. // or a minimum of 1sec.
  80. func (c Client) PresignedPutObject(ctx context.Context, bucketName string, objectName string, expires time.Duration) (u *url.URL, err error) {
  81. if err = s3utils.CheckValidObjectName(objectName); err != nil {
  82. return nil, err
  83. }
  84. return c.presignURL(ctx, http.MethodPut, bucketName, objectName, expires, nil)
  85. }
  86. // Presign - returns a presigned URL for any http method of your choice
  87. // along with custom request params. URL can have a maximum expiry of
  88. // upto 7days or a minimum of 1sec.
  89. func (c Client) Presign(ctx context.Context, method string, bucketName string, objectName string, expires time.Duration, reqParams url.Values) (u *url.URL, err error) {
  90. return c.presignURL(ctx, method, bucketName, objectName, expires, reqParams)
  91. }
  92. // PresignedPostPolicy - Returns POST urlString, form data to upload an object.
  93. func (c Client) PresignedPostPolicy(ctx context.Context, p *PostPolicy) (u *url.URL, formData map[string]string, err error) {
  94. // Validate input arguments.
  95. if p.expiration.IsZero() {
  96. return nil, nil, errors.New("Expiration time must be specified")
  97. }
  98. if _, ok := p.formData["key"]; !ok {
  99. return nil, nil, errors.New("object key must be specified")
  100. }
  101. if _, ok := p.formData["bucket"]; !ok {
  102. return nil, nil, errors.New("bucket name must be specified")
  103. }
  104. bucketName := p.formData["bucket"]
  105. // Fetch the bucket location.
  106. location, err := c.getBucketLocation(ctx, bucketName)
  107. if err != nil {
  108. return nil, nil, err
  109. }
  110. isVirtualHost := c.isVirtualHostStyleRequest(*c.endpointURL, bucketName)
  111. u, err = c.makeTargetURL(bucketName, "", location, isVirtualHost, nil)
  112. if err != nil {
  113. return nil, nil, err
  114. }
  115. // Get credentials from the configured credentials provider.
  116. credValues, err := c.credsProvider.Get()
  117. if err != nil {
  118. return nil, nil, err
  119. }
  120. var (
  121. signerType = credValues.SignerType
  122. sessionToken = credValues.SessionToken
  123. accessKeyID = credValues.AccessKeyID
  124. secretAccessKey = credValues.SecretAccessKey
  125. )
  126. if signerType.IsAnonymous() {
  127. return nil, nil, errInvalidArgument("Presigned operations are not supported for anonymous credentials")
  128. }
  129. // Keep time.
  130. t := time.Now().UTC()
  131. // For signature version '2' handle here.
  132. if signerType.IsV2() {
  133. policyBase64 := p.base64()
  134. p.formData["policy"] = policyBase64
  135. // For Google endpoint set this value to be 'GoogleAccessId'.
  136. if s3utils.IsGoogleEndpoint(*c.endpointURL) {
  137. p.formData["GoogleAccessId"] = accessKeyID
  138. } else {
  139. // For all other endpoints set this value to be 'AWSAccessKeyId'.
  140. p.formData["AWSAccessKeyId"] = accessKeyID
  141. }
  142. // Sign the policy.
  143. p.formData["signature"] = signer.PostPresignSignatureV2(policyBase64, secretAccessKey)
  144. return u, p.formData, nil
  145. }
  146. // Add date policy.
  147. if err = p.addNewPolicy(policyCondition{
  148. matchType: "eq",
  149. condition: "$x-amz-date",
  150. value: t.Format(iso8601DateFormat),
  151. }); err != nil {
  152. return nil, nil, err
  153. }
  154. // Add algorithm policy.
  155. if err = p.addNewPolicy(policyCondition{
  156. matchType: "eq",
  157. condition: "$x-amz-algorithm",
  158. value: signV4Algorithm,
  159. }); err != nil {
  160. return nil, nil, err
  161. }
  162. // Add a credential policy.
  163. credential := signer.GetCredential(accessKeyID, location, t, signer.ServiceTypeS3)
  164. if err = p.addNewPolicy(policyCondition{
  165. matchType: "eq",
  166. condition: "$x-amz-credential",
  167. value: credential,
  168. }); err != nil {
  169. return nil, nil, err
  170. }
  171. if sessionToken != "" {
  172. if err = p.addNewPolicy(policyCondition{
  173. matchType: "eq",
  174. condition: "$x-amz-security-token",
  175. value: sessionToken,
  176. }); err != nil {
  177. return nil, nil, err
  178. }
  179. }
  180. // Get base64 encoded policy.
  181. policyBase64 := p.base64()
  182. // Fill in the form data.
  183. p.formData["policy"] = policyBase64
  184. p.formData["x-amz-algorithm"] = signV4Algorithm
  185. p.formData["x-amz-credential"] = credential
  186. p.formData["x-amz-date"] = t.Format(iso8601DateFormat)
  187. if sessionToken != "" {
  188. p.formData["x-amz-security-token"] = sessionToken
  189. }
  190. p.formData["x-amz-signature"] = signer.PostPresignSignatureV4(policyBase64, t, secretAccessKey, location)
  191. return u, p.formData, nil
  192. }