Files
c53ede92cb 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.

* 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.

* 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.

* 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.

* 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.

* 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.

* 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.

* 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.

* 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.

* 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.

* 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.

* UX Polish

* Linting

* Removed styled components

* Fix session attributes unit tests

Co-authored-by: maria.nunez <[email protected]>

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

Co-authored-by: maria.nunez <[email protected]>

* Stabilize session attributes duration-preset e2e interaction

Co-authored-by: maria.nunez <[email protected]>

* Fix broken session attributes doc link in feature discovery

Co-authored-by: maria.nunez <[email protected]>

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

Co-authored-by: maria.nunez <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

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

Co-authored-by: maria.nunez <[email protected]>

* Cover fetchPropertyFields group name to uuid mapping

Co-authored-by: maria.nunez <[email protected]>

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

Co-authored-by: maria.nunez <[email protected]>

* 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 <[email protected]>

* 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

* 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: Hanzo Dev <[email protected]>
2026-07-16 08:05:03 -04:00
..