github.com/nuvolaris/nuv@v0.0.0-20240511174247-a74e3a52bfd8/auth/login.go (about) 1 // Licensed to the Apache Software Foundation (ASF) under one 2 // or more contributor license agreements. See the NOTICE file 3 // distributed with this work for additional information 4 // regarding copyright ownership. The ASF licenses this file 5 // to you under the Apache License, Version 2.0 (the 6 // "License"); you may not use this file except in compliance 7 // with the License. You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, 12 // software distributed under the License is distributed on an 13 // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 // KIND, either express or implied. See the License for the 15 // specific language governing permissions and limitations 16 // under the License. 17 18 package auth 19 20 import ( 21 "bytes" 22 "encoding/json" 23 "errors" 24 "flag" 25 "fmt" 26 "io" 27 "log" 28 "net/http" 29 "os" 30 "path/filepath" 31 32 "github.com/mitchellh/go-homedir" 33 "github.com/nuvolaris/nuv/config" 34 "github.com/zalando/go-keyring" 35 ) 36 37 type LoginResult struct { 38 Login string 39 Auth string 40 ApiHost string 41 } 42 43 const usage = `Usage: 44 nuv login <apihost> [<user>] 45 46 Login to a Nuvolaris instance. If no user is specified, the default user "nuvolaris" is used. 47 You can use the environment variables NUV_APIHOST and NUV_USER to avoid specifying them on the command line. 48 And NUV_PASSWORD to avoid entering the password interactively. 49 50 Options: 51 -h, --help Show usage` 52 53 const whiskLoginPath = "/api/v1/web/whisk-system/nuv/login" 54 const defaultUser = "nuvolaris" 55 const nuvSecretServiceName = "nuvolaris" 56 57 func LoginCmd() (*LoginResult, error) { 58 flag := flag.NewFlagSet("login", flag.ExitOnError) 59 flag.Usage = func() { 60 fmt.Println(usage) 61 } 62 63 var helpFlag bool 64 flag.BoolVar(&helpFlag, "h", false, "Show usage") 65 flag.BoolVar(&helpFlag, "help", false, "Show usage") 66 err := flag.Parse(os.Args[1:]) 67 if err != nil { 68 return nil, err 69 } 70 71 if helpFlag { 72 flag.Usage() 73 return nil, nil 74 } 75 76 args := flag.Args() 77 78 if len(args) == 0 && os.Getenv("NUV_APIHOST") == "" { 79 flag.Usage() 80 return nil, errors.New("missing apihost") 81 } 82 83 password := os.Getenv("NUV_PASSWORD") 84 if password == "" { 85 fmt.Print("Enter Password: ") 86 pwd, err := AskPassword() 87 if err != nil { 88 return nil, err 89 } 90 password = pwd 91 fmt.Println() 92 } 93 94 apihost := os.Getenv("NUV_APIHOST") 95 if apihost == "" { 96 apihost = args[0] 97 } 98 url := apihost + whiskLoginPath 99 100 // try to get the user from the environment 101 user := os.Getenv("NUV_USER") 102 if user == "" { 103 // if env var not set, try to get it from the command line 104 if os.Getenv("NUV_APIHOST") != "" { 105 // if apihost env var was set, treat the first arg as the user 106 if len(args) > 0 { 107 user = args[0] 108 } 109 } else { 110 // if apihost env var was not set, treat the second arg as the user 111 if len(args) > 1 { 112 user = args[1] 113 } 114 } 115 } 116 117 // if still not set, use the default user 118 if user == "" { 119 log.Println("Using the default user:", defaultUser) 120 user = defaultUser 121 } 122 123 log.Println("Logging in as", user, "to", apihost) 124 125 creds, err := doLogin(url, user, password) 126 if err != nil { 127 return nil, err 128 } 129 130 if _, ok := creds["AUTH"]; !ok { 131 return nil, errors.New("missing AUTH token from login response") 132 } 133 134 nuvHome, err := homedir.Expand("~/.nuv") 135 if err != nil { 136 return nil, err 137 } 138 139 configMap, err := config.NewConfigMapBuilder(). 140 WithConfigJson(filepath.Join(nuvHome, "config.json")). 141 Build() 142 143 if err != nil { 144 return nil, err 145 } 146 147 for k, v := range creds { 148 if err := configMap.Insert(k, v); err != nil { 149 return nil, err 150 } 151 } 152 153 err = configMap.SaveConfig() 154 if err != nil { 155 return nil, err 156 } 157 158 // if err := storeCredentials(creds); err != nil { 159 // return nil, err 160 // } 161 162 // auth, err := keyring.Get(nuvSecretServiceName, "AUTH") 163 // if err != nil { 164 // return nil, err 165 // } 166 167 return &LoginResult{ 168 Login: user, 169 Auth: creds["AUTH"], 170 ApiHost: apihost, 171 }, nil 172 } 173 174 func doLogin(url, user, password string) (map[string]string, error) { 175 data := map[string]string{ 176 "login": user, 177 "password": password, 178 } 179 loginJson, err := json.Marshal(data) 180 if err != nil { 181 return nil, err 182 } 183 184 resp, err := http.Post(url, "application/json", bytes.NewBuffer(loginJson)) 185 if err != nil { 186 return nil, err 187 } 188 defer resp.Body.Close() 189 190 if resp.StatusCode != http.StatusOK { 191 body, err := io.ReadAll(resp.Body) 192 if err != nil { 193 return nil, fmt.Errorf("login failed with status code %d", resp.StatusCode) 194 } 195 return nil, fmt.Errorf("login failed (%d): %s", resp.StatusCode, string(body)) 196 } 197 198 var creds map[string]string 199 err = json.NewDecoder(resp.Body).Decode(&creds) 200 if err != nil { 201 return nil, errors.New("failed to decode response from login request") 202 } 203 204 return creds, nil 205 } 206 207 func storeCredentials(creds map[string]string) error { 208 for k, v := range creds { 209 err := keyring.Set(nuvSecretServiceName, k, v) 210 if err != nil { 211 return err 212 } 213 } 214 215 return nil 216 }