package bearer import ( "context" "encoding/json" "fmt" "os" "time" 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" 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" outFlag = "out" jsonFlag = commonflags.JSON impersonateFlag = "impersonate" ) var createCmd = &cobra.Command{ Use: "create", Short: "Create bearer token", Long: `Create bearer token. All epoch flags can be specified relative to the current epoch with the +n syntax. In this case --` + commonflags.RPC + ` flag should be specified and the epoch in bearer token is set to current epoch + n. `, Run: createToken, PersistentPreRun: func(cmd *cobra.Command, _ []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 and --ape flag)") createCmd.Flags().StringP(apeFlag, "a", "", "Path to the JSON-encoded APE override (mutually exclusive with --impersonate and --eacl 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") createCmd.Flags().StringP(ownerFlag, "o", "", "Token owner") createCmd.Flags().String(outFlag, "", "File to write token to") 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, apeFlag, impersonateFlag) _ = cobra.MarkFlagFilename(createCmd.Flags(), eaclFlag) _ = cobra.MarkFlagFilename(createCmd.Flags(), apeFlag) _ = cobra.MarkFlagRequired(createCmd.Flags(), commonflags.ExpireAt) _ = cobra.MarkFlagRequired(createCmd.Flags(), outFlag) } func createToken(cmd *cobra.Command, _ []string) { iat, iatRelative, err := common.ParseEpoch(cmd, issuedAtFlag) commonCmd.ExitOnErr(cmd, "can't parse --"+issuedAtFlag+" flag: %w", err) exp, expRelative, err := common.ParseEpoch(cmd, commonflags.ExpireAt) commonCmd.ExitOnErr(cmd, "can't parse --"+commonflags.ExpireAt+" flag: %w", err) nvb, nvbRelative, err := common.ParseEpoch(cmd, notValidBeforeFlag) commonCmd.ExitOnErr(cmd, "can't parse --"+notValidBeforeFlag+" flag: %w", err) if iatRelative || expRelative || nvbRelative { endpoint, _ := cmd.Flags().GetString(commonflags.RPC) if len(endpoint) == 0 { commonCmd.ExitOnErr(cmd, "can't fetch current epoch: %w", fmt.Errorf("'%s' flag value must be specified", commonflags.RPC)) } ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() currEpoch, err := internalclient.GetCurrentEpoch(ctx, cmd, endpoint) commonCmd.ExitOnErr(cmd, "can't fetch current epoch: %w", err) if iatRelative { iat += currEpoch } if expRelative { exp += currEpoch } if nvbRelative { nvb += currEpoch } } if exp < nvb { commonCmd.ExitOnErr(cmd, "", fmt.Errorf("expiration epoch is less than not-valid-before epoch: %d < %d", exp, nvb)) } var b bearer.Token b.SetExp(exp) b.SetNbf(nvb) b.SetIat(iat) if ownerStr, _ := cmd.Flags().GetString(ownerFlag); ownerStr != "" { var ownerID user.ID commonCmd.ExitOnErr(cmd, "can't parse recipient: %w", ownerID.DecodeString(ownerStr)) b.ForUser(ownerID) } impersonate, _ := cmd.Flags().GetBool(impersonateFlag) b.SetImpersonate(impersonate) eaclPath, _ := cmd.Flags().GetString(eaclFlag) if eaclPath != "" { table := eaclSDK.NewTable() raw, err := os.ReadFile(eaclPath) commonCmd.ExitOnErr(cmd, "can't read extended ACL file: %w", err) commonCmd.ExitOnErr(cmd, "can't parse extended ACL: %w", json.Unmarshal(raw, table)) b.SetEACLTable(*table) } apePath, _ := cmd.Flags().GetString(apeFlag) if apePath != "" { var apeOverride bearer.APEOverride raw, err := os.ReadFile(apePath) commonCmd.ExitOnErr(cmd, "can't read APE rules: %w", err) commonCmd.ExitOnErr(cmd, "can't parse APE rules: %w", json.Unmarshal(raw, &apeOverride)) b.SetAPEOverride(apeOverride) } var data []byte toJSON, _ := cmd.Flags().GetBool(jsonFlag) if toJSON { data, err = json.Marshal(b) commonCmd.ExitOnErr(cmd, "can't mashal token to JSON: %w", err) } else { data = b.Marshal() } out, _ := cmd.Flags().GetString(outFlag) err = os.WriteFile(out, data, 0o644) commonCmd.ExitOnErr(cmd, "can't write token to file: %w", err) }