package auth import ( "fmt" "net/http" "os" "strconv" "strings" "time" v4 "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/auth/signer/v4" v4a "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/auth/signer/v4asdk2" credentialsv2 "github.com/aws/aws-sdk-go-v2/credentials" "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/private/protocol/rest" "github.com/aws/smithy-go/logging" ) type RequestData struct { Method string Endpoint string Bucket string Object string } type PresignData struct { Service string Region string Lifetime time.Duration SignTime time.Time Headers map[string]string } // PresignRequest forms pre-signed request to access objects without aws credentials. func PresignRequest(creds *credentials.Credentials, reqData RequestData, presignData PresignData) (*http.Request, error) { urlStr := fmt.Sprintf("%s/%s/%s", reqData.Endpoint, rest.EscapePath(reqData.Bucket, false), rest.EscapePath(reqData.Object, false)) req, err := http.NewRequest(strings.ToUpper(reqData.Method), urlStr, nil) if err != nil { return nil, fmt.Errorf("failed to create new request: %w", err) } for k, v := range presignData.Headers { req.Header.Set(k, v) // maybe we should filter system header (or keep responsibility on caller) } req.Header.Set(AmzDate, presignData.SignTime.Format("20060102T150405Z")) signer := v4.NewSigner(creds) signer.DisableURIPathEscaping = true if _, err = signer.Presign(req, nil, presignData.Service, presignData.Region, presignData.Lifetime, presignData.SignTime); err != nil { return nil, fmt.Errorf("presign: %w", err) } return req, nil } // PresignRequestV4a forms pre-signed request to access objects without aws credentials. func PresignRequestV4a(credProvider credentialsv2.StaticCredentialsProvider, reqData RequestData, presignData PresignData) (*http.Request, error) { urlStr := fmt.Sprintf("%s/%s/%s", reqData.Endpoint, rest.EscapePath(reqData.Bucket, false), rest.EscapePath(reqData.Object, false)) req, err := http.NewRequest(strings.ToUpper(reqData.Method), urlStr, nil) if err != nil { return nil, fmt.Errorf("failed to create new request: %w", err) } for k, v := range presignData.Headers { req.Header.Set(k, v) // maybe we should filter system header (or keep responsibility on caller) } req.Header.Set(AmzDate, presignData.SignTime.Format("20060102T150405Z")) req.Header.Set(AmzExpires, strconv.Itoa(int(presignData.Lifetime.Seconds()))) signer := v4a.NewSigner(func(options *v4a.SignerOptions) { options.DisableURIPathEscaping = true options.LogSigning = true options.Logger = logging.NewStandardLogger(os.Stdout) }) credAdapter := v4a.SymmetricCredentialAdaptor{ SymmetricProvider: credProvider, } creds, err := credAdapter.RetrievePrivateKey(req.Context()) if err != nil { return nil, fmt.Errorf("failed to derive assymetric key from credentials: %w", err) } presignedURL, _, err := signer.PresignHTTP(req.Context(), creds, req, "", presignData.Service, []string{presignData.Region}, presignData.SignTime) if err != nil { return nil, fmt.Errorf("presign: %w", err) } fmt.Println(presignedURL) return http.NewRequest(reqData.Method, presignedURL, nil) }