Ga naar inhoud

Beheer (Backend)

Technisch Ontwerp

De Django-app features/beheer is klein en doelgericht: het verzorgt de team-brede broadcasts (e-mail en push) die niet in auth/ (gebruikersbeheer, uitnodigingen) of rbac/ (rollen en permissies) thuishoren. De overige beheerfunctionaliteit die het scherm BeheerScreen toont, leeft in andere modules. Beheer roept die endpoints aan maar implementeert ze niet:

  • auth (admin-endpoints onder /api/auth/admin/...): gebruikers, uitnodigingen, admin-TOTP-reset en de audit-log.
  • rbac: functies (rollen) en de permissiecatalogus.
  • features.tenant: apotheekgegevens, logo, billing-samenvatting en de afmeldflow.
  • email_domains (onder /api/settings/email/...): e-mailafzenderdomeinen en DNS.
  • billing: abonnement, verbruik en facturen.

De broadcast-endpoints zelf zijn drie APIView's. De e-mailcomposer rendert eerst een server-side voorbeeld (MailEmployeesPreviewView) dat byte-identiek is aan de verzonden mail, behalve dat het logo als data-URL wordt ingebed in plaats van als cid:-referentie. Verzenden gebeurt via MailEmployeesSendView en PushEmployeesSendView. Ontvangers worden in beide gevallen via resolve_recipients opgelost; verzending loopt via de notifications-laag (Celery).

flowchart TD
    UI[BeheerScreen / Mail- en PushModal] -->|POST + step-up| PV[MailEmployeesPreviewView]
    UI -->|POST + step-up| MV[MailEmployeesSendView]
    UI -->|POST + step-up| PUV[PushEmployeesSendView]

    PV --> PC0{PermCheck: settings.manage_users + RequiresFreshStepUp}
    PV --> ER[email_render.render_employee_email_content preview=True]

    MV --> PC1{PermCheck: settings.manage_users + RequiresFreshStepUp}
    MV --> RL1{ratelimit.is_rate_limited mail_employees?}
    RL1 -->|Ja| R429[429 Too Many Requests]
    RL1 -->|Nee| RR1[resolve_recipients require_email=True]
    RR1 -->|RecipientResolutionError| R400[400 Bad Request]
    RR1 --> SB[email_render.send_employee_broadcast]
    SB --> EE[notifications.enqueue_email per adres] --> Celery --> SES
    SB --> AL1[audit_logger: settings.mail_employees.sent]

    PUV --> RR2[resolve_recipients require_email=False]
    RR2 --> PE[notifications.push_enabled_users]
    PE --> EN[notifications.enqueue_notification send_email=False] --> Celery --> Push
    PUV --> AL2[audit_logger: settings.push_employees.sent]

Datamodel (ERD)

Beheer kent zelf geen modellen. De broadcasts en de overige beheeracties leunen op bestaande modellen uit tenants, auth en rbac. Het diagram toont uitsluitend de samenhang die voor Beheer relevant is.

