ldap-upstream/internal/ldap/search.go

130 lines
3.7 KiB
Go
Raw Normal View History

2020-08-21 02:03:53 -05:00
package ldap
import (
"context"
"errors"
"strconv"
2020-08-22 22:49:54 -05:00
"strings"
2020-08-21 02:03:53 -05:00
2020-08-22 22:49:54 -05:00
"github.com/ps78674/goldap/message"
2020-08-21 02:03:53 -05:00
ldap "github.com/ps78674/ldapserver"
2020-08-22 22:49:54 -05:00
"github.com/netauth/ldap/internal/buildinfo"
pb "github.com/netauth/protocol"
2020-08-21 02:03:53 -05:00
)
func (s *server) handleSearchDSE(w ldap.ResponseWriter, m *ldap.Message) {
2020-08-22 22:49:54 -05:00
nc := strings.Join(s.nc, ", ")
2020-08-21 02:03:53 -05:00
e := ldap.NewSearchResultEntry("")
e.AddAttribute("vendorName", "NetAuth")
2020-08-22 22:49:54 -05:00
e.AddAttribute("vendorVersion", message.AttributeValue(buildinfo.Version))
2020-08-21 02:03:53 -05:00
e.AddAttribute("objectClass", "top", "extensibleObject")
e.AddAttribute("supportedLDAPVersion", "3")
2020-08-22 22:49:54 -05:00
e.AddAttribute("namingContexts", message.AttributeValue(nc))
2020-08-21 02:03:53 -05:00
w.Write(e)
res := ldap.NewSearchResultDoneResponse(ldap.LDAPResultSuccess)
w.Write(res)
}
func (s *server) handleSearchEntities(w ldap.ResponseWriter, m *ldap.Message) {
ctx := context.Background()
s.l.Debug("Search Entities")
2020-08-21 02:03:53 -05:00
r := m.GetSearchRequest()
// This switch performs stage one of mapping from an ldap
// search expression to a NetAuth search expression. The
// second phase of the mapping happens in another function.
var expr string
var err error
switch r.Filter().(type) {
case message.FilterEqualityMatch:
f := r.Filter().(message.FilterEqualityMatch)
expr, err = entitySearchExprHelper(string(f.AttributeDesc()), "=", string(f.AssertionValue()))
2020-08-21 02:03:53 -05:00
default:
err = errors.New("unsupported filter type")
}
if err != nil {
// If err is non-nil at this point it must mean that
// the above match didn't find a supported filter.
res := ldap.NewSearchResultDoneResponse(ldap.LDAPResultUnwillingToPerform)
res.SetDiagnosticMessage("Filter type not supported")
w.Write(res)
return
2020-08-21 02:03:53 -05:00
}
s.l.Debug("Searching entities", "query", expr)
2020-08-21 02:03:53 -05:00
ents, err := s.c.EntitySearch(ctx, expr)
if err != nil {
res := ldap.NewSearchResultDoneResponse(ldap.LDAPResultOperationsError)
res.SetDiagnosticMessage(err.Error())
w.Write(res)
return
}
for i := range ents {
e, err := s.entitySearchResult(ctx, ents[i], r.BaseObject(), r.Attributes())
if err != nil {
res := ldap.NewSearchResultDoneResponse(ldap.LDAPResultOperationsError)
res.SetDiagnosticMessage(err.Error())
w.Write(res)
return
}
w.Write(e)
}
s.l.Debug("Entities", "res", ents)
2020-08-21 02:03:53 -05:00
res := ldap.NewSearchResultDoneResponse(ldap.LDAPResultSuccess)
w.Write(res)
}
// entitySearchResult maps an entity onto a SearchResultEntry,
// performing the additional lookup for groups to populate the
// memberOf attribute. Though not implemented, the attrs list is
// plumbed down to this level to permit attribute filtering in the
// future.
func (s *server) entitySearchResult(ctx context.Context, e *pb.Entity, dn message.LDAPDN, attrs message.AttributeSelection) (message.SearchResultEntry, error) {
res := ldap.NewSearchResultEntry("uid=" + e.GetID() + "," + string(dn))
res.AddAttribute("uid", message.AttributeValue(e.GetID()))
res.AddAttribute("uidNumber", message.AttributeValue(strconv.Itoa(int(e.GetNumber()))))
grps, err := s.c.EntityGroups(ctx, e.GetID())
if err != nil {
return res, err
}
for i := range grps {
g := "cn=" + grps[i].GetName() + ",ou=groups," + strings.Join(s.nc, ",")
res.AddAttribute("memberOf", message.AttributeValue(g))
}
return res, nil
}
// entitySearchExprHelper helps in mapping ldap search expressions to
// search expressions that NetAuth understands.
func entitySearchExprHelper(attr, op, val string) (string, error) {
var predicate, operator string
switch attr {
case "uid":
predicate = "ID"
default:
return "", errors.New("search attribute is unsupported")
}
switch op {
case "=":
operator = "="
default:
return "", errors.New("search comparison is unsupported")
}
return predicate + operator + val, nil
}