Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit088c833

Browse files
committed
ALTER TABLE .. FORCE ROW LEVEL SECURITY
To allow users to force RLS to always be applied, even for table owners,add ALTER TABLE .. FORCE ROW LEVEL SECURITY.row_security=off overrides FORCE ROW LEVEL SECURITY, to ensure pg_dumpoutput is complete (by default).Also add SECURITY_NOFORCE_RLS context to avoid data corruption whenALTER TABLE .. FORCE ROW SECURITY is being used. TheSECURITY_NOFORCE_RLS security context is used only during referentialintegrity checks and is only considered in check_enable_rls() after wehave already checked that the current user is the owner of the relation(which should always be the case during referential integrity checks).Back-patch to 9.5 where RLS was added.
1 parent16a70e3 commit088c833

File tree

19 files changed

+537
-64
lines changed

19 files changed

+537
-64
lines changed

‎doc/src/sgml/catalogs.sgml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1971,6 +1971,16 @@
19711971
</entry>
19721972
</row>
19731973

1974+
<row>
1975+
<entry><structfield>relforcerowsecurity</structfield></entry>
1976+
<entry><type>bool</type></entry>
1977+
<entry></entry>
1978+
<entry>
1979+
True if row level security (when enabled) will also apply to table owner; see
1980+
<link linkend="catalog-pg-policy"><structname>pg_policy</structname></link> catalog
1981+
</entry>
1982+
</row>
1983+
19741984
<row>
19751985
<entry><structfield>relispopulated</structfield></entry>
19761986
<entry><type>bool</type></entry>

‎doc/src/sgml/ref/alter_table.sgml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ ALTER TABLE ALL IN TABLESPACE <replaceable class="PARAMETER">name</replaceable>
6161
ENABLE ALWAYS RULE <replaceable class="PARAMETER">rewrite_rule_name</replaceable>
6262
DISABLE ROW LEVEL SECURITY
6363
ENABLE ROW LEVEL SECURITY
64+
FORCE ROW LEVEL SECURITY
65+
NO FORCE ROW LEVEL SECURITY
6466
CLUSTER ON <replaceable class="PARAMETER">index_name</replaceable>
6567
SET WITHOUT CLUSTER
6668
SET WITH OIDS
@@ -433,6 +435,21 @@ ALTER TABLE ALL IN TABLESPACE <replaceable class="PARAMETER">name</replaceable>
433435
</listitem>
434436
</varlistentry>
435437

438+
<varlistentry>
439+
<term><literal>NO FORCE</literal>/<literal>FORCE ROW LEVEL SECURITY</literal></term>
440+
<listitem>
441+
<para>
442+
These forms control the application of row security policies belonging
443+
to the table when the user is the table owner. If enabled, row level
444+
security policies will be applied when the user is the table owner. If
445+
disabled (the default) then row level security will not be applied when
446+
the user is the table owner.
447+
See also
448+
<xref linkend="SQL-CREATEPOLICY">.
449+
</para>
450+
</listitem>
451+
</varlistentry>
452+
436453
<varlistentry>
437454
<term><literal>CLUSTER ON</literal></term>
438455
<listitem>

‎src/backend/catalog/heap.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,7 @@ InsertPgClassTuple(Relation pg_class_desc,
802802
values[Anum_pg_class_relhasrules-1]=BoolGetDatum(rd_rel->relhasrules);
803803
values[Anum_pg_class_relhastriggers-1]=BoolGetDatum(rd_rel->relhastriggers);
804804
values[Anum_pg_class_relrowsecurity-1]=BoolGetDatum(rd_rel->relrowsecurity);
805+
values[Anum_pg_class_relforcerowsecurity-1]=BoolGetDatum(rd_rel->relforcerowsecurity);
805806
values[Anum_pg_class_relhassubclass-1]=BoolGetDatum(rd_rel->relhassubclass);
806807
values[Anum_pg_class_relispopulated-1]=BoolGetDatum(rd_rel->relispopulated);
807808
values[Anum_pg_class_relreplident-1]=CharGetDatum(rd_rel->relreplident);

‎src/backend/commands/tablecmds.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,7 @@ static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKM
419419
staticvoidATExecGenericOptions(Relationrel,List*options);
420420
staticvoidATExecEnableRowSecurity(Relationrel);
421421
staticvoidATExecDisableRowSecurity(Relationrel);
422+
staticvoidATExecForceNoForceRowSecurity(Relationrel,boolforce_rls);
422423

423424
staticvoidcopy_relation_data(SMgrRelationrel,SMgrRelationdst,
424425
ForkNumberforkNum,charrelpersistence);
@@ -2930,6 +2931,8 @@ AlterTableGetLockLevel(List *cmds)
29302931
caseAT_SetNotNull:
29312932
caseAT_EnableRowSecurity:
29322933
caseAT_DisableRowSecurity:
2934+
caseAT_ForceRowSecurity:
2935+
caseAT_NoForceRowSecurity:
29332936
cmd_lockmode=AccessExclusiveLock;
29342937
break;
29352938

@@ -3351,6 +3354,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
33513354
caseAT_DropOf:/* NOT OF */
33523355
caseAT_EnableRowSecurity:
33533356
caseAT_DisableRowSecurity:
3357+
caseAT_ForceRowSecurity:
3358+
caseAT_NoForceRowSecurity:
33543359
ATSimplePermissions(rel,ATT_TABLE);
33553360
/* These commands never recurse */
33563361
/* No command-specific prep needed */
@@ -3667,6 +3672,12 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
36673672
caseAT_DisableRowSecurity:
36683673
ATExecDisableRowSecurity(rel);
36693674
break;
3675+
caseAT_ForceRowSecurity:
3676+
ATExecForceNoForceRowSecurity(rel, true);
3677+
break;
3678+
caseAT_NoForceRowSecurity:
3679+
ATExecForceNoForceRowSecurity(rel, false);
3680+
break;
36703681
caseAT_GenericOptions:
36713682
ATExecGenericOptions(rel, (List*)cmd->def);
36723683
break;
@@ -11066,6 +11077,35 @@ ATExecDisableRowSecurity(Relation rel)
1106611077
heap_freetuple(tuple);
1106711078
}
1106811079

