django-heluca-themis (1.16.0)
Installation
pip install --index-url https://git.helu.ca/api/packages/r/pypi/simple/ --extra-index-url https://pypi.org/simple django-heluca-themisAbout this package
Django app providing user preferences, theme management, API key management, Casdoor SSO adapters, and standard navigation templates
Themis
Reusable Django app providing user preferences, DaisyUI theme management, API key management, and standard navigation templates for all Heluca applications.
Themis — titan of order, custom, and law.
Features
- User Preferences — timezone (home + traveling), date/time/number formatting, week start day
- Heluca Design System — the V2 system vendored from
@heluca/svelte: token layer + daisyUI 5 bridge,light/darkplus the five temperaments (consulting, business, business-dark, terminal, terminal-light), self-hosted Marcellus, Literata, Public Sans, IBM Plex Sans, JetBrains Mono and Atkinson Hyperlegible; separate light/dark selection, auto (system) mode - Notifications — in-app notification bell, JS polling, browser desktop notifications, user preferences
- API Key Management — encrypted storage with per-key instructions and documentation links
- Standard Navigation — consistent navbar, user menu, notification bell, theme toggle, and bottom nav across all apps
- Themed Auth Pages — every django-allauth page (login, signup, logout, password reset/change, email management, …) rendered in the Themis DaisyUI chrome, with no per-app templates
- Middleware — automatic timezone activation and theme context
- Formatting Utilities — date, time, number formatting respecting user preferences
- Health Checks — Kubernetes-ready
/ready/and/live/endpoints - REST API — complete API for profiles, keys, and notifications
Installation
Releases are wheels on the estate's Gitea PyPI registry (anonymous read):
pip install django-heluca-themis --extra-index-url https://git.helu.ca/api/packages/r/pypi/simple/
Consumers pin a range in pyproject.toml (django-heluca-themis>=1.13,<2.0)
and set PIP_EXTRA_INDEX_URL in the Dockerfile rather than passing the flag.
Quick Start
- Add to
INSTALLED_APPS:
INSTALLED_APPS = [
...
"rest_framework",
"themis",
...
]
- Configure middleware:
MIDDLEWARE = [
...
"themis.middleware.TimezoneMiddleware",
"themis.middleware.ThemeMiddleware",
...
]
- Configure context processors:
TEMPLATES = [{
"OPTIONS": {
"context_processors": [
...
"themis.context_processors.themis_settings",
"themis.context_processors.user_preferences",
"themis.context_processors.notifications",
"themis.context_processors.navigation",
],
},
}]
- Include URLs:
urlpatterns = [
...
path("", include("themis.urls")),
path("api/v1/", include("themis.api.urls")),
...
]
- Configure app settings:
THEMIS_APP_NAME = "My Application"
# Register navigation entries (block-based extension can't cross {% include %},
# so the chrome iterates these instead). Each entry: label, named URL, optional
# SVG icon `d` path. Bad url_names are skipped with a warning, not a 500.
THEMIS_NAV_ITEMS = [
{"label": "Home", "url_name": "core:home"},
{"label": "Mail", "url_name": "mail:account_list"},
]
THEMIS_USER_MENU_ITEMS = [
{"label": "Mail Accounts", "url_name": "mail:account_list"},
]
- Run migrations:
python manage.py migrate
- Extend the base template:
{% extends "themis/base.html" %}
{% block nav_items %}
<li><a href="{% url 'dashboard' %}">Dashboard</a></li>
{% endblock %}
{% block content %}
<h1 class="text-2xl font-bold">My App</h1>
{% endblock %}
Casdoor SSO (optional)
Themis ships generic django-allauth adapters for Casdoor OIDC. They map the
Casdoor groups claim to Django groups, set is_staff from configurable
staff groups, and block superusers from SSO (they must use local auth).
SOCIALACCOUNT_ADAPTER = "themis.adapters.CasdoorAccountAdapter"
ACCOUNT_ADAPTER = "themis.adapters.LocalAccountAdapter"
Optional settings:
THEMIS_STAFF_GROUPS— Casdoor groups grantingis_staff(default["staff", "sme", "admin"]).THEMIS_GROUP_MAPPING— Casdoor group name → Django group name.THEMIS_ORG_ADAPTER— dotted path tofn(user, org_identifier)invoked with the Casdoororganizationclaim, for apps with an Organization model. Omit it (the default) to skip organization mapping.THEMIS_TENANT_ADAPTER— store mode (Agora ADR-0004): dotted path tofn(user, tenant_id, groups)invoked with thetenant_idclaim and the bare own-org group names, instead of the org adapter, whenever the claim is present. Helpers for the projected groups live inthemis.tenancy.THEMIS_REQUIRED_GROUP_PREFIX— a product slug; an SSO login holding no<prefix>/<prefix>-*group is refused before provisioning and sent tothemis:no-subscription, which links toTHEMIS_STORE_URL(required with the prefix; system checkthemis.E001). Seedocs/Pattern_SaaS-Tenancy_V1-01.md§ Store mode.THEMIS_ENCRYPTION_KEY— any non-empty string; Themis hashes it, so no format is required, but it is the passphrase for every storedUserAPIKeyand a long random value is what counts. Generate one withpython -c "import secrets; print(secrets.token_urlsafe(48))". Required, from the environment, no default (system checkthemis.E006). Identical across every process/container of one deployment, and never changed onceUserAPIKeyrows exist: rows are readable only under the value that wrote them, consumers skip undecryptable rows silently (no error), and the only remedy is reissuing the keys. Independent ofSECRET_KEYby design; upgrading from a release that derived the key fromSECRET_KEY: set it to theSECRET_KEYvalue in force at the time to keep stored keys readable. Seedocs/Themis_V1-00.md§ UserAPIKey.THEMIS_PROFILE_SOURCE—"local"(default) keeps preferences on the app's own settings page;"claims"links the app to the estate's Agora (ADR-0018): the projected preference claims (timezone,date_format,theme_mode, …) are copied intoUserProfileat every login — a missing claim leaves its field alone — and the settings page turns read-only with a "Manage in Agora" link to{AGORA_URL}/profile/.AGORA_URLis required with"claims"(system checkthemis.E002; environment value, no default). Seedocs/Pattern_SSO-Allauth-Casdoor_V1-04.md§ Profile Claims.AGORA_URL,AGORA_PRODUCT_API_KEY— the entitlements client (themis.entitlements:get_limits,check_quota,quota_blocked,report_usage,flush_usage). Both from the environment, no defaults, set together (system checkthemis.E004); the usage PUT is named byTHEMIS_REQUIRED_GROUP_PREFIX(themis.E005). Optional:THEMIS_TENANT_RESOLVER(dotted pathfn(request) -> tenant id; default readsrequest.tenant.uuid),THEMIS_QUOTA_WARN_RATIO(0.8). Addthemis.context_processors.entitlementsfor the warn banner and schedulemanage.py flush_usageevery minute. Seedocs/Pattern_Entitlements_V1-00.md.
Reconnecting an account after an IdP reset
OIDC matches a returning user on the subject id allauth stored as
SocialAccount.uid — for Casdoor, the user's id. Re-provisioning Casdoor
re-issues those ids, so every stored connection is orphaned and SSO login
falls through to auto-signup. Re-point it:
# Preview the change (old -> new uid), write nothing
python manage.py reconnect_socialaccount user@example.com --uid <new-casdoor-id> --dry-run
python manage.py reconnect_socialaccount user@example.com --uid <new-casdoor-id>
Resolves the user by username or email, creates the connection when none
exists, no-ops when the uid already matches, and refuses when that uid
belongs to another user. --provider defaults to casdoor — the
provider_id allauth stores, not openid_connect. Full procedure, including
how to do it without this command: SSO with Allauth & Casdoor.
For sandbox environments with self-signed Casdoor certs, import the SSL bypass
at the top of settings.py before any requests are made (activates only
when CASDOOR_SSL_VERIFY=false):
import themis.ssl_patch # noqa: F401
Documentation
- Themis App — Full documentation
- SSO with Allauth & Casdoor — Adapters, claim mapping, and IdP-reset recovery
- Notification Trigger Pattern — How to trigger notifications from your app
- Organization Pattern — Standard Organization model pattern
- Ansible Deployment Pattern — Compose + nginx on the private estates
- Kubernetes Deployment Pattern —
charts/themis-appfor the public tier; WhiteNoise static standard (themis.settings.static) - Red Panda Standards — Django development standards
- Documentation Style Guide — Documentation standards
🐾 Red Panda Approval™
This project follows Red Panda Approval standards — our gold standard for Django application quality.
The 5 Sacred Django Criteria
- Fresh Migration Test — Clean migrations from empty database
- Elegant Simplicity — No unnecessary complexity
- Observable & Debuggable — Proper logging and error handling
- Consistent Patterns — Follow Django conventions
- Actually Works — Passes all checks and serves real user needs
License
MIT License — see LICENSE file for details.
Author
Robert Helewka r@helu.ca
Package Information
- Package Name: django-heluca-themis
- Django: >=5.2, <6.0
- Python: >=3.10