outposts/ldap: optimise backend Search API requests
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
8cd1223081
commit
0d0dcf8de0
|
@ -99,7 +99,7 @@ func (fe *FlowExecutor) DelegateClientIP(a net.Addr) {
|
||||||
func (fe *FlowExecutor) CheckApplicationAccess(appSlug string) (bool, error) {
|
func (fe *FlowExecutor) CheckApplicationAccess(appSlug string) (bool, error) {
|
||||||
acsp := sentry.StartSpan(fe.Context, "authentik.outposts.flow_executor.check_access")
|
acsp := sentry.StartSpan(fe.Context, "authentik.outposts.flow_executor.check_access")
|
||||||
defer acsp.Finish()
|
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 {
|
if !p.Passing {
|
||||||
fe.log.Info("Access denied for user")
|
fe.log.Info("Access denied for user")
|
||||||
return false, nil
|
return false, nil
|
||||||
|
@ -125,8 +125,9 @@ func (fe *FlowExecutor) Execute() (bool, error) {
|
||||||
func (fe *FlowExecutor) solveFlowChallenge(depth int) (bool, error) {
|
func (fe *FlowExecutor) solveFlowChallenge(depth int) (bool, error) {
|
||||||
defer fe.sp.Finish()
|
defer fe.sp.Finish()
|
||||||
|
|
||||||
|
// Get challenge
|
||||||
gcsp := sentry.StartSpan(fe.Context, "authentik.outposts.flow_executor.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()
|
challenge, _, err := req.Execute()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, errors.New("failed to get challenge")
|
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.SetTag("ak_component", ch.GetComponent())
|
||||||
gcsp.Finish()
|
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() {
|
switch ch.GetComponent() {
|
||||||
case string(StageIdentification):
|
case string(StageIdentification):
|
||||||
responseReq = responseReq.FlowChallengeResponseRequest(api.IdentificationChallengeResponseRequestAsFlowChallengeResponseRequest(api.NewIdentificationChallengeResponseRequest(fe.getAnswer(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())
|
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()
|
response, _, err := responseReq.Execute()
|
||||||
ch = response.GetActualInstance().(ChallengeInt)
|
ch = response.GetActualInstance().(ChallengeInt)
|
||||||
fe.log.WithField("component", ch.GetComponent()).WithField("type", ch.GetType()).Debug("Got response")
|
fe.log.WithField("component", ch.GetComponent()).WithField("type", ch.GetType()).Debug("Got response")
|
||||||
|
|
|
@ -23,7 +23,7 @@ type BindRequest struct {
|
||||||
func (ls *LDAPServer) Bind(bindDN string, bindPW string, conn net.Conn) (ldap.LDAPResultCode, error) {
|
func (ls *LDAPServer) Bind(bindDN string, bindPW string, conn net.Conn) (ldap.LDAPResultCode, error) {
|
||||||
span := sentry.StartSpan(context.TODO(), "authentik.providers.ldap.bind",
|
span := sentry.StartSpan(context.TODO(), "authentik.providers.ldap.bind",
|
||||||
sentry.TransactionName("authentik.providers.ldap.bind"))
|
sentry.TransactionName("authentik.providers.ldap.bind"))
|
||||||
span.SetTag("user", bindDN)
|
span.SetTag("user.username", bindDN)
|
||||||
defer span.Finish()
|
defer span.Finish()
|
||||||
|
|
||||||
bindDN = strings.ToLower(bindDN)
|
bindDN = strings.ToLower(bindDN)
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package ldap
|
package ldap
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -34,8 +33,8 @@ func (pi *ProviderInstance) Search(req SearchRequest) (ldap.ServerSearchResult,
|
||||||
}
|
}
|
||||||
|
|
||||||
pi.boundUsersMutex.RLock()
|
pi.boundUsersMutex.RLock()
|
||||||
defer pi.boundUsersMutex.RUnlock()
|
|
||||||
flags, ok := pi.boundUsers[req.BindDN]
|
flags, ok := pi.boundUsers[req.BindDN]
|
||||||
|
pi.boundUsersMutex.RUnlock()
|
||||||
if !ok {
|
if !ok {
|
||||||
pi.log.Debug("User info not cached")
|
pi.log.Debug("User info not cached")
|
||||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, errors.New("access denied")
|
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)
|
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, fmt.Errorf("Search Error: unhandled filter type: %s [%s]", filterEntity, req.Filter)
|
||||||
case GroupObjectClass:
|
case GroupObjectClass:
|
||||||
gapisp := sentry.StartSpan(req.ctx, "authentik.providers.ldap.search.api_group")
|
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()
|
gapisp.Finish()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, fmt.Errorf("API Error: %s", err)
|
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)))
|
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 {
|
if err != nil {
|
||||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, fmt.Errorf("API Error: %s", err)
|
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, "":
|
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 := 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()
|
uapisp.Finish()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -24,7 +24,9 @@ type SearchRequest struct {
|
||||||
|
|
||||||
func (ls *LDAPServer) Search(bindDN string, searchReq ldap.SearchRequest, conn net.Conn) (ldap.ServerSearchResult, error) {
|
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 := 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()
|
defer span.Finish()
|
||||||
|
|
||||||
|
|
Reference in New Issue