121 lines
2.7 KiB
Go
121 lines
2.7 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: ", 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") {
|
||
|
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
|
||
|
}
|