diff --git a/pkg/services/ldap/service/ldap.go b/pkg/services/ldap/service/ldap.go index d3d87e9b2fc..89567453d8e 100644 --- a/pkg/services/ldap/service/ldap.go +++ b/pkg/services/ldap/service/ldap.go @@ -16,6 +16,7 @@ import ( "github.com/grafana/grafana/pkg/services/ssosettings" "github.com/grafana/grafana/pkg/services/ssosettings/models" "github.com/grafana/grafana/pkg/setting" + "github.com/grafana/grafana/pkg/util" ) var ( @@ -128,6 +129,34 @@ func (s *LDAPImpl) Validate(ctx context.Context, settings models.SSOSettings, ol if server.Host == "" { return fmt.Errorf("no host configured for server with index %d", i) } + + if server.SearchFilter == "" { + return fmt.Errorf("no search filter configured for server with index %d", i) + } + + if len(server.SearchBaseDNs) == 0 { + return fmt.Errorf("no search base DN configured for server with index %d", i) + } + + if server.MinTLSVersion != "" { + _, err = util.TlsNameToVersion(server.MinTLSVersion) + if err != nil { + return fmt.Errorf("invalid min TLS version configured for server with index %d", i) + } + } + + if len(server.TLSCiphers) > 0 { + _, err = util.TlsCiphersToIDs(server.TLSCiphers) + if err != nil { + return fmt.Errorf("invalid TLS ciphers configured for server with index %d", i) + } + } + + for _, groupMap := range server.Groups { + if groupMap.OrgRole == "" && groupMap.IsGrafanaAdmin == nil { + return fmt.Errorf("organization role or Grafana admin status is required in group mappings for server with index %d", i) + } + } } return nil diff --git a/pkg/services/ldap/service/ldap_test.go b/pkg/services/ldap/service/ldap_test.go index 6cf33fc1bff..2fe46701264 100644 --- a/pkg/services/ldap/service/ldap_test.go +++ b/pkg/services/ldap/service/ldap_test.go @@ -314,7 +314,21 @@ func TestValidate(t *testing.T) { "config": map[string]any{ "servers": []any{ map[string]any{ - "host": "127.0.0.1", + "host": "127.0.0.1", + "search_filter": "(cn=%s)", + "search_base_dns": []string{"dc=grafana,dc=org"}, + "min_tls_version": "TLS1.3", + "tls_ciphers": []string{"TLS_AES_128_GCM_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"}, + "group_mappings": []any{ + map[string]any{ + "group_dn": "cn=admins,ou=groups,dc=grafana,dc=org", + "grafana_admin": true, + }, + map[string]any{ + "group_dn": "cn=users,ou=groups,dc=grafana,dc=org", + "org_role": "Editor", + }, + }, }, }, }, @@ -376,10 +390,14 @@ func TestValidate(t *testing.T) { "config": map[string]any{ "servers": []any{ map[string]any{ - "host": "127.0.0.1", + "host": "127.0.0.1", + "search_filter": "(cn=%s)", + "search_base_dns": []string{"dc=grafana,dc=org"}, }, map[string]any{ - "port": 123, + "port": 123, + "search_filter": "(cn=%s)", + "search_base_dns": []string{"dc=grafana,dc=org"}, }, }, }, @@ -388,6 +406,116 @@ func TestValidate(t *testing.T) { isValid: false, containsError: "no host configured", }, + { + description: "validation fails if search filter is not configured", + settings: models.SSOSettings{ + Provider: "ldap", + Settings: map[string]any{ + "enabled": true, + "config": map[string]any{ + "servers": []any{ + map[string]any{ + "host": "127.0.0.1", + "search_base_dns": []string{"dc=grafana,dc=org"}, + }, + }, + }, + }, + }, + isValid: false, + containsError: "no search filter", + }, + { + description: "validation fails if search base DN is not configured", + settings: models.SSOSettings{ + Provider: "ldap", + Settings: map[string]any{ + "enabled": true, + "config": map[string]any{ + "servers": []any{ + map[string]any{ + "host": "127.0.0.1", + "search_filter": "(cn=%s)", + }, + }, + }, + }, + }, + isValid: false, + containsError: "no search base DN", + }, + { + description: "validation fails if min TLS version is invalid", + settings: models.SSOSettings{ + Provider: "ldap", + Settings: map[string]any{ + "enabled": true, + "config": map[string]any{ + "servers": []any{ + map[string]any{ + "host": "127.0.0.1", + "search_filter": "(cn=%s)", + "search_base_dns": []string{"dc=grafana,dc=org"}, + "min_tls_version": "TLS5.18", + }, + }, + }, + }, + }, + isValid: false, + containsError: "invalid min TLS version", + }, + { + description: "validation fails if TLS cyphers are invalid", + settings: models.SSOSettings{ + Provider: "ldap", + Settings: map[string]any{ + "enabled": true, + "config": map[string]any{ + "servers": []any{ + map[string]any{ + "host": "127.0.0.1", + "search_filter": "(cn=%s)", + "search_base_dns": []string{"dc=grafana,dc=org"}, + "tls_ciphers": []string{"TLS_AES_128_GCM_SHA256", "invalid-tls-cypher"}, + }, + }, + }, + }, + }, + isValid: false, + containsError: "invalid TLS ciphers", + }, + { + description: "validation fails if a group mapping contains no organization role", + settings: models.SSOSettings{ + Provider: "ldap", + Settings: map[string]any{ + "enabled": true, + "config": map[string]any{ + "servers": []any{ + map[string]any{ + "host": "127.0.0.1", + "search_filter": "(cn=%s)", + "search_base_dns": []string{"dc=grafana,dc=org"}, + "group_mappings": []any{ + map[string]any{ + "group_dn": "cn=admins,ou=groups,dc=grafana,dc=org", + "org_role": "Admin", + "grafana_admin": true, + }, + map[string]any{ + "group_dn": "cn=users,ou=groups,dc=grafana,dc=org", + }, + }, + }, + }, + }, + }, + }, + isValid: false, + containsError: "organization role", + }, } for _, tt := range testCases {