mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
* Move the ReloadLDAPCfg function to the debug file Appears to be a better suite place for this. * LDAP: Return the server information when we find a specific user We allow you to specify multiple LDAP servers as part of LDAP authentication integration. As part of searching for specific users, we need to understand from which server they come from. Returning the server configuration as part of the search will help us do two things: - Understand in which server we found the user - Have access the groups specified as part of the server configuration * LDAP: Adds the /api/admin/ldap/:username endpoint This endpoint returns a user found within the configured LDAP server(s). Moreso, it provides the mapping information for the user to help administrators understand how the users would be created within Grafana based on the current configuration. No changes are executed or saved to the database, this is all an in-memory representation of how the final result would look like.
202 lines
4.5 KiB
Go
202 lines
4.5 KiB
Go
package authproxy
|
|
|
|
import (
|
|
"encoding/base32"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"testing"
|
|
|
|
"github.com/grafana/grafana/pkg/bus"
|
|
"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/services/multildap"
|
|
"github.com/grafana/grafana/pkg/setting"
|
|
. "github.com/smartystreets/goconvey/convey"
|
|
"gopkg.in/macaron.v1"
|
|
)
|
|
|
|
type TestMultiLDAP struct {
|
|
multildap.MultiLDAP
|
|
ID int64
|
|
userCalled bool
|
|
loginCalled bool
|
|
}
|
|
|
|
func (stub *TestMultiLDAP) Login(query *models.LoginUserQuery) (
|
|
*models.ExternalUserInfo, error,
|
|
) {
|
|
stub.loginCalled = true
|
|
result := &models.ExternalUserInfo{
|
|
UserId: stub.ID,
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func (stub *TestMultiLDAP) User(login string) (
|
|
*models.ExternalUserInfo,
|
|
ldap.ServerConfig,
|
|
error,
|
|
) {
|
|
stub.userCalled = true
|
|
result := &models.ExternalUserInfo{
|
|
UserId: stub.ID,
|
|
}
|
|
return result, ldap.ServerConfig{}, nil
|
|
}
|
|
|
|
func prepareMiddleware(t *testing.T, req *http.Request, store *remotecache.RemoteCache) *AuthProxy {
|
|
t.Helper()
|
|
|
|
ctx := &models.ReqContext{
|
|
Context: &macaron.Context{
|
|
Req: macaron.Request{
|
|
Request: req,
|
|
},
|
|
},
|
|
}
|
|
|
|
auth := New(&Options{
|
|
Store: store,
|
|
Ctx: ctx,
|
|
OrgID: 4,
|
|
})
|
|
|
|
return auth
|
|
}
|
|
|
|
func TestMiddlewareContext(t *testing.T) {
|
|
Convey("auth_proxy helper", t, func() {
|
|
req, _ := http.NewRequest("POST", "http://example.com", nil)
|
|
setting.AuthProxyHeaderName = "X-Killa"
|
|
store := remotecache.NewFakeStore(t)
|
|
|
|
name := "markelog"
|
|
req.Header.Add(setting.AuthProxyHeaderName, name)
|
|
|
|
Convey("when the cache only contains the main header", func() {
|
|
|
|
Convey("with a simple cache key", func() {
|
|
// Set cache key
|
|
key := fmt.Sprintf(CachePrefix, base32.StdEncoding.EncodeToString([]byte(name)))
|
|
store.Set(key, int64(33), 0)
|
|
|
|
// Set up the middleware
|
|
auth := prepareMiddleware(t, req, store)
|
|
id, err := auth.Login()
|
|
|
|
So(auth.getKey(), ShouldEqual, "auth-proxy-sync-ttl:NVQXE23FNRXWO===")
|
|
So(err, ShouldBeNil)
|
|
So(id, ShouldEqual, 33)
|
|
})
|
|
|
|
Convey("when the cache key contains additional headers", func() {
|
|
setting.AuthProxyHeaders = map[string]string{"Groups": "X-WEBAUTH-GROUPS"}
|
|
group := "grafana-core-team"
|
|
req.Header.Add("X-WEBAUTH-GROUPS", group)
|
|
|
|
key := fmt.Sprintf(CachePrefix, base32.StdEncoding.EncodeToString([]byte(name+"-"+group)))
|
|
store.Set(key, int64(33), 0)
|
|
|
|
auth := prepareMiddleware(t, req, store)
|
|
|
|
id, err := auth.Login()
|
|
|
|
So(auth.getKey(), ShouldEqual, "auth-proxy-sync-ttl:NVQXE23FNRXWOLLHOJQWMYLOMEWWG33SMUWXIZLBNU======")
|
|
So(err, ShouldBeNil)
|
|
So(id, ShouldEqual, 33)
|
|
})
|
|
|
|
Convey("when the does not exist", func() {
|
|
})
|
|
})
|
|
|
|
Convey("LDAP", func() {
|
|
Convey("logs in via LDAP", func() {
|
|
bus.AddHandler("test", func(cmd *models.UpsertUserCommand) error {
|
|
cmd.Result = &models.User{
|
|
Id: 42,
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
isLDAPEnabled = func() bool {
|
|
return true
|
|
}
|
|
|
|
stub := &TestMultiLDAP{
|
|
ID: 42,
|
|
}
|
|
|
|
getLDAPConfig = func() (*ldap.Config, error) {
|
|
config := &ldap.Config{
|
|
Servers: []*ldap.ServerConfig{
|
|
{
|
|
SearchBaseDNs: []string{"BaseDNHere"},
|
|
},
|
|
},
|
|
}
|
|
return config, nil
|
|
}
|
|
|
|
newLDAP = func(servers []*ldap.ServerConfig) multildap.IMultiLDAP {
|
|
return stub
|
|
}
|
|
|
|
defer func() {
|
|
newLDAP = multildap.New
|
|
isLDAPEnabled = ldap.IsEnabled
|
|
getLDAPConfig = ldap.GetConfig
|
|
}()
|
|
|
|
store := remotecache.NewFakeStore(t)
|
|
|
|
auth := prepareMiddleware(t, req, store)
|
|
|
|
id, err := auth.Login()
|
|
|
|
So(err, ShouldBeNil)
|
|
So(id, ShouldEqual, 42)
|
|
So(stub.userCalled, ShouldEqual, true)
|
|
})
|
|
|
|
Convey("gets nice error if ldap is enabled but not configured", func() {
|
|
isLDAPEnabled = func() bool {
|
|
return true
|
|
}
|
|
|
|
getLDAPConfig = func() (*ldap.Config, error) {
|
|
return nil, errors.New("Something went wrong")
|
|
}
|
|
|
|
defer func() {
|
|
newLDAP = multildap.New
|
|
isLDAPEnabled = ldap.IsEnabled
|
|
getLDAPConfig = ldap.GetConfig
|
|
}()
|
|
|
|
store := remotecache.NewFakeStore(t)
|
|
|
|
auth := prepareMiddleware(t, req, store)
|
|
|
|
stub := &TestMultiLDAP{
|
|
ID: 42,
|
|
}
|
|
|
|
newLDAP = func(servers []*ldap.ServerConfig) multildap.IMultiLDAP {
|
|
return stub
|
|
}
|
|
|
|
id, err := auth.Login()
|
|
|
|
So(err, ShouldNotBeNil)
|
|
So(err.Error(), ShouldContainSubstring, "Failed to get the user")
|
|
So(id, ShouldNotEqual, 42)
|
|
So(stub.loginCalled, ShouldEqual, false)
|
|
})
|
|
})
|
|
})
|
|
}
|