Browse Source

refactor(ldap): refactoring ldap code, #1450

Torkel Ödegaard 10 years ago
parent
commit
14f439f8ba

+ 93 - 0
docker/blocks/openldap/entrypoint.sh

@@ -0,0 +1,93 @@
+#!/bin/bash
+
+# When not limiting the open file descritors limit, the memory consumption of
+# slapd is absurdly high. See https://github.com/docker/docker/issues/8231
+ulimit -n 8192
+
+
+set -e
+
+chown -R openldap:openldap /var/lib/ldap/
+
+if [[ ! -d /etc/ldap/slapd.d ]]; then
+
+    if [[ -z "$SLAPD_PASSWORD" ]]; then
+        echo -n >&2 "Error: Container not configured and SLAPD_PASSWORD not set. "
+        echo >&2 "Did you forget to add -e SLAPD_PASSWORD=... ?"
+        exit 1
+    fi
+
+    if [[ -z "$SLAPD_DOMAIN" ]]; then
+        echo -n >&2 "Error: Container not configured and SLAPD_DOMAIN not set. "
+        echo >&2 "Did you forget to add -e SLAPD_DOMAIN=... ?"
+        exit 1
+    fi
+
+    SLAPD_ORGANIZATION="${SLAPD_ORGANIZATION:-${SLAPD_DOMAIN}}"
+
+    cp -a /etc/ldap.dist/* /etc/ldap
+
+    cat <<-EOF | debconf-set-selections
+        slapd slapd/no_configuration boolean false
+        slapd slapd/password1 password $SLAPD_PASSWORD
+        slapd slapd/password2 password $SLAPD_PASSWORD
+        slapd shared/organization string $SLAPD_ORGANIZATION
+        slapd slapd/domain string $SLAPD_DOMAIN
+        slapd slapd/backend select HDB
+        slapd slapd/allow_ldap_v2 boolean false
+        slapd slapd/purge_database boolean false
+        slapd slapd/move_old_database boolean true
+EOF
+
+    dpkg-reconfigure -f noninteractive slapd >/dev/null 2>&1
+
+    dc_string=""
+
+    IFS="."; declare -a dc_parts=($SLAPD_DOMAIN)
+
+    for dc_part in "${dc_parts[@]}"; do
+        dc_string="$dc_string,dc=$dc_part"
+    done
+
+    base_string="BASE ${dc_string:1}"
+
+    sed -i "s/^#BASE.*/${base_string}/g" /etc/ldap/ldap.conf
+
+    if [[ -n "$SLAPD_CONFIG_PASSWORD" ]]; then
+        password_hash=`slappasswd -s "${SLAPD_CONFIG_PASSWORD}"`
+
+        sed_safe_password_hash=${password_hash//\//\\\/}
+
+        slapcat -n0 -F /etc/ldap/slapd.d -l /tmp/config.ldif
+        sed -i "s/\(olcRootDN: cn=admin,cn=config\)/\1\nolcRootPW: ${sed_safe_password_hash}/g" /tmp/config.ldif
+        rm -rf /etc/ldap/slapd.d/*
+        slapadd -n0 -F /etc/ldap/slapd.d -l /tmp/config.ldif >/dev/null 2>&1
+    fi
+
+    if [[ -n "$SLAPD_ADDITIONAL_SCHEMAS" ]]; then
+        IFS=","; declare -a schemas=($SLAPD_ADDITIONAL_SCHEMAS)
+
+        for schema in "${schemas[@]}"; do
+            slapadd -n0 -F /etc/ldap/slapd.d -l "/etc/ldap/schema/${schema}.ldif" >/dev/null 2>&1
+        done
+    fi
+
+    if [[ -n "$SLAPD_ADDITIONAL_MODULES" ]]; then
+        IFS=","; declare -a modules=($SLAPD_ADDITIONAL_MODULES)
+
+        for module in "${modules[@]}"; do
+             slapadd -n0 -F /etc/ldap/slapd.d -l "/etc/ldap/modules/${module}.ldif" >/dev/null 2>&1
+        done
+    fi
+
+    chown -R openldap:openldap /etc/ldap/slapd.d/
+else
+    slapd_configs_in_env=`env | grep 'SLAPD_'`
+
+    if [ -n "${slapd_configs_in_env:+x}" ]; then
+        echo "Info: Container already configured, therefore ignoring SLAPD_xxx environment variables"
+    fi
+fi
+
+exec "$@"
+

+ 33 - 0
docker/blocks/openldap/modules/memberof.ldif

@@ -0,0 +1,33 @@
+dn: cn=module,cn=config
+cn: module
+objectClass: olcModuleList
+objectClass: top
+olcModulePath: /usr/lib/ldap
+olcModuleLoad: memberof.la
+
+dn: olcOverlay={0}memberof,olcDatabase={1}hdb,cn=config
+objectClass: olcConfig
+objectClass: olcMemberOf
+objectClass: olcOverlayConfig
+objectClass: top
+olcOverlay: memberof
+olcMemberOfDangling: ignore
+olcMemberOfRefInt: TRUE
+olcMemberOfGroupOC: groupOfNames
+olcMemberOfMemberAD: member
+olcMemberOfMemberOfAD: memberOf
+
+dn: cn=module,cn=config
+cn: module
+objectClass: olcModuleList
+objectClass: top
+olcModulePath: /usr/lib/ldap
+olcModuleLoad: refint.la
+
+dn: olcOverlay={1}refint,olcDatabase={1}hdb,cn=config
+objectClass: olcConfig
+objectClass: olcOverlayConfig
+objectClass: olcRefintConfig
+objectClass: top
+olcOverlay: {1}refint
+olcRefintAttribute: memberof member manager owner

+ 83 - 43
pkg/auth/ldap.go

@@ -5,19 +5,24 @@ import (
 	"fmt"
 
 	"github.com/go-ldap/ldap"
-	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/log"
-	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/setting"
 )
 
 func init() {
 	setting.LdapServers = []*setting.LdapServerConf{
 		&setting.LdapServerConf{
-			UseSSL: false,
-			Host:   "127.0.0.1",
-			Port:   "389",
-			BindDN: "cn=%s,dc=grafana,dc=org",
+			UseSSL:        false,
+			Host:          "127.0.0.1",
+			Port:          "389",
+			BindDN:        "cn=%s,dc=grafana,dc=org",
+			AttrName:      "givenName",
+			AttrSurname:   "sn",
+			AttrUsername:  "cn",
+			AttrMemberOf:  "memberOf",
+			AttrEmail:     "email",
+			SearchFilter:  "(cn=%s)",
+			SearchBaseDNs: []string{"dc=grafana,dc=org"},
 		},
 	}
 }
@@ -27,6 +32,14 @@ type ldapAuther struct {
 	conn   *ldap.Conn
 }
 
+type ldapUserInfo struct {
+	FirstName string
+	LastName  string
+	Username  string
+	Email     string
+	MemberOf  []string
+}
+
 func NewLdapAuthenticator(server *setting.LdapServerConf) *ldapAuther {
 	return &ldapAuther{
 		server: server,
@@ -51,60 +64,87 @@ func (a *ldapAuther) login(query *AuthenticateUserQuery) error {
 	}
 	defer a.conn.Close()
 
-	bindPath := fmt.Sprintf(a.server.BindDN, query.Username)
-
-	if err := a.conn.Bind(bindPath, query.Password); err != nil {
-		if ldapErr, ok := err.(*ldap.Error); ok {
-			if ldapErr.ResultCode == 49 {
-				return ErrInvalidCredentials
-			}
-		}
+	// perform initial authentication
+	if err := a.initialBind(query.Username, query.Password); err != nil {
 		return err
 	}
 
-	searchReq := ldap.SearchRequest{
-		BaseDN:       "dc=grafana,dc=org",
-		Scope:        ldap.ScopeWholeSubtree,
-		DerefAliases: ldap.NeverDerefAliases,
-		Attributes:   []string{"sn", "email", "givenName", "memberOf"},
-		Filter:       fmt.Sprintf("(cn=%s)", query.Username),
-	}
-
-	result, err := a.conn.Search(&searchReq)
-	if err != nil {
+	// find user entry & attributes
+	if user, err := a.searchForUser(query.Username); err != nil {
 		return err
+	} else {
+		log.Info("Surname: %s", user.LastName)
+		log.Info("givenName: %s", user.FirstName)
+		log.Info("email: %s", user.Email)
+		log.Info("memberOf: %s", user.MemberOf)
 	}
 
-	if len(result.Entries) == 0 {
-		return errors.New("Ldap search matched no entry, please review your filter setting.")
+	return errors.New("Aasd")
+}
+
+func (a *ldapAuther) initialBind(username, userPassword string) error {
+	if a.server.BindPassword != "" {
+		userPassword = a.server.BindPassword
 	}
 
-	if len(result.Entries) > 1 {
-		return errors.New("Ldap search matched mopre than one entry, please review your filter setting")
+	bindPath := fmt.Sprintf(a.server.BindDN, username)
+
+	if err := a.conn.Bind(bindPath, userPassword); err != nil {
+		if ldapErr, ok := err.(*ldap.Error); ok {
+			if ldapErr.ResultCode == 49 {
+				return ErrInvalidCredentials
+			}
+		}
+		return err
 	}
 
-	surname := getLdapAttr("sn", result)
-	givenName := getLdapAttr("givenName", result)
-	email := getLdapAttr("email", result)
-	memberOf := getLdapAttrArray("memberOf", result)
+	return nil
+}
 
-	log.Info("Surname: %s", surname)
-	log.Info("givenName: %s", givenName)
-	log.Info("email: %s", email)
-	log.Info("memberOf: %s", memberOf)
+func (a *ldapAuther) searchForUser(username string) (*ldapUserInfo, error) {
+	var searchResult *ldap.SearchResult
+	var err error
 
-	userQuery := m.GetUserByLoginQuery{LoginOrEmail: query.Username}
-	err = bus.Dispatch(&userQuery)
+	for _, searchBase := range a.server.SearchBaseDNs {
+		searchReq := ldap.SearchRequest{
+			BaseDN:       searchBase,
+			Scope:        ldap.ScopeWholeSubtree,
+			DerefAliases: ldap.NeverDerefAliases,
+			Attributes: []string{
+				a.server.AttrUsername,
+				a.server.AttrSurname,
+				a.server.AttrEmail,
+				a.server.AttrName,
+				a.server.AttrMemberOf,
+			},
+			Filter: fmt.Sprintf(a.server.SearchFilter, username),
+		}
 
-	if err != nil {
-		if err == m.ErrUserNotFound {
+		searchResult, err = a.conn.Search(&searchReq)
+		if err != nil {
+			return nil, err
+		}
+
+		if len(searchResult.Entries) > 0 {
+			break
 		}
-		return err
 	}
 
-	query.User = userQuery.Result
+	if len(searchResult.Entries) == 0 {
+		return nil, errors.New("Ldap search matched no entry, please review your filter setting.")
+	}
 
-	return nil
+	if len(searchResult.Entries) > 1 {
+		return nil, errors.New("Ldap search matched mopre than one entry, please review your filter setting")
+	}
+
+	return &ldapUserInfo{
+		LastName:  getLdapAttr(a.server.AttrSurname, searchResult),
+		FirstName: getLdapAttr(a.server.AttrName, searchResult),
+		Username:  getLdapAttr(a.server.AttrUsername, searchResult),
+		Email:     getLdapAttr(a.server.AttrEmail, searchResult),
+		MemberOf:  getLdapAttrArray(a.server.AttrMemberOf, searchResult),
+	}, nil
 }
 
 func getLdapAttr(name string, result *ldap.SearchResult) string {

+ 2 - 2
pkg/setting/setting_ldap.go

@@ -15,10 +15,10 @@ type LdapServerConf struct {
 	AttrUsername string
 	AttrName     string
 	AttrSurname  string
-	AttrMail     string
+	AttrEmail    string
 	AttrMemberOf string
 
-	SearchFilter  []string
+	SearchFilter  string
 	SearchBaseDNs []string
 
 	LdapMemberMap []LdapMemberToOrgRole