mirror of
https://github.com/grafana/grafana.git
synced 2025-01-15 19:22:34 -06:00
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:
parent
0afbaa39df
commit
d074cc7892
@ -195,4 +195,5 @@ export interface FeatureToggles {
|
||||
pinNavItems?: boolean;
|
||||
authZGRPCServer?: boolean;
|
||||
openSearchBackendFlowEnabled?: boolean;
|
||||
ssoSettingsLDAP?: boolean;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -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
|
||||
|
|
@ -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"
|
||||
)
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
299
pkg/services/ldap/service/ldap_test.go
Normal file
299
pkg/services/ldap/service/ldap_test.go
Normal 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)
|
||||
})
|
||||
}
|
||||
}
|
@ -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"`
|
||||
|
@ -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))
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user