forked from TrueCloudLab/frostfs-node
[#1125] objectSvc: Add EC header APE check
Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
This commit is contained in:
parent
368218f0cc
commit
0144117cc9
6 changed files with 272 additions and 23 deletions
|
@ -445,8 +445,11 @@ func createAPEService(c *cfg, splitSvc *objectService.TransportSplitter) *object
|
|||
c.log,
|
||||
objectAPE.NewChecker(
|
||||
c.cfgObject.cfgAccessPolicyEngine.accessPolicyEngine.chainRouter,
|
||||
objectAPE.NewStorageEngineHeaderProvider(c.cfgObject.cfgLocalStorage.localStorage),
|
||||
objectAPE.NewStorageEngineHeaderProvider(c.cfgObject.cfgLocalStorage.localStorage, c.cfgObject.getSvc),
|
||||
c.shared.frostfsidClient,
|
||||
c.netMapSource,
|
||||
c.cfgObject.cnrSource,
|
||||
c.binPublicKey,
|
||||
),
|
||||
splitSvc,
|
||||
)
|
||||
|
|
|
@ -122,7 +122,7 @@ func (c SenderClassifier) isContainerKey(
|
|||
return false, err
|
||||
}
|
||||
|
||||
in, err := lookupKeyInContainer(nm, owner, idCnr, cnr)
|
||||
in, err := LookupKeyInContainer(nm, owner, idCnr, cnr)
|
||||
if err != nil {
|
||||
return false, err
|
||||
} else if in {
|
||||
|
@ -136,12 +136,12 @@ func (c SenderClassifier) isContainerKey(
|
|||
return false, err
|
||||
}
|
||||
|
||||
return lookupKeyInContainer(nm, owner, idCnr, cnr)
|
||||
return LookupKeyInContainer(nm, owner, idCnr, cnr)
|
||||
}
|
||||
|
||||
func lookupKeyInContainer(
|
||||
func LookupKeyInContainer(
|
||||
nm *netmap.NetMap,
|
||||
owner, idCnr []byte,
|
||||
pkey, idCnr []byte,
|
||||
cnr container.Container,
|
||||
) (bool, error) {
|
||||
cnrVectors, err := nm.ContainerNodes(cnr.PlacementPolicy(), idCnr)
|
||||
|
@ -151,7 +151,7 @@ func lookupKeyInContainer(
|
|||
|
||||
for i := range cnrVectors {
|
||||
for j := range cnrVectors[i] {
|
||||
if bytes.Equal(cnrVectors[i][j].PublicKey(), owner) {
|
||||
if bytes.Equal(cnrVectors[i][j].PublicKey(), pkey) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,9 @@ import (
|
|||
"fmt"
|
||||
|
||||
objectV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/container"
|
||||
frostfsidcore "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/frostfsid"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/netmap"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
||||
|
@ -18,19 +20,21 @@ import (
|
|||
|
||||
type checkerImpl struct {
|
||||
chainRouter policyengine.ChainRouter
|
||||
|
||||
headerProvider HeaderProvider
|
||||
|
||||
frostFSIDClient frostfsidcore.SubjectProvider
|
||||
nm netmap.Source
|
||||
cnrSource container.Source
|
||||
nodePK []byte
|
||||
}
|
||||
|
||||
func NewChecker(chainRouter policyengine.ChainRouter, headerProvider HeaderProvider, frostFSIDClient frostfsidcore.SubjectProvider) Checker {
|
||||
func NewChecker(chainRouter policyengine.ChainRouter, headerProvider HeaderProvider, frostFSIDClient frostfsidcore.SubjectProvider, nm netmap.Source, cnrSource container.Source, nodePK []byte) Checker {
|
||||
return &checkerImpl{
|
||||
chainRouter: chainRouter,
|
||||
|
||||
headerProvider: headerProvider,
|
||||
|
||||
frostFSIDClient: frostFSIDClient,
|
||||
nm: nm,
|
||||
cnrSource: cnrSource,
|
||||
nodePK: nodePK,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,15 +3,22 @@ package ape
|
|||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-contract/frostfsid/client"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/container"
|
||||
frostfsidcore "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/frostfsid"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/checksum"
|
||||
containerSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
netmapSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||
oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/version"
|
||||
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
|
||||
|
@ -36,7 +43,7 @@ func (h *headerProviderMock) addHeader(c cid.ID, o oid.ID, header *objectSDK.Obj
|
|||
h.m[addr] = header
|
||||
}
|
||||
|
||||
func (h *headerProviderMock) GetHeader(_ context.Context, c cid.ID, o oid.ID) (*objectSDK.Object, error) {
|
||||
func (h *headerProviderMock) GetHeader(_ context.Context, c cid.ID, o oid.ID, _ bool) (*objectSDK.Object, error) {
|
||||
var addr oid.Address
|
||||
addr.SetContainer(c)
|
||||
addr.SetObject(o)
|
||||
|
@ -404,7 +411,7 @@ func TestAPECheck(t *testing.T) {
|
|||
|
||||
router := policyengine.NewDefaultChainRouterWithLocalOverrides(ms, ls)
|
||||
|
||||
checker := NewChecker(router, headerProvider, frostfsidProvider)
|
||||
checker := NewChecker(router, headerProvider, frostfsidProvider, nil, nil, nil)
|
||||
|
||||
prm := Prm{
|
||||
Method: method,
|
||||
|
@ -436,3 +443,161 @@ func TestAPECheck(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
type netmapStub struct {
|
||||
netmaps map[uint64]*netmapSDK.NetMap
|
||||
currentEpoch uint64
|
||||
}
|
||||
|
||||
func (s *netmapStub) GetNetMap(diff uint64) (*netmapSDK.NetMap, error) {
|
||||
if diff >= s.currentEpoch {
|
||||
return nil, errors.New("invalid diff")
|
||||
}
|
||||
return s.GetNetMapByEpoch(s.currentEpoch - diff)
|
||||
}
|
||||
|
||||
func (s *netmapStub) GetNetMapByEpoch(epoch uint64) (*netmapSDK.NetMap, error) {
|
||||
if nm, found := s.netmaps[epoch]; found {
|
||||
return nm, nil
|
||||
}
|
||||
return nil, errors.New("netmap not found")
|
||||
}
|
||||
|
||||
func (s *netmapStub) Epoch() (uint64, error) {
|
||||
return s.currentEpoch, nil
|
||||
}
|
||||
|
||||
type testContainerSource struct {
|
||||
containers map[cid.ID]*container.Container
|
||||
}
|
||||
|
||||
func (s *testContainerSource) Get(cnrID cid.ID) (*container.Container, error) {
|
||||
if cnr, found := s.containers[cnrID]; found {
|
||||
return cnr, nil
|
||||
}
|
||||
return nil, fmt.Errorf("container not found")
|
||||
}
|
||||
|
||||
func (s *testContainerSource) DeletionInfo(cid.ID) (*container.DelInfo, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func TestPutECChunk(t *testing.T) {
|
||||
headerProvider := newHeaderProviderMock()
|
||||
frostfsidProvider := newFrostfsIDProviderMock(t)
|
||||
|
||||
cnr := newContainerIDSDK(t, containerID)
|
||||
obj := newObjectIDSDK(t, &objectID)
|
||||
|
||||
ls := inmemory.NewInmemoryLocalStorage()
|
||||
ms := inmemory.NewInmemoryMorphRuleChainStorage()
|
||||
|
||||
ls.AddOverride(chain.Ingress, policyengine.ContainerTarget(containerID), &chain.Chain{
|
||||
Rules: []chain.Rule{
|
||||
{
|
||||
Status: chain.AccessDenied,
|
||||
Actions: chain.Actions{Names: methodsOptionalOID},
|
||||
Resources: chain.Resources{
|
||||
Names: []string{fmt.Sprintf(nativeschema.ResourceFormatRootContainerObjects, containerID)},
|
||||
},
|
||||
Any: true,
|
||||
Condition: []chain.Condition{
|
||||
{
|
||||
Op: chain.CondStringEquals,
|
||||
Object: chain.ObjectResource,
|
||||
Key: "attr1",
|
||||
Value: "value",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MatchType: chain.MatchTypeFirstMatch,
|
||||
})
|
||||
|
||||
router := policyengine.NewDefaultChainRouterWithLocalOverrides(ms, ls)
|
||||
|
||||
node1Key, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
node1 := netmapSDK.NodeInfo{}
|
||||
node1.SetPublicKey(node1Key.PublicKey().Bytes())
|
||||
netmap := &netmapSDK.NetMap{}
|
||||
netmap.SetEpoch(100)
|
||||
netmap.SetNodes([]netmapSDK.NodeInfo{node1})
|
||||
|
||||
nm := &netmapStub{
|
||||
currentEpoch: 100,
|
||||
netmaps: map[uint64]*netmapSDK.NetMap{
|
||||
100: netmap,
|
||||
},
|
||||
}
|
||||
|
||||
cont := containerSDK.Container{}
|
||||
cont.Init()
|
||||
pp := netmapSDK.PlacementPolicy{}
|
||||
require.NoError(t, pp.DecodeString("REP 1"))
|
||||
cont.SetPlacementPolicy(pp)
|
||||
cs := &testContainerSource{
|
||||
containers: map[cid.ID]*container.Container{
|
||||
cnr: {
|
||||
Value: cont,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
checker := NewChecker(router, headerProvider, frostfsidProvider, nm, cs, node1Key.PublicKey().Bytes())
|
||||
|
||||
ecParentID := oidtest.ID()
|
||||
chunkHeader := newHeaderObjectSDK(cnr, obj, nil).ToV2().GetHeader()
|
||||
ecHeader := object.ECHeader{
|
||||
Index: 1,
|
||||
Total: 5,
|
||||
Parent: &refs.ObjectID{},
|
||||
}
|
||||
chunkHeader.SetEC(&ecHeader)
|
||||
ecParentID.WriteToV2(ecHeader.Parent)
|
||||
|
||||
parentHeader := newHeaderObjectSDK(cnr, &ecParentID, &headerObjectSDKParams{
|
||||
attributes: []struct {
|
||||
key string
|
||||
val string
|
||||
}{
|
||||
{
|
||||
key: "attr1",
|
||||
val: "value",
|
||||
},
|
||||
},
|
||||
})
|
||||
headerProvider.addHeader(cnr, ecParentID, parentHeader)
|
||||
|
||||
t.Run("access denied for container node", func(t *testing.T) {
|
||||
prm := Prm{
|
||||
Method: nativeschema.MethodPutObject,
|
||||
Container: cnr,
|
||||
Object: obj,
|
||||
Role: role,
|
||||
SenderKey: senderKey,
|
||||
Header: chunkHeader,
|
||||
SoftAPECheck: true,
|
||||
}
|
||||
|
||||
err = checker.CheckAPE(context.Background(), prm)
|
||||
require.Error(t, err)
|
||||
})
|
||||
t.Run("access allowed for non container node", func(t *testing.T) {
|
||||
otherKey, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
checker = NewChecker(router, headerProvider, frostfsidProvider, nm, cs, otherKey.PublicKey().Bytes())
|
||||
prm := Prm{
|
||||
Method: nativeschema.MethodPutObject,
|
||||
Container: cnr,
|
||||
Object: obj,
|
||||
Role: nativeschema.PropertyValueContainerRoleOthers,
|
||||
SenderKey: senderKey,
|
||||
Header: chunkHeader,
|
||||
SoftAPECheck: true,
|
||||
}
|
||||
|
||||
err = checker.CheckAPE(context.Background(), prm)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -2,11 +2,14 @@ package ape
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
objectV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
|
||||
aperequest "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/ape/request"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/netmap"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||
|
@ -110,12 +113,12 @@ func (c *checkerImpl) newAPERequest(ctx context.Context, prm Prm) (aperequest.Re
|
|||
if prm.Header != nil {
|
||||
header = prm.Header
|
||||
} else if prm.Object != nil && !prm.WithoutHeaderRequest {
|
||||
headerObjSDK, err := c.headerProvider.GetHeader(ctx, prm.Container, *prm.Object)
|
||||
headerObjSDK, err := c.headerProvider.GetHeader(ctx, prm.Container, *prm.Object, true)
|
||||
if err == nil {
|
||||
header = headerObjSDK.ToV2().GetHeader()
|
||||
}
|
||||
}
|
||||
|
||||
header = c.fillHeaderWithECParent(ctx, prm, header)
|
||||
reqProps := map[string]string{
|
||||
nativeschema.PropertyKeyActorPublicKey: prm.SenderKey,
|
||||
nativeschema.PropertyKeyActorRole: prm.Role,
|
||||
|
@ -136,6 +139,65 @@ func (c *checkerImpl) newAPERequest(ctx context.Context, prm Prm) (aperequest.Re
|
|||
), nil
|
||||
}
|
||||
|
||||
func (c *checkerImpl) fillHeaderWithECParent(ctx context.Context, prm Prm, header *objectV2.Header) *objectV2.Header {
|
||||
if header == nil {
|
||||
return header
|
||||
}
|
||||
if header.GetEC() == nil {
|
||||
return header
|
||||
}
|
||||
if prm.Role == nativeschema.PropertyValueContainerRoleContainer ||
|
||||
prm.Role == nativeschema.PropertyValueContainerRoleIR {
|
||||
return header
|
||||
}
|
||||
parentObjRefID := header.GetEC().Parent
|
||||
if parentObjRefID == nil {
|
||||
return header
|
||||
}
|
||||
var parentObjID oid.ID
|
||||
if err := parentObjID.ReadFromV2(*parentObjRefID); err != nil {
|
||||
return header
|
||||
}
|
||||
// only container node have access to collect parent object
|
||||
contNode, err := c.currentNodeIsContainerNode(prm.Container)
|
||||
if err != nil || !contNode {
|
||||
return header
|
||||
}
|
||||
parentObj, err := c.headerProvider.GetHeader(ctx, prm.Container, parentObjID, false)
|
||||
if err != nil {
|
||||
return header
|
||||
}
|
||||
return parentObj.ToV2().GetHeader()
|
||||
}
|
||||
|
||||
func (c *checkerImpl) currentNodeIsContainerNode(cnrID cid.ID) (bool, error) {
|
||||
cnr, err := c.cnrSource.Get(cnrID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
nm, err := netmap.GetLatestNetworkMap(c.nm)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
idCnr := make([]byte, sha256.Size)
|
||||
cnrID.Encode(idCnr)
|
||||
|
||||
in, err := object.LookupKeyInContainer(nm, c.nodePK, idCnr, cnr.Value)
|
||||
if err != nil {
|
||||
return false, err
|
||||
} else if in {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
nm, err = netmap.GetPreviousNetworkMap(c.nm)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return object.LookupKeyInContainer(nm, c.nodePK, idCnr, cnr.Value)
|
||||
}
|
||||
|
||||
// fillWithUserClaimTags fills ape request properties with user claim tags getting them from frostfsid contract by actor public key.
|
||||
func (c *checkerImpl) fillWithUserClaimTags(reqProps map[string]string, prm Prm) (map[string]string, error) {
|
||||
if reqProps == nil {
|
||||
|
|
|
@ -10,6 +10,8 @@ import (
|
|||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/engine"
|
||||
objectSvc "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object"
|
||||
getsvc "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object/get"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object/util"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||
|
@ -31,23 +33,36 @@ type Service struct {
|
|||
var _ objectSvc.ServiceServer = (*Service)(nil)
|
||||
|
||||
type HeaderProvider interface {
|
||||
GetHeader(ctx context.Context, cnr cid.ID, oid oid.ID) (*objectSDK.Object, error)
|
||||
GetHeader(ctx context.Context, cnr cid.ID, oid oid.ID, local bool) (*objectSDK.Object, error)
|
||||
}
|
||||
|
||||
type storageEngineHeaderProvider struct {
|
||||
storageEngine *engine.StorageEngine
|
||||
getSvc *getsvc.Service
|
||||
}
|
||||
|
||||
func (p storageEngineHeaderProvider) GetHeader(ctx context.Context, cnr cid.ID, objID oid.ID) (*objectSDK.Object, error) {
|
||||
func (p storageEngineHeaderProvider) GetHeader(ctx context.Context, cnr cid.ID, objID oid.ID, local bool) (*objectSDK.Object, error) {
|
||||
var addr oid.Address
|
||||
addr.SetContainer(cnr)
|
||||
addr.SetObject(objID)
|
||||
if local {
|
||||
return engine.Head(ctx, p.storageEngine, addr)
|
||||
}
|
||||
w := getsvc.NewSimpleObjectWriter()
|
||||
var headPrm getsvc.HeadPrm
|
||||
headPrm.WithAddress(addr)
|
||||
headPrm.SetHeaderWriter(w)
|
||||
headPrm.SetCommonParameters(&util.CommonPrm{}) // default values are ok
|
||||
if err := p.getSvc.Head(ctx, headPrm); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return w.Object(), nil
|
||||
}
|
||||
|
||||
func NewStorageEngineHeaderProvider(e *engine.StorageEngine) HeaderProvider {
|
||||
func NewStorageEngineHeaderProvider(e *engine.StorageEngine, s *getsvc.Service) HeaderProvider {
|
||||
return storageEngineHeaderProvider{
|
||||
storageEngine: e,
|
||||
getSvc: s,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue