goldwarden-vaultwarden-bitw.../cli/agent/bitwarden/http.go

123 lines
3.0 KiB
Go

package bitwarden
import (
"bytes"
"context"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strings"
"time"
)
var httpClient = &http.Client{
Timeout: 20 * time.Second,
}
type errStatusCode struct {
code int
body []byte
}
func (e *errStatusCode) Error() string {
return fmt.Sprintf("%s: %s", http.StatusText(e.code), e.body)
}
type AuthToken struct{}
func authenticatedHTTPPost(ctx context.Context, urlstr string, recv, send interface{}) error {
var r io.Reader
contentType := "application/json"
authEmail := ""
if values, ok := send.(url.Values); ok {
r = strings.NewReader(values.Encode())
contentType = "application/x-www-form-urlencoded"
if email := values.Get("username"); email != "" && values.Get("scope") != "" {
authEmail = email
}
} else {
buf := new(bytes.Buffer)
if err := json.NewEncoder(buf).Encode(send); err != nil {
return err
}
r = buf
}
req, err := http.NewRequest("POST", urlstr, r)
if err != nil {
return err
}
req.Header.Set("Content-Type", contentType)
if authEmail != "" {
req.Header.Set("Auth-Email", base64.URLEncoding.EncodeToString([]byte(authEmail)))
}
return makeAuthenticatedHTTPRequest(ctx, req, recv)
}
func authenticatedHTTPGet(ctx context.Context, urlstr string, recv interface{}) error {
req, err := http.NewRequest("GET", urlstr, nil)
if err != nil {
return err
}
return makeAuthenticatedHTTPRequest(ctx, req, recv)
}
func authenticatedHTTPDelete(ctx context.Context, urlstr string, recv interface{}) error {
req, err := http.NewRequest("DELETE", urlstr, nil)
if err != nil {
return err
}
return makeAuthenticatedHTTPRequest(ctx, req, recv)
}
func authenticatedHTTPPut(ctx context.Context, urlstr string, recv, send interface{}) error {
var r io.Reader
contentType := "application/json"
if values, ok := send.(url.Values); ok {
r = strings.NewReader(values.Encode())
contentType = "application/x-www-form-urlencoded"
} else {
buf := new(bytes.Buffer)
if err := json.NewEncoder(buf).Encode(send); err != nil {
return err
}
r = buf
}
req, err := http.NewRequest("PUT", urlstr, r)
if err != nil {
return err
}
req.Header.Set("Content-Type", contentType)
return makeAuthenticatedHTTPRequest(ctx, req, recv)
}
func makeAuthenticatedHTTPRequest(ctx context.Context, req *http.Request, recv interface{}) error {
if token, ok := ctx.Value(AuthToken{}).(string); ok {
req.Header.Set("Authorization", "Bearer "+token)
}
req.Header.Set("device-type", deviceType())
req.Header.Set("User-Agent", "Goldwarden (github.com/quexten/goldwarden)")
req.Header.Set("Bitwarden-Client-Name", "goldwarden")
req.Header.Set("Bitwarden-Client-Version", "0.0.0")
res, err := httpClient.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
body, err := io.ReadAll(res.Body)
if err != nil {
return err
}
if res.StatusCode != 200 {
return &errStatusCode{res.StatusCode, body}
}
if err := json.Unmarshal(body, recv); err != nil {
fmt.Println(string(body))
return err
}
return nil
}