[#872] object: Introduce APE middlewar for object service

Signed-off-by: Airat Arifullin <a.arifullin@yadro.com>
This commit is contained in:
Airat Arifullin 2023-12-27 17:18:15 +03:00
parent e43609c616
commit c8baf76fae
13 changed files with 1456 additions and 175 deletions

View file

@ -5,6 +5,7 @@ import (
"errors"
"fmt"
containerV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
objectV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/container"
@ -67,10 +68,6 @@ type cfg struct {
checker ACLChecker
// TODO(aarifullin): apeCheck is temporarily the part of
// acl service and must be standalone.
apeChecker APEChainChecker
irFetcher InnerRingFetcher
nm netmap.Source
@ -83,7 +80,6 @@ func New(next object.ServiceServer,
nm netmap.Source,
irf InnerRingFetcher,
acl ACLChecker,
apeChecker APEChainChecker,
cs container.Source,
opts ...Option,
) Service {
@ -93,7 +89,6 @@ func New(next object.ServiceServer,
nm: nm,
irFetcher: irf,
checker: acl,
apeChecker: apeChecker,
containers: cs,
}
@ -107,6 +102,75 @@ func New(next object.ServiceServer,
}
}
// wrappedGetObjectStream propagates RequestContext into GetObjectStream's context.
// This allows to retrieve already calculated immutable request-specific values in next handler invocation.
type wrappedGetObjectStream struct {
object.GetObjectStream
requestInfo RequestInfo
}
func (w *wrappedGetObjectStream) Context() context.Context {
return context.WithValue(w.GetObjectStream.Context(), object.RequestContextKey, &object.RequestContext{
Namespace: w.requestInfo.ContainerNamespace(),
SenderKey: w.requestInfo.SenderKey(),
Role: w.requestInfo.RequestRole(),
})
}
func newWrappedGetObjectStreamStream(getObjectStream object.GetObjectStream, reqInfo RequestInfo) object.GetObjectStream {
return &wrappedGetObjectStream{
GetObjectStream: getObjectStream,
requestInfo: reqInfo,
}
}
// wrappedRangeStream propagates RequestContext into GetObjectRangeStream's context.
// This allows to retrieve already calculated immutable request-specific values in next handler invocation.
type wrappedRangeStream struct {
object.GetObjectRangeStream
requestInfo RequestInfo
}
func (w *wrappedRangeStream) Context() context.Context {
return context.WithValue(w.GetObjectRangeStream.Context(), object.RequestContextKey, &object.RequestContext{
Namespace: w.requestInfo.ContainerNamespace(),
SenderKey: w.requestInfo.SenderKey(),
Role: w.requestInfo.RequestRole(),
})
}
func newWrappedRangeStream(rangeStream object.GetObjectRangeStream, reqInfo RequestInfo) object.GetObjectRangeStream {
return &wrappedRangeStream{
GetObjectRangeStream: rangeStream,
requestInfo: reqInfo,
}
}
// wrappedSearchStream propagates RequestContext into SearchStream's context.
// This allows to retrieve already calculated immutable request-specific values in next handler invocation.
type wrappedSearchStream struct {
object.SearchStream
requestInfo RequestInfo
}
func (w *wrappedSearchStream) Context() context.Context {
return context.WithValue(w.SearchStream.Context(), object.RequestContextKey, &object.RequestContext{
Namespace: w.requestInfo.ContainerNamespace(),
SenderKey: w.requestInfo.SenderKey(),
Role: w.requestInfo.RequestRole(),
})
}
func newWrappedSearchStream(searchStream object.SearchStream, reqInfo RequestInfo) object.SearchStream {
return &wrappedSearchStream{
SearchStream: searchStream,
requestInfo: reqInfo,
}
}
// Get implements ServiceServer interface, makes ACL checks and calls
// next Get method in the ServiceServer pipeline.
func (b Service) Get(request *objectV2.GetRequest, stream object.GetObjectStream) error {
@ -158,7 +222,7 @@ func (b Service) Get(request *objectV2.GetRequest, stream object.GetObjectStream
}
return b.next.Get(request, &getStreamBasicChecker{
GetObjectStream: stream,
GetObjectStream: newWrappedGetObjectStreamStream(stream, reqInfo),
info: reqInfo,
checker: b.checker,
})
@ -224,7 +288,7 @@ func (b Service) Head(
return nil, eACLErr(reqInfo, err)
}
resp, err := b.next.Head(ctx, request)
resp, err := b.next.Head(requestContext(ctx, reqInfo), request)
if err == nil {
if err = b.checker.CheckEACL(resp, reqInfo); err != nil {
err = eACLErr(reqInfo, err)
@ -277,7 +341,7 @@ func (b Service) Search(request *objectV2.SearchRequest, stream object.SearchStr
return b.next.Search(request, &searchStreamBasicChecker{
checker: b.checker,
SearchStream: stream,
SearchStream: newWrappedSearchStream(stream, reqInfo),
info: reqInfo,
})
}
@ -333,7 +397,7 @@ func (b Service) Delete(
return nil, eACLErr(reqInfo, err)
}
return b.next.Delete(ctx, request)
return b.next.Delete(requestContext(ctx, reqInfo), request)
}
func (b Service) GetRange(request *objectV2.GetRangeRequest, stream object.GetObjectRangeStream) error {
@ -386,11 +450,18 @@ func (b Service) GetRange(request *objectV2.GetRangeRequest, stream object.GetOb
return b.next.GetRange(request, &rangeStreamBasicChecker{
checker: b.checker,
GetObjectRangeStream: stream,
GetObjectRangeStream: newWrappedRangeStream(stream, reqInfo),
info: reqInfo,
})
}
func requestContext(ctx context.Context, reqInfo RequestInfo) context.Context {
return context.WithValue(ctx, object.RequestContextKey, &object.RequestContext{
SenderKey: reqInfo.SenderKey(),
Role: reqInfo.RequestRole(),
})
}
func (b Service) GetRangeHash(
ctx context.Context,
request *objectV2.GetRangeHashRequest,
@ -442,7 +513,7 @@ func (b Service) GetRangeHash(
return nil, eACLErr(reqInfo, err)
}
return b.next.GetRangeHash(ctx, request)
return b.next.GetRangeHash(requestContext(ctx, reqInfo), request)
}
func (b Service) PutSingle(ctx context.Context, request *objectV2.PutSingleRequest) (*objectV2.PutSingleResponse, error) {
@ -501,7 +572,7 @@ func (b Service) PutSingle(ctx context.Context, request *objectV2.PutSingleReque
return nil, eACLErr(reqInfo, err)
}
return b.next.PutSingle(ctx, request)
return b.next.PutSingle(requestContext(ctx, reqInfo), request)
}
func (p putStreamBasicChecker) Send(ctx context.Context, request *objectV2.PutRequest) error {
@ -566,9 +637,11 @@ func (p putStreamBasicChecker) Send(ctx context.Context, request *objectV2.PutRe
reqInfo.obj = obj
if err := p.source.apeChecker.CheckIfRequestPermitted(reqInfo); err != nil {
return err
if !p.source.checker.CheckBasicACL(reqInfo) || !p.source.checker.StickyBitCheck(reqInfo, idOwner) {
return basicACLErr(reqInfo)
}
ctx = requestContext(ctx, reqInfo)
}
return p.next.Send(ctx, request)
@ -671,6 +744,7 @@ func (b Service) findRequestInfo(req MetaWithToken, idCnr cid.ID, op acl.Op) (in
info.operation = op
info.cnrOwner = cnr.Value.Owner()
info.idCnr = idCnr
info.cnrNamespace = cnr.Value.Attribute(containerV2.SysAttributeZone)
// it is assumed that at the moment the key will be valid,
// otherwise the request would not pass validation