2022-02-11 12:25:05 +00:00
|
|
|
package v2
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2022-03-17 08:17:45 +00:00
|
|
|
"errors"
|
2022-02-11 12:25:05 +00:00
|
|
|
"fmt"
|
2024-01-29 21:03:18 +00:00
|
|
|
"strings"
|
2022-02-11 12:25:05 +00:00
|
|
|
|
2023-03-07 13:38:26 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/container"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/netmap"
|
2023-08-07 06:54:47 +00:00
|
|
|
objectCore "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
|
2023-03-07 13:38:26 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
|
2024-11-07 14:32:10 +00:00
|
|
|
objectV2 "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/object"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/session"
|
2023-03-07 13:38:26 +00:00
|
|
|
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
2024-01-29 21:03:18 +00:00
|
|
|
cnrSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
2023-03-07 13:38:26 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl"
|
|
|
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
|
|
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
|
|
|
sessionSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
2022-02-11 12:25:05 +00:00
|
|
|
"go.uber.org/zap"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Service checks basic ACL rules.
|
|
|
|
type Service struct {
|
|
|
|
*cfg
|
|
|
|
|
2023-08-07 06:54:47 +00:00
|
|
|
c objectCore.SenderClassifier
|
2022-02-11 12:25:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type putStreamBasicChecker struct {
|
|
|
|
source *Service
|
|
|
|
next object.PutObjectStream
|
|
|
|
}
|
|
|
|
|
2024-08-14 11:38:01 +00:00
|
|
|
type patchStreamBasicChecker struct {
|
|
|
|
source *Service
|
|
|
|
next object.PatchObjectStream
|
|
|
|
nonFirstSend bool
|
|
|
|
}
|
|
|
|
|
2022-02-11 12:25:05 +00:00
|
|
|
// Option represents Service constructor option.
|
|
|
|
type Option func(*cfg)
|
|
|
|
|
|
|
|
type cfg struct {
|
2022-09-28 07:41:01 +00:00
|
|
|
log *logger.Logger
|
2022-02-11 12:25:05 +00:00
|
|
|
|
|
|
|
containers container.Source
|
|
|
|
|
|
|
|
irFetcher InnerRingFetcher
|
|
|
|
|
2022-03-29 12:49:39 +00:00
|
|
|
nm netmap.Source
|
2022-02-11 12:25:05 +00:00
|
|
|
|
|
|
|
next object.ServiceServer
|
|
|
|
}
|
|
|
|
|
|
|
|
// New is a constructor for object ACL checking service.
|
2023-07-06 08:44:37 +00:00
|
|
|
func New(next object.ServiceServer,
|
|
|
|
nm netmap.Source,
|
|
|
|
irf InnerRingFetcher,
|
|
|
|
cs container.Source,
|
2023-10-31 11:56:55 +00:00
|
|
|
opts ...Option,
|
|
|
|
) Service {
|
2023-07-06 08:44:37 +00:00
|
|
|
cfg := &cfg{
|
|
|
|
log: &logger.Logger{Logger: zap.L()},
|
|
|
|
next: next,
|
|
|
|
nm: nm,
|
|
|
|
irFetcher: irf,
|
|
|
|
containers: cs,
|
|
|
|
}
|
2022-02-11 12:25:05 +00:00
|
|
|
|
|
|
|
for i := range opts {
|
|
|
|
opts[i](cfg)
|
|
|
|
}
|
|
|
|
|
|
|
|
return Service{
|
|
|
|
cfg: cfg,
|
2023-08-07 06:54:47 +00:00
|
|
|
c: objectCore.NewSenderClassifier(cfg.irFetcher, cfg.nm, cfg.log),
|
2022-02-11 12:25:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-27 14:18:15 +00:00
|
|
|
// wrappedGetObjectStream propagates RequestContext into GetObjectStream's context.
|
|
|
|
// This allows to retrieve already calculated immutable request-specific values in next handler invocation.
|
|
|
|
type wrappedGetObjectStream struct {
|
|
|
|
object.GetObjectStream
|
|
|
|
|
|
|
|
requestInfo RequestInfo
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *wrappedGetObjectStream) Context() context.Context {
|
|
|
|
return context.WithValue(w.GetObjectStream.Context(), object.RequestContextKey, &object.RequestContext{
|
2024-03-15 12:16:52 +00:00
|
|
|
Namespace: w.requestInfo.ContainerNamespace(),
|
|
|
|
ContainerOwner: w.requestInfo.ContainerOwner(),
|
|
|
|
SenderKey: w.requestInfo.SenderKey(),
|
|
|
|
Role: w.requestInfo.RequestRole(),
|
2024-05-29 08:11:45 +00:00
|
|
|
BearerToken: w.requestInfo.Bearer(),
|
2023-12-27 14:18:15 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func newWrappedGetObjectStreamStream(getObjectStream object.GetObjectStream, reqInfo RequestInfo) object.GetObjectStream {
|
|
|
|
return &wrappedGetObjectStream{
|
|
|
|
GetObjectStream: getObjectStream,
|
|
|
|
requestInfo: reqInfo,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// wrappedRangeStream propagates RequestContext into GetObjectRangeStream's context.
|
|
|
|
// This allows to retrieve already calculated immutable request-specific values in next handler invocation.
|
|
|
|
type wrappedRangeStream struct {
|
|
|
|
object.GetObjectRangeStream
|
|
|
|
|
|
|
|
requestInfo RequestInfo
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *wrappedRangeStream) Context() context.Context {
|
|
|
|
return context.WithValue(w.GetObjectRangeStream.Context(), object.RequestContextKey, &object.RequestContext{
|
2024-03-15 12:16:52 +00:00
|
|
|
Namespace: w.requestInfo.ContainerNamespace(),
|
|
|
|
ContainerOwner: w.requestInfo.ContainerOwner(),
|
|
|
|
SenderKey: w.requestInfo.SenderKey(),
|
|
|
|
Role: w.requestInfo.RequestRole(),
|
2024-05-29 08:11:45 +00:00
|
|
|
BearerToken: w.requestInfo.Bearer(),
|
2023-12-27 14:18:15 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func newWrappedRangeStream(rangeStream object.GetObjectRangeStream, reqInfo RequestInfo) object.GetObjectRangeStream {
|
|
|
|
return &wrappedRangeStream{
|
|
|
|
GetObjectRangeStream: rangeStream,
|
|
|
|
requestInfo: reqInfo,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// wrappedSearchStream propagates RequestContext into SearchStream's context.
|
|
|
|
// This allows to retrieve already calculated immutable request-specific values in next handler invocation.
|
|
|
|
type wrappedSearchStream struct {
|
|
|
|
object.SearchStream
|
|
|
|
|
|
|
|
requestInfo RequestInfo
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *wrappedSearchStream) Context() context.Context {
|
|
|
|
return context.WithValue(w.SearchStream.Context(), object.RequestContextKey, &object.RequestContext{
|
2024-03-15 12:16:52 +00:00
|
|
|
Namespace: w.requestInfo.ContainerNamespace(),
|
|
|
|
ContainerOwner: w.requestInfo.ContainerOwner(),
|
|
|
|
SenderKey: w.requestInfo.SenderKey(),
|
|
|
|
Role: w.requestInfo.RequestRole(),
|
2024-05-29 08:11:45 +00:00
|
|
|
BearerToken: w.requestInfo.Bearer(),
|
2023-12-27 14:18:15 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func newWrappedSearchStream(searchStream object.SearchStream, reqInfo RequestInfo) object.SearchStream {
|
|
|
|
return &wrappedSearchStream{
|
|
|
|
SearchStream: searchStream,
|
|
|
|
requestInfo: reqInfo,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-11 12:25:05 +00:00
|
|
|
// Get implements ServiceServer interface, makes ACL checks and calls
|
|
|
|
// next Get method in the ServiceServer pipeline.
|
|
|
|
func (b Service) Get(request *objectV2.GetRequest, stream object.GetObjectStream) error {
|
2022-05-31 17:00:41 +00:00
|
|
|
cnr, err := getContainerIDFromRequest(request)
|
2022-02-11 12:25:05 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-09-16 10:22:09 +00:00
|
|
|
obj, err := getObjectIDFromRequestBody(request.GetBody())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-05-18 15:20:08 +00:00
|
|
|
sTok, err := originalSessionToken(request.GetMetaHeader())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-02-11 12:25:05 +00:00
|
|
|
|
2022-09-16 10:22:09 +00:00
|
|
|
if sTok != nil {
|
|
|
|
err = assertSessionRelation(*sTok, cnr, obj)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-08 08:53:15 +00:00
|
|
|
bTok, err := originalBearerToken(request.GetMetaHeader())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-02-11 12:25:05 +00:00
|
|
|
req := MetaWithToken{
|
|
|
|
vheader: request.GetVerificationHeader(),
|
|
|
|
token: sTok,
|
2022-06-08 08:53:15 +00:00
|
|
|
bearer: bTok,
|
2022-02-11 12:25:05 +00:00
|
|
|
src: request,
|
|
|
|
}
|
|
|
|
|
2022-06-17 13:40:51 +00:00
|
|
|
reqInfo, err := b.findRequestInfo(req, cnr, acl.OpObjectGet)
|
2022-02-11 12:25:05 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-09-16 10:22:09 +00:00
|
|
|
reqInfo.obj = obj
|
2022-02-11 12:25:05 +00:00
|
|
|
|
2024-11-08 07:10:06 +00:00
|
|
|
return b.next.Get(request, newWrappedGetObjectStreamStream(stream, reqInfo))
|
2022-02-11 12:25:05 +00:00
|
|
|
}
|
|
|
|
|
2023-04-03 11:23:53 +00:00
|
|
|
func (b Service) Put() (object.PutObjectStream, error) {
|
|
|
|
streamer, err := b.next.Put()
|
2022-02-11 12:25:05 +00:00
|
|
|
|
|
|
|
return putStreamBasicChecker{
|
|
|
|
source: &b,
|
|
|
|
next: streamer,
|
|
|
|
}, err
|
|
|
|
}
|
|
|
|
|
2024-08-12 14:11:10 +00:00
|
|
|
func (b Service) Patch() (object.PatchObjectStream, error) {
|
2024-08-14 11:38:01 +00:00
|
|
|
streamer, err := b.next.Patch()
|
|
|
|
|
|
|
|
return &patchStreamBasicChecker{
|
|
|
|
source: &b,
|
|
|
|
next: streamer,
|
|
|
|
}, err
|
2024-08-12 10:01:57 +00:00
|
|
|
}
|
|
|
|
|
2022-02-11 12:25:05 +00:00
|
|
|
func (b Service) Head(
|
|
|
|
ctx context.Context,
|
2023-10-31 11:56:55 +00:00
|
|
|
request *objectV2.HeadRequest,
|
|
|
|
) (*objectV2.HeadResponse, error) {
|
2022-05-31 17:00:41 +00:00
|
|
|
cnr, err := getContainerIDFromRequest(request)
|
2022-02-11 12:25:05 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-09-16 10:22:09 +00:00
|
|
|
obj, err := getObjectIDFromRequestBody(request.GetBody())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-05-18 15:20:08 +00:00
|
|
|
sTok, err := originalSessionToken(request.GetMetaHeader())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-02-11 12:25:05 +00:00
|
|
|
|
2022-09-16 10:22:09 +00:00
|
|
|
if sTok != nil {
|
|
|
|
err = assertSessionRelation(*sTok, cnr, obj)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-08 08:53:15 +00:00
|
|
|
bTok, err := originalBearerToken(request.GetMetaHeader())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-02-11 12:25:05 +00:00
|
|
|
req := MetaWithToken{
|
|
|
|
vheader: request.GetVerificationHeader(),
|
|
|
|
token: sTok,
|
2022-06-08 08:53:15 +00:00
|
|
|
bearer: bTok,
|
2022-02-11 12:25:05 +00:00
|
|
|
src: request,
|
|
|
|
}
|
|
|
|
|
2022-06-17 13:40:51 +00:00
|
|
|
reqInfo, err := b.findRequestInfo(req, cnr, acl.OpObjectHead)
|
2022-02-11 12:25:05 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-09-16 10:22:09 +00:00
|
|
|
reqInfo.obj = obj
|
2022-02-11 12:25:05 +00:00
|
|
|
|
2024-11-08 07:05:37 +00:00
|
|
|
return b.next.Head(requestContext(ctx, reqInfo), request)
|
2022-02-11 12:25:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (b Service) Search(request *objectV2.SearchRequest, stream object.SearchStream) error {
|
|
|
|
id, err := getContainerIDFromRequest(request)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-05-18 15:20:08 +00:00
|
|
|
sTok, err := originalSessionToken(request.GetMetaHeader())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-09-16 10:22:09 +00:00
|
|
|
if sTok != nil {
|
|
|
|
err = assertSessionRelation(*sTok, id, nil)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-08 08:53:15 +00:00
|
|
|
bTok, err := originalBearerToken(request.GetMetaHeader())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-02-11 12:25:05 +00:00
|
|
|
req := MetaWithToken{
|
|
|
|
vheader: request.GetVerificationHeader(),
|
2022-05-18 15:20:08 +00:00
|
|
|
token: sTok,
|
2022-06-08 08:53:15 +00:00
|
|
|
bearer: bTok,
|
2022-02-11 12:25:05 +00:00
|
|
|
src: request,
|
|
|
|
}
|
|
|
|
|
2022-06-17 13:40:51 +00:00
|
|
|
reqInfo, err := b.findRequestInfo(req, id, acl.OpObjectSearch)
|
2022-02-11 12:25:05 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2024-11-08 07:10:06 +00:00
|
|
|
return b.next.Search(request, newWrappedSearchStream(stream, reqInfo))
|
2022-02-11 12:25:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (b Service) Delete(
|
|
|
|
ctx context.Context,
|
2023-10-31 11:56:55 +00:00
|
|
|
request *objectV2.DeleteRequest,
|
|
|
|
) (*objectV2.DeleteResponse, error) {
|
2022-05-31 17:00:41 +00:00
|
|
|
cnr, err := getContainerIDFromRequest(request)
|
2022-02-11 12:25:05 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-09-16 10:22:09 +00:00
|
|
|
obj, err := getObjectIDFromRequestBody(request.GetBody())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-05-18 15:20:08 +00:00
|
|
|
sTok, err := originalSessionToken(request.GetMetaHeader())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-02-11 12:25:05 +00:00
|
|
|
|
2022-09-16 10:22:09 +00:00
|
|
|
if sTok != nil {
|
|
|
|
err = assertSessionRelation(*sTok, cnr, obj)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-08 08:53:15 +00:00
|
|
|
bTok, err := originalBearerToken(request.GetMetaHeader())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-02-11 12:25:05 +00:00
|
|
|
req := MetaWithToken{
|
|
|
|
vheader: request.GetVerificationHeader(),
|
|
|
|
token: sTok,
|
2022-06-08 08:53:15 +00:00
|
|
|
bearer: bTok,
|
2022-02-11 12:25:05 +00:00
|
|
|
src: request,
|
|
|
|
}
|
|
|
|
|
2022-06-17 13:40:51 +00:00
|
|
|
reqInfo, err := b.findRequestInfo(req, cnr, acl.OpObjectDelete)
|
2022-02-11 12:25:05 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-09-16 10:22:09 +00:00
|
|
|
reqInfo.obj = obj
|
2022-02-11 12:25:05 +00:00
|
|
|
|
2023-12-27 14:18:15 +00:00
|
|
|
return b.next.Delete(requestContext(ctx, reqInfo), request)
|
2022-02-11 12:25:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (b Service) GetRange(request *objectV2.GetRangeRequest, stream object.GetObjectRangeStream) error {
|
2022-05-31 17:00:41 +00:00
|
|
|
cnr, err := getContainerIDFromRequest(request)
|
2022-02-11 12:25:05 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-09-16 10:22:09 +00:00
|
|
|
obj, err := getObjectIDFromRequestBody(request.GetBody())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-05-18 15:20:08 +00:00
|
|
|
sTok, err := originalSessionToken(request.GetMetaHeader())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-02-11 12:25:05 +00:00
|
|
|
|
2022-09-16 10:22:09 +00:00
|
|
|
if sTok != nil {
|
|
|
|
err = assertSessionRelation(*sTok, cnr, obj)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-08 08:53:15 +00:00
|
|
|
bTok, err := originalBearerToken(request.GetMetaHeader())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-02-11 12:25:05 +00:00
|
|
|
req := MetaWithToken{
|
|
|
|
vheader: request.GetVerificationHeader(),
|
|
|
|
token: sTok,
|
2022-06-08 08:53:15 +00:00
|
|
|
bearer: bTok,
|
2022-02-11 12:25:05 +00:00
|
|
|
src: request,
|
|
|
|
}
|
|
|
|
|
2022-06-17 13:40:51 +00:00
|
|
|
reqInfo, err := b.findRequestInfo(req, cnr, acl.OpObjectRange)
|
2022-02-11 12:25:05 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-09-16 10:22:09 +00:00
|
|
|
reqInfo.obj = obj
|
2022-02-11 12:25:05 +00:00
|
|
|
|
2024-11-08 07:10:06 +00:00
|
|
|
return b.next.GetRange(request, newWrappedRangeStream(stream, reqInfo))
|
2022-02-11 12:25:05 +00:00
|
|
|
}
|
|
|
|
|
2023-12-27 14:18:15 +00:00
|
|
|
func requestContext(ctx context.Context, reqInfo RequestInfo) context.Context {
|
|
|
|
return context.WithValue(ctx, object.RequestContextKey, &object.RequestContext{
|
2024-03-15 12:16:52 +00:00
|
|
|
Namespace: reqInfo.ContainerNamespace(),
|
|
|
|
ContainerOwner: reqInfo.ContainerOwner(),
|
|
|
|
SenderKey: reqInfo.SenderKey(),
|
|
|
|
Role: reqInfo.RequestRole(),
|
2024-05-29 08:11:45 +00:00
|
|
|
BearerToken: reqInfo.Bearer(),
|
2023-12-27 14:18:15 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-02-11 12:25:05 +00:00
|
|
|
func (b Service) GetRangeHash(
|
|
|
|
ctx context.Context,
|
2023-10-31 11:56:55 +00:00
|
|
|
request *objectV2.GetRangeHashRequest,
|
|
|
|
) (*objectV2.GetRangeHashResponse, error) {
|
2022-05-31 17:00:41 +00:00
|
|
|
cnr, err := getContainerIDFromRequest(request)
|
2022-02-11 12:25:05 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-09-16 10:22:09 +00:00
|
|
|
obj, err := getObjectIDFromRequestBody(request.GetBody())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-05-18 15:20:08 +00:00
|
|
|
sTok, err := originalSessionToken(request.GetMetaHeader())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-02-11 12:25:05 +00:00
|
|
|
|
2022-09-16 10:22:09 +00:00
|
|
|
if sTok != nil {
|
|
|
|
err = assertSessionRelation(*sTok, cnr, obj)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-08 08:53:15 +00:00
|
|
|
bTok, err := originalBearerToken(request.GetMetaHeader())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-02-11 12:25:05 +00:00
|
|
|
req := MetaWithToken{
|
|
|
|
vheader: request.GetVerificationHeader(),
|
|
|
|
token: sTok,
|
2022-06-08 08:53:15 +00:00
|
|
|
bearer: bTok,
|
2022-02-11 12:25:05 +00:00
|
|
|
src: request,
|
|
|
|
}
|
|
|
|
|
2022-06-17 13:40:51 +00:00
|
|
|
reqInfo, err := b.findRequestInfo(req, cnr, acl.OpObjectHash)
|
2022-02-11 12:25:05 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-09-16 10:22:09 +00:00
|
|
|
reqInfo.obj = obj
|
2022-02-11 12:25:05 +00:00
|
|
|
|
2023-12-27 14:18:15 +00:00
|
|
|
return b.next.GetRangeHash(requestContext(ctx, reqInfo), request)
|
2022-02-11 12:25:05 +00:00
|
|
|
}
|
|
|
|
|
2023-07-03 08:36:20 +00:00
|
|
|
func (b Service) PutSingle(ctx context.Context, request *objectV2.PutSingleRequest) (*objectV2.PutSingleResponse, error) {
|
|
|
|
cnr, err := getContainerIDFromRequest(request)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
idV2 := request.GetBody().GetObject().GetHeader().GetOwnerID()
|
|
|
|
if idV2 == nil {
|
|
|
|
return nil, errors.New("missing object owner")
|
|
|
|
}
|
|
|
|
|
|
|
|
var idOwner user.ID
|
|
|
|
|
|
|
|
err = idOwner.ReadFromV2(*idV2)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("invalid object owner: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
obj, err := getObjectIDFromRefObjectID(request.GetBody().GetObject().GetObjectID())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var sTok *sessionSDK.Object
|
|
|
|
sTok, err = readSessionToken(cnr, obj, request.GetMetaHeader().GetSessionToken())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
bTok, err := originalBearerToken(request.GetMetaHeader())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
req := MetaWithToken{
|
|
|
|
vheader: request.GetVerificationHeader(),
|
|
|
|
token: sTok,
|
|
|
|
bearer: bTok,
|
|
|
|
src: request,
|
|
|
|
}
|
|
|
|
|
|
|
|
reqInfo, err := b.findRequestInfo(req, cnr, acl.OpObjectPut)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
reqInfo.obj = obj
|
|
|
|
|
2023-12-27 14:18:15 +00:00
|
|
|
return b.next.PutSingle(requestContext(ctx, reqInfo), request)
|
2023-07-03 08:36:20 +00:00
|
|
|
}
|
|
|
|
|
2023-04-03 11:23:53 +00:00
|
|
|
func (p putStreamBasicChecker) Send(ctx context.Context, request *objectV2.PutRequest) error {
|
2022-02-11 12:25:05 +00:00
|
|
|
body := request.GetBody()
|
|
|
|
if body == nil {
|
2022-11-10 17:46:52 +00:00
|
|
|
return errEmptyBody
|
2022-02-11 12:25:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
part := body.GetObjectPart()
|
|
|
|
if part, ok := part.(*objectV2.PutObjectPartInit); ok {
|
2022-05-31 17:00:41 +00:00
|
|
|
cnr, err := getContainerIDFromRequest(request)
|
2022-02-11 12:25:05 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-05-17 13:59:46 +00:00
|
|
|
idV2 := part.GetHeader().GetOwnerID()
|
|
|
|
if idV2 == nil {
|
|
|
|
return errors.New("missing object owner")
|
|
|
|
}
|
|
|
|
|
|
|
|
var idOwner user.ID
|
|
|
|
|
|
|
|
err = idOwner.ReadFromV2(*idV2)
|
2022-02-11 12:25:05 +00:00
|
|
|
if err != nil {
|
2022-05-17 13:59:46 +00:00
|
|
|
return fmt.Errorf("invalid object owner: %w", err)
|
2022-02-11 12:25:05 +00:00
|
|
|
}
|
|
|
|
|
2022-09-16 10:22:09 +00:00
|
|
|
objV2 := part.GetObjectID()
|
|
|
|
var obj *oid.ID
|
|
|
|
|
|
|
|
if objV2 != nil {
|
|
|
|
obj = new(oid.ID)
|
|
|
|
|
|
|
|
err = obj.ReadFromV2(*objV2)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-18 15:20:08 +00:00
|
|
|
var sTok *sessionSDK.Object
|
2023-07-03 08:36:20 +00:00
|
|
|
sTok, err = readSessionToken(cnr, obj, request.GetMetaHeader().GetSessionToken())
|
2023-04-04 09:18:43 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
2022-05-18 15:20:08 +00:00
|
|
|
}
|
2022-02-11 12:25:05 +00:00
|
|
|
|
2022-06-08 08:53:15 +00:00
|
|
|
bTok, err := originalBearerToken(request.GetMetaHeader())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-02-11 12:25:05 +00:00
|
|
|
req := MetaWithToken{
|
|
|
|
vheader: request.GetVerificationHeader(),
|
|
|
|
token: sTok,
|
2022-06-08 08:53:15 +00:00
|
|
|
bearer: bTok,
|
2022-02-11 12:25:05 +00:00
|
|
|
src: request,
|
|
|
|
}
|
|
|
|
|
2022-06-17 13:40:51 +00:00
|
|
|
reqInfo, err := p.source.findRequestInfo(req, cnr, acl.OpObjectPut)
|
2022-02-11 12:25:05 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-09-16 10:22:09 +00:00
|
|
|
reqInfo.obj = obj
|
2022-02-11 12:25:05 +00:00
|
|
|
|
2023-12-27 14:18:15 +00:00
|
|
|
ctx = requestContext(ctx, reqInfo)
|
2022-02-11 12:25:05 +00:00
|
|
|
}
|
|
|
|
|
2023-04-03 11:23:53 +00:00
|
|
|
return p.next.Send(ctx, request)
|
2022-02-11 12:25:05 +00:00
|
|
|
}
|
2023-04-04 09:18:43 +00:00
|
|
|
|
2023-07-03 08:36:20 +00:00
|
|
|
func readSessionToken(cnr cid.ID, obj *oid.ID, tokV2 *session.Token) (*sessionSDK.Object, error) {
|
2023-04-04 09:18:43 +00:00
|
|
|
var sTok *sessionSDK.Object
|
|
|
|
|
2023-07-03 08:36:20 +00:00
|
|
|
if tokV2 != nil {
|
2023-04-04 09:18:43 +00:00
|
|
|
sTok = new(sessionSDK.Object)
|
|
|
|
|
|
|
|
err := sTok.ReadFromV2(*tokV2)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("invalid session token: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if sTok.AssertVerb(sessionSDK.VerbObjectDelete) {
|
|
|
|
// if session relates to object's removal, we don't check
|
|
|
|
// relation of the tombstone to the session here since user
|
|
|
|
// can't predict tomb's ID.
|
|
|
|
err = assertSessionRelation(*sTok, cnr, nil)
|
|
|
|
} else {
|
|
|
|
err = assertSessionRelation(*sTok, cnr, obj)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return sTok, nil
|
|
|
|
}
|
2022-02-11 12:25:05 +00:00
|
|
|
|
2023-04-03 11:23:53 +00:00
|
|
|
func (p putStreamBasicChecker) CloseAndRecv(ctx context.Context) (*objectV2.PutResponse, error) {
|
|
|
|
return p.next.CloseAndRecv(ctx)
|
2022-02-11 12:25:05 +00:00
|
|
|
}
|
|
|
|
|
2024-08-14 11:38:01 +00:00
|
|
|
func (p *patchStreamBasicChecker) Send(ctx context.Context, request *objectV2.PatchRequest) error {
|
|
|
|
body := request.GetBody()
|
|
|
|
if body == nil {
|
|
|
|
return errEmptyBody
|
|
|
|
}
|
|
|
|
|
|
|
|
if !p.nonFirstSend {
|
|
|
|
p.nonFirstSend = true
|
|
|
|
|
|
|
|
cnr, err := getContainerIDFromRequest(request)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
objV2 := request.GetBody().GetAddress().GetObjectID()
|
|
|
|
if objV2 == nil {
|
|
|
|
return errors.New("missing oid")
|
|
|
|
}
|
|
|
|
obj := new(oid.ID)
|
|
|
|
err = obj.ReadFromV2(*objV2)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var sTok *sessionSDK.Object
|
|
|
|
sTok, err = readSessionToken(cnr, obj, request.GetMetaHeader().GetSessionToken())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
bTok, err := originalBearerToken(request.GetMetaHeader())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
req := MetaWithToken{
|
|
|
|
vheader: request.GetVerificationHeader(),
|
|
|
|
token: sTok,
|
|
|
|
bearer: bTok,
|
|
|
|
src: request,
|
|
|
|
}
|
|
|
|
|
|
|
|
reqInfo, err := p.source.findRequestInfoWithoutACLOperationAssert(req, cnr)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
reqInfo.obj = obj
|
|
|
|
|
|
|
|
ctx = requestContext(ctx, reqInfo)
|
|
|
|
}
|
|
|
|
|
|
|
|
return p.next.Send(ctx, request)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p patchStreamBasicChecker) CloseAndRecv(ctx context.Context) (*objectV2.PatchResponse, error) {
|
|
|
|
return p.next.CloseAndRecv(ctx)
|
|
|
|
}
|
|
|
|
|
2022-06-17 13:40:51 +00:00
|
|
|
func (b Service) findRequestInfo(req MetaWithToken, idCnr cid.ID, op acl.Op) (info RequestInfo, err error) {
|
2022-05-31 17:00:41 +00:00
|
|
|
cnr, err := b.containers.Get(idCnr) // fetch actual container
|
2022-03-17 08:17:45 +00:00
|
|
|
if err != nil {
|
|
|
|
return info, err
|
2022-02-11 12:25:05 +00:00
|
|
|
}
|
|
|
|
|
2022-05-18 15:20:08 +00:00
|
|
|
if req.token != nil {
|
2022-03-29 11:38:01 +00:00
|
|
|
currentEpoch, err := b.nm.Epoch()
|
|
|
|
if err != nil {
|
|
|
|
return info, errors.New("can't fetch current epoch")
|
|
|
|
}
|
2023-01-20 09:57:53 +00:00
|
|
|
if req.token.ExpiredAt(currentEpoch) {
|
2023-08-04 11:14:07 +00:00
|
|
|
return info, new(apistatus.SessionTokenExpired)
|
2023-01-20 09:57:53 +00:00
|
|
|
}
|
2022-11-10 17:58:06 +00:00
|
|
|
if req.token.InvalidAt(currentEpoch) {
|
|
|
|
return info, fmt.Errorf("%s: token is invalid at %d epoch)",
|
2022-11-10 17:46:52 +00:00
|
|
|
invalidRequestMessage, currentEpoch)
|
2022-05-18 15:20:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if !assertVerb(*req.token, op) {
|
2022-11-10 17:46:52 +00:00
|
|
|
return info, errInvalidVerb
|
2022-03-29 11:38:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-11 12:25:05 +00:00
|
|
|
// find request role and key
|
2023-08-07 06:54:47 +00:00
|
|
|
ownerID, ownerKey, err := req.RequestOwner()
|
|
|
|
if err != nil {
|
|
|
|
return info, err
|
|
|
|
}
|
|
|
|
res, err := b.c.Classify(ownerID, ownerKey, idCnr, cnr.Value)
|
2022-02-11 12:25:05 +00:00
|
|
|
if err != nil {
|
|
|
|
return info, err
|
|
|
|
}
|
|
|
|
|
2022-06-22 10:55:31 +00:00
|
|
|
info.basicACL = cnr.Value.BasicACL()
|
2023-08-07 06:54:47 +00:00
|
|
|
info.requestRole = res.Role
|
2022-05-18 15:20:08 +00:00
|
|
|
info.operation = op
|
2022-06-28 07:01:05 +00:00
|
|
|
info.cnrOwner = cnr.Value.Owner()
|
2022-05-31 17:00:41 +00:00
|
|
|
info.idCnr = idCnr
|
2024-01-29 21:03:18 +00:00
|
|
|
|
|
|
|
cnrNamespace, hasNamespace := strings.CutSuffix(cnrSDK.ReadDomain(cnr.Value).Zone(), ".ns")
|
|
|
|
if hasNamespace {
|
|
|
|
info.cnrNamespace = cnrNamespace
|
|
|
|
}
|
2022-02-11 12:25:05 +00:00
|
|
|
|
|
|
|
// it is assumed that at the moment the key will be valid,
|
|
|
|
// otherwise the request would not pass validation
|
2023-08-07 06:54:47 +00:00
|
|
|
info.senderKey = res.Key
|
2022-02-11 12:25:05 +00:00
|
|
|
|
|
|
|
// add bearer token if it is present in request
|
|
|
|
info.bearer = req.bearer
|
|
|
|
|
|
|
|
info.srcRequest = req.src
|
|
|
|
|
|
|
|
return info, nil
|
|
|
|
}
|
2024-08-14 11:38:01 +00:00
|
|
|
|
|
|
|
// findRequestInfoWithoutACLOperationAssert is findRequestInfo without session token verb assert.
|
|
|
|
func (b Service) findRequestInfoWithoutACLOperationAssert(req MetaWithToken, idCnr cid.ID) (info RequestInfo, err error) {
|
|
|
|
cnr, err := b.containers.Get(idCnr) // fetch actual container
|
|
|
|
if err != nil {
|
|
|
|
return info, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if req.token != nil {
|
|
|
|
currentEpoch, err := b.nm.Epoch()
|
|
|
|
if err != nil {
|
|
|
|
return info, errors.New("can't fetch current epoch")
|
|
|
|
}
|
|
|
|
if req.token.ExpiredAt(currentEpoch) {
|
|
|
|
return info, new(apistatus.SessionTokenExpired)
|
|
|
|
}
|
|
|
|
if req.token.InvalidAt(currentEpoch) {
|
|
|
|
return info, fmt.Errorf("%s: token is invalid at %d epoch)",
|
|
|
|
invalidRequestMessage, currentEpoch)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// find request role and key
|
|
|
|
ownerID, ownerKey, err := req.RequestOwner()
|
|
|
|
if err != nil {
|
|
|
|
return info, err
|
|
|
|
}
|
|
|
|
res, err := b.c.Classify(ownerID, ownerKey, idCnr, cnr.Value)
|
|
|
|
if err != nil {
|
|
|
|
return info, err
|
|
|
|
}
|
|
|
|
|
|
|
|
info.basicACL = cnr.Value.BasicACL()
|
|
|
|
info.requestRole = res.Role
|
|
|
|
info.cnrOwner = cnr.Value.Owner()
|
|
|
|
info.idCnr = idCnr
|
|
|
|
|
|
|
|
cnrNamespace, hasNamespace := strings.CutSuffix(cnrSDK.ReadDomain(cnr.Value).Zone(), ".ns")
|
|
|
|
if hasNamespace {
|
|
|
|
info.cnrNamespace = cnrNamespace
|
|
|
|
}
|
|
|
|
|
|
|
|
// it is assumed that at the moment the key will be valid,
|
|
|
|
// otherwise the request would not pass validation
|
|
|
|
info.senderKey = res.Key
|
|
|
|
|
|
|
|
// add bearer token if it is present in request
|
|
|
|
info.bearer = req.bearer
|
|
|
|
|
|
|
|
info.srcRequest = req.src
|
|
|
|
|
|
|
|
return info, nil
|
|
|
|
}
|