Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
94a6462
Add: Integrate fastapi-error-map for enhanced error handling in DNS r…
Nov 21, 2025
f87f3f7
Refactor: Update DNS error handling to use centralized error codes an…
Nov 21, 2025
23c8393
Refactor: Consolidate DishkaErrorAwareRoute implementation and update…
Nov 21, 2025
b9b55f6
Refactor: Introduce ProjectPartCodes for error handling and update DN…
Nov 21, 2025
86b767e
Refactor: Update error handling across multiple routers to utilize ce…
Nov 22, 2025
bc9dd3c
Refactor: Enhance error handling in KRB5 and Network routers with cen…
Nov 22, 2025
da0c967
Refactor: Simplify BaseAdapter error handling and update FastAPI rout…
Nov 22, 2025
3b6b6a1
Refactor: Enhance error handling across multiple routers by implement…
Nov 22, 2025
51c8482
Refactor: Standardize route definitions in krb5_router by ensuring co…
Nov 22, 2025
7413f1e
Refactor: Update add_network_policy route to ensure consistent format…
Nov 22, 2025
5b4a0a8
Refactor: Standardize error codes in MFA exceptions for improved cons…
Nov 22, 2025
6de881d
Refactor: Consolidate exception imports in auth and shadow routers fo…
Nov 25, 2025
9d443e3
Refactor: Streamline error handling across multiple routers by centra…
Nov 25, 2025
b64c3f4
Refactor: Update BaseDomainException to require only 'code' attribute…
Nov 25, 2025
a121ff6
Refactor: Introduce AuthorizationError exception and update error han…
Nov 25, 2025
a950185
Refactor: Rename ProjectPartCodes to DoaminCodes for improved clarity…
Nov 25, 2025
17877b6
Refactor: Remove unused DNS exception handlers and consolidate error …
Nov 25, 2025
6e324e8
Refactor: Remove unused import in shadow router for improved code cla…
Dec 16, 2025
3f1b184
Refactor: Update password validation utility in authenticate_user fun…
Dec 16, 2025
e697d92
Refactor: Remove unused 'type' attribute from ErrorResponse class for…
Dec 16, 2025
50413b5
Refactor: Correct spelling of 'DomainCodes' in enums and update refer…
Dec 16, 2025
9c737ef
Refactor: Remove commented 'warn_on_unmapped' lines in LDAP schema ro…
Dec 16, 2025
e8b06af
Refactor: Simplify error handling in user authentication process by c…
Dec 16, 2025
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
16 changes: 0 additions & 16 deletions app/api/audit/adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,11 @@
License: https://github.com/MultiDirectoryLab/MultiDirectory/blob/main/LICENSE
"""

from typing import ParamSpec, TypeVar

from fastapi import status

from api.base_adapter import BaseAdapter
from ldap_protocol.policies.audit.dataclasses import (
AuditDestinationDTO,
AuditPolicyDTO,
)
from ldap_protocol.policies.audit.exception import (
AuditAlreadyExistsError,
AuditNotFoundError,
)
from ldap_protocol.policies.audit.schemas import (
AuditDestinationResponse,
AuditDestinationSchemaRequest,
Expand All @@ -25,18 +17,10 @@
)
from ldap_protocol.policies.audit.service import AuditService

P = ParamSpec("P")
R = TypeVar("R")


class AuditPoliciesAdapter(BaseAdapter[AuditService]):
"""Adapter for audit policies."""

_exceptions_map: dict[type[Exception], int] = {
AuditNotFoundError: status.HTTP_404_NOT_FOUND,
AuditAlreadyExistsError: status.HTTP_409_CONFLICT,
}

async def get_policies(self) -> list[AuditPolicyResponse]:
"""Get all audit policies."""
return [
Expand Down
49 changes: 39 additions & 10 deletions app/api/audit/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,21 @@
"""

from dishka import FromDishka
from dishka.integrations.fastapi import DishkaRoute
from fastapi import APIRouter, Depends, status
from fastapi import Depends, status
from fastapi_error_map.routing import ErrorAwareRouter
from fastapi_error_map.rules import rule

