Skip to content

fix(access): the Grants grid shows what a principal holds, and changes only that - #478

Merged
huyplb merged 3 commits into
mainfrom
feat/access-held-grants
Oct 4, 2026
Merged

huyplb merged 3 commits into
mainfrom
feat/access-held-grants

Conversation

@huyplb

@huyplb huyplb commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Reported: in Database Access an existing role or user group always shows unticked boxes, and the DDL/DML grid is hard to understand. Both were real, and the first was worse than it looked.

What was wrong

  1. The Grants grid opened empty for a principal that already held privileges.
    • Worse, the SQL compared the ticks with the whole catalog: ticking one box produced REVOKEs for everything else the principal held, including grants the grid cannot show, such as CONNECT on the database.
  2. The privilege columns were off-screen on a real schema.
    • Measured at 1,440 px with 66 objects: a 2,587 px table.
    • The cause was an "All / none: " button per row in one strip that never wraps, inside a pane without min-w-0. The grid pushed past the window and the overflow was cut off, including the credential picker.
  3. DML and DDL were bare column-band labels.
  4. A preset clicked while schemas were loading (seconds on Oracle, Db2 and SQL Server) ticked only the rows already loaded.
  5. An existing account's roles were nowhere as checkboxes. "Member of" existed only when creating an account.
  6. Permission Diff said "N extra" without saying the SQL revokes them.

What changes

  • @foxschema/sql (object-grid.ts):
    • heldGridPermissions() maps a principal's object grants onto grid cells: exact privilege per column, ALL PRIVILEGES to every cell the engine can grant, DENY and scope-wide grants left out.
    • compileGridChanges() turns a grid into GRANTs for new ticks and REVOKEs for held boxes cleared, and nothing else.
  • PermissionMatrix:
    • opens on what is held;
    • each cell is marked held, green (will be granted) or red (will be revoked), with a legend and "N held · N to grant · N to revoke";
    • presets apply to rows that load later;
    • resetNonce puts the grid back to what is held;
    • bands read "Work with rows · DML" and "Change the table · DDL", with tooltips;
    • "All" is a per-row button;
    • catalog rows show their name as text, not an editable box.
  • AccessGrantsStage:
    • SQL is only the change set;
    • the change list reads "Grant INSERT on public.orders";
    • "Custom", which would now revoke everything, becomes "Reset to current".
  • AccessPermissionPanel: the right pane is min-w-0, so the grid scrolls inside it.
  • User Management: a new alteration, "Roles it belongs to".
    • Current roles open ticked; joining is GRANT, leaving is REVOKE.
    • On MySQL and TiDB, joining also runs SET DEFAULT ROLE ALL.
    • MariaDB's single default role is left alone, written as a comment.
  • Permission Diff: "missing — will be granted", "extra — will be revoked".

Verification

  • Unit:
    • object-grid.held.test.ts (8): mapping, ALL, DENY and scope-wide grants left out, by-name matching, and change sets with no stray REVOKE;
    • user-sql.membership.test.ts (5);
    • all 41 existing grid tests still pass.
  • Component (Access, 109):
    • an existing principal's held box opens ticked and marked held;
    • a new tick grants without any REVOKE;
    • clearing a held box revokes only it, and Reset restores it;
    • a preset clicked before the catalog loads still ticks the late rows;
    • an existing account's roles open ticked, and unticking one gives REVOKE "readonly" FROM "alice".
  • A/B:
    • removing the seeding fails 3 of those tests;
    • removing the late-preset branch fails its test;
    • removing the roles pre-tick fails its test.
  • Live, with temporary grants on the Postgres dev database (revoked afterwards): report_user with SELECT and INSERT on two tables.
    • The grid opened with those 4 cells ticked.
    • Ticking SELECT on another table and clearing one held box produced exactly one GRANT and one REVOKE.
    • Layout re-measured: the table is 1,015 px and fits a 1,440 px window.
  • Live on MySQL and MariaDB: a role with SELECT and INSERT on orders opens with exactly those two cells ticked. This needed a follow-up commit, because a schema-less catalog row and a schema-qualified grant were keyed differently.
  • E2E on the final code: access-assistant-dialects (every configured engine), access-assistant-sqlite and database-access-dialects. 140 passed, 24 skipped by design (engines with no saved credential or no account DDL), no failures.
  • npm run typecheck (web + e2e): clean. Unit project: 4,949 passed. web-ui: 332 passed, three runs in a row. ESLint: clean.

