Postgres "REASSIGN OWNED" fails with "permission denied for schema public" - amazon-aurora

In our application we have components that can modify DB structure. At times it is desirable to prohibit this and to do this we reduce the level of privilege of the Postgres group role that owns all of our DB structures (tables, sequences, views, etc.). Additionally, because owners can still do whatever they want to existing structure they own, we reassign ownership of all the structures to a temporary user role who cannot login. The latter step is accomplished by issuing a REASSIGN OWNED BY XXX TO YYY; command. Recently, when testing, we noticed that for one of our databases this command was failing with a "permission denied for schema public" error message. This is odd since all of our DBs define equivalent group roles for reading, writing, and administrative/DDL operations and this operation continues to work fine for other DBs on the same Postgres host. Furthermore I can see no inconsistencies in schema privileges or object ownership. Is there a way to get more information from Postgres about what it is objecting to?
I have run the \dn+ command on both the affected / failing DB and on one behaving normally and see no difference in the privilege of the "superuser" I'm using to issue the commands, not to mention create and administer the roles in question. Most of my searches on the subject yield results where the REASSIGN OWNED BY command is used to help remedy permission denied errors and I've yet to find any information on what to look for if that command itself is throwing such an error. Any help would be greatly appreciated.
The user I'm using is the highest privileged user AWS Aurora provides us. The output from \du for the role is basically unusable since that role is a member of ALL groups for all the DBs on our system resulting in a membership list that is hundreds of elements long and almost unreadable in the terminal let alone here. I'll try and provide more selective information using the pg_roles and pg_auth_members views below.
d0000000033_0000000031=> select current_user;
current_user
--------------
datamart
(1 row)
d0000000033_0000000031=> select * from pg_roles where rolname = 'datamart';
rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolconnlimit | rolpassword | rolvaliduntil | rolbypassrls | rolconfig | oid
----------+----------+------------+---------------+-------------+-------------+----------------+--------------+-------------+---------------+--------------+-----------+-------
datamart | f | t | t | t | t | f | -1 | ******** | infinity | f | | 16393
(1 row)
The schema privileges for the DB are as follows:
d0000000033_0000000031=> \dn+ public
List of schemas
Name | Owner | Access privileges | Description
--------+----------+---------------------------------------------+------------------------
public | rdsadmin | datamart=UC/rdsadmin +| standard public schema
| | d0000000033_0000000031_g_admins=UC/rdsadmin+|
| | d0000000033_0000000031_g_writers=U/rdsadmin+|
| | d0000000033_0000000031_g_readers=U/rdsadmin |
(1 row)
The memberships for the associated group roles are:
d0000000033_0000000031=> select r1.rolname as role, r2.rolname as member, r3.rolname as grantor, am.admin_option
from pg_roles r1
inner join pg_auth_members am on r1.oid = am.roleid
inner join pg_roles r2 on r2.oid = am.member
inner join pg_roles r3 on r3.oid = am.grantor
where r1.rolname like 'd0000000033_0000000031_g%';
role | member | grantor | admin_option
----------------------------------+---------------------------------+----------+--------------
d0000000033_0000000031_g_admins | datamart | datamart | t
d0000000033_0000000031_g_admins | clari_global_admins | datamart | f
d0000000033_0000000031_g_admins | d0000000033_0000000031_u_admin | datamart | f
d0000000033_0000000031_g_readers | datamart | datamart | t
d0000000033_0000000031_g_readers | clari_global_readers | datamart | f
d0000000033_0000000031_g_readers | d0000000033_0000000031_u_reader | datamart | f
d0000000033_0000000031_g_writers | datamart | datamart | t
d0000000033_0000000031_g_writers | clari_global_writers | datamart | f
d0000000033_0000000031_g_writers | d0000000033_0000000031_u_writer | datamart | f
(9 rows)
The details for the associated roles are:
d0000000033_0000000031=> select * from pg_roles where rolname like 'd0000000033_0000000031%' or rolname like 'clari_global%';
rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolconnlimit | rolpassword | rolvaliduntil | rolbypassrls | rolconfig | oid
----------------------------------+----------+------------+---------------+-------------+-------------+----------------+--------------+-------------+---------------+--------------+-----------+-------
clari_global_admins | f | f | f | f | f | f | -1 | ******** | | f | | 25971
clari_global_readers | f | t | f | f | f | f | -1 | ******** | | f | | 25973
clari_global_writers | f | f | f | f | f | f | -1 | ******** | | f | | 25972
d0000000033_0000000031_g_admins | f | f | f | f | f | f | -1 | ******** | | f | | 96724
d0000000033_0000000031_g_readers | f | f | f | f | f | f | -1 | ******** | | f | | 96730
d0000000033_0000000031_g_writers | f | f | f | f | f | f | -1 | ******** | | f | | 96725
d0000000033_0000000031_u_admin | f | t | f | f | t | f | -1 | ******** | | f | | 97411
d0000000033_0000000031_u_reader | f | t | f | f | t | f | -1 | ******** | | f | | 97078
d0000000033_0000000031_u_writer | f | t | f | f | t | f | -1 | ******** | | f | | 96731
(9 rows)
I just realized I missed an important piece of information RE the user I'm reassigning ownership to:
d0000000033_0000000031=> \du d0000000033_0000000031_u_restricted;
List of roles
Role name | Attributes | Member of
-------------------------------------+--------------+-----------
d0000000033_0000000031_u_restricted | Cannot login | {}
The flow of commands leading up to the failure of the REASSIGN OWNED BY is this:
REVOKE ALL ON SCHEMA public FROM d0000000033_0000000031_g_admins;
GRANT USAGE ON SCHEMA public TO d0000000033_0000000031_g_admins;
CREATE ROLE d0000000033_0000000031_u_restricted WITH ADMIN datamart;
GRANT ALL ON SCHEMA public TO d0000000033_0000000031_u_restricted;
REASSIGN OWNED BY d0000000033_0000000031_g_admins TO d0000000033_0000000031_u_restricted;
Executing these for the affected DB I get:
d0000000033_0000000031=> REVOKE ALL ON SCHEMA public FROM d0000000033_0000000031_g_admins;
WARNING: no privileges could be revoked for "public"
REVOKE
d0000000033_0000000031=> GRANT USAGE ON SCHEMA public TO d0000000033_0000000031_g_admins;
WARNING: no privileges were granted for "public"
GRANT
d0000000033_0000000031=> CREATE ROLE d0000000033_0000000031_u_restricted WITH ADMIN datamart;
CREATE ROLE
d0000000033_0000000031=> GRANT ALL ON SCHEMA public TO d0000000033_0000000031_u_restricted;
WARNING: no privileges were granted for "public"
GRANT
d0000000033_0000000031=> REASSIGN OWNED BY d0000000033_0000000031_g_admins TO d0000000033_0000000031_u_restricted;
ERROR: permission denied for schema public
Executing the same for my unaffected DB I get:
d0000000033_0000000029=> REVOKE ALL ON SCHEMA public FROM d0000000033_0000000029_g_admins;
WARNING: no privileges could be revoked for "public"
REVOKE
d0000000033_0000000029=> GRANT USAGE ON SCHEMA public TO d0000000033_0000000029_g_admins;
WARNING: no privileges were granted for "public"
GRANT
d0000000033_0000000029=> CREATE ROLE d0000000033_0000000029_u_restricted WITH ADMIN datamart;
CREATE ROLE
d0000000033_0000000029=> GRANT ALL ON SCHEMA public TO d0000000033_0000000029_u_restricted;
WARNING: no privileges were granted for "public"
GRANT
d0000000033_0000000029=> REASSIGN OWNED BY d0000000033_0000000029_g_admins TO d0000000033_0000000029_u_restricted;
REASSIGN OWNED

