github.com/kbehouse/nsc@v0.0.6/cmd/generatecreds.go (about) 1 /* 2 * Copyright 2018 The NATS Authors 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 package cmd 17 18 import ( 19 "errors" 20 "fmt" 21 22 "github.com/nats-io/jwt/v2" 23 24 "github.com/kbehouse/nsc/cmd/store" 25 "github.com/nats-io/nkeys" 26 "github.com/spf13/cobra" 27 ) 28 29 func createGenerateCredsCmd() *cobra.Command { 30 var params GenerateCredsParams 31 cmd := &cobra.Command{ 32 Use: "creds", 33 Short: "Generate a credentials file for an user", 34 Args: MaxArgs(0), 35 SilenceUsage: true, 36 Example: `nsc generate creds --account a --name u`, 37 RunE: func(cmd *cobra.Command, args []string) error { 38 if err := RunAction(cmd, args, ¶ms); err != nil { 39 return err 40 } 41 if !QuietMode() && params.out != "--" { 42 cmd.Printf("Success!! - generated %#q\n", params.out) 43 } 44 return nil 45 }, 46 } 47 cmd.Flags().StringVarP(¶ms.user, "name", "n", "", "user name") 48 cmd.Flags().StringVarP(¶ms.out, "output-file", "o", "--", "output file '--' is stdout") 49 params.AccountContextParams.BindFlags(cmd) 50 51 return cmd 52 } 53 54 func init() { 55 generateCmd.AddCommand(createGenerateCredsCmd()) 56 } 57 58 type GenerateCredsParams struct { 59 AccountContextParams 60 user string 61 out string 62 entityKP nkeys.KeyPair 63 entityJwt []byte 64 } 65 66 func (p *GenerateCredsParams) SetDefaults(ctx ActionCtx) error { 67 p.AccountContextParams.SetDefaults(ctx) 68 if p.user == "" { 69 if p.AccountContextParams.Name != "" { 70 entries, err := ctx.StoreCtx().Store.ListEntries(store.Accounts, p.AccountContextParams.Name, store.Users) 71 if err != nil { 72 return err 73 } 74 switch len(entries) { 75 case 0: 76 return fmt.Errorf("account %q has no users", p.AccountContextParams.Name) 77 case 1: 78 p.user = entries[0] 79 } 80 } 81 } 82 83 return nil 84 } 85 86 func (p *GenerateCredsParams) PreInteractive(ctx ActionCtx) error { 87 var err error 88 if err = p.AccountContextParams.Edit(ctx); err != nil { 89 return err 90 } 91 p.user, err = ctx.StoreCtx().PickUser(p.AccountContextParams.Name) 92 if err != nil { 93 return err 94 } 95 return nil 96 } 97 98 func (p *GenerateCredsParams) Load(ctx ActionCtx) error { 99 return nil 100 } 101 102 func (p *GenerateCredsParams) PostInteractive(ctx ActionCtx) error { 103 return nil 104 } 105 106 func (p *GenerateCredsParams) Validate(ctx ActionCtx) error { 107 var err error 108 109 if p.AccountContextParams.Name == "" { 110 return fmt.Errorf("account is required") 111 } 112 if p.user == "" { 113 return fmt.Errorf("user is required") 114 } 115 116 if !ctx.StoreCtx().Store.Has(store.Accounts, p.AccountContextParams.Name, store.Users, store.JwtName(p.user)) { 117 return fmt.Errorf("user %q not found in %q", p.user, p.AccountContextParams.Name) 118 } 119 120 p.entityJwt, err = ctx.StoreCtx().Store.Read(store.Accounts, p.AccountContextParams.Name, store.Users, store.JwtName(p.user)) 121 if err != nil { 122 return err 123 } 124 125 uc, err := jwt.DecodeUserClaims(string(p.entityJwt)) 126 if err != nil { 127 return fmt.Errorf("error decoding user %q in %q jwt: %v", p.AccountContextParams.Name, p.user, err) 128 } 129 130 p.entityKP, err = ctx.StoreCtx().KeyStore.GetKeyPair(uc.Subject) 131 if err != nil { 132 return err 133 } 134 135 if p.entityKP == nil { 136 return fmt.Errorf("user was not found - please specify it") 137 } 138 139 return nil 140 } 141 142 func (p *GenerateCredsParams) Run(ctx ActionCtx) (store.Status, error) { 143 d, err := GenerateConfig(ctx.StoreCtx().Store, p.AccountContextParams.Name, p.user, p.entityKP) 144 if err != nil { 145 return nil, err 146 } 147 if err := Write(p.out, d); err != nil { 148 return nil, err 149 } 150 var s store.Status 151 if !IsStdOut(p.out) { 152 s = store.OKStatus("wrote credentials to %#q", AbbrevHomePaths(p.out)) 153 } 154 return s, nil 155 } 156 157 func GenerateConfig(s *store.Store, account string, user string, userKey nkeys.KeyPair) ([]byte, error) { 158 if s.Has(store.Accounts, account, store.Users, store.JwtName(user)) { 159 d, err := s.Read(store.Accounts, account, store.Users, store.JwtName(user)) 160 if err != nil { 161 return nil, err 162 } 163 if userKey == nil { 164 return nil, errors.New("userKey was not provided") 165 } 166 seed, err := userKey.Seed() 167 if err != nil { 168 return nil, fmt.Errorf("error getting seed: %v", err) 169 } 170 return jwt.FormatUserConfig(string(d), seed) 171 } 172 return nil, fmt.Errorf("unable to find user jwt") 173 }