|
| 1 | +// Copyright (c) OpenFaaS Project 2017. All rights reserved. |
| 2 | +// Licensed under the MIT license. See LICENSE file in the project root for full license information. |
| 3 | + |
| 4 | +package commands |
| 5 | + |
| 6 | +import ( |
| 7 | + "crypto/tls" |
| 8 | + "errors" |
| 9 | + "fmt" |
| 10 | + "io/ioutil" |
| 11 | + "net/http" |
| 12 | + "os" |
| 13 | + "strings" |
| 14 | + "time" |
| 15 | + |
| 16 | + "github.com/openfaas/faas-cli/config" |
| 17 | + "github.com/spf13/cobra" |
| 18 | +) |
| 19 | + |
| 20 | +var ( |
| 21 | + username string |
| 22 | + password string |
| 23 | + passwordStdin bool |
| 24 | +) |
| 25 | + |
| 26 | +func init() { |
| 27 | + loginCmd.Flags().StringVar(&gateway, "gateway", defaultGateway, "Gateway URI") |
| 28 | + loginCmd.Flags().StringVarP(&username, "username", "u", "", "Gateway username") |
| 29 | + loginCmd.Flags().StringVarP(&password, "password", "p", "", "Gateway password") |
| 30 | + loginCmd.Flags().BoolVar(&passwordStdin, "password-stdin", false, "Reads the gateway password from stdin") |
| 31 | + |
| 32 | + faasCmd.AddCommand(loginCmd) |
| 33 | +} |
| 34 | + |
| 35 | +var loginCmd = &cobra.Command{ |
| 36 | + Use: `login [--username USERNAME] [--password PASSWORD] [--gateway GATEWAY_URL]`, |
| 37 | + Short: "Log in to OpenFaaS gateway", |
| 38 | + Long: "Log in to OpenFaaS gateway.\nIf no gateway is specified, the default local one will be used.", |
| 39 | + Example: ` faas-cli login -u user -p password --gateway http://localhost:8080 |
| 40 | + cat ~/faas_pass.txt | faas-cli login -u user --password-stdin --gateway https://openfaas.mydomain.com`, |
| 41 | + RunE: runLogin, |
| 42 | +} |
| 43 | + |
| 44 | +func runLogin(cmd *cobra.Command, args []string) error { |
| 45 | + |
| 46 | + if len(username) == 0 { |
| 47 | + return errors.New("must provide --username or -u") |
| 48 | + } |
| 49 | + |
| 50 | + if len(password) > 0 { |
| 51 | + fmt.Println("WARNING! Using --password is insecure, consider using: cat ~/faas_pass.txt | faas-cli login -u user --password-stdin") |
| 52 | + if passwordStdin { |
| 53 | + return errors.New("--password and --password-stdin are mutually exclusive") |
| 54 | + } |
| 55 | + |
| 56 | + if len(username) == 0 { |
| 57 | + return errors.New("must provide --username with --password") |
| 58 | + } |
| 59 | + } |
| 60 | + |
| 61 | + if passwordStdin { |
| 62 | + if len(username) == 0 { |
| 63 | + return errors.New("must provide --username with --password-stdin") |
| 64 | + } |
| 65 | + |
| 66 | + passwordStdin, err := ioutil.ReadAll(os.Stdin) |
| 67 | + if err != nil { |
| 68 | + return err |
| 69 | + } |
| 70 | + |
| 71 | + password = strings.TrimSpace(string(passwordStdin)) |
| 72 | + } |
| 73 | + |
| 74 | + password = strings.TrimSpace(password) |
| 75 | + if len(password) == 0 { |
| 76 | + return errors.New("must provide a non-empty password via --password or --password-stdin") |
| 77 | + } |
| 78 | + |
| 79 | + fmt.Println("Calling the OpenFaaS server to validate the credentials...") |
| 80 | + gateway = strings.TrimRight(strings.TrimSpace(gateway), "/") |
| 81 | + if err := validateLogin(gateway, username, password); err != nil { |
| 82 | + return err |
| 83 | + } |
| 84 | + |
| 85 | + if err := config.UpdateAuthConfig(gateway, username, password); err != nil { |
| 86 | + return err |
| 87 | + } |
| 88 | + |
| 89 | + user, _, err := config.LookupAuthConfig(gateway) |
| 90 | + if err != nil { |
| 91 | + return err |
| 92 | + } |
| 93 | + fmt.Println("credentials saved for", user, gateway) |
| 94 | + |
| 95 | + return nil |
| 96 | +} |
| 97 | + |
| 98 | +func validateLogin(url string, user string, pass string) error { |
| 99 | + // TODO: provide --insecure flag for this |
| 100 | + tr := &http.Transport{ |
| 101 | + DisableKeepAlives: true, |
| 102 | + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, |
| 103 | + } |
| 104 | + client := &http.Client{ |
| 105 | + Transport: tr, |
| 106 | + Timeout: time.Duration(5 * time.Second), |
| 107 | + } |
| 108 | + |
| 109 | + // TODO: implement ping in the gateway API and call that |
| 110 | + gatewayUrl := strings.TrimRight(url, "/") |
| 111 | + req, _ := http.NewRequest("GET", gateway+"/system/functions", nil) |
| 112 | + req.SetBasicAuth(user, pass) |
| 113 | + |
| 114 | + res, err := client.Do(req) |
| 115 | + if err != nil { |
| 116 | + fmt.Println(err) |
| 117 | + return fmt.Errorf("cannot connect to OpenFaaS on URL: %s", gatewayUrl) |
| 118 | + } |
| 119 | + |
| 120 | + if res.Body != nil { |
| 121 | + defer res.Body.Close() |
| 122 | + } |
| 123 | + |
| 124 | + if res.TLS == nil { |
| 125 | + fmt.Println("WARNING! Communication is not secure, please consider using HTTPS. Letsencrypt.org offers free SSL/TLS certificates.") |
| 126 | + } |
| 127 | + |
| 128 | + switch res.StatusCode { |
| 129 | + case http.StatusOK: |
| 130 | + return nil |
| 131 | + case http.StatusUnauthorized: |
| 132 | + return errors.New("unable to login, either username or password is incorrect") |
| 133 | + default: |
| 134 | + bytesOut, err := ioutil.ReadAll(res.Body) |
| 135 | + if err == nil { |
| 136 | + return fmt.Errorf("server returned unexpected status code: %d - %s", res.StatusCode, string(bytesOut)) |
| 137 | + } |
| 138 | + } |
| 139 | + |
| 140 | + return nil |
| 141 | +} |
0 commit comments