Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 6566133

Browse files
committed
Ensure that pg_auth_members.grantor is always valid.
Previously, "GRANT foo TO bar" or "GRANT foo TO bar GRANTED BY baz" would record the OID of the grantor in pg_auth_members.grantor, but that role could later be dropped without modifying or removing the pg_auth_members record. That's not great, because we typically try to avoid dangling references in catalog data. Now, a role grant depends on the grantor, and the grantor can't be dropped without removing the grant or changing the grantor. "DROP OWNED BY" will remove the grant, just as it does for other kinds of privileges. "REASSIGN OWNED BY" will not, again just like what we do in other cases involving privileges. pg_auth_members now has an OID column, because that is needed in order for dependencies to work. It also now has an index on the grantor column, because otherwise dropping a role would require a sequential scan of the entire table to see whether the role's OID is in use as a grantor. That probably wouldn't be too large a problem in practice, but it seems better to have an index just in case. A follow-on patch is planned with the goal of more thoroughly rationalizing the behavior of role grants. This patch is just trying to do enough to make sure that the data we store in the catalogs is at some basic level valid. Patch by me, reviewed by Stephen Frost Discussion: http://postgr.es/m/CA+TgmoaFr-RZeQ+WoQ5nKPv97oT9+aDgK_a5+qWHSgbDsMp1Vg@mail.gmail.com
1 parent 2f17b57 commit 6566133

16 files changed

+397
-68
lines changed

doc/src/sgml/catalogs.sgml

+9
Original file line numberDiff line numberDiff line change
@@ -1669,6 +1669,15 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
16691669
</thead>
16701670

16711671
<tbody>
1672+
<row>
1673+
<entry role="catalog_table_entry"><para role="column_definition">
1674+
<structfield>oid</structfield> <type>oid</type>
1675+
</para>
1676+
<para>
1677+
Row identifier
1678+
</para></entry>
1679+
</row>
1680+
16721681
<row>
16731682
<entry role="catalog_table_entry"><para role="column_definition">
16741683
<structfield>roleid</structfield> <type>oid</type>

src/backend/catalog/catalog.c

+2
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,8 @@ IsSharedRelation(Oid relationId)
262262
relationId == AuthIdRolnameIndexId ||
263263
relationId == AuthMemMemRoleIndexId ||
264264
relationId == AuthMemRoleMemIndexId ||
265+
relationId == AuthMemOidIndexId ||
266+
relationId == AuthMemGrantorIndexId ||
265267
relationId == DatabaseNameIndexId ||
266268
relationId == DatabaseOidIndexId ||
267269
relationId == DbRoleSettingDatidRolidIndexId ||

src/backend/catalog/dependency.c

