Commit Graph

5 Commits

Author SHA1 Message Date
c6a100405d T10.8 - document that a pre-D13 local SQLite database rejects new sign-ins
Found while diagnosing a login failure that turned out to be an account
lockout. The local wpsuite.db has no alembic_version table - it was built by
create_all() before D13 - so users.password_hash is still NOT NULL with no
default while the current model has no such column. Verified on a COPY of the
database rather than reasoned about: provisioning a new account raises

    IntegrityError: NOT NULL constraint failed: users.password_hash

The shape of it is what makes it worth documenting. Accounts already in the file
keep working, so the developer can sign in and nothing looks wrong; it breaks
only when a NEW person signs in, and it surfaces as HTTP 500, which reads as a
server fault rather than a schema one. Nothing anywhere told a developer their
existing database needed migrating.

The note gives the non-destructive fix - stamp a1b8c6d4e2f9 then upgrade head,
which runs only the drop and keeps the data - and says why a plain
`alembic upgrade head` would fail on such a file. Earlier in the session I
suggested deleting the database; stamping is strictly better, since deleting
discards test data for no benefit.

Production is unaffected: Postgres, migrations applied at container start.

Also updated the nested-group done-when to reflect what is now actually known.
The transitive matching rule WAS exercised against the live directory today for
a direct member of a 2,003-member group and returned a match, so the rule and
the filter are right on this estate. A genuinely nested case remains untested
for want of such an account, and the box is marked partial rather than done.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-24 11:44:16 -05:00
dc0cee240e T10.8 D13/D14 - documentation matches what the code now does
DEPLOY-login-portal.md was the most wrong and is rewritten. It described taking
a username/password portal live - bcrypt, and a first admin created with
`create-admin --password`. Every command in it now fails. It keeps its filename
and carries a note saying what it replaced, because an admin holding the old
copy needs to know why the steps stopped working rather than concluding the
deploy is broken. New content leads with the warning that there is no
break-glass, and puts verification BEFORE announcing the deploy - the log line,
the certificate check that binds nothing, then a real sign-in.

DEPLOYMENT.md: AUTH_RESET_* replaced with the LDAP variables; the users table
row no longer claims a password_hash column; "Self-service password reset"
replaced by a section saying there isn't one and pointing at Okta. New "Domain
authentication" section covering the three things that are not obvious - why
prime.local and never a DC or an IP, why the CA bundle is not a certificate
issued to this app (with the thumbprints and a Get-ChildItem line to rebuild
it), and why the outbound network stopped being optional - plus the lockout
arithmetic written out so the next person to raise AUTH_MAX_ATTEMPTS sees the
constraint rather than a magic 2.

server/README.md: endpoint table drops /api/auth/password and gains the role
route; the login-portal section becomes domain authentication; create-admin
becomes the two-step bootstrap (sign in, then promote).

CLAUDE.md: a new "authentication rules" section beside the token rule, for the
same reason that one exists - four things that look like tidying-up if you do
not know why. The empty-password guard that must run before bind(), CERT_REQUIRED
with an explicit CA file, AUTH_MAX_ATTEMPTS being arithmetic rather than taste,
and connecting to the domain name rather than a DC. Plus: no break-glass, and
roles are local - never read a role from AD.

Closed three done-when boxes that were open rather than ticked:

  T10.8  all of them
  T10.9  promote/demote verified against a real bind (Aug 24), not a stub
  T10.3  the Postgres round trip, on postgres:16-alpine

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-24 08:26:31 -05:00
20afb0e565 Add secure username/password login portal
Gate the suite behind a self-contained login (no external IdP):

- User model with bcrypt-hashed passwords; admin/user roles
- /api/auth endpoints: login, logout, me, change-password, and
  admin-only user management (list/create/delete/reset/enable)
- Stateless JWT session in an HttpOnly, SameSite=Lax, auto-Secure
  cookie; middleware refuses every /api data route without a session
- login.html + auth-guard.js: login page and per-page guard with a
  top-right "name / Admin / Sign out" pill
- Admin Console now gated on admin role (passphrase gate removed) with
  a User administration card
- manage_users.py CLI to bootstrap the first admin
- Rebuilt help.js into a searchable, multi-topic help center
- Local-dev convenience: app serves html/ so the site + API share one
  origin under uvicorn (inactive in the prod container)
- Docs/env: AUTH_SECRET_KEY, requirements (bcrypt, PyJWT), README

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 16:55:54 -05:00
ffcaa571d1 Add API to production 2026-06-15 13:46:43 -05:00
a33b777aca Add Python/FastAPI + PostgreSQL backend (Phase 1)
- server/: FastAPI app with SQLAlchemy models for sops, work_packages, comments
- Endpoints for SOP/WP upsert+list+get+delete and comment create+list;
  /api/feedback kept as an alias so the existing client keeps working
- Portable across engines (PostgreSQL prod, SQLite dev fallback)
- requirements.txt, .env.example, and server/README.md (Postgres + systemd)
- NGINX now proxies /api/ to the API (replaces the Power Automate hop;
  comments persist to SQL)
- Rewrite DEPLOYMENT.md for the API + database architecture
- Add .gitignore for venv/.env/sqlite

Phase 2 (wire the client apps to the API) is next.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 10:58:52 -07:00