Postgres GRANT not applied on parent - postgresql

I'm in trouble with grant in postgresql (version 9.3).
I'm trying to restrict a ROLE 'client_1'. I want it to be able to do only select for one table. But there is inheritance between tables.
Here is my table structure:
CREATE TABLE public.table_a (...);
CREATE TABLE table_a_partitions.child_1 (...) INHERITS (public.table_a);
CREATE TABLE table_a_partitions.child_2 (...) INHERITS (public.table_a);
GRANT SELECT ON table_a_child_1 TO client_1;
It's okay when I do a select on child_2, there is an error, but if I do a SELECT * FROM table_a; for example it also reads the forbidden table child_2. I would my client access only child_1 (and some other in the future) results when he does SELECT * FROM table_a;.
Is there a simple way to solve this problem ?
Thank you

You would need to use a VIEW in PostgreSQL 9.3 to solve this problem. If you upgrade to 9.5, however, you could use row-level security.
As a note as to why, the grant check only occurs on the level of the initial relation queried. This means if you query a view, you need access to the view's contents, but the view owner (NOT YOU) needs access to the underlying relations. This allows a view to be useful for information hiding. Similarly with inheritance, this structure allows you to forbid rows to be inserted or queried directly from partitions of a table, but to allow different queries via the parent table. So this is a consequence of design priorities, not a bug.
Before row-level security, you would basically create a view and fold in user privilege criteria into the view (with partitioning/inheritance this is also a good idea for other reasons since your insert/update/delete triggers can return exactly what the db would do even though it cannot on a table).
As for row-level security, PostgreSQL 9.5 does allow you to specify row-level policies (conditions that get appended to insert/select/update/delete queries) and that provides something a little more manageable in some cases than the view approach.

Related

Power BI + Postgres: Read Only User

