Skip to content

Commit

Permalink
Merge pull request #3124 from amazon-aurora/server-db-fixed-role-5-x
Browse files Browse the repository at this point in the history
Support Fixed server and database roles
  • Loading branch information
shardgupta authored Nov 18, 2024
2 parents e47fa84 + 5b8edcf commit 1b3a16d
Show file tree
Hide file tree
Showing 129 changed files with 18,701 additions and 522 deletions.
40 changes: 27 additions & 13 deletions contrib/babelfishpg_tds/src/backend/tds/tdsutils.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,18 @@ static bool handle_dropdb(DropdbStmt *dropdb_stmt);
static char *get_role_name(RoleSpec *role);
char *get_rolespec_name_internal(const RoleSpec *role, bool missing_ok);

static Oid bbf_admin_oid = InvalidOid;

/* Returns OID of bbf_role_admin server role */
static Oid
get_bbf_role_admin_oid(void)
{
if (!OidIsValid(bbf_admin_oid))
bbf_admin_oid = get_role_oid(BABELFISH_ROLE_ADMIN, false);
return bbf_admin_oid;
}


/*
* GetUTF8CodePoint - extract the next Unicode code point from 1..4
* bytes at 'in' in UTF-8 encoding.
Expand Down Expand Up @@ -893,19 +905,15 @@ get_rolespec_name_internal(const RoleSpec *role, bool missing_ok)
static void
check_babelfish_droprole_restrictions(char *role)
{
Oid bbf_role_admin_oid = InvalidOid;

if (MyProcPort->is_tds_conn && sql_dialect == SQL_DIALECT_TSQL)
return;

bbf_role_admin_oid = get_role_oid(BABELFISH_ROLE_ADMIN, false);

/*
* Allow DROP ROLE if current user is bbf_role_admin as we need
* to allow remove_babelfish from PG endpoint. It is safe
* since only superusers can assume this role.
*/
if (bbf_role_admin_oid == GetUserId())
if (get_bbf_role_admin_oid() == GetUserId())
return;

if (is_babelfish_role(role))
Expand Down Expand Up @@ -939,14 +947,21 @@ is_babelfish_role(const char *role)
Oid bbf_master_guest_oid;
Oid bbf_tempdb_guest_oid;
Oid bbf_msdb_guest_oid;
Oid securityadmin;
Oid dbcreator;

sysadmin_oid = get_role_oid(BABELFISH_SYSADMIN, true); /* missing OK */
role_oid = get_role_oid(role, true); /* missing OK */
securityadmin = get_role_oid(BABELFISH_SECURITYADMIN, true); /* missing OK */
dbcreator = get_role_oid(BABELFISH_DBCREATOR, true); /* missing OK */

if (!OidIsValid(sysadmin_oid) || !OidIsValid(role_oid))
if (!OidIsValid(sysadmin_oid) || !OidIsValid(role_oid)
|| !OidIsValid(securityadmin) || !OidIsValid(dbcreator))
return false;

if (is_member_of_role(sysadmin_oid, role_oid) ||
is_member_of_role(securityadmin, role_oid) ||
is_member_of_role(dbcreator, role_oid) ||
pg_strcasecmp(role, BABELFISH_ROLE_ADMIN) == 0) /* check if it is bbf_role_admin */
return true;