11080+
/*
11081+
* ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
11082+
*/
11083+
staticvoid
11084+
ATExecForceNoForceRowSecurity(Relationrel,boolforce_rls)
11085+
{
11086+
Relationpg_class;
11087+
Oidrelid;
11088+
HeapTupletuple;
11089+
11090+
relid=RelationGetRelid(rel);
11091+
11092+
pg_class=heap_open(RelationRelationId,RowExclusiveLock);
11093+
11094+
tuple=SearchSysCacheCopy1(RELOID,ObjectIdGetDatum(relid));
11095+
11096+
if (!HeapTupleIsValid(tuple))
11097+
elog(ERROR,"cache lookup failed for relation %u",relid);
11098+
11099+
((Form_pg_class)GETSTRUCT(tuple))->relforcerowsecurity=force_rls;
11100+
simple_heap_update(pg_class,&tuple->t_self,tuple);
11101+
11102+
/* keep catalog indexes current */
11103+
CatalogUpdateIndexes(pg_class,tuple);
11104+
11105+
heap_close(pg_class,RowExclusiveLock);
11106+
heap_freetuple(tuple);
11107+
}
11108+
1106911109
/*
1107011110
* ALTER FOREIGN TABLE <name> OPTIONS (...)
1107111111
*/

‎src/backend/parser/gram.y

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2353,6 +2353,20 @@ alter_table_cmd:
23532353
n->subtype = AT_DisableRowSecurity;
23542354
$$ = (Node *)n;
23552355
}
2356+
/* ALTER TABLE <name> FORCE ROW LEVEL SECURITY*/
2357+
|FORCEROWLEVELSECURITY
2358+
{
2359+
AlterTableCmd *n = makeNode(AlterTableCmd);
2360+
n->subtype = AT_ForceRowSecurity;
2361+
$$ = (Node *)n;
2362+
}
2363+
/* ALTER TABLE <name> NO FORCE ROW LEVEL SECURITY*/
2364+
|NOFORCEROWLEVELSECURITY
2365+
{
2366+
AlterTableCmd *n = makeNode(AlterTableCmd);
2367+
n->subtype = AT_NoForceRowSecurity;
2368+
$$ = (Node *)n;
2369+
}
23562370
|alter_generic_options
23572371
{
23582372
AlterTableCmd *n = makeNode(AlterTableCmd);

‎src/backend/utils/adt/ri_triggers.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3014,7 +3014,8 @@ ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes,
30143014
/* Switch to proper UID to perform check as */
30153015
GetUserIdAndSecContext(&save_userid,&save_sec_context);
30163016
SetUserIdAndSecContext(RelationGetForm(query_rel)->relowner,
3017-
save_sec_context |SECURITY_LOCAL_USERID_CHANGE);
3017+
save_sec_context |SECURITY_LOCAL_USERID_CHANGE |
3018+
SECURITY_NOFORCE_RLS);
30183019

30193020
/* Create the plan */
30203021
qplan=SPI_prepare(querystr,nargs,argtypes);
@@ -3134,7 +3135,8 @@ ri_PerformCheck(const RI_ConstraintInfo *riinfo,
31343135
/* Switch to proper UID to perform check as */
31353136
GetUserIdAndSecContext(&save_userid,&save_sec_context);
31363137
SetUserIdAndSecContext(RelationGetForm(query_rel)->relowner,
3137-
save_sec_context |SECURITY_LOCAL_USERID_CHANGE);
3138+
save_sec_context |SECURITY_LOCAL_USERID_CHANGE |
3139+
SECURITY_NOFORCE_RLS);
31383140

