forked from External/ergo
improve maintainability and license compliance
0. Maximum parity with upstream code 1. Added Apache-required modification notices 2. Added Apache license
This commit is contained in:
parent
c13597f807
commit
0c2d8adeac
7 changed files with 551 additions and 250 deletions
266
irc/ldap/grafana.go
Normal file
266
irc/ldap/grafana.go
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
// Copyright 2014-2018 Grafana Labs
|
||||
// Released under the Apache 2.0 license
|
||||
|
||||
// Modification notice: these functions were altered by substituting
|
||||
// `serverConn` for `Server`.
|
||||
|
||||
package ldap
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
ldap "github.com/go-ldap/ldap/v3"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrInvalidCredentials is returned if username and password do not match
|
||||
ErrInvalidCredentials = errors.New("Invalid Username or Password")
|
||||
|
||||
// ErrCouldNotFindUser is returned when username hasn't been found (not username+password)
|
||||
ErrCouldNotFindUser = errors.New("Can't find user in LDAP")
|
||||
)
|
||||
|
||||
// shouldAdminBind checks if we should use
|
||||
// admin username & password for LDAP bind
|
||||
func (server *serverConn) shouldAdminBind() bool {
|
||||
return server.Config.BindPassword != ""
|
||||
}
|
||||
|
||||
// singleBindDN combines the bind with the username
|
||||
// in order to get the proper path
|
||||
func (server *serverConn) singleBindDN(username string) string {
|
||||
return fmt.Sprintf(server.Config.BindDN, username)
|
||||
}
|
||||
|
||||
// shouldSingleBind checks if we can use "single bind" approach
|
||||
func (server *serverConn) shouldSingleBind() bool {
|
||||
return strings.Contains(server.Config.BindDN, "%s")
|
||||
}
|
||||
|
||||
// Dial dials in the LDAP
|
||||
// TODO: decrease cyclomatic complexity
|
||||
func (server *serverConn) Dial() error {
|
||||
var err error
|
||||
var certPool *x509.CertPool
|
||||
if server.Config.RootCACert != "" {
|
||||
certPool = x509.NewCertPool()
|
||||
for _, caCertFile := range strings.Split(server.Config.RootCACert, " ") {
|
||||
pem, err := ioutil.ReadFile(caCertFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !certPool.AppendCertsFromPEM(pem) {
|
||||
return errors.New("Failed to append CA certificate " + caCertFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
var clientCert tls.Certificate
|
||||
if server.Config.ClientCert != "" && server.Config.ClientKey != "" {
|
||||
clientCert, err = tls.LoadX509KeyPair(server.Config.ClientCert, server.Config.ClientKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, host := range strings.Split(server.Config.Host, " ") {
|
||||
address := fmt.Sprintf("%s:%d", host, server.Config.Port)
|
||||
if server.Config.UseSSL {
|
||||
tlsCfg := &tls.Config{
|
||||
InsecureSkipVerify: server.Config.SkipVerifySSL,
|
||||
ServerName: host,
|
||||
RootCAs: certPool,
|
||||
}
|
||||
if len(clientCert.Certificate) > 0 {
|
||||
tlsCfg.Certificates = append(tlsCfg.Certificates, clientCert)
|
||||
}
|
||||
if server.Config.StartTLS {
|
||||
server.Connection, err = ldap.Dial("tcp", address)
|
||||
if err == nil {
|
||||
if err = server.Connection.StartTLS(tlsCfg); err == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
server.Connection, err = ldap.DialTLS("tcp", address, tlsCfg)
|
||||
}
|
||||
} else {
|
||||
server.Connection, err = ldap.Dial("tcp", address)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Close closes the LDAP connection
|
||||
// Dial() sets the connection with the server for this Struct. Therefore, we require a
|
||||
// call to Dial() before being able to execute this function.
|
||||
func (server *serverConn) Close() {
|
||||
server.Connection.Close()
|
||||
}
|
||||
|
||||
// userBind binds the user with the LDAP server
|
||||
func (server *serverConn) userBind(path, password string) error {
|
||||
err := server.Connection.Bind(path, password)
|
||||
if err != nil {
|
||||
if ldapErr, ok := err.(*ldap.Error); ok {
|
||||
if ldapErr.ResultCode == 49 {
|
||||
return ErrInvalidCredentials
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// users is helper method for the Users()
|
||||
func (server *serverConn) users(logins []string) (
|
||||
[]*ldap.Entry,
|
||||
error,
|
||||
) {
|
||||
var result *ldap.SearchResult
|
||||
var Config = server.Config
|
||||
var err error
|
||||
|
||||
for _, base := range Config.SearchBaseDNs {
|
||||
result, err = server.Connection.Search(
|
||||
server.getSearchRequest(base, logins),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(result.Entries) > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return result.Entries, nil
|
||||
}
|
||||
|
||||
// getSearchRequest returns LDAP search request for users
|
||||
func (server *serverConn) getSearchRequest(
|
||||
base string,
|
||||
logins []string,
|
||||
) *ldap.SearchRequest {
|
||||
attributes := []string{}
|
||||
|
||||
inputs := server.Config.Attr
|
||||
attributes = appendIfNotEmpty(
|
||||
attributes,
|
||||
inputs.Username,
|
||||
inputs.Surname,
|
||||
inputs.Email,
|
||||
inputs.Name,
|
||||
inputs.MemberOf,
|
||||
|
||||
// In case for the POSIX LDAP schema server
|
||||
server.Config.GroupSearchFilterUserAttribute,
|
||||
)
|
||||
|
||||
search := ""
|
||||
for _, login := range logins {
|
||||
query := strings.Replace(
|
||||
server.Config.SearchFilter,
|
||||
"%s", ldap.EscapeFilter(login),
|
||||
-1,
|
||||
)
|
||||
|
||||
search = search + query
|
||||
}
|
||||
|
||||
filter := fmt.Sprintf("(|%s)", search)
|
||||
|
||||
return &ldap.SearchRequest{
|
||||
BaseDN: base,
|
||||
Scope: ldap.ScopeWholeSubtree,
|
||||
DerefAliases: ldap.NeverDerefAliases,
|
||||
Attributes: attributes,
|
||||
Filter: filter,
|
||||
}
|
||||
}
|
||||
|
||||
// requestMemberOf use this function when POSIX LDAP
|
||||
// schema does not support memberOf, so it manually search the groups
|
||||
func (server *serverConn) requestMemberOf(entry *ldap.Entry) ([]string, error) {
|
||||
var memberOf []string
|
||||
var config = server.Config
|
||||
|
||||
for _, groupSearchBase := range config.GroupSearchBaseDNs {
|
||||
var filterReplace string
|
||||
if config.GroupSearchFilterUserAttribute == "" {
|
||||
filterReplace = getAttribute(config.Attr.Username, entry)
|
||||
} else {
|
||||
filterReplace = getAttribute(
|
||||
config.GroupSearchFilterUserAttribute,
|
||||
entry,
|
||||
)
|
||||
}
|
||||
|
||||
filter := strings.Replace(
|
||||
config.GroupSearchFilter, "%s",
|
||||
ldap.EscapeFilter(filterReplace),
|
||||
-1,
|
||||
)
|
||||
|
||||
server.log.Info("Searching for user's groups", "filter", filter)
|
||||
|
||||
// support old way of reading settings
|
||||
groupIDAttribute := config.Attr.MemberOf
|
||||
// but prefer dn attribute if default settings are used
|
||||
if groupIDAttribute == "" || groupIDAttribute == "memberOf" {
|
||||
groupIDAttribute = "dn"
|
||||
}
|
||||
|
||||
groupSearchReq := ldap.SearchRequest{
|
||||
BaseDN: groupSearchBase,
|
||||
Scope: ldap.ScopeWholeSubtree,
|
||||
DerefAliases: ldap.NeverDerefAliases,
|
||||
Attributes: []string{groupIDAttribute},
|
||||
Filter: filter,
|
||||
}
|
||||
|
||||
groupSearchResult, err := server.Connection.Search(&groupSearchReq)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(groupSearchResult.Entries) > 0 {
|
||||
for _, group := range groupSearchResult.Entries {
|
||||
|
||||
memberOf = append(
|
||||
memberOf,
|
||||
getAttribute(groupIDAttribute, group),
|
||||
)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return memberOf, nil
|
||||
}
|
||||
|
||||
// getMemberOf finds memberOf property or request it
|
||||
func (server *serverConn) getMemberOf(result *ldap.Entry) (
|
||||
[]string, error,
|
||||
) {
|
||||
if server.Config.GroupSearchFilter == "" {
|
||||
memberOf := getArrayAttribute(server.Config.Attr.MemberOf, result)
|
||||
|
||||
return memberOf, nil
|
||||
}
|
||||
|
||||
memberOf, err := server.requestMemberOf(result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return memberOf, nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue