From d5dc14c639a99b7c0338dea665851e88a5a59202 Mon Sep 17 00:00:00 2001
From: Airat Arifullin <a.arifullin@yadro.com>
Date: Fri, 12 Jul 2024 12:02:20 +0300
Subject: [PATCH] [#1243] object: Make APE checker set x-headers to request
 properties

* Update go.mod, go.sum;
* Add x-headers to request properties;
* Add a unit-test.

Signed-off-by: Airat Arifullin <a.arifullin@yadro.com>
---
 go.mod                                  |  2 +-
 go.sum                                  |  4 +--
 pkg/services/object/ape/checker.go      |  4 +++
 pkg/services/object/ape/checker_test.go | 48 +++++++++++++++++++++++++
 pkg/services/object/ape/request.go      |  6 ++++
 pkg/services/object/ape/service.go      | 10 ++++++
 6 files changed, 71 insertions(+), 3 deletions(-)

diff --git a/go.mod b/go.mod
index d4eb52f4d..ee8b1bb16 100644
--- a/go.mod
+++ b/go.mod
@@ -10,7 +10,7 @@ require (
 	git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20231101111734-b3ad3335ff65
 	git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240617140730-1a5886e776de
 	git.frostfs.info/TrueCloudLab/hrw v1.2.1
-	git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240513163744-1f6f4163d40d
+	git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240712081403-2628f6184984
 	git.frostfs.info/TrueCloudLab/tzhash v1.8.0
 	git.frostfs.info/TrueCloudLab/zapjournald v0.0.0-20240124114243-cb2e66427d02
 	github.com/cheggaaa/pb v1.0.29
diff --git a/go.sum b/go.sum
index 84fdbd47a..c7c3b87eb 100644
--- a/go.sum
+++ b/go.sum
@@ -14,8 +14,8 @@ git.frostfs.info/TrueCloudLab/hrw v1.2.1 h1:ccBRK21rFvY5R1WotI6LNoPlizk7qSvdfD8l
 git.frostfs.info/TrueCloudLab/hrw v1.2.1/go.mod h1:C1Ygde2n843yTZEQ0FP69jYiuaYV0kriLvP4zm8JuvM=
 git.frostfs.info/TrueCloudLab/neoneo-go v0.106.1-0.20240611123832-594f716b3d18 h1:JRjwcHaQajTbSCBCK3yZnqvyHvgWBaoThDGuT4kvIIc=
 git.frostfs.info/TrueCloudLab/neoneo-go v0.106.1-0.20240611123832-594f716b3d18/go.mod h1:bZyJexBlrja4ngxiBgo8by5pVHuAbhg9l09/8yVGDyg=
-git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240513163744-1f6f4163d40d h1:hHk8FWbWgEnwm2I045CaBIrZBjy/o81CehIVOySA/pQ=
-git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240513163744-1f6f4163d40d/go.mod h1:SgioiGhQNWqiV5qpFAXRDJF81SEFRBhtwGEiU0FViyA=
+git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240712081403-2628f6184984 h1:O3F2Grz07RWZ68mRz1xsYsNPNvZLwY00BM+xoYb1kNk=
+git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240712081403-2628f6184984/go.mod h1:SgioiGhQNWqiV5qpFAXRDJF81SEFRBhtwGEiU0FViyA=
 git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0 h1:M2KR3iBj7WpY3hP10IevfIB9MURr4O9mwVfJ+SjT3HA=
 git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0/go.mod h1:okpbKfVYf/BpejtfFTfhZqFP+sZ8rsHrP8Rr/jYPNRc=
 git.frostfs.info/TrueCloudLab/tzhash v1.8.0 h1:UFMnUIk0Zh17m8rjGHJMqku2hCgaXDqjqZzS4gsb4UA=
diff --git a/pkg/services/object/ape/checker.go b/pkg/services/object/ape/checker.go
index c6f9b711d..a1972292e 100644
--- a/pkg/services/object/ape/checker.go
+++ b/pkg/services/object/ape/checker.go
@@ -7,6 +7,7 @@ import (
 	"fmt"
 
 	objectV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
+	"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
 	aperequest "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/ape/request"
 	"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/ape/router"
 	"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/container"
@@ -79,6 +80,9 @@ type Prm struct {
 
 	// The request's bearer token. It is used in order to check APE overrides with the token.
 	BearerToken *bearer.Token
+
+	// XHeaders from the request.
+	XHeaders []session.XHeader
 }
 
 var (
diff --git a/pkg/services/object/ape/checker_test.go b/pkg/services/object/ape/checker_test.go
index 27c314dfb..090f6a83c 100644
--- a/pkg/services/object/ape/checker_test.go
+++ b/pkg/services/object/ape/checker_test.go
@@ -10,6 +10,7 @@ import (
 
 	"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
 	"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
+	"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
 	"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"
@@ -240,6 +241,7 @@ var apeCheckTestCases = []struct {
 	object         *string
 	methods        []string
 	header         testHeader
+	xHeaders       []session.XHeader
 	containerRules []chain.Rule
 	groupidRules   []chain.Rule
 	expectAPEErr   bool
@@ -367,6 +369,52 @@ var apeCheckTestCases = []struct {
 		},
 		expectAPEErr: true,
 	},
+	{
+		name:      "oid required requests are denied by xheader",
+		container: containerID,
+		object:    stringPtr(objectID),
+		methods:   methodsRequiredOID,
+		header: testHeader{
+			headerObjSDK: &headerObjectSDKParams{
+				attributes: []struct {
+					key string
+					val string
+				}{
+					{
+						key: "attr1",
+						val: "attribute_value",
+					},
+				},
+			},
+			fromHeaderProvider: true,
+		},
+		xHeaders: []session.XHeader{
+			func() (xhead session.XHeader) {
+				xhead.SetKey("X-Test-ID")
+				xhead.SetValue("aezakmi")
+				return
+			}(),
+		},
+		containerRules: []chain.Rule{
+			{
+				Status:  chain.AccessDenied,
+				Actions: chain.Actions{Names: methodsRequiredOID},
+				Resources: chain.Resources{
+					Names: []string{fmt.Sprintf(nativeschema.ResourceFormatRootContainerObject, containerID, objectID)},
+				},
+				Any: true,
+				Condition: []chain.Condition{
+					{
+						Op:    chain.CondStringLike,
+						Kind:  chain.KindRequest,
+						Key:   fmt.Sprintf(commonschema.PropertyKeyFrostFSXHeader, "X-Test-ID"),
+						Value: "aezakmi",
+					},
+				},
+			},
+		},
+		expectAPEErr: true,
+	},
 	{
 		name:      "optional oid requests reached quota limit by an attribute",
 		container: containerID,
diff --git a/pkg/services/object/ape/request.go b/pkg/services/object/ape/request.go
index 02ead8c60..1c129f65f 100644
--- a/pkg/services/object/ape/request.go
+++ b/pkg/services/object/ape/request.go
@@ -126,6 +126,12 @@ func (c *checkerImpl) newAPERequest(ctx context.Context, prm Prm) (aperequest.Re
 		nativeschema.PropertyKeyActorPublicKey: prm.SenderKey,
 		nativeschema.PropertyKeyActorRole:      prm.Role,
 	}
+
+	for _, xhead := range prm.XHeaders {
+		xheadKey := fmt.Sprintf(commonschema.PropertyKeyFrostFSXHeader, xhead.GetKey())
+		reqProps[xheadKey] = xhead.GetValue()
+	}
+
 	var err error
 	reqProps, err = c.fillWithUserClaimTags(reqProps, prm)
 	if err != nil {
diff --git a/pkg/services/object/ape/service.go b/pkg/services/object/ape/service.go
index 853c3b80d..56c66002d 100644
--- a/pkg/services/object/ape/service.go
+++ b/pkg/services/object/ape/service.go
@@ -111,6 +111,7 @@ func (g *getStreamBasicChecker) Send(resp *objectV2.GetResponse) error {
 			Role:           g.role,
 			SoftAPECheck:   g.softAPECheck,
 			BearerToken:    g.bearerToken,
+			XHeaders:       resp.GetMetaHeader().GetXHeaders(),
 		}
 
 		if err := g.apeChecker.CheckAPE(g.Context(), prm); err != nil {
@@ -154,6 +155,7 @@ func (c *Service) Get(request *objectV2.GetRequest, stream objectSvc.GetObjectSt
 		SoftAPECheck:         reqCtx.SoftAPECheck,
 		WithoutHeaderRequest: true,
 		BearerToken:          reqCtx.BearerToken,
+		XHeaders:             request.GetMetaHeader().GetXHeaders(),
 	})
 	if err != nil {
 		return toStatusErr(err)
@@ -200,6 +202,7 @@ func (p *putStreamBasicChecker) Send(ctx context.Context, request *objectV2.PutR
 			Role:           nativeSchemaRole(reqCtx.Role),
 			SoftAPECheck:   reqCtx.SoftAPECheck,
 			BearerToken:    reqCtx.BearerToken,
+			XHeaders:       request.GetMetaHeader().GetXHeaders(),
 		}
 
 		if err := p.apeChecker.CheckAPE(ctx, prm); err != nil {
@@ -245,6 +248,7 @@ func (c *Service) Head(ctx context.Context, request *objectV2.HeadRequest) (*obj
 		SoftAPECheck:         reqCtx.SoftAPECheck,
 		WithoutHeaderRequest: true,
 		BearerToken:          reqCtx.BearerToken,
+		XHeaders:             request.GetMetaHeader().GetXHeaders(),
 	})
 	if err != nil {
 		return nil, toStatusErr(err)
@@ -285,6 +289,7 @@ func (c *Service) Head(ctx context.Context, request *objectV2.HeadRequest) (*obj
 		ContainerOwner: reqCtx.ContainerOwner,
 		SoftAPECheck:   reqCtx.SoftAPECheck,
 		BearerToken:    reqCtx.BearerToken,
+		XHeaders:       request.GetMetaHeader().GetXHeaders(),
 	})
 	if err != nil {
 		return nil, toStatusErr(err)
@@ -314,6 +319,7 @@ func (c *Service) Search(request *objectV2.SearchRequest, stream objectSvc.Searc
 		ContainerOwner: reqCtx.ContainerOwner,
 		SoftAPECheck:   reqCtx.SoftAPECheck,
 		BearerToken:    reqCtx.BearerToken,
+		XHeaders:       request.GetMetaHeader().GetXHeaders(),
 	})
 	if err != nil {
 		return toStatusErr(err)
@@ -343,6 +349,7 @@ func (c *Service) Delete(ctx context.Context, request *objectV2.DeleteRequest) (
 		ContainerOwner: reqCtx.ContainerOwner,
 		SoftAPECheck:   reqCtx.SoftAPECheck,
 		BearerToken:    reqCtx.BearerToken,
+		XHeaders:       request.GetMetaHeader().GetXHeaders(),
 	})
 	if err != nil {
 		return nil, toStatusErr(err)
@@ -377,6 +384,7 @@ func (c *Service) GetRange(request *objectV2.GetRangeRequest, stream objectSvc.G
 		ContainerOwner: reqCtx.ContainerOwner,
 		SoftAPECheck:   reqCtx.SoftAPECheck,
 		BearerToken:    reqCtx.BearerToken,
+		XHeaders:       request.GetMetaHeader().GetXHeaders(),
 	})
 	if err != nil {
 		return toStatusErr(err)
@@ -406,6 +414,7 @@ func (c *Service) GetRangeHash(ctx context.Context, request *objectV2.GetRangeHa
 		ContainerOwner: reqCtx.ContainerOwner,
 		SoftAPECheck:   reqCtx.SoftAPECheck,
 		BearerToken:    reqCtx.BearerToken,
+		XHeaders:       request.GetMetaHeader().GetXHeaders(),
 	}
 
 	if err = c.apeChecker.CheckAPE(ctx, prm); err != nil {
@@ -445,6 +454,7 @@ func (c *Service) PutSingle(ctx context.Context, request *objectV2.PutSingleRequ
 		ContainerOwner: reqCtx.ContainerOwner,
 		SoftAPECheck:   reqCtx.SoftAPECheck,
 		BearerToken:    reqCtx.BearerToken,
+		XHeaders:       request.GetMetaHeader().GetXHeaders(),
 	}
 
 	if err = c.apeChecker.CheckAPE(ctx, prm); err != nil {