diff --git a/creds/accessbox/bearer_token_test.go b/creds/accessbox/bearer_token_test.go index 57739dba..593332f9 100644 --- a/creds/accessbox/bearer_token_test.go +++ b/creds/accessbox/bearer_token_test.go @@ -1,6 +1,7 @@ package accessbox import ( + "encoding/hex" "testing" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer" @@ -170,3 +171,148 @@ func TestUnknownKey(t *testing.T) { _, err = box.GetTokens(wrongCred) require.Error(t, err) } + +func TestGateDataSessionToken(t *testing.T) { + cred, err := keys.NewPrivateKey() + require.NoError(t, err) + + var tkn bearer.Token + gate := NewGateData(cred.PublicKey(), &tkn) + require.Equal(t, cred.PublicKey(), gate.GateKey) + assertBearerToken(t, tkn, *gate.BearerToken) + + t.Run("session token for put", func(t *testing.T) { + gate.SessionTokens = []*session.Container{} + sessionTkn := gate.SessionTokenForPut() + require.Nil(t, sessionTkn) + + sessionTknPut := new(session.Container) + sessionTknPut.ForVerb(session.VerbContainerPut) + gate.SessionTokens = []*session.Container{sessionTknPut} + sessionTkn = gate.SessionTokenForPut() + require.Equal(t, sessionTknPut, sessionTkn) + }) + + t.Run("session token for delete", func(t *testing.T) { + gate.SessionTokens = []*session.Container{} + sessionTkn := gate.SessionTokenForDelete() + require.Nil(t, sessionTkn) + + sessionTknDelete := new(session.Container) + sessionTknDelete.ForVerb(session.VerbContainerDelete) + gate.SessionTokens = []*session.Container{sessionTknDelete} + sessionTkn = gate.SessionTokenForDelete() + require.Equal(t, sessionTknDelete, sessionTkn) + }) + + t.Run("session token for set eACL", func(t *testing.T) { + gate.SessionTokens = []*session.Container{} + sessionTkn := gate.SessionTokenForSetEACL() + require.Nil(t, sessionTkn) + + sessionTknSetEACL := new(session.Container) + sessionTknSetEACL.ForVerb(session.VerbContainerSetEACL) + gate.SessionTokens = []*session.Container{sessionTknSetEACL} + sessionTkn = gate.SessionTokenForSetEACL() + require.Equal(t, sessionTknSetEACL, sessionTkn) + }) + + t.Run("session token", func(t *testing.T) { + gate.SessionTokens = []*session.Container{} + sessionTkn := gate.SessionToken() + require.Nil(t, sessionTkn) + + sessionTknPut := new(session.Container) + sessionTknPut.ForVerb(session.VerbContainerPut) + gate.SessionTokens = []*session.Container{sessionTknPut} + sessionTkn = gate.SessionToken() + require.Equal(t, sessionTkn, sessionTknPut) + }) +} + +func TestGetBox(t *testing.T) { + cred, err := keys.NewPrivateKey() + require.NoError(t, err) + + var tkn bearer.Token + gate := NewGateData(cred.PublicKey(), &tkn) + + secret := []byte("secret") + accessBox, _, err := PackTokens([]*GateData{gate}, secret) + require.NoError(t, err) + + box, err := accessBox.GetBox(cred) + require.NoError(t, err) + require.Equal(t, hex.EncodeToString(secret), box.Gate.SecretKey) +} + +func TestAccessBox(t *testing.T) { + cred, err := keys.NewPrivateKey() + require.NoError(t, err) + + var tkn bearer.Token + gate := NewGateData(cred.PublicKey(), &tkn) + + accessBox, _, err := PackTokens([]*GateData{gate}, nil) + require.NoError(t, err) + + t.Run("invalid owner", func(t *testing.T) { + randomKey, err := keys.NewPrivateKey() + require.NoError(t, err) + + _, err = accessBox.GetTokens(randomKey) + require.Error(t, err) + + _, err = accessBox.GetBox(randomKey) + require.Error(t, err) + }) + + t.Run("empty placement policy", func(t *testing.T) { + policy, err := accessBox.GetPlacementPolicy() + require.NoError(t, err) + require.Nil(t, policy) + }) + + t.Run("get correct placement policy", func(t *testing.T) { + policy := &AccessBox_ContainerPolicy{LocationConstraint: "locationConstraint"} + accessBox.ContainerPolicy = []*AccessBox_ContainerPolicy{policy} + + policies, err := accessBox.GetPlacementPolicy() + require.NoError(t, err) + require.Len(t, policies, 1) + require.Equal(t, policy.LocationConstraint, policies[0].LocationConstraint) + }) + + t.Run("get incorrect placement policy", func(t *testing.T) { + policy := &AccessBox_ContainerPolicy{ + LocationConstraint: "locationConstraint", + Policy: []byte("policy"), + } + accessBox.ContainerPolicy = []*AccessBox_ContainerPolicy{policy} + + _, err = accessBox.GetPlacementPolicy() + require.Error(t, err) + + _, err = accessBox.GetBox(cred) + require.Error(t, err) + }) + + t.Run("empty seed key", func(t *testing.T) { + accessBox.SeedKey = nil + + _, err = accessBox.GetTokens(cred) + require.Error(t, err) + + _, err = accessBox.GetBox(cred) + require.Error(t, err) + }) + + t.Run("invalid gate key", func(t *testing.T) { + gate = &GateData{ + BearerToken: &tkn, + GateKey: &keys.PublicKey{}, + } + _, _, err = PackTokens([]*GateData{gate}, nil) + require.Error(t, err) + }) +} diff --git a/creds/tokens/credentials_test.go b/creds/tokens/credentials_test.go index b38a465a..35240454 100644 --- a/creds/tokens/credentials_test.go +++ b/creds/tokens/credentials_test.go @@ -11,6 +11,7 @@ import ( "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" @@ -20,12 +21,55 @@ import ( ) type frostfsMock struct { - objects map[oid.Address][]byte + objects map[oid.Address][]*object.Object errors map[oid.Address]error } -func (f *frostfsMock) CreateObject(context.Context, PrmObjectCreate) (oid.ID, error) { - panic("implement me for test") +func newFrostfsMock() *frostfsMock { + return &frostfsMock{ + objects: map[oid.Address][]*object.Object{}, + errors: map[oid.Address]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.NewVersionFor != nil { + var addr oid.Address + addr.SetObject(*prm.NewVersionFor) + addr.SetContainer(prm.Container) + + _, ok := f.objects[addr] + if !ok { + return oid.ID{}, errors.New("not found") + } + + objID := oidtest.ID() + obj.SetID(objID) + f.objects[addr] = append(f.objects[addr], &obj) + + return objID, nil + } + + objID := oidtest.ID() + obj.SetID(objID) + + var addr oid.Address + addr.SetObject(objID) + addr.SetContainer(prm.Container) + f.objects[addr] = []*object.Object{&obj} + + return objID, nil } func (f *frostfsMock) GetCredsObject(_ context.Context, address oid.Address) (*object.Object, error) { @@ -33,14 +77,12 @@ func (f *frostfsMock) GetCredsObject(_ context.Context, address oid.Address) (*o return nil, err } - data, ok := f.objects[address] + objects, ok := f.objects[address] if !ok { return nil, errors.New("not found") } - var obj object.Object - obj.SetPayload(data) - return &obj, nil + return objects[len(objects)-1], nil } func TestRemovingAccessBox(t *testing.T) { @@ -63,9 +105,14 @@ func TestRemovingAccessBox(t *testing.T) { 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()) + frostfs := &frostfsMock{ - objects: map[oid.Address][]byte{addr: data}, + objects: map[oid.Address][]*object.Object{addr: {&obj}}, errors: map[oid.Address]error{}, } @@ -93,3 +140,190 @@ func TestRemovingAccessBox(t *testing.T) { _, _, err = creds.GetBox(ctx, addr) 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) + 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, cnrID, CredentialsParam{Keys: keys.PublicKeys{key.PublicKey()}, AccessBox: accessBox}) + require.NoError(t, err) + + _, _, err = creds.GetBox(ctx, addr) + require.NoError(t, err) + + frostfs.errors[addr] = &apistatus.ObjectAlreadyRemoved{} + _, _, err = creds.GetBox(ctx, addr) + 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, cnrID, CredentialsParam{Keys: keys.PublicKeys{key.PublicKey()}, AccessBox: accessBox}) + require.NoError(t, err) + + frostfs.errors[addr] = errors.New("network error") + _, _, err = creds.GetBox(ctx, addr) + require.Error(t, err) + }) + + t.Run("invalid key", func(t *testing.T) { + frostfs := newFrostfsMock() + + var obj object.Object + obj.SetPayload(data) + addr := oidtest.Address() + frostfs.objects[addr] = []*object.Object{&obj} + + cfg.FrostFS = frostfs + cfg.RemovingCheckAfterDurations = 0 + cfg.Key = &keys.PrivateKey{} + creds := New(cfg) + + _, _, err = creds.GetBox(ctx, addr) + 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() + frostfs.objects[addr] = []*object.Object{&obj} + + cfg.FrostFS = frostfs + cfg.RemovingCheckAfterDurations = 0 + cfg.Key = key + creds := New(cfg) + + _, _, err = creds.GetBox(ctx, addr) + 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, cnrID, CredentialsParam{Keys: keys.PublicKeys{key.PublicKey()}, AccessBox: accessBox}) + require.NoError(t, err) + + _, boxAttrs, err := creds.GetBox(ctx, addr) + require.NoError(t, err) + + _, err = creds.Update(ctx, addr, CredentialsParam{Keys: keys.PublicKeys{key.PublicKey()}, AccessBox: accessBox, CustomAttributes: attrs}) + require.NoError(t, err) + + _, newBoxAttrs, err := creds.GetBox(ctx, addr) + 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, cnrID, CredentialsParam{Keys: keys.PublicKeys{key.PublicKey()}, AccessBox: accessBox}) + require.NoError(t, err) + + box, _, err := creds.GetBox(ctx, addr) + 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) + require.NoError(t, err) + + _, err = creds.Update(ctx, addr, CredentialsParam{Keys: keys.PublicKeys{newKey.PublicKey()}, AccessBox: newAccessBox}) + require.NoError(t, err) + + _, _, err = creds.GetBox(ctx, addr) + require.Error(t, err) + + cfg.Key = newKey + newCreds := New(cfg) + + box, _, err = newCreds.GetBox(ctx, addr) + 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, cnrID, CredentialsParam{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, cnrID, CredentialsParam{Keys: keys.PublicKeys{key.PublicKey()}}) + require.ErrorIs(t, err, ErrEmptyBearerToken) + }) +}