package frostfsid import ( "context" "encoding/hex" "fmt" "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/handler" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/authmate" frostfsutil "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/frostfs/util" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/rpcclient" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/wallet" ) type FrostFSID struct { cli *client.Client } type Config struct { // RPCAddress is an endpoint to connect to neo rpc. RPCAddress string // Contract is hash of contract or its name in NNS. Contract string // ProxyContract is hash of proxy contract or its name in NNS to interact with frostfsid. ProxyContract string // Key is used to interact with frostfsid contract. // If this is nil than random key will be generated. Key *keys.PrivateKey } var ( _ api.FrostFSID = (*FrostFSID)(nil) _ authmate.FrostFSID = (*FrostFSID)(nil) _ handler.FrostFSID = (*FrostFSID)(nil) ) // New creates new FrostfsID contract wrapper that implements auth.FrostFSID interface. func New(ctx context.Context, cfg Config) (*FrostFSID, error) { contractHash, err := frostfsutil.ResolveContractHash(cfg.Contract, cfg.RPCAddress) if err != nil { return nil, fmt.Errorf("resolve frostfs contract hash: %w", err) } key := cfg.Key if key == nil { if key, err = keys.NewPrivateKey(); err != nil { return nil, fmt.Errorf("generate anon private key for frostfsid: %w", err) } } rpcCli, err := rpcclient.New(ctx, cfg.RPCAddress, rpcclient.Options{}) if err != nil { return nil, fmt.Errorf("init rpc client: %w", err) } var opt client.Options opt.ProxyContract, err = frostfsutil.ResolveContractHash(cfg.ProxyContract, cfg.RPCAddress) if err != nil { return nil, fmt.Errorf("resolve frostfs contract hash: %w", err) } cli, err := client.New(rpcCli, wallet.NewAccountFromPrivateKey(key), contractHash, opt) if err != nil { return nil, fmt.Errorf("init frostfsid client: %w", err) } return &FrostFSID{ cli: cli, }, nil } func (f *FrostFSID) ValidatePublicKey(key *keys.PublicKey) error { _, err := f.cli.GetSubjectByKey(key) return err } func (f *FrostFSID) RegisterPublicKey(ns string, key *keys.PublicKey) error { _, err := f.cli.Wait(f.cli.CreateSubject(ns, key)) if err != nil && !strings.Contains(err.Error(), "subject already exists") { return err } return nil } func (f *FrostFSID) GetUserAddress(namespace, name string) (string, error) { key, err := f.cli.GetSubjectKeyByName(namespace, name) if err != nil { return "", err } return key.Address(), nil } func (f *FrostFSID) GetUserKey(account, name string) (string, error) { key, err := f.cli.GetSubjectKeyByName(account, name) if err != nil { return "", err } return hex.EncodeToString(key.Bytes()), nil } func (f *FrostFSID) GetUserGroupIDs(userHash util.Uint160) ([]string, error) { subjExt, err := f.cli.GetSubjectExtended(userHash) if err != nil { if strings.Contains(err.Error(), "not found") { return nil, nil } return nil, err } res := make([]string, len(subjExt.Groups)) for i, group := range subjExt.Groups { res[i] = strconv.FormatInt(group.ID, 10) } return res, nil }