Commit 38bd0d1a by Torkel Ödegaard

Merge branch 'ldap-improvements' of https://github.com/abligh/grafana into abligh-ldap-improvements

parents 59bd029e e8256f0a
......@@ -2,7 +2,7 @@
verbose_logging = false
[[servers]]
# Ldap server host
# Ldap server host (specify multiple hosts space separated)
host = "127.0.0.1"
# Default port is 389 or 636 if use_ssl = true
port = 389
......@@ -10,17 +10,40 @@ port = 389
use_ssl = false
# set to true if you want to skip ssl cert validation
ssl_skip_verify = false
# set to the path to your root CA certificate or leave unset to use system defaults
# root_ca_cert = /path/to/certificate.crt
# Search user bind dn
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"
......
......@@ -2,8 +2,10 @@ package login
import (
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"io/ioutil"
"strings"
"github.com/davecgh/go-spew/spew"
......@@ -24,18 +26,37 @@ func NewLdapAuthenticator(server *LdapServerConf) *ldapAuther {
}
func (a *ldapAuther) Dial() error {
address := fmt.Sprintf("%s:%d", a.server.Host, a.server.Port)
var err error
if a.server.UseSSL {
tlsCfg := &tls.Config{
InsecureSkipVerify: a.server.SkipVerifySSL,
ServerName: a.server.Host,
var certPool *x509.CertPool
if a.server.RootCACert != "" {
certPool := x509.NewCertPool()
for _, caCertFile := range strings.Split(a.server.RootCACert, " ") {
if pem, err := ioutil.ReadFile(caCertFile); err != nil {
return err
} else {
if !certPool.AppendCertsFromPEM(pem) {
return errors.New("Failed to append CA certficate " + caCertFile)
}
}
}
a.conn, err = ldap.DialTLS("tcp", address, tlsCfg)
} else {
a.conn, err = ldap.Dial("tcp", address)
}
for _, host := range strings.Split(a.server.Host, " ") {
address := fmt.Sprintf("%s:%d", host, a.server.Port)
if a.server.UseSSL {
tlsCfg := &tls.Config{
InsecureSkipVerify: a.server.SkipVerifySSL,
ServerName: host,
RootCAs: certPool,
}
a.conn, err = ldap.DialTLS("tcp", address, tlsCfg)
} else {
a.conn, err = ldap.Dial("tcp", address)
}
if err == nil {
return nil
}
}
return err
}
......@@ -290,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]
......@@ -311,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 {
......
......@@ -19,6 +19,7 @@ type LdapServerConf struct {
Port int `toml:"port"`
UseSSL bool `toml:"use_ssl"`
SkipVerifySSL bool `toml:"ssl_skip_verify"`
RootCACert string `toml:"root_ca_cert"`
BindDN string `toml:"bind_dn"`
BindPassword string `toml:"bind_password"`
Attr LdapAttributeMap `toml:"attributes"`
......@@ -26,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"`
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment