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