glapi/ldap.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
}