PostgreSQL Roles Management

roles, access control, privileges, schemas

Roles in PostgreSQL

TOC

There are no users, only roles!

See https://www.postgresql.org/docs/current/user-manag.html

PostgreSQL manages database access permissions using the concept of roles.

Roles englobes the concepts of “users” and “groups”.

In PostgreSQL versions before 8.1, users and groups were distinct kinds of entities, but now there are only roles.

Simply put:

A user is a role that can connect to the database. The role is defined as having LOGIN capability

Database roles

You create a new role with:

CREATE ROLE <name>;

To determine existing roles, examine the pg_roles table:

SELECT rolname FROM pg_roles;

And to find only the roles that can login i.e the users:

SELECT rolname FROM pg_roles WHERE rolcanlogin;

On my system this returns

 SELECT * FROM pg_roles WHERE rolcanlogin;
-[ RECORD 1 ]--+---------
rolname        | alexis
rolsuper       | t
rolinherit     | t
rolcreaterole  | t
rolcreatedb    | t
rolcanlogin    | t
rolreplication | t
rolconnlimit   | -1
rolpassword    | ********
rolvaliduntil  | [null]
rolbypassrls   | t
rolconfig      | [null]
oid            | 10
-[ RECORD 2 ]--+---------
rolname        | spock
rolsuper       | f
rolinherit     | t
rolcreaterole  | f
rolcreatedb    | f
rolcanlogin    | t
rolreplication | f
rolconnlimit   | -1
rolpassword    | ********
rolvaliduntil  | [null]
rolbypassrls   | f
rolconfig      | [null]
oid            | 82244

The role/user alexis can do anything. it is the superuser! Whole spock is not allowed to do much.

\du shows a more readable version of the table:

                             List of roles
 Role name |                         Attributes
-----------+------------------------------------------------------------
 alexis    | Superuser, Create role, Create DB, Replication, Bypass RLS
 spock     |

When you create the server

In order to bootstrap the database system, a freshly initialized system always contains one predefined login-capable role.

This role is always a “superuser”, and it will have the same name as the operating system user that initialized the database cluster with initdb. This role is often named postgres.

Note: On macOS, it’s designed to run under your user account rather than a dedicated system user. Homebrew installs are generally meant to be single-user focused, avoiding system-wide installations that require root privileges. Which explains why we don’t see a postgres user or role in the table

In all cases (Mac, linux or windows), in order to create more roles you first have to connect as this initial role.

Creating a new user

Only roles that have the LOGIN attribute can be used as the initial role name for a database connection.

These 2 statements are equivalent.

CREATE ROLE <name> LOGIN;
CREATE USER <name>;

Default permissions

The statement create role <name is equivalent to

CREATE ROLE new_role WITH
    NOSUPERUSER
    NOCREATEDB
    NOCREATEROLE
    NOINHERIT
    NOLOGIN
    NOREPLICATION
    CONNECTION LIMIT -1;

The role is not superuser, can’t create databases, or roles, can’t login and inherit or do replication. But it has no restrictions on the number of connections.

no restrictions on the number of connections ? but it cannot connect!?

Right but a user that could connect can be given, can be associated with this role. In that case the new_role will not limit the number of connections of the user.

In a mixture of roles it is always the most restrictive permission that takes precedence.

Other default permissions

These privileges are automatically granted (more in schemas in a mn, hold on!)

GRANT USAGE ON SCHEMA public TO PUBLIC;
GRANT CREATE ON SCHEMA public TO PUBLIC;  -- Note: many DBAs revoke this in production

and New roles can:

But new roles CANNOT by default:

Granting

To allow role to log in

ALTER ROLE new_role LOGIN;

To allow role to create databases

ALTER ROLE new_role CREATEDB;

To allow role to access specific database

GRANT CONNECT ON DATABASE your_database TO new_role;

To allow role to use a specific schema

GRANT USAGE ON SCHEMA schema_name TO new_role;

security

For a more secure setup

Revoke public schema creation from PUBLIC (common security practice)

REVOKE CREATE ON SCHEMA public FROM PUBLIC;
``

Revoke public EXECUTE permission on functions (if needed)

