refactor(access): simplify the roles and allow-all code from #435 - #436
Merged
Merged
Conversation
No behaviour change; a /simplify pass over #435 for readability. - Allow-all is worked out once per catalog read: findAllowAllByName indexes privileges by grantee and answers each role once, instead of re-reading the whole privilege list per account (70M comparisons for 1,000 MySQL accounts with *.* grants). Both lists use it. - One name key, principalKey, shared by privilegesForPrincipal, reconcileDbAccess and the allow-all index; reconcile uses Sets, not list scans, for widely held roles. - SQL Server's implied fixed-role rows are their own function, called beside reconcile, instead of hiding inside "make the catalogs agree". - MySQL account quoting has one home: formatDbGrantee and the new SET DEFAULT ROLE statements use mysqlQuote / mysqlAccount / mysqlRoleRef from user-sql-helpers, the same helpers CREATE USER uses. - The MySQL and ClickHouse GRANT branches shared a copied target rule; now one branch. allPrivilegeTargets returns its lists directly and drops the always-null objectSchema. - DatabaseAccessModal: the privilege table is a file-local PrivilegeGroupRows; one confirmRevoke replaces three copies of the revoke flow; the membership role is derived instead of synced by an effect; the confirm field resets where the confirmation opens; granteeKindOf, which returned its input, is gone. - UserManagement: any MySQL-family name parses as user@host (a MariaDB role has no @ and parses to itself), so the role special case goes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
Bugbot couldn't run - usage limit reachedBugbot 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: serverGenReqId_603ba008-3491-4ac7-8e9a-a4035b0f73fe) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This is a readability and maintainability pass over #435. It came out of a
/simplifyreview from four angles: reuse, simplification, efficiency and altitude. Behaviour is unchanged. The same unit, component and live e2e tests pass.What changed
Efficiency, the one that matters at size
findAllowAllByNameindexes privileges by grantee once, and answers each role once, however many accounts share it.findAllowAllper account, and each call re-read the whole privilege list. That's about 70 million comparisons for 1,000 MySQL accounts with*.*grants (70 rows each), run on the UI thread on every catalog load.One way to do each thing
principalKey(quotes stripped, case folded).privilegesForPrincipal,reconcileDbAccessand the allow-all index all use it. Before, there were three spellings of it.formatDbGranteeand the newSET DEFAULT ROLEstatements usemysqlQuote/mysqlAccount/mysqlRoleReffromuser-sql-helpers, the same helpersCREATE USERuses. The MariaDB "role has no host" rule had already been copied into a second place.buildGrantRevokeSqlcarried the same copied*.*/db.*rule. They are now one branch.Right place
db_owner→ CONTROL) are now their own function,impliedFixedRolePrivileges. The server calls it besidereconcileDbAccess, instead of the rows being hidden inside a function documented as "make the two catalogs agree".reconcileDbAccessuses Sets rather than list scans, because a widely held role has thousands of members.Easier to read
DatabaseAccessModal:PrivilegeGroupRowscomponent.confirmRevokereplaces three copies of the build-REVOKE, then error or confirm, flow.granteeKindOf, which returned its input unchanged, is gone.allPrivilegeTargets: returns its lists directly, and drops anobjectSchemafield that was always null.roleMembershipStatements: a plain if/else instead of a nested ternary of object literals.UserManagement: any MySQL-family name parses asuser@host. A MariaDB role has no@and parses to itself, so the role special case is gone.Deliberately not changed
PG_PRINCIPALS/_NO_SUPERUSER,PG_PRIVILEGES/_NO_ACL,MSSQL_PRINCIPALS/_NO_SERVER) stay as whole strings. Building them with string surgery would read worse (WET preference).DbPrincipal.hostfield: not added.name@hostis how the catalog identifies a MySQL account, and a separate field would touch every query and consumer.Verification
packages/sqlaccess + server access pass 405. Access / utilities UI pass 88. That includes a new bulk-vs-single allow-all test and an updated implied-privileges test.cd apps/web && npx tsc --noEmitand the e2e tsc are clean. ESLint is clean on the changed files.root@%still shows the ALL PRIVILEGES (70) row, the banner and the tag, and Revoke all buildsREVOKE ALL PRIVILEGES ON *.* FROM 'root'@'%';. I cancelled it rather than running it.🤖 Generated with Claude Code
Note
Low Risk
Refactor-only with explicit parity tests for allow-all; GRANT/REVOKE and catalog paths are reorganized but described as behaviour-preserving.
Overview
This is a readability and performance refactor of database access / allow-all handling from #435. Intended behaviour is unchanged; tests are updated accordingly.
Performance: User Management and Database Access now call
findAllowAllByNameonce per catalog load instead of loopingfindAllowAllper principal. Privileges are indexed by grantee and each role’s allow-all answer is cached so large MySQL catalogs (many*.*rows) do not re-scan the full list on the UI thread.Shared SQL layer:
principalKeyunifies grantee name matching for reconciliation,privilegesForPrincipal, and allow-all.reconcileDbAccessno longer takesdialector inject SQL Server fixed-role rows;impliedFixedRolePrivilegesis applied inprobeDbAccessafter reconcile. Membership sync uses Sets instead of repeated list scans. MySQL/MariaDBformatDbGranteeandSET DEFAULT ROLEreusemysqlQuote/mysqlAccount/mysqlRoleRef; MySQL and ClickHousebuildGrantRevokeSqlshare one*.*/db.*branch.allPrivilegeTargetsdrops the always-nullobjectSchemafield.UI:
DatabaseAccessModalextractsPrivilegeGroupRows, centralizes revoke confirmation inconfirmRevoke, and derivesmembershipRoleinstead of syncing via an effect.UserManagementparses all MySQL-family principals asuser@hoston edit/drop (MariaDB bare role names unchanged).Reviewed by Cursor Bugbot for commit 82267bb. Bugbot is set up for automated code reviews on this repo. Configure here.