This is an automated email from the ASF dual-hosted git repository.

rohit pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cloudstack-cloudmonkey.git


The following commit(s) were added to refs/heads/master by this push:
     new d0b58be  client: implement login based API calls, simplify config
d0b58be is described below

commit d0b58bee3a7d223c2b521c870c741c0183c65022
Author: Rohit Yadav <ro...@apache.org>
AuthorDate: Mon Apr 16 07:37:41 2018 +0530

    client: implement login based API calls, simplify config
    
    Signed-off-by: Rohit Yadav <ro...@apache.org>
---
 cmd/api.go       |  6 +++-
 cmd/login.go     | 27 +++++++++++++++---
 cmd/network.go   | 87 +++++++++++++++++++++++++++++++++++++++++++++++---------
 config/config.go | 67 ++++++++++++++++++++++---------------------
 4 files changed, 136 insertions(+), 51 deletions(-)

diff --git a/cmd/api.go b/cmd/api.go
index dd4400a..47a0f0f 100644
--- a/cmd/api.go
+++ b/cmd/api.go
@@ -77,7 +77,11 @@ func init() {
                                return nil
                        }
 
-                       b, _ := NewAPIRequest(r, api.Name, apiArgs)
+                       b, err := NewAPIRequest(r, api.Name, apiArgs)
+                       if err != nil {
+                               return err
+                       }
+
                        response, _ := json.MarshalIndent(b, "", "  ")
 
                        // Implement various output formats
diff --git a/cmd/login.go b/cmd/login.go
index a2ace48..36b54e8 100644
--- a/cmd/login.go
+++ b/cmd/login.go
@@ -40,33 +40,52 @@ func init() {
                                return nil
                        }
 
+                       // username
                        prompt := promptui.Prompt{
                                Label:    "Username",
                                Validate: validate,
                                Default:  "",
                        }
-
                        username, err := prompt.Run()
                        if err != nil {
                                fmt.Printf("Prompt failed %v\n", err)
                                return nil
                        }
 
+                       //password
                        prompt = promptui.Prompt{
                                Label:    "Password",
                                Validate: validate,
                                Mask:     '*',
                        }
-
                        password, err := prompt.Run()
+                       if err != nil {
+                               fmt.Printf("Prompt failed %v\n", err)
+                               return nil
+                       }
 
+                       // domain
+                       prompt = promptui.Prompt{
+                               Label:    "Domain",
+                               Validate: validate,
+                               Default:  "/",
+                       }
+                       domain, err := prompt.Run()
                        if err != nil {
                                fmt.Printf("Prompt failed %v\n", err)
                                return nil
                        }
 
-                       // TODO: implement login based key setup workflow
-                       fmt.Println("Trying to log in using", username, 
password)
+                       r.Config.ActiveProfile.Username = username
+                       r.Config.ActiveProfile.Password = password
+                       r.Config.ActiveProfile.Domain = domain
+
+                       client, _, err := Login(r)
+                       if client == nil || err != nil {
+                               fmt.Println("Failed to login, check 
credentials")
+                       } else {
+                               fmt.Println("Success!")
+                       }
 
                        return nil
                },
