mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge branch 'ldap-improvements' of https://github.com/abligh/grafana into abligh-ldap-improvements
This commit is contained in:
commit
38bd0d1aec
@ -2,7 +2,7 @@
|
|||||||
verbose_logging = false
|
verbose_logging = false
|
||||||
|
|
||||||
[[servers]]
|
[[servers]]
|
||||||
# Ldap server host
|
# Ldap server host (specify multiple hosts space separated)
|
||||||
host = "127.0.0.1"
|
host = "127.0.0.1"
|
||||||
# Default port is 389 or 636 if use_ssl = true
|
# Default port is 389 or 636 if use_ssl = true
|
||||||
port = 389
|
port = 389
|
||||||
@ -10,17 +10,40 @@ port = 389
|
|||||||
use_ssl = false
|
use_ssl = false
|
||||||
# set to true if you want to skip ssl cert validation
|
# set to true if you want to skip ssl cert validation
|
||||||
ssl_skip_verify = false
|
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
|
# Search user bind dn
|
||||||
bind_dn = "cn=admin,dc=grafana,dc=org"
|
bind_dn = "cn=admin,dc=grafana,dc=org"
|
||||||
# Search user bind password
|
# Search user bind password
|
||||||
bind_password = 'grafana'
|
bind_password = 'grafana'
|
||||||
|
|
||||||
|
# Schema's supporting memberOf
|
||||||
|
|
||||||
# Search filter, for example "(cn=%s)" or "(sAMAccountName=%s)"
|
# Search filter, for example "(cn=%s)" or "(sAMAccountName=%s)"
|
||||||
search_filter = "(cn=%s)"
|
search_filter = "(cn=%s)"
|
||||||
# An array of base dns to search through
|
# An array of base dns to search through
|
||||||
search_base_dns = ["dc=grafana,dc=org"]
|
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
|
# Specify names of the ldap attributes your ldap uses
|
||||||
[servers.attributes]
|
[servers.attributes]
|
||||||
name = "givenName"
|
name = "givenName"
|
||||||
|
@ -2,8 +2,10 @@ package login
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
@ -24,18 +26,37 @@ func NewLdapAuthenticator(server *LdapServerConf) *ldapAuther {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *ldapAuther) Dial() error {
|
func (a *ldapAuther) Dial() error {
|
||||||
address := fmt.Sprintf("%s:%d", a.server.Host, a.server.Port)
|
|
||||||
var err error
|
var err error
|
||||||
if a.server.UseSSL {
|
var certPool *x509.CertPool
|
||||||
tlsCfg := &tls.Config{
|
if a.server.RootCACert != "" {
|
||||||
InsecureSkipVerify: a.server.SkipVerifySSL,
|
certPool := x509.NewCertPool()
|
||||||
ServerName: a.server.Host,
|
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
|
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")
|
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{
|
return &ldapUserInfo{
|
||||||
DN: searchResult.Entries[0].DN,
|
DN: searchResult.Entries[0].DN,
|
||||||
LastName: getLdapAttr(a.server.Attr.Surname, searchResult),
|
LastName: getLdapAttr(a.server.Attr.Surname, searchResult),
|
||||||
FirstName: getLdapAttr(a.server.Attr.Name, searchResult),
|
FirstName: getLdapAttr(a.server.Attr.Name, searchResult),
|
||||||
Username: getLdapAttr(a.server.Attr.Username, searchResult),
|
Username: getLdapAttr(a.server.Attr.Username, searchResult),
|
||||||
Email: getLdapAttr(a.server.Attr.Email, searchResult),
|
Email: getLdapAttr(a.server.Attr.Email, searchResult),
|
||||||
MemberOf: getLdapAttrArray(a.server.Attr.MemberOf, searchResult),
|
MemberOf: memberOf,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getLdapAttr(name string, result *ldap.SearchResult) string {
|
func getLdapAttrN(name string, result *ldap.SearchResult, n int) string {
|
||||||
for _, attr := range result.Entries[0].Attributes {
|
for _, attr := range result.Entries[n].Attributes {
|
||||||
if attr.Name == name {
|
if attr.Name == name {
|
||||||
if len(attr.Values) > 0 {
|
if len(attr.Values) > 0 {
|
||||||
return attr.Values[0]
|
return attr.Values[0]
|
||||||
@ -311,6 +365,10 @@ func getLdapAttr(name string, result *ldap.SearchResult) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getLdapAttr(name string, result *ldap.SearchResult) string {
|
||||||
|
return getLdapAttrN(name, result, 0)
|
||||||
|
}
|
||||||
|
|
||||||
func getLdapAttrArray(name string, result *ldap.SearchResult) []string {
|
func getLdapAttrArray(name string, result *ldap.SearchResult) []string {
|
||||||
for _, attr := range result.Entries[0].Attributes {
|
for _, attr := range result.Entries[0].Attributes {
|
||||||
if attr.Name == name {
|
if attr.Name == name {
|
||||||
|
@ -19,6 +19,7 @@ type LdapServerConf struct {
|
|||||||
Port int `toml:"port"`
|
Port int `toml:"port"`
|
||||||
UseSSL bool `toml:"use_ssl"`
|
UseSSL bool `toml:"use_ssl"`
|
||||||
SkipVerifySSL bool `toml:"ssl_skip_verify"`
|
SkipVerifySSL bool `toml:"ssl_skip_verify"`
|
||||||
|
RootCACert string `toml:"root_ca_cert"`
|
||||||
BindDN string `toml:"bind_dn"`
|
BindDN string `toml:"bind_dn"`
|
||||||
BindPassword string `toml:"bind_password"`
|
BindPassword string `toml:"bind_password"`
|
||||||
Attr LdapAttributeMap `toml:"attributes"`
|
Attr LdapAttributeMap `toml:"attributes"`
|
||||||
@ -26,6 +27,9 @@ type LdapServerConf struct {
|
|||||||
SearchFilter string `toml:"search_filter"`
|
SearchFilter string `toml:"search_filter"`
|
||||||
SearchBaseDNs []string `toml:"search_base_dns"`
|
SearchBaseDNs []string `toml:"search_base_dns"`
|
||||||
|
|
||||||
|
GroupSearchFilter string `toml:"group_search_filter"`
|
||||||
|
GroupSearchBaseDNs []string `toml:"group_search_base_dns"`
|
||||||
|
|
||||||
LdapGroups []*LdapGroupToOrgRole `toml:"group_mappings"`
|
LdapGroups []*LdapGroupToOrgRole `toml:"group_mappings"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user