PostgreSQL Revoke All Permissions from All Users and Roles - postgresql

I have several tables in Postgres 9.2.23 that I need to update privileges on regularly (daily or multiple times per day). I have a master list maintained elsewhere of who can select that I use to grant privileges. However, I want to make sure that anyone who used to be on the list but has dropped off has all their privileges revoked.
My thought was to do a clean sweep with something like:
revoke all privileges on my_table from [all users/roles]
before I grant select to the authorized users. However, I have not been able to figure out how to do a bulk revoke on all users/roles like that.
I know I can generate a list of users/roles that currently have privileges and go from there, but I am hoping there is a shortcut to bypass this.

There is a canonical answer to this provided by postgres documentation - also related to this post. I had to do a lot of trial and error myself to understand why there isn't a simple REVOKE ALL FROM role solution and the reason comes down to the answer:
REASSIGN OWNED BY doomed_role to surviving_role;
DROP OWNED BY doomed_role;
The reason (or so I have gathered) why this is the solution and you cannot just revoke all permissions, is that the "doomed_role" may be the owner of some entities and therefore they need to be reassigned first before revoking their permissions. So long as you do these commands in this order, it will do what you need to do.

There is a way, but it is not supported and dangerous, because it directly updates the system catalogs.
As superuser, you can do the following:
UPDATE pg_catalog.pg_class
SET relacl = NULL
WHERE oid = 'my_table'::regclass;
That resets the permissions on the object.
The problem is that this leaves some orphaned entries in pg_shdepend, where (among others) dependencies between users and tables are stored, so that you cannot drop a user that still has permissions on an object.
If I made no mistake, you can delete them as follows:
DELETE FROM pg_catalog.pg_shdepend sd
USING pg_catalog.pg_database d, pg_catalog.pg_authid a
WHERE sd.dbid = d.oid
AND sd.refobjid = a.oid
AND d.datname = current_database()
AND sd.classid = 'pg_class'::regclass
AND sd.refclassid = 'pg_authid'::regclass
AND sd.objid = 'my_table'::regclass
AND deptype = 'a';
If that seems too dangerous for you, right you are.
It is better to write a PL/pgSQL procedure that reads out pg_class.relacl, uses aclexplode to get the entries, loops though them and issues individual REVOKE statements.
Or simpler, just loop through all roles and revoke all privileges for everyone except for the owner.
You could safe yourself the whole mess if you don't grant privileges directly to the users, but use roles. Grant privileges to objects to the roles, and assign roles to the users.
That way it is much simpler to manage privileges.

Related

Readonly admin user in postgresql

we have a postgres database we want to run a number of checks against. Part of the tool involves looping over database all the database tables and views, checking grants and other things - so it would be entirely pointless if we had to grant access to this user to individual tables.
We want to be able to create a user that has full read privileges to anything, regardless of what permissions are set in the database - like a db owner - but has no write access at all.
Is this possible in any way?
The only way to do this is granting the SELECT privilege on every individual object that needs to be examined. You can make the work easier with
GRANT SELECT ON ALL TABLES/SEQUENCES/... IN SCHEMA ... TO ...;
You can also use ALTER DEFAULT PRIVILEGES to set the permissions on future objects.
I recommend that you create a readonly role and do all that once. Then you can create a read-only user by making the user a member of that role.
With postgresql 14 you can just do:
GRANT pg_read_all_data TO my_role;
https://www.postgresql.org/docs/14/predefined-roles.html

PostgreSQL Error [42501]: ERROR: must be owner of relation table

I am maintaining a database 'db' in which there are around 100 tables.I have one super user 'A' and 'A' is the owner of all tables. How can I give Alter permission to new user 'B' specific to single table without inheriting all permissions from 'A'.
I tried by providing Grant A to B;. This Grant option given all permissions from 'A' to 'B'. I want above scenario to restrict to one particular table.
Is this possible?
The documentation recently acquired this explanation:
The right to modify or destroy an object is inherent in being the object's owner, and cannot be granted or revoked in itself. (However, like all privileges, that right can be inherited by members of the owning role; see Section 21.3.)
So the only people who can run ALTER TABLE are:
Superusers
the table owner
members of the table owner role
So GRANT a TO b is the only way to give somebody the privilege.
You might be able to use a SECURITY DEFINER function that belongs to a, but be careful with that.

