forked from TrueCloudLab/frostfs-node
743 lines
16 KiB
Go
743 lines
16 KiB
Go
|
package object
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"context"
|
||
|
"crypto/ecdsa"
|
||
|
"fmt"
|
||
|
"strconv"
|
||
|
|
||
|
"github.com/multiformats/go-multiaddr"
|
||
|
eacl "github.com/nspcc-dev/neofs-api-go/acl/extended"
|
||
|
"github.com/nspcc-dev/neofs-api-go/object"
|
||
|
"github.com/nspcc-dev/neofs-api-go/refs"
|
||
|
"github.com/nspcc-dev/neofs-api-go/service"
|
||
|
crypto "github.com/nspcc-dev/neofs-crypto"
|
||
|
"github.com/nspcc-dev/neofs-node/pkg/core/container"
|
||
|
"github.com/nspcc-dev/neofs-node/pkg/core/container/acl/extended"
|
||
|
eaclstorage "github.com/nspcc-dev/neofs-node/pkg/core/container/acl/extended/storage"
|
||
|
"github.com/nspcc-dev/neofs-node/pkg/core/container/storage"
|
||
|
"github.com/nspcc-dev/neofs-node/pkg/core/netmap"
|
||
|
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/localstore"
|
||
|
eaclcheck "github.com/nspcc-dev/neofs-node/pkg/network/transport/object/grpc/eacl"
|
||
|
"github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transport"
|
||
|
"github.com/pkg/errors"
|
||
|
"go.uber.org/zap"
|
||
|
)
|
||
|
|
||
|
type (
|
||
|
// RequestTargeter is an interface of request's ACL group calculator.
|
||
|
RequestTargeter interface {
|
||
|
Target(context.Context, serviceRequest) requestTarget
|
||
|
}
|
||
|
|
||
|
// aclPreProcessor is an implementation of requestPreProcessor interface.
|
||
|
aclPreProcessor struct {
|
||
|
log *zap.Logger
|
||
|
|
||
|
aclInfoReceiver aclInfoReceiver
|
||
|
|
||
|
reqActionCalc requestActionCalculator
|
||
|
|
||
|
localStore localstore.Localstore
|
||
|
|
||
|
extACLSource eaclstorage.Storage
|
||
|
|
||
|
bearerVerifier bearerTokenVerifier
|
||
|
}
|
||
|
|
||
|
// duplicates NetmapClient method, used for testing.
|
||
|
irKeysReceiver interface {
|
||
|
InnerRingKeys() ([][]byte, error)
|
||
|
}
|
||
|
|
||
|
containerNodesLister interface {
|
||
|
ContainerNodes(ctx context.Context, cid CID) ([]multiaddr.Multiaddr, error)
|
||
|
ContainerNodesInfo(ctx context.Context, cid CID, prev int) ([]netmap.Info, error)
|
||
|
}
|
||
|
|
||
|
targetFinder struct {
|
||
|
log *zap.Logger
|
||
|
|
||
|
irKeysRecv irKeysReceiver
|
||
|
cnrLister containerNodesLister
|
||
|
cnrStorage storage.Storage
|
||
|
}
|
||
|
)
|
||
|
|
||
|
type objectHeadersSource interface {
|
||
|
getHeaders() (*Object, bool)
|
||
|
}
|
||
|
|
||
|
type requestActionCalculator interface {
|
||
|
calculateRequestAction(context.Context, requestActionParams) eacl.Action
|
||
|
}
|
||
|
|
||
|
type aclInfoReceiver struct {
|
||
|
cnrStorage storage.Storage
|
||
|
|
||
|
targetFinder RequestTargeter
|
||
|
}
|
||
|
|
||
|
type aclInfo struct {
|
||
|
rule container.BasicACL
|
||
|
|
||
|
checkExtended bool
|
||
|
|
||
|
checkBearer bool
|
||
|
|
||
|
targetInfo requestTarget
|
||
|
}
|
||
|
|
||
|
type reqActionCalc struct {
|
||
|
storage eaclstorage.Storage
|
||
|
|
||
|
log *zap.Logger
|
||
|
}
|
||
|
|
||
|
type serviceRequestInfo struct {
|
||
|
group eacl.Group
|
||
|
|
||
|
req serviceRequest
|
||
|
|
||
|
objHdrSrc objectHeadersSource
|
||
|
}
|
||
|
|
||
|
type requestObjHdrSrc struct {
|
||
|
req serviceRequest
|
||
|
|
||
|
ls localstore.Localstore
|
||
|
}
|
||
|
|
||
|
type eaclFromBearer struct {
|
||
|
eaclstorage.Storage
|
||
|
|
||
|
bearer service.BearerToken
|
||
|
}
|
||
|
|
||
|
type requestTarget struct {
|
||
|
group eacl.Group
|
||
|
|
||
|
ir bool
|
||
|
}
|
||
|
|
||
|
var _ requestPreProcessor = (*aclPreProcessor)(nil)
|
||
|
|
||
|
var errMissingSignatures = errors.New("empty signature list")
|
||
|
|
||
|
func (p *aclPreProcessor) preProcess(ctx context.Context, req serviceRequest) error {
|
||
|
if req == nil {
|
||
|
panic(pmEmptyServiceRequest)
|
||
|
}
|
||
|
|
||
|
// fetch ACL info
|
||
|
aclInfo, err := p.aclInfoReceiver.getACLInfo(ctx, req)
|
||
|
if err != nil {
|
||
|
p.log.Warn("can't get acl of the container", zap.Stringer("cid", req.CID()))
|
||
|
return errAccessDenied
|
||
|
}
|
||
|
|
||
|
// check basic ACL permissions
|
||
|
var checkFn func(uint8) bool
|
||
|
|
||
|
switch aclInfo.targetInfo.group {
|
||
|
case eacl.GroupUser:
|
||
|
checkFn = aclInfo.rule.UserAllowed
|
||
|
case eacl.GroupSystem:
|
||
|
checkFn = aclInfo.rule.SystemAllowed
|
||
|
case eacl.GroupOthers:
|
||
|
checkFn = aclInfo.rule.OthersAllowed
|
||
|
default:
|
||
|
panic(fmt.Sprintf("unknown request group (aclPreProcessor): %d", aclInfo.targetInfo.group))
|
||
|
}
|
||
|
|
||
|
if requestType := req.Type(); !checkFn(requestACLSection(requestType)) ||
|
||
|
aclInfo.targetInfo.ir && !allowedInnerRingRequest(requestType) {
|
||
|
return errAccessDenied
|
||
|
}
|
||
|
|
||
|
if aclInfo.targetInfo.group != eacl.GroupSystem &&
|
||
|
aclInfo.rule.Sticky() &&
|
||
|
!checkObjectRequestOwnerMatch(req) {
|
||
|
return errAccessDenied
|
||
|
}
|
||
|
|
||
|
if !aclInfo.checkBearer && !aclInfo.checkExtended {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
actionParams := requestActionParams{
|
||
|
eaclSrc: p.extACLSource,
|
||
|
request: req,
|
||
|
objHdrSrc: &requestObjHdrSrc{
|
||
|
req: req,
|
||
|
ls: p.localStore,
|
||
|
},
|
||
|
group: aclInfo.targetInfo.group,
|
||
|
}
|
||
|
|
||
|
if aclInfo.checkBearer {
|
||
|
bearer := req.GetBearerToken()
|
||
|
|
||
|
if err := p.bearerVerifier.verifyBearerToken(ctx, req.CID(), bearer); err != nil {
|
||
|
p.log.Warn("bearer token verification failure",
|
||
|
zap.String("error", err.Error()),
|
||
|
)
|
||
|
|
||
|
return errAccessDenied
|
||
|
}
|
||
|
|
||
|
actionParams.eaclSrc = eaclFromBearer{
|
||
|
bearer: bearer,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if p.reqActionCalc.calculateRequestAction(ctx, actionParams) != eacl.ActionAllow {
|
||
|
return errAccessDenied
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (t *targetFinder) Target(ctx context.Context, req serviceRequest) requestTarget {
|
||
|
res := requestTarget{
|
||
|
group: eacl.GroupUnknown,
|
||
|
}
|
||
|
|
||
|
ownerID, ownerKey, err := requestOwner(req)
|
||
|
if err != nil {
|
||
|
t.log.Warn("could not get request owner",
|
||
|
zap.String("error", err.Error()),
|
||
|
)
|
||
|
|
||
|
return res
|
||
|
} else if ownerKey == nil {
|
||
|
t.log.Warn("signature with nil public key detected")
|
||
|
return res
|
||
|
}
|
||
|
|
||
|
// if request from container owner then return GroupUser
|
||
|
isOwner, err := isContainerOwner(t.cnrStorage, req.CID(), ownerID)
|
||
|
if err != nil {
|
||
|
t.log.Warn("can't check container owner", zap.String("err", err.Error()))
|
||
|
return res
|
||
|
} else if isOwner {
|
||
|
res.group = eacl.GroupUser
|
||
|
return res
|
||
|
}
|
||
|
|
||
|
ownerKeyBytes := crypto.MarshalPublicKey(ownerKey)
|
||
|
|
||
|
// if request from inner ring then return GroupSystem
|
||
|
irKeyList, err := t.irKeysRecv.InnerRingKeys()
|
||
|
if err != nil {
|
||
|
t.log.Warn("could not verify the key belongs to the IR node", zap.String("err", err.Error()))
|
||
|
return res
|
||
|
}
|
||
|
|
||
|
for i := range irKeyList {
|
||
|
if bytes.Equal(irKeyList[i], ownerKeyBytes) {
|
||
|
res.group = eacl.GroupSystem
|
||
|
res.ir = true
|
||
|
return res
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// if request from current container node then return GroupSystem
|
||
|
cnr, err := t.cnrLister.ContainerNodesInfo(ctx, req.CID(), 0)
|
||
|
if err != nil {
|
||
|
t.log.Warn("can't get current container list", zap.String("err", err.Error()))
|
||
|
return res
|
||
|
}
|
||
|
|
||
|
for i := range cnr {
|
||
|
if bytes.Equal(cnr[i].PublicKey(), ownerKeyBytes) {
|
||
|
res.group = eacl.GroupSystem
|
||
|
return res
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// if request from previous container node then return GroupSystem
|
||
|
cnr, err = t.cnrLister.ContainerNodesInfo(ctx, req.CID(), 1)
|
||
|
if err != nil {
|
||
|
t.log.Warn("can't get previous container list", zap.String("err", err.Error()))
|
||
|
return res
|
||
|
}
|
||
|
|
||
|
for i := range cnr {
|
||
|
if bytes.Equal(cnr[i].PublicKey(), ownerKeyBytes) {
|
||
|
res.group = eacl.GroupSystem
|
||
|
return res
|
||
|
}
|
||
|
}
|
||
|
|
||
|
res.group = eacl.GroupOthers
|
||
|
|
||
|
// if none of the above return GroupOthers
|
||
|
return res
|
||
|
}
|
||
|
|
||
|
func checkObjectRequestOwnerMatch(req serviceRequest) bool {
|
||
|
rt := req.Type()
|
||
|
|
||
|
// ignore all request types except Put and Delete
|
||
|
if rt != object.RequestPut && rt != object.RequestDelete {
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
// get request owner
|
||
|
reqOwner, _, err := requestOwner(req)
|
||
|
if err != nil {
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
var payloadOwner OwnerID
|
||
|
|
||
|
// get owner from request payload
|
||
|
if rt == object.RequestPut {
|
||
|
obj := req.(transport.PutInfo).GetHead()
|
||
|
if obj == nil {
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
payloadOwner = obj.GetSystemHeader().OwnerID
|
||
|
} else {
|
||
|
payloadOwner = req.(*object.DeleteRequest).OwnerID
|
||
|
}
|
||
|
|
||
|
return reqOwner.Equal(payloadOwner)
|
||
|
}
|
||
|
|
||
|
// FIXME: this solution only works with healthy key-to-owner conversion.
|
||
|
func requestOwner(req serviceRequest) (OwnerID, *ecdsa.PublicKey, error) {
|
||
|
// if session token exists => return its owner
|
||
|
if token := req.GetSessionToken(); token != nil {
|
||
|
return token.GetOwnerID(), crypto.UnmarshalPublicKey(token.GetOwnerKey()), nil
|
||
|
}
|
||
|
|
||
|
signKeys := req.GetSignKeyPairs()
|
||
|
if len(signKeys) == 0 {
|
||
|
return OwnerID{}, nil, errMissingSignatures
|
||
|
}
|
||
|
|
||
|
firstKey := signKeys[0].GetPublicKey()
|
||
|
if firstKey == nil {
|
||
|
return OwnerID{}, nil, crypto.ErrEmptyPublicKey
|
||
|
}
|
||
|
|
||
|
owner, err := refs.NewOwnerID(firstKey)
|
||
|
|
||
|
return owner, firstKey, err
|
||
|
}
|
||
|
|
||
|
// HeadersOfType returns request or object headers.
|
||
|
func (s serviceRequestInfo) HeadersOfType(typ eacl.HeaderType) ([]eacl.Header, bool) {
|
||
|
switch typ {
|
||
|
default:
|
||
|
return nil, true
|
||
|
case eacl.HdrTypeRequest:
|
||
|
return TypedHeaderSourceFromExtendedHeaders(s.req).HeadersOfType(typ)
|
||
|
case eacl.HdrTypeObjSys, eacl.HdrTypeObjUsr:
|
||
|
obj, ok := s.objHdrSrc.getHeaders()
|
||
|
if !ok {
|
||
|
return nil, false
|
||
|
}
|
||
|
|
||
|
return TypedHeaderSourceFromObject(obj).HeadersOfType(typ)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Key returns a binary representation of sender public key.
|
||
|
func (s serviceRequestInfo) Key() []byte {
|
||
|
_, key, err := requestOwner(s.req)
|
||
|
if err != nil {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
return crypto.MarshalPublicKey(key)
|
||
|
}
|
||
|
|
||
|
// TypeOf returns true of object request type corresponds to passed OperationType.
|
||
|
func (s serviceRequestInfo) OperationType() eacl.OperationType {
|
||
|
switch t := s.req.Type(); t {
|
||
|
case object.RequestGet:
|
||
|
return eacl.OpTypeGet
|
||
|
case object.RequestPut:
|
||
|
return eacl.OpTypePut
|
||
|
case object.RequestHead:
|
||
|
return eacl.OpTypeHead
|
||
|
case object.RequestSearch:
|
||
|
return eacl.OpTypeSearch
|
||
|
case object.RequestDelete:
|
||
|
return eacl.OpTypeDelete
|
||
|
case object.RequestRange:
|
||
|
return eacl.OpTypeRange
|
||
|
case object.RequestRangeHash:
|
||
|
return eacl.OpTypeRangeHash
|
||
|
default:
|
||
|
panic(fmt.Sprintf("unknown request type (serviceRequestInfo): %d", t))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Group returns the access group of the request.
|
||
|
func (s serviceRequestInfo) Group() eacl.Group {
|
||
|
return s.group
|
||
|
}
|
||
|
|
||
|
// CID returns the container identifier of request.
|
||
|
func (s serviceRequestInfo) CID() CID {
|
||
|
return s.req.CID()
|
||
|
}
|
||
|
|
||
|
func (s requestObjHdrSrc) getHeaders() (*Object, bool) {
|
||
|
switch s.req.Type() {
|
||
|
case object.RequestSearch:
|
||
|
// object header filters is not supported in Search request now
|
||
|
return nil, true
|
||
|
case object.RequestPut:
|
||
|
// for Put we get object headers from request
|
||
|
return s.req.(transport.PutInfo).GetHead(), true
|
||
|
default:
|
||
|
tReq := &transportRequest{
|
||
|
serviceRequest: s.req,
|
||
|
}
|
||
|
|
||
|
// for other requests we get object headers from local storage
|
||
|
m, err := s.ls.Meta(tReq.GetAddress())
|
||
|
if err == nil {
|
||
|
return m.GetObject(), true
|
||
|
}
|
||
|
|
||
|
return nil, false
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type requestActionParams struct {
|
||
|
eaclSrc eaclstorage.Storage
|
||
|
|
||
|
request serviceRequest
|
||
|
|
||
|
objHdrSrc objectHeadersSource
|
||
|
|
||
|
group eacl.Group
|
||
|
}
|
||
|
|
||
|
func (s reqActionCalc) calculateRequestAction(ctx context.Context, p requestActionParams) eacl.Action {
|
||
|
// build eACL validator
|
||
|
validator, err := eaclcheck.NewValidator(p.eaclSrc, s.log)
|
||
|
if err != nil {
|
||
|
s.log.Warn("could not build eacl acl validator",
|
||
|
zap.String("error", err.Error()),
|
||
|
)
|
||
|
|
||
|
return eacl.ActionUnknown
|
||
|
}
|
||
|
|
||
|
// create RequestInfo instance
|
||
|
reqInfo := &serviceRequestInfo{
|
||
|
group: p.group,
|
||
|
req: p.request,
|
||
|
objHdrSrc: p.objHdrSrc,
|
||
|
}
|
||
|
|
||
|
// calculate ACL action
|
||
|
return validator.CalculateAction(reqInfo)
|
||
|
}
|
||
|
|
||
|
func (s aclInfoReceiver) getACLInfo(ctx context.Context, req serviceRequest) (*aclInfo, error) {
|
||
|
cnr, err := s.cnrStorage.Get(req.CID())
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
rule := cnr.BasicACL()
|
||
|
|
||
|
isBearer := rule.BearerAllowed(requestACLSection(req.Type()))
|
||
|
|
||
|
// fetch group from the request
|
||
|
t := s.targetFinder.Target(ctx, req)
|
||
|
|
||
|
return &aclInfo{
|
||
|
rule: rule,
|
||
|
|
||
|
checkExtended: !rule.Final(),
|
||
|
|
||
|
targetInfo: t,
|
||
|
|
||
|
checkBearer: t.group != eacl.GroupSystem && isBearer && req.GetBearerToken() != nil,
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
func (s eaclFromBearer) GetEACL(cid CID) (eaclstorage.Table, error) {
|
||
|
return eacl.UnmarshalTable(s.bearer.GetACLRules())
|
||
|
}
|
||
|
|
||
|
// returns true if request of type argument is allowed for IR needs (audit).
|
||
|
func allowedInnerRingRequest(t object.RequestType) (res bool) {
|
||
|
switch t {
|
||
|
case
|
||
|
object.RequestSearch,
|
||
|
object.RequestHead,
|
||
|
object.RequestRangeHash:
|
||
|
res = true
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// returns the index number of request section bits.
|
||
|
func requestACLSection(t object.RequestType) uint8 {
|
||
|
switch t {
|
||
|
case object.RequestRangeHash:
|
||
|
return 0
|
||
|
case object.RequestRange:
|
||
|
return 1
|
||
|
case object.RequestSearch:
|
||
|
return 2
|
||
|
case object.RequestDelete:
|
||
|
return 3
|
||
|
case object.RequestPut:
|
||
|
return 4
|
||
|
case object.RequestHead:
|
||
|
return 5
|
||
|
case object.RequestGet:
|
||
|
return 6
|
||
|
default:
|
||
|
panic(fmt.Sprintf("unknown request type (requestACLSection): %d", t))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type objectHeaderSource struct {
|
||
|
obj *Object
|
||
|
}
|
||
|
|
||
|
type typedHeader struct {
|
||
|
n string
|
||
|
v string
|
||
|
t eacl.HeaderType
|
||
|
}
|
||
|
|
||
|
type extendedHeadersWrapper struct {
|
||
|
hdrSrc service.ExtendedHeadersSource
|
||
|
}
|
||
|
|
||
|
type typedExtendedHeader struct {
|
||
|
hdr service.ExtendedHeader
|
||
|
}
|
||
|
|
||
|
func newTypedObjSysHdr(name, value string) eacl.TypedHeader {
|
||
|
return &typedHeader{
|
||
|
n: name,
|
||
|
v: value,
|
||
|
t: eacl.HdrTypeObjSys,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Name is a name field getter.
|
||
|
func (s typedHeader) Name() string {
|
||
|
return s.n
|
||
|
}
|
||
|
|
||
|
// Value is a value field getter.
|
||
|
func (s typedHeader) Value() string {
|
||
|
return s.v
|
||
|
}
|
||
|
|
||
|
// HeaderType is a type field getter.
|
||
|
func (s typedHeader) HeaderType() eacl.HeaderType {
|
||
|
return s.t
|
||
|
}
|
||
|
|
||
|
// TypedHeaderSourceFromObject wraps passed object and returns TypedHeaderSource interface.
|
||
|
func TypedHeaderSourceFromObject(obj *object.Object) extended.TypedHeaderSource {
|
||
|
return &objectHeaderSource{
|
||
|
obj: obj,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// HeaderOfType gathers object headers of passed type and returns Header list.
|
||
|
//
|
||
|
// If value of some header can not be calculated (e.g. nil eacl header), it does not appear in list.
|
||
|
//
|
||
|
// Always returns true.
|
||
|
func (s objectHeaderSource) HeadersOfType(typ eacl.HeaderType) ([]eacl.Header, bool) {
|
||
|
if s.obj == nil {
|
||
|
return nil, true
|
||
|
}
|
||
|
|
||
|
var res []eacl.Header
|
||
|
|
||
|
switch typ {
|
||
|
case eacl.HdrTypeObjUsr:
|
||
|
objHeaders := s.obj.GetHeaders()
|
||
|
|
||
|
res = make([]eacl.Header, 0, len(objHeaders)) // 7 system header fields
|
||
|
|
||
|
for i := range objHeaders {
|
||
|
if h := newTypedObjectExtendedHeader(objHeaders[i]); h != nil {
|
||
|
res = append(res, h)
|
||
|
}
|
||
|
}
|
||
|
case eacl.HdrTypeObjSys:
|
||
|
res = make([]eacl.Header, 0, 7)
|
||
|
|
||
|
sysHdr := s.obj.GetSystemHeader()
|
||
|
|
||
|
created := sysHdr.GetCreatedAt()
|
||
|
|
||
|
res = append(res,
|
||
|
// ID
|
||
|
newTypedObjSysHdr(
|
||
|
eacl.HdrObjSysNameID,
|
||
|
sysHdr.ID.String(),
|
||
|
),
|
||
|
|
||
|
// CID
|
||
|
newTypedObjSysHdr(
|
||
|
eacl.HdrObjSysNameCID,
|
||
|
sysHdr.CID.String(),
|
||
|
),
|
||
|
|
||
|
// OwnerID
|
||
|
newTypedObjSysHdr(
|
||
|
eacl.HdrObjSysNameOwnerID,
|
||
|
sysHdr.OwnerID.String(),
|
||
|
),
|
||
|
|
||
|
// Version
|
||
|
newTypedObjSysHdr(
|
||
|
eacl.HdrObjSysNameVersion,
|
||
|
strconv.FormatUint(sysHdr.GetVersion(), 10),
|
||
|
),
|
||
|
|
||
|
// PayloadLength
|
||
|
newTypedObjSysHdr(
|
||
|
eacl.HdrObjSysNamePayloadLength,
|
||
|
strconv.FormatUint(sysHdr.GetPayloadLength(), 10),
|
||
|
),
|
||
|
|
||
|
// CreatedAt.UnitTime
|
||
|
newTypedObjSysHdr(
|
||
|
eacl.HdrObjSysNameCreatedUnix,
|
||
|
strconv.FormatUint(uint64(created.GetUnixTime()), 10),
|
||
|
),
|
||
|
|
||
|
// CreatedAt.Epoch
|
||
|
newTypedObjSysHdr(
|
||
|
eacl.HdrObjSysNameCreatedEpoch,
|
||
|
strconv.FormatUint(created.GetEpoch(), 10),
|
||
|
),
|
||
|
)
|
||
|
}
|
||
|
|
||
|
return res, true
|
||
|
}
|
||
|
|
||
|
func newTypedObjectExtendedHeader(h object.Header) eacl.TypedHeader {
|
||
|
val := h.GetValue()
|
||
|
if val == nil {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
res := new(typedHeader)
|
||
|
res.t = eacl.HdrTypeObjSys
|
||
|
|
||
|
switch hdr := val.(type) {
|
||
|
case *object.Header_UserHeader:
|
||
|
if hdr.UserHeader == nil {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
res.t = eacl.HdrTypeObjUsr
|
||
|
res.n = hdr.UserHeader.GetKey()
|
||
|
res.v = hdr.UserHeader.GetValue()
|
||
|
case *object.Header_Link:
|
||
|
if hdr.Link == nil {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
switch hdr.Link.GetType() {
|
||
|
case object.Link_Previous:
|
||
|
res.n = eacl.HdrObjSysLinkPrev
|
||
|
case object.Link_Next:
|
||
|
res.n = eacl.HdrObjSysLinkNext
|
||
|
case object.Link_Child:
|
||
|
res.n = eacl.HdrObjSysLinkChild
|
||
|
case object.Link_Parent:
|
||
|
res.n = eacl.HdrObjSysLinkPar
|
||
|
case object.Link_StorageGroup:
|
||
|
res.n = eacl.HdrObjSysLinkSG
|
||
|
default:
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
res.v = hdr.Link.ID.String()
|
||
|
default:
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
return res
|
||
|
}
|
||
|
|
||
|
// TypedHeaderSourceFromExtendedHeaders wraps passed ExtendedHeadersSource and returns TypedHeaderSource interface.
|
||
|
func TypedHeaderSourceFromExtendedHeaders(hdrSrc service.ExtendedHeadersSource) extended.TypedHeaderSource {
|
||
|
return &extendedHeadersWrapper{
|
||
|
hdrSrc: hdrSrc,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Name returns the result of Key method.
|
||
|
func (s typedExtendedHeader) Name() string {
|
||
|
return s.hdr.Key()
|
||
|
}
|
||
|
|
||
|
// Value returns the result of Value method.
|
||
|
func (s typedExtendedHeader) Value() string {
|
||
|
return s.hdr.Value()
|
||
|
}
|
||
|
|
||
|
// HeaderType always returns HdrTypeRequest.
|
||
|
func (s typedExtendedHeader) HeaderType() eacl.HeaderType {
|
||
|
return eacl.HdrTypeRequest
|
||
|
}
|
||
|
|
||
|
// TypedHeaders gathers eacl request headers and returns TypedHeader list.
|
||
|
//
|
||
|
// Nil headers are ignored.
|
||
|
//
|
||
|
// Always returns true.
|
||
|
func (s extendedHeadersWrapper) HeadersOfType(typ eacl.HeaderType) ([]eacl.Header, bool) {
|
||
|
if s.hdrSrc == nil {
|
||
|
return nil, true
|
||
|
}
|
||
|
|
||
|
var res []eacl.Header
|
||
|
|
||
|
if typ == eacl.HdrTypeRequest {
|
||
|
hs := s.hdrSrc.ExtendedHeaders()
|
||
|
|
||
|
res = make([]eacl.Header, 0, len(hs))
|
||
|
|
||
|
for i := range hs {
|
||
|
if hs[i] == nil {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
res = append(res, &typedExtendedHeader{
|
||
|
hdr: hs[i],
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return res, true
|
||
|
}
|
||
|
|
||
|
func isContainerOwner(storage storage.Storage, cid CID, ownerID OwnerID) (bool, error) {
|
||
|
cnr, err := storage.Get(cid)
|
||
|
if err != nil {
|
||
|
return false, err
|
||
|
}
|
||
|
|
||
|
return cnr.OwnerID().Equal(ownerID), nil
|
||
|
}
|