PostgreSQL - Find ALL Privileges for a Group - postgresql

I have been attempting to remove a group role from an AWS PostgreSQL instance, and I've gotten stuck. I have successfully removed all dependencies from the group, except for 6, and I have no idea what those might be. I will explain what I have attempted to use to find these missing privileges, and I really appreciate some direction as to where I can look next. Each of these steps were performed on all of the databases and schemas within the Instance:
I used the following code to revoke all privileges from the group
REVOKE ALL ON DATABASE {dbname} FROM GROUP "Application_Access";
REVOKE ALL ON SCHEMA public FROM GROUP "Application_Access";
REVOKE ALL ON ALL TABLES IN SCHEMA public FROM GROUP "Application_Access";
REVOKE ALL ON ALL FUNCTIONS IN SCHEMA public FROM GROUP "Application_Access";
REVOKE ALL ON ALL SEQUENCES IN SCHEMA public FROM GROUP "Application_Access";
I used the psql commands to list all objects and their privileges, and this is what I found
\z = Nothing listed for Application_Access
\du and \dg = Application_Access - No inheritance, Cannot login, Member of {}
\d = All relations owned by OurAdmin
\db = All tablespaces owned by rdsadmin (AWS Admin)
\dtisv = Nothing
\ddp = This command is the only one showing Application_Access
Results of \ddp Command
I understand that \ddp shows default privileges for future database additions. I attempted to run ALTER PRIVILEGES commands. It forced me to make myself a member of Application_Access (ERROR: Must be a member of Role. why?!?) in order to run the command. This reassigned all of my privileges back to Application_Access and I was back to square one.
ALTER DEFAULT PRIVILEGES ON ROLE "Application_Access" REVOKE ALL ON DATABASE {dbname} FROM "Application_Access";
ALTER DEFAULT PRIVILEGES ON ROLE "Application_Access" REVOKE ALL ON ALL SCHEMAS FROM "Application_Access";
ALTER DEFAULT PRIVILEGES ON ROLE "Application_Access" REVOKE ALL ON TABLES FROM "Application_Access";
ALTER DEFAULT PRIVILEGES ON ROLE "Application_Access" REVOKE ALL ON FUNCTIONS FROM "Application_Access";
ALTER DEFAULT PRIVILEGES ON ROLE "Application_Access" REVOKE ALL ON SEQUENCES FROM "Application_Access";
I reran all of my previous steps, however, now when I run \ddp on all databases, it has added the first 3 lines that you see in the attached image above.
I have a script that I found and altered to show me all of the privileges on each database. I can pass it a Role to simplify the search. It isn't showing Application_Access as being associated with any objects either.
SELECT relacl
, SUBSTRING(
CASE WHEN strpos('r', SPLIT_PART( SPLIT_PART( ARRAY_TO_STRING( RELACL, '|' ), pu.groname, 2 ) , '/', 1 ) ) > 0 THEN ', SELECT' ELSE '' END
|| CASE WHEN strpos('w', SPLIT_PART( SPLIT_PART( ARRAY_TO_STRING( RELACL, '|' ), pu.groname, 2 ) , '/', 1 ) ) > 0 THEN ', UPDATE' ELSE '' END
|| CASE WHEN strpos('a', SPLIT_PART( SPLIT_PART( ARRAY_TO_STRING( RELACL, '|' ), pu.groname, 2 ) , '/', 1 ) ) > 0 THEN ', INSERT' ELSE '' END
|| CASE WHEN strpos('d', SPLIT_PART( SPLIT_PART( ARRAY_TO_STRING( RELACL, '|' ), pu.groname, 2 ) , '/', 1 ) ) > 0 THEN ', DELETE' ELSE '' END
|| CASE WHEN strpos('R', SPLIT_PART( SPLIT_PART( ARRAY_TO_STRING( RELACL, '|' ), pu.groname, 2 ) , '/', 1 ) ) > 0 THEN ', RULE' ELSE '' END
|| CASE WHEN strpos('x', SPLIT_PART( SPLIT_PART( ARRAY_TO_STRING( RELACL, '|' ), pu.groname, 2 ) , '/', 1 ) ) > 0 THEN ', REFERENCES' ELSE '' END
|| CASE WHEN strpos('t', SPLIT_PART( SPLIT_PART( ARRAY_TO_STRING( RELACL, '|' ), pu.groname, 2 ) , '/', 1 ) ) > 0 THEN ', TRIGGER' ELSE '' END
|| CASE WHEN strpos('X', SPLIT_PART( SPLIT_PART( ARRAY_TO_STRING( RELACL, '|' ), pu.groname, 2 ) , '/', 1 ) ) > 0 THEN ', EXECUTE' ELSE '' END
|| CASE WHEN strpos('U', SPLIT_PART( SPLIT_PART( ARRAY_TO_STRING( RELACL, '|' ), pu.groname, 2 ) , '/', 1 ) ) > 0 THEN ', USAGE' ELSE '' END
|| CASE WHEN strpos('C', SPLIT_PART( SPLIT_PART( ARRAY_TO_STRING( RELACL, '|' ), pu.groname, 2 ) , '/', 1 ) ) > 0 THEN ', CREATE' ELSE '' END
|| CASE WHEN strpos('T', SPLIT_PART( SPLIT_PART( ARRAY_TO_STRING( RELACL, '|' ), pu.groname, 2 ) , '/', 1 ) ) > 0 THEN ', TEMPORARY' ELSE '' END
, 3,10000)
|| namespace ||'.'|| item ||' TO '|| pu.groname ||' ;' AS grantsql
FROM (SELECT use.usename AS subject
,nsp.nspname AS namespace
,cls.relname AS item
,cls.relkind AS type
,use2.usename AS owner
,cls.relacl
FROM pg_user use
CROSS JOIN pg_class cls
LEFT JOIN pg_namespace nsp
ON cls.relnamespace = nsp.oid
LEFT JOIN pg_user use2
ON cls.relowner = use2.usesysid
WHERE cls.relowner = use.usesysid
AND nsp.nspname NOT IN ('pg_catalog', 'pg_toast', 'information_schema')
ORDER BY subject
,namespace
,item ) as x
JOIN pg_group pu ON array_to_string(relacl, '|') LIKE '%'|| pu.groname ||'%'
WHERE relacl IS NOT NULL
AND relacl::text LIKE '%Application%'
ORDER BY 2
***Based on everything that you see above, is there anything that anyone can think of that I left out, or didn't do correctly/completely? How can I resolve the default privileges - ALTER PRIVILEGES REVOKE ALL didn't work. Thank you for your help.

