[#1386] services/object: Fail eACL check if the request is invalid

Parse all headers beforehand and reject invalid requests.
Another approach would be to remember the error and check
it after `CalculateAction`, which is a bit faster.
The rule of thumb here is "first validate, then use".

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
This commit is contained in:
Evgenii Stratonikov 2022-05-17 15:46:25 +03:00 committed by LeL
parent f99a0498da
commit 3f4475f97b
3 changed files with 55 additions and 40 deletions

View file

@ -187,14 +187,17 @@ func (c *Checker) CheckEACL(msg interface{}, reqInfo v2.RequestInfo) error {
) )
} }
hdrSrc, err := eaclV2.NewMessageHeaderSource(hdrSrcOpts...)
if err != nil {
return fmt.Errorf("can't parse headers: %w", err)
}
action := c.validator.CalculateAction(new(eaclSDK.ValidationUnit). action := c.validator.CalculateAction(new(eaclSDK.ValidationUnit).
WithRole(reqInfo.RequestRole()). WithRole(reqInfo.RequestRole()).
WithOperation(reqInfo.Operation()). WithOperation(reqInfo.Operation()).
WithContainerID(reqInfo.ContainerID()). WithContainerID(reqInfo.ContainerID()).
WithSenderKey(reqInfo.SenderKey()). WithSenderKey(reqInfo.SenderKey()).
WithHeaderSource( WithHeaderSource(hdrSrc).
eaclV2.NewMessageHeaderSource(hdrSrcOpts...),
).
WithEACLTable(&table), WithEACLTable(&table),
) )

View file

@ -103,31 +103,33 @@ func TestHeadRequest(t *testing.T) {
obj: obj, obj: obj,
} }
newSource := func(t *testing.T) eaclSDK.TypedHeaderSource {
hdrSrc, err := NewMessageHeaderSource(
WithObjectStorage(lStorage),
WithServiceRequest(req),
WithAddress(addr))
require.NoError(t, err)
return hdrSrc
}
cnr, _ := addr.ContainerID() cnr, _ := addr.ContainerID()
unit := new(eaclSDK.ValidationUnit). unit := new(eaclSDK.ValidationUnit).
WithContainerID(&cnr). WithContainerID(&cnr).
WithOperation(eaclSDK.OperationHead). WithOperation(eaclSDK.OperationHead).
WithSenderKey(senderKey.Bytes()). WithSenderKey(senderKey.Bytes()).
WithHeaderSource(
NewMessageHeaderSource(
WithObjectStorage(lStorage),
WithServiceRequest(req),
WithAddress(addr),
),
).
WithEACLTable(table) WithEACLTable(table)
validator := eaclSDK.NewValidator() validator := eaclSDK.NewValidator()
require.Equal(t, eaclSDK.ActionDeny, validator.CalculateAction(unit)) require.Equal(t, eaclSDK.ActionDeny, validator.CalculateAction(unit.WithHeaderSource(newSource(t))))
meta.SetXHeaders(nil) meta.SetXHeaders(nil)
require.Equal(t, eaclSDK.ActionAllow, validator.CalculateAction(unit)) require.Equal(t, eaclSDK.ActionAllow, validator.CalculateAction(unit.WithHeaderSource(newSource(t))))
meta.SetXHeaders(xHdrs) meta.SetXHeaders(xHdrs)
obj.SetAttributes() obj.SetAttributes()
require.Equal(t, eaclSDK.ActionAllow, validator.CalculateAction(unit)) require.Equal(t, eaclSDK.ActionAllow, validator.CalculateAction(unit.WithHeaderSource(newSource(t))))
} }

View file

