From e610d1ea5f9ed3873a2d53b0a65f6f387cc66670 Mon Sep 17 00:00:00 2001 From: Alex Vanin Date: Thu, 16 Dec 2021 18:26:13 +0300 Subject: [PATCH] [#1045] Provide sanity check of session token in container service Without sanity check, container service provides successful response, even though such request will never be approved by Alphabet nodes. Signed-off-by: Alex Vanin --- pkg/services/container/morph/executor.go | 27 +++-- pkg/services/container/morph/executor_test.go | 101 ++++++++++++++++++ 2 files changed, 122 insertions(+), 6 deletions(-) create mode 100644 pkg/services/container/morph/executor_test.go diff --git a/pkg/services/container/morph/executor.go b/pkg/services/container/morph/executor.go index 91f1864c0..0a9d3840a 100644 --- a/pkg/services/container/morph/executor.go +++ b/pkg/services/container/morph/executor.go @@ -2,6 +2,7 @@ package container import ( "context" + "errors" "github.com/nspcc-dev/neofs-api-go/v2/container" "github.com/nspcc-dev/neofs-api-go/v2/refs" @@ -42,6 +43,10 @@ type Writer interface { PutEACL(*eaclSDK.Table) error } +// ErrInvalidContext is thrown by morph ServiceExecutor when provided session +// token does not contain expected container context. +var ErrInvalidContext = errors.New("session token does not contain container context") + func NewExecutor(rdr Reader, wrt Writer) containerSvc.ServiceExecutor { return &morphExecutor{ rdr: rdr, @@ -56,9 +61,12 @@ func (s *morphExecutor) Put(ctx containerSvc.ContextWithToken, body *container.P signature.NewFromV2(body.GetSignature()), ) - cnr.SetSessionToken( - session.NewTokenFromV2(ctx.SessionToken), - ) + tok := session.NewTokenFromV2(ctx.SessionToken) + if ctx.SessionToken != nil && session.GetContainerContext(tok) == nil { + return nil, ErrInvalidContext + } + + cnr.SetSessionToken(tok) cid, err := s.wrt.Put(cnr) if err != nil { @@ -74,7 +82,11 @@ func (s *morphExecutor) Put(ctx containerSvc.ContextWithToken, body *container.P func (s *morphExecutor) Delete(ctx containerSvc.ContextWithToken, body *container.DeleteRequestBody) (*container.DeleteResponseBody, error) { id := cid.NewFromV2(body.GetContainerID()) sig := body.GetSignature().GetSign() + tok := session.NewTokenFromV2(ctx.SessionToken) + if ctx.SessionToken != nil && session.GetContainerContext(tok) == nil { + return nil, ErrInvalidContext + } var rmWitness containercore.RemovalWitness @@ -131,9 +143,12 @@ func (s *morphExecutor) SetExtendedACL(ctx containerSvc.ContextWithToken, body * table.SetSignature(sign) - table.SetSessionToken( - session.NewTokenFromV2(ctx.SessionToken), - ) + tok := session.NewTokenFromV2(ctx.SessionToken) + if ctx.SessionToken != nil && session.GetContainerContext(tok) == nil { + return nil, ErrInvalidContext + } + + table.SetSessionToken(tok) err := s.wrt.PutEACL(table) if err != nil { diff --git a/pkg/services/container/morph/executor_test.go b/pkg/services/container/morph/executor_test.go new file mode 100644 index 000000000..857fd1e9f --- /dev/null +++ b/pkg/services/container/morph/executor_test.go @@ -0,0 +1,101 @@ +package container_test + +import ( + "context" + "testing" + + "github.com/nspcc-dev/neofs-api-go/v2/container" + "github.com/nspcc-dev/neofs-api-go/v2/session" + containerCore "github.com/nspcc-dev/neofs-node/pkg/core/container" + containerSvc "github.com/nspcc-dev/neofs-node/pkg/services/container" + containerSvcMorph "github.com/nspcc-dev/neofs-node/pkg/services/container/morph" + containerSDK "github.com/nspcc-dev/neofs-sdk-go/container" + cid "github.com/nspcc-dev/neofs-sdk-go/container/id" + "github.com/nspcc-dev/neofs-sdk-go/eacl" + "github.com/nspcc-dev/neofs-sdk-go/owner" + "github.com/stretchr/testify/require" +) + +type mock struct{} + +func (m mock) Put(c *containerSDK.Container) (*cid.ID, error) { + return new(cid.ID), nil +} + +func (m mock) Delete(witness containerCore.RemovalWitness) error { + return nil +} + +func (m mock) PutEACL(table *eacl.Table) error { + return nil +} + +func (m mock) Get(id *cid.ID) (*containerSDK.Container, error) { + panic("implement me") +} + +func (m mock) GetEACL(id *cid.ID) (*eacl.Table, error) { + panic("implement me") +} + +func (m mock) List(id *owner.ID) ([]*cid.ID, error) { + panic("implement me") +} + +func TestInvalidToken(t *testing.T) { + m := mock{} + e := containerSvcMorph.NewExecutor(m, m) + + tests := []struct { + name string + op func(e containerSvc.ServiceExecutor, ctx containerSvc.ContextWithToken) error + }{ + { + name: "put", + op: func(e containerSvc.ServiceExecutor, ctx containerSvc.ContextWithToken) (err error) { + _, err = e.Put(ctx, new(container.PutRequestBody)) + return + }, + }, + { + name: "delete", + op: func(e containerSvc.ServiceExecutor, ctx containerSvc.ContextWithToken) (err error) { + _, err = e.Delete(ctx, new(container.DeleteRequestBody)) + return + }, + }, + { + name: "setEACL", + op: func(e containerSvc.ServiceExecutor, ctx containerSvc.ContextWithToken) (err error) { + _, err = e.SetExtendedACL(ctx, new(container.SetExtendedACLRequestBody)) + return + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + ctx := containerSvc.ContextWithToken{ + Context: context.Background(), + SessionToken: generateToken(new(session.ObjectSessionContext)), + } + require.Error(t, test.op(e, ctx), containerSvcMorph.ErrInvalidContext) + + ctx.SessionToken = generateToken(new(session.ContainerSessionContext)) + require.NoError(t, test.op(e, ctx)) + + ctx.SessionToken = nil + require.NoError(t, test.op(e, ctx)) + }) + } +} + +func generateToken(ctx session.SessionTokenContext) *session.SessionToken { + body := new(session.SessionTokenBody) + body.SetContext(ctx) + + tok := new(session.SessionToken) + tok.SetBody(body) + + return tok +}