Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 7 additions & 7 deletions app/api/audit/adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@
License: https://github.com/MultiDirectoryLab/MultiDirectory/blob/main/LICENSE
"""

from api.base_adapter import BaseAdapter
from ldap_protocol.policies.audit.dataclasses import (
AuditDestinationDTO,
AuditPolicyDTO,
)
from ldap_protocol.policies.audit.schemas import (
from api.audit.schemas import (
AuditDestinationResponse,
AuditDestinationSchemaRequest,
AuditPolicyResponse,
AuditPolicySchemaRequest,
)
from api.base_adapter import BaseAdapter
from ldap_protocol.policies.audit.dataclasses import (
AuditDestinationDTO,
AuditPolicyDTO,
)
from ldap_protocol.policies.audit.service import AuditService


Expand Down Expand Up @@ -49,7 +49,7 @@ async def get_destinations(self) -> list[AuditDestinationResponse]:
"""Get all audit destinations."""
return [
AuditDestinationResponse(
id=destination.id, # type: ignore
id=destination.id,
name=destination.name,
service_type=destination.service_type.name.lower(),
host=destination.host,
Expand Down
12 changes: 6 additions & 6 deletions app/api/audit/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@
from fastapi_error_map.routing import ErrorAwareRouter
from fastapi_error_map.rules import rule

from api.audit.schemas import (
AuditDestinationResponse,
AuditDestinationSchemaRequest,
AuditPolicyResponse,
AuditPolicySchemaRequest,
)
from api.auth.utils import verify_auth
from api.error_routing import (
ERROR_MAP_TYPE,
Expand All @@ -21,12 +27,6 @@
AuditAlreadyExistsError,
AuditNotFoundError,
)
from ldap_protocol.policies.audit.schemas import (
AuditDestinationResponse,
AuditDestinationSchemaRequest,
AuditPolicyResponse,
AuditPolicySchemaRequest,
)

from .adapter import AuditPoliciesAdapter

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
"""Audit policies schemas module.
"""Audit schemas.

Copyright (c) 2025 MultiFactor
License: https://github.com/MultiDirectoryLab/MultiDirectory/blob/main/LICENSE
"""

from dataclasses import dataclass

from pydantic import BaseModel, Field

from enums import AuditDestinationProtocolType, AuditDestinationServiceType
Expand All @@ -20,8 +18,7 @@ class AuditPolicySchemaRequest(BaseModel):
severity: str


@dataclass
class AuditPolicyResponse:
class AuditPolicyResponse(BaseModel):
"""Audit policy schema."""

id: int
Expand All @@ -44,8 +41,7 @@ class Config: # noqa: D106
use_enum_values = True


@dataclass
class AuditDestinationResponse:
class AuditDestinationResponse(BaseModel):
"""Audit destination schema."""

id: int
Expand Down
22 changes: 13 additions & 9 deletions app/api/auth/adapters/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,10 @@
from adaptix.conversion import get_converter
from fastapi import Request

from api.auth.schemas import MFAChallengeResponse, OAuth2Form, SetupRequest
from api.base_adapter import BaseAdapter
from ldap_protocol.auth import AuthManager
from ldap_protocol.auth.dto import SetupDTO
from ldap_protocol.auth.schemas import (
MFAChallengeResponse,
OAuth2Form,
SetupRequest,
)
from ldap_protocol.auth.dto import LoginRequestDTO, SetupDTO
from ldap_protocol.dialogue import UserSchema

_convert_request_to_dto = get_converter(SetupRequest, SetupDTO)
Expand All @@ -42,10 +38,13 @@ async def login(
:raises HTTPException: 403 if access is forbidden
(e.g. not in admins, disabled, expired, or policy failed)
:raises HTTPException: 426 if MFA is required
:return: None
:return: MFAChallengeResponse | None
"""
login_dto = await self._service.login(
form=form,
form=LoginRequestDTO(
username=form.username,
password=form.password,
),
url=request.url_for("callback_mfa"),
ip=ip,
user_agent=user_agent,
Expand All @@ -54,7 +53,12 @@ async def login(
self._service.set_new_session_key(
login_dto.session_key,
)
return login_dto.mfa_challenge
if login_dto.mfa_challenge is not None:
return MFAChallengeResponse(
status=login_dto.mfa_challenge.status,
message=login_dto.mfa_challenge.message,
)
return None

async def reset_password(
self,
Expand Down
24 changes: 21 additions & 3 deletions app/api/auth/adapters/mfa.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@
from fastapi import status
from fastapi.responses import RedirectResponse

from api.auth.schemas import MFACreateRequest, MFAGetResponse
from api.base_adapter import BaseAdapter
from ldap_protocol.auth import MFAManager
from ldap_protocol.auth.dto import MFACreateRequestDTO
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


Expand All @@ -25,7 +26,15 @@ async def setup_mfa(self, mfa: MFACreateRequest) -> bool:
:param mfa: MFACreateRequest
:return: bool
"""
return await self._service.setup_mfa(mfa)
return await self._service.setup_mfa(
MFACreateRequestDTO(
mfa_key=mfa.mfa_key,
mfa_secret=mfa.mfa_secret,
is_ldap_scope=mfa.is_ldap_scope,
key_name=mfa.key_name,
secret_name=mfa.secret_name,
),
)

async def remove_mfa(self, scope: str) -> None:
"""Delete MFA keys by scope.
Expand All @@ -46,7 +55,16 @@ async def get_mfa(
:param mfa_creds_ldap: MFA_LDAP_Creds
:return: MFAGetResponse
"""
return await self._service.get_mfa(mfa_creds, mfa_creds_ldap)
mfa_get_response = await self._service.get_mfa(
mfa_creds,
mfa_creds_ldap,
)
return MFAGetResponse(
mfa_key=mfa_get_response.mfa_key,
mfa_secret=mfa_get_response.mfa_secret,
mfa_key_ldap=mfa_get_response.mfa_key_ldap,
mfa_secret_ldap=mfa_get_response.mfa_secret_ldap,
)

async def callback_mfa(
self,
Expand Down
6 changes: 1 addition & 5 deletions app/api/auth/router_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from fastapi_error_map.rules import rule

from api.auth.adapters import AuthFastAPIAdapter
from api.auth.schemas import MFAChallengeResponse, OAuth2Form, SetupRequest
from api.auth.utils import get_ip_from_request, get_user_agent_from_request
from api.error_routing import (
ERROR_MAP_TYPE,
Expand All @@ -27,11 +28,6 @@
MFARequiredError,
MissingMFACredentialsError,
)
from ldap_protocol.auth.schemas import (
MFAChallengeResponse,
OAuth2Form,
SetupRequest,
)
from ldap_protocol.dialogue import UserSchema
from ldap_protocol.identity.exceptions import (
AlreadyConfiguredError,
Expand Down
2 changes: 1 addition & 1 deletion app/api/auth/router_mfa.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from fastapi_error_map.rules import rule

from api.auth.adapters import MFAFastAPIAdapter
from api.auth.schemas import MFACreateRequest, MFAGetResponse
from api.auth.utils import (
get_ip_from_request,
get_user_agent_from_request,
Expand All @@ -35,7 +36,6 @@
NetworkPolicyError,
NotFoundError,
)
from ldap_protocol.auth.schemas import MFACreateRequest, MFAGetResponse
from ldap_protocol.multifactor import MFA_HTTP_Creds, MFA_LDAP_Creds

translator = DomainErrorTranslator(DomainCodes.MFA)
Expand Down
35 changes: 2 additions & 33 deletions app/ldap_protocol/auth/schemas.py → app/api/auth/schemas.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,14 @@
"""Schemas for auth module.
"""Auth schemas.
Copyright (c) 2025 MultiFactor
License: https://github.com/MultiDirectoryLab/MultiDirectory/blob/main/LICENSE
"""

import re
from dataclasses import dataclass
from datetime import datetime
from ipaddress import IPv4Address, IPv6Address
from typing import Literal

from fastapi.param_functions import Form
from fastapi.security import OAuth2PasswordRequestForm
from pydantic import (
BaseModel,
ConfigDict,
Field,
SecretStr,
computed_field,
field_validator,
)
from pydantic import BaseModel, SecretStr, computed_field, field_validator

from ldap_protocol.utils.const import EmailStr

Expand Down Expand Up @@ -96,23 +85,3 @@ class MFAChallengeResponse(BaseModel):

status: str
message: str


@dataclass
class LoginDTO:
"""Login Data Transfer Object."""

session_key: str | None
mfa_challenge: MFAChallengeResponse | None


class SessionContentSchema(BaseModel):
"""Session content schema."""

model_config = ConfigDict(extra="allow")

id: int
sign: str = Field("", description="Session signature")
issued: datetime
ip: IPv4Address | IPv6Address
protocol: Literal["ldap", "http"] = "http"
4 changes: 2 additions & 2 deletions app/api/dhcp/adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@
from ipaddress import IPv4Address

from api.base_adapter import BaseAdapter
from ldap_protocol.dhcp import (
AbstractDHCPManager,
from api.dhcp.schemas import (
DHCPChangeStateSchemaRequest,
DHCPLeaseSchemaRequest,
DHCPLeaseSchemaResponse,
Expand All @@ -19,6 +18,7 @@
DHCPSubnetSchemaAddRequest,
DHCPSubnetSchemaResponse,
)
from ldap_protocol.dhcp import AbstractDHCPManager
from ldap_protocol.dhcp.dataclasses import (
DHCPLease,
DHCPOptionData,
Expand Down
22 changes: 11 additions & 11 deletions app/api/dhcp/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,17 @@
from fastapi_error_map.rules import rule

from api.auth.utils import verify_auth
from api.dhcp.schemas import (
DHCPChangeStateSchemaRequest,
DHCPLeaseSchemaRequest,
DHCPLeaseSchemaResponse,
DHCPLeaseToReservationErrorResponse,
DHCPReservationSchemaRequest,
DHCPReservationSchemaResponse,
DHCPStateSchemaResponse,
DHCPSubnetSchemaAddRequest,
DHCPSubnetSchemaResponse,
)
from api.error_routing import (
ERROR_MAP_TYPE,
DishkaErrorAwareRoute,
Expand All @@ -27,17 +38,6 @@
DHCPOperationError,
DHCPValidationError,
)
from ldap_protocol.dhcp.schemas import (
DHCPChangeStateSchemaRequest,
DHCPLeaseSchemaRequest,
DHCPLeaseSchemaResponse,
DHCPLeaseToReservationErrorResponse,
DHCPReservationSchemaRequest,
DHCPReservationSchemaResponse,
DHCPStateSchemaResponse,
DHCPSubnetSchemaAddRequest,
DHCPSubnetSchemaResponse,
)

from .adapter import DHCPAdapter

Expand Down
45 changes: 2 additions & 43 deletions app/ldap_protocol/dhcp/schemas.py → app/api/dhcp/schemas.py
Original file line number Diff line number Diff line change
@@ -1,56 +1,15 @@
"""Schemas for DHCP manager.
"""DHCP schemas.

Copyright (c) 2025 MultiFactor
License: https://github.com/MultiDirectoryLab/MultiDirectory/blob/main/LICENSE
"""

from dataclasses import dataclass, field
from datetime import datetime
from ipaddress import IPv4Address, IPv4Network

from pydantic import BaseModel, field_serializer

from .dataclasses import DHCPLease, DHCPReservation, DHCPSubnet
from .enums import DHCPManagerState, KeaDHCPCommands


@dataclass
class KeaDHCPCommandRequest:
"""Single command request."""

command: KeaDHCPCommands


@dataclass
class KeaDHCPBaseAPIRequest(KeaDHCPCommandRequest):
"""Base request for Kea DHCP API."""

arguments: list[int] | dict[str, str] | None = None
service: list[str] = field(default_factory=lambda: ["dhcp4"])


@dataclass
class KeaDHCPAPISubnetRequest(KeaDHCPCommandRequest):
"""Request for Kea DHCP API to manage subnets."""

subnet4: DHCPSubnet | list[DHCPSubnet]
service: list[str] = field(default_factory=lambda: ["dhcp4"])


@dataclass
class KeaDHCPAPILeaseRequest(KeaDHCPCommandRequest):
"""Request for Kea DHCP API to manage leases."""

lease: DHCPLease
service: list[str] = field(default_factory=lambda: ["dhcp4"])


@dataclass
class KeaDHCPAPIReservationRequest(KeaDHCPCommandRequest):
"""Request for Kea DHCP API to manage reservations."""

arguments: DHCPReservation
service: list[str] = field(default_factory=lambda: ["dhcp4"])
from ldap_protocol.dhcp.enums import DHCPManagerState


class DHCPSubnetSchemaAddRequest(BaseModel):
Expand Down
Loading