31393141
/* Finally we can run the query. */
31403142
spi_result=SPI_execute_snapshot(qplan,

‎src/backend/utils/init/miscinit.c

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ GetAuthenticatedUserId(void)
341341
* GetUserIdAndSecContext/SetUserIdAndSecContext - get/set the current user ID
342342
* and the SecurityRestrictionContext flags.
343343
*
344-
* Currently there aretwo valid bits in SecurityRestrictionContext:
344+
* Currently there arethree valid bits in SecurityRestrictionContext:
345345
*
346346
* SECURITY_LOCAL_USERID_CHANGE indicates that we are inside an operation
347347
* that is temporarily changing CurrentUserId via these functions. This is
@@ -359,6 +359,13 @@ GetAuthenticatedUserId(void)
359359
* where the called functions are really supposed to be side-effect-free
360360
* anyway, such as VACUUM/ANALYZE/REINDEX.
361361
*
362+
* SECURITY_NOFORCE_RLS indicates that we are inside an operation which should
363+
* ignore the FORCE ROW LEVEL SECURITY per-table indication. This is used to
364+
* ensure that FORCE RLS does not mistakenly break referential integrity
365+
* checks. Note that this is intentionally only checked when running as the
366+
* owner of the table (which should always be the case for referential
367+
* integrity checks).
368+
*
362369
* Unlike GetUserId, GetUserIdAndSecContext does *not* Assert that the current
363370
* value of CurrentUserId is valid; nor does SetUserIdAndSecContext require
364371
* the new value to be valid. In fact, these routines had better not
@@ -401,6 +408,15 @@ InSecurityRestrictedOperation(void)
401408
return (SecurityRestrictionContext&SECURITY_RESTRICTED_OPERATION)!=0;
402409
}
403410

411+
/*
412+
* InNoForceRLSOperation - are we ignoring FORCE ROW LEVEL SECURITY ?
413+
*/
414+
bool
415+
InNoForceRLSOperation(void)
416+
{
417+
return (SecurityRestrictionContext&SECURITY_NOFORCE_RLS)!=0;
418+
}
419+
404420

405421
/*
406422
* These are obsolete versions of Get/SetUserIdAndSecContext that are

‎src/backend/utils/misc/rls.c

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
5555
HeapTupletuple;
5656
Form_pg_classclassform;
5757
boolrelrowsecurity;
58+
boolrelforcerowsecurity;
5859
Oiduser_id=checkAsUser ?checkAsUser :GetUserId();
5960

6061
/* Nothing to do for built-in relations */
@@ -68,6 +69,7 @@ check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
6869
classform= (Form_pg_class)GETSTRUCT(tuple);
6970

7071
relrowsecurity=classform->relrowsecurity;
72+
relforcerowsecurity=classform->relforcerowsecurity;
7173

7274
ReleaseSysCache(tuple);
7375

@@ -76,14 +78,46 @@ check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
7678
returnRLS_NONE;
7779

7880
/*
79-
* Table owners and BYPASSRLS users bypass RLS. Note that a superuser
80-
* qualifies as both. Return RLS_NONE_ENV to indicate that this decision
81-
* depends on the environment (in this case, the user_id).
81+
* BYPASSRLS users always bypass RLS. Note that superusers are always
82+
* considered to have BYPASSRLS.
83+
*
84+
* Return RLS_NONE_ENV to indicate that this decision depends on the
85+
* environment (in this case, the user_id).
8286
*/
83-
if (pg_class_ownercheck(relid,user_id)||
84-
has_bypassrls_privilege(user_id))
87+
if (has_bypassrls_privilege(user_id))
8588
returnRLS_NONE_ENV;
8689

90+
/*
91+
* Table owners generally bypass RLS, except if row_security=true and the
92+
* table has been set (by an owner) to FORCE ROW SECURITY, and this is not
93+
* a referential integrity check.
94+
*
95+
* Return RLS_NONE_ENV to indicate that this decision depends on the
96+
* environment (in this case, the user_id).
97+
*/
98+
if (pg_class_ownercheck(relid,user_id))
99+
{
100+
/*
101+
* If row_security=true and FORCE ROW LEVEL SECURITY has been set on
102+
* the relation then we return RLS_ENABLED to indicate that RLS should
103+
* still be applied. If we are in a SECURITY_NOFORCE_RLS context or if
104+
* row_security=false then we return RLS_NONE_ENV.
105+
*
106+
* The SECURITY_NOFORCE_RLS indicates that we should not apply RLS even
107+
* if the table has FORCE RLS set- IF the current user is the owner.
108+
* This is specifically to ensure that referential integrity checks are
109+
* able to still run correctly.
110+
*
111+
* This is intentionally only done after we have checked that the user
112+
* is the table owner, which should always be the case for referential
113+
* integrity checks.
114+
*/
115+
if (row_security&&relforcerowsecurity&& !InNoForceRLSOperation())
116+
returnRLS_ENABLED;
117+
else
118+
returnRLS_NONE_ENV;
119+
}
120+
87121
/* row_security GUC says to bypass RLS, but user lacks permission */
88122
if (!row_security&& !noError)
89123
ereport(ERROR,

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp