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 }