Unable to remove permissions to schema in Redshift - permissions

I am attempting to remove permissions from a group to a schema in Redshift. I do not want to delete the group...just remove permissions to this one schema. I am executing this command:
revoke all
on schema heap2
from group ba_prod;
The query executes and does not fail.
To doublecheck that said permissions have been removed, I am running this SQL:
select distinct schemname ,
--objectname ,
username ,
usertype
from
(
select namespace schemname,
item objectname,
groname username,
'GROUP' usertype,
SPLIT_PART( SPLIT_PART( ARRAY_TO_STRING( RELACL, '|' ), pu.groname, 2 ) , '/', 1 ) char_perms
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')
and nsp.nspname = 'heap2'
ORDER BY subject
,namespace
,item )
JOIN pg_group pu ON array_to_string(relacl, '|') LIKE '%'|| pu.groname ||'%'
) as ucnu2p
Which shows that the group still has permission to the heap2 schema. What am I missing?
To doublecheck that said permissions have been removed, I am running this SQL:
select distinct schemname ,
--objectname ,
username ,
usertype
from
(
select namespace schemname,
item objectname,
groname username,
'GROUP' usertype,
SPLIT_PART( SPLIT_PART( ARRAY_TO_STRING( RELACL, '|' ), pu.groname, 2 ) , '/', 1 ) char_perms
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')
and nsp.nspname = 'heap2'
ORDER BY subject
,namespace
,item )
JOIN pg_group pu ON array_to_string(relacl, '|') LIKE '%'|| pu.groname ||'%'
) as ucnu2p
Which shows that the group still has permission to the heap2 schema. What am I missing?

Related

Split postgresql table column into multiple columns

