Dmitrii Stepanov
f1b2b8bffa
`debug` is always true. Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
279 lines
8.1 KiB
Go
279 lines
8.1 KiB
Go
package tree
|
|
|
|
import (
|
|
"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(req, cid2, nil, op))
|
|
})
|
|
|
|
require.NoError(t, SignMessage(req, &privs[0].PrivateKey))
|
|
require.NoError(t, s.verifyClient(req, cid1, nil, op))
|
|
|
|
t.Run("invalid CID", func(t *testing.T) {
|
|
require.Error(t, s.verifyClient(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(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(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(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(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(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(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(req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectPut))
|
|
require.NoError(t, s.verifyClient(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(req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectPut))
|
|
require.NoError(t, s.verifyClient(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(req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectPut))
|
|
require.NoError(t, s.verifyClient(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(req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectPut))
|
|
require.Error(t, s.verifyClient(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
|
|
}
|