Changelog¶
Notable changes to CRUDAdmin, newest first. CRUDAdmin is pre-1.0, so minor versions may include breaking changes; they are called out under Breaking changes, and Upgrading says what to do.
0.6.0 - 2026-10-08¶
Authentication moves onto crudauth. Login, sessions, CSRF
and login lockout are crudauth's; CRUDAdmin renders the pages around them. This closes the
remaining security gaps from the 0.5 review (CSRF and login rate limiting were documented but never
enforced, and X-Forwarded-For was trusted unconditionally), adds superuser-only account
management, and fixes a set of audit-log and form bugs. Deploying it signs every admin out once.
Added¶
- CSRF protection. Every POST, PUT, PATCH and DELETE needs the
X-CSRF-Tokenheader. The newstatic/admin.jssends it with htmx requests,fetchcalls and plain<form method="post">submissions. - Login lockout. Five failed attempts lock the username and the IP: one minute at first,
doubling to at most five minutes, cleared by a successful login. Tune it with
CRUDAdmin(lockout=LockoutConfig(...)). trusted_proxy_hops.X-Forwarded-Foris ignored unless you say how many trusted proxies sit in front of the app.- Sessions page (
/management/sessions): device, IP and times for each session, never its id. Admins see and end their own sessions; superusers see and end everyone's. - Password confirmation for account changes. Creating or editing an admin asks the superuser to confirm their password; the confirmation lasts five minutes.
is_activeandtoken_versiononAdminUser. A deactivated admin can't log in and loses their sessions at once; a password change ends the admin's other sessions.admin.shutdown()closes the session stores and the Redis client CRUDAdmin opened.admin.session_manager, crudauth's session manager, for listing and revoking sessions in your own code.- Session backends:
memory,redisanddatabase. Withdatabase, login lockout counters are kept in the admin database too, so they are shared across workers.
Changed¶
- Admin users and the event log are superuser-only. New admins are not superusers unless created as one; the initial admin is.
- Logout is a POST with the CSRF header;
GET /logoutno longer exists. - The last active superuser can't be demoted or deactivated.
- Password hashes from 0.5 keep working and are rehashed in crudauth's format at the next login.
- The login page answers bad credentials with 401, a lockout with 429 and a cross-site login
with 403, and shows only its own messages from the
errorquery parameter. - A form submitted through
admin.jsloads the next page once. A redirect answering it comes back as a 204 withX-CRUDAdmin-Location. track_events=Truewith your owndb_configgets the event and audit tables automatically.- One mypy configuration (
[tool.mypy]inpyproject.toml, with the pydantic plugin) checks the package and the tests; CI and the pre-commit hook both runmypy.
Fixed¶
- Editing an admin user always failed (the update form added
updated_at, whichAdminUserUpdaterejects). - Audit log:
- an action that failed is recorded as
failure, with no audit rows; - a delete writes one audit row per deleted record, keyed by the model's primary key, including
keys not named
id; - creates are keyed by the real primary key too;
- an event and its audit rows are written in one transaction and rolled back together if any write fails.
- Forms and lists:
Optional[bool](and otherOptional[...]) fields render as their real input type, soOptional[bool]is a checkbox;- an empty input clears a nullable column on update;
- the password transformer keeps
Falseand0; - bulk delete works with UUID primary keys, and a malformed id gets a 422;
- pagination keeps the active search;
- a list query that fails is an error page, not "0 items".
- Upgrading several workers at once no longer fails: the new
admin_usercolumns are added in their own transactions, and a worker that loses the race carries on.
Security¶
- Fixes the reports in GHSA-fv5q-2mg9-c62x (
X-Forwarded-Forspoofing), GHSA-4cwg-f726-9hfv and GHSA-7c4p-r94r-6r3j (no login rate limiting). - CSRF tokens are now checked on every state-changing request.
- Session ids and CSRF tokens are stored as HMACs keyed with
SECRET_KEY, never raw.
Removed¶
- The
crudadmin.sessionsession layer (SessionManager,get_session_storage, the memory, Redis, Memcached, database and hybrid backends, and theAdminSessionmodel and view).crudadmin.sessionkeepsRedisConfigandMemcachedConfig. crudadmin.core.auth,crudadmin.core.rate_limiter,AdminUserServiceandlog_auth_action. Logins and logouts are recorded through crudauth hooks.- The
memcachedextra.
Deprecated¶
session_backend="hybrid"now means"redis"and warns; it will be removed.track_sessions_in_dbwarns; with the memory backend it switches to"database". It will be removed.
Breaking changes¶
- Deploying 0.6 signs every admin out once. Sessions are now crudauth's and stored under new keys.
SECRET_KEYis required and used. It keys stored session ids and CSRF tokens; changing it signs everyone out.session_backend="memcached"andmemcached_config=fail at startup. Use"redis"or"database".- Logout is a POST that needs the
X-CSRF-Tokenheader. - New admins aren't superusers by default.
- The removed modules above are gone; use crudauth or
admin.session_manager.
Upgrading¶
- Update:
pip install -U crudadmin. This addscrudauth[useragent]>=0.8.2,<0.9(and PyJWT). - Call
await admin.shutdown()afteryieldin your lifespan. - Replace
session_backend="memcached"with"redis"or"database". - If you run behind a reverse proxy, set
trusted_proxy_hops. - Custom templates or links that log out must POST to
/logoutwith theX-CSRF-Tokenheader; pages extendingbase/base.htmlget it fromadmin.js. -
initialize()addsis_activeandtoken_versionto an existingadmin_usertable. If you manage the admin schema with Alembic instead: -
The
admin_sessiontable is no longer used and can be dropped:DROP TABLE admin_session;
0.5.2 - 2026-10-07¶
A security patch release.
Security¶
- Removed the generic
/{Model}/crudREST routes, which ignoredallowed_actionsand returned every column, password hashes included. - Fixed cross-site scripting in the list page (
sort_orderevaluated by htmx, and string primary keys written into an inline handler). - The IP allowlist and HTTPS redirect apply on every mount path, and the redirect uses the configured HTTPS port.
Fixed¶
- Session ids kept out of logs, the event log and the admin lists; password hashes redacted from audit snapshots.
- Admin passwords must be 8–128 characters;
PasswordTransformerrequires a hash function. - The database session backend no longer shares one
AsyncSessionacross requests. - The session cookie no longer expires after a fixed 30 minutes; audit metadata is stored;
AdminUsertimestamps are set per row; page size is bounded.