🤖 Generated with Claude Code


Note

Medium Risk
Changes how access and role-membership SQL is computed in a security-sensitive area; behavior is heavily tested but mistakes could still grant or revoke the wrong privileges.

Overview
Fixes the Access Grants flow so the privilege matrix starts from what the principal already holds, and generated SQL is only the delta (new ticks → GRANT, cleared held boxes → REVOKE) instead of reconciling an empty grid against the full catalog—which could mass-revoke hidden grants like CONNECT.

@foxschema/sql adds heldGridPermissions, compileGridChanges, and gridObjectKey; User Management gains a “Roles it belongs to” alter path that diffs memberships into GRANT/REVOKE (with MySQL/TiDB default-role handling). The UI updates AccessGrantsStage and PermissionMatrix (held/grant/revoke styling, legend, Reset to current, presets applied to late-loaded catalog rows, clearer DML/DDL headings, per-row All, catalog names as text), fixes wide-grid overflow via min-w-0, and clarifies Permission Diff labels. Tests, E2E expectations, and test IDs follow the new behavior.

Reviewed by Cursor Bugbot for commit 957a6a2. Bugbot is set up for automated code reviews on this repo. Configure here.

…s only that

Reported: an existing role or user showed every box unticked, and the DDL /
DML grid was hard to read. Both were worse than they looked.

- The grid opened empty for a principal that held privileges, and the SQL
  compared the ticks with the whole catalog: ticking one box produced
  REVOKEs for everything else it held, CONNECT on the database included.
  It now opens on what is held (heldGridPermissions) and compiles only the
  difference (compileGridChanges): a new tick is a GRANT, a held box cleared
  is a REVOKE, and nothing the grid does not show is touched.
- Each cell shows what will happen: held, green for a grant, red for a
  revoke, with a legend and "N held · N to grant · N to revoke". The change
  list reads "Grant INSERT on public.orders", not missing/extra. "Custom",
  which cleared every box (now: revoke everything), is "Reset to current".
- The privilege columns were off-screen: an "All / none: <name>" button per
  row sat in one unwrapped strip (66 rows = 2,587px), and the pane had no
  min-w-0, so the grid pushed past the window and was cut off. "All" moved
  into each row; the pane is min-w-0. Catalog rows show their name as text
  rather than an editable box under the schema badge.
- Bands say what they mean: "Work with rows · DML", "Change the table ·
  DDL", with tooltips.
- A preset clicked while schemas were loading ticked only what had loaded;
  it now applies to rows that arrive after it.
- User Management: an existing account's roles were never shown and could
  not be changed ("Member of" existed only on create). New alteration
  "Roles it belongs to": current roles ticked; joining is GRANT, leaving is
  REVOKE (MySQL/TiDB also SET DEFAULT ROLE ALL; MariaDB's single default is
  left alone, as a comment).
- Permission Diff's summary says "missing — will be granted" and "extra —
  will be revoked".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cursor

cursor Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: 812280d6-18e8-436d-8be3-bcdd8d43787c)

@github-actions

github-actions Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

Test IDs

Removed (1) · a test or recorded flow outside this repo may still use these

  • access-grants-preset-clear

Added (7)

  • access-grants-change-{…}
  • access-grants-reset
  • matrix-legend
  • user-membership
  • user-membership-filter
  • user-membership-item-{…}
  • user-membership-list

{…} is a part filled in at run time. The full tree: docs/testing/TEST_IDS.md.

A catalog read with no schema list keeps its rows schema-less, and the grid
then keys them with the connection's default schema; the held map keyed
them with none, so on such a connection a held grant would not tick its
box. heldGridPermissions takes the grid's fallback schema and matches when
either side's schema is unknown; two known schemas must still agree.

Checked live: a MySQL role and a MariaDB role holding SELECT and INSERT on
orders open with exactly those two cells ticked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cursor

cursor Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: e0e6c930-d64b-49d5-be17-092a8990b3a8)

@cursor

cursor Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: 2d0b20ca-688c-418f-8739-1bee20eba7e1)

@huyplb
huyplb merged commit b0efcbe into main Oct 4, 2026
13 checks passed
@huyplb
huyplb deleted the feat/access-held-grants branch October 4, 2026 23:53
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.

1 participant