From 9e107cb4875096f1b780555329b007a4d374ccbb Mon Sep 17 00:00:00 2001 From: Steven Polley Date: Thu, 26 Jul 2018 21:30:23 -0600 Subject: [PATCH] add user impersonation via username and password. --- 3.0/connectwise/connectwise.go | 67 ++++++++++++++++++++++++++++++++-- 3.0/connectwise/requests.go | 8 +++- 2 files changed, 71 insertions(+), 4 deletions(-) diff --git a/3.0/connectwise/connectwise.go b/3.0/connectwise/connectwise.go index 42e52b6..6d39ca5 100644 --- a/3.0/connectwise/connectwise.go +++ b/3.0/connectwise/connectwise.go @@ -3,12 +3,19 @@ package connectwise import ( "encoding/base64" "fmt" + "io/ioutil" + "net/http" + "net/url" + "strings" ) //Site is a stuct containing the URL of the site and the API authorization token in the format that CW expects it. type Site struct { - Site string - Auth string + Site string + AuthAPIKey string //Preferable authentication method + AuthUsername string // User for user impersonation + AuthMemberHash string //Used for user impersonation + CompanyName string //Used for user impersonation } //Count is a struct used for unmarshalling JSON data when using the Count endpoints in Connectwise (eg: counting number of companies) @@ -23,7 +30,61 @@ func NewSite(site string, publicKey string, privateKey string, company string) * authString = base64.StdEncoding.EncodeToString([]byte(authString)) authString = fmt.Sprintf("Basic %s", authString) - cwSite := Site{Site: site, Auth: authString} + cwSite := Site{Site: site, AuthAPIKey: authString} return &cwSite } + +//NewSiteUserImpersonation is similar to NewSite but is used for user impersonation and instead of an API key takes the username and password +func NewSiteUserImpersonation(site string, username string, password string, company string) (*Site, error) { + + //Need to post formdata to https://ndconnect.nextdigital.ca/v4_6_release/login/login.aspx + //username + //password + //companyname + + //Will then receive a hash + + authBaseURL := strings.TrimSuffix(site, "/apis/3.0") + authURL, err := url.Parse(authBaseURL) + if err != nil { + return nil, fmt.Errorf("could not build url %s: %s", authBaseURL, err) + } + authURL.Path += "/login/login.aspx" + + client := &http.Client{} + + authReq, err := http.NewRequest("POST", authURL.String(), nil) + if err != nil { + return nil, fmt.Errorf("could not construct http request: %s", err) + } + + form := url.Values{} + form.Add("username", username) + form.Add("password", password) + form.Add("companyname", company) + authReq.PostForm = form + + authReq.Header.Set("Content-Type", "application/x-www-form-urlencoded") + resp, err := client.Do(authReq) + if err != nil { + return nil, fmt.Errorf("could not perform http authentication request: %s", err) + } + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("cw api returned unexpected http status %d - %s", resp.StatusCode, resp.Status) + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("could not read response body of request") + } + + if string(body) == "" { + return nil, fmt.Errorf("could not authenticate with connectwise as %s: authentication request sent to connectwise, but response body was empty", username) + } + + cwSite := Site{Site: site, AuthUsername: username, AuthMemberHash: string(body), CompanyName: company} + + return &cwSite, nil +} diff --git a/3.0/connectwise/requests.go b/3.0/connectwise/requests.go index 2e81815..6cd535d 100644 --- a/3.0/connectwise/requests.go +++ b/3.0/connectwise/requests.go @@ -77,7 +77,13 @@ func (req *Request) Do() error { if err != nil { return fmt.Errorf("could not construct http request: %s", err) } - httpreq.Header.Set("Authorization", req.CW.Auth) + if req.CW.AuthAPIKey != "" { + httpreq.Header.Set("Authorization", req.CW.AuthAPIKey) + } else { //User impersonation + httpreq.Header.Set("Cookie", fmt.Sprintf("companyName=%s", req.CW.CompanyName)) + httpreq.Header.Set("Cookie", fmt.Sprintf("memberHash=%s", req.CW.AuthMemberHash)) + httpreq.Header.Set("Cookie", fmt.Sprintf("MemberID=%s", req.CW.AuthUsername)) + } httpreq.Header.Set("Content-Type", "application/json") resp, err := client.Do(httpreq) if err != nil {