outposts/ldap: improve parsing of LDAP filters

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
Jens Langhammer 2021-07-23 15:41:09 +02:00
parent 81b9b37e5e
commit 6a8be0dc71
3 changed files with 65 additions and 28 deletions

View File

@ -46,6 +46,11 @@ func (pi *ProviderInstance) Search(req SearchRequest) (ldap.ServerSearchResult,
} }
accsp.Finish() accsp.Finish()
parsedFilter, err := ldap.CompileFilter(req.Filter)
if err != nil {
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, fmt.Errorf("Search Error: error parsing filter: %s", req.Filter)
}
switch filterEntity { switch filterEntity {
default: default:
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, fmt.Errorf("Search Error: unhandled filter type: %s [%s]", filterEntity, req.Filter) return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, fmt.Errorf("Search Error: unhandled filter type: %s [%s]", filterEntity, req.Filter)
@ -59,7 +64,7 @@ func (pi *ProviderInstance) Search(req SearchRequest) (ldap.ServerSearchResult,
go func() { go func() {
defer wg.Done() defer wg.Done()
gapisp := sentry.StartSpan(req.ctx, "authentik.providers.ldap.search.api_group") gapisp := sentry.StartSpan(req.ctx, "authentik.providers.ldap.search.api_group")
groups, _, err := parseFilterForGroup(pi.s.ac.Client.CoreApi.CoreGroupsList(gapisp.Context()), req.Filter).Execute() groups, _, err := parseFilterForGroup(pi.s.ac.Client.CoreApi.CoreGroupsList(gapisp.Context()), parsedFilter).Execute()
gapisp.Finish() gapisp.Finish()
if err != nil { if err != nil {
req.log.WithError(err).Warning("failed to get groups") req.log.WithError(err).Warning("failed to get groups")
@ -75,7 +80,7 @@ func (pi *ProviderInstance) Search(req SearchRequest) (ldap.ServerSearchResult,
go func() { go func() {
defer wg.Done() defer wg.Done()
uapisp := sentry.StartSpan(req.ctx, "authentik.providers.ldap.search.api_user") uapisp := sentry.StartSpan(req.ctx, "authentik.providers.ldap.search.api_user")
users, _, err := parseFilterForUser(pi.s.ac.Client.CoreApi.CoreUsersList(uapisp.Context()), req.Filter).Execute() users, _, err := parseFilterForUser(pi.s.ac.Client.CoreApi.CoreUsersList(uapisp.Context()), parsedFilter).Execute()
uapisp.Finish() uapisp.Finish()
if err != nil { if err != nil {
req.log.WithError(err).Warning("failed to get groups") req.log.WithError(err).Warning("failed to get groups")
@ -90,7 +95,7 @@ func (pi *ProviderInstance) Search(req SearchRequest) (ldap.ServerSearchResult,
entries = append(gEntries, uEntries...) entries = append(gEntries, uEntries...)
case UserObjectClass, "": case UserObjectClass, "":
uapisp := sentry.StartSpan(req.ctx, "authentik.providers.ldap.search.api_user") uapisp := sentry.StartSpan(req.ctx, "authentik.providers.ldap.search.api_user")
users, _, err := parseFilterForUser(pi.s.ac.Client.CoreApi.CoreUsersList(uapisp.Context()), req.Filter).Execute() users, _, err := parseFilterForUser(pi.s.ac.Client.CoreApi.CoreUsersList(uapisp.Context()), parsedFilter).Execute()
uapisp.Finish() uapisp.Finish()
if err != nil { if err != nil {

View File

@ -6,17 +6,13 @@ import (
"goauthentik.io/api" "goauthentik.io/api"
) )
func parseFilterForGroup(req api.ApiCoreGroupsListRequest, filter string) api.ApiCoreGroupsListRequest { func parseFilterForGroup(req api.ApiCoreGroupsListRequest, f *ber.Packet) api.ApiCoreGroupsListRequest {
f, err := ldap.CompileFilter(filter)
if err != nil {
return req
}
switch f.Tag { switch f.Tag {
case ldap.FilterEqualityMatch: case ldap.FilterEqualityMatch:
return parseFilterForGroupSingle(req, f) return parseFilterForGroupSingle(req, f)
case ldap.FilterAnd: case ldap.FilterAnd:
for _, child := range f.Children { for _, child := range f.Children {
req = parseFilterForGroupSingle(req, child) req = parseFilterForGroup(req, child)
} }
return req return req
} }
@ -24,10 +20,30 @@ func parseFilterForGroup(req api.ApiCoreGroupsListRequest, filter string) api.Ap
} }
func parseFilterForGroupSingle(req api.ApiCoreGroupsListRequest, f *ber.Packet) api.ApiCoreGroupsListRequest { func parseFilterForGroupSingle(req api.ApiCoreGroupsListRequest, f *ber.Packet) api.ApiCoreGroupsListRequest {
v := f.Children[1].Value.(string) // We can only handle key = value pairs here
switch f.Children[0].Value.(string) { if len(f.Children) < 2 {
case "cn": return req
return req.Name(v) }
k := f.Children[0].Value
// Ensure key is string
if _, ok := k.(string); !ok {
return req
}
v := f.Children[1].Value
// Null values are ignored
if v == nil {
return req
}
// Switch on type of the value, then check the key
switch vv := v.(type) {
case string:
switch k {
case "cn":
return req.Name(vv)
}
// TODO: Support int
default:
return req
} }
return req return req
} }

View File

@ -6,17 +6,13 @@ import (
"goauthentik.io/api" "goauthentik.io/api"
) )
func parseFilterForUser(req api.ApiCoreUsersListRequest, filter string) api.ApiCoreUsersListRequest { func parseFilterForUser(req api.ApiCoreUsersListRequest, f *ber.Packet) api.ApiCoreUsersListRequest {
f, err := ldap.CompileFilter(filter)
if err != nil {
return req
}
switch f.Tag { switch f.Tag {
case ldap.FilterEqualityMatch: case ldap.FilterEqualityMatch:
return parseFilterForUserSingle(req, f) return parseFilterForUserSingle(req, f)
case ldap.FilterAnd: case ldap.FilterAnd:
for _, child := range f.Children { for _, child := range f.Children {
req = parseFilterForUserSingle(req, child) req = parseFilterForUser(req, child)
} }
return req return req
} }
@ -24,15 +20,35 @@ func parseFilterForUser(req api.ApiCoreUsersListRequest, filter string) api.ApiC
} }
func parseFilterForUserSingle(req api.ApiCoreUsersListRequest, f *ber.Packet) api.ApiCoreUsersListRequest { func parseFilterForUserSingle(req api.ApiCoreUsersListRequest, f *ber.Packet) api.ApiCoreUsersListRequest {
v := f.Children[1].Value.(string) // We can only handle key = value pairs here
switch f.Children[0].Value.(string) { if len(f.Children) < 2 {
case "cn": return req
return req.Username(v) }
case "name": k := f.Children[0].Value
case "displayName": // Ensure key is string
return req.Name(v) if _, ok := k.(string); !ok {
case "mail": return req
return req.Email(v) }
v := f.Children[1].Value
// Null values are ignored
if v == nil {
return req
}
// Switch on type of the value, then check the key
switch vv := v.(type) {
case string:
switch k {
case "cn":
return req.Username(vv)
case "name":
case "displayName":
return req.Name(vv)
case "mail":
return req.Email(vv)
}
// TODO: Support int
default:
return req
} }
return req return req
} }