Loading docker-compose.dev.yaml +17 −0 Changes for docker-compose.dev.yaml: 17 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -23,5 +23,22 @@ services: ports: - "4222:4222" keycloak: image: quay.io/keycloak/keycloak:26.1.4 container_name: fm-keycloak command: ["start-dev", "--import-realm"] environment: KC_BOOTSTRAP_ADMIN_USERNAME: admin KC_BOOTSTRAP_ADMIN_PASSWORD: admin ports: - "8090:8080" volumes: - ./dev/keycloak:/opt/keycloak/data/import healthcheck: test: ["CMD-SHELL", "exec 3<>/dev/tcp/127.0.0.1/8080"] interval: 3s timeout: 3s retries: 30 volumes: fm-pgdata: pyrightconfig.json 0 → 100644 +22 −0 Changes for pyrightconfig.json: 22 added lines, 0 removed lines. Original line number Diff line number Diff line { "typeshedPath": "/home/sergio/.local/share/nvim/mason/packages/pyright/node_modules/pyright/dist/typeshed-fallback", "venvPath": ".", "venv": ".venv", "pythonVersion": "3.12", "include": ["src/federation_manager", "tests"], "exclude": [ ".venv", "**/__pycache__", "src/adapters", "src/api", "src/clients", "src/conf", "src/deploy", "src/models", "src/static", "src/swagger", "src/templates", "src/test" ], "extraPaths": ["src"] } src/federation_manager/application/authorization.py 0 → 100644 +48 −0 Changes for src/federation_manager/application/authorization.py: 48 added lines, 0 removed lines. Original line number Diff line number Diff line from dataclasses import dataclass from datetime import datetime, timezone from uuid import UUID from federation_manager.application.authentication import PartnerAuthenticator from federation_manager.domain.errors import AgreementExpired, AgreementViolation from federation_manager.domain.models import Agreement, PartnerOP from federation_manager.domain.ports import AgreementRepositoryPort @dataclass class AuthorizedRequest: partner: PartnerOP agreement: Agreement service_specification_id: UUID class FederationAuthorizer: def __init__( self, authenticator: PartnerAuthenticator, agreement_repo: AgreementRepositoryPort, ) -> None: self._authenticator = authenticator self._agreement_repo = agreement_repo async def authorize( self, cert_thumbprint: str, api_type: str, app_id: str, zone_id: UUID | None = None, ) -> AuthorizedRequest: partner = await self._authenticator.authenticate(cert_thumbprint) agreement = await self._agreement_repo.find_active_for_partner(partner.id) if agreement is None or not agreement.is_valid_at(datetime.now(timezone.utc)): raise AgreementExpired if not agreement.permits_api(api_type): raise AgreementViolation if zone_id is not None and not agreement.permits_zone(zone_id): raise AgreementViolation spec_id = agreement.resolve_spec_id(app_id) if spec_id is None: raise AgreementViolation return AuthorizedRequest(partner, agreement, spec_id) src/federation_manager/domain/errors.py +8 −0 Changes for src/federation_manager/domain/errors.py: 8 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -16,3 +16,11 @@ class PartnerNotActive(FederationError): def __init__(self, status: str) -> None: super().__init__(f"partner status is {status}") self.status = status class AgreementExpired(FederationError): pass class AgreementViolation(FederationError): pass src/federation_manager/domain/ports.py +6 −1 Changes for src/federation_manager/domain/ports.py: 6 added lines, 1 removed line. Original line number Diff line number Diff line from typing import Protocol from uuid import UUID from federation_manager.domain.models import PartnerOP from federation_manager.domain.models import Agreement, PartnerOP class PartnerRepositoryPort(Protocol): async def find_by_cert_thumbprint(self, thumbprint: str) -> PartnerOP | None: ... class AgreementRepositoryPort(Protocol): async def find_active_for_partner(self, partner_id: UUID) -> Agreement | None: ... class DataBusPublisherPort(Protocol): async def publish(self, subject: str, payload: dict[str, object]) -> None: ... Loading
docker-compose.dev.yaml +17 −0 Changes for docker-compose.dev.yaml: 17 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -23,5 +23,22 @@ services: ports: - "4222:4222" keycloak: image: quay.io/keycloak/keycloak:26.1.4 container_name: fm-keycloak command: ["start-dev", "--import-realm"] environment: KC_BOOTSTRAP_ADMIN_USERNAME: admin KC_BOOTSTRAP_ADMIN_PASSWORD: admin ports: - "8090:8080" volumes: - ./dev/keycloak:/opt/keycloak/data/import healthcheck: test: ["CMD-SHELL", "exec 3<>/dev/tcp/127.0.0.1/8080"] interval: 3s timeout: 3s retries: 30 volumes: fm-pgdata:
pyrightconfig.json 0 → 100644 +22 −0 Changes for pyrightconfig.json: 22 added lines, 0 removed lines. Original line number Diff line number Diff line { "typeshedPath": "/home/sergio/.local/share/nvim/mason/packages/pyright/node_modules/pyright/dist/typeshed-fallback", "venvPath": ".", "venv": ".venv", "pythonVersion": "3.12", "include": ["src/federation_manager", "tests"], "exclude": [ ".venv", "**/__pycache__", "src/adapters", "src/api", "src/clients", "src/conf", "src/deploy", "src/models", "src/static", "src/swagger", "src/templates", "src/test" ], "extraPaths": ["src"] }
src/federation_manager/application/authorization.py 0 → 100644 +48 −0 Changes for src/federation_manager/application/authorization.py: 48 added lines, 0 removed lines. Original line number Diff line number Diff line from dataclasses import dataclass from datetime import datetime, timezone from uuid import UUID from federation_manager.application.authentication import PartnerAuthenticator from federation_manager.domain.errors import AgreementExpired, AgreementViolation from federation_manager.domain.models import Agreement, PartnerOP from federation_manager.domain.ports import AgreementRepositoryPort @dataclass class AuthorizedRequest: partner: PartnerOP agreement: Agreement service_specification_id: UUID class FederationAuthorizer: def __init__( self, authenticator: PartnerAuthenticator, agreement_repo: AgreementRepositoryPort, ) -> None: self._authenticator = authenticator self._agreement_repo = agreement_repo async def authorize( self, cert_thumbprint: str, api_type: str, app_id: str, zone_id: UUID | None = None, ) -> AuthorizedRequest: partner = await self._authenticator.authenticate(cert_thumbprint) agreement = await self._agreement_repo.find_active_for_partner(partner.id) if agreement is None or not agreement.is_valid_at(datetime.now(timezone.utc)): raise AgreementExpired if not agreement.permits_api(api_type): raise AgreementViolation if zone_id is not None and not agreement.permits_zone(zone_id): raise AgreementViolation spec_id = agreement.resolve_spec_id(app_id) if spec_id is None: raise AgreementViolation return AuthorizedRequest(partner, agreement, spec_id)
src/federation_manager/domain/errors.py +8 −0 Changes for src/federation_manager/domain/errors.py: 8 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -16,3 +16,11 @@ class PartnerNotActive(FederationError): def __init__(self, status: str) -> None: super().__init__(f"partner status is {status}") self.status = status class AgreementExpired(FederationError): pass class AgreementViolation(FederationError): pass
src/federation_manager/domain/ports.py +6 −1 Changes for src/federation_manager/domain/ports.py: 6 added lines, 1 removed line. Original line number Diff line number Diff line from typing import Protocol from uuid import UUID from federation_manager.domain.models import PartnerOP from federation_manager.domain.models import Agreement, PartnerOP class PartnerRepositoryPort(Protocol): async def find_by_cert_thumbprint(self, thumbprint: str) -> PartnerOP | None: ... class AgreementRepositoryPort(Protocol): async def find_active_for_partner(self, partner_id: UUID) -> Agreement | None: ... class DataBusPublisherPort(Protocol): async def publish(self, subject: str, payload: dict[str, object]) -> None: ...