Why would you ever GRANT special permissions on a SEQUENCE? - postgresql

What would be a good use case for restricting permissions on a SEQUENCE in a database?
Usually, when I GRANT permissions on a TABLE, I also GRANT the same permissions on the SEQUENCES. It seems like a redundant step, so is there a good use case for when SEQUENCE permissions differ from TABLE permissions?

I can see 3 reasons.
Not all sequences are attached to a table, so it makes sense to allow access to them separately.
For instance, an application that sending messages to a server may need a unique identifier for all messages, in which case a DB sequence is a convenient way to generate ids that will be unique across all clients.
Unlike tables, regular users are very rarely granted all privileges on a sequence when they are on a table and they are different anyway (respectively SELECT USAGE UPDATE and SELECT INSERT UPDATE DELETE TRUNCATE REFERENCES TRIGGER). That would create a mess to grant all privileges on sequences. The right approach is to only grant USAGE on sequences.
You can probably imagine the risk of letting anyone reset a sequence to 1 when the said sequence is used to generate primary key values on a table.
In that sense, privilege on sequences really need to be managed separately.
The standard behavior for all objects in a database and more generally for everything related to security, is that you have to explicitly grant privileges on an object before anybody can use it.
The two bulletpoints above merely explain why privileges must be specific to sequences but here is a more general consideration about why privileges are never granted by default.

Related

very confused about permissions in postgres

I have multiple databases and each of them have multiple schemas.
I have a set of apps that connect to these databases. Each app has it own user and, depending on their function, the apps can:
read / write all schemas and tables of a specific db, set functions/notifications
read only all schemas and tables of a specific db
The schemas and tables can be created at any time, so the permissions need to be set with ALTER DEFAULT.
My understanding is that the ALTER DEFAULT has to be done by the user that will create the future tables. Is that correct?
Since I can have scenarios where User 1 can RO db A, but RW db B, while User 2 can only RO db B, etc.. using roles doesn't seem to be of any help here.
So I'm a bit confused how to set that up.
Then comes the next complication:
I can assign permissions as either SELECT (RO), or SELECT, INSERT, UPDATE, DELETE (RW), but:
what about sequences? I don't want a RW user to be able to alter the sequences, but they need to be able to use them
then how does it work with functions? the RW users need to be able to set/update their own functions
Any example of this setup would be greatly appreciated because going through the doc didn't help me much and most of the questions / answers on SO seems to be very similar yet never exactly the same, so it's quite confusing :)
Edit, following 'a_horse_with_no_name''s suggestion in the comments, I did this:
here is my init.sql:
CREATE DATABASE accounts;
CREATE DATABASE analysis;
CREATE DATABASE exchange;
GRANT CONNECT ON DATABASE exchange TO capture, analyzer, sunny, viewer;
GRANT CONNECT ON DATABASE analysis TO analyzer, sunny, viewer;
GRANT CONNECT ON DATABASE accounts TO sunny;
then I log in as admin to db exchange and do:
GRANT pg_write_all_data TO capture;
GRANT pg_read_all_data TO analyzer, sunny, viewer;
and I create a table called instruments there
then I log in as capture to to db exchange and do an insert, and I get:
42501: permission denied for table instruments
so using the capture user on the exchange db, I should have the pg_write_all_data property; why do I get the error?
It looks like I have to grant usage of instruments to capture.. which defeats the purpose of the pg_write_all_data. If I do the grant, then it works. So it looks like adding the role doesn't work.
Since your users shall have different permissions in different databases, define read-only and read-write roles for each database.
For example, db1_ro has read-only permissions in db1, and db2_rw has read-write permissions in db2. Then you can grant them both to a user, and the user will have different permissions in different databases.
Using a sequence typically means to call nextval(). So you should give that user the USAGE privilege. To use setval(), the user would need UPDATE. Nobody except the owner can ALTER an object anyway.
Functions cannot be set or updated, only executed, for which there is the EXECUTE privilege, which is granted to PUBLIC by default.

How to give access to a database to only specific users?

This is probably a silly question and I'm sure it's a problem with my mental model.
Ultimately I want to set privileges on a role such that any other roles in that role have CRUD access to all the current tables and automatically all of the future tables in perpetuity for said database. but ONLY those roles that have been explicitly added to said 'group role'.
It's not clear how to do this.
There is no way to get exactly what you want, but you can get close enough.
Dealing with existing tables is simple enough: just revoke all privileges that have been granted on the tables and grant access to your group role. The command that makes this easy is
GRANT/REVOKE ALL
ON ALL TABLES IN SCHEMA ... TO/FROM ...;
To deal with future tables, you'll have to restrict the circle of users that may create tables to a few, and for each of them run
ALTER DEFAULT PRIVILEGES FOR ROLE creating_user
GRANT ALL ON TABLES TO ...;
If you cannot enumerate the users that can create tables, an event trigger running at the end of each CREATE statement may be an alternative.

Prevent users from seeing objects to which they have no access

WHAT
I have users that share a database, but have their own schemas. Each user is only able to access objects on their schema - they have been explicitly revoked usage on the schemas that are not theirs.
I am not concerned about a user inappropriately accessing others' schema; however, I would very much like for them not to be able to see the contents or even the existence of the other schema to which they have no access.
WHY
I am aware that this is mostly "cosmetic", but the primary reason for this would be that my users do not have to shift through objects that they cannot access in certain tools (Tableau, DB IDEs, etc) - so I think it does add some practical value.
ATTEMPTS
I've been searching for a solution, but haven't found one that works. For instance, I revoked users' access on information_schema and pg_catalog (I know this is not recommended); however, it had no effect.
Is this at all possible?
It is not possible to limit access to pg_class, pg_attribute, or pg_proc. Therefore what you want can only be achieved by separating each user in their own databases rather than individual schemas.

Automatically allow access to tables in postgres from a user

I have a Postgresql database for a web application. The database is owned by a particular user on the system, let's say foouser. As the owner, this user has full permissions on the database.
The server also has another user, let's say webappuser, which is the user under which the application server runs. Instead of specifying a username and password in the web application's config file, I want to use "peer" authentication. I have gotten the authentication to work properly, but I ran into the following issue.
When I created the webappuser role in Postgresql, I granted it LOGIN permission as well as GRANT ALL ON DATABASE foo TO webappuser; and within the database GRANT ALL ON SCHEMA public TO webappuser;.
The issue that I am having is with the table permissions. Unlike MySQL which allows access by default to all tables if you have access to the database (a reasonable assumption in my opinion), Postgresql denies access to all of the tables even though permission has been given on the schema and the database. In order to get around this, I have to explicitly grant permissions on all new tables, views, procedures, etc. that I create using GRANT ALL ON TABLE table_name TO webappuser; (and similarly for views, etc.).
It ends up that any time I run a database migration, I have to add the permissions to the database for the new tables that were created. The problem is that I can't add this permission information to the migrations themselves because developer machines don't have that additional user. In any case, that really looks like the wrong way of doing things.
How can I allow access to the database tables from this additional user without needing manual intervention every time a table, view, procedure, etc. is created?
BONUS POINTS: Is there a way to restrict the user's permission to only CRUD operations instead of full permissions and still do the whole thing automatically?
Without experience with the specifics of Laravel migrations: When you do migrations on the same server there should be no problem, so long as the permissions are also migrated, because the webappuser is available cluster-wide.
When migrating to a different server you need to create the user on that new server and set the permissions for all migrated objects. You basically have two ways to do that.
The first is to set default privileges on the tables in the schema before you migrate or GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA sch_name TO webappuser after the migration. Default privileges are set with:
ALTER DEFAULT PRIVILEGES IN SCHEMA sch_name
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO webappuser;
Both commands are fully SQL-standard compliant so you should have no problems across compliant architectures.
Keep in mind that any other tables created in the same schema will also have privileges set for webappuser. Setting privileges this way for an "untrusted" user (the person using the web application) is not recommended in a production environment because of potential privilege leaks; in a development environment it may be acceptable.
The second - which I would favour personally - is to write a stored procedure that sets the appropriate permissions. Do the migration, run the stored procedure once and you should be up-and-running. This gives you more control over the permission granting. The procedure could be something like:
CREATE FUNCTION grant_webapp_privileges() RETURNS void AS $$
-- Create the webappuser, if necessary
CREATE ROLE webappuser LOGIN;
-- Grant privileges on all required objects
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE table1 TO webappuser;
...
$$ LANGUAGE SQL;
On the master database you simply need to keep the stored procedure up-to-date when you create or drop new relations. If Laravel supports insertion of code blocks not in the schema you are migrating, you can make the above procedure an anonymous code block that gets executed after the migration.
(As an aside, I NEVER give webappuser-like roles CRUD access. Instead I always provide access through views that hide some of the underlying data model specifics, such as a person having an address, contact_information and other details; the view serves it all up in one big row. That way you can easily change the underlying relations and update the view, rather than having to tweak your web application. Same principle really as OOP and easier to manage privileges.)

Is there a way to disable updates/deletes but still allow triggers to perform them?

Basically, I want to be able to use the REVOKE command to disable UPDATE and DELETE, but I still want the triggers on a table to update my rows.
My triggers perform on newly inserted rows, and update a specific field. So I still want this behaviour, but wouldn't they be disabled with REVOKE or with a RULE. (I saw an SO post)
Is there a way to keep using the UPDATE/INSERT commands in TRIGGERS but disabling the rest?
Yes, this is possible.
Triggers are run with the privileges of the trigger function, defaulting to SECURITY INVOKER which means, the trigger function is effectively executed with the privileges of the current_user, in your case the one inserting rows.
If the current user does not have the required privileges for the tables your trigger function operates on, your original operation in the underlying table will error out.
However, you can use SECURITY DEFINER for the trigger function to have this function run with the privileges of the OWNER of the function.
If you have a superuser own the trigger function, it can do everything - which would be a possible security hazard. Consider the instructions in the manual about Writing SECURITY DEFINER Functions Safely.
But it's wiser to make a plain role with just the necessary privileges OWNER of the trigger function. You can even just create a "daemon" role without login, acting as privilege bundle for such operations. You would then grant only the needed privileges (on schemas, tables, sequences ...) to this daemon role. For more sophisticated designs you should bundle privileges in "group roles" (again, no login) and grant these group roles to roles that need it (to the daemon role in this example), effectively making them "member of the group". I do that a lot.
Consider this related answer on dba.SE concerning the privileges on the function itself:
What are the privileges required to execute a trigger function in PostgreSQL 8.4?