Skip to content

Commit

Permalink
api: Change PresignedPostPolicy to return back proper URL (#393)
Browse files Browse the repository at this point in the history
The reason to do this is because bucket names can be in
various different regions and for users it becomes convenient
if we return back the proper endpoint which can be safely used
without worrying about virtual host style or path style.

We have enough information to provide a proper URL, this is also
consistent with other Presigned operations as it would be an
expected behavior.

Fixes #392
  • Loading branch information
harshavardhana authored and abperiasamy committed May 11, 2016
1 parent 867b277 commit 9892e8f
Show file tree
Hide file tree
Showing 6 changed files with 46 additions and 39 deletions.
10 changes: 5 additions & 5 deletions API.md
Expand Up @@ -446,7 +446,7 @@ if err != nil {
### Presigned operations
---------------------------------------
<a name="PresignedGetObject">
#### PresignedGetObject(bucketName, objectName string, expiry time.Duration, reqParams url.Values) error
#### PresignedGetObject(bucketName, objectName string, expiry time.Duration, reqParams url.Values) (*url.URL, error)
Generate a presigned URL for GET.

__Parameters__
Expand All @@ -471,7 +471,7 @@ if err != nil {

---------------------------------------
<a name="PresignedPutObject">
#### PresignedPutObject(bucketName string, objectName string, expiry time.Duration) (string, error)
#### PresignedPutObject(bucketName string, objectName string, expiry time.Duration) (*url.URL, error)
Generate a presigned URL for PUT.
<blockquote>
NOTE: you can upload to S3 only with specified object name.
Expand All @@ -494,7 +494,7 @@ if err != nil {

---------------------------------------
<a name="PresignedPostPolicy">
#### PresignedPostPolicy(policy PostPolicy) (map[string]string, error)
#### PresignedPostPolicy(policy PostPolicy) (*url.URL, map[string]string, error)
PresignedPostPolicy we can provide policies specifying conditions restricting
what you want to allow in a POST request, such as bucket name where objects can be
uploaded, key name prefixes that you want to allow for the object being created and more.
Expand All @@ -517,7 +517,7 @@ policy.SetContentLengthRange(1024, 1024*1024)
```
Get the POST form key/value object:
```go
formData, err := s3Client.PresignedPostPolicy(policy)
url, formData, err := s3Client.PresignedPostPolicy(policy)
if err != nil {
fmt.Println(err)
return
Expand All @@ -531,5 +531,5 @@ for k, v := range m {
fmt.Printf("-F %s=%s ", k, v)
}
fmt.Printf("-F file=@/etc/bash.bashrc ")
fmt.Printf("https://my-bucketname.s3.amazonaws.com\n")
fmt.Printf("%s\n", url)
```
6 changes: 3 additions & 3 deletions README.md
Expand Up @@ -85,9 +85,9 @@ func main() {
* [FGetObject(bucketName, objectName, filePath) error](examples/s3/fgetobject.go)

### Presigned Operations.
* [PresignedGetObject(bucketName, objectName, time.Duration, url.Values) (string, error)](examples/s3/presignedgetobject.go)
* [PresignedPutObject(bucketName, objectName, time.Duration) (string, error)](examples/s3/presignedputobject.go)
* [PresignedPostPolicy(NewPostPolicy()) (map[string]string, error)](examples/s3/presignedpostpolicy.go)
* [PresignedGetObject(bucketName, objectName, time.Duration, url.Values) (*url.URL, error)](examples/s3/presignedgetobject.go)
* [PresignedPutObject(bucketName, objectName, time.Duration) (*url.URL, error)](examples/s3/presignedputobject.go)
* [PresignedPostPolicy(NewPostPolicy()) (*url.URL, map[string]string, error)](examples/s3/presignedpostpolicy.go)

### Bucket Policy Operations.
* [SetBucketPolicy(bucketName, objectPrefix, BucketPolicy) error](examples/s3/setbucketpolicy.go)
Expand Down
47 changes: 26 additions & 21 deletions api-presigned.go
Expand Up @@ -33,19 +33,19 @@ var supportedGetReqParams = map[string]struct{}{

// presignURL - Returns a presigned URL for an input 'method'.
// Expires maximum is 7days - ie. 604800 and minimum is 1.
func (c Client) presignURL(method string, bucketName string, objectName string, expires time.Duration, reqParams url.Values) (urlStr string, err error) {
func (c Client) presignURL(method string, bucketName string, objectName string, expires time.Duration, reqParams url.Values) (u *url.URL, err error) {
// Input validation.
if method == "" {
return "", ErrInvalidArgument("method cannot be empty.")
return nil, ErrInvalidArgument("method cannot be empty.")
}
if err := isValidBucketName(bucketName); err != nil {
return "", err
return nil, err
}
if err := isValidObjectName(objectName); err != nil {
return "", err
return nil, err
}
if err := isValidExpiry(expires); err != nil {
return "", err
return nil, err
}

// Convert expires into seconds.
Expand All @@ -63,7 +63,7 @@ func (c Client) presignURL(method string, bucketName string, objectName string,
// Verify if input map has unsupported params, if yes exit.
for k := range reqParams {
if _, ok := supportedGetReqParams[k]; !ok {
return "", ErrInvalidArgument(k + " unsupported request parameter for presigned GET.")
return nil, ErrInvalidArgument(k + " unsupported request parameter for presigned GET.")
}
}
// Save the request parameters to be used in presigning for
Expand All @@ -75,43 +75,48 @@ func (c Client) presignURL(method string, bucketName string, objectName string,
// Since expires is set newRequest will presign the request.
req, err := c.newRequest(method, reqMetadata)
if err != nil {
return "", err
return nil, err
}
return req.URL.String(), nil
return req.URL, nil
}

// PresignedGetObject - Returns a presigned URL to access an object
// without credentials. Expires maximum is 7days - ie. 604800 and
// minimum is 1. Additionally you can override a set of response
// headers using the query parameters.
func (c Client) PresignedGetObject(bucketName string, objectName string, expires time.Duration, reqParams url.Values) (url string, err error) {
func (c Client) PresignedGetObject(bucketName string, objectName string, expires time.Duration, reqParams url.Values) (u *url.URL, err error) {
return c.presignURL("GET", bucketName, objectName, expires, reqParams)
}

// PresignedPutObject - Returns a presigned URL to upload an object without credentials.
// Expires maximum is 7days - ie. 604800 and minimum is 1.
func (c Client) PresignedPutObject(bucketName string, objectName string, expires time.Duration) (url string, err error) {
func (c Client) PresignedPutObject(bucketName string, objectName string, expires time.Duration) (u *url.URL, err error) {
return c.presignURL("PUT", bucketName, objectName, expires, nil)
}

// PresignedPostPolicy - Returns POST form data to upload an object at a location.
func (c Client) PresignedPostPolicy(p *PostPolicy) (map[string]string, error) {
// PresignedPostPolicy - Returns POST urlString, form data to upload an object.
func (c Client) PresignedPostPolicy(p *PostPolicy) (u *url.URL, formData map[string]string, err error) {
// Validate input arguments.
if p.expiration.IsZero() {
return nil, errors.New("Expiration time must be specified")
return nil, nil, errors.New("Expiration time must be specified")
}
if _, ok := p.formData["key"]; !ok {
return nil, errors.New("object key must be specified")
return nil, nil, errors.New("object key must be specified")
}
if _, ok := p.formData["bucket"]; !ok {
return nil, errors.New("bucket name must be specified")
return nil, nil, errors.New("bucket name must be specified")
}

bucketName := p.formData["bucket"]
// Fetch the bucket location.
location, err := c.getBucketLocation(bucketName)
if err != nil {
return nil, err
return nil, nil, err
}

u, err = c.makeTargetURL(bucketName, "", location, nil)
if err != nil {
return nil, nil, err
}

// Keep time.
Expand All @@ -129,7 +134,7 @@ func (c Client) PresignedPostPolicy(p *PostPolicy) (map[string]string, error) {
}
// Sign the policy.
p.formData["signature"] = postPresignSignatureV2(policyBase64, c.secretAccessKey)
return p.formData, nil
return u, p.formData, nil
}

// Add date policy.
Expand All @@ -138,7 +143,7 @@ func (c Client) PresignedPostPolicy(p *PostPolicy) (map[string]string, error) {
condition: "$x-amz-date",
value: t.Format(iso8601DateFormat),
}); err != nil {
return nil, err
return nil, nil, err
}

// Add algorithm policy.
Expand All @@ -147,7 +152,7 @@ func (c Client) PresignedPostPolicy(p *PostPolicy) (map[string]string, error) {
condition: "$x-amz-algorithm",
value: signV4Algorithm,
}); err != nil {
return nil, err
return nil, nil, err
}

// Add a credential policy.
Expand All @@ -157,7 +162,7 @@ func (c Client) PresignedPostPolicy(p *PostPolicy) (map[string]string, error) {
condition: "$x-amz-credential",
value: credential,
}); err != nil {
return nil, err
return nil, nil, err
}

// Get base64 encoded policy.
Expand All @@ -168,5 +173,5 @@ func (c Client) PresignedPostPolicy(p *PostPolicy) (map[string]string, error) {
p.formData["x-amz-credential"] = credential
p.formData["x-amz-date"] = t.Format(iso8601DateFormat)
p.formData["x-amz-signature"] = postPresignSignatureV4(policyBase64, t, c.secretAccessKey, location)
return p.formData, nil
return u, p.formData, nil
}
6 changes: 3 additions & 3 deletions api_functional_v2_test.go
Expand Up @@ -1184,7 +1184,7 @@ func TestFunctionalV2(t *testing.T) {
t.Fatal("Error: ", err)
}
// Verify if presigned url works.
resp, err := http.Get(presignedGetURL)
resp, err := http.Get(presignedGetURL.String())
if err != nil {
t.Fatal("Error: ", err)
}
Expand All @@ -1208,7 +1208,7 @@ func TestFunctionalV2(t *testing.T) {
t.Fatal("Error: ", err)
}
// Verify if presigned url works.
resp, err = http.Get(presignedGetURL)
resp, err = http.Get(presignedGetURL.String())
if err != nil {
t.Fatal("Error: ", err)
}
Expand Down Expand Up @@ -1236,7 +1236,7 @@ func TestFunctionalV2(t *testing.T) {
if err != nil {
t.Fatal("Error: ", err)
}
req, err := http.NewRequest("PUT", presignedPutURL, bytes.NewReader(buf))
req, err := http.NewRequest("PUT", presignedPutURL.String(), bytes.NewReader(buf))
if err != nil {
t.Fatal("Error: ", err)
}
Expand Down
6 changes: 3 additions & 3 deletions api_functional_v4_test.go
Expand Up @@ -1256,7 +1256,7 @@ func TestFunctional(t *testing.T) {
}

// Verify if presigned url works.
resp, err := http.Get(presignedGetURL)
resp, err := http.Get(presignedGetURL.String())
if err != nil {
t.Fatal("Error: ", err)
}
Expand All @@ -1279,7 +1279,7 @@ func TestFunctional(t *testing.T) {
t.Fatal("Error: ", err)
}
// Verify if presigned url works.
resp, err = http.Get(presignedGetURL)
resp, err = http.Get(presignedGetURL.String())
if err != nil {
t.Fatal("Error: ", err)
}
Expand All @@ -1306,7 +1306,7 @@ func TestFunctional(t *testing.T) {
if err != nil {
t.Fatal("Error: ", err)
}
req, err := http.NewRequest("PUT", presignedPutURL, bytes.NewReader(buf))
req, err := http.NewRequest("PUT", presignedPutURL.String(), bytes.NewReader(buf))
if err != nil {
t.Fatal("Error: ", err)
}
Expand Down
10 changes: 6 additions & 4 deletions examples/s3/presignedpostpolicy.go
Expand Up @@ -43,15 +43,17 @@ func main() {
policy := minio.NewPostPolicy()
policy.SetBucket("my-bucketname")
policy.SetKey("my-objectname")
policy.SetExpires(time.Now().UTC().AddDate(0, 0, 10)) // expires in 10 days
m, err := s3Client.PresignedPostPolicy(policy)
// Expires in 10 days.
policy.SetExpires(time.Now().UTC().AddDate(0, 0, 10))
// Returns form data for POST form request.
url, formData, err := s3Client.PresignedPostPolicy(policy)
if err != nil {
log.Fatalln(err)
}
fmt.Printf("curl ")
for k, v := range m {
for k, v := range formData {
fmt.Printf("-F %s=%s ", k, v)
}
fmt.Printf("-F file=@/etc/bash.bashrc ")
fmt.Printf("https://my-bucketname.s3.amazonaws.com\n")
fmt.Printf("%s\n", url)
}

0 comments on commit 9892e8f

Please sign in to comment.