Security hardening — breaking.
- Breaking: the admin panel is now fail-closed — when no
authcallable is configured, all requests are denied with HTTP 401 (previously they were silently allowed) (CWE-306). Pass anauthcallable to enable access. - Detail views render only
detail_fields()(derived fromlist_displaywhen configured), so columns hidden from the list no longer leak on the detail page (CWE-200). - Sensitive/authorization field detection extended to
role/admin/superuser/permission/privilege/staff/scope; matched fields are forced read-only unless explicitly opted intoform_fields, preventing mass-assignment privilege escalation (CWE-915). - CSRF cookie now sets
HttpOnlyandMax-Age(CWE-1004). - Admin HTML responses carry
X-Frame-Options,X-Content-Type-Options,Referrer-Policy, and a restrictiveContent-Security-Policy(CWE-1021). - Non-integer
?page=values no longer cause a 500; page is clamped to a maximum (CWE-20). - Security events (auth/CSRF failures, blocked mutations) are now logged (CWE-778).
Security hardening — breaking.
- CSRF tokens required on all admin POSTs by default (
Admin(csrf_enabled=...)to opt out for tests / behind-auth deployments). Token stored inhawkapi_admin_csrfcookie (Secure, SameSite=Lax) and echoed via hidden_csrfform field. - Optional
authcallable onAdmin/init_admin— invoked at the top of every route, raisesHTTPExceptionto reject. AUserWarningis emitted at construction and alogger.warningatattach()when no auth is configured. save()catchesSQLAlchemyError/ValueError/TypeErrorand re-renders the form with a400plus a shorterrors["_form"]message instead of leaking a 500.- Clear-to-NULL semantics — a field that is present-but-empty in the form is now coerced to
None(when nullable) instead of being silently skipped. - Pagination links URL-encode the
?q=parameter. ModelResource.__post_init__warns when an editable field name looks sensitive (password/secret/token/key/hash) and isn't inreadonly_fields.Admin.register()raisesValueErroron duplicate resource names.
Fix wheel build: drop the empty static/ force-include entry that broke uv build in CI.
Initial release.
Adminorchestrator +init_admin(app)— mounts index / list / detail / create / edit / delete routes under/admin.ModelResource— declarative wrapper over a SQLAlchemy model with knobs forlist_display,list_search,form_fields,readonly_fields,page_size,can_create/update/delete, customlabel,icon.- Type-driven widget picker (checkbox / number / date / datetime / textarea / enum / text), automatic from each column's SQLAlchemy type.
- Search on the list page (
?q=) backed by ILIKE against the configured columns. - Pagination.
- Light + dark mode CSS, ~60 lines inline in
_base.html. - Built on top of hawkapi-sqlalchemy — picks up the session factory from
init_database(app, ...).