mirror of
https://github.com/postgres/postgres.git
synced 2025-07-07 00:36:50 +03:00
Add "pg_database_owner" default role.
Membership consists, implicitly, of the current database owner. Expect use in template databases. Once pg_database_owner has rights within a template, each owner of a database instantiated from that template will exercise those rights. Reviewed by John Naylor. Discussion: https://postgr.es/m/20201228043148.GA1053024@rfd.leadboat.com
This commit is contained in:
@ -10138,6 +10138,9 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
|
|||||||
<primary>pg_group</primary>
|
<primary>pg_group</primary>
|
||||||
</indexterm>
|
</indexterm>
|
||||||
|
|
||||||
|
<!-- Unlike information_schema.applicable_roles, this shows no members for
|
||||||
|
pg_database_owner. The v8.1 catalog would have shown no members if
|
||||||
|
that role had existed at the time. -->
|
||||||
<para>
|
<para>
|
||||||
The view <structname>pg_group</structname> exists for backwards
|
The view <structname>pg_group</structname> exists for backwards
|
||||||
compatibility: it emulates a catalog that existed in
|
compatibility: it emulates a catalog that existed in
|
||||||
|
@ -540,6 +540,10 @@ DROP ROLE doomed_role;
|
|||||||
<literal>pg_read_all_stats</literal> and
|
<literal>pg_read_all_stats</literal> and
|
||||||
<literal>pg_stat_scan_tables</literal>.</entry>
|
<literal>pg_stat_scan_tables</literal>.</entry>
|
||||||
</row>
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry>pg_database_owner</entry>
|
||||||
|
<entry>None. Membership consists, implicitly, of the current database owner.</entry>
|
||||||
|
</row>
|
||||||
<row>
|
<row>
|
||||||
<entry>pg_signal_backend</entry>
|
<entry>pg_signal_backend</entry>
|
||||||
<entry>Signal another backend to cancel a query or terminate its session.</entry>
|
<entry>Signal another backend to cancel a query or terminate its session.</entry>
|
||||||
@ -572,6 +576,17 @@ DROP ROLE doomed_role;
|
|||||||
other system information normally restricted to superusers.
|
other system information normally restricted to superusers.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The <literal>pg_database_owner</literal> role has one implicit,
|
||||||
|
situation-dependent member, namely the owner of the current database. The
|
||||||
|
role conveys no rights at first. Like any role, it can own objects or
|
||||||
|
receive grants of access privileges. Consequently, once
|
||||||
|
<literal>pg_database_owner</literal> has rights within a template database,
|
||||||
|
each owner of a database instantiated from that template will exercise those
|
||||||
|
rights. <literal>pg_database_owner</literal> cannot be a member of any
|
||||||
|
role, and it cannot have non-implicit members.
|
||||||
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
The <literal>pg_signal_backend</literal> role is intended to allow
|
The <literal>pg_signal_backend</literal> role is intended to allow
|
||||||
administrators to enable trusted, but non-superuser, roles to send signals
|
administrators to enable trusted, but non-superuser, roles to send signals
|
||||||
|
@ -255,7 +255,14 @@ CREATE VIEW applicable_roles AS
|
|||||||
SELECT CAST(a.rolname AS sql_identifier) AS grantee,
|
SELECT CAST(a.rolname AS sql_identifier) AS grantee,
|
||||||
CAST(b.rolname AS sql_identifier) AS role_name,
|
CAST(b.rolname AS sql_identifier) AS role_name,
|
||||||
CAST(CASE WHEN m.admin_option THEN 'YES' ELSE 'NO' END AS yes_or_no) AS is_grantable
|
CAST(CASE WHEN m.admin_option THEN 'YES' ELSE 'NO' END AS yes_or_no) AS is_grantable
|
||||||
FROM pg_auth_members m
|
FROM (SELECT member, roleid, admin_option FROM pg_auth_members
|
||||||
|
-- This UNION could be UNION ALL, but UNION works even if we start
|
||||||
|
-- to allow explicit pg_database_owner membership.
|
||||||
|
UNION
|
||||||
|
SELECT datdba, pg_authid.oid, false
|
||||||
|
FROM pg_database, pg_authid
|
||||||
|
WHERE datname = current_database() AND rolname = 'pg_database_owner'
|
||||||
|
) m
|
||||||
JOIN pg_authid a ON (m.member = a.oid)
|
JOIN pg_authid a ON (m.member = a.oid)
|
||||||
JOIN pg_authid b ON (m.roleid = b.oid)
|
JOIN pg_authid b ON (m.roleid = b.oid)
|
||||||
WHERE pg_has_role(a.oid, 'USAGE');
|
WHERE pg_has_role(a.oid, 'USAGE');
|
||||||
|
@ -1496,6 +1496,18 @@ AddRoleMems(const char *rolename, Oid roleid,
|
|||||||
rolename)));
|
rolename)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The charter of pg_database_owner is to have exactly one, implicit,
|
||||||
|
* situation-dependent member. There's no technical need for this
|
||||||
|
* restriction. (One could lift it and take the further step of making
|
||||||
|
* pg_database_ownercheck() equivalent to has_privs_of_role(roleid,
|
||||||
|
* DEFAULT_ROLE_DATABASE_OWNER), in which case explicit,
|
||||||
|
* situation-independent members could act as the owner of any database.)
|
||||||
|
*/
|
||||||
|
if (roleid == DEFAULT_ROLE_DATABASE_OWNER)
|
||||||
|
ereport(ERROR,
|
||||||
|
errmsg("role \"%s\" cannot have explicit members", rolename));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The role membership grantor of record has little significance at
|
* The role membership grantor of record has little significance at
|
||||||
* present. Nonetheless, inasmuch as users might look to it for a crude
|
* present. Nonetheless, inasmuch as users might look to it for a crude
|
||||||
@ -1524,6 +1536,30 @@ AddRoleMems(const char *rolename, Oid roleid,
|
|||||||
bool new_record_nulls[Natts_pg_auth_members];
|
bool new_record_nulls[Natts_pg_auth_members];
|
||||||
bool new_record_repl[Natts_pg_auth_members];
|
bool new_record_repl[Natts_pg_auth_members];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pg_database_owner is never a role member. Lifting this restriction
|
||||||
|
* would require a policy decision about membership loops. One could
|
||||||
|
* prevent loops, which would include making "ALTER DATABASE x OWNER
|
||||||
|
* TO proposed_datdba" fail if is_member_of_role(pg_database_owner,
|
||||||
|
* proposed_datdba). Hence, gaining a membership could reduce what a
|
||||||
|
* role could do. Alternately, one could allow these memberships to
|
||||||
|
* complete loops. A role could then have actual WITH ADMIN OPTION on
|
||||||
|
* itself, prompting a decision about is_admin_of_role() treatment of
|
||||||
|
* the case.
|
||||||
|
*
|
||||||
|
* Lifting this restriction also has policy implications for ownership
|
||||||
|
* of shared objects (databases and tablespaces). We allow such
|
||||||
|
* ownership, but we might find cause to ban it in the future.
|
||||||
|
* Designing such a ban would more troublesome if the design had to
|
||||||
|
* address pg_database_owner being a member of role FOO that owns a
|
||||||
|
* shared object. (The effect of such ownership is that any owner of
|
||||||
|
* another database can act as the owner of affected shared objects.)
|
||||||
|
*/
|
||||||
|
if (memberid == DEFAULT_ROLE_DATABASE_OWNER)
|
||||||
|
ereport(ERROR,
|
||||||
|
errmsg("role \"%s\" cannot be a member of any role",
|
||||||
|
get_rolespec_name(memberRole)));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Refuse creation of membership loops, including the trivial case
|
* Refuse creation of membership loops, including the trivial case
|
||||||
* where a role is made a member of itself. We do this by checking to
|
* where a role is made a member of itself. We do this by checking to
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "catalog/pg_auth_members.h"
|
#include "catalog/pg_auth_members.h"
|
||||||
#include "catalog/pg_authid.h"
|
#include "catalog/pg_authid.h"
|
||||||
#include "catalog/pg_class.h"
|
#include "catalog/pg_class.h"
|
||||||
|
#include "catalog/pg_database.h"
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
#include "commands/dbcommands.h"
|
#include "commands/dbcommands.h"
|
||||||
#include "commands/proclang.h"
|
#include "commands/proclang.h"
|
||||||
@ -68,6 +69,7 @@ enum RoleRecurseType
|
|||||||
};
|
};
|
||||||
static Oid cached_role[] = {InvalidOid, InvalidOid};
|
static Oid cached_role[] = {InvalidOid, InvalidOid};
|
||||||
static List *cached_roles[] = {NIL, NIL};
|
static List *cached_roles[] = {NIL, NIL};
|
||||||
|
static uint32 cached_db_hash;
|
||||||
|
|
||||||
|
|
||||||
static const char *getid(const char *s, char *n);
|
static const char *getid(const char *s, char *n);
|
||||||
@ -4665,10 +4667,14 @@ initialize_acl(void)
|
|||||||
{
|
{
|
||||||
if (!IsBootstrapProcessingMode())
|
if (!IsBootstrapProcessingMode())
|
||||||
{
|
{
|
||||||
|
cached_db_hash =
|
||||||
|
GetSysCacheHashValue1(DATABASEOID,
|
||||||
|
ObjectIdGetDatum(MyDatabaseId));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* In normal mode, set a callback on any syscache invalidation of rows
|
* In normal mode, set a callback on any syscache invalidation of rows
|
||||||
* of pg_auth_members (for roles_is_member_of()) or pg_authid (for
|
* of pg_auth_members (for roles_is_member_of()), pg_authid (for
|
||||||
* has_rolinherit())
|
* has_rolinherit()), or pg_database (for roles_is_member_of())
|
||||||
*/
|
*/
|
||||||
CacheRegisterSyscacheCallback(AUTHMEMROLEMEM,
|
CacheRegisterSyscacheCallback(AUTHMEMROLEMEM,
|
||||||
RoleMembershipCacheCallback,
|
RoleMembershipCacheCallback,
|
||||||
@ -4676,6 +4682,9 @@ initialize_acl(void)
|
|||||||
CacheRegisterSyscacheCallback(AUTHOID,
|
CacheRegisterSyscacheCallback(AUTHOID,
|
||||||
RoleMembershipCacheCallback,
|
RoleMembershipCacheCallback,
|
||||||
(Datum) 0);
|
(Datum) 0);
|
||||||
|
CacheRegisterSyscacheCallback(DATABASEOID,
|
||||||
|
RoleMembershipCacheCallback,
|
||||||
|
(Datum) 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4686,6 +4695,13 @@ initialize_acl(void)
|
|||||||
static void
|
static void
|
||||||
RoleMembershipCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
|
RoleMembershipCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
|
||||||
{
|
{
|
||||||
|
if (cacheid == DATABASEOID &&
|
||||||
|
hashvalue != cached_db_hash &&
|
||||||
|
hashvalue != 0)
|
||||||
|
{
|
||||||
|
return; /* ignore pg_database changes for other DBs */
|
||||||
|
}
|
||||||
|
|
||||||
/* Force membership caches to be recomputed on next use */
|
/* Force membership caches to be recomputed on next use */
|
||||||
cached_role[ROLERECURSE_PRIVS] = InvalidOid;
|
cached_role[ROLERECURSE_PRIVS] = InvalidOid;
|
||||||
cached_role[ROLERECURSE_MEMBERS] = InvalidOid;
|
cached_role[ROLERECURSE_MEMBERS] = InvalidOid;
|
||||||
@ -4728,6 +4744,7 @@ static List *
|
|||||||
roles_is_member_of(Oid roleid, enum RoleRecurseType type,
|
roles_is_member_of(Oid roleid, enum RoleRecurseType type,
|
||||||
Oid admin_of, bool *is_admin)
|
Oid admin_of, bool *is_admin)
|
||||||
{
|
{
|
||||||
|
Oid dba;
|
||||||
List *roles_list;
|
List *roles_list;
|
||||||
ListCell *l;
|
ListCell *l;
|
||||||
List *new_cached_roles;
|
List *new_cached_roles;
|
||||||
@ -4740,6 +4757,24 @@ roles_is_member_of(Oid roleid, enum RoleRecurseType type,
|
|||||||
OidIsValid(cached_role[type]))
|
OidIsValid(cached_role[type]))
|
||||||
return cached_roles[type];
|
return cached_roles[type];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Role expansion happens in a non-database backend when guc.c checks
|
||||||
|
* DEFAULT_ROLE_READ_ALL_SETTINGS for a physical walsender SHOW command.
|
||||||
|
* In that case, no role gets pg_database_owner.
|
||||||
|
*/
|
||||||
|
if (!OidIsValid(MyDatabaseId))
|
||||||
|
dba = InvalidOid;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
HeapTuple dbtup;
|
||||||
|
|
||||||
|
dbtup = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(MyDatabaseId));
|
||||||
|
if (!HeapTupleIsValid(dbtup))
|
||||||
|
elog(ERROR, "cache lookup failed for database %u", MyDatabaseId);
|
||||||
|
dba = ((Form_pg_database) GETSTRUCT(dbtup))->datdba;
|
||||||
|
ReleaseSysCache(dbtup);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Find all the roles that roleid is a member of, including multi-level
|
* Find all the roles that roleid is a member of, including multi-level
|
||||||
* recursion. The role itself will always be the first element of the
|
* recursion. The role itself will always be the first element of the
|
||||||
@ -4787,6 +4822,11 @@ roles_is_member_of(Oid roleid, enum RoleRecurseType type,
|
|||||||
roles_list = list_append_unique_oid(roles_list, otherid);
|
roles_list = list_append_unique_oid(roles_list, otherid);
|
||||||
}
|
}
|
||||||
ReleaseSysCacheList(memlist);
|
ReleaseSysCacheList(memlist);
|
||||||
|
|
||||||
|
/* implement pg_database_owner implicit membership */
|
||||||
|
if (memberid == dba && OidIsValid(dba))
|
||||||
|
roles_list = list_append_unique_oid(roles_list,
|
||||||
|
DEFAULT_ROLE_DATABASE_OWNER);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
6
src/backend/utils/cache/catcache.c
vendored
6
src/backend/utils/cache/catcache.c
vendored
@ -1076,8 +1076,9 @@ InitCatCachePhase2(CatCache *cache, bool touch_index)
|
|||||||
* criticalRelcachesBuilt), we don't have to worry anymore.
|
* criticalRelcachesBuilt), we don't have to worry anymore.
|
||||||
*
|
*
|
||||||
* Similarly, during backend startup we have to be able to use the
|
* Similarly, during backend startup we have to be able to use the
|
||||||
* pg_authid and pg_auth_members syscaches for authentication even if
|
* pg_authid, pg_auth_members and pg_database syscaches for
|
||||||
* we don't yet have relcache entries for those catalogs' indexes.
|
* authentication even if we don't yet have relcache entries for those
|
||||||
|
* catalogs' indexes.
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
IndexScanOK(CatCache *cache, ScanKey cur_skey)
|
IndexScanOK(CatCache *cache, ScanKey cur_skey)
|
||||||
@ -1110,6 +1111,7 @@ IndexScanOK(CatCache *cache, ScanKey cur_skey)
|
|||||||
case AUTHNAME:
|
case AUTHNAME:
|
||||||
case AUTHOID:
|
case AUTHOID:
|
||||||
case AUTHMEMMEMROLE:
|
case AUTHMEMMEMROLE:
|
||||||
|
case DATABASEOID:
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Protect authentication lookups occurring before relcache has
|
* Protect authentication lookups occurring before relcache has
|
||||||
|
@ -3557,6 +3557,7 @@ describeRoles(const char *pattern, bool verbose, bool showSystem)
|
|||||||
|
|
||||||
printTableAddHeader(&cont, gettext_noop("Role name"), true, align);
|
printTableAddHeader(&cont, gettext_noop("Role name"), true, align);
|
||||||
printTableAddHeader(&cont, gettext_noop("Attributes"), true, align);
|
printTableAddHeader(&cont, gettext_noop("Attributes"), true, align);
|
||||||
|
/* ignores implicit memberships from superuser & pg_database_owner */
|
||||||
printTableAddHeader(&cont, gettext_noop("Member of"), true, align);
|
printTableAddHeader(&cont, gettext_noop("Member of"), true, align);
|
||||||
|
|
||||||
if (verbose && pset.sversion >= 80200)
|
if (verbose && pset.sversion >= 80200)
|
||||||
|
@ -53,6 +53,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* yyyymmddN */
|
/* yyyymmddN */
|
||||||
#define CATALOG_VERSION_NO 202103264
|
#define CATALOG_VERSION_NO 202103265
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -24,6 +24,11 @@
|
|||||||
rolcreaterole => 't', rolcreatedb => 't', rolcanlogin => 't',
|
rolcreaterole => 't', rolcreatedb => 't', rolcanlogin => 't',
|
||||||
rolreplication => 't', rolbypassrls => 't', rolconnlimit => '-1',
|
rolreplication => 't', rolbypassrls => 't', rolconnlimit => '-1',
|
||||||
rolpassword => '_null_', rolvaliduntil => '_null_' },
|
rolpassword => '_null_', rolvaliduntil => '_null_' },
|
||||||
|
{ oid => '8778', oid_symbol => 'DEFAULT_ROLE_DATABASE_OWNER',
|
||||||
|
rolname => 'pg_database_owner', rolsuper => 'f', rolinherit => 't',
|
||||||
|
rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f',
|
||||||
|
rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1',
|
||||||
|
rolpassword => '_null_', rolvaliduntil => '_null_' },
|
||||||
{ oid => '3373', oid_symbol => 'DEFAULT_ROLE_MONITOR',
|
{ oid => '3373', oid_symbol => 'DEFAULT_ROLE_MONITOR',
|
||||||
rolname => 'pg_monitor', rolsuper => 'f', rolinherit => 't',
|
rolname => 'pg_monitor', rolsuper => 'f', rolinherit => 't',
|
||||||
rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f',
|
rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f',
|
||||||
|
@ -1719,6 +1719,67 @@ SELECT * FROM pg_largeobject LIMIT 0;
|
|||||||
SET SESSION AUTHORIZATION regress_priv_user1;
|
SET SESSION AUTHORIZATION regress_priv_user1;
|
||||||
SELECT * FROM pg_largeobject LIMIT 0; -- to be denied
|
SELECT * FROM pg_largeobject LIMIT 0; -- to be denied
|
||||||
ERROR: permission denied for table pg_largeobject
|
ERROR: permission denied for table pg_largeobject
|
||||||
|
-- test pg_database_owner
|
||||||
|
RESET SESSION AUTHORIZATION;
|
||||||
|
GRANT pg_database_owner TO regress_priv_user1;
|
||||||
|
ERROR: role "pg_database_owner" cannot have explicit members
|
||||||
|
GRANT regress_priv_user1 TO pg_database_owner;
|
||||||
|
ERROR: role "pg_database_owner" cannot be a member of any role
|
||||||
|
CREATE TABLE datdba_only ();
|
||||||
|
ALTER TABLE datdba_only OWNER TO pg_database_owner;
|
||||||
|
REVOKE DELETE ON datdba_only FROM pg_database_owner;
|
||||||
|
SELECT
|
||||||
|
pg_has_role('regress_priv_user1', 'pg_database_owner', 'USAGE') as priv,
|
||||||
|
pg_has_role('regress_priv_user1', 'pg_database_owner', 'MEMBER') as mem,
|
||||||
|
pg_has_role('regress_priv_user1', 'pg_database_owner',
|
||||||
|
'MEMBER WITH ADMIN OPTION') as admin;
|
||||||
|
priv | mem | admin
|
||||||
|
------+-----+-------
|
||||||
|
f | f | f
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
DO $$BEGIN EXECUTE format(
|
||||||
|
'ALTER DATABASE %I OWNER TO regress_priv_group2', current_catalog); END$$;
|
||||||
|
SELECT
|
||||||
|
pg_has_role('regress_priv_user1', 'pg_database_owner', 'USAGE') as priv,
|
||||||
|
pg_has_role('regress_priv_user1', 'pg_database_owner', 'MEMBER') as mem,
|
||||||
|
pg_has_role('regress_priv_user1', 'pg_database_owner',
|
||||||
|
'MEMBER WITH ADMIN OPTION') as admin;
|
||||||
|
priv | mem | admin
|
||||||
|
------+-----+-------
|
||||||
|
t | t | f
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SET SESSION AUTHORIZATION regress_priv_user1;
|
||||||
|
TABLE information_schema.enabled_roles ORDER BY role_name COLLATE "C";
|
||||||
|
role_name
|
||||||
|
---------------------
|
||||||
|
pg_database_owner
|
||||||
|
regress_priv_group2
|
||||||
|
regress_priv_user1
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
TABLE information_schema.applicable_roles ORDER BY role_name COLLATE "C";
|
||||||
|
grantee | role_name | is_grantable
|
||||||
|
---------------------+---------------------+--------------
|
||||||
|
regress_priv_group2 | pg_database_owner | NO
|
||||||
|
regress_priv_user1 | regress_priv_group2 | NO
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
INSERT INTO datdba_only DEFAULT VALUES;
|
||||||
|
SAVEPOINT q; DELETE FROM datdba_only; ROLLBACK TO q;
|
||||||
|
ERROR: permission denied for table datdba_only
|
||||||
|
SET SESSION AUTHORIZATION regress_priv_user2;
|
||||||
|
TABLE information_schema.enabled_roles;
|
||||||
|
role_name
|
||||||
|
--------------------
|
||||||
|
regress_priv_user2
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
INSERT INTO datdba_only DEFAULT VALUES;
|
||||||
|
ERROR: permission denied for table datdba_only
|
||||||
|
ROLLBACK;
|
||||||
-- test default ACLs
|
-- test default ACLs
|
||||||
\c -
|
\c -
|
||||||
CREATE SCHEMA testns;
|
CREATE SCHEMA testns;
|
||||||
|
@ -1034,6 +1034,37 @@ SELECT * FROM pg_largeobject LIMIT 0;
|
|||||||
SET SESSION AUTHORIZATION regress_priv_user1;
|
SET SESSION AUTHORIZATION regress_priv_user1;
|
||||||
SELECT * FROM pg_largeobject LIMIT 0; -- to be denied
|
SELECT * FROM pg_largeobject LIMIT 0; -- to be denied
|
||||||
|
|
||||||
|
-- test pg_database_owner
|
||||||
|
RESET SESSION AUTHORIZATION;
|
||||||
|
GRANT pg_database_owner TO regress_priv_user1;
|
||||||
|
GRANT regress_priv_user1 TO pg_database_owner;
|
||||||
|
CREATE TABLE datdba_only ();
|
||||||
|
ALTER TABLE datdba_only OWNER TO pg_database_owner;
|
||||||
|
REVOKE DELETE ON datdba_only FROM pg_database_owner;
|
||||||
|
SELECT
|
||||||
|
pg_has_role('regress_priv_user1', 'pg_database_owner', 'USAGE') as priv,
|
||||||
|
pg_has_role('regress_priv_user1', 'pg_database_owner', 'MEMBER') as mem,
|
||||||
|
pg_has_role('regress_priv_user1', 'pg_database_owner',
|
||||||
|
'MEMBER WITH ADMIN OPTION') as admin;
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
DO $$BEGIN EXECUTE format(
|
||||||
|
'ALTER DATABASE %I OWNER TO regress_priv_group2', current_catalog); END$$;
|
||||||
|
SELECT
|
||||||
|
pg_has_role('regress_priv_user1', 'pg_database_owner', 'USAGE') as priv,
|
||||||
|
pg_has_role('regress_priv_user1', 'pg_database_owner', 'MEMBER') as mem,
|
||||||
|
pg_has_role('regress_priv_user1', 'pg_database_owner',
|
||||||
|
'MEMBER WITH ADMIN OPTION') as admin;
|
||||||
|
SET SESSION AUTHORIZATION regress_priv_user1;
|
||||||
|
TABLE information_schema.enabled_roles ORDER BY role_name COLLATE "C";
|
||||||
|
TABLE information_schema.applicable_roles ORDER BY role_name COLLATE "C";
|
||||||
|
INSERT INTO datdba_only DEFAULT VALUES;
|
||||||
|
SAVEPOINT q; DELETE FROM datdba_only; ROLLBACK TO q;
|
||||||
|
SET SESSION AUTHORIZATION regress_priv_user2;
|
||||||
|
TABLE information_schema.enabled_roles;
|
||||||
|
INSERT INTO datdba_only DEFAULT VALUES;
|
||||||
|
ROLLBACK;
|
||||||
|
|
||||||
-- test default ACLs
|
-- test default ACLs
|
||||||
\c -
|
\c -
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user