Files
mattermost/webapp
Maria A Nunez 57fe965ac7 Session Attributes: System Console management page and permission-policy picker integration (#37362)
* Add Session Attributes system console page shell

Register the gated System Console "Session Attributes" subsection backed by a
component page that loads the session_attributes property fields, plus a feature
discovery upsell. Removes the former config toggles from the UI. Includes display
helpers and unit tests.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Add Session Attributes listing table

Render the read-only session attributes table with Display Name, Name (with
server-sourced label), derived Type, platform availability icons, TTL, grace
period, and status columns, plus loading and empty states. Includes status
chip, platform icon, and table unit tests.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Add Session Attributes row actions and staged save

Add a per-row actions menu to tune TTL and grace period via presets and to
disable/enable each attribute, with a destructive disable confirmation modal.
Edits stage locally and persist on save via a per-field property patch thunk,
with an unsaved-changes guard. Includes the patch thunk and component tests.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Surface session attributes in permission policy editors only

Include session attributes in the permission policy attribute picker (both advanced and table editor modes) and group them under a dedicated section, while excluding them from membership and parent access-rule editors via a shared helper. Surface the server rejection message when a membership rule references session attributes.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix lint formatting in session attributes table test

Split single-line renderWithContext JSX in session_attributes_table.test.tsx
onto multiple lines to satisfy react/jsx-max-props-per-line, matching the
existing test formatting convention. Surfaced by the pre-commit ESLint gate.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Add Session Attributes E2E specs

Add Playwright end-to-end coverage for the Session Attributes console page: listing render, staged TTL/grace/enable edits with save persistence, disable confirmation modal, dirty navigation guard with cancel revert, and permission-versus-membership picker polarity. Includes the page object and sidebar wiring.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix playwright-lib build to resolve @/* tsconfig path alias

The lib rollup build (@rollup/plugin-typescript) compiled the @/* tsconfig path alias but never rewrote it, so with preserveModules the emitted dist files still imported the unresolved @/ specifier. Node could not load these, failing Playwright config loading for the entire suite (Cannot find module @/ui/components).

Add a minimal inline rollup resolveId plugin that maps @/* to the real src file so emitted dist uses correct relative imports. No new dependency required.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix session attribute group identity to use real group id

The webapp conflated the property group NAME (session_attributes) with the
group's real DB id. The server keys every field under a 26-char group UUID,
so the listing page selected fields under the name and always rendered
"No session attributes found." on a real server, and the ABAC editors
compared field.group_id against the name, which never matched live data.

Resolve the group properly instead:

- fetchPropertyFields now also dispatches RECEIVED_PROPERTY_GROUP (mirroring
  the managed-channel-categories flow) so the group name -> id mapping is
  available, and the listing page selects fields by the resolved group id
  via getPropertyGroupByName.
- Add isSessionAttributeField, keyed on the session object_type, and use it
  in the ABAC consumers (attribute picker, excludeSessionAttributes,
  permission policy picker, simulate-access modal). object_type is intrinsic
  to the autocomplete fields and avoids the name/UUID conflation entirely.
- Relax UserPropertyField.group_id to string since the server returns a UUID;
  the group name is only ever a client-side placeholder for an unsaved field.

Harden the unit tests to use realistic group UUIDs distinct from the group
name and correct object_type values, so fixtures can no longer pass with a
group_id the server never produces.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix SaveChangesPanel footer Cancel to revert in place

The admin SaveChangesPanel rendered its onCancel handler through a
BlockableButton, which intercepts clicks while navigation is blocked and
defers the action via deferNavigation instead of invoking it. On the
Session Attributes page this meant footer Cancel never reverted staged
edits directly: it re-opened the Discard Changes guard and only reverted
if a deferred navigation was later confirmed.

BlockableButton is only appropriate for the cancelLink (navigation) path.
For an in-place revert handler, wire onCancel to a plain button so it
calls the hook cancel() directly, reverting staged edits, clearing the
dirty state, releasing setNavigationBlocked, and never showing the
discard modal.

Repair the page-level jest test to exercise the real footer Cancel and
assert direct revert plus nav-guard release without confirming a deferred
navigation.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Merge enabled session attributes into permission policy editors

Inject enabled session attributes into the two permission-policy attribute
pickers (CEL and table editors) while keeping membership/parent-policy
surfaces restricted to long-lived user attributes via excludeSessionAttributes.
Adds a useEnabledSessionAttributeFields hook plus the shared
mergeSessionAttributes/celPrefixForField helpers so the editors can build CEL
across the user.attributes.* and user.session.* namespaces.

Addresses two review nits:
- Resolve the picked attribute by its unique field id (not bare name) so a
  session attribute sharing a name with a CPA attribute still maps to
  object_type 'session' and emits user.session.<name>; the picker also uses
  id-based DOM ids to avoid collisions.
- Make celPrefixForField the single source of CEL-prefix truth by consuming
  it in rowToCEL instead of inlining the namespace check.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix Session Attributes E2E spec: id-based picker selectors and per-test state isolation

The attribute picker menu items are keyed by field id (#attribute-<id>) rather
than name, so the permission/membership picker assertions now resolve session
attribute ids from the property-fields API and the Department user-attribute id
from the CPA fields API instead of targeting stale name-based ids.

Add an afterEach that restores every seeded session attribute to a baseline
captured at suite start, replacing the per-test finally restores. This keeps the
suite order-independent so the listing test "Disabled by default" assertions no
longer depend on whether another test has enabled a field first.

Co-authored-by: Cursor <cursoragent@cursor.com>

* UX Polish

* Linting

* Removed styled components

* Fix session attributes unit tests

Co-authored-by: maria.nunez <maria.nunez@mattermost.com>

* Fix invalid Sass '&&&' selector breaking production build

Co-authored-by: maria.nunez <maria.nunez@mattermost.com>

* Stabilize session attributes duration-preset e2e interaction

Co-authored-by: maria.nunez <maria.nunez@mattermost.com>

* Fix broken session attributes doc link in feature discovery

Co-authored-by: maria.nunez <maria.nunez@mattermost.com>

* Fix session attributes test type error from non-union group id

Co-authored-by: maria.nunez <maria.nunez@mattermost.com>

* Revert unnecessary playwright-lib rollup @/* alias resolver

@rollup/plugin-typescript already resolves the @/* tsconfig path alias;
building with and without the added resolveSrcAlias plugin produces
byte-identical dist JS, so the change was a no-op.

Co-authored-by: maria.nunez <maria.nunez@mattermost.com>

* Allow session-attribute row defaults and resolve table-editor lookups by namespace

findFirstAvailableAttributeFromList now treats session attributes as
selectable (mirroring AttributeSelectorMenu), so a session-only attribute
set no longer fails addRow. The operator menu reads row.attribute_type and
the value options match on name + object_type, so a user/session name
collision resolves the correct namespace.

Co-authored-by: maria.nunez <maria.nunez@mattermost.com>

* Type session-attribute platforms on the shared property attrs

Add platforms?: string[] to UserPropertyField.attrs so the attribute
picker reads it without an unsafe cast, and narrow the platform filter in
getSessionAttrs with a type guard.

Co-authored-by: maria.nunez <maria.nunez@mattermost.com>

* Session Attributes page: surface load errors, propagate read-only, and improve a11y

Handle a failed property-fields fetch with an explicit error state instead
of leaving the rejection uncaught and showing the empty state. Thread the
page's disabled flag through to the row dot menus so read-only admins cannot
stage edits. Convey each platform icon's active/inactive state in its
accessible name, and document the table border override.

Co-authored-by: maria.nunez <maria.nunez@mattermost.com>

* SaveChangesPanel: use shared Button for the in-place cancel control

Co-authored-by: maria.nunez <maria.nunez@mattermost.com>

* Cover fetchPropertyFields group name to uuid mapping

Co-authored-by: maria.nunez <maria.nunez@mattermost.com>

* E2E: scope session-attribute submenu trigger to its row and document single-field save

Co-authored-by: maria.nunez <maria.nunez@mattermost.com>

* Comment linting

* scss cleanup

* linting

* PR feedback

* PR Feedback. Removed IP + version icons

* PR feedback

* PR feedback

* Fix session attributes table test after IP/Version display types were removed

Co-authored-by: maria.nunez <maria.nunez@mattermost.com>

* Session Attributes admin console UX fixes

- Reduce table row height and shrink-wrap the panel to the table
- Reorder LHS sidebar so Session Attributes follows User Attributes
- Cap attribute selector popover at 400px max-height with scroll

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix native attribute selection by assigning synthetic field IDs

The ABAC editors resolve the selected attribute by field ID, but the
synthetic native user attribute fields were created without an ID. Every
built-in attribute therefore resolved to the first empty-ID field (Email),
so no other built-in could be selected. Assign each native field a stable,
unique, non-empty ID so id-based resolution works for all built-ins.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-16 08:05:03 -04:00
..
2026-04-01 13:03:36 +00:00

Mattermost Web App

This folder contains the client code for the Mattermost web app. It's broken up into multiple packages each of which either contains an area of the app (such as playbooks) or shared logic used across other packages (such as the packages located in the platform directory). For anyone who's used to working in the mattermost/mattermost-webapp repo, most of that is now located in channels.

npm Workspaces

To interact with a workspace using npm, such as to add a dependency or run a script, use the --workspace (or --workspaces) flag. This can be done when using built-in npm commands such as npm add or when running scripts. Those commands should be run from this directory.

# Add a dependency to a single package
npm add react --workspace=playbooks

# Build multiple packages
npm run build --workspace=platform/client --workspace=platform/components

# Test all workspaces
npm test --workspaces

# Clean all workspaces that have a clean script defined
npm run clean --workspaces --if-present

To install dependencies for a workspace, simply run npm install from this folder as you would do normally. Most packages' dependencies will be included in the root node_modules, and all packages' dependencies will appear in the package-lock.json. A node_modules will only be created inside a package if one of its dependencies conflicts with that of another package.

Dependency Changes

Any PR that modifies package.json or package-lock.json needs extra scrutiny:

  • No duplicate libraries. Before adding a new dependency, check whether an existing one already covers the same use case. Multiple libraries for the same purpose (e.g., two different date pickers, or Bootstrap 3 and Bootstrap 4 simultaneously) create long-term upgrade pain.
  • License check. New dependencies must not use GPL or similarly restrictive licenses. Dependencies with no license at all should also be flagged.
  • Justify the addition. A new dependency should solve a real problem that existing code or dependencies don't already address. Push back on adding packages for trivial functionality.
  • Version conflicts. Check whether the new dependency introduces conflicting peer dependency versions. Cascading version conflicts are expensive to untangle later and have historically blocked upgrades for months.