2024-05-02 08:56:46 +00:00
|
|
|
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
|
|
|
|
}
|
2024-04-27 12:24:11 +00:00
|
|
|
|
2024-05-02 08:56:46 +00:00
|
|
|
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)
|
|
|
|
}
|