[#505] authmate: Add flag for headers in generate-presigned-url cmd #526
3 changed files with 52 additions and 2 deletions
|
@ -23,6 +23,7 @@ type PresignData struct {
|
|||
Region string
|
||||
Lifetime time.Duration
|
||||
SignTime time.Time
|
||||
Headers map[string]string
|
||||
}
|
||||
|
||||
// PresignRequest forms pre-signed request to access objects without aws credentials.
|
||||
|
@ -34,7 +35,10 @@ func PresignRequest(creds *credentials.Credentials, reqData RequestData, presign
|
|||
}
|
||||
|
||||
req.Header.Set(AmzDate, presignData.SignTime.Format("20060102T150405Z"))
|
||||
req.Header.Set(ContentTypeHdr, "text/plain")
|
||||
|
||||
for k, v := range presignData.Headers {
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
|
||||
signer := v4.NewSigner(creds)
|
||||
signer.DisableURIPathEscaping = true
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/auth"
|
||||
|
@ -21,7 +22,7 @@ var generatePresignedURLCmd = &cobra.Command{
|
|||
You provide profile to load using --profile flag or explicitly provide credentials and region using
|
||||
--aws-access-key-id, --aws-secret-access-key, --region.
|
||||
Note to override credentials you must provide both access key and secret key.`,
|
||||
Example: `frostfs-s3-authmate generate-presigned-url --method put --bucket my-bucket --object my-object --endpoint http://localhost:8084 --lifetime 12h --region ru --aws-access-key-id ETaA2CadPcA7bAkLsML2PbTudXY8uRt2PDjCCwkvRv9s0FDCxWDXYc1SA1vKv8KbyCNsLY2AmAjJ92Vz5rgvsFCy --aws-secret-access-key c2d65ef2980f03f4f495bdebedeeae760496697880d61d106bb9a4e5cd2e0607`,
|
||||
Example: `frostfs-s3-authmate generate-presigned-url --method put --bucket my-bucket --object my-object --endpoint http://localhost:8084 --lifetime 12h --region ru --aws-access-key-id ETaA2CadPcA7bAkLsML2PbTudXY8uRt2PDjCCwkvRv9s0FDCxWDXYc1SA1vKv8KbyCNsLY2AmAjJ92Vz5rgvsFCy --aws-secret-access-key c2d65ef2980f03f4f495bdebedeeae760496697880d61d106bb9a4e5cd2e0607 --header 'Content-Type: text/plain'`,
|
||||
RunE: runGeneratePresignedURLCmd,
|
||||
}
|
||||
|
||||
|
@ -36,6 +37,7 @@ const (
|
|||
regionFlag = "region"
|
||||
awsAccessKeyIDFlag = "aws-access-key-id"
|
||||
awsSecretAccessKeyFlag = "aws-secret-access-key"
|
||||
dkirillov marked this conversation as resolved
Outdated
|
||||
headerFlag = "header"
|
||||
)
|
||||
|
||||
func initGeneratePresignedURLCmd() {
|
||||
|
@ -48,6 +50,7 @@ func initGeneratePresignedURLCmd() {
|
|||
generatePresignedURLCmd.Flags().String(regionFlag, "", "AWS region to use in signature (default is taken from ~/.aws/config)")
|
||||
generatePresignedURLCmd.Flags().String(awsAccessKeyIDFlag, "", "AWS access key id to sign the URL (default is taken from ~/.aws/credentials)")
|
||||
generatePresignedURLCmd.Flags().String(awsSecretAccessKeyFlag, "", "AWS secret access key to sign the URL (default is taken from ~/.aws/credentials)")
|
||||
generatePresignedURLCmd.Flags().StringSlice(headerFlag, nil, "Header in form of 'Key: value' to use in presigned URL (use flags repeatedly for multiple headers or separate them by comma)")
|
||||
|
||||
_ = generatePresignedURLCmd.MarkFlagRequired(endpointFlag)
|
||||
_ = generatePresignedURLCmd.MarkFlagRequired(bucketFlag)
|
||||
|
@ -92,6 +95,12 @@ func runGeneratePresignedURLCmd(*cobra.Command, []string) error {
|
|||
SignTime: time.Now().UTC(),
|
||||
}
|
||||
|
||||
headers, err := parseHeaders()
|
||||
if err != nil {
|
||||
return wrapPreparationError(fmt.Errorf("failed to parse headers: %s", err))
|
||||
}
|
||||
presignData.Headers = headers
|
||||
|
||||
req, err := auth.PresignRequest(sess.Config.Credentials, reqData, presignData)
|
||||
if err != nil {
|
||||
return wrapBusinessLogicError(err)
|
||||
|
@ -110,3 +119,21 @@ func runGeneratePresignedURLCmd(*cobra.Command, []string) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseHeaders() (map[string]string, error) {
|
||||
headers := viper.GetStringSlice(headerFlag)
|
||||
if len(headers) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
result := make(map[string]string, len(headers))
|
||||
for _, header := range headers {
|
||||
k, v, found := strings.Cut(header, ":")
|
||||
if !found {
|
||||
return nil, fmt.Errorf("invalid header format: %s", header)
|
||||
}
|
||||
result[strings.Trim(k, " ")] = strings.Trim(v, " ")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
|
|
@ -349,6 +349,25 @@ $ frostfs-s3-authmate generate-presigned-url --endpoint http://localhost:8084 \
|
|||
}
|
||||
```
|
||||
|
||||
### Upload file with presigned URL
|
||||
|
||||
1. Generate presigned URL to upload object `obj` to bucket `presigned`
|
||||
|
||||
```shell
|
||||
$ frostfs-s3-authmate generate-presigned-url --endpoint http://localhost:8084 \
|
||||
--method put --bucket presigned --object obj --lifetime 30s
|
||||
|
||||
{
|
||||
"URL": "http://localhost:8084/presigned/obj?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=CYfbvKwSC9VNvttj5snyEZ5Ttr2VaBabpw7mRuEzNXyw09ewUERj6MGDKfyckfg5VZ39GfXbwLwz62UPVeRxhJDet%2F20241029%2Fdefault%2Fs3%2Faws4_request&X-Amz-Date=20241029T145726Z&X-Amz-Expires=30&X-Amz-SignedHeaders=host&X-Amz-Signature=2bb13b3e6448968219ad95147debe49e37bce5ce3ed1344c4015f43cb444a956"
|
||||
}
|
||||
```
|
||||
|
||||
2. Upload file using `curl`
|
||||
|
||||
```shell
|
||||
curl --upload-file /path/to/file 'http://localhost:8084/presigned/obj?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=CYfbvKwSC9VNvttj5snyEZ5Ttr2VaBabpw7mRuEzNXyw09ewUERj6MGDKfyckfg5VZ39GfXbwLwz62UPVeRxhJDet%2F20241029%2Fdefault%2Fs3%2Faws4_request&X-Amz-Date=20241029T145726Z&X-Amz-Expires=30&X-Amz-SignedHeaders=host&X-Amz-Signature=2bb13b3e6448968219ad95147debe49e37bce5ce3ed1344c4015f43cb444a956'
|
||||
```
|
||||
|
||||
### AWS CLI
|
||||
|
||||
You can also can get the presigned URL (only for GET) using aws cli v2:
|
||||
|
|
Loading…
Reference in a new issue
It's better to use more general flag like
header
to be able to pass any headers we want