github.com/kbehouse/nsc@v0.0.6/cmd/revokeuser.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  	"strings"
    21  	"time"
    22  
    23  	"github.com/kbehouse/nsc/cmd/store"
    24  	cli "github.com/nats-io/cliprompts/v2"
    25  	"github.com/nats-io/jwt/v2"
    26  	"github.com/nats-io/nkeys"
    27  	"github.com/spf13/cobra"
    28  )
    29  
    30  func CreateRevokeUserCmd() *cobra.Command {
    31  	var params RevokeUserParams
    32  	cmd := &cobra.Command{
    33  		Use:          "add-user",
    34  		Aliases:      []string{"add_user"},
    35  		Short:        "Revoke a user",
    36  		Args:         MaxArgs(0),
    37  		SilenceUsage: true,
    38  		RunE: func(cmd *cobra.Command, args []string) error {
    39  			return RunAction(cmd, args, &params)
    40  		},
    41  	}
    42  	cmd.Flags().StringVarP(&params.user, "name", "n", "", "user name")
    43  	cmd.Flags().VarP(&params.at, "at", "", "revokes all user credentials created"+
    44  		" or edited before a Unix timestamp ('0' is treated as now, accepted formats are RFC3339 or #seconds since epoch)")
    45  	params.userKey.BindFlags("user-public-key", "u", nkeys.PrefixByteUser, cmd)
    46  	params.AccountContextParams.BindFlags(cmd)
    47  
    48  	return cmd
    49  }
    50  
    51  func init() {
    52  	revokeCmd.AddCommand(CreateRevokeUserCmd())
    53  }
    54  
    55  // RevokeUserParams hold the info necessary to add a user to the revocation list in an account
    56  type RevokeUserParams struct {
    57  	AccountContextParams
    58  	at      dateTime
    59  	user    string
    60  	userKey PubKeyParams
    61  	claim   *jwt.AccountClaims
    62  	SignerParams
    63  }
    64  
    65  func (p *RevokeUserParams) SetDefaults(ctx ActionCtx) error {
    66  	if p.userKey.publicKey != "" && p.user != "" {
    67  		return fmt.Errorf("user and user-public-key are mutually exclusive")
    68  	}
    69  	p.userKey.AllowWildcard = true
    70  	p.AccountContextParams.SetDefaults(ctx)
    71  	p.SignerParams.SetDefaults(nkeys.PrefixByteOperator, true, ctx)
    72  	return nil
    73  }
    74  
    75  func (p *RevokeUserParams) PreInteractive(ctx ActionCtx) error {
    76  	return p.AccountContextParams.Edit(ctx)
    77  }
    78  
    79  func (p *RevokeUserParams) Load(ctx ActionCtx) error {
    80  	var err error
    81  	if err = p.AccountContextParams.Validate(ctx); err != nil {
    82  		return err
    83  	}
    84  
    85  	if p.user != "" {
    86  		entries, err := ListUsers(ctx.StoreCtx().Store, p.AccountContextParams.Name)
    87  		if err != nil {
    88  			return err
    89  		}
    90  
    91  		n := strings.ToLower(p.user)
    92  		for _, e := range entries {
    93  			if e.Err == nil && strings.ToLower(e.Name) == n {
    94  				p.userKey.publicKey = e.Claims.Claims().Subject
    95  				break
    96  			}
    97  		}
    98  		if p.userKey.publicKey == "" {
    99  			return fmt.Errorf("user %q not found", p.user)
   100  		}
   101  	} else if p.user == "" && p.userKey.publicKey == "" && !InteractiveFlag {
   102  		uc, err := ctx.StoreCtx().DefaultUserClaim(p.AccountContextParams.Name)
   103  		if err != nil {
   104  			return err
   105  		}
   106  		p.userKey.publicKey = uc.Subject
   107  	}
   108  
   109  	p.claim, err = ctx.StoreCtx().Store.ReadAccountClaim(p.AccountContextParams.Name)
   110  	return err
   111  }
   112  
   113  func buildUserPublicKeyChoices(accountName string, ctx ActionCtx) ([]PubKeyChoice, error) {
   114  	var choices []PubKeyChoice
   115  	keyToName := map[string]string{}
   116  	keyToName[jwt.All] = "All Users"
   117  	infos, err := ListUsers(ctx.StoreCtx().Store, accountName)
   118  	if err != nil {
   119  		return nil, err
   120  	}
   121  	for _, i := range infos {
   122  		if i.Err == nil {
   123  			pkc := PubKeyChoice{}
   124  			pkc.Key = i.Claims.Claims().Subject
   125  			pkc.Label = i.Name
   126  			choices = append(choices, pkc)
   127  		}
   128  	}
   129  	return choices, nil
   130  }
   131  
   132  func (p *RevokeUserParams) PostInteractive(ctx ActionCtx) error {
   133  	choices, err := buildUserPublicKeyChoices(p.AccountContextParams.Name, ctx)
   134  	byName := false
   135  
   136  	if len(choices) > 0 {
   137  		byName, err = cli.Confirm("Revoke a user by name", true)
   138  		if err != nil {
   139  			return err
   140  		}
   141  	}
   142  	if byName {
   143  		if err != nil || len(choices) == 0 {
   144  			return err
   145  		}
   146  		if err := p.userKey.Select("Select revoked user to clear", choices...); err != nil {
   147  			return err
   148  		}
   149  	} else if err := p.userKey.Edit(); err != nil {
   150  		return err
   151  	}
   152  
   153  	if p.at == 0 {
   154  		if _, err := cli.Prompt("revoke all credentials created before (0 is now, formats are RFC3339 or #seconds since epoch)",
   155  			fmt.Sprintf("%d", p.at), cli.Val(p.at.Set)); err != nil {
   156  			return err
   157  		}
   158  	}
   159  	if err := p.SignerParams.Edit(ctx); err != nil {
   160  		return err
   161  	}
   162  
   163  	return nil
   164  }
   165  
   166  func (p *RevokeUserParams) Validate(ctx ActionCtx) error {
   167  	if p.userKey.publicKey == "" && p.user == "" {
   168  		return fmt.Errorf("user or user-public-key is required")
   169  	}
   170  	if err := p.userKey.Valid(); err != nil {
   171  		return err
   172  	}
   173  	return p.SignerParams.Resolve(ctx)
   174  }
   175  
   176  func (p *RevokeUserParams) Run(ctx ActionCtx) (store.Status, error) {
   177  	if p.at == 0 {
   178  		p.claim.Revoke(p.userKey.publicKey)
   179  	} else {
   180  		p.claim.RevokeAt(p.userKey.publicKey, time.Unix(int64(p.at), 0))
   181  	}
   182  	token, err := p.claim.Encode(p.signerKP)
   183  	if err != nil {
   184  		return nil, err
   185  	}
   186  	r := store.NewDetailedReport(true)
   187  	StoreAccountAndUpdateStatus(ctx, token, r)
   188  	if r.HasNoErrors() {
   189  		if p.userKey.publicKey == jwt.All {
   190  			when := int64(p.at)
   191  			if when == 0 {
   192  				when = time.Now().Unix()
   193  			}
   194  			r.AddOK("revoked all users issued before %s", time.Unix(when, 0).String())
   195  		} else {
   196  			r.AddOK("revoked user %q", p.userKey.publicKey)
   197  		}
   198  	}
   199  	return r, nil
   200  }