Compare commits
3 commits
master
...
feat/ape_m
Author | SHA1 | Date | |
---|---|---|---|
54bc1ee58a | |||
af68ea0051 | |||
a8a33a5e1d |
14 changed files with 841 additions and 0 deletions
141
cmd/frostfs-cli/modules/ape_manager/add_chain.go
Normal file
141
cmd/frostfs-cli/modules/ape_manager/add_chain.go
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
package apemanager
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
internalclient "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/util"
|
||||||
|
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
|
apemanager_sdk "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apemanager"
|
||||||
|
client_sdk "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
||||||
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
|
apechain "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
chainIDFlag = "chain-id"
|
||||||
|
chainIDHexFlag = "chain-id-hex"
|
||||||
|
ruleFlag = "rule"
|
||||||
|
pathFlag = "path"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
targetNameFlag = "target-name"
|
||||||
|
targetNameDesc = "Resource name in APE resource name format"
|
||||||
|
targetTypeFlag = "target-type"
|
||||||
|
targetTypeDesc = "Resource type(container/namespace)"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultNamespace = ""
|
||||||
|
namespaceTarget = "namespace"
|
||||||
|
containerTarget = "container"
|
||||||
|
userTarget = "user"
|
||||||
|
groupTarget = "group"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errUnknownTargetType = errors.New("unknown target type")
|
||||||
|
)
|
||||||
|
|
||||||
|
var addCmd = &cobra.Command{
|
||||||
|
Use: "add",
|
||||||
|
Short: "Add rule chain for a target",
|
||||||
|
Run: add,
|
||||||
|
PersistentPreRun: func(cmd *cobra.Command, _ []string) {
|
||||||
|
commonflags.Bind(cmd)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseTarget(cmd *cobra.Command) (ct apemanager_sdk.ChainTarget) {
|
||||||
|
typ, _ := cmd.Flags().GetString(targetTypeFlag)
|
||||||
|
name, _ := cmd.Flags().GetString(targetNameFlag)
|
||||||
|
|
||||||
|
ct.Name = name
|
||||||
|
|
||||||
|
switch typ {
|
||||||
|
case namespaceTarget:
|
||||||
|
ct.TargetType = apemanager_sdk.TargetTypeNamespace
|
||||||
|
case containerTarget:
|
||||||
|
var cnr cid.ID
|
||||||
|
commonCmd.ExitOnErr(cmd, "can't decode container ID: %w", cnr.DecodeString(name))
|
||||||
|
ct.TargetType = apemanager_sdk.TargetTypeContainer
|
||||||
|
case userTarget:
|
||||||
|
ct.TargetType = apemanager_sdk.TargetTypeUser
|
||||||
|
case groupTarget:
|
||||||
|
ct.TargetType = apemanager_sdk.TargetTypeGroup
|
||||||
|
default:
|
||||||
|
commonCmd.ExitOnErr(cmd, "read target type error: %w", errUnknownTargetType)
|
||||||
|
}
|
||||||
|
return ct
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseChain(cmd *cobra.Command) apemanager_sdk.Chain {
|
||||||
|
chainID, _ := cmd.Flags().GetString(chainIDFlag)
|
||||||
|
hexEncoded, _ := cmd.Flags().GetBool(chainIDHexFlag)
|
||||||
|
|
||||||
|
chainIDRaw := []byte(chainID)
|
||||||
|
|
||||||
|
if hexEncoded {
|
||||||
|
var err error
|
||||||
|
chainIDRaw, err = hex.DecodeString(chainID)
|
||||||
|
commonCmd.ExitOnErr(cmd, "can't decode chain ID as hex: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
chain := new(apechain.Chain)
|
||||||
|
chain.ID = apechain.ID(chainIDRaw)
|
||||||
|
|
||||||
|
if rules, _ := cmd.Flags().GetStringArray(ruleFlag); len(rules) > 0 {
|
||||||
|
commonCmd.ExitOnErr(cmd, "parser error: %w", util.ParseAPEChain(chain, rules))
|
||||||
|
} else if encPath, _ := cmd.Flags().GetString(pathFlag); encPath != "" {
|
||||||
|
commonCmd.ExitOnErr(cmd, "decode binary or json error: %w", util.ParseAPEChainBinaryOrJSON(chain, encPath))
|
||||||
|
} else {
|
||||||
|
commonCmd.ExitOnErr(cmd, "parser error: %w", errors.New("rule is not passed"))
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Println("Parsed chain:")
|
||||||
|
util.PrintHumanReadableAPEChain(cmd, chain)
|
||||||
|
|
||||||
|
serialized := chain.Bytes()
|
||||||
|
return apemanager_sdk.Chain{
|
||||||
|
Raw: serialized,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func add(cmd *cobra.Command, _ []string) {
|
||||||
|
c := parseChain(cmd)
|
||||||
|
|
||||||
|
target := parseTarget(cmd)
|
||||||
|
|
||||||
|
key := key.Get(cmd)
|
||||||
|
cli := internalclient.GetSDKClientByFlag(cmd, key, commonflags.RPC)
|
||||||
|
|
||||||
|
res, err := cli.APEManagerAddChain(cmd.Context(), client_sdk.PrmAPEManagerAddChain{
|
||||||
|
ChainTarget: target,
|
||||||
|
Chain: c,
|
||||||
|
})
|
||||||
|
|
||||||
|
commonCmd.ExitOnErr(cmd, "add chain error: %w", err)
|
||||||
|
|
||||||
|
cmd.Println("Rule has been added.")
|
||||||
|
cmd.Println("Chain ID: ", string(res.ChainID))
|
||||||
|
}
|
||||||
|
|
||||||
|
func initAddCmd() {
|
||||||
|
commonflags.Init(addCmd)
|
||||||
|
|
||||||
|
ff := addCmd.Flags()
|
||||||
|
ff.StringArray(ruleFlag, []string{}, "Rule statement")
|
||||||
|
ff.String(pathFlag, "", "Path to encoded chain in JSON or binary format")
|
||||||
|
ff.String(chainIDFlag, "", "Assign ID to the parsed chain")
|
||||||
|
ff.String(targetNameFlag, "", targetNameDesc)
|
||||||
|
ff.String(targetTypeFlag, "", targetTypeDesc)
|
||||||
|
_ = addCmd.MarkFlagRequired(targetTypeFlag)
|
||||||
|
ff.Bool(chainIDHexFlag, false, "Flag to parse chain ID as hex")
|
||||||
|
|
||||||
|
addCmd.MarkFlagsMutuallyExclusive(pathFlag, ruleFlag)
|
||||||
|
}
|
49
cmd/frostfs-cli/modules/ape_manager/list_chain.go
Normal file
49
cmd/frostfs-cli/modules/ape_manager/list_chain.go
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
package apemanager
|
||||||
|
|
||||||
|
import (
|
||||||
|
internalclient "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
|
||||||
|
apeutil "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/util"
|
||||||
|
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
|
client_sdk "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
||||||
|
apechain "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var listCmd = &cobra.Command{
|
||||||
|
Use: "list",
|
||||||
|
Short: "List rule chains defined on target",
|
||||||
|
Run: list,
|
||||||
|
PersistentPreRun: func(cmd *cobra.Command, _ []string) {
|
||||||
|
commonflags.Bind(cmd)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func list(cmd *cobra.Command, _ []string) {
|
||||||
|
target := parseTarget(cmd)
|
||||||
|
|
||||||
|
key := key.Get(cmd)
|
||||||
|
cli := internalclient.GetSDKClientByFlag(cmd, key, commonflags.RPC)
|
||||||
|
|
||||||
|
resp, err := cli.APEManagerListChains(cmd.Context(),
|
||||||
|
client_sdk.PrmAPEManagerListChains{
|
||||||
|
ChainTarget: target,
|
||||||
|
})
|
||||||
|
commonCmd.ExitOnErr(cmd, "list chains call error: %w", err)
|
||||||
|
|
||||||
|
for _, respChain := range resp.Chains {
|
||||||
|
var chain apechain.Chain
|
||||||
|
commonCmd.ExitOnErr(cmd, "decode error: %w", chain.DecodeBytes(respChain.Raw))
|
||||||
|
apeutil.PrintHumanReadableAPEChain(cmd, &chain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func initListCmd() {
|
||||||
|
commonflags.Init(listCmd)
|
||||||
|
|
||||||
|
ff := listCmd.Flags()
|
||||||
|
ff.String(targetNameFlag, "", targetNameDesc)
|
||||||
|
ff.String(targetTypeFlag, "", targetTypeDesc)
|
||||||
|
_ = listCmd.MarkFlagRequired(targetTypeFlag)
|
||||||
|
}
|
66
cmd/frostfs-cli/modules/ape_manager/remove_chain.go
Normal file
66
cmd/frostfs-cli/modules/ape_manager/remove_chain.go
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
package apemanager
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
internalclient "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
|
||||||
|
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
|
client_sdk "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errEmptyChainID = errors.New("chain id cannot be empty")
|
||||||
|
|
||||||
|
removeCmd = &cobra.Command{
|
||||||
|
Use: "remove",
|
||||||
|
Short: "Remove rule chain for a target",
|
||||||
|
Run: remove,
|
||||||
|
PersistentPreRun: func(cmd *cobra.Command, _ []string) {
|
||||||
|
commonflags.Bind(cmd)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func remove(cmd *cobra.Command, _ []string) {
|
||||||
|
target := parseTarget(cmd)
|
||||||
|
|
||||||
|
key := key.Get(cmd)
|
||||||
|
cli := internalclient.GetSDKClientByFlag(cmd, key, commonflags.RPC)
|
||||||
|
|
||||||
|
chainID, _ := cmd.Flags().GetString(chainIDFlag)
|
||||||
|
if chainID == "" {
|
||||||
|
commonCmd.ExitOnErr(cmd, "read chain id error: %w", errEmptyChainID)
|
||||||
|
}
|
||||||
|
chainIDRaw := []byte(chainID)
|
||||||
|
|
||||||
|
hexEncoded, _ := cmd.Flags().GetBool(chainIDHexFlag)
|
||||||
|
if hexEncoded {
|
||||||
|
var err error
|
||||||
|
chainIDRaw, err = hex.DecodeString(chainID)
|
||||||
|
commonCmd.ExitOnErr(cmd, "can't decode chain ID as hex: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := cli.APEManagerRemoveChain(cmd.Context(), client_sdk.PrmAPEManagerRemoveChain{
|
||||||
|
ChainTarget: target,
|
||||||
|
ChainID: chainIDRaw,
|
||||||
|
})
|
||||||
|
|
||||||
|
commonCmd.ExitOnErr(cmd, "remove chain error: %w", err)
|
||||||
|
|
||||||
|
cmd.Println("\nRule has been removed.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func initRemoveCmd() {
|
||||||
|
commonflags.Init(removeCmd)
|
||||||
|
|
||||||
|
ff := removeCmd.Flags()
|
||||||
|
ff.String(targetNameFlag, "", targetNameDesc)
|
||||||
|
ff.String(targetTypeFlag, "", targetTypeDesc)
|
||||||
|
_ = removeCmd.MarkFlagRequired(targetTypeFlag)
|
||||||
|
ff.String(chainIDFlag, "", "Chain id")
|
||||||
|
ff.Bool(chainIDHexFlag, false, "Flag to parse chain ID as hex")
|
||||||
|
}
|
21
cmd/frostfs-cli/modules/ape_manager/root.go
Normal file
21
cmd/frostfs-cli/modules/ape_manager/root.go
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
package apemanager
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Cmd = &cobra.Command{
|
||||||
|
Use: "ape-manager",
|
||||||
|
Short: "Operations with APE manager",
|
||||||
|
Long: `Operations with APE manager`,
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
Cmd.AddCommand(addCmd)
|
||||||
|
Cmd.AddCommand(removeCmd)
|
||||||
|
Cmd.AddCommand(listCmd)
|
||||||
|
|
||||||
|
initAddCmd()
|
||||||
|
initRemoveCmd()
|
||||||
|
initListCmd()
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
||||||
accountingCli "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/accounting"
|
accountingCli "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/accounting"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/acl"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/acl"
|
||||||
|
apemanager "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/ape_manager"
|
||||||
bearerCli "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/bearer"
|
bearerCli "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/bearer"
|
||||||
containerCli "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/container"
|
containerCli "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/container"
|
||||||
controlCli "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/control"
|
controlCli "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/control"
|
||||||
|
@ -81,6 +82,7 @@ func init() {
|
||||||
rootCmd.Flags().Bool("version", false, "Application version and FrostFS API compatibility")
|
rootCmd.Flags().Bool("version", false, "Application version and FrostFS API compatibility")
|
||||||
|
|
||||||
rootCmd.AddCommand(acl.Cmd)
|
rootCmd.AddCommand(acl.Cmd)
|
||||||
|
rootCmd.AddCommand(apemanager.Cmd)
|
||||||
rootCmd.AddCommand(bearerCli.Cmd)
|
rootCmd.AddCommand(bearerCli.Cmd)
|
||||||
rootCmd.AddCommand(sessionCli.Cmd)
|
rootCmd.AddCommand(sessionCli.Cmd)
|
||||||
rootCmd.AddCommand(accountingCli.Cmd)
|
rootCmd.AddCommand(accountingCli.Cmd)
|
||||||
|
|
29
cmd/frostfs-node/apemanager.go
Normal file
29
cmd/frostfs-node/apemanager.go
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
|
||||||
|
apemanager_grpc "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/apemanager/grpc"
|
||||||
|
ape_contract "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/ape/contract_storage"
|
||||||
|
morph "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
|
||||||
|
apemanager_transport "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/network/transport/apemanager/grpc"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/apemanager"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func initAPEManagerService(c *cfg) {
|
||||||
|
contractStorage := ape_contract.NewProxyVerificationContractStorage(
|
||||||
|
morph.NewSwitchRPCGuardedActor(c.cfgMorph.client),
|
||||||
|
c.shared.key,
|
||||||
|
c.cfgMorph.proxyScriptHash,
|
||||||
|
c.cfgObject.cfgAccessPolicyEngine.policyContractHash)
|
||||||
|
|
||||||
|
execsvc := apemanager.New(c.cfgObject.cnrSource, contractStorage,
|
||||||
|
apemanager.WithLogger(c.log))
|
||||||
|
sigsvc := apemanager.NewSignService(&c.key.PrivateKey, execsvc)
|
||||||
|
server := apemanager_transport.New(sigsvc)
|
||||||
|
|
||||||
|
c.cfgGRPC.performAndSave(func(_ string, _ net.Listener, s *grpc.Server) {
|
||||||
|
apemanager_grpc.RegisterAPEManagerServiceServer(s, server)
|
||||||
|
})
|
||||||
|
}
|
|
@ -114,6 +114,7 @@ func initApp(ctx context.Context, c *cfg) {
|
||||||
initAndLog(c, "notification", func(c *cfg) { initNotifications(ctx, c) })
|
initAndLog(c, "notification", func(c *cfg) { initNotifications(ctx, c) })
|
||||||
initAndLog(c, "object", initObjectService)
|
initAndLog(c, "object", initObjectService)
|
||||||
initAndLog(c, "tree", initTreeService)
|
initAndLog(c, "tree", initTreeService)
|
||||||
|
initAndLog(c, "apemanager", initAPEManagerService)
|
||||||
initAndLog(c, "control", initControlService)
|
initAndLog(c, "control", initControlService)
|
||||||
|
|
||||||
initAndLog(c, "morph notifications", func(c *cfg) { listenMorphNotifications(ctx, c) })
|
initAndLog(c, "morph notifications", func(c *cfg) { listenMorphNotifications(ctx, c) })
|
||||||
|
|
128
pkg/ape/contract_storage/proxy.go
Normal file
128
pkg/ape/contract_storage/proxy.go
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
package contractstorage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
|
||||||
|
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine"
|
||||||
|
policy_morph "git.frostfs.info/TrueCloudLab/policy-engine/pkg/morph/policy"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/notary"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ProxyAdaptedContractStorage interface {
|
||||||
|
AddMorphRuleChain(name chain.Name, target engine.Target, c *chain.Chain) (util.Uint256, uint32, error)
|
||||||
|
|
||||||
|
RemoveMorphRuleChain(name chain.Name, target engine.Target, chainID chain.ID) (util.Uint256, uint32, error)
|
||||||
|
|
||||||
|
ListMorphRuleChains(name chain.Name, target engine.Target) ([]*chain.Chain, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ProxyAdaptedContractStorage = (engine.MorphRuleChainStorage)(nil)
|
||||||
|
|
||||||
|
type RPCActorProvider interface {
|
||||||
|
GetRPCActor() actor.RPCActor
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProxyVerificationContractStorage uses decorated MorphRuleChainStorage with actor where cosigner is a proxy contract.
|
||||||
|
type ProxyVerificationContractStorage struct {
|
||||||
|
rpcActorProvider RPCActorProvider
|
||||||
|
|
||||||
|
acc *wallet.Account
|
||||||
|
|
||||||
|
proxyScriptHash util.Uint160
|
||||||
|
|
||||||
|
policyScriptHash util.Uint160
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ProxyAdaptedContractStorage = (*ProxyVerificationContractStorage)(nil)
|
||||||
|
|
||||||
|
func NewProxyVerificationContractStorage(rpcActorProvider RPCActorProvider, key *keys.PrivateKey, proxyScriptHash, policyScriptHash util.Uint160) *ProxyVerificationContractStorage {
|
||||||
|
return &ProxyVerificationContractStorage{
|
||||||
|
rpcActorProvider: rpcActorProvider,
|
||||||
|
|
||||||
|
acc: wallet.NewAccountFromPrivateKey(key),
|
||||||
|
|
||||||
|
proxyScriptHash: proxyScriptHash,
|
||||||
|
|
||||||
|
policyScriptHash: policyScriptHash,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// contractStorageActorAdapter adapats *actor.Actor to policy_morph.ContractStorageActor interface.
|
||||||
|
type contractStorageActorAdapter struct {
|
||||||
|
*actor.Actor
|
||||||
|
rpcActor invoker.RPCInvoke
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *contractStorageActorAdapter) GetRPCInvoker() invoker.RPCInvoke {
|
||||||
|
return n.rpcActor
|
||||||
|
}
|
||||||
|
|
||||||
|
func (contractStorage *ProxyVerificationContractStorage) newContractStorageActor() (policy_morph.ContractStorageActor, error) {
|
||||||
|
rpcActor := contractStorage.rpcActorProvider.GetRPCActor()
|
||||||
|
act, err := actor.New(rpcActor, cosigners(contractStorage.acc, contractStorage.proxyScriptHash, contractStorage.policyScriptHash))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &contractStorageActorAdapter{
|
||||||
|
Actor: act,
|
||||||
|
rpcActor: rpcActor,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddMorphRuleChain add morph rule chain to Policy contract using both Proxy contract and storage account as consigners.
|
||||||
|
func (contractStorage *ProxyVerificationContractStorage) AddMorphRuleChain(name chain.Name, target engine.Target, c *chain.Chain) (util.Uint256, uint32, error) {
|
||||||
|
// contractStorageActor is reconstructed per each method invocation because RPCActor's (that is, basically, WSClient) connection may get invalidated, but
|
||||||
|
// ProxyVerificationContractStorage does not manage reconnections.
|
||||||
|
contractStorageActor, err := contractStorage.newContractStorageActor()
|
||||||
|
if err != nil {
|
||||||
|
return util.Uint256{}, 0, err
|
||||||
|
}
|
||||||
|
return policy_morph.NewContractStorage(contractStorageActor, contractStorage.policyScriptHash).AddMorphRuleChain(name, target, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveMorphRuleChain removes morph rule chain from Policy contract using both Proxy contract and storage account as consigners.
|
||||||
|
func (contractStorage *ProxyVerificationContractStorage) RemoveMorphRuleChain(name chain.Name, target engine.Target, chainID chain.ID) (util.Uint256, uint32, error) {
|
||||||
|
// contractStorageActor is reconstructed per each method invocation because RPCActor's (that is, basically, WSClient) connection may get invalidated, but
|
||||||
|
// ProxyVerificationContractStorage does not manage reconnections.
|
||||||
|
contractStorageActor, err := contractStorage.newContractStorageActor()
|
||||||
|
if err != nil {
|
||||||
|
return util.Uint256{}, 0, err
|
||||||
|
}
|
||||||
|
return policy_morph.NewContractStorage(contractStorageActor, contractStorage.policyScriptHash).RemoveMorphRuleChain(name, target, chainID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListMorphRuleChains lists morph rule chains from Policy contract using both Proxy contract and storage account as consigners.
|
||||||
|
func (contractStorage *ProxyVerificationContractStorage) ListMorphRuleChains(name chain.Name, target engine.Target) ([]*chain.Chain, error) {
|
||||||
|
// contractStorageActor is reconstructed per each method invocation because RPCActor's (that is, basically, WSClient) connection may get invalidated, but
|
||||||
|
// ProxyVerificationContractStorage does not manage reconnections.
|
||||||
|
contractStorageActor, err := contractStorage.newContractStorageActor()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return policy_morph.NewContractStorage(contractStorageActor, contractStorage.policyScriptHash).ListMorphRuleChains(name, target)
|
||||||
|
}
|
||||||
|
|
||||||
|
func cosigners(acc *wallet.Account, proxyScriptHash, policyScriptHash util.Uint160) []actor.SignerAccount {
|
||||||
|
return []actor.SignerAccount{
|
||||||
|
{
|
||||||
|
Signer: transaction.Signer{
|
||||||
|
Account: proxyScriptHash,
|
||||||
|
Scopes: transaction.CustomContracts,
|
||||||
|
AllowedContracts: []util.Uint160{policyScriptHash},
|
||||||
|
},
|
||||||
|
Account: notary.FakeContractAccount(proxyScriptHash),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Signer: transaction.Signer{
|
||||||
|
Account: acc.Contract.ScriptHash(),
|
||||||
|
Scopes: transaction.CalledByEntry,
|
||||||
|
},
|
||||||
|
Account: acc,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
63
pkg/network/transport/apemanager/grpc/service.go
Normal file
63
pkg/network/transport/apemanager/grpc/service.go
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
package apemanager
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
apemanager_v2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/apemanager"
|
||||||
|
apemanager_grpc "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/apemanager/grpc"
|
||||||
|
apemanager_svc "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/apemanager"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
srv apemanager_svc.Server
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ apemanager_grpc.APEManagerServiceServer = (*Server)(nil)
|
||||||
|
|
||||||
|
func New(c apemanager_svc.Server) *Server {
|
||||||
|
return &Server{
|
||||||
|
srv: c,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) AddChain(ctx context.Context, req *apemanager_grpc.AddChainRequest) (*apemanager_grpc.AddChainResponse, error) {
|
||||||
|
v2req := new(apemanager_v2.AddChainRequest)
|
||||||
|
if err := v2req.FromGRPCMessage(req); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := s.srv.AddChain(ctx, v2req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp.ToGRPCMessage().(*apemanager_grpc.AddChainResponse), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) RemoveChain(ctx context.Context, req *apemanager_grpc.RemoveChainRequest) (*apemanager_grpc.RemoveChainResponse, error) {
|
||||||
|
v2req := new(apemanager_v2.RemoveChainRequest)
|
||||||
|
if err := v2req.FromGRPCMessage(req); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := s.srv.RemoveChain(ctx, v2req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp.ToGRPCMessage().(*apemanager_grpc.RemoveChainResponse), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) ListChains(ctx context.Context, req *apemanager_grpc.ListChainsRequest) (*apemanager_grpc.ListChainsResponse, error) {
|
||||||
|
v2req := new(apemanager_v2.ListChainsRequest)
|
||||||
|
if err := v2req.FromGRPCMessage(req); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := s.srv.ListChains(ctx, v2req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp.ToGRPCMessage().(*apemanager_grpc.ListChainsResponse), nil
|
||||||
|
}
|
11
pkg/services/apemanager/errors/errors.go
Normal file
11
pkg/services/apemanager/errors/errors.go
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package errors
|
||||||
|
|
||||||
|
import (
|
||||||
|
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ErrAPEManagerAccessDenied(reason string) error {
|
||||||
|
err := new(apistatus.APEManagerAccessDenied)
|
||||||
|
err.WriteReason(reason)
|
||||||
|
return err
|
||||||
|
}
|
245
pkg/services/apemanager/executor.go
Normal file
245
pkg/services/apemanager/executor.go
Normal file
|
@ -0,0 +1,245 @@
|
||||||
|
package apemanager
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rand"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
apemanager_v2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/apemanager"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||||
|
session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
||||||
|
ape_contract "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/ape/contract_storage"
|
||||||
|
containercore "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/container"
|
||||||
|
apemanager_errors "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/apemanager/errors"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
|
||||||
|
cidSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
||||||
|
apechain "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
|
||||||
|
policy_engine "git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine"
|
||||||
|
"github.com/mr-tron/base58/base58"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errEmptyBodySignature = errors.New("malformed request: empty body signature")
|
||||||
|
)
|
||||||
|
|
||||||
|
type cfg struct {
|
||||||
|
log *logger.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
type Service struct {
|
||||||
|
cfg
|
||||||
|
|
||||||
|
cnrSrc containercore.Source
|
||||||
|
|
||||||
|
contractStorage ape_contract.ProxyAdaptedContractStorage
|
||||||
|
}
|
||||||
|
|
||||||
|
type Option func(*cfg)
|
||||||
|
|
||||||
|
func New(cnrSrc containercore.Source, contractStorage ape_contract.ProxyAdaptedContractStorage, opts ...Option) *Service {
|
||||||
|
s := &Service{
|
||||||
|
cnrSrc: cnrSrc,
|
||||||
|
|
||||||
|
contractStorage: contractStorage,
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range opts {
|
||||||
|
opts[i](&s.cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.log == nil {
|
||||||
|
s.log = &logger.Logger{Logger: zap.NewNop()}
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithLogger(log *logger.Logger) Option {
|
||||||
|
return func(c *cfg) {
|
||||||
|
c.log = log
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ Server = (*Service)(nil)
|
||||||
|
|
||||||
|
// validateContainerTargetRequest validates request for the container target.
|
||||||
|
// It checks if request actor is the owner of the container, otherwise it denies the request.
|
||||||
|
func (s *Service) validateContainerTargetRequest(cid string, pubKey *keys.PublicKey) error {
|
||||||
|
var cidSDK cidSDK.ID
|
||||||
|
if err := cidSDK.DecodeString(cid); err != nil {
|
||||||
|
return fmt.Errorf("invalid CID format: %w", err)
|
||||||
|
}
|
||||||
|
isOwner, err := s.isActorContainerOwner(cidSDK, pubKey)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to check owner: %w", err)
|
||||||
|
}
|
||||||
|
if !isOwner {
|
||||||
|
return apemanager_errors.ErrAPEManagerAccessDenied("actor must be container owner")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) AddChain(_ context.Context, req *apemanager_v2.AddChainRequest) (*apemanager_v2.AddChainResponse, error) {
|
||||||
|
pub, err := getSignaturePublicKey(req.GetVerificationHeader())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
chain, err := decodeAndValidateChain(req.GetBody().GetChain().GetKind().(*apemanager_v2.ChainRaw).GetRaw())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(chain.ID) == 0 {
|
||||||
|
const randomIDLength = 10
|
||||||
|
randID, err := base58Str(randomIDLength)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("randomize chain ID error: %w", err)
|
||||||
|
}
|
||||||
|
chain.ID = apechain.ID(randID)
|
||||||
|
}
|
||||||
|
|
||||||
|
var target policy_engine.Target
|
||||||
|
|
||||||
|
switch targetType := req.GetBody().GetTarget().GetTargetType(); targetType {
|
||||||
|
case apemanager_v2.TargetTypeContainer:
|
||||||
|
reqCID := req.GetBody().GetTarget().GetName()
|
||||||
|
if err = s.validateContainerTargetRequest(reqCID, pub); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
target = policy_engine.ContainerTarget(reqCID)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported target type: %s", targetType)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, _, err = s.contractStorage.AddMorphRuleChain(apechain.Ingress, target, &chain); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
body := new(apemanager_v2.AddChainResponseBody)
|
||||||
|
body.SetChainID(chain.ID)
|
||||||
|
|
||||||
|
resp := new(apemanager_v2.AddChainResponse)
|
||||||
|
resp.SetBody(body)
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) RemoveChain(_ context.Context, req *apemanager_v2.RemoveChainRequest) (*apemanager_v2.RemoveChainResponse, error) {
|
||||||
|
pub, err := getSignaturePublicKey(req.GetVerificationHeader())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var target policy_engine.Target
|
||||||
|
|
||||||
|
switch targetType := req.GetBody().GetTarget().GetTargetType(); targetType {
|
||||||
|
case apemanager_v2.TargetTypeContainer:
|
||||||
|
reqCID := req.GetBody().GetTarget().GetName()
|
||||||
|
if err = s.validateContainerTargetRequest(reqCID, pub); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
target = policy_engine.ContainerTarget(reqCID)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported target type: %s", targetType)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, _, err = s.contractStorage.RemoveMorphRuleChain(apechain.Ingress, target, req.GetBody().GetChainID()); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
body := new(apemanager_v2.RemoveChainResponseBody)
|
||||||
|
|
||||||
|
resp := new(apemanager_v2.RemoveChainResponse)
|
||||||
|
resp.SetBody(body)
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) ListChains(_ context.Context, req *apemanager_v2.ListChainsRequest) (*apemanager_v2.ListChainsResponse, error) {
|
||||||
|
pub, err := getSignaturePublicKey(req.GetVerificationHeader())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var target policy_engine.Target
|
||||||
|
|
||||||
|
switch targetType := req.GetBody().GetTarget().GetTargetType(); targetType {
|
||||||
|
case apemanager_v2.TargetTypeContainer:
|
||||||
|
reqCID := req.GetBody().GetTarget().GetName()
|
||||||
|
if err = s.validateContainerTargetRequest(reqCID, pub); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
target = policy_engine.ContainerTarget(reqCID)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported target type: %s", targetType)
|
||||||
|
}
|
||||||
|
|
||||||
|
chs, err := s.contractStorage.ListMorphRuleChains(apechain.Ingress, target)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
res := make([]*apemanager_v2.Chain, 0, len(chs))
|
||||||
|
for _, ch := range chs {
|
||||||
|
v2chraw := new(apemanager_v2.ChainRaw)
|
||||||
|
v2chraw.SetRaw(ch.Bytes())
|
||||||
|
|
||||||
|
v2ch := new(apemanager_v2.Chain)
|
||||||
|
v2ch.SetKind(v2chraw)
|
||||||
|
|
||||||
|
res = append(res, v2ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
body := new(apemanager_v2.ListChainsResponseBody)
|
||||||
|
body.SetChains(res)
|
||||||
|
|
||||||
|
resp := new(apemanager_v2.ListChainsResponse)
|
||||||
|
resp.SetBody(body)
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSignaturePublicKey(vh *session.RequestVerificationHeader) (*keys.PublicKey, error) {
|
||||||
|
for vh.GetOrigin() != nil {
|
||||||
|
vh = vh.GetOrigin()
|
||||||
|
}
|
||||||
|
sig := vh.GetBodySignature()
|
||||||
|
if sig == nil {
|
||||||
|
return nil, errEmptyBodySignature
|
||||||
|
}
|
||||||
|
key, err := keys.NewPublicKeyFromBytes(sig.GetKey(), elliptic.P256())
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid signature key: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return key, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) isActorContainerOwner(cid cidSDK.ID, pk *keys.PublicKey) (bool, error) {
|
||||||
|
var actor user.ID
|
||||||
|
user.IDFromKey(&actor, (ecdsa.PublicKey)(*pk))
|
||||||
|
actorOwnerID := new(refs.OwnerID)
|
||||||
|
actor.WriteToV2(actorOwnerID)
|
||||||
|
|
||||||
|
cnr, err := s.cnrSrc.Get(cid)
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("get container error: %w", err)
|
||||||
|
}
|
||||||
|
return cnr.Value.Owner().Equals(actor), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// base58Str generates base58 string.
|
||||||
|
func base58Str(n int) (string, error) {
|
||||||
|
b := make([]byte, n)
|
||||||
|
_, err := rand.Read(b)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return base58.FastBase58Encoding(b), nil
|
||||||
|
}
|
13
pkg/services/apemanager/server.go
Normal file
13
pkg/services/apemanager/server.go
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package apemanager
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
apemanager_v2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/apemanager"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Server interface {
|
||||||
|
AddChain(context.Context, *apemanager_v2.AddChainRequest) (*apemanager_v2.AddChainResponse, error)
|
||||||
|
RemoveChain(context.Context, *apemanager_v2.RemoveChainRequest) (*apemanager_v2.RemoveChainResponse, error)
|
||||||
|
ListChains(context.Context, *apemanager_v2.ListChainsRequest) (*apemanager_v2.ListChainsResponse, error)
|
||||||
|
}
|
49
pkg/services/apemanager/sign.go
Normal file
49
pkg/services/apemanager/sign.go
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
package apemanager
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
|
||||||
|
apemanager_v2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/apemanager"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
type signService struct {
|
||||||
|
sigSvc *util.SignService
|
||||||
|
|
||||||
|
next Server
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSignService(key *ecdsa.PrivateKey, next Server) Server {
|
||||||
|
return &signService{
|
||||||
|
sigSvc: util.NewUnarySignService(key),
|
||||||
|
next: next,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *signService) AddChain(ctx context.Context, req *apemanager_v2.AddChainRequest) (*apemanager_v2.AddChainResponse, error) {
|
||||||
|
if err := s.sigSvc.VerifyRequest(req); err != nil {
|
||||||
|
resp := new(apemanager_v2.AddChainResponse)
|
||||||
|
return resp, s.sigSvc.SignResponse(resp, err)
|
||||||
|
}
|
||||||
|
resp, err := util.EnsureNonNilResponse(s.next.AddChain(ctx, req))
|
||||||
|
return resp, s.sigSvc.SignResponse(resp, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *signService) RemoveChain(ctx context.Context, req *apemanager_v2.RemoveChainRequest) (*apemanager_v2.RemoveChainResponse, error) {
|
||||||
|
if err := s.sigSvc.VerifyRequest(req); err != nil {
|
||||||
|
resp := new(apemanager_v2.RemoveChainResponse)
|
||||||
|
return resp, s.sigSvc.SignResponse(resp, err)
|
||||||
|
}
|
||||||
|
resp, err := util.EnsureNonNilResponse(s.next.RemoveChain(ctx, req))
|
||||||
|
return resp, s.sigSvc.SignResponse(resp, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *signService) ListChains(ctx context.Context, req *apemanager_v2.ListChainsRequest) (*apemanager_v2.ListChainsResponse, error) {
|
||||||
|
if err := s.sigSvc.VerifyRequest(req); err != nil {
|
||||||
|
resp := new(apemanager_v2.ListChainsResponse)
|
||||||
|
return resp, s.sigSvc.SignResponse(resp, err)
|
||||||
|
}
|
||||||
|
resp, err := util.EnsureNonNilResponse(s.next.ListChains(ctx, req))
|
||||||
|
return resp, s.sigSvc.SignResponse(resp, err)
|
||||||
|
}
|
23
pkg/services/apemanager/validation.go
Normal file
23
pkg/services/apemanager/validation.go
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
package apemanager
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control/server/ape"
|
||||||
|
apechain "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
|
||||||
|
)
|
||||||
|
|
||||||
|
func decodeAndValidateChain(encodedChain []byte) (chain apechain.Chain, err error) {
|
||||||
|
if err = chain.DecodeBytes(encodedChain); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, rule := range chain.Rules {
|
||||||
|
for _, name := range rule.Resources.Names {
|
||||||
|
if err = ape.ValidateResourceName(name); err != nil {
|
||||||
|
err = fmt.Errorf("invalid resource: %w", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
Loading…
Reference in a new issue