forked from TrueCloudLab/frostfs-node
[#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 <alexey@nspcc.ru>
This commit is contained in:
parent
acb4a9e5b4
commit
e610d1ea5f
2 changed files with 122 additions and 6 deletions
|
@ -2,6 +2,7 @@ package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neofs-api-go/v2/container"
|
"github.com/nspcc-dev/neofs-api-go/v2/container"
|
||||||
"github.com/nspcc-dev/neofs-api-go/v2/refs"
|
"github.com/nspcc-dev/neofs-api-go/v2/refs"
|
||||||
|
@ -42,6 +43,10 @@ type Writer interface {
|
||||||
PutEACL(*eaclSDK.Table) error
|
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 {
|
func NewExecutor(rdr Reader, wrt Writer) containerSvc.ServiceExecutor {
|
||||||
return &morphExecutor{
|
return &morphExecutor{
|
||||||
rdr: rdr,
|
rdr: rdr,
|
||||||
|
@ -56,9 +61,12 @@ func (s *morphExecutor) Put(ctx containerSvc.ContextWithToken, body *container.P
|
||||||
signature.NewFromV2(body.GetSignature()),
|
signature.NewFromV2(body.GetSignature()),
|
||||||
)
|
)
|
||||||
|
|
||||||
cnr.SetSessionToken(
|
tok := session.NewTokenFromV2(ctx.SessionToken)
|
||||||
session.NewTokenFromV2(ctx.SessionToken),
|
if ctx.SessionToken != nil && session.GetContainerContext(tok) == nil {
|
||||||
)
|
return nil, ErrInvalidContext
|
||||||
|
}
|
||||||
|
|
||||||
|
cnr.SetSessionToken(tok)
|
||||||
|
|
||||||
cid, err := s.wrt.Put(cnr)
|
cid, err := s.wrt.Put(cnr)
|
||||||
if err != nil {
|
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) {
|
func (s *morphExecutor) Delete(ctx containerSvc.ContextWithToken, body *container.DeleteRequestBody) (*container.DeleteResponseBody, error) {
|
||||||
id := cid.NewFromV2(body.GetContainerID())
|
id := cid.NewFromV2(body.GetContainerID())
|
||||||
sig := body.GetSignature().GetSign()
|
sig := body.GetSignature().GetSign()
|
||||||
|
|
||||||
tok := session.NewTokenFromV2(ctx.SessionToken)
|
tok := session.NewTokenFromV2(ctx.SessionToken)
|
||||||
|
if ctx.SessionToken != nil && session.GetContainerContext(tok) == nil {
|
||||||
|
return nil, ErrInvalidContext
|
||||||
|
}
|
||||||
|
|
||||||
var rmWitness containercore.RemovalWitness
|
var rmWitness containercore.RemovalWitness
|
||||||
|
|
||||||
|
@ -131,9 +143,12 @@ func (s *morphExecutor) SetExtendedACL(ctx containerSvc.ContextWithToken, body *
|
||||||
|
|
||||||
table.SetSignature(sign)
|
table.SetSignature(sign)
|
||||||
|
|
||||||
table.SetSessionToken(
|
tok := session.NewTokenFromV2(ctx.SessionToken)
|
||||||
session.NewTokenFromV2(ctx.SessionToken),
|
if ctx.SessionToken != nil && session.GetContainerContext(tok) == nil {
|
||||||
)
|
return nil, ErrInvalidContext
|
||||||
|
}
|
||||||
|
|
||||||
|
table.SetSessionToken(tok)
|
||||||
|
|
||||||
err := s.wrt.PutEACL(table)
|
err := s.wrt.PutEACL(table)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
101
pkg/services/container/morph/executor_test.go
Normal file
101
pkg/services/container/morph/executor_test.go
Normal file
|
@ -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
|
||||||
|
}
|
Loading…
Reference in a new issue