PostgreSQL can I inherit search_path variable from a "group role"? - postgresql

I know I can define the search_path variable for a login this way:
ALTER ROLE myrole SET search_path=public, foo, bar;
But if I use a lot of logins I will also use groups (groups in pgadmin which are in fact roles). I would like to define the search_path variable on group level, so I remove this previous SET and instead wrote:
ALTER ROLE mygroup SET search_path=public, foo, bar;
And just define login as a member of the group
GRANT mygroup TO myrole
Now, a SHOW search_path; show me only the default search-path "$user",public. Is there a way to define variables for the group level (so with inheritance)?

No you can't do that. The search_path is a variable not a grant-able object.

Related

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.

Storing a SQL user in a variable

I've generated some create scripts in SSMS (2014) and as part of the script there are several accounts created. We will be using different accounts based on whether the db is deployed to test, preproduction or production.
I would like to make it easy for the installer to change the accounts so I thought it would be a good idea if I could store the login(s) and declare this at the very top of the script, however I'm not sure how to go about this.
Currently I have:
CREATE USER [RAMBOLL\SVC_D-StructureDB] FOR LOGIN [somedomain\someaccount] WITH DEFAULT_SCHEMA=[dbo]
GO
ALTER ROLE [db_datareader] ADD MEMBER [somedomain\someaccount]
GO
ALTER ROLE [db_datawriter] ADD MEMBER [somedomain\someaccount]
GO
How can I declare the "somedomain\someaccount" and then reference it in the SQL code above? I tried but SQL didn't like it.
DECLARE #SomeAccount VARCHAR(255)
SET #SomeAccount = "[somedomain\someaccount]"
CREATE USER #SomeAccount FOR LOGIN #SomeAccount WITH DEFAULT_SCHEMA=[dbo]
GO
ALTER ROLE [db_datareader] ADD MEMBER #SomeAccount
GO
ALTER ROLE [db_datawriter] ADD MEMBER #SomeAccount
GO
These legacy and will-be-removed stored procedures allow parametrisation
sp_grantdbaccess
sp_addrolemember
However, I would consider an IF statement if only a few options. Then dynamic SQL if you need more flexibility
DECLARE #env VARCHAR(10)
SET #env = 'test'
IF #env = 'test'
BEGIN
CREATE USER [somedomain\someaccount] FOR LOGIN [somedomain\someaccount] WITH DEFAULT_SCHEMA=[dbo]
ALTER ROLE [db_datareader] ADD MEMBER [somedomain\someaccount]
ALTER ROLE [db_datawriter] ADD MEMBER [somedomain\someaccount]
END
ELSE IF #env = 'live'
BEGIN
CREATE USER [somedomain\liveaccount] FOR LOGIN [somedomain\liveaccount]WITH DEFAULT_SCHEMA=[dbo]
ALTER ROLE [db_datareader] ADD MEMBER [somedomain\liveaccount]
ALTER ROLE [db_datawriter] ADD MEMBER [somedomain\liveaccount]
END
...

Revoke SELECT from inherited role

I am having a slight problem getting permissions to work the way I want them.
I have a role that should generally be allowed to SELECT everywhere, there are a bunch of members to this role. One of them should NOT be allowed to select from a certain table.
I thought this would be possible by granting role membership to the general reader role and revoking SELECT from the restricted table.
It seems the the permissions of the parent role apply and not the specific permissions. Is there a way around this without having to maintain the permissions of the more restricted role or am I applying the role concept in PostgreSQL in a wrong way?
Here's a sample script:
-- as superuser
CREATE DATABASE permission_test;
\c permission_test
CREATE ROLE r_general_select;
CREATE ROLE r_restricted_select IN ROLE r_general_select;
-- set the default permissions
ALTER DEFAULT PRIVILEGES IN SCHEMA "public" GRANT SELECT ON TABLES TO "r_general_select";
CREATE TABLE "open"(
id SERIAL,
payload TEXT
);
insert into "open"(payload) values ('test');
-- covered by default privileges
GRANT SELECT ON "open" TO PUBLIC;
-- Tests
-- this is good
SET ROLE r_general_select;
SELECT * FROM "open";
RESET ROLE;
-- this is good
SET ROLE r_restricted_select;
SELECT * FROM "open";
RESET ROLE;
CREATE TABLE "restricted" (
id SERIAL,
payload TEXT
);
insert into "restricted"(payload) values ('test');
-- the role and it's members should be able to read
GRANT SELECT ON "restricted" TO r_general_select;
-- except for this one!
REVOKE SELECT ON "restricted" FROM r_restricted_select;
-- Tests
-- this is good
SET ROLE r_general_select;
SELECT * FROM restricted;
RESET ROLE;
-- this should barf with a permission violation
SET ROLE r_restricted_select;
SELECT * FROM restricted;
RESET ROLE;
--- CLEANUP
DROP OWNED BY "r_restricted_select" CASCADE;
DROP ROLE r_restricted_select ;
DROP OWNED BY "r_general_select" CASCADE;
DROP ROLE r_general_select ;
In PostgreSQL, role permissions are purely additive. There is no way in such a model to revoke from a descendant, inheriting role a permission granted on the inherited one.
To fix this you need to change your permissions approach and base it on permissions that always occur together. I usually do this by looking at functional dependencies and operational dependencies together.

What is a PostgreSQL table owner?

I am unsure about what does a PostgreSQL table owner means. I notice that it changes an attribute of the table itself and not about the owner because it is specified through an
ALTER TABLE table_name OWNER TO role_name;
You can see who is owner in certain table:
select * from pg_tables where tablename = 'my_tbl';
or you can see all tables by certain owner:
select * from pg_tables where tableowner = 'username';
The owner is (if nothing else happened) the user (role) that created the table. So if user arthur runs CREATE TABLE foo (id INTEGER), arthur owns the table.
The owner of a table has all privileges on it - including the privilege to drop it. Or the privilege to grant other users (roles) access to the table.
The SQL script generated by pg_dump typically includes the ALTER TABLE ... OWNER TO ... statement as those scripts are intended to be run by the DBA and in that case all tables would be owned by the DBA - which means the "real" owner could not change or access the tables.
Some excerpts from the official docs:
When an object is created, it is assigned an owner. The owner is normally the role that executed the creation statement. For most kinds of objects, the initial state is that only the owner (or a superuser) can do anything with the object. To allow other roles to use it, privileges must be granted.
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.)
Ordinarily, only the object's owner (or a superuser) can grant or revoke privileges on an object.
An object's owner can choose to revoke their own ordinary privileges, for example to make a table read-only for themselves as well as others. But owners are always treated as holding all grant options, so they can always re-grant their own privileges.

Grant all on a specific schema in the db to a group role in PostgreSQL

Using PostgreSQL 9.0, I have a group role called "staff" and would like to grant all (or certain) privileges to this role on tables in a particular schema. None of the following work
GRANT ALL ON SCHEMA foo TO staff;
GRANT ALL ON DATABASE mydb TO staff;
Members of "staff" are still unable to SELECT or UPDATE on the individual tables in the schema "foo" or (in the case of the second command) to any table in the database unless I grant all on that specific table.
What can I do make my and my users' lives easier?
Update: Figured it out with the help of a similar question on serverfault.com.
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA foo TO staff;
You found the shorthand to set privileges for all existing tables in the given schema. The manual clarifies:
(but note that ALL TABLES is considered to include views and foreign tables).
Bold emphasis mine. serial columns are implemented with nextval() on a sequence as column default and, quoting the manual:
For sequences, this privilege allows the use of the currval and nextval functions.
So if there are serial columns, you'll also want to grant USAGE (or ALL PRIVILEGES) on sequences
GRANT USAGE ON ALL SEQUENCES IN SCHEMA foo TO mygrp;
Note: IDENTITY columns in Postgres 10 or later use implicit sequences that don't require additional privileges. (Consider upgrading serial columns.)
What about new objects?
You'll also be interested in DEFAULT PRIVILEGES for users or schemas:
ALTER DEFAULT PRIVILEGES IN SCHEMA foo GRANT ALL PRIVILEGES ON TABLES TO staff;
ALTER DEFAULT PRIVILEGES IN SCHEMA foo GRANT USAGE ON SEQUENCES TO staff;
ALTER DEFAULT PRIVILEGES IN SCHEMA foo REVOKE ...;
This sets privileges for objects created in the future automatically - but not for pre-existing objects.
Default privileges are only applied to objects created by the targeted user (FOR ROLE my_creating_role). If that clause is omitted, it defaults to the current user executing ALTER DEFAULT PRIVILEGES. To be explicit:
ALTER DEFAULT PRIVILEGES FOR ROLE my_creating_role IN SCHEMA foo GRANT ...;
ALTER DEFAULT PRIVILEGES FOR ROLE my_creating_role IN SCHEMA foo REVOKE ...;
Note also that all versions of pgAdmin III have a subtle bug and display default privileges in the SQL pane, even if they do not apply to the current role. Be sure to adjust the FOR ROLE clause manually when copying the SQL script.
My answer is similar to this one on ServerFault.com.
To Be Conservative
If you want to be more conservative than granting "all privileges", you might want to try something more like these.
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO some_user_;
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA public TO some_user_;
The use of public there refers to the name of the default schema created for every new database/catalog. Replace with your own name if you created a schema.
Access to the Schema
To access a schema at all, for any action, the user must be granted "usage" rights. Before a user can select, insert, update, or delete, a user must first be granted "usage" to a schema.
You will not notice this requirement when first using Postgres. By default every database has a first schema named public. And every user by default has been automatically been granted "usage" rights to that particular schema. When adding additional schema, then you must explicitly grant usage rights.
GRANT USAGE ON SCHEMA some_schema_ TO some_user_ ;
Excerpt from the Postgres doc:
For schemas, allows access to objects contained in the specified schema (assuming that the objects' own privilege requirements are also met). Essentially this allows the grantee to "look up" objects within the schema. Without this permission, it is still possible to see the object names, e.g. by querying the system tables. Also, after revoking this permission, existing backends might have statements that have previously performed this lookup, so this is not a completely secure way to prevent object access.
For more discussion see the Question, What GRANT USAGE ON SCHEMA exactly do?. Pay special attention to the Answer by Postgres expert Craig Ringer.
Existing Objects Versus Future
These commands only affect existing objects. Tables and such you create in the future get default privileges until you re-execute those lines above. See the other answer by Erwin Brandstetter to change the defaults thereby affecting future objects.