Redshift Default Permissions for new tables in schema [duplicate] - postgresql

This question already has an answer here:
PostgreSQL privilege grant not visible
(1 answer)
Closed 3 years ago.
I am using a service to generate tables in our redshift warehouse. It drops tables in a schema and rebuilds them daily. I altered the default privileges to give user1 read access to it but it isn't working.
Running this from the redshift docs:
select pg_get_userbyid(d.defacluser) as user,
n.nspname as schema,
case d.defaclobjtype when 'r' then 'tables' when 'f' then 'functions' end
as object_type,
array_to_string(d.defaclacl, ' + ') as default_privileges
from pg_catalog.pg_default_acl d
left join pg_catalog.pg_namespace n on n.oid = d.defaclnamespace;
Gives me this line for the schema in question:
+-------------+------------------+-------------+---------------------+
| user | schema | object_type | default_privileges |
+-------------+------------------+-------------+---------------------+
| mysuperuser | schemainquestion | tables | user1=r/mysuperuser |
+-------------+------------------+-------------+---------------------+
This is the command I ran to alter the privileges:
ALTER DEFAULT PRIVILEGES IN SCHEMA schemainquestion GRANT SELECT ON TABLES to user1;
I verified with has_schema_privilege that user1 has USAGE permissions on the schema.

You must run the ALTER DEFAULT PRIVILEGES statement as the user that will create the tables, or add a FOR ROLE clause for that user.

Related

Dropping a User, Handling Blank Default Privileges in Redshift, pg_default_acl

I am looking to reset permissions in a Development environment and I noticed two users I could not drop. I checked all of the permissions etc and the only reference to these users I found was in pg_default_acl.
select * from pg_default_acl;
Returns:
defacluser | defaclnamespace | defaclobjtype | defaclacl
----------------------------------------------------------------------
101 | 0 | r | {}
122 | 0 | r | {}
Are these empty permissions preventing me from dropping these users?
I ended up finding a resolution when I came across these admin scripts on github:
awslabs/amazon-redshift-utils
The issue ended up being default privileges that were granted to other users.
What is the exact error with which the drop fails? These users could be owning tables or views or functions.
You can find the tables with select * from pg_tables where tableowner in ('one', 'two');
The views with: select * from pg_views where viewowner in ('one', 'two');
The functions with: select * from pg_proc where proowner in (select usesysid from pg_user where usename in ('one', 'two'));
And then your users may own schemas too:
select * from pg_namespace where nspowner in (select usesysid from pg_user where usename in ('one', 'two'));

How to access information_schema foreign key constraints with read-only user in Postgres?

