outposts/ldap: optimise backend Search API requests

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
Jens Langhammer 2021-07-22 20:38:30 +02:00
parent 8cd1223081
commit 0d0dcf8de0
6 changed files with 87 additions and 11 deletions

View file

@ -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")

View file

@ -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)

View file

@ -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 {

View file

@ -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
}

View file

@ -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
}

View file

@ -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()