internal/ldap: Implement search for groups

This commit is contained in:
Michael Aldridge 2020-08-23 00:50:43 -07:00
parent f8157ba366
commit 792f53e2da
3 changed files with 105 additions and 0 deletions

View file

@ -94,4 +94,10 @@ func (s *server) SetDomain(domain string) {
Scope(ldap.SearchRequestHomeSubtree). Scope(ldap.SearchRequestHomeSubtree).
Label("Search - Entities") Label("Search - Entities")
groupSearchDN := "ou=groups," + strings.Join(s.nc, ",")
s.routes.Search(s.handleSearchGroups).
BaseDn(groupSearchDN).
Scope(ldap.SearchRequestHomeSubtree).
Label("Search - Entities")
} }

View file

@ -127,3 +127,99 @@ func entitySearchExprHelper(attr, op, val string) (string, error) {
return predicate + operator + val, nil return predicate + operator + val, nil
} }
func (s *server) handleSearchGroups(w ldap.ResponseWriter, m *ldap.Message) {
ctx := context.Background()
s.l.Debug("Search Groups")
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 = groupSearchExprHelper(string(f.AttributeDesc()), "=", string(f.AssertionValue()))
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
}
s.l.Debug("Searching groups", "expr", expr)
members, err := s.c.GroupSearch(ctx, expr)
if err != nil {
res := ldap.NewSearchResultDoneResponse(ldap.LDAPResultOperationsError)
res.SetDiagnosticMessage(err.Error())
w.Write(res)
return
}
for i := range members {
e, err := s.groupSearchResult(ctx, members[i], r.BaseObject(), r.Attributes())
if err != nil {
res := ldap.NewSearchResultDoneResponse(ldap.LDAPResultOperationsError)
res.SetDiagnosticMessage(err.Error())
w.Write(res)
return
}
w.Write(e)
}
res := ldap.NewSearchResultDoneResponse(ldap.LDAPResultSuccess)
w.Write(res)
}
// groupSearchResult maps a group onto a SearchResultEntry, performing
// the additional lookup for groups to populate the member attribute.
// Though not implemented, the attrs list is plumbed down to this
// level to permit attribute filtering in the future.
func (s *server) groupSearchResult(ctx context.Context, g *pb.Group, dn message.LDAPDN, attrs message.AttributeSelection) (message.SearchResultEntry, error) {
res := ldap.NewSearchResultEntry("cn=" + g.GetName() + "," + string(dn))
res.AddAttribute("cn", message.AttributeValue(g.GetName()))
res.AddAttribute("gidNumber", message.AttributeValue(strconv.Itoa(int(g.GetNumber()))))
members, err := s.c.GroupMembers(ctx, g.GetName())
if err != nil {
return res, err
}
for i := range members {
g := "uid=" + members[i].GetID() + ",ou=entities," + strings.Join(s.nc, ",")
res.AddAttribute("member", message.AttributeValue(g))
}
return res, nil
}
// groupSearchExprHelper helps in mapping ldap search expressions to
// search expressions that NetAuth understands.
func groupSearchExprHelper(attr, op, val string) (string, error) {
var predicate, operator string
switch attr {
case "cn":
predicate = "name"
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
}

View file

@ -13,6 +13,9 @@ type naClient interface {
AuthEntity(context.Context, string, string) error AuthEntity(context.Context, string, string) error
EntitySearch(context.Context, string) ([]*pb.Entity, error) EntitySearch(context.Context, string) ([]*pb.Entity, error)
EntityGroups(context.Context, string) ([]*pb.Group, error) EntityGroups(context.Context, string) ([]*pb.Group, error)
GroupSearch(context.Context, string) ([]*pb.Group, error)
GroupMembers(context.Context, string) ([]*pb.Entity, error)
} }
type server struct { type server struct {