Introduction
I've been developing a wizard to create complex database Postgres queries for users without any programming/SQL background. Thanks to foreign key constraints stored in a view in information_schema, the user may select any number of tables and the tool will find the correct join settings (thus, the user does not have to add ON table_a.field_1 = table_b.field_2).
While developing, I have been using an administration database user and now wanted to change that to a read-only user to make it more secure. However, this read-only user seems not to be able to access the foreign key constraints.
Current situation
When more than one table has been selected, the tool tries to get the connections between the various tables in order to know how to join them. During that process, the following query is executed:
SELECT
tc.constraint_name,
tc.table_name,
kcu.column_name,
ccu.table_name AS foreign_table_name,
ccu.column_name AS foreign_column_name
FROM information_schema.table_constraints AS tc
JOIN information_schema.key_column_usage AS kcu
ON tc.constraint_name = kcu.constraint_name
JOIN information_schema.constraint_column_usage AS ccu
ON ccu.constraint_name = tc.constraint_name
WHERE constraint_type = 'FOREIGN KEY'
AND ccu.table_name = 'TableB'
AND tc.table_name IN ('TableA');
(Note: the last WHERE clause uses IN because there can be more than one base table available. TableA is the base table and each successfully connected/joined table will be available for additional joins, e.g. a third table could use AND ccu.table_name = 'TableC' AND tc.table_name IN ('TableA', 'TableB'); and so on.)
When using the admin db user (with most common privileges like GRANT, SELECT, INSERT, UPDATE, DELETE, TRUNCATE, ...) executes the query, the result looks something like this:
constraint_name | table_name | column_name | foreign_table_name | foreign_column_name
----------------+------------+-------------+--------------------+---------------------
constraint1 | TableA | field_1 | TableB | field_2
(1 row)
But when the read-only db user runs that query, it returns:
constraint_name | table_name | column_name | foreign_table_name | foreign_column_name
----------------+------------+-------------+--------------------+---------------------
(0 rows)
Due to the existing but not returned foreign key constraint entry, the joins can not be properly written as SQL and the user generated query (by using the wizard) fails.
What I tried
First of course, I thought the read-only user (ro_user) might not have the permissions to access tables and views in database information_schema. So I ran
GRANT SELECT ON ALL TABLES IN SCHEMA information_schema TO ro_user;
as admin but to no avail. Getting more into the depths of the documentation, I found that all tables and views in information_schema are available and accessible to any user by default in postgres anyways. So granting the select privilege shouldn't even change anything.
Just to make sure, I also ran
GRANT REFERENCES ON ALL TABLES IN SCHEMA actual_database TO ro_user;
but of course, this didn't change anything neither, since REFERENCESis only needed for creating new foreign key, I just need to read them.
Next, I thought, maybe the sql from the tool is failing due to some information not being available, so I queried the three views separately by running:
SELECT * FROM information_schema.table_constraints AS tc WHERE constraint_type = 'FOREIGN KEY';
SELECT * FROM information_schema.key_column_usage AS kcu;
SELECT * FROM information_schema.constraint_column_usage AS ccu;
And sure enough, the last one wouldn't return any single row for the ro_user:
psql=> SELECT * FROM information_schema.constraint_column_usage AS ccu;
table_catalog | table_schema | table_name | column_name | constraint_catalog | constraint_schema | constraint_name
---------------+--------------+------------+-------------+--------------------+-------------------+-----------------
(0 rows)
whereas the admin user got lots of results. So, it was coming down to that one view information_schema.constraint_column_usage.
As I was typing out that question over the course of an hour recollecting and boiling down all the ideas I tried during the last days, I finally found the cause.
The view constraint_column_usage identifies all columns in the current database that are used by some constraint. Only those columns are shown that are contained in a table owned by a currently enabled role.
From documentation via this SO answer
And through that I found a solution
SELECT
conrelid::regclass AS table_from,
conname,
pg_get_constraintdef(c.oid) AS cdef
FROM pg_constraint c
JOIN pg_namespace n
ON n.oid = c.connamespace
WHERE contype IN ('f')
AND n.nspname = 'public'
AND pg_get_constraintdef(c.oid) LIKE '%"TableB"%'
AND conrelid::regclass::text IN ('"TableA"')
ORDER BY conrelid::regclass::text, contype DESC;
It doesn't output the same format as the old query, but it contains the same information and is - most importantly - available to the ro_user.

Why does a PostgreSQL SELECT query return different results when a schema name is specified?

I have a PostgreSQL database table with 4 columns - labeled column_a, column_b, etc. I want to query this table with a simple select query:
select * from table_name;
I get a handful of results looking like:
column_a | column_b
---------+---------
'a value'|'b_value'
But when I use this query:
select * from schema_name.table_name;
I get the full result:
column_a | column_b | column_c | column_d
---------+----------+----------+---------
'a value'|'b value' |'c value' |'d_value'
Columns c and d were added at a later date, after initial table creation. My question is: Why would the database ignore the later columns when the schema name is left out of the select query?
Table names are not unique within a database in Postgres. There can be any number of tables named 'table_name' in different schemas - including the temporary schema, which always comes first unless you explicitly list it after other schemas in the search_path. Obviously, there are multiple tables named table_name. You must understand the role of the search_path to interpret this correctly:
How does the search_path influence identifier resolution and the "current schema"
The first table lives in a schema that comes before schema_name in your search_path (or schema_name is not listed there at all). So the unqualified table name is resolved to this table (or view). Check the list of tables named 'table_name' that your current role has access to in your database:
SELECT *
FROM information_schema.tables
WHERE table_name = 'table_name';
Views are just special tables with an attached RULE internally. They could play the same role as a regular table and are included in the above query.
Details:
How to check if a table exists in a given schema

How to retrieve the comment of a PostgreSQL database?

