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

     1  /*
     2   * Copyright 2019 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  	"os"
    22  	"time"
    23  
    24  	cli "github.com/nats-io/cliprompts/v2"
    25  	"github.com/nats-io/jwt/v2"
    26  	"github.com/nats-io/nkeys"
    27  	"github.com/nats-io/nsc/cmd/store"
    28  	"github.com/spf13/cobra"
    29  )
    30  
    31  func createDeleteAccountCmd() *cobra.Command {
    32  	var params DeleteAccountParams
    33  	cmd := &cobra.Command{
    34  		Use:   "account",
    35  		Short: "Delete an account and associated users",
    36  		Args:  cobra.MaximumNArgs(1),
    37  		Example: `nsc delete account -n name
    38  nsc delete account -i
    39  `,
    40  		RunE: func(cmd *cobra.Command, args []string) error {
    41  			return RunAction(cmd, args, &params)
    42  		},
    43  	}
    44  
    45  	cmd.Flags().StringVarP(&params.AccountContextParams.Name, "name", "n", "", "name of account to delete")
    46  	cmd.Flags().BoolVarP(&params.revoke, "revoke", "R", true, "revoke users before deleting")
    47  	cmd.Flags().BoolVarP(&params.rmNkeys, "rm-nkey", "D", false, "delete user keys")
    48  	cmd.Flags().BoolVarP(&params.rmCreds, "rm-creds", "C", false, "delete users creds")
    49  	cmd.Flags().BoolVarP(&params.force, "force", "F", false, "managed accounts must supply --force")
    50  
    51  	return cmd
    52  }
    53  
    54  func init() {
    55  	deleteCmd.AddCommand(createDeleteAccountCmd())
    56  }
    57  
    58  type DeleteAccountParams struct {
    59  	AccountContextParams
    60  	SignerParams
    61  	ac      *jwt.AccountClaims
    62  	force   bool
    63  	revoke  bool
    64  	rmCreds bool
    65  	rmNkeys bool
    66  	users   []string
    67  }
    68  
    69  func (p *DeleteAccountParams) SetDefaults(ctx ActionCtx) error {
    70  	p.AccountContextParams.Name = NameFlagOrArgument(p.AccountContextParams.Name, ctx)
    71  	if err := p.AccountContextParams.SetDefaults(ctx); err != nil {
    72  		return err
    73  	}
    74  	p.SignerParams.SetDefaults(nkeys.PrefixByteOperator, true, ctx)
    75  
    76  	return nil
    77  }
    78  
    79  func (p *DeleteAccountParams) PreInteractive(ctx ActionCtx) error {
    80  	var err error
    81  	if err = p.AccountContextParams.Edit(ctx); err != nil {
    82  		return err
    83  	}
    84  	p.revoke, err = cli.Confirm("revoke all account users before deleting", true)
    85  	if err != nil {
    86  		return err
    87  	}
    88  	p.rmNkeys, err = cli.Confirm("delete associated account and user nkeys", false)
    89  	if err != nil {
    90  		return err
    91  	}
    92  	p.rmCreds, err = cli.Confirm("delete associated user creds files", false)
    93  	if err != nil {
    94  		return err
    95  	}
    96  	return nil
    97  }
    98  
    99  func (p *DeleteAccountParams) Load(ctx ActionCtx) error {
   100  	var err error
   101  	s := ctx.StoreCtx().Store
   102  	p.ac, err = s.ReadAccountClaim(p.AccountContextParams.Name)
   103  	if err != nil {
   104  		return err
   105  	}
   106  
   107  	p.users, err = s.ListEntries(store.Accounts, p.AccountContextParams.Name, store.Users)
   108  	if err != nil {
   109  		return err
   110  	}
   111  
   112  	return nil
   113  }
   114  
   115  func (p *DeleteAccountParams) PostInteractive(ctx ActionCtx) error {
   116  	if ctx.StoreCtx().Store.IsManaged() {
   117  		m := "managed accounts may require the account JWT or nkeys to cancel a service - continue"
   118  		ok, err := cli.Confirm(m, false)
   119  		if err != nil {
   120  			return err
   121  		}
   122  		if !ok {
   123  			return errors.New("delete cancelled")
   124  		}
   125  	}
   126  
   127  	m := "deleting account or account nkey files cannot be undone - continue"
   128  	if len(p.users) > 0 {
   129  		m = "deleting accounts, users, nkeys or creds files cannot be undone - continue"
   130  	}
   131  	ok, err := cli.Confirm(m, false)
   132  	if err != nil {
   133  		return err
   134  	}
   135  	if !ok {
   136  		return errors.New("delete cancelled")
   137  	}
   138  	return nil
   139  }
   140  
   141  func (p *DeleteAccountParams) Validate(ctx ActionCtx) error {
   142  	if ctx.StoreCtx().Store.IsManaged() && !p.force {
   143  		return errors.New("managed accounts may require the account JWT or nkeys to cancel a service, specify the --force to override")
   144  	}
   145  
   146  	if err := p.SignerParams.Resolve(ctx); err != nil {
   147  		return err
   148  	}
   149  	return nil
   150  }
   151  
   152  func (p *DeleteAccountParams) Run(ctx ActionCtx) (store.Status, error) {
   153  	ctx.CurrentCmd().SilenceUsage = true
   154  
   155  	r := store.NewReport(store.OK, "delete account")
   156  	r.Opt = store.DetailsOnly
   157  	s := ctx.StoreCtx().Store
   158  	for _, n := range p.users {
   159  		uc, err := s.ReadUserClaim(p.AccountContextParams.Name, n)
   160  		if err != nil {
   161  			r.AddError("error loading user %s: %v", n, err)
   162  			continue
   163  		}
   164  
   165  		ru := store.NewReport(store.OK, fmt.Sprintf("user %s [%s]", n, uc.Subject))
   166  		r.Add(ru)
   167  		if p.revoke {
   168  			if p.ac.Revocations[uc.Subject] == 0 {
   169  				p.ac.Revoke(uc.Subject)
   170  				ru.AddOK("revoked user")
   171  			} else {
   172  				ru.AddOK("user is already revoked")
   173  			}
   174  		}
   175  		if err := s.Delete(store.Accounts, p.AccountContextParams.Name, store.Users, store.JwtName(n)); err != nil {
   176  			ru.AddFromError(err)
   177  		} else {
   178  			ru.AddOK("user deleted")
   179  		}
   180  
   181  		if p.rmNkeys {
   182  			if ctx.StoreCtx().KeyStore.HasPrivateKey(uc.Subject) {
   183  				if err := ctx.StoreCtx().KeyStore.Remove(uc.Subject); err != nil {
   184  					ru.AddFromError(err)
   185  				} else {
   186  					ru.AddOK("deleted private key")
   187  				}
   188  			} else {
   189  				ru.AddOK("private key is not stored")
   190  			}
   191  		}
   192  
   193  		if p.rmCreds {
   194  			fp := ctx.StoreCtx().KeyStore.GetUserCredsPath(p.AccountContextParams.Name, n)
   195  			if _, err := os.Stat(fp); os.IsNotExist(err) {
   196  				ru.AddOK("creds file is not stored")
   197  			} else {
   198  				if err := os.Remove(fp); err != nil {
   199  					ru.AddError("error deleting creds file %s: %v", fp, err)
   200  				} else {
   201  					ru.AddOK("removed creds file")
   202  				}
   203  			}
   204  		}
   205  	}
   206  
   207  	// maybe remove the users dir
   208  	_ = s.Delete(store.Accounts, p.AccountContextParams.Name, store.Users)
   209  
   210  	// we cannot currently remove the account JWT from the system, but we can expire it
   211  	p.ac.Expires = time.Now().Add(time.Minute).Unix()
   212  	token, err := p.ac.Encode(p.signerKP)
   213  	if err != nil {
   214  		r.AddError("error encoding account jwt: %v", err)
   215  		return r, err
   216  	}
   217  	StoreAccountAndUpdateStatus(ctx, token, r)
   218  	if ctx.StoreCtx().Store.IsManaged() {
   219  		_, err := ctx.StoreCtx().Store.ReadAccountClaim(p.AccountContextParams.Name)
   220  		if err != nil {
   221  			r.AddWarning("unable to read account %q: %v", p.AccountContextParams.Name, err)
   222  		}
   223  	}
   224  	r.AddOK("expired account %q", p.AccountContextParams.Name)
   225  
   226  	if p.rmNkeys {
   227  		// delete the account nkeys
   228  		for sk := range p.ac.SigningKeys {
   229  			if ctx.StoreCtx().KeyStore.HasPrivateKey(sk) {
   230  				if err := ctx.StoreCtx().KeyStore.Remove(sk); err != nil {
   231  					r.AddFromError(err)
   232  				} else {
   233  					r.AddOK("deleted signing key %q", sk)
   234  				}
   235  			} else {
   236  				r.AddOK("signing key %q is not stored", sk)
   237  			}
   238  		}
   239  		if ctx.StoreCtx().KeyStore.HasPrivateKey(p.ac.Subject) {
   240  			if err := ctx.StoreCtx().KeyStore.Remove(p.ac.Subject); err != nil {
   241  				r.AddFromError(err)
   242  			} else {
   243  				r.AddOK("deleted privated key %q", p.ac.Subject)
   244  			}
   245  		} else {
   246  			r.AddOK("private key %q is not stored", p.ac.Subject)
   247  		}
   248  	}
   249  
   250  	// delete the jwt
   251  	if err := s.Delete(store.Accounts, p.AccountContextParams.Name, store.JwtName(p.AccountContextParams.Name)); err != nil {
   252  		r.AddFromError(err)
   253  	} else {
   254  		r.AddOK("deleted account")
   255  	}
   256  
   257  	if err := s.Delete(store.Accounts, p.AccountContextParams.Name); err != nil {
   258  		r.AddFromError(err)
   259  	} else {
   260  		r.AddOK("deleted account directory")
   261  	}
   262  
   263  	return r, nil
   264  }