Skip to content

Commit

Permalink
Add support for contexts requiring confirmation (#1087)
Browse files Browse the repository at this point in the history
* Add support for contexts requiring confirmation

* Add back
  • Loading branch information
nickmisasi authored Dec 9, 2024
1 parent 25c3cdb commit b1b8f71
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 25 deletions.
24 changes: 13 additions & 11 deletions cmd/cloud/clicontext/clicontext.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 12,12 @@ import (
type ContextKeyServerURL struct{}

type CLIContext struct {
AuthData *auth.AuthorizationResponse `json:"auth_data"`
ClientID string `json:"client_id"`
OrgURL string `json:"org_url"`
ServerURL string `json:"server_url"`
Alias string `json:"alias"`
AuthData *auth.AuthorizationResponse `json:"auth_data"`
ClientID string `json:"client_id"`
OrgURL string `json:"org_url"`
ServerURL string `json:"server_url"`
Alias string `json:"alias"`
ConfirmationRequired bool `json:"confirmation_required"`
}

type Contexts struct {
Expand All @@ -42,13 43,14 @@ func (c *Contexts) Get(contextName string) *CLIContext {
return &context
}

func (c *Contexts) UpdateContext(contextName string, authData *auth.AuthorizationResponse, clientID, orgURL, alias, serverURL string) error {
func (c *Contexts) UpdateContext(contextName string, authData *auth.AuthorizationResponse, clientID, orgURL, alias, serverURL string, confirmationRequired bool) error {
c.Contexts[contextName] = CLIContext{
AuthData: authData,
ClientID: clientID,
OrgURL: orgURL,
Alias: alias,
ServerURL: serverURL,
AuthData: authData,
ClientID: clientID,
OrgURL: orgURL,
Alias: alias,
ServerURL: serverURL,
ConfirmationRequired: confirmationRequired,
}

return WriteContexts(c)
Expand Down
34 changes: 22 additions & 12 deletions cmd/cloud/contexts.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 82,17 @@ func newCmdContextGet() *cobra.Command {

type updateContextFlags struct {
createContextFlags
Context string
ClearAuth bool
Context string
ClearAuth bool
ConfirmationRequired bool
}

func (f *updateContextFlags) addFlags(command *cobra.Command) {
f.createContextFlags.addFlags(command)
command.Flags().StringVar(&f.Context, "context", "", "Name of the context to update")
command.MarkFlagRequired("context")
command.Flags().BoolVar(&f.ClearAuth, "clear-auth", false, "Turns off authentication for this context")
command.Flags().BoolVar(&f.ConfirmationRequired, "confirmation-required", false, "Require confirmation for commands run in this context")
}

func newCmdContextUpdate() *cobra.Command {
Expand All @@ -107,6 109,7 @@ func newCmdContextUpdate() *cobra.Command {
Alias := flags.Alias
clearAuth := flags.ClearAuth
contextName := flags.Context
confirmationRequired := flags.ConfirmationRequired

contexts, err := clicontext.ReadContexts()
if err != nil {
Expand Down Expand Up @@ -135,6 138,10 @@ func newCmdContextUpdate() *cobra.Command {
context.Alias = Alias
}

if confirmationRequired != context.ConfirmationRequired {
context.ConfirmationRequired = confirmationRequired
}

if !skipAuth && !clearAuth && clientID != "" && orgURL != "" {
login, err := auth.Login(command.Context(), orgURL, clientID)
if err != nil {
Expand Down Expand Up @@ -163,11 170,12 @@ func newCmdContextUpdate() *cobra.Command {

type createContextFlags struct {
clusterFlags
ClientID string
OrgURL string
SkipAuth bool
Alias string
ServerURL string
ClientID string
OrgURL string
SkipAuth bool
Alias string
ServerURL string
ConfirmationRequired bool
}

func (f *createContextFlags) addFlags(command *cobra.Command) {
Expand All @@ -189,6 197,7 @@ func newCmdContextCreate() *cobra.Command {
orgURL := flags.OrgURL
skipAuth := flags.SkipAuth
Alias := flags.Alias
confirmationRequired := flags.ConfirmationRequired
contextName := ""

if Alias != "" {
Expand Down Expand Up @@ -217,11 226,12 @@ func newCmdContextCreate() *cobra.Command {
}

contexts.Contexts[contextName] = clicontext.CLIContext{
ClientID: clientID,
OrgURL: orgURL,
AuthData: authData,
Alias: Alias,
ServerURL: serverAddress,
ClientID: clientID,
OrgURL: orgURL,
AuthData: authData,
Alias: Alias,
ServerURL: serverAddress,
ConfirmationRequired: confirmationRequired,
}

contexts.CurrentContext = contextName
Expand Down
2 changes: 1 addition & 1 deletion cmd/cloud/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 52,7 @@ func newCmdLogin() *cobra.Command {

context.AuthData = &login

err = contexts.UpdateContext(contextName, context.AuthData, context.ClientID, context.OrgURL, context.Alias, context.ServerURL)
err = contexts.UpdateContext(contextName, context.AuthData, context.ClientID, context.OrgURL, context.Alias, context.ServerURL, context.ConfirmationRequired)
return err
},
}
Expand Down
27 changes: 26 additions & 1 deletion cmd/cloud/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 6,9 @@
package main

import (
"bufio"
"context"
"fmt"
"os"
"strings"

Expand All @@ -25,6 27,18 @@ func isCommandThatSkipsAuth(cmd *cobra.Command) bool {
return cmd.Use == "migrate" || cmd.Use == "server" || cmd.Use == "login" || cmd.Parent().Use == "contexts"
}

func askConfirmation(contextURL string) bool {
reader := bufio.NewReader(os.Stdin)
fmt.Printf("Commands against this context (URL: %s) require confirmation. Do you want to proceed? (Y/N): ", contextURL)
response, err := reader.ReadString('\n')
if err != nil {
logger.WithError(err).Fatal("Failed to read user input.")
return false
}
response = strings.TrimSpace(strings.ToLower(response))
return response == "y" || response == "yes"
}

// TODO: Add support for --context flag to all commands that can use a context different from current one
var rootCmd = &cobra.Command{
Use: "cloud",
Expand Down Expand Up @@ -75,11 89,21 @@ var rootCmd = &cobra.Command{
}

// Update disk copy of context with new auth data if any
err = contexts.UpdateContext(currentContext.Alias, authData, currentContext.ClientID, currentContext.OrgURL, currentContext.Alias, currentContext.ServerURL)
err = contexts.UpdateContext(contexts.CurrentContext, authData, currentContext.ClientID, currentContext.OrgURL, currentContext.Alias, currentContext.ServerURL, currentContext.ConfirmationRequired)
if err != nil {
logger.WithError(err).Fatal("Failed to update context with new auth data.")
}
cmd.SetContext(context.WithValue(cmd.Context(), clicontext.ContextKeyServerURL{}, currentContext.ServerURL))

skipConfirmation, _ := cmd.Flags().GetBool("y")
if currentContext.ConfirmationRequired && !skipConfirmation {
if !askConfirmation(currentContext.ServerURL) {
logger.Fatal("Confirmation required to proceed.")
return
}
} else if strings.Contains(currentContext.ServerURL, "prod") {
logger.Warn("\"Prod\" detected in server URL. Consider requiring confirmation on this context. Proceed with caution.")
}
}
},
RunE: func(cmd *cobra.Command, args []string) error {
Expand All @@ -101,6 125,7 @@ func init() {

_ = rootCmd.MarkFlagRequired("database")

rootCmd.PersistentFlags().BoolVarP(new(bool), "y", "y", false, "Skip confirmation prompts")
rootCmd.PersistentFlags().String("context", viper.GetString("context"), "Override the current context")

rootCmd.AddCommand(newCmdServer())
Expand Down

0 comments on commit b1b8f71

Please sign in to comment.