2020-09-21 09:51:15 +00:00
|
|
|
package acl
|
|
|
|
|
|
|
|
import (
|
2020-09-22 16:18:41 +00:00
|
|
|
"bytes"
|
2020-09-21 09:51:15 +00:00
|
|
|
"context"
|
2020-10-03 07:39:02 +00:00
|
|
|
"fmt"
|
2020-09-21 09:51:15 +00:00
|
|
|
|
2020-09-21 16:30:43 +00:00
|
|
|
acl "github.com/nspcc-dev/neofs-api-go/pkg/acl/eacl"
|
2020-09-28 10:54:24 +00:00
|
|
|
"github.com/nspcc-dev/neofs-api-go/pkg/container"
|
|
|
|
"github.com/nspcc-dev/neofs-api-go/pkg/owner"
|
2020-10-21 13:11:18 +00:00
|
|
|
"github.com/nspcc-dev/neofs-api-go/util/signature"
|
|
|
|
bearer "github.com/nspcc-dev/neofs-api-go/v2/acl"
|
2020-09-21 09:51:15 +00:00
|
|
|
"github.com/nspcc-dev/neofs-api-go/v2/object"
|
2020-10-02 11:39:32 +00:00
|
|
|
"github.com/nspcc-dev/neofs-api-go/v2/session"
|
2020-10-21 13:11:18 +00:00
|
|
|
v2signature "github.com/nspcc-dev/neofs-api-go/v2/signature"
|
|
|
|
crypto "github.com/nspcc-dev/neofs-crypto"
|
2020-09-28 10:54:24 +00:00
|
|
|
core "github.com/nspcc-dev/neofs-node/pkg/core/container"
|
2020-10-03 07:46:57 +00:00
|
|
|
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/localstore"
|
|
|
|
"github.com/nspcc-dev/neofs-node/pkg/services/object/acl/eacl"
|
2020-10-03 07:50:41 +00:00
|
|
|
eaclV2 "github.com/nspcc-dev/neofs-node/pkg/services/object/acl/eacl/v2"
|
2020-09-21 13:33:49 +00:00
|
|
|
"github.com/pkg/errors"
|
2020-09-21 09:51:15 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type (
|
2020-10-03 05:09:58 +00:00
|
|
|
// Service checks basic ACL rules.
|
|
|
|
Service struct {
|
2020-10-03 05:19:22 +00:00
|
|
|
*cfg
|
2020-09-21 09:51:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
putStreamBasicChecker struct {
|
2020-10-03 05:09:58 +00:00
|
|
|
source *Service
|
2020-09-21 13:33:49 +00:00
|
|
|
next object.PutObjectStreamer
|
2020-10-03 07:50:41 +00:00
|
|
|
|
|
|
|
*eACLCfg
|
2020-09-21 09:51:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
getStreamBasicChecker struct {
|
2020-09-22 16:18:41 +00:00
|
|
|
next object.GetObjectStreamer
|
|
|
|
info requestInfo
|
2020-10-03 07:50:41 +00:00
|
|
|
|
|
|
|
*eACLCfg
|
2020-09-21 09:51:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
searchStreamBasicChecker struct {
|
2020-09-21 16:12:52 +00:00
|
|
|
object.SearchObjectStreamer
|
2020-09-21 09:51:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
getRangeStreamBasicChecker struct {
|
2020-09-21 16:12:52 +00:00
|
|
|
object.GetRangeObjectStreamer
|
|
|
|
}
|
|
|
|
|
|
|
|
requestInfo struct {
|
2020-10-03 05:23:54 +00:00
|
|
|
basicACL basicACLHelper
|
2020-09-21 16:12:52 +00:00
|
|
|
requestRole acl.Role
|
|
|
|
operation acl.Operation // put, get, head, etc.
|
2020-09-28 10:54:24 +00:00
|
|
|
owner *owner.ID // container owner
|
2020-10-03 07:40:50 +00:00
|
|
|
|
|
|
|
cid *container.ID
|
|
|
|
|
|
|
|
senderKey []byte
|
2020-10-21 13:11:18 +00:00
|
|
|
|
|
|
|
bearer *bearer.BearerToken // bearer token of request
|
2020-09-21 09:51:15 +00:00
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2020-10-03 05:19:22 +00:00
|
|
|
// Option represents Service constructor option.
|
|
|
|
type Option func(*cfg)
|
|
|
|
|
|
|
|
type cfg struct {
|
|
|
|
containers core.Source
|
|
|
|
|
|
|
|
sender SenderClassifier
|
|
|
|
|
|
|
|
next object.Service
|
2020-10-03 07:46:57 +00:00
|
|
|
|
|
|
|
*eACLCfg
|
|
|
|
}
|
|
|
|
|
|
|
|
type eACLCfg struct {
|
|
|
|
eACLOpts []eacl.Option
|
|
|
|
|
|
|
|
eACL *eacl.Validator
|
|
|
|
|
|
|
|
localStorage *localstore.Storage
|
2020-10-03 05:19:22 +00:00
|
|
|
}
|
|
|
|
|
2020-10-03 07:39:02 +00:00
|
|
|
type accessErr struct {
|
|
|
|
requestInfo
|
|
|
|
|
|
|
|
failedCheckTyp string
|
|
|
|
}
|
|
|
|
|
2020-09-21 13:33:49 +00:00
|
|
|
var (
|
2020-10-03 07:39:02 +00:00
|
|
|
ErrMalformedRequest = errors.New("malformed request")
|
2020-10-21 17:39:14 +00:00
|
|
|
ErrInternal = errors.New("internal error")
|
2020-10-03 07:39:02 +00:00
|
|
|
ErrUnknownRole = errors.New("can't classify request sender")
|
|
|
|
ErrUnknownContainer = errors.New("can't fetch container info")
|
2020-09-21 13:33:49 +00:00
|
|
|
)
|
|
|
|
|
2020-10-03 05:19:22 +00:00
|
|
|
func defaultCfg() *cfg {
|
2020-10-03 07:46:57 +00:00
|
|
|
return &cfg{
|
|
|
|
eACLCfg: new(eACLCfg),
|
|
|
|
}
|
2020-10-03 05:19:22 +00:00
|
|
|
}
|
|
|
|
|
2020-10-03 05:09:58 +00:00
|
|
|
// New is a constructor for object ACL checking service.
|
2020-10-03 05:19:22 +00:00
|
|
|
func New(opts ...Option) Service {
|
|
|
|
cfg := defaultCfg()
|
|
|
|
|
|
|
|
for i := range opts {
|
|
|
|
opts[i](cfg)
|
|
|
|
}
|
2020-09-21 16:12:52 +00:00
|
|
|
|
2020-10-03 07:46:57 +00:00
|
|
|
cfg.eACL = eacl.NewValidator(cfg.eACLOpts...)
|
|
|
|
|
2020-10-03 05:09:58 +00:00
|
|
|
return Service{
|
2020-10-03 05:19:22 +00:00
|
|
|
cfg: cfg,
|
2020-09-21 09:51:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-03 05:09:58 +00:00
|
|
|
func (b Service) Get(
|
2020-09-21 09:51:15 +00:00
|
|
|
ctx context.Context,
|
|
|
|
request *object.GetRequest) (object.GetObjectStreamer, error) {
|
2020-09-21 16:12:52 +00:00
|
|
|
cid, err := getContainerIDFromRequest(request)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2020-09-21 13:33:49 +00:00
|
|
|
}
|
|
|
|
|
2020-10-02 11:39:32 +00:00
|
|
|
req := metaWithToken{
|
|
|
|
vheader: request.GetVerificationHeader(),
|
|
|
|
token: request.GetMetaHeader().GetSessionToken(),
|
2020-10-21 13:11:18 +00:00
|
|
|
bearer: request.GetMetaHeader().GetBearerToken(),
|
2020-10-02 11:39:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
reqInfo, err := b.findRequestInfo(req, cid, acl.OperationGet)
|
2020-09-21 16:12:52 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if !basicACLCheck(reqInfo) {
|
2020-10-03 07:39:02 +00:00
|
|
|
return nil, basicACLErr(reqInfo)
|
2020-10-03 07:50:41 +00:00
|
|
|
} else if !eACLCheck(request, reqInfo, b.eACLCfg) {
|
|
|
|
return nil, eACLErr(reqInfo)
|
2020-09-21 13:33:49 +00:00
|
|
|
}
|
|
|
|
|
2020-09-21 09:51:15 +00:00
|
|
|
stream, err := b.next.Get(ctx, request)
|
2020-09-22 16:18:41 +00:00
|
|
|
|
|
|
|
return getStreamBasicChecker{
|
2020-10-03 07:50:41 +00:00
|
|
|
next: stream,
|
|
|
|
info: reqInfo,
|
|
|
|
eACLCfg: b.eACLCfg,
|
2020-09-22 16:18:41 +00:00
|
|
|
}, err
|
2020-09-21 09:51:15 +00:00
|
|
|
}
|
|
|
|
|
2020-10-03 05:09:58 +00:00
|
|
|
func (b Service) Put(ctx context.Context) (object.PutObjectStreamer, error) {
|
2020-09-21 09:51:15 +00:00
|
|
|
streamer, err := b.next.Put(ctx)
|
|
|
|
|
|
|
|
return putStreamBasicChecker{
|
2020-10-03 07:50:41 +00:00
|
|
|
source: &b,
|
|
|
|
next: streamer,
|
|
|
|
eACLCfg: b.eACLCfg,
|
2020-09-21 09:51:15 +00:00
|
|
|
}, err
|
|
|
|
}
|
|
|
|
|
2020-10-03 05:09:58 +00:00
|
|
|
func (b Service) Head(
|
2020-09-21 09:51:15 +00:00
|
|
|
ctx context.Context,
|
|
|
|
request *object.HeadRequest) (*object.HeadResponse, error) {
|
|
|
|
|
2020-09-21 16:12:52 +00:00
|
|
|
cid, err := getContainerIDFromRequest(request)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-10-02 11:39:32 +00:00
|
|
|
req := metaWithToken{
|
|
|
|
vheader: request.GetVerificationHeader(),
|
|
|
|
token: request.GetMetaHeader().GetSessionToken(),
|
2020-10-21 13:11:18 +00:00
|
|
|
bearer: request.GetMetaHeader().GetBearerToken(),
|
2020-10-02 11:39:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
reqInfo, err := b.findRequestInfo(req, cid, acl.OperationHead)
|
2020-09-21 16:12:52 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if !basicACLCheck(reqInfo) {
|
2020-10-03 07:39:02 +00:00
|
|
|
return nil, basicACLErr(reqInfo)
|
2020-10-03 07:50:41 +00:00
|
|
|
} else if !eACLCheck(request, reqInfo, b.eACLCfg) {
|
|
|
|
return nil, eACLErr(reqInfo)
|
|
|
|
}
|
|
|
|
|
|
|
|
resp, err := b.next.Head(ctx, request)
|
|
|
|
if err == nil {
|
|
|
|
if !eACLCheck(resp, reqInfo, b.eACLCfg) {
|
|
|
|
err = eACLErr(reqInfo)
|
|
|
|
}
|
2020-09-21 16:12:52 +00:00
|
|
|
}
|
|
|
|
|
2020-10-03 07:50:41 +00:00
|
|
|
return resp, err
|
2020-09-21 09:51:15 +00:00
|
|
|
}
|
|
|
|
|
2020-10-03 05:09:58 +00:00
|
|
|
func (b Service) Search(
|
2020-09-21 09:51:15 +00:00
|
|
|
ctx context.Context,
|
|
|
|
request *object.SearchRequest) (object.SearchObjectStreamer, error) {
|
|
|
|
|
2020-09-28 10:54:24 +00:00
|
|
|
var cid *container.ID
|
2020-09-21 16:12:52 +00:00
|
|
|
|
|
|
|
cid, err := getContainerIDFromRequest(request)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-10-02 11:39:32 +00:00
|
|
|
req := metaWithToken{
|
|
|
|
vheader: request.GetVerificationHeader(),
|
|
|
|
token: request.GetMetaHeader().GetSessionToken(),
|
2020-10-21 13:11:18 +00:00
|
|
|
bearer: request.GetMetaHeader().GetBearerToken(),
|
2020-10-02 11:39:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
reqInfo, err := b.findRequestInfo(req, cid, acl.OperationSearch)
|
2020-09-21 16:12:52 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if !basicACLCheck(reqInfo) {
|
2020-10-03 07:39:02 +00:00
|
|
|
return nil, basicACLErr(reqInfo)
|
2020-10-03 07:50:41 +00:00
|
|
|
} else if !eACLCheck(request, reqInfo, b.eACLCfg) {
|
|
|
|
return nil, eACLErr(reqInfo)
|
2020-09-21 16:12:52 +00:00
|
|
|
}
|
|
|
|
|
2020-09-21 09:51:15 +00:00
|
|
|
stream, err := b.next.Search(ctx, request)
|
2020-09-21 16:12:52 +00:00
|
|
|
return searchStreamBasicChecker{stream}, err
|
2020-09-21 09:51:15 +00:00
|
|
|
}
|
|
|
|
|
2020-10-03 05:09:58 +00:00
|
|
|
func (b Service) Delete(
|
2020-09-21 09:51:15 +00:00
|
|
|
ctx context.Context,
|
|
|
|
request *object.DeleteRequest) (*object.DeleteResponse, error) {
|
|
|
|
|
2020-09-21 16:12:52 +00:00
|
|
|
cid, err := getContainerIDFromRequest(request)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-10-02 11:39:32 +00:00
|
|
|
req := metaWithToken{
|
|
|
|
vheader: request.GetVerificationHeader(),
|
|
|
|
token: request.GetMetaHeader().GetSessionToken(),
|
2020-10-21 13:11:18 +00:00
|
|
|
bearer: request.GetMetaHeader().GetBearerToken(),
|
2020-10-02 11:39:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
reqInfo, err := b.findRequestInfo(req, cid, acl.OperationDelete)
|
2020-09-21 16:12:52 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if !basicACLCheck(reqInfo) {
|
2020-10-03 07:39:02 +00:00
|
|
|
return nil, basicACLErr(reqInfo)
|
2020-10-03 07:50:41 +00:00
|
|
|
} else if !eACLCheck(request, reqInfo, b.eACLCfg) {
|
|
|
|
return nil, eACLErr(reqInfo)
|
2020-09-21 16:12:52 +00:00
|
|
|
}
|
|
|
|
|
2020-09-21 09:51:15 +00:00
|
|
|
return b.next.Delete(ctx, request)
|
|
|
|
}
|
|
|
|
|
2020-10-03 05:09:58 +00:00
|
|
|
func (b Service) GetRange(
|
2020-09-21 09:51:15 +00:00
|
|
|
ctx context.Context,
|
|
|
|
request *object.GetRangeRequest) (object.GetRangeObjectStreamer, error) {
|
|
|
|
|
2020-09-21 16:12:52 +00:00
|
|
|
cid, err := getContainerIDFromRequest(request)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-10-02 11:39:32 +00:00
|
|
|
req := metaWithToken{
|
|
|
|
vheader: request.GetVerificationHeader(),
|
|
|
|
token: request.GetMetaHeader().GetSessionToken(),
|
2020-10-21 13:11:18 +00:00
|
|
|
bearer: request.GetMetaHeader().GetBearerToken(),
|
2020-10-02 11:39:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
reqInfo, err := b.findRequestInfo(req, cid, acl.OperationRange)
|
2020-09-21 16:12:52 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if !basicACLCheck(reqInfo) {
|
2020-10-03 07:39:02 +00:00
|
|
|
return nil, basicACLErr(reqInfo)
|
2020-10-03 07:50:41 +00:00
|
|
|
} else if !eACLCheck(request, reqInfo, b.eACLCfg) {
|
|
|
|
return nil, eACLErr(reqInfo)
|
2020-09-21 16:12:52 +00:00
|
|
|
}
|
|
|
|
|
2020-09-21 09:51:15 +00:00
|
|
|
stream, err := b.next.GetRange(ctx, request)
|
2020-09-21 16:12:52 +00:00
|
|
|
return getRangeStreamBasicChecker{stream}, err
|
2020-09-21 09:51:15 +00:00
|
|
|
}
|
|
|
|
|
2020-10-03 05:09:58 +00:00
|
|
|
func (b Service) GetRangeHash(
|
2020-09-21 09:51:15 +00:00
|
|
|
ctx context.Context,
|
|
|
|
request *object.GetRangeHashRequest) (*object.GetRangeHashResponse, error) {
|
|
|
|
|
2020-09-21 16:12:52 +00:00
|
|
|
cid, err := getContainerIDFromRequest(request)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-10-02 11:39:32 +00:00
|
|
|
req := metaWithToken{
|
|
|
|
vheader: request.GetVerificationHeader(),
|
|
|
|
token: request.GetMetaHeader().GetSessionToken(),
|
2020-10-21 13:11:18 +00:00
|
|
|
bearer: request.GetMetaHeader().GetBearerToken(),
|
2020-10-02 11:39:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
reqInfo, err := b.findRequestInfo(req, cid, acl.OperationRangeHash)
|
2020-09-21 16:12:52 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if !basicACLCheck(reqInfo) {
|
2020-10-03 07:39:02 +00:00
|
|
|
return nil, basicACLErr(reqInfo)
|
2020-10-03 07:50:41 +00:00
|
|
|
} else if !eACLCheck(request, reqInfo, b.eACLCfg) {
|
|
|
|
return nil, eACLErr(reqInfo)
|
2020-09-21 16:12:52 +00:00
|
|
|
}
|
|
|
|
|
2020-09-21 09:51:15 +00:00
|
|
|
return b.next.GetRangeHash(ctx, request)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p putStreamBasicChecker) Send(request *object.PutRequest) error {
|
2020-09-21 16:12:52 +00:00
|
|
|
body := request.GetBody()
|
|
|
|
if body == nil {
|
|
|
|
return ErrMalformedRequest
|
|
|
|
}
|
|
|
|
|
|
|
|
part := body.GetObjectPart()
|
2020-10-02 11:39:32 +00:00
|
|
|
if part, ok := part.(*object.PutObjectPartInit); ok {
|
2020-09-21 16:12:52 +00:00
|
|
|
cid, err := getContainerIDFromRequest(request)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-10-02 11:39:32 +00:00
|
|
|
ownerID, err := getObjectOwnerFromMessage(request)
|
2020-09-22 16:18:41 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-10-02 11:39:32 +00:00
|
|
|
req := metaWithToken{
|
|
|
|
vheader: request.GetVerificationHeader(),
|
|
|
|
token: part.GetHeader().GetSessionToken(),
|
2020-10-21 13:11:18 +00:00
|
|
|
bearer: request.GetMetaHeader().GetBearerToken(),
|
2020-10-02 11:39:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
reqInfo, err := p.source.findRequestInfo(req, cid, acl.OperationPut)
|
2020-09-21 16:12:52 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-10-02 11:39:32 +00:00
|
|
|
if !basicACLCheck(reqInfo) || !stickyBitCheck(reqInfo, ownerID) {
|
2020-10-03 07:39:02 +00:00
|
|
|
return basicACLErr(reqInfo)
|
2020-10-03 07:50:41 +00:00
|
|
|
} else if !eACLCheck(request, reqInfo, p.eACLCfg) {
|
|
|
|
return eACLErr(reqInfo)
|
2020-09-21 16:12:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-21 09:51:15 +00:00
|
|
|
return p.next.Send(request)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p putStreamBasicChecker) CloseAndRecv() (*object.PutResponse, error) {
|
|
|
|
return p.next.CloseAndRecv()
|
|
|
|
}
|
|
|
|
|
2020-09-22 16:18:41 +00:00
|
|
|
func (g getStreamBasicChecker) Recv() (*object.GetResponse, error) {
|
|
|
|
resp, err := g.next.Recv()
|
|
|
|
if err != nil {
|
|
|
|
return resp, err
|
|
|
|
}
|
|
|
|
|
|
|
|
body := resp.GetBody()
|
|
|
|
if body == nil {
|
|
|
|
return resp, err
|
|
|
|
}
|
|
|
|
|
|
|
|
part := body.GetObjectPart()
|
|
|
|
if _, ok := part.(*object.GetObjectPartInit); ok {
|
2020-09-28 10:54:24 +00:00
|
|
|
ownerID, err := getObjectOwnerFromMessage(resp)
|
2020-09-22 16:18:41 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-09-28 10:54:24 +00:00
|
|
|
if !stickyBitCheck(g.info, ownerID) {
|
2020-10-03 07:39:02 +00:00
|
|
|
return nil, basicACLErr(g.info)
|
2020-10-03 07:50:41 +00:00
|
|
|
} else if !eACLCheck(resp, g.info, g.eACLCfg) {
|
|
|
|
return nil, eACLErr(g.info)
|
2020-09-22 16:18:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return resp, err
|
|
|
|
}
|
|
|
|
|
2020-10-03 05:09:58 +00:00
|
|
|
func (b Service) findRequestInfo(
|
2020-10-02 11:39:32 +00:00
|
|
|
req metaWithToken,
|
2020-09-28 10:54:24 +00:00
|
|
|
cid *container.ID,
|
2020-09-21 16:12:52 +00:00
|
|
|
op acl.Operation) (info requestInfo, err error) {
|
|
|
|
|
|
|
|
// fetch actual container
|
2020-09-28 10:54:24 +00:00
|
|
|
cnr, err := b.containers.Get(cid)
|
2020-09-21 16:12:52 +00:00
|
|
|
if err != nil || cnr.GetOwnerID() == nil {
|
|
|
|
return info, ErrUnknownContainer
|
|
|
|
}
|
|
|
|
|
2020-10-05 14:13:23 +00:00
|
|
|
// find request role and key
|
2020-10-21 17:39:14 +00:00
|
|
|
role, key, err := b.sender.Classify(req, cid, cnr)
|
|
|
|
if err != nil {
|
|
|
|
return info, err
|
|
|
|
}
|
|
|
|
|
2020-09-21 16:12:52 +00:00
|
|
|
if role == acl.RoleUnknown {
|
|
|
|
return info, ErrUnknownRole
|
|
|
|
}
|
|
|
|
|
2020-10-02 11:40:09 +00:00
|
|
|
// find verb from token if it is present
|
|
|
|
verb := sourceVerbOfRequest(req, op)
|
|
|
|
// todo: check verb sanity, if it was generated correctly. Do we need it ?
|
|
|
|
|
2020-10-03 05:23:54 +00:00
|
|
|
info.basicACL = basicACLHelper(cnr.GetBasicACL())
|
2020-09-21 16:12:52 +00:00
|
|
|
info.requestRole = role
|
2020-10-02 11:40:09 +00:00
|
|
|
info.operation = verb
|
2020-09-28 10:54:24 +00:00
|
|
|
info.owner = owner.NewIDFromV2(cnr.GetOwnerID())
|
2020-10-03 07:40:50 +00:00
|
|
|
info.cid = cid
|
|
|
|
|
|
|
|
// it is assumed that at the moment the key will be valid,
|
|
|
|
// otherwise the request would not pass validation
|
2020-10-05 14:13:23 +00:00
|
|
|
info.senderKey = key
|
2020-10-03 07:40:50 +00:00
|
|
|
|
2020-10-21 13:11:18 +00:00
|
|
|
// add bearer token if it is present in request
|
|
|
|
info.bearer = req.bearer
|
|
|
|
|
2020-09-21 16:12:52 +00:00
|
|
|
return info, nil
|
2020-09-21 09:51:15 +00:00
|
|
|
}
|
|
|
|
|
2020-09-28 10:54:24 +00:00
|
|
|
func getContainerIDFromRequest(req interface{}) (id *container.ID, err error) {
|
2020-09-21 16:12:52 +00:00
|
|
|
switch v := req.(type) {
|
|
|
|
case *object.GetRequest:
|
2020-09-28 10:54:24 +00:00
|
|
|
return container.NewIDFromV2(v.GetBody().GetAddress().GetContainerID()), nil
|
2020-09-21 16:12:52 +00:00
|
|
|
case *object.PutRequest:
|
|
|
|
objPart := v.GetBody().GetObjectPart()
|
|
|
|
if part, ok := objPart.(*object.PutObjectPartInit); ok {
|
2020-09-28 10:54:24 +00:00
|
|
|
return container.NewIDFromV2(part.GetHeader().GetContainerID()), nil
|
2020-09-21 16:12:52 +00:00
|
|
|
} else {
|
|
|
|
return nil, errors.New("can't get cid in chunk")
|
|
|
|
}
|
|
|
|
case *object.HeadRequest:
|
2020-09-28 10:54:24 +00:00
|
|
|
return container.NewIDFromV2(v.GetBody().GetAddress().GetContainerID()), nil
|
2020-09-21 16:12:52 +00:00
|
|
|
case *object.SearchRequest:
|
2020-09-28 10:54:24 +00:00
|
|
|
return container.NewIDFromV2(v.GetBody().GetContainerID()), nil
|
2020-09-21 16:12:52 +00:00
|
|
|
case *object.DeleteRequest:
|
2020-09-28 10:54:24 +00:00
|
|
|
return container.NewIDFromV2(v.GetBody().GetAddress().GetContainerID()), nil
|
2020-09-21 16:12:52 +00:00
|
|
|
case *object.GetRangeRequest:
|
2020-09-28 10:54:24 +00:00
|
|
|
return container.NewIDFromV2(v.GetBody().GetAddress().GetContainerID()), nil
|
2020-09-21 16:12:52 +00:00
|
|
|
case *object.GetRangeHashRequest:
|
2020-09-28 10:54:24 +00:00
|
|
|
return container.NewIDFromV2(v.GetBody().GetAddress().GetContainerID()), nil
|
2020-09-21 16:12:52 +00:00
|
|
|
default:
|
|
|
|
return nil, errors.New("unknown request type")
|
|
|
|
}
|
2020-09-21 09:51:15 +00:00
|
|
|
}
|
|
|
|
|
2020-09-28 10:54:24 +00:00
|
|
|
func getObjectOwnerFromMessage(req interface{}) (id *owner.ID, err error) {
|
2020-09-22 16:18:41 +00:00
|
|
|
switch v := req.(type) {
|
|
|
|
case *object.PutRequest:
|
|
|
|
objPart := v.GetBody().GetObjectPart()
|
|
|
|
if part, ok := objPart.(*object.PutObjectPartInit); ok {
|
2020-09-28 10:54:24 +00:00
|
|
|
return owner.NewIDFromV2(part.GetHeader().GetOwnerID()), nil
|
2020-09-22 16:18:41 +00:00
|
|
|
} else {
|
|
|
|
return nil, errors.New("can't get cid in chunk")
|
|
|
|
}
|
|
|
|
case *object.GetResponse:
|
|
|
|
objPart := v.GetBody().GetObjectPart()
|
|
|
|
if part, ok := objPart.(*object.GetObjectPartInit); ok {
|
2020-09-28 10:54:24 +00:00
|
|
|
return owner.NewIDFromV2(part.GetHeader().GetOwnerID()), nil
|
2020-09-22 16:18:41 +00:00
|
|
|
} else {
|
|
|
|
return nil, errors.New("can't get cid in chunk")
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return nil, errors.New("unsupported request type")
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// main check function for basic ACL
|
2020-09-21 16:12:52 +00:00
|
|
|
func basicACLCheck(info requestInfo) bool {
|
2020-09-22 16:18:41 +00:00
|
|
|
// check basic ACL permissions
|
|
|
|
var checkFn func(acl.Operation) bool
|
|
|
|
|
|
|
|
switch info.requestRole {
|
|
|
|
case acl.RoleUser:
|
2020-10-03 05:23:54 +00:00
|
|
|
checkFn = info.basicACL.UserAllowed
|
2020-09-22 16:18:41 +00:00
|
|
|
case acl.RoleSystem:
|
2020-10-03 05:23:54 +00:00
|
|
|
checkFn = info.basicACL.SystemAllowed
|
2020-09-22 16:18:41 +00:00
|
|
|
case acl.RoleOthers:
|
2020-10-03 05:23:54 +00:00
|
|
|
checkFn = info.basicACL.OthersAllowed
|
2020-09-22 16:18:41 +00:00
|
|
|
default:
|
|
|
|
// log there
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return checkFn(info.operation)
|
|
|
|
}
|
|
|
|
|
2020-09-28 10:54:24 +00:00
|
|
|
func stickyBitCheck(info requestInfo, owner *owner.ID) bool {
|
2020-09-22 16:18:41 +00:00
|
|
|
if owner == nil || info.owner == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2020-10-03 05:23:54 +00:00
|
|
|
if !info.basicACL.Sticky() {
|
2020-09-22 16:18:41 +00:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2020-09-28 10:54:24 +00:00
|
|
|
return bytes.Equal(owner.ToV2().GetValue(), info.owner.ToV2().GetValue())
|
2020-09-21 09:51:15 +00:00
|
|
|
}
|
2020-10-02 11:40:09 +00:00
|
|
|
|
2020-10-03 07:50:41 +00:00
|
|
|
func eACLCheck(msg interface{}, reqInfo requestInfo, cfg *eACLCfg) bool {
|
|
|
|
if reqInfo.basicACL.Final() {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2020-10-21 16:08:22 +00:00
|
|
|
// if bearer token is not allowed, then ignore it
|
|
|
|
if !reqInfo.basicACL.BearerAllowed(reqInfo.operation) {
|
|
|
|
reqInfo.bearer = nil
|
|
|
|
}
|
|
|
|
|
2020-10-21 13:11:18 +00:00
|
|
|
// if bearer token is not present, isValidBearer returns true
|
|
|
|
if !isValidBearer(reqInfo) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2020-10-03 07:50:41 +00:00
|
|
|
hdrSrcOpts := make([]eaclV2.Option, 0, 2)
|
|
|
|
|
|
|
|
hdrSrcOpts = append(hdrSrcOpts, eaclV2.WithLocalObjectStorage(cfg.localStorage))
|
|
|
|
|
|
|
|
if req, ok := msg.(eaclV2.Request); ok {
|
|
|
|
hdrSrcOpts = append(hdrSrcOpts, eaclV2.WithServiceRequest(req))
|
|
|
|
} else {
|
|
|
|
hdrSrcOpts = append(hdrSrcOpts, eaclV2.WithServiceResponse(msg.(eaclV2.Response)))
|
|
|
|
}
|
|
|
|
|
|
|
|
action := cfg.eACL.CalculateAction(new(eacl.ValidationUnit).
|
|
|
|
WithRole(reqInfo.requestRole).
|
|
|
|
WithOperation(reqInfo.operation).
|
|
|
|
WithContainerID(reqInfo.cid).
|
|
|
|
WithSenderKey(reqInfo.senderKey).
|
|
|
|
WithHeaderSource(
|
|
|
|
eaclV2.NewMessageHeaderSource(hdrSrcOpts...),
|
2020-10-21 13:11:18 +00:00
|
|
|
).
|
|
|
|
WithBearerToken(reqInfo.bearer),
|
2020-10-03 07:50:41 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
return action == acl.ActionAllow
|
|
|
|
}
|
|
|
|
|
2020-10-02 11:40:09 +00:00
|
|
|
// sourceVerbOfRequest looks for verb in session token and if it is not found,
|
|
|
|
// returns reqVerb.
|
|
|
|
func sourceVerbOfRequest(req metaWithToken, reqVerb acl.Operation) acl.Operation {
|
|
|
|
if req.token != nil {
|
|
|
|
switch v := req.token.GetBody().GetContext().(type) {
|
|
|
|
case *session.ObjectSessionContext:
|
|
|
|
return tokenVerbToOperation(v.GetVerb())
|
|
|
|
default:
|
|
|
|
// do nothing, return request verb
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return reqVerb
|
|
|
|
}
|
|
|
|
|
|
|
|
func tokenVerbToOperation(verb session.ObjectSessionVerb) acl.Operation {
|
|
|
|
switch verb {
|
|
|
|
case session.ObjectVerbGet:
|
|
|
|
return acl.OperationGet
|
|
|
|
case session.ObjectVerbPut:
|
|
|
|
return acl.OperationPut
|
|
|
|
case session.ObjectVerbHead:
|
|
|
|
return acl.OperationHead
|
|
|
|
case session.ObjectVerbSearch:
|
|
|
|
return acl.OperationSearch
|
|
|
|
case session.ObjectVerbDelete:
|
|
|
|
return acl.OperationDelete
|
|
|
|
case session.ObjectVerbRange:
|
|
|
|
return acl.OperationRange
|
|
|
|
case session.ObjectVerbRangeHash:
|
|
|
|
return acl.OperationRangeHash
|
|
|
|
default:
|
|
|
|
return acl.OperationUnknown
|
|
|
|
}
|
|
|
|
}
|
2020-10-03 07:39:02 +00:00
|
|
|
|
|
|
|
func (a *accessErr) Error() string {
|
|
|
|
return fmt.Sprintf("access to operation %v is denied by %s check", a.operation, a.failedCheckTyp)
|
|
|
|
}
|
|
|
|
|
|
|
|
func basicACLErr(info requestInfo) error {
|
|
|
|
return &accessErr{
|
|
|
|
requestInfo: info,
|
|
|
|
failedCheckTyp: "basic ACL",
|
|
|
|
}
|
|
|
|
}
|
2020-10-03 07:50:41 +00:00
|
|
|
|
|
|
|
func eACLErr(info requestInfo) error {
|
|
|
|
return &accessErr{
|
|
|
|
requestInfo: info,
|
|
|
|
failedCheckTyp: "extended ACL",
|
|
|
|
}
|
|
|
|
}
|
2020-10-21 13:11:18 +00:00
|
|
|
|
|
|
|
// isValidBearer returns true if bearer token correctly signed by authorized
|
|
|
|
// entity. This method might be define on whole ACL service because it will
|
|
|
|
// require to fetch current epoch to check lifetime.
|
|
|
|
func isValidBearer(reqInfo requestInfo) bool {
|
|
|
|
token := reqInfo.bearer
|
|
|
|
|
|
|
|
// 0. Check if bearer token is present in reqInfo. It might be non nil
|
|
|
|
// empty structure.
|
|
|
|
if token == nil || (token.GetBody() == nil && token.GetSignature() == nil) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// 1. First check if bearer token is signed correctly.
|
|
|
|
signWrapper := v2signature.StableMarshalerWrapper{SM: token.GetBody()}
|
|
|
|
if err := signature.VerifyDataWithSource(signWrapper, func() (key, sig []byte) {
|
|
|
|
tokenSignature := token.GetSignature()
|
|
|
|
return tokenSignature.GetKey(), tokenSignature.GetSign()
|
|
|
|
}); err != nil {
|
|
|
|
return false // invalid signature
|
|
|
|
}
|
|
|
|
|
|
|
|
// 2. Then check if container owner signed this token.
|
|
|
|
tokenIssuerKey := crypto.UnmarshalPublicKey(token.GetSignature().GetKey())
|
|
|
|
tokenIssuerWallet, err := owner.NEO3WalletFromPublicKey(tokenIssuerKey)
|
|
|
|
if err != nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
// here we compare `owner.ID -> wallet` with `wallet <- publicKey`
|
|
|
|
// consider making equal method on owner.ID structure
|
|
|
|
// we can compare .String() version of owners but don't think it is good idea
|
|
|
|
// binary comparison is better but MarshalBinary is more expensive
|
|
|
|
if !bytes.Equal(reqInfo.owner.ToV2().GetValue(), tokenIssuerWallet.Bytes()) {
|
|
|
|
// todo: in this case we can issue all owner keys from neofs.id and check once again
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// 3. Then check if request sender has rights to use this token.
|
|
|
|
tokenOwnerField := token.GetBody().GetOwnerID()
|
|
|
|
if tokenOwnerField != nil { // see bearer token owner field description
|
|
|
|
requestSenderKey := crypto.UnmarshalPublicKey(reqInfo.senderKey)
|
|
|
|
requestSenderWallet, err := owner.NEO3WalletFromPublicKey(requestSenderKey)
|
|
|
|
if err != nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
// the same issue as above
|
|
|
|
if !bytes.Equal(tokenOwnerField.GetValue(), requestSenderWallet.Bytes()) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// todo: 4. Then check token lifetime.
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|