erDiagram
    APOTHEEK ||--o{ ROLE : "heeft"
    APOTHEEK ||--o{ USER : "heeft via tenant"
    APOTHEEK ||--o{ USERINVITATION : "heeft"
    ROLE }o--o{ PERMISSION : "ROLE_PERMISSIONS"
    USER }o--o{ ROLE : "users m2m"

    APOTHEEK {
        int id PK
        string schema_name
        string name
        string logo_s3_key
        string billing_email
        string general_email
        string straat_adres
        string postcode
        string stad
        boolean is_active
        datetime pending_unregister_at
        datetime deleted_at
    }
    USER {
        int id PK
        string email
        string first_name
        string last_name
        date birth_date
        string phone_number "versleuteld"
        boolean is_active
        datetime date_joined
    }
    USERINVITATION {
        uuid id PK
        int tenant_id FK
        string email
        string first_name
        string last_name
        datetime created_at
        datetime expires_at
        datetime cancelled_at
    }
    ROLE {
        int id PK
        int tenant_id FK
        string name
        string code
    }
    PERMISSION {
        int id PK
        string code
    }

API & Communicatie

Eigen endpoints (/api/beheer/)

  • POST /api/beheer/mail-employees/preview/: server-side render van een team-e-mail. Body subject (max 200 tekens), body (max 20000 tekens). Antwoord bevat subject, text, html, tenant_name, preview_logo_src en from_label. Het voorbeeld is byte-identiek aan de verzonden mail, alleen het logo verschilt (data-URL hier, cid: bij verzenden).
  • POST /api/beheer/mail-employees/: verstuurt de e-mail. Body subject, body, participants_mode (everyone of selection), roles (codes uit ROLE_OPTION_CODES), people (gebruiker-id's, integers). Antwoord { "sent_to": <aantal> }. Eén enqueue_email per adres, zodat ontvangers elkaar niet zien.
  • POST /api/beheer/push-employees/: verstuurt een pushbericht. Body title (max 50 tekens), body (max 150 tekens) plus dezelfde ontvangerselectie. Antwoord { "sent_to": <aantal> }. Alleen ontvangers met push aangezet en een actief native device-token worden bereikt; web push wordt bewust overgeslagen (gereserveerd voor Atlas-antwoorden).

De ontvangerlogica zit in services.resolve_recipients(tenant, data, require_email). Bij everyone levert dit alle actieve tenant-gebruikers; bij selection de gededuceerde unie van opgegeven personen en rol-gematchte gebruikers. Een opgegeven people-id dat niet tot een actieve gebruiker van deze tenant herleidt, levert een RecipientResolutionError op (in tegenstelling tot de agenda, die zulke id's stil laat vallen). Voor e-mail worden adresloze gebruikers eruit gefilterd; voor push is dat niet nodig.

Verbruikte endpoints (in andere modules)

  • Gebruikers en uitnodigingen (/api/auth/admin/...): GET|POST /users/, PATCH|DELETE /users/<id>/, POST /users/<id>/totp-reset/, GET /pending-invitations/, POST /invitations/<uuid>/resend/, DELETE /invitations/<uuid>/.
  • Audit-log: GET /api/auth/admin/audit-log/ (gepagineerd, met filters op gebeurtenis, ernst, actor, resource en periode).
  • RBAC: GET /api/rbac/catalog/, GET|POST /api/rbac/roles/, PUT|DELETE /api/rbac/roles/<id>/.
  • Tenant: GET|PATCH /api/tenant/info/, POST /api/tenant/logo/ (multipart, veldnaam image), GET /api/tenant/billing/, POST /api/tenant/unregister/ (TOTP vereist).
  • E-mailafzender: GET|POST /api/settings/email/sending-domains/, plus per domein refresh, enable, disable, send-test en DELETE.

Publieke (niet-ingelogde) endpoints die door Beheer worden aangeroepen:

  • GET /api/auth/invite/<token>/validate/ en POST /api/auth/invite/<token>/accept/.
  • GET /api/auth/totp-admin-reset/<token>/validate/ en POST /api/auth/totp-admin-reset/<token>/confirm/.

Foutafhandeling & Statuscodes

  • 200 OK: succesvolle preview of verzending.
  • 400 Bad Request: validatiefout (te lang onderwerp of bericht, lege selectie), ontbrekende tenant-context, of RecipientResolutionError (lege of ongeldige selectie).
  • 401 Unauthorized: ongeldige of verlopen sessie. De frontend stuurt naar /login.
  • 403 Forbidden: gebruiker mist settings.manage_users, of de step-up is niet recent genoeg (de frontend start dan de step-up-flow).
  • 429 Too Many Requests: de verzendlimiet voor deze apotheek is bereikt (apart geteld voor mail en push).

De rate limiter is een per-tenant, per-actie Redis-teller (ratelimit.py, sleutel {action}:{tenant.id}). De limieten staan in settings: MAIL_EMPLOYEES_RATE_LIMIT = 10 per MAIL_EMPLOYEES_RATE_WINDOW_SECONDS = 3600, en PUSH_EMPLOYEES_RATE_LIMIT = 10 per PUSH_EMPLOYEES_RATE_WINDOW_SECONDS = 3600. De teller wordt alleen verhoogd na een geslaagde enqueue, nooit na een 400, zodat een mislukte poging geen budget verbruikt. Het is een zachte rem: onder zware gelijktijdigheid is één extra verzending mogelijk.

Voor specifieke foutcodes binnen auth, billing, rbac of email_domains verwijst dit naar de respectieve module-documentatie.

Autorisatie & Beveiliging

De drie eigen endpoints vereisen HasPermissionCode met required_permission = "settings.manage_users" en RequiresFreshStepUp (een recente step-up via TOTP of biometrie). Tenant-context komt uit common.tenants.current_tenant(request); ontbreekt die, dan volgt een 400.

De permissiecodes voor de overige secties (afgehandeld door de onderliggende modules) zijn:

  • settings.view: toegang tot /beheer en GET /api/tenant/info/.
  • settings.manage_users: gebruikers- en uitnodigingsbeheer, plus de team-broadcasts.
  • settings.manage_roles: permissiecatalogus en rollen.
  • settings.manage_tenant: apotheekgegevens, logo en e-mailafzender.
  • settings.manage_billing: billing-samenvatting, billing-mutaties en het afmelden van de apotheek.
  • settings.view_audit_log: de audit-log inzien.

Alle codes staan in backend/rbac/permissions_catalog.py. Tenant-scoping verloopt via de django-tenants-middleware en de User.tenant-FK. Destructieve admin-acties (gebruiker verwijderen, TOTP-reset, uitnodiging intrekken of opnieuw versturen, apotheek afmelden) vereisen aanvullend een geldige TOTP-code in de request body. Elke verzending wordt vastgelegd via audit_logger met de gebeurtenissen settings.mail_employees.sent en settings.push_employees.sent, inclusief de gekozen modus en de aantallen.

Bestandsstructuur & Verantwoordelijkheden

  • backend/features/beheer/views.py: MailEmployeesPreviewView, MailEmployeesSendView, PushEmployeesSendView.
  • backend/features/beheer/services.py: resolve_recipients, ROLE_OPTION_CODES, RecipientResolutionError.
  • backend/features/beheer/email_render.py: render_employee_email_content (parity preview/verzending) en send_employee_broadcast.
  • backend/features/beheer/ratelimit.py: per-tenant, per-actie verzendlimiet.
  • backend/features/beheer/serializers.py: request- en response-serializers, met de tekenlimieten uit settings.
  • backend/features/beheer/urls.py: de drie URL-patronen.
  • backend/auth/views/admin_users.py, backend/auth/views/audit_log.py, backend/auth/views/totp_admin_reset.py, backend/auth/views/invite_accept.py: gebruikers-, audit- en uitnodiging-endpoints.
  • backend/rbac/views.py en backend/rbac/permissions_catalog.py: rollen, catalogus en permissiecodes.
  • backend/features/tenant/views.py: TenantInfoView, TenantLogoUploadView, BillingInfoView, ApotheekUnregisterView.
  • backend/tenants/models.py: model Apotheek.

Belangrijke bestanden

  • backend/features/beheer/views.py
  • backend/features/beheer/services.py
  • backend/features/beheer/email_render.py
  • backend/features/beheer/ratelimit.py
  • backend/rbac/permissions_catalog.py
  • backend/features/tenant/views.py

API & Communicatie (Swagger)

De openapi.yaml hieronder dekt uitsluitend de eigen /api/beheer/-endpoints (de drie broadcast-acties). De gebruikers-, RBAC-, tenant-, audit-log- en e-mailafzender-endpoints die Beheer verbruikt, staan in de Swagger-specificaties van hun eigen modules (auth, rbac, tenant, email_domains).