From e22ff521652c41cf7b7391f851f7c15adea6bdb6 Mon Sep 17 00:00:00 2001 From: Marina Biryukova Date: Tue, 16 Apr 2024 11:20:35 +0300 Subject: [PATCH] [#367] Add check of AccessBox attributes Signed-off-by: Marina Biryukova --- api/auth/center.go | 7 ++-- api/auth/presign_test.go | 7 ++-- api/cache/accessbox.go | 13 ++++--- api/cache/cache_test.go | 5 ++- api/handler/acl_test.go | 8 ++--- api/handler/cors_test.go | 4 +-- api/handler/handlers_test.go | 2 +- api/handler/head_test.go | 2 +- api/handler/put_test.go | 22 ++++++------ api/layer/listing.go | 2 +- api/layer/versioning_test.go | 4 +-- api/middleware/auth.go | 8 ++--- api/middleware/policy.go | 7 ++++ api/middleware/util.go | 60 +++++++++++++++++--------------- api/router_mock_test.go | 7 ++-- api/router_test.go | 24 +++++++++++++ authmate/authmate.go | 4 +-- creds/tokens/credentials.go | 50 +++++++++++++------------- creds/tokens/credentials_test.go | 14 +++++--- go.mod | 2 +- go.sum | 4 +-- internal/frostfs/authmate.go | 11 +++--- 22 files changed, 157 insertions(+), 110 deletions(-) diff --git a/api/auth/center.go b/api/auth/center.go index 338088f..72fb7fe 100644 --- a/api/auth/center.go +++ b/api/auth/center.go @@ -186,7 +186,7 @@ func (c *Center) Authenticate(r *http.Request) (*middleware.Box, error) { return nil, err } - box, err := c.cli.GetBox(r.Context(), addr) + box, attrs, err := c.cli.GetBox(r.Context(), addr) if err != nil { return nil, fmt.Errorf("get box '%s': %w", addr, err) } @@ -207,6 +207,7 @@ func (c *Center) Authenticate(r *http.Request) (*middleware.Box, error) { Region: authHdr.Region, SignatureV4: authHdr.SignatureV4, }, + Attributes: attrs, } if needClientTime { result.ClientTime = signatureDateTime @@ -274,7 +275,7 @@ func (c *Center) checkFormData(r *http.Request) (*middleware.Box, error) { return nil, err } - box, err := c.cli.GetBox(r.Context(), addr) + box, attrs, err := c.cli.GetBox(r.Context(), addr) if err != nil { return nil, fmt.Errorf("get box '%s': %w", addr, err) } @@ -289,7 +290,7 @@ func (c *Center) checkFormData(r *http.Request) (*middleware.Box, error) { reqSignature, signature) } - return &middleware.Box{AccessBox: box}, nil + return &middleware.Box{AccessBox: box, Attributes: attrs}, nil } func cloneRequest(r *http.Request, authHeader *AuthHeader) *http.Request { diff --git a/api/auth/presign_test.go b/api/auth/presign_test.go index 9cebb81..9e5d330 100644 --- a/api/auth/presign_test.go +++ b/api/auth/presign_test.go @@ -10,6 +10,7 @@ import ( "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/tokens" apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" "github.com/aws/aws-sdk-go/aws/credentials" "github.com/stretchr/testify/require" @@ -31,13 +32,13 @@ func (m credentialsMock) addBox(addr oid.Address, box *accessbox.Box) { m.boxes[addr.String()] = box } -func (m credentialsMock) GetBox(_ context.Context, addr oid.Address) (*accessbox.Box, error) { +func (m credentialsMock) GetBox(_ context.Context, addr oid.Address) (*accessbox.Box, []object.Attribute, error) { box, ok := m.boxes[addr.String()] if !ok { - return nil, &apistatus.ObjectNotFound{} + return nil, nil, &apistatus.ObjectNotFound{} } - return box, nil + return box, nil, nil } func (m credentialsMock) Put(context.Context, cid.ID, tokens.CredentialsParam) (oid.Address, error) { diff --git a/api/cache/accessbox.go b/api/cache/accessbox.go index c8e3be9..9401804 100644 --- a/api/cache/accessbox.go +++ b/api/cache/accessbox.go @@ -6,6 +6,7 @@ import ( "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/logs" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" "github.com/bluele/gcache" "go.uber.org/zap" @@ -26,8 +27,9 @@ type ( } AccessBoxCacheValue struct { - Box *accessbox.Box - PutTime time.Time + Box *accessbox.Box + Attributes []object.Attribute + PutTime time.Time } ) @@ -72,10 +74,11 @@ func (o *AccessBoxCache) Get(address oid.Address) *AccessBoxCacheValue { } // Put stores an accessbox to cache. -func (o *AccessBoxCache) Put(address oid.Address, box *accessbox.Box) error { +func (o *AccessBoxCache) Put(address oid.Address, box *accessbox.Box, attrs []object.Attribute) error { val := &AccessBoxCacheValue{ - Box: box, - PutTime: time.Now(), + Box: box, + Attributes: attrs, + PutTime: time.Now(), } return o.cache.Set(address, val) } diff --git a/api/cache/cache_test.go b/api/cache/cache_test.go index 095a8fc..b4666c1 100644 --- a/api/cache/cache_test.go +++ b/api/cache/cache_test.go @@ -7,6 +7,7 @@ import ( "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox" cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/util" @@ -21,11 +22,13 @@ func TestAccessBoxCacheType(t *testing.T) { addr := oidtest.Address() box := &accessbox.Box{} + var attrs []object.Attribute - err := cache.Put(addr, box) + err := cache.Put(addr, box, attrs) require.NoError(t, err) val := cache.Get(addr) require.Equal(t, box, val.Box) + require.Equal(t, attrs, val.Attributes) require.Equal(t, 0, observedLog.Len()) err = cache.cache.Set(addr, "tmp") diff --git a/api/handler/acl_test.go b/api/handler/acl_test.go index f0c3b3f..838e682 100644 --- a/api/handler/acl_test.go +++ b/api/handler/acl_test.go @@ -1728,7 +1728,7 @@ func createBucketAssertS3Error(hc *handlerContext, bktName string, box *accessbo func createBucketBase(hc *handlerContext, bktName string, box *accessbox.Box) *httptest.ResponseRecorder { w, r := prepareTestRequest(hc, bktName, "", nil) - ctx := middleware.SetBoxData(r.Context(), box) + ctx := middleware.SetBox(r.Context(), &middleware.Box{AccessBox: box}) r = r.WithContext(ctx) hc.Handler().CreateBucketHandler(w, r) return w @@ -1749,7 +1749,7 @@ func putBucketACLBase(hc *handlerContext, bktName string, box *accessbox.Box, he for key, val := range header { r.Header.Set(key, val) } - ctx := middleware.SetBoxData(r.Context(), box) + ctx := middleware.SetBox(r.Context(), &middleware.Box{AccessBox: box}) r = r.WithContext(ctx) hc.Handler().PutBucketACLHandler(w, r) return w @@ -1779,7 +1779,7 @@ func putObjectACLBase(hc *handlerContext, bktName, objName string, box *accessbo for key, val := range header { r.Header.Set(key, val) } - ctx := middleware.SetBoxData(r.Context(), box) + ctx := middleware.SetBox(r.Context(), &middleware.Box{AccessBox: box}) r = r.WithContext(ctx) hc.Handler().PutObjectACLHandler(w, r) return w @@ -1818,7 +1818,7 @@ func putObjectWithHeadersBase(hc *handlerContext, bktName, objName string, heade r.Header.Set(k, v) } - ctx := middleware.SetBoxData(r.Context(), box) + ctx := middleware.SetBox(r.Context(), &middleware.Box{AccessBox: box}) r = r.WithContext(ctx) hc.Handler().PutObjectHandler(w, r) diff --git a/api/handler/cors_test.go b/api/handler/cors_test.go index 0bfad16..1c4bd9e 100644 --- a/api/handler/cors_test.go +++ b/api/handler/cors_test.go @@ -23,14 +23,14 @@ func TestCORSOriginWildcard(t *testing.T) { bktName := "bucket-for-cors" box, _ := createAccessBox(t) w, r := prepareTestRequest(hc, bktName, "", nil) - ctx := middleware.SetBoxData(r.Context(), box) + ctx := middleware.SetBox(r.Context(), &middleware.Box{AccessBox: box}) r = r.WithContext(ctx) r.Header.Add(api.AmzACL, "public-read") hc.Handler().CreateBucketHandler(w, r) assertStatus(t, w, http.StatusOK) w, r = prepareTestPayloadRequest(hc, bktName, "", strings.NewReader(body)) - ctx = middleware.SetBoxData(r.Context(), box) + ctx = middleware.SetBox(r.Context(), &middleware.Box{AccessBox: box}) r = r.WithContext(ctx) hc.Handler().PutBucketCorsHandler(w, r) assertStatus(t, w, http.StatusOK) diff --git a/api/handler/handlers_test.go b/api/handler/handlers_test.go index 9b8f3f4..eb677d5 100644 --- a/api/handler/handlers_test.go +++ b/api/handler/handlers_test.go @@ -192,7 +192,7 @@ func prepareHandlerContextBase(t *testing.T, cacheCfg *layer.CachesConfig) *hand h: h, tp: tp, tree: treeMock, - context: middleware.SetBoxData(context.Background(), newTestAccessBox(t, key)), + context: middleware.SetBox(context.Background(), &middleware.Box{AccessBox: newTestAccessBox(t, key)}), config: cfg, layerFeatures: features, diff --git a/api/handler/head_test.go b/api/handler/head_test.go index 821f651..fc68a17 100644 --- a/api/handler/head_test.go +++ b/api/handler/head_test.go @@ -94,7 +94,7 @@ func TestInvalidAccessThroughCache(t *testing.T) { headObject(t, hc, bktName, objName, nil, http.StatusOK) w, r := prepareTestRequest(hc, bktName, objName, nil) - hc.Handler().HeadObjectHandler(w, r.WithContext(middleware.SetBoxData(r.Context(), newTestAccessBox(t, nil)))) + hc.Handler().HeadObjectHandler(w, r.WithContext(middleware.SetBox(r.Context(), &middleware.Box{AccessBox: newTestAccessBox(t, nil)}))) assertStatus(t, w, http.StatusForbidden) } diff --git a/api/handler/put_test.go b/api/handler/put_test.go index 889bdef..4ff00da 100644 --- a/api/handler/put_test.go +++ b/api/handler/put_test.go @@ -353,15 +353,17 @@ func getChunkedRequest(ctx context.Context, t *testing.T, bktName, objName strin w := httptest.NewRecorder() reqInfo := middleware.NewReqInfo(w, req, middleware.ObjectRequest{Bucket: bktName, Object: objName}) req = req.WithContext(middleware.SetReqInfo(ctx, reqInfo)) - req = req.WithContext(middleware.SetClientTime(req.Context(), signTime)) - req = req.WithContext(middleware.SetAuthHeaders(req.Context(), &middleware.AuthHeader{ - AccessKeyID: AWSAccessKeyID, - SignatureV4: "4f232c4386841ef735655705268965c44a0e4690baa4adea153f7db9fa80a0a9", - Region: "us-east-1", - })) - req = req.WithContext(middleware.SetBoxData(req.Context(), &accessbox.Box{ - Gate: &accessbox.GateData{ - SecretKey: AWSSecretAccessKey, + req = req.WithContext(middleware.SetBox(req.Context(), &middleware.Box{ + ClientTime: signTime, + AuthHeaders: &middleware.AuthHeader{ + AccessKeyID: AWSAccessKeyID, + SignatureV4: "4f232c4386841ef735655705268965c44a0e4690baa4adea153f7db9fa80a0a9", + Region: "us-east-1", + }, + AccessBox: &accessbox.Box{ + Gate: &accessbox.GateData{ + SecretKey: AWSSecretAccessKey, + }, }, })) @@ -401,7 +403,7 @@ func TestCreateNamespacedBucket(t *testing.T) { box, _ := createAccessBox(t) w, r := prepareTestRequest(hc, bktName, "", nil) - ctx := middleware.SetBoxData(r.Context(), box) + ctx := middleware.SetBox(r.Context(), &middleware.Box{AccessBox: box}) reqInfo := middleware.GetReqInfo(ctx) reqInfo.Namespace = namespace r = r.WithContext(middleware.SetReqInfo(ctx, reqInfo)) diff --git a/api/layer/listing.go b/api/layer/listing.go index 2de4a21..cda75d1 100644 --- a/api/layer/listing.go +++ b/api/layer/listing.go @@ -334,7 +334,7 @@ func (n *layer) initNewVersionsByPrefixSession(ctx context.Context, p commonVers session.Context, session.Cancel = context.WithCancel(context.Background()) if bd, err := middleware.GetBoxData(ctx); err == nil { - session.Context = middleware.SetBoxData(session.Context, bd) + session.Context = middleware.SetBox(session.Context, &middleware.Box{AccessBox: bd}) } session.Stream, err = n.treeService.InitVersionsByPrefixStream(session.Context, p.BktInfo, p.Prefix, latestOnly) diff --git a/api/layer/versioning_test.go b/api/layer/versioning_test.go index 705ec00..2c5ca5f 100644 --- a/api/layer/versioning_test.go +++ b/api/layer/versioning_test.go @@ -145,12 +145,12 @@ func prepareContext(t *testing.T, cachesConfig ...*CachesConfig) *testContext { bearerToken := bearertest.Token() require.NoError(t, bearerToken.Sign(key.PrivateKey)) - ctx := middleware.SetBoxData(context.Background(), &accessbox.Box{ + ctx := middleware.SetBox(context.Background(), &middleware.Box{AccessBox: &accessbox.Box{ Gate: &accessbox.GateData{ BearerToken: &bearerToken, GateKey: key.PublicKey(), }, - }) + }}) tp := NewTestFrostFS(key) bktName := "testbucket1" diff --git a/api/middleware/auth.go b/api/middleware/auth.go index 950e3c7..c5318ca 100644 --- a/api/middleware/auth.go +++ b/api/middleware/auth.go @@ -13,6 +13,7 @@ import ( frostfsErrors "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/frostfs/errors" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/logs" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "go.uber.org/zap" ) @@ -23,6 +24,7 @@ type ( AccessBox *accessbox.Box ClientTime time.Time AuthHeaders *AuthHeader + Attributes []object.Attribute } // Center is a user authentication interface. @@ -65,11 +67,7 @@ func Auth(center Center, log *zap.Logger) Func { return } } else { - ctx = SetBoxData(ctx, box.AccessBox) - if !box.ClientTime.IsZero() { - ctx = SetClientTime(ctx, box.ClientTime) - } - ctx = SetAuthHeaders(ctx, box.AuthHeaders) + ctx = SetBox(ctx, box) if box.AccessBox.Gate.BearerToken != nil { reqInfo.User = bearer.ResolveIssuer(*box.AccessBox.Gate.BearerToken).String() diff --git a/api/middleware/policy.go b/api/middleware/policy.go index 48f1eb3..3af3fd5 100644 --- a/api/middleware/policy.go +++ b/api/middleware/policy.go @@ -444,6 +444,13 @@ func determineProperties(r *http.Request, decoder XMLDecoder, resolver BucketRes res[k] = v } + attrs, err := GetAccessBoxAttrs(r.Context()) + if err == nil { + for _, attr := range attrs { + res[fmt.Sprintf(s3.PropertyKeyFormatAccessBoxAttr, attr.Key())] = attr.Value() + } + } + return res, nil } diff --git a/api/middleware/util.go b/api/middleware/util.go index 8bdd38a..dd62c55 100644 --- a/api/middleware/util.go +++ b/api/middleware/util.go @@ -6,29 +6,27 @@ import ( "time" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" ) // keyWrapper is wrapper for context keys. type keyWrapper string -// authHeaders is a wrapper for authentication headers of a request. -var authHeadersKey = keyWrapper("__context_auth_headers_key") - -// boxData is an ID used to store accessbox.Box in a context. -var boxDataKey = keyWrapper("__context_box_key") - -// clientTime is an ID used to store client time.Time in a context. -var clientTimeKey = keyWrapper("__context_client_time") +// boxKey is an ID used to store Box in a context. +var boxKey = keyWrapper("__context_box_key") // GetBoxData extracts accessbox.Box from context. func GetBoxData(ctx context.Context) (*accessbox.Box, error) { - var box *accessbox.Box - data, ok := ctx.Value(boxDataKey).(*accessbox.Box) + data, ok := ctx.Value(boxKey).(*Box) if !ok || data == nil { + return nil, fmt.Errorf("couldn't get box from context") + } + + if data.AccessBox == nil { return nil, fmt.Errorf("couldn't get box data from context") } - box = data + box := data.AccessBox if box.Gate == nil { box.Gate = &accessbox.GateData{} } @@ -37,35 +35,39 @@ func GetBoxData(ctx context.Context) (*accessbox.Box, error) { // GetAuthHeaders extracts auth.AuthHeader from context. func GetAuthHeaders(ctx context.Context) (*AuthHeader, error) { - authHeaders, ok := ctx.Value(authHeadersKey).(*AuthHeader) - if !ok { - return nil, fmt.Errorf("couldn't get auth headers from context") + data, ok := ctx.Value(boxKey).(*Box) + if !ok || data == nil { + return nil, fmt.Errorf("couldn't get box from context") } - return authHeaders, nil + return data.AuthHeaders, nil } // GetClientTime extracts time.Time from context. func GetClientTime(ctx context.Context) (time.Time, error) { - clientTime, ok := ctx.Value(clientTimeKey).(time.Time) - if !ok { + data, ok := ctx.Value(boxKey).(*Box) + if !ok || data == nil { + return time.Time{}, fmt.Errorf("couldn't get box from context") + } + + if data.ClientTime.IsZero() { return time.Time{}, fmt.Errorf("couldn't get client time from context") } - return clientTime, nil + return data.ClientTime, nil } -// SetBoxData sets accessbox.Box in the context. -func SetBoxData(ctx context.Context, box *accessbox.Box) context.Context { - return context.WithValue(ctx, boxDataKey, box) +// GetAccessBoxAttrs extracts []object.Attribute from context. +func GetAccessBoxAttrs(ctx context.Context) ([]object.Attribute, error) { + data, ok := ctx.Value(boxKey).(*Box) + if !ok || data == nil { + return nil, fmt.Errorf("couldn't get box from context") + } + + return data.Attributes, nil } -// SetAuthHeaders sets auth.AuthHeader in the context. -func SetAuthHeaders(ctx context.Context, header *AuthHeader) context.Context { - return context.WithValue(ctx, authHeadersKey, header) -} - -// SetClientTime sets time.Time in the context. -func SetClientTime(ctx context.Context, newTime time.Time) context.Context { - return context.WithValue(ctx, clientTimeKey, newTime) +// SetBox sets Box in the context. +func SetBox(ctx context.Context, box *Box) context.Context { + return context.WithValue(ctx, boxKey, box) } diff --git a/api/router_mock_test.go b/api/router_mock_test.go index 8747b1a..49ff865 100644 --- a/api/router_mock_test.go +++ b/api/router_mock_test.go @@ -15,6 +15,7 @@ import ( "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer" bearertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer/test" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/util" @@ -31,8 +32,9 @@ func (p *poolStatisticMock) Statistic() pool.Statistic { } type centerMock struct { - t *testing.T - anon bool + t *testing.T + anon bool + attrs []object.Attribute } func (c *centerMock) Authenticate(*http.Request) (*middleware.Box, error) { @@ -53,6 +55,7 @@ func (c *centerMock) Authenticate(*http.Request) (*middleware.Box, error) { BearerToken: token, }, }, + Attributes: c.attrs, }, nil } diff --git a/api/router_test.go b/api/router_test.go index a401196..fdeb851 100644 --- a/api/router_test.go +++ b/api/router_test.go @@ -17,6 +17,7 @@ import ( apiErrors "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors" s3middleware "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/metrics" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" engineiam "git.frostfs.info/TrueCloudLab/policy-engine/iam" "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain" "git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine" @@ -576,6 +577,29 @@ func TestResourceTagsCheck(t *testing.T) { }) } +func TestAccessBoxAttributesCheck(t *testing.T) { + router := prepareRouter(t) + + ns, bktName, attrKey, attrValue := "", "bucket", "key", "true" + router.middlewareSettings.denyByDefault = true + + allowOperations(router, ns, []string{"s3:CreateBucket"}, nil) + createBucket(router, ns, bktName) + + // Add policy and check + allowOperations(router, ns, []string{"s3:ListBucket"}, engineiam.Conditions{ + engineiam.CondBool: engineiam.Condition{fmt.Sprintf(s3.PropertyKeyFormatAccessBoxAttr, attrKey): []string{attrValue}}, + }) + + listObjectsV1Err(router, ns, bktName, "", "", "", apiErrors.ErrAccessDenied) + + var attr object.Attribute + attr.SetKey(attrKey) + attr.SetValue(attrValue) + router.cfg.Center.(*centerMock).attrs = []object.Attribute{attr} + listObjectsV1(router, ns, bktName, "", "", "") +} + func allowOperations(router *routerMock, ns string, operations []string, conditions engineiam.Conditions) { addPolicy(router, ns, "allow", engineiam.AllowEffect, operations, conditions) } diff --git a/authmate/authmate.go b/authmate/authmate.go index 7b19ee0..f35d549 100644 --- a/authmate/authmate.go +++ b/authmate/authmate.go @@ -339,7 +339,7 @@ func (a *Agent) UpdateSecret(ctx context.Context, w io.Writer, options *UpdateSe creds := tokens.New(cfg) - box, err := creds.GetBox(ctx, options.Address) + box, _, err := creds.GetBox(ctx, options.Address) if err != nil { return fmt.Errorf("get accessbox: %w", err) } @@ -426,7 +426,7 @@ func (a *Agent) ObtainSecret(ctx context.Context, w io.Writer, options *ObtainSe return fmt.Errorf("failed to parse secret address: %w", err) } - box, err := bearerCreds.GetBox(ctx, addr) + box, _, err := bearerCreds.GetBox(ctx, addr) if err != nil { return fmt.Errorf("failed to get tokens: %w", err) } diff --git a/creds/tokens/credentials.go b/creds/tokens/credentials.go index 24abb4b..f926536 100644 --- a/creds/tokens/credentials.go +++ b/creds/tokens/credentials.go @@ -22,7 +22,7 @@ import ( type ( // Credentials is a bearer token get/put interface. Credentials interface { - GetBox(context.Context, oid.Address) (*accessbox.Box, error) + GetBox(context.Context, oid.Address) (*accessbox.Box, []object.Attribute, error) Put(context.Context, cid.ID, CredentialsParam) (oid.Address, error) Update(context.Context, oid.Address, CredentialsParam) (oid.Address, error) } @@ -86,13 +86,13 @@ type FrostFS interface { // prevented the object from being created. CreateObject(context.Context, PrmObjectCreate) (oid.ID, error) - // GetCredsPayload gets payload of the credential object from FrostFS network. + // GetCredsObject gets the credential object from FrostFS network. // It uses search by system name and select using CRDT 2PSet. In case of absence CRDT header // it heads object by address. // // It returns exactly one non-nil value. It returns any error encountered which // prevented the object payload from being read. - GetCredsPayload(context.Context, oid.Address) ([]byte, error) + GetCredsObject(context.Context, oid.Address) (*object.Object, error) } var ( @@ -115,72 +115,72 @@ func New(cfg Config) Credentials { } } -func (c *cred) GetBox(ctx context.Context, addr oid.Address) (*accessbox.Box, error) { +func (c *cred) GetBox(ctx context.Context, addr oid.Address) (*accessbox.Box, []object.Attribute, error) { cachedBoxValue := c.cache.Get(addr) if cachedBoxValue != nil { return c.checkIfCredentialsAreRemoved(ctx, addr, cachedBoxValue) } - box, err := c.getAccessBox(ctx, addr) + box, attrs, err := c.getAccessBox(ctx, addr) if err != nil { - return nil, fmt.Errorf("get access box: %w", err) + return nil, nil, fmt.Errorf("get access box: %w", err) } cachedBox, err := box.GetBox(c.key) if err != nil { - return nil, fmt.Errorf("get gate box: %w", err) + return nil, nil, fmt.Errorf("get gate box: %w", err) } - c.putBoxToCache(addr, cachedBox) + c.putBoxToCache(addr, cachedBox, attrs) - return cachedBox, nil + return cachedBox, attrs, nil } -func (c *cred) checkIfCredentialsAreRemoved(ctx context.Context, addr oid.Address, cachedBoxValue *cache.AccessBoxCacheValue) (*accessbox.Box, error) { +func (c *cred) checkIfCredentialsAreRemoved(ctx context.Context, addr oid.Address, cachedBoxValue *cache.AccessBoxCacheValue) (*accessbox.Box, []object.Attribute, error) { if time.Since(cachedBoxValue.PutTime) < c.removingCheckDuration { - return cachedBoxValue.Box, nil + return cachedBoxValue.Box, cachedBoxValue.Attributes, nil } - box, err := c.getAccessBox(ctx, addr) + box, attrs, err := c.getAccessBox(ctx, addr) if err != nil { if client.IsErrObjectAlreadyRemoved(err) { c.cache.Delete(addr) - return nil, fmt.Errorf("get access box: %w", err) + return nil, nil, fmt.Errorf("get access box: %w", err) } - return cachedBoxValue.Box, nil + return cachedBoxValue.Box, cachedBoxValue.Attributes, nil } cachedBox, err := box.GetBox(c.key) if err != nil { c.cache.Delete(addr) - return nil, fmt.Errorf("get gate box: %w", err) + return nil, nil, fmt.Errorf("get gate box: %w", err) } // we need this to reset PutTime // to don't check for removing each time after removingCheckDuration interval - c.putBoxToCache(addr, cachedBox) + c.putBoxToCache(addr, cachedBox, attrs) - return cachedBoxValue.Box, nil + return cachedBoxValue.Box, attrs, nil } -func (c *cred) putBoxToCache(addr oid.Address, box *accessbox.Box) { - if err := c.cache.Put(addr, box); err != nil { +func (c *cred) putBoxToCache(addr oid.Address, box *accessbox.Box, attrs []object.Attribute) { + if err := c.cache.Put(addr, box, attrs); err != nil { c.log.Warn(logs.CouldntPutAccessBoxIntoCache, zap.String("address", addr.EncodeToString())) } } -func (c *cred) getAccessBox(ctx context.Context, addr oid.Address) (*accessbox.AccessBox, error) { - data, err := c.frostFS.GetCredsPayload(ctx, addr) +func (c *cred) getAccessBox(ctx context.Context, addr oid.Address) (*accessbox.AccessBox, []object.Attribute, error) { + obj, err := c.frostFS.GetCredsObject(ctx, addr) if err != nil { - return nil, fmt.Errorf("read payload: %w", err) + return nil, nil, fmt.Errorf("read payload and attributes: %w", err) } // decode access box var box accessbox.AccessBox - if err = box.Unmarshal(data); err != nil { - return nil, fmt.Errorf("unmarhal access box: %w", err) + if err = box.Unmarshal(obj.Payload()); err != nil { + return nil, nil, fmt.Errorf("unmarhal access box: %w", err) } - return &box, nil + return &box, obj.Attributes(), nil } func (c *cred) Put(ctx context.Context, idCnr cid.ID, prm CredentialsParam) (oid.Address, error) { diff --git a/creds/tokens/credentials_test.go b/creds/tokens/credentials_test.go index dfd3c56..b38a465 100644 --- a/creds/tokens/credentials_test.go +++ b/creds/tokens/credentials_test.go @@ -11,6 +11,7 @@ import ( "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer" apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" @@ -27,7 +28,7 @@ func (f *frostfsMock) CreateObject(context.Context, PrmObjectCreate) (oid.ID, er panic("implement me for test") } -func (f *frostfsMock) GetCredsPayload(_ context.Context, address oid.Address) ([]byte, error) { +func (f *frostfsMock) GetCredsObject(_ context.Context, address oid.Address) (*object.Object, error) { if err := f.errors[address]; err != nil { return nil, err } @@ -36,7 +37,10 @@ func (f *frostfsMock) GetCredsPayload(_ context.Context, address oid.Address) ([ if !ok { return nil, errors.New("not found") } - return data, nil + + var obj object.Object + obj.SetPayload(data) + return &obj, nil } func TestRemovingAccessBox(t *testing.T) { @@ -78,14 +82,14 @@ func TestRemovingAccessBox(t *testing.T) { creds := New(cfg) - _, err = creds.GetBox(ctx, addr) + _, _, err = creds.GetBox(ctx, addr) require.NoError(t, err) frostfs.errors[addr] = errors.New("network error") - _, err = creds.GetBox(ctx, addr) + _, _, err = creds.GetBox(ctx, addr) require.NoError(t, err) frostfs.errors[addr] = &apistatus.ObjectAlreadyRemoved{} - _, err = creds.GetBox(ctx, addr) + _, _, err = creds.GetBox(ctx, addr) require.Error(t, err) } diff --git a/go.mod b/go.mod index 9a36895..b17e58c 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240409115729-6eb492025bdd git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20230531082742-c97d21411eb6 git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240402141549-3790142b10c7 - git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240412102212-530248de754c + git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240416071728-04a79f57ef1f git.frostfs.info/TrueCloudLab/zapjournald v0.0.0-20240124114243-cb2e66427d02 github.com/aws/aws-sdk-go v1.44.6 github.com/bluele/gcache v0.0.2 diff --git a/go.sum b/go.sum index e64ba5b..042a80e 100644 --- a/go.sum +++ b/go.sum @@ -48,8 +48,8 @@ git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240402141549-3790142b10c7 git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240402141549-3790142b10c7/go.mod h1:i0RKqiF4z3UOxLSNwhHw+cUz/JyYWuTRpnn9ere4Y3w= 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-20240412102212-530248de754c h1:Ei15WKKLDXoFqEIe292szb3RfsyLqRZfeJX2FjFvz6k= -git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240412102212-530248de754c/go.mod h1:H/AW85RtYxVTbcgwHW76DqXeKlsiCIOeNXHPqyDBrfQ= +git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240416071728-04a79f57ef1f h1:hP1Q/MJvRsHSBIWXn48C+hVsRHfPWWLhdOg6IxjaWBs= +git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240416071728-04a79f57ef1f/go.mod h1:H/AW85RtYxVTbcgwHW76DqXeKlsiCIOeNXHPqyDBrfQ= git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0 h1:M2KR3iBj7WpY3hP10IevfIB9MURr4O9mwVfJ+SjT3HA= git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0/go.mod h1:okpbKfVYf/BpejtfFTfhZqFP+sZ8rsHrP8Rr/jYPNRc= git.frostfs.info/TrueCloudLab/tzhash v1.8.0 h1:UFMnUIk0Zh17m8rjGHJMqku2hCgaXDqjqZzS4gsb4UA= diff --git a/internal/frostfs/authmate.go b/internal/frostfs/authmate.go index 42bade3..9de2263 100644 --- a/internal/frostfs/authmate.go +++ b/internal/frostfs/authmate.go @@ -4,7 +4,6 @@ import ( "bytes" "context" "fmt" - "io" "strconv" "time" @@ -15,6 +14,7 @@ import ( "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/frostfs/crdt" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" @@ -69,8 +69,8 @@ func (x *AuthmateFrostFS) CreateContainer(ctx context.Context, prm authmate.PrmC return res.ContainerID, nil } -// GetCredsPayload implements authmate.FrostFS interface method. -func (x *AuthmateFrostFS) GetCredsPayload(ctx context.Context, addr oid.Address) ([]byte, error) { +// GetCredsObject implements authmate.FrostFS interface method. +func (x *AuthmateFrostFS) GetCredsObject(ctx context.Context, addr oid.Address) (*object.Object, error) { versions, err := x.getCredVersions(ctx, addr) if err != nil { return nil, err @@ -85,14 +85,13 @@ func (x *AuthmateFrostFS) GetCredsPayload(ctx context.Context, addr oid.Address) Container: addr.Container(), Object: credObjID, WithPayload: true, + WithHeader: true, }) if err != nil { return nil, err } - defer res.Payload.Close() - - return io.ReadAll(res.Payload) + return res.Head, err } // CreateObject implements authmate.FrostFS interface method.