LCOV - code coverage report
Current view: top level - src/backend/utils/misc - rls.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 97.8 % 45 44
Test Date: 2026-01-26 10:56:24 Functions: 100.0 % 3 3
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 84.6 % 26 22

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * rls.c
       4                 :             :  *                RLS-related utility functions.
       5                 :             :  *
       6                 :             :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       7                 :             :  * Portions Copyright (c) 1994, Regents of the University of California
       8                 :             :  *
       9                 :             :  *
      10                 :             :  * IDENTIFICATION
      11                 :             :  *                src/backend/utils/misc/rls.c
      12                 :             :  *
      13                 :             :  *-------------------------------------------------------------------------
      14                 :             : */
      15                 :             : #include "postgres.h"
      16                 :             : 
      17                 :             : #include "access/htup.h"
      18                 :             : #include "access/htup_details.h"
      19                 :             : #include "access/transam.h"
      20                 :             : #include "catalog/namespace.h"
      21                 :             : #include "catalog/pg_class.h"
      22                 :             : #include "miscadmin.h"
      23                 :             : #include "utils/acl.h"
      24                 :             : #include "utils/fmgrprotos.h"
      25                 :             : #include "utils/lsyscache.h"
      26                 :             : #include "utils/rls.h"
      27                 :             : #include "utils/syscache.h"
      28                 :             : #include "utils/varlena.h"
      29                 :             : 
      30                 :             : 
      31                 :             : /*
      32                 :             :  * check_enable_rls
      33                 :             :  *
      34                 :             :  * Determine, based on the relation, row_security setting, and current role,
      35                 :             :  * if RLS is applicable to this query.  RLS_NONE_ENV indicates that, while
      36                 :             :  * RLS is not to be added for this query, a change in the environment may change
      37                 :             :  * that.  RLS_NONE means that RLS is not on the relation at all and therefore
      38                 :             :  * we don't need to worry about it.  RLS_ENABLED means RLS should be implemented
      39                 :             :  * for the table and the plan cache needs to be invalidated if the environment
      40                 :             :  * changes.
      41                 :             :  *
      42                 :             :  * Handle checking as another role via checkAsUser (for views, etc).  Pass
      43                 :             :  * InvalidOid to check the current user.
      44                 :             :  *
      45                 :             :  * If noError is set to 'true' then we just return RLS_ENABLED instead of doing
      46                 :             :  * an ereport() if the user has attempted to bypass RLS and they are not
      47                 :             :  * allowed to.  This allows users to check if RLS is enabled without having to
      48                 :             :  * deal with the actual error case (eg: error cases which are trying to decide
      49                 :             :  * if the user should get data from the relation back as part of the error).
      50                 :             :  */
      51                 :             : int
      52                 :       46545 : check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
      53                 :             : {
      54         [ +  + ]:       46545 :         Oid                     user_id = OidIsValid(checkAsUser) ? checkAsUser : GetUserId();
      55                 :       46545 :         HeapTuple       tuple;
      56                 :       46545 :         Form_pg_class classform;
      57                 :       46545 :         bool            relrowsecurity;
      58                 :       46545 :         bool            relforcerowsecurity;
      59                 :       46545 :         bool            amowner;
      60                 :             : 
      61                 :             :         /* Nothing to do for built-in relations */
      62         [ +  + ]:       46545 :         if (relid < (Oid) FirstNormalObjectId)
      63                 :       21961 :                 return RLS_NONE;
      64                 :             : 
      65                 :             :         /* Fetch relation's relrowsecurity and relforcerowsecurity flags */
      66                 :       24584 :         tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
      67         [ +  - ]:       24584 :         if (!HeapTupleIsValid(tuple))
      68                 :           0 :                 return RLS_NONE;
      69                 :       24584 :         classform = (Form_pg_class) GETSTRUCT(tuple);
      70                 :             : 
      71                 :       24584 :         relrowsecurity = classform->relrowsecurity;
      72                 :       24584 :         relforcerowsecurity = classform->relforcerowsecurity;
      73                 :             : 
      74                 :       24584 :         ReleaseSysCache(tuple);
      75                 :             : 
      76                 :             :         /* Nothing to do if the relation does not have RLS */
      77         [ +  + ]:       24584 :         if (!relrowsecurity)
      78                 :       23933 :                 return RLS_NONE;
      79                 :             : 
      80                 :             :         /*
      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).
      86                 :             :          */
      87         [ +  + ]:         651 :         if (has_bypassrls_privilege(user_id))
      88                 :          79 :                 return RLS_NONE_ENV;
      89                 :             : 
      90                 :             :         /*
      91                 :             :          * Table owners generally bypass RLS, except if the table has been set (by
      92                 :             :          * an owner) to FORCE ROW SECURITY, and this is not a referential
      93                 :             :          * 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                 :         572 :         amowner = object_ownercheck(RelationRelationId, relid, user_id);
      99         [ +  + ]:         572 :         if (amowner)
     100                 :             :         {
     101                 :             :                 /*
     102                 :             :                  * If FORCE ROW LEVEL SECURITY has been set on the relation then we
     103                 :             :                  * should return RLS_ENABLED to indicate that RLS should be applied.
     104                 :             :                  * If not, or if we are in an InNoForceRLSOperation context, we return
     105                 :             :                  * RLS_NONE_ENV.
     106                 :             :                  *
     107                 :             :                  * InNoForceRLSOperation indicates that we should not apply RLS even
     108                 :             :                  * if the table has FORCE RLS set - IF the current user is the owner.
     109                 :             :                  * This is specifically to ensure that referential integrity checks
     110                 :             :                  * are able to still run correctly.
     111                 :             :                  *
     112                 :             :                  * This is intentionally only done after we have checked that the user
     113                 :             :                  * is the table owner, which should always be the case for referential
     114                 :             :                  * integrity checks.
     115                 :             :                  */
     116   [ +  +  +  + ]:          75 :                 if (!relforcerowsecurity || InNoForceRLSOperation())
     117                 :          36 :                         return RLS_NONE_ENV;
     118                 :          39 :         }
     119                 :             : 
     120                 :             :         /*
     121                 :             :          * We should apply RLS.  However, the user may turn off the row_security
     122                 :             :          * GUC to get a forced error instead.
     123                 :             :          */
     124   [ +  +  -  + ]:         536 :         if (!row_security && !noError)
     125   [ +  -  +  -  :          13 :                 ereport(ERROR,
                   +  + ]
     126                 :             :                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     127                 :             :                                  errmsg("query would be affected by row-level security policy for table \"%s\"",
     128                 :             :                                                 get_rel_name(relid)),
     129                 :             :                                  amowner ? errhint("To disable the policy for the table's owner, use ALTER TABLE NO FORCE ROW LEVEL SECURITY.") : 0));
     130                 :             : 
     131                 :             :         /* RLS should be fully enabled for this relation. */
     132                 :         523 :         return RLS_ENABLED;
     133                 :       46532 : }
     134                 :             : 
     135                 :             : /*
     136                 :             :  * row_security_active
     137                 :             :  *
     138                 :             :  * check_enable_rls wrapped as a SQL callable function except
     139                 :             :  * RLS_NONE_ENV and RLS_NONE are the same for this purpose.
     140                 :             :  */
     141                 :             : Datum
     142                 :           2 : row_security_active(PG_FUNCTION_ARGS)
     143                 :             : {
     144                 :             :         /* By OID */
     145                 :           2 :         Oid                     tableoid = PG_GETARG_OID(0);
     146                 :           2 :         int                     rls_status;
     147                 :             : 
     148                 :           2 :         rls_status = check_enable_rls(tableoid, InvalidOid, true);
     149                 :           4 :         PG_RETURN_BOOL(rls_status == RLS_ENABLED);
     150                 :           2 : }
     151                 :             : 
     152                 :             : Datum
     153                 :           2 : row_security_active_name(PG_FUNCTION_ARGS)
     154                 :             : {
     155                 :             :         /* By qualified name */
     156                 :           2 :         text       *tablename = PG_GETARG_TEXT_PP(0);
     157                 :           2 :         RangeVar   *tablerel;
     158                 :           2 :         Oid                     tableoid;
     159                 :           2 :         int                     rls_status;
     160                 :             : 
     161                 :             :         /* Look up table name.  Can't lock it - we might not have privileges. */
     162                 :           2 :         tablerel = makeRangeVarFromNameList(textToQualifiedNameList(tablename));
     163                 :           2 :         tableoid = RangeVarGetRelid(tablerel, NoLock, false);
     164                 :             : 
     165                 :           2 :         rls_status = check_enable_rls(tableoid, InvalidOid, true);
     166                 :           4 :         PG_RETURN_BOOL(rls_status == RLS_ENABLED);
     167                 :           2 : }
        

Generated by: LCOV version 2.3.2-1