Forbid the owner of a user from GRANTing on that table

I'm trying to allow a database user to be able to alter/drop (certain) tables, but not GRANT privileges on them. Is this possible?
It looks like they need to be the owner of the tables, but from https://www.postgresql.org/docs/current/sql-grant.html
The right to drop an object, or to alter its definition in any way, is not treated as a grantable privilege; it is inherent in the owner, and cannot be granted or revoked. (However, a similar effect can be obtained by granting or revoking membership in the role that owns the object; see below.) The owner implicitly has all grant options for the object, too.
This sounds like it's not possible. However, is this definitely the case? Is there some way with triggers for example to make certain GRANTs fail?
Yes, only the owner of a table or a superuser can ALTER or DROP it, and these users can always GRANT privileges on the table.
Your only option is to create an event trigger that fires on GRANT and throws an error for the tables where it should be forbidden.

Locking down Public Group Role in PostgreSQL

Based on information I've been reading about privileges, I've learned it was good to revoke privileges before assigning any privileges to roles.
I had 1 user I was working with called appuser and I did the following:
REVOKE ALL PRIVILEGES ON SCHEMA PUBLIC FROM GROUP PUBLIC;
GRANT SELECT
ON public.table1 TO appuser;
GRANT SELECT
ON public.table1_id_seq TO appuser;
GRANT SELECT
ON public.table2 TO appuser;
GRANT SELECT
ON public.table2_id_seq TO appuser;
I did this in the hopes of removing any privileges to attached to any newly added users automatically part of the public role membership. Instead I got an error to the effect of appuser does not have permissions to the schema. However this is my misunderstanding with this error.
1. PgAdminIII doesn't show a public role under Group Roles for this schema
2. appuser does not appear to be a member of a group called public (because it doesn't exist in the interface).
3. appuser's privileges were explicitly granted, even if it were part of the public role
So...is there some sort of implicit group role called "Public" that is affecting this user's privileges? I don't understand what's going on. I've also attempted to find out where to display the group role membership from the pgAdminIII command line with no luck there either. By the way to fix it, I simply reversed the first command (i.e. GRANT ALL PRIVILEGES ON SCHEMA PUBLIC TO GROUP PUBLIC); However that basically undoes what I was originally trying to do (i.e. simply lock down the tables).
Any ideas?
Update: I have a hunch, I discovered what the issue may be. I don't think appuser has permissions to the schema. If I'm not mistaken schema permissions must be granted before access to any objects beneath the schema can happen.
Ok, so the answer here is kind of complicated as to why things behave the way they do and it is probably worth discussing the PostgreSQL security model to some extent.
PUBLIC is not a role or a group but rather a reserved word. The way the permissions work is that access granted to current inherited roles or PUBLIC apply. I have actually been bitten by this in the past as I build frameworks that wrap PostgreSQL roles.
In general, my view is that you want to do as follows:
Don't allow everyone to log into the db.
REVOKE CONNECT ON DATABASE mydb FROM public;
You may want to revoke access to schemas and tables from public too.
What I think you have done is something else, which is to revoke permissions from the schema (but not the tables in the schema!) and therefore reserve PUBLIC access for default permissions to tables in that schema. This may or may not be what you want. At any rate as you found out you need to add back permissions to the schema to get back to where you are.
Now I am not entirely sure what you are trying to do here. If you want to fully lock down your db, you need to do the same for all tables as well. Otherwise if you:
CREATE USER foo;
GRANT USAGE ON SCHEMA PUBLIC TO foo;
Then foo will have access to all the permissions that PUBLIC has on the tables.

Grant privileges to all users, current and future

I have a multi-schema, multi-user Postgres DB. There is one table that I would like ALL users, both current and future, to be able to SELECT from.
I can GRANT SELECT to all current users... but how can I create a table that allows any future user to select? Is there a way to set table permissions, rather than granting user privileges?
A filesystem analogy would be using chmod to make a file to be readable by the public.
grant select on the_table to public;
From the manual:
The key word PUBLIC indicates that the privileges are to be granted to all roles, including those that might be created later. PUBLIC can be thought of as an implicitly defined group that always includes all roles