feat: Fully featured Go client for the secrets service.
This commit is contained in:
1 parent
7b715da87f
commit
39ea9d708a
2 files changed
+187
No files matched your search
@@ -0,0 +1,149 @@
|
|||||||
|
package secretsclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Client interface {
|
||||||
|
GetString(key string) (string, error)
|
||||||
|
PutString(key string, val string) error
|
||||||
|
|
||||||
|
GetStringArr(key string) ([]string, error)
|
||||||
|
PutStringArr(key string, val []string) error
|
||||||
|
|
||||||
|
GetStringMap(key string) (map[string]string, error)
|
||||||
|
PutStringMap(key string, val map[string]string) error
|
||||||
|
|
||||||
|
Delete(key string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(base, ca, cert, certKey string) (Client, error) {
|
||||||
|
h, err := newMTLSClient(ca, cert, certKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &client{
|
||||||
|
base: base,
|
||||||
|
h: h,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type client struct {
|
||||||
|
base string
|
||||||
|
h *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) GetString(key string) (string, error) {
|
||||||
|
res, err := c.get(key)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(res), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) PutString(key, val string) error {
|
||||||
|
return c.put(key, []byte(val))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) GetStringArr(key string) ([]string, error) {
|
||||||
|
arrBytes, err := c.get(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var res []string
|
||||||
|
err = json.Unmarshal(arrBytes, &res)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) PutStringArr(key string, val []string) error {
|
||||||
|
arrBytes, err := json.Marshal(val)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return c.put(key, arrBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) GetStringMap(key string) (map[string]string, error) {
|
||||||
|
mapBytes, err := c.get(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var res map[string]string
|
||||||
|
err = json.Unmarshal(mapBytes, &res)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) PutStringMap(key string, val map[string]string) error {
|
||||||
|
mapBytes, err := json.Marshal(val)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return c.put(key, mapBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) get(key string) ([]byte, error) {
|
||||||
|
res, err := c.h.Get(c.urlFromKey(key))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if res.StatusCode != http.StatusOK {
|
||||||
|
return nil, errors.New("request did not succeed")
|
||||||
|
}
|
||||||
|
val, err := io.ReadAll(res.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
|
||||||
|
return val, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) put(key string, val []byte) error {
|
||||||
|
body := bytes.NewBuffer(val)
|
||||||
|
req, err := http.NewRequest("PUT", c.urlFromKey(key), body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
res, err := c.h.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if res.StatusCode != http.StatusOK {
|
||||||
|
return errors.New("request did not succeed")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) Delete(key string) error {
|
||||||
|
req, err := http.NewRequest("DELETE", c.urlFromKey(key), nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := c.h.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if res.StatusCode != http.StatusOK {
|
||||||
|
return errors.New("request did not succeed")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) urlFromKey(key string) string {
|
||||||
|
b64Key := base64.RawURLEncoding.EncodeToString([]byte(key))
|
||||||
|
return c.base + "?key=" + b64Key
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package secretsclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newMTLSClient(ca, cert, key string) (*http.Client, error) {
|
||||||
|
caPEMBytes, err := os.ReadFile(ca)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
caPool := x509.NewCertPool()
|
||||||
|
if !caPool.AppendCertsFromPEM(caPEMBytes) {
|
||||||
|
return nil, errors.New("could create a ca pool from file")
|
||||||
|
}
|
||||||
|
|
||||||
|
cliCert, err := tls.LoadX509KeyPair(cert, key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not load client cert/key: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &http.Client{
|
||||||
|
// TODO: Tune other params.
|
||||||
|
Timeout: 10 * time.Second,
|
||||||
|
Transport: &http.Transport{
|
||||||
|
TLSClientConfig: &tls.Config{
|
||||||
|
RootCAs: caPool,
|
||||||
|
Certificates: []tls.Certificate{cliCert},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
Reference in new issue
Block a user