I'd like to view grants on redshifts.
I found this view for postgres:
CREATE OR REPLACE VIEW view_all_grants AS
SELECT
use.usename as subject,
nsp.nspname as namespace,
c.relname as item,
c.relkind as type,
use2.usename as owner,
c.relacl,
(use2.usename != use.usename and c.relacl::text !~ ('({|,)' || use.usename || '=')) as public
FROM
pg_user use
cross join pg_class c
left join pg_namespace nsp on (c.relnamespace = nsp.oid)
left join pg_user use2 on (c.relowner = use2.usesysid)
WHERE
c.relowner = use.usesysid or
c.relacl::text ~ ('({|,)(|' || use.usename || ')=')
ORDER BY
subject,
namespace,
item
Which doesn't work because the ::text
cast of relacl
fails with the following:
ERROR: cannot cast type aclitem[] to character varying [SQL State=42846]
Modifying the query to
CREATE OR REPLACE VIEW view_all_grants AS
SELECT
use.usename as subject,
nsp.nspname as namespace,
c.relname as item,
c.relkind as type,
use2.usename as owner,
c.relacl
-- , (use2.usename != use.usename and c.relacl::text !~ ('({|,)' || use.usename || '=')) as public
FROM
pg_user use
cross join pg_class c
left join pg_namespace nsp on (c.relnamespace = nsp.oid)
left join pg_user use2 on (c.relowner = use2.usesysid)
WHERE
c.relowner = use.usesysid
-- or c.relacl::text ~ ('({|,)(|' || use.usename || ')=')
ORDER BY
subject,
namespace,
item
Allows the view to be created, but I'm concerned that this is not showing all relevant data.
How can I modify the view to work on redshift or is there an better/alternative way to view grants on redshift ?
UPDATE: Redshift has the HAS_TABLE_PRIVILEGE function to check grants. (see here)
To view the permissions of a specific user on a specific schema, simply change the bold user name and schema name to the user and schema of interest on the following code. For a full list of every user - schema permission status, simply delete the entire WHERE clause. SELECT u. usename, s.
The following is the syntax for granting role privileges on Amazon Redshift. GRANT { ROLE role_name } [, ...] TO { { user_name [ WITH ADMIN OPTION ] } | ROLE role_name }[, ...] The following is the syntax for granting system privileges to roles on Amazon Redshift.
To view assigned roles to users in your Redshift cluster, you can use the following command: SELECT usename AS user_name, groname AS group_name FROM pg_user, pg_group WHERE pg_user.
To query databases hosted by your Amazon Redshift cluster, you have two options: Connect to your cluster and run queries on the AWS Management Console with the query editor. If you use the query editor on the Amazon Redshift console, you don't have to download and set up a SQL client application.
Another variation be like:
SELECT * FROM ( SELECT schemaname ,objectname ,usename ,HAS_TABLE_PRIVILEGE(usrs.usename, fullobj, 'select') AND has_schema_privilege(usrs.usename, schemaname, 'usage') AS sel ,HAS_TABLE_PRIVILEGE(usrs.usename, fullobj, 'insert') AND has_schema_privilege(usrs.usename, schemaname, 'usage') AS ins ,HAS_TABLE_PRIVILEGE(usrs.usename, fullobj, 'update') AND has_schema_privilege(usrs.usename, schemaname, 'usage') AS upd ,HAS_TABLE_PRIVILEGE(usrs.usename, fullobj, 'delete') AND has_schema_privilege(usrs.usename, schemaname, 'usage') AS del ,HAS_TABLE_PRIVILEGE(usrs.usename, fullobj, 'references') AND has_schema_privilege(usrs.usename, schemaname, 'usage') AS ref FROM ( SELECT schemaname, 't' AS obj_type, tablename AS objectname, schemaname + '.' + tablename AS fullobj FROM pg_tables WHERE schemaname not in ('pg_internal') UNION SELECT schemaname, 'v' AS obj_type, viewname AS objectname, schemaname + '.' + viewname AS fullobj FROM pg_views WHERE schemaname not in ('pg_internal') ) AS objs ,(SELECT * FROM pg_user) AS usrs ORDER BY fullobj ) WHERE (sel = true or ins = true or upd = true or del = true or ref = true) and schemaname='<opt schema>' and usename = '<opt username>';
Something along the lines off:
select tablename, HAS_TABLE_PRIVILEGE(tablename, 'select') as select, HAS_TABLE_PRIVILEGE(tablename, 'insert') as insert, HAS_TABLE_PRIVILEGE(tablename, 'update') as update, HAS_TABLE_PRIVILEGE(tablename, 'delete') as delete, HAS_TABLE_PRIVILEGE(tablename, 'references') as references from pg_tables where schemaname='public' order by tablename;
gives me all I need.
A development on the answer from mike_pdb I came up with the following
WITH object_list(schema_name,object_name,permission_info)
AS (
SELECT N.nspname, C.relname, array_to_string(relacl,',')
FROM pg_class AS C
INNER JOIN pg_namespace AS N
ON C.relnamespace = N.oid
WHERE C.relkind in ('v','r')
AND N.nspname NOT IN ('pg_catalog', 'pg_toast', 'information_schema')
AND C.relacl[1] IS NOT NULL
),
object_permissions(schema_name,object_name,permission_string)
AS (
SELECT schema_name,object_name, SPLIT_PART(permission_info,',',1) FROM object_list
UNION ALL
SELECT schema_name,object_name, SPLIT_PART(permission_info,',',2) FROM object_list
UNION ALL
SELECT schema_name,object_name, SPLIT_PART(permission_info,',',3) FROM object_list
UNION ALL
SELECT schema_name,object_name, SPLIT_PART(permission_info,',',4) FROM object_list
UNION ALL
SELECT schema_name,object_name, SPLIT_PART(permission_info,',',5) FROM object_list
UNION ALL
SELECT schema_name,object_name, SPLIT_PART(permission_info,',',6) FROM object_list
UNION ALL
SELECT schema_name,object_name, SPLIT_PART(permission_info,',',7) FROM object_list
UNION ALL
SELECT schema_name,object_name, SPLIT_PART(permission_info,',',8) FROM object_list
UNION ALL
SELECT schema_name,object_name, SPLIT_PART(permission_info,',',9) FROM object_list
UNION ALL
SELECT schema_name,object_name, SPLIT_PART(permission_info,',',10) FROM object_list
),
permission_parts(schema_name, object_name,security_principal, permission_pattern)
AS (
SELECT
schema_name,
object_name,
LEFT(permission_string ,CHARINDEX('=',permission_string)-1),
SPLIT_PART(SPLIT_PART(permission_string,'=',2),'/',1)
FROM object_permissions
WHERE permission_string >''
)
SELECT
schema_name,
object_name,
'GRANT ' ||
SUBSTRING(
case when charindex('r',permission_pattern) > 0 then ',SELECT ' else '' end
||case when charindex('w',permission_pattern) > 0 then ',UPDATE ' else '' end
||case when charindex('a',permission_pattern) > 0 then ',INSERT ' else '' end
||case when charindex('d',permission_pattern) > 0 then ',DELETE ' else '' end
||case when charindex('R',permission_pattern) > 0 then ',RULE ' else '' end
||case when charindex('x',permission_pattern) > 0 then ',REFERENCES ' else '' end
||case when charindex('t',permission_pattern) > 0 then ',TRIGGER ' else '' end
||case when charindex('X',permission_pattern) > 0 then ',EXECUTE ' else '' end
||case when charindex('U',permission_pattern) > 0 then ',USAGE ' else '' end
||case when charindex('C',permission_pattern) > 0 then ',CREATE ' else '' end
||case when charindex('T',permission_pattern) > 0 then ',TEMPORARY ' else '' end
,2,10000
)
|| ' ON ' || schema_name||'.'||object_name
|| ' TO ' || security_principal
|| ';' as grantsql
FROM permission_parts
;
There are 3 common table expressions used here.
As per mike_pdb's solution the individual permission characters are converted into a concatenated list of grants. As we don't which grants will be used we use SUBSTRING from position 2 to discard the first comma in the list.
You can use exactly the same approach for scripting off schema permissions
WITH schema_list(schema_name, permission_info)
AS (
SELECT nspname, array_to_string(nspacl,',')
FROM pg_namespace
WHERE nspacl[1] IS NOT NULL
AND nspname NOT LIKE 'pg%' AND nspname NOT IN ('public','information_schema')
),
schema_permissions(schema_name,permission_string)
AS (
SELECT schema_name,SPLIT_PART(permission_info,',',1) FROM schema_list
UNION ALL
SELECT schema_name,SPLIT_PART(permission_info,',',2) FROM schema_list
UNION ALL
SELECT schema_name,SPLIT_PART(permission_info,',',3) FROM schema_list
UNION ALL
SELECT schema_name,SPLIT_PART(permission_info,',',4) FROM schema_list
UNION ALL
SELECT schema_name,SPLIT_PART(permission_info,',',5) FROM schema_list
UNION ALL
SELECT schema_name,SPLIT_PART(permission_info,',',6) FROM schema_list
UNION ALL
SELECT schema_name,SPLIT_PART(permission_info,',',7) FROM schema_list
UNION ALL
SELECT schema_name,SPLIT_PART(permission_info,',',8) FROM schema_list
UNION ALL
SELECT schema_name,SPLIT_PART(permission_info,',',9) FROM schema_list
UNION ALL
SELECT schema_name,SPLIT_PART(permission_info,',',10) FROM schema_list
),
permission_parts(schema_name, security_principal, permission_pattern)
AS (
SELECT
schema_name,
LEFT(permission_string ,CHARINDEX('=',permission_string)-1),
SPLIT_PART(SPLIT_PART(permission_string,'=',2),'/',1)
FROM schema_permissions
WHERE permission_string >''
)
SELECT
schema_name,
'GRANT ' ||
SUBSTRING(
case when charindex('r',permission_pattern) > 0 then ',SELECT ' else '' end
||case when charindex('w',permission_pattern) > 0 then ',UPDATE ' else '' end
||case when charindex('a',permission_pattern) > 0 then ',INSERT ' else '' end
||case when charindex('d',permission_pattern) > 0 then ',DELETE ' else '' end
||case when charindex('R',permission_pattern) > 0 then ',RULE ' else '' end
||case when charindex('x',permission_pattern) > 0 then ',REFERENCES ' else '' end
||case when charindex('t',permission_pattern) > 0 then ',TRIGGER ' else '' end
||case when charindex('X',permission_pattern) > 0 then ',EXECUTE ' else '' end
||case when charindex('U',permission_pattern) > 0 then ',USAGE ' else '' end
||case when charindex('C',permission_pattern) > 0 then ',CREATE ' else '' end
||case when charindex('T',permission_pattern) > 0 then ',TEMPORARY ' else '' end
,2,10000
)
|| ' ON SCHEMA ' || schema_name
|| ' TO ' || security_principal
|| ';' as grantsql
FROM permission_parts;
The has_table_privilege function is handy, but doesn't always help in administration when you want to manage groups. I morphed your original query to create grant scripts for specific users or groups. This sample query can be easily morphed to accommodate your needs
select namespace||'.'||item as tablename ,
'grant ' || substring(
case when charindex('r',split_part(split_part(array_to_string(relacl, '|'),'group dw_developers=',2 ) ,'/',1)) > 0 then ',select ' else '' end
||case when charindex('w',split_part(split_part(array_to_string(relacl, '|'),'group dw_developers=',2 ) ,'/',1)) > 0 then ',update ' else '' end
||case when charindex('a',split_part(split_part(array_to_string(relacl, '|'),'group dw_developers=',2 ) ,'/',1)) > 0 then ',insert ' else '' end
||case when charindex('d',split_part(split_part(array_to_string(relacl, '|'),'group dw_developers=',2 ) ,'/',1)) > 0 then ',delete ' else '' end
||case when charindex('R',split_part(split_part(array_to_string(relacl, '|'),'group dw_developers=',2 ) ,'/',1)) > 0 then ',rule ' else '' end
||case when charindex('x',split_part(split_part(array_to_string(relacl, '|'),'group dw_developers=',2 ) ,'/',1)) > 0 then ',references ' else '' end
||case when charindex('t',split_part(split_part(array_to_string(relacl, '|'),'group dw_developers=',2 ) ,'/',1)) > 0 then ',trigger ' else '' end
||case when charindex('X',split_part(split_part(array_to_string(relacl, '|'),'group dw_developers=',2 ) ,'/',1)) > 0 then ',execute ' else '' end
||case when charindex('U',split_part(split_part(array_to_string(relacl, '|'),'group dw_developers=',2 ) ,'/',1)) > 0 then ',usage ' else '' end
||case when charindex('C',split_part(split_part(array_to_string(relacl, '|'),'group dw_developers=',2 ) ,'/',1)) > 0 then ',create ' else '' end
||case when charindex('T',split_part(split_part(array_to_string(relacl, '|'),'group dw_developers=',2 ) ,'/',1)) > 0 then ',temporary ' else '' end
, 2,10000)
|| ' on '||namespace||'.'||item ||' to group dw_developers;' as grantsql
from
(SELECT
use.usename as subject,
nsp.nspname as namespace,
c.relname as item,
c.relkind as type,
use2.usename as owner,
c.relacl
FROM
pg_user use
cross join pg_class c
left join pg_namespace nsp on (c.relnamespace = nsp.oid)
left join pg_user use2 on (c.relowner = use2.usesysid)
WHERE
c.relowner = use.usesysid
and nsp.nspname NOT IN ('pg_catalog', 'pg_toast', 'information_schema')
ORDER BY
subject, namespace, item
) where relacl is not null
and array_to_string(relacl, '|') like '%group dw_developers%' order by 1
Here is another useful query to view grants on schema (usage, create) by user that I created based on the query above by @drtf:
SELECT *
FROM
(
SELECT
schemaname
,usename
,HAS_SCHEMA_PRIVILEGE(usrs.usename, schemaname, 'usage') AS usg
,HAS_SCHEMA_PRIVILEGE(usrs.usename, schemaname, 'create') AS crt
FROM
(
SELECT distinct(schemaname) FROM pg_tables
WHERE schemaname not in ('pg_internal')
UNION
SELECT distinct(schemaname) FROM pg_views
WHERE schemaname not in ('pg_internal')
) AS objs
,(SELECT * FROM pg_user) AS usrs
ORDER BY schemaname
)
WHERE (usg = true or crt = true)
--and schemaname='<opt schemaname>'
--and usename = '<opt username>';
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With