Related

Query optimization to split all authorized pairs values in two columns with defaults

I would like to optimize oracle query to transform table with all authorized pair values in two columns. The first with exceptions of authorized by default and the second exceptions of forbidden by default.
For example :
| ROOM | USER AUTHORIZED |
| ROOM1 | A |
| ROOM2 | A |
| ROOM2 | B |
| ROOM3 | B |
| ROOM4 | B |
| ROOM5 | B |
| ROOM2 | C |
| ROOM3 | C |
| ROOM4 | C |
To:
ROOM | USER EXCEPTION AUTHORIZED | USER EXCEPTION FORBIDDEN |
ROOM1| A | B;C
ROOM2| A |
ROOM3| |
ROOM4| |
ROOM5| | C
I used this query to get exception forbidden:
WITH USERS
AS( SELECT USER FROM ROOM_USERS GROUP BY USER HAVING count(*)>(SELECT count(*)/2 FROM ROOM_USERS )),
ROOMS
AS( SELECT DISTINCT(ROOM) as room FROM ROOM_USERS)
SELECT ROOM, LISTAGG(USER, ';') WITHIN GROUP (ORDER BY USER)
FROM USERS, ROOMS
WHERE USERS.user||ROOMS.room NOT IN (
SELECT user||room
FROM ROOMS_USERS
)
GROUP BY room;

Is there any trigger run before dynamic sql (as execute immediate or dbms_sql.execute)?

