Configure Row-Level Security Policy
Row-level security (RLS) policy allows the table owner to define access policies that control users' access to specific rows of the table. When a user tries to query or update a table, the RLS policy will be applied first before any user command is executed to truncate the rows in the table.
Row-level security policies can be created for specific commands, such as SELECT/INSERT/UPDATE/DELETE, or for all commands (ALL). Row-level security policies can also control access to specific rows in a table based on certain users, user groups, or according to specific conditions.
Row-Level Security Policy Overview
-
By default, no row-level security policy is set for a table. If a user has access to the table according to the SQL permission system, all rows in the table can be queried or updated.
-
Users can enable row-level security policies on a table using the
ALTER TABLE ... ENABLE ROW LEVEL SECURITYcommand. After the policy is enabled, no other users except the table owner will be able to access and modify the table. Appropriate security policies must be set before other users can access and manipulate rows within the table.noteOperations that apply to the entire table (such as
TRUNCATEandREFERENCES) are not restricted by row-level security. -
Row-level security policies can be specific to commands, user roles, or both. A policy can be applied to all commands, or just to
SELECT,INSERT,UPDATE, orDELETEindividually. Multiple roles can be granted the same policy, and the usual role hierarchy and inheritance rules also apply to row-level security policies. -
Superusers and roles with the
BYPASSRLSattribute are not subject to row-level security policies. The table owner is typically not constrained by the policy, but row-level security can be enforced on the table owner by usingALTER TABLE ... FORCE ROW LEVEL SECURITY. -
Only the table owner can enable, disable, or add row-level security policies.
Enable and create row-level security policy
The table owner must first enable row-level security policies for a table before creating specific policies.
Step 1. Enable row-level security policy
The table owner must first enable row-level security policy using the following command:
ALTER TABLE <table_name> ENABLE ROW LEVEL SECURITY;
Step 2. Create policy
After enabling row-level security policies, you can create policies with CREATE POLICY.
A RLS policy must provide an expression in the USING or WITH CHECK clause that returns a boolean value determining which rows can be returned. This expression is evaluated row-by-row before any conditions and functions in the user's query are executed. Rows for which the expression does not return true will not be processed.
You can specify separate expressions to provide independent control over which rows are visible and which can be modified. The policy expressions run as part of the query and have the permissions of the user running the query. Users can also use Security-definer functions to access data not available to the user.
Refer to the following syntax to create a row-level security policy:
CREATE POLICY <name> ON <table_name>
[ AS { PERMISSIVE | RESTRICTIVE } ]
[ FOR { ALL | SELECT | INSERT | UPDATE | DELETE } ]
[ TO { <role_name> | PUBLIC | CURRENT_USER | SESSION_USER } [, ...] ]
[ USING ( <using_expression> ) ]
[ WITH CHECK ( <check_expression> ) ]
See the following parameter descriptions for detailed usage:
-
name: The name of the policy. -
table_name: The name of the table to which the policy applies. -
PERMISSIVE: Specifies that the policy will be created as a permissive policy. All permissive policies applicable to a given query will be combined using theORoperator. By creating permissive policies, administrators can add accessible record sets. By default, policies are permissive. -
RESTRICTIVE: Specifies that the policy will be created as a restrictive policy. All restrictive policies applicable to a given query will be combined using theANDoperator. By creating restrictive policies, administrators can reduce the accessible record set, as each record must pass all restrictive policies. -
Commands (
ALL,SELECT,INSERT,UPDATE,DELETE): The commands to which the policy applies. -
role_name: The roles to which the policy applies; the default isPUBLIC, meaning the policy will apply to all roles. -
using_expression: Any SQL conditional expression (returning a boolean value). The conditional expression cannot contain any aggregate functions or window functions. If row-level security is enabled, this expression will be added to queries that reference the table. Rows for which the expression returnstruewill be visible. Any rows for which the expression returnsfalseornullwill be invisible to the user (inSELECTcommands) and cannot be modified (UPDATEorDELETE). Invisible or unmodifiable rows will not be returned and will not generate an error. -
check_expression: Any SQL conditional expression (returning a boolean value). The conditional expression cannot contain any aggregate functions or window functions. If row-level security is enabled, this expression will be used forINSERTandUPDATEoperations on the table. Only rows for which the expression evaluates totruewill be allowed. An error will be raised if any records inserted or resulting from an update evaluate tofalseornull.notecheck_expressionis evaluated based on the proposed new content of the command, not the original content.
Example of using row-level security policy
The following example sets up a row-level access security policy on a table that only allows rows to be returned where (department = current_setting('myapp.current_department')) is true.
-
Access the database as an administrator:
psql -h <host_ip> -p <port> -U <user_name> -d <db_name> -
Create a table and insert data:
CREATE TABLE projects (
project_id SERIAL PRIMARY KEY,
project_name TEXT,
project_manager TEXT,
department TEXT
);
INSERT INTO projects (project_name, project_manager, department) VALUES
('Project Alpha', 'zhangsan', 'Engineering'),
('Project Beta', 'lisi', 'HR'),
('Project Gamma', 'wangwu', 'Sales'); -
Enable row-level security policy:
ALTER TABLE projects ENABLE ROW LEVEL SECURITY; -
Create the row-level security policy:
CREATE POLICY department_policy
ON projects
FOR SELECTUSING (department = current_setting('myapp.current_department'));This policy will only return rows where
(department = current_setting('myapp.current_department'))istrue. -
Create test users:
CREATE USER zhangsan WITH PASSWORD '<password>';
CREATE USER lisi WITH PASSWORD '<password>';
CREATE USER wangwu WITH PASSWORD '<password>'; -
Grant the test users permission to query the
projectstable:GRANT SELECT ON projects TO zhangsan; -
Switch to the test user and define the value of the variable
myapp.current_departmentfor the current session:SET ROLE zhangsan;
SET myapp.current_department = 'Engineering'; -
Query the
projectstable as the current user:SELECT * FROM projects;
Because the value of myapp.current_department is set to Engineering, you would expect the following data to be returned:
project_id | project_name | project_manager | department
------------+--------------+-----------------+------------
1 | Project Alpha | zhangsan | Engineering
Only the row related to the "Engineering" department will be visible to the user zhangsan because of the row-level security policy in place.