Skip to content

Share a matrix with named colleagues (behind the Matrix sharing feature flag) - #1175

Merged
WimvandenHeijkant merged 15 commits into
mainfrom
dor/issue-1166
Sep 13, 2026
Merged

WimvandenHeijkant merged 15 commits into
mainfrom
dor/issue-1166

Conversation

@fortigi-ci-bot

@fortigi-ci-bot fortigi-ci-bot Bot commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor

Closes #1166

Share a configured matrix with named colleagues who have no Identity Atlas role, such as a team manager or an application owner. The whole feature ships behind the experimental matrixSharing feature flag, switched off by default.

Functional-test env: https://9.build.identityatlas.io. The requestor has tested it there.

What it does

  • Share step in the wizard: the matrix wizard ends with an optional Share step. You name the view, pick one or more people from the directory and copy the link. Apply still works from that step.
  • Share view… in the toolbar: the same form, for a matrix you already have open. The old Share Link button is renamed Copy link.
  • Specific people only: only the chosen recipients (and the sharer) can open a link; a forwarded link gets anyone else nowhere.
  • Recipient view: recipients sign in with their normal Microsoft account (no Identity Atlas role needed) and see a bare, read-only view.
    • It shows the view name, the governed toggle and the matrix.
    • It has no navigation, dashboard, export, scope strip, scope statistics or way to change the matrix.
    • They can click through to detail pages; the analyst-only tabs are hidden there.
  • Live data, fixed view: a share stores a snapshot of the filter, the governed toggle and the display mode. The data behind it stays live.
  • Admin → Shared Matrices: lists every share, who it is for, who opened it and how often. Any share can be revoked, and usage history is kept. Old #shared-matrices links still land on this tab.
  • New permission data.share ("Create matrix share links"), granted to RoleMiner by default.

Feature flag

  • Where to turn it on: Admin → Experimental → Matrix sharing, or FEATURE_MATRIX_SHARING=true.
  • When it is off: the Share step, the Share view… button and the Shared Matrices tab are hidden, including for admins. Every /api/matrix/shares route answers 404 before any permission check or database read. That includes resolve, so links that were already sent stop opening.
  • Nothing is deleted: switching the flag back on restores all shares that were not revoked.
  • In CI: docker-compose.ci.yml starts with the flag on so the sharing e2e keeps running. The off behaviour is covered by unit tests.

Schema

  • Migrations 062_matrix_shares.sql and 063_matrix_share_recipients.sql add MatrixShares, MatrixShareRecipients and MatrixShareAccesses. The tables are created whether or not the flag is on.
  • Share tokens are stored as a hash only. The token travels in the URL fragment, so it never reaches server logs.

Known residual (by design, per the issue)

A recipient is a signed-in reader. The bare view simplifies the UI but is not a data boundary: a technically capable recipient could call other read endpoints. Tightening that is tracked under #1105 and #931. The docs say so.

Tests

  • Unit suites: the full API suite (3025 tests) and the full UI suite (1890 tests) pass locally on the final commit.
  • Flag tests: the flag resolver and requireFeature are tested, and every share route is checked with the flag off. The UI tests mount each sharing surface with the flag both on and off.
  • Other test layers: there is a contract test for the share SQL and a Playwright e2e for the share flow and the recipient view.

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

🤖 Built + verified on dev-docker-09 (e2e + CI green) → https://9.build.identityatlas.io

fortigi-ci-bot Bot and others added 4 commits September 9, 2026 09:24
…1166)

The DoR adjustment run for #1166 died on an expired app token before it could
push its second fix attempt, leaving three Playwright failures on the PR. All
three are addressed here, and two of them were real product problems rather
than test problems.

1. The share dialog was destroyed whenever the matrix decided a different view
   should render. MatrixArea picks one of RollupMatrixView / RotatedMatrixView /
   MatrixView from the data, so a roll-up payload landing mid-session unmounts
   the whole view — and the dialog lived inside the toolbar inside that view.
   In CI the e2e clicked "Share view…" during the cold-start load, typed a name,
   and the dialog vanished under it when the matrix finished loading; the same
   would happen to an analyst on a slow tenant. The dialog now lives in
   MatrixArea, above the swap, with the button reduced to asking it to open.
   It gets its own Suspense boundary so loading the lazy chunk doesn't blank
   the matrix behind it.

2. Two toolbar buttons were both called "Share…". The pre-existing "Share Link"
   (copy the current URL, for a colleague who already uses Identity Atlas) sat
   next to the new "Share view…" (mint a read-only link for someone with no
   role at all). Renamed the former to "Copy link" and gave it a tooltip that
   says what it actually does. The e2e's loose /Share/i locator was a symptom,
   not the bug.

3. The wizard's new optional "Share" step was unknown to the e2e's STEP_MARKERS
   map, failing four matrix adjust tests.

