From 8050ca2d517ebfd64e33764c137f86b703611146 Mon Sep 17 00:00:00 2001 From: Denis Kirillov Date: Fri, 1 Mar 2024 17:11:07 +0300 Subject: [PATCH] [#306] Use session token for container read operations Signed-off-by: Denis Kirillov --- api/handler/acl_test.go | 3 ++- api/layer/container.go | 36 +++++++++++++++++++++++++---------- api/layer/frostfs.go | 33 +++++++++++++++++++++++++++++--- api/layer/frostfs_mock.go | 14 +++++++------- api/layer/layer.go | 16 +++++++++++++++- api/layer/util.go | 2 +- creds/accessbox/accessbox.go | 8 ++++++++ go.mod | 4 ++-- go.sum | 8 ++++---- internal/frostfs/authmate.go | 2 +- internal/frostfs/frostfs.go | 27 ++++++++++++++++---------- pkg/service/tree/tree_test.go | 4 +++- 12 files changed, 116 insertions(+), 41 deletions(-) diff --git a/api/handler/acl_test.go b/api/handler/acl_test.go index bb1bcc93..55abb4d1 100644 --- a/api/handler/acl_test.go +++ b/api/handler/acl_test.go @@ -16,6 +16,7 @@ import ( "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data" s3errors "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors" + "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer" @@ -1320,7 +1321,7 @@ func TestPutBucketAPE(t *testing.T) { info := createBucket(hc, bktName) - _, err := hc.tp.ContainerEACL(hc.Context(), info.BktInfo.CID) + _, err := hc.tp.ContainerEACL(hc.Context(), layer.PrmContainerEACL{ContainerID: info.BktInfo.CID}) require.ErrorContains(t, err, "not found") chains, err := hc.h.ape.(*apeMock).ListChains(engine.NamespaceTarget("")) diff --git a/api/layer/container.go b/api/layer/container.go index d683cd6d..6c36b303 100644 --- a/api/layer/container.go +++ b/api/layer/container.go @@ -32,20 +32,21 @@ const ( AttributeLockEnabled = "LockEnabled" ) -func (n *layer) containerInfo(ctx context.Context, idCnr cid.ID) (*data.BucketInfo, error) { +func (n *layer) containerInfo(ctx context.Context, prm PrmContainer) (*data.BucketInfo, error) { var ( err error res *container.Container - log = n.reqLogger(ctx).With(zap.Stringer("cid", idCnr)) + log = n.reqLogger(ctx).With(zap.Stringer("cid", prm.ContainerID)) info = &data.BucketInfo{ - CID: idCnr, - Name: idCnr.EncodeToString(), + CID: prm.ContainerID, + Name: prm.ContainerID.EncodeToString(), } reqInfo = middleware.GetReqInfo(ctx) ) - res, err = n.frostFS.Container(ctx, idCnr) + + res, err = n.frostFS.Container(ctx, prm) if err != nil { if client.IsErrContainerNotFound(err) { return nil, fmt.Errorf("%w: %s", s3errors.GetAPIError(s3errors.ErrNoSuchBucket), err.Error()) @@ -78,7 +79,7 @@ func (n *layer) containerInfo(ctx context.Context, idCnr cid.ID) (*data.BucketIn zone, _ := n.features.FormContainerZone(reqInfo.Namespace) if zone != info.Zone { - return nil, fmt.Errorf("ns '%s' and zone '%s' are mismatched for container '%s'", zone, info.Zone, idCnr) + return nil, fmt.Errorf("ns '%s' and zone '%s' are mismatched for container '%s'", zone, info.Zone, prm.ContainerID) } n.cache.PutBucket(info) @@ -87,7 +88,14 @@ func (n *layer) containerInfo(ctx context.Context, idCnr cid.ID) (*data.BucketIn } func (n *layer) containerList(ctx context.Context) ([]*data.BucketInfo, error) { - res, err := n.frostFS.UserContainers(ctx, n.BearerOwner(ctx)) + stoken := n.SessionTokenForRead(ctx) + + prm := PrmUserContainers{ + UserID: n.BearerOwner(ctx), + SessionToken: stoken, + } + + res, err := n.frostFS.UserContainers(ctx, prm) if err != nil { n.reqLogger(ctx).Error(logs.CouldNotListUserContainers, zap.Error(err)) return nil, err @@ -95,7 +103,11 @@ func (n *layer) containerList(ctx context.Context) ([]*data.BucketInfo, error) { list := make([]*data.BucketInfo, 0, len(res)) for i := range res { - info, err := n.containerInfo(ctx, res[i]) + getPrm := PrmContainer{ + ContainerID: res[i], + SessionToken: stoken, + } + info, err := n.containerInfo(ctx, getPrm) if err != nil { n.reqLogger(ctx).Error(logs.CouldNotFetchContainerInfo, zap.Error(err)) continue @@ -167,6 +179,10 @@ func (n *layer) setContainerEACLTable(ctx context.Context, idCnr cid.ID, table * return n.frostFS.SetContainerEACL(ctx, *table, sessionToken) } -func (n *layer) GetContainerEACL(ctx context.Context, idCnr cid.ID) (*eacl.Table, error) { - return n.frostFS.ContainerEACL(ctx, idCnr) +func (n *layer) GetContainerEACL(ctx context.Context, cnrID cid.ID) (*eacl.Table, error) { + prm := PrmContainerEACL{ + ContainerID: cnrID, + SessionToken: n.SessionTokenForRead(ctx), + } + return n.frostFS.ContainerEACL(ctx, prm) } diff --git a/api/layer/frostfs.go b/api/layer/frostfs.go index 9be762a0..ce9fa45c 100644 --- a/api/layer/frostfs.go +++ b/api/layer/frostfs.go @@ -46,6 +46,33 @@ type PrmContainerCreate struct { AdditionalAttributes [][2]string } +// PrmContainer groups parameters of FrostFS.Container operation. +type PrmContainer struct { + // Container identifier. + ContainerID cid.ID + + // Token of the container's creation session. Nil means session absence. + SessionToken *session.Container +} + +// PrmUserContainers groups parameters of FrostFS.UserContainers operation. +type PrmUserContainers struct { + // User identifier. + UserID user.ID + + // Token of the container's creation session. Nil means session absence. + SessionToken *session.Container +} + +// PrmContainerEACL groups parameters of FrostFS.ContainerEACL operation. +type PrmContainerEACL struct { + // Container identifier. + ContainerID cid.ID + + // Token of the container's creation session. Nil means session absence. + SessionToken *session.Container +} + // ContainerCreateResult is a result parameter of FrostFS.CreateContainer operation. type ContainerCreateResult struct { ContainerID cid.ID @@ -181,13 +208,13 @@ type FrostFS interface { // // It returns exactly one non-nil value. It returns any error encountered which // prevented the container from being read. - Container(context.Context, cid.ID) (*container.Container, error) + Container(context.Context, PrmContainer) (*container.Container, error) // UserContainers reads a list of the containers owned by the specified user. // // It returns exactly one non-nil value. It returns any error encountered which // prevented the containers from being listed. - UserContainers(context.Context, user.ID) ([]cid.ID, error) + UserContainers(context.Context, PrmUserContainers) ([]cid.ID, error) // SetContainerEACL saves the eACL table of the container in FrostFS. The // extended ACL is modified within session if session token is not nil. @@ -199,7 +226,7 @@ type FrostFS interface { // // It returns exactly one non-nil value. It returns any error encountered which // prevented the eACL from being read. - ContainerEACL(context.Context, cid.ID) (*eacl.Table, error) + ContainerEACL(context.Context, PrmContainerEACL) (*eacl.Table, error) // DeleteContainer marks the container to be removed from FrostFS by ID. // Request is sent within session if the session token is specified. diff --git a/api/layer/frostfs_mock.go b/api/layer/frostfs_mock.go index 193654e9..786e14bd 100644 --- a/api/layer/frostfs_mock.go +++ b/api/layer/frostfs_mock.go @@ -184,17 +184,17 @@ func (t *TestFrostFS) DeleteContainer(_ context.Context, cnrID cid.ID, _ *sessio return nil } -func (t *TestFrostFS) Container(_ context.Context, id cid.ID) (*container.Container, error) { +func (t *TestFrostFS) Container(_ context.Context, prm PrmContainer) (*container.Container, error) { for k, v := range t.containers { - if k == id.EncodeToString() { + if k == prm.ContainerID.EncodeToString() { return v, nil } } - return nil, fmt.Errorf("container not found %s", id) + return nil, fmt.Errorf("container not found %s", prm.ContainerID) } -func (t *TestFrostFS) UserContainers(_ context.Context, _ user.ID) ([]cid.ID, error) { +func (t *TestFrostFS) UserContainers(context.Context, PrmUserContainers) ([]cid.ID, error) { var res []cid.ID for k := range t.containers { var idCnr cid.ID @@ -284,7 +284,7 @@ func (t *TestFrostFS) CreateObject(_ context.Context, prm PrmObjectCreate) (oid. obj.SetPayloadSize(prm.PayloadSize) obj.SetAttributes(attrs...) obj.SetCreationEpoch(t.currentEpoch) - obj.SetOwnerID(&owner) + obj.SetOwnerID(owner) t.currentEpoch++ if len(prm.Locks) > 0 { @@ -367,8 +367,8 @@ func (t *TestFrostFS) SetContainerEACL(_ context.Context, table eacl.Table, _ *s return nil } -func (t *TestFrostFS) ContainerEACL(_ context.Context, cnrID cid.ID) (*eacl.Table, error) { - table, ok := t.eaclTables[cnrID.EncodeToString()] +func (t *TestFrostFS) ContainerEACL(_ context.Context, prm PrmContainerEACL) (*eacl.Table, error) { + table, ok := t.eaclTables[prm.ContainerID.EncodeToString()] if !ok { return nil, errors.New("not found") } diff --git a/api/layer/layer.go b/api/layer/layer.go index 9879ecfe..b23b0f56 100644 --- a/api/layer/layer.go +++ b/api/layer/layer.go @@ -378,6 +378,15 @@ func (n *layer) BearerOwner(ctx context.Context) user.ID { return ownerID } +// SessionTokenForRead returns session container token. +func (n *layer) SessionTokenForRead(ctx context.Context) *session.Container { + if bd, err := middleware.GetBoxData(ctx); err == nil && bd.Gate != nil { + return bd.Gate.SessionToken() + } + + return nil +} + func (n *layer) reqLogger(ctx context.Context) *zap.Logger { reqLogger := middleware.GetReqLog(ctx) if reqLogger != nil { @@ -419,7 +428,12 @@ func (n *layer) GetBucketInfo(ctx context.Context, name string) (*data.BucketInf return nil, err } - return n.containerInfo(ctx, containerID) + prm := PrmContainer{ + ContainerID: containerID, + SessionToken: n.SessionTokenForRead(ctx), + } + + return n.containerInfo(ctx, prm) } // ResolveCID returns container id by name. diff --git a/api/layer/util.go b/api/layer/util.go index 644da2e1..f3ba2b04 100644 --- a/api/layer/util.go +++ b/api/layer/util.go @@ -56,7 +56,7 @@ func objectInfoFromMeta(bkt *data.BucketInfo, meta *object.Object) *data.ObjectI Created: creation, ContentType: mimeType, Headers: headers, - Owner: *meta.OwnerID(), + Owner: meta.OwnerID(), Size: meta.PayloadSize(), CreationEpoch: meta.CreationEpoch(), HashSum: hex.EncodeToString(payloadChecksum.Value()), diff --git a/creds/accessbox/accessbox.go b/creds/accessbox/accessbox.go index 71a76b1c..3c02723e 100644 --- a/creds/accessbox/accessbox.go +++ b/creds/accessbox/accessbox.go @@ -59,6 +59,14 @@ func (g *GateData) SessionTokenForSetEACL() *session.Container { return g.containerSessionToken(session.VerbContainerSetEACL) } +// SessionToken returns the first container session context. +func (g *GateData) SessionToken() *session.Container { + if len(g.SessionTokens) != 0 { + return g.SessionTokens[0] + } + return nil +} + func (g *GateData) containerSessionToken(verb session.ContainerVerb) *session.Container { for _, sessionToken := range g.SessionTokens { if isAppropriateContainerContext(sessionToken, verb) { diff --git a/go.mod b/go.mod index 9bd8818c..e00880b1 100644 --- a/go.mod +++ b/go.mod @@ -3,10 +3,10 @@ module git.frostfs.info/TrueCloudLab/frostfs-s3-gw go 1.20 require ( - git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20231121085847-241a9f1ad0a4 + git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240215114728-2a124b95bc02 git.frostfs.info/TrueCloudLab/frostfs-contract v0.18.1-0.20231218084346-bce7ef18c83b git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20230531082742-c97d21411eb6 - git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20231107114540-ab75edd70939 + git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240301150205-6fe4e2541d0b git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240226094215-c960b1b08831 git.frostfs.info/TrueCloudLab/zapjournald v0.0.0-20240124114243-cb2e66427d02 github.com/aws/aws-sdk-go v1.44.6 diff --git a/go.sum b/go.sum index dcaee33d..3089632e 100644 --- a/go.sum +++ b/go.sum @@ -36,16 +36,16 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20231121085847-241a9f1ad0a4 h1:wjLfZ3WCt7qNGsQv+Jl0TXnmtg0uVk/jToKPFTBc/jo= -git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20231121085847-241a9f1ad0a4/go.mod h1:uY0AYmCznjZdghDnAk7THFIe1Vlg531IxUcus7ZfUJI= +git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240215114728-2a124b95bc02 h1:SAoUNpK1KBcY9NwP3ZZwDMXB5bvGCQiHxpXCw6wdpAI= +git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240215114728-2a124b95bc02/go.mod h1:uY0AYmCznjZdghDnAk7THFIe1Vlg531IxUcus7ZfUJI= git.frostfs.info/TrueCloudLab/frostfs-contract v0.18.1-0.20231218084346-bce7ef18c83b h1:zdbOxyqkxRyOLc7/2oNFu5tBwwg0Q6+0tJM3RkAxHlE= git.frostfs.info/TrueCloudLab/frostfs-contract v0.18.1-0.20231218084346-bce7ef18c83b/go.mod h1:YMFtNZy2MgeiSwt0t8lqk8dYBGzlbhmV1cbbstJJ6oY= git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0 h1:FxqFDhQYYgpe41qsIHVOcdzSVCB8JNSfPG7Uk4r2oSk= git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0/go.mod h1:RUIKZATQLJ+TaYQa60X2fTDwfuhMfm8Ar60bQ5fr+vU= git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20230531082742-c97d21411eb6 h1:aGQ6QaAnTerQ5Dq5b2/f9DUQtSqPkZZ/bkMx/HKuLCo= git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20230531082742-c97d21411eb6/go.mod h1:W8Nn08/l6aQ7UlIbpF7FsQou7TVpcRD1ZT1KG4TrFhE= -git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20231107114540-ab75edd70939 h1:jZEepi9yWmqrWgLRQcHQu4YPJaudmd7d2AEhpmM3m4U= -git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20231107114540-ab75edd70939/go.mod h1:t1akKcUH7iBrFHX8rSXScYMP17k2kYQXMbZooiL5Juw= +git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240301150205-6fe4e2541d0b h1:nLIWYXe4e1fWgpKeMfVke/CNBn388egh4fArFdvhfHw= +git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240301150205-6fe4e2541d0b/go.mod h1:XcgrbZ88XfvhAMxmZCQJ0dv6FyRSq6Mg2J7nN8uuO0k= git.frostfs.info/TrueCloudLab/hrw v1.2.1 h1:ccBRK21rFvY5R1WotI6LNoPlizk7qSvdfD8lNIRudVc= git.frostfs.info/TrueCloudLab/hrw v1.2.1/go.mod h1:C1Ygde2n843yTZEQ0FP69jYiuaYV0kriLvP4zm8JuvM= git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240226094215-c960b1b08831 h1:yK2iGQlg5kMmU47ZHor/g52mVS1xEgJSRQ4Olp76Cg8= diff --git a/internal/frostfs/authmate.go b/internal/frostfs/authmate.go index 398e080f..42bade31 100644 --- a/internal/frostfs/authmate.go +++ b/internal/frostfs/authmate.go @@ -36,7 +36,7 @@ func NewAuthmateFrostFS(p *pool.Pool, key *keys.PrivateKey) *AuthmateFrostFS { // ContainerExists implements authmate.FrostFS interface method. func (x *AuthmateFrostFS) ContainerExists(ctx context.Context, idCnr cid.ID) error { - _, err := x.frostFS.Container(ctx, idCnr) + _, err := x.frostFS.Container(ctx, layer.PrmContainer{ContainerID: idCnr}) if err != nil { return fmt.Errorf("get container via connection pool: %w", err) } diff --git a/internal/frostfs/frostfs.go b/internal/frostfs/frostfs.go index 7f85c8c2..3948cbd7 100644 --- a/internal/frostfs/frostfs.go +++ b/internal/frostfs/frostfs.go @@ -89,8 +89,11 @@ func (x *FrostFS) TimeToEpoch(ctx context.Context, now, futureTime time.Time) (u } // Container implements frostfs.FrostFS interface method. -func (x *FrostFS) Container(ctx context.Context, idCnr cid.ID) (*container.Container, error) { - prm := pool.PrmContainerGet{ContainerID: idCnr} +func (x *FrostFS) Container(ctx context.Context, layerPrm layer.PrmContainer) (*container.Container, error) { + prm := pool.PrmContainerGet{ + ContainerID: layerPrm.ContainerID, + Session: layerPrm.SessionToken, + } res, err := x.pool.GetContainer(ctx, prm) if err != nil { @@ -149,9 +152,11 @@ func (x *FrostFS) CreateContainer(ctx context.Context, prm layer.PrmContainerCre } // UserContainers implements frostfs.FrostFS interface method. -func (x *FrostFS) UserContainers(ctx context.Context, id user.ID) ([]cid.ID, error) { - var prm pool.PrmContainerList - prm.SetOwnerID(id) +func (x *FrostFS) UserContainers(ctx context.Context, layerPrm layer.PrmUserContainers) ([]cid.ID, error) { + prm := pool.PrmContainerList{ + OwnerID: layerPrm.UserID, + Session: layerPrm.SessionToken, + } r, err := x.pool.ListContainers(ctx, prm) return r, handleObjectError("list user containers via connection pool", err) @@ -166,9 +171,11 @@ func (x *FrostFS) SetContainerEACL(ctx context.Context, table eacl.Table, sessio } // ContainerEACL implements frostfs.FrostFS interface method. -func (x *FrostFS) ContainerEACL(ctx context.Context, id cid.ID) (*eacl.Table, error) { - var prm pool.PrmContainerEACL - prm.SetContainerID(id) +func (x *FrostFS) ContainerEACL(ctx context.Context, layerPrm layer.PrmContainerEACL) (*eacl.Table, error) { + prm := pool.PrmContainerEACL{ + ContainerID: layerPrm.ContainerID, + Session: layerPrm.SessionToken, + } res, err := x.pool.GetEACL(ctx, prm) if err != nil { @@ -224,14 +231,14 @@ func (x *FrostFS) CreateObject(ctx context.Context, prm layer.PrmObjectCreate) ( obj := object.New() obj.SetContainerID(prm.Container) - obj.SetOwnerID(&x.owner) + obj.SetOwnerID(x.owner) obj.SetAttributes(attrs...) obj.SetPayloadSize(prm.PayloadSize) if prm.BearerToken == nil && prm.PrivateKey != nil { var owner user.ID user.IDFromKey(&owner, prm.PrivateKey.PublicKey) - obj.SetOwnerID(&owner) + obj.SetOwnerID(owner) } if len(prm.Locks) > 0 { diff --git a/pkg/service/tree/tree_test.go b/pkg/service/tree/tree_test.go index cfb46051..101876b2 100644 --- a/pkg/service/tree/tree_test.go +++ b/pkg/service/tree/tree_test.go @@ -148,6 +148,8 @@ func TestTreeServiceAddVersion(t *testing.T) { CID: cidtest.ID(), } + userID := usertest.ID() + now := time.Now() version := &data.NodeVersion{ BaseNodeVersion: data.BaseNodeVersion{ @@ -155,7 +157,7 @@ func TestTreeServiceAddVersion(t *testing.T) { Size: 10, ETag: "etag", FilePath: "path/to/version", - Owner: usertest.ID(), + Owner: &userID, Created: &now, }, IsUnversioned: true,