Replace Basic Auth with multi-user login, roles, groups, and Lib\Access

Admin login & user management (novaconium/ISSUES.md): session-based
login against a SQLite users table replaces the single-user HTTP Basic
Auth stopgap (admin_username/admin_password_hash and /admin/password-hash
are gone; one admin_auth_enabled flag, off by default with zero DB
footprint). New /admin/login, /admin/logout (POST-only, real page), and
/admin/users pages plus bin/create-admin-user.php.

First user created is the admin; everyone after is registered with a
unique normalized email and an optional group. /admin/* and drafts are
admin-only; Lib\Access gates page content from sidecars
(Access::require('group:members')) with login-redirect/404 responses —
public by default, static pages always public by construction. User
management covers disable/enable, delete, promote/demote, group, email,
and password, with last-active-admin lockout guards.

Also: Session::regenerate() against fixation, friendly missing-PDO-driver
errors in Lib\Db, docs at /admin/docs/access-control and updates across
admin-auth/drafts/sidecars/config/libraries and README/AGENTS.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
code
2026-07-15 00:17:54 +00:00
parent cb64836901
commit b882c304b1
39 changed files with 1476 additions and 318 deletions
+15 -10
View File
@@ -28,16 +28,21 @@ return [
'matomo_url' => '',
'matomo_site_id' => '',
// Gates every /admin/* route (clear-cache, docs, and any future admin
// page) behind HTTP Basic Auth. Leave admin_password_hash empty (the
// default) to disable the gate entirely — matches this project's
// existing wide-open behavior until a project opts in. Generate a hash
// with: php -r "echo password_hash('yourpassword', PASSWORD_DEFAULT), PHP_EOL;"
// and set both via App/config.php, e.g.:
// 'admin_username' => 'admin',
// 'admin_password_hash' => '$2y$10$...',
'admin_username' => 'admin',
'admin_password_hash' => '',
// Gates every /admin/* route (clear-cache, docs, users, and any future
// admin page) behind a session login against the `users` table on
// Lib\Db's default connection — see /admin/docs/admin-auth. The first
// user created is the admin; users after that are 'registered', each
// with an optional group, and see whatever content sidecars grant via
// Lib\Access (see /admin/docs/access-control) — /admin/* itself 404s
// for them. Off by default because it depends on SQLite (same
// reasoning as content_index_enabled below): when false, /admin/* is
// wide open, /admin/login, /admin/logout, and /admin/users 404,
// Access::require() allows everything, and nothing ever touches
// Lib\Db because of this feature. After enabling it via
// App/config.php, create the first user at /admin/users (open access
// until at least one user exists) or with:
// php novaconium/bin/create-admin-user.php <username>
'admin_auth_enabled' => false,
// Lib\Db (see /admin/docs/database) — named, simultaneously-usable
// connections, keyed by name; 'default' is the only one required. A