PostgreSQL authorization
PostgreSQL Authorization Model Tutorial
This document provides a focused tutorial on PostgreSQL’s authorization model, including roles (users and groups), table- and row-level access control, views, granting and revoking privileges, and how PostgreSQL verifies authorization.
A running example is included throughout.
1. Roles: Users and Groups
PostgreSQL uses roles to model both individual users and groups. A role can:
Log in (“LOGIN” attribute)
Own objects
Be granted privileges
Contain other roles (group-like behavior)
Creating roles
Assigning roles to other roles (group membership)
This makes app_reader inherit all privileges granted to reporting_group (unless NOINHERIT).
2. Example Schema
We use a small sales schema.
CREATE TABLE customer (
customer_id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
region TEXT NOT NULL
);
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
customer_id INT NOT NULL REFERENCES customer(customer_id),
amount NUMERIC(12,2) NOT NULL,
region TEXT NOT NULL
);Populate minimal sample data:
INSERT INTO customer(name, region) VALUES
('Alice', 'NA'),
('Bob', 'EU');
INSERT INTO orders(customer_id, amount, region) VALUES
(1, 300, 'NA'),
(2, 500, 'EU');3. Table-Level Privileges
Privileges available for tables:
SELECT
INSERT
UPDATE (optionally per-column)
DELETE
TRUNCATE
REFERENCES
TRIGGER
Granting table access
Revoking
Note: If a user inherits a privilege through a role, revoking must be done at the role where granted.
4. Row-Level Security (RLS)
Row-level security enforces tuple-level access control.
Enable it per table:
Define policies:
Allow users to see only orders from their region. Assume each login role has a setting:
Policy:
CREATE POLICY order_region_policy ON orders
FOR SELECT
USING (region = current_setting('my.region'));Users now see only rows in their region.
Write policy:
CREATE POLICY order_write_policy ON orders
FOR INSERT
WITH CHECK (region = current_setting('my.region'));5. Views and Access Control
Views help enforce read-only or filtered access.
Example restricted view
Grant privileges on the view only:
The underlying table permissions are not automatically exposed. Users need no direct access to orders.
Updatable view with INSTEAD OF triggers (outline)
Make it writable:
CREATE FUNCTION orders_regional_ins() RETURNS trigger AS $$
BEGIN
INSERT INTO orders(customer_id, amount, region)
VALUES(NEW.customer_id, NEW.amount, current_setting('my.region'));
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER orders_regional_ins_trg
INSTEAD OF INSERT ON orders_regional
FOR EACH ROW EXECUTE FUNCTION orders_regional_ins();Users get controlled write access.
6. Authorization Verification Policy
PostgreSQL evaluates permissions in this order:
Object ownership: Owners bypass privilege checks.
Explicit GRANTs on the object.
Inherited privileges from parent roles.
Row-level security policies, if enabled.
For views: permissions check whether user has privilege on the view, not the base table (unless
security_barrieror modifiers apply).For functions: if declared
SECURITY DEFINER, checks occur under the function owner’s privileges; otherwise under caller.
Checking privileges
Check role memberships:
7. End-to-End Example Workflow
- Create users and groups.
- Grant table-level privileges.
- Enable RLS.
ALTER TABLE customer ENABLE ROW LEVEL SECURITY;
CREATE POLICY customer_region ON customer
FOR SELECT USING (region = current_setting('my.region'));- Set user session attribute.
- Create a view for narrower reporting.
CREATE VIEW customer_names AS
SELECT customer_id, name
FROM customer;
GRANT SELECT ON customer_names TO analyst;- Verify access.
SET ROLE analyst;
SELECT * FROM customer; -- only EU rows
SELECT * FROM customer_names; -- restricted viewSummary
Roles unify users and groups.
Privileges apply at object level; RLS applies at tuple level.
Views can narrow permissions and expose limited, controlled interfaces.
GRANT/REVOKE manage access.
Authorization checks follow a deterministic hierarchy including role inheritance, object ownership, and RLS.