[#269] Create frostfsid wrapper with cache
All checks were successful
/ DCO (pull_request) Successful in 2m10s
/ Vulncheck (pull_request) Successful in 2m0s
/ Builds (1.20) (pull_request) Successful in 2m31s
/ Builds (1.21) (pull_request) Successful in 1m31s
/ Lint (pull_request) Successful in 3m34s
/ Tests (1.20) (pull_request) Successful in 2m26s
/ Tests (1.21) (pull_request) Successful in 2m21s

Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
This commit is contained in:
Denis Kirillov 2024-03-12 11:32:51 +03:00
parent 43a687b572
commit 5315f7b733
5 changed files with 143 additions and 9 deletions

View file

@ -29,6 +29,7 @@ This document outlines major changes between releases.
- Authmate: support custom attributes (#292)
- Add new `reconnect_interval` config param (#291)
- Support `GetBucketPolicyStatus` (#301)
- Add FrostfsID cache (#269)
### Changed
- Generalise config param `use_default_xmlns_for_complete_multipart` to `use_default_xmlns` so that use default xmlns for all requests (#221)

View file

@ -6,8 +6,6 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-contract/frostfsid/client"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/logs"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine"
"github.com/bluele/gcache"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/util"
@ -20,11 +18,6 @@ type FrostfsIDCache struct {
logger *zap.Logger
}
type FrostfsIDCacheKey struct {
Target engine.Target
Name chain.Name
}
const (
// DefaultFrostfsIDCacheSize is a default maximum number of entries in cache.
DefaultFrostfsIDCacheSize = 1e4

View file

@ -30,6 +30,7 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/tokens"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/frostfs"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/frostfs/frostfsid"
ffidcontract "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/frostfs/frostfsid/contract"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/frostfs/policy"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/frostfs/policy/contract"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/frostfs/services"
@ -457,8 +458,7 @@ func (a *App) initMetrics() {
}
func (a *App) initFrostfsID(ctx context.Context) {
var err error
a.frostfsid, err = frostfsid.New(ctx, frostfsid.Config{
cli, err := ffidcontract.New(ctx, ffidcontract.Config{
RPCAddress: a.cfg.GetString(cfgRPCEndpoint),
Contract: a.cfg.GetString(cfgFrostfsIDContract),
ProxyContract: a.cfg.GetString(cfgProxyContract),
@ -467,6 +467,15 @@ func (a *App) initFrostfsID(ctx context.Context) {
if err != nil {
a.log.Fatal(logs.InitFrostfsIDContractFailed, zap.Error(err))
}
a.frostfsid, err = frostfsid.NewFrostFSID(frostfsid.Config{
Cache: cache.NewFrostfsIDCache(getFrostfsIDCacheConfig(a.cfg, a.log)),
FrostFSID: cli,
Logger: a.log,
})
if err != nil {
a.log.Fatal(logs.InitFrostfsIDContractFailed, zap.Error(err))
}
}
func (a *App) initPolicyStorage(ctx context.Context) {

View file

@ -0,0 +1,128 @@
package frostfsid
import (
"encoding/hex"
"errors"
"strconv"
"strings"
"git.frostfs.info/TrueCloudLab/frostfs-contract/frostfsid/client"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/cache"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/handler"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/frostfs/frostfsid/contract"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/logs"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/util"
"go.uber.org/zap"
)
type FrostFSID struct {
frostfsid *contract.FrostFSID
cache *cache.FrostfsIDCache
log *zap.Logger
}
type Config struct {
Cache *cache.FrostfsIDCache
FrostFSID *contract.FrostFSID
Logger *zap.Logger
}
var (
_ api.FrostFSID = (*FrostFSID)(nil)
_ handler.FrostFSID = (*FrostFSID)(nil)
)
// NewFrostFSID creates new FrostfsID wrapper.
func NewFrostFSID(cfg Config) (*FrostFSID, error) {
switch {
case cfg.FrostFSID == nil:
return nil, errors.New("missing frostfsid client")
case cfg.Cache == nil:
return nil, errors.New("missing frostfsid cache")
case cfg.Logger == nil:
return nil, errors.New("missing frostfsid logger")
}
return &FrostFSID{
frostfsid: cfg.FrostFSID,
cache: cfg.Cache,
log: cfg.Logger,
}, nil
}
func (f *FrostFSID) ValidatePublicKey(key *keys.PublicKey) error {
_, err := f.getSubject(key.GetScriptHash())
return err
}
func (f *FrostFSID) GetUserGroupIDs(userHash util.Uint160) ([]string, error) {
subj, err := f.getSubject(userHash)
if err != nil {
if strings.Contains(err.Error(), "not found") {
f.log.Debug(logs.UserGroupsListIsEmpty, zap.Error(err))
return nil, nil
}
return nil, err
}
res := make([]string, len(subj.Groups))
for i, group := range subj.Groups {
res[i] = strconv.FormatInt(group.ID, 10)
}
return res, nil
}
func (f *FrostFSID) getSubject(addr util.Uint160) (*client.SubjectExtended, error) {
if subj := f.cache.GetSubject(addr); subj != nil {
return subj, nil
}
subj, err := f.frostfsid.GetSubjectExtended(addr)
if err != nil {
return nil, err
}
if err = f.cache.PutSubject(addr, subj); err != nil {
f.log.Warn(logs.CouldntCacheSubject, zap.Error(err))
}
return subj, nil
}
func (f *FrostFSID) GetUserAddress(namespace, name string) (string, error) {
userKey, err := f.getUserKey(namespace, name)
if err != nil {
return "", err
}
return userKey.Address(), nil
}
func (f *FrostFSID) GetUserKey(namespace, name string) (string, error) {
userKey, err := f.getUserKey(namespace, name)
if err != nil {
return "", err
}
return hex.EncodeToString(userKey.Bytes()), nil
}
func (f *FrostFSID) getUserKey(namespace, name string) (*keys.PublicKey, error) {
if userKey := f.cache.GetUserKey(namespace, name); userKey != nil {
return userKey, nil
}
userKey, err := f.frostfsid.GetSubjectKeyByName(namespace, name)
if err != nil {
return nil, err
}
if err = f.cache.PutUserKey(namespace, name, userKey); err != nil {
f.log.Warn(logs.CouldntCacheUserKey, zap.Error(err))
}
return userKey, nil
}

View file

@ -154,4 +154,7 @@ const (
FailedToWriteResponse = "failed to write response"
WarnDuplicateAddress = "duplicate address"
PolicyCouldntBeConvertedToNativeRules = "policy couldn't be converted to native rules, only s3 rules be applied"
CouldntCacheSubject = "couldn't cache subject info"
UserGroupsListIsEmpty = "user groups list is empty, subject not found"
CouldntCacheUserKey = "couldn't cache user key"
)