policy-reader/modules/chains/list_user.go
Denis Kirillov 9cb198d411 Add printing user info
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2024-08-29 09:49:56 +03:00

203 lines
5.7 KiB
Go

package chains
import (
"encoding/base64"
"encoding/hex"
"errors"
"fmt"
"math/big"
"git.frostfs.info/TrueCloudLab/frostfs-contract/commonclient"
ffsidclient "git.frostfs.info/TrueCloudLab/frostfs-contract/frostfsid/client"
policycontract "git.frostfs.info/TrueCloudLab/frostfs-contract/policy"
apechain "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
"git.frostfs.info/dkirillov/policy-reader/internal/resolver"
neoflags "github.com/nspcc-dev/neo-go/cli/flags"
"github.com/nspcc-dev/neo-go/pkg/rpcclient"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var listUserCmd = &cobra.Command{
Use: "list-user",
Short: "List user related policies",
Long: "List user related policies along with filtering by service (s3/storage)",
Example: `policy-reader user -r http://localhost:40332 list --user NiGqBpUdMvAC68SxUeyYwVPyBCsqzNuof
policy-reader user -r http://localhost:40332 --policy-hash 81c1a41d09e08087a4b679418b12be5d3ab15742 list --user NiGqBpUdMvAC68SxUeyYwVPyBCsqzNuofL --service s3`,
RunE: runListCmd,
}
var errUnknownServiceType = errors.New("unknown service type")
const (
userFlag = "user"
namespaceFlag = "namespace"
serviceFlag = "service"
decodeIDFlag = "decode-id"
)
func initListUserCmd() {
listUserCmd.Flags().String(userFlag, "", "User address or name in frostfsid contract (if name is provided than 'namespace' should be set too)")
listUserCmd.Flags().String(namespaceFlag, "", "Namespace where user name will be looked up")
listUserCmd.Flags().String(serviceFlag, "none", "Service (none/s3/storage) to filter chains")
listUserCmd.Flags().Bool(decodeIDFlag, false, "Use this flag to additionally decode chain id")
_ = listUserCmd.MarkFlagRequired(userFlag)
}
func runListCmd(cmd *cobra.Command, _ []string) error {
service, err := parseService(viper.GetString(serviceFlag))
if err != nil {
return err
}
endpoint := viper.GetString(rpcEndpointFlag)
policyHash, err := resolver.ResolveContractHash(endpoint, viper.GetString(policyHashFlag))
if err != nil {
return err
}
frostfsidHash, err := resolver.ResolveContractHash(endpoint, viper.GetString(frostfsidHashFlag))
if err != nil {
return err
}
rpcCli, err := rpcclient.New(cmd.Context(), endpoint, rpcclient.Options{})
if err != nil {
return err
}
acc, err := wallet.NewAccount()
if err != nil {
return err
}
ffsidCli, err := ffsidclient.New(rpcCli, acc, frostfsidHash, ffsidclient.Options{})
if err != nil {
return err
}
subj, err := resolveSubject(ffsidCli, viper.GetString(namespaceFlag), viper.GetString(userFlag))
if err != nil {
return err
}
printSubject(cmd, subj)
res, err := commonclient.ReadIteratorItems(invoker.New(rpcCli, nil), 100, policyHash, "iteratorChainsByPrefix", big.NewInt(int64(policycontract.Namespace)), subj.Namespace, string(service))
if err != nil {
return err
}
decodeID := viper.GetBool(decodeIDFlag)
cmd.Printf("user namespace '%s' policies: %d\n", subj.Namespace, len(res))
if err = printChains(cmd, res, decodeID); err != nil {
return err
}
userEntity := big.NewInt(int64(policycontract.User))
userEntityName := fmt.Sprintf("%s:%s", subj.Namespace, subj.PrimaryKey.Address())
res, err = commonclient.ReadIteratorItems(invoker.New(rpcCli, nil), 100, policyHash, "iteratorChainsByPrefix", userEntity, userEntityName, string(service))
if err != nil {
return err
}
cmd.Printf("user policies: %d\n", len(res))
if err = printChains(cmd, res, decodeID); err != nil {
return err
}
cmd.Printf("user group policies: %d\n", len(subj.Groups))
groupEntity := big.NewInt(int64(policycontract.Group))
for _, group := range subj.Groups {
groupEntityName := fmt.Sprintf("%s:%d", group.Namespace, group.ID)
res, err = commonclient.ReadIteratorItems(invoker.New(rpcCli, nil), 100, policyHash, "iteratorChainsByPrefix", groupEntity, groupEntityName, string(service))
if err != nil {
return err
}
cmd.Printf("user group '%s' (id: %d) policies: %d\n", group.Name, group.ID, len(res))
if err = printChains(cmd, res, decodeID); err != nil {
return err
}
}
return nil
}
func printChains(cmd *cobra.Command, list []stackitem.Item, decodeID bool) error {
for _, item := range list {
bytes, err := item.TryBytes()
if err != nil {
return err
}
var chain apechain.Chain
if err = chain.DecodeBytes(bytes); err != nil {
cmd.Printf("invalid chain format: %s\n", base64.StdEncoding.EncodeToString(bytes))
continue
}
raw, err := chain.MarshalJSON()
if err != nil {
return err
}
if decodeID {
cmd.Println(string(chain.ID), string(raw))
} else {
cmd.Println(string(raw))
}
}
return nil
}
func resolveSubject(ffsid *ffsidclient.Client, namespace, userName string) (*ffsidclient.SubjectExtended, error) {
if userHash, err := neoflags.ParseAddress(userName); err == nil {
subj, err := ffsid.GetSubject(userHash)
if err != nil {
return nil, err
}
return ffsid.GetSubjectExtended(subj.PrimaryKey.GetScriptHash())
}
subj, err := ffsid.GetSubjectByName(namespace, userName)
if err != nil {
return nil, err
}
return ffsid.GetSubjectExtended(subj.PrimaryKey.GetScriptHash())
}
func parseService(service string) (apechain.Name, error) {
switch service {
case "none":
return "", nil
case "s3":
return apechain.S3, nil
case "storage":
return apechain.Ingress, nil
}
return "", errUnknownServiceType
}
func printSubject(cmd *cobra.Command, subj *ffsidclient.SubjectExtended) {
cmd.Println("ns:", subj.Namespace)
cmd.Println("name:", subj.Name)
cmd.Println("key:", hex.EncodeToString(subj.PrimaryKey.Bytes()))
cmd.Println("claims:")
for k, v := range subj.KV {
cmd.Println(k, v)
}
}