LDAP: Fetch teams in debug view (#18951)

Adds the definition of `GetTeamsForLDAPGroupCommand` which handles the lookup of team information based on LDAP groupDNs.

This is an Enterprise only feature. To diferentiate,a response will contain the `team` key as `null` on OSS while on Enterprise the key will contain an empty array `[]` when no teams are found.
This commit is contained in:
gotjosh 2019-09-08 11:48:47 +01:00 committed by GitHub
parent 545be60138
commit 10fe101add
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 120 additions and 18 deletions

View File

@ -5,6 +5,7 @@ import (
"net/http"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/ldap"
"github.com/grafana/grafana/pkg/services/multildap"
@ -15,6 +16,8 @@ var (
getLDAPConfig = multildap.GetConfig
newLDAP = multildap.New
logger = log.New("LDAP.debug")
errOrganizationNotFound = func(orgId int64) error {
return fmt.Errorf("Unable to find organization with ID '%d'", orgId)
}
@ -34,23 +37,16 @@ type RoleDTO struct {
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"`
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 []models.TeamOrgGroupDTO `json:"teams"`
}
// FetchOrgs fetches the organization(s) information by executing a single query to the database. Then, populating the DTO with the information retrieved.
@ -154,7 +150,7 @@ func (server *HTTPServer) GetUserFromLDAP(c *models.ReqContext) Response {
ldapConfig, err := getLDAPConfig()
if err != nil {
return Error(http.StatusBadRequest, "Failed to obtain the LDAP configuration. Please ", err)
return Error(http.StatusBadRequest, "Failed to obtain the LDAP configuration", err)
}
ldap := newLDAP(ldapConfig.Servers)
@ -171,6 +167,8 @@ func (server *HTTPServer) GetUserFromLDAP(c *models.ReqContext) Response {
return Error(http.StatusNotFound, "No user was found on the LDAP server(s)", err)
}
logger.Debug("user found", "user", user)
name, surname := splitName(user.Name)
u := &LDAPUserDTO{
@ -203,12 +201,22 @@ func (server *HTTPServer) GetUserFromLDAP(c *models.ReqContext) Response {
u.OrgRoles = orgRoles
logger.Debug("mapping org roles", "orgsRoles", u.OrgRoles)
err = u.FetchOrgs()
if err != nil {
return Error(http.StatusBadRequest, "Organization not found - Please verify your LDAP configuration", err)
return Error(http.StatusBadRequest, "An oganization was not found - Please verify your LDAP configuration", err)
}
cmd := &models.GetTeamsForLDAPGroupCommand{Groups: user.Groups}
err = bus.Dispatch(cmd)
if err != bus.ErrHandlerNotFound && err != nil {
return Error(http.StatusBadRequest, "Unable to find the teams for this user", err)
}
u.Teams = cmd.Result
return JSON(200, u)
}

View File

@ -150,7 +150,7 @@ func TestGetUserFromLDAPApiEndpoint_OrgNotfound(t *testing.T) {
expected := `
{
"error": "Unable to find organization with ID '2'",
"message": "Organization not found - Please verify your LDAP configuration"
"message": "An oganization was not found - Please verify your LDAP configuration"
}
`
var expectedJSON interface{}
@ -237,6 +237,89 @@ func TestGetUserFromLDAPApiEndpoint(t *testing.T) {
assert.Equal(t, expectedJSON, jsonResponse)
}
func TestGetUserFromLDAPApiEndpoint_WithTeamHandler(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
})
bus.AddHandler("test", func(cmd *models.GetTeamsForLDAPGroupCommand) error {
cmd.Result = []models.TeamOrgGroupDTO{}
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": []
}
`
var expectedJSON interface{}
_ = json.Unmarshal([]byte(expected), &expectedJSON)
assert.Equal(t, expectedJSON, jsonResponse)
}
//***
// GetLDAPStatus tests
//***

View File

@ -100,6 +100,17 @@ type GetAuthInfoQuery struct {
Result *UserAuth
}
type TeamOrgGroupDTO struct {
TeamName string `json:"teamName"`
OrgName string `json:"orgName"`
GroupDN string `json:"groupDN"`
}
type GetTeamsForLDAPGroupCommand struct {
Groups []string
Result []TeamOrgGroupDTO
}
type SyncTeamsCommand struct {
ExternalUser *ExternalUserInfo
User *User