From 0d0dcf8de07885089b4790796888904a26b4779b Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Thu, 22 Jul 2021 20:38:30 +0200 Subject: [PATCH] outposts/ldap: optimise backend Search API requests Signed-off-by: Jens Langhammer --- internal/outpost/flow.go | 10 +++-- internal/outpost/ldap/bind.go | 2 +- internal/outpost/ldap/instance_search.go | 11 +++--- .../outpost/ldap/instance_search_group.go | 33 ++++++++++++++++ internal/outpost/ldap/instance_search_user.go | 38 +++++++++++++++++++ internal/outpost/ldap/search.go | 4 +- 6 files changed, 87 insertions(+), 11 deletions(-) create mode 100644 internal/outpost/ldap/instance_search_group.go create mode 100644 internal/outpost/ldap/instance_search_user.go diff --git a/internal/outpost/flow.go b/internal/outpost/flow.go index 1d2fa84ab..0ffbdabf6 100644 --- a/internal/outpost/flow.go +++ b/internal/outpost/flow.go @@ -99,7 +99,7 @@ func (fe *FlowExecutor) DelegateClientIP(a net.Addr) { func (fe *FlowExecutor) CheckApplicationAccess(appSlug string) (bool, error) { acsp := sentry.StartSpan(fe.Context, "authentik.outposts.flow_executor.check_access") defer acsp.Finish() - p, _, err := fe.api.CoreApi.CoreApplicationsCheckAccessRetrieve(context.Background(), appSlug).Execute() + p, _, err := fe.api.CoreApi.CoreApplicationsCheckAccessRetrieve(acsp.Context(), appSlug).Execute() if !p.Passing { fe.log.Info("Access denied for user") return false, nil @@ -125,8 +125,9 @@ func (fe *FlowExecutor) Execute() (bool, error) { func (fe *FlowExecutor) solveFlowChallenge(depth int) (bool, error) { defer fe.sp.Finish() + // Get challenge gcsp := sentry.StartSpan(fe.Context, "authentik.outposts.flow_executor.get_challenge") - req := fe.api.FlowsApi.FlowsExecutorGet(context.Background(), fe.flowSlug).Query(fe.Params.Encode()) + req := fe.api.FlowsApi.FlowsExecutorGet(gcsp.Context(), fe.flowSlug).Query(fe.Params.Encode()) challenge, _, err := req.Execute() if err != nil { return false, errors.New("failed to get challenge") @@ -137,7 +138,9 @@ func (fe *FlowExecutor) solveFlowChallenge(depth int) (bool, error) { gcsp.SetTag("ak_component", ch.GetComponent()) gcsp.Finish() - responseReq := fe.api.FlowsApi.FlowsExecutorSolve(context.Background(), fe.flowSlug).Query(fe.Params.Encode()) + // Resole challenge + scsp := sentry.StartSpan(fe.Context, "authentik.outposts.flow_executor.solve_challenge") + responseReq := fe.api.FlowsApi.FlowsExecutorSolve(scsp.Context(), fe.flowSlug).Query(fe.Params.Encode()) switch ch.GetComponent() { case string(StageIdentification): responseReq = responseReq.FlowChallengeResponseRequest(api.IdentificationChallengeResponseRequestAsFlowChallengeResponseRequest(api.NewIdentificationChallengeResponseRequest(fe.getAnswer(StageIdentification)))) @@ -168,7 +171,6 @@ func (fe *FlowExecutor) solveFlowChallenge(depth int) (bool, error) { return false, fmt.Errorf("unsupported challenge type %s", ch.GetComponent()) } - scsp := sentry.StartSpan(fe.Context, "authentik.outposts.flow_executor.solve_challenge") response, _, err := responseReq.Execute() ch = response.GetActualInstance().(ChallengeInt) fe.log.WithField("component", ch.GetComponent()).WithField("type", ch.GetType()).Debug("Got response") diff --git a/internal/outpost/ldap/bind.go b/internal/outpost/ldap/bind.go index b8e22f87c..c2f973647 100644 --- a/internal/outpost/ldap/bind.go +++ b/internal/outpost/ldap/bind.go @@ -23,7 +23,7 @@ type BindRequest struct { func (ls *LDAPServer) Bind(bindDN string, bindPW string, conn net.Conn) (ldap.LDAPResultCode, error) { span := sentry.StartSpan(context.TODO(), "authentik.providers.ldap.bind", sentry.TransactionName("authentik.providers.ldap.bind")) - span.SetTag("user", bindDN) + span.SetTag("user.username", bindDN) defer span.Finish() bindDN = strings.ToLower(bindDN) diff --git a/internal/outpost/ldap/instance_search.go b/internal/outpost/ldap/instance_search.go index f5abd1c1d..b2c6e1d40 100644 --- a/internal/outpost/ldap/instance_search.go +++ b/internal/outpost/ldap/instance_search.go @@ -1,7 +1,6 @@ package ldap import ( - "context" "errors" "fmt" "strings" @@ -34,8 +33,8 @@ func (pi *ProviderInstance) Search(req SearchRequest) (ldap.ServerSearchResult, } pi.boundUsersMutex.RLock() - defer pi.boundUsersMutex.RUnlock() flags, ok := pi.boundUsers[req.BindDN] + pi.boundUsersMutex.RUnlock() if !ok { pi.log.Debug("User info not cached") return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, errors.New("access denied") @@ -51,7 +50,7 @@ func (pi *ProviderInstance) Search(req SearchRequest) (ldap.ServerSearchResult, return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, fmt.Errorf("Search Error: unhandled filter type: %s [%s]", filterEntity, req.Filter) case GroupObjectClass: gapisp := sentry.StartSpan(req.ctx, "authentik.providers.ldap.search.api_group") - groups, _, err := pi.s.ac.Client.CoreApi.CoreGroupsList(context.Background()).Execute() + groups, _, err := parseFilterForGroup(pi.s.ac.Client.CoreApi.CoreGroupsList(gapisp.Context()), req.Filter).Execute() gapisp.Finish() if err != nil { return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, fmt.Errorf("API Error: %s", err) @@ -62,7 +61,9 @@ func (pi *ProviderInstance) Search(req SearchRequest) (ldap.ServerSearchResult, entries = append(entries, pi.GroupEntry(pi.APIGroupToLDAPGroup(g))) } - users, _, err := pi.s.ac.Client.CoreApi.CoreUsersList(context.Background()).Execute() + 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() + uapisp.Finish() if err != nil { return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, fmt.Errorf("API Error: %s", err) } @@ -72,7 +73,7 @@ func (pi *ProviderInstance) Search(req SearchRequest) (ldap.ServerSearchResult, } case UserObjectClass, "": uapisp := sentry.StartSpan(req.ctx, "authentik.providers.ldap.search.api_user") - users, _, err := pi.s.ac.Client.CoreApi.CoreUsersList(context.Background()).Execute() + users, _, err := parseFilterForUser(pi.s.ac.Client.CoreApi.CoreUsersList(uapisp.Context()), req.Filter).Execute() uapisp.Finish() if err != nil { diff --git a/internal/outpost/ldap/instance_search_group.go b/internal/outpost/ldap/instance_search_group.go new file mode 100644 index 000000000..6c629a702 --- /dev/null +++ b/internal/outpost/ldap/instance_search_group.go @@ -0,0 +1,33 @@ +package ldap + +import ( + ber "github.com/nmcclain/asn1-ber" + "github.com/nmcclain/ldap" + "goauthentik.io/api" +) + +func parseFilterForGroup(req api.ApiCoreGroupsListRequest, filter string) api.ApiCoreGroupsListRequest { + f, err := ldap.CompileFilter(filter) + if err != nil { + return req + } + switch f.Tag { + case ldap.FilterEqualityMatch: + return parseFilterForGroupSingle(req, f) + case ldap.FilterAnd: + for _, child := range f.Children { + req = parseFilterForGroupSingle(req, child) + } + return req + } + return req +} + +func parseFilterForGroupSingle(req api.ApiCoreGroupsListRequest, f *ber.Packet) api.ApiCoreGroupsListRequest { + v := f.Children[1].Value.(string) + switch f.Children[0].Value.(string) { + case "cn": + return req.Name(v) + } + return req +} diff --git a/internal/outpost/ldap/instance_search_user.go b/internal/outpost/ldap/instance_search_user.go new file mode 100644 index 000000000..e5bafa0d9 --- /dev/null +++ b/internal/outpost/ldap/instance_search_user.go @@ -0,0 +1,38 @@ +package ldap + +import ( + ber "github.com/nmcclain/asn1-ber" + "github.com/nmcclain/ldap" + "goauthentik.io/api" +) + +func parseFilterForUser(req api.ApiCoreUsersListRequest, filter string) api.ApiCoreUsersListRequest { + f, err := ldap.CompileFilter(filter) + if err != nil { + return req + } + switch f.Tag { + case ldap.FilterEqualityMatch: + return parseFilterForUserSingle(req, f) + case ldap.FilterAnd: + for _, child := range f.Children { + req = parseFilterForUserSingle(req, child) + } + return req + } + return req +} + +func parseFilterForUserSingle(req api.ApiCoreUsersListRequest, f *ber.Packet) api.ApiCoreUsersListRequest { + v := f.Children[1].Value.(string) + switch f.Children[0].Value.(string) { + case "cn": + return req.Username(v) + case "name": + case "displayName": + return req.Name(v) + case "mail": + return req.Email(v) + } + return req +} diff --git a/internal/outpost/ldap/search.go b/internal/outpost/ldap/search.go index 9fb7ce8da..dad0680bb 100644 --- a/internal/outpost/ldap/search.go +++ b/internal/outpost/ldap/search.go @@ -24,7 +24,9 @@ type SearchRequest struct { func (ls *LDAPServer) Search(bindDN string, searchReq ldap.SearchRequest, conn net.Conn) (ldap.ServerSearchResult, error) { span := sentry.StartSpan(context.TODO(), "authentik.providers.ldap.search", sentry.TransactionName("authentik.providers.ldap.search")) - span.SetTag("user", bindDN) + span.SetTag("user.username", bindDN) + span.SetTag("ak_filter", searchReq.Filter) + span.SetTag("ak_base_dn", searchReq.BaseDN) defer span.Finish()