github.com/kbehouse/nsc@v0.0.6/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 "github.com/kbehouse/nsc/cmd/store" 26 cli "github.com/nats-io/cliprompts/v2" 27 "github.com/nats-io/jwt/v2" 28 "github.com/nats-io/nkeys" 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 p.name == "*" { 184 p.name = GetRandomName(0) 185 } 186 187 names, err := GetConfig().ListAccounts() 188 if err != nil { 189 return err 190 } 191 found := false 192 lcn := strings.ToLower(p.name) 193 for _, v := range names { 194 if lcn == strings.ToLower(v) { 195 found = true 196 break 197 } 198 } 199 if found { 200 return fmt.Errorf("the account %q already exists", p.name) 201 } 202 203 if p.akp == nil { 204 return errors.New("path to an account nkey or nkey is required - specify --public-key") 205 } 206 207 kt, err := store.KeyType(p.akp) 208 if err != nil { 209 return err 210 } 211 212 if kt != nkeys.PrefixByteAccount { 213 return errors.New("invalid account key") 214 } 215 216 if err = p.TimeParams.Validate(); err != nil { 217 return err 218 } 219 220 if err := p.PermissionsParams.Validate(); err != nil { 221 return err 222 } 223 224 // the account doesn't exist, so insure self signed works 225 p.SignerParams.ForceManagedAccountKey(ctx, p.akp) 226 if err := p.SignerParams.Resolve(ctx); err != nil { 227 return err 228 } 229 230 signers, err := p.validSigners(ctx) 231 if err != nil { 232 return err 233 } 234 ok, err := ValidSigner(p.SignerParams.signerKP, signers) 235 if err != nil { 236 return err 237 } 238 if !ok { 239 return errors.New("invalid account signer") 240 } 241 return nil 242 } 243 244 func (p *AddAccountParams) Run(ctx ActionCtx) (store.Status, error) { 245 var err error 246 pk, err := p.akp.PublicKey() 247 if err != nil { 248 return nil, err 249 } 250 r := store.NewDetailedReport(false) 251 252 ac := jwt.NewAccountClaims(pk) 253 ac.Name = p.name 254 if p.TimeParams.IsStartChanged() { 255 ac.NotBefore, _ = p.TimeParams.StartDate() 256 } 257 258 if p.TimeParams.IsExpiryChanged() { 259 ac.Expires, _ = p.TimeParams.ExpiryDate() 260 } 261 262 if s, err := p.PermissionsParams.Run(&ac.DefaultPermissions, ctx); err != nil { 263 return nil, err 264 } else if s != nil { 265 r.Add(s.Details...) 266 } 267 268 signer := p.akp 269 if !ctx.StoreCtx().Store.IsManaged() || p.signerKP != nil { 270 signer = p.signerKP 271 } 272 p.token, err = ac.Encode(signer) 273 if err != nil { 274 return nil, err 275 } 276 277 if p.generate { 278 r.AddOK("generated and stored account key %q", pk) 279 } 280 StoreAccountAndUpdateStatus(ctx, p.token, r) 281 if r.HasNoErrors() { 282 r.AddOK("added account %q", p.name) 283 } 284 return r, err 285 }