Expand Down Expand Up @@ -1203,22 +1218,20 @@ static bool
handle_grant_role(GrantRoleStmt *grant_stmt)
{
ListCell *item;
Oid bbf_role_admin_oid = InvalidOid;

if (MyProcPort->is_tds_conn && sql_dialect == SQL_DIALECT_TSQL)
return true;

bbf_role_admin_oid = get_role_oid(BABELFISH_ROLE_ADMIN, false);

/*
* Allow GRANT ROLE if current user is bbf_role_admin as we need
* to allow initialise_babelfish from PG endpoint. It is safe
* since only superusers can assume this role.
*/
if (bbf_role_admin_oid == GetUserId())
if (get_bbf_role_admin_oid() == GetUserId())
return true;

/* Restrict roles to added as a member of bbf_role_admin */
/* Restrict roles to added as a member of BBF default server roles */
foreach(item, grant_stmt->granted_roles)
{
AccessPriv *priv = (AccessPriv *) lfirst(item);
Expand All @@ -1229,18 +1242,19 @@ handle_grant_role(GrantRoleStmt *grant_stmt)
continue;

roleid = get_role_oid(rolename, false);
if (OidIsValid(roleid) && roleid == bbf_role_admin_oid)
if (OidIsValid(roleid) && IS_DEFAULT_BBF_SERVER_ROLE(rolename))
check_babelfish_alterrole_restictions(false);
}

/* Restrict grant to/from bbf_role_admin role */
/* Restrict grant to/from bbf_role_admin, securityadmin or dbcreator role */

foreach(item, grant_stmt->grantee_roles)
{
RoleSpec *rolespec = lfirst_node(RoleSpec, item);
Oid roleid;

roleid = get_rolespec_oid(rolespec, false);
if (OidIsValid(roleid) && roleid == bbf_role_admin_oid)
if (OidIsValid(roleid) && IS_DEFAULT_BBF_SERVER_ROLE(rolespec->rolename))
check_babelfish_alterrole_restictions(false);
}

Expand Down
8 changes: 8 additions & 0 deletions contrib/babelfishpg_tds/src/include/tds_int.h
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,14 @@ extern ProcessUtility_hook_type next_ProcessUtility;
#define PUBLIC_ROLE_NAME "public"
#define BABELFISH_SYSADMIN "sysadmin"
#define BABELFISH_ROLE_ADMIN "bbf_role_admin"
#define BABELFISH_SECURITYADMIN "securityadmin"
#define BABELFISH_DBCREATOR "dbcreator"

#define IS_DEFAULT_BBF_SERVER_ROLE(rolename) \
((strlen(rolename) == 13 && strncmp(rolename, BABELFISH_SECURITYADMIN, 13) == 0) || \
(strlen(rolename) == 14 && strncmp(rolename, BABELFISH_ROLE_ADMIN, 14) == 0) || \
(strlen(rolename) == 9 && strncmp(rolename, BABELFISH_DBCREATOR, 9) == 0) || \
(strlen(rolename) == 8 && strncmp(rolename, BABELFISH_SYSADMIN, 8) == 0))

/* Functions in backend/tds/tdscomm.c */
extern void TdsSetMessageType(uint8_t msgType);
Expand Down
59 changes: 38 additions & 21 deletions contrib/babelfishpg_tsql/sql/babelfishpg_tsql.sql
Original file line number Diff line number Diff line change
Expand Up @@ -1254,7 +1254,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 @@ -1362,7 +1364,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 @@ -2046,22 +2049,24 @@ DECLARE login_valid BOOLEAN;
BEGIN
role := TRIM(trailing from LOWER(role));
login := TRIM(trailing from LOWER(login));

login_valid = (login = suser_name() COLLATE sys.database_default) OR
(EXISTS (SELECT name
FROM sys.server_principals
WHERE
LOWER(name) = login COLLATE sys.database_default
AND type = 'S'));
AND type IN ('S', 'R')));

IF NOT login_valid THEN
RETURN NULL;

ELSIF role = 'public' COLLATE sys.database_default THEN
RETURN 1;

ELSIF role = 'sysadmin' COLLATE sys.database_default THEN
has_role = pg_has_role(login::TEXT, role::TEXT, 'MEMBER');
ELSIF role COLLATE sys.database_default IN ('sysadmin', 'securityadmin', 'dbcreator') THEN
has_role = (pg_has_role(login::TEXT, role::TEXT, 'MEMBER')
OR ((login COLLATE sys.database_default NOT IN ('sysadmin', 'securityadmin', 'dbcreator'))
AND pg_has_role(login::TEXT, 'sysadmin'::TEXT, 'MEMBER')));
IF has_role THEN
RETURN 1;
ELSE
Expand All @@ -2070,11 +2075,8 @@ BEGIN

