mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -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;
|
pinNavItems?: boolean;
|
||||||
authZGRPCServer?: boolean;
|
authZGRPCServer?: boolean;
|
||||||
openSearchBackendFlowEnabled?: boolean;
|
openSearchBackendFlowEnabled?: boolean;
|
||||||
|
ssoSettingsLDAP?: boolean;
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,6 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/login/social/connectors"
|
"github.com/grafana/grafana/pkg/login/social/connectors"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
|
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
|
||||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||||
ldap "github.com/grafana/grafana/pkg/services/ldap/service"
|
|
||||||
"github.com/grafana/grafana/pkg/services/licensing"
|
"github.com/grafana/grafana/pkg/services/licensing"
|
||||||
secretsfake "github.com/grafana/grafana/pkg/services/secrets/fakes"
|
secretsfake "github.com/grafana/grafana/pkg/services/secrets/fakes"
|
||||||
"github.com/grafana/grafana/pkg/services/ssosettings/ssosettingsimpl"
|
"github.com/grafana/grafana/pkg/services/ssosettings/ssosettingsimpl"
|
||||||
@ -82,7 +81,6 @@ func TestSocialService_ProvideService(t *testing.T) {
|
|||||||
nil,
|
nil,
|
||||||
&setting.OSSImpl{Cfg: cfg},
|
&setting.OSSImpl{Cfg: cfg},
|
||||||
&licensing.OSSLicensingService{},
|
&licensing.OSSLicensingService{},
|
||||||
ldap.ProvideService(cfg),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
@ -195,7 +193,6 @@ func TestSocialService_ProvideService_GrafanaComGrafanaNet(t *testing.T) {
|
|||||||
nil,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
&licensing.OSSLicensingService{},
|
&licensing.OSSLicensingService{},
|
||||||
ldap.ProvideService(cfg),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
|
@ -1323,6 +1323,14 @@ var (
|
|||||||
Stage: FeatureStagePublicPreview,
|
Stage: FeatureStagePublicPreview,
|
||||||
Owner: awsDatasourcesSquad,
|
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
|
pinNavItems,experimental,@grafana/grafana-frontend-platform,false,false,false
|
||||||
authZGRPCServer,experimental,@grafana/identity-access-team,false,false,false
|
authZGRPCServer,experimental,@grafana/identity-access-team,false,false,false
|
||||||
openSearchBackendFlowEnabled,preview,@grafana/aws-datasources,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
|
// FlagOpenSearchBackendFlowEnabled
|
||||||
// Enables the backend query flow for Open Search datasource plugin
|
// Enables the backend query flow for Open Search datasource plugin
|
||||||
FlagOpenSearchBackendFlowEnabled = "openSearchBackendFlowEnabled"
|
FlagOpenSearchBackendFlowEnabled = "openSearchBackendFlowEnabled"
|
||||||
|
|
||||||
|
// FlagSsoSettingsLDAP
|
||||||
|
// Use the new SSO Settings API to configure LDAP
|
||||||
|
FlagSsoSettingsLDAP = "ssoSettingsLDAP"
|
||||||
)
|
)
|
||||||
|
@ -2126,6 +2126,20 @@
|
|||||||
"allowSelfServe": true
|
"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": {
|
"metadata": {
|
||||||
"name": "ssoSettingsSAML",
|
"name": "ssoSettingsSAML",
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/BurntSushi/toml"
|
"github.com/BurntSushi/toml"
|
||||||
|
|
||||||
@ -85,3 +87,27 @@ func assertNotEmptyCfg(val any, propName string) error {
|
|||||||
}
|
}
|
||||||
return nil
|
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
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"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"
|
||||||
"github.com/grafana/grafana/pkg/services/ldap/multildap"
|
"github.com/grafana/grafana/pkg/services/ldap/multildap"
|
||||||
"github.com/grafana/grafana/pkg/services/login"
|
"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"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -33,20 +39,37 @@ type LDAPImpl struct {
|
|||||||
cfg *ldap.Config
|
cfg *ldap.Config
|
||||||
ldapCfg *ldap.ServersConfig
|
ldapCfg *ldap.ServersConfig
|
||||||
log log.Logger
|
log log.Logger
|
||||||
|
features featuremgmt.FeatureToggles
|
||||||
|
ssoSettings ssosettings.Service
|
||||||
|
|
||||||
// loadingMutex locks the reading of the config so multiple requests for reloading are sequential.
|
// loadingMutex locks the reading of the config so multiple requests for reloading are sequential.
|
||||||
loadingMutex *sync.Mutex
|
loadingMutex *sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func ProvideService(cfg *setting.Cfg) *LDAPImpl {
|
func ProvideService(cfg *setting.Cfg, features featuremgmt.FeatureToggles, ssoSettings ssosettings.Service) *LDAPImpl {
|
||||||
s := &LDAPImpl{
|
s := &LDAPImpl{
|
||||||
client: nil,
|
|
||||||
ldapCfg: nil,
|
|
||||||
cfg: ldap.GetLDAPConfig(cfg),
|
|
||||||
log: log.New("ldap.service"),
|
log: log.New("ldap.service"),
|
||||||
loadingMutex: &sync.Mutex{},
|
loadingMutex: &sync.Mutex{},
|
||||||
|
features: features,
|
||||||
|
ssoSettings: ssoSettings,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.features.IsEnabledGlobally(featuremgmt.FlagSsoSettingsApi) && s.features.IsEnabledGlobally(featuremgmt.FlagSsoSettingsLDAP) {
|
||||||
|
s.ssoSettings.RegisterReloadable(social.LDAPProviderName, s)
|
||||||
|
|
||||||
|
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.cfg = ldap.GetLDAPConfig(cfg)
|
||||||
if !cfg.LDAPAuthEnabled {
|
if !cfg.LDAPAuthEnabled {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
@ -58,10 +81,36 @@ func ProvideService(cfg *setting.Cfg) *LDAPImpl {
|
|||||||
s.ldapCfg = ldapCfg
|
s.ldapCfg = ldapCfg
|
||||||
s.client = multildap.New(s.ldapCfg.Servers, s.cfg)
|
s.client = multildap.New(s.ldapCfg.Servers, s.cfg)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return s
|
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 {
|
func (s *LDAPImpl) ReloadConfig() error {
|
||||||
if !s.cfg.Enabled {
|
if !s.cfg.Enabled {
|
||||||
return nil
|
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
|
// ServerConfig holds connection data to LDAP
|
||||||
type ServerConfig struct {
|
type ServerConfig struct {
|
||||||
Host string `toml:"host"`
|
Host string `toml:"host" json:"host"`
|
||||||
Port int `toml:"port"`
|
Port int `toml:"port" json:"port,omitempty"`
|
||||||
|
|
||||||
UseSSL bool `toml:"use_ssl" json:"use_ssl,omitempty"`
|
UseSSL bool `toml:"use_ssl" json:"use_ssl,omitempty"`
|
||||||
StartTLS bool `toml:"start_tls" json:"start_tls,omitempty"`
|
StartTLS bool `toml:"start_tls" json:"start_tls,omitempty"`
|
||||||
|
@ -18,7 +18,6 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/login/social"
|
"github.com/grafana/grafana/pkg/login/social"
|
||||||
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
"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/licensing"
|
||||||
"github.com/grafana/grafana/pkg/services/secrets"
|
"github.com/grafana/grafana/pkg/services/secrets"
|
||||||
"github.com/grafana/grafana/pkg/services/ssosettings"
|
"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,
|
func ProvideService(cfg *setting.Cfg, sqlStore db.DB, ac ac.AccessControl,
|
||||||
routeRegister routing.RouteRegister, features featuremgmt.FeatureToggles,
|
routeRegister routing.RouteRegister, features featuremgmt.FeatureToggles,
|
||||||
secrets secrets.Service, usageStats usagestats.Service, registerer prometheus.Registerer,
|
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{
|
fbStrategies := []ssosettings.FallbackStrategy{
|
||||||
strategies.NewOAuthStrategy(cfg),
|
strategies.NewOAuthStrategy(cfg),
|
||||||
strategies.NewLDAPStrategy(cfg, ldap),
|
strategies.NewLDAPStrategy(cfg),
|
||||||
}
|
}
|
||||||
|
|
||||||
configurableProviders := make(map[string]bool)
|
configurableProviders := make(map[string]bool)
|
||||||
@ -60,6 +59,11 @@ func ProvideService(cfg *setting.Cfg, sqlStore db.DB, ac ac.AccessControl,
|
|||||||
}
|
}
|
||||||
|
|
||||||
providersList := ssosettings.AllOAuthProviders
|
providersList := ssosettings.AllOAuthProviders
|
||||||
|
|
||||||
|
if features.IsEnabledGlobally(featuremgmt.FlagSsoSettingsLDAP) {
|
||||||
|
providersList = append(providersList, social.LDAPProviderName)
|
||||||
|
}
|
||||||
|
|
||||||
if licensing.FeatureEnabled(social.SAMLProviderName) {
|
if licensing.FeatureEnabled(social.SAMLProviderName) {
|
||||||
fbStrategies = append(fbStrategies, strategies.NewSAMLStrategy(settingsProvider))
|
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"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
|
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
|
||||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||||
"github.com/grafana/grafana/pkg/services/ldap/service"
|
|
||||||
"github.com/grafana/grafana/pkg/services/licensing/licensingtest"
|
"github.com/grafana/grafana/pkg/services/licensing/licensingtest"
|
||||||
secretsFakes "github.com/grafana/grafana/pkg/services/secrets/fakes"
|
secretsFakes "github.com/grafana/grafana/pkg/services/secrets/fakes"
|
||||||
"github.com/grafana/grafana/pkg/services/ssosettings"
|
"github.com/grafana/grafana/pkg/services/ssosettings"
|
||||||
@ -1648,7 +1647,6 @@ func setupTestEnv(t *testing.T, isLicensingEnabled, keepFallbackStratergies, sam
|
|||||||
prometheus.NewRegistry(),
|
prometheus.NewRegistry(),
|
||||||
&setting.OSSImpl{Cfg: cfg},
|
&setting.OSSImpl{Cfg: cfg},
|
||||||
licensing,
|
licensing,
|
||||||
service.ProvideService(cfg),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// overriding values for exposed fields
|
// overriding values for exposed fields
|
||||||
|
@ -6,22 +6,20 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/login/social"
|
"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/services/ssosettings"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
type LDAPStrategy struct {
|
type LDAPStrategy struct {
|
||||||
cfg *setting.Cfg
|
cfg *setting.Cfg
|
||||||
ldap service.LDAP
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ ssosettings.FallbackStrategy = (*LDAPStrategy)(nil)
|
var _ ssosettings.FallbackStrategy = (*LDAPStrategy)(nil)
|
||||||
|
|
||||||
func NewLDAPStrategy(cfg *setting.Cfg, ldap service.LDAP) *LDAPStrategy {
|
func NewLDAPStrategy(cfg *setting.Cfg) *LDAPStrategy {
|
||||||
return &LDAPStrategy{
|
return &LDAPStrategy{
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
ldap: ldap,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,8 +50,13 @@ func (s *LDAPStrategy) GetProviderConfig(_ context.Context, _ string) (map[strin
|
|||||||
func (s *LDAPStrategy) getLDAPConfig() (map[string]any, error) {
|
func (s *LDAPStrategy) getLDAPConfig() (map[string]any, error) {
|
||||||
var configMap map[string]any
|
var configMap map[string]any
|
||||||
|
|
||||||
config := s.ldap.Config()
|
config := ldap.GetLDAPConfig(s.cfg)
|
||||||
configJson, err := json.Marshal(config)
|
ldapConfig, err := ldap.GetConfig(config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
configJson, err := json.Marshal(ldapConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"gopkg.in/ini.v1"
|
"gopkg.in/ini.v1"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/services/ldap/service"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -29,8 +28,8 @@ var (
|
|||||||
"config": map[string]interface{}{
|
"config": map[string]interface{}{
|
||||||
"servers": []interface{}{
|
"servers": []interface{}{
|
||||||
map[string]interface{}{
|
map[string]interface{}{
|
||||||
"Host": "127.0.0.1",
|
"host": "127.0.0.1",
|
||||||
"Port": int64(3389),
|
"port": int64(3389),
|
||||||
"attributes": map[string]interface{}{
|
"attributes": map[string]interface{}{
|
||||||
"email": "mail",
|
"email": "mail",
|
||||||
"member_of": "memberOf",
|
"member_of": "memberOf",
|
||||||
@ -76,9 +75,7 @@ func TestGetLDAPConfig(t *testing.T) {
|
|||||||
cfg, err := setting.NewCfgFromINIFile(iniFile)
|
cfg, err := setting.NewCfgFromINIFile(iniFile)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
ldap := service.ProvideService(cfg)
|
strategy := NewLDAPStrategy(cfg)
|
||||||
|
|
||||||
strategy := NewLDAPStrategy(cfg, ldap)
|
|
||||||
|
|
||||||
result, err := strategy.GetProviderConfig(context.Background(), "ldap")
|
result, err := strategy.GetProviderConfig(context.Background(), "ldap")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
Loading…
Reference in New Issue
Block a user