diff --git a/cmd/frostfs-cli/modules/bearer/create.go b/cmd/frostfs-cli/modules/bearer/create.go index 8a7500db3..66182d5bf 100644 --- a/cmd/frostfs-cli/modules/bearer/create.go +++ b/cmd/frostfs-cli/modules/bearer/create.go @@ -10,15 +10,18 @@ import ( internalclient "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client" "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/common" "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" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer" eaclSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user" "github.com/spf13/cobra" + "github.com/spf13/viper" ) const ( eaclFlag = "eacl" + apeFlag = "ape" issuedAtFlag = "issued-at" notValidBeforeFlag = "not-valid-before" ownerFlag = "owner" @@ -37,10 +40,17 @@ In this case --` + commonflags.RPC + ` flag should be specified and the epoch in is set to current epoch + n. `, Run: createToken, + PersistentPreRun: func(cmd *cobra.Command, args []string) { + ff := cmd.Flags() + + _ = viper.BindPFlag(commonflags.WalletPath, ff.Lookup(commonflags.WalletPath)) + _ = viper.BindPFlag(commonflags.Account, ff.Lookup(commonflags.Account)) + }, } func init() { createCmd.Flags().StringP(eaclFlag, "e", "", "Path to the extended ACL table (mutually exclusive with --impersonate flag)") + createCmd.Flags().StringP(apeFlag, "a", "", "Path to the JSON-encoded APE override (mutually exclusive with --impersonate flag)") createCmd.Flags().StringP(issuedAtFlag, "i", "+0", "Epoch to issue token at") createCmd.Flags().StringP(notValidBeforeFlag, "n", "+0", "Not valid before epoch") createCmd.Flags().StringP(commonflags.ExpireAt, "x", "", "The last active epoch for the token") @@ -49,10 +59,13 @@ func init() { createCmd.Flags().Bool(jsonFlag, false, "Output token in JSON") createCmd.Flags().Bool(impersonateFlag, false, "Mark token as impersonate to consider the token signer as the request owner (mutually exclusive with --eacl flag)") createCmd.Flags().StringP(commonflags.RPC, commonflags.RPCShorthand, commonflags.RPCDefault, commonflags.RPCUsage) + createCmd.Flags().StringP(commonflags.WalletPath, commonflags.WalletPathShorthand, commonflags.WalletPathDefault, commonflags.WalletPathUsage) + createCmd.Flags().StringP(commonflags.Account, commonflags.AccountShorthand, commonflags.AccountDefault, commonflags.AccountUsage) - createCmd.MarkFlagsMutuallyExclusive(eaclFlag, impersonateFlag) + createCmd.MarkFlagsMutuallyExclusive(eaclFlag, apeFlag, impersonateFlag) _ = cobra.MarkFlagFilename(createCmd.Flags(), eaclFlag) + _ = cobra.MarkFlagFilename(createCmd.Flags(), apeFlag) _ = cobra.MarkFlagRequired(createCmd.Flags(), commonflags.ExpireAt) _ = cobra.MarkFlagRequired(createCmd.Flags(), ownerFlag) @@ -119,8 +132,21 @@ func createToken(cmd *cobra.Command, _ []string) { b.SetEACLTable(*table) } + apePath, _ := cmd.Flags().GetString(apeFlag) + if apePath != "" { + apeOverride := new(bearer.APEOverride) + raw, err := os.ReadFile(apePath) + commonCmd.ExitOnErr(cmd, "can't read extended ACL file: %w", err) + commonCmd.ExitOnErr(cmd, "can't parse extended ACL: %w", json.Unmarshal(raw, apeOverride)) + b.SetAPEOverride(apeOverride) + } + var data []byte + key := key.Get(cmd) + + commonCmd.ExitOnErr(cmd, "sign err: %w", b.Sign(*key)) + toJSON, _ := cmd.Flags().GetBool(jsonFlag) if toJSON { data, err = json.Marshal(b) diff --git a/cmd/frostfs-cli/modules/bearer/generate_override.go b/cmd/frostfs-cli/modules/bearer/generate_override.go new file mode 100644 index 000000000..50b5ad789 --- /dev/null +++ b/cmd/frostfs-cli/modules/bearer/generate_override.go @@ -0,0 +1,115 @@ +package bearer + +import ( + "errors" + "fmt" + "os" + + "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags" + parseutil "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/util" + commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common" + apeSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/ape" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer" + cidSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" + apechain "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain" + "github.com/spf13/cobra" +) + +var ( + errChainIDCannotBeEmpty = errors.New("chain id cannot be empty") + errRuleIsNotParsed = errors.New("rule is not passed") +) + +const ( + chainIDFlag = "chain-id" + chainIDHexFlag = "chain-id-hex" + ruleFlag = "rule" + pathFlag = "path" + outputFlag = "output" +) + +var genereateAPEOverrideCmd = &cobra.Command{ + Use: "generate-ape-override", + Short: "Generate APE override.", + Long: `Generate APE override by target and APE chains. Util command. + +Generated APE override can be dumped to a file in JSON format that is passed to +"create" command. +`, + Run: genereateAPEOverride, +} + +func genereateAPEOverride(cmd *cobra.Command, _ []string) { + c := parseChain(cmd) + + targetCID, _ := cmd.Flags().GetString(commonflags.CIDFlag) + var cid cidSDK.ID + commonCmd.ExitOnErr(cmd, "invalid cid format: %w", cid.DecodeString(targetCID)) + + override := &bearer.APEOverride{ + Target: apeSDK.ChainTarget{ + TargetType: apeSDK.TargetTypeContainer, + Name: targetCID, + }, + Chains: []apeSDK.Chain{ + { + Raw: c.Bytes(), + }, + }, + } + + overrideMarshalled, err := override.MarshalJSON() + commonCmd.ExitOnErr(cmd, "failed to marshal APE override: %w", err) + + outputPath, _ := cmd.Flags().GetString(outputFlag) + if outputPath != "" { + err := os.WriteFile(outputPath, []byte(overrideMarshalled), 0o644) + commonCmd.ExitOnErr(cmd, "dump error: %w", err) + } else { + fmt.Print("\n") + fmt.Println(string(overrideMarshalled)) + } +} + +func init() { + ff := genereateAPEOverrideCmd.Flags() + + ff.StringP(commonflags.CIDFlag, "", "", "Target container ID.") + _ = cobra.MarkFlagRequired(createCmd.Flags(), commonflags.CIDFlag) + + 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.Bool(chainIDHexFlag, false, "Flag to parse chain ID as hex") + + ff.String(outputFlag, "", "Output path to dump result JSON-encoded APE override") + _ = cobra.MarkFlagFilename(createCmd.Flags(), outputFlag) +} + +func parseChainID(cmd *cobra.Command) apechain.ID { + chainID, _ := cmd.Flags().GetString(chainIDFlag) + if chainID == "" { + commonCmd.ExitOnErr(cmd, "read chain id error: %w", + errChainIDCannotBeEmpty) + } + return apechain.ID(chainID) +} + +func parseChain(cmd *cobra.Command) *apechain.Chain { + chain := new(apechain.Chain) + + if rules, _ := cmd.Flags().GetStringArray(ruleFlag); len(rules) > 0 { + commonCmd.ExitOnErr(cmd, "parser error: %w", parseutil.ParseAPEChain(chain, rules)) + } else if encPath, _ := cmd.Flags().GetString(pathFlag); encPath != "" { + commonCmd.ExitOnErr(cmd, "decode binary or json error: %w", parseutil.ParseAPEChainBinaryOrJSON(chain, encPath)) + } else { + commonCmd.ExitOnErr(cmd, "parser error: %w", errRuleIsNotParsed) + } + + chain.ID = parseChainID(cmd) + + cmd.Println("Parsed chain:") + parseutil.PrintHumanReadableAPEChain(cmd, chain) + + return chain +} diff --git a/cmd/frostfs-cli/modules/bearer/root.go b/cmd/frostfs-cli/modules/bearer/root.go index 200d096ac..634ba788c 100644 --- a/cmd/frostfs-cli/modules/bearer/root.go +++ b/cmd/frostfs-cli/modules/bearer/root.go @@ -11,4 +11,5 @@ var Cmd = &cobra.Command{ func init() { Cmd.AddCommand(createCmd) + Cmd.AddCommand(genereateAPEOverrideCmd) }