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

Commit90f334d

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 parente78dc6b commit90f334d

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
@@ -431,6 +433,21 @@ ALTER TABLE ALL IN TABLESPACE <replaceable class="PARAMETER">name</replaceable>
431433
</listitem>
432434
</varlistentry>
433435

436+
<varlistentry>
437+
<term><literal>NO FORCE</literal>/<literal>FORCE ROW LEVEL SECURITY</literal></term>
438+
<listitem>
439+
<para>
440+
These forms control the application of row security policies belonging
441+
to the table when the user is the table owner. If enabled, row level
442+
security policies will be applied when the user is the table owner. If
443+
disabled (the default) then row level security will not be applied when
444+
the user is the table owner.
445+
See also
446+
<xref linkend="SQL-CREATEPOLICY">.
447+
</para>
448+
</listitem>
449+
</varlistentry>
450+
434451
<varlistentry>
435452
<term><literal>CLUSTER ON</literal></term>
436453
<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
@@ -418,6 +418,7 @@ static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKM
418418
staticvoidATExecGenericOptions(Relationrel,List*options);
419419
staticvoidATExecEnableRowSecurity(Relationrel);
420420
staticvoidATExecDisableRowSecurity(Relationrel);
421+
staticvoidATExecForceNoForceRowSecurity(Relationrel,boolforce_rls);
421422

422423
staticvoidcopy_relation_data(SMgrRelationrel,SMgrRelationdst,
423424
ForkNumberforkNum,charrelpersistence);
@@ -2929,6 +2930,8 @@ AlterTableGetLockLevel(List *cmds)
29292930
caseAT_SetNotNull:
29302931
caseAT_EnableRowSecurity:
29312932
caseAT_DisableRowSecurity:
2933+
caseAT_ForceRowSecurity:
2934+
caseAT_NoForceRowSecurity:
29322935
cmd_lockmode=AccessExclusiveLock;
29332936
break;
29342937

@@ -3354,6 +3357,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
33543357
caseAT_DropOf:/* NOT OF */
33553358
caseAT_EnableRowSecurity:
33563359
caseAT_DisableRowSecurity:
3360+
caseAT_ForceRowSecurity:
3361+
caseAT_NoForceRowSecurity:
33573362
ATSimplePermissions(rel,ATT_TABLE);
33583363
/* These commands never recurse */
33593364
/* No command-specific prep needed */
@@ -3670,6 +3675,12 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
36703675
caseAT_DisableRowSecurity:
36713676
ATExecDisableRowSecurity(rel);
36723677
break;
3678+
caseAT_ForceRowSecurity:
3679+
ATExecForceNoForceRowSecurity(rel, true);
3680+
break;
3681+
caseAT_NoForceRowSecurity:
3682+
ATExecForceNoForceRowSecurity(rel, false);
3683+
break;
36733684
caseAT_GenericOptions:
36743685
ATExecGenericOptions(rel, (List*)cmd->def);
36753686
break;
@@ -11048,6 +11059,35 @@ ATExecDisableRowSecurity(Relation rel)
1104811059
heap_freetuple(tuple);
1104911060
}
1105011061

11062+
/*
11063+
* ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
11064+
*/
11065+
staticvoid
11066+
ATExecForceNoForceRowSecurity(Relationrel,boolforce_rls)
11067+
{
11068+
Relationpg_class;
11069+
Oidrelid;
11070+
HeapTupletuple;
11071+
11072+
relid=RelationGetRelid(rel);
11073+
11074+
pg_class=heap_open(RelationRelationId,RowExclusiveLock);
11075+
11076+
tuple=SearchSysCacheCopy1(RELOID,ObjectIdGetDatum(relid));
11077+
11078+
if (!HeapTupleIsValid(tuple))
11079+
elog(ERROR,"cache lookup failed for relation %u",relid);
11080+
11081+
((Form_pg_class)GETSTRUCT(tuple))->relforcerowsecurity=force_rls;
11082+
simple_heap_update(pg_class,&tuple->t_self,tuple);
11083+
11084+
/* keep catalog indexes current */
11085+
CatalogUpdateIndexes(pg_class,tuple);
11086+
11087+
heap_close(pg_class,RowExclusiveLock);
11088+
heap_freetuple(tuple);
11089+
}
11090+
1105111091
/*
1105211092
* ALTER FOREIGN TABLE <name> OPTIONS (...)
1105311093
*/

‎src/backend/parser/gram.y

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2332,6 +2332,20 @@ alter_table_cmd:
23322332
n->subtype = AT_DisableRowSecurity;
23332333
$$ = (Node *)n;
23342334
}
2335+
/* ALTER TABLE <name> FORCE ROW LEVEL SECURITY*/
2336+
|FORCEROWLEVELSECURITY
2337+
{
2338+
AlterTableCmd *n = makeNode(AlterTableCmd);
2339+
n->subtype = AT_ForceRowSecurity;
2340+
$$ = (Node *)n;
2341+
}
2342+
/* ALTER TABLE <name> NO FORCE ROW LEVEL SECURITY*/
2343+
|NOFORCEROWLEVELSECURITY
2344+
{
2345+
AlterTableCmd *n = makeNode(AlterTableCmd);
2346+
n->subtype = AT_NoForceRowSecurity;
2347+
$$ = (Node *)n;
2348+
}
23352349
|alter_generic_options
23362350
{
23372351
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