mirror of
https://github.com/grafana/grafana.git
synced 2025-01-15 19:22:34 -06:00
LDAP: Add API endpoint to debug user mapping from LDAP (#18833)
* 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.
This commit is contained in:
parent
09032cd766
commit
0cbbb43222
@ -1,17 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana/pkg/services/ldap"
|
||||
)
|
||||
|
||||
func (server *HTTPServer) ReloadLDAPCfg() Response {
|
||||
if !ldap.IsEnabled() {
|
||||
return Error(400, "LDAP is not enabled", nil)
|
||||
}
|
||||
|
||||
err := ldap.ReloadConfig()
|
||||
if err != nil {
|
||||
return Error(500, "Failed to reload ldap config.", err)
|
||||
}
|
||||
return Success("LDAP config reloaded")
|
||||
}
|
@ -395,6 +395,7 @@ func (hs *HTTPServer) registerRoutes() {
|
||||
adminRoute.Post("/provisioning/datasources/reload", Wrap(hs.AdminProvisioningReloadDatasources))
|
||||
adminRoute.Post("/provisioning/notifications/reload", Wrap(hs.AdminProvisioningReloadNotifications))
|
||||
adminRoute.Post("/ldap/reload", Wrap(hs.ReloadLDAPCfg))
|
||||
adminRoute.Get("/ldap/:username", Wrap(hs.GetUserFromLDAP))
|
||||
}, reqGrafanaAdmin)
|
||||
|
||||
// rendering
|
||||
|
183
pkg/api/ldap_debug.go
Normal file
183
pkg/api/ldap_debug.go
Normal file
@ -0,0 +1,183 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"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/util"
|
||||
)
|
||||
|
||||
var (
|
||||
getLDAPConfig = multildap.GetConfig
|
||||
newLDAP = multildap.New
|
||||
|
||||
errOrganizationNotFound = func(orgId int64) error {
|
||||
return fmt.Errorf("Unable to find organization with ID '%d'", orgId)
|
||||
}
|
||||
)
|
||||
|
||||
// LDAPAttribute is a serializer for user attributes mapped from LDAP. Is meant to display both the serialized value and the LDAP key we received it from.
|
||||
type LDAPAttribute struct {
|
||||
ConfigAttributeValue string `json:"cfgAttrValue"`
|
||||
LDAPAttributeValue string `json:"ldapValue"`
|
||||
}
|
||||
|
||||
// RoleDTO is a serializer for mapped roles from LDAP
|
||||
type RoleDTO struct {
|
||||
OrgId int64 `json:"orgId"`
|
||||
OrgName string `json:"orgName"`
|
||||
OrgRole models.RoleType `json:"orgRole"`
|
||||
GroupDN string `json:"groupDN"`
|
||||
}
|
||||
|
||||
// TeamDTO is a serializer for mapped Teams from LDAP
|
||||
type TeamDTO struct {
|
||||
GroupDN string `json:"groupDN"`
|
||||
TeamId int64 `json:"teamId"`
|
||||
TeamName string `json:"teamName"`
|
||||
}
|
||||
|
||||
// LDAPUserDTO is a serializer for users mapped from LDAP
|
||||
type LDAPUserDTO struct {
|
||||
Name *LDAPAttribute `json:"name"`
|
||||
Surname *LDAPAttribute `json:"surname"`
|
||||
Email *LDAPAttribute `json:"email"`
|
||||
Username *LDAPAttribute `json:"login"`
|
||||
IsGrafanaAdmin *bool `json:"isGrafanaAdmin"`
|
||||
IsDisabled bool `json:"isDisabled"`
|
||||
OrgRoles []RoleDTO `json:"roles"`
|
||||
Teams []TeamDTO `json:"teams"`
|
||||
}
|
||||
|
||||
// FetchOrgs fetches the organization(s) information by executing a single query to the database. Then, populating the DTO with the information retrieved.
|
||||
func (user *LDAPUserDTO) FetchOrgs() error {
|
||||
orgIds := []int64{}
|
||||
|
||||
for _, or := range user.OrgRoles {
|
||||
orgIds = append(orgIds, or.OrgId)
|
||||
}
|
||||
|
||||
q := &models.SearchOrgsQuery{}
|
||||
q.Ids = orgIds
|
||||
|
||||
if err := bus.Dispatch(q); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
orgNamesById := map[int64]string{}
|
||||
for _, org := range q.Result {
|
||||
orgNamesById[org.Id] = org.Name
|
||||
}
|
||||
|
||||
for i, orgDTO := range user.OrgRoles {
|
||||
orgName := orgNamesById[orgDTO.OrgId]
|
||||
|
||||
if orgName != "" {
|
||||
user.OrgRoles[i].OrgName = orgName
|
||||
} else {
|
||||
return errOrganizationNotFound(orgDTO.OrgId)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReloadLDAPCfg reloads the LDAP configuration
|
||||
func (server *HTTPServer) ReloadLDAPCfg() Response {
|
||||
if !ldap.IsEnabled() {
|
||||
return Error(400, "LDAP is not enabled", nil)
|
||||
}
|
||||
|
||||
err := ldap.ReloadConfig()
|
||||
if err != nil {
|
||||
return Error(500, "Failed to reload ldap config.", err)
|
||||
}
|
||||
return Success("LDAP config reloaded")
|
||||
}
|
||||
|
||||
// GetUserFromLDAP finds an user based on a username in LDAP. This helps illustrate how would the particular user be mapped in Grafana when synced.
|
||||
func (server *HTTPServer) GetUserFromLDAP(c *models.ReqContext) Response {
|
||||
ldapConfig, err := getLDAPConfig()
|
||||
|
||||
if err != nil {
|
||||
return Error(400, "Failed to obtain the LDAP configuration. Please ", err)
|
||||
}
|
||||
|
||||
ldap := newLDAP(ldapConfig.Servers)
|
||||
|
||||
username := c.Params(":username")
|
||||
|
||||
if len(username) == 0 {
|
||||
return Error(http.StatusBadRequest, "Validation error. You must specify an username", nil)
|
||||
}
|
||||
|
||||
user, serverConfig, err := ldap.User(username)
|
||||
|
||||
if user == nil {
|
||||
return Error(http.StatusNotFound, "No user was found on the LDAP server(s)", err)
|
||||
}
|
||||
|
||||
name, surname := splitName(user.Name)
|
||||
|
||||
u := &LDAPUserDTO{
|
||||
Name: &LDAPAttribute{serverConfig.Attr.Name, name},
|
||||
Surname: &LDAPAttribute{serverConfig.Attr.Surname, surname},
|
||||
Email: &LDAPAttribute{serverConfig.Attr.Email, user.Email},
|
||||
Username: &LDAPAttribute{serverConfig.Attr.Username, user.Login},
|
||||
IsGrafanaAdmin: user.IsGrafanaAdmin,
|
||||
IsDisabled: user.IsDisabled,
|
||||
}
|
||||
|
||||
orgRoles := []RoleDTO{}
|
||||
|
||||
for _, g := range serverConfig.Groups {
|
||||
role := &RoleDTO{}
|
||||
|
||||
if isMatchToLDAPGroup(user, g) {
|
||||
role.OrgId = g.OrgID
|
||||
role.OrgRole = user.OrgRoles[g.OrgID]
|
||||
role.GroupDN = g.GroupDN
|
||||
|
||||
orgRoles = append(orgRoles, *role)
|
||||
} else {
|
||||
role.OrgId = g.OrgID
|
||||
role.GroupDN = g.GroupDN
|
||||
|
||||
orgRoles = append(orgRoles, *role)
|
||||
}
|
||||
}
|
||||
|
||||
u.OrgRoles = orgRoles
|
||||
|
||||
err = u.FetchOrgs()
|
||||
|
||||
if err != nil {
|
||||
return Error(http.StatusBadRequest, "Organization not found - Please verify your LDAP configuration", err)
|
||||
}
|
||||
|
||||
return JSON(200, u)
|
||||
}
|
||||
|
||||
// isMatchToLDAPGroup determines if we were able to match an LDAP group to an organization+role.
|
||||
// Since we allow one role per organization. If it's set, we were able to match it.
|
||||
func isMatchToLDAPGroup(user *models.ExternalUserInfo, groupConfig *ldap.GroupToOrgRole) bool {
|
||||
return user.OrgRoles[groupConfig.OrgID] == groupConfig.OrgRole
|
||||
}
|
||||
|
||||
// splitName receives the full name of a user and splits it into two parts: A name and a surname.
|
||||
func splitName(name string) (string, string) {
|
||||
names := util.SplitString(name)
|
||||
|
||||
switch len(names) {
|
||||
case 0:
|
||||
return "", ""
|
||||
case 1:
|
||||
return names[0], ""
|
||||
default:
|
||||
return names[0], names[1]
|
||||
}
|
||||
}
|
223
pkg/api/ldap_debug_test.go
Normal file
223
pkg/api/ldap_debug_test.go
Normal file
@ -0,0 +1,223 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"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/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type LDAPMock struct {
|
||||
Results []*models.ExternalUserInfo
|
||||
}
|
||||
|
||||
var userSearchResult *models.ExternalUserInfo
|
||||
var userSearchConfig ldap.ServerConfig
|
||||
|
||||
func (m *LDAPMock) Login(query *models.LoginUserQuery) (*models.ExternalUserInfo, error) {
|
||||
return &models.ExternalUserInfo{}, nil
|
||||
}
|
||||
|
||||
func (m *LDAPMock) Users(logins []string) ([]*models.ExternalUserInfo, error) {
|
||||
s := []*models.ExternalUserInfo{}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (m *LDAPMock) User(login string) (*models.ExternalUserInfo, ldap.ServerConfig, error) {
|
||||
return userSearchResult, userSearchConfig, nil
|
||||
}
|
||||
|
||||
func getUserFromLDAPContext(t *testing.T, requestURL string) *scenarioContext {
|
||||
t.Helper()
|
||||
|
||||
sc := setupScenarioContext(requestURL)
|
||||
|
||||
hs := &HTTPServer{Cfg: setting.NewCfg()}
|
||||
|
||||
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
|
||||
sc.context = c
|
||||
return hs.GetUserFromLDAP(c)
|
||||
})
|
||||
|
||||
sc.m.Get("/api/admin/ldap/:username", sc.defaultHandler)
|
||||
|
||||
sc.resp = httptest.NewRecorder()
|
||||
req, _ := http.NewRequest(http.MethodGet, requestURL, nil)
|
||||
sc.req = req
|
||||
sc.exec()
|
||||
|
||||
return sc
|
||||
}
|
||||
|
||||
func TestGetUserFromLDAPApiEndpoint_UserNotFound(t *testing.T) {
|
||||
getLDAPConfig = func() (*ldap.Config, error) {
|
||||
return &ldap.Config{}, nil
|
||||
}
|
||||
|
||||
newLDAP = func(_ []*ldap.ServerConfig) multildap.IMultiLDAP {
|
||||
return &LDAPMock{}
|
||||
}
|
||||
|
||||
userSearchResult = nil
|
||||
|
||||
sc := getUserFromLDAPContext(t, "/api/admin/ldap/user-that-does-not-exist")
|
||||
|
||||
require.Equal(t, sc.resp.Code, http.StatusNotFound)
|
||||
responseString, err := getBody(sc.resp)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "{\"message\":\"No user was found on the LDAP server(s)\"}", responseString)
|
||||
}
|
||||
|
||||
func TestGetUserFromLDAPApiEndpoint_OrgNotfound(t *testing.T) {
|
||||
isAdmin := true
|
||||
userSearchResult = &models.ExternalUserInfo{
|
||||
Name: "John Doe",
|
||||
Email: "john.doe@example.com",
|
||||
Login: "johndoe",
|
||||
OrgRoles: map[int64]models.RoleType{1: models.ROLE_ADMIN, 2: models.ROLE_VIEWER},
|
||||
IsGrafanaAdmin: &isAdmin,
|
||||
}
|
||||
|
||||
userSearchConfig = ldap.ServerConfig{
|
||||
Attr: ldap.AttributeMap{
|
||||
Name: "ldap-name",
|
||||
Surname: "ldap-surname",
|
||||
Email: "ldap-email",
|
||||
Username: "ldap-username",
|
||||
},
|
||||
Groups: []*ldap.GroupToOrgRole{
|
||||
{
|
||||
GroupDN: "cn=admins,ou=groups,dc=grafana,dc=org",
|
||||
OrgID: 1,
|
||||
OrgRole: models.ROLE_ADMIN,
|
||||
},
|
||||
{
|
||||
GroupDN: "cn=admins,ou=groups,dc=grafana2,dc=org",
|
||||
OrgID: 2,
|
||||
OrgRole: models.ROLE_VIEWER,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
mockOrgSearchResult := []*models.OrgDTO{
|
||||
{Id: 1, Name: "Main Org."},
|
||||
}
|
||||
|
||||
bus.AddHandler("test", func(query *models.SearchOrgsQuery) error {
|
||||
query.Result = mockOrgSearchResult
|
||||
return nil
|
||||
})
|
||||
|
||||
getLDAPConfig = func() (*ldap.Config, error) {
|
||||
return &ldap.Config{}, nil
|
||||
}
|
||||
|
||||
newLDAP = func(_ []*ldap.ServerConfig) multildap.IMultiLDAP {
|
||||
return &LDAPMock{}
|
||||
}
|
||||
|
||||
sc := getUserFromLDAPContext(t, "/api/admin/ldap/johndoe")
|
||||
|
||||
require.Equal(t, sc.resp.Code, http.StatusBadRequest)
|
||||
|
||||
jsonResponse, err := getJSONbody(sc.resp)
|
||||
assert.Nil(t, err)
|
||||
|
||||
expected := `
|
||||
{
|
||||
"error": "Unable to find organization with ID '2'",
|
||||
"message": "Organization not found - Please verify your LDAP configuration"
|
||||
}
|
||||
`
|
||||
var expectedJSON interface{}
|
||||
_ = json.Unmarshal([]byte(expected), &expectedJSON)
|
||||
|
||||
assert.Equal(t, jsonResponse, expectedJSON)
|
||||
}
|
||||
|
||||
func TestGetUserFromLDAPApiEndpoint(t *testing.T) {
|
||||
isAdmin := true
|
||||
userSearchResult = &models.ExternalUserInfo{
|
||||
Name: "John Doe",
|
||||
Email: "john.doe@example.com",
|
||||
Login: "johndoe",
|
||||
OrgRoles: map[int64]models.RoleType{1: models.ROLE_ADMIN},
|
||||
IsGrafanaAdmin: &isAdmin,
|
||||
}
|
||||
|
||||
userSearchConfig = ldap.ServerConfig{
|
||||
Attr: ldap.AttributeMap{
|
||||
Name: "ldap-name",
|
||||
Surname: "ldap-surname",
|
||||
Email: "ldap-email",
|
||||
Username: "ldap-username",
|
||||
},
|
||||
Groups: []*ldap.GroupToOrgRole{
|
||||
{
|
||||
GroupDN: "cn=admins,ou=groups,dc=grafana,dc=org",
|
||||
OrgID: 1,
|
||||
OrgRole: models.ROLE_ADMIN,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
mockOrgSearchResult := []*models.OrgDTO{
|
||||
{Id: 1, Name: "Main Org."},
|
||||
}
|
||||
|
||||
bus.AddHandler("test", func(query *models.SearchOrgsQuery) error {
|
||||
query.Result = mockOrgSearchResult
|
||||
return nil
|
||||
})
|
||||
|
||||
getLDAPConfig = func() (*ldap.Config, error) {
|
||||
return &ldap.Config{}, nil
|
||||
}
|
||||
|
||||
newLDAP = func(_ []*ldap.ServerConfig) multildap.IMultiLDAP {
|
||||
return &LDAPMock{}
|
||||
}
|
||||
|
||||
sc := getUserFromLDAPContext(t, "/api/admin/ldap/johndoe")
|
||||
|
||||
require.Equal(t, sc.resp.Code, http.StatusOK)
|
||||
|
||||
jsonResponse, err := getJSONbody(sc.resp)
|
||||
assert.Nil(t, err)
|
||||
|
||||
expected := `
|
||||
{
|
||||
"name": {
|
||||
"cfgAttrValue": "ldap-name", "ldapValue": "John"
|
||||
},
|
||||
"surname": {
|
||||
"cfgAttrValue": "ldap-surname", "ldapValue": "Doe"
|
||||
},
|
||||
"email": {
|
||||
"cfgAttrValue": "ldap-email", "ldapValue": "john.doe@example.com"
|
||||
},
|
||||
"login": {
|
||||
"cfgAttrValue": "ldap-username", "ldapValue": "johndoe"
|
||||
},
|
||||
"isGrafanaAdmin": true,
|
||||
"isDisabled": false,
|
||||
"roles": [
|
||||
{ "orgId": 1, "orgRole": "Admin", "orgName": "Main Org.", "groupDN": "cn=admins,ou=groups,dc=grafana,dc=org" }
|
||||
],
|
||||
"teams": null
|
||||
}
|
||||
`
|
||||
var expectedJSON interface{}
|
||||
_ = json.Unmarshal([]byte(expected), &expectedJSON)
|
||||
|
||||
assert.Equal(t, jsonResponse, expectedJSON)
|
||||
}
|
@ -2,17 +2,19 @@ package api
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func mockSetIndexViewData() {
|
||||
@ -50,6 +52,18 @@ func getBody(resp *httptest.ResponseRecorder) (string, error) {
|
||||
return string(responseData), nil
|
||||
}
|
||||
|
||||
func getJSONbody(resp *httptest.ResponseRecorder) (interface{}, error) {
|
||||
var j interface{}
|
||||
|
||||
err := json.Unmarshal(resp.Body.Bytes(), &j)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return j, nil
|
||||
}
|
||||
|
||||
func TestLoginErrorCookieApiEndpoint(t *testing.T) {
|
||||
mockSetIndexViewData()
|
||||
defer resetSetIndexViewData()
|
||||
|
@ -4,12 +4,11 @@ import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
var errTest = errors.New("Test error")
|
||||
@ -98,9 +97,10 @@ func (auth *mockAuth) Users(logins []string) (
|
||||
|
||||
func (auth *mockAuth) User(login string) (
|
||||
*models.ExternalUserInfo,
|
||||
ldap.ServerConfig,
|
||||
error,
|
||||
) {
|
||||
return nil, nil
|
||||
return nil, ldap.ServerConfig{}, nil
|
||||
}
|
||||
|
||||
func (auth *mockAuth) Add(dn string, values map[string][]string) error {
|
||||
|
@ -218,7 +218,7 @@ func (auth *AuthProxy) LoginViaLDAP() (int64, *Error) {
|
||||
return 0, newError("Failed to get LDAP config", nil)
|
||||
}
|
||||
|
||||
extUser, err := newLDAP(config.Servers).User(auth.header)
|
||||
extUser, _, err := newLDAP(config.Servers).User(auth.header)
|
||||
if err != nil {
|
||||
return 0, newError(err.Error(), nil)
|
||||
}
|
||||
|
@ -36,13 +36,14 @@ func (stub *TestMultiLDAP) Login(query *models.LoginUserQuery) (
|
||||
|
||||
func (stub *TestMultiLDAP) User(login string) (
|
||||
*models.ExternalUserInfo,
|
||||
ldap.ServerConfig,
|
||||
error,
|
||||
) {
|
||||
stub.userCalled = true
|
||||
result := &models.ExternalUserInfo{
|
||||
UserId: stub.ID,
|
||||
}
|
||||
return result, nil
|
||||
return result, ldap.ServerConfig{}, nil
|
||||
}
|
||||
|
||||
func prepareMiddleware(t *testing.T, req *http.Request, store *remotecache.RemoteCache) *AuthProxy {
|
||||
|
@ -67,6 +67,7 @@ type SearchOrgsQuery struct {
|
||||
Name string
|
||||
Limit int
|
||||
Page int
|
||||
Ids []int64
|
||||
|
||||
Result []*OrgDTO
|
||||
}
|
||||
|
@ -10,10 +10,9 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"gopkg.in/ldap.v3"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"gopkg.in/ldap.v3"
|
||||
)
|
||||
|
||||
// IConnection is interface for LDAP connection manipulation
|
||||
|
@ -39,7 +39,7 @@ type IMultiLDAP interface {
|
||||
)
|
||||
|
||||
User(login string) (
|
||||
*models.ExternalUserInfo, error,
|
||||
*models.ExternalUserInfo, ldap.ServerConfig, error,
|
||||
)
|
||||
}
|
||||
|
||||
@ -92,14 +92,15 @@ func (multiples *MultiLDAP) Login(query *models.LoginUserQuery) (
|
||||
return nil, ErrInvalidCredentials
|
||||
}
|
||||
|
||||
// User gets a user by login
|
||||
// User attempts to find an user by login/username by searching into all of the configured LDAP servers. Then, if the user is found it returns the user alongisde the server it was found.
|
||||
func (multiples *MultiLDAP) User(login string) (
|
||||
*models.ExternalUserInfo,
|
||||
ldap.ServerConfig,
|
||||
error,
|
||||
) {
|
||||
|
||||
if len(multiples.configs) == 0 {
|
||||
return nil, ErrNoLDAPServers
|
||||
return nil, ldap.ServerConfig{}, ErrNoLDAPServers
|
||||
}
|
||||
|
||||
search := []string{login}
|
||||
@ -107,26 +108,26 @@ func (multiples *MultiLDAP) User(login string) (
|
||||
server := newLDAP(config)
|
||||
|
||||
if err := server.Dial(); err != nil {
|
||||
return nil, err
|
||||
return nil, *config, err
|
||||
}
|
||||
|
||||
defer server.Close()
|
||||
|
||||
if err := server.Bind(); err != nil {
|
||||
return nil, err
|
||||
return nil, *config, err
|
||||
}
|
||||
|
||||
users, err := server.Users(search)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, *config, err
|
||||
}
|
||||
|
||||
if len(users) != 0 {
|
||||
return users[0], nil
|
||||
return users[0], *config, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, ErrDidNotFindUser
|
||||
return nil, ldap.ServerConfig{}, ErrDidNotFindUser
|
||||
}
|
||||
|
||||
// Users gets users from multiple LDAP servers
|
||||
|
@ -4,10 +4,9 @@ import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/ldap"
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestMultiLDAP(t *testing.T) {
|
||||
@ -127,7 +126,7 @@ func TestMultiLDAP(t *testing.T) {
|
||||
setup()
|
||||
|
||||
multi := New([]*ldap.ServerConfig{})
|
||||
_, err := multi.User("test")
|
||||
_, _, err := multi.User("test")
|
||||
|
||||
So(err, ShouldBeError)
|
||||
So(err, ShouldEqual, ErrNoLDAPServers)
|
||||
@ -145,7 +144,7 @@ func TestMultiLDAP(t *testing.T) {
|
||||
{}, {},
|
||||
})
|
||||
|
||||
_, err := multi.User("test")
|
||||
_, _, err := multi.User("test")
|
||||
|
||||
So(err, ShouldBeError)
|
||||
So(err, ShouldEqual, expected)
|
||||
@ -159,7 +158,7 @@ func TestMultiLDAP(t *testing.T) {
|
||||
multi := New([]*ldap.ServerConfig{
|
||||
{}, {},
|
||||
})
|
||||
_, err := multi.User("test")
|
||||
_, _, err := multi.User("test")
|
||||
|
||||
So(mock.dialCalledTimes, ShouldEqual, 2)
|
||||
So(mock.usersCalledTimes, ShouldEqual, 2)
|
||||
@ -179,7 +178,7 @@ func TestMultiLDAP(t *testing.T) {
|
||||
multi := New([]*ldap.ServerConfig{
|
||||
{}, {},
|
||||
})
|
||||
_, err := multi.User("test")
|
||||
_, _, err := multi.User("test")
|
||||
|
||||
So(mock.dialCalledTimes, ShouldEqual, 1)
|
||||
So(mock.usersCalledTimes, ShouldEqual, 1)
|
||||
@ -206,7 +205,7 @@ func TestMultiLDAP(t *testing.T) {
|
||||
multi := New([]*ldap.ServerConfig{
|
||||
{}, {},
|
||||
})
|
||||
user, err := multi.User("test")
|
||||
user, _, err := multi.User("test")
|
||||
|
||||
So(mock.dialCalledTimes, ShouldEqual, 1)
|
||||
So(mock.usersCalledTimes, ShouldEqual, 1)
|
||||
|
@ -91,10 +91,10 @@ func (mock *MockMultiLDAP) Users(logins []string) (
|
||||
|
||||
// User test fn
|
||||
func (mock *MockMultiLDAP) User(login string) (
|
||||
*models.ExternalUserInfo, error,
|
||||
*models.ExternalUserInfo, ldap.ServerConfig, error,
|
||||
) {
|
||||
mock.UserCalledTimes = mock.UserCalledTimes + 1
|
||||
return nil, nil
|
||||
return nil, ldap.ServerConfig{}, nil
|
||||
}
|
||||
|
||||
func setup() *MockLDAP {
|
||||
|
@ -27,6 +27,11 @@ func SearchOrgs(query *m.SearchOrgsQuery) error {
|
||||
if query.Name != "" {
|
||||
sess.Where("name=?", query.Name)
|
||||
}
|
||||
|
||||
if len(query.Ids) > 0 {
|
||||
sess.In("id", query.Ids)
|
||||
}
|
||||
|
||||
sess.Limit(query.Limit, query.Limit*query.Page)
|
||||
sess.Cols("id", "name")
|
||||
err := sess.Find(&query.Result)
|
||||
|
@ -2,19 +2,39 @@ package sqlstore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestAccountDataAccess(t *testing.T) {
|
||||
Convey("Testing Account DB Access", t, func() {
|
||||
InitTestDB(t)
|
||||
|
||||
Convey("Given we have organizations, we can query them by IDs", func() {
|
||||
var err error
|
||||
var cmd *m.CreateOrgCommand
|
||||
ids := []int64{}
|
||||
|
||||
for i := 1; i < 4; i++ {
|
||||
cmd = &m.CreateOrgCommand{Name: fmt.Sprint("Org #", i)}
|
||||
err = CreateOrg(cmd)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
ids = append(ids, cmd.Result.Id)
|
||||
}
|
||||
|
||||
query := &m.SearchOrgsQuery{Ids: ids}
|
||||
err = SearchOrgs(query)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(len(query.Result), ShouldEqual, 3)
|
||||
})
|
||||
|
||||
Convey("Given single org mode", func() {
|
||||
setting.AutoAssignOrg = true
|
||||
setting.AutoAssignOrgId = 1
|
||||
|
Loading…
Reference in New Issue
Block a user