[#125] api/auth: DisableURIPathEscaping for presign
Don't use escaping when presign url. Escape manually before. Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
This commit is contained in:
parent
868edfdb31
commit
b445f7bbf9
4 changed files with 153 additions and 12 deletions
|
@ -291,6 +291,7 @@ func cloneRequest(r *http.Request, authHeader *authHeader) *http.Request {
|
||||||
func (c *center) checkSign(authHeader *authHeader, box *accessbox.Box, request *http.Request, signatureDateTime time.Time) error {
|
func (c *center) checkSign(authHeader *authHeader, box *accessbox.Box, request *http.Request, signatureDateTime time.Time) error {
|
||||||
awsCreds := credentials.NewStaticCredentials(authHeader.AccessKeyID, box.Gate.AccessKey, "")
|
awsCreds := credentials.NewStaticCredentials(authHeader.AccessKeyID, box.Gate.AccessKey, "")
|
||||||
signer := v4.NewSigner(awsCreds)
|
signer := v4.NewSigner(awsCreds)
|
||||||
|
signer.DisableURIPathEscaping = true
|
||||||
|
|
||||||
var signature string
|
var signature string
|
||||||
if authHeader.IsPresigned {
|
if authHeader.IsPresigned {
|
||||||
|
@ -306,7 +307,6 @@ func (c *center) checkSign(authHeader *authHeader, box *accessbox.Box, request *
|
||||||
}
|
}
|
||||||
signature = request.URL.Query().Get(AmzSignature)
|
signature = request.URL.Query().Get(AmzSignature)
|
||||||
} else {
|
} else {
|
||||||
signer.DisableURIPathEscaping = true
|
|
||||||
if _, err := signer.Sign(request, nil, authHeader.Service, authHeader.Region, signatureDateTime); err != nil {
|
if _, err := signer.Sign(request, nil, authHeader.Service, authHeader.Region, signatureDateTime); err != nil {
|
||||||
return fmt.Errorf("failed to sign temporary HTTP request: %w", err)
|
return fmt.Errorf("failed to sign temporary HTTP request: %w", err)
|
||||||
}
|
}
|
||||||
|
|
46
api/auth/presign.go
Normal file
46
api/auth/presign.go
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
v4 "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/auth/signer/v4"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||||
|
"github.com/aws/aws-sdk-go/private/protocol/rest"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RequestData struct {
|
||||||
|
Method string
|
||||||
|
Endpoint string
|
||||||
|
Bucket string
|
||||||
|
Object string
|
||||||
|
}
|
||||||
|
|
||||||
|
type PresignData struct {
|
||||||
|
Service string
|
||||||
|
Region string
|
||||||
|
Lifetime time.Duration
|
||||||
|
SignTime time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
91
api/auth/presign_test.go
Normal file
91
api/auth/presign_test.go
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/tokens"
|
||||||
|
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||||
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ tokens.Credentials = (*credentialsMock)(nil)
|
||||||
|
|
||||||
|
type credentialsMock struct {
|
||||||
|
boxes map[string]*accessbox.Box
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTokensFrostfsMock() *credentialsMock {
|
||||||
|
return &credentialsMock{
|
||||||
|
boxes: make(map[string]*accessbox.Box),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m credentialsMock) addBox(addr oid.Address, box *accessbox.Box) {
|
||||||
|
m.boxes[addr.String()] = box
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m credentialsMock) GetBox(_ context.Context, addr oid.Address) (*accessbox.Box, error) {
|
||||||
|
box, ok := m.boxes[addr.String()]
|
||||||
|
if !ok {
|
||||||
|
return nil, apistatus.ObjectNotFound{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return box, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m credentialsMock) Put(context.Context, cid.ID, user.ID, *accessbox.AccessBox, uint64, ...*keys.PublicKey) (oid.Address, error) {
|
||||||
|
return oid.Address{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckSign(t *testing.T) {
|
||||||
|
var accessKeyAddr oid.Address
|
||||||
|
err := accessKeyAddr.DecodeString("8N7CYBY74kxZXoyvA5UNdmovaXqFpwNfvEPsqaN81es2/3tDwq5tR8fByrJcyJwyiuYX7Dae8tyDT7pd8oaL1MBto")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
accessKeyID := strings.ReplaceAll(accessKeyAddr.String(), "/", "0")
|
||||||
|
secretKey := "713d0a0b9efc7d22923e17b0402a6a89b4273bc711c8bacb2da1b643d0006aeb"
|
||||||
|
awsCreds := credentials.NewStaticCredentials(accessKeyID, secretKey, "")
|
||||||
|
|
||||||
|
reqData := RequestData{
|
||||||
|
Method: "GET",
|
||||||
|
Endpoint: "http://localhost:8084",
|
||||||
|
Bucket: "my-bucket",
|
||||||
|
Object: "@obj/name",
|
||||||
|
}
|
||||||
|
presignData := PresignData{
|
||||||
|
Service: "s3",
|
||||||
|
Region: "spb",
|
||||||
|
Lifetime: 10 * time.Minute,
|
||||||
|
SignTime: time.Now().UTC(),
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := PresignRequest(awsCreds, reqData, presignData)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
expBox := &accessbox.Box{
|
||||||
|
Gate: &accessbox.GateData{
|
||||||
|
AccessKey: secretKey,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
mock := newTokensFrostfsMock()
|
||||||
|
mock.addBox(accessKeyAddr, expBox)
|
||||||
|
|
||||||
|
c := ¢er{
|
||||||
|
cli: mock,
|
||||||
|
reg: NewRegexpMatcher(authorizationFieldRegexp),
|
||||||
|
postReg: NewRegexpMatcher(postPolicyCredentialRegexp),
|
||||||
|
}
|
||||||
|
box, err := c.Authenticate(req)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.EqualValues(t, expBox, box.AccessBox)
|
||||||
|
}
|
|
@ -5,7 +5,6 @@ import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
@ -14,6 +13,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/auth"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/authmate"
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/authmate"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/frostfs"
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/frostfs"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/version"
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/version"
|
||||||
|
@ -23,7 +23,6 @@ import (
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||||
"github.com/aws/aws-sdk-go/aws/session"
|
"github.com/aws/aws-sdk-go/aws/session"
|
||||||
v4 "github.com/aws/aws-sdk-go/aws/signer/v4"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
|
@ -482,17 +481,22 @@ It will be ceil rounded to the nearest amount of epoch.`,
|
||||||
return fmt.Errorf("couldn't get credentials: %w", err)
|
return fmt.Errorf("couldn't get credentials: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
signer := v4.NewSigner(sess.Config.Credentials)
|
reqData := auth.RequestData{
|
||||||
req, err := http.NewRequest(strings.ToUpper(methodFlag), fmt.Sprintf("%s/%s/%s", endpointFlag, bucketFlag, objectFlag), nil)
|
Method: methodFlag,
|
||||||
if err != nil {
|
Endpoint: endpointFlag,
|
||||||
return fmt.Errorf("failed to create new request: %w", err)
|
Bucket: bucketFlag,
|
||||||
|
Object: objectFlag,
|
||||||
|
}
|
||||||
|
presignData := auth.PresignData{
|
||||||
|
Service: "s3",
|
||||||
|
Region: *sess.Config.Region,
|
||||||
|
Lifetime: lifetimeFlag,
|
||||||
|
SignTime: time.Now().UTC(),
|
||||||
}
|
}
|
||||||
|
|
||||||
date := time.Now().UTC()
|
req, err := auth.PresignRequest(sess.Config.Credentials, reqData, presignData)
|
||||||
req.Header.Set(api.AmzDate, date.Format("20060102T150405Z"))
|
if err != nil {
|
||||||
|
return err
|
||||||
if _, err = signer.Presign(req, nil, "s3", *sess.Config.Region, lifetimeFlag, date); err != nil {
|
|
||||||
return fmt.Errorf("presign: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
res := &struct{ URL string }{
|
res := &struct{ URL string }{
|
||||||
|
|
Loading…
Reference in a new issue