object: Introduce soft ape checks #986

Merged
fyrchik merged 3 commits from aarifullin/frostfs-node:fix/strict_ape_checks into master 2024-02-28 19:05:58 +00:00
6 changed files with 176 additions and 108 deletions
Showing only changes of commit e9363a5094 - Show all commits

View file

@ -104,6 +104,13 @@ func (r RequestInfo) RequestRole() acl.Role {
return r.requestRole
}
// IsSoftAPECheck states if APE should perform soft checks.
// Soft APE check allows a request if CheckAPE returns NoRuleFound for it,
// otherwise it denies the request.
func (r RequestInfo) IsSoftAPECheck() bool {
return r.BasicACL().Bits() != 0

You are always using NOT operator with result of this function and with field StrictAPECheck. How about to replace it with SoftAPECheck?

You are always using NOT operator with result of this function and with field `StrictAPECheck`. How about to replace it with `SoftAPECheck`?

Replaced to SoftAPECheck. Could u check please if I've left mistakes. Also, I fixed commit message

Replaced to `SoftAPECheck`. Could u check please if I've left mistakes. Also, I fixed commit message
}
// MetaWithToken groups session and bearer tokens,
// verification header and raw API request.
type MetaWithToken struct {

View file

@ -113,9 +113,10 @@ type wrappedGetObjectStream struct {
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(),
Namespace: w.requestInfo.ContainerNamespace(),
SenderKey: w.requestInfo.SenderKey(),
Role: w.requestInfo.RequestRole(),
SoftAPECheck: w.requestInfo.IsSoftAPECheck(),
})
}
@ -136,9 +137,10 @@ type wrappedRangeStream struct {
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(),
Namespace: w.requestInfo.ContainerNamespace(),
SenderKey: w.requestInfo.SenderKey(),
Role: w.requestInfo.RequestRole(),
SoftAPECheck: w.requestInfo.IsSoftAPECheck(),
})
}
@ -159,9 +161,10 @@ type wrappedSearchStream struct {
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(),
Namespace: w.requestInfo.ContainerNamespace(),
SenderKey: w.requestInfo.SenderKey(),
Role: w.requestInfo.RequestRole(),
SoftAPECheck: w.requestInfo.IsSoftAPECheck(),
})
}
@ -216,10 +219,12 @@ func (b Service) Get(request *objectV2.GetRequest, stream object.GetObjectStream
reqInfo.obj = obj
if !b.checker.CheckBasicACL(reqInfo) {
return basicACLErr(reqInfo)
} else if err := b.checker.CheckEACL(request, reqInfo); err != nil {
return eACLErr(reqInfo, err)
if reqInfo.IsSoftAPECheck() {
if !b.checker.CheckBasicACL(reqInfo) {
return basicACLErr(reqInfo)
} else if err := b.checker.CheckEACL(request, reqInfo); err != nil {
return eACLErr(reqInfo, err)
}
}
return b.next.Get(request, &getStreamBasicChecker{
@ -283,10 +288,12 @@ func (b Service) Head(
reqInfo.obj = obj
if !b.checker.CheckBasicACL(reqInfo) {
return nil, basicACLErr(reqInfo)
} else if err := b.checker.CheckEACL(request, reqInfo); err != nil {
return nil, eACLErr(reqInfo, err)
if reqInfo.IsSoftAPECheck() {
if !b.checker.CheckBasicACL(reqInfo) {
return nil, basicACLErr(reqInfo)
} else if err := b.checker.CheckEACL(request, reqInfo); err != nil {
return nil, eACLErr(reqInfo, err)
}
}
resp, err := b.next.Head(requestContext(ctx, reqInfo), request)
@ -334,10 +341,12 @@ func (b Service) Search(request *objectV2.SearchRequest, stream object.SearchStr
return err
}
if !b.checker.CheckBasicACL(reqInfo) {
return basicACLErr(reqInfo)
} else if err := b.checker.CheckEACL(request, reqInfo); err != nil {
return eACLErr(reqInfo, err)
if reqInfo.IsSoftAPECheck() {
if !b.checker.CheckBasicACL(reqInfo) {
return basicACLErr(reqInfo)
} else if err := b.checker.CheckEACL(request, reqInfo); err != nil {
return eACLErr(reqInfo, err)
}
}
return b.next.Search(request, &searchStreamBasicChecker{
@ -392,10 +401,12 @@ func (b Service) Delete(
reqInfo.obj = obj
if !b.checker.CheckBasicACL(reqInfo) {
return nil, basicACLErr(reqInfo)
} else if err := b.checker.CheckEACL(request, reqInfo); err != nil {
return nil, eACLErr(reqInfo, err)
if reqInfo.IsSoftAPECheck() {
if !b.checker.CheckBasicACL(reqInfo) {
return nil, basicACLErr(reqInfo)
} else if err := b.checker.CheckEACL(request, reqInfo); err != nil {
return nil, eACLErr(reqInfo, err)
}
}
return b.next.Delete(requestContext(ctx, reqInfo), request)
@ -443,10 +454,12 @@ func (b Service) GetRange(request *objectV2.GetRangeRequest, stream object.GetOb
reqInfo.obj = obj
if !b.checker.CheckBasicACL(reqInfo) {
return basicACLErr(reqInfo)
} else if err := b.checker.CheckEACL(request, reqInfo); err != nil {
return eACLErr(reqInfo, err)
if reqInfo.IsSoftAPECheck() {
if !b.checker.CheckBasicACL(reqInfo) {
return basicACLErr(reqInfo)
} else if err := b.checker.CheckEACL(request, reqInfo); err != nil {
return eACLErr(reqInfo, err)
}
}
return b.next.GetRange(request, &rangeStreamBasicChecker{
@ -458,9 +471,10 @@ func (b Service) GetRange(request *objectV2.GetRangeRequest, stream object.GetOb
func requestContext(ctx context.Context, reqInfo RequestInfo) context.Context {
return context.WithValue(ctx, object.RequestContextKey, &object.RequestContext{
Namespace: reqInfo.ContainerNamespace(),
SenderKey: reqInfo.SenderKey(),
Role: reqInfo.RequestRole(),
Namespace: reqInfo.ContainerNamespace(),
SenderKey: reqInfo.SenderKey(),
Role: reqInfo.RequestRole(),
SoftAPECheck: reqInfo.IsSoftAPECheck(),
})
}
@ -509,10 +523,12 @@ func (b Service) GetRangeHash(
reqInfo.obj = obj
if !b.checker.CheckBasicACL(reqInfo) {
return nil, basicACLErr(reqInfo)
} else if err := b.checker.CheckEACL(request, reqInfo); err != nil {
return nil, eACLErr(reqInfo, err)
if reqInfo.IsSoftAPECheck() {
if !b.checker.CheckBasicACL(reqInfo) {
return nil, basicACLErr(reqInfo)
} else if err := b.checker.CheckEACL(request, reqInfo); err != nil {
return nil, eACLErr(reqInfo, err)
}
}
return b.next.GetRangeHash(requestContext(ctx, reqInfo), request)
@ -566,12 +582,13 @@ func (b Service) PutSingle(ctx context.Context, request *objectV2.PutSingleReque
reqInfo.obj = obj
if !b.checker.CheckBasicACL(reqInfo) || !b.checker.StickyBitCheck(reqInfo, idOwner) {
return nil, basicACLErr(reqInfo)
}
if err := b.checker.CheckEACL(request, reqInfo); err != nil {
return nil, eACLErr(reqInfo, err)
if reqInfo.IsSoftAPECheck() {
if !b.checker.CheckBasicACL(reqInfo) || !b.checker.StickyBitCheck(reqInfo, idOwner) {
return nil, basicACLErr(reqInfo)
}
if err := b.checker.CheckEACL(request, reqInfo); err != nil {
return nil, eACLErr(reqInfo, err)
}
}
return b.next.PutSingle(requestContext(ctx, reqInfo), request)
@ -639,8 +656,10 @@ func (p putStreamBasicChecker) Send(ctx context.Context, request *objectV2.PutRe
reqInfo.obj = obj
if !p.source.checker.CheckBasicACL(reqInfo) || !p.source.checker.StickyBitCheck(reqInfo, idOwner) {
return basicACLErr(reqInfo)
if reqInfo.IsSoftAPECheck() {
if !p.source.checker.CheckBasicACL(reqInfo) || !p.source.checker.StickyBitCheck(reqInfo, idOwner) {
return basicACLErr(reqInfo)
}
}
ctx = requestContext(ctx, reqInfo)

View file

@ -45,6 +45,9 @@ type Prm struct {
// An encoded sender's public key string.
SenderKey string
// If SoftAPECheck is set to true, then NoRuleFound is interpreted as allow.
SoftAPECheck bool
}
var errMissingOID = fmt.Errorf("object ID is not set")
@ -63,9 +66,9 @@ func (c *checkerImpl) CheckAPE(ctx context.Context, prm Prm) error {
return err
}
if !ruleFound || status == apechain.Allow {
if !ruleFound && prm.SoftAPECheck || status == apechain.Allow {
return nil
}
return fmt.Errorf("found denying rule for %s: %s", prm.Method, status)
return fmt.Errorf("method %s: %s", prm.Method, status)
}

View file

@ -165,11 +165,29 @@ func TestAPECheck(t *testing.T) {
container: containerID,
object: stringPtr(objectID),
methods: methodsRequiredOID,
containerRules: []chain.Rule{
{
Status: chain.Allow,
Actions: chain.Actions{Names: methodsRequiredOID},
Resources: chain.Resources{
Names: []string{fmt.Sprintf(nativeschema.ResourceFormatRootContainerObject, containerID, objectID)},
},
},
},
},
{
name: "oid optional requests are allowed",
container: containerID,
methods: methodsOptionalOID,
containerRules: []chain.Rule{
{
Status: chain.Allow,
Actions: chain.Actions{Names: methodsOptionalOID},
Resources: chain.Resources{
Names: []string{fmt.Sprintf(nativeschema.ResourceFormatRootContainerObjects, containerID)},
},
},
},
},
{
name: "oid required requests are denied",

View file

@ -60,9 +60,13 @@ type getStreamBasicChecker struct {
apeChecker Checker
namespace string
senderKey []byte
role string
softAPECheck bool
}
func (g *getStreamBasicChecker) Send(resp *objectV2.GetResponse) error {
@ -73,12 +77,14 @@ func (g *getStreamBasicChecker) Send(resp *objectV2.GetResponse) error {
}
prm := Prm{
Container: cnrID,
Object: objID,
Header: partInit.GetHeader(),
Method: nativeschema.MethodGetObject,
SenderKey: hex.EncodeToString(g.senderKey),
Role: g.role,
Namespace: g.namespace,
Container: cnrID,
Object: objID,
Header: partInit.GetHeader(),
Method: nativeschema.MethodGetObject,
SenderKey: hex.EncodeToString(g.senderKey),
Role: g.role,
SoftAPECheck: g.softAPECheck,
}
if err := g.apeChecker.CheckAPE(g.Context(), prm); err != nil {
@ -112,12 +118,13 @@ func (c *Service) Get(request *objectV2.GetRequest, stream objectSvc.GetObjectSt
}
err = c.apeChecker.CheckAPE(stream.Context(), Prm{
Namespace: reqCtx.Namespace,
Container: cnrID,
Object: objID,
Method: nativeschema.MethodGetObject,
Role: nativeSchemaRole(reqCtx.Role),
SenderKey: hex.EncodeToString(reqCtx.SenderKey),
Namespace: reqCtx.Namespace,
Container: cnrID,
Object: objID,
Method: nativeschema.MethodGetObject,
Role: nativeSchemaRole(reqCtx.Role),
SenderKey: hex.EncodeToString(reqCtx.SenderKey),
SoftAPECheck: reqCtx.SoftAPECheck,
})
if err != nil {
return toStatusErr(err)
@ -126,6 +133,10 @@ func (c *Service) Get(request *objectV2.GetRequest, stream objectSvc.GetObjectSt
return c.next.Get(request, &getStreamBasicChecker{
GetObjectStream: stream,
apeChecker: c.apeChecker,
namespace: reqCtx.Namespace,
senderKey: reqCtx.SenderKey,
role: nativeSchemaRole(reqCtx.Role),
softAPECheck: reqCtx.SoftAPECheck,
})
}
@ -148,13 +159,14 @@ func (p *putStreamBasicChecker) Send(ctx context.Context, request *objectV2.PutR
}
prm := Prm{
Namespace: reqCtx.Namespace,
Container: cnrID,
Object: objID,
Header: partInit.GetHeader(),
Method: nativeschema.MethodPutObject,
SenderKey: hex.EncodeToString(reqCtx.SenderKey),
Role: nativeSchemaRole(reqCtx.Role),
Namespace: reqCtx.Namespace,
Container: cnrID,
Object: objID,
Header: partInit.GetHeader(),
Method: nativeschema.MethodPutObject,
SenderKey: hex.EncodeToString(reqCtx.SenderKey),
Role: nativeSchemaRole(reqCtx.Role),
SoftAPECheck: reqCtx.SoftAPECheck,
}
if err := p.apeChecker.CheckAPE(ctx, prm); err != nil {
@ -190,12 +202,13 @@ func (c *Service) Head(ctx context.Context, request *objectV2.HeadRequest) (*obj
}
err = c.apeChecker.CheckAPE(ctx, Prm{
Namespace: reqCtx.Namespace,
Container: cnrID,
Object: objID,
Method: nativeschema.MethodHeadObject,
Role: nativeSchemaRole(reqCtx.Role),
SenderKey: hex.EncodeToString(reqCtx.SenderKey),
Namespace: reqCtx.Namespace,
Container: cnrID,
Object: objID,
Method: nativeschema.MethodHeadObject,
Role: nativeSchemaRole(reqCtx.Role),
SenderKey: hex.EncodeToString(reqCtx.SenderKey),
SoftAPECheck: reqCtx.SoftAPECheck,
})
if err != nil {
return nil, err
@ -226,13 +239,14 @@ func (c *Service) Head(ctx context.Context, request *objectV2.HeadRequest) (*obj
}
err = c.apeChecker.CheckAPE(ctx, Prm{
Namespace: reqCtx.Namespace,
Container: cnrID,
Object: objID,
Header: header,
Method: nativeschema.MethodHeadObject,
Role: nativeSchemaRole(reqCtx.Role),
SenderKey: hex.EncodeToString(reqCtx.SenderKey),
Namespace: reqCtx.Namespace,
Container: cnrID,
Object: objID,
Header: header,
Method: nativeschema.MethodHeadObject,
Role: nativeSchemaRole(reqCtx.Role),
SenderKey: hex.EncodeToString(reqCtx.SenderKey),
SoftAPECheck: reqCtx.SoftAPECheck,
})
if err != nil {
return nil, err
@ -254,11 +268,12 @@ func (c *Service) Search(request *objectV2.SearchRequest, stream objectSvc.Searc
}
err = c.apeChecker.CheckAPE(stream.Context(), Prm{
Namespace: reqCtx.Namespace,
Container: cnrID,
Method: nativeschema.MethodSearchObject,
Role: nativeSchemaRole(reqCtx.Role),
SenderKey: hex.EncodeToString(reqCtx.SenderKey),
Namespace: reqCtx.Namespace,
Container: cnrID,
Method: nativeschema.MethodSearchObject,
Role: nativeSchemaRole(reqCtx.Role),
SenderKey: hex.EncodeToString(reqCtx.SenderKey),
SoftAPECheck: reqCtx.SoftAPECheck,
})
if err != nil {
return toStatusErr(err)
@ -279,12 +294,13 @@ func (c *Service) Delete(ctx context.Context, request *objectV2.DeleteRequest) (
}
err = c.apeChecker.CheckAPE(ctx, Prm{
Namespace: reqCtx.Namespace,
Container: cnrID,
Object: objID,
Method: nativeschema.MethodDeleteObject,
Role: nativeSchemaRole(reqCtx.Role),
SenderKey: hex.EncodeToString(reqCtx.SenderKey),
Namespace: reqCtx.Namespace,
Container: cnrID,
Object: objID,
Method: nativeschema.MethodDeleteObject,
Role: nativeSchemaRole(reqCtx.Role),
SenderKey: hex.EncodeToString(reqCtx.SenderKey),
SoftAPECheck: reqCtx.SoftAPECheck,
})
if err != nil {
return nil, err
@ -310,12 +326,13 @@ func (c *Service) GetRange(request *objectV2.GetRangeRequest, stream objectSvc.G
}
err = c.apeChecker.CheckAPE(stream.Context(), Prm{
Namespace: reqCtx.Namespace,
Container: cnrID,
Object: objID,
Method: nativeschema.MethodRangeObject,
Role: nativeSchemaRole(reqCtx.Role),
SenderKey: hex.EncodeToString(reqCtx.SenderKey),
Namespace: reqCtx.Namespace,
Container: cnrID,
Object: objID,
Method: nativeschema.MethodRangeObject,
Role: nativeSchemaRole(reqCtx.Role),
SenderKey: hex.EncodeToString(reqCtx.SenderKey),
SoftAPECheck: reqCtx.SoftAPECheck,
})
if err != nil {
return toStatusErr(err)
@ -336,12 +353,13 @@ func (c *Service) GetRangeHash(ctx context.Context, request *objectV2.GetRangeHa
}
prm := Prm{
Namespace: reqCtx.Namespace,
Container: cnrID,
Object: objID,
Method: nativeschema.MethodHashObject,
Role: nativeSchemaRole(reqCtx.Role),
SenderKey: hex.EncodeToString(reqCtx.SenderKey),
Namespace: reqCtx.Namespace,
Container: cnrID,
Object: objID,
Method: nativeschema.MethodHashObject,
Role: nativeSchemaRole(reqCtx.Role),
SenderKey: hex.EncodeToString(reqCtx.SenderKey),
SoftAPECheck: reqCtx.SoftAPECheck,
}
if err = c.apeChecker.CheckAPE(ctx, prm); err != nil {
@ -371,13 +389,14 @@ func (c *Service) PutSingle(ctx context.Context, request *objectV2.PutSingleRequ
}
prm := Prm{
Namespace: reqCtx.Namespace,
Container: cnrID,
Object: objID,
Header: request.GetBody().GetObject().GetHeader(),
Method: nativeschema.MethodPutObject,
Role: nativeSchemaRole(reqCtx.Role),
SenderKey: hex.EncodeToString(reqCtx.SenderKey),
Namespace: reqCtx.Namespace,
Container: cnrID,
Object: objID,
Header: request.GetBody().GetObject().GetHeader(),
Method: nativeschema.MethodPutObject,
Role: nativeSchemaRole(reqCtx.Role),
SenderKey: hex.EncodeToString(reqCtx.SenderKey),
SoftAPECheck: reqCtx.SoftAPECheck,
}
if err = c.apeChecker.CheckAPE(ctx, prm); err != nil {

View file

@ -13,4 +13,6 @@ type RequestContext struct {
SenderKey []byte
Role acl.Role
SoftAPECheck bool
}