hydra-maester/hydra/client.go

210 lines
5.0 KiB
Go

// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package hydra
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"path"
hydrav1alpha1 "github.com/ory/hydra-maester/api/v1alpha1"
"github.com/ory/hydra-maester/helpers"
)
type Client interface {
GetOAuth2Client(id string) (*OAuth2ClientJSON, bool, error)
ListOAuth2Client() ([]*OAuth2ClientJSON, error)
PostOAuth2Client(o *OAuth2ClientJSON) (*OAuth2ClientJSON, error)
PutOAuth2Client(o *OAuth2ClientJSON) (*OAuth2ClientJSON, error)
DeleteOAuth2Client(id string) error
}
type InternalClient struct {
HydraURL url.URL
HTTPClient *http.Client
ForwardedProto string
}
// New returns a new hydra InternalClient instance.
func New(spec hydrav1alpha1.OAuth2ClientSpec, tlsTrustStore string, insecureSkipVerify bool) (Client, error) {
address := fmt.Sprintf("%s:%d", spec.HydraAdmin.URL, spec.HydraAdmin.Port)
u, err := url.Parse(address)
if err != nil {
return nil, err
}
c, err := helpers.CreateHttpClient(insecureSkipVerify, tlsTrustStore)
if err != nil {
return nil, err
}
client := &InternalClient{
HydraURL: *u.ResolveReference(&url.URL{Path: spec.HydraAdmin.Endpoint}),
HTTPClient: c,
}
if spec.HydraAdmin.ForwardedProto != "" && spec.HydraAdmin.ForwardedProto != "off" {
client.ForwardedProto = spec.HydraAdmin.ForwardedProto
}
return client, nil
}
func (c *InternalClient) GetOAuth2Client(id string) (*OAuth2ClientJSON, bool, error) {
var jsonClient *OAuth2ClientJSON
req, err := c.newRequest(http.MethodGet, id, nil)
if err != nil {
return nil, false, err
}
resp, err := c.do(req, &jsonClient)
if err != nil {
return nil, false, err
}
switch resp.StatusCode {
case http.StatusOK:
return jsonClient, true, nil
case http.StatusNotFound, http.StatusUnauthorized:
return nil, false, nil
default:
return nil, false, fmt.Errorf("%s %s http request returned unexpected status code %s", req.Method, req.URL.String(), resp.Status)
}
}
func (c *InternalClient) ListOAuth2Client() ([]*OAuth2ClientJSON, error) {
var jsonClientList []*OAuth2ClientJSON
req, err := c.newRequest(http.MethodGet, "", nil)
if err != nil {
return nil, err
}
resp, err := c.do(req, &jsonClientList)
if err != nil {
return nil, err
}
switch resp.StatusCode {
case http.StatusOK:
return jsonClientList, nil
default:
return nil, fmt.Errorf("%s %s http request returned unexpected status code %s", req.Method, req.URL.String(), resp.Status)
}
}
func (c *InternalClient) PostOAuth2Client(o *OAuth2ClientJSON) (*OAuth2ClientJSON, error) {
var jsonClient *OAuth2ClientJSON
req, err := c.newRequest(http.MethodPost, "", o)
if err != nil {
return nil, err
}
resp, err := c.do(req, &jsonClient)
if err != nil {
return nil, err
}
switch resp.StatusCode {
case http.StatusCreated:
return jsonClient, nil
case http.StatusConflict:
return nil, fmt.Errorf("%s %s http request failed: requested ID already exists", req.Method, req.URL)
default:
return nil, fmt.Errorf("%s %s http request returned unexpected status code: %s", req.Method, req.URL, resp.Status)
}
}
func (c *InternalClient) PutOAuth2Client(o *OAuth2ClientJSON) (*OAuth2ClientJSON, error) {
var jsonClient *OAuth2ClientJSON
req, err := c.newRequest(http.MethodPut, *o.ClientID, o)
if err != nil {
return nil, err
}
resp, err := c.do(req, &jsonClient)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("%s %s http request returned unexpected status code: %s", req.Method, req.URL, resp.Status)
}
return jsonClient, nil
}
func (c *InternalClient) DeleteOAuth2Client(id string) error {
req, err := c.newRequest(http.MethodDelete, id, nil)
if err != nil {
return err
}
resp, err := c.do(req, nil)
if err != nil {
return err
}
switch resp.StatusCode {
case http.StatusNoContent:
return nil
case http.StatusNotFound:
fmt.Printf("InternalClient with id %s does not exist", id)
return nil
default:
return fmt.Errorf("%s %s http request returned unexpected status code %s", req.Method, req.URL.String(), resp.Status)
}
}
func (c *InternalClient) newRequest(method, relativePath string, body interface{}) (*http.Request, error) {
var buf io.ReadWriter
if body != nil {
buf = new(bytes.Buffer)
err := json.NewEncoder(buf).Encode(body)
if err != nil {
return nil, err
}
}
u := c.HydraURL
u.Path = path.Join(u.Path, relativePath)
req, err := http.NewRequest(method, u.String(), buf)
if err != nil {
return nil, err
}
if c.ForwardedProto != "" {
req.Header.Add("X-Forwarded-Proto", c.ForwardedProto)
}
if body != nil {
req.Header.Set("Content-Type", "application/json")
}
req.Header.Set("Accept", "application/json")
return req, nil
}
func (c *InternalClient) do(req *http.Request, v interface{}) (*http.Response, error) {
resp, err := c.HTTPClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if v != nil && resp.StatusCode < 300 {
err = json.NewDecoder(resp.Body).Decode(v)
}
return resp, err
}