diff --git a/cmd/network.go b/cmd/network.go
index d40b7bd..1a57494 100644
--- a/cmd/network.go
+++ b/cmd/network.go
@@ -21,15 +21,18 @@ import (
        "bytes"
        "crypto/hmac"
        "crypto/sha1"
+       "crypto/tls"
        "encoding/base64"
        "encoding/json"
        "errors"
        "fmt"
        "io/ioutil"
        "net/http"
+       "net/http/cookiejar"
        "net/url"
        "sort"
        "strings"
+       "time"
 )
 
 func encodeRequestParams(params url.Values) string {
@@ -56,6 +59,42 @@ func encodeRequestParams(params url.Values) string {
        return buf.String()
 }
 
+// Login logs in a user based on provided request and returns http client and 
session key
+func Login(r *Request) (*http.Client, string, error) {
+       params := make(url.Values)
+       params.Add("command", "login")
+       params.Add("username", r.Config.ActiveProfile.Username)
+       params.Add("password", r.Config.ActiveProfile.Password)
+       params.Add("domain", r.Config.ActiveProfile.Domain)
+       params.Add("response", "json")
+
+       jar, _ := cookiejar.New(nil)
+       client := &http.Client{
+               Jar: jar,
+               Transport: &http.Transport{
+                       TLSClientConfig: &tls.Config{InsecureSkipVerify: 
!r.Config.ActiveProfile.VerifyCert},
+               },
+       }
+
+       sessionKey := ""
+       resp, err := client.PostForm(r.Config.ActiveProfile.URL, params)
+       if resp.StatusCode != http.StatusOK {
+               e := errors.New("failed to log in")
+               if err != nil {
+                       e = errors.New("failed to log in due to:" + err.Error())
+               }
+               return client, sessionKey, e
+       }
+
+       for _, cookie := range resp.Cookies() {
+               if cookie.Name == "sessionkey" {
+                       sessionKey = cookie.Value
+                       break
+               }
+       }
+       return client, sessionKey, nil
+}
+
 // NewAPIRequest makes an API request to configured management server
 func NewAPIRequest(r *Request, api string, args []string) 
(map[string]interface{}, error) {
        params := make(url.Values)
@@ -66,25 +105,47 @@ func NewAPIRequest(r *Request, api string, args []string) 
(map[string]interface{
                        params.Add(parts[0], parts[1])
                }
        }
+       params.Add("response", "json")
 
-       apiKey := r.Config.Core.ActiveProfile.APIKey
-       secretKey := r.Config.Core.ActiveProfile.SecretKey
+       var client *http.Client
+       var encodedParams string
+       var err error
+       if len(r.Config.ActiveProfile.APIKey) > 0 && 
len(r.Config.ActiveProfile.SecretKey) > 0 {
+               apiKey := r.Config.ActiveProfile.APIKey
+               secretKey := r.Config.ActiveProfile.SecretKey
 
-       if len(apiKey) > 0 {
-               params.Add("apiKey", apiKey)
-       }
+               if len(apiKey) > 0 {
+                       params.Add("apiKey", apiKey)
+               }
 
-       params.Add("response", "json")
-       encodedParams := encodeRequestParams(params)
+               client = &http.Client{
+                       Transport: &http.Transport{
+                               TLSClientConfig: 
&tls.Config{InsecureSkipVerify: !r.Config.ActiveProfile.VerifyCert},
+                       },
+               }
+               encodedParams = encodeRequestParams(params)
 
-       mac := hmac.New(sha1.New, []byte(secretKey))
-       mac.Write([]byte(strings.Replace(strings.ToLower(encodedParams), "+", 
"%20", -1)))
-       signature := base64.StdEncoding.EncodeToString(mac.Sum(nil))
-       encodedParams = encodedParams + fmt.Sprintf("&signature=%s", 
url.QueryEscape(signature))
+               mac := hmac.New(sha1.New, []byte(secretKey))
+               
mac.Write([]byte(strings.Replace(strings.ToLower(encodedParams), "+", "%20", 
-1)))
+               signature := base64.StdEncoding.EncodeToString(mac.Sum(nil))
+               encodedParams = encodedParams + fmt.Sprintf("&signature=%s", 
url.QueryEscape(signature))
+       } else if len(r.Config.ActiveProfile.Username) > 0 && 
len(r.Config.ActiveProfile.Password) > 0 {
+               var sessionKey string
+               client, sessionKey, err = Login(r)
+               if err != nil {
+                       return nil, err
+               }
+               params.Add("sessionkey", sessionKey)
+               encodedParams = encodeRequestParams(params)
+       } else {
+               fmt.Println("Please provide either apikey/secretkey or 
username/password to make an API call")
+               return nil, errors.New("failed to authenticate to make API 
call")
+       }
 
-       apiURL := fmt.Sprintf("%s?%s", r.Config.Core.ActiveProfile.URL, 
encodedParams)
+       apiURL := fmt.Sprintf("%s?%s", r.Config.ActiveProfile.URL, 
encodedParams)
 
-       response, err := http.Get(apiURL)
+       client.Timeout = time.Duration(time.Duration(r.Config.Core.Timeout) * 
time.Second)
+       response, err := client.Get(apiURL)
        if err != nil {
                fmt.Println("Error:", err)
                return nil, err
diff --git a/config/config.go b/config/config.go
index 16b2b64..97fa6da 100644
--- a/config/config.go
+++ b/config/config.go
@@ -47,21 +47,21 @@ type ServerProfile struct {
 
 // Core block describes common options for the CLI
 type Core struct {
-       AsyncBlock    bool           `ini:"asyncblock"`
-       Timeout       int            `ini:"timeout"`
-       Output        string         `ini:"output"`
-       ProfileName   string         `ini:"profile"`
-       ActiveProfile *ServerProfile `ini:"-"`
+       AsyncBlock  bool   `ini:"asyncblock"`
+       Timeout     int    `ini:"timeout"`
+       Output      string `ini:"output"`
+       ProfileName string `ini:"profile"`
 }
 
 // Config describes CLI config file and default options
 type Config struct {
-       Dir         string
-       ConfigFile  string
-       HistoryFile string
-       CacheFile   string
-       LogFile     string
-       Core        *Core
+       Dir           string
+       ConfigFile    string
+       HistoryFile   string
+       CacheFile     string
+       LogFile       string
+       Core          *Core
+       ActiveProfile *ServerProfile
 }
 
 func getDefaultConfigDir() string {
@@ -86,15 +86,15 @@ func defaultConfig() *Config {
                        Timeout:     1800,
                        Output:      JSON,
                        ProfileName: "local",
-                       ActiveProfile: &ServerProfile{
-                               URL:        "http://localhost:8080/client/api";,
-                               Username:   "admin",
-                               Password:   "password",
-                               Domain:     "/",
-                               APIKey:     "",
-                               SecretKey:  "",
-                               VerifyCert: false,
-                       },
+               },
+               ActiveProfile: &ServerProfile{
+                       URL:        "http://localhost:8080/client/api";,
+                       Username:   "admin",
+                       Password:   "password",
+                       Domain:     "/",
+                       APIKey:     "",
+                       SecretKey:  "",
+                       VerifyCert: false,
                },
        }
 }
@@ -117,7 +117,7 @@ func reloadConfig(cfg *Config) *Config {
                defaultConf := defaultConfig()
                conf := ini.Empty()
                conf.Section(ini.DEFAULT_SECTION).ReflectFrom(defaultConf.Core)
-               
conf.Section(cfg.Core.ProfileName).ReflectFrom(defaultConf.Core.ActiveProfile)
+               
conf.Section(cfg.Core.ProfileName).ReflectFrom(defaultConf.ActiveProfile)
                conf.SaveTo(cfg.ConfigFile)
        }
 
@@ -149,16 +149,16 @@ func reloadConfig(cfg *Config) *Config {
        profile, err := conf.GetSection(cfg.Core.ProfileName)
        if profile == nil {
                section, _ := conf.NewSection(cfg.Core.ProfileName)
-               section.ReflectFrom(&defaultConfig().Core.ActiveProfile)
+               section.ReflectFrom(&defaultConfig().ActiveProfile)
        } else {
                // Write
-               if cfg.Core.ActiveProfile != nil {
-                       
conf.Section(cfg.Core.ProfileName).ReflectFrom(&cfg.Core.ActiveProfile)
+               if cfg.ActiveProfile != nil {
+                       
conf.Section(cfg.Core.ProfileName).ReflectFrom(&cfg.ActiveProfile)
                }
                // Update
                profile := new(ServerProfile)
                conf.Section(cfg.Core.ProfileName).MapTo(profile)
-               cfg.Core.ActiveProfile = profile
+               cfg.ActiveProfile = profile
        }
        // Save
        conf.SaveTo(cfg.ConfigFile)
@@ -187,21 +187,21 @@ func (c *Config) UpdateConfig(key string, value string) {
                c.Core.Timeout = intValue
        case "profile":
                c.Core.ProfileName = value
-               c.Core.ActiveProfile = nil
+               c.ActiveProfile = nil
        case "url":
-               c.Core.ActiveProfile.URL = value
+               c.ActiveProfile.URL = value
        case "username":
-               c.Core.ActiveProfile.Username = value
+               c.ActiveProfile.Username = value
        case "password":
-               c.Core.ActiveProfile.Password = value
+               c.ActiveProfile.Password = value
        case "domain":
-               c.Core.ActiveProfile.Domain = value
+               c.ActiveProfile.Domain = value
        case "apikey":
-               c.Core.ActiveProfile.APIKey = value
+               c.ActiveProfile.APIKey = value
        case "secretkey":
-               c.Core.ActiveProfile.SecretKey = value
+               c.ActiveProfile.SecretKey = value
        case "verifycert":
-               c.Core.ActiveProfile.VerifyCert = value == "true"
+               c.ActiveProfile.VerifyCert = value == "true"
        default:
                return
        }
@@ -213,6 +213,7 @@ func (c *Config) UpdateConfig(key string, value string) {
 func NewConfig() *Config {
        defaultConf := defaultConfig()
        defaultConf.Core = nil
+       defaultConf.ActiveProfile = nil
        cfg := reloadConfig(defaultConf)
        LoadCache(cfg)
        return cfg

-- 
To stop receiving notification emails like this one, please contact
ro...@apache.org.

Reply via email to