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

     1  /*
     2   * Copyright 2018-2022 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  
    22  	"github.com/nats-io/jwt/v2"
    23  	"github.com/nats-io/nkeys"
    24  	"github.com/nats-io/nsc/cmd/store"
    25  	"github.com/spf13/cobra"
    26  )
    27  
    28  func createClearRevokeUserCmd() *cobra.Command {
    29  	var params ClearRevokeUserParams
    30  	cmd := &cobra.Command{
    31  		Use:          "delete-user",
    32  		Aliases:      []string{"delete_user"},
    33  		Short:        "Remove a user revocation",
    34  		Args:         MaxArgs(0),
    35  		SilenceUsage: true,
    36  		RunE: func(cmd *cobra.Command, args []string) error {
    37  			return RunAction(cmd, args, &params)
    38  		},
    39  	}
    40  	cmd.Flags().StringVarP(&params.user, "name", "n", "", "user name")
    41  	params.userKey.BindFlags("user-public-key", "u", nkeys.PrefixByteUser, cmd)
    42  	params.AccountContextParams.BindFlags(cmd)
    43  
    44  	return cmd
    45  }
    46  
    47  func init() {
    48  	revokeCmd.AddCommand(createClearRevokeUserCmd())
    49  }
    50  
    51  // ClearRevokeUserParams hold the info necessary to add a user to the revocation list in an account
    52  type ClearRevokeUserParams struct {
    53  	AccountContextParams
    54  	user    string
    55  	userKey PubKeyParams
    56  	claim   *jwt.AccountClaims
    57  	SignerParams
    58  }
    59  
    60  func (p *ClearRevokeUserParams) SetDefaults(ctx ActionCtx) error {
    61  	if p.userKey.publicKey != "" && p.user != "" {
    62  		return fmt.Errorf("user and user-public-key are mutually exclusive")
    63  	}
    64  	p.userKey.AllowWildcard = true
    65  	p.AccountContextParams.SetDefaults(ctx)
    66  	if err := p.userKey.SetDefaults(ctx); err != nil {
    67  		return err
    68  	}
    69  	p.SignerParams.SetDefaults(nkeys.PrefixByteOperator, true, ctx)
    70  	return nil
    71  }
    72  
    73  func (p *ClearRevokeUserParams) PreInteractive(ctx ActionCtx) error {
    74  	return p.AccountContextParams.Edit(ctx)
    75  }
    76  
    77  func (p *ClearRevokeUserParams) Load(ctx ActionCtx) error {
    78  	var err error
    79  	if err = p.AccountContextParams.Validate(ctx); err != nil {
    80  		return err
    81  	}
    82  
    83  	if p.user != "" {
    84  		entries, err := ListUsers(ctx.StoreCtx().Store, p.AccountContextParams.Name)
    85  		if err != nil {
    86  			return err
    87  		}
    88  
    89  		n := strings.ToLower(p.user)
    90  		for _, e := range entries {
    91  			if e.Err == nil && strings.ToLower(e.Name) == n {
    92  				p.userKey.publicKey = e.Claims.Claims().Subject
    93  				break
    94  			}
    95  		}
    96  		if p.userKey.publicKey == "" {
    97  			return fmt.Errorf("user %q not found", p.user)
    98  		}
    99  	} else if p.user == "" && p.userKey.publicKey == "" && !InteractiveFlag {
   100  		uc, err := ctx.StoreCtx().DefaultUserClaim(p.AccountContextParams.Name)
   101  		if err != nil {
   102  			return err
   103  		}
   104  		p.userKey.publicKey = uc.Subject
   105  	}
   106  
   107  	p.claim, err = ctx.StoreCtx().Store.ReadAccountClaim(p.AccountContextParams.Name)
   108  	return err
   109  }
   110  
   111  func buildRevokedPublicKeyChoices(accountName string, ctx ActionCtx) ([]PubKeyChoice, error) {
   112  	var choices []PubKeyChoice
   113  	st := ctx.StoreCtx().Store
   114  	accClaim, err := st.ReadAccountClaim(accountName)
   115  	if err != nil || len(accClaim.Revocations) == 0 {
   116  		return choices, err
   117  	}
   118  
   119  	keyToName := map[string]string{}
   120  	keyToName[jwt.All] = "All Users"
   121  	infos, err := ListUsers(ctx.StoreCtx().Store, accountName)
   122  	if err != nil {
   123  		return nil, err
   124  	}
   125  	for _, i := range infos {
   126  		if i.Err == nil {
   127  			keyToName[i.Claims.Claims().Subject] = i.Name
   128  		}
   129  	}
   130  
   131  	for key := range accClaim.Revocations {
   132  		pkc := PubKeyChoice{}
   133  		pkc.Key = key
   134  		n := keyToName[key]
   135  		if n == "" {
   136  			n = "[Unknown User]"
   137  		}
   138  		pkc.Label = fmt.Sprintf("%s: %s", key, n)
   139  		choices = append(choices, pkc)
   140  	}
   141  	return choices, nil
   142  }
   143  
   144  func (p *ClearRevokeUserParams) PostInteractive(ctx ActionCtx) error {
   145  	choices, err := buildRevokedPublicKeyChoices(p.AccountContextParams.Name, ctx)
   146  	if err != nil || len(choices) == 0 {
   147  		return err
   148  	}
   149  	if err := p.userKey.Select("select revoked user to clear", choices...); err != nil {
   150  		return err
   151  	}
   152  	return p.SignerParams.Edit(ctx)
   153  }
   154  
   155  func (p *ClearRevokeUserParams) Validate(ctx ActionCtx) error {
   156  	if len(p.claim.Revocations) == 0 {
   157  		return fmt.Errorf("no user revocations set in account %s", p.AccountContextParams.Name)
   158  	}
   159  	if p.userKey.publicKey == "" && p.user == "" {
   160  		return fmt.Errorf("user or user-public-key is required")
   161  	}
   162  	if err := p.userKey.Valid(); err != nil {
   163  		return err
   164  	}
   165  	return p.SignerParams.Resolve(ctx)
   166  }
   167  
   168  func (p *ClearRevokeUserParams) Run(ctx ActionCtx) (store.Status, error) {
   169  	if _, ok := p.claim.Revocations[p.userKey.publicKey]; !ok {
   170  		return nil, fmt.Errorf("user with public key %s is not revoked", p.userKey.publicKey)
   171  	}
   172  	p.claim.ClearRevocation(p.userKey.publicKey)
   173  	token, err := p.claim.Encode(p.signerKP)
   174  	if err != nil {
   175  		return nil, err
   176  	}
   177  	r := store.NewDetailedReport(true)
   178  	StoreAccountAndUpdateStatus(ctx, token, r)
   179  	if r.HasNoErrors() {
   180  		if p.userKey.publicKey == jwt.All {
   181  			r.AddOK("deleted all user revocation")
   182  		} else {
   183  			r.AddOK("deleted user revocation for %q", p.userKey.publicKey)
   184  		}
   185  	}
   186  	return r, nil
   187  }