Auth: Implement reload function for LDAP (#89267)

* keep config in a separate struct in LDAP

* implement reload function for LDAP

* remove param from sso service constructor

* update unit tests

* add feature flag

* remove nil params

* address feedback

* add unit test for disabled config
This commit is contained in:
Mihai Doarna 2024-06-18 14:31:27 +03:00 committed by GitHub
parent 0afbaa39df
commit d074cc7892
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 441 additions and 40 deletions

View File

@ -195,4 +195,5 @@ export interface FeatureToggles {
pinNavItems?: boolean;
authZGRPCServer?: boolean;
openSearchBackendFlowEnabled?: boolean;
ssoSettingsLDAP?: boolean;
}

View File

@ -14,7 +14,6 @@ import (
"github.com/grafana/grafana/pkg/login/social/connectors"
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
"github.com/grafana/grafana/pkg/services/featuremgmt"
ldap "github.com/grafana/grafana/pkg/services/ldap/service"
"github.com/grafana/grafana/pkg/services/licensing"
secretsfake "github.com/grafana/grafana/pkg/services/secrets/fakes"
"github.com/grafana/grafana/pkg/services/ssosettings/ssosettingsimpl"
@ -82,7 +81,6 @@ func TestSocialService_ProvideService(t *testing.T) {
nil,
&setting.OSSImpl{Cfg: cfg},
&licensing.OSSLicensingService{},
ldap.ProvideService(cfg),
)
for _, tc := range testCases {
@ -195,7 +193,6 @@ func TestSocialService_ProvideService_GrafanaComGrafanaNet(t *testing.T) {
nil,
nil,
&licensing.OSSLicensingService{},
ldap.ProvideService(cfg),
)
for _, tc := range testCases {

View File

@ -1323,6 +1323,14 @@ var (
Stage: FeatureStagePublicPreview,
Owner: awsDatasourcesSquad,
},
{
Name: "ssoSettingsLDAP",
Description: "Use the new SSO Settings API to configure LDAP",
Stage: FeatureStageExperimental,
Owner: identityAccessTeam,
HideFromDocs: true,
HideFromAdminPage: true,
},
}
)

View File

@ -176,3 +176,4 @@ azureMonitorPrometheusExemplars,experimental,@grafana/partner-datasources,false,
pinNavItems,experimental,@grafana/grafana-frontend-platform,false,false,false
authZGRPCServer,experimental,@grafana/identity-access-team,false,false,false
openSearchBackendFlowEnabled,preview,@grafana/aws-datasources,false,false,false
ssoSettingsLDAP,experimental,@grafana/identity-access-team,false,false,false

1 Name Stage Owner requiresDevMode RequiresRestart FrontendOnly
176 pinNavItems experimental @grafana/grafana-frontend-platform false false false
177 authZGRPCServer experimental @grafana/identity-access-team false false false
178 openSearchBackendFlowEnabled preview @grafana/aws-datasources false false false
179 ssoSettingsLDAP experimental @grafana/identity-access-team false false false

View File

@ -714,4 +714,8 @@ const (
// FlagOpenSearchBackendFlowEnabled
// Enables the backend query flow for Open Search datasource plugin
FlagOpenSearchBackendFlowEnabled = "openSearchBackendFlowEnabled"
// FlagSsoSettingsLDAP
// Use the new SSO Settings API to configure LDAP
FlagSsoSettingsLDAP = "ssoSettingsLDAP"
)

View File

@ -2126,6 +2126,20 @@
"allowSelfServe": true
}
},
{
"metadata": {
"name": "ssoSettingsLDAP",
"resourceVersion": "1718624569822",
"creationTimestamp": "2024-06-17T11:42:49Z"
},
"spec": {
"description": "Use the new SSO Settings API to configure LDAP",
"stage": "experimental",
"codeowner": "@grafana/identity-access-team",
"hideFromAdminPage": true,
"hideFromDocs": true
}
},
{
"metadata": {
"name": "ssoSettingsSAML",

View File

@ -1,8 +1,10 @@
package service
import (
"encoding/json"
"fmt"
"os"
"strconv"
"github.com/BurntSushi/toml"
@ -85,3 +87,27 @@ func assertNotEmptyCfg(val any, propName string) error {
}
return nil
}
func resolveBool(input any, defaultValue bool) bool {
strInput := fmt.Sprintf("%v", input)
result, err := strconv.ParseBool(strInput)
if err != nil {
return defaultValue
}
return result
}
func resolveServerConfig(input any) (*ldap.ServersConfig, error) {
var ldapCfg ldap.ServersConfig
inputJson, err := json.Marshal(input)
if err != nil {
return nil, err
}
if err = json.Unmarshal(inputJson, &ldapCfg); err != nil {
return nil, err
}
return &ldapCfg, nil
}

View File

@ -1,13 +1,19 @@
package service
import (
"context"
"errors"
"sync"
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/login/social"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/ldap"
"github.com/grafana/grafana/pkg/services/ldap/multildap"
"github.com/grafana/grafana/pkg/services/login"
"github.com/grafana/grafana/pkg/services/ssosettings"
"github.com/grafana/grafana/pkg/services/ssosettings/models"
"github.com/grafana/grafana/pkg/setting"
)
@ -29,39 +35,82 @@ type LDAP interface {
}
type LDAPImpl struct {
client multildap.IMultiLDAP
cfg *ldap.Config
ldapCfg *ldap.ServersConfig
log log.Logger
client multildap.IMultiLDAP
cfg *ldap.Config
ldapCfg *ldap.ServersConfig
log log.Logger
features featuremgmt.FeatureToggles
ssoSettings ssosettings.Service
// loadingMutex locks the reading of the config so multiple requests for reloading are sequential.
loadingMutex *sync.Mutex
}
func ProvideService(cfg *setting.Cfg) *LDAPImpl {
func ProvideService(cfg *setting.Cfg, features featuremgmt.FeatureToggles, ssoSettings ssosettings.Service) *LDAPImpl {
s := &LDAPImpl{
client: nil,
ldapCfg: nil,
cfg: ldap.GetLDAPConfig(cfg),
log: log.New("ldap.service"),
loadingMutex: &sync.Mutex{},
features: features,
ssoSettings: ssoSettings,
}
if !cfg.LDAPAuthEnabled {
return s
}
if s.features.IsEnabledGlobally(featuremgmt.FlagSsoSettingsApi) && s.features.IsEnabledGlobally(featuremgmt.FlagSsoSettingsLDAP) {
s.ssoSettings.RegisterReloadable(social.LDAPProviderName, s)
ldapCfg, err := multildap.GetConfig(s.cfg)
if err != nil {
s.log.Error("Failed to get LDAP config", "error", err)
ldapSettings, err := s.ssoSettings.GetForProvider(context.Background(), social.LDAPProviderName)
if err != nil {
s.log.Error("Failed to retrieve LDAP settings from SSO settings service", "error", err)
return s
}
err = s.Reload(context.Background(), *ldapSettings)
if err != nil {
s.log.Error("Failed to load LDAP settings", "error", err)
return s
}
} else {
s.ldapCfg = ldapCfg
s.client = multildap.New(s.ldapCfg.Servers, s.cfg)
s.cfg = ldap.GetLDAPConfig(cfg)
if !cfg.LDAPAuthEnabled {
return s
}
ldapCfg, err := multildap.GetConfig(s.cfg)
if err != nil {
s.log.Error("Failed to get LDAP config", "error", err)
} else {
s.ldapCfg = ldapCfg
s.client = multildap.New(s.ldapCfg.Servers, s.cfg)
}
}
return s
}
func (s *LDAPImpl) Reload(ctx context.Context, settings models.SSOSettings) error {
cfg := &ldap.Config{}
cfg.Enabled = resolveBool(settings.Settings["enabled"], false)
cfg.SkipOrgRoleSync = resolveBool(settings.Settings["skip_org_role_sync"], false)
cfg.AllowSignUp = resolveBool(settings.Settings["allow_sign_up"], true)
ldapCfg, err := resolveServerConfig(settings.Settings["config"])
if err != nil {
return err
}
s.loadingMutex.Lock()
defer s.loadingMutex.Unlock()
s.cfg = cfg
s.ldapCfg = ldapCfg
s.client = multildap.New(s.ldapCfg.Servers, s.cfg)
return nil
}
func (s *LDAPImpl) Validate(ctx context.Context, settings models.SSOSettings, oldSettings models.SSOSettings, requester identity.Requester) error {
return nil
}
func (s *LDAPImpl) ReloadConfig() error {
if !s.cfg.Enabled {
return nil

View File

@ -0,0 +1,299 @@
package service
import (
"context"
"sync"
"testing"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/ldap"
"github.com/grafana/grafana/pkg/services/ssosettings/models"
"github.com/stretchr/testify/require"
)
const (
validCert = `LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURYVENDQWtXZ0F3SUJBZ0lKQUxtVlZ1RFd1NE5ZTUEwR0NTcUdTSWIzRFFFQkN
3VUFNRVV4Q3pBSkJnTlYKQkFZVEFrRlZNUk13RVFZRFZRUUlEQXBUYjIxbExWTjBZWFJsTVNFd0h3WURWUVFLREJoSmJuUmxjbTVsZENCWAphV1JuYVhSekl
GQjBlU0JNZEdRd0hoY05NVFl4TWpNeE1UUXpORFEzV2hjTk5EZ3dOakkxTVRRek5EUTNXakJGCk1Rc3dDUVlEVlFRR0V3SkJWVEVUTUJFR0ExVUVDQXdLVTI
5dFpTMVRkR0YwWlRFaE1COEdBMVVFQ2d3WVNXNTAKWlhKdVpYUWdWMmxrWjJsMGN5QlFkSGtnVEhSa01JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUTh
BTUlJQgpDZ0tDQVFFQXpVQ0ZvemdOYjFoMU0wanpOUlNDamhPQm5SK3VWYlZwYVdmWFlJUitBaFdEZEVlNXJ5WStDZ2F2Ck9nOGJmTHlieXpGZGVobFlkRFJ
na2VkRUIvR2pHOGFKdzA2bDBxRjRqRE9BdzBrRXlnV0N1Mm1jSDdYT3hSdCsKWUFIM1RWSGEvSHUxVzNXanprb2JxcXFMUThna0tXV00yN2ZPZ0FaNkdpZWF
KQk42VkJTTU1jUGV5M0hXTEJtYworVFlKbXYxZGJhTzJqSGhLaDhwZkt3MFcxMlZNOFAxUElPOGd2NFBodS91dUpZaWVCV0tpeEJFeXkwbEhqeWl4CllGQ1I
xMnhkaDRDQTQ3cTk1OFpSR25uRFVHRlZFMVFoZ1JhY0pDT1o5YmQ1dDltcjhLTGFWQllUQ0pvNUVSRTgKanltYWI1ZFBxZTVxS2ZKc0NaaXFXZ2xialVvOXR
3SURBUUFCbzFBd1RqQWRCZ05WSFE0RUZnUVV4cHV3Y3MvQwpZUU95dWkrcjFHKzNLeEJOaHhrd0h3WURWUjBqQkJnd0ZvQVV4cHV3Y3MvQ1lRT3l1aStyMUc
rM0t4Qk5oeGt3CkRBWURWUjBUQkFVd0F3RUIvekFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBQWlXVUtzLzJ4L3ZpTkNLaTNZNmIKbEV1Q3RBR2h6T09aOUV
qcnZKOCtDT0gzUmFnM3RWQldyY0JaMy91aGhQcTVneTlscXc0T2t2RXdzOTkvNWpGcwpYMUZKNk1LQmdxZnV5N3loNXMxWWZNMEFOSFljek1tWXBaZUFjUWY
yQ0dBYVZmd1RUZlNsek5Mc0YybFcvbHk3CnlhcEZ6bFlTSkxHb1ZFK09IRXU4ZzVTbE5BQ1VFZmtYdys1RWdoaCtLemxJTjdSNlE3cjJpeFdORkJDL2pXZjc
KTktVZkp5WDhxSUc1bWQxWVVlVDZHQlc5Qm0yLzEvUmlPMjRKVGFZbGZMZEtLOVRZYjhzRzVCK09MYWIyREltRwo5OUNKMjVSa0FjU29iV05GNXpEME82bGd
PbzNjRWRCL2tzQ3EzaG10bEMvRGxMWi9EOENKKzdWdVpuUzFyUjJuCmFRPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ==`
validKey = `LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFdlFJQkFEQU5CZ2txaGtpRzl3MEJBUUVGQUFTQ0JLY3dnZ1NqQWdFQUFv
SUJBUUROUUlXak9BMXZXSFV6ClNQTTFGSUtPRTRHZEg2NVZ0V2xwWjlkZ2hINENGWU4wUjdtdkpqNEtCcTg2RHh0OHZKdkxNVjE2R1ZoME5HQ1IKNTBRSDhh
TWJ4b25EVHFYU29YaU1NNEREU1FUS0JZSzdhWndmdGM3RkczNWdBZmROVWRyOGU3VmJkYVBPU2h1cQpxb3REeUNRcFpZemJ0ODZBQm5vYUo1b2tFM3BVRkl3
eHc5N0xjZFlzR1p6NU5nbWEvVjF0bzdhTWVFcUh5bDhyCkRSYlhaVXp3L1U4Zzd5Qy9nK0c3KzY0bGlKNEZZcUxFRVRMTFNVZVBLTEZnVUpIWGJGMkhnSURq
dXIzbnhsRWEKZWNOUVlWVVRWQ0dCRnB3a0k1bjF0M20zMmF2d290cFVGaE1JbWprUkVUeVBLWnB2bDArcDdtb3A4bXdKbUtwYQpDVnVOU2oyM0FnTUJBQUVD
Z2dFQUJuNEkvQjIweHhYY056QVNpVlpKdnVhOURkUkh0bXhUbGtMem5CajB4Mm9ZCnkxL05iczNkM29GUm41dUV1aEJaT1RjcGhzZ3dkUlNIRFhac1AzZ1VP
YmV3K2QyTi96aWVVSWo4aExEVmx2SlAKclUvczRVL2w1M1EwTGlOQnlFOVRodkwrekpMUENLSnRkNXVIWmpCNWZGbTY5K1E3Z3U4eGc0eEhJdWIrMHBQNQpQ
SGFubUhDRHJiZ05OL29xbGFyNEZaMk1YVGdla1c2QW15Yy9rb0U5aEluNEJhYTJLZS9CL0FVR1k0cE1STHFwClRBcnQrR1RWZVdlb0ZZOVFBQ1VwYUhwSmhH
Yi9QaW91NnRsVTU3ZTQyY0xva2kxZjArU0FSc0JCS3lYQTdCQjEKMWZNSDEwS1FZRkE2OGRUWVdsS3pRYXUvSzR4YXFnNEZLbXR3RjY2R1FRS0JnUUQ5T3BO
VVM3b1J4TUhWSmFCUgpUTldXK1YxRlh5Y3FvamVrRnBEaWpQYjJYNUNXVjE2b2VXZ2FYcDBuT0hGZHk5RVdzM0d0R3BmWmFzYVJWSHNYClNIdFBoNE5iOEpx
SGRHRTAvQ0Q2dDArNERuczhCbjljU3F0ZFFCN1IzSm43SU1YaTlYL1U4TERLbytBMTgvSnEKVjhWZ1VuZ01ueTlZak1rUUliSzhUUldrWVFLQmdRRFBmNG54
TzZqdSt0T0hIT1JRdHkzYllERDArT1YzSTArTAoweXowdVByZXJ5QlZpOW5ZNDNLYWtINTJEN1VaRXd3c0JqakdYRCtXSDh4RXNtQldzR05YSnUwMjVQdnpJ
Sm96CmxBRWlYdk1wL05tWXArdFk0ckRtTzhSaHlWb2NCcVdIemgzOG0wSUZPZDRCeUZENW5MRURyQTNwRFZvMGFOZ1kKbjBHd1J5c1pGd0tCZ1FEa0NqM202
Wk1Vc1VXRXR5K2FSMEVKaG1LeU9EQkRPblkwOUlWaEgyUy9GZXhWRnpVTgpMdGZLOTIwNmhwL0F3ZXozTG4ydVQ0WnpxcTVLN2ZNelVuaUpkQldkVkIwMDRs
OHZvZVhwSWU5T1p1d2ZjQko5CmdGaTF6eXB4L3VGRHY0MjFCelFwQk4rUWZPZEtidmJkUVZGam5xQ3hiU0RyODB5VmxHTXJJNWZid1FLQmdHMDkKb1JyZXBP
N0VJTzhHTi9HQ3J1TEsvcHRLR2t5aHkzUTZ4blZFbWRiNDdoWDduY0pBNUlvWlBtcmJsQ1ZTVU5zdwpuMTFYSGFia3NMOE9CZ2c5cnQ4b1FFVGhRdi9hRHpU
T1c5YURsSk5yYWdlamlCVHdxOTlhWWVaMWdqbzFDWnE0CjJqS3VicENmeVpDNHJHRHRySWZaWWkxcStTMlVjUWh0ZDhEZGh3UWJBb0dBQU00RXBEQTR5SEI1
eWllazFwL28KQ2JxUkN0YS9EeDZFeW8wS2xOQXlQdUZQQXNodXBHNE5CeDdtVDJBU2ZMKzJWQkhvaTZtSFNyaStCRFg1cnlZRgpmTVl2cDdVUllvcTd3N3Fp
dlJsdnZFZzV5b1lySzEzRjIrR2o2eEo0akVOOW0wS2RNL2czbUpHcTBIQlRJUXJwClNtNzVXWHNmbE94dVRuMDhMYmdHYzRzPQotLS0tLUVORCBSU0EgUFJJ
VkFURSBLRVktLS0tLQ==`
)
var (
isAdmin = true
)
func TestReload(t *testing.T) {
testCases := []struct {
description string
settings models.SSOSettings
expectedServersConfig *ldap.ServersConfig
expectedConfig *ldap.Config
}{
{
description: "basic flow with minimal config",
settings: models.SSOSettings{
Provider: "ldap",
Settings: map[string]any{
"enabled": true,
"skip_org_role_sync": false,
"allow_sign_up": true,
"config": map[string]any{
"servers": []any{
map[string]any{
"host": "127.0.0.1",
},
},
},
},
IsDeleted: false,
},
expectedServersConfig: &ldap.ServersConfig{
Servers: []*ldap.ServerConfig{
{
Host: "127.0.0.1",
},
},
},
expectedConfig: &ldap.Config{
Enabled: true,
AllowSignUp: true,
SkipOrgRoleSync: false,
},
},
{
description: "complete set of server parameters",
settings: models.SSOSettings{
Provider: "ldap",
Settings: map[string]any{
"enabled": true,
"skip_org_role_sync": false,
"allow_sign_up": true,
"config": map[string]any{
"servers": []any{
map[string]any{
"host": "127.0.0.1",
"port": 3389,
"bind_dn": "cn=ldapservice,ou=users,dc=ldap,dc=goauthentik,dc=io",
"bind_password": "grafana",
"search_filter": "(cn=%s)",
"ssl_skip_verify": false,
"use_ssl": true,
"start_tls": true,
"min_tls_version": "TLS1.3",
"timeout": 10,
"root_ca_cert": "/path/to/certificate.crt",
"root_ca_cert_value": []string{validCert},
"client_cert": "/path/to/client.crt",
"client_cert_value": validCert,
"client_key": "/path/to/client.key",
"client_key_value": validKey,
"group_search_filter": "(&(objectClass=posixGroup)(memberUid=%s))",
"group_search_filter_user_attribute": "distinguishedName",
"group_search_base_dns": []string{"ou=groups,dc=grafana,dc=org"},
"tls_ciphers": []string{
"TLS_AES_256_GCM_SHA384",
},
"attributes": map[string]string{
"email": "mail",
"member_of": "memberOf",
"name": "displayName",
"surname": "sn",
"username": "cn",
},
"search_base_dns": []string{
"DC=ldap,DC=goauthentik,DC=io",
},
"group_mappings": []any{
map[string]any{
"group_dn": "cn=admin,ou=groups,dc=ldap,dc=goauthentik,dc=io",
"org_id": 1,
"org_role": "Admin",
"grafana_admin": true,
},
map[string]any{
"group_dn": "cn=editor,ou=groups,dc=ldap,dc=goauthentik,dc=io",
"org_id": 1,
"org_role": "Editor",
},
map[string]any{
"group_dn": "cn=viewer,ou=groups,dc=ldap,dc=goauthentik,dc=io",
"org_id": 1,
"org_role": "Viewer",
},
},
},
},
},
},
IsDeleted: false,
},
expectedServersConfig: &ldap.ServersConfig{
Servers: []*ldap.ServerConfig{
{
Host: "127.0.0.1",
Port: 3389,
UseSSL: true,
StartTLS: true,
SkipVerifySSL: false,
MinTLSVersion: "TLS1.3",
TLSCiphers: []string{
"TLS_AES_256_GCM_SHA384",
},
RootCACert: "/path/to/certificate.crt",
RootCACertValue: []string{validCert},
ClientCert: "/path/to/client.crt",
ClientCertValue: validCert,
ClientKey: "/path/to/client.key",
ClientKeyValue: validKey,
BindDN: "cn=ldapservice,ou=users,dc=ldap,dc=goauthentik,dc=io",
BindPassword: "grafana",
Timeout: 10,
Attr: ldap.AttributeMap{
Username: "cn",
Name: "displayName",
Surname: "sn",
Email: "mail",
MemberOf: "memberOf",
},
SearchFilter: "(cn=%s)",
SearchBaseDNs: []string{"DC=ldap,DC=goauthentik,DC=io"},
GroupSearchFilter: "(&(objectClass=posixGroup)(memberUid=%s))",
GroupSearchFilterUserAttribute: "distinguishedName",
GroupSearchBaseDNs: []string{"ou=groups,dc=grafana,dc=org"},
Groups: []*ldap.GroupToOrgRole{
{
GroupDN: "cn=admin,ou=groups,dc=ldap,dc=goauthentik,dc=io",
OrgId: 1,
OrgRole: "Admin",
IsGrafanaAdmin: &isAdmin,
},
{
GroupDN: "cn=editor,ou=groups,dc=ldap,dc=goauthentik,dc=io",
OrgId: 1,
OrgRole: "Editor",
},
{
GroupDN: "cn=viewer,ou=groups,dc=ldap,dc=goauthentik,dc=io",
OrgId: 1,
OrgRole: "Viewer",
},
},
},
},
},
expectedConfig: &ldap.Config{
Enabled: true,
AllowSignUp: true,
SkipOrgRoleSync: false,
},
},
{
description: "no servers config",
settings: models.SSOSettings{
Provider: "ldap",
Settings: map[string]any{
"enabled": true,
"skip_org_role_sync": false,
"allow_sign_up": true,
},
IsDeleted: false,
},
expectedServersConfig: &ldap.ServersConfig{Servers: nil},
expectedConfig: &ldap.Config{
Enabled: true,
AllowSignUp: true,
SkipOrgRoleSync: false,
},
},
{
description: "invalid settings",
settings: models.SSOSettings{
Provider: "ldap",
Settings: map[string]any{
"invalid": "some-value",
"another_one": true,
},
IsDeleted: false,
},
expectedServersConfig: &ldap.ServersConfig{Servers: nil},
expectedConfig: &ldap.Config{
AllowSignUp: true,
},
},
{
description: "config disabled",
settings: models.SSOSettings{
Provider: "ldap",
Settings: map[string]any{
"enabled": false,
"skip_org_role_sync": false,
"allow_sign_up": true,
"config": map[string]any{
"servers": []any{
map[string]any{
"host": "127.0.0.1",
},
},
},
},
IsDeleted: false,
},
expectedServersConfig: &ldap.ServersConfig{
Servers: []*ldap.ServerConfig{
{
Host: "127.0.0.1",
},
},
},
expectedConfig: &ldap.Config{
Enabled: false,
AllowSignUp: true,
SkipOrgRoleSync: false,
},
},
}
for _, tt := range testCases {
t.Run(tt.description, func(t *testing.T) {
ldapImpl := &LDAPImpl{
features: featuremgmt.WithManager(featuremgmt.FlagSsoSettingsApi),
loadingMutex: &sync.Mutex{},
}
err := ldapImpl.Reload(context.Background(), tt.settings)
require.NoError(t, err)
require.Equal(t, *tt.expectedServersConfig, *ldapImpl.ldapCfg)
require.Equal(t, *tt.expectedConfig, *ldapImpl.cfg)
})
}
}

View File

@ -32,8 +32,8 @@ type ServersConfig struct {
// ServerConfig holds connection data to LDAP
type ServerConfig struct {
Host string `toml:"host"`
Port int `toml:"port"`
Host string `toml:"host" json:"host"`
Port int `toml:"port" json:"port,omitempty"`
UseSSL bool `toml:"use_ssl" json:"use_ssl,omitempty"`
StartTLS bool `toml:"start_tls" json:"start_tls,omitempty"`

View File

@ -18,7 +18,6 @@ import (
"github.com/grafana/grafana/pkg/login/social"
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/ldap/service"
"github.com/grafana/grafana/pkg/services/licensing"
"github.com/grafana/grafana/pkg/services/secrets"
"github.com/grafana/grafana/pkg/services/ssosettings"
@ -48,10 +47,10 @@ type Service struct {
func ProvideService(cfg *setting.Cfg, sqlStore db.DB, ac ac.AccessControl,
routeRegister routing.RouteRegister, features featuremgmt.FeatureToggles,
secrets secrets.Service, usageStats usagestats.Service, registerer prometheus.Registerer,
settingsProvider setting.Provider, licensing licensing.Licensing, ldap service.LDAP) *Service {
settingsProvider setting.Provider, licensing licensing.Licensing) *Service {
fbStrategies := []ssosettings.FallbackStrategy{
strategies.NewOAuthStrategy(cfg),
strategies.NewLDAPStrategy(cfg, ldap),
strategies.NewLDAPStrategy(cfg),
}
configurableProviders := make(map[string]bool)
@ -60,6 +59,11 @@ func ProvideService(cfg *setting.Cfg, sqlStore db.DB, ac ac.AccessControl,
}
providersList := ssosettings.AllOAuthProviders
if features.IsEnabledGlobally(featuremgmt.FlagSsoSettingsLDAP) {
providersList = append(providersList, social.LDAPProviderName)
}
if licensing.FeatureEnabled(social.SAMLProviderName) {
fbStrategies = append(fbStrategies, strategies.NewSAMLStrategy(settingsProvider))

View File

@ -22,7 +22,6 @@ import (
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/ldap/service"
"github.com/grafana/grafana/pkg/services/licensing/licensingtest"
secretsFakes "github.com/grafana/grafana/pkg/services/secrets/fakes"
"github.com/grafana/grafana/pkg/services/ssosettings"
@ -1648,7 +1647,6 @@ func setupTestEnv(t *testing.T, isLicensingEnabled, keepFallbackStratergies, sam
prometheus.NewRegistry(),
&setting.OSSImpl{Cfg: cfg},
licensing,
service.ProvideService(cfg),
)
// overriding values for exposed fields

View File

@ -6,22 +6,20 @@ import (
"encoding/json"
"github.com/grafana/grafana/pkg/login/social"
"github.com/grafana/grafana/pkg/services/ldap/service"
"github.com/grafana/grafana/pkg/services/ldap"
"github.com/grafana/grafana/pkg/services/ssosettings"
"github.com/grafana/grafana/pkg/setting"
)
type LDAPStrategy struct {
cfg *setting.Cfg
ldap service.LDAP
cfg *setting.Cfg
}
var _ ssosettings.FallbackStrategy = (*LDAPStrategy)(nil)
func NewLDAPStrategy(cfg *setting.Cfg, ldap service.LDAP) *LDAPStrategy {
func NewLDAPStrategy(cfg *setting.Cfg) *LDAPStrategy {
return &LDAPStrategy{
cfg: cfg,
ldap: ldap,
cfg: cfg,
}
}
@ -52,8 +50,13 @@ func (s *LDAPStrategy) GetProviderConfig(_ context.Context, _ string) (map[strin
func (s *LDAPStrategy) getLDAPConfig() (map[string]any, error) {
var configMap map[string]any
config := s.ldap.Config()
configJson, err := json.Marshal(config)
config := ldap.GetLDAPConfig(s.cfg)
ldapConfig, err := ldap.GetConfig(config)
if err != nil {
return nil, err
}
configJson, err := json.Marshal(ldapConfig)
if err != nil {
return nil, err
}

View File

@ -7,7 +7,6 @@ import (
"github.com/stretchr/testify/require"
"gopkg.in/ini.v1"
"github.com/grafana/grafana/pkg/services/ldap/service"
"github.com/grafana/grafana/pkg/setting"
)
@ -29,8 +28,8 @@ var (
"config": map[string]interface{}{
"servers": []interface{}{
map[string]interface{}{
"Host": "127.0.0.1",
"Port": int64(3389),
"host": "127.0.0.1",
"port": int64(3389),
"attributes": map[string]interface{}{
"email": "mail",
"member_of": "memberOf",
@ -76,9 +75,7 @@ func TestGetLDAPConfig(t *testing.T) {
cfg, err := setting.NewCfgFromINIFile(iniFile)
require.NoError(t, err)
ldap := service.ProvideService(cfg)
strategy := NewLDAPStrategy(cfg, ldap)
strategy := NewLDAPStrategy(cfg)
result, err := strategy.GetProviderConfig(context.Background(), "ldap")
require.NoError(t, err)