From 1cb57cb7cc0643f232daf5e07dcfd9f9151b9177 Mon Sep 17 00:00:00 2001 From: Edbert Linardi Date: Fri, 10 May 2024 10:12:22 -0700 Subject: [PATCH] Add confirmation to mpa approve (#424) * Add confirmation to mpa approve * always display the request, exit non non-yes, remove useless message --- services/mpa/client/client.go | 38 +++++++++++++++++++++++++++++++---- testing/integrate.sh | 2 +- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/services/mpa/client/client.go b/services/mpa/client/client.go index e29c0774..ade8b6ec 100644 --- a/services/mpa/client/client.go +++ b/services/mpa/client/client.go @@ -18,6 +18,7 @@ package client import ( + "bufio" "context" "flag" "fmt" @@ -103,17 +104,24 @@ func getAction(ctx context.Context, state *util.ExecuteState, c pb.MpaClientProx return anyAction } -type approveCmd struct{} +type approveCmd struct { + skipConfirmation bool +} func (*approveCmd) Name() string { return "approve" } func (*approveCmd) Synopsis() string { return "Approves an MPA request" } func (*approveCmd) Usage() string { - return `approve : + return `approve [--skip-confirmation]: Approves an MPA request with the specified ID. + + The --skip-confirmation flag can be used to bypass + the confirmation prompt, proceeding with the request approval. ` } -func (p *approveCmd) SetFlags(f *flag.FlagSet) {} +func (p *approveCmd) SetFlags(f *flag.FlagSet) { + f.BoolVar(&p.skipConfirmation, "skip-confirmation", false, "If true won't ask for confirmation") +} func (p *approveCmd) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus { state := args[0].(*util.ExecuteState) @@ -121,12 +129,34 @@ func (p *approveCmd) Execute(ctx context.Context, f *flag.FlagSet, args ...inter fmt.Fprintln(os.Stderr, "Please specify a single ID to approve.") return subcommands.ExitUsageError } + id := f.Args()[0] c := pb.NewMpaClientProxy(state.Conn) - action := getAction(ctx, state, c, f.Args()[0]) + action := getAction(ctx, state, c, id) if action == nil { return subcommands.ExitFailure } + fmt.Printf("MPA Request:\n%s\n", protojson.MarshalOptions{UseProtoNames: true, Multiline: true}.Format(action)) + + if !p.skipConfirmation { + // ask for confirmation + reader := bufio.NewReader(os.Stdin) + for { + fmt.Printf("Would you like to approve the request? (yes/no): ") + input, err := reader.ReadString('\n') + if err != nil { + fmt.Println("Error reading input. Please try again.") + continue + } + input = strings.TrimSpace(input) + if strings.ToLower(input) == "yes" || strings.ToLower(input) == "y" { + break + } + fmt.Print("Request is not approved. Exiting.\n") + return subcommands.ExitSuccess + } + } + approved, err := c.ApproveOneMany(ctx, &pb.ApproveRequest{ Action: action, }) diff --git a/testing/integrate.sh b/testing/integrate.sh index 90a2b33e..61a4eb4a 100755 --- a/testing/integrate.sh +++ b/testing/integrate.sh @@ -926,7 +926,7 @@ check_mv echo "healthcheck with mpa" # MPA ID is deterministic, so we approve it in parallel -sleep 1 && ./bin/sanssh ${SINGLE_TARGET} --justification 'approving' --root-ca=./auth/mtls/testdata/root.pem --client-cert=./services/mpa/testdata/approver.pem --client-key=./services/mpa/testdata/approver.key mpa approve dc83bd71-8945e78a-ff01a54c & +sleep 1 && ./bin/sanssh ${SINGLE_TARGET} --justification 'approving' --root-ca=./auth/mtls/testdata/root.pem --client-cert=./services/mpa/testdata/approver.pem --client-key=./services/mpa/testdata/approver.key mpa approve --skip-confirmation dc83bd71-8945e78a-ff01a54c & ${SANSSH_NOPROXY} ${SINGLE_TARGET} -mpa healthcheck validate ${SANSSH_PROXY} ${SINGLE_TARGET} -mpa healthcheck validate check_status $? /dev/null mv failed