diff --git a/conf/ldap.toml b/conf/ldap.toml index af131ad23b9..bfe28d4e835 100644 --- a/conf/ldap.toml +++ b/conf/ldap.toml @@ -18,11 +18,32 @@ bind_dn = "cn=admin,dc=grafana,dc=org" # Search user bind password bind_password = 'grafana' +# Schema's supporting memberOf + # Search filter, for example "(cn=%s)" or "(sAMAccountName=%s)" search_filter = "(cn=%s)" # An array of base dns to search through search_base_dns = ["dc=grafana,dc=org"] +# Uncomment this section (and comment out the previous 2 entries) to use POSIX schema. +# In POSIX LDAP schemas, querying the people 'ou' gives you entries that do not have a +# memberOf attribute, so a secondary query must be made for groups. This is done by +# enabling group_search_filter below. You must also set +# member_of = "cn" +# in [servers.attributes] below. +# +# Search filter, used to retrieve the user +# search_filter = "(uid=%s)" +# +# An array of the base DNs to search through for users. Typically uses ou=people. +# search_base_dns = ["ou=people,dc=grafana,dc=org"] +# +# Group search filter, to retrieve the groups of which the user is a member +# group_search_filter = "(&(objectClass=posixGroup)(memberUid=%s))" +# +# An array of the base DNs to search through for groups. Typically uses ou=groups +# group_search_base_dns = ["ou=groups,dc=grafana,dc=org"] + # Specify names of the ldap attributes your ldap uses [servers.attributes] name = "givenName" diff --git a/pkg/login/ldap.go b/pkg/login/ldap.go index b98e54446f2..6337f932256 100644 --- a/pkg/login/ldap.go +++ b/pkg/login/ldap.go @@ -311,18 +311,51 @@ func (a *ldapAuther) searchForUser(username string) (*ldapUserInfo, error) { return nil, errors.New("Ldap search matched more than one entry, please review your filter setting") } + var memberOf []string + if a.server.GroupSearchFilter == "" { + memberOf = getLdapAttrArray(a.server.Attr.MemberOf, searchResult) + } else { + // If we are using a POSIX LDAP schema it won't support memberOf, so we manually search the groups + var groupSearchResult *ldap.SearchResult + for _, groupSearchBase := range a.server.GroupSearchBaseDNs { + filter := strings.Replace(a.server.GroupSearchFilter, "%s", username, -1) + groupSearchReq := ldap.SearchRequest{ + BaseDN: groupSearchBase, + Scope: ldap.ScopeWholeSubtree, + DerefAliases: ldap.NeverDerefAliases, + Attributes: []string{ + // Here MemberOf would be the thing that identifies the group, which is normally 'cn' + a.server.Attr.MemberOf, + }, + Filter: filter, + } + + groupSearchResult, err = a.conn.Search(&groupSearchReq) + if err != nil { + return nil, err + } + + if len(groupSearchResult.Entries) > 0 { + for i := range groupSearchResult.Entries { + memberOf = append(memberOf, getLdapAttrN(a.server.Attr.MemberOf, groupSearchResult, i)) + } + break + } + } + } + return &ldapUserInfo{ DN: searchResult.Entries[0].DN, LastName: getLdapAttr(a.server.Attr.Surname, searchResult), FirstName: getLdapAttr(a.server.Attr.Name, searchResult), Username: getLdapAttr(a.server.Attr.Username, searchResult), Email: getLdapAttr(a.server.Attr.Email, searchResult), - MemberOf: getLdapAttrArray(a.server.Attr.MemberOf, searchResult), + MemberOf: memberOf, }, nil } -func getLdapAttr(name string, result *ldap.SearchResult) string { - for _, attr := range result.Entries[0].Attributes { +func getLdapAttrN(name string, result *ldap.SearchResult, n int) string { + for _, attr := range result.Entries[n].Attributes { if attr.Name == name { if len(attr.Values) > 0 { return attr.Values[0] @@ -332,6 +365,10 @@ func getLdapAttr(name string, result *ldap.SearchResult) string { return "" } +func getLdapAttr(name string, result *ldap.SearchResult) string { + return getLdapAttrN(name, result, 0) +} + func getLdapAttrArray(name string, result *ldap.SearchResult) []string { for _, attr := range result.Entries[0].Attributes { if attr.Name == name { diff --git a/pkg/login/settings.go b/pkg/login/settings.go index 64fcf4da56d..b181dac3281 100644 --- a/pkg/login/settings.go +++ b/pkg/login/settings.go @@ -27,6 +27,9 @@ type LdapServerConf struct { SearchFilter string `toml:"search_filter"` SearchBaseDNs []string `toml:"search_base_dns"` + GroupSearchFilter string `toml:"group_search_filter"` + GroupSearchBaseDNs []string `toml:"group_search_base_dns"` + LdapGroups []*LdapGroupToOrgRole `toml:"group_mappings"` }