I recently discovered you can attach a comment to all sort of objects in PostgreSQL. In particular, I'm interested on playing with the comment of a database. For example, to set the comment of a database:
COMMENT ON DATABASE mydatabase IS 'DB Comment';
However, what is the opposite statement, to get the comment of mydatabase?
From the psql command line, I can see the comment along with other information as a result of the \l+ command; which I could use with the aid of awk in order to achieve my goal. But I'd rather use an SQL statement, if possible.
First off, your query for table comments can be simplified using a cast to the appropriate object identifier type:
SELECT description
FROM pg_description
WHERE objoid = 'myschema.mytbl'::regclass;
The schema part is optional. If you omit it, your current search_path decides visibility of any table named mytbl.
Better yet, there are dedicated functions in PostgreSQL to simplify and canonize these queries. The manual:
obj_description(object_oid, catalog_name) ... get comment for a
database object
shobj_description(object_oid, catalog_name) ... get comment for a shared database object
Description for table:
SELECT obj_description('myschema.mytbl'::regclass, 'pg_class');
Description for database:
SELECT pg_catalog.shobj_description(d.oid, 'pg_database') AS "Description"
FROM pg_catalog.pg_database d
WHERE datname = 'mydb';
How do you find out about that?
Well, reading the excellent manual is enlightening. :)
But there is a more direct route in this case: most psql meta commands are implemented with plain SQL. Start a session with psql -E, to see the magic behind the curtains. The manual:
-E
--echo-hidden
Echo the actual queries generated by \d and other backslash commands. You can use this to study psql's internal operations. This
is equivalent to setting the variable ECHO_HIDDEN to on.
To get the comment on the database, use the following query:
select description from pg_shdescription
join pg_database on objoid = pg_database.oid
where datname = '<database name>'
This query will get you table comment for the given table name:
select description from pg_description
join pg_class on pg_description.objoid = pg_class.oid
where relname = '<your table name>'
If you use the same table name in different schemas, you need to modify it a bit:
select description from pg_description
join pg_class on pg_description.objoid = pg_class.oid
join pg_namespace on pg_class.relnamespace = pg_namespace.oid
where relname = '<table name>' and nspname='<schema name>'
For tables, try
\dd TABLENAME
This shows the comment I added to a table
This query will get only table comment for all tables
SELECT RelName,Description
FROM pg_Description
JOIN pg_Class
ON pg_Description.ObjOID = pg_Class.OID
WHERE ObjSubID = 0
This query will return the comment of a table
SELECT obj_description('public.myTable'::regclass)
FROM pg_class
WHERE relkind = 'r' limit 1
To get the comments on all the databases (not on their objects like tables etc.) :
SELECT datname, shobj_description( oid, 'pg_database' ) AS comment
FROM pg_database
ORDER BY datname
An example showing databases, sizes and descriptions from a shell script:
psql -U postgres -c "SELECT datname,
format('%8s MB.', pg_database_size(datname)/1000000) AS size,
shobj_description( oid, 'pg_database' ) as comment
FROM pg_database ORDER BY datname"
Sample output:
datname | size | comment
----------------------+--------------+-----------------------------------------------------
last_wikidb | 18 MB. | Wiki backup from yesterday
postgres | 7 MB. | default administrative connection database
previous_wikidb | 18 MB. | Wiki backup from the day before yesterday
some_db | 82 MB. |
template0 | 7 MB. | unmodifiable empty database
template1 | 7 MB. | default template for new databases

What PostgreSQL query or view can determine who set a users permissions?

I am trying to revoke a database user's permissions and it seems that permissions can only be revoked by the user who granted them. There is thread here discussing the issue.
http://archives.postgresql.org/pgsql-bugs/2007-05/msg00234.php
The thread dates back to 2007 and I am not quite sure whether it is viewed as bug and whether that problem is still present in PostgreSQL 8.4 which I am using.
Is there a query or a view that can display that information? That way I can use set session authorization and revoke it.
PostgreSQL 8.4 is outdated. Check out the versioning policy for details. But since it is the standard behavior of SQL (as Tom Lane states in the linked discussion you provided), it's not likely to have changed.
Privileges are stored in the system catalog with the respective object. For instance, for a table:
SELECT n.nspname, c.relname, c.relacl
FROM pg_catalog.pg_class c
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.oid = 'myschema.mytbl'::regclass -- your tablename here
Would produce something like:
nspname | relname | relacl
----------+---------+---------------------------------------------
myschema | mytbl | {postgres=arwdDxt/postgres,fuser=r/fadmin}
The rolename after the slash is the grantor. To revoke, as user fadmin (or any superuser):
REVOKE SELECT ON TABLE myschema.mytbl FROM fuser;
There are similar *acl columns in other system tables. pg_namespace for schemas etc. See the list of system tables in the manual.
A simpler way would be to use pgAdmin and select an object in the object browser to the left. The ACL will be displayed in the properties pane, top right.