Skip to content

Commit d074cc7

Browse files
authored
Auth: Implement reload function for LDAP (grafana#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
1 parent 0afbaa3 commit d074cc7

File tree

14 files changed

+441
-40
lines changed

14 files changed

+441
-40
lines changed

packages/grafana-data/src/types/featureToggles.gen.ts

+1
Original file line numberDiff line numberDiff line change
@@ -195,4 +195,5 @@ export interface FeatureToggles {
195195
pinNavItems?: boolean;
196196
authZGRPCServer?: boolean;
197197
openSearchBackendFlowEnabled?: boolean;
198+
ssoSettingsLDAP?: boolean;
198199
}

pkg/login/social/socialimpl/service_test.go

-3
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import (
1414
"github.com/grafana/grafana/pkg/login/social/connectors"
1515
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
1616
"github.com/grafana/grafana/pkg/services/featuremgmt"
17-
ldap "github.com/grafana/grafana/pkg/services/ldap/service"
1817
"github.com/grafana/grafana/pkg/services/licensing"
1918
secretsfake "github.com/grafana/grafana/pkg/services/secrets/fakes"
2019
"github.com/grafana/grafana/pkg/services/ssosettings/ssosettingsimpl"
@@ -82,7 +81,6 @@ func TestSocialService_ProvideService(t *testing.T) {
8281
nil,
8382
&setting.OSSImpl{Cfg: cfg},
8483
&licensing.OSSLicensingService{},
85-
ldap.ProvideService(cfg),
8684
)
8785

8886
for _, tc := range testCases {
@@ -195,7 +193,6 @@ func TestSocialService_ProvideService_GrafanaComGrafanaNet(t *testing.T) {
195193
nil,
196194
nil,
197195
&licensing.OSSLicensingService{},
198-
ldap.ProvideService(cfg),
199196
)
200197

201198
for _, tc := range testCases {

pkg/services/featuremgmt/registry.go

+8
Original file line numberDiff line numberDiff line change
@@ -1323,6 +1323,14 @@ var (
13231323
Stage: FeatureStagePublicPreview,
13241324
Owner: awsDatasourcesSquad,
13251325
},
1326+
{
1327+
Name: "ssoSettingsLDAP",
1328+
Description: "Use the new SSO Settings API to configure LDAP",
1329+
Stage: FeatureStageExperimental,
1330+
Owner: identityAccessTeam,
1331+
HideFromDocs: true,
1332+
HideFromAdminPage: true,
1333+
},
13261334
}
13271335
)
13281336

pkg/services/featuremgmt/toggles_gen.csv

+1
Original file line numberDiff line numberDiff line change
@@ -176,3 +176,4 @@ azureMonitorPrometheusExemplars,experimental,@grafana/partner-datasources,false,
176176
pinNavItems,experimental,@grafana/grafana-frontend-platform,false,false,false
177177
authZGRPCServer,experimental,@grafana/identity-access-team,false,false,false
178178
openSearchBackendFlowEnabled,preview,@grafana/aws-datasources,false,false,false
179+
ssoSettingsLDAP,experimental,@grafana/identity-access-team,false,false,false

pkg/services/featuremgmt/toggles_gen.go

+4
Original file line numberDiff line numberDiff line change
@@ -714,4 +714,8 @@ const (
714714
// FlagOpenSearchBackendFlowEnabled
715715
// Enables the backend query flow for Open Search datasource plugin
716716
FlagOpenSearchBackendFlowEnabled = "openSearchBackendFlowEnabled"
717+
718+
// FlagSsoSettingsLDAP
719+
// Use the new SSO Settings API to configure LDAP
720+
FlagSsoSettingsLDAP = "ssoSettingsLDAP"
717721
)

pkg/services/featuremgmt/toggles_gen.json

+14
Original file line numberDiff line numberDiff line change
@@ -2126,6 +2126,20 @@
21262126
"allowSelfServe": true
21272127
}
21282128
},
2129+
{
2130+
"metadata": {
2131+
"name": "ssoSettingsLDAP",
2132+
"resourceVersion": "1718624569822",
2133+
"creationTimestamp": "2024-06-17T11:42:49Z"
2134+
},
2135+
"spec": {
2136+
"description": "Use the new SSO Settings API to configure LDAP",
2137+
"stage": "experimental",
2138+
"codeowner": "@grafana/identity-access-team",
2139+
"hideFromAdminPage": true,
2140+
"hideFromDocs": true
2141+
}
2142+
},
21292143
{
21302144
"metadata": {
21312145
"name": "ssoSettingsSAML",

pkg/services/ldap/service/helpers.go

+26
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package service
22

33
import (
4+
"encoding/json"
45
"fmt"
56
"os"
7+
"strconv"
68

79
"github.com/BurntSushi/toml"
810

@@ -85,3 +87,27 @@ func assertNotEmptyCfg(val any, propName string) error {
8587
}
8688
return nil
8789
}
90+
91+
func resolveBool(input any, defaultValue bool) bool {
92+
strInput := fmt.Sprintf("%v", input)
93+
result, err := strconv.ParseBool(strInput)
94+
if err != nil {
95+
return defaultValue
96+
}
97+
return result
98+
}
99+
100+
func resolveServerConfig(input any) (*ldap.ServersConfig, error) {
101+
var ldapCfg ldap.ServersConfig
102+
103+
inputJson, err := json.Marshal(input)
104+
if err != nil {
105+
return nil, err
106+
}
107+
108+
if err = json.Unmarshal(inputJson, &ldapCfg); err != nil {
109+
return nil, err
110+
}
111+
112+
return &ldapCfg, nil
113+
}

pkg/services/ldap/service/ldap.go

+65-16
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
package service
22

33
import (
4+
"context"
45
"errors"
56
"sync"
67

8+
"github.com/grafana/grafana/pkg/apimachinery/identity"
79
"github.com/grafana/grafana/pkg/infra/log"
10+
"github.com/grafana/grafana/pkg/login/social"
11+
"github.com/grafana/grafana/pkg/services/featuremgmt"
812
"github.com/grafana/grafana/pkg/services/ldap"
913
"github.com/grafana/grafana/pkg/services/ldap/multildap"
1014
"github.com/grafana/grafana/pkg/services/login"
15+
"github.com/grafana/grafana/pkg/services/ssosettings"
16+
"github.com/grafana/grafana/pkg/services/ssosettings/models"
1117
"github.com/grafana/grafana/pkg/setting"
1218
)
1319

@@ -29,39 +35,82 @@ type LDAP interface {
2935
}
3036

3137
type LDAPImpl struct {
32-
client multildap.IMultiLDAP
33-
cfg *ldap.Config
34-
ldapCfg *ldap.ServersConfig
35-
log log.Logger
38+
client multildap.IMultiLDAP
39+
cfg *ldap.Config
40+
ldapCfg *ldap.ServersConfig
41+
log log.Logger
42+
features featuremgmt.FeatureToggles
43+
ssoSettings ssosettings.Service
3644

3745
// loadingMutex locks the reading of the config so multiple requests for reloading are sequential.
3846
loadingMutex *sync.Mutex
3947
}
4048

41-
func ProvideService(cfg *setting.Cfg) *LDAPImpl {
49+
func ProvideService(cfg *setting.Cfg, features featuremgmt.FeatureToggles, ssoSettings ssosettings.Service) *LDAPImpl {
4250
s := &LDAPImpl{
43-
client: nil,
44-
ldapCfg: nil,
45-
cfg: ldap.GetLDAPConfig(cfg),
4651
log: log.New("ldap.service"),
4752
loadingMutex: &sync.Mutex{},
53+
features: features,
54+
ssoSettings: ssoSettings,
4855
}
4956

50-
if !cfg.LDAPAuthEnabled {
51-
return s
52-
}
57+
if s.features.IsEnabledGlobally(featuremgmt.FlagSsoSettingsApi) && s.features.IsEnabledGlobally(featuremgmt.FlagSsoSettingsLDAP) {
58+
s.ssoSettings.RegisterReloadable(social.LDAPProviderName, s)
5359

54-
ldapCfg, err := multildap.GetConfig(s.cfg)
55-
if err != nil {
56-
s.log.Error("Failed to get LDAP config", "error", err)
60+
ldapSettings, err := s.ssoSettings.GetForProvider(context.Background(), social.LDAPProviderName)
61+
if err != nil {
62+
s.log.Error("Failed to retrieve LDAP settings from SSO settings service", "error", err)
63+
return s
64+
}
65+
66+
err = s.Reload(context.Background(), *ldapSettings)
67+
if err != nil {
68+
s.log.Error("Failed to load LDAP settings", "error", err)
69+
return s
70+
}
5771
} else {
58-
s.ldapCfg = ldapCfg
59-
s.client = multildap.New(s.ldapCfg.Servers, s.cfg)
72+
s.cfg = ldap.GetLDAPConfig(cfg)
73+
if !cfg.LDAPAuthEnabled {
74+
return s
75+
}
76+
77+
ldapCfg, err := multildap.GetConfig(s.cfg)
78+
if err != nil {
79+
s.log.Error("Failed to get LDAP config", "error", err)
80+
} else {
81+
s.ldapCfg = ldapCfg
82+
s.client = multildap.New(s.ldapCfg.Servers, s.cfg)
83+
}
6084
}
6185

6286
return s
6387
}
6488

89+
func (s *LDAPImpl) Reload(ctx context.Context, settings models.SSOSettings) error {
90+
cfg := &ldap.Config{}
91+
cfg.Enabled = resolveBool(settings.Settings["enabled"], false)
92+
cfg.SkipOrgRoleSync = resolveBool(settings.Settings["skip_org_role_sync"], false)
93+
cfg.AllowSignUp = resolveBool(settings.Settings["allow_sign_up"], true)
94+
95+
ldapCfg, err := resolveServerConfig(settings.Settings["config"])
96+
if err != nil {
97+
return err
98+
}
99+
100+
s.loadingMutex.Lock()
101+
defer s.loadingMutex.Unlock()
102+
103+
s.cfg = cfg
104+
s.ldapCfg = ldapCfg
105+
s.client = multildap.New(s.ldapCfg.Servers, s.cfg)
106+
107+
return nil
108+
}
109+
110+
func (s *LDAPImpl) Validate(ctx context.Context, settings models.SSOSettings, oldSettings models.SSOSettings, requester identity.Requester) error {
111+
return nil
112+
}
113+
65114
func (s *LDAPImpl) ReloadConfig() error {
66115
if !s.cfg.Enabled {
67116
return nil

0 commit comments

Comments
 (0)