```sql
REVOKE EXECUTE ON ALL FUNCTIONS IN SCHEMA public FROM PUBLIC;

Grant specific permissions as needed

GRANT USAGE ON SCHEMA app_schema TO new_role;
GRANT SELECT ON ALL TABLES IN SCHEMA app_schema TO new_role;

Example of setting up a basic working role:

Always start by creating a role with minimal required privileges

CREATE ROLE app_user;
GRANT CONNECT ON DATABASE app_db TO app_user;
GRANT USAGE ON SCHEMA app_schema TO app_user;
GRANT SELECT, INSERT, UPDATE ON ALL TABLES IN SCHEMA app_schema TO app_user;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA app_schema TO app_user;

For future tables:

ALTER DEFAULT PRIVILEGES IN SCHEMA app_schema GRANT SELECT, INSERT, UPDATE ON TABLES TO app_user;
ALTER DEFAULT PRIVILEGES IN SCHEMA app_schema GRANT USAGE, SELECT ON SEQUENCES TO app_user;

what roles can do or don’t

super user has great powers

superuser status: A database superuser bypasses all permission checks, except the right to log in. This is a dangerous privilege and should not be used carelessly; it is best to do most of your work as a role that is not a superuser. To create a new database superuser, use CREATE ROLE name SUPERUSER. You must do this as a role that is already a superuser.

database creation: A role must be explicitly given permission to create databases (except for superusers, since those bypass all permission checks). To create such a role, use CREATE ROLE name CREATEDB.

role creation: same with creating roles

specify the password: A password is only significant if the client authentication method requires the user to supply a password when connecting to the database. Specify a password upon role creation with CREATE ROLE name PASSWORD ‘string’.

connection limit: Connection limit can specify how many concurrent connections a role can make. -1 (the default) means no limit. Specify connection limit upon role creation with CREATE ROLE name CONNECTION LIMIT ‘integer’.

Example of INHERIT

CREATE ROLE joe LOGIN; CREATE ROLE admin; CREATE ROLE wheel; CREATE ROLE island; GRANT admin TO joe WITH INHERIT TRUE; GRANT wheel TO admin WITH INHERIT FALSE; GRANT island TO joe WITH INHERIT TRUE, SET FALSE;

Immediately after connecting as role joe, a database session will have use of privileges granted directly to joe plus any privileges granted to admin and island, because joe “inherits” those privileges.

However, privileges granted to wheel are not available, because even though joe is indirectly a member of wheel, the membership is via admin which was granted using WITH INHERIT FALSE.

Ownership

each object has an owner which has super powers on the object

Transfering ownership

– Transfer ownership of specific tables ALTER TABLE trees OWNER TO trees_admin; ALTER TABLE maintenance_logs OWNER TO trees_admin;

predefined roles

https://www.postgresql.org/docs/current/predefined-roles.html

Predefined Roles | Role | Allowed Access | | — | — | | pg_read_all_data | Read all data (tables, views, sequences), as if having SELECT rights on those objects, and USAGE rights on all schemas, even without having it explicitly. This role does not have the role attribute BYPASSRLS set. If RLS is being used, an administrator may wish to set BYPASSRLS on roles which this role is GRANTed to. | | pg_write_all_data | Write all data (tables, views, sequences), as if having INSERT, UPDATE, and DELETE rights on those objects, and USAGE rights on all schemas, even without having it explicitly. This role does not have the role attribute BYPASSRLS set. If RLS is being used, an administrator may wish to set BYPASSRLS on roles which this role is GRANTed to. |

example

Create a basic user with login privilege

CREATE USER john WITH PASSWORD 'secure123';

Create a user with additional attributes

CREATE USER sarah WITH
    PASSWORD 'secure456'
    VALID UNTIL '2025-12-31'
    CONNECTION LIMIT 5;

Create a superuser (be careful with this in production!)

CREATE USER admin_user WITH
    SUPERUSER
    CREATEDB
    CREATEROLE
    LOGIN
    PASSWORD 'very_secure789';

Modify existing user attributes

ALTER USER john WITH CONNECTION LIMIT 2;
ALTER USER john VALID UNTIL '2024-12-31';

Set user-specific configurations

ALTER USER sarah SET search_path TO tree_inventory, public;
ALTER USER sarah SET timezone = 'Europe/Paris';

search_path & schemas

Schemas in PostgreSQL are like folders that help organize database objects (tables, views, functions, etc.).

Schemas are important to manage and secure access to important resources.

see https://neon.tech/postgresql/postgresql-administration/postgresql-schema

Default Schema

By default, objects go into the ‘public’ schema

CREATE TABLE trees (
    id SERIAL PRIMARY KEY,
    species VARCHAR(100),
    height NUMERIC(5,2)
);

Custom Schemas

Create schemas for different aspects of tree management

CREATE SCHEMA tree_maintenance;
CREATE SCHEMA tree_statistics;

Then create tables in specific schemas:

CREATE TABLE tree_maintenance.inspections (
    tree_id INTEGER REFERENCES public.trees(id),
    inspection_date DATE
);

Search Path:

so your user can only see objects that are in

A superuser can of course access all schemas.

Check current search path

SHOW search_path;

-- Modify search path
SET search_path TO tree_maintenance, public;

-- Now you can write queries without schema qualification
SELECT * FROM inspections; -- looks in tree_maintenance first, then public
  1. Organization: Separate administrative data from scientific measurements
  2. Access Control: Grant permissions at the schema level
  3. Namespace isolation: Same table names in different schemas won’t conflict

Existing schemas

There are a few shemas besides the public one in your PostgreSQL server:

SELECT schema_name
FROM information_schema.schemata;

returns:

   schema_name
--------------------
 information_schema
 pg_catalog
 pg_toast
 public

Resources

https://neon.tech/postgresql/postgresql-administration/postgresql-schema

https://www.postgresql.org/docs/current/ddl-schemas.html