This commit is contained in:
2026-10-29 11:51:44 -04:00
parent 27a6305b22
commit e4be5bfdf4
10 changed files with 432 additions and 71 deletions
+64 -4
View File
@@ -16,8 +16,10 @@ Both payment methods feed the same LDAP group reconciliation and local expiry.
2. Sign-in uses the single Dex LDAP option with Authorization Code + PKCE, state
and nonce. Authlib verifies the RS256 ID token, issuer, audience, signature
and lifetime. The signed connector/UID must match the configured connector
and LDAP identity; account ownership is anchored to immutable `entryUUID`,
never email alone.
and username. Local account ownership is pinned to the verified Dex issuer
and subject, never email alone. Membership makes **no LDAP connection during
login**, including first login by an existing Dex user. The worker separately
resolves and pins LDAP `entryUUID` before synchronizing paid groups.
3. Authenticated users choose a server-allowlisted recurring Stripe Price.
Stripe hosts Checkout and the billing portal. Return URLs never grant access.
4. Stripe webhooks are signature-verified and durably deduplicated. The worker
@@ -116,8 +118,8 @@ of these five prices when inspected.
preserve subjects in other apps. Add the website `inetOrgPerson` branch to the
LDAP filter; this allows those entries to authenticate to all Dex clients, so
review that scope. Dex uses signed `federated:id` claims, while the app resolves
UID to LDAP and checks immutable `entryUUID`. RS256 and all discovery endpoints
must be HTTPS on the issuer's origin. Existing lowercase LDAP usernames
UID from verified claims without querying LDAP during login. RS256 and all
discovery endpoints must be HTTPS on the issuer's origin. Existing lowercase LDAP usernames
matching the registration syntax can sign in without signup.
5. The supplied tier file contains live Price IDs. Set `STRIPE_LIVE_MODE=1` only
with the matching live Stripe API key. For sandbox testing, override
@@ -167,6 +169,64 @@ No production credentials or live account changes are made by the test suite.
## Operations and limitations
### Dex-only login and upgrade
Deploy the new membership image and apply migration `0004_dex_login_identity`
before serving it. **Keep `DEX_ISSUER` unchanged during the upgrade**: the
migration pins existing non-null subjects to the currently configured issuer,
because older records did not store their issuer. Back up the database and
coordinate replacing web and worker together; older code cannot handle accounts
whose directory UUID has not yet been resolved. Do not roll back to the old
schema while unresolved accounts exist.
- Returning users sign in using their existing issuer/subject binding, without
LDAP or payment-provider calls. Signed verified email updates their contact
address; it never identifies an account or attaches billing history.
- First-time Dex users with no local username collision receive a free local
account. The worker discovers their LDAP DN/UUID on its next successful group
sync and thereafter uses only that UUID. Missing/ambiguous directory entries
stop synchronization; a missing pinned UUID never falls back to username.
- Website registration still provisions LDAP. Its browser session retains a
one-hour, server-side proof permitting the first Dex login to bind only the
just-created local account, with the same signed connector/username. Successful
login consumes that proof. Without it, existing unbound accounts require an
administrator mapping; matching email or username alone is insufficient.
- For an existing **unbound** account, the management command `bind_dex_identity`
requires `--account` (local account UUID), `--uid` and `--subject` (the exact
Dex `sub` for this issuer/client/connector). Independently verify the identity
with trusted Dex/directory administration before running it. Do not accept
subjects asserted by users or decode an unverified token as proof. It makes
no LDAP calls, preserves directory/billing data and refuses to replace an
existing binding or reuse another account's subject. Lost registration proof
can be recovered through this reviewed mapping, not by repeating registration.
- Issuer, connector, username and subject changes require an explicit identity
migration. The old automatic rebinding of retired `www-ldap` subjects is
intentionally removed; review those bindings separately before deployment.
The existing Dex LDAP connector uses `idAttr: uid`. **Do not recycle usernames**:
Dex itself cannot distinguish a recreated LDAP entry with the same connector/uid
from the previous identity. Directory UUID pinning still protects group writes,
but membership no longer checks directory recreation during login. Preserve the
connector mapping for other services; migrating Dex to immutable user IDs is a
separate coordinated change, not an automatic part of this release.
### LDAP service credentials still required for directory writes
LDAP is still needed for registration and worker group synchronization. Configure
the least-privilege `LDAP_BIND_DN` and its **plaintext bind password** in
`LDAP_BIND_PASSWORD`, not a stored `{CRYPT}$1$...` hash or already-base64-encoded
value. Kubernetes Secret `data` encodes original values once. Check service
password rotation/account locks and redeploy web and worker after Secret updates;
existing pods do not refresh environment credentials automatically. Never use
root/admin credentials or disable TLS verification to bypass failures.
Older images may log `LDAP service bind rejected during Dex callback` or
`Dex callback rejected kind=LDAPInvalidCredentialsResult`. These login-time
service binds are removed by this release. OpenLDAP remains responsible for
verifying users' existing password hashes through Dex. `LDAP_PASSWORD_SCHEME`
only gates new registration; its Argon2 requirement is unchanged. Plan migration
away from weak legacy password hashes separately.
### SHKeeper prepaid crypto
Stripe remains the automatically recurring payment method. SHKeeper accepts