forked from TrueCloudLab/frostfs-s3-gw
98 lines
2.7 KiB
Go
98 lines
2.7 KiB
Go
|
package playback
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"io"
|
||
|
"net/http"
|
||
|
"net/url"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
"time"
|
||
|
|
||
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
|
||
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/auth"
|
||
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/pkg/detector"
|
||
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/pkg/xmlutils"
|
||
|
v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4"
|
||
|
"github.com/aws/aws-sdk-go-v2/credentials"
|
||
|
)
|
||
|
|
||
|
type (
|
||
|
httpBody []byte
|
||
|
LoggedRequest struct {
|
||
|
From string `json:"from"`
|
||
|
URI string `json:"URI"`
|
||
|
Method string `json:"method"`
|
||
|
Payload httpBody `json:"payload,omitempty"`
|
||
|
Response httpBody `json:"response,omitempty"`
|
||
|
Query url.Values `json:"query"`
|
||
|
Header http.Header `json:"headers"`
|
||
|
}
|
||
|
Credentials struct {
|
||
|
AccessKey string
|
||
|
SecretKey string
|
||
|
}
|
||
|
Settings struct {
|
||
|
Endpoint string
|
||
|
Creds Credentials
|
||
|
Multiparts map[string]MultipartUpload
|
||
|
Client *http.Client
|
||
|
}
|
||
|
)
|
||
|
|
||
|
func (h *httpBody) UnmarshalJSON(data []byte) error {
|
||
|
unquoted, err := strconv.Unquote(string(data))
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("failed to unquote data: %w", err)
|
||
|
}
|
||
|
detect := detector.NewDetector(strings.NewReader(unquoted), xmlutils.DetectXML)
|
||
|
dataType, err := detect.Detect()
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("failed to detect data: %w", err)
|
||
|
}
|
||
|
reader := xmlutils.ChooseReader(dataType, detect.RestoredReader())
|
||
|
*h, err = io.ReadAll(reader)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("failed to unmarshal httpbody: %w", err)
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Sign replace Authorization header with new Access key id and Signature values.
|
||
|
func Sign(ctx context.Context, r *http.Request, creds Credentials) error {
|
||
|
credProvider := credentials.NewStaticCredentialsProvider(creds.AccessKey, creds.SecretKey, "")
|
||
|
awsCred, err := credProvider.Retrieve(ctx)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
authHdr := r.Header.Get(auth.AuthorizationHdr)
|
||
|
authInfo, err := parseAuthHeader(authHdr)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
newHeader := strings.Replace(authHdr, authInfo["access_key_id"], creds.AccessKey, 1)
|
||
|
r.Header.Set(auth.AuthorizationHdr, newHeader)
|
||
|
|
||
|
signer := v4.NewSigner()
|
||
|
signatureDateTimeStr := r.Header.Get(api.AmzDate)
|
||
|
signatureDateTime, err := time.Parse("20060102T150405Z", signatureDateTimeStr)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
return signer.SignHTTP(ctx, awsCred, r, r.Header.Get(api.AmzContentSha256), authInfo["service"], authInfo["region"], signatureDateTime)
|
||
|
}
|
||
|
|
||
|
func parseAuthHeader(authHeader string) (map[string]string, error) {
|
||
|
authInfo := auth.NewRegexpMatcher(auth.AuthorizationFieldRegexp).GetSubmatches(authHeader)
|
||
|
if len(authInfo) == 0 {
|
||
|
return nil, errors.New("no matches found")
|
||
|
}
|
||
|
|
||
|
return authInfo, nil
|
||
|
}
|