Schema privileges vs Database privileges in PostgreSQL - postgresql

In a PostgreSQL server, I want to create a database (db1) and give all privileges on that database to a user (user1). I run these commands:
CREATE USER user1 WITH PASSWORD 'password';
CREATE DATABASE db1;
\c db1
CREATE SCHEMA user1;
DROP SCHEMA public;
Now the database (db1) has only the schema user1. The next step is to grant all privileges to the user (user1).
If I run the following commands, and try to create a table as user1, it works:
GRANT ALL PRIVILEGES ON SCHEMA user1 TO user1;
\c db1 user1
CREATE TABLE t1(a int);
If I only grant privileges on the database (and not the schema), it does not work:
GRANT ALL PRIVILEGES ON DATABASE db1 TO user1;
\c db1 user1
CREATE TABLE t1(a int);
The create table will fail with these errors:
db1=> CREATE TABLE t1(a int);
ERROR: no schema has been selected to create in
LINE 1: CREATE TABLE t1(a int);
^
db1=> CREATE TABLE user1.t1(a int);
ERROR: permission denied for schema user1
LINE 1: CREATE TABLE user1.t1(a int);
^
So, my questions are: Is the GRANT ALL PRIVILEGES ON DATABASE really needed here? What are the privileges granted by that command?

What are the privileges granted by that command?
According to the privileges documentation, a GRANT ALL on the DATABASE level encompasses:
CREATE: allows new schemas and publications to be created within the database, and allows trusted extensions to be installed within the database.
CONNECT: Allows the grantee to connect to the database. This privilege is checked at connection startup (in addition to checking any restrictions imposed by pg_hba.conf).
TEMPORARY: Allows temporary tables to be created while using the database.
By default, anyone (PUBLIC) has CONNECT and TEMPORARY privileges already, so GRANT ALL PRIVILEGES ON DATABASE db1 TO user1; will only affect the CREATE privilege. Judge yourself whether that's really needed.
Instead of creating the schema and granting all privileges on it to user1, you may want to grant the CREATE privilege on the database, and let the user1 create their schema themselves (so that they become its owner and will thereby get all the privileges on it). It will allow them to also create any other schema then.

Thanks #Bergi it is clear now. Using the GRANT ALL PRIVILEGES ON DATABASE the following works:
postgres=# CREATE USER user1 WITH PASSWORD 'password';
CREATE ROLE
postgres=# CREATE DATABASE db1;
CREATE DATABASE
postgres=# GRANT ALL PRIVILEGES ON DATABASE db1 TO user1;
GRANT
postgres=# \c db1 user1
You are now connected to database "db1" as user "user1".
db1=> CREATE SCHEMA user1;
CREATE SCHEMA
db1=> DROP SCHEMA public;
ERROR: must be owner of schema public
db1=> CREATE TABLE t1(a int);
CREATE TABLE
db1-> \dt
List of relations
Schema | Name | Type | Owner
--------+------+-------+-------
user1 | t1 | table | user1
(1 row)
db1-> \dn
List of schemas
Name | Owner
--------+----------
public | postgres
user1 | user1
(2 rows)
Observe that the user (user1) cannot delete the schema public because the owner is postgres, even the user has all privileges on the database (db1).

Related

Setting default privileges on tables not working as expected in Postgres 13