Tests: new MatrixArea.mount.test.jsx pins the dialog (and its half-typed input)
surviving the view swap; SharedViewContext gains an assertion that the two
sharing controls are named apart; the weak `if count > 0` e2e share-button test
is replaced with one that names both buttons.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… view swap (#1166)

The previous commit moved the share dialog up to MatrixArea, which was not far
enough. The Playwright trace from that run shows the dialog opening, its lazy
chunk loading, and the dialog then disappearing before anything could be typed
into it.

MatrixArea was the wrong level because AppMain replaces the ENTIRE body with the
loading pane whenever useMatrix reports `loading` — which a matrix refetch does
routinely. So the dialog now survived the roll-up view swap and still died on
the refetch that usually precedes it. AppMain is the shallowest level above both
swaps, so the dialog lives there and MatrixArea is back to passing `onShareView`
through.

Also updated the one adjust test that pins the wizard's step list: 'Share' is a
real fifth step for anyone holding `data.share`, which on an auth-off deployment
is everyone.

The regression test moved with the dialog and now drives both swaps in sequence
— refetch, then roll-up payload — asserting the dialog and its half-typed input
come through intact. Verified it discriminates: putting the dialog back inside
AppMain's body switch fails it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

🤖 Built + verified on dev-docker-07 (e2e + CI green) → https://7.build.identityatlas.io

fortigi-ci-bot Bot and others added 8 commits September 12, 2026 15:22
The adjustment commit 839594a landed on the branch but GitHub created no
workflow run for it, so the build could not be verified. Empty commit to
re-request CI on the same tree.
The PR had drifted into a conflicting state against main, which is why CI
stopped running on it: GitHub cannot build a merge ref for a conflicting PR,
so it creates no pull_request workflow runs at all, and the DoR verify loop
timed out waiting for checks that could never appear.

Conflicts resolved:
- adminTabs / AdminPage / pageRegistry: keep both the Shared Matrices tab and
  main's Experimental tab; AdminRoute takes main's features/version props.
- stryker.pilot.config.json + vitest.stryker.config.js: union of both mutate
  and test lists; the _comment keeps main's newer measurements and the
  #1166 pure-logic scope paragraph.
- migrations: main and this branch both added a 061. The share migrations are
  renumbered 062/063 (never deployed anywhere, so renaming is safe) and the
  hash baseline updated to match.
- MatrixFilterWizard.mount.test.jsx: main's business-role test walked to the
  end and clicked Apply; sharing is now the last step, so it advances one more
  step first — the same pattern the other tests in this file already use.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…erge exposed

The merge of main had to be landed before CI could run on this PR at all, and it
surfaced three things:

- stryker.pilot.config.json lost main's five crawler `mutationExclusions` when the
  two sides' disjoint blocks were reconciled, so the mutation-scope guard reported
  five undecided files. Both sides' exclusions and mutate lists are now unioned.

- The share form's copy control was labelled "Copy link", the same accessible name
  as the matrix toolbar's existing "Copy link" — which copies the current URL
  rather than a share. With the wizard open both were in the DOM, so the e2e hit a
  strict-mode violation. MatrixToolbar already documents these two actions as
  "deliberately named apart"; the form's is now "Copy share link", which is what
  makes that comment true.

- The two sharing e2e tests drove the same ShareMatrixForm through the same twelve
  lines, which jscpd counted as a new clone. Extracted as fillShareForm().

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Resolve pageRegistry.jsx: keep both the new reports route (#1173) and the
legacy shared-matrices Admin route.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#1166)

A share recipient now sees only the matrix: MatrixFilterSummary and
MatrixScopePanel render nothing (and fetch nothing) under the shared-view flag.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Resolve matrixWizard.js (keep the openWizard helper, arm main's full-columns
wait through a beforeOpen hook) and stryker.pilot.config.json (union of both
sides' scope, exclusions and notes).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… flag (#1166)

Off by default. While off, every /api/matrix/shares route (resolve included)
answers 404, and the wizard's Share step, the toolbar's Share view… and the
Admin → Shared Matrices tab are hidden. Toggle under Admin → Experimental, or
FEATURE_MATRIX_SHARING=true. Existing shares are kept.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@WimvandenHeijkant WimvandenHeijkant changed the title Share a Matrix with anyone inside my org Share a matrix with named colleagues (behind the Matrix sharing feature flag) Sep 13, 2026
@WimvandenHeijkant
WimvandenHeijkant marked this pull request as ready for review September 13, 2026 12:13
The flag-off case duplicated the no-permission case line for line, which the
jscpd gate counted as a new clone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@WimvandenHeijkant
WimvandenHeijkant merged commit e2edaf7 into main Sep 13, 2026
34 checks passed
@WimvandenHeijkant
WimvandenHeijkant deleted the dor/issue-1166 branch September 13, 2026 13:20
@github-actions

Copy link
Copy Markdown
Contributor

♻️ PR merged. Reset dev-docker-09 (stack down, volumes + images pruned) and released it back to the dor-build pool.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Share a Matrix with anyone inside my org

1 participant