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 }