ELSIF role COLLATE sys.database_default IN (
'serveradmin',
'securityadmin',
'setupadmin',
'securityadmin',
'processadmin',
'dbcreator',
'diskadmin',
'bulkadmin') THEN
RETURN 0;
Expand Down Expand Up @@ -2120,7 +2122,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 != 'db_owner'
AND Ext1.orig_username NOT IN ('db_owner', 'db_securityadmin', '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 @@ -2152,7 +2154,7 @@ BEGIN
WHERE Ext1.database_name = DB_NAME()
AND Ext2.database_name = DB_NAME()
AND Ext1.type = 'R'
AND Ext2.orig_username != 'db_owner'
AND Ext2.orig_username NOT IN ('db_owner', 'db_securityadmin', '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 @@ -2190,7 +2192,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 != 'db_owner'
AND Ext1.orig_username NOT IN ('db_owner', 'db_securityadmin', '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 @@ -2323,8 +2325,8 @@ BEGIN
OR lower(rolname) = lower(RTRIM(@srvrolename)))
AND type = 'R')
OR lower(RTRIM(@srvrolename)) IN (
'serveradmin', 'setupadmin', 'securityadmin', 'processadmin',
'dbcreator', 'diskadmin', 'bulkadmin')
'serveradmin', 'setupadmin', 'processadmin',
'diskadmin', 'bulkadmin')
BEGIN
SELECT CAST(Ext1.rolname AS sys.SYSNAME) AS 'ServerRole',
CAST(Ext2.rolname AS sys.SYSNAME) AS 'MemberName',
Expand All @@ -2350,14 +2352,19 @@ CREATE OR REPLACE PROCEDURE sys.sp_helpdbfixedrole("@rolename" sys.SYSNAME = NUL
$$
BEGIN
-- Returns a list of the fixed database roles.
-- Only fixed role present in babelfish is db_owner.
IF LOWER(RTRIM(@rolename)) IS NULL OR LOWER(RTRIM(@rolename)) = 'db_owner'
IF LOWER(RTRIM(@rolename)) IS NULL OR LOWER(RTRIM(@rolename)) IN ('db_owner', 'db_accessadmin', 'db_securityadmin', 'db_datareader', 'db_datawriter')
BEGIN
SELECT CAST('db_owner' AS sys.SYSNAME) AS DbFixedRole, CAST('DB Owners' AS sys.nvarchar(70)) AS Description;
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'),
('db_securityadmin', 'DB Security 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 LOWER(RTRIM(@rolename)) IN (
'db_accessadmin','db_securityadmin','db_ddladmin', 'db_backupoperator',
'db_datareader', 'db_datawriter', 'db_denydatareader', 'db_denydatawriter')
'db_ddladmin', 'db_backupoperator',
'db_denydatareader', 'db_denydatawriter')
BEGIN
-- Return an empty result set instead of raising an error
SELECT CAST(NULL AS sys.SYSNAME) AS DbFixedRole, CAST(NULL AS sys.nvarchar(70)) AS Description
Expand Down Expand Up @@ -2427,12 +2434,22 @@ CAST(
ELSE 0
END
AS INT) AS sysadmin,
CAST(0 AS INT) AS securityadmin,
CAST(
CASE
WHEN is_srvrolemember('securityadmin', Base.name) = 1 THEN 1
ELSE 0
END
AS INT) AS securityadmin,
CAST(0 AS INT) AS serveradmin,
CAST(0 AS INT) AS setupadmin,
CAST(0 AS INT) AS processadmin,
CAST(0 AS INT) AS diskadmin,
CAST(0 AS INT) AS dbcreator,
CAST(
CASE
WHEN is_srvrolemember('dbcreator', Base.name) = 1 THEN 1
ELSE 0
END
AS INT) AS dbcreator,
CAST(0 AS INT) AS bulkadmin
FROM sys.server_principals AS Base
WHERE Base.type in ('S', 'U');
Expand Down
30 changes: 25 additions & 5 deletions contrib/babelfishpg_tsql/sql/ownership.sql
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,16 @@ CREATE OR REPLACE PROCEDURE initialize_babelfish ( sa_name VARCHAR(128) )
LANGUAGE plpgsql
AS $$
DECLARE
reserved_roles varchar[] := ARRAY['sysadmin', 'master_dbo', 'master_guest', 'master_db_owner', 'tempdb_dbo', 'tempdb_guest', 'tempdb_db_owner', 'msdb_dbo', 'msdb_guest', 'msdb_db_owner'];
reserved_roles varchar[] := ARRAY['sysadmin', 'securityadmin', 'dbcreator',
'master_dbo', 'master_guest', 'master_db_owner',
'master_db_accessadmin', 'master_db_securityadmin',
'master_db_datareader', 'master_db_datawriter',
'tempdb_dbo', 'tempdb_guest', 'tempdb_db_owner',
'tempdb_db_accessadmin', 'tempdb_db_securityadmin',
'tempdb_db_datareader', 'tempdb_db_datawriter',
'msdb_dbo', 'msdb_guest', 'msdb_db_owner',
'msdb_db_accessadmin', 'msdb_db_securityadmin',
'msdb_db_datareader', 'msdb_db_datawriter'];
user_id oid := -1;
db_name name := NULL;
role_name varchar;
Expand All @@ -285,11 +294,15 @@ BEGIN
RAISE E'Could not initialize babelfish with given role name: % is not the DB owner of current database.', sa_name;
END IF;

