PostgreSQL limit audit table permission for user - sql

I was creating a Audit trail table where some_user can edit a table, Autid table logs it. But some_user cannot edit the audit table
I have the following table items. which looks like this
| date | id | s_px | c_px | fee |
+------------+-----+----------+----------+-----+
| 2015-01-01 | 001 | 5355.00 | 5355.00 | 2 |
| 2015-01-01 | 002 | 13240.00 | 13240.00 | 3 |
| 2015-01-01 | 003 | 5840.00 | 5840.00 | 1 |
| 2015-01-01 | 004 | 20.55 | 20.59 | 5 |
| 2015-01-01 | 005 | 64.42 | 64.42 | 6 |
I created an audit_tb to track any changes for the table items with trigger that call a function audit_function() for any Insert, Update, Delete.
audit_function() insert any changes on items table to audit_tb.
https://wiki.postgresql.org/wiki/Audit_trigger
Everything works fine when I am a power user have all access to items and audit_tb. Problem is power_user can also modify the audit_tb.
So I created some_user which can change items, and can only select audit_tb.
The Problem is with this audit_function() cannot Insert since user some_user is limited to Select only.
ERROR: permission denied for relation audit_tb

You need to grand permission to the user
GRANT ALL PRIVILEGES ON DATABASE mydb TO admin_user;

Create audit_function() as the superuser with the option security definer. If you do that, the function will run with the privileges of the superuser (=the owner), not with the privileges of the users that triggered the function.

Related

Complex SQL query aggregation and grouping on athena

I have a table like this:
| db | chat_id | Admin | user |
+-------------+-------------------+------------+---------------+
| db_1 | chat_id1 | max | greg |
| db_1 | chat_id2 | max | bob |
| db_1 | chat_id3 | max | greg |
| db_1 | chat_id2 | helen | greg |
| db_2 | chat_id1 | alan | greg |
I would like to retrieve the number of chat performed by users for each database (db) and the last part where I fail, retrieve also a list of all mentors by users.
The final output should be like this for example (notice there is only one time max for greg in the admin column)
| db | user | nb_of_chat | admins |
+-------------+---------------+--------------+---------------+
| db_1 | greg | 3 | max, helen |
| db_1 | bob | 1 | max |
| db_2 | greg | 1 | alan |
I wrote the following query but it doesn't aggregate the admins and i have separated nb_of chats/mentors.
SELECT db, user, COUNT(chat_id), admins
FROM "chat_db"."chats"
GROUP BY db, user, admins;
As expected I am getting the following result (but I only want it on one line by db/user with grouped admin in the same column):
| db | user | nb_of_chat | admins |
+-------------+---------------+--------------+---------------+
| db_1 | greg | 2 | max |
| db_1 | greg | 1 | helen |
| ... | ... | ... | ... |
Have you an idea how to perform it ?
Thank you for your time !
Regards.
Firsly, remove adminsfrom the group by clause, since you want to aggregate it. Then, in Presto, you can do string aggregation as follows:
select db,user, count(*) no_of_chats,
array_join(array_agg(admins), ', ') all_admins
from "chat_db"."chats"
group by db, user;
You can add an order by clause to array_agg() if needed:
select db,user, count(*) no_of_chats,
array_join(array_agg(admins order by admins), ', ') all_admins
from "chat_db"."chats"
group by db, user;
Note that I changed count(chat_id) to count(*): both are equivalent (since chat_id probably is a non-nullable column), and the former is (sligthly) more efficient, and makes the intent clearer in my opinion.
Try using array_agg():
select db, user, count(chat_id), array_agg(admins)
from "chat_db"."chats"
group by db, user;
If you want one row per db:
select db, count(*) as num_chats, count(distinct user) as num_users, array_agg(admins)
from "chat_db"."chats"
group by db;

Better way to grant access to data within a table based on user?

