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 { 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.request.Operation(), e.status) } func NewChainRouterError(target policyengine.RequestTarget, request aperequest.Request, status apechain.Status) *ChainRouterError { return &ChainRouterError{ 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) } }