diff --git a/README.md b/README.md index f1a98326..ce8042fb 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 PM_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..9dd2ac55 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(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) (c *proxmox.Client, err error) 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") @@ -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 == "" { + http_headers = os.Getenv("PM_HTTP_HEADERS") + } + 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/main.go b/main.go index f57bcd32..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, 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.go b/proxmox/client.go index 732ed27f..51bd69c8 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 @@ -1320,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/client_test.go b/proxmox/client_test.go index 3f275ed3..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, &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/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 { diff --git a/proxmox/util.go b/proxmox/util.go index eb2fe50f..806f3a73 100644 --- a/proxmox/util.go +++ b/proxmox/util.go @@ -188,3 +188,19 @@ 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) { + 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 { + return nil, err + } + for i := 0; i < len(header_string_split); i += 2 { + sess.Headers[header_string_split[i]] = []string{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 {