Commit Graph

73 Commits

Author SHA1 Message Date
wmantly 3ceeeeeca1 Clean up footer: fix copyright, move GitHub link out of the nav (#41)
- Copyright was "© <year> <name>" where <name> is conf.name — an
  operator-configurable display name (e.g. whatever CFG_ORG is set
  to), not a real copyright holder. Changed to "© <year> theta42",
  matching the LICENSE file. Also dropped "All rights reserved",
  which contradicts the MIT license this project ships under; added
  an explicit MIT License link instead.
- Moved the GitHub icon link out of the top nav (where it competed
  with actual navigation items) and into the footer, alongside the
  license link and version/build info.
- Deduplicated the identical buildVersion/buildHash/buildYear
  computation that was copy-pasted in both routes/index.js and
  routes/oauth.js into a shared nodejs/utils/build_info.js.

Verified by rendering top+bottom with the real ejs package: no
template errors, GitHub link present exactly once (in the footer,
not the nav), "All rights reserved" gone, MIT License link present.
npm test failures (155) are pre-existing/environmental (no LDAP
server here) — identical failure count with these changes stashed
out.
2026-07-14 20:54:36 -04:00
wmantly 3d3b15b283 README: rewrite with feature overview, comparison, and tiered quick start (#40)
* README: rewrite with feature overview, comparison, and tiered quick start

Expands the README with a fuller feature description, a "why this over
the alternatives" comparison against Keycloak/Authentik/Authelia/Zitadel,
an architecture diagram, and a three-tier quick start (unified theta-env
stack, standalone Docker, bare metal) instead of the old OpenLDAP-setup-
first structure.

* docs/index.md: fix stale setup.env quickstart snippet (.env -> setup.env, CFG_DOMAIN)
2026-07-14 00:24:40 -04:00
wmantly 158109de59 Documentation cleanup for public release (#38)
* docs: cleanup for public release (fix stale/wrong API docs, LICENSE, versions)

Documentation cleanup ahead of the public release announcement. Fixes a set
of confirmed issues from a prior audit:

- LICENSE: fill in MIT template placeholders (theta42, 2026).
- README.md: fix broken API docs link (api.md -> API.md), correct required
  Node.js version (13.x -> 20.x), add the missing app_sso_invite group to
  the LDAP groups table, scrub hardcoded dc=theta42,dc=com to the generic
  dc=example,dc=com used elsewhere, add a "Recommended: Docker or
  install.sh" section pointing to DEPLOYMENT.md/docs before the manual
  OpenLDAP walkthrough, and drop an emoji from a warning callout.
- nodejs/api.md: deleted — it was a stale/legacy doc with wrong routes,
  wrong request bodies, and endpoints that are dead/commented-out code.
  The root API.md is the accurate, current reference; README now links
  there directly.
- API.md: add the missing app_sso_invite permission group, fix the
  documented invite response to match the real {token, link, mail_sent}
  payload, document the previously-undocumented GET/PUT/DELETE
  /api/user/invite endpoints, add the real allowed_groups field to the
  OAuth client management examples, and document POST /api/oauth/authorize
  (the endpoint that actually issues the code after consent).
- nodejs/routes/auth.js + API.md: fix "emaill address" typo in the
  password-reset response message (source and docs kept in sync).
- DEPLOYMENT.md: fix the top-level summary to mention Redis, matching
  docs/deployment.md and the entrypoint behavior it already documents.

Flagged, not changed: tos.md reads like a personal home-lab acceptable-use
policy (Emby/Gitea/Proxmox/Discord/Signal, first-person "the admin") rather
than generic OSS docs. Left in place pending a manual decision to
genericize, relocate, or remove it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* docs: genericize tos.md template, track runtime-editable terms in #39

Removes operator-specific references (Emby, Gitea, Proxmox, Discord,
Signal, first-person "the admin") so the shipped tos.md reads as a
neutral starting template rather than one operator's internal policy.
Actual runtime editability (admin/legal editing terms without a code
change) is tracked in issue #39, not implemented here.

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-13 23:21:27 -04:00
wmantly 11fb2c0a54 docs: document that the domain/base DN is entered once (#37)
The base DN (stack.ldapBaseDn) is the single place the domain is set; the
LDAP DNs (bindDN/userBase/groupBase) and oauth.issuer all derive from it and
must stay consistent. Drifting them apart (leaving bindDN at dc=example,dc=com
while ldapBaseDn is the real domain) makes the SSO bind against a non-existent
root DN and every login fails with Invalid Credentials.

- secrets.js.example: clarifying comment at ldapBaseDn
- DEPLOYMENT.md: "domain entered once as the base DN" note + theta-env
  setup.env cross-link (merged the two duplicate theta-env blockquotes)
- README.md: cross-link to DEPLOYMENT.md from "Server set up"

Docs only; no app/secrets-structure change.

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-13 21:56:48 -04:00
wmantly 796e013234 Fix api-tokens date display + quiet authIO no-token log (#36)
- api_tokens.ejs: created_on/last_used_on come back from Redis as strings
  (model-redis only coerces fields with an explicit `type`), so `new Date(ms)`
  yielded "Invalid date". Use `moment(ms, "x")` (the hosts.ejs/dns.ejs
  precedent) which parses a numeric string-or-number as a Unix-ms timestamp.
- api_tokens.ejs: `isExpired` is a class getter not serialized to the client
  JSON, so the "expired" badge never showed — compute expiry in the view via
  `Date.now() > Number(expires_at)`. Also guard the `last_used_on: 0` / falsy
  case (string "0" is truthy) so unset timestamps render "—" not "1970".
- middleware/auth.js: authIO did `checkToken(socket.handshake.auth.token || 0)`,
  so any socket connect without a token (login page, pre-login) did an
  `AuthToken.get(0)` lookup and logged a noisy `EntryNotFound` trace. Guard:
  reject the socket with a generic 401 when there's no token (behavior-
  preserving — unauth sockets were already rejected; just no Redis lookup / 404).

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-12 17:42:00 -04:00
wmantly 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 6920a9f9f0 Persist Redis (AOF+vol), read config from mounted secrets.js, add backup/restore docs (#34)
Lossless upgrades + config story for the all-in-one image.

Redis persistence (Part A):
- Replace in-memory `--save "" --appendonly no` with AOF + RDB persisted to /data
  (--appendonly yes, periodic saves, --dbfilename dump.rdb). OAuth clients,
  tokens, and other model-redis state now survive container recreation.
- Add the `sso-data` named volume -> /data in docker-compose.yml.

Config from ./config/sso-secrets.js (Part B):
- docker-entrypoint.sh: when /config/sso-secrets.js is mounted, symlink it to
  /app/conf/secrets.js and read the server-side LDAP vars (base DN, admin pass,
  org, domain, cert CN, JWT) from the file via one `node` call (base64-decoded,
  no eval/quoting hazards). No app_* env is exported in this mode, so the file
  is authoritative (@simpleworkjs/conf precedence: base < env < secrets.js <
  app_* env). Falls back to the existing LDAP_* env-var mode when the file is
  absent (standalone/bare-metal still works).
- docker-compose.yml: trim `environment:` to NODE_ENV/NODE_PORT only and add
  `./config:/config:ro`. Removing the app_* env is required — any leftover
  app_* would silently override secrets.js.
- secrets.js.example: add orchestrator-only `stack`, `bootstrap`, and
  `serviceAccountPass` keys (ignored by the app; read by the entrypoint, the
  theta-env bootstrap, and setup.sh).

Backup/restore docs:
- Full "Backups and restore" runbook in DEPLOYMENT.md (what lives where,
  manual backup, full / Redis-only / LDAP-only restore, AOF-vs-RDB note,
  upgrades). Restore uses slapadd -f (static slapd.conf), and RDB restore
  requires deleting the AOF first (AOF wins on startup).
- Pointers in docs/deployment.md and docs/ldap.md; update the Docker Setup
  section for the new ./config/ approach (env vars now advanced/optional).

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-12 12:55:22 -04:00
wmantly d5e951fa9a Merge pull request #33 from theta42/fix/load-sudo-lpk-schema-in-image
Load sudo + openssh-lpk schemas in the all-in-one image (fix schema COPY too)
2026-07-11 22:24:35 -04:00
wmantly 665a41cfde Load sudo + openssh-lpk schemas in the all-in-one image; fix schema COPY
The SSO app (nodejs/models/user_ldap.js addPosixAccount) tags every new user
with objectClasses [inetOrgPerson, sudoRole, ldapPublicKey, posixAccount, top,
theta42Person] and writes sudoHost/sudoCommand/sudoUser + sshPublicKey. The
all-in-one image's slapd.conf only included core/cosine/inetorgperson/nis +
theta42, so creating a user failed: sudoRole and ldapPublicKey were unknown
objectClasses (LDAP objectClassViolation 65) and sudoHost/sudoCommand/sudoUser
and sshPublicKey were unknown attributes.

Ship the two missing schemas and include them in slapd.conf:
  - ops/schema/sudo.schema       (sudoRole + sudo* attributes)
  - ops/schema/openssh-lpk.schema (sshPublicKey + ldapPublicKey)

sudoRole is AUXILIARY here, not STRUCTURAL as in upstream sudo. The app
attaches sudoRole directly onto the user entry, which is already inetOrgPerson
(STRUCTURAL); two unrelated structural classes violate RFC 4512 and OpenLDAP
rejects with 65. AUXILIARY lets it coexist with inetOrgPerson — the app's
per-user-sudoers model. sudo's LDAP backend still finds entries via
(objectClass=sudoRole) regardless. ldapPublicKey is AUXILIARY as in upstream
openssh-lpk.

Also fix the build error from the previous theta42 schema PR: .dockerignore
excluded all of ops/, so 'COPY ops/schema/theta42.schema' failed at build
time ('not found' — file is git-tracked but stripped from the context). Re-
include ops/schema/*.schema with !exceptions, matching the existing
README.md/tos.md pattern.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-11 22:24:12 -04:00
wmantly 84e5358e08 Merge pull request #32 from theta42/fix/load-theta42-schema-in-image
Load the theta42 (dateOfBirth) schema in the all-in-one image
2026-07-11 21:41:40 -04:00
wmantly ef7b6215bd Load the theta42 (dateOfBirth) schema in the all-in-one image
The app (nodejs/models/user_ldap.js) adds objectClass theta42Person whenever a
user has a dateOfBirth, and lists it among every new user's objectClasses. But
the all-in-one image's static slapd.conf only included core/cosine/inetorgperson/
nis — never the project's own theta42 schema — so slapd rejects
objectClass: theta42Person with LDAP 0x15 (objectClass: value #0 invalid per
syntax). Symptom: PUT /api/user/<uid> (and user creation) failing.

ops/ldap-setup.sh loads this schema for bare-metal (as a cn=config LDIF); the
Docker image uses slapd.conf, so ship it as a .schema file and include it.

- ops/schema/theta42.schema: dateOfBirth attribute + theta42Person auxiliary
  objectClass (same OIDs/definition as ldap-setup.sh section 5).
- Dockerfile.openldap: COPY it to /etc/openldap/schema/theta42.schema.
- docker-entrypoint.sh: include it in the generated slapd.conf (after nis).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-11 21:41:27 -04:00
wmantly d1620dd002 Merge pull request #31 from theta42/docs-logs
docs: add a Logs (Docker) section
2026-07-11 21:06:38 -04:00
wmantly faa694aef3 docs: add a Logs (Docker) section
Document how to get logs when running the all-in-one image: docker compose
logs for the app + slapd (both stdout/stderr, slapd runs -d 0), plus a direct
ldapsearch health check. Added to README.md, DEPLOYMENT.md (Method 1), and the
GitHub Pages docs/deployment.md.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-11 21:05:42 -04:00
wmantly fe9b7c168b Dockerize SSO Manager (all-in-one image) + GitHub Pages docs
All-in-one Dockerfile.openldap bundling the app + OpenLDAP + Redis in one
container, plus an idempotent bare-metal install.sh, and a Jekyll docs site
for GitHub Pages:
- Dockerfile.openldap (node:20-alpine; openldap + pw-sha2/ppolicy/memberof/
  refint; dumb-init PID 1; npm ci --omit=dev; tos.md copied to /).
- docker-entrypoint.sh: generate slapd.conf (mdb + overlays + TLS + indexes +
  access), self-signed LDAPS cert, seed directory tree + required groups,
  bundled redis, export app_* config, exec node.
- docker-compose.yml, .dockerignore, DEPLOYMENT.md, secrets.js.example.
- install.sh: idempotent Debian/Ubuntu bare-metal installer (Node 20.x,
  OpenLDAP, Redis, systemd unit) with flags + --dry-run/--skip-ldap/--skip-app.
- ops/ldif/: memberof/refint/tls/index/nodes/logging LDIFs.
- nodejs/conf/base.js: generic defaults (dc=example,dc=com / localhost /
  SSO Manager) so per-deployment values move to secrets.js or app_* env.
- nodejs/package.json: bump @simpleworkjs/conf to ^1.1.0 (app_* env overrides).
- nodejs/routes/index.js: /health endpoint for healthchecks.
- docs/: _config.yml + index/deployment/configuration/oauth/ldap pages
  (jekyll-theme-cayman) for GitHub Pages from /docs.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-11 17:03:16 -04:00
wmantly e28fb63784 Merge pull request #30 from theta42/Permissions
oauth edit
2026-07-11 00:55:53 -04:00
wmantly 4f432ec448 oauth edit 2026-07-11 00:54:49 -04:00
wmantly a48e8c71d7 Merge pull request #29 from theta42/Permissions
oath grpup fixes
2026-07-02 16:52:16 -04:00
wmantly bb79247054 oath grpup fixes 2026-07-02 16:49:22 -04:00
wmantly 54250a6f98 Merge pull request #28 from theta42/Permissions
oath fixes
2026-07-02 16:23:42 -04:00
wmantly 93df047a21 oath fixes 2026-07-02 16:22:16 -04:00
wmantly 7a1f81fc5d Merge pull request #27 from theta42/Permissions
Setup
2026-07-01 23:30:23 -04:00
wmantly 46e0be2271 Setup 2026-07-01 23:29:48 -04:00
wmantly 5476ea9347 Merge pull request #26 from theta42/Permissions
Script fix
2026-07-01 23:19:54 -04:00
wmantly 12262ec85b Script fix 2026-07-01 23:18:25 -04:00
wmantly 94bdbe3093 Merge pull request #25 from theta42/Permissions
Script fix
2026-07-01 23:10:12 -04:00
wmantly 196bbadc20 Script fix 2026-07-01 23:09:44 -04:00
wmantly 7f8ce9afe0 Merge pull request #24 from theta42/Permissions
Login page
2026-07-01 12:08:34 -04:00
wmantly cf2418a9eb Login page 2026-07-01 12:08:11 -04:00
wmantly 7d2d6f5b8c Merge pull request #23 from theta42/Permissions
Testing fix
2026-07-01 11:47:44 -04:00
wmantly c92dbe4aa6 Testing fix 2026-07-01 11:40:38 -04:00
wmantly dc398cf04a Merge pull request #22 from theta42/Permissions
Updated frontend
2026-07-01 11:16:55 -04:00
wmantly 5644bfa5ec Updated frontend 2026-07-01 11:10:09 -04:00
wmantly 5b643dbaec Merge pull request #21 from theta42/Permissions
Corrected email address
2024-02-19 10:52:44 -05:00
wmantly ee21bdbf22 Added app name to conf file 2024-02-19 10:52:06 -05:00
wmantly 24d5364da0 Corrected email address 2024-02-19 10:49:37 -05:00
wmantly 693bdecfc8 Merge pull request #20 from theta42/Permissions
Permissions
2024-02-19 10:44:35 -05:00
wmantly e132015871 Added error message when non-admin cant see groups 2024-02-19 10:42:40 -05:00
wmantly 2a6aaafa3d Added permission to block non admins from seeing all users 2024-02-19 10:42:02 -05:00
wmantly 763e8af7a0 Merge pull request #11 from theta42/updates
name error fixed
2021-04-28 12:56:13 -04:00
wmantly 2654c31f68 name error fixed 2021-04-28 12:55:36 -04:00
wmantly 1022d9da86 name error fixed 2021-04-28 12:52:59 -04:00
wmantly b8ed0f2171 Merge pull request #9 from theta42/updates
Updates
2021-03-23 00:49:39 -04:00
wmantly 623e52e135 started token API 2021-03-23 00:41:12 -04:00
wmantly b97970c8de Updated packages 2021-03-23 00:40:22 -04:00
wmantly 0e212c48cc added white lable name 2021-03-23 00:36:53 -04:00
wmantly 02bc89f750 Create sso-manager.service 2021-03-22 16:54:11 -04:00
wmantly 2c7b6d3fe4 Update README.md 2021-01-29 23:47:41 -05:00
wmantly 9db36a43af Merge pull request #4 from theta42/updates
Updates
2020-12-31 16:15:31 -05:00
wmantly 96a97c1d6d Moved timestamps to footer for user cards 2020-12-31 16:07:22 -05:00
wmantly b56094841f Editing a user updates the card and shows a message 2020-12-31 15:19:39 -05:00