github.com/kbehouse/nsc@v0.0.6/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 "github.com/kbehouse/nsc/cmd/store" 25 cli "github.com/nats-io/cliprompts/v2" 26 "github.com/nats-io/jwt/v2" 27 "github.com/nats-io/nkeys" 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, ¶ms) 42 }, 43 } 44 45 cmd.Flags().StringVarP(¶ms.AccountContextParams.Name, "name", "n", "", "name of account to delete") 46 cmd.Flags().BoolVarP(¶ms.revoke, "revoke", "R", true, "revoke users before deleting") 47 cmd.Flags().BoolVarP(¶ms.rmNkeys, "rm-nkey", "D", false, "delete user keys") 48 cmd.Flags().BoolVarP(¶ms.rmCreds, "rm-creds", "C", false, "delete users creds") 49 cmd.Flags().BoolVarP(¶ms.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 }