I'm creating a feature to allow user create proceduce, function, package and run they with some permissions (grant by this program, not database permission) save in a table.
I want create a trigger run before any dynamic SQL and analyse it to prevent dangerous SQL statements from bad users? Like following
+------------+
| PROGRAM |------------------------------+
+------------+ |
| |
v |
+------------+ +------------+ +---------------+
| USER'S SQL | | PERMISSION | | SESSION INFO |
+------------+ +------------+ +---------------+
|___________________|________________|
v
+-----------------+
| TRIGGER |-------------+
+-----------------+ |
| Allow | Not allow
v v
+-----------------------+ +-------------+
| EXECUTE IMMEDIATE | | ERROR |
+-----------------------+ +-------------+
Can I create anythings like this?

Materialized View having UNKNOWN staleness - Oracle 11G

I am working on Oracle 11G.
One of my Materialized view has become UNKNOWN (MY_MAT_VW1). You can check the output of the ALL_MVIEWS below.
OWNER | MVIEW_NAME | CONTAINER_NAME | QUERY | QUERY_LEN | UPDATABLE | UPDATE_LOG | MASTER_ROLLBACK_SEG | MASTER_LINK | REWRITE_ENABLED | REWRITE_CAPABILITY | REFRESH_MODE | REFRESH_METHOD | BUILD_MODE | FAST_REFRESHABLE | LAST_REFRESH_TYPE | LAST_REFRESH_DATE | STALENESS | AFTER_FAST_REFRESH | UNKNOWN_PREBUILT | UNKNOWN_PLSQL_FUNC | UNKNOWN_EXTERNAL_TABLE | UNKNOWN_CONSIDER_FRESH | UNKNOWN_IMPORT | UNKNOWN_TRUSTED_FD | COMPILE_STATE | USE_NO_INDEX | STALE_SINCE | NUM_PCT_TABLES | NUM_FRESH_PCT_REGIONS | NUM_STALE_PCT_REGIONS
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
MY_DB | MY_MAT_VW1 | MY_MAT_VW1 | select.. | 6728 | N | | | | N | GENERAL | DEMAND | COMPLETE | IMMEDIATE | NO | COMPLETE | 14-Nov-16 | UNKNOWN | NA | N | Y | N | N | N | N | VALID | N | 0 | | |
MY_DB | MY_MAT_VW2 | MY_MAT_VW2 | select.. | 7074 | N | | | | N | TEXTMATCH | DEMAND | COMPLETE | IMMEDIATE | NO | COMPLETE | 13-Nov-16 | FRESH | NA | N | N | N | N | N | N | FRESH | N | 0 | 0 | |
The queries for the materialized view contain complex joins between multiple tables, inline views and unions.
As per my understanding (UNKNOWN_PLSQL_FUNC column) I guess there is a PLSQL Function which is causing the staleness to become UNKNOWN. However I am not sure which one.
I tried re-compiling and refreshing it but no luck.
Can anyone provide me some information on how to detect the root cause and make sure it does not become UNKNOWN again.
Also is there any implication of it on the data stored within it?
Below is just a sample I've created to replicate the scenario.
SELECT * FROM ENTITY_T;
ID | ENTITY_TYPE | FIRST_NAME | LAST_NAME | LEGAL_NAME
--------------------------------------------------
1 | INDIVIDUAL | JOHN | LESSEN |
2 | INDIVIDUAL | ROSAN | MEL |
3 | CORP | SIGMA | | SIGMA CORPORATION
--Function to get name base upon type
CREATE OR REPLACE FUNCTION GET_NAME (P_ID IN NUMBER)
RETURN VARCHAR2
DETERMINISTIC
AS
LV_NAME VARCHAR2(200);
BEGIN
SELECT CASE ENTITY_TYPE WHEN 'INDIVIDUAL' THEN FIRST_NAME ||' '|| LAST_NAME
WHEN 'CORP' THEN LEGAL_NAME
ELSE 'NONE'
END INTO LV_NAME
FROM ENTITY_T
WHERE ID=P_ID;
RETURN LV_NAME;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN 'NO ID FOUND';
WHEN OTHERS THEN
RETURN 'OTHER ERROR';
END;
--Materialized view creation
CREATE MATERIALIZED VIEW TEST_MV
AS
SELECT ID,ENTITY_TYPE,GET_NAME(ID) NAME
FROM ENTITY_T;
SELECT MVIEW_NAME,STALENESS,AFTER_FAST_REFRESH,UNKNOWN_PLSQL_FUNC,COMPILE_STATE,STALE_SINCE
FROM ALL_MVIEWS WHERE MVIEW_NAME='TEST_MV';
MVIEW_NAME | STALENESS | AFTER_FAST_REFRESH | UNKNOWN_PLSQL_FUNC | COMPILE_STATE | STALE_SINCE
----------------------------------------------------------------------------------------------
TEST_MV | UNKNOWN | NA | Y | VALID |
The Oracle Issue/Doc ID 757537.1 mentioned by JSapkota states clearly, that this is not a bug, but correct/expected behaviour:
STALENESS of the mview, refering to PL/SQL function is set to UNKOWN
as one cannot determine PL/SQL function changes. Current behaviour is
correct as per the design & code.
I guess using DETERMINISTIC functions instead of the default scope could prevent it.
As per the My Oracle Support this could be a bug(7582462).
As there is no solution to this bug, you have to deal with fact that staleness will show unknown, or not use functions on Materialized View definition.
Reference:DBA_MVIEWS Shows STALENESS Value of UNKNOWN After Refresh (Doc ID 757537.1)