@ -38,7 +38,8 @@ type Response interface {
} }
type headerSource struct { type headerSource struct {
*cfg requestHeaders []eaclSDK.Header
objectHeaders []eaclSDK.Header
} }
func defaultCfg() *cfg { func defaultCfg() *cfg {
@ -47,26 +48,32 @@ func defaultCfg() *cfg {
} }
} }
func NewMessageHeaderSource(opts ...Option) eaclSDK.TypedHeaderSource { func NewMessageHeaderSource(opts ...Option) (eaclSDK.TypedHeaderSource, error) {
cfg := defaultCfg() cfg := defaultCfg()
for i := range opts { for i := range opts {
opts[i](cfg) opts[i](cfg)
} }
return &headerSource{ objHdrs, err := cfg.objectHeaders()
cfg: cfg, if err != nil {
return nil, err
} }
return headerSource{
objectHeaders: objHdrs,
requestHeaders: requestHeaders(cfg.msg),
}, nil
} }
func (h *headerSource) HeadersOfType(typ eaclSDK.FilterHeaderType) ([]eaclSDK.Header, bool) { func (h headerSource) HeadersOfType(typ eaclSDK.FilterHeaderType) ([]eaclSDK.Header, bool) {
switch typ { switch typ {
default: default:
return nil, true return nil, true
case eaclSDK.HeaderFromRequest: case eaclSDK.HeaderFromRequest:
return requestHeaders(h.msg), true return h.requestHeaders, true
case eaclSDK.HeaderFromObject: case eaclSDK.HeaderFromObject:
return h.objectHeaders() return h.objectHeaders, true
} }
} }
@ -82,7 +89,7 @@ func requestHeaders(msg xHeaderSource) []eaclSDK.Header {
return res return res
} }
func (h *headerSource) objectHeaders() ([]eaclSDK.Header, bool) { func (h *cfg) objectHeaders() ([]eaclSDK.Header, error) {
switch m := h.msg.(type) { switch m := h.msg.(type) {
default: default:
panic(fmt.Sprintf("unexpected message type %T", h.msg)) panic(fmt.Sprintf("unexpected message type %T", h.msg))
@ -96,7 +103,7 @@ func (h *headerSource) objectHeaders() ([]eaclSDK.Header, bool) {
*objectV2.GetRangeRequest, *objectV2.GetRangeRequest,
*objectV2.GetRangeHashRequest, *objectV2.GetRangeHashRequest,
*objectV2.DeleteRequest: *objectV2.DeleteRequest:
return addressHeaders(h.addr), true return addressHeaders(h.addr), nil
case *objectV2.PutRequest: case *objectV2.PutRequest:
if v, ok := req.GetBody().GetObjectPart().(*objectV2.PutObjectPartInit); ok { if v, ok := req.GetBody().GetObjectPart().(*objectV2.PutObjectPartInit); ok {
oV2 := new(objectV2.Object) oV2 := new(objectV2.Object)
@ -107,17 +114,19 @@ func (h *headerSource) objectHeaders() ([]eaclSDK.Header, bool) {
idV2 := v.GetObjectID() idV2 := v.GetObjectID()
var id objectSDKID.ID var id objectSDKID.ID
if idV2 == nil { if idV2 != nil {
// FIXME(@cthulhu-rider): #1386 we need to either return error or check it earlier if err := id.ReadFromV2(*idV2); err != nil {
_ = id.ReadFromV2(*idV2) return nil, fmt.Errorf("can't parse object ID: %w", err)
}
} }
cnrV2 := v.GetHeader().GetContainerID() cnrV2 := v.GetHeader().GetContainerID()
var cnr cid.ID var cnr cid.ID
if cnrV2 != nil { if cnrV2 != nil {
// FIXME(@cthulhu-rider): #1386 we need to either return error or check it earlier if err := cnr.ReadFromV2(*cnrV2); err != nil {
_ = cnr.ReadFromV2(*cnrV2) return nil, fmt.Errorf("can't parse container ID: %w", err)
}
} }
h.addr = new(objectSDKAddress.Address) h.addr = new(objectSDKAddress.Address)
@ -127,31 +136,32 @@ func (h *headerSource) objectHeaders() ([]eaclSDK.Header, bool) {
hs := headersFromObject(object.NewFromV2(oV2), h.addr) hs := headersFromObject(object.NewFromV2(oV2), h.addr)
return hs, true return hs, nil
} }
case *objectV2.SearchRequest: case *objectV2.SearchRequest:
cnrV2 := req.GetBody().GetContainerID() cnrV2 := req.GetBody().GetContainerID()
var cnr cid.ID var cnr cid.ID
if cnrV2 != nil { if cnrV2 != nil {
// FIXME(@cthulhu-rider): #1386 we need to either return error or check it earlier if err := cnr.ReadFromV2(*cnrV2); err != nil {
_ = cnr.ReadFromV2(*cnrV2) return nil, fmt.Errorf("can't parse container ID: %w", err)
}
} }
return []eaclSDK.Header{cidHeader(&cnr)}, true return []eaclSDK.Header{cidHeader(&cnr)}, nil
} }
case *responseXHeaderSource: case *responseXHeaderSource:
switch resp := m.resp.(type) { switch resp := m.resp.(type) {
default: default:
hs, _ := h.localObjectHeaders(h.addr) hs, _ := h.localObjectHeaders(h.addr)
return hs, true return hs, nil
case *objectV2.GetResponse: case *objectV2.GetResponse:
if v, ok := resp.GetBody().GetObjectPart().(*objectV2.GetObjectPartInit); ok { if v, ok := resp.GetBody().GetObjectPart().(*objectV2.GetObjectPartInit); ok {
oV2 := new(objectV2.Object) oV2 := new(objectV2.Object)
oV2.SetObjectID(v.GetObjectID()) oV2.SetObjectID(v.GetObjectID())
oV2.SetHeader(v.GetHeader()) oV2.SetHeader(v.GetHeader())
return headersFromObject(object.NewFromV2(oV2), h.addr), true return headersFromObject(object.NewFromV2(oV2), h.addr), nil
} }
case *objectV2.HeadResponse: case *objectV2.HeadResponse:
oV2 := new(objectV2.Object) oV2 := new(objectV2.Object)
@ -179,20 +189,20 @@ func (h *headerSource) objectHeaders() ([]eaclSDK.Header, bool) {
oV2.SetHeader(hdr) oV2.SetHeader(hdr)
return headersFromObject(object.NewFromV2(oV2), h.addr), true return headersFromObject(object.NewFromV2(oV2), h.addr), nil
} }
} }
return nil, true return nil, nil
} }
func (h *headerSource) localObjectHeaders(addr *objectSDKAddress.Address) ([]eaclSDK.Header, bool) { func (h *cfg) localObjectHeaders(addr *objectSDKAddress.Address) ([]eaclSDK.Header, error) {
obj, err := h.storage.Head(addr) obj, err := h.storage.Head(addr)
if err == nil { if err != nil {
return headersFromObject(obj, addr), true // Still parse addressHeaders, because the errors is ignored in some places.
return addressHeaders(addr), err
} }
return headersFromObject(obj, addr), nil
return addressHeaders(addr), false
} }
func cidHeader(idCnr *cid.ID) eaclSDK.Header { func cidHeader(idCnr *cid.ID) eaclSDK.Header {