mirror of
https://github.com/passbolt/go-passbolt.git
synced 2025-05-09 17:48:20 +00:00
159 lines
4 KiB
Go
159 lines
4 KiB
Go
package api
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"net/url"
|
|
|
|
"github.com/ProtonMail/gopenpgp/v2/crypto"
|
|
"github.com/google/go-querystring/query"
|
|
)
|
|
|
|
// Client is a Client struct for the Passbolt api
|
|
type Client struct {
|
|
baseURL *url.URL
|
|
userAgent string
|
|
httpClient *http.Client
|
|
|
|
sessionToken http.Cookie
|
|
csrfToken http.Cookie
|
|
|
|
// for some reason []byte is used for Passwords in gopenpgp instead of string like they do for keys...
|
|
userPassword []byte
|
|
userPrivateKey string
|
|
userPublicKey string
|
|
userID string
|
|
|
|
// Enable Debug Logging
|
|
Debug bool
|
|
}
|
|
|
|
// NewClient Returns a new Passbolt Client.
|
|
// if httpClient is nil http.DefaultClient will be used.
|
|
// if UserAgent is "" "goPassboltClient/1.0" will be used.
|
|
func NewClient(httpClient *http.Client, UserAgent, BaseURL, UserPrivateKey, UserPassword string) (*Client, error) {
|
|
if httpClient == nil {
|
|
httpClient = http.DefaultClient
|
|
}
|
|
if UserAgent == "" {
|
|
UserAgent = "goPassboltClient/1.0"
|
|
}
|
|
|
|
u, err := url.Parse(BaseURL)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Parsing Base URL: %w", err)
|
|
}
|
|
|
|
// Verify that the Given Privatekey and Password are valid and work Together
|
|
privateKeyObj, err := crypto.NewKeyFromArmored(UserPrivateKey)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Unable to Create Key From UserPrivateKey string: %w", err)
|
|
}
|
|
unlockedKeyObj, err := privateKeyObj.Unlock([]byte(UserPassword))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Unable to Unlock UserPrivateKey using UserPassword: %w", err)
|
|
}
|
|
privateKeyRing, err := crypto.NewKeyRing(unlockedKeyObj)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Unable to Create a new Key Ring using the unlocked UserPrivateKey: %w", err)
|
|
}
|
|
|
|
// Cleanup Secrets
|
|
privateKeyRing.ClearPrivateParams()
|
|
|
|
// Create Client Object
|
|
c := &Client{
|
|
httpClient: httpClient,
|
|
baseURL: u,
|
|
userAgent: UserAgent,
|
|
userPassword: []byte(UserPassword),
|
|
userPrivateKey: UserPrivateKey,
|
|
}
|
|
return c, err
|
|
}
|
|
|
|
func (c *Client) newRequest(method, path string, body interface{}) (*http.Request, error) {
|
|
rel, err := url.Parse(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Parsing URL: %w", err)
|
|
}
|
|
u := c.baseURL.ResolveReference(rel)
|
|
var buf io.ReadWriter
|
|
if body != nil {
|
|
buf = new(bytes.Buffer)
|
|
err := json.NewEncoder(buf).Encode(body)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("JSON Encoding Request: %w", err)
|
|
}
|
|
}
|
|
req, err := http.NewRequest(method, u.String(), buf)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Creating HTTP Request: %w", err)
|
|
}
|
|
if body != nil {
|
|
req.Header.Set("Content-Type", "application/json")
|
|
}
|
|
req.Header.Set("Accept", "application/json")
|
|
req.Header.Set("User-Agent", c.userAgent)
|
|
req.Header.Set("X-CSRF-Token", c.csrfToken.Value)
|
|
req.AddCookie(&c.sessionToken)
|
|
req.AddCookie(&c.csrfToken)
|
|
|
|
return req, nil
|
|
}
|
|
|
|
func (c *Client) do(ctx context.Context, req *http.Request, v *APIResponse) (*http.Response, error) {
|
|
req = req.WithContext(ctx)
|
|
resp, err := c.httpClient.Do(req)
|
|
if err != nil {
|
|
select {
|
|
case <-ctx.Done():
|
|
return nil, fmt.Errorf("Request Context: %w", ctx.Err())
|
|
default:
|
|
return nil, fmt.Errorf("Request: %w", err)
|
|
}
|
|
}
|
|
defer func() {
|
|
resp.Body.Close()
|
|
}()
|
|
|
|
bodyBytes, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return resp, fmt.Errorf("Error Reading Resopnse Body: %w", err)
|
|
}
|
|
|
|
err = json.Unmarshal(bodyBytes, v)
|
|
if err != nil {
|
|
return resp, fmt.Errorf("Unable to Parse JSON API Response with HTTP Status Code %v: %w", resp.StatusCode, err)
|
|
}
|
|
return resp, nil
|
|
}
|
|
|
|
func (c *Client) log(msg string, args ...interface{}) {
|
|
if !c.Debug {
|
|
return
|
|
}
|
|
fmt.Printf("[go-passbolt] "+msg+"\n", args...)
|
|
}
|
|
|
|
func addOptions(s, version string, opt interface{}) (string, error) {
|
|
u, err := url.Parse(s)
|
|
if err != nil {
|
|
return s, fmt.Errorf("Parsing URL: %w", err)
|
|
}
|
|
|
|
vs, err := query.Values(opt)
|
|
if err != nil {
|
|
return s, fmt.Errorf("Getting URL Query Values: %w", err)
|
|
}
|
|
if version != "" {
|
|
vs.Add("api-version", version)
|
|
}
|
|
u.RawQuery = vs.Encode()
|
|
return u.String(), nil
|
|
}
|