Setup: I'm using Google Cloud Platform's managed Postgres 13 instance, which is a fresh install with no existing tables or users (expect for the postgres admin).
Goal: I want to create a new database called my_db and have two new users steve and mike be able to execute DDL and DML commands on future tables (e.g., create new tables, insert data, read, etc). This also means steve and mike should be able to modify and read/write each other's tables.
Problem: Even when I create the users and set the default privileges in schema public grant all to tables in my_db only the creator (steve) of the new table can read/write the table while mike cannot. Furthermore, even the postgres admin cannot read the new table!
Steps: How to recreate
First I'll create the new database and users, plus give them lenient permissions to alter/read/write future tables in my_db.
-- Logged in as user = postgres (Connection 1)
\c postgres
create database my_db;
-- connect to my_db and create the new users
\c my_db
-- steve user
CREATE USER steve WITH PASSWORD 'pass123';
GRANT connect ON DATABASE my_db TO steve;
alter default privileges in schema public grant all on tables to steve;
-- mike user
CREATE USER mike WITH PASSWORD 'pass456';
GRANT connect ON DATABASE my_db TO mike;
alter default privileges in schema public grant all on tables to mike;
If my understanding is correct, steve and mike can now create and modify tables in my_db. Let's test this.
Then login as steve in a new connection #2 and create a new test table:
-- Logged in as user = steve (Connection 2)
\c my_db
create table test_tbl ( id int4 ); -- success
select * from test_tbl; -- 0 records
Now let's see if mike can read from the test_tbl which he should given the default privileges. We create connection #3 for mike:
-- Logged in as user = mike (Connection 3)
\c my_db
select * from test_tbl; -- ERROR: steve does not have permissions to read test_tbl!
This is my first point of confusion as I thought default permissions would let mike read the test_tbl created by steve.
As a final oddity I decided to circle back to the postgres user to test reading test_tbl:
-- Logged in as user = postgres (Connection 1)
\c my_db
select * from test_tbl; -- ERROR: steve does not have permissions to read test_tbl!
GRANT SELECT, INSERT, UPDATE, delete ON ALL TABLES IN SCHEMA public TO mike; -- same error above!
So not even the admin user postgres can read this new table NOR can I grant permissions...
The only thing that worked is logging back in as steve--the original table creator--and granting postgres and mike permissions:
-- Logged in as user = steve (Connection 2)
\c my_db
GRANT SELECT, INSERT, UPDATE, delete ON ALL TABLES IN SCHEMA public TO postgres; -- success
GRANT SELECT, INSERT, UPDATE, delete ON ALL TABLES IN SCHEMA public TO mike; -- success
This all seems backwards. The default privileges in schema public grant all should take care of allowing users to modify future tables, right? What am I missing?
Thanks in advance.
ALTER DEFAULT PRIVILEGES without the FOR ROLE clause affects only objects created by the role that ran the ALTER DEFAULT PRIVILEGES statement.
You would need two such statements to get what you want:
ALTER DEFAULT PRIVILEGES FOR ROLE mike GRANT ... TO steve;
ALTER DEFAULT PRIVILEGES FOR ROLE steve GRANT ... TO mike;
The other thing you want cannot be had in a straigtforward fashion. Only the owner (or members of that role) and superusers can ALTER or DROP an object. There is no way to grant that privilege. Your only solution would be to have a common table_owner role and have both users be a member of the role:
CREATE ROLE table_owner NOLOGIN;
GRANT CREATE ON SCHEMA myschema TO table_owner;
ALTER ROLE mike NOINHERIT;
ALTER ROLE steve NOINHERIT;
GRANT table_owner TO mike, steve;
Now both users need to SET ROLE to create a table in the schema:
SET ROLE table_owner;
CREATE TABLE myschema.atable (...);
That table is then owned by table_owner, and both users can ALTER or DROP it.

PostgreSQL: roles can only access tables they've created

