mirror of
https://github.com/StackExchange/dnscontrol.git
synced 2024-11-10 09:12:47 +08:00
7fd6a74e0c
Co-authored-by: Josh Zhang <jzhang1@stackoverflow.com>
101 lines
2.7 KiB
Go
101 lines
2.7 KiB
Go
package cloudflare
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
|
|
"github.com/goccy/go-json"
|
|
|
|
"github.com/hashicorp/go-retryablehttp"
|
|
)
|
|
|
|
var (
|
|
ErrSTSFailure = errors.New("failed to fetch security token")
|
|
ErrSTSHTTPFailure = errors.New("failed making securtiy token issuer call")
|
|
ErrSTSHTTPResponseError = errors.New("security token request returned a failure")
|
|
ErrSTSMissingServiceSecret = errors.New("service secret missing but is required")
|
|
ErrSTSMissingServiceTag = errors.New("service tag missing but is required")
|
|
ErrSTSMissingIssuerHostname = errors.New("issuer hostname missing but is required")
|
|
ErrSTSMissingServicePath = errors.New("issuer path missing but is required")
|
|
)
|
|
|
|
// IssuerConfiguration allows the configuration of the issuance provider.
|
|
type IssuerConfiguration struct {
|
|
Hostname string
|
|
Path string
|
|
}
|
|
|
|
// SecurityTokenConfiguration holds the configuration for requesting a security
|
|
// token from the service.
|
|
type SecurityTokenConfiguration struct {
|
|
Issuer *IssuerConfiguration
|
|
ServiceTag string
|
|
Secret string
|
|
}
|
|
|
|
type securityToken struct {
|
|
Token string `json:"json_web_token"`
|
|
}
|
|
|
|
type securityTokenResponse struct {
|
|
Result securityToken `json:"result"`
|
|
Response
|
|
}
|
|
|
|
// fetchSTSCredentials provides a way to authenticate with the security token
|
|
// service and issue a usable token for the system.
|
|
func fetchSTSCredentials(stsConfig *SecurityTokenConfiguration) (string, error) {
|
|
if stsConfig.Secret == "" {
|
|
return "", ErrSTSMissingServiceSecret
|
|
}
|
|
|
|
if stsConfig.ServiceTag == "" {
|
|
return "", ErrSTSMissingServiceTag
|
|
}
|
|
|
|
if stsConfig.Issuer.Hostname == "" {
|
|
return "", ErrSTSMissingIssuerHostname
|
|
}
|
|
|
|
if stsConfig.Issuer.Path == "" {
|
|
return "", ErrSTSMissingServicePath
|
|
}
|
|
|
|
retryableClient := retryablehttp.NewClient()
|
|
retryableClient.RetryMax = 3
|
|
stsClient := retryableClient.StandardClient()
|
|
|
|
uri := fmt.Sprintf("https://%s%s", stsConfig.Issuer.Hostname, stsConfig.Issuer.Path)
|
|
req, err := http.NewRequest(http.MethodGet, uri, nil)
|
|
if err != nil {
|
|
return "", fmt.Errorf("HTTP request creation failed: %w", err)
|
|
}
|
|
|
|
req.Header.Set("Authorization", "Bearer "+stsConfig.ServiceTag+stsConfig.Secret)
|
|
|
|
resp, err := stsClient.Do(req)
|
|
if err != nil {
|
|
return "", ErrSTSHTTPFailure
|
|
}
|
|
|
|
var respBody []byte
|
|
respBody, err = io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to read response body: %w", err)
|
|
}
|
|
resp.Body.Close()
|
|
|
|
var stsTokenResponse *securityTokenResponse
|
|
err = json.Unmarshal(respBody, &stsTokenResponse)
|
|
if err != nil {
|
|
return "", fmt.Errorf("%s: %w", errUnmarshalError, err)
|
|
}
|
|
|
|
if !stsTokenResponse.Success {
|
|
return "", ErrSTSHTTPResponseError
|
|
}
|
|
|
|
return stsTokenResponse.Result.Token, nil
|
|
}
|