frostfs-node/pkg/services/object/acl/eacl/v2/headers.go

247 lines
5.8 KiB
Go

package v2
import (
"context"
"errors"
"fmt"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
objectV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
refsV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
eaclSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
)
type Option func(*cfg)
type cfg struct {
storage ObjectStorage
msg XHeaderSource
cnr cid.ID
obj *oid.ID
}
type ObjectStorage interface {
Head(context.Context, oid.Address) (*objectSDK.Object, error)
}
type Request interface {
GetMetaHeader() *session.RequestMetaHeader
}
type Response interface {
GetMetaHeader() *session.ResponseMetaHeader
}
type headerSource struct {
requestHeaders []eaclSDK.Header
objectHeaders []eaclSDK.Header
incompleteObjectHeaders bool
}
func NewMessageHeaderSource(os ObjectStorage, xhs XHeaderSource, cnrID cid.ID, opts ...Option) (eaclSDK.TypedHeaderSource, error) {
cfg := &cfg{
storage: os,
cnr: cnrID,
msg: xhs,
}
for i := range opts {
opts[i](cfg)
}
if cfg.msg == nil {
return nil, errors.New("message is not provided")
}
var res headerSource
err := cfg.readObjectHeaders(&res)
if err != nil {
return nil, err
}
res.requestHeaders = cfg.msg.GetXHeaders()
return res, nil
}
func (h headerSource) HeadersOfType(typ eaclSDK.FilterHeaderType) ([]eaclSDK.Header, bool) {
switch typ {
default:
return nil, true
case eaclSDK.HeaderFromRequest:
return h.requestHeaders, true
case eaclSDK.HeaderFromObject:
return h.objectHeaders, !h.incompleteObjectHeaders
}
}
type xHeader session.XHeader
func (x xHeader) Key() string {
return (*session.XHeader)(&x).GetKey()
}
func (x xHeader) Value() string {
return (*session.XHeader)(&x).GetValue()
}
var errMissingOID = errors.New("object ID is missing")
func (h *cfg) readObjectHeaders(dst *headerSource) error {
switch m := h.msg.(type) {
default:
panic(fmt.Sprintf("unexpected message type %T", h.msg))
case requestXHeaderSource:
return h.readObjectHeadersFromRequestXHeaderSource(m, dst)
case responseXHeaderSource:
return h.readObjectHeadersResponseXHeaderSource(m, dst)
}
}
func (h *cfg) readObjectHeadersFromRequestXHeaderSource(m requestXHeaderSource, dst *headerSource) error {
switch req := m.req.(type) {
case
*objectV2.GetRequest,
*objectV2.HeadRequest:
if h.obj == nil {
return errMissingOID
}
objHeaders, completed := h.localObjectHeaders(h.cnr, h.obj)
dst.objectHeaders = objHeaders
dst.incompleteObjectHeaders = !completed
case
*objectV2.GetRangeRequest,
*objectV2.GetRangeHashRequest,
*objectV2.DeleteRequest:
if h.obj == nil {
return errMissingOID
}
dst.objectHeaders = addressHeaders(h.cnr, h.obj)
case *objectV2.PutRequest:
if v, ok := req.GetBody().GetObjectPart().(*objectV2.PutObjectPartInit); ok {
oV2 := new(objectV2.Object)
oV2.SetObjectID(v.GetObjectID())
oV2.SetHeader(v.GetHeader())
dst.objectHeaders = headersFromObject(objectSDK.NewFromV2(oV2), h.cnr, h.obj)
}
case *objectV2.PutSingleRequest:
dst.objectHeaders = headersFromObject(objectSDK.NewFromV2(req.GetBody().GetObject()), h.cnr, h.obj)
case *objectV2.SearchRequest:
cnrV2 := req.GetBody().GetContainerID()
var cnr cid.ID
if cnrV2 != nil {
if err := cnr.ReadFromV2(*cnrV2); err != nil {
return fmt.Errorf("can't parse container ID: %w", err)
}
}
dst.objectHeaders = []eaclSDK.Header{cidHeader(cnr)}
}
return nil
}
func (h *cfg) readObjectHeadersResponseXHeaderSource(m responseXHeaderSource, dst *headerSource) error {
switch resp := m.resp.(type) {
default:
objectHeaders, completed := h.localObjectHeaders(h.cnr, h.obj)
dst.objectHeaders = objectHeaders
dst.incompleteObjectHeaders = !completed
case *objectV2.GetResponse:
if v, ok := resp.GetBody().GetObjectPart().(*objectV2.GetObjectPartInit); ok {
oV2 := new(objectV2.Object)
oV2.SetObjectID(v.GetObjectID())
oV2.SetHeader(v.GetHeader())
dst.objectHeaders = headersFromObject(objectSDK.NewFromV2(oV2), h.cnr, h.obj)
}
case *objectV2.HeadResponse:
oV2 := new(objectV2.Object)
var hdr *objectV2.Header
switch v := resp.GetBody().GetHeaderPart().(type) {
case *objectV2.ShortHeader:
hdr = new(objectV2.Header)
var idV2 refsV2.ContainerID
h.cnr.WriteToV2(&idV2)
hdr.SetContainerID(&idV2)
hdr.SetVersion(v.GetVersion())
hdr.SetCreationEpoch(v.GetCreationEpoch())
hdr.SetOwnerID(v.GetOwnerID())
hdr.SetObjectType(v.GetObjectType())
hdr.SetPayloadLength(v.GetPayloadLength())
case *objectV2.HeaderWithSignature:
hdr = v.GetHeader()
}
oV2.SetHeader(hdr)
dst.objectHeaders = headersFromObject(objectSDK.NewFromV2(oV2), h.cnr, h.obj)
}
return nil
}
func (h *cfg) localObjectHeaders(cnr cid.ID, idObj *oid.ID) ([]eaclSDK.Header, bool) {
if idObj != nil {
var addr oid.Address
addr.SetContainer(cnr)
addr.SetObject(*idObj)
obj, err := h.storage.Head(context.TODO(), addr)
if err == nil {
return headersFromObject(obj, cnr, idObj), true
}
}
return addressHeaders(cnr, idObj), false
}
func cidHeader(idCnr cid.ID) sysObjHdr {
return sysObjHdr{
k: acl.FilterObjectContainerID,
v: idCnr.EncodeToString(),
}
}
func oidHeader(obj oid.ID) sysObjHdr {
return sysObjHdr{
k: acl.FilterObjectID,
v: obj.EncodeToString(),
}
}
func ownerIDHeader(ownerID user.ID) sysObjHdr {
return sysObjHdr{
k: acl.FilterObjectOwnerID,
v: ownerID.EncodeToString(),
}
}
func addressHeaders(cnr cid.ID, oid *oid.ID) []eaclSDK.Header {
hh := make([]eaclSDK.Header, 0, 2)
hh = append(hh, cidHeader(cnr))
if oid != nil {
hh = append(hh, oidHeader(*oid))
}
return hh
}