pg.ddx.io pgsql-hackers@postgresql.org mailing list archive
help / color / mirror / Atom feedimproving user.c error messages
32+ messages / 5 participants
[nested] [flat]
Commits from this thread:
2023-03-17 de4d456b40 Improve several permission-related error messages.
2023-03-16 442f870065 Integrate superuser check into has_rolreplication()
2023-03-16 3b7cd8c690 Small code simplification
* improving user.c error messages
@ 2023-01-26 00:22 Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 01:45 ` Re: improving user.c error messages Tom Lane <tgl@sss.pgh.pa.us>
2023-01-26 09:07 ` Re: improving user.c error messages Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-27 10:00 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
0 siblings, 3 replies; 32+ messages in thread
From: Nathan Bossart @ 2023-01-26 00:22 UTC (permalink / raw)
To: Robert Haas <robertmhaas@gmail.com>; +Cc: pgsql-hackers
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 <nathandbossart@gmail.com> 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
Attachments:
enhance_error_messages.patch (text/x-diff, 28.3K) download
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;
^ permalink raw reply [nested|flat] 32+ messages in thread
* Re: improving user.c error messages
2023-01-26 00:22 improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
@ 2023-01-26 01:45 ` Tom Lane <tgl@sss.pgh.pa.us>
2 siblings, 0 replies; 32+ messages in thread
From: Tom Lane @ 2023-01-26 01:45 UTC (permalink / raw)
To: Nathan Bossart <nathandbossart@gmail.com>; +Cc: Robert Haas <robertmhaas@gmail.com>; pgsql-hackers
Nathan Bossart <nathandbossart@gmail.com> writes:
> On Thu, Jan 19, 2023 at 10:20:33AM -0500, Robert Haas wrote:
>> 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
> 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.
Yeah, I think the right fix is to keep the primary message pretty terse
and add detail in secondary messages. IMO most of these are errdetail not
errhint, because they are factual details about the rules [1]. But other
than that quibble, Nathan's draft looked pretty good in a quick once-over.
regards, tom lane
[1] https://www.postgresql.org/docs/devel/error-style-guide.html
^ permalink raw reply [nested|flat] 32+ messages in thread
* Re: improving user.c error messages
2023-01-26 00:22 improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
@ 2023-01-26 09:07 ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-26 19:13 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2 siblings, 1 reply; 32+ messages in thread
From: Alvaro Herrera @ 2023-01-26 09:07 UTC (permalink / raw)
To: Nathan Bossart <nathandbossart@gmail.com>; +Cc: Robert Haas <robertmhaas@gmail.com>; pgsql-hackers
Please use
errdetail("You must have %s privilege to create roles with %s.",
"SUPERUSER", "SUPERUSER")));
in this kind of message where multiple copies appear that only differ in
the keyword to use, to avoid creating four copies of essentially the
same string.
This applies in several places.
> - 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.")));
I think this one is a bit ambiguous; does "with" mean that roles that
have that priv cannot be changed, or does it mean that you cannot meddle
with that bit in particular? I think it'd be better to say
"You must have %s privilege to change the %s attribute."
or something like that.
--
Álvaro Herrera PostgreSQL Developer — https://www.EnterpriseDB.com/
Maybe there's lots of data loss but the records of data loss are also lost.
(Lincoln Yeoh)
^ permalink raw reply [nested|flat] 32+ messages in thread
* Re: improving user.c error messages
2023-01-26 00:22 improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 09:07 ` Re: improving user.c error messages Alvaro Herrera <alvherre@alvh.no-ip.org>
@ 2023-01-26 19:13 ` Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 19:42 ` Re: improving user.c error messages Robert Haas <robertmhaas@gmail.com>
0 siblings, 1 reply; 32+ messages in thread
From: Nathan Bossart @ 2023-01-26 19:13 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; tgl@sss.pgh.pa.us
Thanks for taking a look.
On Thu, Jan 26, 2023 at 10:07:39AM +0100, Alvaro Herrera wrote:
> Please use
> errdetail("You must have %s privilege to create roles with %s.",
> "SUPERUSER", "SUPERUSER")));
>
> in this kind of message where multiple copies appear that only differ in
> the keyword to use, to avoid creating four copies of essentially the
> same string.
>
> This applies in several places.
I did this in v2.
>> - 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.")));
>
> I think this one is a bit ambiguous; does "with" mean that roles that
> have that priv cannot be changed, or does it mean that you cannot meddle
> with that bit in particular? I think it'd be better to say
> "You must have %s privilege to change the %s attribute."
> or something like that.
Yeah, it's probably better to say "to alter roles with %s" to refer to
roles that presently have the attribute and "to change the %s attribute"
when referring to privileges for the attribute. I did this in v2, too.
I've also switched from errhint() to errdetail() as suggested by Tom.
--
Nathan Bossart
Amazon Web Services: https://aws.amazon.com
Attachments:
v2-0001-Improve-user.c-error-messages.patch (text/x-diff, 29.8K) download
From a6db1f28f0e7079193af4fca3fde27cf4780dbb7 Mon Sep 17 00:00:00 2001
From: Nathan Bossart <nathandbossart@gmail.com>
Date: Thu, 26 Jan 2023 11:05:13 -0800
Subject: [PATCH v2 1/1] Improve user.c error messages.
---
src/backend/commands/user.c | 171 ++++++++++++++++------
src/test/regress/expected/create_role.out | 77 ++++++----
src/test/regress/expected/dependency.out | 4 +
src/test/regress/expected/privileges.out | 23 ++-
4 files changed, 195 insertions(+), 80 deletions(-)
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 3a92e930c0..e2c80f5060 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("You must have %s privilege to 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("You must have %s privilege to 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("You must have %s privilege to 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("You must have %s privilege to 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("You must have %s privilege to 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("You must have %s privilege to alter roles with %s.",
+ "SUPERUSER", "SUPERUSER")));
+ if (!superuser() && dissuper)
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to alter role"),
+ errdetail("You must have %s privilege to change the %s attribute.",
+ "SUPERUSER", "SUPERUSER")));
/*
* Most changes to a role require that you both have CREATEROLE privileges
@@ -758,16 +776,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"),
+ errdetail("You must have %s privilege and %s on role \"%s\".",
+ "CREATEROLE", "ADMIN OPTION", rolename)));
}
else if (!superuser())
{
@@ -779,23 +794,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("You must have %s privilege to 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("You must have %s privilege to 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("You must have %s privilege to 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("You must have %s on role \"%s\" to add members.",
+ "ADMIN OPTION", rolename)));
/* Convert validuntil to internal form */
if (dvalidUntil)
@@ -838,7 +860,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"),
+ errdetail("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 +1022,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("You must have %s privilege to alter roles with %s.",
+ "SUPERUSER", "SUPERUSER")));
}
else
{
@@ -1008,7 +1033,9 @@ AlterRoleSet(AlterRoleSetStmt *stmt)
&& roleid != GetUserId())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("permission denied")));
+ errmsg("permission denied to alter role"),
+ errdetail("You must have %s privilege and %s on role \"%s\".",
+ "CREATROLE", "ADMIN OPTION", NameStr(roleform->rolname))));
}
ReleaseSysCache(roletuple);
@@ -1038,7 +1065,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("You must have %s privilege to alter settings globally.",
+ "SUPERUSER")));
}
AlterSetting(databaseid, roleid, stmt->setstmt);
@@ -1061,7 +1090,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("You must have %s privilege and %s on the target roles.",
+ "CREATEROLE", "ADMIN OPTION")));
/*
* Scan the pg_authid relation to find the Oid of the role(s) to be
@@ -1131,12 +1162,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("You must have %s privilege to 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("You must have %s privilege and %s on role \"%s\".",
+ "CREATEROLE", "ADMIN OPTION", NameStr(roleform->rolname))));
/* DROP hook for the role being removed */
InvokeObjectDropHook(AuthIdRelationId, roleid, 0);
@@ -1378,12 +1412,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("You must have %s privilege to rename roles with %s.",
+ "SUPERUSER", "SUPERUSER")));
}
else
{
@@ -1391,7 +1427,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("You must have %s privilege and %s on role \"%s\".",
+ "CREATEROLE", "ADMIN OPTION", NameStr(authform->rolname))));
}
/* OK, construct the modified tuple */
@@ -1554,7 +1592,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("You must have privileges of role \"%s\".",
+ GetUserNameFromId(roleid, false))));
}
/* Ok, do it */
@@ -1581,7 +1621,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("You must have privileges of role \"%s\".",
+ GetUserNameFromId(roleid, false))));
}
/* Must have privileges on the receiving side too */
@@ -1590,7 +1632,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("You must have privileges of role \"%s\".",
+ GetUserNameFromId(newrole, false))));
/* Ok, do it */
shdepReassignOwned(role_ids, newrole);
@@ -1738,7 +1782,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 cannot be granted back to your own grantor",
+ "ADMIN OPTION")));
plan_member_revoke(memlist, actions, memberid);
}
@@ -1763,7 +1808,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 cannot be granted back to your own grantor",
+ "ADMIN OPTION")));
ReleaseSysCacheList(memlist);
}
@@ -2081,9 +2127,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("You must have %s privilege to grant roles with %s.",
+ "SUPERUSER", "SUPERUSER")));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to revoke role \"%s\"",
+ GetUserNameFromId(roleid, false)),
+ errdetail("You must have %s privilege to revoke roles with %s.",
+ "SUPERUSER", "SUPERUSER")));
+ }
}
else
{
@@ -2091,10 +2150,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("You must have %s on role \"%s\".",
+ "ADMIN OPTION", GetUserNameFromId(roleid, false))));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to revoke role \"%s\"",
+ GetUserNameFromId(roleid, false)),
+ errdetail("You must have %s on role \"%s\".",
+ "ADMIN OPTION", GetUserNameFromId(roleid, false))));
+ }
}
}
@@ -2173,14 +2244,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("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)),
+ errdetail("The grantor must have %s on role \"%s\".",
+ "ADMIN OPTION", GetUserNameFromId(roleid, false))));
}
else
{
@@ -2188,7 +2263,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("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 9f431bd4f5..2a7d14dba9 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: 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
+DETAIL: 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
+DETAIL: 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
+DETAIL: 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
+DETAIL: 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
+DETAIL: You must have SUPERUSER privilege to 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: You must have REPLICATION privilege to 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: You must have CREATEDB privilege to 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: You must have BYPASSRLS privilege to 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: You must have SUPERUSER privilege to 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: You must have SUPERUSER privilege to 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: 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
+DETAIL: 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
+DETAIL: 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
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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
+DETAIL: 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
+DETAIL: 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..78ee4d7448 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: You must have privileges of role "regress_dep_user1".
DROP OWNED BY regress_dep_user0, regress_dep_user2;
ERROR: permission denied to drop objects
+DETAIL: 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
+DETAIL: 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
+DETAIL: 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..30c1d11a7c 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: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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;
--
2.25.1
^ permalink raw reply [nested|flat] 32+ messages in thread
* Re: improving user.c error messages
2023-01-26 00:22 improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 09:07 ` Re: improving user.c error messages Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-26 19:13 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
@ 2023-01-26 19:42 ` Robert Haas <robertmhaas@gmail.com>
2023-01-26 19:59 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
0 siblings, 1 reply; 32+ messages in thread
From: Robert Haas @ 2023-01-26 19:42 UTC (permalink / raw)
To: Nathan Bossart <nathandbossart@gmail.com>; +Cc: Alvaro Herrera <alvherre@alvh.no-ip.org>; pgsql-hackers; tgl@sss.pgh.pa.us
On Thu, Jan 26, 2023 at 2:14 PM Nathan Bossart <nathandbossart@gmail.com> wrote:
> Yeah, it's probably better to say "to alter roles with %s" to refer to
> roles that presently have the attribute and "to change the %s attribute"
> when referring to privileges for the attribute. I did this in v2, too.
>
> I've also switched from errhint() to errdetail() as suggested by Tom.
This seems fine to me in general but I'm not entirely sure about this part:
@@ -758,16 +776,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"),
+ errdetail("You must have %s privilege and %s on role \"%s\".",
+ "CREATEROLE", "ADMIN OPTION", rolename)));
}
else if (!superuser())
{
Basically my question is whether having one error message for all of
those cases is good enough, or whether we should be trying harder. I
don't mind if the conclusion is that it's OK as-is, and I'm not
entirely sure what would be better. But when I was working on this
code, all of those cases OR'd together feeding into a single error
message seemed a little sketchy to me, so I am wondering what others
think.
--
Robert Haas
EDB: http://www.enterprisedb.com
^ permalink raw reply [nested|flat] 32+ messages in thread
* Re: improving user.c error messages
2023-01-26 00:22 improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 09:07 ` Re: improving user.c error messages Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-26 19:13 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 19:42 ` Re: improving user.c error messages Robert Haas <robertmhaas@gmail.com>
@ 2023-01-26 19:59 ` Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 20:07 ` Re: improving user.c error messages Tom Lane <tgl@sss.pgh.pa.us>
0 siblings, 1 reply; 32+ messages in thread
From: Nathan Bossart @ 2023-01-26 19:59 UTC (permalink / raw)
To: Robert Haas <robertmhaas@gmail.com>; +Cc: Alvaro Herrera <alvherre@alvh.no-ip.org>; pgsql-hackers; tgl@sss.pgh.pa.us
On Thu, Jan 26, 2023 at 02:42:05PM -0500, Robert Haas wrote:
> @@ -758,16 +776,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"),
> + errdetail("You must have %s privilege and %s on role \"%s\".",
> + "CREATEROLE", "ADMIN OPTION", rolename)));
> }
> else if (!superuser())
> {
>
> Basically my question is whether having one error message for all of
> those cases is good enough, or whether we should be trying harder. I
> don't mind if the conclusion is that it's OK as-is, and I'm not
> entirely sure what would be better. But when I was working on this
> code, all of those cases OR'd together feeding into a single error
> message seemed a little sketchy to me, so I am wondering what others
> think.
I wondered the same thing, but I hesitated because I didn't want to change
too much in a patch for error messaging. I can give it a try.
--
Nathan Bossart
Amazon Web Services: https://aws.amazon.com
^ permalink raw reply [nested|flat] 32+ messages in thread
* Re: improving user.c error messages
2023-01-26 00:22 improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 09:07 ` Re: improving user.c error messages Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-26 19:13 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 19:42 ` Re: improving user.c error messages Robert Haas <robertmhaas@gmail.com>
2023-01-26 19:59 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
@ 2023-01-26 20:07 ` Tom Lane <tgl@sss.pgh.pa.us>
2023-01-26 22:02 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
0 siblings, 1 reply; 32+ messages in thread
From: Tom Lane @ 2023-01-26 20:07 UTC (permalink / raw)
To: Nathan Bossart <nathandbossart@gmail.com>; +Cc: Robert Haas <robertmhaas@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; pgsql-hackers
Nathan Bossart <nathandbossart@gmail.com> writes:
> On Thu, Jan 26, 2023 at 02:42:05PM -0500, Robert Haas wrote:
>> Basically my question is whether having one error message for all of
>> those cases is good enough, or whether we should be trying harder.
I think the password case needs to be kept separate, because the
conditions for it are different (specifically the exception that
you can alter your own password). Lumping the rest together
seems OK to me.
regards, tom lane
^ permalink raw reply [nested|flat] 32+ messages in thread
* Re: improving user.c error messages
2023-01-26 00:22 improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 09:07 ` Re: improving user.c error messages Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-26 19:13 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 19:42 ` Re: improving user.c error messages Robert Haas <robertmhaas@gmail.com>
2023-01-26 19:59 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 20:07 ` Re: improving user.c error messages Tom Lane <tgl@sss.pgh.pa.us>
@ 2023-01-26 22:02 ` Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 22:41 ` Re: improving user.c error messages Tom Lane <tgl@sss.pgh.pa.us>
0 siblings, 1 reply; 32+ messages in thread
From: Nathan Bossart @ 2023-01-26 22:02 UTC (permalink / raw)
To: Tom Lane <tgl@sss.pgh.pa.us>; +Cc: Robert Haas <robertmhaas@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; pgsql-hackers
On Thu, Jan 26, 2023 at 03:07:43PM -0500, Tom Lane wrote:
> Nathan Bossart <nathandbossart@gmail.com> writes:
>> On Thu, Jan 26, 2023 at 02:42:05PM -0500, Robert Haas wrote:
>>> Basically my question is whether having one error message for all of
>>> those cases is good enough, or whether we should be trying harder.
>
> I think the password case needs to be kept separate, because the
> conditions for it are different (specifically the exception that
> you can alter your own password). Lumping the rest together
> seems OK to me.
Hm. In v2, the error message for both cases is the same:
ERROR: permission denied to alter role
DETAIL: You must have CREATEROLE privilege and ADMIN OPTION on role "regress_priv_user2".
We could add "to change its attributes" and "to change its password" to
separate the two, but I'm not sure that adds much. ISTM the current error
message for ALTER ROLE PASSWORD implies that you can change your own
password, and that's lost with my patch. Perhaps we should add an
errhint() with that information instead. WDYT?
--
Nathan Bossart
Amazon Web Services: https://aws.amazon.com
^ permalink raw reply [nested|flat] 32+ messages in thread
* Re: improving user.c error messages
2023-01-26 00:22 improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 09:07 ` Re: improving user.c error messages Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-26 19:13 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 19:42 ` Re: improving user.c error messages Robert Haas <robertmhaas@gmail.com>
2023-01-26 19:59 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 20:07 ` Re: improving user.c error messages Tom Lane <tgl@sss.pgh.pa.us>
2023-01-26 22:02 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
@ 2023-01-26 22:41 ` Tom Lane <tgl@sss.pgh.pa.us>
2023-01-27 00:09 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
0 siblings, 1 reply; 32+ messages in thread
From: Tom Lane @ 2023-01-26 22:41 UTC (permalink / raw)
To: Nathan Bossart <nathandbossart@gmail.com>; +Cc: Robert Haas <robertmhaas@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; pgsql-hackers
Nathan Bossart <nathandbossart@gmail.com> writes:
> On Thu, Jan 26, 2023 at 03:07:43PM -0500, Tom Lane wrote:
>> I think the password case needs to be kept separate, because the
>> conditions for it are different (specifically the exception that
>> you can alter your own password). Lumping the rest together
>> seems OK to me.
> Hm. In v2, the error message for both cases is the same:
> ERROR: permission denied to alter role
> DETAIL: You must have CREATEROLE privilege and ADMIN OPTION on role "regress_priv_user2".
> We could add "to change its attributes" and "to change its password" to
> separate the two, but I'm not sure that adds much. ISTM the current error
> message for ALTER ROLE PASSWORD implies that you can change your own
> password, and that's lost with my patch. Perhaps we should add an
> errhint() with that information instead. WDYT?
Well, it's not a hint. I think the above is fine for non-password
cases, but for passwords maybe
ERROR: permission denied to alter role password
DETAIL: To change another role's password, you must have CREATEROLE privilege and ADMIN OPTION on role "%s".
regards, tom lane
^ permalink raw reply [nested|flat] 32+ messages in thread
* Re: improving user.c error messages
2023-01-26 00:22 improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 09:07 ` Re: improving user.c error messages Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-26 19:13 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 19:42 ` Re: improving user.c error messages Robert Haas <robertmhaas@gmail.com>
2023-01-26 19:59 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 20:07 ` Re: improving user.c error messages Tom Lane <tgl@sss.pgh.pa.us>
2023-01-26 22:02 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 22:41 ` Re: improving user.c error messages Tom Lane <tgl@sss.pgh.pa.us>
@ 2023-01-27 00:09 ` Nathan Bossart <nathandbossart@gmail.com>
2023-01-27 18:31 ` Re: improving user.c error messages Alvaro Herrera <alvherre@alvh.no-ip.org>
0 siblings, 1 reply; 32+ messages in thread
From: Nathan Bossart @ 2023-01-27 00:09 UTC (permalink / raw)
To: Tom Lane <tgl@sss.pgh.pa.us>; +Cc: Robert Haas <robertmhaas@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; pgsql-hackers
On Thu, Jan 26, 2023 at 05:41:32PM -0500, Tom Lane wrote:
> Well, it's not a hint. I think the above is fine for non-password
> cases, but for passwords maybe
>
> ERROR: permission denied to alter role password
> DETAIL: To change another role's password, you must have CREATEROLE privilege and ADMIN OPTION on role "%s".
Okay. I used this phrasing in v3.
--
Nathan Bossart
Amazon Web Services: https://aws.amazon.com
Attachments:
v3-0001-Improve-user.c-error-messages.patch (text/x-diff, 29.8K) download
From 02938746f72d0545d6c5cc3d1e9c6dc30e6e4175 Mon Sep 17 00:00:00 2001
From: Nathan Bossart <nathandbossart@gmail.com>
Date: Thu, 26 Jan 2023 11:05:13 -0800
Subject: [PATCH v3 1/1] Improve user.c error messages.
---
src/backend/commands/user.c | 166 ++++++++++++++++------
src/test/regress/expected/create_role.out | 77 ++++++----
src/test/regress/expected/dependency.out | 4 +
src/test/regress/expected/privileges.out | 23 +--
4 files changed, 196 insertions(+), 74 deletions(-)
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 3a92e930c0..ad22a89298 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("You must have %s privilege to 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("You must have %s privilege to 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("You must have %s privilege to 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("You must have %s privilege to 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("You must have %s privilege to 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("You must have %s privilege to alter roles with %s.",
+ "SUPERUSER", "SUPERUSER")));
+ if (!superuser() && dissuper)
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to alter role"),
+ errdetail("You must have %s privilege to 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("You must have %s privilege and %s on role \"%s\".",
+ "CREATEROLE", "ADMIN OPTION", 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, you must have %s privilege and %s on the role.",
+ "CREATEROLE", "ADMIN OPTION")));
}
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("You must have %s privilege to 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("You must have %s privilege to 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("You must have %s privilege to 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("You must have %s on role \"%s\" to add members.",
+ "ADMIN OPTION", rolename)));
/* Convert validuntil to internal form */
if (dvalidUntil)
@@ -838,7 +867,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"),
+ errdetail("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 +1029,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("You must have %s privilege to alter roles with %s.",
+ "SUPERUSER", "SUPERUSER")));
}
else
{
@@ -1008,7 +1040,9 @@ AlterRoleSet(AlterRoleSetStmt *stmt)
&& roleid != GetUserId())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("permission denied")));
+ errmsg("permission denied to alter role"),
+ errdetail("You must have %s privilege and %s on role \"%s\".",
+ "CREATROLE", "ADMIN OPTION", NameStr(roleform->rolname))));
}
ReleaseSysCache(roletuple);
@@ -1038,7 +1072,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("You must have %s privilege to alter settings globally.",
+ "SUPERUSER")));
}
AlterSetting(databaseid, roleid, stmt->setstmt);
@@ -1061,7 +1097,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("You must have %s privilege and %s on the target roles.",
+ "CREATEROLE", "ADMIN OPTION")));
/*
* Scan the pg_authid relation to find the Oid of the role(s) to be
@@ -1131,12 +1169,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("You must have %s privilege to 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("You must have %s privilege and %s on role \"%s\".",
+ "CREATEROLE", "ADMIN OPTION", NameStr(roleform->rolname))));
/* DROP hook for the role being removed */
InvokeObjectDropHook(AuthIdRelationId, roleid, 0);
@@ -1378,12 +1419,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("You must have %s privilege to rename roles with %s.",
+ "SUPERUSER", "SUPERUSER")));
}
else
{
@@ -1391,7 +1434,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("You must have %s privilege and %s on role \"%s\".",
+ "CREATEROLE", "ADMIN OPTION", NameStr(authform->rolname))));
}
/* OK, construct the modified tuple */
@@ -1554,7 +1599,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("You must have privileges of role \"%s\".",
+ GetUserNameFromId(roleid, false))));
}
/* Ok, do it */
@@ -1581,7 +1628,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("You must have privileges of role \"%s\".",
+ GetUserNameFromId(roleid, false))));
}
/* Must have privileges on the receiving side too */
@@ -1590,7 +1639,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("You must have privileges of role \"%s\".",
+ GetUserNameFromId(newrole, false))));
/* Ok, do it */
shdepReassignOwned(role_ids, newrole);
@@ -1738,7 +1789,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 cannot be granted back to your own grantor",
+ "ADMIN OPTION")));
plan_member_revoke(memlist, actions, memberid);
}
@@ -1763,7 +1815,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 cannot be granted back to your own grantor",
+ "ADMIN OPTION")));
ReleaseSysCacheList(memlist);
}
@@ -2081,9 +2134,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("You must have %s privilege to grant roles with %s.",
+ "SUPERUSER", "SUPERUSER")));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to revoke role \"%s\"",
+ GetUserNameFromId(roleid, false)),
+ errdetail("You must have %s privilege to revoke roles with %s.",
+ "SUPERUSER", "SUPERUSER")));
+ }
}
else
{
@@ -2091,10 +2157,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("You must have %s on role \"%s\".",
+ "ADMIN OPTION", GetUserNameFromId(roleid, false))));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to revoke role \"%s\"",
+ GetUserNameFromId(roleid, false)),
+ errdetail("You must have %s on role \"%s\".",
+ "ADMIN OPTION", GetUserNameFromId(roleid, false))));
+ }
}
}
@@ -2173,14 +2251,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("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)),
+ errdetail("The grantor must have %s on role \"%s\".",
+ "ADMIN OPTION", GetUserNameFromId(roleid, false))));
}
else
{
@@ -2188,7 +2270,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("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 9f431bd4f5..2a7d14dba9 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: 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
+DETAIL: 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
+DETAIL: 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
+DETAIL: 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
+DETAIL: 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
+DETAIL: You must have SUPERUSER privilege to 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: You must have REPLICATION privilege to 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: You must have CREATEDB privilege to 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: You must have BYPASSRLS privilege to 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: You must have SUPERUSER privilege to 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: You must have SUPERUSER privilege to 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: 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
+DETAIL: 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
+DETAIL: 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
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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
+DETAIL: 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
+DETAIL: 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..78ee4d7448 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: You must have privileges of role "regress_dep_user1".
DROP OWNED BY regress_dep_user0, regress_dep_user2;
ERROR: permission denied to drop objects
+DETAIL: 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
+DETAIL: 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
+DETAIL: 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..6794645e56 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, you must have CREATEROLE privilege and 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 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: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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;
--
2.25.1
^ permalink raw reply [nested|flat] 32+ messages in thread
* Re: improving user.c error messages
2023-01-26 00:22 improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 09:07 ` Re: improving user.c error messages Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-26 19:13 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 19:42 ` Re: improving user.c error messages Robert Haas <robertmhaas@gmail.com>
2023-01-26 19:59 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 20:07 ` Re: improving user.c error messages Tom Lane <tgl@sss.pgh.pa.us>
2023-01-26 22:02 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 22:41 ` Re: improving user.c error messages Tom Lane <tgl@sss.pgh.pa.us>
2023-01-27 00:09 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
@ 2023-01-27 18:31 ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-27 23:15 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
0 siblings, 1 reply; 32+ messages in thread
From: Alvaro Herrera @ 2023-01-27 18:31 UTC (permalink / raw)
To: Nathan Bossart <nathandbossart@gmail.com>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers
While we're here,
On 2023-Jan-26, Nathan Bossart wrote:
> @@ -838,7 +867,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"),
> + errdetail("The bootstrap user must be superuser.")));
I think this one isn't using the right errcode; this is not a case of
insufficient privileges. There's no priv you can acquire that lets you
do it. So I'd change it to unsupported operation.
I was confused a bit by this one:
> /* 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, you must have %s privilege and %s on the role.",
> + "CREATEROLE", "ADMIN OPTION")));
> }
In no other message we say what operation is being attempted in the
errdetail; all the others start with "You must have" and that's it.
However, looking closer I think this one being different is okay,
because the errmsg() you're using is vague, and I think the error report
would be confusing if you were to remove the "To change another role's
password" bit.
The patch looks good to me.
--
Álvaro Herrera Breisgau, Deutschland — https://www.EnterpriseDB.com/
^ permalink raw reply [nested|flat] 32+ messages in thread
* Re: improving user.c error messages
2023-01-26 00:22 improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 09:07 ` Re: improving user.c error messages Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-26 19:13 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 19:42 ` Re: improving user.c error messages Robert Haas <robertmhaas@gmail.com>
2023-01-26 19:59 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 20:07 ` Re: improving user.c error messages Tom Lane <tgl@sss.pgh.pa.us>
2023-01-26 22:02 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 22:41 ` Re: improving user.c error messages Tom Lane <tgl@sss.pgh.pa.us>
2023-01-27 00:09 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-27 18:31 ` Re: improving user.c error messages Alvaro Herrera <alvherre@alvh.no-ip.org>
@ 2023-01-27 23:15 ` Nathan Bossart <nathandbossart@gmail.com>
2023-02-07 20:10 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
0 siblings, 1 reply; 32+ messages in thread
From: Nathan Bossart @ 2023-01-27 23:15 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers
On Fri, Jan 27, 2023 at 07:31:19PM +0100, Alvaro Herrera wrote:
> On 2023-Jan-26, Nathan Bossart wrote:
>> ereport(ERROR,
>> (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
>> - errmsg("permission denied: bootstrap user must be superuser")));
>> + errmsg("permission denied to alter role"),
>> + errdetail("The bootstrap user must be superuser.")));
>
> I think this one isn't using the right errcode; this is not a case of
> insufficient privileges. There's no priv you can acquire that lets you
> do it. So I'd change it to unsupported operation.
І fixed this in v4. I've also attached a second patch in which I've
adjusted the messages that Peter mentioned upthread.
One thing that feels a bit odd is how some of the DETAILs mention the
operation being attempted while others do not. For example, we have
ERROR: permission denied to drop role
DETAIL: You must have SUPERUSER privilege to drop roles with SUPERUSER.
In this case, the DETAIL explains the action that is prohibited. In other
cases, we have something like
ERROR: permission denied to alter role
DETAIL: You must have CREATEROLE privilege and ADMIN OPTION on role "myrole".
which does not. I think this is okay because adding "to alter the role" to
the end of the DETAIL seems kind of awkward. But in other cases, such as
ERROR: permission denied to use replication slots
DETAIL: You must have REPLICATION privilege.
adding the operation to the end seems less awkward (i.e., "You must have
REPLICATION privilege to use replication slots."). I don't think there's
any information lost by omitting the action in the DETAIL, so perhaps this
is just a stylistic choice. I think I'm inclined to add the action to the
DETAIL whenever it doesn't make the message lengthy and awkward, and leave
it out otherwise. Thoughts?
--
Nathan Bossart
Amazon Web Services: https://aws.amazon.com
Attachments: download all (2 files, .tar.gz)
v4-0001-Improve-user.c-error-messages.patch (text/x-diff, 29.8K) download
From d369e35e6a44cb9708a08a1f32d1755e04f04de1 Mon Sep 17 00:00:00 2001
From: Nathan Bossart <nathandbossart@gmail.com>
Date: Thu, 26 Jan 2023 11:05:13 -0800
Subject: [PATCH v4 1/2] Improve user.c error messages.
---
src/backend/commands/user.c | 168 ++++++++++++++++------
src/test/regress/expected/create_role.out | 77 ++++++----
src/test/regress/expected/dependency.out | 4 +
src/test/regress/expected/privileges.out | 23 +--
4 files changed, 197 insertions(+), 75 deletions(-)
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 3a92e930c0..26b533f1be 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("You must have %s privilege to 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("You must have %s privilege to 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("You must have %s privilege to 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("You must have %s privilege to 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("You must have %s privilege to 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("You must have %s privilege to alter roles with %s.",
+ "SUPERUSER", "SUPERUSER")));
+ if (!superuser() && dissuper)
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to alter role"),
+ errdetail("You must have %s privilege to 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("You must have %s privilege and %s on role \"%s\".",
+ "CREATEROLE", "ADMIN OPTION", 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, you must have %s privilege and %s on the role.",
+ "CREATEROLE", "ADMIN OPTION")));
}
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("You must have %s privilege to 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("You must have %s privilege to 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("You must have %s privilege to 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("You must have %s on role \"%s\" to add members.",
+ "ADMIN OPTION", rolename)));
/* Convert validuntil to internal form */
if (dvalidUntil)
@@ -837,8 +866,9 @@ 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 be superuser.")));
new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(should_be_super);
new_record_repl[Anum_pg_authid_rolsuper - 1] = true;
@@ -999,7 +1029,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("You must have %s privilege to alter roles with %s.",
+ "SUPERUSER", "SUPERUSER")));
}
else
{
@@ -1008,7 +1040,9 @@ AlterRoleSet(AlterRoleSetStmt *stmt)
&& roleid != GetUserId())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("permission denied")));
+ errmsg("permission denied to alter role"),
+ errdetail("You must have %s privilege and %s on role \"%s\".",
+ "CREATROLE", "ADMIN OPTION", NameStr(roleform->rolname))));
}
ReleaseSysCache(roletuple);
@@ -1038,7 +1072,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("You must have %s privilege to alter settings globally.",
+ "SUPERUSER")));
}
AlterSetting(databaseid, roleid, stmt->setstmt);
@@ -1061,7 +1097,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("You must have %s privilege and %s on the target roles.",
+ "CREATEROLE", "ADMIN OPTION")));
/*
* Scan the pg_authid relation to find the Oid of the role(s) to be
@@ -1131,12 +1169,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("You must have %s privilege to 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("You must have %s privilege and %s on role \"%s\".",
+ "CREATEROLE", "ADMIN OPTION", NameStr(roleform->rolname))));
/* DROP hook for the role being removed */
InvokeObjectDropHook(AuthIdRelationId, roleid, 0);
@@ -1378,12 +1419,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("You must have %s privilege to rename roles with %s.",
+ "SUPERUSER", "SUPERUSER")));
}
else
{
@@ -1391,7 +1434,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("You must have %s privilege and %s on role \"%s\".",
+ "CREATEROLE", "ADMIN OPTION", NameStr(authform->rolname))));
}
/* OK, construct the modified tuple */
@@ -1554,7 +1599,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("You must have privileges of role \"%s\".",
+ GetUserNameFromId(roleid, false))));
}
/* Ok, do it */
@@ -1581,7 +1628,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("You must have privileges of role \"%s\".",
+ GetUserNameFromId(roleid, false))));
}
/* Must have privileges on the receiving side too */
@@ -1590,7 +1639,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("You must have privileges of role \"%s\".",
+ GetUserNameFromId(newrole, false))));
/* Ok, do it */
shdepReassignOwned(role_ids, newrole);
@@ -1738,7 +1789,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 cannot be granted back to your own grantor",
+ "ADMIN OPTION")));
plan_member_revoke(memlist, actions, memberid);
}
@@ -1763,7 +1815,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 cannot be granted back to your own grantor",
+ "ADMIN OPTION")));
ReleaseSysCacheList(memlist);
}
@@ -2081,9 +2134,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("You must have %s privilege to grant roles with %s.",
+ "SUPERUSER", "SUPERUSER")));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to revoke role \"%s\"",
+ GetUserNameFromId(roleid, false)),
+ errdetail("You must have %s privilege to revoke roles with %s.",
+ "SUPERUSER", "SUPERUSER")));
+ }
}
else
{
@@ -2091,10 +2157,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("You must have %s on role \"%s\".",
+ "ADMIN OPTION", GetUserNameFromId(roleid, false))));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to revoke role \"%s\"",
+ GetUserNameFromId(roleid, false)),
+ errdetail("You must have %s on role \"%s\".",
+ "ADMIN OPTION", GetUserNameFromId(roleid, false))));
+ }
}
}
@@ -2173,14 +2251,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("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)),
+ errdetail("The grantor must have %s on role \"%s\".",
+ "ADMIN OPTION", GetUserNameFromId(roleid, false))));
}
else
{
@@ -2188,7 +2270,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("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 9f431bd4f5..2a7d14dba9 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: 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
+DETAIL: 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
+DETAIL: 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
+DETAIL: 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
+DETAIL: 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
+DETAIL: You must have SUPERUSER privilege to 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: You must have REPLICATION privilege to 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: You must have CREATEDB privilege to 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: You must have BYPASSRLS privilege to 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: You must have SUPERUSER privilege to 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: You must have SUPERUSER privilege to 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: 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
+DETAIL: 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
+DETAIL: 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
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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
+DETAIL: 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
+DETAIL: 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..78ee4d7448 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: You must have privileges of role "regress_dep_user1".
DROP OWNED BY regress_dep_user0, regress_dep_user2;
ERROR: permission denied to drop objects
+DETAIL: 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
+DETAIL: 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
+DETAIL: 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..6794645e56 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, you must have CREATEROLE privilege and 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 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: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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;
--
2.25.1
v4-0002-Improve-more-insufficient-privileges-error-messag.patch (text/x-diff, 19.0K) download
From 61248faddb4ec9d1bcf4e4eca3c1315b718150f0 Mon Sep 17 00:00:00 2001
From: Nathan Bossart <nathandbossart@gmail.com>
Date: Fri, 27 Jan 2023 14:52:57 -0800
Subject: [PATCH v4 2/2] Improve more insufficient-privileges error messages.
---
contrib/file_fdw/expected/file_fdw.out | 3 ++-
contrib/file_fdw/file_fdw.c | 8 ++++++--
contrib/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/replication/slot.c | 5 +++--
src/backend/storage/ipc/procarray.c | 4 +++-
src/backend/storage/ipc/signalfuncs.c | 16 ++++++++++++----
src/backend/tcop/utility.c | 4 +++-
src/backend/utils/init/miscinit.c | 4 ++++
src/backend/utils/init/postinit.c | 8 +++++---
src/backend/utils/misc/guc.c | 15 +++++++++------
.../dummy_seclabel/expected/dummy_seclabel.out | 3 ++-
.../modules/unsafe_tests/expected/rolenames.out | 3 ++-
src/test/regress/expected/create_role.out | 3 ++-
16 files changed, 84 insertions(+), 36 deletions(-)
diff --git a/contrib/file_fdw/expected/file_fdw.out b/contrib/file_fdw/expected/file_fdw.out
index 36d76ba26c..7e57ad2990 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 specify the "filename" option of a file_fdw foreign table
+DETAIL: You must have privileges of the "pg_read_server_files" role.
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 8ccc167548..696d9e5718 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 specify the \"filename\" option of a file_fdw foreign table"),
+ errdetail("You must have privileges of the \"%s\" role.",
+ "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 specify the \"program\" option of a file_fdw foreign table"),
+ errdetail("You must have privileges of the \"%s\" role.",
+ "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..d102ff066b 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: You must have REPLICATION privilege.
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: You must have REPLICATION privilege.
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: You must have REPLICATION privilege.
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: You must have REPLICATION privilege.
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..de89df190f 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("You must have privileges of the \"%s\" role.",
+ "pg_write_server_files")));
CommitTransactionCommand();
/*
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 25c50d66fd..2a39ce0567 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("You must have %s privilege.",
+ "SUPERUSER")));
}
else
{
if (!has_createrole_privilege(roleid))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("must have CREATEROLE privilege")));
+ errmsg("permission denied"),
+ errdetail("You must have %s privilege.",
+ "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("You must have %s on role \"%s\".",
+ "ADMIN OPTION",
+ GetUserNameFromId(address.objectId,
+ true))));
}
break;
case OBJECT_TSPARSER:
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index e34f583ea7..e557eb17b6 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("You must have privileges of the \"%s\" role.",
+ "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("You must have privileges of the \"%s\" role.",
+ "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("You must have privileges of the \"%s\" role.",
+ "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/replication/slot.c b/src/backend/replication/slot.c
index f286918f69..e4df06a05d 100644
--- a/src/backend/replication/slot.c
+++ b/src/backend/replication/slot.c
@@ -1140,10 +1140,11 @@ 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("You must have %s privilege.", "REPLICATION")));
}
/*
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 4340bf9641..95967f8dfa 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -3860,7 +3860,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("You must have privileges of the role whose process is being terminated or have privileges of the \"%s\" role.",
+ "pg_signal_backend")));
}
}
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index bc93ab5b52..7e0f0aef73 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("You must have %s privilege to 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("You must have privileges of the role whose query is being canceled or have privileges of the \"%s\" role.",
+ "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("You must have %s privilege to 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("You must have privileges of the role whose process is being terminated or have privileges of the \"%s\" role.",
+ "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..fe77f260c5 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -950,7 +950,9 @@ 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 do CHECKPOINT"),
+ errdetail("You must have privileges of the \"%s\" role.",
+ "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 0cdc1e11a3..3a98fafaeb 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -701,6 +701,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 2f07ca7a0e..808c24b480 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -945,7 +945,8 @@ InitPostgres(const char *in_dbname, Oid dboid,
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 \"%s\"",
+ "pg_use_reserved_connections")));
}
/* Check replication permissions needed for walsender processes. */
@@ -953,10 +954,11 @@ 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 walsender"),
+ errdetail("You must have %s privilege.", "REPLICATION")));
}
/*
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 978b385568..07d97ad75e 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -4190,8 +4190,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("You must have privileges of the \"%s\" role.",
+ "pg_read_all_settings")));
switch (record->vartype)
{
@@ -4236,8 +4237,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("You must have privileges of the \"%s\" role.",
+ "pg_read_all_settings")));
switch (record->vartype)
{
@@ -5242,8 +5244,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("You must have privileges of the \"%s\" role.",
+ "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..abb0b2fa0f 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: You must have CREATEROLE privilege.
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..6f1fc210a7 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: You must have privileges of the "pg_read_all_settings" role.
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 2a7d14dba9..d9620fb8d7 100644
--- a/src/test/regress/expected/create_role.out
+++ b/src/test/regress/expected/create_role.out
@@ -106,7 +106,8 @@ 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: You 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
DETAIL: You must have CREATEROLE privilege and ADMIN OPTION on role "regress_role_normal".
--
2.25.1
^ permalink raw reply [nested|flat] 32+ messages in thread
* Re: improving user.c error messages
2023-01-26 00:22 improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 09:07 ` Re: improving user.c error messages Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-26 19:13 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 19:42 ` Re: improving user.c error messages Robert Haas <robertmhaas@gmail.com>
2023-01-26 19:59 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 20:07 ` Re: improving user.c error messages Tom Lane <tgl@sss.pgh.pa.us>
2023-01-26 22:02 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 22:41 ` Re: improving user.c error messages Tom Lane <tgl@sss.pgh.pa.us>
2023-01-27 00:09 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-27 18:31 ` Re: improving user.c error messages Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-27 23:15 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
@ 2023-02-07 20:10 ` Nathan Bossart <nathandbossart@gmail.com>
2023-02-20 07:54 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
0 siblings, 1 reply; 32+ messages in thread
From: Nathan Bossart @ 2023-02-07 20:10 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers
On Fri, Jan 27, 2023 at 03:15:07PM -0800, Nathan Bossart wrote:
> One thing that feels a bit odd is how some of the DETAILs mention the
> operation being attempted while others do not. For example, we have
>
> ERROR: permission denied to drop role
> DETAIL: You must have SUPERUSER privilege to drop roles with SUPERUSER.
>
> In this case, the DETAIL explains the action that is prohibited. In other
> cases, we have something like
>
> ERROR: permission denied to alter role
> DETAIL: You must have CREATEROLE privilege and ADMIN OPTION on role "myrole".
>
> which does not. I think this is okay because adding "to alter the role" to
> the end of the DETAIL seems kind of awkward. But in other cases, such as
>
> ERROR: permission denied to use replication slots
> DETAIL: You must have REPLICATION privilege.
>
> adding the operation to the end seems less awkward (i.e., "You must have
> REPLICATION privilege to use replication slots."). I don't think there's
> any information lost by omitting the action in the DETAIL, so perhaps this
> is just a stylistic choice. I think I'm inclined to add the action to the
> DETAIL whenever it doesn't make the message lengthy and awkward, and leave
> it out otherwise. Thoughts?
Here is a new patch set with this change and some other light editing.
--
Nathan Bossart
Amazon Web Services: https://aws.amazon.com
Attachments: download all (2 files, .tar.gz)
v5-0001-Improve-user.c-error-messages.patch (text/x-diff, 29.9K) download
From 196c52841d19d507d695122ac5e9ecab45a908f2 Mon Sep 17 00:00:00 2001
From: Nathan Bossart <nathandbossart@gmail.com>
Date: Thu, 26 Jan 2023 11:05:13 -0800
Subject: [PATCH v5 1/2] Improve user.c error messages.
---
src/backend/commands/user.c | 169 ++++++++++++++++------
src/test/regress/expected/create_role.out | 77 ++++++----
src/test/regress/expected/dependency.out | 4 +
src/test/regress/expected/privileges.out | 23 ++-
4 files changed, 198 insertions(+), 75 deletions(-)
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 3a92e930c0..3d0b8b9ea6 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("You must have %s privilege to 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("You must have %s privilege to 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("You must have %s privilege to 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("You must have %s privilege to 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("You must have %s privilege to 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("You must have %s privilege to alter roles with %s.",
+ "SUPERUSER", "SUPERUSER")));
+ if (!superuser() && dissuper)
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to alter role"),
+ errdetail("You must have %s privilege to 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("You must have %s privilege and %s on role \"%s\".",
+ "CREATEROLE", "ADMIN OPTION", 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, you must have %s privilege and %s on the role.",
+ "CREATEROLE", "ADMIN OPTION")));
}
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("You must have %s privilege to 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("You must have %s privilege to 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("You must have %s privilege to 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("You must have %s on role \"%s\" to add members.",
+ "ADMIN OPTION", 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 %s privilege.",
+ "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("You must have %s privilege to 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("You must have %s privilege and %s on role \"%s\".",
+ "CREATROLE", "ADMIN OPTION", 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("You must have %s privilege to 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("You must have %s privilege and %s on the target roles.",
+ "CREATEROLE", "ADMIN OPTION")));
/*
* 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("You must have %s privilege to 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("You must have %s privilege and %s on role \"%s\".",
+ "CREATEROLE", "ADMIN OPTION", 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("You must have %s privilege to 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("You must have %s privilege and %s on role \"%s\".",
+ "CREATEROLE", "ADMIN OPTION", 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("You must have privileges of role \"%s\".",
+ 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("You must have privileges of role \"%s\".",
+ 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("You must have privileges of role \"%s\".",
+ 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 cannot be granted back to your own grantor",
+ "ADMIN OPTION")));
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 cannot be granted back to your own grantor",
+ "ADMIN OPTION")));
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("You must have %s privilege to grant roles with %s.",
+ "SUPERUSER", "SUPERUSER")));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to revoke role \"%s\"",
+ GetUserNameFromId(roleid, false)),
+ errdetail("You must have %s privilege to 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("You must have %s on role \"%s\".",
+ "ADMIN OPTION", GetUserNameFromId(roleid, false))));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to revoke role \"%s\"",
+ GetUserNameFromId(roleid, false)),
+ errdetail("You must have %s on role \"%s\".",
+ "ADMIN OPTION", 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("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)),
+ errdetail("The grantor must have %s on role \"%s\".",
+ "ADMIN OPTION", 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("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 9f431bd4f5..2a7d14dba9 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: 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
+DETAIL: 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
+DETAIL: 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
+DETAIL: 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
+DETAIL: 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
+DETAIL: You must have SUPERUSER privilege to 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: You must have REPLICATION privilege to 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: You must have CREATEDB privilege to 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: You must have BYPASSRLS privilege to 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: You must have SUPERUSER privilege to 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: You must have SUPERUSER privilege to 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: 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
+DETAIL: 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
+DETAIL: 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
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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
+DETAIL: 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
+DETAIL: 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..78ee4d7448 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: You must have privileges of role "regress_dep_user1".
DROP OWNED BY regress_dep_user0, regress_dep_user2;
ERROR: permission denied to drop objects
+DETAIL: 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
+DETAIL: 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
+DETAIL: 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..6794645e56 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, you must have CREATEROLE privilege and 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 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: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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"
+DETAIL: 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;
--
2.25.1
v5-0002-Improve-more-insufficient-privileges-error-messag.patch (text/x-diff, 19.3K) download
From 7ea85df2ab6a5f9224d0cbf1b11f9323e4ce7de1 Mon Sep 17 00:00:00 2001
From: Nathan Bossart <nathandbossart@gmail.com>
Date: Fri, 27 Jan 2023 14:52:57 -0800
Subject: [PATCH v5 2/2] Improve more insufficient-privileges error messages.
---
contrib/file_fdw/expected/file_fdw.out | 3 ++-
contrib/file_fdw/file_fdw.c | 8 ++++++--
contrib/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/replication/slot.c | 5 +++--
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 | 8 +++++---
src/backend/utils/misc/guc.c | 15 +++++++++------
.../dummy_seclabel/expected/dummy_seclabel.out | 3 ++-
.../modules/unsafe_tests/expected/rolenames.out | 3 ++-
src/test/regress/expected/create_role.out | 3 ++-
16 files changed, 85 insertions(+), 36 deletions(-)
diff --git a/contrib/file_fdw/expected/file_fdw.out b/contrib/file_fdw/expected/file_fdw.out
index 36d76ba26c..f67a33dace 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: You must have privileges of the "pg_read_server_files" role to 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 8ccc167548..b00fd0a5c7 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("You must have privileges of the \"%s\" role to 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("You must have privileges of the \"%s\" role to 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..5a0bdab326 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: You must have REPLICATION privilege to 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: You must have REPLICATION privilege to 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: You must have REPLICATION privilege to 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: You must have REPLICATION privilege to 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..de89df190f 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("You must have privileges of the \"%s\" role.",
+ "pg_write_server_files")));
CommitTransactionCommand();
/*
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 25c50d66fd..2a39ce0567 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("You must have %s privilege.",
+ "SUPERUSER")));
}
else
{
if (!has_createrole_privilege(roleid))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("must have CREATEROLE privilege")));
+ errmsg("permission denied"),
+ errdetail("You must have %s privilege.",
+ "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("You must have %s on role \"%s\".",
+ "ADMIN OPTION",
+ GetUserNameFromId(address.objectId,
+ true))));
}
break;
case OBJECT_TSPARSER:
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index e34f583ea7..e557eb17b6 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("You must have privileges of the \"%s\" role.",
+ "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("You must have privileges of the \"%s\" role.",
+ "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("You must have privileges of the \"%s\" role.",
+ "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/replication/slot.c b/src/backend/replication/slot.c
index f286918f69..699005bd04 100644
--- a/src/backend/replication/slot.c
+++ b/src/backend/replication/slot.c
@@ -1140,10 +1140,11 @@ 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("You must have %s privilege to use replication slots.", "REPLICATION")));
}
/*
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index a7071b2fce..4086938d28 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -3860,7 +3860,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("You must have privileges of the role whose process is being terminated or have privileges of the \"%s\" role.",
+ "pg_signal_backend")));
}
}
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index bc93ab5b52..7e0f0aef73 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("You must have %s privilege to 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("You must have privileges of the role whose query is being canceled or have privileges of the \"%s\" role.",
+ "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("You must have %s privilege to 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("You must have privileges of the role whose process is being terminated or have privileges of the \"%s\" role.",
+ "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..0ff9aad106 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("You must have privileges of the \"%s\" role to 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 59532bbd80..b0cfbc742b 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -701,6 +701,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 2f07ca7a0e..269712215c 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -945,7 +945,8 @@ InitPostgres(const char *in_dbname, Oid dboid,
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 \"%s\"",
+ "pg_use_reserved_connections")));
}
/* Check replication permissions needed for walsender processes. */
@@ -953,10 +954,11 @@ 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("You must have %s privilege to start a WAL sender process.", "REPLICATION")));
}
/*
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index bc83f3577d..d1aa464571 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -4208,8 +4208,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("You must have privileges of the \"%s\" role to examine this parameter.",
+ "pg_read_all_settings")));
switch (record->vartype)
{
@@ -4254,8 +4255,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("You must have privileges of the \"%s\" role to examine this parameter.",
+ "pg_read_all_settings")));
switch (record->vartype)
{
@@ -5260,8 +5262,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("You must have privileges of the \"%s\" role to 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..abb0b2fa0f 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: You must have CREATEROLE privilege.
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..98029cab01 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: You must have privileges of the "pg_read_all_settings" role to 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 2a7d14dba9..d9620fb8d7 100644
--- a/src/test/regress/expected/create_role.out
+++ b/src/test/regress/expected/create_role.out
@@ -106,7 +106,8 @@ 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: You 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
DETAIL: You must have CREATEROLE privilege and ADMIN OPTION on role "regress_role_normal".
--
2.25.1
^ permalink raw reply [nested|flat] 32+ messages in thread
* Re: improving user.c error messages
2023-01-26 00:22 improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 09:07 ` Re: improving user.c error messages Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-26 19:13 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 19:42 ` Re: improving user.c error messages Robert Haas <robertmhaas@gmail.com>
2023-01-26 19:59 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 20:07 ` Re: improving user.c error messages Tom Lane <tgl@sss.pgh.pa.us>
2023-01-26 22:02 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 22:41 ` Re: improving user.c error messages Tom Lane <tgl@sss.pgh.pa.us>
2023-01-27 00:09 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-27 18:31 ` Re: improving user.c error messages Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-27 23:15 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-02-07 20:10 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
@ 2023-02-20 07:54 ` Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-02-20 19:02 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
0 siblings, 1 reply; 32+ messages in thread
From: Peter Eisentraut @ 2023-02-20 07:54 UTC (permalink / raw)
To: Nathan Bossart <nathandbossart@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers
On 07.02.23 21:10, Nathan Bossart wrote:
>> ERROR: permission denied to use replication slots
>> DETAIL: You must have REPLICATION privilege.
>>
>> adding the operation to the end seems less awkward (i.e., "You must have
>> REPLICATION privilege to use replication slots."). I don't think there's
>> any information lost by omitting the action in the DETAIL, so perhaps this
>> is just a stylistic choice. I think I'm inclined to add the action to the
>> DETAIL whenever it doesn't make the message lengthy and awkward, and leave
>> it out otherwise. Thoughts?
> Here is a new patch set with this change and some other light editing.
I'm concerned about the loose use of "privilege" here. A privilege is
something I can grant. So if someone doesn't have the "REPLICATION
privilege", as in the above example, I would expect to be able to do
"GRANT REPLICATION TO someuser". Since that is not what is happening,
we should use some other term. The documentation around CREATE USER
uses the terms "attribute" and "option" (and also "privilege") for these
things.
Similarly -- this is an existing issue but we might as well look at it
-- in something like
must be superuser or a role with privileges of the
pg_write_server_files role
the phrase "a role with the privileges of that other role" seems
ambiguous. Doesn't it really mean you must be a member of that role?
I also feel that in sentences like
"You must have %s privilege to create roles."
a "the" is missing.
^ permalink raw reply [nested|flat] 32+ messages in thread
* Re: improving user.c error messages
2023-01-26 00:22 improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 09:07 ` Re: improving user.c error messages Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-26 19:13 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 19:42 ` Re: improving user.c error messages Robert Haas <robertmhaas@gmail.com>
2023-01-26 19:59 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 20:07 ` Re: improving user.c error messages Tom Lane <tgl@sss.pgh.pa.us>
2023-01-26 22:02 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 22:41 ` Re: improving user.c error messages Tom Lane <tgl@sss.pgh.pa.us>
2023-01-27 00:09 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-27 18:31 ` Re: improving user.c error messages Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-27 23:15 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-02-07 20:10 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-02-20 07:54 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
@ 2023-02-20 19:02 ` Nathan Bossart <nathandbossart@gmail.com>
2023-02-20 22:58 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
0 siblings, 1 reply; 32+ messages in thread
From: Nathan Bossart @ 2023-02-20 19:02 UTC (permalink / raw)
To: Peter Eisentraut <peter.eisentraut@enterprisedb.com>; +Cc: Alvaro Herrera <alvherre@alvh.no-ip.org>; Tom Lane <tgl@sss.pgh.pa.us>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers
On Mon, Feb 20, 2023 at 08:54:48AM +0100, Peter Eisentraut wrote:
> I'm concerned about the loose use of "privilege" here. A privilege is
> something I can grant. So if someone doesn't have the "REPLICATION
> privilege", as in the above example, I would expect to be able to do "GRANT
> REPLICATION TO someuser". Since that is not what is happening, we should
> use some other term. The documentation around CREATE USER uses the terms
> "attribute" and "option" (and also "privilege") for these things.
Good point. I will adjust these to use "attribute" instead.
> Similarly -- this is an existing issue but we might as well look at it -- in
> something like
>
> must be superuser or a role with privileges of the
> pg_write_server_files role
>
> the phrase "a role with the privileges of that other role" seems ambiguous.
> Doesn't it really mean you must be a member of that role?
Membership alone is not sufficient. You must also inherit the privileges
of the role via the INHERIT option. I thought about making this something
like
must have the INHERIT option on role %s
but I'm not sure that's accurate either. That wording makes it sound lіke
you need to be granted membership to the role directly WITH INHERIT OPTION,
but what you really need is membership, direct or indirect, with an INHERIT
chain up to the role in question. However, it looks like "must have the
ADMIN option on role %s" is used to mean something similar, so perhaps I am
overthinking it.
--
Nathan Bossart
Amazon Web Services: https://aws.amazon.com
^ permalink raw reply [nested|flat] 32+ messages in thread
* Re: improving user.c error messages
2023-01-26 00:22 improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 09:07 ` Re: improving user.c error messages Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-26 19:13 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 19:42 ` Re: improving user.c error messages Robert Haas <robertmhaas@gmail.com>
2023-01-26 19:59 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 20:07 ` Re: improving user.c error messages Tom Lane <tgl@sss.pgh.pa.us>
2023-01-26 22:02 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 22:41 ` Re: improving user.c error messages Tom Lane <tgl@sss.pgh.pa.us>
2023-01-27 00:09 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-27 18:31 ` Re: improving user.c error messages Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-27 23:15 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-02-07 20:10 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-02-20 07:54 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-02-20 19:02 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
@ 2023-02-20 22:58 ` Nathan Bossart <nathandbossart@gmail.com>
2023-03-09 09:55 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
0 siblings, 1 reply; 32+ messages in thread
From: Nathan Bossart @ 2023-02-20 22:58 UTC (permalink / raw)
To: Peter Eisentraut <peter.eisentraut@enterprisedb.com>; +Cc: Alvaro Herrera <alvherre@alvh.no-ip.org>; Tom Lane <tgl@sss.pgh.pa.us>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers
On Mon, Feb 20, 2023 at 11:02:10AM -0800, Nathan Bossart wrote:
> On Mon, Feb 20, 2023 at 08:54:48AM +0100, Peter Eisentraut wrote:
>> I'm concerned about the loose use of "privilege" here. A privilege is
>> something I can grant. So if someone doesn't have the "REPLICATION
>> privilege", as in the above example, I would expect to be able to do "GRANT
>> REPLICATION TO someuser". Since that is not what is happening, we should
>> use some other term. The documentation around CREATE USER uses the terms
>> "attribute" and "option" (and also "privilege") for these things.
>
> Good point. I will adjust these to use "attribute" instead.
done in v6
>> Similarly -- this is an existing issue but we might as well look at it -- in
>> something like
>>
>> must be superuser or a role with privileges of the
>> pg_write_server_files role
>>
>> the phrase "a role with the privileges of that other role" seems ambiguous.
>> Doesn't it really mean you must be a member of that role?
>
> Membership alone is not sufficient. You must also inherit the privileges
> of the role via the INHERIT option. I thought about making this something
> like
>
> must have the INHERIT option on role %s
>
> but I'm not sure that's accurate either. That wording makes it sound lіke
> you need to be granted membership to the role directly WITH INHERIT OPTION,
> but what you really need is membership, direct or indirect, with an INHERIT
> chain up to the role in question. However, it looks like "must have the
> ADMIN option on role %s" is used to mean something similar, so perhaps I am
> overthinking it.
For now, I've reworded these as "must inherit privileges of".
--
Nathan Bossart
Amazon Web Services: https://aws.amazon.com
Attachments: download all (2 files, .tar.gz)
v6-0001-Improve-user.c-error-messages.patch (text/x-diff, 30.2K) download
From cc6131da14200f32778ca8ba9628f86dccdbd43d Mon Sep 17 00:00:00 2001
From: Nathan Bossart <nathandbossart@gmail.com>
Date: Thu, 26 Jan 2023 11:05:13 -0800
Subject: [PATCH v6 1/2] Improve user.c error messages.
---
src/backend/commands/user.c | 169 ++++++++++++++++------
src/test/regress/expected/create_role.out | 77 ++++++----
src/test/regress/expected/dependency.out | 4 +
src/test/regress/expected/privileges.out | 23 ++-
4 files changed, 198 insertions(+), 75 deletions(-)
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 3a92e930c0..e92bb656f9 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("You must have the %s attribute to 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("You must have the %s attribute to 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("You must have the %s attribute to 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("You must have the %s attribute to 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("You must have the %s attribute to 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("You must have the %s attribute to alter roles with %s.",
+ "SUPERUSER", "SUPERUSER")));
+ if (!superuser() && dissuper)
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to alter role"),
+ errdetail("You must have the %s attribute to 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("You must have the %s attribute and the %s option on role \"%s\".",
+ "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, you 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("You must have the %s attribute to 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("You must have the %s attribute to 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("You must have the %s attribute to 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("You must have the %s option on role \"%s\" to 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("You must have the %s attribute to 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("You must have the %s attribute and the %s option on role \"%s\".",
+ "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("You must have the %s attribute to 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("You must have the %s attribute and the %s option on the target 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("You must have the %s attribute to 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("You must have the %s attribute and the %s option on role \"%s\".",
+ "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("You must have the %s attribute to 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("You must have the %s attribute and the %s option on role \"%s\".",
+ "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("You must inherit privileges of role \"%s\".",
+ 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("You must inherit privileges of role \"%s\".",
+ 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("You must inherit privileges role \"%s\".",
+ 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("You must have the %s attribute to grant roles with %s.",
+ "SUPERUSER", "SUPERUSER")));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to revoke role \"%s\"",
+ GetUserNameFromId(roleid, false)),
+ errdetail("You must have the %s attribute to 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("You must have the %s option on role \"%s\".",
+ "ADMIN", GetUserNameFromId(roleid, false))));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to revoke role \"%s\"",
+ GetUserNameFromId(roleid, false)),
+ errdetail("You must have the %s option on role \"%s\".",
+ "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("You must inherit 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)),
+ 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("You must inherit 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 9f431bd4f5..7157d4c589 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: You must have the SUPERUSER attribute 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
+DETAIL: You must have the REPLICATION attribute 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
+DETAIL: You must have the REPLICATION attribute 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
+DETAIL: You must have the BYPASSRLS attribute 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
+DETAIL: You must have the CREATEDB attribute 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
+DETAIL: You must have the SUPERUSER attribute to 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: You must have the REPLICATION attribute to 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: You must have the CREATEDB attribute to 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: You must have the BYPASSRLS attribute to 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: You must have the SUPERUSER attribute to 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: You must have the SUPERUSER attribute to 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: You must have the SUPERUSER attribute 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
+DETAIL: You must have the CREATEROLE attribute and the 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
+DETAIL: You must have the CREATEROLE attribute and the 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
+DETAIL: You must inherit 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"
+DETAIL: You must have the 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"
+DETAIL: You must have the 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"
+DETAIL: You must have the 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"
+DETAIL: You must have the 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"
+DETAIL: You must have the 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"
+DETAIL: You must have the 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"
+DETAIL: You must have the 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"
+DETAIL: You must have the 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"
+DETAIL: You must have the 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"
+DETAIL: You must have the 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
+DETAIL: You must have the SUPERUSER attribute 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
+DETAIL: You must have the CREATEROLE attribute and the 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..d1e8dcdf5c 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: You must inherit privileges of role "regress_dep_user1".
DROP OWNED BY regress_dep_user0, regress_dep_user2;
ERROR: permission denied to drop objects
+DETAIL: You must inherit privileges of role "regress_dep_user2".
REASSIGN OWNED BY regress_dep_user0 TO regress_dep_user1;
ERROR: permission denied to reassign objects
+DETAIL: You must inherit privileges role "regress_dep_user1".
REASSIGN OWNED BY regress_dep_user1 TO regress_dep_user0;
ERROR: permission denied to reassign objects
+DETAIL: You must inherit 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..72829ad764 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, you 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: You must have the 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"
+DETAIL: You must have the 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"
+DETAIL: You must have the 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"
+DETAIL: You must have the 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"
+DETAIL: You must have the 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;
--
2.25.1
v6-0002-Improve-more-insufficient-privileges-error-messag.patch (text/x-diff, 19.7K) download
From 4664631c96a303d2066380966d31df113e1fea06 Mon Sep 17 00:00:00 2001
From: Nathan Bossart <nathandbossart@gmail.com>
Date: Fri, 27 Jan 2023 14:52:57 -0800
Subject: [PATCH v6 2/2] Improve more insufficient-privileges error messages.
---
contrib/file_fdw/expected/file_fdw.out | 3 ++-
contrib/file_fdw/file_fdw.c | 8 ++++++--
contrib/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/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 +++++++++------
.../dummy_seclabel/expected/dummy_seclabel.out | 3 ++-
.../modules/unsafe_tests/expected/rolenames.out | 3 ++-
src/test/regress/expected/create_role.out | 3 ++-
16 files changed, 89 insertions(+), 37 deletions(-)
diff --git a/contrib/file_fdw/expected/file_fdw.out b/contrib/file_fdw/expected/file_fdw.out
index 36d76ba26c..ea7afd6fe8 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: You must inherit privileges of the "pg_read_server_files" role to 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 8ccc167548..8553d93b3f 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("You must inherit privileges of the \"%s\" role to 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("You must inherit privileges of the \"%s\" role to 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..b45f5cf0df 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: You must have the REPLICATION attribute to 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: You must have the REPLICATION attribute to 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: You must have the REPLICATION attribute to 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: You must have the REPLICATION attribute to 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..aac64408d1 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("You must inherit privileges of the \"%s\" role.",
+ "pg_write_server_files")));
CommitTransactionCommand();
/*
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 2f688166e1..c1ab4ea161 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("You 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("You 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("You 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..388cfd7cae 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("You must inherit privileges of the \"%s\" role.",
+ "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("You must inherit privileges of the \"%s\" role.",
+ "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("You must inherit privileges of the \"%s\" role.",
+ "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/replication/slot.c b/src/backend/replication/slot.c
index f286918f69..51adcd8418 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("You must have the %s attribute to use replication slots.",
+ "REPLICATION")));
}
/*
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index a7071b2fce..09a69f2c76 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -3860,7 +3860,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("You must inherit privileges of the role whose process is being terminated or inherit privileges of the \"%s\" role.",
+ "pg_signal_backend")));
}
}
diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index bc93ab5b52..c1be67e7d0 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("You must have the %s attribute to 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("You must inherit privileges of the role whose query is being canceled or inherit privileges of the \"%s\" role.",
+ "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("You must have the %s attribute to 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("You must inherit privileges of the role whose process is being terminated or inherit privileges of the \"%s\" role.",
+ "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..3fd9cfb842 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("You must inherit privileges of the \"%s\" role to 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 59532bbd80..b0cfbc742b 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -701,6 +701,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 2f07ca7a0e..2b103586ab 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -940,12 +940,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 that inherit privileges of the \"%s\" role",
+ "pg_use_reserved_connections")));
}
/* Check replication permissions needed for walsender processes. */
@@ -953,10 +955,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("You must have the %s attribute to start a WAL sender process.",
+ "REPLICATION")));
}
/*
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 51e07d5582..7805a6015b 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("You must inherit privileges of the \"%s\" role to 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("You must inherit privileges of the \"%s\" role to 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("You must inherit privileges of the \"%s\" role to 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..18f8db0e39 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: You 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..6e55850705 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: You must inherit privileges of the "pg_read_all_settings" role to 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 7157d4c589..e72eb91624 100644
--- a/src/test/regress/expected/create_role.out
+++ b/src/test/regress/expected/create_role.out
@@ -106,7 +106,8 @@ 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: You 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: You must have the CREATEROLE attribute and the ADMIN option on role "regress_role_normal".
--
2.25.1
^ permalink raw reply [nested|flat] 32+ messages in thread
* Re: improving user.c error messages
2023-01-26 00:22 improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 09:07 ` Re: improving user.c error messages Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-26 19:13 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 19:42 ` Re: improving user.c error messages Robert Haas <robertmhaas@gmail.com>
2023-01-26 19:59 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 20:07 ` Re: improving user.c error messages Tom Lane <tgl@sss.pgh.pa.us>
2023-01-26 22:02 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 22:41 ` Re: improving user.c error messages Tom Lane <tgl@sss.pgh.pa.us>
2023-01-27 00:09 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-27 18:31 ` Re: improving user.c error messages Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-27 23:15 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-02-07 20:10 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-02-20 07:54 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-02-20 19:02 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-02-20 22:58 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
@ 2023-03-09 09:55 ` Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-03-09 17:58 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
0 siblings, 1 reply; 32+ messages in thread
From: Peter Eisentraut @ 2023-03-09 09:55 UTC (permalink / raw)
To: Nathan Bossart <nathandbossart@gmail.com>; +Cc: Alvaro Herrera <alvherre@alvh.no-ip.org>; Tom Lane <tgl@sss.pgh.pa.us>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers
On 20.02.23 23:58, Nathan Bossart wrote:
>>> Similarly -- this is an existing issue but we might as well look at it -- in
>>> something like
>>>
>>> must be superuser or a role with privileges of the
>>> pg_write_server_files role
>>>
>>> the phrase "a role with the privileges of that other role" seems ambiguous.
>>> Doesn't it really mean you must be a member of that role?
>>
>> Membership alone is not sufficient. You must also inherit the privileges
>> of the role via the INHERIT option. I thought about making this something
>> like
>>
>> must have the INHERIT option on role %s
>>
>> but I'm not sure that's accurate either. That wording makes it sound lіke
>> you need to be granted membership to the role directly WITH INHERIT OPTION,
>> but what you really need is membership, direct or indirect, with an INHERIT
>> chain up to the role in question. However, it looks like "must have the
>> ADMIN option on role %s" is used to mean something similar, so perhaps I am
>> overthinking it.
>
> 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.
A couple of other thoughts:
"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.
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."
By the way, I'm not sure what the separation between 0001 and 0002 is
supposed to be.
^ permalink raw reply [nested|flat] 32+ messages in thread
* Re: improving user.c error messages
2023-01-26 00:22 improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 09:07 ` Re: improving user.c error messages Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-26 19:13 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 19:42 ` Re: improving user.c error messages Robert Haas <robertmhaas@gmail.com>
2023-01-26 19:59 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 20:07 ` Re: improving user.c error messages Tom Lane <tgl@sss.pgh.pa.us>
2023-01-26 22:02 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 22:41 ` Re: improving user.c error messages Tom Lane <tgl@sss.pgh.pa.us>
2023-01-27 00:09 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-27 18:31 ` Re: improving user.c error messages Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-27 23:15 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-02-07 20:10 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-02-20 07:54 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-02-20 19:02 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-02-20 22:58 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-03-09 09:55 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
@ 2023-03-09 17:58 ` Nathan Bossart <nathandbossart@gmail.com>
2023-03-10 00:03 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
0 siblings, 1 reply; 32+ messages in thread
From: Nathan Bossart @ 2023-03-09 17:58 UTC (permalink / raw)
To: Peter Eisentraut <peter.eisentraut@enterprisedb.com>; +Cc: Alvaro Herrera <alvherre@alvh.no-ip.org>; Tom Lane <tgl@sss.pgh.pa.us>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers
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.
> "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.
> 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.
> 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.
--
Nathan Bossart
Amazon Web Services: https://aws.amazon.com
^ permalink raw reply [nested|flat] 32+ messages in thread
* Re: improving user.c error messages
2023-01-26 00:22 improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 09:07 ` Re: improving user.c error messages Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-26 19:13 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 19:42 ` Re: improving user.c error messages Robert Haas <robertmhaas@gmail.com>
2023-01-26 19:59 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 20:07 ` Re: improving user.c error messages Tom Lane <tgl@sss.pgh.pa.us>
2023-01-26 22:02 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 22:41 ` Re: improving user.c error messages Tom Lane <tgl@sss.pgh.pa.us>
2023-01-27 00:09 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-27 18:31 ` Re: improving user.c error messages Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-27 23:15 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-02-07 20:10 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-02-20 07:54 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-02-20 19:02 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-02-20 22:58 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-03-09 09:55 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-03-09 17:58 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
@ 2023-03-10 00:03 ` Nathan Bossart <nathandbossart@gmail.com>
2023-03-16 15:24 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
0 siblings, 1 reply; 32+ messages in thread
From: Nathan Bossart @ 2023-03-10 00:03 UTC (permalink / raw)
To: Peter Eisentraut <peter.eisentraut@enterprisedb.com>; +Cc: Alvaro Herrera <alvherre@alvh.no-ip.org>; Tom Lane <tgl@sss.pgh.pa.us>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers
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
Attachments:
v7-0001-Improve-several-permission-related-error-messages.patch (text/x-diff, 50.1K) download
From 66cc7a3999cae48c1b97ef067bc16872b31b887f Mon Sep 17 00:00:00 2001
From: Nathan Bossart <nathandbossart@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
^ permalink raw reply [nested|flat] 32+ messages in thread
* Re: improving user.c error messages
2023-01-26 00:22 improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 09:07 ` Re: improving user.c error messages Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-26 19:13 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 19:42 ` Re: improving user.c error messages Robert Haas <robertmhaas@gmail.com>
2023-01-26 19:59 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 20:07 ` Re: improving user.c error messages Tom Lane <tgl@sss.pgh.pa.us>
2023-01-26 22:02 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 22:41 ` Re: improving user.c error messages Tom Lane <tgl@sss.pgh.pa.us>
2023-01-27 00:09 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-27 18:31 ` Re: improving user.c error messages Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-27 23:15 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-02-07 20:10 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-02-20 07:54 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-02-20 19:02 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-02-20 22:58 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-03-09 09:55 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-03-09 17:58 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-03-10 00:03 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
@ 2023-03-16 15:24 ` Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-03-16 15:48 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
0 siblings, 1 reply; 32+ messages in thread
From: Peter Eisentraut @ 2023-03-16 15:24 UTC (permalink / raw)
To: Nathan Bossart <nathandbossart@gmail.com>; +Cc: Alvaro Herrera <alvherre@alvh.no-ip.org>; Tom Lane <tgl@sss.pgh.pa.us>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers
On 10.03.23 01:03, Nathan Bossart wrote:
>>> 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.
I have committed two pieces that were not message changes separately.
I think the following change in DropRole() is incorrect:
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))));
The message does not reflect what check is actually performed. (Perhaps
this was confused with a similar but not exactly the same check in
RenameRole().)
That was the only "factual" error that I found.
In file_fdw_validator(), the option names "filename" and "program" could
be parameterized.
In DropOwnedObjects() and ReassignOwnedObjects(), I suggest the
following changes, for clarity:
- errdetail("Only roles with privileges of role \"%s\" may drop its
objects.",
+ errdetail("Only roles with privileges of role \"%s\" may drop objects
owned by it.",
- errdetail("Only roles with privileges of role \"%s\" may reassign its
objects.",
+ errdetail("Only roles with privileges of role \"%s\" may reassign
objects owned by it.",
The rest looks okay to me.
^ permalink raw reply [nested|flat] 32+ messages in thread
* Re: improving user.c error messages
2023-01-26 00:22 improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 09:07 ` Re: improving user.c error messages Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-26 19:13 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 19:42 ` Re: improving user.c error messages Robert Haas <robertmhaas@gmail.com>
2023-01-26 19:59 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 20:07 ` Re: improving user.c error messages Tom Lane <tgl@sss.pgh.pa.us>
2023-01-26 22:02 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 22:41 ` Re: improving user.c error messages Tom Lane <tgl@sss.pgh.pa.us>
2023-01-27 00:09 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-27 18:31 ` Re: improving user.c error messages Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-27 23:15 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-02-07 20:10 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-02-20 07:54 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-02-20 19:02 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-02-20 22:58 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-03-09 09:55 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-03-09 17:58 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-03-10 00:03 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-03-16 15:24 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
@ 2023-03-16 15:48 ` Nathan Bossart <nathandbossart@gmail.com>
2023-03-16 15:59 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
0 siblings, 1 reply; 32+ messages in thread
From: Nathan Bossart @ 2023-03-16 15:48 UTC (permalink / raw)
To: Peter Eisentraut <peter.eisentraut@enterprisedb.com>; +Cc: Alvaro Herrera <alvherre@alvh.no-ip.org>; Tom Lane <tgl@sss.pgh.pa.us>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers
On Thu, Mar 16, 2023 at 04:24:07PM +0100, Peter Eisentraut wrote:
> I have committed two pieces that were not message changes separately.
Thanks!
> I think the following change in DropRole() is incorrect:
>
> 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))));
>
> The message does not reflect what check is actually performed. (Perhaps
> this was confused with a similar but not exactly the same check in
> RenameRole().)
Hm. Is your point that we should only mention the admin option here? I
mentioned both createrole and admin option in this message (and the
createrole check above this point) in an attempt to avoid giving partial
information.
> In file_fdw_validator(), the option names "filename" and "program" could be
> parameterized.
>
>
> In DropOwnedObjects() and ReassignOwnedObjects(), I suggest the following
> changes, for clarity:
>
> - errdetail("Only roles with privileges of role \"%s\" may drop its
> objects.",
> + errdetail("Only roles with privileges of role \"%s\" may drop objects
> owned by it.",
>
> - errdetail("Only roles with privileges of role \"%s\" may reassign its
> objects.",
> + errdetail("Only roles with privileges of role \"%s\" may reassign objects
> owned by it.",
Will do.
--
Nathan Bossart
Amazon Web Services: https://aws.amazon.com
^ permalink raw reply [nested|flat] 32+ messages in thread
* Re: improving user.c error messages
2023-01-26 00:22 improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 09:07 ` Re: improving user.c error messages Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-26 19:13 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 19:42 ` Re: improving user.c error messages Robert Haas <robertmhaas@gmail.com>
2023-01-26 19:59 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 20:07 ` Re: improving user.c error messages Tom Lane <tgl@sss.pgh.pa.us>
2023-01-26 22:02 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 22:41 ` Re: improving user.c error messages Tom Lane <tgl@sss.pgh.pa.us>
2023-01-27 00:09 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-27 18:31 ` Re: improving user.c error messages Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-27 23:15 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-02-07 20:10 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-02-20 07:54 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-02-20 19:02 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-02-20 22:58 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-03-09 09:55 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-03-09 17:58 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-03-10 00:03 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-03-16 15:24 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-03-16 15:48 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
@ 2023-03-16 15:59 ` Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-03-16 16:27 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
0 siblings, 1 reply; 32+ messages in thread
From: Peter Eisentraut @ 2023-03-16 15:59 UTC (permalink / raw)
To: Nathan Bossart <nathandbossart@gmail.com>; +Cc: Alvaro Herrera <alvherre@alvh.no-ip.org>; Tom Lane <tgl@sss.pgh.pa.us>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers
On 16.03.23 16:48, Nathan Bossart wrote:
>> I think the following change in DropRole() is incorrect:
>>
>> 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))));
>>
>> The message does not reflect what check is actually performed. (Perhaps
>> this was confused with a similar but not exactly the same check in
>> RenameRole().)
> Hm. Is your point that we should only mention the admin option here? I
> mentioned both createrole and admin option in this message (and the
> createrole check above this point) in an attempt to avoid giving partial
> information.
AFAICT, the mention of CREATEROLE is incorrect, because the code doesn't
actually check for the CREATEROLE attribute.
^ permalink raw reply [nested|flat] 32+ messages in thread
* Re: improving user.c error messages
2023-01-26 00:22 improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 09:07 ` Re: improving user.c error messages Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-26 19:13 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 19:42 ` Re: improving user.c error messages Robert Haas <robertmhaas@gmail.com>
2023-01-26 19:59 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 20:07 ` Re: improving user.c error messages Tom Lane <tgl@sss.pgh.pa.us>
2023-01-26 22:02 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 22:41 ` Re: improving user.c error messages Tom Lane <tgl@sss.pgh.pa.us>
2023-01-27 00:09 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-27 18:31 ` Re: improving user.c error messages Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-27 23:15 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-02-07 20:10 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-02-20 07:54 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-02-20 19:02 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-02-20 22:58 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-03-09 09:55 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-03-09 17:58 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-03-10 00:03 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-03-16 15:24 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-03-16 15:48 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-03-16 15:59 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
@ 2023-03-16 16:27 ` Nathan Bossart <nathandbossart@gmail.com>
2023-03-16 23:47 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
0 siblings, 1 reply; 32+ messages in thread
From: Nathan Bossart @ 2023-03-16 16:27 UTC (permalink / raw)
To: Peter Eisentraut <peter.eisentraut@enterprisedb.com>; +Cc: Alvaro Herrera <alvherre@alvh.no-ip.org>; Tom Lane <tgl@sss.pgh.pa.us>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers
On Thu, Mar 16, 2023 at 04:59:53PM +0100, Peter Eisentraut wrote:
> On 16.03.23 16:48, Nathan Bossart wrote:
>> > I think the following change in DropRole() is incorrect:
>> >
>> > 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))));
>> >
>> > The message does not reflect what check is actually performed. (Perhaps
>> > this was confused with a similar but not exactly the same check in
>> > RenameRole().)
>> Hm. Is your point that we should only mention the admin option here? I
>> mentioned both createrole and admin option in this message (and the
>> createrole check above this point) in an attempt to avoid giving partial
>> information.
>
> AFAICT, the mention of CREATEROLE is incorrect, because the code doesn't
> actually check for the CREATEROLE attribute.
There is a createrole check at the top of DropRole():
/*
* DROP ROLE
*/
void
DropRole(DropRoleStmt *stmt)
{
Relation pg_authid_rel,
pg_auth_members_rel;
ListCell *item;
List *role_addresses = NIL;
if (!have_createrole_privilege())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to drop role")));
Granted, no one will see the admin option error unless they at least have
createrole, so we could leave it out, but my intent was to list the full
set of privileges required to drop the role to avoid ambiguity.
--
Nathan Bossart
Amazon Web Services: https://aws.amazon.com
^ permalink raw reply [nested|flat] 32+ messages in thread
* Re: improving user.c error messages
2023-01-26 00:22 improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 09:07 ` Re: improving user.c error messages Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-26 19:13 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 19:42 ` Re: improving user.c error messages Robert Haas <robertmhaas@gmail.com>
2023-01-26 19:59 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 20:07 ` Re: improving user.c error messages Tom Lane <tgl@sss.pgh.pa.us>
2023-01-26 22:02 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 22:41 ` Re: improving user.c error messages Tom Lane <tgl@sss.pgh.pa.us>
2023-01-27 00:09 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-27 18:31 ` Re: improving user.c error messages Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-27 23:15 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-02-07 20:10 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-02-20 07:54 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-02-20 19:02 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-02-20 22:58 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-03-09 09:55 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-03-09 17:58 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-03-10 00:03 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-03-16 15:24 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-03-16 15:48 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-03-16 15:59 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-03-16 16:27 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
@ 2023-03-16 23:47 ` Nathan Bossart <nathandbossart@gmail.com>
2023-03-17 09:40 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
0 siblings, 1 reply; 32+ messages in thread
From: Nathan Bossart @ 2023-03-16 23:47 UTC (permalink / raw)
To: Peter Eisentraut <peter.eisentraut@enterprisedb.com>; +Cc: Alvaro Herrera <alvherre@alvh.no-ip.org>; Tom Lane <tgl@sss.pgh.pa.us>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers
Here is a rebased patch in which I've addressed the latest feedback except
for the DropRole() part that is under discussion.
--
Nathan Bossart
Amazon Web Services: https://aws.amazon.com
Attachments:
v8-0001-Improve-several-permission-related-error-messages.patch (text/x-diff, 49.2K) download
From cd6a75109471e173869a15b39342ff4882eac61f Mon Sep 17 00:00:00 2001
From: Nathan Bossart <nathandbossart@gmail.com>
Date: Thu, 26 Jan 2023 11:05:13 -0800
Subject: [PATCH v8 1/1] Improve several permission-related error messages.
---
contrib/file_fdw/expected/file_fdw.out | 3 +-
contrib/file_fdw/file_fdw.c | 10 +-
.../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 | 167 +++++++++++++-----
src/backend/replication/slot.c | 4 +-
src/backend/storage/ipc/procarray.c | 4 +-
src/backend/storage/ipc/signalfuncs.c | 16 +-
src/backend/tcop/utility.c | 5 +-
src/backend/utils/init/postinit.c | 10 +-
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 ++-
18 files changed, 282 insertions(+), 109 deletions(-)
diff --git a/contrib/file_fdw/expected/file_fdw.out b/contrib/file_fdw/expected/file_fdw.out
index f5ae29732a..72304e0ff3 100644
--- a/contrib/file_fdw/expected/file_fdw.out
+++ b/contrib/file_fdw/expected/file_fdw.out
@@ -491,7 +491,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 99b21e8316..9e330b9934 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -279,13 +279,19 @@ 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 \"%s\" option of a file_fdw foreign table",
+ "filename"),
+ 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 \"%s\" option of a file_fdw foreign table",
+ "program"),
+ 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 167d31a2d9..f14fae3308 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 43fe530a96..52796e9548 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);
@@ -1383,7 +1425,9 @@ RenameRole(const char *oldname, const char *newname)
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 objects owned by it.",
+ 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 objects owned by it.",
+ 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 3506b77cc7..2293c0c6fc 100644
--- a/src/backend/replication/slot.c
+++ b/src/backend/replication/slot.c
@@ -1143,7 +1143,9 @@ CheckSlotPermissions(void)
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/postinit.c b/src/backend/utils/init/postinit.c
index 3026317bfc..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. */
@@ -965,7 +967,9 @@ InitPostgres(const char *in_dbname, Oid dboid,
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..5526e34a7f 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 objects owned by it.
-- 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..2b96720e29 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 objects owned by it.
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 objects owned by it.
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 objects owned by it.
-- 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
^ permalink raw reply [nested|flat] 32+ messages in thread
* Re: improving user.c error messages
2023-01-26 00:22 improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 09:07 ` Re: improving user.c error messages Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-26 19:13 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 19:42 ` Re: improving user.c error messages Robert Haas <robertmhaas@gmail.com>
2023-01-26 19:59 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 20:07 ` Re: improving user.c error messages Tom Lane <tgl@sss.pgh.pa.us>
2023-01-26 22:02 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 22:41 ` Re: improving user.c error messages Tom Lane <tgl@sss.pgh.pa.us>
2023-01-27 00:09 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-27 18:31 ` Re: improving user.c error messages Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-27 23:15 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-02-07 20:10 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-02-20 07:54 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-02-20 19:02 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-02-20 22:58 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-03-09 09:55 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-03-09 17:58 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-03-10 00:03 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-03-16 15:24 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-03-16 15:48 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-03-16 15:59 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-03-16 16:27 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-03-16 23:47 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
@ 2023-03-17 09:40 ` Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-03-17 18:10 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
0 siblings, 1 reply; 32+ messages in thread
From: Peter Eisentraut @ 2023-03-17 09:40 UTC (permalink / raw)
To: Nathan Bossart <nathandbossart@gmail.com>; +Cc: Alvaro Herrera <alvherre@alvh.no-ip.org>; Tom Lane <tgl@sss.pgh.pa.us>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers
On 17.03.23 00:47, Nathan Bossart wrote:
> Here is a rebased patch in which I've addressed the latest feedback except
> for the DropRole() part that is under discussion.
committed
^ permalink raw reply [nested|flat] 32+ messages in thread
* Re: improving user.c error messages
2023-01-26 00:22 improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 09:07 ` Re: improving user.c error messages Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-26 19:13 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 19:42 ` Re: improving user.c error messages Robert Haas <robertmhaas@gmail.com>
2023-01-26 19:59 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 20:07 ` Re: improving user.c error messages Tom Lane <tgl@sss.pgh.pa.us>
2023-01-26 22:02 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 22:41 ` Re: improving user.c error messages Tom Lane <tgl@sss.pgh.pa.us>
2023-01-27 00:09 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-27 18:31 ` Re: improving user.c error messages Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-27 23:15 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-02-07 20:10 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-02-20 07:54 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-02-20 19:02 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-02-20 22:58 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-03-09 09:55 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-03-09 17:58 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-03-10 00:03 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-03-16 15:24 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-03-16 15:48 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-03-16 15:59 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-03-16 16:27 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-03-16 23:47 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-03-17 09:40 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
@ 2023-03-17 18:10 ` Nathan Bossart <nathandbossart@gmail.com>
0 siblings, 0 replies; 32+ messages in thread
From: Nathan Bossart @ 2023-03-17 18:10 UTC (permalink / raw)
To: Peter Eisentraut <peter.eisentraut@enterprisedb.com>; +Cc: Alvaro Herrera <alvherre@alvh.no-ip.org>; Tom Lane <tgl@sss.pgh.pa.us>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers
On Fri, Mar 17, 2023 at 10:40:06AM +0100, Peter Eisentraut wrote:
> committed
Thanks!
--
Nathan Bossart
Amazon Web Services: https://aws.amazon.com
^ permalink raw reply [nested|flat] 32+ messages in thread
* Re: improving user.c error messages
2023-01-26 00:22 improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
@ 2023-01-27 10:00 ` Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-01-27 13:31 ` Re: improving user.c error messages Robert Haas <robertmhaas@gmail.com>
2 siblings, 1 reply; 32+ messages in thread
From: Peter Eisentraut @ 2023-01-27 10:00 UTC (permalink / raw)
To: Nathan Bossart <nathandbossart@gmail.com>; Robert Haas <robertmhaas@gmail.com>; +Cc: pgsql-hackers
On 26.01.23 01:22, Nathan Bossart wrote:
> 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.
This is good. If I may assign some more work ;-), we have a bunch of
error messages like
errmsg("must be superuser or a role with privileges of the
pg_write_server_files role to create backup stored on server")
errmsg("must be superuser or have privileges of the
pg_execute_server_program role to COPY to or from an external program")
errmsg("must be superuser or have privileges of pg_read_all_settings to
examine \"%s\"", ...)
which could also be split up into a pair of
errmsg("permission denied to xxx")
errdetail("You must be superuser or ...")
^ permalink raw reply [nested|flat] 32+ messages in thread
* Re: improving user.c error messages
2023-01-26 00:22 improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-27 10:00 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
@ 2023-01-27 13:31 ` Robert Haas <robertmhaas@gmail.com>
2023-01-27 15:52 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-27 16:17 ` Re: improving user.c error messages Tom Lane <tgl@sss.pgh.pa.us>
0 siblings, 2 replies; 32+ messages in thread
From: Robert Haas @ 2023-01-27 13:31 UTC (permalink / raw)
To: Peter Eisentraut <peter.eisentraut@enterprisedb.com>; +Cc: Nathan Bossart <nathandbossart@gmail.com>; pgsql-hackers
On Fri, Jan 27, 2023 at 5:00 AM Peter Eisentraut
<peter.eisentraut@enterprisedb.com> wrote:
> This is good. If I may assign some more work ;-), we have a bunch of
> error messages like
>
> errmsg("must be superuser or a role with privileges of the
> pg_write_server_files role to create backup stored on server")
>
> errmsg("must be superuser or have privileges of the
> pg_execute_server_program role to COPY to or from an external program")
>
> errmsg("must be superuser or have privileges of pg_read_all_settings to
> examine \"%s\"", ...)
>
> which could also be split up into a pair of
>
> errmsg("permission denied to xxx")
> errdetail("You must be superuser or ...")
I almost hate to bring this up since I'm not sure how far we want to
go down this rat hole, but what should be our policy about mentioning
superuser? I don't think we're entirely consistent right now, and I'm
not sure whether every error message needs to mention that if you were
the superuser you could do everything. Is that something we should
mention always, never, or in some set of circumstances?
--
Robert Haas
EDB: http://www.enterprisedb.com
^ permalink raw reply [nested|flat] 32+ messages in thread
* Re: improving user.c error messages
2023-01-26 00:22 improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-27 10:00 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-01-27 13:31 ` Re: improving user.c error messages Robert Haas <robertmhaas@gmail.com>
@ 2023-01-27 15:52 ` Nathan Bossart <nathandbossart@gmail.com>
2023-01-27 15:53 ` Re: improving user.c error messages Robert Haas <robertmhaas@gmail.com>
1 sibling, 1 reply; 32+ messages in thread
From: Nathan Bossart @ 2023-01-27 15:52 UTC (permalink / raw)
To: Robert Haas <robertmhaas@gmail.com>; +Cc: Peter Eisentraut <peter.eisentraut@enterprisedb.com>; pgsql-hackers
On Fri, Jan 27, 2023 at 08:31:32AM -0500, Robert Haas wrote:
> I almost hate to bring this up since I'm not sure how far we want to
> go down this rat hole, but what should be our policy about mentioning
> superuser? I don't think we're entirely consistent right now, and I'm
> not sure whether every error message needs to mention that if you were
> the superuser you could do everything. Is that something we should
> mention always, never, or in some set of circumstances?
IMHO superuser should typically only be mentioned when it is the only way
to do something. Since superusers have all privileges, I think logs like
"superuser or privileges of X" are kind of redundant. If Robert has
privileges of X, we wouldn't say "privileges of X or Robert." We'd just
point to X. Ultimately, I feel like mentioning superuser in error messages
usually just makes the message longer without adding any useful
information.
I recognize that this is a bold opinion and that the policy to mention
superuser might need to be more nuanced in practice...
--
Nathan Bossart
Amazon Web Services: https://aws.amazon.com
^ permalink raw reply [nested|flat] 32+ messages in thread
* Re: improving user.c error messages
2023-01-26 00:22 improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-27 10:00 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-01-27 13:31 ` Re: improving user.c error messages Robert Haas <robertmhaas@gmail.com>
2023-01-27 15:52 ` Re: improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
@ 2023-01-27 15:53 ` Robert Haas <robertmhaas@gmail.com>
0 siblings, 0 replies; 32+ messages in thread
From: Robert Haas @ 2023-01-27 15:53 UTC (permalink / raw)
To: Nathan Bossart <nathandbossart@gmail.com>; +Cc: Peter Eisentraut <peter.eisentraut@enterprisedb.com>; pgsql-hackers
On Fri, Jan 27, 2023 at 10:52 AM Nathan Bossart
<nathandbossart@gmail.com> wrote:
> IMHO superuser should typically only be mentioned when it is the only way
> to do something. Since superusers have all privileges, I think logs like
> "superuser or privileges of X" are kind of redundant. If Robert has
> privileges of X, we wouldn't say "privileges of X or Robert." We'd just
> point to X. Ultimately, I feel like mentioning superuser in error messages
> usually just makes the message longer without adding any useful
> information.
That's kind of my opinion too, but I'm not sure whether there are
cases where it will lead to confusion.
--
Robert Haas
EDB: http://www.enterprisedb.com
^ permalink raw reply [nested|flat] 32+ messages in thread
* Re: improving user.c error messages
2023-01-26 00:22 improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-27 10:00 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-01-27 13:31 ` Re: improving user.c error messages Robert Haas <robertmhaas@gmail.com>
@ 2023-01-27 16:17 ` Tom Lane <tgl@sss.pgh.pa.us>
2023-01-27 18:13 ` Re: improving user.c error messages Alvaro Herrera <alvherre@alvh.no-ip.org>
1 sibling, 1 reply; 32+ messages in thread
From: Tom Lane @ 2023-01-27 16:17 UTC (permalink / raw)
To: Robert Haas <robertmhaas@gmail.com>; +Cc: Peter Eisentraut <peter.eisentraut@enterprisedb.com>; Nathan Bossart <nathandbossart@gmail.com>; pgsql-hackers
Robert Haas <robertmhaas@gmail.com> writes:
> I almost hate to bring this up since I'm not sure how far we want to
> go down this rat hole, but what should be our policy about mentioning
> superuser? I don't think we're entirely consistent right now, and I'm
> not sure whether every error message needs to mention that if you were
> the superuser you could do everything. Is that something we should
> mention always, never, or in some set of circumstances?
Good point. My vote is for standardizing on *not* mentioning it.
Error messages should say "you need privilege X". That is not
the place to go into all the ways you could hold privilege X
(one of which is being superuser).
regards, tom lane
^ permalink raw reply [nested|flat] 32+ messages in thread
* Re: improving user.c error messages
2023-01-26 00:22 improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-27 10:00 ` Re: improving user.c error messages Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-01-27 13:31 ` Re: improving user.c error messages Robert Haas <robertmhaas@gmail.com>
2023-01-27 16:17 ` Re: improving user.c error messages Tom Lane <tgl@sss.pgh.pa.us>
@ 2023-01-27 18:13 ` Alvaro Herrera <alvherre@alvh.no-ip.org>
0 siblings, 0 replies; 32+ messages in thread
From: Alvaro Herrera @ 2023-01-27 18:13 UTC (permalink / raw)
To: Tom Lane <tgl@sss.pgh.pa.us>; +Cc: Robert Haas <robertmhaas@gmail.com>; Peter Eisentraut <peter.eisentraut@enterprisedb.com>; Nathan Bossart <nathandbossart@gmail.com>; pgsql-hackers
On 2023-Jan-27, Tom Lane wrote:
> Good point. My vote is for standardizing on *not* mentioning it.
> Error messages should say "you need privilege X". That is not
> the place to go into all the ways you could hold privilege X
> (one of which is being superuser).
+1
--
Álvaro Herrera 48°01'N 7°57'E — https://www.EnterpriseDB.com/
"El sabio habla porque tiene algo que decir;
el tonto, porque tiene que decir algo" (Platon).
^ permalink raw reply [nested|flat] 32+ messages in thread
end of thread, other threads:[~2023-03-17 18:10 UTC | newest]
Thread overview: 32+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2023-01-26 00:22 improving user.c error messages Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 01:45 ` Tom Lane <tgl@sss.pgh.pa.us>
2023-01-26 09:07 ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-26 19:13 ` Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 19:42 ` Robert Haas <robertmhaas@gmail.com>
2023-01-26 19:59 ` Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 20:07 ` Tom Lane <tgl@sss.pgh.pa.us>
2023-01-26 22:02 ` Nathan Bossart <nathandbossart@gmail.com>
2023-01-26 22:41 ` Tom Lane <tgl@sss.pgh.pa.us>
2023-01-27 00:09 ` Nathan Bossart <nathandbossart@gmail.com>
2023-01-27 18:31 ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2023-01-27 23:15 ` Nathan Bossart <nathandbossart@gmail.com>
2023-02-07 20:10 ` Nathan Bossart <nathandbossart@gmail.com>
2023-02-20 07:54 ` Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-02-20 19:02 ` Nathan Bossart <nathandbossart@gmail.com>
2023-02-20 22:58 ` Nathan Bossart <nathandbossart@gmail.com>
2023-03-09 09:55 ` Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-03-09 17:58 ` Nathan Bossart <nathandbossart@gmail.com>
2023-03-10 00:03 ` Nathan Bossart <nathandbossart@gmail.com>
2023-03-16 15:24 ` Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-03-16 15:48 ` Nathan Bossart <nathandbossart@gmail.com>
2023-03-16 15:59 ` Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-03-16 16:27 ` Nathan Bossart <nathandbossart@gmail.com>
2023-03-16 23:47 ` Nathan Bossart <nathandbossart@gmail.com>
2023-03-17 09:40 ` Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-03-17 18:10 ` Nathan Bossart <nathandbossart@gmail.com>
2023-01-27 10:00 ` Peter Eisentraut <peter.eisentraut@enterprisedb.com>
2023-01-27 13:31 ` Robert Haas <robertmhaas@gmail.com>
2023-01-27 15:52 ` Nathan Bossart <nathandbossart@gmail.com>
2023-01-27 15:53 ` Robert Haas <robertmhaas@gmail.com>
2023-01-27 16:17 ` Tom Lane <tgl@sss.pgh.pa.us>
2023-01-27 18:13 ` Alvaro Herrera <alvherre@alvh.no-ip.org>
This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox