aclsvc: Add tests for request ownership #1114
1 changed files with 163 additions and 0 deletions
163
pkg/services/object/acl/v2/request_test.go
Normal file
163
pkg/services/object/acl/v2/request_test.go
Normal file
|
@ -0,0 +1,163 @@
|
|||
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)
|
||||
}
|
Loading…
Reference in a new issue