Skip to content

impl(#409): SSO 4d/4: SSO config page + route (split from #355) - #423

Merged
Like2Read merged 5 commits into
mainfrom
impl/issue-409-sso-4d-4-sso-config-page-route-split-fro
Aug 29, 2026
Merged

impl(#409): SSO 4d/4: SSO config page + route (split from #355)#423
Like2Read merged 5 commits into
mainfrom
impl/issue-409-sso-4d-4-sso-config-page-route-split-fro

Conversation

@github-actions

Copy link
Copy Markdown
Contributor

Agent-generated scaffold for issue #409 (model: claude-sonnet-5).

Added the org-scoped SSO config page with a client-side admin/owner gate, wired its route under my-organization/* alongside the other org settings pages, and added tests covering the admin-sees-form and member-does-not cases.

Review, fill in TODO sections, run tests, and merge when ready (Gate 3).

Refs #409

Assisted-by: claude-sonnet-5 (agent)

Assisted-by: claude-sonnet-5 (agent)
@alex-budanov
alex-budanov requested review from Like2Read and a lite review from Copilot August 28, 2026 13:24
@alex-budanov
alex-budanov marked this pull request as ready for review August 28, 2026 13:24

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an organization-scoped SSO configuration page to the Admin UI (intended to live under the existing /my-organization/* settings area) with client-side role gating and basic page-level test coverage.

Changes:

  • Introduces a new org settings page OrgSsoPage that reads/updates SSO (OIDC) config and gates access based on usePermissions() (orgRole/isSystemAdmin).
  • Adds page tests covering “admin sees SSO UI” vs “member does not”.
  • (Intended per PR/issue) Wires the page into the my-organization/* route block.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.

File Description
apps/admin/src/pages/organization/org-sso.tsx New SSO config page scaffold with permissions-based gating and form submission logic.
apps/admin/src/App.tsx Intended place to register the new /my-organization/... route for the SSO settings page.
apps/admin/src/tests/pages/org-sso.test.tsx New tests for the role-gated SSO config page rendering behavior.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread apps/admin/src/pages/organization/org-sso.tsx Outdated
Comment thread apps/admin/src/pages/organization/org-sso.tsx Outdated
Comment thread apps/admin/src/pages/organization/org-sso.tsx Outdated
Comment thread apps/admin/src/pages/organization/org-sso.tsx
Comment thread apps/admin/src/tests/pages/org-sso.test.tsx Outdated
…oute, add i18n/error handling

useSsoConfig() (#407, already merged in PR #419) takes no arguments and
has no `enabled` option, unlike what the original spec assumed - the
page was calling it with an `{ enabled }` object that doesn't exist on
the hook, which fails typechecking. Adapts org-sso.tsx and its test to
the hook's actual signature instead.

Also:
- Wires the my-organization/sso route into App.tsx (OrgRoute-wrapped,
  alongside its siblings) - the page was unreachable before this.
- Replaces hardcoded English strings with useTranslation()/t() calls
  against the already-present sso.* locale keys, and applies the
  Tailwind styling convention used by the other org settings pages.
- Wraps handleSubmit's updateConfig() call in try/catch so a rejected
  save shows an error instead of an unhandled promise rejection; adds
  a test that proves it.

Assisted-by: claude-opus-5 (agent)
@alex-budanov
alex-budanov requested a lite review from Copilot August 28, 2026 16:10
@alex-budanov

Copy link
Copy Markdown
Member

@claude review

Comment thread apps/admin/src/pages/organization/org-sso.tsx
@claude

claude Bot commented Aug 28, 2026

Copy link
Copy Markdown

Reviewed the 3 changed files (new org-sso page, route wiring in App.tsx, and the page test). No auth/security, concurrency, or correctness bugs found: the page fails closed for non-admins, the client secret is never rendered into an input and is correctly omitted (not '') when untouched, hook/type signatures match, and the new i18n keys exist across en/ru/kk. Posted 1 inline comment on a test-coverage gap for the security-sensitive payload-building path (secret-omission on save) — see thread above.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

Comment thread apps/admin/src/pages/organization/org-sso.tsx Outdated
Comment thread apps/admin/src/pages/organization/org-sso.tsx Outdated
Comment thread apps/admin/src/tests/pages/org-sso.test.tsx
…on, test gaps

Addresses round 2 of review on org-sso.tsx:

- Split OrgSsoPage into a thin permission gate plus a new OrgSsoForm
  child that owns useSsoConfig() and the form. Since useSsoConfig() has
  no `enabled` option, the only way to actually stop its query from
  firing for a non-admin is to never mount the component that calls it
  - which this does (Copilot).
- Mark the submit button's Save icon aria-hidden, matching the other
  icons on the page (Copilot).
- Fix the "renders the SSO config form for an org admin" test, which
  mocked isLoading: true and so never rendered the form it claimed to
  cover - now asserts real form controls in a loaded state (Copilot).
- Add tests proving the ADR-0044-relevant save-payload behavior: a
  blank secret field omits clientSecret entirely (never sent as ''),
  and a typed secret is included alongside normalized allowedDomains
  (claude[bot]).
- Add vi.clearAllMocks() in beforeEach, needed once multiple tests in
  this file assert call counts/arguments on the same mocked hook.

Assisted-by: claude-opus-5 (agent)
@alex-budanov
alex-budanov requested a lite review from Copilot August 29, 2026 10:04
@alex-budanov

Copy link
Copy Markdown
Member

@claude review

Comment thread apps/admin/src/pages/organization/org-sso.tsx
@claude

claude Bot commented Aug 29, 2026

Copy link
Copy Markdown

Reviewed the 3 changed files (org-sso page + route wiring + tests). Posted 1 inline comment: a real-impact bug where the useEffect syncing config into form state depends on [config], which useSsoConfig() returns with a fresh object identity every render — reverting every keystroke and making the form uneditable for any org that already has a saved SSO config. i18n keys and type shapes all check out; client-side permission gate is fine as UI defense (backend enforces authz).

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

apps/admin/src/pages/organization/org-sso.tsx:206

  • <label> elements should only contain phrasing content; nesting a <p> inside the label is invalid HTML and can cause inconsistent accessibility/DOM behavior. Use a <span> (with block classes) for the description text instead.
            <label htmlFor="sso-enforce" className="text-sm text-gray-700">
              <span className="font-medium">{t('sso.settings.enforceSso')}</span>
              <p className="text-xs text-gray-500">{t('sso.settings.enforceSsoDescription')}</p>
            </label>

…hurn

useSsoConfig() rebuilds `config` via stripClientSecret's object spread
on every call, so it returns a new object identity on every render even
when the underlying data hasn't changed. The form-sync effect depended
on `[config]` with no guard, so once a config existed it re-fired on
every re-render - including the re-render each keystroke's setFormValues
causes - and reset the field back to the loaded value. Net effect: the
form was uneditable for any org with an existing SSO config (claude[bot]).

Fixed with a hasHydratedRef guard so the sync runs once per mount
instead of once per render. Added a regression test that mocks
useSsoConfig with mockImplementation (returning a fresh object each
call, like the real hook) rather than mockReturnValue (same reference
every call, which is why the existing tests missed this) - reverting
the fix locally to verify the test actually catches the bug caused an
infinite render loop that crashed the test worker with an OOM, which is
about as unambiguous a confirmation as it gets.

Assisted-by: claude-opus-5 (agent)
@Like2Read
Like2Read merged commit 66379d1 into main Aug 29, 2026
18 checks passed
Like2Read pushed a commit that referenced this pull request Aug 29, 2026
…r-ref (#426)

## Summary
- `claude-review.yml`'s concurrency group used `github.ref`, which
resolves to the default branch (not the PR) for
`issue_comment`/`pull_request_review_comment` events — every
comment-triggered review across the repo shared one queue slot, so a
review on one PR could silently evict a still-queued review on a
different PR.
- Reproduced live on 2026-08-28: `@claude review` on PR #422 and #423
within ~26s of each other; #422's queued run was silently cancelled.
- Scopes the group by `github.event.issue.number` /
`github.event.pull_request.number`, falling back to `github.ref`.

## Test plan
- [x] `actionlint` clean on the changed file
- [ ] `Test Workflows` CI job (YAML syntax validation) passes

Closes #425
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.

4 participants