I'm trying design a system for an API that grants users access to a data table Data based on a permission table Permissions which is related to a group table Group. When a user makes a request for data (from the Data table), my API should only return rows from the Data table based on the values within the columns of the Data table that they have been granted to
view.
By default, a user will have no access to any rows when requesting data through my API. However, I'd like to grant access to Data based on values in columns.
For example If my Data table contains information about news articles and has columns title, news_source, posted_date, and other similar columns
id | title | news_source | posted_date | ...
-----+----------+-----------------------+-------------+------
1 | ... | NYTimes | 2019-12-30 |
2 | ... | BBC | 2019-12-30 |
3 | ... | BBC | 2019-12-30 |
4 | ... | Washington Post | 2019-12-30 |
5 | ... | NYTimes | 2019-12-30 |
6 | ... | NYTimes | 2020-01-01 |
7 | ... | Boston Globe | 2020-01-01 |
In this example, I'd like to grant a group access to get data only from NYTimes, posted after 2020-01-01, etc...
To do this, I've implemented the schema below
+-----+ +--------------+
|Group|<-------|Permission |
+-----+ +--------------+
|name | |group_id |
|... | |column_name |
+-----+ |text_value |
|date_value |
+--------------+
For Group, name is just the name of the group and the ellipse represents some other non-relevant columns. In Permissions, I have the foreign key to Group (group_id), the name of the column in the Data table that I'm accessing (column_name), and the value I'm granting access to (text_value or date_value depending on the column I'm referencing).
Right now, when a user makes a request for data, I run this SQL to apply the permissions (if the user's group has id = 1).
SELECT * FROM Data d
INNER JOIN Permission p1 ON p1.group_id = 1 AND p1.column_name = 'news_source' AND p1.text_value = d.news_source
INNER JOIN Permission p2 ON p2.group_id = 1 AND p2.column_name = 'posted_date' AND p2.date_value >= d.posted_date;
This will work, but I was wondering if there was a better more organized way to go about this. I feel there would be a lot of redundancy in this model across multiple groups with the same permissions.

Audit data migration into Oracle

I am having a task to migrate data from another database to Oracle database.
And data from previous database has audit information, i.e. tracking of create/update of records with update_time and update_user. For simplicity, let's assume the previous database I am talking about is an excel file of the following format:
Key | Value | Update_Time | Update_User |
----|-------|-------------|-------------|
a | 1 | 23/04/2020 | user1 |
b | 2 | 21/04/2020 | user2 |
a | 3 | 20/04/2020 | user1 |
a | 4 | 19/04/2020 | user5 |
a | 5 | 18/04/2020 | user2 |
What is the best practice to move data into Oracle such that user can still query those audit info along with the new audit given that the data is now being saved to a new table in Oracle below? Does Oracle provide any native solution for this? I try Oracle Flashback, but not sure how to include those previous audit, because as I understand, we can only query Flashback for data change from now on. Ideally, I want to store only the latest data table in Oracle like this, as they are the actual active data:
Key | Value | Last_Update_Time | Last_Update_User |
----|-------|------------------|------------------|
a | 1 | 23/04/2020 | user1 |
b | 2 | 21/04/2020 | user2 |
Let's say user continue edit row with key b on 24/04/2020, then I want to fetch those result for UI display (currently I am using python sqlalchemy to access the db, but a solution with a sql query should be fine for the start)
Key | Value | Update_Time | Update_User |
----|-------|-------------|-------------|
b | 7 | 24/04/2020 | user2 | ---> this is an update on the new oracle table above
a | 1 | 23/04/2020 | user1 | ---> those rows below I want to somehow load into the oracle without explicitly create a new table for it
b | 2 | 21/04/2020 | user2 |
a | 3 | 20/04/2020 | user1 |
a | 4 | 19/04/2020 | user5 |
a | 5 | 18/04/2020 | user2 |
After the change, the main data table in Oracle should look below
Key | Value | Last_Update_Time | Last_Update_User |
----|-------|------------------|------------------|
a | 1 | 23/04/2020 | user1 |
b | 7 | 24/04/2020 | user2 |
YOu can use the below select query
SELECT AD.* FROM Audit_table AD,
(SELECT Key,Max(Update_time) Updated_Time,Last_updated_USer
From Audit_table
group by Key,Last_updated_USer)rec
where AD.Key=rec.Key
AND AD.Updated_Time=rec.Updated_Time
AND AD.Last_updated_USer=rec.Last_updated_USer;

Last accessed timestamp of a Netezza table?

Does anyone know of a query that gives me details on the last time a Netezza table was accessed for any of the operations (select, insert or update) ?
Depending on your setup you may want to try the following query:
select *
from _v_qryhist
where lower(qh_sql) like '%tablename %'
There are a collection of history views in Netezza that should provide the information you require.
Netezza does not track this information in the catalog, so you will typically have to mine that from the query history database, if one is configured.
Modern Netezza query history information is typically stored in a dedicated database. Depending on permissions, you may be able to see if history collection is enabled, and which database it is using with the following command. Apologies in advance for the screen-breaking wrap to come.
SYSTEM.ADMIN(ADMIN)=> show history configuration;
CONFIG_NAME | CONFIG_DBNAME | CONFIG_DBTYPE | CONFIG_TARGETTYPE | CONFIG_LEVEL | CONFIG_HOSTNAME | CONFIG_USER | CONFIG_PASSWORD | CONFIG_LOADINTERVAL | CONFIG_LOADMINTHRESHOLD | CONFIG_LOADMAXTHRESHOLD | CONFIG_DISKFULLTHRESHOLD | CONFIG_STORAGELIMIT | CONFIG_LOADRETRY | CONFIG_ENABLEHIST | CONFIG_ENABLESYSTEM | CONFIG_NEXT | CONFIG_CURRENT | CONFIG_VERSION | CONFIG_COLLECTFILTER | CONFIG_KEYSTORE_ID | CONFIG_KEY_ID | KEYSTORE_NAME | KEY_ALIAS | CONFIG_SCHEMANAME | CONFIG_NAME_DELIMITED | CONFIG_DBNAME_DELIMITED | CONFIG_USER_DELIMITED | CONFIG_SCHEMANAME_DELIMITED
-------------+---------------+---------------+-------------------+--------------+-----------------+-------------+---------------------------------------+---------------------+-------------------------+-------------------------+--------------------------+---------------------+------------------+-------------------+---------------------+-------------+----------------+----------------+----------------------+--------------------+---------------+---------------+-----------+-------------------+-----------------------+-------------------------+-----------------------+-----------------------------
ALL_HIST_V3 | NEWHISTDB | 1 | 1 | 20 | localhost | HISTUSER | aFkqABhjApzE$flT/vZ7hU0vAflmU2MmPNQ== | 5 | 4 | 20 | 0 | 250 | 1 | f | f | f | t | 3 | 1 | 0 | 0 | | | HISTUSER | f | f | f | f
(1 row)
Also make note of the CONFIG_VERSION, as it will come into play when crafting the following query example. In my case, I happen to be using the version 3 format of the query history database.
Assuming history collection is configured, and that you have access to the history database, you can get the information you're looking for from the tables and views in that database. These are documented here. The following is an example, which reports when the given table was the target of a successful insert, update, or delete by referencing the "usage" column. Here I use one of the history table helper functions to unpack that column.
SELECT FORMAT_TABLE_ACCESS(usage),
hq.submittime
FROM "$v_hist_queries" hq
INNER JOIN "$hist_table_access_3" hta
USING (NPSID, NPSINSTANCEID, OPID, SESSIONID)
WHERE hq.dbname = 'PROD'
AND hta.schemaname = 'ADMIN'
AND hta.tablename = 'TEST_1'
AND hq.SUBMITTIME > '01-01-2015'
AND hq.SUBMITTIME <= '08-06-2015'
AND
(
instr(FORMAT_TABLE_ACCESS(usage),'ins') > 0
OR instr(FORMAT_TABLE_ACCESS(usage),'upd') > 0
OR instr(FORMAT_TABLE_ACCESS(usage),'del') > 0
)
AND status=0;
FORMAT_TABLE_ACCESS | SUBMITTIME
---------------------+----------------------------
ins | 2015-06-16 18:32:25.728042
ins | 2015-06-16 17:46:14.337105
ins | 2015-06-16 17:47:14.430995
(3 rows)
You will need to change the digit at the end of the $v_hist_table_access_3 view to match your query history version.

Combining the datas of two columns with the same name to create a view

I am working on a project and for my login credentials checking process I am trying to create a view in which the name,surname,username and password of customers,workers and admins are stored so that I can search faster and I have two questions.
Do you think it is a good idea to do that ?
If yes, can you help me how to do that?
Thank you in advance.
1) yes, but for simplicity rather than performance (and a few other reasons)
2) CREATE OR REPLACE VIEW viewname AS your_select_statement;
If the front-end is a single interface for both customers and employees then the tables should not be separated in the first place. If you have a person who is both a customer and a worker then they would appear on two tables and it is possible the data would not be synchronized between the two and if you create a view then they would appear twice. Instead create a single table for all people and have separate tables for data specific to customers, workers and admins.
Something like:
People
id | firstname | surname | username | password_hash | password_salt
--------------------------------------------------------------------
1 | alice | abbot | aa | abc | 123
2 | bob | barnes | bb | def | 456
3 | charlotte | carol | cc | ghi | 789
4 | daniel | david | dd | jkl | 036
Customers
id | Credit_Limit | has_Trade_Account
-------------------------------------
2 | 0 | 0
3 | 2000 | 1
Workers
id | Joining_Date | Grade
--------------------------
1 | 2015-01-01 | 5
3 | 2000-12-25 | 3
Admins
id | Edit_Permissions
----------------------
3 | Orders
3 | Stock