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, &params); 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(&params.user, "name", "n", "", "user name")
    48  	cmd.Flags().StringVarP(&params.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  }