Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
86306f9
refactor: change async functions to synchronous in MainProvider
Naksen Dec 9, 2025
b62519d
refactor: optimize query loading for entity type in SearchRequest
Naksen Dec 9, 2025
57354b2
refactor: change loading strategy for Directory group in SearchRequest
Naksen Dec 9, 2025
ef6e467
refactor: change loading strategy from selectinload to joinedload for…
Naksen Dec 9, 2025
4c05c89
refactor: change async functions to synchronous in DHCP manager and A…
Naksen Dec 9, 2025
62e2709
refactor: change async iteration to synchronous for directory results…
Naksen Dec 9, 2025
416987a
refactor: change loading strategy to use contains_eager for Directory…
Naksen Dec 9, 2025
e0465ed
refactor: reorganize import statements in search.py for clarity
Naksen Dec 9, 2025
ab973a8
refactor: add CONTEXT_TYPE class variable to LDAP request classes
Naksen Dec 9, 2025
757a756
refactor: LDAP context provider to use AsyncSessionSearchRequest and …
Naksen Dec 16, 2025
4e0c379
test: enhance LDAP search request context provision with a dedicated …
Naksen Dec 16, 2025
37fde5a
refactor: optimize event processing logic in BaseRequest class
Naksen Dec 16, 2025
6941c94
refactor: add async method to provide LDAP search request context
Naksen Dec 16, 2025
33a7d40
refactor: add blank line for improved readability in LDAP context pro…
Naksen Dec 16, 2025
2e4b692
refactor: specify type for ctx parameter in handle method of AbandonR…
Naksen Dec 16, 2025
8d578da
refactor: remove debug logging for group membership in SearchRequest …
Naksen Dec 16, 2025
165e9f0
refactor: update context handling in BaseRequest class for improved p…
Naksen Dec 16, 2025
848ef18
refactor: change get_search_request_context method to synchronous
Naksen Dec 16, 2025
406426d
refactor: change search context scope back to request
Naksen Jan 13, 2026
ed35abe
test: change search request context scope to request
Naksen Jan 13, 2026
dbcea97
add: abandon request context stub
Naksen Jan 13, 2026
adfd386
refactor: format
Naksen Jan 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 31 additions & 26 deletions app/ioc.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
from ldap_protocol.kerberos.service import KerberosService
from ldap_protocol.kerberos.template_render import KRBTemplateRenderer
from ldap_protocol.ldap_requests.contexts import (
LDAPAbandonRequestContext,
LDAPAddRequestContext,
LDAPBindRequestContext,
LDAPDeleteRequestContext,
Expand Down Expand Up @@ -214,7 +215,7 @@ async def get_kadmin_http(
yield KadminHTTPClient(client)

@provide(scope=Scope.REQUEST)
async def get_kadmin(
def get_kadmin(
self,
client: KadminHTTPClient,
kadmin_class: type[AbstractKadmin],
Expand Down Expand Up @@ -269,14 +270,14 @@ async def get_dns_http_client(
yield DNSManagerHTTPClient(client)

@provide(scope=Scope.REQUEST)
async def get_dns_mngr(
def get_dns_mngr(
self,
settings: DNSManagerSettings,
dns_manager_class: type[AbstractDNSManager],
http_client: DNSManagerHTTPClient,
) -> AsyncIterator[AbstractDNSManager]:
) -> AbstractDNSManager:
"""Get DNSManager class."""
yield dns_manager_class(settings=settings, http_client=http_client)
return dns_manager_class(settings=settings, http_client=http_client)

@provide(scope=Scope.APP)
async def get_redis_for_sessions(
Expand All @@ -293,7 +294,7 @@ async def get_redis_for_sessions(
await client.aclose()

@provide(scope=Scope.APP)
async def get_session_storage(
def get_session_storage(
self,
client: SessionStorageClient,
settings: Settings,
Expand All @@ -306,7 +307,7 @@ async def get_session_storage(
)

@provide()
async def get_normalized_audit_event(
def get_normalized_audit_event(
self,
) -> type[NormalizedAuditEvent]:
"""Get normalized audit event class."""
Expand All @@ -327,13 +328,13 @@ async def get_audit_redis_client(
await client.aclose()

@provide(scope=Scope.APP)
async def get_raw_audit_manager(
def get_raw_audit_manager(
self,
client: AuditRedisClient,
settings: Settings,
) -> AsyncIterator[RawAuditManager]:
) -> RawAuditManager:
"""Get raw audit manager."""
yield RawAuditManager(
return RawAuditManager(
client,
settings.RAW_EVENT_STREAM_NAME,
settings.EVENT_HANDLER_GROUP,
Expand All @@ -342,13 +343,13 @@ async def get_raw_audit_manager(
)

@provide(scope=Scope.APP)
async def get_normalized_audit_manager(
def get_normalized_audit_manager(
self,
client: AuditRedisClient,
settings: Settings,
) -> AsyncIterator[NormalizedAuditManager]:
) -> NormalizedAuditManager:
"""Get raw audit manager."""
yield NormalizedAuditManager(
return NormalizedAuditManager(
client,
settings.NORMALIZED_EVENT_STREAM_NAME,
settings.EVENT_SENDER_GROUP,
Expand All @@ -361,7 +362,7 @@ async def get_normalized_audit_manager(
audit_destination_dao = provide(AuditDestinationDAO, scope=Scope.REQUEST)

@provide(scope=Scope.REQUEST)
async def get_dhcp_manager_repository(
def get_dhcp_manager_repository(
self,
session: AsyncSession,
) -> DHCPManagerRepository:
Expand All @@ -377,20 +378,20 @@ async def get_dhcp_manager_state(
return await dhcp_manager_repository.ensure_state()

@provide(scope=Scope.REQUEST)
async def get_dhcp_mngr_class(
def get_dhcp_mngr_class(
self,
dhcp_state: DHCPManagerState,
) -> type[AbstractDHCPManager]:
"""Get DHCP manager type."""
return await get_dhcp_manager_class(dhcp_state)
return get_dhcp_manager_class(dhcp_state)

@provide(scope=Scope.REQUEST)
async def get_dhcp_api_repository_class(
def get_dhcp_api_repository_class(
self,
dhcp_state: DHCPManagerState,
) -> type[DHCPAPIRepository]:
"""Get DHCP API repository type."""
return await get_dhcp_api_repository_class(dhcp_state)
return get_dhcp_api_repository_class(dhcp_state)

@provide(scope=Scope.APP)
async def get_dhcp_http_client(
Expand All @@ -404,7 +405,7 @@ async def get_dhcp_http_client(
yield DHCPManagerHTTPClient(http_client)

@provide(scope=Scope.REQUEST)
async def get_dhcp_api_repository(
def get_dhcp_api_repository(
self,
http_client: DHCPManagerHTTPClient,
dhcp_api_repository_class: type[DHCPAPIRepository],
Expand All @@ -413,7 +414,7 @@ async def get_dhcp_api_repository(
return dhcp_api_repository_class(http_client)

@provide(scope=Scope.REQUEST)
async def get_dhcp_mngr(
def get_dhcp_mngr(
self,
dhcp_manager_class: type[AbstractDHCPManager],
dhcp_api_repository: DHCPAPIRepository,
Expand Down Expand Up @@ -458,7 +459,7 @@ async def get_dhcp_mngr(
)
password_utils = provide(PasswordUtils, scope=Scope.RUNTIME)

access_manager = provide(AccessManager, scope=Scope.REQUEST)
access_manager = provide(AccessManager, scope=Scope.RUNTIME)
role_dao = provide(RoleDAO, scope=Scope.REQUEST)
ace_dao = provide(AccessControlEntryDAO, scope=Scope.REQUEST)
role_use_case = provide(RoleUseCase, scope=Scope.REQUEST)
Expand Down Expand Up @@ -503,12 +504,16 @@ class LDAPContextProvider(Provider):
LDAPModifyDNRequestContext,
scope=Scope.REQUEST,
)
unbind_request_context = provide(
LDAPUnbindRequestContext,
scope=Scope.REQUEST,
)
search_request_context = provide(
LDAPSearchRequestContext,
scope=Scope.REQUEST,
)
unbind_request_context = provide(
LDAPUnbindRequestContext,
abandon_request_context = provide(
LDAPAbandonRequestContext,
scope=Scope.REQUEST,
)

Expand All @@ -535,7 +540,7 @@ class HTTPProvider(LDAPContextProvider):
)

@provide()
async def get_audit_monitor(
def get_audit_monitor(
self,
session: AsyncSession,
audit_use_case: "AuditUseCase",
Expand Down Expand Up @@ -595,7 +600,7 @@ def get_permissions_provider(
return auth_provider

@provide()
async def get_identity_provider(
def get_identity_provider(
self,
request: Request,
session_storage: SessionStorage,
Expand Down Expand Up @@ -816,7 +821,7 @@ async def get_client(
yield MFAHTTPClient(client)

@provide(provides=MultifactorAPI)
async def get_http_mfa(
def get_http_mfa(
self,
credentials: MFA_HTTP_Creds,
client: MFAHTTPClient,
Expand All @@ -838,7 +843,7 @@ async def get_http_mfa(
)

@provide(provides=LDAPMultiFactorAPI)
async def get_ldap_mfa(
def get_ldap_mfa(
self,
credentials: MFA_LDAP_Creds,
client: MFAHTTPClient,
Expand Down
4 changes: 2 additions & 2 deletions app/ldap_protocol/dhcp/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from .stub import StubDHCPAPIRepository, StubDHCPManager


async def get_dhcp_manager_class(
def get_dhcp_manager_class(
dhcp_state: DHCPManagerState,
) -> type[AbstractDHCPManager]:
"""Get an instance of the DHCP manager."""
Expand All @@ -35,7 +35,7 @@ async def get_dhcp_manager_class(
return StubDHCPManager


async def get_dhcp_api_repository_class(
def get_dhcp_api_repository_class(
dhcp_state: DHCPManagerState,
) -> type[DHCPAPIRepository]:
"""Get an instance of the DHCP API repository."""
Expand Down
4 changes: 3 additions & 1 deletion app/ldap_protocol/ldap_requests/abandon.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from typing import AsyncGenerator, ClassVar

from ldap_protocol.asn1parser import ASN1Row
from ldap_protocol.ldap_requests.contexts import LDAPAbandonRequestContext
from ldap_protocol.objects import ProtocolRequests

from .base import BaseRequest
Expand All @@ -16,6 +17,7 @@
class AbandonRequest(BaseRequest):
"""Abandon protocol."""

CONTEXT_TYPE: ClassVar[type] = LDAPAbandonRequestContext
PROTOCOL_OP: ClassVar[int] = ProtocolRequests.ABANDON
message_id: int

Expand All @@ -27,7 +29,7 @@ def from_data(
"""Create structure from ASN1Row dataclass list."""
return cls(message_id=1)

async def handle(self) -> AsyncGenerator:
async def handle(self, ctx: LDAPAbandonRequestContext) -> AsyncGenerator: # noqa: ARG002
"""Handle message with current user."""
await asyncio.sleep(0)
return
Expand Down
1 change: 1 addition & 0 deletions app/ldap_protocol/ldap_requests/add.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ class AddRequest(BaseRequest):
"""

PROTOCOL_OP: ClassVar[int] = ProtocolRequests.ADD
CONTEXT_TYPE: ClassVar[type] = LDAPAddRequestContext

entry: str = Field(..., description="Any `DistinguishedName`")
attributes: list[PartialAttribute]
Expand Down
62 changes: 33 additions & 29 deletions app/ldap_protocol/ldap_requests/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from ldap_protocol.dependency import resolve_deps
from ldap_protocol.dialogue import LDAPSession
from ldap_protocol.ldap_responses import BaseResponse, LDAPResult
from ldap_protocol.objects import ProtocolRequests
from ldap_protocol.policies.audit.audit_use_case import AuditUseCase
from ldap_protocol.policies.audit.events.factory import (
RawAuditEventBuilderRedis,
Expand Down Expand Up @@ -62,6 +63,7 @@ class _APIProtocol: ...
class BaseRequest(ABC, _APIProtocol, BaseModel):
"""Base request builder."""

CONTEXT_TYPE: ClassVar[type]
handle: ClassVar[handler]
from_data: ClassVar[serializer]
__event_data: dict = {}
Expand Down Expand Up @@ -113,38 +115,39 @@ async def handle_tcp(
container: AsyncContainer,
) -> AsyncIterator[BaseResponse]:
"""Hanlde response with tcp."""
kwargs = await resolve_deps(func=self.handle, container=container)
responses = []
ctx = await container.get(self.CONTEXT_TYPE) # type: ignore

async for response in self.handle(**kwargs):
responses = []
async for response in self.handle(ctx=ctx):
responses.append(response)
yield response

ldap_session = await container.get(LDAPSession)
settings = await container.get(Settings)
audit_use_case = await container.get(AuditUseCase)

if await audit_use_case.check_event_processing_enabled(
self.PROTOCOL_OP,
):
username = getattr(
ldap_session.user,
"user_principal_name",
"ANONYMOUS",
)
event = RawAuditEventBuilderRedis.from_ldap_request(
self,
responses=responses,
username=username,
ip=ldap_session.ip,
protocol="TCP_LDAP",
settings=settings,
context=self.get_event_data(),
)
if self.PROTOCOL_OP != ProtocolRequests.SEARCH:
ldap_session = await container.get(LDAPSession)
settings = await container.get(Settings)
audit_use_case = await container.get(AuditUseCase)

if await audit_use_case.check_event_processing_enabled(
self.PROTOCOL_OP,
):
username = getattr(
ldap_session.user,
"user_principal_name",
"ANONYMOUS",
)
event = RawAuditEventBuilderRedis.from_ldap_request(
self,
responses=responses,
username=username,
ip=ldap_session.ip,
protocol="TCP_LDAP",
settings=settings,
context=self.get_event_data(),
)

ldap_session.event_task_group.create_task(
audit_use_case.manager.send_event(event),
)
ldap_session.event_task_group.create_task(
audit_use_case.manager.send_event(event),
)

async def _handle_api(
self,
Expand All @@ -156,7 +159,8 @@ async def _handle_api(
:param AsyncSession session: db session
:return list[BaseResponse]: list of handled responses
"""
kwargs = await resolve_deps(func=self.handle, container=container)
ctx = await container.get(self.CONTEXT_TYPE) # type: ignore

ldap_session = await container.get(LDAPSession)
settings = await container.get(Settings)
audit_use_case = await container.get(AuditUseCase)
Expand All @@ -168,7 +172,7 @@ async def _handle_api(
else:
log_api.info(f"{get_class_name(self)}[{un}]")

responses = [response async for response in self.handle(**kwargs)]
responses = [response async for response in self.handle(ctx=ctx)]

if settings.DEBUG:
for response in responses:
Expand Down
2 changes: 2 additions & 0 deletions app/ldap_protocol/ldap_requests/bind.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class BindRequest(BaseRequest):
"""Bind request fields mapping."""

PROTOCOL_OP: ClassVar[int] = ProtocolRequests.BIND
CONTEXT_TYPE: ClassVar[type] = LDAPBindRequestContext

version: int
name: str
Expand Down Expand Up @@ -230,6 +231,7 @@ class UnbindRequest(BaseRequest):
"""Remove user from ldap_session."""

PROTOCOL_OP: ClassVar[int] = ProtocolRequests.UNBIND
CONTEXT_TYPE: ClassVar[type] = LDAPUnbindRequestContext

@classmethod
def from_data(
Expand Down
4 changes: 4 additions & 0 deletions app/ldap_protocol/ldap_requests/contexts.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,7 @@ class LDAPModifyDNRequestContext:
access_manager: AccessManager
role_use_case: RoleUseCase
attribute_value_validator: AttributeValueValidator


@dataclass
class LDAPAbandonRequestContext: ...
1 change: 1 addition & 0 deletions app/ldap_protocol/ldap_requests/delete.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class DeleteRequest(BaseRequest):
"""

PROTOCOL_OP: ClassVar[int] = ProtocolRequests.DELETE
CONTEXT_TYPE: ClassVar[type] = LDAPDeleteRequestContext

entry: str

Expand Down
1 change: 1 addition & 0 deletions app/ldap_protocol/ldap_requests/extended.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ class ExtendedRequest(BaseRequest):
"""

PROTOCOL_OP: ClassVar[int] = ProtocolRequests.EXTENDED
CONTEXT_TYPE: ClassVar[type] = LDAPExtendedRequestContext
request_name: LDAPOID
request_value: SerializeAsAny[BaseExtendedValue]

Expand Down
Loading