frostfs-s3-gw/creds/tokens/credentials_test.go

360 lines
9.5 KiB
Go
Raw Normal View History

package tokens
import (
"context"
"encoding/hex"
"errors"
"strings"
"testing"
"time"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/cache"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest"
)
type frostfsMock struct {
objects map[string][]*object.Object
errors map[string]error
}
func newFrostfsMock() *frostfsMock {
return &frostfsMock{
objects: map[string][]*object.Object{},
errors: map[string]error{},
}
}
func (f *frostfsMock) CreateObject(_ context.Context, prm PrmObjectCreate) (oid.ID, error) {
var obj object.Object
obj.SetPayload(prm.Payload)
obj.SetOwnerID(prm.Creator)
obj.SetContainerID(prm.Container)
a := object.NewAttribute()
a.SetKey(object.AttributeFilePath)
a.SetValue(prm.Filepath)
prm.CustomAttributes = append(prm.CustomAttributes, *a)
obj.SetAttributes(prm.CustomAttributes...)
if prm.NewVersionForAccessKeyID != "" {
_, ok := f.objects[prm.NewVersionForAccessKeyID]
if !ok {
return oid.ID{}, errors.New("not found")
}
objID := oidtest.ID()
obj.SetID(objID)
f.objects[prm.NewVersionForAccessKeyID] = append(f.objects[prm.NewVersionForAccessKeyID], &obj)
return objID, nil
}
objID := oidtest.ID()
obj.SetID(objID)
accessKeyID := prm.CustomAccessKey
if accessKeyID == "" {
accessKeyID = prm.Container.EncodeToString() + "0" + objID.EncodeToString()
}
var addr oid.Address
addr.SetObject(objID)
addr.SetContainer(prm.Container)
f.objects[accessKeyID] = []*object.Object{&obj}
return objID, nil
}
func (f *frostfsMock) GetCredsObject(_ context.Context, prm PrmGetCredsObject) (*object.Object, error) {
if err := f.errors[prm.AccessKeyID]; err != nil {
return nil, err
}
objects, ok := f.objects[prm.AccessKeyID]
if !ok {
return nil, errors.New("not found")
}
return objects[len(objects)-1], nil
}
func TestRemovingAccessBox(t *testing.T) {
ctx := context.Background()
key, err := keys.NewPrivateKey()
require.NoError(t, err)
gateData := []*accessbox.GateData{{
BearerToken: &bearer.Token{},
GateKey: key.PublicKey(),
}}
secretKey := "713d0a0b9efc7d22923e17b0402a6a89b4273bc711c8bacb2da1b643d0006aeb"
sk, err := hex.DecodeString(secretKey)
require.NoError(t, err)
accessBox, _, err := accessbox.PackTokens(gateData, sk, false)
require.NoError(t, err)
data, err := accessBox.Marshal()
require.NoError(t, err)
var obj object.Object
obj.SetPayload(data)
addr := oidtest.Address()
obj.SetID(addr.Object())
obj.SetContainerID(addr.Container())
accessKeyID := getAccessKeyID(addr)
frostfs := &frostfsMock{
objects: map[string][]*object.Object{accessKeyID: {&obj}},
errors: map[string]error{},
}
cfg := Config{
FrostFS: frostfs,
Key: key,
CacheConfig: &cache.Config{
Size: 10,
Lifetime: 24 * time.Hour,
Logger: zaptest.NewLogger(t),
},
RemovingCheckAfterDurations: 0, // means check always
}
creds := New(cfg)
_, _, err = creds.GetBox(ctx, addr.Container(), accessKeyID)
require.NoError(t, err)
frostfs.errors[accessKeyID] = errors.New("network error")
_, _, err = creds.GetBox(ctx, addr.Container(), accessKeyID)
require.NoError(t, err)
frostfs.errors[accessKeyID] = &apistatus.ObjectAlreadyRemoved{}
_, _, err = creds.GetBox(ctx, addr.Container(), accessKeyID)
require.Error(t, err)
}
func TestGetBox(t *testing.T) {
ctx := context.Background()
key, err := keys.NewPrivateKey()
require.NoError(t, err)
gateData := []*accessbox.GateData{{
BearerToken: &bearer.Token{},
GateKey: key.PublicKey(),
}}
secret := []byte("secret")
accessBox, _, err := accessbox.PackTokens(gateData, secret, false)
require.NoError(t, err)
data, err := accessBox.Marshal()
require.NoError(t, err)
var attr object.Attribute
attr.SetKey("key")
attr.SetValue("value")
attrs := []object.Attribute{attr}
cfg := Config{
CacheConfig: &cache.Config{
Size: 10,
Lifetime: 24 * time.Hour,
Logger: zaptest.NewLogger(t),
},
}
t.Run("no removing check, accessbox from cache", func(t *testing.T) {
frostfs := newFrostfsMock()
cfg.FrostFS = frostfs
cfg.RemovingCheckAfterDurations = time.Hour
cfg.Key = key
creds := New(cfg)
cnrID := cidtest.ID()
addr, err := creds.Put(ctx, CredentialsParam{Container: cnrID, Keys: keys.PublicKeys{key.PublicKey()}, AccessBox: accessBox})
require.NoError(t, err)
accessKeyID := getAccessKeyID(addr)
_, _, err = creds.GetBox(ctx, addr.Container(), accessKeyID)
require.NoError(t, err)
frostfs.errors[accessKeyID] = &apistatus.ObjectAlreadyRemoved{}
_, _, err = creds.GetBox(ctx, addr.Container(), accessKeyID)
require.NoError(t, err)
})
t.Run("error while getting box from frostfs", func(t *testing.T) {
frostfs := newFrostfsMock()
cfg.FrostFS = frostfs
cfg.RemovingCheckAfterDurations = 0
cfg.Key = key
creds := New(cfg)
cnrID := cidtest.ID()
addr, err := creds.Put(ctx, CredentialsParam{Container: cnrID, Keys: keys.PublicKeys{key.PublicKey()}, AccessBox: accessBox})
require.NoError(t, err)
accessKeyID := getAccessKeyID(addr)
frostfs.errors[accessKeyID] = errors.New("network error")
_, _, err = creds.GetBox(ctx, addr.Container(), accessKeyID)
require.Error(t, err)
})
t.Run("invalid key", func(t *testing.T) {
frostfs := newFrostfsMock()
var obj object.Object
obj.SetPayload(data)
addr := oidtest.Address()
accessKeyID := getAccessKeyID(addr)
frostfs.objects[accessKeyID] = []*object.Object{&obj}
cfg.FrostFS = frostfs
cfg.RemovingCheckAfterDurations = 0
cfg.Key = &keys.PrivateKey{}
creds := New(cfg)
_, _, err = creds.GetBox(ctx, addr.Container(), accessKeyID)
require.Error(t, err)
})
t.Run("invalid payload", func(t *testing.T) {
frostfs := newFrostfsMock()
var obj object.Object
obj.SetPayload([]byte("invalid"))
addr := oidtest.Address()
accessKeyID := getAccessKeyID(addr)
frostfs.objects[accessKeyID] = []*object.Object{&obj}
cfg.FrostFS = frostfs
cfg.RemovingCheckAfterDurations = 0
cfg.Key = key
creds := New(cfg)
_, _, err = creds.GetBox(ctx, addr.Container(), accessKeyID)
require.Error(t, err)
})
t.Run("check attributes update", func(t *testing.T) {
frostfs := newFrostfsMock()
cfg.FrostFS = frostfs
cfg.RemovingCheckAfterDurations = 0
cfg.Key = key
creds := New(cfg)
cnrID := cidtest.ID()
addr, err := creds.Put(ctx, CredentialsParam{Container: cnrID, Keys: keys.PublicKeys{key.PublicKey()}, AccessBox: accessBox})
require.NoError(t, err)
accessKeyID := getAccessKeyID(addr)
_, boxAttrs, err := creds.GetBox(ctx, addr.Container(), accessKeyID)
require.NoError(t, err)
prm := CredentialsParam{
Container: addr.Container(),
AccessKeyID: accessKeyID,
Keys: keys.PublicKeys{key.PublicKey()},
AccessBox: accessBox,
CustomAttributes: attrs,
}
_, err = creds.Update(ctx, prm)
require.NoError(t, err)
_, newBoxAttrs, err := creds.GetBox(ctx, addr.Container(), accessKeyID)
require.NoError(t, err)
require.Equal(t, len(boxAttrs)+1, len(newBoxAttrs))
})
t.Run("check accessbox update", func(t *testing.T) {
frostfs := newFrostfsMock()
cfg.FrostFS = frostfs
cfg.RemovingCheckAfterDurations = 0
cfg.Key = key
creds := New(cfg)
cnrID := cidtest.ID()
addr, err := creds.Put(ctx, CredentialsParam{Container: cnrID, Keys: keys.PublicKeys{key.PublicKey()}, AccessBox: accessBox})
require.NoError(t, err)
accessKeyID := getAccessKeyID(addr)
box, _, err := creds.GetBox(ctx, addr.Container(), accessKeyID)
require.NoError(t, err)
require.Equal(t, hex.EncodeToString(secret), box.Gate.SecretKey)
newKey, err := keys.NewPrivateKey()
require.NoError(t, err)
newGateData := []*accessbox.GateData{{
BearerToken: &bearer.Token{},
GateKey: newKey.PublicKey(),
}}
newSecret := []byte("new-secret")
newAccessBox, _, err := accessbox.PackTokens(newGateData, newSecret, false)
require.NoError(t, err)
prm := CredentialsParam{
Container: addr.Container(),
AccessKeyID: accessKeyID,
Keys: keys.PublicKeys{newKey.PublicKey()},
AccessBox: newAccessBox,
}
_, err = creds.Update(ctx, prm)
require.NoError(t, err)
_, _, err = creds.GetBox(ctx, addr.Container(), accessKeyID)
require.Error(t, err)
cfg.Key = newKey
newCreds := New(cfg)
box, _, err = newCreds.GetBox(ctx, addr.Container(), accessKeyID)
require.NoError(t, err)
require.Equal(t, hex.EncodeToString(newSecret), box.Gate.SecretKey)
})
t.Run("empty keys", func(t *testing.T) {
frostfs := newFrostfsMock()
cfg.FrostFS = frostfs
cfg.RemovingCheckAfterDurations = 0
cfg.Key = key
creds := New(cfg)
cnrID := cidtest.ID()
_, err = creds.Put(ctx, CredentialsParam{Container: cnrID, AccessBox: accessBox})
require.ErrorIs(t, err, ErrEmptyPublicKeys)
})
t.Run("empty accessbox", func(t *testing.T) {
frostfs := newFrostfsMock()
cfg.FrostFS = frostfs
cfg.RemovingCheckAfterDurations = 0
cfg.Key = key
creds := New(cfg)
cnrID := cidtest.ID()
_, err = creds.Put(ctx, CredentialsParam{Container: cnrID, Keys: keys.PublicKeys{key.PublicKey()}})
require.ErrorIs(t, err, ErrEmptyBearerToken)
})
}
func getAccessKeyID(addr oid.Address) string {
return strings.ReplaceAll(addr.EncodeToString(), "/", "0")
}