package playback import ( "context" "errors" "net/http" "net/url" "regexp" "strings" "time" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/auth" v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4" "github.com/aws/aws-sdk-go-v2/credentials" ) var ( // authorizationFieldRegexp -- is regexp for credentials with Base58 encoded cid and oid and '0' (zero) as delimiter. authorizationFieldRegexp = regexp.MustCompile(`AWS4-HMAC-SHA256 Credential=(?P[^/]+)/(?P[^/]+)/(?P[^/]*)/(?P[^/]+)/aws4_request,\s*SignedHeaders=(?P.+),\s*Signature=(?P.+)`) ErrNoMatches = errors.New("no matches found") ) type ( LoggedRequest struct { From string `json:"from"` URI string `json:"URI"` Method string `json:"method"` Query url.Values `json:"query"` Body []byte `json:"body"` Header http.Header `json:"headers"` Response []byte `json:"response"` } Credentials struct { AccessKey string SecretKey string } contextKey struct{} multipartKey struct{} ) func SetCredentials(ctx context.Context, accessKey, secretKey string) context.Context { return context.WithValue(ctx, contextKey{}, Credentials{ AccessKey: accessKey, SecretKey: secretKey, }, ) } func GetCredentials(ctx context.Context) (Credentials, error) { val, ok := ctx.Value(contextKey{}).(Credentials) if !ok { return val, errors.New("credentials not set") } return val, nil } // Sign replace Authorization header with new Access key id and Signature values. func Sign(ctx context.Context, r *http.Request) error { creds, err := GetCredentials(ctx) if err != nil { return errors.New("failed to get credentials") } credProvider, err := credentials.NewStaticCredentialsProvider(creds.AccessKey, creds.SecretKey, "").Retrieve(ctx) if err != nil { return err } authInfo, err := parseAuthHeader(r.Header.Get(auth.AuthorizationHdr)) if err != nil { return err } r.Header[auth.AuthorizationHdr][0] = strings.Replace(r.Header[auth.AuthorizationHdr][0], authInfo["access_key_id"], creds.AccessKey, 1) signer := v4.NewSigner() signatureDateTimeStr := r.Header.Get(api.AmzDate) signatureDateTime, err := time.Parse("20060102T150405Z", signatureDateTimeStr) if err != nil { return err } err = signer.SignHTTP(ctx, credProvider, r, r.Header.Get(api.AmzContentSha256), authInfo["service"], authInfo["region"], signatureDateTime) if err != nil { return err } return nil } func parseAuthHeader(authHeader string) (map[string]string, error) { authInfo := auth.NewRegexpMatcher(authorizationFieldRegexp).GetSubmatches(authHeader) if len(authInfo) == 0 { return nil, ErrNoMatches } return authInfo, nil }