From 10fe101add260c4cca558065f9fa2d5b2e9d2586 Mon Sep 17 00:00:00 2001 From: gotjosh Date: Sun, 8 Sep 2019 11:48:47 +0100 Subject: [PATCH] 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. --- pkg/api/ldap_debug.go | 42 +++++++++++-------- pkg/api/ldap_debug_test.go | 85 +++++++++++++++++++++++++++++++++++++- pkg/models/user_auth.go | 11 +++++ 3 files changed, 120 insertions(+), 18 deletions(-) diff --git a/pkg/api/ldap_debug.go b/pkg/api/ldap_debug.go index 0f22f9a7ddd..22252622e84 100644 --- a/pkg/api/ldap_debug.go +++ b/pkg/api/ldap_debug.go @@ -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) } diff --git a/pkg/api/ldap_debug_test.go b/pkg/api/ldap_debug_test.go index 986ac5083f4..46bfe57f7c7 100644 --- a/pkg/api/ldap_debug_test.go +++ b/pkg/api/ldap_debug_test.go @@ -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 //*** diff --git a/pkg/models/user_auth.go b/pkg/models/user_auth.go index e6f02212eba..c8fb46e357a 100644 --- a/pkg/models/user_auth.go +++ b/pkg/models/user_auth.go @@ -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