Skip to content

Commit

Permalink
APIGOV-24403 - Support for requesting token with scope and mTLS conne…
Browse files Browse the repository at this point in the history
…ction with IdP (#666)

* APIGOV-24403 - Changes to setup certs for mTLS and scope for requesting token from IdP
- Updated IDP configuration to setup root CA certificate, client certificate and client key to be used for setting up mTLS connection to IdP
- Updated IDP configuration to setup scope for requesting token
- Updated client secret authenticator to include scope while requesting the token from IdP
- Updated provider auth client to use the TLS config from IdP config if provided otherwise use the Central TLS config

* APIGOV-24403 - Update to reformat description to apply stage
  • Loading branch information
vivekschauhan authored Jul 26, 2023
1 parent a77393a commit a9146a5
Show file tree
Hide file tree
Showing 9 changed files with 156 additions and 47 deletions.
6 changes: 5 additions & 1 deletion pkg/agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,10 +236,14 @@ func InitializeProfiling(cpuProfile, memProfile string) {
func registerExternalIDPs() {
if agent.cfg.GetAgentType() != config.TraceabilityAgent {
idPCfg := agent.agentFeaturesCfg.GetExternalIDPConfig()
tlsCfg := agent.cfg.GetTLSConfig()

proxy := agent.cfg.GetProxyURL()
timeout := agent.cfg.GetClientTimeout()
for _, idp := range idPCfg.GetIDPList() {
tlsCfg := idp.GetTLSConfig()
if idp.GetTLSConfig() == nil {
tlsCfg = agent.cfg.GetTLSConfig()
}
registerCredentialProvider(idp, tlsCfg, proxy, timeout)
}
}
Expand Down
12 changes: 9 additions & 3 deletions pkg/apic/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,11 +193,17 @@ func (c *ServiceClient) UpdateSubscriptionDefinitionPropertiesForCatalogItem(cat
// Update description and title after updating or creating APIService to include the stage name if it exists
func (c *ServiceClient) postAPIServiceUpdate(serviceBody *ServiceBody) {
if serviceBody.Stage != "" {
addDescription := fmt.Sprintf("%s: %s", serviceBody.StageDescriptor, serviceBody.Stage)
stageDescription := fmt.Sprintf("%s: %s", serviceBody.StageDescriptor, serviceBody.Stage)
if len(serviceBody.Description) > 0 {
serviceBody.Description = fmt.Sprintf("%s, %s", serviceBody.Description, addDescription)
stageDescription = fmt.Sprintf(", %s", stageDescription)
if len(serviceBody.Description)+len(stageDescription) >= maxDescriptionLength {
description := serviceBody.Description[0 : maxDescriptionLength-len(strEllipsis)-len(stageDescription)]
serviceBody.Description = fmt.Sprintf("%s%s%s", description, strEllipsis, stageDescription)
} else {
serviceBody.Description = fmt.Sprintf("%s%s", serviceBody.Description, stageDescription)
}
} else {
serviceBody.Description = addDescription
serviceBody.Description = stageDescription
}
serviceBody.NameToPush = fmt.Sprintf("%v (%s: %v)", serviceBody.NameToPush, serviceBody.StageDescriptor, serviceBody.Stage)
} else if c.cfg.GetAppendEnvironmentToTitle() {
Expand Down
4 changes: 3 additions & 1 deletion pkg/authz/oauth/authclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,12 @@ func WithServerName(serverName string) AuthClientOption {
}

// WithClientSecretAuth - sets up to use client secret authenticator
func WithClientSecretAuth(clientID, clientSecret string) AuthClientOption {
func WithClientSecretAuth(clientID, clientSecret, scope string) AuthClientOption {
return func(opt *authClientOptions) {
opt.authenticator = &clientSecretAuthenticator{
clientID,
clientSecret,
scope,
}
}
}
Expand Down Expand Up @@ -160,6 +161,7 @@ func (c *authClient) getOAuthTokens() (*tokenResponse, error) {
WithField("server", c.options.serverName).
WithField("url", c.tokenURL).
WithField("status", resp.Code).
WithField("body", string(resp.Body)).
WithError(err).
Debug(err.Error())
return nil, err
Expand Down
4 changes: 2 additions & 2 deletions pkg/authz/oauth/authclient_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func TestGetPlatformTokensHttpError(t *testing.T) {
s.SetTokenResponse("", 0, http.StatusBadRequest)
ac, err := NewAuthClient(s.GetTokenURL(), apiClient,
WithServerName("testServer"),
WithClientSecretAuth("invalid_client", "invalid-secrt"))
WithClientSecretAuth("invalid_client", "invalid-secrt", ""))
assert.Nil(t, err)
assert.NotNil(t, ac)

Expand Down Expand Up @@ -106,7 +106,7 @@ func TestGetPlatformTokensTimeout(t *testing.T) {
apiClient := api.NewClientWithTimeout(config.NewTLSConfig(), "", time.Second)
ac, err := NewAuthClient(s.URL, apiClient,
WithServerName("testServer"),
WithClientSecretAuth("invalid_client", "invalid-secrt"))
WithClientSecretAuth("invalid_client", "invalid-secrt", ""))

assert.Nil(t, err)
assert.NotNil(t, ac)
Expand Down
19 changes: 14 additions & 5 deletions pkg/authz/oauth/clientsecretauthenticator.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,21 @@ import "net/url"
type clientSecretAuthenticator struct {
clientID string
clientSecret string
scope string
}

func (p *clientSecretAuthenticator) prepareRequest() (url.Values, error) {
return url.Values{
metaGrantType: []string{grantClientCredentials},
metaClientID: []string{p.clientID},
metaClientSecret: []string{p.clientSecret},
}, nil
v := url.Values{
metaGrantType: []string{grantClientCredentials},
metaClientID: []string{p.clientID},
}

if p.clientSecret != "" {
v.Add(metaClientSecret, p.clientSecret)
}

if p.scope != "" {
v.Add(metaScope, p.scope)
}
return v, nil
}
1 change: 1 addition & 0 deletions pkg/authz/oauth/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ const (
metaGrantType = "grant_type"
metaClientID = "client_id"
metaClientSecret = "client_secret"
metaScope = "scope"
metaClientAssertionType = "client_assertion_type"
metaClientAssertion = "client_assertion"
)
2 changes: 1 addition & 1 deletion pkg/authz/oauth/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ func NewProvider(idp corecfg.IDPConfig, tlsCfg corecfg.TLSConfig, proxyURL strin
if p.cfg.GetAuthConfig() != nil && p.cfg.GetAuthConfig().GetType() == IDPAuthTypeClient {
p.authClient, err = NewAuthClient(p.authServerMetadata.TokenEndpoint, apiClient,
WithServerName(idp.GetIDPName()),
WithClientSecretAuth(p.cfg.GetAuthConfig().GetClientID(), p.cfg.GetAuthConfig().GetClientSecret()))
WithClientSecretAuth(p.cfg.GetAuthConfig().GetClientID(), p.cfg.GetAuthConfig().GetClientSecret(), p.cfg.GetAuthConfig().GetClientScope()))
if err != nil {
return nil, err
}
Expand Down
103 changes: 75 additions & 28 deletions pkg/config/externalidpconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,28 @@ import (
)

const (
accessToken = "accessToken"
client = "client"

pathExternalIDP = "agentFeatures.idp"
fldName = "name"
fldTitle = "title"
fldType = "type"
fldMetadataURL = "metadataUrl"
fldExtraProperties = "extraProperties"
fldScope = "scope"
fldGrantType = "grantType"
fldAuthMethod = "authMethod"
fldAuthResponseType = "authResponseType"
fldAuthType = "auth.type"
fldAuthAccessToken = "auth.accessToken"
fldAuthClientID = "auth.clientId"
fldAuthClientSecret = "auth.clientSecret"
accessToken = "accessToken"
client = "client"
propInsecureSkipVerify = "insecureSkipVerify"
pathExternalIDP = "agentFeatures.idp"
fldName = "name"
fldTitle = "title"
fldType = "type"
fldMetadataURL = "metadataUrl"
fldExtraProperties = "extraProperties"
fldScope = "scope"
fldGrantType = "grantType"
fldAuthMethod = "authMethod"
fldAuthResponseType = "authResponseType"
fldAuthType = "auth.type"
fldAuthAccessToken = "auth.accessToken"
fldAuthClientID = "auth.clientId"
fldAuthClientSecret = "auth.clientSecret"
fldAuthClientScope = "auth.clientScope"
fldSSLInsecureSkipVerify = "ssl." + propInsecureSkipVerify
fldSSLRootCACertPath = "ssl.rootCACertPath"
fldSSLClientCertPath = "ssl.clientCertPath"
fldSSLClientKeyPath = "ssl.clientKeyPath"
)

var configProperties = []string{
Expand All @@ -42,6 +47,11 @@ var configProperties = []string{
fldAuthAccessToken,
fldAuthClientID,
fldAuthClientSecret,
fldAuthClientScope,
fldSSLInsecureSkipVerify,
fldSSLRootCACertPath,
fldSSLClientCertPath,
fldSSLClientKeyPath,
}

var validIDPAuthType = map[string]bool{accessToken: true, client: true}
Expand Down Expand Up @@ -105,8 +115,10 @@ type IDPAuthConfig interface {
GetClientID() string
// GetClientSecret - Secret for the client in IdP that can used to create new OAuth clients
GetClientSecret() string
// GetClientScope - Scopes used for requesting the token using the ID client
GetClientScope() string
// validate - Validates the IDP auth configuration
validate()
validate(isMTLS bool)
}

// IDPConfig - interface for IdP provider config
Expand All @@ -131,17 +143,19 @@ type IDPConfig interface {
GetAuthResponseType() string
// GetExtraProperties - set of additional properties to be applied when registering the client
GetExtraProperties() map[string]string
// GetTLSConfig - tls config for IDP connection
GetTLSConfig() TLSConfig
// validate - Validates the IDP configuration
validate()
}

// IDPAuthConfiguration - Structure to hold the IdP provider auth config
type IDPAuthConfiguration struct {
Type string `json:"type,omitempty"`
AccessToken string `json:"accessToken,omitempty"`
ClientID string `json:"clientId,omitempty"`
ClientSecret string `json:"clientSecret,omitempty"`
ClientScopes []string `json:"clientScopes,omitempty"`
Type string `json:"type,omitempty"`
AccessToken string `json:"accessToken,omitempty"`
ClientID string `json:"clientId,omitempty"`
ClientSecret string `json:"clientSecret,omitempty"`
ClientScope string `json:"clientScope,omitempty"`
}

// IDPConfiguration - Structure to hold the IdP provider config
Expand All @@ -156,6 +170,7 @@ type IDPConfiguration struct {
AuthMethod string `json:"authMethod,omitempty"`
AuthResponseType string `json:"authResponseType,omitempty"`
ExtraProperties ExtraProperties `json:"extraProperties,omitempty"`
TLSConfig TLSConfig `json:"ssl,omitempty"`
}

// GetIDPName - for the identity provider
Expand Down Expand Up @@ -208,6 +223,11 @@ func (i *IDPConfiguration) GetAuthResponseType() string {
return i.AuthResponseType
}

// GetTLSConfig - tls config for IDP connection
func (i *IDPConfiguration) GetTLSConfig() TLSConfig {
return i.TLSConfig
}

// validate - Validates the IDP configuration
func (i *IDPConfiguration) validate() {
if i.Name == "" {
Expand All @@ -221,7 +241,11 @@ func (i *IDPConfiguration) validate() {
exception.Throw(ErrBadConfig.FormatError(pathExternalIDP + "." + fldMetadataURL))
}

i.AuthConfig.validate()
isMTLS := false
if i.TLSConfig != nil {
isMTLS = i.TLSConfig.(*TLSConfiguration).ClientCertificatePath != "" && i.TLSConfig.(*TLSConfiguration).ClientKeyPath != ""
}
i.AuthConfig.validate(isMTLS)
}

// GetType - type of authentication mechanism to use "accessToken" or "client"
Expand All @@ -244,8 +268,13 @@ func (i *IDPAuthConfiguration) GetClientSecret() string {
return i.ClientSecret
}

// GetClientScope - scopes used for requesting the token using the ID client
func (i *IDPAuthConfiguration) GetClientScope() string {
return i.ClientScope
}

// validate - Validates the IDP auth configuration
func (i *IDPAuthConfiguration) validate() {
func (i *IDPAuthConfiguration) validate(isMTLS bool) {
if ok := validIDPAuthType[i.GetType()]; !ok {
exception.Throw(ErrBadConfig.FormatError(pathExternalIDP + "." + fldAuthType))
}
Expand All @@ -258,8 +287,10 @@ func (i *IDPAuthConfiguration) validate() {
if i.GetClientID() == "" {
exception.Throw(ErrBadConfig.FormatError(pathExternalIDP + "." + fldAuthClientID))
}
if i.GetClientSecret() == "" {
exception.Throw(ErrBadConfig.FormatError(pathExternalIDP + "." + fldAuthClientSecret))
if !isMTLS {
if i.GetClientSecret() == "" {
exception.Throw(ErrBadConfig.FormatError(pathExternalIDP + "." + fldAuthClientSecret))
}
}
}
}
Expand Down Expand Up @@ -287,9 +318,25 @@ func parseExternalIDPConfig(props properties.Properties) (ExternalIDPConfig, err

buf, _ := json.Marshal(envIdpCfg)
json.Unmarshal(buf, idpCfg)

if entry, ok := envIdpCfg["ssl"]; ok && entry != nil {
idpCfg.TLSConfig = parseExternalIDPTLSConfig(entry)
}
cfg.IDPConfigs[idpCfg.Name] = idpCfg
}

return cfg, nil
}

func parseExternalIDPTLSConfig(idpTLSCfg interface{}) TLSConfig {
tlsCfg := NewTLSConfig()
tlsConfig, ok := idpTLSCfg.(map[string]interface{})
if ok {
buf, _ := json.Marshal(tlsConfig)
json.Unmarshal(buf, tlsCfg)
v := tlsConfig[propInsecureSkipVerify]
if s, ok := v.(string); ok && s == "true" {
tlsCfg.(*TLSConfiguration).InsecureSkipVerify = true
}
}
return tlsCfg
}
Loading

0 comments on commit a9146a5

Please sign in to comment.