137 lines
3.1 KiB
Go
137 lines
3.1 KiB
Go
// Go Ldap Api
|
|
// Copyright (c) 2022 yo000 <johan@nosd.in>
|
|
//
|
|
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
"strings"
|
|
|
|
"github.com/go-ldap/ldap/v3"
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
type MyLdap struct {
|
|
Conn *ldap.Conn
|
|
Host string
|
|
User string
|
|
Pass string
|
|
BaseDN string
|
|
}
|
|
|
|
var (
|
|
mutex sync.Mutex
|
|
)
|
|
|
|
func connectLdap(myldap *MyLdap) (*MyLdap, error) {
|
|
var err error
|
|
var conLdap *MyLdap
|
|
|
|
myldap.Conn, err = ldap.DialURL(myldap.Host)
|
|
if err != nil {
|
|
//log.Errorf("Error dialing LDAP: %v", err)
|
|
return conLdap, err
|
|
}
|
|
|
|
err = myldap.Conn.Bind(myldap.User, myldap.Pass)
|
|
if err != nil {
|
|
//log.Errorf("Error binding LDAP: %v", err)
|
|
return conLdap, err
|
|
}
|
|
return myldap, err
|
|
}
|
|
|
|
func ldapSearch(conLdap *MyLdap, searchReq *ldap.SearchRequest, attempt int) (*ldap.SearchResult, error) {
|
|
var err error
|
|
if conLdap.Conn == nil {
|
|
conLdap, err = connectLdap(conLdap)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
mutex.Lock()
|
|
result, err := conLdap.Conn.Search(searchReq)
|
|
mutex.Unlock()
|
|
// Manage connection errors here
|
|
if err != nil && strings.HasSuffix(err.Error(), "ldap: connection closed") {
|
|
log.Error("LDAP connection closed, retrying")
|
|
mutex.Lock()
|
|
conLdap.Conn.Close()
|
|
conLdap, err = connectLdap(conLdap)
|
|
mutex.Unlock()
|
|
if err != nil {
|
|
return result, err
|
|
} else {
|
|
attempt = attempt + 1
|
|
return ldapSearch(conLdap, searchReq, attempt)
|
|
}
|
|
}
|
|
|
|
return result, err
|
|
}
|
|
|
|
func doLdapSearch(myldap *MyLdap, baseDn, cn, class, attributes string) (*ldap.SearchResult, error) {
|
|
var filter string
|
|
var realBaseDn string
|
|
var realAttributes []string
|
|
|
|
// Build search filter
|
|
if strings.EqualFold(class, "ALL") {
|
|
filter = fmt.Sprintf("(objectClass=*)")
|
|
} else {
|
|
filter = fmt.Sprintf("(objectClass=%s)", class)
|
|
}
|
|
|
|
if false == strings.EqualFold(cn, "ALL") {
|
|
filter = fmt.Sprintf("(&%s(cn=%s))", filter, ldap.EscapeFilter(cn))
|
|
}
|
|
|
|
log.Debugf("LDAP search filter: %s", filter)
|
|
|
|
// Build absolute search base DN from configuration & provided DN (which is relative)
|
|
if strings.EqualFold(baseDn, "ALL") || len(baseDn) == 0 {
|
|
realBaseDn = fmt.Sprintf("%s", myldap.BaseDN)
|
|
} else {
|
|
realBaseDn = fmt.Sprintf("%s,%s", baseDn, myldap.BaseDN)
|
|
}
|
|
|
|
log.Debugf("LDAP search base dn: %s", realBaseDn)
|
|
|
|
// Build attributes array
|
|
if false == strings.EqualFold(attributes, "ALL") {
|
|
for _, a := range strings.Split(attributes, ",") {
|
|
realAttributes = append(realAttributes, a)
|
|
}
|
|
}
|
|
|
|
log.Debugf("LDAP search attributes to return (all if empty): %v", realAttributes)
|
|
|
|
searchReq := ldap.NewSearchRequest(realBaseDn, ldap.ScopeWholeSubtree, 0, 0, 0,
|
|
false, filter, realAttributes, []ldap.Control{})
|
|
|
|
result, err := ldapSearch(myldap, searchReq, 0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func findUserFullDN(myldap *MyLdap, username string) (string, error) {
|
|
sr, err := doLdapSearch(myldap, "", username, "ALL", "")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if len(sr.Entries) == 0 {
|
|
return "", fmt.Errorf("User not found with cn=%s", username)
|
|
} else if len(sr.Entries) > 1 {
|
|
return "", fmt.Errorf("More than one object (%d) found with cn=%s", len(sr.Entries), username)
|
|
}
|
|
|
|
result := sr.Entries[0].DN
|
|
|
|
return result, nil
|
|
}
|