[#1689] object: Make APE middleware form container system attributes

* Extract container system attributes into request info;
* Form APE-resource proprties from the extracted attributes;
* Fix unit-test.

Change-Id: I8fbd9a167ad05af0e75df350ac882b866da7bdcb
Signed-off-by: Airat Arifullin <a.arifullin@yadro.com>
This commit is contained in:
Airat Arifullin 2025-04-24 18:57:23 +03:00
parent a5f76a609d
commit c2a495814f
5 changed files with 128 additions and 88 deletions

View file

@ -64,6 +64,9 @@ type Prm struct {
// An encoded container's owner user ID. // An encoded container's owner user ID.
ContainerOwner user.ID ContainerOwner user.ID
// Attributes defined for the container.
ContainerAttributes map[string]string
// The request's bearer token. It is used in order to check APE overrides with the token. // The request's bearer token. It is used in order to check APE overrides with the token.
BearerToken *bearer.Token BearerToken *bearer.Token

View file

@ -63,6 +63,8 @@ type RequestInfo struct {
ContainerOwner user.ID ContainerOwner user.ID
ContainerAttributes map[string]string
// Namespace defines to which namespace a container is belonged. // Namespace defines to which namespace a container is belonged.
Namespace string Namespace string
@ -131,6 +133,11 @@ func (e *extractor) GetRequestInfo(ctx context.Context, m Metadata, method strin
ri.Role = nativeSchemaRole(res.Role) ri.Role = nativeSchemaRole(res.Role)
ri.ContainerOwner = cnr.Value.Owner() ri.ContainerOwner = cnr.Value.Owner()
ri.ContainerAttributes = map[string]string{}
for key, val := range cnr.Value.Attributes() {
ri.ContainerAttributes[key] = val
}
cnrNamespace, hasNamespace := strings.CutSuffix(cnrSDK.ReadDomain(cnr.Value).Zone(), ".ns") cnrNamespace, hasNamespace := strings.CutSuffix(cnrSDK.ReadDomain(cnr.Value).Zone(), ".ns")
if hasNamespace { if hasNamespace {
ri.Namespace = cnrNamespace ri.Namespace = cnrNamespace

View file

@ -57,11 +57,16 @@ func resourceName(cid cid.ID, oid *oid.ID, namespace string) string {
} }
// objectProperties collects object properties from address parameters and a header if it is passed. // objectProperties collects object properties from address parameters and a header if it is passed.
func objectProperties(cnr cid.ID, oid *oid.ID, cnrOwner user.ID, header *objectV2.Header) map[string]string { func objectProperties(cnr cid.ID, oid *oid.ID, cnrOwner user.ID, cnrAttrs map[string]string, header *objectV2.Header) map[string]string {
objectProps := map[string]string{ objectProps := map[string]string{
nativeschema.PropertyKeyObjectContainerID: cnr.EncodeToString(), nativeschema.PropertyKeyObjectContainerID: cnr.EncodeToString(),
} }
for attrName, attrValue := range cnrAttrs {
prop := fmt.Sprintf(nativeschema.PropertyKeyFormatObjectContainerAttribute, attrName)
objectProps[prop] = attrValue
}
objectProps[nativeschema.PropertyKeyContainerOwnerID] = cnrOwner.EncodeToString() objectProps[nativeschema.PropertyKeyContainerOwnerID] = cnrOwner.EncodeToString()
if oid != nil { if oid != nil {
@ -155,7 +160,7 @@ func (c *checkerImpl) newAPERequest(ctx context.Context, prm Prm) (aperequest.Re
prm.Method, prm.Method,
aperequest.NewResource( aperequest.NewResource(
resourceName(prm.Container, prm.Object, prm.Namespace), resourceName(prm.Container, prm.Object, prm.Namespace),
objectProperties(prm.Container, prm.Object, prm.ContainerOwner, header), objectProperties(prm.Container, prm.Object, prm.ContainerOwner, prm.ContainerAttributes, header),
), ),
reqProps, reqProps,
), nil ), nil

View file

@ -7,6 +7,7 @@ import (
"testing" "testing"
aperequest "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/ape/request" aperequest "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/ape/request"
cnrV2 "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/container"
objectV2 "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/object" objectV2 "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/object"
checksumtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/checksum/test" checksumtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/checksum/test"
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
@ -22,8 +23,17 @@ const (
testOwnerID = "NURFM8PWbLA2aLt2vrD8q4FyfAdgESwM8y" testOwnerID = "NURFM8PWbLA2aLt2vrD8q4FyfAdgESwM8y"
incomingIP = "192.92.33.1" incomingIP = "192.92.33.1"
testSysAttrName = "unittest"
testSysAttrZone = "eggplant"
) )
var containerAttrs = map[string]string{
cnrV2.SysAttributeName: testSysAttrName,
cnrV2.SysAttributeZone: testSysAttrZone,
}
func ctxWithPeerInfo() context.Context { func ctxWithPeerInfo() context.Context {
return peer.NewContext(context.Background(), &peer.Peer{ return peer.NewContext(context.Background(), &peer.Peer{
Addr: &net.TCPAddr{ Addr: &net.TCPAddr{
@ -105,7 +115,7 @@ func TestObjectProperties(t *testing.T) {
var testCnrOwner user.ID var testCnrOwner user.ID
require.NoError(t, testCnrOwner.DecodeString(testOwnerID)) require.NoError(t, testCnrOwner.DecodeString(testOwnerID))
props := objectProperties(cnr, obj, testCnrOwner, header.ToV2().GetHeader()) props := objectProperties(cnr, obj, testCnrOwner, containerAttrs, header.ToV2().GetHeader())
require.Equal(t, test.container, props[nativeschema.PropertyKeyObjectContainerID]) require.Equal(t, test.container, props[nativeschema.PropertyKeyObjectContainerID])
require.Equal(t, testOwnerID, props[nativeschema.PropertyKeyContainerOwnerID]) require.Equal(t, testOwnerID, props[nativeschema.PropertyKeyContainerOwnerID])
@ -124,6 +134,8 @@ func TestObjectProperties(t *testing.T) {
require.Equal(t, test.header.typ.String(), props[nativeschema.PropertyKeyObjectType]) require.Equal(t, test.header.typ.String(), props[nativeschema.PropertyKeyObjectType])
require.Equal(t, test.header.payloadChecksum.String(), props[nativeschema.PropertyKeyObjectPayloadHash]) require.Equal(t, test.header.payloadChecksum.String(), props[nativeschema.PropertyKeyObjectPayloadHash])
require.Equal(t, test.header.payloadHomomorphicHash.String(), props[nativeschema.PropertyKeyObjectHomomorphicHash]) require.Equal(t, test.header.payloadHomomorphicHash.String(), props[nativeschema.PropertyKeyObjectHomomorphicHash])
require.Equal(t, containerAttrs[cnrV2.SysAttributeName], props[fmt.Sprintf(nativeschema.PropertyKeyFormatObjectContainerAttribute, cnrV2.SysAttributeName)])
require.Equal(t, containerAttrs[cnrV2.SysAttributeZone], props[fmt.Sprintf(nativeschema.PropertyKeyFormatObjectContainerAttribute, cnrV2.SysAttributeZone)])
for _, attr := range test.header.attributes { for _, attr := range test.header.attributes {
require.Equal(t, attr.val, props[attr.key]) require.Equal(t, attr.val, props[attr.key])
@ -245,6 +257,10 @@ func TestNewAPERequest(t *testing.T) {
Role: role, Role: role,
SenderKey: senderKey, SenderKey: senderKey,
ContainerOwner: testCnrOwner, ContainerOwner: testCnrOwner,
ContainerAttributes: map[string]string{
cnrV2.SysAttributeZone: testSysAttrZone,
cnrV2.SysAttributeName: testSysAttrName,
},
} }
headerSource := newHeaderProviderMock() headerSource := newHeaderProviderMock()
@ -277,7 +293,7 @@ func TestNewAPERequest(t *testing.T) {
method, method,
aperequest.NewResource( aperequest.NewResource(
resourceName(cnr, obj, prm.Namespace), resourceName(cnr, obj, prm.Namespace),
objectProperties(cnr, obj, testCnrOwner, func() *objectV2.Header { objectProperties(cnr, obj, testCnrOwner, containerAttrs, func() *objectV2.Header {
if headerObjSDK != nil { if headerObjSDK != nil {
return headerObjSDK.ToV2().GetHeader() return headerObjSDK.ToV2().GetHeader()
} }

View file

@ -86,16 +86,17 @@ func (g *getStreamBasicChecker) Send(resp *objectV2.GetResponse) error {
} }
prm := Prm{ prm := Prm{
Namespace: g.reqInfo.Namespace, Namespace: g.reqInfo.Namespace,
Container: cnrID, Container: cnrID,
Object: objID, Object: objID,
Header: partInit.GetHeader(), Header: partInit.GetHeader(),
Method: nativeschema.MethodGetObject, Method: nativeschema.MethodGetObject,
SenderKey: g.reqInfo.SenderKey, SenderKey: g.reqInfo.SenderKey,
ContainerOwner: g.reqInfo.ContainerOwner, ContainerOwner: g.reqInfo.ContainerOwner,
Role: g.reqInfo.Role, ContainerAttributes: g.reqInfo.ContainerAttributes,
BearerToken: g.metadata.BearerToken, Role: g.reqInfo.Role,
XHeaders: resp.GetMetaHeader().GetXHeaders(), BearerToken: g.metadata.BearerToken,
XHeaders: resp.GetMetaHeader().GetXHeaders(),
} }
if err := g.apeChecker.CheckAPE(g.Context(), prm); err != nil { if err := g.apeChecker.CheckAPE(g.Context(), prm); err != nil {
@ -142,16 +143,17 @@ func (p *putStreamBasicChecker) Send(ctx context.Context, request *objectV2.PutR
} }
prm := Prm{ prm := Prm{
Namespace: reqInfo.Namespace, Namespace: reqInfo.Namespace,
Container: md.Container, Container: md.Container,
Object: md.Object, Object: md.Object,
Header: partInit.GetHeader(), Header: partInit.GetHeader(),
Method: nativeschema.MethodPutObject, Method: nativeschema.MethodPutObject,
SenderKey: reqInfo.SenderKey, SenderKey: reqInfo.SenderKey,
ContainerOwner: reqInfo.ContainerOwner, ContainerOwner: reqInfo.ContainerOwner,
Role: reqInfo.Role, ContainerAttributes: reqInfo.ContainerAttributes,
BearerToken: md.BearerToken, Role: reqInfo.Role,
XHeaders: md.MetaHeader.GetXHeaders(), BearerToken: md.BearerToken,
XHeaders: md.MetaHeader.GetXHeaders(),
} }
if err := p.apeChecker.CheckAPE(ctx, prm); err != nil { if err := p.apeChecker.CheckAPE(ctx, prm); err != nil {
@ -200,15 +202,16 @@ func (p *patchStreamBasicChecker) Send(ctx context.Context, request *objectV2.Pa
} }
prm := Prm{ prm := Prm{
Namespace: reqInfo.Namespace, Namespace: reqInfo.Namespace,
Container: md.Container, Container: md.Container,
Object: md.Object, Object: md.Object,
Method: nativeschema.MethodPatchObject, Method: nativeschema.MethodPatchObject,
SenderKey: reqInfo.SenderKey, SenderKey: reqInfo.SenderKey,
ContainerOwner: reqInfo.ContainerOwner, ContainerOwner: reqInfo.ContainerOwner,
Role: reqInfo.Role, ContainerAttributes: reqInfo.ContainerAttributes,
BearerToken: md.BearerToken, Role: reqInfo.Role,
XHeaders: md.MetaHeader.GetXHeaders(), BearerToken: md.BearerToken,
XHeaders: md.MetaHeader.GetXHeaders(),
} }
if err := p.apeChecker.CheckAPE(ctx, prm); err != nil { if err := p.apeChecker.CheckAPE(ctx, prm); err != nil {
@ -268,16 +271,17 @@ func (c *Service) Head(ctx context.Context, request *objectV2.HeadRequest) (*obj
} }
err = c.apeChecker.CheckAPE(ctx, Prm{ err = c.apeChecker.CheckAPE(ctx, Prm{
Namespace: reqInfo.Namespace, Namespace: reqInfo.Namespace,
Container: md.Container, Container: md.Container,
Object: md.Object, Object: md.Object,
Header: header, Header: header,
Method: nativeschema.MethodHeadObject, Method: nativeschema.MethodHeadObject,
Role: reqInfo.Role, Role: reqInfo.Role,
SenderKey: reqInfo.SenderKey, SenderKey: reqInfo.SenderKey,
ContainerOwner: reqInfo.ContainerOwner, ContainerOwner: reqInfo.ContainerOwner,
BearerToken: md.BearerToken, ContainerAttributes: reqInfo.ContainerAttributes,
XHeaders: md.MetaHeader.GetXHeaders(), BearerToken: md.BearerToken,
XHeaders: md.MetaHeader.GetXHeaders(),
}) })
if err != nil { if err != nil {
return nil, toStatusErr(err) return nil, toStatusErr(err)
@ -296,14 +300,15 @@ func (c *Service) Search(request *objectV2.SearchRequest, stream objectSvc.Searc
} }
err = c.apeChecker.CheckAPE(stream.Context(), Prm{ err = c.apeChecker.CheckAPE(stream.Context(), Prm{
Namespace: reqInfo.Namespace, Namespace: reqInfo.Namespace,
Container: md.Container, Container: md.Container,
Method: nativeschema.MethodSearchObject, Method: nativeschema.MethodSearchObject,
Role: reqInfo.Role, Role: reqInfo.Role,
SenderKey: reqInfo.SenderKey, SenderKey: reqInfo.SenderKey,
ContainerOwner: reqInfo.ContainerOwner, ContainerOwner: reqInfo.ContainerOwner,
BearerToken: md.BearerToken, ContainerAttributes: reqInfo.ContainerAttributes,
XHeaders: md.MetaHeader.GetXHeaders(), BearerToken: md.BearerToken,
XHeaders: md.MetaHeader.GetXHeaders(),
}) })
if err != nil { if err != nil {
return toStatusErr(err) return toStatusErr(err)
@ -323,15 +328,16 @@ func (c *Service) Delete(ctx context.Context, request *objectV2.DeleteRequest) (
} }
err = c.apeChecker.CheckAPE(ctx, Prm{ err = c.apeChecker.CheckAPE(ctx, Prm{
Namespace: reqInfo.Namespace, Namespace: reqInfo.Namespace,
Container: md.Container, Container: md.Container,
Object: md.Object, Object: md.Object,
Method: nativeschema.MethodDeleteObject, Method: nativeschema.MethodDeleteObject,
Role: reqInfo.Role, Role: reqInfo.Role,
SenderKey: reqInfo.SenderKey, SenderKey: reqInfo.SenderKey,
ContainerOwner: reqInfo.ContainerOwner, ContainerOwner: reqInfo.ContainerOwner,
BearerToken: md.BearerToken, ContainerAttributes: reqInfo.ContainerAttributes,
XHeaders: md.MetaHeader.GetXHeaders(), BearerToken: md.BearerToken,
XHeaders: md.MetaHeader.GetXHeaders(),
}) })
if err != nil { if err != nil {
return nil, toStatusErr(err) return nil, toStatusErr(err)
@ -356,15 +362,16 @@ func (c *Service) GetRange(request *objectV2.GetRangeRequest, stream objectSvc.G
} }
err = c.apeChecker.CheckAPE(stream.Context(), Prm{ err = c.apeChecker.CheckAPE(stream.Context(), Prm{
Namespace: reqInfo.Namespace, Namespace: reqInfo.Namespace,
Container: md.Container, Container: md.Container,
Object: md.Object, Object: md.Object,
Method: nativeschema.MethodRangeObject, Method: nativeschema.MethodRangeObject,
Role: reqInfo.Role, Role: reqInfo.Role,
SenderKey: reqInfo.SenderKey, SenderKey: reqInfo.SenderKey,
ContainerOwner: reqInfo.ContainerOwner, ContainerOwner: reqInfo.ContainerOwner,
BearerToken: md.BearerToken, ContainerAttributes: reqInfo.ContainerAttributes,
XHeaders: md.MetaHeader.GetXHeaders(), BearerToken: md.BearerToken,
XHeaders: md.MetaHeader.GetXHeaders(),
}) })
if err != nil { if err != nil {
return toStatusErr(err) return toStatusErr(err)
@ -384,15 +391,16 @@ func (c *Service) GetRangeHash(ctx context.Context, request *objectV2.GetRangeHa
} }
prm := Prm{ prm := Prm{
Namespace: reqInfo.Namespace, Namespace: reqInfo.Namespace,
Container: md.Container, Container: md.Container,
Object: md.Object, Object: md.Object,
Method: nativeschema.MethodHashObject, Method: nativeschema.MethodHashObject,
Role: reqInfo.Role, Role: reqInfo.Role,
SenderKey: reqInfo.SenderKey, SenderKey: reqInfo.SenderKey,
ContainerOwner: reqInfo.ContainerOwner, ContainerOwner: reqInfo.ContainerOwner,
BearerToken: md.BearerToken, ContainerAttributes: reqInfo.ContainerAttributes,
XHeaders: md.MetaHeader.GetXHeaders(), BearerToken: md.BearerToken,
XHeaders: md.MetaHeader.GetXHeaders(),
} }
resp, err := c.next.GetRangeHash(ctx, request) resp, err := c.next.GetRangeHash(ctx, request)
@ -417,16 +425,17 @@ func (c *Service) PutSingle(ctx context.Context, request *objectV2.PutSingleRequ
} }
prm := Prm{ prm := Prm{
Namespace: reqInfo.Namespace, Namespace: reqInfo.Namespace,
Container: md.Container, Container: md.Container,
Object: md.Object, Object: md.Object,
Header: request.GetBody().GetObject().GetHeader(), Header: request.GetBody().GetObject().GetHeader(),
Method: nativeschema.MethodPutObject, Method: nativeschema.MethodPutObject,
Role: reqInfo.Role, Role: reqInfo.Role,
SenderKey: reqInfo.SenderKey, SenderKey: reqInfo.SenderKey,
ContainerOwner: reqInfo.ContainerOwner, ContainerOwner: reqInfo.ContainerOwner,
BearerToken: md.BearerToken, ContainerAttributes: reqInfo.ContainerAttributes,
XHeaders: md.MetaHeader.GetXHeaders(), BearerToken: md.BearerToken,
XHeaders: md.MetaHeader.GetXHeaders(),
} }
if err = c.apeChecker.CheckAPE(ctx, prm); err != nil { if err = c.apeChecker.CheckAPE(ctx, prm); err != nil {