Core Services¶
Activation¶
activation ¶
Activation-link helpers - ported from MasterplanOptimiserV2 Server. Tokens stored as SHA-256 hashes so a DB dump never reveals a usable token.
ActivationDeliveryInProgressError ¶
hash_token ¶
resolve_activation_purpose ¶
resolve_activation_purpose(*, is_activated: bool, requested: ManagedPasskeyPurpose | None) -> ActivationPurpose
Resolve a safe link purpose from account state and an optional request.
Pending accounts always use initial setup. Active accounts retain the historical reset default when an older client omits the purpose.
Source code in backend/app/core/activation.py
create_activation_link ¶
create_activation_link(user_id: int, created_by_id: int, db: Session, purpose: str = 'initial_setup', expiry_hours: int | None = None, delivery_pending: bool = False, permit_email_delivery_start: bool = False) -> Tuple[str, ActivationLink]
Create an activation link for a user.
Automatically invalidates any previous active link for the same user.
Email links may be held pending until SMTP acceptance is confirmed. Only
the email delivery workflow may set permit_email_delivery_start so a
manual link cannot invalidate a token while its email is being handed off.
Returns (raw_token, link_row).
Source code in backend/app/core/activation.py
validate_activation_token ¶
validate_activation_token(token: str, db: Session, *, for_update: bool = False) -> Optional[ActivationLink]
Look up a token and return the link row if it is still valid.
Source code in backend/app/core/activation.py
mark_link_used ¶
Mark an activation link as used.
Source code in backend/app/core/activation.py
Audit¶
audit ¶
Audit helper - single function to record security-relevant actions.
Usage::
from app.core.audit import audit
audit(db, user=current_user, action="event.create", resource_type="event",
resource_id=new_event.id, request=request)
The entry is added to the session but NOT committed - the caller's existing transaction will include it.
audit ¶
audit(db: Session, *, user: Optional[User], action: str, resource_type: Optional[str] = None, resource_id: Optional[int] = None, detail: Optional[str] = None, request: Optional[Request] = None, outcome: str = 'success') -> AuditLog
Create one schema-bound, minimised audit entry (uncommitted).
Source code in backend/app/core/audit.py
Diff¶
diff ¶
Schedule diff helpers - compute per-person change summaries between publishes.
compute_per_person_diffs ¶
compute_per_person_diffs(old_tasks: List[PublishedTask], old_edits_map: Dict[int, TaskEdit], new_tasks: List[PublishedTask], new_edits_map: Optional[Dict[int, TaskEdit]] = None) -> Dict[int, dict]
Compare old resolved tasks against new live tasks per person.
Returns {person_id: changes_dict} for persons with actual changes. changes_dict has keys: type, summary, added, removed, modified.
Source code in backend/app/core/diff.py
store_schedule_changes ¶
Store per-person diffs as ScheduleChange records for linked users. Returns number of records created.
Source code in backend/app/core/diff.py
Permissions¶
permissions ¶
Permission enforcement middleware for V3 Server.
Simplified from V2: no desktop/web mode distinction. - Unauthenticated paths (passkey, activation, publish) always pass through. - All other writes require a valid session cookie + CSRF token. - Admin endpoints require admin role (enforced by route dependencies). - Calendar edits require can_edit flag (enforced by route dependencies).
Returns JSONResponse (not raise HTTPException) so outer CORSMiddleware can still add CORS headers on denied requests.
enforce_permissions_middleware
async
¶
Enforce CSRF on cookie-authenticated write requests.
Route-level dependencies handle role checks (require_admin, can_edit). This middleware only ensures CSRF protection on writes that use cookies.
Source code in backend/app/core/permissions.py
Push¶
push ¶
Web Push helper - send push notifications to subscribed users. Uses pywebpush with VAPID authentication.
VAPID_PRIVATE_KEY should be a base64url-encoded raw 32-byte EC private key (the same format py-vapid and many VAPID generators output).
get_application_server_key ¶
Return the VAPID public key in base64url format for the Push API.
Source code in backend/app/core/push.py
send_push ¶
Send one push, returning false only for an expired subscription.
Source code in backend/app/core/push.py
send_push_to_event ¶
send_push_to_event(event_id: int, title: str, body: str, url: Optional[str], db: Session, notification_type: Optional[str] = None) -> int
Send push to all subscribers of an event. Returns count of successful deliveries. Removes expired subscriptions (410/404). notification_type: "announcement" or "schedule" (used by SW to pick icon).
Source code in backend/app/core/push.py
Runtime Settings¶
runtime_settings ¶
Runtime-configurable security settings.
Reads overrides from the server_settings DB table and falls back to the
static values in config.py / hard-coded defaults. Every public getter
accepts an optional db session so callers that already have one can
avoid opening a second connection.
get_all ¶
Return every tuneable setting with its current effective value and metadata.
Source code in backend/app/core/runtime_settings.py
get_int ¶
Return the effective integer value for key.
Source code in backend/app/core/runtime_settings.py
apply_governance_runtime_values ¶
apply_governance_runtime_values(structured: dict[str, Any], db: Session) -> tuple[dict[str, Any], list[dict[str, Any]]]
Overlay only settings the Server actually enforces onto a draft.
Source code in backend/app/core/runtime_settings.py
set_value ¶
Persist a runtime override (upsert).
Source code in backend/app/core/runtime_settings.py
Security¶
security ¶
Security helpers - session auth, current user dependency. Passkey-only: no password hashing needed for regular auth.
get_current_user ¶
Get the current authenticated user and refresh session activity.
get_current_user_read_only ¶
Authenticate without writing session activity to a fenced database.
get_current_user_for_commissioning ¶
Authenticate a root session without lifting the commissioning fence.
Source code in backend/app/core/security.py
require_commissioning_root ¶
require_commissioning_root(current_user: User = Depends(get_current_user_for_commissioning)) -> User
Require the authenticated root while the setup wizard is active.
Source code in backend/app/core/security.py
require_commissioning_root_recent_reauth ¶
require_commissioning_root_recent_reauth(current_user: User = Depends(require_commissioning_root), db: Session = Depends(get_db)) -> User
Require a setup root whose current session has a recent passkey proof.
Source code in backend/app/core/security.py
require_admin ¶
Dependency: require that the current user is an admin or root admin.
Source code in backend/app/core/security.py
require_admin_or_issuer ¶
Dependency: require admin, root admin, or issuer.
Source code in backend/app/core/security.py
require_root_or_issuer ¶
Dependency: require a root administrator or an issuer account.
Source code in backend/app/core/security.py
require_same_event ¶
Raise 403 if current issuer-only user doesn't share event with target.
Source code in backend/app/core/security.py
require_user_management_access ¶
Enforce account hierarchy and issuer event scope for user management.
Only root may manage another root, global administrator, or issuer account. Issuers may manage ordinary users only within their own event.
Source code in backend/app/core/security.py
require_event_access ¶
Return an event when the current user may access it.
Root and global admins may access every event. Issuers, editors, and viewers must have an exact, non-null event assignment.
Source code in backend/app/core/security.py
require_root_admin ¶
Dependency: require root admin.
Source code in backend/app/core/security.py
require_root_admin_read_only ¶
Require root access without mutating session state.
Source code in backend/app/core/security.py
ensure_recent_reauth ¶
Require a recent passkey verification on the current session.
Source code in backend/app/core/security.py
require_recent_reauth ¶
require_recent_reauth(current_user: User = Depends(require_admin_or_issuer), db: Session = Depends(get_db)) -> User
Require a recently re-authenticated global admin or issuer.
Source code in backend/app/core/security.py
require_admin_recent_reauth ¶
require_admin_recent_reauth(current_user: User = Depends(require_admin), db: Session = Depends(get_db)) -> User
Require a recently re-authenticated root or global admin.
require_root_recent_reauth ¶
require_root_recent_reauth(current_user: User = Depends(require_root_admin), db: Session = Depends(get_db)) -> User
Dependency: require root admin with recent re-authentication.
Source code in backend/app/core/security.py
create_default_admin ¶
Create root admin user (passkey-only) if it doesn't exist.
Source code in backend/app/core/security.py
Sessions¶
sessions ¶
Server-side session management - ported from MasterplanOptimiserV2 Server. All users authenticate via session cookies (HttpOnly, Secure, SameSite=Lax).
create_session ¶
create_session(user_id: int, db: Session, ip_address: Optional[str] = None, user_agent: Optional[str] = None, accept_language: Optional[str] = None, is_privileged: bool = False, reauthenticated: bool = False) -> AuthSession
Create a new server-side session.
Source code in backend/app/core/sessions.py
validate_session ¶
validate_session(session_token: str, db: Session, user_agent: Optional[str] = None, accept_language: Optional[str] = None, *, update_last_seen: bool = True) -> Optional[AuthSession]
Look up a session token and return it if still valid.
Source code in backend/app/core/sessions.py
revoke_session ¶
Revoke a single session.
Source code in backend/app/core/sessions.py
revoke_all_user_sessions ¶
Revoke every active session for a user.
Source code in backend/app/core/sessions.py
cleanup_expired_sessions ¶
Delete sessions that are expired or were revoked beyond retention period.
Source code in backend/app/core/sessions.py
Snapshots¶
snapshots ¶
Snapshot helpers - create / deduplicate / prune publish snapshots.
Used by publish.py (after data insertion) and history.py (rollback).
create_snapshot ¶
Snapshot the current published state for an event.
Returns the new snapshot, or None if: - There are no existing tasks (nothing to archive) - Any existing snapshot already has the same content hash (dedup)
Source code in backend/app/core/snapshots.py
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 | |