code.vegaprotocol.io/vega@v0.79.0/cmd/vegawallet/commands/api_token_generate.go (about)

     1  // Copyright (C) 2023 Gobalsky Labs Limited
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15  
    16  package cmd
    17  
    18  import (
    19  	"context"
    20  	"errors"
    21  	"fmt"
    22  	"io"
    23  	"time"
    24  
    25  	"code.vegaprotocol.io/vega/cmd/vegawallet/commands/cli"
    26  	"code.vegaprotocol.io/vega/cmd/vegawallet/commands/flags"
    27  	"code.vegaprotocol.io/vega/cmd/vegawallet/commands/printer"
    28  	"code.vegaprotocol.io/vega/paths"
    29  	v2 "code.vegaprotocol.io/vega/wallet/service/v2"
    30  	"code.vegaprotocol.io/vega/wallet/service/v2/connections"
    31  	tokenStoreV1 "code.vegaprotocol.io/vega/wallet/service/v2/connections/store/longliving/v1"
    32  	"code.vegaprotocol.io/vega/wallet/wallets"
    33  
    34  	"github.com/spf13/cobra"
    35  )
    36  
    37  var (
    38  	generateAPITokenLong = cli.LongDesc(`
    39  		Generate a long-living API token
    40  	`)
    41  
    42  	generateAPITokenExample = cli.Examples(`
    43  		# Generate a long-living API token
    44  		{{.Software}} api-token generate --description DESCRIPTION --wallet-name WALLET
    45  	`)
    46  )
    47  
    48  type GenerateAPITokenHandler func(f GenerateAPITokenFlags, params connections.GenerateAPITokenParams) (connections.Token, error)
    49  
    50  func NewCmdGenerateAPIToken(w io.Writer, rf *RootFlags) *cobra.Command {
    51  	h := func(f GenerateAPITokenFlags, params connections.GenerateAPITokenParams) (connections.Token, error) {
    52  		vegaPaths := paths.New(rf.Home)
    53  
    54  		walletStore, err := wallets.InitialiseStoreFromPaths(vegaPaths, false)
    55  		if err != nil {
    56  			return "", fmt.Errorf("couldn't initialise wallets store: %w", err)
    57  		}
    58  		defer walletStore.Close()
    59  
    60  		tokenStore, err := tokenStoreV1.InitialiseStore(vegaPaths, f.passphrase)
    61  		if err != nil {
    62  			if errors.Is(err, tokenStoreV1.ErrWrongPassphrase) {
    63  				return "", fmt.Errorf("could not unlock the token store: %w", err)
    64  			}
    65  			return "", fmt.Errorf("couldn't load the token store: %w", err)
    66  		}
    67  		defer tokenStore.Close()
    68  
    69  		handler := connections.NewGenerateAPITokenHandler(walletStore, tokenStore, v2.NewStdTime())
    70  		return handler.Handle(context.Background(), params)
    71  	}
    72  
    73  	return BuildCmdGenerateAPIToken(w, ensureAPITokenStoreIsInit, h, rf)
    74  }
    75  
    76  func BuildCmdGenerateAPIToken(w io.Writer, preCheck APITokenPreCheck, handler GenerateAPITokenHandler, rf *RootFlags) *cobra.Command {
    77  	f := &GenerateAPITokenFlags{}
    78  
    79  	cmd := &cobra.Command{
    80  		Use:     "generate",
    81  		Short:   "Generate a long-living API token",
    82  		Long:    generateAPITokenLong,
    83  		Example: generateAPITokenExample,
    84  		RunE: func(_ *cobra.Command, _ []string) error {
    85  			if err := preCheck(rf); err != nil {
    86  				return err
    87  			}
    88  
    89  			params, err := f.Validate()
    90  			if err != nil {
    91  				return err
    92  			}
    93  
    94  			res, err := handler(*f, params)
    95  			if err != nil {
    96  				return err
    97  			}
    98  
    99  			switch rf.Output {
   100  			case flags.InteractiveOutput:
   101  				printGeneratedAPIToken(w, params, res)
   102  			case flags.JSONOutput:
   103  				return printer.FprintJSON(w, struct {
   104  					Token connections.Token `json:"token"`
   105  				}{
   106  					Token: res,
   107  				})
   108  			}
   109  			return nil
   110  		},
   111  	}
   112  
   113  	cmd.Flags().StringVar(&f.Description,
   114  		"description",
   115  		"",
   116  		"Description of the token purpose",
   117  	)
   118  
   119  	cmd.Flags().StringVar(&f.PassphraseFile,
   120  		"tokens-passphrase-file",
   121  		"",
   122  		"Path to the file containing the tokens database passphrase",
   123  	)
   124  
   125  	cmd.Flags().StringVar(&f.WalletName,
   126  		"wallet-name",
   127  		"",
   128  		"Name of the wallet associated to the token",
   129  	)
   130  
   131  	cmd.Flags().StringVar(&f.WalletPassphraseFile,
   132  		"wallet-passphrase-file",
   133  		"",
   134  		"Path to the file containing the wallet's passphrase",
   135  	)
   136  
   137  	cmd.Flags().DurationVar(&f.ExpiresIn,
   138  		"expires-in",
   139  		0,
   140  		"How duration for which the token will be valid",
   141  	)
   142  
   143  	autoCompleteWallet(cmd, f.WalletName, "wallet-name")
   144  
   145  	return cmd
   146  }
   147  
   148  type GenerateAPITokenFlags struct {
   149  	Description          string
   150  	PassphraseFile       string
   151  	WalletName           string
   152  	WalletPassphraseFile string
   153  	ExpiresIn            time.Duration
   154  	passphrase           string
   155  }
   156  
   157  func (f *GenerateAPITokenFlags) Validate() (connections.GenerateAPITokenParams, error) {
   158  	if len(f.WalletName) == 0 {
   159  		return connections.GenerateAPITokenParams{}, flags.MustBeSpecifiedError("wallet-name")
   160  	}
   161  
   162  	passphrase, err := flags.GetPassphraseWithOptions(flags.PassphraseOptions{Name: "tokens"}, f.PassphraseFile)
   163  	if err != nil {
   164  		return connections.GenerateAPITokenParams{}, err
   165  	}
   166  	f.passphrase = passphrase
   167  
   168  	walletPassphrase, err := flags.GetPassphraseWithOptions(flags.PassphraseOptions{Name: "wallet"}, f.WalletPassphraseFile)
   169  	if err != nil {
   170  		return connections.GenerateAPITokenParams{}, err
   171  	}
   172  
   173  	var expiresIn *time.Duration
   174  	if f.ExpiresIn != 0 {
   175  		expiresIn = &f.ExpiresIn
   176  	}
   177  
   178  	tokenParams := connections.GenerateAPITokenParams{
   179  		Description: f.Description,
   180  		ExpiresIn:   expiresIn,
   181  		Wallet: connections.GenerateAPITokenWalletParams{
   182  			Name:       f.WalletName,
   183  			Passphrase: walletPassphrase,
   184  		},
   185  	}
   186  	params := tokenParams
   187  	return params, nil
   188  }
   189  
   190  func printGeneratedAPIToken(w io.Writer, params connections.GenerateAPITokenParams, token connections.Token) {
   191  	p := printer.NewInteractivePrinter(w)
   192  
   193  	str := p.String()
   194  	defer p.Print(str)
   195  
   196  	str.CheckMark().Text("The API token has been successfully generated: ").SuccessText(token.String()).NextSection()
   197  
   198  	str.RedArrow().DangerText("Important").NextLine()
   199  	str.DangerText("This token can be used by third-party applications to access the wallet ").DangerBold(params.Wallet.Name).DangerText(" and send transactions from it, automatically, without human intervention!").NextLine()
   200  	str.DangerBold("Only distribute it to applications you have absolute trust in.").NextLine()
   201  }