From c8c760a65415e3def68e14270ae7b99962ea9958 Mon Sep 17 00:00:00 2001 From: Benedikt Bertsch Date: Mon, 3 Oct 2022 19:14:29 +0200 Subject: [PATCH 1/8] add HTTP_HEADERS function --- README.md | 1 + cli/cobra.go | 9 ++++++--- main.go | 2 +- proxmox/client.go | 13 +++++++++---- proxmox/client_test.go | 2 +- proxmox/util.go | 13 +++++++++++++ proxmox/validate.go | 7 +++++++ test/cli/shared_tests.go | 13 +++++++------ 8 files changed, 45 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index f1a98326..b1b3cabf 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ export PM_API_URL="https://xxxx.com:8006/api2/json" export PM_USER=user@pam export PM_PASS=password export PM_OTP=otpcode (only if required) +export HTTP_HEADERS=Key,Value,Key1,Value1 (only if required) ./proxmox-api-go installQemu proxmox-node-name < qemu1.json diff --git a/cli/cobra.go b/cli/cobra.go index 091fd62b..231de734 100644 --- a/cli/cobra.go +++ b/cli/cobra.go @@ -33,12 +33,12 @@ func Execute() (err error) { } func NewClient() (c *proxmox.Client) { - c, err := Client("", "", "", "") + c, err := Client("", "", "", "", "") LogFatalError(err) return } -func Client(apiUlr, userID, password, otp string) (c *proxmox.Client, err error) { +func Client(apiUlr, userID, password, otp string, http_headers string) (c *proxmox.Client, err error) { insecure, _ := RootCmd.Flags().GetBool("insecure") timeout, _ := RootCmd.Flags().GetInt("timeout") proxyUrl, _ := RootCmd.Flags().GetString("proxyurl") @@ -59,7 +59,10 @@ func Client(apiUlr, userID, password, otp string) (c *proxmox.Client, err error) if otp == "" { otp = os.Getenv("PM_OTP") } - c, err = proxmox.NewClient(apiUlr, nil, tlsconf, proxyUrl, timeout) + if http_headers == "" { + otp = os.Getenv("HTTP_HEADERS") + } + c, err = proxmox.NewClient(apiUlr, nil, http_headers, tlsconf, proxyUrl, timeout) LogFatalError(err) if userRequiresAPIToken(userID) { c.SetAPIToken(userID, password) diff --git a/main.go b/main.go index f57bcd32..527bd44f 100644 --- a/main.go +++ b/main.go @@ -36,7 +36,7 @@ func main() { if !*insecure { tlsconf = nil } - c, err := proxmox.NewClient(os.Getenv("PM_API_URL"), nil, tlsconf, *proxyURL, *taskTimeout) + c, err := proxmox.NewClient(os.Getenv("PM_API_URL"), nil, os.Getenv("HTTP_HEADERS"), tlsconf, *proxyURL, *taskTimeout) failError(err) if userRequiresAPIToken(os.Getenv("PM_USER")) { c.SetAPIToken(os.Getenv("PM_USER"), os.Getenv("PM_PASS")) diff --git a/proxmox/client.go b/proxmox/client.go index 732ed27f..b89c17fd 100644 --- a/proxmox/client.go +++ b/proxmox/client.go @@ -85,13 +85,18 @@ func NewVmRef(vmId int) (vmr *VmRef) { return } -func NewClient(apiUrl string, hclient *http.Client, tls *tls.Config, proxyString string, taskTimeout int) (client *Client, err error) { +func NewClient(apiUrl string, hclient *http.Client, http_headers string, tls *tls.Config, proxyString string, taskTimeout int) (client *Client, err error) { var sess *Session - sess, err = NewSession(apiUrl, hclient, proxyString, tls) - if err == nil { + sess, err_s := NewSession(apiUrl, hclient, proxyString, tls) + sess, err = createHeaderList(http_headers, sess) + if err != nil { + return nil, err + } + if err_s == nil { client = &Client{session: sess, ApiUrl: apiUrl, TaskTimeout: taskTimeout} } - return client, err + + return client, err_s } // SetAPIToken specifies a pair of user identifier and token UUID to use diff --git a/proxmox/client_test.go b/proxmox/client_test.go index 3f275ed3..08a8b539 100644 --- a/proxmox/client_test.go +++ b/proxmox/client_test.go @@ -9,7 +9,7 @@ import ( ) func TestClient_Login(t *testing.T) { - client, err := NewClient(os.Getenv("PM_API_URL"), nil, &tls.Config{InsecureSkipVerify: true}, "", 300) + client, err := NewClient(os.Getenv("PM_API_URL"), nil, os.Getenv("HTTP_HEADERS"), &tls.Config{InsecureSkipVerify: true}, "", 300) assert.Nil(t, err) err = client.Login(os.Getenv("PM_USER"), os.Getenv("PM_PASS"), "") diff --git a/proxmox/util.go b/proxmox/util.go index eb2fe50f..7adb812e 100644 --- a/proxmox/util.go +++ b/proxmox/util.go @@ -188,3 +188,16 @@ func failError(err error) { log.Fatal(err) } } + +// Create list of http.Header out of string, separator is "," +func createHeaderList(header_string string, sess *Session) (*Session, error) { + header_string_split := strings.Split(header_string, ",") + err := ValidateArrayEven(header_string_split, "Header key(s) and value(s) not even. Check your HTTP_HEADERS.") + if err != nil { + return nil, err + } + for i := 0; i < len(header_string_split); i += 2 { + sess.Headers.Add(header_string_split[i], header_string_split[i+1]) + } + return sess, nil +} diff --git a/proxmox/validate.go b/proxmox/validate.go index ed8b363f..3c0dbada 100644 --- a/proxmox/validate.go +++ b/proxmox/validate.go @@ -70,6 +70,13 @@ func ValidateArrayNotEmpty(array interface{}, text string) error { return ErrorKeyEmpty(text) } +func ValidateArrayEven(array interface{}, text string) error { + if len(array.([]string))%2 == 0 { + return nil + } + return ErrorKeyEmpty(text) +} + func ErrorKeyEmpty(text string) error { return fmt.Errorf("error the value of key (%s) may not be empty", text) } diff --git a/test/cli/shared_tests.go b/test/cli/shared_tests.go index d30af943..2f45e33a 100644 --- a/test/cli/shared_tests.go +++ b/test/cli/shared_tests.go @@ -80,15 +80,16 @@ func (test *Test) StandardTest(t *testing.T) { } type LoginTest struct { - APIurl string - UserID string - Password string - OTP string - ReqErr bool //if an error is expected as output + APIurl string + UserID string + Password string + OTP string + HttpHeaders string + ReqErr bool //if an error is expected as output } func (test *LoginTest) Login(t *testing.T) { - _, err := cli.Client(test.APIurl, test.UserID, test.Password, test.OTP) + _, err := cli.Client(test.APIurl, test.UserID, test.Password, test.OTP, test.HttpHeaders) if test.ReqErr { require.Error(t, err) } else { From 5c4c6abd12a96b20f61719583599b6af01cbda37 Mon Sep 17 00:00:00 2001 From: Benedikt Bertsch Date: Mon, 3 Oct 2022 23:13:40 +0200 Subject: [PATCH 2/8] fix: headers are now case sensitive --- proxmox/util.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxmox/util.go b/proxmox/util.go index 7adb812e..8b801d42 100644 --- a/proxmox/util.go +++ b/proxmox/util.go @@ -197,7 +197,7 @@ func createHeaderList(header_string string, sess *Session) (*Session, error) { return nil, err } for i := 0; i < len(header_string_split); i += 2 { - sess.Headers.Add(header_string_split[i], header_string_split[i+1]) + sess.Headers[header_string_split[i]] = []string{header_string_split[i+1]} } return sess, nil } From 261c2315aa426ab95b44b3c7e42bb99f5d778abe Mon Sep 17 00:00:00 2001 From: forthal Date: Tue, 4 Oct 2022 02:09:40 +0200 Subject: [PATCH 3/8] fix: utilizing custom http headers --- cli/cobra.go | 8 ++++---- go.sum | 8 -------- proxmox/client.go | 2 +- proxmox/session.go | 12 ++++++------ 4 files changed, 11 insertions(+), 19 deletions(-) diff --git a/cli/cobra.go b/cli/cobra.go index 231de734..64c09091 100644 --- a/cli/cobra.go +++ b/cli/cobra.go @@ -38,7 +38,7 @@ func NewClient() (c *proxmox.Client) { return } -func Client(apiUlr, userID, password, otp string, http_headers string) (c *proxmox.Client, err error) { +func Client(apiUrl, userID, password, otp string, http_headers string) (c *proxmox.Client, err error) { insecure, _ := RootCmd.Flags().GetBool("insecure") timeout, _ := RootCmd.Flags().GetInt("timeout") proxyUrl, _ := RootCmd.Flags().GetString("proxyurl") @@ -47,8 +47,8 @@ func Client(apiUlr, userID, password, otp string, http_headers string) (c *proxm if !insecure { tlsconf = nil } - if apiUlr == "" { - apiUlr = os.Getenv("PM_API_URL") + if apiUrl == "" { + apiUrl = os.Getenv("PM_API_URL") } if userID == "" { userID = os.Getenv("PM_USER") @@ -62,7 +62,7 @@ func Client(apiUlr, userID, password, otp string, http_headers string) (c *proxm if http_headers == "" { otp = os.Getenv("HTTP_HEADERS") } - c, err = proxmox.NewClient(apiUlr, nil, http_headers, tlsconf, proxyUrl, timeout) + c, err = proxmox.NewClient(apiUrl, nil, http_headers, tlsconf, proxyUrl, timeout) LogFatalError(err) if userRequiresAPIToken(userID) { c.SetAPIToken(userID, password) diff --git a/go.sum b/go.sum index 095ce04a..00ef3216 100644 --- a/go.sum +++ b/go.sum @@ -1,33 +1,25 @@ -github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q= -github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/proxmox/client.go b/proxmox/client.go index b89c17fd..51bd69c8 100644 --- a/proxmox/client.go +++ b/proxmox/client.go @@ -1325,7 +1325,7 @@ func (c *Client) Upload(node string, storage string, contentType string, filenam } url := fmt.Sprintf("%s/nodes/%s/storage/%s/upload", c.session.ApiUrl, node, storage) - req, err := c.session.NewRequest(http.MethodPost, url, nil, body) + req, err := c.session.NewRequest(http.MethodPost, url, &c.session.Headers, body) if err != nil { return err } diff --git a/proxmox/session.go b/proxmox/session.go index 75dac18d..70fe5a30 100644 --- a/proxmox/session.go +++ b/proxmox/session.go @@ -167,7 +167,7 @@ func (s *Session) Login(username string, password string, otp string) (err error reqbody := ParamsToBody(reqUser) olddebug := *Debug *Debug = false // don't share passwords in debug log - resp, err := s.Post("/access/ticket", nil, nil, &reqbody) + resp, err := s.Post("/access/ticket", nil, &s.Headers, &reqbody) *Debug = olddebug if err != nil { return err @@ -202,18 +202,18 @@ func (s *Session) NewRequest(method, url string, headers *http.Header, body io.R req.Header = *headers } if s.AuthToken != "" { - req.Header.Add("Authorization", "PVEAPIToken="+s.AuthToken) + req.Header["Authorization"] = []string{"PVEAPIToken=" + s.AuthToken} } else if s.AuthTicket != "" { - req.Header.Add("Cookie", "PVEAuthCookie="+s.AuthTicket) - req.Header.Add("CSRFPreventionToken", s.CsrfToken) + req.Header["Authorization"] = []string{"PVEAuthCookie=" + s.AuthTicket} + req.Header["CSRFPreventionToken"] = []string{s.CsrfToken} } return } func (s *Session) Do(req *http.Request) (*http.Response, error) { // Add session headers - for k := range s.Headers { - req.Header.Set(k, s.Headers.Get(k)) + for k, v := range s.Headers { + req.Header[k] = v } if *Debug { From 769f958b0fea7664e65d1052ac8f664e293ab956 Mon Sep 17 00:00:00 2001 From: Benedikt Bertsch Date: Tue, 4 Oct 2022 20:37:12 +0200 Subject: [PATCH 4/8] fix: wrong assignment for http_headers --- cli/cobra.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/cobra.go b/cli/cobra.go index 64c09091..e1bb44a0 100644 --- a/cli/cobra.go +++ b/cli/cobra.go @@ -60,7 +60,7 @@ func Client(apiUrl, userID, password, otp string, http_headers string) (c *proxm otp = os.Getenv("PM_OTP") } if http_headers == "" { - otp = os.Getenv("HTTP_HEADERS") + http_headers = os.Getenv("HTTP_HEADERS") } c, err = proxmox.NewClient(apiUrl, nil, http_headers, tlsconf, proxyUrl, timeout) LogFatalError(err) From 8a1ad40482b9c0d3e7c542d44e69c84dc1d148df Mon Sep 17 00:00:00 2001 From: Benedikt Bertsch Date: Wed, 5 Oct 2022 16:39:48 +0200 Subject: [PATCH 5/8] change HTTP_HEADERS to PM_HTTP_HEADERS --- cli/cobra.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/cobra.go b/cli/cobra.go index e1bb44a0..9dd2ac55 100644 --- a/cli/cobra.go +++ b/cli/cobra.go @@ -60,7 +60,7 @@ func Client(apiUrl, userID, password, otp string, http_headers string) (c *proxm otp = os.Getenv("PM_OTP") } if http_headers == "" { - http_headers = os.Getenv("HTTP_HEADERS") + http_headers = os.Getenv("PM_HTTP_HEADERS") } c, err = proxmox.NewClient(apiUrl, nil, http_headers, tlsconf, proxyUrl, timeout) LogFatalError(err) From b092b70ff476f414c9b26c5e42ac0af31180c56b Mon Sep 17 00:00:00 2001 From: Benedikt Bertsch Date: Wed, 5 Oct 2022 16:40:48 +0200 Subject: [PATCH 6/8] correct new env in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b1b3cabf..ce8042fb 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ export PM_API_URL="https://xxxx.com:8006/api2/json" export PM_USER=user@pam export PM_PASS=password export PM_OTP=otpcode (only if required) -export HTTP_HEADERS=Key,Value,Key1,Value1 (only if required) +export PM_HTTP_HEADERS=Key,Value,Key1,Value1 (only if required) ./proxmox-api-go installQemu proxmox-node-name < qemu1.json From 5be5017c59d0a48815e57ef1d12d5b56ab43d2e7 Mon Sep 17 00:00:00 2001 From: Benedikt Bertsch Date: Wed, 5 Oct 2022 16:46:44 +0200 Subject: [PATCH 7/8] changed HTTP_HEADERS env to PM_HTTP_HEADERS --- main.go | 2 +- proxmox/client_test.go | 2 +- proxmox/util.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/main.go b/main.go index 527bd44f..c8b16a84 100644 --- a/main.go +++ b/main.go @@ -36,7 +36,7 @@ func main() { if !*insecure { tlsconf = nil } - c, err := proxmox.NewClient(os.Getenv("PM_API_URL"), nil, os.Getenv("HTTP_HEADERS"), tlsconf, *proxyURL, *taskTimeout) + c, err := proxmox.NewClient(os.Getenv("PM_API_URL"), nil, os.Getenv("PM_HTTP_HEADERS"), tlsconf, *proxyURL, *taskTimeout) failError(err) if userRequiresAPIToken(os.Getenv("PM_USER")) { c.SetAPIToken(os.Getenv("PM_USER"), os.Getenv("PM_PASS")) diff --git a/proxmox/client_test.go b/proxmox/client_test.go index 08a8b539..19d6fc34 100644 --- a/proxmox/client_test.go +++ b/proxmox/client_test.go @@ -9,7 +9,7 @@ import ( ) func TestClient_Login(t *testing.T) { - client, err := NewClient(os.Getenv("PM_API_URL"), nil, os.Getenv("HTTP_HEADERS"), &tls.Config{InsecureSkipVerify: true}, "", 300) + client, err := NewClient(os.Getenv("PM_API_URL"), nil, os.Getenv("PM_HTTP_HEADERS"), &tls.Config{InsecureSkipVerify: true}, "", 300) assert.Nil(t, err) err = client.Login(os.Getenv("PM_USER"), os.Getenv("PM_PASS"), "") diff --git a/proxmox/util.go b/proxmox/util.go index 8b801d42..53fd13f6 100644 --- a/proxmox/util.go +++ b/proxmox/util.go @@ -192,7 +192,7 @@ func failError(err error) { // Create list of http.Header out of string, separator is "," func createHeaderList(header_string string, sess *Session) (*Session, error) { header_string_split := strings.Split(header_string, ",") - err := ValidateArrayEven(header_string_split, "Header key(s) and value(s) not even. Check your HTTP_HEADERS.") + err := ValidateArrayEven(header_string_split, "Header key(s) and value(s) not even. Check your PM_HTTP_HEADERS env.") if err != nil { return nil, err } From d5f3fea8c0445f54bc8b670b91284a09addebeb4 Mon Sep 17 00:00:00 2001 From: Benedikt Bertsch Date: Wed, 5 Oct 2022 18:12:48 +0200 Subject: [PATCH 8/8] fix: no header set skip setting headers --- proxmox/util.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/proxmox/util.go b/proxmox/util.go index 53fd13f6..806f3a73 100644 --- a/proxmox/util.go +++ b/proxmox/util.go @@ -191,6 +191,9 @@ func failError(err error) { // Create list of http.Header out of string, separator is "," func createHeaderList(header_string string, sess *Session) (*Session, error) { + if header_string == "" { + return sess, nil + } header_string_split := strings.Split(header_string, ",") err := ValidateArrayEven(header_string_split, "Header key(s) and value(s) not even. Check your PM_HTTP_HEADERS env.") if err != nil {