package ape import ( "encoding/hex" "errors" "fmt" "strconv" "strings" commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common" apeutil "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/ape" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" apechain "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain" "git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine" "github.com/nspcc-dev/neo-go/cli/input" "github.com/spf13/cobra" ) const ( defaultNamespace = "root" namespaceTarget = "namespace" containerTarget = "container" userTarget = "user" groupTarget = "group" Ingress = "ingress" S3 = "s3" ) var mChainName = map[string]apechain.Name{ Ingress: apechain.Ingress, S3: apechain.S3, } var ( errSettingDefaultValueWasDeclined = errors.New("setting default value was declined") errUnknownTargetType = errors.New("unknown target type") errUnsupportedChainName = errors.New("unsupported chain name") ) // PrintHumanReadableAPEChain print APE chain rules. func PrintHumanReadableAPEChain(cmd *cobra.Command, chain *apechain.Chain) { cmd.Println("Chain ID: " + string(chain.ID)) cmd.Printf(" HEX: %x\n", chain.ID) cmd.Println("Rules:") for _, rule := range chain.Rules { cmd.Println("\n\tStatus: " + rule.Status.String()) cmd.Println("\tAny: " + strconv.FormatBool(rule.Any)) cmd.Println("\tConditions:") for _, c := range rule.Condition { var ot string switch c.Kind { case apechain.KindResource: ot = "Resource" case apechain.KindRequest: ot = "Request" default: panic("unknown object type") } cmd.Println(fmt.Sprintf("\t\t%s %s %s %s", ot, c.Key, c.Op, c.Value)) } cmd.Println("\tActions:\tInverted:" + strconv.FormatBool(rule.Actions.Inverted)) for _, name := range rule.Actions.Names { cmd.Println("\t\t" + name) } cmd.Println("\tResources:\tInverted:" + strconv.FormatBool(rule.Resources.Inverted)) for _, name := range rule.Resources.Names { cmd.Println("\t\t" + name) } } } // ParseTarget handles target parsing of an APE chain. func ParseTarget(cmd *cobra.Command) engine.Target { typ := ParseTargetType(cmd) name, _ := cmd.Flags().GetString(TargetNameFlag) switch typ { case engine.Namespace: if name == "" { ln, err := input.ReadLine(fmt.Sprintf("Target name is not set. Confirm to use %s namespace (n|Y)> ", defaultNamespace)) commonCmd.ExitOnErr(cmd, "read line error: %w", err) ln = strings.ToLower(ln) if len(ln) > 0 && (ln[0] == 'n') { commonCmd.ExitOnErr(cmd, "read namespace error: %w", errSettingDefaultValueWasDeclined) } name = defaultNamespace } return engine.NamespaceTarget(name) case engine.Container: var cnr cid.ID commonCmd.ExitOnErr(cmd, "can't decode container ID: %w", cnr.DecodeString(name)) return engine.ContainerTarget(name) case engine.User: return engine.UserTarget(name) case engine.Group: return engine.GroupTarget(name) default: commonCmd.ExitOnErr(cmd, "read target type error: %w", errUnknownTargetType) } return engine.Target{} } // ParseTargetType handles target type parsing of an APE chain. func ParseTargetType(cmd *cobra.Command) engine.TargetType { typ, _ := cmd.Flags().GetString(TargetTypeFlag) switch typ { case namespaceTarget: return engine.Namespace case containerTarget: return engine.Container case userTarget: return engine.User case groupTarget: return engine.Group } commonCmd.ExitOnErr(cmd, "parse target type error: %w", errUnknownTargetType) return engine.TargetType(0) } // ParseChainID handles the parsing of APE-chain identifier. // For some subcommands, chain ID is optional as an input parameter and should be generated by // the service instead. func ParseChainID(cmd *cobra.Command) (id apechain.ID) { chainID, _ := cmd.Flags().GetString(ChainIDFlag) id = apechain.ID(chainID) hexEncoded, _ := cmd.Flags().GetBool(ChainIDHexFlag) if !hexEncoded { return } chainIDRaw, err := hex.DecodeString(chainID) commonCmd.ExitOnErr(cmd, "can't decode chain ID as hex: %w", err) id = apechain.ID(chainIDRaw) return } // ParseChain parses an APE chain which can be provided either as a rule statement // or loaded from a binary/JSON file path. func ParseChain(cmd *cobra.Command) *apechain.Chain { chain := new(apechain.Chain) chain.ID = ParseChainID(cmd) if rules, _ := cmd.Flags().GetStringArray(RuleFlag); len(rules) > 0 { commonCmd.ExitOnErr(cmd, "parser error: %w", apeutil.ParseAPEChain(chain, rules)) } else if encPath, _ := cmd.Flags().GetString(PathFlag); encPath != "" { commonCmd.ExitOnErr(cmd, "decode binary or json error: %w", apeutil.ParseAPEChainBinaryOrJSON(chain, encPath)) } else { commonCmd.ExitOnErr(cmd, "parser error", errors.New("rule is not passed")) } cmd.Println("Parsed chain:") PrintHumanReadableAPEChain(cmd, chain) return chain } // ParseChainName parses chain name: the place in the request lifecycle where policy is applied. func ParseChainName(cmd *cobra.Command) apechain.Name { chainName, _ := cmd.Flags().GetString(ChainNameFlag) apeChainName, ok := mChainName[strings.ToLower(chainName)] if !ok { commonCmd.ExitOnErr(cmd, "", errUnsupportedChainName) } return apeChainName }