Skip to content

SSO: Improve Azure AD validation #1390

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Apr 11, 2024
Merged
110 changes: 77 additions & 33 deletions internal/resources/grafana/resource_sso_settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -454,40 +454,55 @@ func getSettingsFromResourceData(d *schema.ResourceData, settingsKey string) (ma
return nil, fmt.Errorf("no valid settings found for the provider %s", d.Get(providerKey).(string))
}

func validateOAuth2Settings(provider string, settings map[string]any) error {
authURL := settings["auth_url"].(string)
tokenURL := settings["token_url"].(string)
apiURL := settings["api_url"].(string)
type validateFunc func(settingsMap map[string]any, provider string) error

var validationsByProvider = map[string][]validateFunc{
"azuread": {
validateNotEmpty("auth_url"),
validateNotEmpty("token_url"),
validateEmpty("api_url"),
validateURL("auth_url"),
validateURL("token_url"),
},
"generic_oauth": {
validateNotEmpty("auth_url"),
validateNotEmpty("token_url"),
validateNotEmpty("api_url"),
validateURL("auth_url"),
validateURL("token_url"),
validateURL("api_url"),
},
"okta": {
validateNotEmpty("auth_url"),
validateNotEmpty("token_url"),
validateNotEmpty("api_url"),
validateURL("auth_url"),
validateURL("token_url"),
validateURL("api_url"),
},
"github": {
validateEmpty("auth_url"),
validateEmpty("token_url"),
validateEmpty("api_url"),
},
"gitlab": {
validateEmpty("auth_url"),
validateEmpty("token_url"),
validateEmpty("api_url"),
},
"google": {
validateEmpty("auth_url"),
validateEmpty("token_url"),
validateEmpty("api_url"),
},
}

switch provider {
case "github", "gitlab", "google":
if authURL != "" {
return fmt.Errorf("auth_url must be empty for the provider %s", provider)
}
if tokenURL != "" {
return fmt.Errorf("token_url must be empty for the provider %s", provider)
}
if apiURL != "" {
return fmt.Errorf("api_url must be empty for the provider %s", provider)
}
case "azuread", "generic_oauth", "okta":
if authURL == "" {
return fmt.Errorf("auth_url must be set for the provider %s", provider)
}
if !isValidURL(authURL) {
return fmt.Errorf("auth_url must be a valid http/https URL")
}
if tokenURL == "" {
return fmt.Errorf("token_url must be set for the provider %s", provider)
}
if !isValidURL(tokenURL) {
return fmt.Errorf("token_url must be a valid http/https URL")
}
if apiURL == "" {
return fmt.Errorf("api_url must be set for the provider %s", provider)
}
if !isValidURL(apiURL) {
return fmt.Errorf("api_url must be a valid http/https URL")
func validateOAuth2Settings(provider string, settings map[string]any) error {
validators := validationsByProvider[provider]
for _, validateF := range validators {
err := validateF(settings, provider)
if err != nil {
return err
}
}

Expand Down Expand Up @@ -599,3 +614,32 @@ func isValidURL(actual string) bool {
}
return strings.HasPrefix(parsed.Scheme, "http") && parsed.Host != ""
}

func validateNotEmpty(key string) validateFunc {
return func(settingsMap map[string]any, provider string) error {
if settingsMap[key] == "" {
return fmt.Errorf("%s must be set for the provider %s", key, provider)
}

return nil
}
}

func validateEmpty(key string) validateFunc {
return func(settingsMap map[string]any, provider string) error {
if settingsMap[key].(string) != "" {
return fmt.Errorf("%s must be empty for the provider %s", key, provider)
}

return nil
}
}

func validateURL(key string) validateFunc {
return func(settingsMap map[string]any, provider string) error {
if !isValidURL(settingsMap[key].(string)) {
return fmt.Errorf("%s must be a valid http/https URL for the provider %s", key, provider)
}
return nil
}
}
36 changes: 29 additions & 7 deletions internal/resources/grafana/resource_sso_settings_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,10 +236,13 @@ func checkSsoSettingsReset(api *client.GrafanaHTTPAPI, provider string, defaultS
func testConfigForProvider(provider string, prefix string) string {
urls := ""
switch provider {
case "azuread", "generic_oauth", "okta":
case "generic_oauth", "okta":
urls = `auth_url = "https://myidp.com/oauth/authorize"
token_url = "https://myidp.com/oauth/token"
api_url = "https://myidp.com/oauth/userinfo"`
case "azuread":
urls = `auth_url = "https://myidp.com/oauth/authorize"
token_url = "https://myidp.com/oauth/token"`
}

return fmt.Sprintf(`resource "grafana_sso_settings" "%[2]s_sso_settings" {
Expand Down Expand Up @@ -308,12 +311,31 @@ const testConfigWithInvalidCustomField = `resource "grafana_sso_settings" "sso_s
var testConfigsWithValidationErrors = []string{
// no token_url provided for azuread
`resource "grafana_sso_settings" "azure_sso_settings" {
provider_name = "azuread"
oauth2_settings {
client_id = "client_id"
auth_url = "https://login.microsoftonline.com/12345/oauth2/v2.0/authorize"
}
}`,
provider_name = "azuread"
oauth2_settings {
client_id = "client_id"
auth_url = "https://login.microsoftonline.com/12345/oauth2/v2.0/authorize"
}
}`,
// api_url is not empty for azuread
`resource "grafana_sso_settings" "azure_sso_settings" {
provider_name = "azuread"
oauth2_settings {
client_id = "client_id"
auth_url = "https://login.microsoftonline.com/12345/oauth2/v2.0/authorize"
token_url = "https://login.microsoftonline.com/12345/oauth2/v2.0/token"
api_url = "https://login.microsoftonline.com/12345/oauth2/v2.0/user"
}
}`,
// token_url is not a valid url for azuread
`resource "grafana_sso_settings" "azure_sso_settings" {
provider_name = "azuread"
oauth2_settings {
client_id = "client_id"
auth_url = "https://login.microsoftonline.com/12345/oauth2/v2.0/authorize"
token_url = "this-is-an-invalid-url"
}
}`,
// invalid auth_url provided for okta
`resource "grafana_sso_settings" "okta_sso_settings" {
provider_name = "okta"
Expand Down