Ok so this is the partial query, working 100% as we speak. There's a comment as you can see where the query of the 4 columns is supposed to go.
SELECT DISTINCT
p.id AS "Project ID",
p.title AS "Project Title",
p.summary AS "Project Summary",
to_char(p.expected_start_date, 'YYYY-MM-dd') AS "Expected Start Date",
to_char(p.expected_end_date, 'YYYY-MM-dd') AS "Expected End Date",
to_char(p.actual_start_date, 'YYYY-MM-dd') AS "Actual Start Date",
to_char(p.actual_end_date, 'YYYY-MM-dd') AS "Actual End Date",
d.name AS "Center/Department",
/* THIS IS WHERE THE Query has to go.*/
SELECT DISTINCT string_agg(distinct gd.name ||' ('||gs.name||')', ' | ') AS "GRANT NAME AND STATUS"
from grant_detail gd
JOIN grant_status gs on gd.status_id = gs.id
JOIN project_budget pb ON gd.id = pb.grant_id
WHERE pb.project_id = p.id
group by p.id),
SELECT DISTINCT string_agg(distinct o.name, ', ') AS "FUNDER"
FROM organization o
JOIN organization_type ot ON ot.id = o.type_id
JOIN grant_detail gt ON gt.organization_id = o.id
JOIN project_budget pb ON pb.grant_id = gt.id
WHERE pb.project_id = p.id
AND ot.name = 'Funder'),
SELECT string_agg(pc.name, ', ') AS "Categories"
FROM project_project_categories ppc
JOIN project_category pc ON ppc.project_category_id = pc.id
WHERE ppc.project_id = p.id),
( /*========= Student Researcher Full Name (+email) =========*/
SELECT DISTINCT string_agg(distinct s.first_name || ' ' ||
s.last_name ||' ('||
s.email ||')', ', ')
AS "Student Researcher"
FROM project_stakeholder ps
JOIN stakeholder s ON s.id = ps.stakeholder_id
JOIN project_role pr ON ps.role_id = pr.id
WHERE pr.name = 'Student Researcher'
AND ps.project_id = p.id
GROUP BY p.id)
FROM
/*CONTEXT*/
project p
LEFT JOIN innovation_category c2 ON p.innovation_category_id = c2.id
LEFT JOIN department d ON p.department_id = d.id
JOIN project_health ph ON p.health_id = ph.id
JOIN project_status ps ON p.status_id = ps.id
-- WHERE
-- -- p.actual_start_date <= '__beforeActualStartDate'
-- -- AND p.actual_end_date >= '__afterActualEndDate'
-- -- AND p.expected_start_date <= '__beforeStartDate'
-- -- AND p.expected_end_date >= '__afterEndDate'
ORDER BY
p.title
UPDATE: For Phelipe:
You can see where I've commented my code in the place where the four columns need to be que
Here is a fully dynamic solution that can work with a variable list of roles.
This solution relies on the creation of a composite type role_type which includes the list of roles and which can be called at the runtime :
CREATE OR REPLACE PROCEDURE role_type () LANGUAGE plpgsql AS
$$
DECLARE
role_list text ;
BEGIN
SELECT string_agg(r_name || ' text', ',' ORDER BY r_id)
INTO role_list
FROM role ;
EXECUTE 'DROP TYPE IF EXISTS role_type' ;
EXECUTE 'CREATE TYPE role_type AS (' || role_list || ')' ;
END ;
$$
After calling the procedure role_type(), the result is provided by a simple query using the jsonb_object_agg and jsonb_populate_record standard functions :
CALL role_type () ;
SELECT a.p_id AS "Project id"
, a.p_title AS "Project Title"
, (jsonb_populate_record(NULL :: role_type, jsonb_object_agg(lower(a.r_name), (a.usr_list)))).*
FROM
( SELECT p.p_id
, p.p_title
, r.r_name
, string_agg(u.fname || ' ' || u.lname, ', ') AS usr_list
FROM project_usr AS pu
INNER JOIN project AS p
ON p.p_id = pu.project_id
INNER JOIN usr AS u
ON u.usr_id = pu.usr_id
INNER JOIN role AS r
ON r.r_id = pu.role_id
GROUP BY p.p_id, p.p_title, r.r_name
) AS a
GROUP BY a.p_id, a.p_title
ORDER BY a.p_id
Finally, instead of calling the role_type() procedure each time before executing the query, it can be called by trigger when the role list is changing :
CREATE OR REPLACE FUNCTION role_after_insert_update ()
RETURNS trigger LANGUAGE plpgsql AS
$$
BEGIN
CALL role_type () ;
RETURN NEW ;
END ;
$$ ;
CREATE OR REPLACE TRIGGER role_after_insert_update AFTER INSERT OR UPDATE ON role
FOR EACH ROW EXECUTE FUNCTION role_after_insert_update() ;
CREATE OR REPLACE FUNCTION role_after_delete ()
RETURNS trigger LANGUAGE plpgsql AS
$$
BEGIN
CALL role_type () ;
RETURN OLD ;
END ;
$$ ;
CREATE OR REPLACE TRIGGER role_after_delete AFTER DELETE ON role
FOR EACH ROW EXECUTE FUNCTION role_after_delete() ;
see the full test result in dbfiddle
you can do this but it supposes that role table is always the same with same id
select pu.project_id,
case r.r_id when 1 then u.fname||' '||u.lname else '' end as Teacher,
case r.r_id when 2 then u.fname||' '||u.lname else '' end as Student,
case r.r_id when 3 then u.fname||' '||u.lname else '' end as Volunteer
from project_usr pu join role r on r.r_id = pu.role_id
join usr u on pu.usr_id = u.usr_id
Result here
Crosstab
You could use crosstab too but the problem will be the same, you have to know the roles
select * from crosstab($$
select pu.project_id,r.r_name, u.fname||' '||u.lname
from project_usr pu join role r on r.r_id = pu.role_id
join usr u on pu.usr_id = u.usr_id
$$,'select r_name from role')
as final_result (project_id integer, Teacher varchar,Student varchar,Volunteer varchar)
Result here
Maybe something like this (not tested, not enough informations)
with r as (
select * from crosstab($$
select pu.project_id,r.r_name, u.fname||' '||u.lname
from project_usr pu join role r on r.r_id = pu.role_id
join usr u on pu.usr_id = u.usr_id
$$,'select r_name from role')
as final_result (project_id integer, Teacher varchar,Student varchar,Volunteer varchar)
)
SELECT DISTINCT
p.id AS "Project ID",
p.title AS "Project Title",
p.summary AS "Project Summary",
to_char(p.expected_start_date, 'YYYY-MM-dd') AS "Expected Start Date",
to_char(p.expected_end_date, 'YYYY-MM-dd') AS "Expected End Date",
to_char(p.actual_start_date, 'YYYY-MM-dd') AS "Actual Start Date",
to_char(p.actual_end_date, 'YYYY-MM-dd') AS "Actual End Date",
d.name AS "Center/Department",
/* THIS IS WHERE THE Query has to go.*/
r.teacher,
r.student,
r.volunteer,
SELECT DISTINCT string_agg(distinct gd.name ||' ('||gs.name||')', ' | ') AS "GRANT NAME AND STATUS"
from grant_detail gd
JOIN grant_status gs on gd.status_id = gs.id
JOIN project_budget pb ON gd.id = pb.grant_id
WHERE pb.project_id = p.id
group by p.id),
SELECT DISTINCT string_agg(distinct o.name, ', ') AS "FUNDER"
FROM organization o
JOIN organization_type ot ON ot.id = o.type_id
JOIN grant_detail gt ON gt.organization_id = o.id
JOIN project_budget pb ON pb.grant_id = gt.id
WHERE pb.project_id = p.id
AND ot.name = 'Funder'),
SELECT string_agg(pc.name, ', ') AS "Categories"
FROM project_project_categories ppc
JOIN project_category pc ON ppc.project_category_id = pc.id
WHERE ppc.project_id = p.id),
( /*========= Student Researcher Full Name (+email) =========*/
SELECT DISTINCT string_agg(distinct s.first_name || ' ' ||
s.last_name ||' ('||
s.email ||')', ', ')
AS "Student Researcher"
FROM project_stakeholder ps
JOIN stakeholder s ON s.id = ps.stakeholder_id
JOIN project_role pr ON ps.role_id = pr.id
WHERE pr.name = 'Student Researcher'
AND ps.project_id = p.id
GROUP BY p.id)
FROM
/*CONTEXT*/
project p
LEFT JOIN innovation_category c2 ON p.innovation_category_id = c2.id
LEFT JOIN department d ON p.department_id = d.id
JOIN project_health ph ON p.health_id = ph.id
JOIN project_status ps ON p.status_id = ps.id
/* don't know the key */
join r on ...
ORDER BY
p.title