I have a postgresql (v10) database. I've created database tn_beta_db with schema tn_schema. I've created three users and executed the following, which is meant to grant all of them read and maybe modify access on all tables, current and future that tn_beta_migrator might create.
\c tn_beta_db
-- User tn_beta_reader --
ALTER DEFAULT PRIVILEGES IN SCHEMA tn_schema FOR ROLE tn_beta_reader GRANT SELECT ON TABLES TO tn_beta_reader;
GRANT CONNECT ON DATABASE tn_beta_db TO tn_beta_reader;
GRANT USAGE ON SCHEMA tn_schema TO tn_beta_reader;
GRANT SELECT ON ALL TABLES IN SCHEMA tn_schema TO tn_beta_reader;
-- User tn_beta_migrator --
ALTER DEFAULT PRIVILEGES IN SCHEMA tn_schema FOR ROLE tn_beta_migrator GRANT ALL ON TABLES TO tn_beta_migrator;
GRANT CONNECT ON DATABASE tn_beta_db TO tn_beta_migrator;
GRANT USAGE ON SCHEMA tn_schema TO tn_beta_migrator;
GRANT ALL ON ALL TABLES IN SCHEMA tn_schema TO tn_beta_migrator;
GRANT CREATE ON SCHEMA tn_schema TO tn_beta_migrator;
-- User tn_beta_writer --
ALTER DEFAULT PRIVILEGES IN SCHEMA tn_schema FOR ROLE tn_beta_writer GRANT SELECT,INSERT,DELETE,UPDATE ON TABLES TO tn_beta_writer;
GRANT CONNECT ON DATABASE tn_beta_db TO tn_beta_writer;
GRANT USAGE ON SCHEMA tn_schema TO tn_beta_writer;
GRANT SELECT,INSERT,DELETE,UPDATE ON ALL TABLES IN SCHEMA tn_schema TO tn_beta_writer;
If I now connect as tn_beta_migrator, I can create a table and do things with it.
create table tn_schema.foo(x int);
-- and then INSERT, SELECT, UPDATE, even DROP
But now if I connect as either of tn_beta_reader or tn_beta_writer, I can not use that table.
tn_beta_db=> select * from tn_schema.foo ;
ERROR: permission denied for relation foo
tn_beta_db=>
I would expect to be able to read/write/modify/delete as tn_beta_writer and to be able to read as tn_beta_reader.
If I rerun the grant script, above, this permits me to access foo, but a newly created table bar would then be inaccessible.
I'd thought that the alter default privileges commands would permit these roles, in the future, to access the tables created by tn_beta_migrator.
Any pointers on what I've misunderstood?
The role in the FOR ROLE clause in ALTER DEFAULT PRIVILEGES is not the role that will get the privileges, it is the role that creates the tables.
So your statements should start with
ALTER DEFAULT PRIVILEGES FOR ROLE tn_beta_migrator ...

Postgresql forgets grants after recreating tables

We use liquibase to manage and execute our database changes. On our DEV environment (and especially on local machines) we frequently recreate the tables to have a clean slate.
We have just migrated from MySQL to Postgres and are facing a problem related to these table recreations.
Initially we have granted our DB user with this:
GRANT SELECT,INSERT,UPDATE,DELETE ON ALL TABLES IN SCHEMA public TO mydbuser;
GRANT SELECT, USAGE ON ALL SEQUENCES IN SCHEMA public TO mydbuser;
This is executed through psql after connecting to our own DB (where public is the default/only schema).
Everything is fine until we ask liquibase to recreate the tables in which case it will drop all tables and create them again.
After that it appears that mydbuser has lost all its grants on the tables.
According to several resources (like this) we need to alter the default privileges, so we obey:
GRANT SELECT,INSERT,UPDATE,DELETE ON ALL TABLES IN SCHEMA public TO mydbuser;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT,INSERT,UPDATE,DELETE ON TABLES TO mydbuser;
GRANT SELECT, USAGE ON ALL SEQUENCES IN SCHEMA public TO mydbuser;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, USAGE ON SEQUENCES TO mydbuser;
However, although this seems very logical it changed nothing. All grants (even select) are still lost after recreating the tables.
What are we doing wrong or what else do we need to do?
UPDATE
\ddp shows:
Default access privileges
Owner | Schema | Type | Access privileges
----------+--------+----------+--------------------
postgres | public | sequence | mydb=rU/postgres
postgres | public | table | mydb=arwd/postgres
If trying to mimic what liquibase is doing in a simplified test case, it just works.
Quick demo with 9.3:
1) Create the objects from scratch with the postgres user:
postgres=# create database dbtest;
CREATE DATABASE
postgres=# create user mydbuser;
CREATE ROLE
postgres=# \c dbtest
You are now connected to database "dbtest" as user "postgres".
dbtest=# ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT,INSERT,UPDATE,DELETE ON TABLES TO mydbuser;
ALTER DEFAULT PRIVILEGES
dbtest=# create table foobar(id int);
CREATE TABLE
2) In another session let's connect with mydbuser and see if SELECT is allowed.
dbtest=> select * from foobar;
id
----
(0 rows)
Result: Yes, it's allowed. Note the result of \ddp too:
dbtest=> \ddp
Default access privileges
Owner | Schema | Type | Access privileges
----------+--------+-------+------------------------
postgres | public | table | mydbuser=arwd/postgres
(1 row)
3) Let's have postgres drop the table and recreate it:
dbtest=# drop table foobar;
DROP TABLE
dbtest=# create table foobar(id int);
CREATE TABLE
4) See if mydbuser can still SELECT from it.
$ psql -d dbtest -U mydbuser
dbtest=> select * from foobar;
id
----
(0 rows)
Result: Yes, as expected.
5) See if another user can SELECT from it.
$ psql -d dbtest -U daniel
dbtest=> select * from foobar;
ERROR: permission denied for relation foobar
Result: No, as expected.
This does not explain what doesn't work for you, but you may compare the above with the set of commands issued by liquibase.

