github.com/kbehouse/nsc@v0.0.6/cmd/revokeactivation.go (about) 1 /* 2 * Copyright 2018-2020 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 "github.com/kbehouse/nsc/cmd/store" 23 cli "github.com/nats-io/cliprompts/v2" 24 "github.com/nats-io/jwt/v2" 25 "github.com/nats-io/nkeys" 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 p.SignerParams.SetDefaults(nkeys.PrefixByteOperator, true, ctx) 74 return nil 75 } 76 77 func (p *RevokeActivationParams) PreInteractive(ctx ActionCtx) error { 78 var err error 79 80 if err = p.AccountContextParams.Edit(ctx); err != nil { 81 return err 82 } 83 84 p.service, err = cli.Confirm("is service", p.service) 85 if err != nil { 86 return err 87 } 88 89 return nil 90 } 91 92 func (p *RevokeActivationParams) Load(ctx ActionCtx) error { 93 var err error 94 95 if err = p.AccountContextParams.Validate(ctx); err != nil { 96 return err 97 } 98 99 p.claim, err = ctx.StoreCtx().Store.ReadAccountClaim(p.AccountContextParams.Name) 100 if err != nil { 101 return err 102 } 103 104 if len(p.claim.Exports) == 0 { 105 return fmt.Errorf("account %q doesn't have exports", p.AccountContextParams.Name) 106 } 107 108 kind := jwt.Stream 109 if p.service { 110 kind = jwt.Service 111 } 112 113 for _, v := range p.claim.Exports { 114 if v.Type != kind { 115 continue 116 } 117 p.possibleExports.Add(v) 118 } 119 120 if len(p.possibleExports) == 0 { 121 return fmt.Errorf("account %q doesn't have %v exports", 122 p.AccountContextParams.Name, kind) 123 } 124 125 return nil 126 } 127 128 func (p *RevokeActivationParams) PostInteractive(ctx ActionCtx) error { 129 var choices []string 130 if p.subject == "" { 131 for _, v := range p.possibleExports { 132 choices = append(choices, string(v.Subject)) 133 } 134 } 135 kind := jwt.Stream 136 if p.service { 137 kind = jwt.Service 138 } 139 140 i, err := cli.Select(fmt.Sprintf("select %s export", kind.String()), "", choices) 141 if err != nil { 142 return err 143 } 144 p.export = p.possibleExports[i] 145 if p.subject == "" { 146 p.subject = string(p.export.Subject) 147 } 148 149 if err = p.accountKey.Edit(); err != nil { 150 return err 151 } 152 153 if p.at == 0 { 154 if _, err := cli.Prompt("revoke all credentials created before (0 is now, formats are RFC3339 or #seconds since epoch)", 155 fmt.Sprintf("%d", p.at), cli.Val(p.at.Set)); err != nil { 156 return err 157 } 158 } 159 160 return p.SignerParams.Edit(ctx) 161 } 162 163 func (p *RevokeActivationParams) Validate(ctx ActionCtx) error { 164 165 if len(p.possibleExports) == 1 && p.subject == "" { 166 p.subject = string(p.possibleExports[0].Subject) 167 } 168 169 if p.subject == "" { 170 ctx.CurrentCmd().SilenceUsage = false 171 return fmt.Errorf("a subject is required") 172 } 173 if err := p.accountKey.Valid(); err != nil { 174 return err 175 } 176 177 sub := jwt.Subject(p.subject) 178 for _, e := range p.possibleExports { 179 if sub.IsContainedIn(e.Subject) { 180 p.export = e 181 break 182 } 183 } 184 185 if err := p.SignerParams.Resolve(ctx); err != nil { 186 return err 187 } 188 189 return nil 190 } 191 192 func (p *RevokeActivationParams) Run(ctx ActionCtx) (store.Status, error) { 193 if p.export == nil { 194 return nil, fmt.Errorf("unable to locate export") 195 } 196 197 if p.at == 0 { 198 p.export.Revoke(p.accountKey.publicKey) 199 } else { 200 p.export.RevokeAt(p.accountKey.publicKey, time.Unix(int64(p.at), 0)) 201 } 202 203 token, err := p.claim.Encode(p.signerKP) 204 if err != nil { 205 return nil, err 206 } 207 r := store.NewDetailedReport(true) 208 StoreAccountAndUpdateStatus(ctx, token, r) 209 if r.HasNoErrors() { 210 if p.accountKey.publicKey == jwt.All { 211 when := int64(p.at) 212 if when == 0 { 213 when = time.Now().Unix() 214 } 215 r.AddOK("revoked all activations for %q issued before %s", p.export.Name, time.Unix(when, 0).String()) 216 } else { 217 r.AddOK("revoked activation %q for account %s", p.export.Name, p.accountKey.publicKey) 218 } 219 } 220 return r, nil 221 }