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) }