ERROR: permission denied for relation tablename on Postgres while trying a SELECT as a readonly user

GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly;
The readonly user can connect, see the tables but when it tries to do a simple select it gets:
ERROR: permission denied for relation mytable
SQL state: 42501
This is happening on PostgreSQL 9.1
What I did wrong?
Here is the complete solution for PostgreSQL 9+, updated recently.
CREATE USER readonly WITH ENCRYPTED PASSWORD 'readonly';
GRANT USAGE ON SCHEMA public to readonly;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO readonly;
-- repeat code below for each database:
GRANT CONNECT ON DATABASE foo to readonly;
\c foo
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO readonly; --- this grants privileges on new tables generated in new database "foo"
GRANT USAGE ON SCHEMA public to readonly;
GRANT SELECT ON ALL SEQUENCES IN SCHEMA public TO readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly;
Thanks to https://jamie.curle.io/creating-a-read-only-user-in-postgres/ for several important aspects
If anyone find shorter code, and preferably one that is able to perform this for all existing databases, extra kudos.
Try to add
GRANT USAGE ON SCHEMA public to readonly;
You probably were not aware that one needs to have the requisite permissions to a schema, in order to use objects in the schema.
This worked for me:
Check the current role you are logged into by using:
SELECT CURRENT_USER, SESSION_USER;
Note: It must match with Owner of the schema.
Schema | Name | Type | Owner
--------+--------+-------+----------
If the owner is different, then give all the grants to the current user role from the admin role by :
GRANT 'ROLE_OWNER' to 'CURRENT ROLENAME';
Then try to execute the query, it will give the output as it has access to all the relations now.
make sure your user has attributes on its role. for example:
postgres=# \du
List of roles
Role name | Attributes | Member of
-----------+------------------------------------------------+-----------
flux | | {}
postgres | Superuser, Create role, Create DB, Replication | {}
after performing the following command:
postgres=# ALTER ROLE flux WITH Superuser;
ALTER ROLE
postgres=# \du
List of roles
Role name | Attributes | Member of
-----------+------------------------------------------------+-----------
flux | Superuser | {}
postgres | Superuser, Create role, Create DB, Replication | {}
it fixed the problem.
see tutorial for roles and stuff here: https://www.digitalocean.com/community/tutorials/how-to-use-roles-and-manage-grant-permissions-in-postgresql-on-a-vps--2
You should execute the next query:
GRANT ALL ON TABLE mytable TO myuser;
Or if your error is in a view then maybe the table does not have permission, so you should execute the next query:
GRANT ALL ON TABLE tbm_grupo TO myuser;

How do you create a read-only user in PostgreSQL?

