Introducing Crunchy Data Warehouse: A next-generation Postgres-native data warehouse. Crunchy Data Warehouse Learn more
Tutorial Instructions
Let’s learn about creating and managing PostgreSQL database user roles.
A role can be either an individual database user, or a group of database users. A group could be a representation of a job description i.e. accountant, sales, marketing etc.
Database roles are global across a database cluster installation, they are not per individual database.
At any time, you can use psql to show you the roles in your database
\du
Let’s create a quick database to explore roles:
CREATE DATABASE finance;
CREATE SCHEMA accounting;
CREATE TABLE accounting.invoices(
invoice_id serial PRIMARY KEY,
invoice_date DATE,
amount NUMERIC
);
INSERT INTO accounting.invoices(invoice_date, amount)
VALUES ('2024-03-15', 250.50);
INSERT INTO accounting.invoices(invoice_date, amount)
VALUES ('2024-01-20', 110.99);
INSERT INTO accounting.invoices(invoice_date, amount)
VALUES ('2024-03-29', 1000);
Let’s create a new role for a person named simon
. Postgres is case sensitive, so using all lowercase will make basic sql and psql interactions easier. Create a role for this user with a password:
CREATE ROLE simon WITH PASSWORD '21654641seeswf!2@' LOGIN;
Assume the simon identity for this session:
SET ROLE simon;
With our new login, now let’s query the invoices table:
SELECT * FROM accounting.invoices;
ERROR: permission denied for schema accounting
LINE 1: SELECT FROM accounting.invoices;
This error is expected, the role simon
only have the permission to login.
To fix that, assume the postgres identity:
SET ROLE postgres;
Add the necessary privileges to query the table invoices
: Note that adding the GRANT SELECT
privilege will grant ‘read’ permissions to this user.
GRANT CONNECT ON DATABASE finance to simon;
GRANT USAGE ON SCHEMA accounting TO simon;
GRANT SELECT ON accounting.invoices TO simon;
We can also add write privileges to insert, update, or delete as needed. Note that these can be added separately as needed.
GRANT INSERT, UPDATE, DELETE accounting.invoices TO simon;
Now lets try that simon identity again:
SET ROLE simon;
Verify you can query the invoices
table:
SELECT * FROM accounting.invoices;
The role simon
is now able to query the data from the table accounting.invoices
.
To remove a privilege use the REVOKE
and FROM
keywords.
Assume the postgres identity:
SET ROLE postgres;
Similar to the grant commands used earlier, replace GRANT
with REVOKE
and TO
with FROM
REVOKE CONNECT ON DATABASE finance FROM simon;
REVOKE USAGE ON SCHEMA accounting FROM simon;
REVOKE SELECT, INSERT, UPDATE, DELETE ON accounting.invoices FROM simon;
Imagine you have a lot of employees; creating individual permissions could become tedious quite fast. Instead of manually assigning grants to every user, it's recommended to:
Postgres used to have a system of groups and roles, but that was phased out in favor of roles within roles.
Let’s try making a group. Create a read only role named accounting_ro
for the accounting department.
SET ROLE postgres;
CREATE ROLE accounting_ro NOLOGIN;
GRANT CONNECT ON DATABASE finance TO accounting_ro;
GRANT USAGE ON SCHEMA accounting TO accounting_ro;
GRANT SELECT ON ALL TABLES IN SCHEMA accounting TO accounting_ro;
Notice the role creation is using NOLOGIN
. This ensure no one can connect using that role, look at this role more like a role definition for a group.
Now grant role membership accounting_ro
to simon.
GRANT accounting_ro TO simon;
Look at the list of roles:
\du
A new role accounting_ro
has been created and simon
is now a member of that role.
Create a new customers
table:
CREATE TABLE accounting.customers(
customer_id serial PRIMARY KEY,
name TEXT,
address TEXT
);
Assume the simon identity:
SET ROLE simon;
Verify the invoices
table can be queried:
SELECT * FROM accounting.invoices;
Verify the customers
table can be queried:
SELECT * FROM accounting.customers;
ERROR: permission denied for table customers
This error message is expected. At the time we granted SELECT ON ALL TABLES
to the role accounting_ro
, the customers
table did not exist. To fix this, we can add GRANT
permissions for the accounting.customers table.
Assume the postgres identity:
SET ROLE postgres;
GRANT SELECT ON ALL TABLES IN SCHEMA accounting TO accounting_ro;
To grant access to new tables in the future automatically, the default privileges in the schema needs to be created.
Alter the default privileges so any new tables also have select privileges
ALTER DEFAULT PRIVILEGES IN SCHEMA accounting GRANT SELECT ON TABLES TO accounting_ro;
Just like you gave simon
access to some of the accounting tables, you’ll also need to have roles for each service or application that connects to your database. You might need roles for:
When you’re creating roles inside the database, you may also need to use these roles when making connections to the database from your other applications or services.
The connection string as a database URL contains the following parameters:
postgres://
postgres
, application
, user
, etc5432
unless you’ve altered thispostgres
unless you’ve created a new databaseHere’s a visual example of a connection string.
When giving out roles and privileges in Postgres, keep in mind that for the safety of your data and security of your business, you should give out role privileges based on the least amount of privilege the role should have. Never assume more or add roles in addition to the roles core duty. Roles can always be edited.
Roles are an important part of any database. As you saw during this tutorial, Postgres roles are flexible and powerful. You can:
Always keep in mind the best practices when creating roles. Follow the least amount of privileges approach as it will ensure tight and secure access to your data.
See our tutorial on row level security for additional layers of role and table security.
Loading terminal...
Loading terminal...