From 8e12d14ee01ef00ebb1b044449809ff593bfd580 Mon Sep 17 00:00:00 2001 From: Robert Haas Date: Fri, 18 Nov 2022 16:03:06 -0500 Subject: [PATCH v1 4/4] Add role attributes INHERITCREATEDROLES and SETCREATEDROLES. These control whether the implicit grants created when a CREATEROLE user creates a new role are marked with the inherit and set options respectively. FIXME: Add some regression tests. FIXME: Add documentation. FIXME: REMEMBER TO BUMP THE CATALOG VERSION --- src/backend/commands/user.c | 66 +++++++++++++++++++++++++++++-- src/backend/parser/gram.y | 8 ++++ src/include/catalog/pg_authid.dat | 39 ++++++++++++------ src/include/catalog/pg_authid.h | 2 + 4 files changed, 99 insertions(+), 16 deletions(-) diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c index 1bfc42a157..599d545363 100644 --- a/src/backend/commands/user.c +++ b/src/backend/commands/user.c @@ -141,6 +141,8 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt) bool issuper = false; /* Make the user a superuser? */ bool inherit = true; /* Auto inherit privileges? */ bool createrole = false; /* Can this user create roles? */ + bool inheritcreatedroles = false; /* inherit any created roles? */ + bool setcreatedroles = false; /* set role to any created roles? */ bool createdb = false; /* Can the user create databases? */ bool canlogin = false; /* Can this user login? */ bool isreplication = false; /* Is this a replication role? */ @@ -156,6 +158,8 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt) DefElem *dissuper = NULL; DefElem *dinherit = NULL; DefElem *dcreaterole = NULL; + DefElem *dinheritcreatedroles = NULL; + DefElem *dsetcreatedroles = NULL; DefElem *dcreatedb = NULL; DefElem *dcanlogin = NULL; DefElem *disreplication = NULL; @@ -214,6 +218,18 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt) errorConflictingDefElem(defel, pstate); dcreaterole = defel; } + else if (strcmp(defel->defname, "inheritcreatedroles") == 0) + { + if (dinheritcreatedroles) + errorConflictingDefElem(defel, pstate); + dinheritcreatedroles = defel; + } + else if (strcmp(defel->defname, "setcreatedroles") == 0) + { + if (dsetcreatedroles) + errorConflictingDefElem(defel, pstate); + dsetcreatedroles = defel; + } else if (strcmp(defel->defname, "createdb") == 0) { if (dcreatedb) @@ -281,6 +297,10 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt) inherit = boolVal(dinherit->arg); if (dcreaterole) createrole = boolVal(dcreaterole->arg); + if (dinheritcreatedroles) + inheritcreatedroles = boolVal(dinheritcreatedroles->arg); + if (dsetcreatedroles) + setcreatedroles = boolVal(dsetcreatedroles->arg); if (dcreatedb) createdb = boolVal(dcreatedb->arg); if (dcanlogin) @@ -402,6 +422,10 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt) new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper); new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(inherit); new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(createrole); + new_record[Anum_pg_authid_rolinheritcreatedroles - 1] = + BoolGetDatum(inheritcreatedroles); + new_record[Anum_pg_authid_rolsetcreatedroles - 1] = + BoolGetDatum(setcreatedroles); new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(createdb); new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin); new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(isreplication); @@ -531,18 +555,28 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt) */ if (!superuser()) { - RoleSpec *current_role = makeNode(RoleSpec); + RoleSpec *current_role; + HeapTuple utup; + Form_pg_authid uform; GrantRoleOptions poptself; + current_role = makeNode(RoleSpec); current_role->roletype = ROLESPEC_CURRENT_ROLE; current_role->location = -1; + utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(GetUserId())); + if (!HeapTupleIsValid(utup)) + elog(LOG, "cache lookup failed for role %u", GetUserId()); + uform = (Form_pg_authid) GETSTRUCT(utup); + poptself.specified = GRANT_ROLE_SPECIFIED_ADMIN | GRANT_ROLE_SPECIFIED_INHERIT | GRANT_ROLE_SPECIFIED_SET; poptself.admin = true; - poptself.inherit = false; - poptself.set = false; + poptself.inherit = uform->rolinheritcreatedroles; + poptself.set = uform->rolsetcreatedroles; + + ReleaseSysCache(utup); AddRoleMems(BOOTSTRAP_SUPERUSERID, stmt->role, roleid, list_make1(current_role), list_make1_oid(GetUserId()), @@ -612,6 +646,8 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt) DefElem *dissuper = NULL; DefElem *dinherit = NULL; DefElem *dcreaterole = NULL; + DefElem *dinheritcreatedroles = NULL; + DefElem *dsetcreatedroles = NULL; DefElem *dcreatedb = NULL; DefElem *dcanlogin = NULL; DefElem *disreplication = NULL; @@ -655,6 +691,18 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt) errorConflictingDefElem(defel, pstate); dcreaterole = defel; } + else if (strcmp(defel->defname, "inheritcreatedroles") == 0) + { + if (dinheritcreatedroles) + errorConflictingDefElem(defel, pstate); + dinheritcreatedroles = defel; + } + else if (strcmp(defel->defname, "setcreatedroles") == 0) + { + if (dsetcreatedroles) + errorConflictingDefElem(defel, pstate); + dsetcreatedroles = defel; + } else if (strcmp(defel->defname, "createdb") == 0) { if (dcreatedb) @@ -841,6 +889,18 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt) new_record_repl[Anum_pg_authid_rolcreaterole - 1] = true; } + if (dinheritcreatedroles) + { + new_record[Anum_pg_authid_rolinheritcreatedroles - 1] = BoolGetDatum(boolVal(dinheritcreatedroles->arg)); + new_record_repl[Anum_pg_authid_rolinheritcreatedroles - 1] = true; + } + + if (dsetcreatedroles) + { + new_record[Anum_pg_authid_rolsetcreatedroles - 1] = BoolGetDatum(boolVal(dsetcreatedroles->arg)); + new_record_repl[Anum_pg_authid_rolsetcreatedroles - 1] = true; + } + if (dcreatedb) { new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(boolVal(dcreatedb->arg)); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 9384214942..0d6bdf1531 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -1206,6 +1206,14 @@ AlterOptRoleElem: $$ = makeDefElem("createrole", (Node *) makeBoolean(true), @1); else if (strcmp($1, "nocreaterole") == 0) $$ = makeDefElem("createrole", (Node *) makeBoolean(false), @1); + else if (strcmp($1, "inheritcreatedroles") == 0) + $$ = makeDefElem("inheritcreatedroles", (Node *) makeBoolean(true), @1); + else if (strcmp($1, "noinheritcreatedroles") == 0) + $$ = makeDefElem("inheritcreatedroles", (Node *) makeBoolean(false), @1); + else if (strcmp($1, "setcreatedroles") == 0) + $$ = makeDefElem("setcreatedroles", (Node *) makeBoolean(true), @1); + else if (strcmp($1, "nosetcreatedroles") == 0) + $$ = makeDefElem("setcreatedroles", (Node *) makeBoolean(false), @1); else if (strcmp($1, "replication") == 0) $$ = makeDefElem("isreplication", (Node *) makeBoolean(true), @1); else if (strcmp($1, "noreplication") == 0) diff --git a/src/include/catalog/pg_authid.dat b/src/include/catalog/pg_authid.dat index 3343a69ddb..79390240ca 100644 --- a/src/include/catalog/pg_authid.dat +++ b/src/include/catalog/pg_authid.dat @@ -21,67 +21,80 @@ { oid => '10', oid_symbol => 'BOOTSTRAP_SUPERUSERID', rolname => 'POSTGRES', rolsuper => 't', rolinherit => 't', - rolcreaterole => 't', rolcreatedb => 't', rolcanlogin => 't', + rolcreaterole => 't', rolinheritcreatedroles => 't', + rolsetcreatedroles => 't', rolcreatedb => 't', rolcanlogin => 't', rolreplication => 't', rolbypassrls => 't', rolconnlimit => '-1', rolpassword => '_null_', rolvaliduntil => '_null_' }, { oid => '6171', oid_symbol => 'ROLE_PG_DATABASE_OWNER', rolname => 'pg_database_owner', rolsuper => 'f', rolinherit => 't', - rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f', + rolcreaterole => 'f', rolinheritcreatedroles => 't', + rolsetcreatedroles => 't', rolcreatedb => 'f', rolcanlogin => 'f', rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1', rolpassword => '_null_', rolvaliduntil => '_null_' }, { oid => '6181', oid_symbol => 'ROLE_PG_READ_ALL_DATA', rolname => 'pg_read_all_data', rolsuper => 'f', rolinherit => 't', - rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f', + rolcreaterole => 'f', rolinheritcreatedroles => 't', + rolsetcreatedroles => 't', rolcreatedb => 'f', rolcanlogin => 'f', rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1', rolpassword => '_null_', rolvaliduntil => '_null_' }, { oid => '6182', oid_symbol => 'ROLE_PG_WRITE_ALL_DATA', rolname => 'pg_write_all_data', rolsuper => 'f', rolinherit => 't', - rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f', + rolcreaterole => 'f', rolinheritcreatedroles => 't', + rolsetcreatedroles => 't', rolcreatedb => 'f', rolcanlogin => 'f', rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1', rolpassword => '_null_', rolvaliduntil => '_null_' }, { oid => '3373', oid_symbol => 'ROLE_PG_MONITOR', rolname => 'pg_monitor', rolsuper => 'f', rolinherit => 't', - rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f', + rolcreaterole => 'f', rolinheritcreatedroles => 't', + rolsetcreatedroles => 't', rolcreatedb => 'f', rolcanlogin => 'f', rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1', rolpassword => '_null_', rolvaliduntil => '_null_' }, { oid => '3374', oid_symbol => 'ROLE_PG_READ_ALL_SETTINGS', rolname => 'pg_read_all_settings', rolsuper => 'f', rolinherit => 't', - rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f', + rolcreaterole => 'f', rolinheritcreatedroles => 't', + rolsetcreatedroles => 't', rolcreatedb => 'f', rolcanlogin => 'f', rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1', rolpassword => '_null_', rolvaliduntil => '_null_' }, { oid => '3375', oid_symbol => 'ROLE_PG_READ_ALL_STATS', rolname => 'pg_read_all_stats', rolsuper => 'f', rolinherit => 't', - rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f', + rolcreaterole => 'f', rolinheritcreatedroles => 't', + rolsetcreatedroles => 't', rolcreatedb => 'f', rolcanlogin => 'f', rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1', rolpassword => '_null_', rolvaliduntil => '_null_' }, { oid => '3377', oid_symbol => 'ROLE_PG_STAT_SCAN_TABLES', rolname => 'pg_stat_scan_tables', rolsuper => 'f', rolinherit => 't', - rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f', + rolcreaterole => 'f', rolinheritcreatedroles => 't', + rolsetcreatedroles => 't', rolcreatedb => 'f', rolcanlogin => 'f', rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1', rolpassword => '_null_', rolvaliduntil => '_null_' }, { oid => '4569', oid_symbol => 'ROLE_PG_READ_SERVER_FILES', rolname => 'pg_read_server_files', rolsuper => 'f', rolinherit => 't', - rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f', + rolcreaterole => 'f', rolinheritcreatedroles => 't', + rolsetcreatedroles => 't', rolcreatedb => 'f', rolcanlogin => 'f', rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1', rolpassword => '_null_', rolvaliduntil => '_null_' }, { oid => '4570', oid_symbol => 'ROLE_PG_WRITE_SERVER_FILES', rolname => 'pg_write_server_files', rolsuper => 'f', rolinherit => 't', - rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f', + rolcreaterole => 'f', rolinheritcreatedroles => 't', + rolsetcreatedroles => 't', rolcreatedb => 'f', rolcanlogin => 'f', rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1', rolpassword => '_null_', rolvaliduntil => '_null_' }, { oid => '4571', oid_symbol => 'ROLE_PG_EXECUTE_SERVER_PROGRAM', rolname => 'pg_execute_server_program', rolsuper => 'f', rolinherit => 't', - rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f', + rolcreaterole => 'f', rolinheritcreatedroles => 't', + rolsetcreatedroles => 't', rolcreatedb => 'f', rolcanlogin => 'f', rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1', rolpassword => '_null_', rolvaliduntil => '_null_' }, { oid => '4200', oid_symbol => 'ROLE_PG_SIGNAL_BACKEND', rolname => 'pg_signal_backend', rolsuper => 'f', rolinherit => 't', - rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f', + rolcreaterole => 'f', rolinheritcreatedroles => 't', + rolsetcreatedroles => 't', rolcreatedb => 'f', rolcanlogin => 'f', rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1', rolpassword => '_null_', rolvaliduntil => '_null_' }, { oid => '4544', oid_symbol => 'ROLE_PG_CHECKPOINT', rolname => 'pg_checkpoint', rolsuper => 'f', rolinherit => 't', - rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f', + rolcreaterole => 'f', rolinheritcreatedroles => 't', + rolsetcreatedroles => 't', rolcreatedb => 'f', rolcanlogin => 'f', rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1', rolpassword => '_null_', rolvaliduntil => '_null_' }, diff --git a/src/include/catalog/pg_authid.h b/src/include/catalog/pg_authid.h index 3512601c80..83fca311ed 100644 --- a/src/include/catalog/pg_authid.h +++ b/src/include/catalog/pg_authid.h @@ -35,6 +35,8 @@ CATALOG(pg_authid,1260,AuthIdRelationId) BKI_SHARED_RELATION BKI_ROWTYPE_OID(284 bool rolsuper; /* read this field via superuser() only! */ bool rolinherit; /* inherit privileges from other roles? */ bool rolcreaterole; /* allowed to create more roles? */ + bool rolinheritcreatedroles; /* if creates role, inherit it? */ + bool rolsetcreatedroles; /* if creates role, set role to it? */ bool rolcreatedb; /* allowed to create databases? */ bool rolcanlogin; /* allowed to log in as session user? */ bool rolreplication; /* role used for streaming replication */ -- 2.24.3 (Apple Git-128)