It took several days, but I guess I finally asked Google the right question. I thought I would post my solution in case anyone might be having a similar problem on PostgreSQL:
I was close with my ALTER PRIVILEGES commands. In this screenshot (psql command \ddp)-
there are 4 columns: Owner, Schema, Type, and Access Privilege. You will need each of these columns to generate the ALTER PRIVILEGES commands:
Owner - This is the user/group that will have the privileges altered - seemed weird to me too. :)
Schema - This is the schema containing the commands to assign the default privileges
Type - This will tell you what type of REVOKE command you need to use
Access Privilege - This is the user/group that you can't drop because "it has dependencies"
Based on this list, your command structure will look similar to this - filling {} in from above:
ALTER DEFAULT PRIVILEGES FOR ROLE {Owner} IN SCHEMA {Schema} REVOKE ALL PRIVILEGES ON {Type} FROM {Access Privilege};
In order to perform this command, you must be logged into the Postgres database as either the Owner, or a member of the Owner group, otherwise, you will get the error "ERROR: Must be a member of Role {Owner}."
I had to perform 2 sets of ALTER PRIVILEGES - logging into the database as the owner dbac and Application_Access.
Logging in as Application_Access and executing the commands below eliminated the first 3 rows (where Application_Access is the Owner) from the above screenshot of the \ddp psql command.
alter default privileges for role "Application_Access" in schema public revoke all privileges on functions from "Application_Access";
alter default privileges for role "Application_Access" in schema public revoke all privileges on sequences from "Application_Access";
alter default privileges for role "Application_Access" in schema public revoke all privileges on tables from "Application_Access";
Logging in as dbac and executing the commands below eliminated the other rows (where dbac is the Owner) from the above screenshot of the psql \ddp command.
alter default privileges for role "dbac" in schema public revoke all privileges on functions from "Application_Access";
alter default privileges for role "dbac" in schema public revoke all privileges on sequences from "Application_Access";
alter default privileges for role "dbac" in schema public revoke all privileges on tables from "Application_Access";

Related

DB2 revoke user privileges from a database from multiple users

I want to revoke all privileges from all users but one from a database.
DB2 10.5 LUW
I was thinking along the lines of:
db2 "revoke all on database from user IN (select grantee from syscat.dbauth where grantee not IN 'SAFEUSER')"
but I can't get it to work.
Any ideas?
There is no ALL clause in the REVOKE (database authorities) statement.
You may generate the set of statements needed by the following select statement:
select
'REVOKE '
|| SUBSTR
(
CASE ACCESSCTRLAUTH WHEN 'N' THEN '' ELSE ', ACCESSCTRL' END
||CASE BINDADDAUTH WHEN 'N' THEN '' ELSE ', BINDADD' END
||CASE CONNECTAUTH WHEN 'N' THEN '' ELSE ', CONNECT' END
--- add here expressions with all other *AUTH columns
, 2)
||' ON DATABASE FROM '
|| CASE
WHEN GRANTEE = 'PUBLIC' THEN ''
WHEN GRANTEETYPE = 'U' THEN 'USER'
WHEN GRANTEETYPE = 'G' THEN 'GROUP'
WHEN GRANTEETYPE = 'R' THEN 'ROLE'
END
||' '||GRANTEE
from syscat.dbauth
WHERE 'Y' IN
(
ACCESSCTRLAUTH, BINDADDAUTH, CONNECTAUTH
--- add here all other *AUTH columns separated by ','
)
AND grantee <> 'SAFEUSER'
;

Postgres permission issue

I have a schema owned by app_owner. I have run the following grants to another user:
GRANT USAGE ON SCHEMA app_schema TO app_writer;
ALTER DEFAULT PRIVILEGES IN SCHEMA app_schema
GRANT ALL ON TABLES to app_writer;
How do I check (maybe after a month) if app_writer has all these privileges?
You can simply query the metadata to check if all privileges are granted on all tables in a schema:
SELECT t.table_name,
bool_and(
has_table_privilege(
'app_writer',
format('%I.%I', t.table_schema, t.table_name),
p.priv
)
) AS has_all_privs
FROM information_schema.tables AS t
CROSS JOIN (
VALUES ('SELECT'), ('INSERT'), ('UPDATE'), ('DELETE'),
('TRUNCATE'), ('REFERENCES'), ('TRIGGER')
) AS p(priv)
WHERE t.table_schema = 'app_schema'
GROUP BY t.table_name;

How can I review all database and object grants for a role?

