From a991c9d6a91ce6886a2acc0cb402d13d4231f762 Mon Sep 17 00:00:00 2001 From: Airat Arifullin Date: Mon, 24 Feb 2025 19:30:05 +0300 Subject: [PATCH] [#1574] ape: Extend `ChainRouterError` * Introduce new fields and getters for them; * Introduce `MarshalLogObject` for `ChainRouterError`; * Fix `CheckAPE` in `checkerCoreImpl` at `newChainRouterError`. Signed-off-by: Airat Arifullin --- pkg/services/common/ape/checker.go | 2 +- pkg/services/common/ape/error.go | 135 ++++++++++++++++++++++++++--- 2 files changed, 122 insertions(+), 15 deletions(-) diff --git a/pkg/services/common/ape/checker.go b/pkg/services/common/ape/checker.go index c9b0b7363..0f342df46 100644 --- a/pkg/services/common/ape/checker.go +++ b/pkg/services/common/ape/checker.go @@ -104,7 +104,7 @@ func (c *checkerCoreImpl) CheckAPE(ctx context.Context, prm CheckPrm) error { if found && status == apechain.Allow { return nil } - return newChainRouterError(prm.Request.Operation(), status) + return newChainRouterError(rt, prm.Request, status) } // isValidBearer checks whether bearer token was correctly signed by authorized diff --git a/pkg/services/common/ape/error.go b/pkg/services/common/ape/error.go index d3c381de7..8d57c67fa 100644 --- a/pkg/services/common/ape/error.go +++ b/pkg/services/common/ape/error.go @@ -3,31 +3,138 @@ package ape import ( "fmt" + aperequest "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/ape/request" apechain "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain" + policyengine "git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine" + nativeschema "git.frostfs.info/TrueCloudLab/policy-engine/schema/native" + "go.uber.org/zap/zapcore" ) // ChainRouterError is returned when chain router validation prevents // the APE request from being processed (no rule found, access denied, etc.). type ChainRouterError struct { - operation string - status apechain.Status + target policyengine.RequestTarget + request aperequest.Request + status apechain.Status } func (e *ChainRouterError) Error() string { - return fmt.Sprintf("access to operation %s is denied by access policy engine: %s", e.Operation(), e.Status()) + return fmt.Sprintf("access to operation %s is denied by access policy engine: %s", e.request.Operation(), e.status) } -func (e *ChainRouterError) Operation() string { - return e.operation -} - -func (e *ChainRouterError) Status() apechain.Status { - return e.status -} - -func newChainRouterError(operation string, status apechain.Status) *ChainRouterError { +func newChainRouterError(target policyengine.RequestTarget, request aperequest.Request, status apechain.Status) *ChainRouterError { return &ChainRouterError{ - operation: operation, - status: status, + target: target, + request: request, + status: status, + } +} + +func (e *ChainRouterError) MarshalLogObject(enc zapcore.ObjectEncoder) error { + enc.AddString("status", e.status.String()) + + if err := enc.AddObject("request", zapcore.ObjectMarshalerFunc(e.marshalRequest)); err != nil { + return err + } + + return nil +} + +func (e *ChainRouterError) marshalTarget(enc zapcore.ObjectEncoder) error { + target := e.target + + hasNamespace := target.Namespace != nil + hasContainer := target.Container != nil + hasUser := target.User != nil + hasGroups := len(target.Groups) > 0 + + if !hasNamespace && !hasContainer && !hasUser && !hasGroups { + enc.AddString("type", "empty") + return nil + } + + if hasNamespace { + enc.AddString("namespace", target.Namespace.Name) + } + + if hasContainer { + enc.AddString("container", target.Container.Name) + } + + if hasUser { + enc.AddString("user", target.User.Name) + } + + if hasGroups { + if err := enc.AddArray("groups", zapcore.ArrayMarshalerFunc( + func(arrayEnc zapcore.ArrayEncoder) error { + for i := range target.Groups { + arrayEnc.AppendString(target.Groups[i].Name) + } + return nil + })); err != nil { + return err + } + } + + return nil +} + +func (e *ChainRouterError) marshalRequest(enc zapcore.ObjectEncoder) error { + enc.AddString("operation", e.request.Operation()) + + if err := enc.AddObject("target", zapcore.ObjectMarshalerFunc(e.marshalTarget)); err != nil { + return err + } + + if err := enc.AddObject("properties", zapcore.ObjectMarshalerFunc( + func(innerEnc zapcore.ObjectEncoder) error { + marshalProperties(innerEnc, e.request.Properties()) + return nil + })); err != nil { + return err + } + + if err := enc.AddObject("resource", zapcore.ObjectMarshalerFunc(e.marshalResource)); err != nil { + return err + } + + return nil +} + +func (e *ChainRouterError) marshalResource(enc zapcore.ObjectEncoder) error { + resource, ok := e.request.Resource().(aperequest.Resource) + if !ok { + return nil + } + + enc.AddString("name", resource.Name()) + if err := enc.AddObject("properties", zapcore.ObjectMarshalerFunc( + func(innerEnc zapcore.ObjectEncoder) error { + marshalProperties(innerEnc, resource.Properties()) + return nil + })); err != nil { + return err + } + + return nil +} + +func marshalProperties(enc zapcore.ObjectEncoder, props map[string]string) { + // For some properties, we can display the value in a shorter format. + const shortFormatLength = 15 + + for key, value := range props { + switch key { + case nativeschema.PropertyKeyObjectPayloadHash, + nativeschema.PropertyKeyObjectHomomorphicHash, + nativeschema.PropertyKeyActorPublicKey: + if len(value) >= shortFormatLength { + value = value[:shortFormatLength] + "..." + } + default: + } + + enc.AddString(key, value) } }