+11-3
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "catalog/pg_amproc.h"
2929
#include "catalog/pg_attrdef.h"
3030
#include "catalog/pg_authid.h"
31+
#include "catalog/pg_auth_members.h"
3132
#include "catalog/pg_cast.h"
3233
#include "catalog/pg_collation.h"
3334
#include "catalog/pg_constraint.h"
@@ -172,6 +173,7 @@ static const Oid object_classes[] = {
172173
TSTemplateRelationId, /* OCLASS_TSTEMPLATE */
173174
TSConfigRelationId, /* OCLASS_TSCONFIG */
174175
AuthIdRelationId, /* OCLASS_ROLE */
176+
AuthMemRelationId, /* OCLASS_ROLE_MEMBERSHIP */
175177
DatabaseRelationId, /* OCLASS_DATABASE */
176178
TableSpaceRelationId, /* OCLASS_TBLSPACE */
177179
ForeignDataWrapperRelationId, /* OCLASS_FDW */
@@ -1502,6 +1504,7 @@ doDeletion(const ObjectAddress *object, int flags)
15021504
case OCLASS_DEFACL:
15031505
case OCLASS_EVENT_TRIGGER:
15041506
case OCLASS_TRANSFORM:
1507+
case OCLASS_ROLE_MEMBERSHIP:
15051508
DropObjectById(object);
15061509
break;
15071510

@@ -1529,9 +1532,8 @@ doDeletion(const ObjectAddress *object, int flags)
15291532
* Accepts the same flags as performDeletion (though currently only
15301533
* PERFORM_DELETION_CONCURRENTLY does anything).
15311534
*
1532-
* We use LockRelation for relations, LockDatabaseObject for everything
1533-
* else. Shared-across-databases objects are not currently supported
1534-
* because no caller cares, but could be modified to use LockSharedObject.
1535+
* We use LockRelation for relations, and otherwise LockSharedObject or
1536+
* LockDatabaseObject as appropriate for the object type.
15351537
*/
15361538
void
15371539
AcquireDeletionLock(const ObjectAddress *object, int flags)
@@ -1549,6 +1551,9 @@ AcquireDeletionLock(const ObjectAddress *object, int flags)
15491551
else
15501552
LockRelationOid(object->objectId, AccessExclusiveLock);
15511553
}
1554+
else if (object->classId == AuthMemRelationId)
1555+
LockSharedObject(object->classId, object->objectId, 0,
1556+
AccessExclusiveLock);
15521557
else
15531558
{
15541559
/* assume we should lock the whole object not a sub-object */
@@ -2914,6 +2919,9 @@ getObjectClass(const ObjectAddress *object)
29142919
case AuthIdRelationId:
29152920
return OCLASS_ROLE;
29162921

2922+
case AuthMemRelationId:
2923+
return OCLASS_ROLE_MEMBERSHIP;
2924+
29172925
case DatabaseRelationId:
29182926
return OCLASS_DATABASE;
29192927

src/backend/catalog/objectaddress.c

+108
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "catalog/pg_amproc.h"
2828
#include "catalog/pg_attrdef.h"
2929
#include "catalog/pg_authid.h"
30+
#include "catalog/pg_auth_members.h"
3031
#include "catalog/pg_cast.h"
3132
#include "catalog/pg_collation.h"
3233
#include "catalog/pg_constraint.h"
@@ -386,6 +387,20 @@ static const ObjectPropertyType ObjectProperty[] =
386387
-1,
387388
true
388389
},
390+
{
391+
"role membership",
392+
AuthMemRelationId,
393+
AuthMemOidIndexId,
394+
-1,
395+
-1,
396+
Anum_pg_auth_members_oid,
397+
InvalidAttrNumber,
398+
InvalidAttrNumber,
399+
Anum_pg_auth_members_grantor,
400+
InvalidAttrNumber,
401+
-1,
402+
true
403+
},
389404
{
390405
"rule",
391406
RewriteRelationId,
@@ -787,6 +802,10 @@ static const struct object_type_map
787802
{
788803
"role", OBJECT_ROLE
789804
},
805+
/* OCLASS_ROLE_MEMBERSHIP */
806+
{
807+
"role membership", -1 /* unmapped */
808+
},
790809
/* OCLASS_DATABASE */
791810
{
792811
"database", OBJECT_DATABASE
@@ -3644,6 +3663,48 @@ getObjectDescription(const ObjectAddress *object, bool missing_ok)
36443663
break;
36453664
}
36463665

3666+
case OCLASS_ROLE_MEMBERSHIP:
3667+
{
3668+
Relation amDesc;
3669+
ScanKeyData skey[1];
3670+
SysScanDesc rcscan;
3671+
HeapTuple tup;
3672+
Form_pg_auth_members amForm;
3673+
3674+
amDesc = table_open(AuthMemRelationId, AccessShareLock);
3675+
3676+
ScanKeyInit(&skey[0],
3677+
Anum_pg_auth_members_oid,
3678+
BTEqualStrategyNumber, F_OIDEQ,
3679+
ObjectIdGetDatum(object->objectId));
3680+
3681+
rcscan = systable_beginscan(amDesc, AuthMemOidIndexId, true,
3682+
NULL, 1, skey);
3683+
3684+
tup = systable_getnext(rcscan);
3685+
3686+
if (!HeapTupleIsValid(tup))
3687+
{
3688+
if (!missing_ok)
3689+
elog(ERROR, "could not find tuple for role membership %u",
3690+
object->objectId);
3691+
3692+
systable_endscan(rcscan);
3693+
table_close(amDesc, AccessShareLock);
3694+
break;
3695+
}
3696+
3697+
amForm = (Form_pg_auth_members) GETSTRUCT(tup);
3698+
3699+
appendStringInfo(&buffer, _("membership of role %s in role %s"),
3700+
GetUserNameFromId(amForm->member, false),
3701+
GetUserNameFromId(amForm->roleid, false));
3702+
3703+
systable_endscan(rcscan);
3704+
table_close(amDesc, AccessShareLock);
3705+
break;
3706+
}
3707+
36473708
case OCLASS_DATABASE:
36483709
{
36493710
char *datname;
@@ -4533,6 +4594,10 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
45334594
appendStringInfoString(&buffer, "role");
45344595
break;
45354596

4597+
case OCLASS_ROLE_MEMBERSHIP:
4598+
appendStringInfoString(&buffer, "role membership");
4599+
break;
4600+
45364601
case OCLASS_DATABASE:
45374602
appendStringInfoString(&buffer, "database");
45384603
break;
@@ -5476,6 +5541,49 @@ getObjectIdentityParts(const ObjectAddress *object,
54765541
break;
54775542
}
54785543

5544+
case OCLASS_ROLE_MEMBERSHIP:
5545+
{
5546+
Relation authMemDesc;
5547+
ScanKeyData skey[1];
5548+
SysScanDesc amscan;
5549+
HeapTuple tup;
5550+
Form_pg_auth_members amForm;
5551+
5552+
authMemDesc = table_open(AuthMemRelationId,
5553+
AccessShareLock);
5554+
5555+
ScanKeyInit(&skey[0],
5556+
Anum_pg_auth_members_oid,
5557+
BTEqualStrategyNumber, F_OIDEQ,
5558+
ObjectIdGetDatum(object->objectId));
5559+
5560+
amscan = systable_beginscan(authMemDesc, AuthMemOidIndexId, true,
5561+
NULL, 1, skey);
5562+
5563+
tup = systable_getnext(amscan);
5564+
5565+
if (!HeapTupleIsValid(tup))
5566+
{
5567+
if (!missing_ok)
5568+
elog(ERROR, "could not find tuple for pg_auth_members entry %u",
5569+
object->objectId);
5570+
5571+
systable_endscan(amscan);
5572+
table_close(authMemDesc, AccessShareLock);
5573+
break;
5574+
}
5575+
5576+
amForm = (Form_pg_auth_members) GETSTRUCT(tup);
5577+
5578+
appendStringInfo(&buffer, _("membership of role %s in role %s"),
5579+
GetUserNameFromId(amForm->member, false),
5580+
GetUserNameFromId(amForm->roleid, false));
5581+
5582+
systable_endscan(amscan);
5583+
table_close(authMemDesc, AccessShareLock);
5584+
break;
5585+
}
5586+
54795587
case OCLASS_DATABASE:
54805588
{
54815589
char *datname;

src/backend/catalog/pg_shdepend.c

+29-18
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "catalog/dependency.h"
2323
#include "catalog/indexing.h"
2424
#include "catalog/pg_authid.h"
25+
#include "catalog/pg_auth_members.h"
2526
#include "catalog/pg_collation.h"
2627
#include "catalog/pg_conversion.h"
2728
#include "catalog/pg_database.h"
@@ -1364,11 +1365,6 @@ shdepDropOwned(List *roleids, DropBehavior behavior)
13641365
case SHARED_DEPENDENCY_INVALID:
13651366
elog(ERROR, "unexpected dependency type");
13661367
break;
1367-
case SHARED_DEPENDENCY_ACL:
1368-
RemoveRoleFromObjectACL(roleid,
1369-
sdepForm->classid,
1370-
sdepForm->objid);
1371-
break;
13721368
case SHARED_DEPENDENCY_POLICY:
13731369

13741370
/*
@@ -1398,22 +1394,37 @@ shdepDropOwned(List *roleids, DropBehavior behavior)
13981394
add_exact_object_address(&obj, deleteobjs);
13991395
}
14001396
break;
1397+
case SHARED_DEPENDENCY_ACL:
1398+
1399+
/*
1400+
* Dependencies on role grants are recorded using
1401+
* SHARED_DEPENDENCY_ACL, but unlike a regular ACL list
1402+
* which stores all permissions for a particular object in
1403+
* a single ACL array, there's a separate catalog row for
1404+
* each grant - so removing the grant just means removing
1405+
* the entire row.
1406+
*/
1407+
if (sdepForm->classid != AuthMemRelationId)
1408+
{
1409+
RemoveRoleFromObjectACL(roleid,
1410+
sdepForm->classid,
1411+
sdepForm->objid);
1412+
break;
1413+
}
1414+
/* FALLTHROUGH */
14011415
case SHARED_DEPENDENCY_OWNER:
1402-
/* If a local object, save it for deletion below */
1403-
if (sdepForm->dbid == MyDatabaseId)
1416+
/* Save it for deletion below */
1417+
obj.classId = sdepForm->classid;
1418+
obj.objectId = sdepForm->objid;
1419+
obj.objectSubId = sdepForm->objsubid;
1420+
/* as above */
1421+
AcquireDeletionLock(&obj, 0);
1422+
if (!systable_recheck_tuple(scan, tuple))
14041423
{
1405-
obj.classId = sdepForm->classid;
1406-
obj.objectId = sdepForm->objid;
1407-
obj.objectSubId = sdepForm->objsubid;
1408-
/* as above */
1409-
AcquireDeletionLock(&obj, 0);
1410-
if (!systable_recheck_tuple(scan, tuple))
1411-
{
1412-
ReleaseDeletionLock(&obj);
1413-
break;
1414-
}
1415-
add_exact_object_address(&obj, deleteobjs);
1424+
ReleaseDeletionLock(&obj);
1425+
break;
14161426
}
1427+
add_exact_object_address(&obj, deleteobjs);
14171428
break;
14181429
}
14191430
}

src/backend/catalog/system_views.sql

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ CREATE VIEW pg_group AS
5353
SELECT
5454
rolname AS groname,
5555
oid AS grosysid,
56-
ARRAY(SELECT member FROM pg_auth_members WHERE roleid = oid) AS grolist
56+
ARRAY(SELECT member FROM pg_auth_members WHERE roleid = pg_authid.oid) AS grolist
5757
FROM pg_authid
5858
WHERE NOT rolcanlogin;
5959

src/backend/commands/alter.c

+1
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,7 @@ AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
650650
case OCLASS_TRIGGER:
651651
case OCLASS_SCHEMA:
652652
case OCLASS_ROLE:
653+
case OCLASS_ROLE_MEMBERSHIP:
653654
case OCLASS_DATABASE:
654655
case OCLASS_TBLSPACE:
655656
case OCLASS_FDW:

src/backend/commands/event_trigger.c

+1
Original file line numberDiff line numberDiff line change
@@ -1015,6 +1015,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
10151015
case OCLASS_DATABASE:
10161016
case OCLASS_TBLSPACE:
10171017
case OCLASS_ROLE:
1018+
case OCLASS_ROLE_MEMBERSHIP:
10181019
case OCLASS_PARAMETER_ACL:
10191020
/* no support for global objects */
10201021
return false;

src/backend/commands/tablecmds.c

+1
Original file line numberDiff line numberDiff line change
@@ -12667,6 +12667,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
1266712667
case OCLASS_TSTEMPLATE:
1266812668
case OCLASS_TSCONFIG:
1266912669
case OCLASS_ROLE:
12670+
case OCLASS_ROLE_MEMBERSHIP:
1267012671
case OCLASS_DATABASE:
1267112672
case OCLASS_TBLSPACE:
1267212673
case OCLASS_FDW:

0 commit comments

Comments
 (0)