Skip to content

Commit

Permalink
APIGOV-26190 - Update to set property dependencies using schema build…
Browse files Browse the repository at this point in the history
…er (#795)

- Changes to setup dependent properties for string based schema properties
- Updates to generate property and schema dependencies
- Updated OAuth external CRD to setup dependencies for private_key_jwt and tls_client_auth grant types
  • Loading branch information
vivekschauhan authored Jun 24, 2024
1 parent 1ed9528 commit e9f8e7a
Show file tree
Hide file tree
Showing 5 changed files with 442 additions and 86 deletions.
99 changes: 55 additions & 44 deletions pkg/agent/provisioning.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,24 +256,10 @@ func WithCRDForIDP(p oauth.Provider, scopes []string) func(c *crdBuilderOptions)

setIDPClientSecretSchemaProperty(c)
setIDPTokenURLSchemaProperty(p, c)
setIDPScopesSchemaProperty(p, scopes, c)
setIDPScopesSchemaProperty(scopes, c)
setIDPGrantTypesSchemaProperty(p, c)
tokenAuthMethods := setIDPTokenAuthMethodSchemaProperty(p, c)
setIDPRedirectURIsSchemaProperty(p, c)

usePrivateKeyJWTAuth := idpUsesPrivateKeyJWTAuth(tokenAuthMethods)
useTLSClientAuth := idpUsesTLSClientAuth(tokenAuthMethods)
if usePrivateKeyJWTAuth || useTLSClientAuth {
setIDPJWKSURISchemaProperty(p, c)
}

if usePrivateKeyJWTAuth {
setIDPJWKSSchemaProperty(p, c)
}

if useTLSClientAuth {
setIDPTLSClientAuthSchemaProperty(p, c)
}
setIDPTokenAuthMethodSchemaProperty(p, c)
setIDPRedirectURIsSchemaProperty(c)
}
}

Expand All @@ -297,7 +283,7 @@ func setIDPTokenURLSchemaProperty(p oauth.Provider, c *crdBuilderOptions) {
SetDefaultValue(p.GetTokenEndpoint()))
}

func setIDPScopesSchemaProperty(p oauth.Provider, scopes []string, c *crdBuilderOptions) {
func setIDPScopesSchemaProperty(scopes []string, c *crdBuilderOptions) {
c.reqProps = append(c.reqProps,
provisioning.NewSchemaPropertyBuilder().
SetName(provisioning.OauthScopes).
Expand Down Expand Up @@ -348,17 +334,28 @@ func setIDPTokenAuthMethodSchemaProperty(p oauth.Provider, c *crdBuilderOptions)
tokenAuthMethods, defaultTokenMethod := removeUnsupportedTypes(
p.GetSupportedTokenAuthMethods(), supportedIDPTokenAuthMethods, config.ClientSecretBasic)

c.reqProps = append(c.reqProps,
provisioning.NewSchemaPropertyBuilder().
SetName(provisioning.OauthTokenAuthMethod).
SetLabel("Token Auth Method").
IsString().
SetDefaultValue(defaultTokenMethod).
SetEnumValues(tokenAuthMethods))
tmBuilder := provisioning.NewSchemaPropertyBuilder().
SetName(provisioning.OauthTokenAuthMethod).
SetLabel("Token Auth Method").
IsString().
SetDefaultValue(defaultTokenMethod).
SetEnumValues(tokenAuthMethods)

if idpUsesPrivateKeyJWTAuth(tokenAuthMethods) {
setIDPJWKSURISchemaProperty(config.PrivateKeyJWT, tmBuilder)
setIDPJWKSSchemaProperty(config.PrivateKeyJWT, tmBuilder)
}

if idpUsesTLSClientAuth(tokenAuthMethods) {
setIDPJWKSURISchemaProperty(config.TLSClientAuth, tmBuilder)
setIDPTLSClientAuthSchemaProperty(tmBuilder)
}

c.reqProps = append(c.reqProps, tmBuilder)
return tokenAuthMethods
}

func setIDPRedirectURIsSchemaProperty(p oauth.Provider, c *crdBuilderOptions) {
func setIDPRedirectURIsSchemaProperty(c *crdBuilderOptions) {
c.reqProps = append(c.reqProps,
provisioning.NewSchemaPropertyBuilder().
SetName(provisioning.OauthRedirectURIs).
Expand All @@ -370,52 +367,66 @@ func setIDPRedirectURIsSchemaProperty(p oauth.Provider, c *crdBuilderOptions) {
IsString()))
}

func setIDPJWKSURISchemaProperty(p oauth.Provider, c *crdBuilderOptions) {
c.reqProps = append(c.reqProps,
func setIDPJWKSURISchemaProperty(depValue string, propBuilder provisioning.StringPropertyBuilder) {
propBuilder.AddDependency(
depValue,
provisioning.NewSchemaPropertyBuilder().
SetName(provisioning.OauthJwksURI).
SetLabel("JWKS URI").
IsString())
}

func setIDPJWKSSchemaProperty(p oauth.Provider, c *crdBuilderOptions) {
c.reqProps = append(c.reqProps,
func setIDPJWKSSchemaProperty(depValue string, propBuilder provisioning.StringPropertyBuilder) {
propBuilder.AddDependency(
depValue,
provisioning.NewSchemaPropertyBuilder().
SetName(provisioning.OauthJwks).
SetLabel("Public Key").
IsString())

}

func setIDPTLSClientAuthSchemaProperty(p oauth.Provider, c *crdBuilderOptions) {
c.reqProps = append(c.reqProps,
func setIDPTLSClientAuthSchemaProperty(propBuilder provisioning.StringPropertyBuilder) {
propBuilder.AddDependency(
config.TLSClientAuth,
provisioning.NewSchemaPropertyBuilder().
SetName(provisioning.OauthCertificate).
SetLabel("Public Certificate").
IsString())
c.reqProps = append(c.reqProps,
provisioning.NewSchemaPropertyBuilder().
SetName(provisioning.OauthCertificateMetadata).
SetLabel("Certificate Metadata").
IsString().
SetDefaultValue(oauth.TLSClientAuthSubjectDN).
SetEnumValues(tlsAuthCertificateMetadata))
c.reqProps = append(c.reqProps,

certMetadataBuilder := provisioning.NewSchemaPropertyBuilder().
SetName(provisioning.OauthCertificateMetadata).
SetLabel("Certificate Metadata").
IsString().
SetDefaultValue(oauth.TLSClientAuthSubjectDN).
SetEnumValues(tlsAuthCertificateMetadata)

propBuilder.AddDependency(
config.TLSClientAuth,
certMetadataBuilder,
)
certMetadataBuilder.AddDependency(
oauth.TLSClientAuthSanDNS,
provisioning.NewSchemaPropertyBuilder().
SetName(provisioning.OauthTLSAuthSANDNS).
SetLabel("Certificate Subject Alternative Name, DNS").
IsString())
c.reqProps = append(c.reqProps,

certMetadataBuilder.AddDependency(
oauth.TLSClientAuthSanEmail,
provisioning.NewSchemaPropertyBuilder().
SetName(provisioning.OauthTLSAuthSANEmail).
SetLabel("Certificate Subject Alternative Name, Email").
IsString())
c.reqProps = append(c.reqProps,

certMetadataBuilder.AddDependency(
oauth.TLSClientAuthSanIP,
provisioning.NewSchemaPropertyBuilder().
SetName(provisioning.OauthTLSAuthSANIP).
SetLabel("Certificate Subject Alternative Name, IP address").
IsString())
c.reqProps = append(c.reqProps,

certMetadataBuilder.AddDependency(
oauth.TLSClientAuthSanURI,
provisioning.NewSchemaPropertyBuilder().
SetName(provisioning.OauthTLSAuthSANURI).
SetLabel("Certificate Subject Alternative Name, URI").
Expand Down
141 changes: 120 additions & 21 deletions pkg/apic/provisioning/propertybuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ const (
DataTypeObject = "object"
)

// oneOfPropertyDefinitions - used for items of propertyDefinition
type oneOfPropertyDefinitions struct {
OneOf []*propertyDefinition `json:"oneOf,omitempty"`
}

// anyOfPropertyDefinitions - used for items of propertyDefinition
type anyOfPropertyDefinitions struct {
AnyOf []propertyDefinition `json:"anyOf,omitempty"`
Expand All @@ -27,26 +32,27 @@ type PropertyDefinition interface {

// propertyDefinition -
type propertyDefinition struct {
Type string `json:"type"`
Title string `json:"title"`
Description string `json:"description,omitempty"`
Enum []string `json:"enum,omitempty"`
DefaultValue interface{} `json:"default,omitempty"`
ReadOnly bool `json:"readOnly,omitempty"`
Format string `json:"format,omitempty"`
Properties map[string]propertyDefinition `json:"properties,omitempty"`
RequiredProperties []string `json:"required,omitempty"`
Items *anyOfPropertyDefinitions `json:"items,omitempty"` // We use a pointer to avoid generating an empty struct if not set
MinItems *uint `json:"minItems,omitempty"` // We use a pointer to differentiate the "blank value" from a chosen 0 min value
MaxItems *uint `json:"maxItems,omitempty"` // We use a pointer to differentiate the "blank value" from a chosen 0 min value
Minimum *float64 `json:"minimum,omitempty"` // We use a pointer to differentiate the "blank value" from a chosen 0 min value
Maximum *float64 `json:"maximum,omitempty"` // We use a pointer to differentiate the "blank value" from a chosen 0 max value
IsEncrypted bool `json:"x-axway-encrypted,omitempty"`
Widget string `json:"x-axway-widget,omitempty"`
IsCopyable bool `json:"x-axway-copyable,omitempty"`
UniqueItems bool `json:"uniqueItems,omitempty"`
Name string `json:"-"`
Required bool `json:"-"`
Type string `json:"type,omitempty"`
Title string `json:"title,omitempty"`
Description string `json:"description,omitempty"`
Enum []string `json:"enum,omitempty"`
DefaultValue interface{} `json:"default,omitempty"`
ReadOnly bool `json:"readOnly,omitempty"`
Format string `json:"format,omitempty"`
Properties map[string]propertyDefinition `json:"properties,omitempty"`
RequiredProperties []string `json:"required,omitempty"`
Dependencies map[string]*oneOfPropertyDefinitions `json:"dependencies,omitempty"`
Items *anyOfPropertyDefinitions `json:"items,omitempty"` // We use a pointer to avoid generating an empty struct if not set
MinItems *uint `json:"minItems,omitempty"` // We use a pointer to differentiate the "blank value" from a chosen 0 min value
MaxItems *uint `json:"maxItems,omitempty"` // We use a pointer to differentiate the "blank value" from a chosen 0 min value
Minimum *float64 `json:"minimum,omitempty"` // We use a pointer to differentiate the "blank value" from a chosen 0 min value
Maximum *float64 `json:"maximum,omitempty"` // We use a pointer to differentiate the "blank value" from a chosen 0 max value
IsEncrypted bool `json:"x-axway-encrypted,omitempty"`
Widget string `json:"x-axway-widget,omitempty"`
IsCopyable bool `json:"x-axway-copyable,omitempty"`
UniqueItems bool `json:"uniqueItems,omitempty"`
Name string `json:"-"`
Required bool `json:"-"`
}

func (p *propertyDefinition) GetType() string {
Expand All @@ -71,6 +77,8 @@ func (p *propertyDefinition) GetEnums() []string {
type PropertyBuilder interface {
// Build - builds the property, this is called automatically by the schema builder
Build() (*propertyDefinition, error)
// BuildDependencies - builds the dependencies for the property, this is called automatically by the schema builder
BuildDependencies() (*oneOfPropertyDefinitions, error)
}

// TypePropertyBuilder - common methods related to type property builders
Expand Down Expand Up @@ -118,6 +126,8 @@ type StringPropertyBuilder interface {
SetDefaultValue(value string) StringPropertyBuilder
// SetAsTextArea - Set value to be rendered as a textarea box within the UI
SetAsTextArea() StringPropertyBuilder
// AddDependency - Add property dependencies
AddDependency(value string, property PropertyBuilder) StringPropertyBuilder
PropertyBuilder
}

Expand Down Expand Up @@ -298,6 +308,7 @@ type stringSchemaProperty struct {
enums []string
widget string
defaultValue string
dependencies map[string][]PropertyBuilder
StringPropertyBuilder
}

Expand Down Expand Up @@ -370,9 +381,20 @@ func (p *stringSchemaProperty) IsCopyable() StringPropertyBuilder {
return p
}

func (p *stringSchemaProperty) AddDependency(value string, property PropertyBuilder) StringPropertyBuilder {
if p.dependencies == nil {
p.dependencies = map[string][]PropertyBuilder{}
}
_, ok := p.dependencies[value]
if !ok {
p.dependencies[value] = make([]PropertyBuilder, 0)
}
p.dependencies[value] = append(p.dependencies[value], property)
return p
}

// Build - create a string propertyDefinition for use in the subscription schema builder
func (p *stringSchemaProperty) Build() (def *propertyDefinition, err error) {

def, err = p.schemaProperty.Build()
if err != nil {
return
Expand Down Expand Up @@ -419,6 +441,68 @@ func (p *stringSchemaProperty) Build() (def *propertyDefinition, err error) {
return def, err
}

// BuildDependencies - builds the dependencies for the property, this is called automatically by the schema builder
func (p *stringSchemaProperty) BuildDependencies() (*oneOfPropertyDefinitions, error) {
if len(p.dependencies) > 0 {
deps := &oneOfPropertyDefinitions{
OneOf: make([]*propertyDefinition, 0),
}
noDep := make(map[string]bool)
for _, enum := range p.enums {
props, ok := p.dependencies[enum]
if !ok {
noDep[enum] = true
continue
}

depDef, err := p.buildDependenciesDef(enum, props)
if err != nil {
return nil, err
}
deps.OneOf = append(deps.OneOf, depDef)
}
if len(noDep) > 0 {
for enum := range noDep {
depDef := &propertyDefinition{
Properties: make(map[string]propertyDefinition),
}
depDef.Properties[p.schemaProperty.name] = propertyDefinition{Enum: []string{enum}}
deps.OneOf = append(deps.OneOf, depDef)
}
}

return deps, nil
}
return nil, nil
}

func (p *stringSchemaProperty) buildDependenciesDef(val string, props []PropertyBuilder) (*propertyDefinition, error) {
depDef := &propertyDefinition{
Properties: make(map[string]propertyDefinition),
Dependencies: make(map[string]*oneOfPropertyDefinitions),
}
// value match property
depDef.Properties[p.schemaProperty.name] = propertyDefinition{Enum: []string{val}}

for _, prop := range props {
dp, err := prop.Build()
if err != nil {
return nil, err
}

depDef.Properties[dp.Name] = *dp
dep, err := prop.BuildDependencies()
if err != nil {
return nil, err
}

if dep != nil {
depDef.Dependencies[dp.Name] = dep
}
}
return depDef, nil
}

/**
number property datatype builder
*/
Expand Down Expand Up @@ -475,6 +559,11 @@ func (p *numberSchemaProperty) Build() (def *propertyDefinition, err error) {
return def, err
}

// BuildDependencies - builds the dependencies for the property, this is called automatically by the schema builder
func (p *numberSchemaProperty) BuildDependencies() (*oneOfPropertyDefinitions, error) {
return nil, nil
}

/**
integer property datatype builder
*/
Expand Down Expand Up @@ -575,6 +664,11 @@ func (p *arraySchemaProperty) Build() (def *propertyDefinition, err error) {
return def, err
}

// BuildDependencies - builds the dependencies for the property, this is called automatically by the schema builder
func (p *arraySchemaProperty) BuildDependencies() (*oneOfPropertyDefinitions, error) {
return nil, nil
}

/**
object property datatype builder
*/
Expand Down Expand Up @@ -622,3 +716,8 @@ func (p *objectSchemaProperty) Build() (def *propertyDefinition, err error) {
def.RequiredProperties = requiredProperties
return def, err
}

// BuildDependencies - builds the dependencies for the property, this is called automatically by the schema builder
func (p *objectSchemaProperty) BuildDependencies() (*oneOfPropertyDefinitions, error) {
return nil, nil
}
Loading

0 comments on commit e9f8e7a

Please sign in to comment.