2021-07-29 01:41:26 +00:00
|
|
|
package client
|
|
|
|
|
|
|
|
import (
|
2022-03-10 01:53:51 +00:00
|
|
|
"context"
|
2021-07-29 01:41:26 +00:00
|
|
|
"crypto/tls"
|
2022-03-10 01:53:51 +00:00
|
|
|
"errors"
|
2021-07-29 01:41:26 +00:00
|
|
|
"net/http"
|
|
|
|
"time"
|
2022-03-10 01:53:51 +00:00
|
|
|
|
|
|
|
"golang.org/x/oauth2"
|
|
|
|
"golang.org/x/oauth2/clientcredentials"
|
2021-07-29 01:41:26 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
defaultHTTPTimeout = 10 * time.Second
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
// DefaultConfig is the default client configuration
|
|
|
|
defaultConfig = Config{
|
|
|
|
Insecure: false,
|
|
|
|
IgnoreRedirect: false,
|
|
|
|
Timeout: defaultHTTPTimeout,
|
|
|
|
}
|
2022-03-10 01:53:51 +00:00
|
|
|
|
|
|
|
ErrInvalidClientOAuth2Config = errors.New(
|
|
|
|
"invalid OAuth2 configuration, all fields are required",
|
|
|
|
)
|
2021-07-29 01:41:26 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// GetDefaultConfig returns a copy of the default configuration
|
|
|
|
func GetDefaultConfig() *Config {
|
|
|
|
cfg := defaultConfig
|
|
|
|
return &cfg
|
|
|
|
}
|
|
|
|
|
|
|
|
// Config is the configuration for clients
|
|
|
|
type Config struct {
|
|
|
|
// Insecure determines whether to skip verifying the server's certificate chain and host name
|
|
|
|
Insecure bool `yaml:"insecure"`
|
|
|
|
|
|
|
|
// IgnoreRedirect determines whether to ignore redirects (true) or follow them (false, default)
|
|
|
|
IgnoreRedirect bool `yaml:"ignore-redirect"`
|
|
|
|
|
|
|
|
// Timeout for the client
|
|
|
|
Timeout time.Duration `yaml:"timeout"`
|
|
|
|
|
2022-03-10 01:53:51 +00:00
|
|
|
// OAuth2 configuration for the client
|
|
|
|
OAuth2Config *OAuth2Config `yaml:"oauth2,omitempty"`
|
|
|
|
|
2021-07-29 01:41:26 +00:00
|
|
|
httpClient *http.Client
|
|
|
|
}
|
|
|
|
|
2022-03-10 01:53:51 +00:00
|
|
|
// OAuth2Config is the configuration for the OAuth2 client credentials flow
|
|
|
|
type OAuth2Config struct {
|
|
|
|
TokenURL string `yaml:"token-url"` // e.g. https://dev-12345678.okta.com/token
|
|
|
|
ClientID string `yaml:"client-id"`
|
|
|
|
ClientSecret string `yaml:"client-secret"`
|
|
|
|
Scopes []string `yaml:"scopes"` // e.g. ["openid"]
|
|
|
|
}
|
|
|
|
|
2021-07-29 01:41:26 +00:00
|
|
|
// ValidateAndSetDefaults validates the client configuration and sets the default values if necessary
|
2022-03-10 01:53:51 +00:00
|
|
|
func (c *Config) ValidateAndSetDefaults() error {
|
2021-07-29 01:41:26 +00:00
|
|
|
if c.Timeout < time.Millisecond {
|
|
|
|
c.Timeout = 10 * time.Second
|
|
|
|
}
|
2022-03-10 01:53:51 +00:00
|
|
|
if c.HasOAuth2Config() && !c.OAuth2Config.isValid() {
|
|
|
|
return ErrInvalidClientOAuth2Config
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// HasOAuth2Config returns true if the client has OAuth2 configuration parameters
|
|
|
|
func (c *Config) HasOAuth2Config() bool {
|
|
|
|
return c.OAuth2Config != nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// isValid() returns true if the OAuth2 configuration is valid
|
|
|
|
func (c *OAuth2Config) isValid() bool {
|
|
|
|
return len(c.TokenURL) > 0 && len(c.ClientID) > 0 && len(c.ClientSecret) > 0 && len(c.Scopes) > 0
|
2021-07-29 01:41:26 +00:00
|
|
|
}
|
|
|
|
|
2021-10-23 20:47:12 +00:00
|
|
|
// GetHTTPClient return an HTTP client matching the Config's parameters.
|
2021-07-29 22:13:37 +00:00
|
|
|
func (c *Config) getHTTPClient() *http.Client {
|
2021-07-29 01:41:26 +00:00
|
|
|
if c.httpClient == nil {
|
|
|
|
c.httpClient = &http.Client{
|
|
|
|
Timeout: c.Timeout,
|
|
|
|
Transport: &http.Transport{
|
|
|
|
MaxIdleConns: 100,
|
|
|
|
MaxIdleConnsPerHost: 20,
|
|
|
|
Proxy: http.ProxyFromEnvironment,
|
|
|
|
TLSClientConfig: &tls.Config{
|
|
|
|
InsecureSkipVerify: c.Insecure,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
|
|
|
if c.IgnoreRedirect {
|
|
|
|
// Don't follow redirects
|
|
|
|
return http.ErrUseLastResponse
|
|
|
|
}
|
|
|
|
// Follow redirects
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
2022-03-10 01:53:51 +00:00
|
|
|
if c.HasOAuth2Config() {
|
|
|
|
c.httpClient = configureOAuth2(c.httpClient, *c.OAuth2Config)
|
|
|
|
}
|
2021-07-29 01:41:26 +00:00
|
|
|
}
|
|
|
|
return c.httpClient
|
|
|
|
}
|
2022-03-10 01:53:51 +00:00
|
|
|
|
|
|
|
// configureOAuth2 returns an HTTP client that will obtain and refresh tokens as necessary.
|
|
|
|
// The returned Client and its Transport should not be modified.
|
|
|
|
func configureOAuth2(httpClient *http.Client, c OAuth2Config) *http.Client {
|
|
|
|
oauth2cfg := clientcredentials.Config{
|
|
|
|
ClientID: c.ClientID,
|
|
|
|
ClientSecret: c.ClientSecret,
|
|
|
|
Scopes: c.Scopes,
|
|
|
|
TokenURL: c.TokenURL,
|
|
|
|
}
|
|
|
|
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, httpClient)
|
|
|
|
return oauth2cfg.Client(ctx)
|
|
|
|
}
|