github.com/hernad/nomad@v1.6.112/command/acl_auth_method_update.go (about)

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