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, &params)
    39  		},
    40  	}
    41  
    42  	cmd.Flags().VarP(&params.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(&params.subject, "subject", "s", "", "export subject")
    45  	cmd.Flags().BoolVarP(&params.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  }