EXECUTE format('CREATE ROLE securityadmin CREATEROLE INHERIT PASSWORD NULL');
EXECUTE format('CREATE ROLE dbcreator CREATEDB INHERIT PASSWORD NULL');
EXECUTE format('CREATE ROLE bbf_role_admin CREATEDB CREATEROLE INHERIT PASSWORD NULL');
EXECUTE format('GRANT CREATE ON DATABASE %s TO bbf_role_admin WITH GRANT OPTION', CURRENT_DATABASE());
EXECUTE format('GRANT %I to bbf_role_admin WITH ADMIN TRUE;', sa_name);
EXECUTE format('CREATE ROLE sysadmin CREATEDB CREATEROLE INHERIT ROLE %I', sa_name);
EXECUTE format('GRANT sysadmin TO bbf_role_admin WITH ADMIN TRUE');
EXECUTE format('GRANT securityadmin TO bbf_role_admin WITH ADMIN TRUE');
EXECUTE format('GRANT dbcreator TO bbf_role_admin WITH ADMIN TRUE');
EXECUTE format('GRANT USAGE, SELECT ON SEQUENCE sys.babelfish_partition_function_seq TO sysadmin WITH GRANT OPTION');
EXECUTE format('GRANT USAGE, SELECT ON SEQUENCE sys.babelfish_partition_scheme_seq TO sysadmin WITH GRANT OPTION');
EXECUTE format('GRANT USAGE, SELECT ON SEQUENCE sys.babelfish_db_seq TO sysadmin WITH GRANT OPTION');
Expand All @@ -299,6 +312,8 @@ BEGIN
CALL sys.babel_initialize_logins(sa_name);
CALL sys.babel_initialize_logins('sysadmin');
CALL sys.babel_initialize_logins('bbf_role_admin');
CALL sys.babel_initialize_logins('securityadmin');
CALL sys.babel_initialize_logins('dbcreator');
CALL sys.babel_create_builtin_dbs(sa_name);
CALL sys.initialize_babel_extras();
-- run analyze for all babelfish catalog
Expand All @@ -320,6 +335,10 @@ BEGIN
DROP ROLE sysadmin;
DROP OWNED BY bbf_role_admin;
DROP ROLE bbf_role_admin;
DROP OWNED BY securityadmin;
DROP ROLE securityadmin;
DROP OWNED BY dbcreator;
DROP ROLE dbcreator;
END
$$;

