Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sql: SHOW GRANTS includes inherited roles #104119

Merged
merged 1 commit into from
Jun 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions pkg/bench/rttanalysis/grant_revoke_role_bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,75 @@ CREATE ROLE c;`,
})
}

func BenchmarkShowGrants(b *testing.B) { reg.Run(b) }
func init() {
reg.Register("ShowGrants", []RoundTripBenchTestCase{
{
Name: "grant 2 roles",
Setup: `
CREATE DATABASE db;
CREATE ROLE a;
CREATE ROLE b;
CREATE ROLE c;
GRANT a TO b;
GRANT b TO c;
GRANT ALL ON DATABASE db TO c;
GRANT DROP ON DATABASE db TO b;
GRANT CONNECT ON DATABASE db TO a;
`,
Stmt: "SHOW GRANTS ON DATABASE db FOR c",
Reset: `
DROP DATABASE db;
DROP ROLE a,b,c;
`,
},
{
Name: "grant 3 roles",
Setup: `
CREATE DATABASE db;
CREATE ROLE a;
CREATE ROLE b;
CREATE ROLE c;
CREATE ROLE d;
GRANT a TO b;
GRANT b TO c;
GRANT c TO d;
GRANT ALL ON DATABASE db TO c;
GRANT DROP ON DATABASE db TO b;
GRANT CONNECT ON DATABASE db TO a;
`,
Stmt: "SHOW GRANTS ON DATABASE db FOR d",
Reset: `
DROP DATABASE db;
DROP ROLE a,b,c,d;
`,
},
{
Name: "grant 4 roles",
Setup: `
CREATE DATABASE db;
CREATE ROLE a;
CREATE ROLE b;
CREATE ROLE c;
CREATE ROLE d;
CREATE ROLE e;
GRANT a TO b;
GRANT b TO c;
GRANT c TO d;
GRANT d TO e;
GRANT ALL ON DATABASE db TO c;
GRANT DROP ON DATABASE db TO b;
GRANT CONNECT ON DATABASE db TO a;
`,
Stmt: "SHOW GRANTS ON DATABASE db FOR d",
Reset: `
DROP DATABASE db;
DROP ROLE a,b,c,d,e;
`,
},
})
}

func BenchmarkRevokeRole(b *testing.B) { reg.Run(b) }
func init() {
reg.Register("RevokeRole", []RoundTripBenchTestCase{
Expand Down
3 changes: 3 additions & 0 deletions pkg/bench/rttanalysis/testdata/benchmark_expectations
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ exp,benchmark
21,Revoke/revoke_all_on_3_tables
14,RevokeRole/revoke_1_role
16,RevokeRole/revoke_2_roles
10,ShowGrants/grant_2_roles
11,ShowGrants/grant_3_roles
12,ShowGrants/grant_4_roles
1,SystemDatabaseQueries/select_system.users_with_empty_database_Name
1,SystemDatabaseQueries/select_system.users_with_schema_Name
1,SystemDatabaseQueries/select_system.users_without_schema_Name
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ crdb_internal kv_catalog_descriptor table admin NULL NULL
crdb_internal kv_catalog_namespace table admin NULL NULL
crdb_internal kv_catalog_zones table admin NULL NULL
crdb_internal kv_dropped_relations view admin NULL NULL
crdb_internal kv_inherited_role_members table admin NULL NULL
crdb_internal kv_node_liveness table admin NULL NULL
crdb_internal kv_node_status table admin NULL NULL
crdb_internal kv_store_status table admin NULL NULL
Expand Down
1 change: 1 addition & 0 deletions pkg/cli/zip_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ table_name NOT IN (
'kv_catalog_namespace',
'kv_catalog_zones',
'kv_dropped_relations',
'kv_inherited_role_members',
'lost_descriptors_with_data',
'table_columns',
'table_row_statistics',
Expand Down
69 changes: 69 additions & 0 deletions pkg/sql/crdb_internal.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ var crdbInternal = virtualSchema{
catconstants.CrdbInternalTenantUsageDetailsViewID: crdbInternalTenantUsageDetailsView,
catconstants.CrdbInternalPgCatalogTableIsImplementedTableID: crdbInternalPgCatalogTableIsImplementedTable,
catconstants.CrdbInternalShowTenantCapabilitiesCacheTableID: crdbInternalShowTenantCapabilitiesCache,
catconstants.CrdbInternalInheritedRoleMembersTableID: crdbInternalInheritedRoleMembers,
},
validWithNoDatabaseContext: true,
}
Expand Down Expand Up @@ -8042,3 +8043,71 @@ CREATE TABLE crdb_internal.node_tenant_capabilities_cache (
return nil
},
}

var crdbInternalInheritedRoleMembers = virtualSchemaTable{
comment: `table listing transitive closure of system.role_members`,
schema: `
CREATE TABLE crdb_internal.kv_inherited_role_members (
role STRING,
inheriting_member STRING,
member_is_explicit BOOL,
member_is_admin BOOL
);`,
populate: func(ctx context.Context, p *planner, _ catalog.DatabaseDescriptor, addRow func(...tree.Datum) error) (retErr error) {
// Populate explicitMemberships with the contents of system.role_members.
// This data structure maps each role to the set of roles it's explicitly
// a member of. Consider for example:
// CREATE ROLE a;
// CREATE ROLE b;
// CREATE ROLE c;
// GRANT a TO b;
// GRANT b TO c;
// The role `a` is explicitly a member of `b` and the role `c` is
// explicitly a member of `b`. The role `c` is also a member of `a`,
// but not explicitly, because it inherits the membership through `b`.
explicitMemberships := make(map[username.SQLUsername]map[username.SQLUsername]bool)
if err := forEachRoleMembership(ctx, p.InternalSQLTxn(), func(role, member username.SQLUsername, isAdmin bool) error {
if _, found := explicitMemberships[member]; !found {
explicitMemberships[member] = make(map[username.SQLUsername]bool)
}
explicitMemberships[member][role] = true
return nil
}); err != nil {
return err
}
// Iterate through all members in order for stability.
members := make([]username.SQLUsername, 0, len(explicitMemberships))
for m := range explicitMemberships {
members = append(members, m)
}
sort.Slice(members, func(i, j int) bool {
return members[i].Normalized() < members[j].Normalized()
})
for _, member := range members {
memberOf, err := p.MemberOfWithAdminOption(ctx, member)
if err != nil {
return err
}
explicitRoles := explicitMemberships[member]
// Iterate through all roles in order for stability.
roles := make([]username.SQLUsername, 0, len(memberOf))
for r := range memberOf {
roles = append(roles, r)
}
sort.Slice(roles, func(i, j int) bool {
return roles[i].Normalized() < roles[j].Normalized()
})
for _, r := range roles {
if err := addRow(
tree.NewDString(r.Normalized()), // role
tree.NewDString(member.Normalized()), // inheriting_member
tree.MakeDBool(tree.DBool(explicitRoles[r])), // member_is_explicit
tree.MakeDBool(tree.DBool(memberOf[r])), // member_is_admin
); err != nil {
return err
}
}
}
return nil
},
}
Loading