go-cw/3.0/connectwise/connectwise.go

90 lines
3.2 KiB
Go

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
AuthAPIKey string //Preferable authentication method
CompanyName string //Used for user impersonation, but collected for API key as well so it can be accessed publicly later on if required
AuthUsername string // User for user impersonation
AuthMemberHash string //Used for user impersonation
ClientID string // Required as of 2019.02 https://developer.connectwise.com/ClientID
}
//Count is a struct used for unmarshalling JSON data when using the Count endpoints in Connectwise (eg: counting number of companies)
type Count struct {
Count int `json:"count"`
}
//NewSite returns a pointer to a ConnectwiseSite struct with the site and auth string available for use in API requests
func NewSite(site, publicKey, privateKey, company, clientID string) *Site {
//The auth string must be formatted in this way when used in requests to the API
authString := fmt.Sprintf("%s+%s:%s", company, publicKey, privateKey)
authString = base64.StdEncoding.EncodeToString([]byte(authString))
authString = fmt.Sprintf("Basic %s", authString)
cwSite := Site{Site: site, AuthAPIKey: authString, CompanyName: company, ClientID: clientID}
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, username, password, company, clientID string) (*Site, error) {
//We must retrieve a user hash which is good for 4 hours
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"
form := url.Values{}
form.Add("username", username)
form.Add("password", password)
form.Add("companyname", company)
client := &http.Client{}
authReq, err := http.NewRequest("POST", authURL.String(), strings.NewReader(form.Encode()))
if err != nil {
return nil, fmt.Errorf("could not construct http request: %s", err)
}
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)
}
if string(body) == "CompanyFAIL" {
return nil, fmt.Errorf("could not authenticate with connectwise as %s: connectwise response body is %s", username, string(body))
}
cwSite := Site{Site: site, AuthUsername: username, AuthMemberHash: string(body), CompanyName: company, ClientID: clientID}
return &cwSite, nil
}