How to Move a whole partition to another tabel on another database?

Database: Oracle 12c
I want to take single partition, or a set of partitions, disconnect it from a Table, or set of tables on DB1 and move it to another table on another database. I would like to avoid doing DML to do this for performance reasons (It needs to be fast).
Each Partition will contain between three and four hundred million records.
Each Partition will be broken up into approximately 300 Sub-Partitions.
The task will need to be automated.
Some thoughts I had:
Somehow put each partition in it's own datafile upon creation, then detaching from the source and attaching it to the destination?
Extract the whole partition (not record-by-record)
Any other non-DML Solutions are also welcom
Example (Move Part#33 from both to DB#2, preferably with a single, operation):
__________________ __________________
| DB#1 | | DB#2 |
|------------------| |------------------|
|Table1 | |Table1 |
| Part#1 | | Part#1 |
| ... | | ... |
| Part#33 | ----> | Part#32 |
| Subpart#1 | | |
| ... | | |
| Subpart#300 | | |
|------------------| |------------------|
|Table2 | |Table2 |
| Part#1 | | Part#1 |
| ... | | ... |
| Part#33 | ----> | Part#32 |
| Subpart#1 | | |
| ... | | |
| Subpart#300 | | |
|__________________| |__________________|
Please read the document below with all the examples of exchanging partitions of table.
https://oracle-base.com/articles/misc/partitioning-an-existing-table-using-exchange-partition

Linux - Postgres psql retrieving undesired table

I've got the following problem:
There is a Postgres database which I need to get data from, via a Nagios Linux distribution.
My intention is to make a resulting SELECT be saved to a .txt, that would be sent via email to me using MUTT.
Until now, I've done:
#!/bin/sh
psql -d roaming -U thdroaming -o saida.txt << EOF
\d
\pset border 2
SELECT central, imsi, mapver, camel, nrrg, plmn, inoper, natms, cba, cbaz, stall, ownms, imsi_translation, forbrat FROM vw_erros_mgisp_totalizador
EOF
My problem is:
The .txt "saida.txt" is bringing me info about the database, as follows:
Lista de relações
Esquema | Nome | Tipo | Dono
---------+----------------------------------+-----------+------------
public | apns | tabela | jmsilva
public | config_imsis_centrais | tabela | thdroaming
public | config_imsis_sgsn | tabela | postgres
(3 Registers)
+---------+---------+----------+---------+---------+--------+------------+-------+---------+----------+-------+-------+------------------+-----------+
| central | imsi | mapver | camel | nrrg | plmn | inoper | natms | cba | cbaz | stall | ownms | imsi_translation | forbrat |
+---------+---------+----------+---------+---------+--------+------------+-------+---------+----------+-------+-------+------------------+-----------+
| MCTA02 | 20210 | | | | | INOPER-127 | | | | | | | |
| MCTA02 | 20404 | | | | | INOPER-127 | | | | | | | |
| MCTA02 | 20408 | | | | | INOPER-127 | | | | | | | |
| MCTA02 | 20412 | | | | | INOPER-127 | | | | | | | |
.
.
.
How could I make the first table not to be imported to the .txt?
Remove the '\d' portion of the script which causing listing the tables in the DB you see at the top of your output. So your script will become:
#!/bin/sh
psql -d roaming -U thdroaming -o saida.txt << EOF
\pset border 2
SELECT central, imsi, mapver, camel, nrrg, plmn, inoper, natms, cba, cbaz, stall, ownms, imsi_translation, forbrat FROM vw_erros_mgisp_totalizador
EOF
To get the output to appear CSV formatted in a file named /tmp/output.csv do you can do the following:
#!/bin/sh
psql -d roaming -U thdroaming -o saida.txt << EOF
\pset border 2
COPY (SELECT central, imsi, mapver, camel, nrrg, plmn, inoper, natms, cba, cbaz, stall, ownms, imsi_translation, forbrat FROM vw_erros_mgisp_totalizador) TO '/tmp/output.csv' WITH (FORMAT CSV)
EOF

Resources