github.com/nats-io/nsc/v2@v2.8.7-0.20240307184528-efd7023c6896/cmd/generatecontext.go (about)

     1  /*
     2   * Copyright 2023 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  	"encoding/json"
    20  	"fmt"
    21  	"os"
    22  	"path/filepath"
    23  	"strings"
    24  
    25  	"github.com/nats-io/nsc/v2/cmd/store"
    26  	"github.com/nats-io/nsc/v2/home"
    27  	"github.com/spf13/cobra"
    28  )
    29  
    30  func init() {
    31  	generateCmd.AddCommand(createGenerateContext())
    32  }
    33  
    34  type GenerateContextParams struct {
    35  	AccountContextParams
    36  	user        string
    37  	context     string
    38  	creds       string
    39  	operatorURL string
    40  }
    41  
    42  func createGenerateContext() *cobra.Command {
    43  	var params GenerateContextParams
    44  	cmd := &cobra.Command{
    45  		Use:          "context",
    46  		Short:        "Generate nats cli user context",
    47  		Args:         MaxArgs(0),
    48  		SilenceUsage: true,
    49  		Example:      `nsc generate context --account a --user u --context contextName`,
    50  		RunE: func(cmd *cobra.Command, args []string) error {
    51  			return RunAction(cmd, args, &params)
    52  		},
    53  	}
    54  	cmd.Flags().StringVarP(&params.user, "user", "u", "", "user name")
    55  	cmd.Flags().StringVarP(&params.context, "context", "", "", "context name")
    56  	params.AccountContextParams.BindFlags(cmd)
    57  
    58  	return cmd
    59  }
    60  
    61  func (p *GenerateContextParams) SetDefaults(ctx ActionCtx) error {
    62  	p.AccountContextParams.SetDefaults(ctx)
    63  	if p.user == "" && p.AccountContextParams.Name != "" {
    64  		entries, err := ctx.StoreCtx().Store.ListEntries(store.Accounts, p.AccountContextParams.Name, store.Users)
    65  		if err != nil {
    66  			return err
    67  		}
    68  		switch len(entries) {
    69  		case 0:
    70  			return fmt.Errorf("account %q has no users", p.AccountContextParams.Name)
    71  		case 1:
    72  			p.user = entries[0]
    73  		}
    74  	}
    75  
    76  	return nil
    77  }
    78  
    79  func (p *GenerateContextParams) PreInteractive(_ ActionCtx) error {
    80  	return nil
    81  }
    82  
    83  func (p *GenerateContextParams) Load(ctx ActionCtx) error {
    84  	oc, err := ctx.StoreCtx().Store.ReadOperatorClaim()
    85  	if err != nil {
    86  		return err
    87  	}
    88  	if len(oc.OperatorServiceURLs) > 0 {
    89  		p.operatorURL = oc.OperatorServiceURLs[0]
    90  	}
    91  	return nil
    92  }
    93  
    94  func (p *GenerateContextParams) PostInteractive(_ ActionCtx) error {
    95  	return nil
    96  }
    97  
    98  func (p *GenerateContextParams) Validate(ctx ActionCtx) error {
    99  	if p.context == "" {
   100  		return fmt.Errorf("context name is required")
   101  	}
   102  	if strings.ContainsAny(p.context, "/\\") {
   103  		return fmt.Errorf("context name cannot contain filepath separators")
   104  	}
   105  	if p.AccountContextParams.Name == "" {
   106  		return fmt.Errorf("account is required")
   107  	}
   108  	if p.user == "" {
   109  		return fmt.Errorf("user is required")
   110  	}
   111  	if !ctx.StoreCtx().Store.Has(store.Accounts, p.AccountContextParams.Name, store.Users, store.JwtName(p.user)) {
   112  		return fmt.Errorf("user %q not found in %q", p.user, p.AccountContextParams.Name)
   113  	}
   114  	p.creds = ctx.StoreCtx().KeyStore.CalcUserCredsPath(p.AccountContextParams.Name, p.user)
   115  	if _, err := os.Stat(p.creds); err != nil {
   116  		return fmt.Errorf("creds file %q - doesn't exist - nsc generate creds first", p.creds)
   117  	}
   118  	return nil
   119  }
   120  
   121  type cliContext struct {
   122  	Nsc   string `json:"nsc,omitempty"`
   123  	Url   string `json:"url,omitempty"`
   124  	Creds string `json:"creds,omitempty"`
   125  }
   126  
   127  func (p *GenerateContextParams) Run(ctx ActionCtx) (store.Status, error) {
   128  	var s store.Status
   129  	natsCli := cliContext{}
   130  	natsCli.Creds = p.creds
   131  	natsCli.Nsc = fmt.Sprintf(`nsc://%s/%s/%s`, ctx.StoreCtx().Operator.Name, p.AccountContextParams.Name, p.user)
   132  	natsCli.Url = p.operatorURL
   133  
   134  	if err := os.MkdirAll(home.NatsCliContextDir(), 0755); err != nil {
   135  		s = store.ErrorStatus("failed to create context dir %q: %v", home.NatsCliContextDir(), err)
   136  		return s, nil
   137  	}
   138  	if !strings.HasSuffix(p.context, ".json") {
   139  		p.context = fmt.Sprintf("%s.json", p.context)
   140  	}
   141  	fn := filepath.Join(home.NatsCliContextDir(), p.context)
   142  	ctx.CurrentCmd().Println(fn)
   143  	bytes, err := json.Marshal(natsCli)
   144  	if err != nil {
   145  		s = store.FromError(err)
   146  		return s, nil
   147  	}
   148  	if err := Write(fn, bytes); err != nil {
   149  		s = store.ErrorStatus("failed to write context file: %v", err)
   150  		return s, nil
   151  	}
   152  	s = store.OKStatus("wrote nats cli context file to %#q", AbbrevHomePaths(fn))
   153  
   154  	return s, nil
   155  }