from api.auth.utils import verify_auth
from api.error_routing import (
ERROR_MAP_TYPE,
DishkaErrorAwareRoute,
DomainErrorTranslator,
)
from enums import DomainCodes
from ldap_protocol.policies.audit.exception import (
AuditAlreadyExistsError,
AuditNotFoundError,
)
from ldap_protocol.policies.audit.schemas import (
AuditDestinationResponse,
AuditDestinationSchemaRequest,
Expand All @@ -18,23 +29,37 @@

from .adapter import AuditPoliciesAdapter

audit_router = APIRouter(
translator = DomainErrorTranslator(DomainCodes.AUDIT)


error_map: ERROR_MAP_TYPE = {
AuditNotFoundError: rule(
status=status.HTTP_400_BAD_REQUEST,
translator=translator,
),
AuditAlreadyExistsError: rule(
status=status.HTTP_400_BAD_REQUEST,
translator=translator,
),
}

audit_router = ErrorAwareRouter(
prefix="/audit",
tags=["Audit policy"],
dependencies=[Depends(verify_auth)],
route_class=DishkaRoute,
route_class=DishkaErrorAwareRoute,
)


@audit_router.get("/policies")
@audit_router.get("/policies", error_map=error_map)
async def get_audit_policies(
audit_adapter: FromDishka[AuditPoliciesAdapter],
) -> list[AuditPolicyResponse]:
"""Get all audit policies."""
return await audit_adapter.get_policies()


@audit_router.put("/policy/{policy_id}")
@audit_router.put("/policy/{policy_id}", error_map=error_map)
async def update_audit_policy(
policy_id: int,
policy_data: AuditPolicySchemaRequest,
Expand All @@ -44,15 +69,19 @@ async def update_audit_policy(
return await audit_adapter.update_policy(policy_id, policy_data)


@audit_router.get("/destinations")
@audit_router.get("/destinations", error_map=error_map)
async def get_audit_destinations(
audit_adapter: FromDishka[AuditPoliciesAdapter],
) -> list[AuditDestinationResponse]:
"""Get all audit destinations."""
return await audit_adapter.get_destinations()


@audit_router.post("/destination", status_code=status.HTTP_201_CREATED)
@audit_router.post(
"/destination",
status_code=status.HTTP_201_CREATED,
error_map=error_map,
)
async def create_audit_destination(
destination_data: AuditDestinationSchemaRequest,
audit_adapter: FromDishka[AuditPoliciesAdapter],
Expand All @@ -61,7 +90,7 @@ async def create_audit_destination(
return await audit_adapter.create_destination(destination_data)


@audit_router.delete("/destination/{destination_id}")
@audit_router.delete("/destination/{destination_id}", error_map=error_map)
async def delete_audit_destination(
destination_id: int,
audit_adapter: FromDishka[AuditPoliciesAdapter],
Expand All @@ -70,7 +99,7 @@ async def delete_audit_destination(
await audit_adapter.delete_destination(destination_id)


@audit_router.put("/destination/{destination_id}")
@audit_router.put("/destination/{destination_id}", error_map=error_map)
async def update_audit_destination(
destination_id: int,
destination_data: AuditDestinationSchemaRequest,
Expand Down
32 changes: 1 addition & 31 deletions app/api/auth/adapters/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,54 +7,24 @@
from ipaddress import IPv4Address, IPv6Address

from adaptix.conversion import get_converter
from fastapi import Request, status
from fastapi import Request

from api.base_adapter import BaseAdapter
from ldap_protocol.auth import AuthManager
from ldap_protocol.auth.dto import SetupDTO
from ldap_protocol.auth.exceptions.mfa import (
MFAAPIError,
MFAConnectError,
MFARequiredError,
MissingMFACredentialsError,
)
from ldap_protocol.auth.schemas import (
MFAChallengeResponse,
OAuth2Form,
SetupRequest,
)
from ldap_protocol.dialogue import UserSchema
from ldap_protocol.identity.exceptions import (
AlreadyConfiguredError,
AuthValidationError,
LoginFailedError,
PasswordPolicyError,
UnauthorizedError,
UserNotFoundError,
)
from ldap_protocol.kerberos.exceptions import KRBAPIChangePasswordError

_convert_request_to_dto = get_converter(SetupRequest, SetupDTO)


class AuthFastAPIAdapter(BaseAdapter[AuthManager]):
"""Adapter for using IdentityManager with FastAPI."""

_exceptions_map: dict[type[Exception], int] = {
UnauthorizedError: status.HTTP_401_UNAUTHORIZED,
LoginFailedError: status.HTTP_403_FORBIDDEN,
MFARequiredError: status.HTTP_426_UPGRADE_REQUIRED,
PasswordPolicyError: status.HTTP_422_UNPROCESSABLE_ENTITY,
AuthValidationError: status.HTTP_422_UNPROCESSABLE_ENTITY,
PermissionError: status.HTTP_403_FORBIDDEN,
UserNotFoundError: status.HTTP_404_NOT_FOUND,
KRBAPIChangePasswordError: status.HTTP_424_FAILED_DEPENDENCY,
AlreadyConfiguredError: status.HTTP_423_LOCKED,
MissingMFACredentialsError: status.HTTP_403_FORBIDDEN,
MFAAPIError: status.HTTP_406_NOT_ACCEPTABLE,
MFAConnectError: status.HTTP_406_NOT_ACCEPTABLE,
}

async def login(
self,
form: OAuth2Form,
Expand Down
21 changes: 1 addition & 20 deletions app/api/auth/adapters/mfa.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,33 +11,14 @@

from api.base_adapter import BaseAdapter
from ldap_protocol.auth import MFAManager
from ldap_protocol.auth.exceptions.mfa import (
ForbiddenError,
InvalidCredentialsError,
MFAAPIError,
MFAConnectError,
MFATokenError,
MissingMFACredentialsError,
NetworkPolicyError,
NotFoundError,
)
from ldap_protocol.auth.exceptions.mfa import MFATokenError
from ldap_protocol.auth.schemas import MFACreateRequest, MFAGetResponse
from ldap_protocol.multifactor import MFA_HTTP_Creds, MFA_LDAP_Creds


class MFAFastAPIAdapter(BaseAdapter[MFAManager]):
"""Adapter for using MFAManager with FastAPI."""

_exceptions_map: dict[type[Exception], int] = {
MissingMFACredentialsError: status.HTTP_403_FORBIDDEN,
NetworkPolicyError: status.HTTP_403_FORBIDDEN,
ForbiddenError: status.HTTP_403_FORBIDDEN,
InvalidCredentialsError: status.HTTP_422_UNPROCESSABLE_ENTITY,
NotFoundError: status.HTTP_404_NOT_FOUND,
MFAAPIError: status.HTTP_406_NOT_ACCEPTABLE,
MFAConnectError: status.HTTP_406_NOT_ACCEPTABLE,
}

async def setup_mfa(self, mfa: MFACreateRequest) -> bool:
"""Create or update MFA keys.

Expand Down
8 changes: 3 additions & 5 deletions app/api/auth/adapters/session_gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
from ipaddress import IPv4Address, IPv6Address
from typing import Literal, ParamSpec, TypeVar

from fastapi import status

from api.base_adapter import BaseAdapter
from ldap_protocol.session_storage import SessionRepository

Expand Down Expand Up @@ -54,9 +52,9 @@ class UserSessionsResponseSchema:
class SessionFastAPIGateway(BaseAdapter[SessionRepository]):
"""Base class for session storage."""

_exceptions_map: dict[type[Exception], int] = {
LookupError: status.HTTP_404_NOT_FOUND,
}
def __init__(self, repository: SessionRepository) -> None:
"""Initialize the session gateway with a repository."""
self._service = repository

async def get_user_sessions(
self,
Expand Down
Loading