github.com/nats-io/nsc@v0.0.0-20221206222106-35db9400b257/cmd/revokeactivation.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 "time" 21 22 cli "github.com/nats-io/cliprompts/v2" 23 "github.com/nats-io/jwt/v2" 24 "github.com/nats-io/nkeys" 25 "github.com/nats-io/nsc/cmd/store" 26 "github.com/spf13/cobra" 27 ) 28 29 func createRevokeActivationCmd() *cobra.Command { 30 var params RevokeActivationParams 31 cmd := &cobra.Command{ 32 Use: "add_activation", 33 Aliases: []string{"add-activation"}, 34 Short: "Revoke an accounts access to an export", 35 Args: MaxArgs(0), 36 SilenceUsage: true, 37 RunE: func(cmd *cobra.Command, args []string) error { 38 return RunAction(cmd, args, ¶ms) 39 }, 40 } 41 42 cmd.Flags().VarP(¶ms.at, "at", "", "revokes all activations for an account created"+ 43 " or edited before a Unix timestamp ('0' is treated as now, accepted formats are RFC3339 or #seconds since epoch)") 44 cmd.Flags().StringVarP(¶ms.subject, "subject", "s", "", "export subject") 45 cmd.Flags().BoolVarP(¶ms.service, "service", "", false, "service") 46 params.accountKey.BindFlags("target-account", "t", nkeys.PrefixByteAccount, cmd) 47 48 params.AccountContextParams.BindFlags(cmd) 49 50 return cmd 51 } 52 53 func init() { 54 revokeCmd.AddCommand(createRevokeActivationCmd()) 55 } 56 57 // RevokeActivationParams hold the info necessary to add a user to the revocation list in an account 58 type RevokeActivationParams struct { 59 AccountContextParams 60 SignerParams 61 claim *jwt.AccountClaims 62 export *jwt.Export 63 possibleExports jwt.Exports 64 at dateTime 65 subject string 66 service bool 67 accountKey PubKeyParams 68 } 69 70 func (p *RevokeActivationParams) SetDefaults(ctx ActionCtx) error { 71 p.accountKey.AllowWildcard = true 72 p.AccountContextParams.SetDefaults(ctx) 73 if err := p.accountKey.SetDefaults(ctx); err != nil { 74 return err 75 } 76 p.SignerParams.SetDefaults(nkeys.PrefixByteOperator, true, ctx) 77 return nil 78 } 79 80 func (p *RevokeActivationParams) PreInteractive(ctx ActionCtx) error { 81 var err error 82 83 if err = p.AccountContextParams.Edit(ctx); err != nil { 84 return err 85 } 86 87 p.service, err = cli.Confirm("is service", p.service) 88 if err != nil { 89 return err 90 } 91 92 return nil 93 } 94 95 func (p *RevokeActivationParams) Load(ctx ActionCtx) error { 96 var err error 97 98 if err = p.AccountContextParams.Validate(ctx); err != nil { 99 return err 100 } 101 102 p.claim, err = ctx.StoreCtx().Store.ReadAccountClaim(p.AccountContextParams.Name) 103 if err != nil { 104 return err 105 } 106 107 if len(p.claim.Exports) == 0 { 108 return fmt.Errorf("account %q doesn't have exports", p.AccountContextParams.Name) 109 } 110 111 kind := jwt.Stream 112 if p.service { 113 kind = jwt.Service 114 } 115 116 for _, v := range p.claim.Exports { 117 if v.Type != kind { 118 continue 119 } 120 p.possibleExports.Add(v) 121 } 122 123 if len(p.possibleExports) == 0 { 124 return fmt.Errorf("account %q doesn't have %v exports", 125 p.AccountContextParams.Name, kind) 126 } 127 128 return nil 129 } 130 131 func (p *RevokeActivationParams) PostInteractive(ctx ActionCtx) error { 132 var choices []string 133 if p.subject == "" { 134 for _, v := range p.possibleExports { 135 choices = append(choices, string(v.Subject)) 136 } 137 } 138 kind := jwt.Stream 139 if p.service { 140 kind = jwt.Service 141 } 142 143 i, err := cli.Select(fmt.Sprintf("select %s export", kind.String()), "", choices) 144 if err != nil { 145 return err 146 } 147 p.export = p.possibleExports[i] 148 if p.subject == "" { 149 p.subject = string(p.export.Subject) 150 } 151 152 if err = p.accountKey.Edit(); err != nil { 153 return err 154 } 155 156 if p.at == 0 { 157 if _, err := cli.Prompt("revoke all credentials created before (0 is now, formats are RFC3339 or #seconds since epoch)", 158 fmt.Sprintf("%d", p.at), cli.Val(p.at.Set)); err != nil { 159 return err 160 } 161 } 162 163 return p.SignerParams.Edit(ctx) 164 } 165 166 func (p *RevokeActivationParams) Validate(ctx ActionCtx) error { 167 168 if len(p.possibleExports) == 1 && p.subject == "" { 169 p.subject = string(p.possibleExports[0].Subject) 170 } 171 172 if p.subject == "" { 173 ctx.CurrentCmd().SilenceUsage = false 174 return fmt.Errorf("a subject is required") 175 } 176 if err := p.accountKey.Valid(); err != nil { 177 return err 178 } 179 180 sub := jwt.Subject(p.subject) 181 for _, e := range p.possibleExports { 182 if sub.IsContainedIn(e.Subject) { 183 p.export = e 184 break 185 } 186 } 187 188 if err := p.SignerParams.Resolve(ctx); err != nil { 189 return err 190 } 191 192 return nil 193 } 194 195 func (p *RevokeActivationParams) Run(ctx ActionCtx) (store.Status, error) { 196 if p.export == nil { 197 return nil, fmt.Errorf("unable to locate export") 198 } 199 200 if p.at == 0 { 201 p.export.Revoke(p.accountKey.publicKey) 202 } else { 203 p.export.RevokeAt(p.accountKey.publicKey, time.Unix(int64(p.at), 0)) 204 } 205 206 token, err := p.claim.Encode(p.signerKP) 207 if err != nil { 208 return nil, err 209 } 210 r := store.NewDetailedReport(true) 211 StoreAccountAndUpdateStatus(ctx, token, r) 212 if r.HasNoErrors() { 213 if p.accountKey.publicKey == jwt.All { 214 when := int64(p.at) 215 if when == 0 { 216 when = time.Now().Unix() 217 } 218 r.AddOK("revoked all activations for %q issued before %s", p.export.Name, time.Unix(when, 0).String()) 219 } else { 220 r.AddOK("revoked activation %q for account %s", p.export.Name, p.accountKey.publicKey) 221 } 222 } 223 return r, nil 224 }