github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/command/acl_auth_method_update.go (about)

     1  package command
     2  
     3  import (
     4  	"encoding/json"
     5  	"flag"
     6  	"fmt"
     7  	"io"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/hashicorp/nomad/api"
    12  	"github.com/mitchellh/cli"
    13  	"github.com/posener/complete"
    14  	"golang.org/x/exp/slices"
    15  )
    16  
    17  // Ensure ACLAuthMethodUpdateCommand satisfies the cli.Command interface.
    18  var _ cli.Command = &ACLAuthMethodUpdateCommand{}
    19  
    20  // ACLAuthMethodUpdateCommand implements cli.Command.
    21  type ACLAuthMethodUpdateCommand struct {
    22  	Meta
    23  
    24  	methodType    string
    25  	tokenLocality string
    26  	maxTokenTTL   time.Duration
    27  	isDefault     bool
    28  	config        string
    29  	json          bool
    30  	tmpl          string
    31  
    32  	testStdin io.Reader
    33  }
    34  
    35  // Help satisfies the cli.Command Help function.
    36  func (a *ACLAuthMethodUpdateCommand) Help() string {
    37  	helpText := `
    38  Usage: nomad acl auth-method update [options] <acl_auth_method_name>
    39  
    40    Update is used to update ACL auth methods. Use requires a management token.
    41  
    42  General Options:
    43  
    44    ` + generalOptionsUsage(usageOptsDefault|usageOptsNoNamespace) + `
    45  
    46  ACL Auth Method Update Options:
    47  
    48    -type
    49      Updates the type of the auth method. Currently the only supported type is
    50      'OIDC'.
    51  
    52    -max-token-ttl
    53      Updates the duration of time all tokens created by this auth method should be
    54      valid for.
    55  
    56    -token-locality
    57      Updates the kind of token that this auth method should produce. This can be
    58      either 'local' or 'global'.
    59  
    60    -default
    61      Specifies whether this auth method should be treated as a default one in
    62      case no auth method is explicitly specified for a login command.
    63  
    64    -config
    65      Updates auth method configuration (in JSON format). May be prefixed with
    66      '@' to indicate that the value is a file path to load the config from. '-'
    67      may also be given to indicate that the config is available on stdin.
    68  
    69    -json
    70      Output the ACL auth-method in a JSON format.
    71  
    72    -t
    73      Format and display the ACL auth-method using a Go template.
    74  `
    75  
    76  	return strings.TrimSpace(helpText)
    77  }
    78  
    79  func (a *ACLAuthMethodUpdateCommand) AutocompleteFlags() complete.Flags {
    80  	return mergeAutocompleteFlags(a.Meta.AutocompleteFlags(FlagSetClient),
    81  		complete.Flags{
    82  			"-type":           complete.PredictSet("OIDC"),
    83  			"-max-token-ttl":  complete.PredictAnything,
    84  			"-token-locality": complete.PredictSet("local", "global"),
    85  			"-default":        complete.PredictSet("true", "false"),
    86  			"-config":         complete.PredictNothing,
    87  			"-json":           complete.PredictNothing,
    88  			"-t":              complete.PredictAnything,
    89  		})
    90  }
    91  
    92  func (a *ACLAuthMethodUpdateCommand) AutocompleteArgs() complete.Predictor {
    93  	return complete.PredictNothing
    94  }
    95  
    96  // Synopsis satisfies the cli.Command Synopsis function.
    97  func (a *ACLAuthMethodUpdateCommand) Synopsis() string { return "Update an existing ACL auth method" }
    98  
    99  // Name returns the name of this command.
   100  func (*ACLAuthMethodUpdateCommand) Name() string { return "acl auth-method update" }
   101  
   102  // Run satisfies the cli.Command Run function.
   103  func (a *ACLAuthMethodUpdateCommand) Run(args []string) int {
   104  
   105  	flags := a.Meta.FlagSet(a.Name(), FlagSetClient)
   106  	flags.Usage = func() { a.Ui.Output(a.Help()) }
   107  	flags.StringVar(&a.methodType, "type", "", "")
   108  	flags.StringVar(&a.tokenLocality, "token-locality", "", "")
   109  	flags.DurationVar(&a.maxTokenTTL, "max-token-ttl", 0, "")
   110  	flags.StringVar(&a.config, "config", "", "")
   111  	flags.BoolVar(&a.isDefault, "default", false, "")
   112  	flags.BoolVar(&a.json, "json", false, "")
   113  	flags.StringVar(&a.tmpl, "t", "", "")
   114  	if err := flags.Parse(args); err != nil {
   115  		return 1
   116  	}
   117  
   118  	// Check that the last argument is the auth method name to delete.
   119  	if len(flags.Args()) != 1 {
   120  		a.Ui.Error("This command takes one argument: <acl_auth_method_name>")
   121  		a.Ui.Error(commandErrorText(a))
   122  		return 1
   123  	}
   124  
   125  	originalMethodName := flags.Args()[0]
   126  
   127  	// Get the HTTP client.
   128  	client, err := a.Meta.Client()
   129  	if err != nil {
   130  		a.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
   131  		return 1
   132  	}
   133  
   134  	// Check if the method we want to update exists
   135  	originalMethod, _, err := client.ACLAuthMethods().Get(originalMethodName, nil)
   136  	if err != nil {
   137  		a.Ui.Error(fmt.Sprintf("Error when retrieving ACL auth method: %v", err))
   138  		return 1
   139  	}
   140  
   141  	// Check if any command-specific flags were set
   142  	setFlags := []string{}
   143  	for _, f := range []string{"type", "token-locality", "max-token-ttl", "config", "default"} {
   144  		if flagPassed(flags, f) {
   145  			setFlags = append(setFlags, f)
   146  		}
   147  	}
   148  	if len(setFlags) == 0 {
   149  		a.Ui.Error("Please provide at least one flag to update the ACL auth method")
   150  		return 1
   151  	}
   152  
   153  	updatedMethod := *originalMethod
   154  
   155  	if slices.Contains(setFlags, "token-locality") {
   156  		if !slices.Contains([]string{"global", "local"}, a.tokenLocality) {
   157  			a.Ui.Error("Token locality must be set to either 'local' or 'global'")
   158  			return 1
   159  		}
   160  		updatedMethod.TokenLocality = a.tokenLocality
   161  	}
   162  
   163  	if slices.Contains(setFlags, "type") {
   164  		if strings.ToLower(a.methodType) != "oidc" {
   165  			a.Ui.Error("ACL auth method type must be set to 'OIDC'")
   166  			return 1
   167  		}
   168  		updatedMethod.Type = a.methodType
   169  	}
   170  
   171  	if slices.Contains(setFlags, "max-token-ttl") {
   172  		if a.maxTokenTTL < 1 {
   173  			a.Ui.Error("Max token TTL must be set to a value between min and max TTL configured for the server.")
   174  			return 1
   175  		}
   176  		updatedMethod.MaxTokenTTL = a.maxTokenTTL
   177  	}
   178  
   179  	if slices.Contains(setFlags, "default") {
   180  		updatedMethod.Default = a.isDefault
   181  	}
   182  
   183  	if len(a.config) != 0 {
   184  		config, err := loadDataSource(a.config, a.testStdin)
   185  		if err != nil {
   186  			a.Ui.Error(fmt.Sprintf("Error loading configuration: %v", err))
   187  			return 1
   188  		}
   189  
   190  		configJSON := api.ACLAuthMethodConfig{}
   191  		err = json.Unmarshal([]byte(config), &configJSON)
   192  		if err != nil {
   193  			a.Ui.Error(fmt.Sprintf("Unable to parse config: %v", err))
   194  			return 1
   195  		}
   196  		updatedMethod.Config = &configJSON
   197  	}
   198  
   199  	// Update the auth method via the API.
   200  	method, _, err := client.ACLAuthMethods().Update(&updatedMethod, nil)
   201  	if err != nil {
   202  		a.Ui.Error(fmt.Sprintf("Error updating ACL auth method: %v", err))
   203  		return 1
   204  	}
   205  
   206  	if a.json || len(a.tmpl) > 0 {
   207  		out, err := Format(a.json, a.tmpl, method)
   208  		if err != nil {
   209  			a.Ui.Error(err.Error())
   210  			return 1
   211  		}
   212  
   213  		a.Ui.Output(out)
   214  		return 0
   215  	}
   216  
   217  	a.Ui.Output(fmt.Sprintf("Updated ACL auth method:\n%s", formatAuthMethod(method)))
   218  	return 0
   219  }
   220  
   221  func flagPassed(flags *flag.FlagSet, name string) bool {
   222  	found := false
   223  	flags.Visit(func(f *flag.Flag) {
   224  		if f.Name == name {
   225  			found = true
   226  		}
   227  	})
   228  	return found
   229  }