moving this discussion to a new thread...
On Thu, Jan 19, 2023 at 10:20:33AM -0500, Robert Haas wrote:
> On Wed, Jan 18, 2023 at 6:17 PM Nathan Bossart <[email protected]>
> wrote:
>> However, as the attribute
>> system becomes more sophisticated, I think we ought to improve the error
>> messages in user.c. IMHO messages like "permission denied" could be
>> greatly improved with some added context.
>>
>> This probably shouldn't block your patch, but I think it's worth doing in
>> v16 since there are other changes in this area. I'm happy to help.
>
> That would be great. I agree that it's good to try to improve the
> error messages. It hasn't been entirely clear to me how to do that.
> For instance, I don't think we want to say something like:
>
> ERROR: must have CREATEROLE privilege and ADMIN OPTION on the target
> role, or in lieu of both of those to be superuser, to set the
> CONNECTION LIMIT for another role
> ERROR: must have CREATEROLE privilege and ADMIN OPTION on the target
> role, plus also CREATEDB, or in lieu of all that to be superuser, to
> remove the CREATEDB property from another role
>
> Such messages are long and we'd end up with a lot of variants. It's
> possible that the messages could be multi-tier. For instance, if we
> determine that you're trying to manage users and you don't have
> permission to manage ANY user, we could say:
>
> ERROR: permission to manage roles denied
> DETAIL: You must have the CREATEROLE privilege or be a superuser to
> manage roles.
>
> If you could potentially manage some user, but not the one you're
> trying to manage, we could say:
>
> ERROR: permission to manage role "%s" denied
> DETAIL: You need ADMIN OPTION on the target role to manage it.
>
> If you have permission to manage the target role but not in the
> requested manner, we could then say something like:
>
> ERROR: permission to manage CREATEDB for role "%s" denied
> DETAIL: You need CREATEDB to manage it.
>
> This is just one idea, and maybe not the best one. I'm just trying to
> say that I think this is basically an organizational problem. We need
> a plan for how we're going to report errors that is not too
> complicated to implement with reasonable effort, and that will produce
> messages that users will understand. I'd be delighted if you wanted to
> provide either ideas or patches...
Here is an early draft of some modest improvements to the user.c error
messages. I basically just tried to standardize the style of and add
context to the existing error messages. I used errhint() for this extra
context, but errdetail() would work, too. This isn't perfect. You might
still have to go through a couple rounds of errors before your role has all
the privileges it needs for a command, but this seems to improve matters a
little.
I think there is still a lot of room for improvement, but I wanted to at
least get the discussion started before I went too far.
--
Nathan Bossart
Amazon Web Services: https://aws.amazon.com
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 3a92e930c0..1982831e2a 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -316,23 +316,28 @@ 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"),
+ errhint("You must have CREATEROLE privilege to create roles.")));
if (issuper)
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("must be superuser to create superusers")));
+ errmsg("permission denied to create role"),
+ errhint("You must have SUPERUSER privilege to create roles with 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"),
+ errhint("You must have CREATEDB privilege to create roles with 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"),
+ errhint("You must have REPLICATION privilege to create roles with 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"),
+ errhint("You must have BYPASSRLS privilege to create roles with BYPASSRLS.")));
}
/*
@@ -747,7 +752,8 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
if (!superuser() && (authform->rolsuper || dissuper))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("must be superuser to alter superuser roles or change superuser attribute")));
+ errmsg("permission denied to alter role"),
+ errhint("You must have SUPERUSER privilege to alter roles with SUPERUSER.")));
/*
* Most changes to a role require that you both have CREATEROLE privileges
@@ -758,16 +764,13 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
{
/* things an unprivileged user certainly can't do */
if (dinherit || dcreaterole || dcreatedb || dcanlogin || dconnlimit ||
- dvalidUntil || disreplication || dbypassRLS)
+ dvalidUntil || disreplication || dbypassRLS ||
+ (dpassword && roleid != currentUserId))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("permission denied")));
-
- /* 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"),
+ errhint("You must have CREATEROLE privilege and ADMIN OPTION on role \"%s\".",
+ rolename)));
}
else if (!superuser())
{
@@ -779,23 +782,27 @@ 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"),
+ errhint("You must have CREATEDB privilege to alter roles with 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"),
+ errhint("You must have REPLICATION privilege to alter roles with 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"),
+ errhint("You must have BYPASSRLS privilege to alter roles with 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"),
+ errhint("You must have ADMIN OPTION on role \"%s\" to add members.",
+ rolename)));
/* Convert validuntil to internal form */
if (dvalidUntil)
@@ -838,7 +845,8 @@ 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")));
+ errmsg("permission denied to alter role"),
+ errhint("The bootstrap user must be superuser.")));
new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(should_be_super);
new_record_repl[Anum_pg_authid_rolsuper - 1] = true;
@@ -999,7 +1007,8 @@ AlterRoleSet(AlterRoleSetStmt *stmt)
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("must be superuser to alter superusers")));
+ errmsg("permission denied to alter role"),
+ errhint("You must have SUPERUSER privilege to alter roles with SUPERUSER.")));
}
else
{
@@ -1008,7 +1017,9 @@ AlterRoleSet(AlterRoleSetStmt *stmt)
&& roleid != GetUserId())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("permission denied")));
+ errmsg("permission denied to alter role"),
+ errhint("You must have CREATEROLE privilege and ADMIN OPTION on role \"%s\".",
+ NameStr(roleform->rolname))));
}
ReleaseSysCache(roletuple);
@@ -1038,7 +1049,8 @@ 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"),
+ errhint("You must have SUPERUSER privilege to alter settings globally.")));
}
AlterSetting(databaseid, roleid, stmt->setstmt);
@@ -1061,7 +1073,8 @@ 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"),
+ errhint("You must have CREATEROLE privilege and ADMIN OPTION on the target roles.")));
/*
* Scan the pg_authid relation to find the Oid of the role(s) to be
@@ -1131,12 +1144,14 @@ 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"),
+ errhint("You must have SUPERUSER privilege to drop roles with 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"),
+ errhint("You must have CREATEROLE privilege and ADMIN OPTION on role \"%s\".",
+ NameStr(roleform->rolname))));
/* DROP hook for the role being removed */
InvokeObjectDropHook(AuthIdRelationId, roleid, 0);
@@ -1378,12 +1393,13 @@ 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"),
+ errhint("You must have SUPERUSER privilege to rename roles with SUPERUSER.")));
}
else
{
@@ -1391,7 +1407,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"),
+ errhint("You must have CREATEROLE privilege and ADMIN OPTION on role \"%s\".",
+ NameStr(authform->rolname))));
}
/* OK, construct the modified tuple */
@@ -1554,7 +1572,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"),
+ errhint("You must have privileges of role \"%s\".",
+ GetUserNameFromId(roleid, false))));
}
/* Ok, do it */
@@ -1581,7 +1601,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"),
+ errhint("You must have privileges of role \"%s\".",
+ GetUserNameFromId(roleid, false))));
}
/* Must have privileges on the receiving side too */
@@ -1590,7 +1612,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"),
+ errhint("You must have privileges of role \"%s\".",
+ GetUserNameFromId(newrole, false))));
/* Ok, do it */
shdepReassignOwned(role_ids, newrole);
@@ -1738,7 +1762,7 @@ 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("ADMIN OPTION cannot be granted back to your own grantor")));
plan_member_revoke(memlist, actions, memberid);
}
@@ -1763,7 +1787,7 @@ 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("ADMIN OPTION cannot be granted back to your own grantor")));
ReleaseSysCacheList(memlist);
}
@@ -2081,9 +2105,20 @@ 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)),
+ errhint("You must have SUPERUSER privilege to grant roles with SUPERUSER.")));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to revoke role \"%s\"",
+ GetUserNameFromId(roleid, false)),
+ errhint("You must have SUPERUSER privilege to revoke roles with SUPERUSER.")));
+ }
}
else
{
@@ -2091,10 +2126,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)),
+ errhint("You must have ADMIN OPTION on role \"%s\".",
+ GetUserNameFromId(roleid, false))));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to revoke role \"%s\"",
+ GetUserNameFromId(roleid, false)),
+ errhint("You must have ADMIN OPTION on role \"%s\".",
+ GetUserNameFromId(roleid, false))));
+ }
}
}
@@ -2173,14 +2220,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)),
+ errhint("You must have privileges of role \"%s\".",
+ 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)),
+ errhint("The grantor must have ADMIN OPTION on role \"%s\".",
+ GetUserNameFromId(roleid, false))));
}
else
{
@@ -2188,7 +2239,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)),
+ errhint("You must have privileges of role \"%s\".",
+ GetUserNameFromId(grantorId, false))));
}
/*
diff --git a/src/test/regress/expected/create_role.out b/src/test/regress/expected/create_role.out
index c8beb36bab..7549873ee5 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
+HINT: You must have SUPERUSER privilege to 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
+HINT: You must have REPLICATION privilege to 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
+HINT: You must have REPLICATION privilege to create roles with REPLICATION.
CREATE ROLE regress_nosuch_bypassrls BYPASSRLS;
-ERROR: must have bypassrls to create bypassrls users
+ERROR: permission denied to create role
+HINT: You must have BYPASSRLS privilege to 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
+HINT: You must have CREATEDB privilege to 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
+HINT: You must have SUPERUSER privilege to alter roles with SUPERUSER.
ALTER ROLE regress_role_limited REPLICATION;
-ERROR: must have replication privilege to change replication attribute
+ERROR: permission denied to alter role
+HINT: You must have REPLICATION privilege to alter roles with REPLICATION.
ALTER ROLE regress_role_limited CREATEDB;
-ERROR: must have createdb privilege to change createdb attribute
+ERROR: permission denied to alter role
+HINT: You must have CREATEDB privilege to alter roles with CREATEDB.
ALTER ROLE regress_role_limited BYPASSRLS;
-ERROR: must have bypassrls privilege to change bypassrls attribute
+ERROR: permission denied to alter role
+HINT: You must have BYPASSRLS privilege to alter roles with BYPASSRLS.
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
+HINT: You must have SUPERUSER privilege to alter roles with SUPERUSER.
ALTER ROLE regress_createdb NOSUPERUSER;
-ERROR: must be superuser to alter superuser roles or change superuser attribute
+ERROR: permission denied to alter role
+HINT: You must have SUPERUSER privilege to alter roles with SUPERUSER.
-- 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"
+HINT: You must have SUPERUSER privilege to 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
@@ -97,8 +109,10 @@ COMMENT ON ROLE regress_role_normal IS 'some comment';
ERROR: must have admin option on role "regress_role_normal"
ALTER ROLE regress_role_normal RENAME TO regress_role_abnormal;
ERROR: permission denied to rename role
+HINT: You must have CREATEROLE privilege and ADMIN OPTION on role "regress_role_normal".
ALTER ROLE regress_role_normal NOINHERIT NOLOGIN CONNECTION LIMIT 7;
-ERROR: permission denied
+ERROR: permission denied to alter role
+HINT: You must have CREATEROLE privilege and ADMIN OPTION on role "regress_role_normal".
-- ok, regress_tenant can create objects within the database
SET SESSION AUTHORIZATION regress_tenant;
CREATE TABLE tenant_table (i integer);
@@ -123,6 +137,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
+HINT: You must have privileges of role "regress_tenant".
-- ok, create a role with a value for createrole_self_grant
SET createrole_self_grant = 'set, inherit';
CREATE ROLE regress_tenant2;
@@ -150,25 +165,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"
+HINT: You must have ADMIN OPTION on role "pg_read_all_data".
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"
+HINT: You must have ADMIN OPTION on role "pg_write_all_data".
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"
+HINT: You must have ADMIN OPTION on role "pg_monitor".
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"
+HINT: You must have ADMIN OPTION on role "pg_read_all_settings".
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"
+HINT: You must have ADMIN OPTION on role "pg_read_all_stats".
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"
+HINT: You must have ADMIN OPTION on role "pg_stat_scan_tables".
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"
+HINT: You must have ADMIN OPTION on role "pg_read_server_files".
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"
+HINT: You must have ADMIN OPTION on role "pg_write_server_files".
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"
+HINT: You must have ADMIN OPTION on role "pg_execute_server_program".
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"
+HINT: You must have ADMIN OPTION on role "pg_signal_backend".
-- 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 +236,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
+HINT: You must have SUPERUSER privilege to 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
+HINT: You must have CREATEROLE privilege and ADMIN OPTION on role "regress_rolecreator".
-- 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..0556068e32 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
+HINT: You must have privileges of role "regress_dep_user1".
DROP OWNED BY regress_dep_user0, regress_dep_user2;
ERROR: permission denied to drop objects
+HINT: You must have privileges of role "regress_dep_user2".
REASSIGN OWNED BY regress_dep_user0 TO regress_dep_user1;
ERROR: permission denied to reassign objects
+HINT: You must have privileges of role "regress_dep_user1".
REASSIGN OWNED BY regress_dep_user1 TO regress_dep_user0;
ERROR: permission denied to reassign objects
+HINT: You must have privileges of role "regress_dep_user1".
-- 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 95d1e5515f..e0f5a486b9 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
+HINT: You must have CREATEROLE privilege and ADMIN OPTION on role "regress_priv_user2".
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"
+HINT: The grantor must have 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"
+HINT: You must have ADMIN OPTION on role "regress_priv_group2".
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"
+HINT: You must have ADMIN OPTION on role "regress_priv_group2".
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"
+HINT: You must have ADMIN OPTION on role "regress_priv_group2".
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"
+HINT: You must have ADMIN OPTION on role "regress_priv_group2".
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"
+HINT: You must have ADMIN OPTION on role "regress_priv_group2".
SET SESSION AUTHORIZATION regress_priv_user4;
DROP FUNCTION dogrant_ok();
REVOKE regress_priv_group2 FROM regress_priv_user5;