Commit Graph
16 Commits
Author SHA1 Message Date
wmantlyandClaude Sonnet 5 0b701dfc6f Merge OAuth Apps + LDAP Info into one tabbed page; add Service Accounts
- OAuth Apps and LDAP Info are both "how do other apps/hosts plug into
  this SSO" concerns -- merged into a single /integrations page with
  tabs, replacing the two separate nav items with one. /oauth-clients
  and /ldap-info 301-redirect there for compat.
- Add a Service Accounts section under the LDAP tab: bind-only LDAP
  identities (organizationalRole + simpleSecurityObject, no
  posixAccount) for apps/hosts, as opposed to real people. Create,
  rotate password, and delete, all from the UI -- previously the only
  such account (theta-env's bootstrap-created cn=ldapclient) was
  invisible to the Users page entirely (filtered out by
  conf.ldap.userFilter) and had no GUI way to see or rotate it; the new
  ServiceAccount model uses the exact same objectClasses bootstrap.js
  already creates cn=ldapclient with, so it recognizes and manages that
  account too, not just ones created through this UI.
- The ldap-client bash snippet now points at "create one under Service
  Accounts above" instead of a bare textual example.

Verified against a real LDAP server (not just the dev sandbox's usual
unreachable one): created a service account, confirmed it binds
successfully with the generated password, rotated its password, and
deleted it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-15 19:57:32 -04:00
wmantlyandClaude Sonnet 5 ed62e70678 Stop the notification Compose form from defaulting to "email everyone"
The Compose form's "Send to" radio group had "All active users" checked
by default with no confirmation before Send -- anyone opening the
Dashboard to see how the feature works, typing a test subject/message,
and clicking Send would broadcast to every active user. Remove the
default (a target must now be explicitly chosen) and require a confirm
step before actually sending to "all" or "all_active".

Also add a hard safety net in models/email.js: Mail.send is a no-op
under NODE_ENV=test, so the automated test suite (which exercises the
real notification/password-reset/invite/OTP-by-email routes with
NODE_ENV=test) can never deliver real mail regardless of what recipient
list a test resolves.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-15 17:02:57 -04:00
wmantly f45349e0cd Fix uid/gidNumber allocation crash, add a configurable id floor (#44)
Reported: creating any user via the API failed with

  {"name":"InvalidSyntaxError","message":"gidNumber: value #0 invalid per syntax Code: 0x15"}

Root cause: addPosixGroup() computes the next gidNumber as
`Math.max(...groups.map(i => i.gidNumber)) + 1`. theta-env's
bootstrap.js creates the first admin via raw ldapadd with a hardcoded
uidNumber/gidNumber (10000) directly on the user entry, but never
creates a matching posixGroup entry -- so on a theta-env-bootstrapped
directory there are zero posixGroup entries, `Math.max()` on an empty
array is `-Infinity` in JS (not 0), and `-Infinity + 1` stringifies to
"-Infinity" -- an invalid LDAP integer, rejected by the directory. This
broke every single user creation, not just this one.

Separately: the reporter's intended scheme is for organically-created
users to start at uidNumber/gidNumber 1500, distinct from the
bootstrap admin's reserved 10000. Fixing the crash with a bare "floor
of 1500" alone wouldn't achieve that, since addPosixAccount's own
Math.max() would still find the admin's posixAccount entry (uidNumber
10000, found via a different, correctly-indexed search) and allocate
10001 for the next user.

Added a shared nextPosixId(entries, key) helper: takes the highest
existing value strictly below conf.ldap.uidGidReservedFloor (default
9000) plus one, or conf.ldap.uidGidMin (default 1500) if there are no
such entries. Ids at/above the reserved floor -- like the bootstrap
admin's 10000 -- are ignored entirely when computing the next
available number, so real users always start at 1500 and grow upward
regardless of the admin's reserved id.

Verified against a real theta-env deployment end to end:
- Reproduced the exact reported crash on a fresh bootstrap
- After the fix: first real user gets uidNumber/gidNumber "1500",
  second gets "1501" -- admin's 10000 never enters the calculation
- New unit tests (nodejs/tests/posix_id.test.js, no LDAP required):
  6/6 pass, covering the empty-array case, the reserved-floor
  exclusion, and the NaN-from-missing-value case
- npm test: 18/18 passing tests still pass (unchanged); the other 155
  failures are pre-existing/environmental (no LDAP server in this
  sandbox) -- confirmed via git stash before starting this fix
2026-07-14 23:03:59 -04:00
wmantlyandClaude b91ef2792d Add self-service API tokens (PATs) with UI + Bearer auth (#35)
Personal access tokens so scripts/CI can call the management API without a
browser session. Each logged-in user mints their own token; it authenticates as
the creator (carries their LDAP group permissions, re-resolved live), so the
existing permission.byGroup checks apply unchanged.

- models/api_token.js: new ApiToken model (sso_<id>_<secret> format; id is the
  lookup key, secret bcrypt-hashed + isPrivate, shown once). add()/rotate()/
  authenticate(); optional expires_at; best-effort last_used_on. No _ttl
  (persists; lifetime via expires_at).
- routes/api_token.js: self-service CRUD (list/get/update/delete/rotate),
  owner-scoped (created_by === req.user.uid, 403 otherwise).
- middleware/auth.js + models/auth.js: accept `Authorization: Bearer sso_...`
  (precedence over the auth-token session header); checkApiToken collapses
  every failure to one generic 401 (no existence/secret/expiry leak).
- views/api_tokens.ejs + routes/index.js (GET /api-tokens): self-service page
  (forceLogin, no group gate) — create (token shown once), edit, rotate, revoke.
- views/top.ejs: "API Tokens" nav entry visible to all logged-in users.
- public/js/app.js: app.apiToken client module.
- DEPLOYMENT.md + docs/deployment.md: API tokens section.

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-12 17:12:35 -04:00
wmantly bb79247054 oath grpup fixes 2026-07-02 16:49:22 -04:00
wmantly 5644bfa5ec Updated frontend 2026-07-01 11:10:09 -04:00
wmantly 24d5364da0 Corrected email address 2024-02-19 10:49:37 -05:00
wmantly 0e212c48cc added white lable name 2021-03-23 00:36:53 -04:00
wmantly cb51fae2f6 added group owners 2020-12-31 09:53:44 -05:00
wmantly 5143162510 Better error message failed removing user 2020-12-30 15:27:06 -05:00
wmantly adf073bab7 Forced user names to be lowercase. 2020-12-30 13:21:18 -05:00
wmantly b8f632e644 user edit 2020-05-15 15:17:57 -04:00
wmantly 0889832efc groups and reset 2020-05-15 00:40:15 -04:00
wmantly 4d51a4ac9e rc1 2020-05-05 23:07:00 -04:00
wmantly f2309463a4 beta 2020-05-03 18:28:11 -04:00
wmantly 8dc0e946b1 beta 2020-05-03 18:22:51 -04:00