I am trying to audit all of the permissions for an application before release and I want to ensure no role has more access than it needs. I have looked at the different functions and system tables, but everything is very piecemeal.
Is there a good query or method to be able to dump out every grant a particular role has?
I am using pg 9.5.
The column relacl of the system catalog pg_class contains all informations on privileges.
Example data in schema public owned by postgres with grants to newuser:
create table test(id int);
create view test_view as select * from test;
grant select, insert, update on test to newuser;
grant select on test_view to newuser;
Querying the pg_class:
select
relname,
relkind,
coalesce(nullif(s[1], ''), 'public') as grantee,
s[2] as privileges
from
pg_class c
join pg_namespace n on n.oid = relnamespace
join pg_roles r on r.oid = relowner,
unnest(coalesce(relacl::text[], format('{%s=arwdDxt/%s}', rolname, rolname)::text[])) acl,
regexp_split_to_array(acl, '=|/') s
where nspname = 'public'
and relname like 'test%';
relname | relkind | grantee | privileges
-----------+---------+----------+------------
test | r | postgres | arwdDxt <- owner postgres has all privileges on the table
test | r | newuser | arw <- newuser has append/read/write privileges
test_view | v | postgres | arwdDxt <- owner postgres has all privileges on the view
test_view | v | newuser | r <- newuser has read privilege
(4 rows)
Comments:
coalesce(relacl::text[], format('{%s=arwdDxt/%s}', rolname, rolname)) - Null in relacl means that the owner has all privileges;
unnest(...) acl - relacl is an array of aclitem, one array element for a user;
regexp_split_to_array(acl, '=|/') s - split aclitem into: s[1] username, s[2] privileges;
coalesce(nullif(s[1], ''), 'public') as grantee - empty username means public.
Modify the query to select individual user or specific kind of relation or another schemas, etc...
Read in the documentation:
The catalog pg_class,
GRANT with the description of acl system.
In a similar way you can get information about privileges granted on schemas (the column nspacl in pg_namespace) and databases (datacl in pg_database)
The relacl column (and others of type aclitem) doesn't have to be parsed as text.
The function aclexplode unnests array, which makes it suitable for lateral join. Result is record with well named fields, just convert oid to human-readable name:
select c.*, n.nspname,
acl.grantor, acl.grantee,
pg_catalog.pg_get_userbyid(acl.grantor), pg_catalog.pg_get_userbyid(acl.grantee),
acl.privilege_type, acl.is_grantable
from pg_catalog.pg_class c
join pg_catalog.pg_namespace n on n.oid = c.relnamespace,
lateral aclexplode(c.relacl) acl;

List grants and privileges for a materialized view in PostgreSQL

