github.com/nats-io/nsc/v2@v2.8.7-0.20240307184528-efd7023c6896/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/v2/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, ¶ms) 38 }, 39 } 40 cmd.Flags().StringVarP(¶ms.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 }