Skip to content

DB Console Overview page crash when user has VIEWACTIVITY and MODIFYSQLCLUSTERSETTING grants #165444

@dalecrane-crl

Description

@dalecrane-crl

Describe the problem

The Overview page of the DB Console will crash despite the user having the permissions required to view the page.

This occurs when the logged-in SQL user has MODIFYSQLCLUSTERSETTING but lacks
VIEWCLUSTERSETTING or MODIFYCLUSTERSETTING. The Settings API returns a filtered set of
cluster settings that excludes "version", and the UI crashes trying to access it as it never expects the value to be null.

To Reproduce

  • Spin up a cockroach cluster (personally I just used cockroach demo) and connect to it
  • Run:
CREATE USER testuser WITH PASSWORD 'abcd';
GRANT SYSTEM VIEWACTIVITY TO testuser;
GRANT SYSTEM MODIFYSQLCLUSTERSETTING TO testuser;
  • Attempt to log in and see the Overview page does not render
  • There will be a trace in the console similar to:
bundle.js:5033  TypeError: Cannot read properties of undefined (reading 'value')
    at bundle.js:10086:36129
    at bundle.js:9986:4310
    at bundle.js:9986:3728
    at bundle.js:9986:4449
    at bundle.js:9986:3728
    at bundle.js:9986:4417
    at bundle.js:9986:3728
    at bundle.js:9986:4417
    at bundle.js:9986:3728
    at Ue.mapToProps (bundle.js:10124:4574)

Workaround:

  • Additionally run:
    GRANT SYSTEM VIEWCLUSTERSETTING TO testuser;
  • Refresh the page, Overview loads as expected.

Expected behavior
The Overview page should load, as the user has the relevant permissions.

Additional data / screenshots

Investigation

The Settings endpoint (pkg/server/admin.go, lines 1957-2032) queries crdb_internal.cluster_settings
as the logged-in user. The virtual table (pkg/sql/crdb_internal.go, lines 2031-2082) checks
privileges in order:

  1. MODIFYCLUSTERSETTING or VIEWCLUSTERSETTING - all settings returned (includes "version")
  2. MODIFYSQLCLUSTERSETTING only - only sql.defaults.* settings returned ("version" excluded)
  3. None of the above - InsufficientPrivilege error
// crdb_internal.go line 2032
		hasModify, err := p.HasGlobalPrivilegeOrRoleOption(ctx, privilege.MODIFYCLUSTERSETTING)
		if err != nil {
			return err
		}
		canViewAll := hasModify
		if !canViewAll {
			canViewAll, err = p.HasGlobalPrivilegeOrRoleOption(ctx, privilege.VIEWCLUSTERSETTING)
			if err != nil {
				return err
			}
		}
		canViewSqlOnly := false
		if !canViewAll {
			canViewSqlOnly, err = p.HasGlobalPrivilegeOrRoleOption(ctx, privilege.MODIFYSQLCLUSTERSETTING)
			if err != nil {
				return err
			}
		}
		if !canViewAll && !canViewSqlOnly {
			return pgerror.Newf(pgcode.InsufficientPrivilege,
				"only users with %s, %s or %s system privileges are allowed to read "+
					"crdb_internal.cluster_settings", privilege.MODIFYCLUSTERSETTING, privilege.MODIFYSQLCLUSTERSETTING, privilege.VIEWCLUSTERSETTING)
		}

In the case of insufficient privilege, the handler in admin.go catches the error and correctly falls back to
ConsoleKeys(), which includes "version". But in case 2, the query succeeds with
a filtered result set, meaning err is nil, and the fallback block is never entered:

// admin.go line 1997
if err != nil {
    if pgerror.GetPGCode(err) != pgcode.InsufficientPrivilege {
        return nil, srverrors.ServerError(ctx, err)
    }
    // VIEWACTIVITY check and ConsoleKeys() fallback live here
    // but are never reached when MODIFYSQLCLUSTERSETTING made the query succeed
}

It assumes that no error means all settings were returned, which is false if the user has VIEWACTIVITY and
MODIFYSQLCLUSTERSETTING, but not VIEWCLUSTERSETTING.

On the frontend, the selector selectClusterSettingVersion in
/ui/workspaces/db-console/src/redux/clusterSettings/clusterSettings.selectors.ts accesses settings["version"].value without handling a missing key.

export const selectClusterSettingVersion = createSelector(
  selectClusterSettings,
  (settings): string => {
    if (!settings) {
      return "";
    }
    return settings["version"].value;  // <--- crashes if "version" is missing
  },
);

Other selectors in the same file use settings["version"]?.value || "", but this one doesn't.

Jira issue: CRDB-61392

Metadata

Metadata

Assignees

Labels

C-bugCode not up to spec/doc, specs & docs deemed correct. Solution expected to change code/behavior.O-supportWould prevent or help troubleshoot a customer escalation - bugs, missing observability/tooling, docsT-observabilitybranch-masterFailures and bugs on the master branch.target-release-26.3.0

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions