github.com/criteo/command-launcher@v0.0.0-20230407142452-fb616f546e98/cmd/login.go (about)

     1  package cmd
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"os"
     7  	"syscall"
     8  
     9  	"github.com/criteo/command-launcher/internal/command"
    10  	"github.com/criteo/command-launcher/internal/context"
    11  	"github.com/criteo/command-launcher/internal/helper"
    12  	"github.com/spf13/cobra"
    13  	"golang.org/x/crypto/ssh/terminal"
    14  
    15  	log "github.com/sirupsen/logrus"
    16  )
    17  
    18  type LoginFlags struct {
    19  	username string
    20  	password string
    21  }
    22  
    23  var (
    24  	loginFlags = LoginFlags{}
    25  )
    26  
    27  func defaultUsername() string {
    28  	user, present := os.LookupEnv("USER")
    29  	if !present {
    30  		user, _ = os.LookupEnv("USERNAME")
    31  	}
    32  
    33  	return user
    34  }
    35  
    36  func AddLoginCmd(rootCmd *cobra.Command, appCtx context.LauncherContext, loginHook command.Command) {
    37  	loginCmd := &cobra.Command{
    38  		Use:   "login",
    39  		Short: "Login to use services",
    40  		Long: fmt.Sprintf(`
    41  Login to use services.
    42  
    43  You can specify your password from:
    44  1. command option: --password (-p)
    45  2. environment variable: %s
    46  3. command line input
    47  
    48  The credential will be stored in your system vault.`, appCtx.PasswordEnvVar()),
    49  		RunE: func(cmd *cobra.Command, args []string) error {
    50  			appCtx, _ := context.AppContext()
    51  			username := loginFlags.username
    52  			if username == "" {
    53  				username = os.Getenv(appCtx.UsernameEnvVar())
    54  				if username == "" {
    55  					fmt.Printf("Please enter your user name: ")
    56  					nb, err := fmt.Scan(&username)
    57  					if err != nil {
    58  						return err
    59  					}
    60  
    61  					if nb != 1 {
    62  						return fmt.Errorf("invalid entries (expected only one argument)")
    63  					}
    64  				}
    65  			}
    66  
    67  			passwd := loginFlags.password
    68  			if passwd == "" {
    69  				passwd = os.Getenv(appCtx.PasswordEnvVar())
    70  				if passwd == "" {
    71  					fmt.Printf("Please enter your password: ")
    72  					pass, err := terminal.ReadPassword(int(syscall.Stdin))
    73  					if err != nil {
    74  						return err
    75  					}
    76  					passwd = string(pass)
    77  				}
    78  			}
    79  
    80  			fmt.Println()
    81  
    82  			// call system login hook if defined
    83  			if loginHook != nil {
    84  				log.Debug("calling login system hook")
    85  				_, hookOutput, err := loginHook.ExecuteWithOutput([]string{}, username, passwd)
    86  				if err != nil {
    87  					return err
    88  				}
    89  				credentials, err := parseLoginHookOutput(hookOutput)
    90  				if err != nil {
    91  					return err
    92  				}
    93  				for k, v := range credentials {
    94  					helper.SetSecret(k, v)
    95  				}
    96  			} else {
    97  				log.Debug("no login system hook registered, use default")
    98  				helper.SetUsername(username)
    99  				helper.SetPassword(passwd)
   100  			}
   101  			return nil
   102  		},
   103  	}
   104  	loginCmd.Flags().StringVarP(&loginFlags.username, "user", "u", defaultUsername(), "User name")
   105  	loginCmd.Flags().StringVarP(&loginFlags.password, "password", "p", "", "User password")
   106  
   107  	rootCmd.AddCommand(loginCmd)
   108  }
   109  
   110  func parseLoginHookOutput(output string) (map[string]string, error) {
   111  	credentials := map[string]string{}
   112  	if err := json.Unmarshal([]byte(output), &credentials); err != nil {
   113  		return nil, err
   114  	}
   115  	return credentials, nil
   116  }