I need to determine what privileges are currently granted for some materialized views in my database.
The query to do this for a table or standard view is pretty straight forward:
SELECT grantee, string_agg(privilege_type, ', ') AS privileges
FROM information_schema.table_privileges
WHERE table_schema = 'some_schema' AND table_name = 'some_table'
GROUP by grantee;
That said, there doesn't seem to be an analogous table for materialized views. Where does PostgreSQL store this information?
In Postgres system catalogs are the basic set of complete information about the installation and databases. System catalogs are the most reliable source of information.
Information schema as an auxiliary feature is based on system catalogs and is provided for compatibility with other RDBMs:
The information schema is defined in the SQL standard and can therefore be expected to be portable and remain stable — unlike the system catalogs, which are specific to PostgreSQL and are modeled after implementation concerns. The information schema views do not, however, contain information about PostgreSQL-specific features; to inquire about those you need to query the system catalogs or other PostgreSQL-specific views.
Materialized views are not SQL-standard objects hence the information schema does not contain information about them.
The system catalog pg_class contains all informations on privileges in the column relacl.
If the column is null then the owner has all privileges.
An empty string as a user name in acl string means public.
create materialized view test_view as select 1;
grant select on test_view to public;
grant delete on test_view to a_user;
select
coalesce(nullif(s[1], ''), 'public') as grantee,
s[2] as privileges
from
pg_class c
join pg_namespace n on n.oid = relnamespace
join pg_roles r on r.oid = relowner,
unnest(coalesce(relacl::text[], format('{%s=arwdDxt/%s}', rolname, rolname)::text[])) acl,
regexp_split_to_array(acl, '=|/') s
where nspname = 'public' and relname = 'test_view';
grantee | privileges
----------+------------
postgres | arwdDxt
public | r
a_user | d
(3 rows)
You need a function to show privileges in readable format:
create or replace function priviliges_from_acl(text)
returns text language sql as $$
select string_agg(privilege, ', ')
from (
select
case ch
when 'r' then 'SELECT'
when 'w' then 'UPDATE'
when 'a' then 'INSERT'
when 'd' then 'DELETE'
when 'D' then 'TRUNCATE'
when 'x' then 'REFERENCES'
when 't' then 'TRIGGER'
end privilege
from
regexp_split_to_table($1, '') ch
) s
$$;
Use:
select
coalesce(nullif(s[1], ''), 'public') as grantee,
priviliges_from_acl(s[2]) as privileges
from
pg_class c
join pg_namespace n on n.oid = relnamespace
join pg_roles r on r.oid = relowner,
unnest(coalesce(relacl::text[], format('{%s=arwdDxt/%s}', rolname, rolname)::text[])) acl,
regexp_split_to_array(acl, '=|/') s
where nspname = 'public' and relname = 'test_view';
grantee | privileges
----------+---------------------------------------------------------------
postgres | INSERT, SELECT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER
public | SELECT
a_user | DELETE
(3 rows)
Following klin's helpful answer, I've come up with a view that lists a summary of all privileges for all relations that appear in pg_class (tables, views, m. views, indexes, sequences, foreign tables, composite types) for all roles:
CREATE VIEW show_privileges AS (
SELECT
grantee,
string_agg(relname, ', ' ORDER BY relname) AS rel_names,
privileges
FROM (
SELECT
relname,
coalesce(nullif(s[1], ''), 'public') grantee,
(SELECT string_agg(privilege, ', ' ORDER BY privilege ASC)
FROM (SELECT
CASE ch
WHEN 'r' THEN 'SELECT'
WHEN 'w' THEN 'UPDATE'
WHEN 'a' THEN 'INSERT'
WHEN 'd' THEN 'DELETE'
WHEN 'D' THEN 'TRUNCATE'
WHEN 'x' THEN 'REFERENCES'
WHEN 't' THEN 'TRIGGER'
END AS privilege
FROM regexp_split_to_table(s[2], '') ch
) s
) AS privileges
FROM
pg_class
JOIN pg_namespace ON pg_namespace.oid = relnamespace
JOIN pg_roles ON pg_roles.oid = relowner,
unnest(coalesce(relacl::text[], format('{%s=arwdDxt/%s}', rolname, rolname)::text[])) AS acl,
regexp_split_to_array(acl, '=|/') AS s
WHERE nspname = 'public'
) AS t
GROUP BY grantee, privileges
ORDER BY grantee, privileges, rel_names
);

make sql dump with stdin like syntax to update table with condition

when making dump file with pg_dump command this performace is very fine but in dump is only create alter and inserts with syntax
COPY table1 (id, name) FROM stdin;
1 developer's portal
...
.
I want to make with this syntax sql dump where table will updated
for example
update table1 set name ='hello' where id=1
with pg_dump syntax
This script will do, although, for simplicity's sake, it assumes the table's primary key is the first field:
table=table1
psql -Atc "with a as (select attrelid::regclass::text rel, attnum i, quote_ident(attname) col from pg_attribute where attrelid='$table'::regclass)
select 'select format(\$\$update '||rel||' set '||string_agg(col||'=%s', ', ' order by i)||'\$\$, '||string_agg('quote_nullable('||col||')', ',' order by i)||')||\$\$ where '||
(select col||'=\$\$||quote_nullable('||col||')||\$\$;\$\$ from '||rel from a where i = 1) from a where i > 1 group by rel" |\
psql -At
If you're using PostgreSQL 9.5+, then you could use INSERT ... ON CONFLICT UPDATE ... instead:
table=table1
psql -Atc "with a as (select attrelid::regclass::text rel, attnum i, quote_ident(attname) col from pg_attribute where attrelid='$table'::regclass)
select 'select format(\$\$insert into '||rel||'('||string_agg(col, ',' order by i)||
') values ('||string_agg('%s', ',')||
') on conflict ('||(select col from a where i=1)||') do update set '||
string_agg(case when i > 1 then col||'=excluded.'||col end, ', ' order by i)||';\$\$, '||
string_agg('quote_nullable('||col||')', ',' order by i)||') from '||rel from a where i > 0 group by rel" |\
psql -At