I'd like to create a user in PostgreSQL that can only do SELECTs from a particular database. In MySQL the command would be:
GRANT SELECT ON mydb.* TO 'xxx'#'%' IDENTIFIED BY 'yyy';
What is the equivalent command or series of commands in PostgreSQL?
I tried...
postgres=# CREATE ROLE xxx LOGIN PASSWORD 'yyy';
postgres=# GRANT SELECT ON DATABASE mydb TO xxx;
But it appears that the only things you can grant on a database are CREATE, CONNECT, TEMPORARY, and TEMP.
Grant usage/select to a single table
If you only grant CONNECT to a database, the user can connect but has no other privileges. You have to grant USAGE on namespaces (schemas) and SELECT on tables and views individually like so:
GRANT CONNECT ON DATABASE mydb TO xxx;
-- This assumes you're actually connected to mydb..
GRANT USAGE ON SCHEMA public TO xxx;
GRANT SELECT ON mytable TO xxx;
Multiple tables/views (PostgreSQL 9.0+)
In the latest versions of PostgreSQL, you can grant permissions on all tables/views/etc in the schema using a single command rather than having to type them one by one:
GRANT SELECT ON ALL TABLES IN SCHEMA public TO xxx;
This only affects tables that have already been created. More powerfully, you can automatically have default roles assigned to new objects in future:
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT ON TABLES TO xxx;
Note that by default this will only affect objects (tables) created by the user that issued this command: although it can also be set on any role that the issuing user is a member of. However, you don't pick up default privileges for all roles you're a member of when creating new objects... so there's still some faffing around. If you adopt the approach that a database has an owning role, and schema changes are performed as that owning role, then you should assign default privileges to that owning role. IMHO this is all a bit confusing and you may need to experiment to come up with a functional workflow.
Multiple tables/views (PostgreSQL versions before 9.0)
To avoid errors in lengthy, multi-table changes, it is recommended to use the following 'automatic' process to generate the required GRANT SELECT to each table/view:
SELECT 'GRANT SELECT ON ' || relname || ' TO xxx;'
FROM pg_class JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace
WHERE nspname = 'public' AND relkind IN ('r', 'v', 'S');
This should output the relevant GRANT commands to GRANT SELECT on all tables, views, and sequences in public, for copy-n-paste love. Naturally, this will only be applied to tables that have already been created.
Reference taken from this blog:
Script to Create Read-Only user:
CREATE ROLE Read_Only_User WITH LOGIN PASSWORD 'Test1234'
NOSUPERUSER INHERIT NOCREATEDB NOCREATEROLE NOREPLICATION VALID UNTIL 'infinity';
\connect YourDatabaseName;
Assign permission to this read-only user:
GRANT CONNECT ON DATABASE YourDatabaseName TO Read_Only_User;
GRANT USAGE ON SCHEMA public TO Read_Only_User;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO Read_Only_User;
GRANT SELECT ON ALL SEQUENCES IN SCHEMA public TO Read_Only_User;
REVOKE CREATE ON SCHEMA public FROM PUBLIC;
Assign permissions to read all newly tables created in the future
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO Read_Only_User;
From PostgreSQL v14 on, you can do that simply by granting the predefined pg_read_all_data role:
GRANT pg_read_all_data TO xxx;
Do note that PostgreSQL 9.0 (today in beta testing) will have a simple way to do that:
test=> GRANT SELECT ON ALL TABLES IN SCHEMA public TO joeuser;
Here is the best way I've found to add read-only users (using PostgreSQL 9.0 or newer):
$ sudo -upostgres psql postgres
postgres=# CREATE ROLE readonly WITH LOGIN ENCRYPTED PASSWORD '<USE_A_NICE_STRONG_PASSWORD_PLEASE';
postgres=# GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly;
Then log in to all related machines (master + read-slave(s)/hot-standby(s), etc..) and run:
$ echo "hostssl <PUT_DBNAME_HERE> <PUT_READONLY_USERNAME_HERE> 0.0.0.0/0 md5" | sudo tee -a /etc/postgresql/9.2/main/pg_hba.conf
$ sudo service postgresql reload
By default new users will have permission to create tables. If you are planning to create a read-only user, this is probably not what you want.
To create a true read-only user with PostgreSQL 9.0+, run the following steps:
# This will prevent default users from creating tables
REVOKE CREATE ON SCHEMA public FROM public;
# If you want to grant a write user permission to create tables
# note that superusers will always be able to create tables anyway
GRANT CREATE ON SCHEMA public to writeuser;
# Now create the read-only user
CREATE ROLE readonlyuser WITH LOGIN ENCRYPTED PASSWORD 'strongpassword';
GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonlyuser;
If your read-only user doesn't have permission to list tables (i.e. \d returns no results), it's probably because you don't have USAGE permissions for the schema. USAGE is a permission that allows users to actually use the permissions they have been assigned. What's the point of this? I'm not sure. To fix:
# You can either grant USAGE to everyone
GRANT USAGE ON SCHEMA public TO public;
# Or grant it just to your read only user
GRANT USAGE ON SCHEMA public TO readonlyuser;
I’ve created a convenient script for that; pg_grant_read_to_db.sh. This script grants read-only privileges to a specified role on all tables, views and sequences in a database schema and sets them as default.
I read trough all the possible solutions, which are all fine, if you remember to connect to the database before you grant the things ;) Thanks anyway to all other solutions!!!
user#server:~$ sudo su - postgres
create psql user:
postgres#server:~$ createuser --interactive
Enter name of role to add: readonly
Shall the new role be a superuser? (y/n) n
Shall the new role be allowed to create databases? (y/n) n
Shall the new role be allowed to create more new roles? (y/n) n
start psql cli and set a password for the created user:
postgres#server:~$ psql
psql (10.6 (Ubuntu 10.6-0ubuntu0.18.04.1), server 9.5.14)
Type "help" for help.
postgres=# alter user readonly with password 'readonly';
ALTER ROLE
connect to the target database:
postgres=# \c target_database
psql (10.6 (Ubuntu 10.6-0ubuntu0.18.04.1), server 9.5.14)
You are now connected to database "target_database" as user "postgres".
grant all the needed privileges:
target_database=# GRANT CONNECT ON DATABASE target_database TO readonly;
GRANT
target_database=# GRANT USAGE ON SCHEMA public TO readonly ;
GRANT
target_database=# GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly ;
GRANT
alter default privileges for targets db public shema:
target_database=# ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO readonly;
ALTER DEFAULT PRIVILEGES
If your database is in the public schema, it is easy (this assumes you have already created the readonlyuser)
db=> GRANT SELECT ON ALL TABLES IN SCHEMA public to readonlyuser;
GRANT
db=> GRANT CONNECT ON DATABASE mydatabase to readonlyuser;
GRANT
db=> GRANT SELECT ON ALL SEQUENCES IN SCHEMA public to readonlyuser;
GRANT
If your database is using customschema, execute the above but add one more command:
db=> ALTER USER readonlyuser SET search_path=customschema, public;
ALTER ROLE
The not straightforward way of doing it would be granting select on each table of the database:
postgres=# grant select on db_name.table_name to read_only_user;
You could automate that by generating your grant statements from the database metadata.
Taken from a link posted in response to despesz' link.
Postgres 9.x appears to have the capability to do what is requested. See the Grant On Database Objects paragraph of:
http://www.postgresql.org/docs/current/interactive/sql-grant.html
Where it says: "There is also an option to grant privileges on all objects of the same type within one or more schemas. This functionality is currently supported only for tables, sequences, and functions (but note that ALL TABLES is considered to include views and foreign tables)."
This page also discusses use of ROLEs and a PRIVILEGE called "ALL PRIVILEGES".
Also present is information about how GRANT functionalities compare to SQL standards.
CREATE USER username SUPERUSER password 'userpass';
ALTER USER username set default_transaction_read_only = on;