Skip to content

v2.16.0

Choose a tag to compare

@wmantly wmantly released this 12 Aug 16:14
· 8 commits to master since this release
114bd7d
  • feat: model events are standardized, so storage backend no longer decides what can be live. The ORM announced changes for models it managed; everything else was silent, which meant LDAP groups and users, Redis-backed notifications and PATs could not update a page no matter what the view did. utils/model_events gives them the identical contract — model:<Name>:<action> with {model, action, pk, data} — so a subscriber cannot tell which backend a model uses. data goes through toJSON() (stripping isPrivate fields), and a delete never carries a body, enforced in the emitter rather than trusted to each call site. ORM and bespoke models share one filtered bus, so "does this model have a read gate?" is answered in exactly one place.
  • feat: LDAP groups and users announce their changes. Group covers add, remove and member/owner changes; User covers add, update and remove. The users and profile views consume them, so a user added or a group membership changed by another admin now shows up without a refresh — including on someone's own profile, where a stale page is most misleading since it is showing them their own access.
  • security: read gates for every model whose data the UI renders — ResourceEdge, Group, User, Notification, ApiToken, MeshClient, MeshSite, MeshExitGrant, AccessRequest, Agent. Several are row-level, not merely model-level: a user receives their own User record, notifications, PATs and mesh clients and nobody else's, while a directory admin receives all of them except PATs — which have no admin path because the REST route has none either.
  • security: User payloads strip userPassword explicitly. It is present on a record read with attributes: ['*','+'] as the admin bind, and stayed off the wire only because user_parse() sets it to undefined inside an if branch — an incidental protection in an unrelated function, not somewhere to hang a credential.
  • note: the Group gate mirrors its currently ungated REST route (routes/group.js:8) rather than being tightened past it. A socket stricter than the endpoint feeding the same page shows a list that silently stops updating; tighten the route and the gate follows automatically.