frostfs-node/pkg/services/object/acl/v2/request_test.go

165 lines
4.7 KiB
Go
Raw Normal View History

package v2
import (
"testing"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
sessionV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
sigutilV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/util/signature"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
sessionSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
"github.com/google/uuid"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/stretchr/testify/require"
)
func TestRequestOwner(t *testing.T) {
containerOwner, err := keys.NewPrivateKey()
require.NoError(t, err)
userPk, err := keys.NewPrivateKey()
require.NoError(t, err)
var userID user.ID
user.IDFromKey(&userID, userPk.PrivateKey.PublicKey)
var userSignature refs.Signature
userSignature.SetKey(userPk.PublicKey().Bytes())
vh := new(sessionV2.RequestVerificationHeader)
vh.SetBodySignature(&userSignature)
t.Run("empty verification header", func(t *testing.T) {
req := MetaWithToken{}
checkOwner(t, req, nil, errEmptyVerificationHeader)
})
t.Run("empty verification header signature", func(t *testing.T) {
req := MetaWithToken{
vheader: new(sessionV2.RequestVerificationHeader),
}
checkOwner(t, req, nil, errEmptyBodySig)
})
t.Run("no tokens", func(t *testing.T) {
req := MetaWithToken{
vheader: vh,
}
checkOwner(t, req, userPk.PublicKey(), nil)
})
t.Run("bearer without impersonate, no session", func(t *testing.T) {
req := MetaWithToken{
vheader: vh,
bearer: newBearer(t, containerOwner, userID, false),
}
checkOwner(t, req, userPk.PublicKey(), nil)
})
t.Run("bearer with impersonate, no session", func(t *testing.T) {
req := MetaWithToken{
vheader: vh,
bearer: newBearer(t, containerOwner, userID, true),
}
checkOwner(t, req, containerOwner.PublicKey(), nil)
})
t.Run("bearer with impersonate, with session", func(t *testing.T) {
// To check that bearer token takes priority, use different key to sign session token.
pk, err := keys.NewPrivateKey()
require.NoError(t, err)
req := MetaWithToken{
vheader: vh,
bearer: newBearer(t, containerOwner, userID, true),
token: newSession(t, pk),
}
checkOwner(t, req, containerOwner.PublicKey(), nil)
})
t.Run("with session", func(t *testing.T) {
req := MetaWithToken{
vheader: vh,
token: newSession(t, containerOwner),
}
checkOwner(t, req, containerOwner.PublicKey(), nil)
})
t.Run("malformed session token", func(t *testing.T) {
// This test is tricky: session token has issuer field and signature, which must correspond to each other.
// SDK prevents constructing such token in the first place, but it is still possible via API.
// Thus, construct v2 token, convert it to SDK one and pass to our function.
pk, err := keys.NewPrivateKey()
require.NoError(t, err)
var user1 user.ID
user.IDFromKey(&user1, pk.PrivateKey.PublicKey)
var id refs.OwnerID
id.SetValue(user1.WalletBytes())
raw, err := uuid.New().MarshalBinary()
require.NoError(t, err)
var cidV2 refs.ContainerID
cidtest.ID().WriteToV2(&cidV2)
sessionCtx := new(sessionV2.ObjectSessionContext)
sessionCtx.SetTarget(&cidV2)
var body sessionV2.TokenBody
body.SetOwnerID(&id)
body.SetID(raw)
body.SetLifetime(new(sessionV2.TokenLifetime))
body.SetSessionKey(pk.PublicKey().Bytes())
body.SetContext(sessionCtx)
var tokV2 sessionV2.Token
tokV2.SetBody(&body)
require.NoError(t, sigutilV2.SignData(&containerOwner.PrivateKey, smWrapper{Token: &tokV2}))
require.NoError(t, sigutilV2.VerifyData(smWrapper{Token: &tokV2}))
var tok sessionSDK.Object
require.NoError(t, tok.ReadFromV2(tokV2))
req := MetaWithToken{
vheader: vh,
token: &tok,
}
checkOwner(t, req, nil, errInvalidSessionOwner)
})
}
type smWrapper struct {
*sessionV2.Token
}
func (s smWrapper) ReadSignedData(data []byte) ([]byte, error) {
return s.Token.GetBody().StableMarshal(data), nil
}
func (s smWrapper) SignedDataSize() int {
return s.Token.GetBody().StableSize()
}
func newSession(t *testing.T, pk *keys.PrivateKey) *sessionSDK.Object {
var tok sessionSDK.Object
require.NoError(t, tok.Sign(pk.PrivateKey))
return &tok
}
func newBearer(t *testing.T, pk *keys.PrivateKey, user user.ID, impersonate bool) *bearer.Token {
var tok bearer.Token
tok.SetImpersonate(impersonate)
tok.ForUser(user)
require.NoError(t, tok.Sign(pk.PrivateKey))
return &tok
}
func checkOwner(t *testing.T, req MetaWithToken, expected *keys.PublicKey, expectedErr error) {
_, actual, err := req.RequestOwner()
if expectedErr != nil {
require.ErrorIs(t, err, expectedErr)
return
}
require.NoError(t, err)
require.Equal(t, expected, actual)
}