diff --git a/cmd/frostfs-node/container.go b/cmd/frostfs-node/container.go index b14e19161..79643d2d2 100644 --- a/cmd/frostfs-node/container.go +++ b/cmd/frostfs-node/container.go @@ -19,6 +19,7 @@ import ( containerMorph "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/container/morph" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user" + "git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine" "go.uber.org/zap" "google.golang.org/grpc" ) @@ -46,9 +47,13 @@ func initContainerService(_ context.Context, c *cfg) { c.shared.frostfsidClient = frostfsIDSubjectProvider + defaultChainRouter := engine.NewDefaultChainRouterWithLocalOverrides( + c.cfgObject.cfgAccessPolicyEngine.accessPolicyEngine.MorphRuleChainStorage(), + c.cfgObject.cfgAccessPolicyEngine.accessPolicyEngine.LocalStorage(), + ) service := containerService.NewSignService( &c.key.PrivateKey, - containerService.NewAPEServer(c.cfgObject.cfgAccessPolicyEngine.accessPolicyEngine, cnrRdr, + containerService.NewAPEServer(defaultChainRouter, cnrRdr, newCachedIRFetcher(createInnerRingFetcher(c)), c.netMapSource, c.shared.frostfsidClient, containerService.NewExecutionService(containerMorph.NewExecutor(cnrRdr, cnrWrt), c.respSvc), ), diff --git a/cmd/frostfs-node/object.go b/cmd/frostfs-node/object.go index 62183d314..0124bf772 100644 --- a/cmd/frostfs-node/object.go +++ b/cmd/frostfs-node/object.go @@ -460,7 +460,8 @@ func createAPEService(c *cfg, splitSvc *objectService.TransportSplitter) *object return objectAPE.NewService( c.log, objectAPE.NewChecker( - c.cfgObject.cfgAccessPolicyEngine.accessPolicyEngine.chainRouter, + c.cfgObject.cfgAccessPolicyEngine.accessPolicyEngine.LocalStorage(), + c.cfgObject.cfgAccessPolicyEngine.accessPolicyEngine.MorphRuleChainStorage(), objectAPE.NewStorageEngineHeaderProvider(c.cfgObject.cfgLocalStorage.localStorage, c.cfgObject.getSvc), c.shared.frostfsidClient, c.netMapSource, diff --git a/cmd/frostfs-node/policy_engine.go b/cmd/frostfs-node/policy_engine.go index 13e30a17e..22fda2b4c 100644 --- a/cmd/frostfs-node/policy_engine.go +++ b/cmd/frostfs-node/policy_engine.go @@ -1,13 +1,11 @@ package main import ( - "sync" "time" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/ape/chainbase" "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain" "git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine" - "git.frostfs.info/TrueCloudLab/policy-engine/pkg/resource" "github.com/google/uuid" "github.com/hashicorp/golang-lru/v2/expirable" "github.com/nspcc-dev/neo-go/pkg/neorpc/result" @@ -15,11 +13,9 @@ import ( ) type accessPolicyEngine struct { - mtx sync.RWMutex - - chainRouter engine.ChainRouter - localOverrideDatabase chainbase.LocalOverrideDatabase + + morphChainStorage engine.MorphRuleChainStorageReader } var _ engine.MorphRuleChainStorageReader = (*morphAPEChainCache)(nil) @@ -70,32 +66,20 @@ func newAccessPolicyEngine( localOverrideDatabase chainbase.LocalOverrideDatabase, ) *accessPolicyEngine { return &accessPolicyEngine{ - chainRouter: engine.NewDefaultChainRouterWithLocalOverrides( - morphChainStorage, - localOverrideDatabase, - ), + morphChainStorage: morphChainStorage, localOverrideDatabase: localOverrideDatabase, } } -func (a *accessPolicyEngine) IsAllowed(name chain.Name, target engine.RequestTarget, r resource.Request) (status chain.Status, found bool, err error) { - a.mtx.RLock() - defer a.mtx.RUnlock() - - return a.chainRouter.IsAllowed(name, target, r) +func (a *accessPolicyEngine) LocalStorage() engine.LocalOverrideStorage { + return a.localOverrideDatabase } -func (a *accessPolicyEngine) LocalStorage() engine.LocalOverrideStorage { - a.mtx.Lock() - defer a.mtx.Unlock() - - return a.localOverrideDatabase +func (a *accessPolicyEngine) MorphRuleChainStorage() engine.MorphRuleChainStorageReader { + return a.morphChainStorage } func (a *accessPolicyEngine) LocalOverrideDatabaseCore() chainbase.DatabaseCore { - a.mtx.Lock() - defer a.mtx.Unlock() - return a.localOverrideDatabase } diff --git a/cmd/frostfs-node/tree.go b/cmd/frostfs-node/tree.go index daaaa64a2..d22e510de 100644 --- a/cmd/frostfs-node/tree.go +++ b/cmd/frostfs-node/tree.go @@ -65,7 +65,8 @@ func initTreeService(c *cfg) { tree.WithReplicationWorkerCount(treeConfig.ReplicationWorkerCount()), tree.WithAuthorizedKeys(treeConfig.AuthorizedKeys()), tree.WithMetrics(c.metricsCollector.TreeService()), - tree.WithAPERouter(c.cfgObject.cfgAccessPolicyEngine.accessPolicyEngine), + tree.WithAPELocalOverrideStorage(c.cfgObject.cfgAccessPolicyEngine.accessPolicyEngine.LocalStorage()), + tree.WithAPEMorphRuleStorage(c.cfgObject.cfgAccessPolicyEngine.accessPolicyEngine.MorphRuleChainStorage()), tree.WithNetmapState(c.cfgNetmap.state), ) diff --git a/pkg/ape/router/bearer_overrides.go b/pkg/ape/router/bearer_overrides.go new file mode 100644 index 000000000..2bc8ad614 --- /dev/null +++ b/pkg/ape/router/bearer_overrides.go @@ -0,0 +1,94 @@ +package router + +import ( + "errors" + "fmt" + + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/ape" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer" + cidSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" + "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain" + policyengine "git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine" +) + +func newTarget(ct ape.ChainTarget) (policyengine.Target, error) { + var target policyengine.Target + switch ct.TargetType { + case ape.TargetTypeContainer: + var cid cidSDK.ID + err := cid.DecodeString(ct.Name) + if err != nil { + return target, fmt.Errorf("invalid cid format: %s", target.Name) + } + target.Type = policyengine.Container + case ape.TargetTypeGroup: + target.Type = policyengine.Group + case ape.TargetTypeNamespace: + target.Type = policyengine.Namespace + case ape.TargetTypeUser: + target.Type = policyengine.User + default: + return target, fmt.Errorf("unsupported target type: %v", ct.TargetType) + } + target.Name = ct.Name + return target, nil +} + +type morphReaderDecorator struct { + policyengine.MorphRuleChainStorageReader + + bearerTokenTarget policyengine.Target + + bearerTokenChains []*chain.Chain +} + +func newMorphReaderDecorator(r policyengine.MorphRuleChainStorageReader, override bearer.APEOverride) (*morphReaderDecorator, error) { + if r == nil { + return nil, errors.New("empty morph chain rule reader") + } + t, err := newTarget(override.Target) + if err != nil { + return nil, err + } + + bearerTokenChains := make([]*chain.Chain, len(override.Chains)) + for i := range override.Chains { + chain := new(chain.Chain) + if err := chain.DecodeBytes(override.Chains[i].Raw); err != nil { + return nil, fmt.Errorf("invalid ape chain: %w", err) + } + bearerTokenChains[i] = chain + } + + return &morphReaderDecorator{ + MorphRuleChainStorageReader: r, + bearerTokenTarget: t, + bearerTokenChains: bearerTokenChains, + }, nil +} + +func (m *morphReaderDecorator) ListMorphRuleChains(name chain.Name, target policyengine.Target) ([]*chain.Chain, error) { + if len(m.bearerTokenChains) > 0 && m.bearerTokenTarget.Type == target.Type { + if m.bearerTokenTarget.Name != target.Name { + return nil, fmt.Errorf("unexpected bearer token target: %s", m.bearerTokenTarget.Name) + } + return m.bearerTokenChains, nil + } + return m.MorphRuleChainStorageReader.ListMorphRuleChains(name, target) +} + +// BearerChainFeedRouter creates a chain router emplacing bearer token rule chains. +// Bearer token chains override only container target chains within Policy contract. This means the order of checking +// is as follows: +// +// 1. Local overrides; +// 2. Policy contract chains for a namespace target (as namespace chains have higher priority); +// 3. Bearer token chains for a container target - if they're not defined, then it checks Policy contract chains; +// 4. Checks for the remaining targets. +func BearerChainFeedRouter(localOverrideStorage policyengine.LocalOverrideStorage, morphChainStorage policyengine.MorphRuleChainStorageReader, override bearer.APEOverride) (policyengine.ChainRouter, error) { + mr, err := newMorphReaderDecorator(morphChainStorage, override) + if err != nil { + return nil, fmt.Errorf("create morph reader with bearer override error: %w", err) + } + return policyengine.NewDefaultChainRouterWithLocalOverrides(mr, localOverrideStorage), nil +} diff --git a/pkg/ape/router/bearer_overrides_test.go b/pkg/ape/router/bearer_overrides_test.go new file mode 100644 index 000000000..3c12ee6fa --- /dev/null +++ b/pkg/ape/router/bearer_overrides_test.go @@ -0,0 +1,165 @@ +package router_test + +import ( + "fmt" + "testing" + + "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/ape/router" + apeSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/ape" + bearerSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer" + "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain" + "git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine" + "git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine/inmemory" + resourcetest "git.frostfs.info/TrueCloudLab/policy-engine/pkg/resource/testutil" + nativeschema "git.frostfs.info/TrueCloudLab/policy-engine/schema/native" + "github.com/stretchr/testify/require" +) + +const ( + container = "67ETTZzbzJC6WxdQhHHHsJNCttVMBqYrSoFaUFVDNfiX" + rootNs = "" +) + +var ( + allowBySourceIP = &chain.Chain{ + Rules: []chain.Rule{ + { + Status: chain.Allow, + Actions: chain.Actions{Names: []string{nativeschema.MethodPutObject}}, + Resources: chain.Resources{Names: []string{fmt.Sprintf(nativeschema.ResourceFormatRootContainer, container)}}, + Condition: []chain.Condition{ + { + Op: chain.CondStringEquals, + Kind: chain.KindRequest, + Key: "SourceIP", + Value: "10.122.1.20", + }, + }, + }, + }, + } + + denyBySourceIP = &chain.Chain{ + Rules: []chain.Rule{ + { + Status: chain.AccessDenied, + Actions: chain.Actions{Names: []string{nativeschema.MethodPutObject}}, + Resources: chain.Resources{Names: []string{fmt.Sprintf(nativeschema.ResourceFormatRootContainer, container)}}, + Condition: []chain.Condition{ + { + Op: chain.CondStringEquals, + Kind: chain.KindRequest, + Key: "SourceIP", + Value: "10.122.1.20", + }, + }, + }, + }, + } +) + +func TestBearerChainFedRouter(t *testing.T) { + t.Run("no bearer token overrides", func(t *testing.T) { + inmem := inmemory.NewInMemoryLocalOverrides() + + inmem.LocalStorage().AddOverride(chain.Ingress, engine.ContainerTarget(container), denyBySourceIP) + inmem.MorphRuleChainStorage().AddMorphRuleChain(chain.Ingress, engine.ContainerTarget(container), allowBySourceIP) + + _, err := router.BearerChainFeedRouter(inmem.LocalStorage(), inmem.MorphRuleChainStorage(), bearerSDK.APEOverride{}) + require.Error(t, err) + }) + t.Run("allow by container with deny by bearer overrides", func(t *testing.T) { + inmem := inmemory.NewInMemoryLocalOverrides() + + inmem.MorphRuleChainStorage().AddMorphRuleChain(chain.Ingress, engine.ContainerTarget(container), allowBySourceIP) + + bt := bearerSDK.APEOverride{ + Target: apeSDK.ChainTarget{ + TargetType: apeSDK.TargetTypeContainer, + Name: container, + }, + Chains: []apeSDK.Chain{{ + Raw: denyBySourceIP.Bytes(), + }}, + } + + r, err := router.BearerChainFeedRouter(inmem.LocalStorage(), inmem.MorphRuleChainStorage(), bt) + require.NoError(t, err) + + req := resourcetest.NewRequest(nativeschema.MethodPutObject, + resourcetest.NewResource(fmt.Sprintf(nativeschema.ResourceFormatRootContainer, container), map[string]string{}), + map[string]string{ + "SourceIP": "10.122.1.20", + "Actor": "someOwner", + }, + ) + + st, found, err := r.IsAllowed(chain.Ingress, engine.NewRequestTarget(rootNs, container), req) + require.NoError(t, err) + require.True(t, found) + require.Equal(t, st, chain.AccessDenied) + }) + t.Run("allow by namespace with deny by bearer overrides", func(t *testing.T) { + inmem := inmemory.NewInMemoryLocalOverrides() + + inmem.MorphRuleChainStorage().AddMorphRuleChain(chain.Ingress, engine.ContainerTarget(container), allowBySourceIP) + inmem.MorphRuleChainStorage().AddMorphRuleChain(chain.Ingress, engine.NamespaceTarget(rootNs), allowBySourceIP) + + bt := bearerSDK.APEOverride{ + Target: apeSDK.ChainTarget{ + TargetType: apeSDK.TargetTypeContainer, + Name: container, + }, + Chains: []apeSDK.Chain{{ + Raw: denyBySourceIP.Bytes(), + }}, + } + + r, err := router.BearerChainFeedRouter(inmem.LocalStorage(), inmem.MorphRuleChainStorage(), bt) + require.NoError(t, err) + + req := resourcetest.NewRequest(nativeschema.MethodPutObject, + resourcetest.NewResource(fmt.Sprintf(nativeschema.ResourceFormatRootContainer, container), map[string]string{}), + map[string]string{ + "SourceIP": "10.122.1.20", + "Actor": "someOwner", + }, + ) + + st, found, err := r.IsAllowed(chain.Ingress, engine.NewRequestTarget(rootNs, container), req) + require.NoError(t, err) + require.True(t, found) + require.Equal(t, st, chain.AccessDenied) + }) + t.Run("deny by namespace with allow by bearer overrides", func(t *testing.T) { + inmem := inmemory.NewInMemoryLocalOverrides() + + inmem.MorphRuleChainStorage().AddMorphRuleChain(chain.Ingress, engine.NamespaceTarget(rootNs), denyBySourceIP) + + bt := bearerSDK.APEOverride{ + Target: apeSDK.ChainTarget{ + TargetType: apeSDK.TargetTypeContainer, + Name: container, + }, + Chains: []apeSDK.Chain{{ + Raw: allowBySourceIP.Bytes(), + }}, + } + + r, err := router.BearerChainFeedRouter(inmem.LocalStorage(), inmem.MorphRuleChainStorage(), bt) + require.NoError(t, err) + + req := resourcetest.NewRequest(nativeschema.MethodPutObject, + resourcetest.NewResource(fmt.Sprintf(nativeschema.ResourceFormatRootContainer, container), map[string]string{}), + map[string]string{ + "SourceIP": "10.122.1.20", + "Actor": "someOwner", + }, + ) + + st, found, err := r.IsAllowed(chain.Ingress, engine.NewRequestTarget(rootNs, container), req) + require.NoError(t, err) + require.True(t, found) + require.Equal(t, st, chain.AccessDenied) + }) +} diff --git a/pkg/ape/router/single_pass.go b/pkg/ape/router/single_pass.go index c0c78db76..ec9244bae 100644 --- a/pkg/ape/router/single_pass.go +++ b/pkg/ape/router/single_pass.go @@ -3,37 +3,12 @@ package router import ( "fmt" - "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/ape" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer" - cidSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" apechain "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain" "git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine" "git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine/inmemory" ) -func newTarget(ct ape.ChainTarget) (engine.Target, error) { - var target engine.Target - switch ct.TargetType { - case ape.TargetTypeContainer: - var cid cidSDK.ID - err := cid.DecodeString(ct.Name) - if err != nil { - return target, fmt.Errorf("invalid cid format: %s", target.Name) - } - target.Type = engine.Container - case ape.TargetTypeGroup: - target.Type = engine.Group - case ape.TargetTypeNamespace: - target.Type = engine.Namespace - case ape.TargetTypeUser: - target.Type = engine.User - default: - return target, fmt.Errorf("unsupported target type: %v", ct.TargetType) - } - target.Name = ct.Name - return target, nil -} - // SingleUseRouterWithBearerTokenChains creates chain router with inmemory storage implementation and // fed with APE chains defined in Bearer token. func SingleUseRouterWithBearerTokenChains(overrides []bearer.APEOverride) (engine.ChainRouter, error) { diff --git a/pkg/services/object/ape/checker.go b/pkg/services/object/ape/checker.go index ee12d7b97..c6946025d 100644 --- a/pkg/services/object/ape/checker.go +++ b/pkg/services/object/ape/checker.go @@ -24,24 +24,26 @@ import ( ) type checkerImpl struct { - chainRouter policyengine.ChainRouter - headerProvider HeaderProvider - frostFSIDClient frostfsidcore.SubjectProvider - nm netmap.Source - st netmap.State - cnrSource container.Source - nodePK []byte + localOverrideStorage policyengine.LocalOverrideStorage + morphChainStorage policyengine.MorphRuleChainStorageReader + headerProvider HeaderProvider + frostFSIDClient frostfsidcore.SubjectProvider + nm netmap.Source + st netmap.State + cnrSource container.Source + nodePK []byte } -func NewChecker(chainRouter policyengine.ChainRouter, headerProvider HeaderProvider, frostFSIDClient frostfsidcore.SubjectProvider, nm netmap.Source, st netmap.State, cnrSource container.Source, nodePK []byte) Checker { +func NewChecker(localOverrideStorage policyengine.LocalOverrideStorage, morphChainStorage policyengine.MorphRuleChainStorageReader, headerProvider HeaderProvider, frostFSIDClient frostfsidcore.SubjectProvider, nm netmap.Source, st netmap.State, cnrSource container.Source, nodePK []byte) Checker { return &checkerImpl{ - chainRouter: chainRouter, - headerProvider: headerProvider, - frostFSIDClient: frostFSIDClient, - nm: nm, - st: st, - cnrSource: cnrSource, - nodePK: nodePK, + localOverrideStorage: localOverrideStorage, + morphChainStorage: morphChainStorage, + headerProvider: headerProvider, + frostFSIDClient: frostFSIDClient, + nm: nm, + st: st, + cnrSource: cnrSource, + nodePK: nodePK, } } @@ -174,28 +176,21 @@ func (c *checkerImpl) CheckAPE(ctx context.Context, prm Prm) error { groups[i] = fmt.Sprintf("%s:%s", prm.Namespace, groups[i]) } + var cr policyengine.ChainRouter if prm.BearerToken != nil && !prm.BearerToken.Impersonate() { if err := isValidBearer(prm.BearerToken, prm.ContainerOwner, prm.Container, pub, c.st); err != nil { return fmt.Errorf("bearer token validation error: %w", err) } - btRouter, err := router.SingleUseRouterWithBearerTokenChains([]bearer.APEOverride{prm.BearerToken.APEOverride()}) + cr, err = router.BearerChainFeedRouter(c.localOverrideStorage, c.morphChainStorage, prm.BearerToken.APEOverride()) if err != nil { - return err - } - status, found, err := btRouter.IsAllowed(apechain.Ingress, policyengine.NewRequestTargetWithContainer(prm.Container.EncodeToString()), r) - if err != nil { - return err - } - if found && status == apechain.Allow { - return nil - } - if status != apechain.NoRuleFound { - return fmt.Errorf("bearer token: method %s: %s", prm.Method, status) + return fmt.Errorf("create chain router error: %w", err) } + } else { + cr = policyengine.NewDefaultChainRouterWithLocalOverrides(c.morphChainStorage, c.localOverrideStorage) } rt := policyengine.NewRequestTargetExtended(prm.Namespace, prm.Container.EncodeToString(), fmt.Sprintf("%s:%s", prm.Namespace, pub.Address()), groups) - status, ruleFound, err := c.chainRouter.IsAllowed(apechain.Ingress, rt, r) + status, ruleFound, err := cr.IsAllowed(apechain.Ingress, rt, r) if err != nil { return err } diff --git a/pkg/services/object/ape/checker_test.go b/pkg/services/object/ape/checker_test.go index 5efd3669b..27c314dfb 100644 --- a/pkg/services/object/ape/checker_test.go +++ b/pkg/services/object/ape/checker_test.go @@ -469,9 +469,8 @@ func TestAPECheck_BearerTokenOverrides(t *testing.T) { ls := inmemory.NewInmemoryLocalStorage() ms := inmemory.NewInmemoryMorphRuleChainStorage() - r := policyengine.NewDefaultChainRouterWithLocalOverrides(ms, ls) - checker := NewChecker(r, headerProvider, frostfsidProvider, nil, &stMock{}, nil, nil) + checker := NewChecker(ls, ms, headerProvider, frostfsidProvider, nil, &stMock{}, nil, nil) prm := Prm{ Method: method, @@ -534,9 +533,7 @@ func TestAPECheck(t *testing.T) { }) } - router := policyengine.NewDefaultChainRouterWithLocalOverrides(ms, ls) - - checker := NewChecker(router, headerProvider, frostfsidProvider, nil, &stMock{}, nil, nil) + checker := NewChecker(ls, ms, headerProvider, frostfsidProvider, nil, &stMock{}, nil, nil) prm := Prm{ Method: method, @@ -639,8 +636,6 @@ func TestPutECChunk(t *testing.T) { MatchType: chain.MatchTypeFirstMatch, }) - router := policyengine.NewDefaultChainRouterWithLocalOverrides(ms, ls) - node1Key, err := keys.NewPrivateKey() require.NoError(t, err) node1 := netmapSDK.NodeInfo{} @@ -669,7 +664,7 @@ func TestPutECChunk(t *testing.T) { }, } - checker := NewChecker(router, headerProvider, frostfsidProvider, nm, &stMock{}, cs, node1Key.PublicKey().Bytes()) + checker := NewChecker(ls, ms, headerProvider, frostfsidProvider, nm, &stMock{}, cs, node1Key.PublicKey().Bytes()) ecParentID := oidtest.ID() chunkHeader := newHeaderObjectSDK(cnr, obj, nil).ToV2().GetHeader() @@ -711,7 +706,7 @@ func TestPutECChunk(t *testing.T) { 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, &stMock{}, cs, otherKey.PublicKey().Bytes()) + checker = NewChecker(ls, ms, headerProvider, frostfsidProvider, nm, &stMock{}, cs, otherKey.PublicKey().Bytes()) prm := Prm{ Method: nativeschema.MethodPutObject, Container: cnr, diff --git a/pkg/services/tree/ape.go b/pkg/services/tree/ape.go index 116adf5db..67a1943b0 100644 --- a/pkg/services/tree/ape.go +++ b/pkg/services/tree/ape.go @@ -140,25 +140,17 @@ func (s *Service) checkAPE(ctx context.Context, bt *bearer.Token, return apeErr(err) } + var cr engine.ChainRouter if bt != nil && !bt.Impersonate() { if err := isValidBearer(bt, container.Value.Owner(), cid, publicKey, s.state); err != nil { return fmt.Errorf("bearer validation error: %w", err) } - btRouter, err := router.SingleUseRouterWithBearerTokenChains([]bearer.APEOverride{bt.APEOverride()}) + cr, err = router.BearerChainFeedRouter(s.localOverrideStorage, s.morphChainStorage, bt.APEOverride()) if err != nil { - return apeErr(err) - } - status, found, err := btRouter.IsAllowed(apechain.Ingress, engine.NewRequestTargetWithContainer(cid.EncodeToString()), request) - if err != nil { - return apeErr(err) - } - if found && status == apechain.Allow { - return nil - } - if status != apechain.NoRuleFound { - err = fmt.Errorf("access to operation %s is denied by access policy engine (bearer token): %s", request.Operation(), status.String()) - return apeErr(err) + return fmt.Errorf("create chain router error: %w", err) } + } else { + cr = engine.NewDefaultChainRouterWithLocalOverrides(s.morphChainStorage, s.localOverrideStorage) } groups, err := aperequest.Groups(s.frostfsidSubjectProvider, publicKey) @@ -172,7 +164,7 @@ func (s *Service) checkAPE(ctx context.Context, bt *bearer.Token, } rt := engine.NewRequestTargetExtended(namespace, cid.EncodeToString(), fmt.Sprintf("%s:%s", namespace, publicKey.Address()), groups) - status, found, err := s.router.IsAllowed(apechain.Ingress, rt, request) + status, found, err := cr.IsAllowed(apechain.Ingress, rt, request) if err != nil { return apeErr(err) } diff --git a/pkg/services/tree/options.go b/pkg/services/tree/options.go index ea5539938..6a20fe5cc 100644 --- a/pkg/services/tree/options.go +++ b/pkg/services/tree/options.go @@ -42,7 +42,8 @@ type cfg struct { containerCacheSize int authorizedKeys [][]byte - router policyengine.ChainRouter + localOverrideStorage policyengine.LocalOverrideStorage + morphChainStorage policyengine.MorphRuleChainStorageReader metrics MetricsRegister } @@ -152,9 +153,15 @@ func WithAuthorizedKeys(keys keys.PublicKeys) Option { } } -func WithAPERouter(router policyengine.ChainRouter) Option { +func WithAPELocalOverrideStorage(localOverrideStorage policyengine.LocalOverrideStorage) Option { return func(c *cfg) { - c.router = router + c.localOverrideStorage = localOverrideStorage + } +} + +func WithAPEMorphRuleStorage(morphRuleStorage policyengine.MorphRuleChainStorageReader) Option { + return func(c *cfg) { + c.morphChainStorage = morphRuleStorage } }