List all Securable\Permissions on all tables for all roles and owners

I need to make a list of all tables in two or more databases on one server. This list needs to contain the TableName, Permission\Securable, Role\List role name and a Y/N for the permission.
I have tried several different options, however the results are significantly different than the report from last year (query from last year doesn't exist).
I am probably missing something obvious. What is happening is the table doesn't show in the results because there are no explicit permissions. However the report last year displays that there are. So I think I am just using the wrong method all together.
Here is the "Generic" for on the code I have attempted to utilize in my last attempt:
SELECT s.name AS [Schema]
, o.name AS Object
-- , u.name AS [User]
, dp.permission_name, dp.state_desc
FROM sys.database_permissions dp
JOIN sys.objects o ON dp.major_id = o.object_id
JOIN sys.schemas s ON o.schema_id = s.schema_id
JOIN sys.database_principals u ON dp.grantee_principal_id = u.principal_id
WHERE o.name = 'tables'--#ObjName
UNION ALL
SELECT s.name AS [Schema]
--, NULL
, u.name AS [User]
, dp.permission_name
, dp.state_desc
FROM sys.database_permissions dp
JOIN sys.schemas s ON dp.major_id = s.schema_id
JOIN sys.database_principals u ON dp.grantee_principal_id = u.principal_id
--ORDER BY s.name, o.name --, u.name
What I was Missing was Implied permissions Versus Explicit Permissions. I assumed explicted permissions were used, not implied\inheritied permissions.
I made this query to view the permissions of a role on the tables and the views.
DECLARE #RoleName VARCHAR(MAX) = 'role_gestionale'
IF OBJECT_ID('tempdb..#roles') IS NOT NULL DROP TABLE #roles
SELECT dp.major_id Object_id, dp.permission_name Permission
INTO #roles
FROM sys.database_permissions dp
INNER JOIN sys.database_principals u ON dp.grantee_principal_id = u.principal_id
WHERE u.name=#RoleName
SELECT *
FROM (
SELECT o.type_desc Tipo, s.name AS [SCHEMA], o.name AS [Table]
, (SELECT COUNT(*) FROM #roles r WHERE r.Object_id=o.object_id AND Permission='SELECT') AS [SELECT]
, (SELECT COUNT(*) FROM #roles r WHERE r.Object_id=o.object_id AND Permission='INSERT') AS [INSERT]
, (SELECT COUNT(*) FROM #roles r WHERE r.Object_id=o.object_id AND Permission='UPDATE') AS [UPDATE]
, (SELECT COUNT(*) FROM #roles r WHERE r.Object_id=o.object_id AND Permission='DELETE') AS [DELETE]
FROM sys.objects o
INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
WHERE o.type_desc IN ('USER_TABLE', 'VIEW')
) a
--WHERE [INSERT]=0 OR [SELECT]=0 OR [UPDATE]=0 OR [DELETE]=0
ORDER BY 1, 2, 3
Alessandro Lettieri

MSSQL T-SQL Table Hierachy including parent/child(group)

I have a query that successfully lists a databases table hierarchy in relation to level of FK/checks on referenced tables and columns.
This will depict the order in which data is deleted/inserted.
Problem is, within this I want to include that path i.e.
levels 1 through to 'x' but also the group path each follows (some might have multiple paths through the levels).
Anyone have any suggestions, currently use CTE to recursively grab the level. Possibly use the FK referenced table-name?
;with cteTbHierarchy
as (
SELECT distinct
1 as LevelID
, OBJECT_SCHEMA_NAME(Parent.object_id) as TableOwner
, Parent.name as TableName
, Parent.object_id as TbObjID
, OBJECT_NAME(RefKey.referenced_object_id) as Referenced_Tablename
, RefKey.referenced_object_id
, RefKey.referenced_column_id
from sys.objects Parent
left outer join sys.foreign_key_columns RefKey
On RefKey.parent_object_id = Parent.object_id
and RefKey.parent_object_id <> RefKey.referenced_object_id
and RefKey.constraint_column_id = 1
where RefKey.parent_object_id is null
and Parent.type = 'U'
and Parent.name <> 'dtproperties'
UNION ALL
/* add tables that reference the anchor rows */
SELECT H.LevelID + 1
, OBJECT_SCHEMA_NAME(Parent.object_id) as TableOwner
, OBJECT_NAME(Parent.object_id) as TableName
, Parent.object_id as TbObjID
, OBJECT_NAME(RefKey.referenced_object_id) as Referenced_Tablename
, RefKey.referenced_object_id
, RefKey.referenced_column_id
from sys.objects Parent
inner join sys.foreign_key_columns RefKey
On RefKey.parent_object_id = Parent.object_id
and RefKey.parent_object_id <> RefKey.referenced_object_id
and RefKey.constraint_column_id = 1
inner join cteTbHierarchy H
on H.TbObjID = RefKey.referenced_object_id
where Parent.type = 'U'
and Parent.name <> 'dtproperties'
)
select distinct a.LevelID,
a.TableOwner,
a.TableName,
a.Referenced_Tablename,
cols.name as Referenced_ColName,
REPLACE(a.tablename,(LEFT(SUBSTRING(a.TableName, charindex('.',a.TableName), 50),50)),'') as TableArea,
REPLACE(a.Referenced_Tablename,(LEFT(SUBSTRING(a.Referenced_Tablename, charindex('.',a.Referenced_Tablename), 50),50)),'') as RefTableArea
from cteTbHierarchy a
left join sys.columns cols
on a.referenced_object_id = cols.object_id
AND a.referenced_column_id = cols.column_id
order by LevelID asc
, TableName;

Creating A Script For Dropping and Creating Views of a Database With Dependency Order, Programatically

I just see this topic.
it's very similar to my question.
but i don't want to use any third party for creating the script.
i want to create the script of dropping and creating the views of a database in dependency order and programmatically.
how i can do such a thing ?
At last I Found My Answer to be like this,
But Remember that this is just for views that has one nesting level. not more ! :
SELECT MIN(Row) AS Row, CreateCmd FROM (
SELECT Row_Number() OVER (ORDER BY S.Row) As Row, S.CreateCmd FROM (
SELECT 0 AS Row, RTRIM(ISNULL(smv.definition, ssmv.definition)) AS CreateCmd
FROM sys.all_objects AS v
LEFT OUTER JOIN sys.sql_modules AS smv ON smv.object_id = v.object_id
LEFT OUTER JOIN sys.system_sql_modules AS ssmv ON ssmv.object_id = v.object_id
INNER JOIN (SELECT object_id, referenced_major_id FROM sys.sql_dependencies
GROUP BY object_id, referenced_major_id) AS a ON v.object_id = a.referenced_major_id
WHERE (v.type = 'V' OR v.type = 'P' OR v.type = 'IF' OR v.type = 'TF' OR v.type = 'FN')
AND SCHEMA_NAME(v.schema_id) = N'dbo' AND is_ms_shipped <> 1
AND smv.execute_as_principal_id IS NULL AND ssmv.execute_as_principal_id IS NULL
GROUP BY v.Name, smv.definition, ssmv.definition
UNION ALL
SELECT 1 AS Row, RTRIM(ISNULL(smv.definition, ssmv.definition)) AS CreateCmd
FROM sys.all_objects AS v
LEFT OUTER JOIN sys.sql_modules AS smv ON smv.object_id = v.object_id
LEFT OUTER JOIN sys.system_sql_modules AS ssmv ON ssmv.object_id = v.object_id
INNER JOIN (SELECT object_id, referenced_major_id FROM sys.sql_dependencies
GROUP BY object_id, referenced_major_id) AS a ON v.object_id = a.object_id
WHERE (v.type = 'V' OR v.type = 'P' OR v.type = 'IF' OR v.type = 'TF' OR v.type = 'FN')
AND SCHEMA_NAME(v.schema_id) = N'dbo' AND is_ms_shipped <> 1
AND smv.execute_as_principal_id IS NULL AND ssmv.execute_as_principal_id IS NULL
GROUP BY v.Name, smv.definition, ssmv.definition
) S GROUP BY S.Row, CreateCmd
) D GROUP BY CreateCmd ORDER BY Row

Finding user account access to tables in a DB

I'm trying to write a query to tell me what tables in a database a certain user can access. This is a domain user and not just a SQL user.
Thanks in advance.
Assuming your only interested in users/logins as opposed to roles etc, you grab the information your after as follows:
WITH RootPermissions
AS ( SELECT USER_NAME(p.grantee_principal_id) AS UserName ,
dp.principal_id ,
dp.type_desc AS UserType ,
OBJECT_NAME(p.major_id) AS ObjectName ,
p.class_desc AS ObjectType ,
p.permission_name AS Permission,
p.state_desc AS PermissionState
FROM sys.database_permissions AS p
INNER JOIN sys.database_principals AS dp ON p.grantee_principal_id = dp.principal_id
),
UnionResults ( UserName, UserType, ObjectName, ObjectType, Permission, PermissionState, role_name )
AS ( SELECT UserName ,
UserType ,
ObjectName ,
ObjectType ,
Permission ,
PermissionState ,
CAST(NULL AS SYSNAME) AS role_name
FROM RootPermissions AS p
WHERE ( UserType <> 'DATABASE_ROLE' )
UNION
SELECT rm.member_principal_name ,
rm.principal_type_desc ,
p.ObjectType ,
p.ObjectName ,
p.Permission ,
p.PermissionState ,
rm.role_name
FROM RootPermissions AS p
RIGHT OUTER JOIN ( SELECT rm.role_principal_id ,
dp.type_desc AS principal_type_desc ,
rm.member_principal_id ,
USER_NAME(rm.member_principal_id) AS member_principal_name ,
USER_NAME(rm.role_principal_id) AS role_name
FROM sys.database_role_members AS rm
INNER JOIN sys.database_principals AS dp ON rm.member_principal_id = dp.principal_id
) AS rm ON rm.role_principal_id = p.principal_id
)
SELECT ObjectName ,
UserName ,
ObjectType ,
UserType ,
Permission ,
PermissionState ,
role_name
FROM UnionResults
WHERE ( ObjectName IS NOT NULL )
ORDER BY ObjectName ,
UserName