github.com/kbehouse/nsc@v0.0.6/cmd/signerparams.go (about) 1 /* 2 * Copyright 2018-2020 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 "fmt" 20 "os" 21 "strings" 22 23 "github.com/nats-io/jwt/v2" 24 25 "github.com/kbehouse/nsc/cmd/store" 26 cli "github.com/nats-io/cliprompts/v2" 27 "github.com/nats-io/nkeys" 28 ) 29 30 // SignerParams is shared UI for a signer (-K flag). The key 31 // for a signer is never generated and must be provided 32 type SignerParams struct { 33 kind []nkeys.PrefixByte 34 signerKP nkeys.KeyPair 35 prompt string 36 } 37 38 func (p *SignerParams) SetDefaults(kind nkeys.PrefixByte, allowManaged bool, ctx ActionCtx) { 39 p.kind = append(p.kind, kind) 40 if allowManaged && ctx.StoreCtx().Store.IsManaged() && kind == nkeys.PrefixByteOperator { 41 p.kind = append(p.kind, nkeys.PrefixByteAccount) 42 } 43 } 44 45 func (p *SignerParams) SetPrompt(message string) { 46 p.prompt = message 47 } 48 49 func (p *SignerParams) SelectFromSigners(ctx ActionCtx, signers []string) error { 50 // build a list of the signing keys 51 // allow selecting a key from the ones we have 52 var notFound []string 53 var keys []string 54 var choices []string 55 56 for _, s := range signers { 57 fp := ctx.StoreCtx().KeyStore.GetKeyPath(s) 58 _, err := os.Stat(fp) 59 if err == nil { 60 keys = append(keys, fp) 61 choices = append(choices, s) 62 } else { 63 notFound = append(notFound, s) 64 } 65 } 66 // if we have more than one key, we prompt 67 if len(keys) == 1 && len(notFound) == 0 { 68 var err error 69 p.signerKP, err = ctx.StoreCtx().ResolveKey(keys[0], p.kind...) 70 if err != nil { 71 return err 72 } 73 } else { 74 // maybe add an option for asking for one we don't have 75 idx := -1 76 if len(notFound) > 0 { 77 idx = len(choices) 78 choices = append(choices, "Other...") 79 } 80 // pick a key 81 if p.prompt == "" { 82 p.prompt = "select the key to use for signing" 83 } 84 choice, err := cli.Select(p.prompt, choices[0], choices) 85 if err != nil { 86 return err 87 } 88 // if it is the extra option, ask for a path/key 89 if idx != -1 && choice == idx { 90 kinds := []string{} 91 for _, kind := range p.kind { 92 kinds = append(kinds, kind.String()) 93 } 94 label := fmt.Sprintf("path to signer %s nkey or nkey", kinds) 95 // key must be one from signing keys 96 KeyPathFlag, err = cli.Prompt(label, "", cli.Val(SeedNKeyValidatorMatching(signers, p.kind...))) 97 if err != nil { 98 return err 99 } 100 p.signerKP, err = ctx.StoreCtx().ResolveKey(KeyPathFlag, p.kind...) 101 return err 102 } else { 103 // they picked one 104 p.signerKP, err = ctx.StoreCtx().ResolveKey(keys[choice], p.kind...) 105 return err 106 } 107 } 108 109 return nil 110 } 111 112 func (p *SignerParams) Edit(ctx ActionCtx) error { 113 var err error 114 sctx := ctx.StoreCtx() 115 p.signerKP, _ = sctx.ResolveKey(KeyPathFlag, p.kind...) 116 117 if p.signerKP != nil && ctx.StoreCtx().Store.IsManaged() { 118 return nil 119 } 120 121 // build a list of the signing keys 122 var signers []string 123 if KeyPathFlag == "" { 124 signers, err = p.getSigners(ctx) 125 if err != nil { 126 return err 127 } 128 } 129 if err := p.SelectFromSigners(ctx, signers); err != nil { 130 return err 131 } 132 133 return nil 134 } 135 136 func (p *SignerParams) getSigners(ctx ActionCtx) ([]string, error) { 137 sctx := ctx.StoreCtx() 138 ks := sctx.KeyStore 139 oc, err := ctx.StoreCtx().Store.ReadOperatorClaim() 140 if err != nil { 141 return nil, err 142 } 143 144 var signers []string 145 for _, kind := range p.kind { 146 switch kind { 147 case nkeys.PrefixByteOperator: 148 KeyPathFlag = ks.GetKeyPath(sctx.Operator.PublicKey) 149 sgnrs, err := ctx.StoreCtx().GetOperatorKeys() 150 if err != nil { 151 return nil, err 152 } 153 if oc.StrictSigningKeyUsage { 154 if len(sgnrs) > 1 { 155 sgnrs = sgnrs[1:] 156 } else { 157 sgnrs = []string{} 158 } 159 } 160 signers = append(signers, sgnrs...) 161 case nkeys.PrefixByteAccount: 162 KeyPathFlag = ks.GetKeyPath(sctx.Account.PublicKey) 163 sgnrs, err := ctx.StoreCtx().GetAccountKeys(sctx.Account.Name) 164 if err != nil { 165 return nil, err 166 } 167 if oc.StrictSigningKeyUsage { 168 skipId := sctx.Store.IsManaged() 169 if !skipId && len(p.kind) == 1 { 170 skipId = true 171 } 172 if len(sgnrs) > 1 { 173 sgnrs = sgnrs[1:] 174 } else { 175 sgnrs = []string{} 176 } 177 } 178 signers = append(signers, sgnrs...) 179 } 180 } 181 return signers, nil 182 } 183 184 func (p *SignerParams) Resolve(ctx ActionCtx) error { 185 return p.ResolveWithPriority(ctx, "") 186 } 187 188 func keyByRoleName(keyStore store.KeyStore, claim *jwt.AccountClaims, role string) nkeys.KeyPair { 189 for key, v := range claim.SigningKeys { 190 if v, ok := v.(*jwt.UserScope); ok { 191 if v.Role == role { 192 if kp, _ := keyStore.GetKeyPair(key); kp != nil { 193 return kp 194 } 195 } 196 } 197 } 198 return nil 199 } 200 201 func (p *SignerParams) ResolveWithPriority(ctx ActionCtx, preferKey string) error { 202 if p.signerKP != nil { 203 return nil 204 } 205 var err error 206 // if they specified -K resolve or fail 207 if KeyPathFlag != "" { 208 // try to find key by role. on error try other methods 209 if acc := ctx.StoreCtx().Account.Name; acc != "" { 210 if claim, err := ctx.StoreCtx().Store.ReadAccountClaim(acc); err == nil { 211 p.signerKP = keyByRoleName(ctx.StoreCtx().KeyStore, claim, KeyPathFlag) 212 if p.signerKP != nil { 213 return nil 214 } 215 } 216 } 217 // try to interpret as public key. on error try other methods 218 if nkeys.IsValidPublicKey(KeyPathFlag) { 219 for _, k := range p.kind { 220 if k, err := nkeys.Decode(k, []byte(KeyPathFlag)); err == nil && k != nil { 221 if kp, _ := ctx.StoreCtx().KeyStore.GetKeyPair(KeyPathFlag); kp != nil { 222 p.signerKP = kp 223 return nil 224 } 225 } 226 } 227 } 228 // try file or seed 229 p.signerKP, err = ctx.StoreCtx().ResolveKey(KeyPathFlag, p.kind...) 230 if err != nil { 231 return err 232 } 233 if p.signerKP == nil { 234 signers, err := p.getSigners(ctx) 235 if err != nil { 236 return err 237 } 238 return fmt.Errorf("unable to resolve any of the following signing keys in the keystore: %s", strings.Join(signers, ", ")) 239 } 240 return err 241 } 242 // if nothing specified, autoselect anything we can find 243 signers, err := p.getSigners(ctx) 244 if err != nil { 245 return fmt.Errorf("error reading signers: %v", err) 246 } 247 for _, s := range signers { 248 if s == preferKey { 249 signers = append([]string{s}, signers...) 250 break 251 } 252 } 253 254 var selected string 255 for _, s := range signers { 256 fp := ctx.StoreCtx().KeyStore.GetKeyPath(s) 257 _, err = os.Stat(fp) 258 if err == nil { 259 selected = fp 260 break 261 } 262 } 263 if selected == "" { 264 return fmt.Errorf("unable to resolve any of the following signing keys in the keystore: %s", strings.Join(signers, ", ")) 265 } 266 p.signerKP, err = ctx.StoreCtx().ResolveKey(selected, p.kind...) 267 return err 268 } 269 270 func (p *SignerParams) ForceManagedAccountKey(ctx ActionCtx, kp nkeys.KeyPair) { 271 p.Resolve(ctx) 272 if !ctx.StoreCtx().Store.IsManaged() { 273 return 274 } 275 if p.signerKP != nil && store.KeyPairTypeOk(nkeys.PrefixByteAccount, p.signerKP) { 276 p.signerKP = nil 277 } 278 if p.signerKP != nil { 279 return 280 } 281 // use the account as the signer 282 p.signerKP = kp 283 // check we have a private key available 284 pk, _ := p.signerKP.PrivateKey() 285 if pk == nil { 286 // try to load it 287 pub, _ := p.signerKP.PublicKey() 288 kp, err := ctx.StoreCtx().KeyStore.GetKeyPair(pub) 289 if err == nil { 290 pk, _ := kp.PrivateKey() 291 if pk != nil { 292 p.signerKP = kp 293 } 294 } 295 } 296 }