frostfs-node/pkg/services/tree/signature_test.go

281 lines
8.5 KiB
Go
Raw Permalink Normal View History

package tree
import (
"context"
"crypto/ecdsa"
"crypto/sha256"
"errors"
"testing"
aclV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
containercore "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/container"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/netmap"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger/test"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
eaclSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
netmapSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/stretchr/testify/require"
)
type dummyNetmapSource struct {
netmap.Source
}
type dummyContainerSource map[string]*containercore.Container
func (s dummyContainerSource) List() ([]cid.ID, error) {
res := make([]cid.ID, 0, len(s))
var cnr cid.ID
for cidStr := range s {
err := cnr.DecodeString(cidStr)
if err != nil {
return nil, err
}
res = append(res, cnr)
}
return res, nil
}
func (s dummyContainerSource) Get(id cid.ID) (*containercore.Container, error) {
cnt, ok := s[id.String()]
if !ok {
return nil, errors.New("container not found")
}
return cnt, nil
}
func (s dummyContainerSource) DeletionInfo(id cid.ID) (*containercore.DelInfo, error) {
return &containercore.DelInfo{}, nil
}
type dummyEACLSource map[string]*containercore.EACL
func (s dummyEACLSource) GetEACL(id cid.ID) (*containercore.EACL, error) {
cntEACL, ok := s[id.String()]
if !ok {
return nil, errors.New("container not found")
}
return cntEACL, nil
}
func testContainer(owner user.ID) container.Container {
var r netmapSDK.ReplicaDescriptor
r.SetNumberOfObjects(1)
var pp netmapSDK.PlacementPolicy
pp.AddReplicas(r)
var cnt container.Container
cnt.SetOwner(owner)
cnt.SetPlacementPolicy(pp)
return cnt
}
func TestMessageSign(t *testing.T) {
privs := make([]*keys.PrivateKey, 4)
for i := range privs {
p, err := keys.NewPrivateKey()
require.NoError(t, err)
privs[i] = p
}
cid1 := cidtest.ID()
cid2 := cidtest.ID()
var ownerID user.ID
user.IDFromKey(&ownerID, (ecdsa.PublicKey)(*privs[0].PublicKey()))
cnr := &containercore.Container{
Value: testContainer(ownerID),
}
s := &Service{
cfg: cfg{
log: test.NewLogger(t),
key: &privs[0].PrivateKey,
nmSource: dummyNetmapSource{},
cnrSource: dummyContainerSource{
cid1.String(): cnr,
},
eaclSource: dummyEACLSource{
cid1.String(): &containercore.EACL{
Value: testTable(cid1, privs[0].PublicKey(), privs[1].PublicKey()),
},
},
},
}
rawCID1 := make([]byte, sha256.Size)
cid1.Encode(rawCID1)
req := &MoveRequest{
Body: &MoveRequest_Body{
ContainerId: rawCID1,
ParentId: 1,
NodeId: 2,
Meta: []KeyValue{
{Key: "kkk", Value: []byte("vvv")},
},
},
}
op := acl.OpObjectPut
cnr.Value.SetBasicACL(acl.PublicRW)
t.Run("missing signature, no panic", func(t *testing.T) {
require.Error(t, s.verifyClient(context.Background(), req, cid2, nil, op))
})
require.NoError(t, SignMessage(req, &privs[0].PrivateKey))
require.NoError(t, s.verifyClient(context.Background(), req, cid1, nil, op))
t.Run("invalid CID", func(t *testing.T) {
require.Error(t, s.verifyClient(context.Background(), req, cid2, nil, op))
})
cnr.Value.SetBasicACL(acl.Private)
t.Run("extension disabled", func(t *testing.T) {
require.NoError(t, SignMessage(req, &privs[0].PrivateKey))
require.Error(t, s.verifyClient(context.Background(), req, cid2, nil, op))
})
t.Run("invalid key", func(t *testing.T) {
require.NoError(t, SignMessage(req, &privs[1].PrivateKey))
require.Error(t, s.verifyClient(context.Background(), req, cid1, nil, op))
})
t.Run("bearer", func(t *testing.T) {
bACL := acl.PrivateExtended
bACL.AllowBearerRules(op)
cnr.Value.SetBasicACL(bACL)
bACL.DisableExtension()
t.Run("invalid bearer", func(t *testing.T) {
req.Body.BearerToken = []byte{0xFF}
require.NoError(t, SignMessage(req, &privs[0].PrivateKey))
require.Error(t, s.verifyClient(context.Background(), req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectPut))
})
t.Run("invalid bearer CID", func(t *testing.T) {
bt := testBearerToken(cid2, privs[1].PublicKey(), privs[2].PublicKey())
require.NoError(t, bt.Sign(privs[0].PrivateKey))
req.Body.BearerToken = bt.Marshal()
require.NoError(t, SignMessage(req, &privs[1].PrivateKey))
require.Error(t, s.verifyClient(context.Background(), req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectPut))
})
t.Run("invalid bearer owner", func(t *testing.T) {
bt := testBearerToken(cid1, privs[1].PublicKey(), privs[2].PublicKey())
require.NoError(t, bt.Sign(privs[1].PrivateKey))
req.Body.BearerToken = bt.Marshal()
require.NoError(t, SignMessage(req, &privs[1].PrivateKey))
require.Error(t, s.verifyClient(context.Background(), req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectPut))
})
t.Run("invalid bearer signature", func(t *testing.T) {
bt := testBearerToken(cid1, privs[1].PublicKey(), privs[2].PublicKey())
require.NoError(t, bt.Sign(privs[0].PrivateKey))
var bv2 aclV2.BearerToken
bt.WriteToV2(&bv2)
bv2.GetSignature().SetSign([]byte{1, 2, 3})
req.Body.BearerToken = bv2.StableMarshal(nil)
require.NoError(t, SignMessage(req, &privs[1].PrivateKey))
require.Error(t, s.verifyClient(context.Background(), req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectPut))
})
t.Run("impersonate", func(t *testing.T) {
cnr.Value.SetBasicACL(acl.PublicRWExtended)
var bt bearer.Token
bt.SetImpersonate(true)
require.NoError(t, bt.Sign(privs[1].PrivateKey))
req.Body.BearerToken = bt.Marshal()
require.NoError(t, SignMessage(req, &privs[0].PrivateKey))
require.Error(t, s.verifyClient(context.Background(), req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectPut))
require.NoError(t, s.verifyClient(context.Background(), req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectGet))
})
bt := testBearerToken(cid1, privs[1].PublicKey(), privs[2].PublicKey())
require.NoError(t, bt.Sign(privs[0].PrivateKey))
req.Body.BearerToken = bt.Marshal()
cnr.Value.SetBasicACL(acl.PublicRWExtended)
t.Run("put and get", func(t *testing.T) {
require.NoError(t, SignMessage(req, &privs[1].PrivateKey))
require.NoError(t, s.verifyClient(context.Background(), req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectPut))
require.NoError(t, s.verifyClient(context.Background(), req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectGet))
})
t.Run("only get", func(t *testing.T) {
require.NoError(t, SignMessage(req, &privs[2].PrivateKey))
require.Error(t, s.verifyClient(context.Background(), req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectPut))
require.NoError(t, s.verifyClient(context.Background(), req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectGet))
})
t.Run("none", func(t *testing.T) {
require.NoError(t, SignMessage(req, &privs[3].PrivateKey))
require.Error(t, s.verifyClient(context.Background(), req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectPut))
require.Error(t, s.verifyClient(context.Background(), req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectGet))
})
})
}
func testBearerToken(cid cid.ID, forPutGet, forGet *keys.PublicKey) bearer.Token {
var b bearer.Token
b.SetEACLTable(*testTable(cid, forPutGet, forGet))
return b
}
func testTable(cid cid.ID, forPutGet, forGet *keys.PublicKey) *eaclSDK.Table {
tgtGet := eaclSDK.NewTarget()
tgtGet.SetRole(eaclSDK.RoleUnknown)
tgtGet.SetBinaryKeys([][]byte{forPutGet.Bytes(), forGet.Bytes()})
rGet := eaclSDK.NewRecord()
rGet.SetAction(eaclSDK.ActionAllow)
rGet.SetOperation(eaclSDK.OperationGet)
rGet.SetTargets(*tgtGet)
tgtPut := eaclSDK.NewTarget()
tgtPut.SetRole(eaclSDK.RoleUnknown)
tgtPut.SetBinaryKeys([][]byte{forPutGet.Bytes()})
rPut := eaclSDK.NewRecord()
rPut.SetAction(eaclSDK.ActionAllow)
rPut.SetOperation(eaclSDK.OperationPut)
rPut.SetTargets(*tgtPut)
tb := eaclSDK.NewTable()
tb.AddRecord(rGet)
tb.AddRecord(rPut)
tgt := eaclSDK.NewTarget()
tgt.SetRole(eaclSDK.RoleOthers)
for _, op := range []eaclSDK.Operation{eaclSDK.OperationGet, eaclSDK.OperationPut} {
r := eaclSDK.NewRecord()
r.SetAction(eaclSDK.ActionDeny)
r.SetTargets(*tgt)
r.SetOperation(op)
tb.AddRecord(r)
}
tb.SetCID(cid)
return tb
}