[#509] Add tests for custom credentials

Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
This commit is contained in:
Denis Kirillov 2024-10-11 15:20:10 +03:00
parent 4e73270b81
commit b456e6a287
3 changed files with 294 additions and 72 deletions

View file

@ -224,14 +224,27 @@ func TestGetBox(t *testing.T) {
var tkn bearer.Token var tkn bearer.Token
gate := NewGateData(cred.PublicKey(), &tkn) gate := NewGateData(cred.PublicKey(), &tkn)
secret := []byte("secret") secret := []byte("secret")
accessBox, _, err := PackTokens([]*GateData{gate}, secret, false)
t.Run("regular secret", func(t *testing.T) {
accessBox, secrets, err := PackTokens([]*GateData{gate}, secret, false)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, hex.EncodeToString(secret), secrets.SecretKey)
box, err := accessBox.GetBox(cred, false) box, err := accessBox.GetBox(cred, false)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, hex.EncodeToString(secret), box.Gate.SecretKey) require.Equal(t, hex.EncodeToString(secret), box.Gate.SecretKey)
})
t.Run("custom secret", func(t *testing.T) {
accessBox, secrets, err := PackTokens([]*GateData{gate}, secret, true)
require.NoError(t, err)
require.Equal(t, string(secret), secrets.SecretKey)
box, err := accessBox.GetBox(cred, true)
require.NoError(t, err)
require.Equal(t, string(secret), box.Gate.SecretKey)
})
} }
func TestAccessBox(t *testing.T) { func TestAccessBox(t *testing.T) {

View file

@ -16,27 +16,40 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test" oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest" "go.uber.org/zap/zaptest"
) )
type frostfsMock struct { type frostfsMock struct {
key *keys.PrivateKey
objects map[string][]*object.Object objects map[string][]*object.Object
errors map[string]error errors map[string]error
} }
func newFrostfsMock() *frostfsMock { func newFrostfsMock(key *keys.PrivateKey) *frostfsMock {
return &frostfsMock{ return &frostfsMock{
objects: map[string][]*object.Object{}, objects: map[string][]*object.Object{},
errors: map[string]error{}, errors: map[string]error{},
key: key,
} }
} }
func (f *frostfsMock) ownerID() user.ID {
if f.key == nil {
return user.ID{}
}
var ownerID user.ID
user.IDFromKey(&ownerID, f.key.PrivateKey.PublicKey)
return ownerID
}
func (f *frostfsMock) CreateObject(_ context.Context, prm PrmObjectCreate) (oid.ID, error) { func (f *frostfsMock) CreateObject(_ context.Context, prm PrmObjectCreate) (oid.ID, error) {
var obj object.Object var obj object.Object
obj.SetPayload(prm.Payload) obj.SetPayload(prm.Payload)
obj.SetOwnerID(prm.Creator) obj.SetOwnerID(f.ownerID())
obj.SetContainerID(prm.Container) obj.SetContainerID(prm.Container)
a := object.NewAttribute() a := object.NewAttribute()
@ -81,7 +94,7 @@ func (f *frostfsMock) GetCredsObject(_ context.Context, prm PrmGetCredsObject) (
objects, ok := f.objects[prm.AccessKeyID] objects, ok := f.objects[prm.AccessKeyID]
if !ok { if !ok {
return nil, errors.New("not found") return nil, ErrCustomAccessKeyIDNotFound
} }
return objects[len(objects)-1], nil return objects[len(objects)-1], nil
@ -157,8 +170,9 @@ func TestGetBox(t *testing.T) {
}} }}
secret := []byte("secret") secret := []byte("secret")
accessBox, _, err := accessbox.PackTokens(gateData, secret, false) accessBox, secrets, err := accessbox.PackTokens(gateData, secret, false)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, hex.EncodeToString(secret), secrets.SecretKey)
data, err := accessBox.Marshal() data, err := accessBox.Marshal()
require.NoError(t, err) require.NoError(t, err)
@ -176,11 +190,7 @@ func TestGetBox(t *testing.T) {
} }
t.Run("no removing check, accessbox from cache", func(t *testing.T) { t.Run("no removing check, accessbox from cache", func(t *testing.T) {
frostfs := newFrostfsMock() creds := newCreds(key, cfg, time.Hour)
cfg.FrostFS = frostfs
cfg.RemovingCheckAfterDurations = time.Hour
cfg.Key = key
creds := New(cfg)
cnrID := cidtest.ID() cnrID := cidtest.ID()
addr, err := creds.Put(ctx, CredentialsParam{Container: cnrID, Keys: keys.PublicKeys{key.PublicKey()}, AccessBox: accessBox}) addr, err := creds.Put(ctx, CredentialsParam{Container: cnrID, Keys: keys.PublicKeys{key.PublicKey()}, AccessBox: accessBox})
@ -191,30 +201,26 @@ func TestGetBox(t *testing.T) {
_, _, err = creds.GetBox(ctx, addr.Container(), accessKeyID) _, _, err = creds.GetBox(ctx, addr.Container(), accessKeyID)
require.NoError(t, err) require.NoError(t, err)
frostfs.errors[accessKeyID] = &apistatus.ObjectAlreadyRemoved{} creds.(*cred).frostFS.(*frostfsMock).errors[accessKeyID] = &apistatus.ObjectAlreadyRemoved{}
_, _, err = creds.GetBox(ctx, addr.Container(), accessKeyID) _, _, err = creds.GetBox(ctx, addr.Container(), accessKeyID)
require.NoError(t, err) require.NoError(t, err)
}) })
t.Run("error while getting box from frostfs", func(t *testing.T) { t.Run("error while getting box from frostfs", func(t *testing.T) {
frostfs := newFrostfsMock() creds := newCreds(key, cfg, 0)
cfg.FrostFS = frostfs
cfg.RemovingCheckAfterDurations = 0
cfg.Key = key
creds := New(cfg)
cnrID := cidtest.ID() cnrID := cidtest.ID()
addr, err := creds.Put(ctx, CredentialsParam{Container: cnrID, Keys: keys.PublicKeys{key.PublicKey()}, AccessBox: accessBox}) addr, err := creds.Put(ctx, CredentialsParam{Container: cnrID, Keys: keys.PublicKeys{key.PublicKey()}, AccessBox: accessBox})
require.NoError(t, err) require.NoError(t, err)
accessKeyID := getAccessKeyID(addr) accessKeyID := getAccessKeyID(addr)
frostfs.errors[accessKeyID] = errors.New("network error") creds.(*cred).frostFS.(*frostfsMock).errors[accessKeyID] = errors.New("network error")
_, _, err = creds.GetBox(ctx, addr.Container(), accessKeyID) _, _, err = creds.GetBox(ctx, addr.Container(), accessKeyID)
require.Error(t, err) require.Error(t, err)
}) })
t.Run("invalid key", func(t *testing.T) { t.Run("invalid key", func(t *testing.T) {
frostfs := newFrostfsMock() frostfs := newFrostfsMock(key)
var obj object.Object var obj object.Object
obj.SetPayload(data) obj.SetPayload(data)
@ -232,7 +238,7 @@ func TestGetBox(t *testing.T) {
}) })
t.Run("invalid payload", func(t *testing.T) { t.Run("invalid payload", func(t *testing.T) {
frostfs := newFrostfsMock() frostfs := newFrostfsMock(key)
var obj object.Object var obj object.Object
obj.SetPayload([]byte("invalid")) obj.SetPayload([]byte("invalid"))
@ -250,11 +256,7 @@ func TestGetBox(t *testing.T) {
}) })
t.Run("check attributes update", func(t *testing.T) { t.Run("check attributes update", func(t *testing.T) {
frostfs := newFrostfsMock() creds := newCreds(key, cfg, 0)
cfg.FrostFS = frostfs
cfg.RemovingCheckAfterDurations = 0
cfg.Key = key
creds := New(cfg)
cnrID := cidtest.ID() cnrID := cidtest.ID()
addr, err := creds.Put(ctx, CredentialsParam{Container: cnrID, Keys: keys.PublicKeys{key.PublicKey()}, AccessBox: accessBox}) addr, err := creds.Put(ctx, CredentialsParam{Container: cnrID, Keys: keys.PublicKeys{key.PublicKey()}, AccessBox: accessBox})
@ -280,11 +282,7 @@ func TestGetBox(t *testing.T) {
}) })
t.Run("check accessbox update", func(t *testing.T) { t.Run("check accessbox update", func(t *testing.T) {
frostfs := newFrostfsMock() creds := newCreds(key, cfg, 0)
cfg.FrostFS = frostfs
cfg.RemovingCheckAfterDurations = 0
cfg.Key = key
creds := New(cfg)
cnrID := cidtest.ID() cnrID := cidtest.ID()
addr, err := creds.Put(ctx, CredentialsParam{Container: cnrID, Keys: keys.PublicKeys{key.PublicKey()}, AccessBox: accessBox}) addr, err := creds.Put(ctx, CredentialsParam{Container: cnrID, Keys: keys.PublicKeys{key.PublicKey()}, AccessBox: accessBox})
@ -321,20 +319,36 @@ func TestGetBox(t *testing.T) {
_, _, err = creds.GetBox(ctx, addr.Container(), accessKeyID) _, _, err = creds.GetBox(ctx, addr.Container(), accessKeyID)
require.Error(t, err) require.Error(t, err)
cfg.Key = newKey newCfg := Config{
newCreds := New(cfg) FrostFS: creds.(*cred).frostFS,
Key: newKey,
CacheConfig: cfg.CacheConfig,
}
newCreds := New(newCfg)
box, _, err = newCreds.GetBox(ctx, addr.Container(), accessKeyID) box, _, err = newCreds.GetBox(ctx, addr.Container(), accessKeyID)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, hex.EncodeToString(newSecret), box.Gate.SecretKey) require.Equal(t, hex.EncodeToString(newSecret), box.Gate.SecretKey)
}) })
t.Run("check access key id uniqueness", func(t *testing.T) {
creds := newCreds(key, cfg, 0)
prm := CredentialsParam{
Container: cidtest.ID(),
AccessBox: accessBox,
Keys: keys.PublicKeys{key.PublicKey()},
}
_, err = creds.Put(ctx, prm)
require.NoError(t, err)
_, err = creds.Put(ctx, prm)
require.NoError(t, err)
})
t.Run("empty keys", func(t *testing.T) { t.Run("empty keys", func(t *testing.T) {
frostfs := newFrostfsMock() creds := newCreds(key, cfg, 0)
cfg.FrostFS = frostfs
cfg.RemovingCheckAfterDurations = 0
cfg.Key = key
creds := New(cfg)
cnrID := cidtest.ID() cnrID := cidtest.ID()
_, err = creds.Put(ctx, CredentialsParam{Container: cnrID, AccessBox: accessBox}) _, err = creds.Put(ctx, CredentialsParam{Container: cnrID, AccessBox: accessBox})
@ -342,11 +356,7 @@ func TestGetBox(t *testing.T) {
}) })
t.Run("empty accessbox", func(t *testing.T) { t.Run("empty accessbox", func(t *testing.T) {
frostfs := newFrostfsMock() creds := newCreds(key, cfg, 0)
cfg.FrostFS = frostfs
cfg.RemovingCheckAfterDurations = 0
cfg.Key = key
creds := New(cfg)
cnrID := cidtest.ID() cnrID := cidtest.ID()
_, err = creds.Put(ctx, CredentialsParam{Container: cnrID, Keys: keys.PublicKeys{key.PublicKey()}}) _, err = creds.Put(ctx, CredentialsParam{Container: cnrID, Keys: keys.PublicKeys{key.PublicKey()}})
@ -354,6 +364,73 @@ func TestGetBox(t *testing.T) {
}) })
} }
func TestBoxWithCustomAccessKeyID(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, secrets, err := accessbox.PackTokens(gateData, secret, true)
require.NoError(t, err)
require.Equal(t, string(secret), secrets.SecretKey)
cfg := Config{
CacheConfig: &cache.Config{
Size: 10,
Lifetime: 24 * time.Hour,
Logger: zaptest.NewLogger(t),
},
}
t.Run("check secret format", func(t *testing.T) {
creds := newCreds(key, cfg, 0)
prm := CredentialsParam{
Container: cidtest.ID(),
AccessKeyID: "custom-access-key-id",
AccessBox: accessBox,
Keys: keys.PublicKeys{key.PublicKey()},
}
_, err = creds.Put(ctx, prm)
require.NoError(t, err)
box, _, err := creds.GetBox(ctx, prm.Container, prm.AccessKeyID)
require.NoError(t, err)
require.Equal(t, string(secret), box.Gate.SecretKey)
})
t.Run("check custom access key id uniqueness", func(t *testing.T) {
creds := newCreds(key, cfg, 0)
prm := CredentialsParam{
Container: cidtest.ID(),
AccessKeyID: "custom-access-key-id",
AccessBox: accessBox,
Keys: keys.PublicKeys{key.PublicKey()},
}
_, err = creds.Put(ctx, prm)
require.NoError(t, err)
_, err = creds.Put(ctx, prm)
require.Error(t, err)
})
}
func newCreds(key *keys.PrivateKey, cfg Config, removingCheckDuration time.Duration) Credentials {
frostfs := newFrostfsMock(key)
cfg.FrostFS = frostfs
cfg.RemovingCheckAfterDurations = removingCheckDuration
cfg.Key = key
return New(cfg)
}
func getAccessKeyID(addr oid.Address) string { func getAccessKeyID(addr oid.Address) string {
return strings.ReplaceAll(addr.EncodeToString(), "/", "0") return strings.ReplaceAll(addr.EncodeToString(), "/", "0")
} }

View file

@ -2,23 +2,27 @@ package frostfs
import ( import (
"context" "context"
"strconv"
"strings" "strings"
"testing" "testing"
objectv2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/authmate" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/authmate"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/tokens" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/tokens"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest" "go.uber.org/zap/zaptest"
) )
func TestGetCredsObject(t *testing.T) { func TestCredsObject(t *testing.T) {
ctx, bktName, payload, newPayload := context.Background(), "bucket", []byte("payload"), []byte("new-payload") ctx, bktName, payload, newPayload := context.Background(), "bucket", []byte("payload"), []byte("new-payload")
key, err := keys.NewPrivateKey() key, err := keys.NewPrivateKey()
@ -45,38 +49,166 @@ func TestGetCredsObject(t *testing.T) {
}) })
require.NoError(t, err) require.NoError(t, err)
objID, err := frostfs.CreateObject(ctx, tokens.PrmObjectCreate{ t.Run("regular access key", func(t *testing.T) {
attr1 := object.NewAttribute()
attr1.SetKey("attr1")
attr1.SetValue("val1")
prm := tokens.PrmObjectCreate{
Container: cnrID, Container: cnrID,
Filepath: "regular-obj",
ExpirationEpoch: 10,
Payload: payload, Payload: payload,
}) CustomAttributes: []object.Attribute{*attr1},
}
objID, err := frostfs.CreateObject(ctx, prm)
require.NoError(t, err) require.NoError(t, err)
var addr oid.Address accessKeyID := cnrID.EncodeToString() + "0" + objID.EncodeToString()
addr.SetContainer(cnrID)
addr.SetObject(objID)
accessKeyID := getAccessKeyID(addr) obj, err := frostfs.GetCredsObject(ctx, tokens.PrmGetCredsObject{Container: cnrID, AccessKeyID: accessKeyID})
require.NoError(t, err)
assertParamsSet(t, prm, obj, userID)
obj, err := frostfs.GetCredsObject(ctx, tokens.PrmGetCredsObject{ t.Run("update existing", func(t *testing.T) {
attr2 := object.NewAttribute()
attr2.SetKey("attr2")
attr2.SetValue("val2")
prmNew := tokens.PrmObjectCreate{
Container: cnrID, Container: cnrID,
AccessKeyID: accessKeyID, Filepath: "regular-obj-new",
}) ExpirationEpoch: 11,
require.NoError(t, err) Payload: newPayload,
require.Equal(t, payload, obj.Payload()) CustomAttributes: []object.Attribute{*attr2},
NewVersionForAccessKeyID: accessKeyID,
}
_, err = frostfs.CreateObject(ctx, tokens.PrmObjectCreate{ _, err = frostfs.CreateObject(ctx, prmNew)
require.NoError(t, err)
obj, err = frostfs.GetCredsObject(ctx, tokens.PrmGetCredsObject{Container: cnrID, AccessKeyID: accessKeyID})
require.NoError(t, err)
assertParamsSet(t, prmNew, obj, userID)
})
t.Run("update not existing", func(t *testing.T) {
attr2 := object.NewAttribute()
attr2.SetKey("attr2")
attr2.SetValue("val2")
addr := oidtest.Address()
accessKeyIDNotExisting := getAccessKeyID(addr)
prmNew := tokens.PrmObjectCreate{
Container: cnrID,
Filepath: "regular-obj-new",
ExpirationEpoch: 11,
Payload: newPayload,
CustomAttributes: []object.Attribute{*attr2},
NewVersionForAccessKeyID: accessKeyIDNotExisting,
}
_, err = frostfs.CreateObject(ctx, prmNew)
require.NoError(t, err)
obj, err = frostfs.GetCredsObject(ctx, tokens.PrmGetCredsObject{Container: cnrID, AccessKeyID: accessKeyIDNotExisting})
require.NoError(t, err)
assertParamsSet(t, prmNew, obj, userID)
})
})
t.Run("custom access key", func(t *testing.T) {
attr1 := object.NewAttribute()
attr1.SetKey("attr1")
attr1.SetValue("val1")
accessKeyID := "custom-access-key-id"
prm := tokens.PrmObjectCreate{
Container: cnrID,
Filepath: "custom-obj",
ExpirationEpoch: 10,
Payload: payload,
CustomAccessKey: accessKeyID,
CustomAttributes: []object.Attribute{*attr1},
}
_, err = frostfs.CreateObject(ctx, prm)
require.NoError(t, err)
obj, err := frostfs.GetCredsObject(ctx, tokens.PrmGetCredsObject{Container: cnrID, AccessKeyID: accessKeyID})
require.NoError(t, err)
assertParamsSet(t, prm, obj, userID)
t.Run("update", func(t *testing.T) {
attr2 := object.NewAttribute()
attr2.SetKey("attr2")
attr2.SetValue("val2")
prmNew := tokens.PrmObjectCreate{
Container: cnrID,
Filepath: "custom-obj-new",
ExpirationEpoch: 11,
Payload: newPayload,
CustomAttributes: []object.Attribute{*attr2},
NewVersionForAccessKeyID: accessKeyID,
}
_, err = frostfs.CreateObject(ctx, prmNew)
require.NoError(t, err)
obj, err = frostfs.GetCredsObject(ctx, tokens.PrmGetCredsObject{Container: cnrID, AccessKeyID: accessKeyID})
require.NoError(t, err)
assertParamsSet(t, prmNew, obj, userID)
})
t.Run("update not existing", func(t *testing.T) {
accessKeyIDNotExisting := "unknown"
prmNew := tokens.PrmObjectCreate{
Container: cnrID, Container: cnrID,
Payload: newPayload, Payload: newPayload,
NewVersionForAccessKeyID: accessKeyID, NewVersionForAccessKeyID: accessKeyIDNotExisting,
}) }
require.NoError(t, err)
obj, err = frostfs.GetCredsObject(ctx, tokens.PrmGetCredsObject{ _, err = frostfs.CreateObject(ctx, prmNew)
Container: cnrID, require.Error(t, err)
AccessKeyID: getAccessKeyID(addr),
}) })
require.NoError(t, err) })
require.Equal(t, newPayload, obj.Payload()) }
func assertParamsSet(t *testing.T, prm tokens.PrmObjectCreate, obj *object.Object, userID user.ID) {
require.Equal(t, prm.Payload, obj.Payload())
require.True(t, userID.Equals(obj.OwnerID()), "owners not matched")
require.True(t, containerAttribute(obj.Attributes(), object.AttributeFilePath, prm.Filepath), "missing FilePath")
require.True(t, containerAttribute(obj.Attributes(), objectv2.SysAttributeExpEpoch, strconv.FormatUint(prm.ExpirationEpoch, 10)), "missing expiration epoch")
var crdtName string
if prm.CustomAccessKey != "" {
crdtName = prm.CustomAccessKey
} else if prm.NewVersionForAccessKeyID != "" {
crdtName = prm.NewVersionForAccessKeyID
}
if crdtName != "" {
require.Truef(t, containerAttribute(obj.Attributes(), accessBoxCRDTNameAttr, crdtName), "wrong crdt name '%s'", crdtName)
}
for _, attr := range prm.CustomAttributes {
require.True(t, containerAttribute(obj.Attributes(), attr.Key(), attr.Value()), "missing custom attribute")
}
}
func containerAttribute(attrs []object.Attribute, key, val string) bool {
for _, attr := range attrs {
if attr.Key() == key && attr.Value() == val {
return true
}
}
return false
} }
func getAccessKeyID(addr oid.Address) string { func getAccessKeyID(addr oid.Address) string {