2022-03-20 19:55:58 +00:00
|
|
|
package persistent
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"context"
|
|
|
|
"crypto/ecdsa"
|
|
|
|
"crypto/elliptic"
|
|
|
|
"path/filepath"
|
|
|
|
"testing"
|
|
|
|
|
2023-03-07 13:38:26 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
|
|
|
usertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user/test"
|
2022-03-20 19:55:58 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"go.etcd.io/bbolt"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestTokenStore(t *testing.T) {
|
|
|
|
ts, err := NewTokenStore(filepath.Join(t.TempDir(), ".storage"))
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
defer ts.Close()
|
|
|
|
|
2023-11-21 08:42:30 +00:00
|
|
|
owner := usertest.ID()
|
2022-05-17 13:59:46 +00:00
|
|
|
|
|
|
|
var ownerV2 refs.OwnerID
|
|
|
|
owner.WriteToV2(&ownerV2)
|
2022-03-20 19:55:58 +00:00
|
|
|
|
|
|
|
req := new(session.CreateRequestBody)
|
2022-05-17 13:59:46 +00:00
|
|
|
req.SetOwnerID(&ownerV2)
|
2022-03-20 19:55:58 +00:00
|
|
|
|
|
|
|
const tokenNumber = 5
|
|
|
|
|
|
|
|
type tok struct {
|
|
|
|
id []byte
|
|
|
|
key []byte
|
|
|
|
}
|
|
|
|
|
|
|
|
tokens := make([]tok, 0, tokenNumber)
|
|
|
|
|
2024-08-30 16:20:55 +00:00
|
|
|
for i := range tokenNumber {
|
2022-03-20 19:55:58 +00:00
|
|
|
req.SetExpiration(uint64(i))
|
|
|
|
|
|
|
|
res, err := ts.Create(context.Background(), req)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
tokens = append(tokens, tok{
|
|
|
|
id: res.GetID(),
|
|
|
|
key: res.GetSessionKey(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, token := range tokens {
|
2022-05-17 13:59:46 +00:00
|
|
|
savedToken := ts.Get(owner, token.id)
|
2022-03-20 19:55:58 +00:00
|
|
|
|
|
|
|
require.Equal(t, uint64(i), savedToken.ExpiredAt())
|
|
|
|
|
|
|
|
equalKeys(t, token.key, savedToken.SessionKey())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestTokenStore_Persistent(t *testing.T) {
|
|
|
|
path := filepath.Join(t.TempDir(), ".storage")
|
|
|
|
|
|
|
|
ts, err := NewTokenStore(path)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
2023-11-21 08:42:30 +00:00
|
|
|
idOwner := usertest.ID()
|
2022-05-17 13:59:46 +00:00
|
|
|
|
|
|
|
var idOwnerV2 refs.OwnerID
|
|
|
|
idOwner.WriteToV2(&idOwnerV2)
|
2022-03-20 19:55:58 +00:00
|
|
|
|
|
|
|
const exp = 12345
|
|
|
|
|
|
|
|
req := new(session.CreateRequestBody)
|
2022-05-17 13:59:46 +00:00
|
|
|
req.SetOwnerID(&idOwnerV2)
|
2022-03-20 19:55:58 +00:00
|
|
|
req.SetExpiration(exp)
|
|
|
|
|
|
|
|
res, err := ts.Create(context.Background(), req)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
id := res.GetID()
|
|
|
|
pubKey := res.GetSessionKey()
|
|
|
|
|
|
|
|
// close db (stop the node)
|
|
|
|
require.NoError(t, ts.Close())
|
|
|
|
|
|
|
|
// open persistent storage again
|
|
|
|
ts, err = NewTokenStore(path)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
defer ts.Close()
|
|
|
|
|
2022-05-17 13:59:46 +00:00
|
|
|
savedToken := ts.Get(idOwner, id)
|
2022-03-20 19:55:58 +00:00
|
|
|
|
|
|
|
equalKeys(t, pubKey, savedToken.SessionKey())
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestTokenStore_RemoveOld(t *testing.T) {
|
|
|
|
tests := []*struct {
|
|
|
|
epoch uint64
|
|
|
|
id, key []byte
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
epoch: 1,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
epoch: 2,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
epoch: 3,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
epoch: 4,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
epoch: 5,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
epoch: 6,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
ts, err := NewTokenStore(filepath.Join(t.TempDir(), ".storage"))
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
defer ts.Close()
|
|
|
|
|
2023-11-21 08:42:30 +00:00
|
|
|
owner := usertest.ID()
|
2022-05-17 13:59:46 +00:00
|
|
|
|
|
|
|
var ownerV2 refs.OwnerID
|
|
|
|
owner.WriteToV2(&ownerV2)
|
2022-03-20 19:55:58 +00:00
|
|
|
|
|
|
|
req := new(session.CreateRequestBody)
|
2022-05-17 13:59:46 +00:00
|
|
|
req.SetOwnerID(&ownerV2)
|
2022-03-20 19:55:58 +00:00
|
|
|
|
|
|
|
for _, test := range tests {
|
|
|
|
req.SetExpiration(test.epoch)
|
|
|
|
|
|
|
|
res, err := ts.Create(context.Background(), req)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
test.id = res.GetID()
|
|
|
|
test.key = res.GetSessionKey()
|
|
|
|
}
|
|
|
|
|
|
|
|
const currEpoch = 3
|
|
|
|
|
|
|
|
ts.RemoveOld(currEpoch)
|
|
|
|
|
|
|
|
for _, test := range tests {
|
2022-05-17 13:59:46 +00:00
|
|
|
token := ts.Get(owner, test.id)
|
2022-03-20 19:55:58 +00:00
|
|
|
|
|
|
|
if test.epoch <= currEpoch {
|
|
|
|
require.Nil(t, token)
|
|
|
|
} else {
|
|
|
|
equalKeys(t, test.key, token.SessionKey())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// This test was added to fix bolt's behaviour since the persistent
|
|
|
|
// storage uses cursor and there is an issue about `cursor.Delete`
|
|
|
|
// method: https://github.com/etcd-io/bbolt/issues/146.
|
|
|
|
//
|
|
|
|
// If this test is passing, TokenStore works correctly.
|
|
|
|
func TestBolt_Cursor(t *testing.T) {
|
2023-10-31 11:56:55 +00:00
|
|
|
db, err := bbolt.Open(filepath.Join(t.TempDir(), ".storage"), 0o666, nil)
|
2022-03-20 19:55:58 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
defer db.Close()
|
|
|
|
|
|
|
|
cursorKeys := make(map[string]struct{})
|
|
|
|
|
2023-10-31 11:56:55 +00:00
|
|
|
bucketName := []byte("bucket")
|
2022-03-20 19:55:58 +00:00
|
|
|
|
|
|
|
err = db.Update(func(tx *bbolt.Tx) (err error) {
|
|
|
|
b, err := tx.CreateBucket(bucketName)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
put := func(s []byte) {
|
|
|
|
if err == nil {
|
|
|
|
err = b.Put(s, s)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
put([]byte("1"))
|
|
|
|
put([]byte("2"))
|
|
|
|
put([]byte("3"))
|
|
|
|
put([]byte("4"))
|
|
|
|
|
|
|
|
return
|
|
|
|
})
|
|
|
|
|
|
|
|
err = db.Update(func(tx *bbolt.Tx) error {
|
|
|
|
b := tx.Bucket(bucketName)
|
|
|
|
c := b.Cursor()
|
|
|
|
|
|
|
|
for k, _ := c.First(); k != nil; k, _ = c.Next() {
|
|
|
|
// fill key that was viewed
|
|
|
|
cursorKeys[string(k)] = struct{}{}
|
|
|
|
|
|
|
|
if bytes.Equal(k, []byte("1")) {
|
|
|
|
// delete the first one
|
|
|
|
err = c.Delete()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
_, ok := cursorKeys["2"]
|
|
|
|
if !ok {
|
|
|
|
t.Fatal("unexpectedly skipped '2' value")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func equalKeys(t *testing.T, sessionKey []byte, savedPrivateKey *ecdsa.PrivateKey) {
|
|
|
|
returnedPubKey, err := keys.NewPublicKeyFromBytes(sessionKey, elliptic.P256())
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
savedPubKey := (keys.PublicKey)(savedPrivateKey.PublicKey)
|
|
|
|
|
|
|
|
require.Equal(t, true, returnedPubKey.Equal(&savedPubKey))
|
|
|
|
}
|