LDAP Refactoring to support syncronizing more than one user at a time. (#16705)

* Feature: add cron setting for the ldap settings

* Move ldap configuration read to special function

* Introduce cron setting (no docs for it yet, pending approval)

* Chore: duplicate ldap module as a service

* Feature: implement active sync

This is very early preliminary implementation of active sync.
There is only one thing that's going right for this code - it works.

Aside from that, there is no tests, error handling, docs, transactions,
it's very much duplicative and etc.

But this is the overall direction with architecture I'm going for

* Chore: introduce login service

* Chore: gradually switch to ldap service

* Chore: use new approach for auth_proxy

* Chore: use new approach along with refactoring

* Chore: use new ldap interface for auth_proxy

* Chore: improve auth_proxy and subsequently ldap

* Chore: more of the refactoring bits

* Chore: address comments from code review

* Chore: more refactoring stuff

* Chore: make linter happy

* Chore: add cron dep for grafana enterprise

* Chore: initialize config package var

* Chore: disable gosec for now

* Chore: update dependencies

* Chore: remove unused module

* Chore: address review comments

* Chore: make linter happy
This commit is contained in:
Oleg Gaidarenko
2019-04-26 15:47:16 +03:00
committed by GitHub
parent a8326e3e93
commit 62b85a886e
33 changed files with 2210 additions and 878 deletions
+22 -13
View File
@@ -10,8 +10,8 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/remotecache"
"github.com/grafana/grafana/pkg/login"
models "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/ldap"
"github.com/grafana/grafana/pkg/setting"
)
@@ -21,6 +21,11 @@ const (
CachePrefix = "auth-proxy-sync-ttl:%s"
)
var (
readLDAPConfig = ldap.ReadConfig
isLDAPEnabled = ldap.IsEnabled
)
// AuthProxy struct
type AuthProxy struct {
store *remotecache.RemoteCache
@@ -28,14 +33,13 @@ type AuthProxy struct {
orgID int64
header string
LDAP func(server *login.LdapServerConf) login.ILdapAuther
LDAP func(server *ldap.ServerConfig) ldap.IAuth
enabled bool
whitelistIP string
headerType string
headers map[string]string
cacheTTL int
ldapEnabled bool
}
// Error auth proxy specific error
@@ -74,14 +78,13 @@ func New(options *Options) *AuthProxy {
orgID: options.OrgID,
header: header,
LDAP: login.NewLdapAuthenticator,
LDAP: ldap.New,
enabled: setting.AuthProxyEnabled,
headerType: setting.AuthProxyHeaderProperty,
headers: setting.AuthProxyHeaders,
whitelistIP: setting.AuthProxyWhitelist,
cacheTTL: setting.AuthProxyLdapSyncTtl,
ldapEnabled: setting.LdapEnabled,
}
}
@@ -167,11 +170,14 @@ func (auth *AuthProxy) GetUserID() (int64, *Error) {
return id, nil
}
if auth.ldapEnabled {
if isLDAPEnabled() {
id, err := auth.GetUserIDViaLDAP()
if err == login.ErrInvalidCredentials {
return 0, newError("Proxy authentication required", login.ErrInvalidCredentials)
if err == ldap.ErrInvalidCredentials {
return 0, newError(
"Proxy authentication required",
ldap.ErrInvalidCredentials,
)
}
if err != nil {
@@ -183,7 +189,10 @@ func (auth *AuthProxy) GetUserID() (int64, *Error) {
id, err := auth.GetUserIDViaHeader()
if err != nil {
return 0, newError("Failed to login as user specified in auth proxy header", err)
return 0, newError(
"Failed to login as user specified in auth proxy header",
err,
)
}
return id, nil
@@ -210,12 +219,12 @@ func (auth *AuthProxy) GetUserIDViaLDAP() (int64, *Error) {
Username: auth.header,
}
ldapCfg := login.LdapCfg
if len(ldapCfg.Servers) < 1 {
config := readLDAPConfig()
if len(config.Servers) == 0 {
return 0, newError("No LDAP servers available", nil)
}
for _, server := range ldapCfg.Servers {
for _, server := range config.Servers {
author := auth.LDAP(server)
if err := author.SyncUser(query); err != nil {
return 0, newError(err.Error(), nil)
+39 -13
View File
@@ -5,16 +5,17 @@ import (
"net/http"
"testing"
"github.com/grafana/grafana/pkg/infra/remotecache"
"github.com/grafana/grafana/pkg/login"
models "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
. "github.com/smartystreets/goconvey/convey"
"gopkg.in/macaron.v1"
"github.com/grafana/grafana/pkg/infra/remotecache"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/ldap"
"github.com/grafana/grafana/pkg/setting"
)
type TestLDAP struct {
login.ILdapAuther
ldap.Auth
ID int64
syncCalled bool
}
@@ -62,13 +63,23 @@ func TestMiddlewareContext(t *testing.T) {
Convey("LDAP", func() {
Convey("gets data from the LDAP", func() {
login.LdapCfg = login.LdapConfig{
Servers: []*login.LdapServerConf{
{},
},
isLDAPEnabled = func() bool {
return true
}
setting.LdapEnabled = true
readLDAPConfig = func() *ldap.Config {
config := &ldap.Config{
Servers: []*ldap.ServerConfig{
{},
},
}
return config
}
defer func() {
isLDAPEnabled = ldap.IsEnabled
readLDAPConfig = ldap.ReadConfig
}()
store := remotecache.NewFakeStore(t)
@@ -82,7 +93,7 @@ func TestMiddlewareContext(t *testing.T) {
ID: 42,
}
auth.LDAP = func(server *login.LdapServerConf) login.ILdapAuther {
auth.LDAP = func(server *ldap.ServerConfig) ldap.IAuth {
return stub
}
@@ -94,7 +105,21 @@ func TestMiddlewareContext(t *testing.T) {
})
Convey("gets nice error if ldap is enabled but not configured", func() {
setting.LdapEnabled = false
isLDAPEnabled = func() bool {
return true
}
readLDAPConfig = func() *ldap.Config {
config := &ldap.Config{
Servers: []*ldap.ServerConfig{},
}
return config
}
defer func() {
isLDAPEnabled = ldap.IsEnabled
readLDAPConfig = ldap.ReadConfig
}()
store := remotecache.NewFakeStore(t)
@@ -108,13 +133,14 @@ func TestMiddlewareContext(t *testing.T) {
ID: 42,
}
auth.LDAP = func(server *login.LdapServerConf) login.ILdapAuther {
auth.LDAP = func(server *ldap.ServerConfig) ldap.IAuth {
return stub
}
id, err := auth.GetUserID()
So(err, ShouldNotBeNil)
So(err.Error(), ShouldContainSubstring, "Failed to sync user")
So(id, ShouldNotEqual, 42)
So(stub.syncCalled, ShouldEqual, false)
})