code.vegaprotocol.io/vega@v0.79.0/core/config/flags.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 config
    17  
    18  import (
    19  	"errors"
    20  	"fmt"
    21  	"io/ioutil"
    22  	"os"
    23  	"strings"
    24  
    25  	vgos "code.vegaprotocol.io/vega/libs/os"
    26  
    27  	"golang.org/x/crypto/ssh/terminal"
    28  )
    29  
    30  // Empty is used when a command or sub-command receives no argument and has no execution.
    31  type Empty struct{}
    32  
    33  var (
    34  	ErrPassphraseDoNotMatch = errors.New("passphrase do not match")
    35  
    36  	supportedOutputs = []string{
    37  		"json",
    38  		"human",
    39  	}
    40  )
    41  
    42  type OutputFlag struct {
    43  	Output Output `default:"human" description:"Specify the output format: json,human" long:"output" required:"true"`
    44  }
    45  
    46  func (f OutputFlag) GetOutput() (Output, error) {
    47  	outputStr := string(f.Output)
    48  	if !isSupportedOutput(outputStr) {
    49  		return "", fmt.Errorf("unsupported output \"%s\"", outputStr)
    50  	}
    51  	if f.Output == "human" && vgos.HasNoTTY() {
    52  		return "", errors.New("output \"human\" is not script-friendly, use \"json\" instead")
    53  	}
    54  	return f.Output, nil
    55  }
    56  
    57  func isSupportedOutput(output string) bool {
    58  	for _, o := range supportedOutputs {
    59  		if output == o {
    60  			return true
    61  		}
    62  	}
    63  	return false
    64  }
    65  
    66  type Output string
    67  
    68  func (o Output) IsHuman() bool {
    69  	return string(o) == "human"
    70  }
    71  
    72  func (o Output) IsJSON() bool {
    73  	return string(o) == "json"
    74  }
    75  
    76  type VegaHomeFlag struct {
    77  	VegaHome string `description:"Path to the custom home for vega" long:"home"`
    78  }
    79  
    80  type PassphraseFlag struct {
    81  	PassphraseFile Passphrase `description:"A file containing the passphrase for the wallet, if empty will prompt for input" long:"passphrase-file" short:"p"`
    82  }
    83  
    84  type Passphrase string
    85  
    86  func (p Passphrase) Get(prompt string, withConfirmation bool) (string, error) {
    87  	if len(p) == 0 {
    88  		if vgos.HasNoTTY() {
    89  			return "", errors.New("passphrase-file flag required without TTY")
    90  		}
    91  		return p.getFromUser(prompt, withConfirmation)
    92  	}
    93  
    94  	return p.getFromFile(string(p))
    95  }
    96  
    97  func (p Passphrase) getFromUser(prompt string, withConfirmation bool) (string, error) {
    98  	passphrase, err := promptForPassphrase(fmt.Sprintf("Enter %s passphrase:", prompt))
    99  	if err != nil {
   100  		return "", err
   101  	}
   102  
   103  	if withConfirmation {
   104  		passphraseConfirmation, err := promptForPassphrase(fmt.Sprintf("Confirm %s passphrase:", prompt))
   105  		if err != nil {
   106  			return "", err
   107  		}
   108  
   109  		if passphrase != passphraseConfirmation {
   110  			return "", ErrPassphraseDoNotMatch
   111  		}
   112  	}
   113  
   114  	return passphrase, nil
   115  }
   116  
   117  func promptForPassphrase(msg string) (string, error) {
   118  	fmt.Print(msg)
   119  	password, err := terminal.ReadPassword(int(os.Stdin.Fd()))
   120  	if err != nil {
   121  		return "", fmt.Errorf("failed to read passphrase input: %w", err)
   122  	}
   123  	fmt.Println()
   124  
   125  	return string(password), nil
   126  }
   127  
   128  func (p Passphrase) getFromFile(path string) (string, error) {
   129  	buf, err := ioutil.ReadFile(path)
   130  	if err != nil {
   131  		return "", err
   132  	}
   133  
   134  	return strings.TrimRight(string(buf), "\n"), nil
   135  }
   136  
   137  type PromptString string
   138  
   139  // Get returns a string if set or prompts user otherwise.
   140  func (p PromptString) Get(prompt, name string) (string, error) {
   141  	if len(p) == 0 {
   142  		if vgos.HasNoTTY() {
   143  			return "", fmt.Errorf("%s flag required without TTY", name)
   144  		}
   145  		return p.getFromUser(prompt)
   146  	}
   147  
   148  	return string(p), nil
   149  }
   150  
   151  func (p PromptString) getFromUser(prompt string) (string, error) {
   152  	var s string
   153  	fmt.Printf("Enter %s:", prompt)
   154  	defer func() { fmt.Printf("\n") }()
   155  	if _, err := fmt.Scanf("%s", &s); err != nil {
   156  		return "", fmt.Errorf("failed read the input: %w", err)
   157  	}
   158  
   159  	return s, nil
   160  }