openvpn-mgt/vendor/github.com/mattevans/pwned-passwords/hibp.go

116 lines
2.5 KiB
Go

package hibp
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strings"
)
const (
packageVersion = "0.0.1"
backendURL = "https://api.pwnedpasswords.com"
userAgent = "pwned-passwords-golang/" + packageVersion
)
// Client holds a connection to the HIBP API.
type Client struct {
client *http.Client
AppID string
UserAgent string
BackendURL *url.URL
// Services used for communicating with the API.
Pwned *PwnedService
Cache *CacheService
}
type service struct {
client *Client
}
// NewClient creates a new Client with the appropriate connection details and
// services used for communicating with the API.
func NewClient() *Client {
// Init new http.Client.
httpClient := http.DefaultClient
// Parse BE URL.
baseURL, _ := url.Parse(backendURL)
c := &Client{
client: httpClient,
BackendURL: baseURL,
UserAgent: userAgent,
}
c.Pwned = &PwnedService{client: c}
c.Cache = &CacheService{client: c}
return c
}
// NewRequest creates an API request. A relative URL can be provided in urlPath,
// which will be resolved to the BackendURL of the Client.
func (c *Client) NewRequest(method, urlPath string, body interface{}) (*http.Request, error) {
// Parse our URL.
rel, err := url.Parse(urlPath)
if err != nil {
return nil, err
}
// Resolve to absolute URI.
u := c.BackendURL.ResolveReference(rel)
buf := new(bytes.Buffer)
if body != nil {
err = json.NewEncoder(buf).Encode(body)
if err != nil {
return nil, err
}
}
// Create the request.
req, err := http.NewRequest(method, u.String(), buf)
if err != nil {
return nil, err
}
// Add our packages UA.
req.Header.Add("User-Agent", c.UserAgent)
return req, nil
}
// Do sends an API request and returns the API response.
func (c *Client) Do(req *http.Request) ([]string, error) {
resp, err := c.client.Do(req)
if err != nil {
return nil, err
}
defer func() {
if rerr := resp.Body.Close(); err == nil {
err = rerr
}
}()
// Error if anything else but 200.
// The API should always return a 200 (unless something is wrong) as per
// https://haveibeenpwned.com/API/v2#SearchingPwnedPasswordsByRange
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("Unexpected API response status: %v", resp.StatusCode)
}
// Parse our resp.Body.
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
// Response is returned as new-line'd string, split and return.
return strings.Split(string(body), "\r\n"), err
}