I already posted a similar question to the Power Bi forum, but probably it's too short-sighted to search the solution on Power Bi side.
We have a postgres schema and a postgres user that has the following rights: granted connect to db and schema, revoked create on schema, granted select privilege on all tables in schema. Nothing more. The user works in the db client, can only read from the tables in the schema, cannot create, insert, read, update, delete anything.
When we import the schema in Power Bi with our Read Only User, the relations between the tables is not displayed. The postgres documentation writes the following:
"The view table_constraints contains all constraints belonging to tables that the current user owns or has some privilege other than SELECT on."
This would mean, our Read Only User must have granted insert, delete or update permissions to make Power Bi able to display the table relations?! Our postgres db shall work as a datawarehouse and give customers the possibility to connect from their local Power Bi Desktop or their companies Power Bi instance, so we don't have too much influence on Power Bi side at all.
Is there a permission we can give the Read Only User that keeps him read only but allows to resolve the table relations in Power Bi?
Thanks and kind regards!
We tried giving the user a insert privilege on all tables, that made Power Bi resolve the table relations but is conflicting with our Read Only behave.
We read documentation and tried to find out, if there are other possibilities than the above.
That is really unfortunate, but the SQL standard explicitly requires that (e.g. in ISO/IEC 9075-11:2003, 5.58):
5.58 TABLE_CONSTRAINTS view
Function
Identify the table constraints defined on tables in this catalog that are accessible to a given user or role.
Definition
CREATE VIEW TABLE_CONSTRAINTS AS
SELECT CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME,
TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME,
CONSTRAINT_TYPE, IS_DEFERRABLE, INITIALLY_DEFERRED
FROM DEFINITION_SCHEMA.TABLE_CONSTRAINTS
WHERE ( TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME ) IN
( SELECT TP.TABLE_CATALOG, TP.TABLE_SCHEMA, TP.TABLE_NAME
FROM DEFINITION_SCHEMA.TABLE_PRIVILEGES AS TP
WHERE TP.PRIVILEGE_TYPE <> 'SELECT'
[...]
So really this is a shortcoming of Power BI, if it is supposed to work with a read-only user. On the other hand, if they want to be database agnostic, they have little choice but to use the information_schema.
But interesting as that may be, you are looking for a solution or a workaround. If you look at the view definition of information_schema.table_constraints, you will see that visibility is determined by this condition:
pg_has_role(r.relowner, 'USAGE'::text) OR
has_table_privilege(r.oid, 'INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER'::text) OR
has_any_column_privilege(r.oid, 'INSERT, UPDATE, REFERENCES'::text)
I'd say that the most promising privilege here is REFERENCES, which allows a user to create a foreign key pointing to the table. If the user doesn't have the CREATE privilege on any schema, they cannot create a table that references the table, so the privilege is effectively useless. Still, if you grant the user REFERENCES on all tables, the constraints will become visible. It is hacky, but it should be good enough.

Access a PostgreSQL table without directly using its name

Problem
I want to prevent a database user from discovering other tables through the use of queries like select * from pg_tables; by adding sanitization in the application that executes the query.
I'm busy investigating how to limit access to a PostgreSQL database for queries coming through an application layer where I can still sanitize a query with code. The user does not connect straight to the database. They can execute queries through an application layer (e.g. query is passed through as text and then executed by my application).
I've already enabled Row Security Policies (row level security) and the user is accessing their data through a view, so "access to data" has been solved (I believe). The problem I'm trying to solve now is to prevent them from "seeing" other tables in the database, especially the built-in PG tables.
The only grant the user has is grant select on my_view to my_user_role;
Assumption / attempted solution
My assumption is that a user can't access a table without explicitly writing it into the query, so if I were to look for certain keywords in the query string, I can reject a query without executing it. E.g. if the clause/characters "pg_tables" are anywhere in the query, then I can reject it. But, this feels naive.
const query = "select * from pg_tables;";
const flagged = query.includes("pg_tables");
if (flagged) throw Error("Not allowed!");
// Continue to run the user's query
^ This would work reliably if the only way to access a table like pg_tables is to type that out explicitly.
Question
Is there a way to access a table like pg_tables without naming it explicitly, in PostgreSQL [given the context above]?
An example of a similar situation in javascript is if you have a function fooBar(), then you can access it indirectly by calling global["foo" + "Bar"]() – hence not using the text "fooBar" exactly.

DB2 iseries materialized view refresh

I have created the following materialized query table:
CREATE TABLE SCHEMA.TABLE AS
(SELECT * FROM SCHEMA.TABLEEXAMPLE)
DATA INITIALLY DEFERRED
REFRESH DEFERRED
MAINTAINED BY USER
DISABLE QUERY OPTIMIZATION;
When I execute a REFRESH TABLE SCHEMA.TABLE it get locked for others users to read from it.
Reading this doc from IBM https://www.ibm.com/support/knowledgecenter/en/SSEPGG_9.7.0/com.ibm.db2.luw.sql.ref.doc/doc/r0000977.html
I tried to execute this statement:
REFRESH TABLE SCHEMA.TABLE ALLOW READ ACCESS
But I get the following error: SQL State: 42601 Unexpected keyword ALLOW
What I'm missing on statement? Is there other way to allow read access to materialized query table while it is beign updated?
MQTs on Db2 for IBM i lag behind the functionality available in Db2 for LUW.
I've never bother with them, instead an encoded vector index (EVI) with computed columns meets every need I've every considered. (Note that Db2 LUW doesn't have EVIs)
Per Mao's comment, you might try deleting an recreating the MQT with the following:
CREATE TABLE SCHEMA.TABLE AS
(SELECT * FROM SCHEMA.TABLEEXAMPLE)
DATA INITIALLY DEFERRED
REFRESH DEFERRED
MAINTAINED BY USER
DISABLE QUERY OPTIMIZATION
with NC;
But I think a refresh would still require exclusive access to the MQT.
The only options I can think of for "refreshing" an MQT while it is being used
programmatically , using either triggers on the base tables or perhaps a process that uses SQL to update a few rows at a time.
removing the DISABLE QUERY OPTIMIZATION and not accessing the MQT directly. Instead depend on the optimizer to access it when appropriate. Now you can create a version of it every few hours and the Db should start using the newer version for new queries. Once the older one is no longer being used, you delete it (or REFRESH it)

How to create a synonym for a table in PostgreSQL

I am migrating this Oracle command to PostgreSQL:
CREATE SYNONYM &user..emp FOR &schema..emp;
Please suggest to me how I can migrate the above command.
PostgreSQL does not support SYNOSYM or ALIAS. Synonym is a non SQL 2003 feature implemented by Microsoft SQL 2005 (I think). While it does provide an interesting abstraction, but due to lack of relational integrity, it can be considered a risk.
That is, you can create a synonym, advertise it to you programmers, the code is written around it, including stored procedures, then one day the backend of this synonym (or link or pointer) is changed/deleted/etc leading to a run time error. I don't even think a prepare would catch that.
It is the same trap as the symbolic links in unix and null pointers in C/C++.
But rather create a DB view and it is already updateable as long as it contains one table select only.
CREATE VIEW schema_name.synonym_name AS SELECT * FROM schema_name.table_name;
You don't need synonyms.
There are two approaches:
using the schema search path:
ALTER DATABASE xyz SET search_path = schema1, schema2, ...;
Put the schema that holds the table on the search_path of the database (or user), then it can be used without schema qualification.
using a view:
CREATE VIEW dest_schema.tab AS SELECT * FROM source_schema.tab;
The first approach is good if you have a lot of synonyms for objects in the same schema.

how to grant update or select on some specified row in postgresql table?

I want to create some roles in my Postgresql DB and grant some access.
I have student role and i want to grant this user type : can edit only record a bout him/her in student table and can not edit other rows
how can i do it?
thanks
Create a view on the table with an appropriate where clause, then grant access to that:
create view students_view as
select col1, col2, col3 -- limit column access here
from mytable
where <whatever>; -- limit row access here
-- limit what he can do here
grant update, select to student_role;
BTW It is a commonly held misconception that you can't update a view, but that is only true if the view is a join or similarly complicated query.
PostgreSQL doesn't have row-level declarative security (yet, there's ongoing work into it) so if you can't just create a view - say, if you have many different people who need this access - you will probably need a SECURITY DEFINER helper function or trigger.
You've got a couple of options:
Write a SECURITY DEFINER function that lets them make only the permitted changes and limit their access to the table to SELECT, revoking UPDATE, DELETE, TRUNCATE and INSERT rights; or
write a trigger that tries to restrict them from making changes you don't want them to make and GRANT them write access to the table.
Of the two, the function and restricted rights approach is by far the safest option so long as you follow the SECURITY DEFINER secure coding guidelines set above - setting search_path for the function, avoiding dynamic SQL (EXECUTE) with string substitutions, etc.
The view approach given above can work quite nicely if it's a view that filters by current_user. You may also want to look at the new SECURITY BARRIER views; see this post for a useful discussion of them.
GRANT UPDATE(column) on tabela to user_name;