github.com/nats-io/nsc@v0.0.0-20221206222106-35db9400b257/cmd/addmapping.go (about)

     1  /*
     2   * Copyright 2021 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  	"strings"
    22  
    23  	"github.com/nats-io/jwt/v2"
    24  	"github.com/nats-io/nkeys"
    25  	"github.com/nats-io/nsc/cmd/store"
    26  	"github.com/spf13/cobra"
    27  )
    28  
    29  func createAddMappingCmd() *cobra.Command {
    30  	var params AddMappingParams
    31  	cmd := &cobra.Command{
    32  		Use:          "mapping",
    33  		Short:        "Add (or modify) a mapping entry",
    34  		Args:         MaxArgs(0),
    35  		Example:      params.longHelp(),
    36  		SilenceUsage: true,
    37  		RunE: func(cmd *cobra.Command, args []string) error {
    38  			return RunAction(cmd, args, &params)
    39  		},
    40  	}
    41  	cmd.Flags().StringVarP((*string)(&params.from), "from", "f", "", "map from subject (required)")
    42  	cmd.Flags().StringVarP((*string)(&params.to.Subject), "to", "t", "", "to subject (required)")
    43  	cmd.Flags().Uint8VarP(&params.to.Weight, "weight", "", 0, "weight [1-100] of this mapping entry (default: 100)")
    44  	cmd.Flags().StringVarP(&params.to.Cluster, "cluster", "", "", "in which cluster this mapping should apply")
    45  	params.AccountContextParams.BindFlags(cmd)
    46  
    47  	return cmd
    48  }
    49  
    50  func init() {
    51  	addCmd.AddCommand(createAddMappingCmd())
    52  }
    53  
    54  type AddMappingParams struct {
    55  	AccountContextParams
    56  	SignerParams
    57  	from  jwt.Subject
    58  	to    jwt.WeightedMapping
    59  	claim *jwt.AccountClaims
    60  }
    61  
    62  func (p *AddMappingParams) longHelp() string {
    63  	s := `toolName add mapping --from "a" --to "b"
    64  # to modify an entry, say to set a weight after the fact
    65  toolName add mapping --from "a" --to "b" --weight 50
    66  # to add two entries from one subject, set weights and execute multiple times
    67  toolName add mapping --from "a" --to "c" --weight 50
    68  `
    69  	return strings.Replace(s, "toolName", GetToolName(), -1)
    70  }
    71  
    72  func (p *AddMappingParams) SetDefaults(ctx ActionCtx) error {
    73  	if err := p.AccountContextParams.SetDefaults(ctx); err != nil {
    74  		return err
    75  	}
    76  	p.SignerParams.SetDefaults(nkeys.PrefixByteOperator, true, ctx)
    77  
    78  	return nil
    79  }
    80  
    81  func (p *AddMappingParams) PreInteractive(ctx ActionCtx) error {
    82  	return nil
    83  }
    84  
    85  func (p *AddMappingParams) Load(ctx ActionCtx) error {
    86  	var err error
    87  
    88  	if err = p.AccountContextParams.Validate(ctx); err != nil {
    89  		return err
    90  	}
    91  
    92  	p.claim, err = ctx.StoreCtx().Store.ReadAccountClaim(p.AccountContextParams.Name)
    93  	if err != nil {
    94  		return err
    95  	}
    96  
    97  	return nil
    98  }
    99  
   100  func (p *AddMappingParams) PostInteractive(_ ActionCtx) error {
   101  	return nil
   102  }
   103  
   104  func (p *AddMappingParams) Validate(ctx ActionCtx) error {
   105  
   106  	var err error
   107  	if p.from == "" {
   108  		ctx.CurrentCmd().SilenceUsage = false
   109  		return errors.New("from subject is required")
   110  	}
   111  	if p.to.Subject == "" {
   112  		return errors.New("to subject is required")
   113  	}
   114  
   115  	if p.claim.Mappings == nil {
   116  		p.claim.Mappings = jwt.Mapping{}
   117  	}
   118  
   119  	m := p.to
   120  	if v, ok := (p.claim.Mappings)[p.from]; ok {
   121  		set := false
   122  		for i, w := range v {
   123  			if w.Subject == m.Subject {
   124  				v[i] = m
   125  				set = true
   126  				break
   127  			}
   128  		}
   129  		if !set {
   130  			p.claim.Mappings[p.from] = append(v, m)
   131  		}
   132  	} else {
   133  		p.claim.Mappings[p.from] = []jwt.WeightedMapping{m}
   134  	}
   135  	var vr jwt.ValidationResults
   136  	p.claim.Mappings.Validate(&vr)
   137  	if vr.IsBlocking(true) {
   138  		return fmt.Errorf("mapping validation failed: %v", vr.Errors())
   139  	}
   140  
   141  	if err = p.SignerParams.Resolve(ctx); err != nil {
   142  		return fmt.Errorf("mapping %s", err)
   143  	}
   144  
   145  	return nil
   146  }
   147  
   148  func (p *AddMappingParams) Run(ctx ActionCtx) (store.Status, error) {
   149  	token, err := p.claim.Encode(p.signerKP)
   150  	if err != nil {
   151  		return nil, err
   152  	}
   153  
   154  	r := store.NewDetailedReport(false)
   155  	StoreAccountAndUpdateStatus(ctx, token, r)
   156  	if r.HasNoErrors() {
   157  		r.AddOK("added mapping %s -> %s, weight %d%%", p.from, p.to.Subject, p.to.GetWeight())
   158  	}
   159  	return r, err
   160  }