Skip to main content
Version: 2.0.0 (Latest)

Users, roles & permissions

This page is the UI identity management and runtime RBAC reference for padas-ui: human authentication to Express only, session-backed authorization via JWT in HTTP-only cookies, and identity persistence in users.json / secret.json under PADAS_UI_HOME/data/security/. The identity boundary for operators is the UI server—the browser authenticates to the UI only; the UI then mediates Core access with per-row service Bearer tokens (see Security — runtime operational security reference). Core does not read users.json, validate JWT cookies, or enforce admin / userruntime authorization on Core is Bearer-gated for the whole /api/v1/* surface, independent of console identity.

Related: Security · REST API Reference · Runtime configurations · Cores · Monitoring · Troubleshooting & Logs · Glossary · Quickstart: Core + UI · Installation — Linux


Overview

ConcernRuntime behavior
Human authenticationPOST /auth/login (authController.js) validates credentials (local users.json or LDAP bind); success issues JWT pair via handleToken.js into access_token / refresh_token cookies.
Route-level authorizationauthenticateToken (authMiddleware.js) verifies JWT with ACCESS_SECRET from secret.json; authorizeRoles([...]) gates routes.js by req.user.roles.
JWT cookie lifecycleShort-lived access token + refresh path; middleware may reject expired access and depend on client refresh flows documented in the same middleware chain.
UI as authorization boundaryAll mutating console APIs hit Express first; no UI role is sent to Core as a claim—delegated authority to Core is the stored account_token, not the human’s JWT.
Human vs service identityHuman identity = JWT + users.json roles. Service identity = Core service-account.token mirrored in the UI registry—orthogonal layers (Security).

Initial admin bootstrap and first login

First-run operational flow (server/server.js, config/users.js): on first start after licensing, createUsersFile() writes { "local": [] }; createSecretFile() materializes secret.json (ACCESS_SECRET, REFRESH_SECRET). Until users.local is non-empty, the sign-in surface shows System Not Initialized—from a runtime perspective the UI is unauthenticated for human operators but already exposes bootstrap attack surface: GET /auth/init and POST /auth/init are mounted without authenticateToken.

Why /auth/init matters: it is the only supported path to mint the first users.json row with roles: ['admin']. GET /auth/init returns { "initialized": false } until a user exists; POST /auth/init accepts name, username, password, email and always assigns roles: ['admin']. If any user already exists → 409 Conflict. Operational hardening: after bootstrap, /auth/init should be treated as reachable only on a trusted network (ingress allowlist, reverse proxy ACL, or private admin VLAN)—the handler stays mounted; defense is deployment isolation, not a second code gate.

JWT secret generation implications: secret.json defines signing trust for all future sessions. Loss or rotation of that file without coordinated user re-login invalidates existing JWTs—plan JWT secret rotation as a session invalidation event (see Operational identity management).

users.json operational persistence: bootstrap appends the first local user; subsequent CRUD goes through authenticated admin routes. Filesystem permission expectations: treat PADAS_UI_HOME/data/security/ as sensitive—same ownership model as other operational secrets. Bootstrap recovery / reinstall / migration: restoring an old users.json without matching secret.json breaks verification; copying only secret.json from another host desynchronizes issued tokens vs stored password hashes—treat backup/restore as a single security directory concern.

curl -sS http://UI_HOST:PORT/auth/init

curl -sS -X POST http://UI_HOST:PORT/auth/init \
-H "Content-Type: application/json" \
-d '{"name":"Ops Admin","username":"admin1","password":"YourComplexPass1","email":"admin1@example.com"}'

Finally POST /auth/login sets cookies; further API calls require authenticateToken on routes.js. Then register Cores with each engine’s service-account.token (Cores).


In the UI: session menu, profile, and password

Header menu (role in context)

Runtime session identity is whatever authenticateToken decoded into req.user: username and roles drive the header label and Administrator badge when admin is present. This is JWT-backed UI state, not a second server round-trip per click—the badge is a visualization of the same roles array authorizeRoles consults.

Home: user avatar menu with admin username, Administrator badge, Profile, Change password, Logout
Runtime session: user label, role badge, and account actions from the header.

User profile

Profile reflects authorization identity as persisted: Name, Username, E-Mail, Roles from users.json for local users. LDAP-backed users see the same shape after bind—the server still issues a local JWT carrying req.user.roles. This screen does not grant privilege by itself; it mirrors what the authentication middleware already embedded in the token.

User Profile: Name, Username, E-Mail, Roles fields and Change Password button
User Profile: fields map to persisted identity; roles drive route access on the UI server.

Change password

Change password is local-user self-service: modal submits credential update paths in authController.js / user controller stack. Operational behavior: successful update changes stored hash in users.json; existing JWTs may remain valid until expiry—operators should assume password rotation does not instantly revoke all sessions unless secrets or token lists are cleared. Local-user scope limitations: LDAP directory passwords are not managed through this modal; operators use IdP flows.

Change Password modal with password fields and Update password / Cancel
Self-service password update for the current local user.

Roles and Express route enforcement

Runtime RBAC in padas-ui is two role strings only: admin and user, declared per route in server/routes/routes.js. The middleware chain is: authenticateToken (JWT from cookie → verify/decode → attach req.user) then, where declared, authorizeRoles(['admin']) or authorizeRoles(['admin','user']), which compares allowed role names to req.user.roles. On failure authorizeRoles responds 403 Forbidden; missing/invalid JWT is 401 from authenticateToken.

Surfaceadmin onlyadmin + user
Licensesverify, list, add, delete
Users / LDAP / audit logslist, CRUD
POST /logs
Registry create/update/delete, bulk delete
Registry GET
Management deploy / start / stop / restart
Monitoring, captures, POST /test/run

Why Core does not enforce UI roles: padas-api has no concept of admin / user for HTTP—runtime authorization there is Bearer (or disabled). The UI is a delegated Core authority client: whoever passes authenticateToken + route RBAC can trigger server-side calls that attach the row’s account_token. UI-server-side mediation means effective authority = min(JWT route access, stored Core token scope).

Implementation notes (userController.js, authController.js):

  • POST /auth/users (guarded by authorizeRoles(['admin'])) currently forces req.body.roles = ['admin'] for API-created users—runtime RBAC today collapses new local users to admin-equivalent in code.
  • LDAP path: after successful bind, ldapUser.roles = ['admin'] before handleToken.js issues JWT—admin-equivalent LDAP behavior for authorization surface on Express.

Security implications (explicit):

TopicBehavior
Stored Core tokensRegistry holds account_token per Core; any session that can reach management/registry routes can cause the UI process to call Core with that Bearer.
UI compromiseHost-level compromise of padas-ui exposes secret.json, users.json, and DB/registry—effective authority includes all configured Core tokens.
Current implementation limitationsNo fine-grained resource ABAC on UI; LDAP lacks per-group role mapping in code; API user create cannot mint user-only rows today.

Multiple local users

Local user persistence: accounts live under users.jsonlocal array entries with username, password hash, roles, profile fields. POST /auth/users (admin) appends users but, as above, forces admin role in the handler—operational account management via API cannot yet express least-privilege user creation without code or manual file edit (unsupported operationally).

Admin / user role model: user can read registry and run deploy/monitor flows per route table; admin additionally manages users, LDAP settings, licenses, audit exports, and destructive registry operations. Shared operational environments: shared admin accounts share audit attribution only as well as the last editor metadata on objects—prefer named operators where policy requires accountability.

RBAC limitations: no row-level UI permissions, no per-Core segregation by role—authorization surface is route-wide. Audit implications: auditMiddleware on mutating verbs logs actor from token context; correlate with Monitoring for incident review.


LDAP directory sign-in

Runtime LDAP auth flow: credential POST /auth/login can target the LDAP branch in authController.js (when UI LDAP is configured). Successful LDAP bind builds a user object, assigns ldapUser.roles = ['admin'], then uses the same handleToken.js path as local users—local JWT issuance after LDAP bind; the browser still only ever holds UI JWT cookies, not directory credentials on subsequent requests.

Role mapping limitations: there is no group-to-role matrix in current code—every LDAP session is admin-equivalent for authorizeRoles. Identity provider trust boundary: the UI server trusts the directory for password verification only; directory outage surfaces as LDAP bind failures at login, not as Core errors.

Operational deployment implications: secure LDAPS/TLS to IdP, lock down users.json and LDAP config backup paths, and document that fallback to local admin may be required during IdP incidents. Admin-equivalent LDAP behavior is a known implementation limitation—do not assume least-privilege directory groups map to user until product changes land.


Runtime identity flow

StepRuntime behavior
Browser loginOperator submits username / password to POST /auth/login.
/auth/loginauthController.js validates local users.json or LDAP; on success calls token helpers.
JWT creationhandleToken.js signs access and refresh with ACCESS_SECRET / REFRESH_SECRET.
Cookie persistenceHTTP-only cookies carry tokens; browser stores no Core Bearer for human flows.
Request authenticationauthenticateToken in authMiddleware.js reads access_token, verifies signature/expiry, attaches req.user.
Role authorizationauthorizeRoles on routes.js checks req.user.roles against allowed list.
Backend API accessMutating handlers run as the Node process, using DB + registry state.
Core API mediationHTTP clients add Authorization: Bearer <account_token> from the selected Core row—delegated authority, not JWT forwarded to Core.

Trust boundaries and authorization model

BoundaryEnforcement
Browser ↔ UIHuman authentication + session-backed authorization via JWT cookies + Express middleware only.
UI ↔ CoreService authentication with per-Core Bearer; Core does not enforce UI roles.
JWT vs BearerJWT = operator session to Node; Bearer = automation/Core client secret—separate trust boundary and rotation.
Human identity vs machine identityusers.json / LDAP vs service-account.token / registry account_token.
Delegated authorityUI actions that call Core always use server-side stored tokens—effective power is host + DB trust, not browser-held Core secrets.
Runtime authorization surfaceExpress route table + Core /api/v1/* Bearer gate—two stacked surfaces operators must harden (Security).

Operational identity management

  • Password rotation: local password change updates users.json; plan companion session review because outstanding JWTs may persist until expiry/invalidation paths fire.
  • Admin account hygiene: avoid long-lived shared admin; split duties where policy allows once user-creation API behavior supports it.
  • Shared accounts risks: weak audit attribution and password churn ambiguity.
  • Bootstrap recovery: re-exposing /auth/init on a fresh users.local empty install vs 409 on populated data—never mix prod data dirs with lab secret.json casually.
  • Session invalidation expectations: clearing userTokenList / restart behaviors (if configured) and secret rotation all invalidate or strand sessions—communicate maintenance windows.
  • JWT secret rotation implications: new secret.json invalidates all issued JWTs—operators must re-login every human session.
  • UI migration considerations: move PADAS_UI_HOME/data/security/ atomically with the registry DB backing routes.js consumers expect.
  • Backup sensitivity: users.json (hashes), secret.json (signing keys), and DB hold operational identity—encrypt backups and restrict restore RBAC.

Security considerations and troubleshooting

SymptomLikely cause
Expired JWT401 from authenticateToken; refresh path or re-login required.
Cookie / session issuesBrowser blocked cookies, wrong UI origin, or refresh token mismatch leading to token list removal in middleware paths.
Invalid role errors403 from authorizeRoles—JWT lacks required admin / user entry.
LDAP bind failuresDirectory unreachable, bad bind DN/password template, or TLS to IdP failing—check UI LDAP config and network.
Bootstrap conflicts (409)POST /auth/init when users.local already has a user—expected guard.
Corrupted users.jsonJSON parse failures or manual edit errors—restore from backup; validate file permissions.
Lost secret.jsonCannot verify any JWT; treat as crypto outage—restore secrets backup or regenerate and force global re-login.
Auth redirect loopsClient repeatedly hitting protected routes without valid cookie—check reverse proxy cookie forwarding.
Stale sessions after role changesJWT still carries old roles until re-issue—user must re-login or wait for access token expiry depending on middleware behavior.

Deeper triage: Troubleshooting & Logs.


  • Security — trust boundaries, TLS, Core Bearer, token lifecycle, runtime API exposure
  • REST API Referenceservice authentication to /api/v1/*
  • GlossaryJWT session, Bearer authentication, service account token
  • Runtime configurationsPADAS_UI_HOME, engine connectivity context
  • CoresCore registration and account_token lifecycle
  • Monitoring — correlating auth errors with pipeline health
  • Troubleshooting & Logs — session, TLS, and reachability playbooks