Skip to content

Commit

Permalink
Support db_datareader/db_datawriter (#68)
Browse files Browse the repository at this point in the history
Implement database roles `db_datareader` and `db_datawriter`

* Members of the db_datareader fixed database role can read all data from all user tables and views. User objects can exist in any schema except sys and INFORMATION_SCHEMA.
* Members of the db_datawriter fixed database role can add, delete, or change data in all user tables. In most use cases, this role is combined with db_datareader membership to allow reading the data that is to be modified.

Engine PR: amazon-aurora/postgresql_modified_for_babelfish#98

Task: [BABEL-3883](https://jira.rds.a2z.com/browse/BABEL-3883)
Signed-off-by: Shalini Lohia <[email protected]>
  • Loading branch information
shalinilohia50 authored and ANJU BHARTI committed Dec 12, 2024
1 parent 3d38190 commit 20076d2
Show file tree
Hide file tree
Showing 75 changed files with 7,137 additions and 85 deletions.
19 changes: 12 additions & 7 deletions contrib/babelfishpg_tsql/sql/babelfishpg_tsql.sql
Original file line number Diff line number Diff line change
Expand Up @@ -1340,7 +1340,9 @@ FROM pg_catalog.pg_class t1
JOIN sys.pg_namespace_ext t2 ON t1.relnamespace = t2.oid
JOIN sys.schemas s1 ON s1.schema_id = t1.relnamespace
JOIN information_schema.column_privileges t5 ON t1.relname = t5.table_name AND t2.nspname = t5.table_schema
JOIN pg_attribute t6 ON t6.attrelid = t1.oid AND t6.attname = t5.column_name;
JOIN pg_attribute t6 ON t6.attrelid = t1.oid AND t6.attname = t5.column_name
JOIN sys.babelfish_authid_user_ext ext ON ext.rolname = t5.grantee
WHERE ext.orig_username NOT IN ('db_datawriter', 'db_datareader');
GRANT SELECT ON sys.sp_column_privileges_view TO PUBLIC;

CREATE OR REPLACE PROCEDURE sys.sp_column_privileges(
Expand Down Expand Up @@ -1448,7 +1450,8 @@ FROM pg_catalog.pg_class t1
JOIN sys.pg_namespace_ext t2 ON t1.relnamespace = t2.oid
JOIN sys.schemas s1 ON s1.schema_id = t1.relnamespace
JOIN information_schema.table_privileges t4 ON t1.relname = t4.table_name
WHERE t4.privilege_type = 'DELETE';
JOIN sys.babelfish_authid_user_ext ext ON ext.rolname = t4.grantee
WHERE t4.privilege_type = 'DELETE' AND ext.orig_username != 'db_datawriter';
GRANT SELECT on sys.sp_table_privileges_view TO PUBLIC;

CREATE OR REPLACE PROCEDURE sys.sp_table_privileges(
Expand Down Expand Up @@ -2204,7 +2207,7 @@ BEGIN
LEFT OUTER JOIN pg_catalog.pg_roles AS Base4 ON Base4.rolname = Bsdb.owner
WHERE Ext1.database_name = DB_NAME()
AND (Ext1.type != 'R' OR Ext1.type != 'A')
AND Ext1.orig_username NOT IN ('db_owner', 'db_accessadmin')
AND Ext1.orig_username NOT IN ('db_owner', 'db_accessadmin', 'db_datareader', 'db_datawriter')
ORDER BY UserName, RoleName;
END
-- If the security account is the db fixed role - db_owner
Expand Down Expand Up @@ -2236,7 +2239,7 @@ BEGIN
WHERE Ext1.database_name = DB_NAME()
AND Ext2.database_name = DB_NAME()
AND Ext1.type = 'R'
AND Ext2.orig_username NOT IN ('db_owner', 'db_accessadmin')
AND Ext2.orig_username NOT IN ('db_owner', 'db_accessadmin', 'db_datareader', 'db_datawriter')
AND (Ext1.orig_username = @name_in_db OR pg_catalog.lower(Ext1.orig_username) = pg_catalog.lower(@name_in_db))
ORDER BY Role_name, Users_in_role;
END
Expand Down Expand Up @@ -2274,7 +2277,7 @@ BEGIN
LEFT OUTER JOIN pg_catalog.pg_roles AS Base4 ON Base4.rolname = Bsdb.owner
WHERE Ext1.database_name = DB_NAME()
AND (Ext1.type != 'R' OR Ext1.type != 'A')
AND Ext1.orig_username NOT IN ('db_owner', 'db_accessadmin')
AND Ext1.orig_username NOT IN ('db_owner', 'db_accessadmin', 'db_datareader', 'db_datawriter')
AND (Ext1.orig_username = @name_in_db OR pg_catalog.lower(Ext1.orig_username) = pg_catalog.lower(@name_in_db))
ORDER BY UserName, RoleName;
END
Expand Down Expand Up @@ -2435,11 +2438,13 @@ $$
BEGIN
-- Returns a list of the fixed database roles.
-- Only fixed role present in babelfish is db_owner.
IF pg_catalog.lower(PG_CATALOG.RTRIM(@rolename)) IS NULL OR pg_catalog.lower(PG_CATALOG.RTRIM(@rolename)) IN ('db_owner', 'db_accessadmin')
IF pg_catalog.lower(PG_CATALOG.RTRIM(@rolename)) IS NULL OR pg_catalog.lower(PG_CATALOG.RTRIM(@rolename)) IN ('db_owner', 'db_accessadmin', 'db_datareader', 'db_datawriter')
BEGIN
SELECT CAST(DbFixedRole as sys.SYSNAME) AS DbFixedRole, CAST(Description AS sys.nvarchar(70)) AS Description FROM (
VALUES ('db_owner', 'DB Owners'),
('db_accessadmin', 'DB Access Administrators')) x(DbFixedRole, Description)
('db_accessadmin', 'DB Access Administrators'),
('db_datareader', 'DB Data Reader'),
('db_datawriter', 'DB Data Writer')) x(DbFixedRole, Description)
WHERE LOWER(RTRIM(@rolename)) IS NULL OR LOWER(RTRIM(@rolename)) = DbFixedRole;
END
ELSE IF pg_catalog.lower(PG_CATALOG.RTRIM(@rolename)) IN (
Expand Down
8 changes: 4 additions & 4 deletions contrib/babelfishpg_tsql/sql/ownership.sql
Original file line number Diff line number Diff line change
Expand Up @@ -260,9 +260,9 @@ LANGUAGE plpgsql
AS $$
DECLARE
reserved_roles varchar[] := ARRAY['sysadmin', 'securityadmin',
'master_dbo', 'master_guest', 'master_db_owner', 'master_db_accessadmin',
'tempdb_dbo', 'tempdb_guest', 'tempdb_db_owner', 'tempdb_db_accessadmin',
'msdb_dbo', 'msdb_guest', 'msdb_db_owner', 'msdb_db_accessadmin'];
'master_dbo', 'master_guest', 'master_db_owner', 'master_db_accessadmin', 'master_db_datareader', 'master_db_datawriter',
'tempdb_dbo', 'tempdb_guest', 'tempdb_db_owner', 'tempdb_db_accessadmin', 'tempdb_db_datareader', 'tempdb_db_datawriter',
'msdb_dbo', 'msdb_guest', 'msdb_db_owner', 'msdb_db_accessadmin', 'msdb_db_datareader', 'msdb_db_datawriter'];

user_id oid := -1;
db_name name := NULL;
Expand Down Expand Up @@ -460,7 +460,7 @@ ON Base.rolname = Ext.rolname
LEFT OUTER JOIN pg_catalog.pg_roles Base2
ON Ext.login_name = Base2.rolname
WHERE Ext.database_name = DB_NAME()
AND (Ext.orig_username IN ('dbo', 'db_owner', 'db_accessadmin', 'guest') -- system users should always be visible
AND (Ext.orig_username IN ('dbo', 'db_owner', 'db_accessadmin', 'db_datareader', 'db_datawriter', 'guest') -- system users should always be visible
OR pg_has_role(Ext.rolname, 'MEMBER')) -- Current user should be able to see users it has permission of
UNION ALL
SELECT
Expand Down
2 changes: 1 addition & 1 deletion contrib/babelfishpg_tsql/sql/sys_functions.sql
Original file line number Diff line number Diff line change
Expand Up @@ -4500,7 +4500,7 @@ BEGIN
END IF;
ELSIF EXISTS (SELECT orig_username FROM sys.babelfish_authid_user_ext WHERE orig_username = role COLLATE sys.database_default)
THEN
IF (((SELECT orig_username FROM sys.babelfish_authid_user_ext WHERE rolname = CURRENT_USER) = 'dbo' COLLATE sys.database_default) AND role COLLATE sys.database_default IN ('db_owner', 'db_accessadmin'))
IF (((SELECT orig_username FROM sys.babelfish_authid_user_ext WHERE rolname = CURRENT_USER) = 'dbo' COLLATE sys.database_default) AND role COLLATE sys.database_default IN ('db_owner', 'db_accessadmin', 'db_datareader', 'db_datawriter'))
THEN RETURN 1;
ELSIF EXISTS (SELECT name FROM sys.user_token WHERE name = role COLLATE sys.database_default)
THEN RETURN 1; -- Return 1 if current session user is a member of role or windows group
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ BEGIN
END IF;
ELSIF EXISTS (SELECT orig_username FROM sys.babelfish_authid_user_ext WHERE orig_username = role COLLATE sys.database_default)
THEN
IF (((SELECT orig_username FROM sys.babelfish_authid_user_ext WHERE rolname = CURRENT_USER) = 'dbo' COLLATE sys.database_default) AND role COLLATE sys.database_default IN ('db_owner', 'db_accessadmin'))
IF (((SELECT orig_username FROM sys.babelfish_authid_user_ext WHERE rolname = CURRENT_USER) = 'dbo' COLLATE sys.database_default) AND role COLLATE sys.database_default IN ('db_owner', 'db_accessadmin', 'db_datareader', 'db_datawriter'))
THEN RETURN 1;
ELSIF EXISTS (SELECT name FROM sys.user_token WHERE name = role COLLATE sys.database_default)
THEN RETURN 1; -- Return 1 if current session user is a member of role or windows group
Expand Down Expand Up @@ -186,7 +186,7 @@ ON Base.rolname = Ext.rolname
LEFT OUTER JOIN pg_catalog.pg_roles Base2
ON Ext.login_name = Base2.rolname
WHERE Ext.database_name = DB_NAME()
AND (Ext.orig_username IN ('dbo', 'db_owner', 'db_accessadmin', 'guest') -- system users should always be visible
AND (Ext.orig_username IN ('dbo', 'db_owner', 'db_accessadmin', 'db_datareader', 'db_datawriter', 'guest') -- system users should always be visible
OR pg_has_role(Ext.rolname, 'MEMBER')) -- Current user should be able to see users it has permission of
UNION ALL
SELECT
Expand Down Expand Up @@ -248,7 +248,7 @@ BEGIN
LEFT OUTER JOIN pg_catalog.pg_roles AS Base4 ON Base4.rolname = Bsdb.owner
WHERE Ext1.database_name = DB_NAME()
AND (Ext1.type != 'R' OR Ext1.type != 'A')
AND Ext1.orig_username NOT IN ('db_owner', 'db_accessadmin')
AND Ext1.orig_username NOT IN ('db_owner', 'db_accessadmin', 'db_datareader', 'db_datawriter')
ORDER BY UserName, RoleName;
END
-- If the security account is the db fixed role - db_owner
Expand Down Expand Up @@ -280,7 +280,7 @@ BEGIN
WHERE Ext1.database_name = DB_NAME()
AND Ext2.database_name = DB_NAME()
AND Ext1.type = 'R'
AND Ext2.orig_username NOT IN ('db_owner', 'db_accessadmin')
AND Ext2.orig_username NOT IN ('db_owner', 'db_accessadmin', 'db_datareader', 'db_datawriter')
AND (Ext1.orig_username = @name_in_db OR pg_catalog.lower(Ext1.orig_username) = pg_catalog.lower(@name_in_db))
ORDER BY Role_name, Users_in_role;
END
Expand Down Expand Up @@ -318,7 +318,7 @@ BEGIN
LEFT OUTER JOIN pg_catalog.pg_roles AS Base4 ON Base4.rolname = Bsdb.owner
WHERE Ext1.database_name = DB_NAME()
AND (Ext1.type != 'R' OR Ext1.type != 'A')
AND Ext1.orig_username NOT IN ('db_owner', 'db_accessadmin')
AND Ext1.orig_username NOT IN ('db_owner', 'db_accessadmin', 'db_datareader', 'db_datawriter')
AND (Ext1.orig_username = @name_in_db OR pg_catalog.lower(Ext1.orig_username) = pg_catalog.lower(@name_in_db))
ORDER BY UserName, RoleName;
END
Expand Down Expand Up @@ -982,6 +982,25 @@ WHERE CAST(t4."ORDINAL_POSITION" AS smallint) = ANY (t5.indkey)
AND CAST(t4."ORDINAL_POSITION" AS smallint) = t5.indkey[seq];
GRANT SELECT on sys.sp_statistics_view TO PUBLIC;

CREATE OR REPLACE VIEW sys.sp_column_privileges_view AS
SELECT
CAST(t2.dbname AS sys.sysname) AS TABLE_QUALIFIER,
CAST(s1.name AS sys.sysname) AS TABLE_OWNER,
CAST(t1.relname AS sys.sysname) AS TABLE_NAME,
CAST(COALESCE(SPLIT_PART(t6.attoptions[1], '=', 2), t5.column_name) AS sys.sysname) AS COLUMN_NAME,
CAST((select orig_username from sys.babelfish_authid_user_ext where rolname = t5.grantor::name) AS sys.sysname) AS GRANTOR,
CAST((select orig_username from sys.babelfish_authid_user_ext where rolname = t5.grantee::name) AS sys.sysname) AS GRANTEE,
CAST(t5.privilege_type AS sys.varchar(32)) COLLATE sys.database_default AS PRIVILEGE,
CAST(t5.is_grantable AS sys.varchar(3)) COLLATE sys.database_default AS IS_GRANTABLE
FROM pg_catalog.pg_class t1
JOIN sys.pg_namespace_ext t2 ON t1.relnamespace = t2.oid
JOIN sys.schemas s1 ON s1.schema_id = t1.relnamespace
JOIN information_schema.column_privileges t5 ON t1.relname = t5.table_name AND t2.nspname = t5.table_schema
JOIN pg_attribute t6 ON t6.attrelid = t1.oid AND t6.attname = t5.column_name
JOIN sys.babelfish_authid_user_ext ext ON ext.rolname = t5.grantee
WHERE ext.orig_username NOT IN ('db_datawriter', 'db_datareader');
GRANT SELECT ON sys.sp_column_privileges_view TO PUBLIC;

CREATE OR REPLACE PROCEDURE sys.sp_column_privileges(
"@table_name" sys.sysname,
"@table_owner" sys.sysname = '',
Expand Down Expand Up @@ -1061,6 +1080,36 @@ $$
LANGUAGE 'pltsql';
GRANT EXECUTE ON PROCEDURE sys.sp_column_privileges TO PUBLIC;

CREATE OR REPLACE VIEW sys.sp_table_privileges_view AS
-- Will use sp_column_priivleges_view to get information from SELECT, INSERT and REFERENCES (only need permission from 1 column in table)
SELECT DISTINCT
CAST(TABLE_QUALIFIER AS sys.sysname) COLLATE sys.database_default AS TABLE_QUALIFIER,
CAST(TABLE_OWNER AS sys.sysname) AS TABLE_OWNER,
CAST(TABLE_NAME AS sys.sysname) COLLATE sys.database_default AS TABLE_NAME,
CAST(GRANTOR AS sys.sysname) AS GRANTOR,
CAST(GRANTEE AS sys.sysname) AS GRANTEE,
CAST(PRIVILEGE AS sys.sysname) COLLATE sys.database_default AS PRIVILEGE,
CAST(IS_GRANTABLE AS sys.sysname) COLLATE sys.database_default AS IS_GRANTABLE
FROM sys.sp_column_privileges_view

UNION
-- We need these set of joins only for the DELETE privilege
SELECT
CAST(t2.dbname AS sys.sysname) AS TABLE_QUALIFIER,
CAST(s1.name AS sys.sysname) AS TABLE_OWNER,
CAST(t1.relname AS sys.sysname) AS TABLE_NAME,
CAST((select orig_username from sys.babelfish_authid_user_ext where rolname = t4.grantor) AS sys.sysname) AS GRANTOR,
CAST((select orig_username from sys.babelfish_authid_user_ext where rolname = t4.grantee) AS sys.sysname) AS GRANTEE,
CAST(t4.privilege_type AS sys.sysname) AS PRIVILEGE,
CAST(t4.is_grantable AS sys.sysname) AS IS_GRANTABLE
FROM pg_catalog.pg_class t1
JOIN sys.pg_namespace_ext t2 ON t1.relnamespace = t2.oid
JOIN sys.schemas s1 ON s1.schema_id = t1.relnamespace
JOIN information_schema.table_privileges t4 ON t1.relname = t4.table_name
JOIN sys.babelfish_authid_user_ext ext ON ext.rolname = t4.grantee
WHERE t4.privilege_type = 'DELETE' AND ext.orig_username != 'db_datawriter';
GRANT SELECT on sys.sp_table_privileges_view TO PUBLIC;

CREATE OR REPLACE PROCEDURE sys.sp_table_privileges(
"@table_name" sys.nvarchar(384),
"@table_owner" sys.nvarchar(384) = '',
Expand Down Expand Up @@ -1716,11 +1765,13 @@ $$
BEGIN
-- Returns a list of the fixed database roles.
-- Only fixed role present in babelfish is db_owner.
IF pg_catalog.lower(PG_CATALOG.RTRIM(@rolename)) IS NULL OR pg_catalog.lower(PG_CATALOG.RTRIM(@rolename)) IN ('db_owner', 'db_accessadmin')
IF pg_catalog.lower(PG_CATALOG.RTRIM(@rolename)) IS NULL OR pg_catalog.lower(PG_CATALOG.RTRIM(@rolename)) IN ('db_owner', 'db_accessadmin', 'db_datareader', 'db_datawriter')
BEGIN
SELECT CAST(DbFixedRole as sys.SYSNAME) AS DbFixedRole, CAST(Description AS sys.nvarchar(70)) AS Description FROM (
VALUES ('db_owner', 'DB Owners'),
('db_accessadmin', 'DB Access Administrators')) x(DbFixedRole, Description)
('db_accessadmin', 'DB Access Administrators'),
('db_datareader', 'DB Data Reader'),
('db_datawriter', 'DB Data Writer')) x(DbFixedRole, Description)
WHERE LOWER(RTRIM(@rolename)) IS NULL OR LOWER(RTRIM(@rolename)) = DbFixedRole;
END
ELSE IF pg_catalog.lower(PG_CATALOG.RTRIM(@rolename)) IN (
Expand Down
4 changes: 2 additions & 2 deletions contrib/babelfishpg_tsql/src/catalog.c
Original file line number Diff line number Diff line change
Expand Up @@ -4268,7 +4268,7 @@ grant_perms_to_objects_in_schema(const char *schema_name,

/* do this step */
ProcessUtility(wrapper,
"(GRANT STATEMENT )",
INTERNAL_GRANT_STATEMENT,
false,
PROCESS_UTILITY_SUBCOMMAND,
NULL,
Expand Down Expand Up @@ -4366,7 +4366,7 @@ exec_internal_grant_on_function(const char *logicalschema,

/* do this step */
ProcessUtility(wrapper,
"(GRANT STATEMENT )",
INTERNAL_GRANT_STATEMENT,
false,
PROCESS_UTILITY_SUBCOMMAND,
NULL,
Expand Down
Loading

0 comments on commit 20076d2

Please sign in to comment.