Expand Down Expand Up @@ -365,7 +384,8 @@ CAST(CASE WHEN Ext.type = 'R' THEN NULL ELSE Ext.credential_id END AS INT) AS cr
CAST(CASE WHEN Ext.type = 'R' THEN 1 ELSE Ext.owning_principal_id END AS INT) AS owning_principal_id,
CAST(CASE WHEN Ext.type = 'R' THEN 1 ELSE Ext.is_fixed_role END AS sys.BIT) AS is_fixed_role
FROM pg_catalog.pg_roles AS Base INNER JOIN sys.babelfish_authid_login_ext AS Ext ON Base.rolname = Ext.rolname
WHERE (pg_has_role(suser_id(), 'sysadmin'::TEXT, 'MEMBER')
WHERE (pg_has_role(suser_id(), 'sysadmin'::TEXT, 'MEMBER')
OR pg_has_role(suser_id(), 'securityadmin'::TEXT, 'MEMBER')
OR Ext.orig_loginname = suser_name()
OR Ext.orig_loginname = (SELECT pg_get_userbyid(datdba) FROM pg_database WHERE datname = CURRENT_DATABASE()) COLLATE sys.database_default
OR Ext.type = 'R')
Expand Down Expand Up @@ -450,7 +470,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', 'guest') -- system users should always be visible
AND (Ext.orig_username IN ('dbo', 'db_owner', 'db_securityadmin', '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 @@ -501,8 +521,8 @@ CAST(Ext.orig_loginname AS sys.nvarchar(128)) AS name,
CAST('SERVER ROLE' AS sys.nvarchar(128)) AS type,
CAST ('GRANT OR DENY' as sys.nvarchar(128)) as usage
FROM pg_catalog.pg_roles AS Base INNER JOIN sys.babelfish_authid_login_ext AS Ext ON Base.rolname = Ext.rolname
WHERE Ext.type = 'R' AND
(pg_has_role(sys.suser_id(), 'sysadmin'::TEXT, 'MEMBER'));
WHERE Ext.type = 'R'
AND bbf_is_member_of_role_nosuper(sys.suser_id(), Base.oid);

GRANT SELECT ON sys.login_token TO PUBLIC;

Expand Down
8 changes: 7 additions & 1 deletion contrib/babelfishpg_tsql/sql/sys_functions.sql
Original file line number Diff line number Diff line change
Expand Up @@ -4501,7 +4501,9 @@ BEGIN
END IF;
ELSIF EXISTS (SELECT orig_username FROM sys.babelfish_authid_user_ext WHERE orig_username = role COLLATE sys.database_default)
THEN
IF EXISTS (SELECT name FROM sys.user_token WHERE name = role COLLATE sys.database_default)
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
ELSIF (is_windows_grp)
THEN RETURN NULL; -- Return NULL if session is not a windows auth session but argument is a windows group
Expand Down Expand Up @@ -4530,6 +4532,10 @@ $$
$$
LANGUAGE SQL STRICT STABLE PARALLEL SAFE;

CREATE OR REPLACE FUNCTION sys.bbf_is_member_of_role_nosuper(OID, OID)
RETURNS BOOLEAN AS 'babelfishpg_tsql', 'bbf_is_member_of_role_nosuper'
LANGUAGE C STABLE STRICT PARALLEL SAFE;

CREATE OR REPLACE FUNCTION sys.replace (input_string sys.VARCHAR, pattern sys.VARCHAR, replacement sys.VARCHAR)
RETURNS sys.VARCHAR AS
$BODY$
Expand Down
Loading

0 comments on commit 1b3a16d

Please sign in to comment.