diff --git a/docs/waves/wave-10.md b/docs/waves/wave-10.md index f1e72af..168dfe7 100644 --- a/docs/waves/wave-10.md +++ b/docs/waves/wave-10.md @@ -64,7 +64,7 @@ across resolved addresses, because round-robin will hand out a rebooting DC's ad - [x] an empty username returns failure without calling `bind()` - [x] `Tls` is constructed with `validate=ssl.CERT_REQUIRED` and an explicit `ca_certs_file` - [x] no code path sets `CERT_NONE`, and none falls back to the system trust store — asserted by an AST walk in `ldap_auth_check`, not a grep: the docstring names it to explain the ban -- [ ] `member_of` returns true for an account in a **nested** child of the required group +- [~] `member_of` returns true for an account in a **nested** child of the required group — **partially closed Aug 24.** The transitive matching rule was exercised against the live directory for a DIRECT member (`CN=Prime Employees`, 2,003 members) and returned a match, so the rule and the filter are correct on this estate. A genuinely NESTED case (member of a group that is a member of the required group) still has no test, because no such account was to hand. `member_of` now also warns loudly if the transitive query returns nothing where a direct check succeeds, which is how a broken nested lookup would announce itself rather than refusing real members silently. - [x] a bind against `192.168.3.37` (raw IP) fails hostname validation rather than silently passing — `selftest()` to the raw IP returns `untrusted`; to `prime.local`, `0 (ok)` - [ ] `docker compose exec api openssl s_client -connect prime.local:636 -CAfile $LDAP_CA_FILE` reports `Verify return code: 0 (ok)` - [x] the module imports cleanly with no LDAP env set (unconfigured is a first-class state, as with `MICRON_DB_URL`) diff --git a/server/README.md b/server/README.md index b6e4ff2..51a478b 100644 --- a/server/README.md +++ b/server/README.md @@ -138,6 +138,37 @@ previously did not. ## Local dev +> ### A SQLite database created before D13 will reject new sign-ins +> +> `Base.metadata.create_all()` creates missing tables; it never alters existing ones. +> So a `wpsuite.db` built before D13 still has `users.password_hash` declared +> `NOT NULL` with no default, while the current model has no such column — and an +> INSERT that omits it is rejected: +> +> ``` +> IntegrityError: NOT NULL constraint failed: users.password_hash +> ``` +> +> Accounts already in the file keep working, so **you** can sign in and nothing looks +> wrong. It breaks the moment a *new* person signs in, because provisioning them is an +> INSERT — and it surfaces as an HTTP **500**, not a 401, so it reads as a server fault +> rather than anything to do with the schema. +> +> Such a database also has no `alembic_version` table, so `alembic upgrade head` would +> try to replay the baseline against tables that already exist. Stamp it first: +> +> ```bash +> python -m alembic -c server/alembic.ini stamp a1b8c6d4e2f9 # the revision before the drop +> python -m alembic -c server/alembic.ini upgrade head # runs only the drop +> ``` +> +> That keeps whatever is in the file. Deleting the database also works and +> `create_all()` rebuilds a correct schema, but it throws away your test data. +> +> Production is unaffected: it runs Postgres and the container applies migrations at +> start, so the column is dropped properly there. + + ```bash cd server python -m venv .venv && . .venv/bin/activate # Windows: .venv\Scripts\activate