Support talking to multiple ORY Hydra deployments (#35)
This commit is contained in:
@ -25,12 +25,46 @@ import (
|
||||
type StatusCode string
|
||||
|
||||
const (
|
||||
StatusRegistrationFailed StatusCode = "CLIENT_REGISTRATION_FAILED"
|
||||
StatusCreateSecretFailed StatusCode = "SECRET_CREATION_FAILED"
|
||||
StatusUpdateFailed StatusCode = "CLIENT_UPDATE_FAILED"
|
||||
StatusInvalidSecret StatusCode = "INVALID_SECRET"
|
||||
StatusRegistrationFailed StatusCode = "CLIENT_REGISTRATION_FAILED"
|
||||
StatusCreateSecretFailed StatusCode = "SECRET_CREATION_FAILED"
|
||||
StatusUpdateFailed StatusCode = "CLIENT_UPDATE_FAILED"
|
||||
StatusInvalidSecret StatusCode = "INVALID_SECRET"
|
||||
StatusInvalidHydraAddress StatusCode = "INVALID_HYDRA_ADDRESS"
|
||||
)
|
||||
|
||||
// HydraAdmin defines the desired hydra admin instance to use for OAuth2Client
|
||||
type HydraAdmin struct {
|
||||
// +kubebuilder:validation:MaxLength=64
|
||||
// +kubebuilder:validation:Pattern=(^$|^https?://.*)
|
||||
//
|
||||
// URL is the URL for the hydra instance on
|
||||
// which to set up the client. This value will override the value
|
||||
// provided to `--hydra-url`
|
||||
URL string `json:"url,omitempty"`
|
||||
|
||||
// +kubebuilder:validation:Maximum=65535
|
||||
//
|
||||
// Port is the port for the hydra instance on
|
||||
// which to set up the client. This value will override the value
|
||||
// provided to `--hydra-port`
|
||||
Port int `json:"port,omitempty"`
|
||||
|
||||
// +kubebuilder:validation:Pattern=(^$|^/.*)
|
||||
//
|
||||
// Endpoint is the endpoint for the hydra instance on which
|
||||
// to set up the client. This value will override the value
|
||||
// provided to `--endpoint` (defaults to `"/clients"` in the
|
||||
// application)
|
||||
Endpoint string `json:"endpoint,omitempty"`
|
||||
|
||||
// +kubebuilder:validation:Pattern=(^$|https?|off)
|
||||
//
|
||||
// ForwardedProto overrides the `--forwarded-proto` flag. The
|
||||
// value "off" will force this to be off even if
|
||||
// `--forwarded-proto` is specified
|
||||
ForwardedProto string `json:"forwardedProto,omitempty"`
|
||||
}
|
||||
|
||||
// OAuth2ClientSpec defines the desired state of OAuth2Client
|
||||
type OAuth2ClientSpec struct {
|
||||
// +kubebuilder:validation:MaxItems=4
|
||||
@ -46,6 +80,9 @@ type OAuth2ClientSpec struct {
|
||||
// use at the authorization endpoint.
|
||||
ResponseTypes []ResponseType `json:"responseTypes,omitempty"`
|
||||
|
||||
// RedirectURIs is an array of the redirect URIs allowed for the application
|
||||
RedirectURIs []RedirectURI `json:"redirectUris,omitempty"`
|
||||
|
||||
// +kubebuilder:validation:Pattern=([a-zA-Z0-9\.\*]+\s?)+
|
||||
//
|
||||
// Scope is a string containing a space-separated list of scope values (as
|
||||
@ -59,6 +96,10 @@ type OAuth2ClientSpec struct {
|
||||
//
|
||||
// SecretName points to the K8s secret that contains this client's ID and password
|
||||
SecretName string `json:"secretName"`
|
||||
|
||||
// HydraAdmin is the optional configuration to use for managing
|
||||
// this client
|
||||
HydraAdmin HydraAdmin `json:"hydraAdmin,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:validation:Enum=client_credentials;authorization_code;implicit;refresh_token
|
||||
@ -69,6 +110,10 @@ type GrantType string
|
||||
// ResponseType represents an OAuth 2.0 response type strings
|
||||
type ResponseType string
|
||||
|
||||
// +kubebuilder:validation:Pattern=\w+:/?/?[^\s]+
|
||||
// RedirectURI represents a redirect URI for the client
|
||||
type RedirectURI string
|
||||
|
||||
// OAuth2ClientStatus defines the observed state of OAuth2Client
|
||||
type OAuth2ClientStatus struct {
|
||||
// ObservedGeneration represents the most recent generation observed by the daemon set controller.
|
||||
@ -114,6 +159,7 @@ func (c *OAuth2Client) ToOAuth2ClientJSON() *hydra.OAuth2ClientJSON {
|
||||
return &hydra.OAuth2ClientJSON{
|
||||
GrantTypes: grantToStringSlice(c.Spec.GrantTypes),
|
||||
ResponseTypes: responseToStringSlice(c.Spec.ResponseTypes),
|
||||
RedirectURIs: redirectToStringSlice(c.Spec.RedirectURIs),
|
||||
Scope: c.Spec.Scope,
|
||||
Owner: fmt.Sprintf("%s/%s", c.Name, c.Namespace),
|
||||
}
|
||||
@ -134,3 +180,11 @@ func grantToStringSlice(gt []GrantType) []string {
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
func redirectToStringSlice(ru []RedirectURI) []string {
|
||||
var output = make([]string, len(ru))
|
||||
for i, elem := range ru {
|
||||
output[i] = string(elem)
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ func TestCreateAPI(t *testing.T) {
|
||||
Namespace: "default",
|
||||
}
|
||||
|
||||
t.Run("by creating an API object if it meets CRD requirements", func(t *testing.T) {
|
||||
t.Run("by creating an API object if it meets CRD requirements without optional parameters", func(t *testing.T) {
|
||||
|
||||
resetTestClient()
|
||||
|
||||
@ -71,13 +71,45 @@ func TestCreateAPI(t *testing.T) {
|
||||
require.Error(t, getErr)
|
||||
})
|
||||
|
||||
t.Run("by creating an API object if it meets CRD requirements with optional parameters", func(t *testing.T) {
|
||||
|
||||
resetTestClient()
|
||||
|
||||
created.Spec.RedirectURIs = []RedirectURI{"https://client/account", "http://localhost:8080/account"}
|
||||
created.Spec.HydraAdmin = HydraAdmin{
|
||||
URL: "http://localhost",
|
||||
Port: 4445,
|
||||
// Endpoint: "/clients",
|
||||
ForwardedProto: "https",
|
||||
}
|
||||
|
||||
createErr = k8sClient.Create(context.TODO(), created)
|
||||
require.NoError(t, createErr)
|
||||
|
||||
fetched = &OAuth2Client{}
|
||||
getErr = k8sClient.Get(context.TODO(), key, fetched)
|
||||
require.NoError(t, getErr)
|
||||
assert.Equal(t, created, fetched)
|
||||
|
||||
deleteErr = k8sClient.Delete(context.TODO(), created)
|
||||
require.NoError(t, deleteErr)
|
||||
|
||||
getErr = k8sClient.Get(context.TODO(), key, created)
|
||||
require.Error(t, getErr)
|
||||
})
|
||||
|
||||
t.Run("by failing if the requested object doesn't meet CRD requirements", func(t *testing.T) {
|
||||
|
||||
for desc, modifyClient := range map[string]func(){
|
||||
"invalid grant type": func() { created.Spec.GrantTypes = []GrantType{"invalid"} },
|
||||
"invalid response type": func() { created.Spec.ResponseTypes = []ResponseType{"invalid"} },
|
||||
"invalid scope": func() { created.Spec.Scope = "" },
|
||||
"missing secret name": func() { created.Spec.SecretName = "" },
|
||||
"invalid grant type": func() { created.Spec.GrantTypes = []GrantType{"invalid"} },
|
||||
"invalid response type": func() { created.Spec.ResponseTypes = []ResponseType{"invalid"} },
|
||||
"invalid scope": func() { created.Spec.Scope = "" },
|
||||
"missing secret name": func() { created.Spec.SecretName = "" },
|
||||
"invalid redirect URI": func() { created.Spec.RedirectURIs = []RedirectURI{"invalid"} },
|
||||
"invalid hydra url": func() { created.Spec.HydraAdmin.URL = "invalid" },
|
||||
"invalid hydra port high": func() { created.Spec.HydraAdmin.Port = 65536 },
|
||||
"invalid hydra endpoint": func() { created.Spec.HydraAdmin.Endpoint = "invalid" },
|
||||
"invalid hydra forwarded proto": func() { created.Spec.HydraAdmin.Endpoint = "invalid" },
|
||||
} {
|
||||
t.Run(fmt.Sprintf("case=%s", desc), func(t *testing.T) {
|
||||
|
||||
|
@ -23,6 +23,21 @@ import (
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HydraAdmin) DeepCopyInto(out *HydraAdmin) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HydraAdmin.
|
||||
func (in *HydraAdmin) DeepCopy() *HydraAdmin {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HydraAdmin)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *OAuth2Client) DeepCopyInto(out *OAuth2Client) {
|
||||
*out = *in
|
||||
@ -95,6 +110,12 @@ func (in *OAuth2ClientSpec) DeepCopyInto(out *OAuth2ClientSpec) {
|
||||
*out = make([]ResponseType, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.RedirectURIs != nil {
|
||||
in, out := &in.RedirectURIs, &out.RedirectURIs
|
||||
*out = make([]RedirectURI, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
out.HydraAdmin = in.HydraAdmin
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OAuth2ClientSpec.
|
||||
|
Reference in New Issue
Block a user