Commit 0a8ff9e4 authored by Sergio Gimenez's avatar Sergio Gimenez
Browse files

add keycloak service

parent 6bdd1f71
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -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
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"]
}
+48 −0
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)
+8 −0
Original line number Diff line number Diff line
@@ -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
+6 −1
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