github.com/nats-io/nsc/v2@v2.8.7-0.20240307184528-efd7023c6896/cmd/addaccount.go (about) 1 /* 2 * 3 * * Copyright 2018-2019 The NATS Authors 4 * * Licensed under the Apache License, Version 2.0 (the "License"); 5 * * you may not use this file except in compliance with the License. 6 * * You may obtain a copy of the License at 7 * * 8 * * http://www.apache.org/licenses/LICENSE-2.0 9 * * 10 * * Unless required by applicable law or agreed to in writing, software 11 * * distributed under the License is distributed on an "AS IS" BASIS, 12 * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * * See the License for the specific language governing permissions and 14 * * limitations under the License. 15 * 16 */ 17 18 package cmd 19 20 import ( 21 "errors" 22 "fmt" 23 "strings" 24 25 cli "github.com/nats-io/cliprompts/v2" 26 "github.com/nats-io/jwt/v2" 27 "github.com/nats-io/nkeys" 28 "github.com/nats-io/nsc/v2/cmd/store" 29 "github.com/spf13/cobra" 30 ) 31 32 func CreateAddAccountCmd() *cobra.Command { 33 var params AddAccountParams 34 cmd := &cobra.Command{ 35 Use: "account", 36 Short: "Add an account", 37 Args: cobra.MaximumNArgs(1), 38 SilenceUsage: true, 39 40 RunE: func(cmd *cobra.Command, args []string) error { 41 if err := RunAction(cmd, args, ¶ms); err != nil { 42 return err 43 } 44 return GetConfig().SetAccount(params.name) 45 }, 46 } 47 cmd.Flags().StringVarP(¶ms.name, "name", "n", "", "account name") 48 cmd.Flags().StringVarP(¶ms.keyPath, "public-key", "k", "", "public key identifying the account") 49 params.TimeParams.BindFlags(cmd) 50 params.PermissionsParams.bindSetFlags(cmd, "default permissions") 51 52 return cmd 53 } 54 55 func init() { 56 addCmd.AddCommand(CreateAddAccountCmd()) 57 } 58 59 type AddAccountParams struct { 60 SignerParams 61 TimeParams 62 PermissionsParams 63 token string 64 name string 65 generate bool 66 keyPath string 67 akp nkeys.KeyPair 68 } 69 70 func (p *AddAccountParams) SetDefaults(ctx ActionCtx) error { 71 p.name = NameFlagOrArgument(p.name, ctx) 72 if p.name == "*" { 73 p.name = GetRandomName(0) 74 } 75 p.generate = p.keyPath == "" 76 p.SignerParams.SetDefaults(nkeys.PrefixByteOperator, true, ctx) 77 return nil 78 } 79 80 func (p *AddAccountParams) resolveAccountNKey(s string) (nkeys.KeyPair, error) { 81 nk, err := store.ResolveKey(s) 82 if err != nil { 83 return nil, err 84 } 85 if nk == nil { 86 return nil, fmt.Errorf("a key is required") 87 } 88 t, err := store.KeyType(nk) 89 if err != nil { 90 return nil, err 91 } 92 if t != nkeys.PrefixByteAccount { 93 return nil, errors.New("specified key is not a valid account nkey") 94 } 95 return nk, nil 96 } 97 98 func (p *AddAccountParams) validateAccountNKey(s string) error { 99 _, err := p.resolveAccountNKey(s) 100 return err 101 } 102 103 func (p *AddAccountParams) PreInteractive(ctx ActionCtx) error { 104 var err error 105 p.name, err = cli.Prompt("account name", p.name, cli.NewLengthValidator(1)) 106 if err != nil { 107 return err 108 } 109 110 p.generate, err = cli.Confirm("generate an account nkey", true) 111 if err != nil { 112 return err 113 } 114 if !p.generate { 115 p.keyPath, err = cli.Prompt("path to an account nkey or nkey", p.keyPath, cli.Val(p.validateAccountNKey)) 116 if err != nil { 117 return err 118 } 119 } 120 121 if err = p.TimeParams.Edit(); err != nil { 122 return err 123 } 124 125 return nil 126 } 127 128 func (p *AddAccountParams) Load(ctx ActionCtx) error { 129 var err error 130 if p.generate { 131 p.akp, err = nkeys.CreateAccount() 132 if err != nil { 133 return err 134 } 135 if p.keyPath, err = ctx.StoreCtx().KeyStore.Store(p.akp); err != nil { 136 return err 137 } 138 } else { 139 p.akp, err = p.resolveAccountNKey(p.keyPath) 140 if err != nil { 141 return err 142 } 143 } 144 145 return nil 146 } 147 148 func (p *AddAccountParams) validSigners(ctx ActionCtx) ([]string, error) { 149 oc, err := ctx.StoreCtx().Store.ReadOperatorClaim() 150 if err != nil { 151 return nil, err 152 } 153 var signers []string 154 if !oc.StrictSigningKeyUsage { 155 signers = append(signers, oc.Subject) 156 } 157 signers = append(signers, oc.SigningKeys...) 158 if ctx.StoreCtx().Store.IsManaged() && p.akp != nil { 159 pk, err := p.akp.PublicKey() 160 if err != nil { 161 return nil, err 162 } 163 signers = append(signers, pk) 164 } 165 return signers, nil 166 } 167 168 func (p *AddAccountParams) PostInteractive(ctx ActionCtx) error { 169 signers, err := p.validSigners(ctx) 170 if err != nil { 171 return err 172 } 173 p.SignerParams.SetPrompt("select the key to sign the account") 174 return p.SignerParams.SelectFromSigners(ctx, signers) 175 } 176 177 func (p *AddAccountParams) Validate(ctx ActionCtx) error { 178 var err error 179 if p.name == "" { 180 return fmt.Errorf("account name is required") 181 } 182 183 if strings.ContainsAny(p.name, "/\\") { 184 ctx.CurrentCmd().SilenceUsage = false 185 return fmt.Errorf("name cannot contain '/' or '\\'") 186 } 187 188 if p.name == "*" { 189 p.name = GetRandomName(0) 190 } 191 192 names, err := GetConfig().ListAccounts() 193 if err != nil { 194 return err 195 } 196 found := false 197 lcn := strings.ToLower(p.name) 198 for _, v := range names { 199 if lcn == strings.ToLower(v) { 200 found = true 201 break 202 } 203 } 204 if found { 205 return fmt.Errorf("the account %q already exists", p.name) 206 } 207 208 if p.akp == nil { 209 return errors.New("path to an account nkey or nkey is required - specify --public-key") 210 } 211 212 kt, err := store.KeyType(p.akp) 213 if err != nil { 214 return err 215 } 216 217 if kt != nkeys.PrefixByteAccount { 218 return errors.New("invalid account key") 219 } 220 221 if err = p.TimeParams.Validate(); err != nil { 222 return err 223 } 224 225 if err := p.PermissionsParams.Validate(); err != nil { 226 return err 227 } 228 229 // the account doesn't exist, so insure self signed works 230 p.SignerParams.ForceManagedAccountKey(ctx, p.akp) 231 if err := p.SignerParams.Resolve(ctx); err != nil { 232 return err 233 } 234 235 signers, err := p.validSigners(ctx) 236 if err != nil { 237 return err 238 } 239 ok, err := ValidSigner(p.SignerParams.signerKP, signers) 240 if err != nil { 241 return err 242 } 243 if !ok { 244 return errors.New("invalid account signer") 245 } 246 return nil 247 } 248 249 func (p *AddAccountParams) Run(ctx ActionCtx) (store.Status, error) { 250 var err error 251 pk, err := p.akp.PublicKey() 252 if err != nil { 253 return nil, err 254 } 255 r := store.NewDetailedReport(false) 256 257 ac := jwt.NewAccountClaims(pk) 258 ac.Name = p.name 259 if p.TimeParams.IsStartChanged() { 260 ac.NotBefore, _ = p.TimeParams.StartDate() 261 } 262 263 if p.TimeParams.IsExpiryChanged() { 264 ac.Expires, _ = p.TimeParams.ExpiryDate() 265 } 266 267 if s, err := p.PermissionsParams.Run(&ac.DefaultPermissions, ctx); err != nil { 268 return nil, err 269 } else if s != nil { 270 r.Add(s.Details...) 271 } 272 273 signer := p.akp 274 if !ctx.StoreCtx().Store.IsManaged() || p.signerKP != nil { 275 signer = p.signerKP 276 } 277 p.token, err = ac.Encode(signer) 278 if err != nil { 279 return nil, err 280 } 281 282 if p.generate { 283 r.AddOK("generated and stored account key %q", pk) 284 } 285 StoreAccountAndUpdateStatus(ctx, p.token, r) 286 if r.HasNoErrors() { 287 r.AddOK("added account %q", p.name) 288 } 289 return r, err 290 }