On Thu, Mar 09, 2023 at 09:58:46AM -0800, Nathan Bossart wrote: > On Thu, Mar 09, 2023 at 10:55:54AM +0100, Peter Eisentraut wrote: >> On 20.02.23 23:58, Nathan Bossart wrote: >>> For now, I've reworded these as "must inherit privileges of". >> >> I don't have a good mental model of all this role inheritance, personally, >> but I fear that this change makes the messages more jargony and less clear. >> Maybe the original wording was good enough. > > I'm fine with that.
I used the original wording in v7. >> "admin option" is sort of a natural language term, I think, so we don't need >> to parametrize it as "%s option". Also, there are no other "options" in >> this context, I think. > > v16 introduces the INHERIT and SET options. I don't have a strong opinion > about parameterizing it, though. My intent was to consistently capitalize > all the attributes and options. I didn't change this in v7, but I can do so if you still think it shouldn't be parameterized. >> A general thought: It seems we currently don't have any error messages that >> address the user like "You must do this". Do we want to go there? Should we >> try for a more impersonal wording like >> >> "You must have the %s attribute to create roles." >> >> "Current user must have the %s attribute to create roles." >> >> "%s attribute is required to create roles." > > I think I like the last option the most. In general, I agree with trying > to avoid the second-person phrasing. I ended up using the "current user must have" wording in a few places, and for most others, I used "only roles with X may do Y." That seemed to flow relatively well, and IMO it made the required privileges abundantly clear. I initially was going to use the "X attribute is required to Y" wording, but I was worried that didn't make it sufficiently clear that the _role_ must have the attribute. In any case, I'm not wedded to the approach I used in the patch and am willing to try out other wordings. BTW I did find one example of a "you must" message while I was updating the patch: write_stderr("%s does not know where to find the server configuration file.\n" "You must specify the --config-file or -D invocation " "option or set the PGDATA environment variable.\n", progname); I don't think it's a common style, though. >> By the way, I'm not sure what the separation between 0001 and 0002 is >> supposed to be. > > I'll combine them. I first started with user.c only, but we kept finding > new messages to improve. I combined the patches in v7. -- Nathan Bossart Amazon Web Services: https://aws.amazon.com
>From 66cc7a3999cae48c1b97ef067bc16872b31b887f Mon Sep 17 00:00:00 2001 From: Nathan Bossart <nathandboss...@gmail.com> Date: Thu, 26 Jan 2023 11:05:13 -0800 Subject: [PATCH v7 1/1] Improve several permission-related error messages. --- contrib/file_fdw/expected/file_fdw.out | 3 +- contrib/file_fdw/file_fdw.c | 8 +- .../test_decoding/expected/permissions.out | 12 +- src/backend/backup/basebackup_server.c | 4 +- src/backend/catalog/objectaddress.c | 16 +- src/backend/commands/copy.c | 12 +- src/backend/commands/user.c | 169 +++++++++++++----- src/backend/replication/slot.c | 6 +- src/backend/storage/ipc/procarray.c | 4 +- src/backend/storage/ipc/signalfuncs.c | 16 +- src/backend/tcop/utility.c | 5 +- src/backend/utils/init/miscinit.c | 4 + src/backend/utils/init/postinit.c | 12 +- src/backend/utils/misc/guc.c | 15 +- .../expected/dummy_seclabel.out | 3 +- .../unsafe_tests/expected/rolenames.out | 3 +- src/test/regress/expected/create_role.out | 80 ++++++--- src/test/regress/expected/dependency.out | 4 + src/test/regress/expected/privileges.out | 23 ++- 19 files changed, 287 insertions(+), 112 deletions(-) diff --git a/contrib/file_fdw/expected/file_fdw.out b/contrib/file_fdw/expected/file_fdw.out index 36d76ba26c..a5acf6d4f7 100644 --- a/contrib/file_fdw/expected/file_fdw.out +++ b/contrib/file_fdw/expected/file_fdw.out @@ -474,7 +474,8 @@ ALTER FOREIGN TABLE agg_text OWNER TO regress_file_fdw_user; ALTER FOREIGN TABLE agg_text OPTIONS (SET format 'text'); SET ROLE regress_file_fdw_user; ALTER FOREIGN TABLE agg_text OPTIONS (SET format 'text'); -ERROR: only superuser or a role with privileges of the pg_read_server_files role may specify the filename option of a file_fdw foreign table +ERROR: permission denied to set the "filename" option of a file_fdw foreign table +DETAIL: Only roles with privileges of the "pg_read_server_files" role may set this option. SET ROLE regress_file_fdw_superuser; -- cleanup RESET ROLE; diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c index 2d2b0b6a6b..8a312b5d0e 100644 --- a/contrib/file_fdw/file_fdw.c +++ b/contrib/file_fdw/file_fdw.c @@ -278,13 +278,17 @@ file_fdw_validator(PG_FUNCTION_ARGS) !has_privs_of_role(GetUserId(), ROLE_PG_READ_SERVER_FILES)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("only superuser or a role with privileges of the pg_read_server_files role may specify the filename option of a file_fdw foreign table"))); + errmsg("permission denied to set the \"filename\" option of a file_fdw foreign table"), + errdetail("Only roles with privileges of the \"%s\" role may set this option.", + "pg_read_server_files"))); if (strcmp(def->defname, "program") == 0 && !has_privs_of_role(GetUserId(), ROLE_PG_EXECUTE_SERVER_PROGRAM)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("only superuser or a role with privileges of the pg_execute_server_program role may specify the program option of a file_fdw foreign table"))); + errmsg("permission denied to set the \"program\" option of a file_fdw foreign table"), + errdetail("Only roles with privileges of the \"%s\" role may set this option.", + "pg_execute_server_program"))); filename = defGetString(def); } diff --git a/contrib/test_decoding/expected/permissions.out b/contrib/test_decoding/expected/permissions.out index ed97f81dda..d6eaba8c55 100644 --- a/contrib/test_decoding/expected/permissions.out +++ b/contrib/test_decoding/expected/permissions.out @@ -54,13 +54,16 @@ RESET ROLE; -- plain user *can't* can control replication SET ROLE regress_lr_normal; SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding'); -ERROR: must be superuser or replication role to use replication slots +ERROR: permission denied to use replication slots +DETAIL: Only roles with the REPLICATION attribute may use replication slots. INSERT INTO lr_test VALUES('lr_superuser_init'); ERROR: permission denied for table lr_test SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1'); -ERROR: must be superuser or replication role to use replication slots +ERROR: permission denied to use replication slots +DETAIL: Only roles with the REPLICATION attribute may use replication slots. SELECT pg_drop_replication_slot('regression_slot'); -ERROR: must be superuser or replication role to use replication slots +ERROR: permission denied to use replication slots +DETAIL: Only roles with the REPLICATION attribute may use replication slots. RESET ROLE; -- replication users can drop superuser created slots SET ROLE regress_lr_superuser; @@ -90,7 +93,8 @@ SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_d RESET ROLE; SET ROLE regress_lr_normal; SELECT pg_drop_replication_slot('regression_slot'); -ERROR: must be superuser or replication role to use replication slots +ERROR: permission denied to use replication slots +DETAIL: Only roles with the REPLICATION attribute may use replication slots. RESET ROLE; -- all users can see existing slots SET ROLE regress_lr_superuser; diff --git a/src/backend/backup/basebackup_server.c b/src/backend/backup/basebackup_server.c index 0258d7a03b..2b9d9d2932 100644 --- a/src/backend/backup/basebackup_server.c +++ b/src/backend/backup/basebackup_server.c @@ -72,7 +72,9 @@ bbsink_server_new(bbsink *next, char *pathname) if (!has_privs_of_role(GetUserId(), ROLE_PG_WRITE_SERVER_FILES)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser or a role with privileges of the pg_write_server_files role to create backup stored on server"))); + errmsg("permission denied to create backup stored on server"), + errdetail("Only roles with privileges of the \"%s\" role may create a backup stored on the server.", + "pg_write_server_files"))); CommitTransactionCommand(); /* diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c index 2f688166e1..d59492934c 100644 --- a/src/backend/catalog/objectaddress.c +++ b/src/backend/catalog/objectaddress.c @@ -2547,20 +2547,26 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address, if (!superuser_arg(roleid)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser"))); + errmsg("permission denied"), + errdetail("The current user must have the %s attribute.", + "SUPERUSER"))); } else { if (!has_createrole_privilege(roleid)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must have CREATEROLE privilege"))); + errmsg("permission denied"), + errdetail("The current user must have the %s attribute.", + "CREATEROLE"))); if (!is_admin_of_role(roleid, address.objectId)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must have admin option on role \"%s\"", - GetUserNameFromId(address.objectId, - true)))); + errmsg("permission denied"), + errdetail("The current user must have the %s option on role \"%s\".", + "ADMIN", + GetUserNameFromId(address.objectId, + true)))); } break; case OBJECT_TSPARSER: diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index e34f583ea7..5fee0d963b 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -83,7 +83,9 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, if (!has_privs_of_role(GetUserId(), ROLE_PG_EXECUTE_SERVER_PROGRAM)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser or have privileges of the pg_execute_server_program role to COPY to or from an external program"), + errmsg("permission denied to COPY to or from an external program"), + errdetail("Only roles with privileges of the \"%s\" role may COPY to or from an external program.", + "pg_execute_server_program"), errhint("Anyone can COPY to stdout or from stdin. " "psql's \\copy command also works for anyone."))); } @@ -92,14 +94,18 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, if (is_from && !has_privs_of_role(GetUserId(), ROLE_PG_READ_SERVER_FILES)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser or have privileges of the pg_read_server_files role to COPY from a file"), + errmsg("permission denied to COPY from a file"), + errdetail("Only roles with privileges of the \"%s\" role may COPY from a file.", + "pg_read_server_files"), errhint("Anyone can COPY to stdout or from stdin. " "psql's \\copy command also works for anyone."))); if (!is_from && !has_privs_of_role(GetUserId(), ROLE_PG_WRITE_SERVER_FILES)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser or have privileges of the pg_write_server_files role to COPY to a file"), + errmsg("permission denied to COPY to a file"), + errdetail("Only roles with privileges of the \"%s\" role may COPY to a file.", + "pg_write_server_files"), errhint("Anyone can COPY to stdout or from stdin. " "psql's \\copy command also works for anyone."))); } diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c index 3a92e930c0..2411f9ab9d 100644 --- a/src/backend/commands/user.c +++ b/src/backend/commands/user.c @@ -316,23 +316,33 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt) if (!has_createrole_privilege(currentUserId)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("permission denied to create role"))); + errmsg("permission denied to create role"), + errdetail("Only roles with the %s attribute may create roles.", + "CREATEROLE"))); if (issuper) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser to create superusers"))); + errmsg("permission denied to create role"), + errdetail("Only roles with the %s attribute may create roles with %s.", + "SUPERUSER", "SUPERUSER"))); if (createdb && !have_createdb_privilege()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must have createdb permission to create createdb users"))); + errmsg("permission denied to create role"), + errdetail("Only roles with the %s attribute may create roles with %s.", + "CREATEDB", "CREATEDB"))); if (isreplication && !has_rolreplication(currentUserId)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must have replication permission to create replication users"))); + errmsg("permission denied to create role"), + errdetail("Only roles with the %s attribute may create roles with %s.", + "REPLICATION", "REPLICATION"))); if (bypassrls && !has_bypassrls_privilege(currentUserId)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must have bypassrls to create bypassrls users"))); + errmsg("permission denied to create role"), + errdetail("Only roles with the %s attribute may create roles with %s.", + "BYPASSRLS", "BYPASSRLS"))); } /* @@ -744,10 +754,18 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt) roleid = authform->oid; /* To mess with a superuser in any way you gotta be superuser. */ - if (!superuser() && (authform->rolsuper || dissuper)) + if (!superuser() && authform->rolsuper) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser to alter superuser roles or change superuser attribute"))); + errmsg("permission denied to alter role"), + errdetail("Only roles with the %s attribute may alter roles with %s.", + "SUPERUSER", "SUPERUSER"))); + if (!superuser() && dissuper) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied to alter role"), + errdetail("Only roles with the %s attribute may change the %s attribute.", + "SUPERUSER", "SUPERUSER"))); /* * Most changes to a role require that you both have CREATEROLE privileges @@ -761,13 +779,17 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt) dvalidUntil || disreplication || dbypassRLS) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("permission denied"))); + errmsg("permission denied to alter role"), + errdetail("Only roles with the %s attribute and the %s option on role \"%s\" may alter this role.", + "CREATEROLE", "ADMIN", rolename))); /* an unprivileged user can change their own password */ if (dpassword && roleid != currentUserId) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must have CREATEROLE privilege to change another user's password"))); + errmsg("permission denied to alter role"), + errdetail("To change another role's password, the current user must have the %s attribute and the %s option on the role.", + "CREATEROLE", "ADMIN"))); } else if (!superuser()) { @@ -779,23 +801,30 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt) if (dcreatedb && !have_createdb_privilege()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must have createdb privilege to change createdb attribute"))); + errmsg("permission denied to alter role"), + errdetail("Only roles with the %s attribute may change the %s attribute.", + "CREATEDB", "CREATEDB"))); if (disreplication && !has_rolreplication(currentUserId)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must have replication privilege to change replication attribute"))); + errmsg("permission denied to alter role"), + errdetail("Only roles with the %s attribute may change the %s attribute.", + "REPLICATION", "REPLICATION"))); if (dbypassRLS && !has_bypassrls_privilege(currentUserId)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must have bypassrls privilege to change bypassrls attribute"))); + errmsg("permission denied to alter role"), + errdetail("Only roles with the %s attribute may change the %s attribute.", + "BYPASSRLS", "BYPASSRLS"))); } /* To add members to a role, you need ADMIN OPTION. */ if (drolemembers && !is_admin_of_role(currentUserId, roleid)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must have admin option on role \"%s\" to add members", - rolename))); + errmsg("permission denied to alter role"), + errdetail("Only roles with the %s option on role \"%s\" may add members.", + "ADMIN", rolename))); /* Convert validuntil to internal form */ if (dvalidUntil) @@ -837,8 +866,10 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt) if (!should_be_super && roleid == BOOTSTRAP_SUPERUSERID) ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("permission denied: bootstrap user must be superuser"))); + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("permission denied to alter role"), + errdetail("The bootstrap user must have the %s attribute.", + "SUPERUSER"))); new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(should_be_super); new_record_repl[Anum_pg_authid_rolsuper - 1] = true; @@ -999,7 +1030,9 @@ AlterRoleSet(AlterRoleSetStmt *stmt) if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser to alter superusers"))); + errmsg("permission denied to alter role"), + errdetail("Only roles with the %s attribute may alter roles with %s.", + "SUPERUSER", "SUPERUSER"))); } else { @@ -1008,7 +1041,9 @@ AlterRoleSet(AlterRoleSetStmt *stmt) && roleid != GetUserId()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("permission denied"))); + errmsg("permission denied to alter role"), + errdetail("Only roles with the %s attribute and the %s option on role \"%s\" may alter this role.", + "CREATROLE", "ADMIN", NameStr(roleform->rolname)))); } ReleaseSysCache(roletuple); @@ -1038,7 +1073,9 @@ AlterRoleSet(AlterRoleSetStmt *stmt) if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser to alter settings globally"))); + errmsg("permission denied to alter setting"), + errdetail("Only roles with the %s attribute may alter settings globally.", + "SUPERUSER"))); } AlterSetting(databaseid, roleid, stmt->setstmt); @@ -1061,7 +1098,9 @@ DropRole(DropRoleStmt *stmt) if (!have_createrole_privilege()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("permission denied to drop role"))); + errmsg("permission denied to drop role"), + errdetail("Only roles with the %s attribute and the %s option on the target roles may drop roles.", + "CREATEROLE", "ADMIN"))); /* * Scan the pg_authid relation to find the Oid of the role(s) to be @@ -1131,12 +1170,15 @@ DropRole(DropRoleStmt *stmt) if (roleform->rolsuper && !superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser to drop superusers"))); + errmsg("permission denied to drop role"), + errdetail("Only roles with the %s attribute may drop roles with %s.", + "SUPERUSER", "SUPERUSER"))); if (!is_admin_of_role(GetUserId(), roleid)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must have admin option on role \"%s\"", - role))); + errmsg("permission denied to drop role"), + errdetail("Only roles with the %s attribute and the %s option on role \"%s\" may drop this role.", + "CREATEROLE", "ADMIN", NameStr(roleform->rolname)))); /* DROP hook for the role being removed */ InvokeObjectDropHook(AuthIdRelationId, roleid, 0); @@ -1378,12 +1420,14 @@ RenameRole(const char *oldname, const char *newname) * Only superusers can mess with superusers. Otherwise, a user with * CREATEROLE can rename a role for which they have ADMIN OPTION. */ - if (((Form_pg_authid) GETSTRUCT(oldtuple))->rolsuper) + if (authform->rolsuper) { if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser to rename superusers"))); + errmsg("permission denied to rename role"), + errdetail("Only roles with the %s attribute may rename roles with %s.", + "SUPERUSER", "SUPERUSER"))); } else { @@ -1391,7 +1435,9 @@ RenameRole(const char *oldname, const char *newname) !is_admin_of_role(GetUserId(), roleid)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("permission denied to rename role"))); + errmsg("permission denied to rename role"), + errdetail("Only roles with the %s attribute and the %s option on role \"%s\" may rename this role.", + "CREATEROLE", "ADMIN", NameStr(authform->rolname)))); } /* OK, construct the modified tuple */ @@ -1554,7 +1600,9 @@ DropOwnedObjects(DropOwnedStmt *stmt) if (!has_privs_of_role(GetUserId(), roleid)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("permission denied to drop objects"))); + errmsg("permission denied to drop objects"), + errdetail("Only roles with privileges of role \"%s\" may drop its objects.", + GetUserNameFromId(roleid, false)))); } /* Ok, do it */ @@ -1581,7 +1629,9 @@ ReassignOwnedObjects(ReassignOwnedStmt *stmt) if (!has_privs_of_role(GetUserId(), roleid)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("permission denied to reassign objects"))); + errmsg("permission denied to reassign objects"), + errdetail("Only roles with privileges of role \"%s\" may reassign its objects.", + GetUserNameFromId(roleid, false)))); } /* Must have privileges on the receiving side too */ @@ -1590,7 +1640,9 @@ ReassignOwnedObjects(ReassignOwnedStmt *stmt) if (!has_privs_of_role(GetUserId(), newrole)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("permission denied to reassign objects"))); + errmsg("permission denied to reassign objects"), + errdetail("Only roles with privileges of role \"%s\" may reassign objects to it.", + GetUserNameFromId(newrole, false)))); /* Ok, do it */ shdepReassignOwned(role_ids, newrole); @@ -1738,7 +1790,8 @@ AddRoleMems(Oid currentUserId, const char *rolename, Oid roleid, if (memberid == BOOTSTRAP_SUPERUSERID) ereport(ERROR, (errcode(ERRCODE_INVALID_GRANT_OPERATION), - errmsg("admin option cannot be granted back to your own grantor"))); + errmsg("%s option cannot be granted back to your own grantor", + "ADMIN"))); plan_member_revoke(memlist, actions, memberid); } @@ -1763,7 +1816,8 @@ AddRoleMems(Oid currentUserId, const char *rolename, Oid roleid, if (i >= memlist->n_members) ereport(ERROR, (errcode(ERRCODE_INVALID_GRANT_OPERATION), - errmsg("admin option cannot be granted back to your own grantor"))); + errmsg("%s option cannot be granted back to your own grantor", + "ADMIN"))); ReleaseSysCacheList(memlist); } @@ -2081,9 +2135,22 @@ check_role_membership_authorization(Oid currentUserId, Oid roleid, if (superuser_arg(roleid)) { if (!superuser_arg(currentUserId)) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser to alter superusers"))); + { + if (is_grant) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied to grant role \"%s\"", + GetUserNameFromId(roleid, false)), + errdetail("Only roles with the %s attribute may grant roles with %s.", + "SUPERUSER", "SUPERUSER"))); + else + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied to revoke role \"%s\"", + GetUserNameFromId(roleid, false)), + errdetail("Only roles with the %s attribute may revoke roles with %s.", + "SUPERUSER", "SUPERUSER"))); + } } else { @@ -2091,10 +2158,22 @@ check_role_membership_authorization(Oid currentUserId, Oid roleid, * Otherwise, must have admin option on the role to be changed. */ if (!is_admin_of_role(currentUserId, roleid)) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must have admin option on role \"%s\"", - GetUserNameFromId(roleid, false)))); + { + if (is_grant) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied to grant role \"%s\"", + GetUserNameFromId(roleid, false)), + errdetail("Only roles with the %s option on role \"%s\" may grant this role.", + "ADMIN", GetUserNameFromId(roleid, false)))); + else + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied to revoke role \"%s\"", + GetUserNameFromId(roleid, false)), + errdetail("Only roles with the %s option on role \"%s\" may revoke this role.", + "ADMIN", GetUserNameFromId(roleid, false)))); + } } } @@ -2173,14 +2252,18 @@ check_role_grantor(Oid currentUserId, Oid roleid, Oid grantorId, bool is_grant) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to grant privileges as role \"%s\"", - GetUserNameFromId(grantorId, false)))); + GetUserNameFromId(grantorId, false)), + errdetail("Only roles with privileges of role \"%s\" may grant privileges as this role.", + GetUserNameFromId(grantorId, false)))); if (grantorId != BOOTSTRAP_SUPERUSERID && select_best_admin(grantorId, roleid) != grantorId) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("grantor must have ADMIN OPTION on \"%s\"", - GetUserNameFromId(roleid, false)))); + errmsg("permission denied to grant privileges as role \"%s\"", + GetUserNameFromId(grantorId, false)), + errdetail("The grantor must have the %s option on role \"%s\".", + "ADMIN", GetUserNameFromId(roleid, false)))); } else { @@ -2188,7 +2271,9 @@ check_role_grantor(Oid currentUserId, Oid roleid, Oid grantorId, bool is_grant) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to revoke privileges granted by role \"%s\"", - GetUserNameFromId(grantorId, false)))); + GetUserNameFromId(grantorId, false)), + errdetail("Only roles with privileges of role \"%s\" may revoke privileges granted by this role.", + GetUserNameFromId(grantorId, false)))); } /* diff --git a/src/backend/replication/slot.c b/src/backend/replication/slot.c index f286918f69..2293c0c6fc 100644 --- a/src/backend/replication/slot.c +++ b/src/backend/replication/slot.c @@ -1140,10 +1140,12 @@ CheckSlotRequirements(void) void CheckSlotPermissions(void) { - if (!superuser() && !has_rolreplication(GetUserId())) + if (!has_rolreplication(GetUserId())) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser or replication role to use replication slots"))); + errmsg("permission denied to use replication slots"), + errdetail("Only roles with the %s attribute may use replication slots.", + "REPLICATION"))); } /* diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c index a9fb97460d..ea91ce355f 100644 --- a/src/backend/storage/ipc/procarray.c +++ b/src/backend/storage/ipc/procarray.c @@ -3883,7 +3883,9 @@ TerminateOtherDBBackends(Oid databaseId) !has_privs_of_role(GetUserId(), ROLE_PG_SIGNAL_BACKEND)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be a member of the role whose process is being terminated or member of pg_signal_backend"))); + errmsg("permission denied to terminate process"), + errdetail("Only roles with privileges of the role whose process is being terminated or with privileges of the \"%s\" role may terminate this process.", + "pg_signal_backend"))); } } diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c index bc93ab5b52..eabb68a9e1 100644 --- a/src/backend/storage/ipc/signalfuncs.c +++ b/src/backend/storage/ipc/signalfuncs.c @@ -121,12 +121,16 @@ pg_cancel_backend(PG_FUNCTION_ARGS) if (r == SIGNAL_BACKEND_NOSUPERUSER) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be a superuser to cancel superuser query"))); + errmsg("permission denied to cancel query"), + errdetail("Only roles with the %s attribute may cancel queries of roles with %s.", + "SUPERUSER", "SUPERUSER"))); if (r == SIGNAL_BACKEND_NOPERMISSION) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be a member of the role whose query is being canceled or member of pg_signal_backend"))); + errmsg("permission denied to cancel query"), + errdetail("Only roles with privileges of the role whose query is being canceled or with privileges of the \"%s\" role may cancel this query.", + "pg_signal_backend"))); PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS); } @@ -223,12 +227,16 @@ pg_terminate_backend(PG_FUNCTION_ARGS) if (r == SIGNAL_BACKEND_NOSUPERUSER) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be a superuser to terminate superuser process"))); + errmsg("permission denied to terminate process"), + errdetail("Only roles with the %s attribute may terminate processes of roles with %s.", + "SUPERUSER", "SUPERUSER"))); if (r == SIGNAL_BACKEND_NOPERMISSION) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be a member of the role whose process is being terminated or member of pg_signal_backend"))); + errmsg("permission denied to terminate process"), + errdetail("Only roles with privileges of the role whose process is being terminated or with privileges of the \"%s\" role may terminate this process.", + "pg_signal_backend"))); /* Wait only on success and if actually requested */ if (r == SIGNAL_BACKEND_SUCCESS && timeout > 0) diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index c7d9d96b45..eada735363 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -950,7 +950,10 @@ standard_ProcessUtility(PlannedStmt *pstmt, if (!has_privs_of_role(GetUserId(), ROLE_PG_CHECKPOINT)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser or have privileges of pg_checkpoint to do CHECKPOINT"))); + errmsg("permission denied to execute %s command", + "CHECKPOINT"), + errdetail("Only roles with privileges of the \"%s\" role may execute this command.", + "pg_checkpoint"))); RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_WAIT | (RecoveryInProgress() ? 0 : CHECKPOINT_FORCE)); diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index 7eb7fe87f6..a604432126 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -709,6 +709,10 @@ has_rolreplication(Oid roleid) bool result = false; HeapTuple utup; + /* Superusers bypass all permission checking. */ + if (superuser_arg(roleid)) + return true; + utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid)); if (HeapTupleIsValid(utup)) { diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c index b0e20cc635..92bac8b63f 100644 --- a/src/backend/utils/init/postinit.c +++ b/src/backend/utils/init/postinit.c @@ -949,12 +949,14 @@ InitPostgres(const char *in_dbname, Oid dboid, if (nfree < SuperuserReservedConnections) ereport(FATAL, (errcode(ERRCODE_TOO_MANY_CONNECTIONS), - errmsg("remaining connection slots are reserved for superusers"))); + errmsg("remaining connection slots are reserved for roles with %s", + "SUPERUSER"))); if (!has_privs_of_role(GetUserId(), ROLE_PG_USE_RESERVED_CONNECTIONS)) ereport(FATAL, (errcode(ERRCODE_TOO_MANY_CONNECTIONS), - errmsg("remaining connection slots are reserved for roles with privileges of pg_use_reserved_connections"))); + errmsg("remaining connection slots are reserved for roles with privileges of the \"%s\" role", + "pg_use_reserved_connections"))); } /* Check replication permissions needed for walsender processes. */ @@ -962,10 +964,12 @@ InitPostgres(const char *in_dbname, Oid dboid, { Assert(!bootstrap); - if (!superuser() && !has_rolreplication(GetUserId())) + if (!has_rolreplication(GetUserId())) ereport(FATAL, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser or replication role to start walsender"))); + errmsg("permission denied to start WAL sender"), + errdetail("Only roles with the %s attribute may start a WAL sender process.", + "REPLICATION"))); } /* diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 51e07d5582..ea67cfa5e5 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -4209,8 +4209,9 @@ GetConfigOption(const char *name, bool missing_ok, bool restrict_privileged) !ConfigOptionIsVisible(record)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser or have privileges of pg_read_all_settings to examine \"%s\"", - name))); + errmsg("permission denied to examine \"%s\"", name), + errdetail("Only roles with privileges of the \"%s\" role may examine this parameter.", + "pg_read_all_settings"))); switch (record->vartype) { @@ -4255,8 +4256,9 @@ GetConfigOptionResetString(const char *name) if (!ConfigOptionIsVisible(record)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser or have privileges of pg_read_all_settings to examine \"%s\"", - name))); + errmsg("permission denied to examine \"%s\"", name), + errdetail("Only roles with privileges of the \"%s\" role may examine this parameter.", + "pg_read_all_settings"))); switch (record->vartype) { @@ -5261,8 +5263,9 @@ GetConfigOptionByName(const char *name, const char **varname, bool missing_ok) if (!ConfigOptionIsVisible(record)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser or have privileges of pg_read_all_settings to examine \"%s\"", - name))); + errmsg("permission denied to examine \"%s\"", name), + errdetail("Only roles with privileges of the \"%s\" role may examine this parameter.", + "pg_read_all_settings"))); if (varname) *varname = record->name; diff --git a/src/test/modules/dummy_seclabel/expected/dummy_seclabel.out b/src/test/modules/dummy_seclabel/expected/dummy_seclabel.out index c57d4fd2df..de671e5a17 100644 --- a/src/test/modules/dummy_seclabel/expected/dummy_seclabel.out +++ b/src/test/modules/dummy_seclabel/expected/dummy_seclabel.out @@ -59,7 +59,8 @@ SECURITY LABEL ON ROLE regress_dummy_seclabel_user4 IS 'unclassified'; -- fail ( ERROR: role "regress_dummy_seclabel_user4" does not exist SET SESSION AUTHORIZATION regress_dummy_seclabel_user2; SECURITY LABEL ON ROLE regress_dummy_seclabel_user2 IS 'unclassified'; -- fail (not privileged) -ERROR: must have CREATEROLE privilege +ERROR: permission denied +DETAIL: The current user must have the CREATEROLE attribute. RESET SESSION AUTHORIZATION; -- -- Test for various types of object diff --git a/src/test/modules/unsafe_tests/expected/rolenames.out b/src/test/modules/unsafe_tests/expected/rolenames.out index 88b1ff843b..61396b2a80 100644 --- a/src/test/modules/unsafe_tests/expected/rolenames.out +++ b/src/test/modules/unsafe_tests/expected/rolenames.out @@ -1077,7 +1077,8 @@ SHOW session_preload_libraries; SET SESSION AUTHORIZATION regress_role_nopriv; -- fails with role not member of pg_read_all_settings SHOW session_preload_libraries; -ERROR: must be superuser or have privileges of pg_read_all_settings to examine "session_preload_libraries" +ERROR: permission denied to examine "session_preload_libraries" +DETAIL: Only roles with privileges of the "pg_read_all_settings" role may examine this parameter. RESET SESSION AUTHORIZATION; ERROR: current transaction is aborted, commands ignored until end of transaction block ROLLBACK; diff --git a/src/test/regress/expected/create_role.out b/src/test/regress/expected/create_role.out index 9f431bd4f5..98451287ab 100644 --- a/src/test/regress/expected/create_role.out +++ b/src/test/regress/expected/create_role.out @@ -7,26 +7,35 @@ CREATE ROLE regress_role_normal; -- fail, CREATEROLE user can't give away role attributes without having them SET SESSION AUTHORIZATION regress_role_limited_admin; CREATE ROLE regress_nosuch_superuser SUPERUSER; -ERROR: must be superuser to create superusers +ERROR: permission denied to create role +DETAIL: Only roles with the SUPERUSER attribute may create roles with SUPERUSER. CREATE ROLE regress_nosuch_replication_bypassrls REPLICATION BYPASSRLS; -ERROR: must have replication permission to create replication users +ERROR: permission denied to create role +DETAIL: Only roles with the REPLICATION attribute may create roles with REPLICATION. CREATE ROLE regress_nosuch_replication REPLICATION; -ERROR: must have replication permission to create replication users +ERROR: permission denied to create role +DETAIL: Only roles with the REPLICATION attribute may create roles with REPLICATION. CREATE ROLE regress_nosuch_bypassrls BYPASSRLS; -ERROR: must have bypassrls to create bypassrls users +ERROR: permission denied to create role +DETAIL: Only roles with the BYPASSRLS attribute may create roles with BYPASSRLS. CREATE ROLE regress_nosuch_createdb CREATEDB; -ERROR: must have createdb permission to create createdb users +ERROR: permission denied to create role +DETAIL: Only roles with the CREATEDB attribute may create roles with CREATEDB. -- ok, can create a role without any special attributes CREATE ROLE regress_role_limited; -- fail, can't give it in any of the restricted attributes ALTER ROLE regress_role_limited SUPERUSER; -ERROR: must be superuser to alter superuser roles or change superuser attribute +ERROR: permission denied to alter role +DETAIL: Only roles with the SUPERUSER attribute may change the SUPERUSER attribute. ALTER ROLE regress_role_limited REPLICATION; -ERROR: must have replication privilege to change replication attribute +ERROR: permission denied to alter role +DETAIL: Only roles with the REPLICATION attribute may change the REPLICATION attribute. ALTER ROLE regress_role_limited CREATEDB; -ERROR: must have createdb privilege to change createdb attribute +ERROR: permission denied to alter role +DETAIL: Only roles with the CREATEDB attribute may change the CREATEDB attribute. ALTER ROLE regress_role_limited BYPASSRLS; -ERROR: must have bypassrls privilege to change bypassrls attribute +ERROR: permission denied to alter role +DETAIL: Only roles with the BYPASSRLS attribute may change the BYPASSRLS attribute. DROP ROLE regress_role_limited; -- ok, can give away these role attributes if you have them SET SESSION AUTHORIZATION regress_role_admin; @@ -43,9 +52,11 @@ ALTER ROLE regress_createdb NOCREATEDB; ALTER ROLE regress_createdb CREATEDB; -- fail, can't toggle SUPERUSER ALTER ROLE regress_createdb SUPERUSER; -ERROR: must be superuser to alter superuser roles or change superuser attribute +ERROR: permission denied to alter role +DETAIL: Only roles with the SUPERUSER attribute may change the SUPERUSER attribute. ALTER ROLE regress_createdb NOSUPERUSER; -ERROR: must be superuser to alter superuser roles or change superuser attribute +ERROR: permission denied to alter role +DETAIL: Only roles with the SUPERUSER attribute may change the SUPERUSER attribute. -- ok, having CREATEROLE is enough to create users with these privileges CREATE ROLE regress_createrole CREATEROLE NOINHERIT; GRANT CREATE ON DATABASE regression TO regress_createrole WITH GRANT OPTION; @@ -59,7 +70,8 @@ CREATE ROLE regress_noiseword SYSID 12345; NOTICE: SYSID can no longer be specified -- fail, cannot grant membership in superuser role CREATE ROLE regress_nosuch_super IN ROLE regress_role_super; -ERROR: must be superuser to alter superusers +ERROR: permission denied to grant role "regress_role_super" +DETAIL: Only roles with the SUPERUSER attribute may grant roles with SUPERUSER. -- fail, database owner cannot have members CREATE ROLE regress_nosuch_dbowner IN ROLE pg_database_owner; ERROR: role "pg_database_owner" cannot have explicit members @@ -94,11 +106,14 @@ ALTER ROLE regress_hasprivs RENAME TO regress_tenant; ALTER ROLE regress_tenant NOINHERIT NOLOGIN CONNECTION LIMIT 7; -- fail, we should be unable to modify a role we did not create COMMENT ON ROLE regress_role_normal IS 'some comment'; -ERROR: must have admin option on role "regress_role_normal" +ERROR: permission denied +DETAIL: The current user must have the ADMIN option on role "regress_role_normal". ALTER ROLE regress_role_normal RENAME TO regress_role_abnormal; ERROR: permission denied to rename role +DETAIL: Only roles with the CREATEROLE attribute and the ADMIN option on role "regress_role_normal" may rename this role. ALTER ROLE regress_role_normal NOINHERIT NOLOGIN CONNECTION LIMIT 7; -ERROR: permission denied +ERROR: permission denied to alter role +DETAIL: Only roles with the CREATEROLE attribute and the ADMIN option on role "regress_role_normal" may alter this role. -- ok, regress_tenant can create objects within the database SET SESSION AUTHORIZATION regress_tenant; CREATE TABLE tenant_table (i integer); @@ -123,6 +138,7 @@ ERROR: must be able to SET ROLE "regress_tenant" -- fail, we don't inherit permissions from regress_tenant REASSIGN OWNED BY regress_tenant TO regress_createrole; ERROR: permission denied to reassign objects +DETAIL: Only roles with privileges of role "regress_tenant" may reassign its objects. -- ok, create a role with a value for createrole_self_grant SET createrole_self_grant = 'set, inherit'; CREATE ROLE regress_tenant2; @@ -150,25 +166,35 @@ ERROR: must be able to SET ROLE "regress_tenant2" DROP TABLE tenant2_table; -- fail, CREATEROLE is not enough to create roles in privileged roles CREATE ROLE regress_read_all_data IN ROLE pg_read_all_data; -ERROR: must have admin option on role "pg_read_all_data" +ERROR: permission denied to grant role "pg_read_all_data" +DETAIL: Only roles with the ADMIN option on role "pg_read_all_data" may grant this role. CREATE ROLE regress_write_all_data IN ROLE pg_write_all_data; -ERROR: must have admin option on role "pg_write_all_data" +ERROR: permission denied to grant role "pg_write_all_data" +DETAIL: Only roles with the ADMIN option on role "pg_write_all_data" may grant this role. CREATE ROLE regress_monitor IN ROLE pg_monitor; -ERROR: must have admin option on role "pg_monitor" +ERROR: permission denied to grant role "pg_monitor" +DETAIL: Only roles with the ADMIN option on role "pg_monitor" may grant this role. CREATE ROLE regress_read_all_settings IN ROLE pg_read_all_settings; -ERROR: must have admin option on role "pg_read_all_settings" +ERROR: permission denied to grant role "pg_read_all_settings" +DETAIL: Only roles with the ADMIN option on role "pg_read_all_settings" may grant this role. CREATE ROLE regress_read_all_stats IN ROLE pg_read_all_stats; -ERROR: must have admin option on role "pg_read_all_stats" +ERROR: permission denied to grant role "pg_read_all_stats" +DETAIL: Only roles with the ADMIN option on role "pg_read_all_stats" may grant this role. CREATE ROLE regress_stat_scan_tables IN ROLE pg_stat_scan_tables; -ERROR: must have admin option on role "pg_stat_scan_tables" +ERROR: permission denied to grant role "pg_stat_scan_tables" +DETAIL: Only roles with the ADMIN option on role "pg_stat_scan_tables" may grant this role. CREATE ROLE regress_read_server_files IN ROLE pg_read_server_files; -ERROR: must have admin option on role "pg_read_server_files" +ERROR: permission denied to grant role "pg_read_server_files" +DETAIL: Only roles with the ADMIN option on role "pg_read_server_files" may grant this role. CREATE ROLE regress_write_server_files IN ROLE pg_write_server_files; -ERROR: must have admin option on role "pg_write_server_files" +ERROR: permission denied to grant role "pg_write_server_files" +DETAIL: Only roles with the ADMIN option on role "pg_write_server_files" may grant this role. CREATE ROLE regress_execute_server_program IN ROLE pg_execute_server_program; -ERROR: must have admin option on role "pg_execute_server_program" +ERROR: permission denied to grant role "pg_execute_server_program" +DETAIL: Only roles with the ADMIN option on role "pg_execute_server_program" may grant this role. CREATE ROLE regress_signal_backend IN ROLE pg_signal_backend; -ERROR: must have admin option on role "pg_signal_backend" +ERROR: permission denied to grant role "pg_signal_backend" +DETAIL: Only roles with the ADMIN option on role "pg_signal_backend" may grant this role. -- fail, role still owns database objects DROP ROLE regress_tenant; ERROR: role "regress_tenant" cannot be dropped because some objects depend on it @@ -211,11 +237,13 @@ DROP ROLE regress_inroles; DROP ROLE regress_adminroles; -- fail, cannot drop ourself, nor superusers or roles we lack ADMIN for DROP ROLE regress_role_super; -ERROR: must be superuser to drop superusers +ERROR: permission denied to drop role +DETAIL: Only roles with the SUPERUSER attribute may drop roles with SUPERUSER. DROP ROLE regress_role_admin; ERROR: current user cannot be dropped DROP ROLE regress_rolecreator; -ERROR: must have admin option on role "regress_rolecreator" +ERROR: permission denied to drop role +DETAIL: Only roles with the CREATEROLE attribute and the ADMIN option on role "regress_rolecreator" may drop this role. -- ok RESET SESSION AUTHORIZATION; REVOKE CREATE ON DATABASE regression FROM regress_role_admin CASCADE; diff --git a/src/test/regress/expected/dependency.out b/src/test/regress/expected/dependency.out index 520035f6a0..4b9a6d044e 100644 --- a/src/test/regress/expected/dependency.out +++ b/src/test/regress/expected/dependency.out @@ -48,12 +48,16 @@ SET SESSION AUTHORIZATION regress_dep_user0; -- permission denied DROP OWNED BY regress_dep_user1; ERROR: permission denied to drop objects +DETAIL: Only roles with privileges of role "regress_dep_user1" may drop its objects. DROP OWNED BY regress_dep_user0, regress_dep_user2; ERROR: permission denied to drop objects +DETAIL: Only roles with privileges of role "regress_dep_user2" may drop its objects. REASSIGN OWNED BY regress_dep_user0 TO regress_dep_user1; ERROR: permission denied to reassign objects +DETAIL: Only roles with privileges of role "regress_dep_user1" may reassign objects to it. REASSIGN OWNED BY regress_dep_user1 TO regress_dep_user0; ERROR: permission denied to reassign objects +DETAIL: Only roles with privileges of role "regress_dep_user1" may reassign its objects. -- this one is allowed DROP OWNED BY regress_dep_user0; CREATE TABLE deptest1 (f1 int unique); diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out index 5496ec8f55..3cf4ac8c9e 100644 --- a/src/test/regress/expected/privileges.out +++ b/src/test/regress/expected/privileges.out @@ -37,7 +37,7 @@ CREATE ROLE regress_priv_role; GRANT regress_priv_user1 TO regress_priv_user2 WITH ADMIN OPTION; GRANT regress_priv_user1 TO regress_priv_user3 WITH ADMIN OPTION GRANTED BY regress_priv_user2; GRANT regress_priv_user1 TO regress_priv_user2 WITH ADMIN OPTION GRANTED BY regress_priv_user3; -ERROR: admin option cannot be granted back to your own grantor +ERROR: ADMIN option cannot be granted back to your own grantor -- need CASCADE to revoke grant or admin option if dependent grants exist REVOKE ADMIN OPTION FOR regress_priv_user1 FROM regress_priv_user2; -- fail ERROR: dependent privileges exist @@ -156,7 +156,8 @@ ALTER GROUP regress_priv_group2 ADD USER regress_priv_user2; -- duplicate NOTICE: role "regress_priv_user2" has already been granted membership in role "regress_priv_group2" by role "regress_priv_user1" ALTER GROUP regress_priv_group2 DROP USER regress_priv_user2; ALTER USER regress_priv_user2 PASSWORD 'verysecret'; -- not permitted -ERROR: must have CREATEROLE privilege to change another user's password +ERROR: permission denied to alter role +DETAIL: To change another role's password, the current user must have the CREATEROLE attribute and the ADMIN option on the role. RESET SESSION AUTHORIZATION; ALTER GROUP regress_priv_group2 DROP USER regress_priv_user2; REVOKE ADMIN OPTION FOR regress_priv_group2 FROM regress_priv_user1; @@ -168,7 +169,8 @@ CREATE FUNCTION leak(integer,integer) RETURNS boolean ALTER FUNCTION leak(integer,integer) OWNER TO regress_priv_user1; -- test owner privileges GRANT regress_priv_role TO regress_priv_user1 WITH ADMIN OPTION GRANTED BY regress_priv_role; -- error, doesn't have ADMIN OPTION -ERROR: grantor must have ADMIN OPTION on "regress_priv_role" +ERROR: permission denied to grant privileges as role "regress_priv_role" +DETAIL: The grantor must have the ADMIN option on role "regress_priv_role". GRANT regress_priv_role TO regress_priv_user1 WITH ADMIN OPTION GRANTED BY CURRENT_ROLE; REVOKE ADMIN OPTION FOR regress_priv_role FROM regress_priv_user1 GRANTED BY foo; -- error ERROR: role "foo" does not exist @@ -1795,7 +1797,8 @@ REFRESH MATERIALIZED VIEW sro_mv; ERROR: cannot fire deferred trigger within security-restricted operation CONTEXT: SQL function "mv_action" statement 1 BEGIN; SET CONSTRAINTS ALL IMMEDIATE; REFRESH MATERIALIZED VIEW sro_mv; COMMIT; -ERROR: must have admin option on role "regress_priv_group2" +ERROR: permission denied to grant role "regress_priv_group2" +DETAIL: Only roles with the ADMIN option on role "regress_priv_group2" may grant this role. CONTEXT: SQL function "unwanted_grant" statement 1 SQL statement "SELECT unwanted_grant()" PL/pgSQL function sro_trojan() line 1 at PERFORM @@ -1825,10 +1828,12 @@ CREATE FUNCTION dogrant_ok() RETURNS void LANGUAGE sql SECURITY DEFINER AS GRANT regress_priv_group2 TO regress_priv_user5; -- ok: had ADMIN OPTION SET ROLE regress_priv_group2; GRANT regress_priv_group2 TO regress_priv_user5; -- fails: SET ROLE suspended privilege -ERROR: must have admin option on role "regress_priv_group2" +ERROR: permission denied to grant role "regress_priv_group2" +DETAIL: Only roles with the ADMIN option on role "regress_priv_group2" may grant this role. SET SESSION AUTHORIZATION regress_priv_user1; GRANT regress_priv_group2 TO regress_priv_user5; -- fails: no ADMIN OPTION -ERROR: must have admin option on role "regress_priv_group2" +ERROR: permission denied to grant role "regress_priv_group2" +DETAIL: Only roles with the ADMIN option on role "regress_priv_group2" may grant this role. SELECT dogrant_ok(); -- ok: SECURITY DEFINER conveys ADMIN NOTICE: role "regress_priv_user5" has already been granted membership in role "regress_priv_group2" by role "regress_priv_user4" dogrant_ok @@ -1838,10 +1843,12 @@ NOTICE: role "regress_priv_user5" has already been granted membership in role " SET ROLE regress_priv_group2; GRANT regress_priv_group2 TO regress_priv_user5; -- fails: SET ROLE did not help -ERROR: must have admin option on role "regress_priv_group2" +ERROR: permission denied to grant role "regress_priv_group2" +DETAIL: Only roles with the ADMIN option on role "regress_priv_group2" may grant this role. SET SESSION AUTHORIZATION regress_priv_group2; GRANT regress_priv_group2 TO regress_priv_user5; -- fails: no self-admin -ERROR: must have admin option on role "regress_priv_group2" +ERROR: permission denied to grant role "regress_priv_group2" +DETAIL: Only roles with the ADMIN option on role "regress_priv_group2" may grant this role. SET SESSION AUTHORIZATION regress_priv_user4; DROP FUNCTION dogrant_ok(); REVOKE regress_priv_group2 FROM regress_priv_user5; -- 2.25.1