diff --git a/contrib/babelfishpg_tsql/src/catalog.c b/contrib/babelfishpg_tsql/src/catalog.c index a9209fe2d8..4207522e5c 100644 --- a/contrib/babelfishpg_tsql/src/catalog.c +++ b/contrib/babelfishpg_tsql/src/catalog.c @@ -9,6 +9,7 @@ #include "catalog/catalog.h" #include "catalog/heap.h" #include "catalog/indexing.h" +#include "catalog/pg_auth_members.h" #include "catalog/pg_namespace.h" #include "catalog/pg_authid.h" #include "catalog/pg_proc.h" @@ -6287,3 +6288,49 @@ get_tvp_typename_typeschemaname(char *proc_name, char *target_arg_name, char **t if(!xactStarted) CommitTransactionCommand(); } + +/* + * Checks if the given member is 'bbf_role_admin' role and has the privilege + * to grant a specified role through direct membership. This privilege is + * determined by the presence of the 'admin_option' flag, which allows the + * member to grant the role to others. + * + * @return true if the member is 'bbf_role_admin' and also has the admin option + * to grant the specified role; otherwise, returns false. + */ +bool +bbf_check_member_has_direct_priv_to_grant_role(Oid member, Oid role) +{ + CatCList *memlist; + + /* + * TSQL specific behavior and member must be bbf_role_admin. + * If not return false. + */ + if (sql_dialect != SQL_DIALECT_TSQL || !IS_TDS_CONN() + || member != get_bbf_role_admin_oid()) + return false; + + /* + * Find all existing tuples for given role + * whose member is bbf_role_admin + */ + memlist = SearchSysCacheList2(AUTHMEMROLEMEM, + ObjectIdGetDatum(role), + ObjectIdGetDatum(member)); + for (int i = 0; i < memlist->n_members; i++) + { + HeapTuple tup = &memlist->members[i]->tuple; + Form_pg_auth_members form = (Form_pg_auth_members) GETSTRUCT(tup); + + /* Return true if admin_option is true */ + if (form->admin_option) + { + ReleaseSysCacheList(memlist); + return true; + } + } + + ReleaseSysCacheList(memlist); + return false; +} \ No newline at end of file diff --git a/contrib/babelfishpg_tsql/src/catalog.h b/contrib/babelfishpg_tsql/src/catalog.h index c6ecfab882..32c2f9cffb 100644 --- a/contrib/babelfishpg_tsql/src/catalog.h +++ b/contrib/babelfishpg_tsql/src/catalog.h @@ -28,6 +28,7 @@ extern bool IsPLtsqlExtendedCatalog(Oid relationId); extern bool IsPltsqlToastRelationHook(Relation relation); extern bool IsPltsqlToastClassHook(Form_pg_class pg_class_tup); extern void pltsql_drop_relation_refcnt_hook(Relation relation); +extern bool bbf_check_member_has_direct_priv_to_grant_role(Oid member, Oid role); /***************************************** * SYS schema diff --git a/contrib/babelfishpg_tsql/src/hooks.c b/contrib/babelfishpg_tsql/src/hooks.c index 68ed3069bf..add2654c3e 100644 --- a/contrib/babelfishpg_tsql/src/hooks.c +++ b/contrib/babelfishpg_tsql/src/hooks.c @@ -304,6 +304,7 @@ static pltsql_replace_non_determinstic_hook_type prev_pltsql_replace_non_determi static pltsql_is_partitioned_table_reloptions_allowed_hook_type prev_pltsql_is_partitioned_table_reloptions_allowed_hook = NULL; static ExecFuncProc_AclCheck_hook_type prev_ExecFuncProc_AclCheck_hook = NULL; static bbf_execute_grantstmt_as_dbsecadmin_hook_type prev_bbf_execute_grantstmt_as_dbsecadmin_hook = NULL; +static bbf_check_member_has_direct_priv_to_grant_role_hook_type prev_bbf_check_member_has_direct_priv_to_grant_role_hook = NULL; /***************************************** * Install / Uninstall @@ -522,6 +523,9 @@ InstallExtendedHooks(void) prev_bbf_execute_grantstmt_as_dbsecadmin_hook = bbf_execute_grantstmt_as_dbsecadmin_hook; bbf_execute_grantstmt_as_dbsecadmin_hook = handle_grantstmt_for_dbsecadmin; + + prev_bbf_check_member_has_direct_priv_to_grant_role_hook = bbf_check_member_has_direct_priv_to_grant_role_hook; + bbf_check_member_has_direct_priv_to_grant_role_hook = bbf_check_member_has_direct_priv_to_grant_role; pltsql_get_object_identity_event_trigger_hook = pltsql_get_object_identity_event_trigger; @@ -600,6 +604,8 @@ UninstallExtendedHooks(void) pltsql_is_partitioned_table_reloptions_allowed_hook = prev_pltsql_is_partitioned_table_reloptions_allowed_hook; ExecFuncProc_AclCheck_hook = prev_ExecFuncProc_AclCheck_hook; bbf_execute_grantstmt_as_dbsecadmin_hook = prev_bbf_execute_grantstmt_as_dbsecadmin_hook; + bbf_check_member_has_direct_priv_to_grant_role_hook = prev_bbf_check_member_has_direct_priv_to_grant_role_hook; + bbf_InitializeParallelDSM_hook = NULL; bbf_ParallelWorkerMain_hook = NULL; @@ -6032,4 +6038,4 @@ remove_db_name_in_schema(const char *object_name, const char *object_type) pfree(splited_object_name); return (const char *)pstrdup(object_name + prefix_len); -} +} \ No newline at end of file diff --git a/test/JDBC/expected/db_securityadmin-vu-verify.out b/test/JDBC/expected/db_securityadmin-vu-verify.out index e8bed8ed3f..c4aa540f35 100644 --- a/test/JDBC/expected/db_securityadmin-vu-verify.out +++ b/test/JDBC/expected/db_securityadmin-vu-verify.out @@ -1439,6 +1439,15 @@ t ~~END~~ +-- Wait to sync with another session +SELECT pg_sleep(1); +go +~~START~~ +void + +~~END~~ + + -- tsql DROP LOGIN db_securityadmin_restrictions_login; GO diff --git a/test/JDBC/expected/test_windows_login-vu-prepare.out b/test/JDBC/expected/test_windows_login-vu-prepare.out index 6cb5a741ec..1edefb9eb2 100644 --- a/test/JDBC/expected/test_windows_login-vu-prepare.out +++ b/test/JDBC/expected/test_windows_login-vu-prepare.out @@ -1,5 +1,4 @@ -- psql --- TODO: BABEL-5349 do $$ begin if not exists (select * from pg_catalog.pg_roles where rolname = 'rds_ad') diff --git a/test/JDBC/expected/test_windows_login-vu-verify.out b/test/JDBC/expected/test_windows_login-vu-verify.out index f09f266b3b..4d9f2b0ff4 100644 --- a/test/JDBC/expected/test_windows_login-vu-verify.out +++ b/test/JDBC/expected/test_windows_login-vu-verify.out @@ -208,3 +208,27 @@ int8 1 ~~END~~ + +-- validates bbf_role_admin is direct member of all the role entries of babelfish_authid_user_ext and babelfish_authid_login_ext with admin option +-- show entries of which bbf_role_admin is not direct member of. +-- should be 0 +WITH bbf_admin_roles AS ( + SELECT r.rolname + FROM pg_auth_members m + JOIN pg_roles r ON (m.roleid = r.oid) + WHERE m.member IN (SELECT oid FROM pg_roles WHERE rolname = 'bbf_role_admin') + AND m.admin_option = true +) +SELECT COUNT(*) +FROM ( + SELECT rolname FROM sys.babelfish_authid_login_ext WHERE rolname != 'bbf_role_admin' + UNION ALL + SELECT rolname FROM sys.babelfish_authid_user_ext +) +WHERE rolname NOT IN (SELECT rolname FROM bbf_admin_roles); +GO +~~START~~ +int8 +0 +~~END~~ + diff --git a/test/JDBC/expected/test_windows_login_before-17_2-vu-cleanup.out b/test/JDBC/expected/test_windows_login_before-17_2-vu-cleanup.out new file mode 100644 index 0000000000..870cf9753e --- /dev/null +++ b/test/JDBC/expected/test_windows_login_before-17_2-vu-cleanup.out @@ -0,0 +1,91 @@ +-- tsql +DROP LOGIN PassWordUser; +GO + +-- test for drop login with upn format +DROP LOGIN [aduser@AD]; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: Cannot drop the login 'aduser@ad', because it does not exist or you do not have permission.)~~ + + +DROP LOGIN [ad\adUSer]; +GO + +DROP LOGIN [ad\aduserDB]; +GO + +DROP LOGIN [ad\Aduserlanguage]; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: Cannot drop the login 'aduserlanguage@AD', because it does not exist or you do not have permission.)~~ + + +DROP LOGIN [ad\Aduserdblanguage]; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: Cannot drop the login 'aduserdblanguage@AD', because it does not exist or you do not have permission.)~~ + + +DROP LOGIN [ad\AduserdbEnglish]; +GO + +DROP LOGIN [ad\Aduseroption]; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: Cannot drop the login 'aduseroption@AD', because it does not exist or you do not have permission.)~~ + + +DROP LOGIN [babel\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa] +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: Cannot drop the login 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@BABEL', because it does not exist or you do not have permission.)~~ + + +DROP LOGIN [ba.bel\username]; +GO + +-- test for non-existent login +DROP LOGIN [babel\non_existent_login] +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: Cannot drop the login 'non_existent_login@BABEL', because it does not exist or you do not have permission.)~~ + + +-- drop login with different casing +DROP LOGIN UserPassword; +GO + +DROP LOGIN [BabeL\DupLicate]; +GO + +DROP LOGIN [Babel\DuplicateDefaultDB]; +GO + +-- test drop logins for logins with different language names +-- Arabic +DROP LOGIN [babel\كلب]; +GO + +-- Mongolian +DROP LOGIN [babel\өглөө]; +GO + +-- Greek +DROP LOGIN [babel\ελπίδα]; +GO + +-- Chinese +DROP LOGIN [babel\爱]; +GO + +DROP DATABASE ad_db; +GO + + diff --git a/test/JDBC/expected/test_windows_login_before-17_2-vu-prepare.out b/test/JDBC/expected/test_windows_login_before-17_2-vu-prepare.out new file mode 100644 index 0000000000..1edefb9eb2 --- /dev/null +++ b/test/JDBC/expected/test_windows_login_before-17_2-vu-prepare.out @@ -0,0 +1,498 @@ +-- psql +do +$$ begin + if not exists (select * from pg_catalog.pg_roles where rolname = 'rds_ad') + then create role rds_ad NOSUPERUSER NOLOGIN NOCREATEROLE INHERIT NOREPLICATION; + end if; +end $$; +GO +-- terminate-psql-conn + +-- tsql +CREATE DATABASE ad_db; +GO + +CREATE LOGIN PassWordUser with PASSWORD='123'; +GO + +-- positive test case +CREATE LOGIN [ad\Aduser] from windows; +GO + +-- test for default database +CREATE LOGIN [ad\Aduserdb] from windows with default_database=[ad_db]; +GO + +-- test for default language +CREATE LOGIN [ad\Aduserlanguage] from windows with default_language=[German]; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: '[German]' is not currently supported in Babelfish. please use babelfishpg_tsql.escape_hatch_language_non_english to ignore)~~ + + +-- test for default database and default language +CREATE LOGIN [ad\Aduserdblanguage] from windows with default_database=[ad_db], default_language=[German]; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: '[German]' is not currently supported in Babelfish. please use babelfishpg_tsql.escape_hatch_language_non_english to ignore)~~ + + +CREATE LOGIN [ad\AduserdbEnglish] from windows with default_database=[ad_db], default_language=[English]; +GO + +-- test for invalid options for windows +CREATE LOGIN [ad\Aduseroption] from windows with CHECK_POLICY = OFF, CHECK_EXPIRATION = OFF; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: syntax error near '=' at line 2 and character position 62)~~ + + + +-- test boundary conditions +-- test for when the login_name is greater than 21 characters +create login [babel\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa] from windows; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: The login name 'babel\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' has invalid length. Login name length should be between 1 and 20 for windows login.)~~ + + +-- test for when the login_name length is 0 +CREATE LOGIN [babel\] from windows; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: The login name 'babel\' has invalid length. Login name length should be between 1 and 20 for windows login.)~~ + + +--test for when the total login name, i.e., domain+user is greater than 64 +CREATE LOGIN [babelforapg.individualad.testfornumberofcharactersintotalwhichisnotallowedinpostgres\user] from windows; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: The login name 'user@BABELFORAPG.INDIVIDUALAD.TESTFORNUMBEROFCHARACTERSINTOTALWHICHISNOTALLOWEDINPOSTGRES' is too long. Maximum length is 63.)~~ + + +-- test for when the login_name contains invalid characters +CREATE LOGIN [babeluser] from windows; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: 'babeluser' is not a valid Windows NT name. Give the complete name: .)~~ + +CREATE LOGIN [babel\user\] from windows; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: 'babel\user\' is not a valid name because it contains invalid characters.)~~ + +CREATE LOGIN [babel\us\er] from windows; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: 'babel\us\er' is not a valid name because it contains invalid characters.)~~ + +CREATE LOGIN [babel\user/] from windows; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: 'babel\user/' is not a valid name because it contains invalid characters.)~~ + +CREATE LOGIN [babel\us/er] from windows; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: 'babel\us/er' is not a valid name because it contains invalid characters.)~~ + +CREATE LOGIN [babel\user[] from windows; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: 'babel\user[' is not a valid name because it contains invalid characters.)~~ + +CREATE LOGIN [babel\u[ser] from windows; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: 'babel\u[ser' is not a valid name because it contains invalid characters.)~~ + +CREATE LOGIN [babel\use]r] from windows; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: syntax error near 'r' at line 1 and character position 24)~~ + +CREATE LOGIN [babel\user;] from windows; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: 'babel\user;' is not a valid name because it contains invalid characters.)~~ + +CREATE LOGIN [babel\us;er] from windows; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: 'babel\us;er' is not a valid name because it contains invalid characters.)~~ + +CREATE LOGIN [babel\user:] from windows; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: 'babel\user:' is not a valid name because it contains invalid characters.)~~ + +CREATE LOGIN [babel\us:er] from windows; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: 'babel\us:er' is not a valid name because it contains invalid characters.)~~ + +CREATE LOGIN [babel\user|] from windows; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: 'babel\user|' is not a valid name because it contains invalid characters.)~~ + +CREATE LOGIN [babel\use|r] from windows; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: 'babel\use|r' is not a valid name because it contains invalid characters.)~~ + +CREATE LOGIN [babel\user=] from windows; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: 'babel\user=' is not a valid name because it contains invalid characters.)~~ + +CREATE LOGIN [babel\u=ser] from windows; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: 'babel\u=ser' is not a valid name because it contains invalid characters.)~~ + +CREATE LOGIN [babel\user,] from windows; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: 'babel\user,' is not a valid name because it contains invalid characters.)~~ + +CREATE LOGIN [babel\us,er] from windows; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: 'babel\us,er' is not a valid name because it contains invalid characters.)~~ + +CREATE LOGIN [babel\user+] from windows; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: 'babel\user+' is not a valid name because it contains invalid characters.)~~ + +CREATE LOGIN [babel\u+ser] from windows; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: 'babel\u+ser' is not a valid name because it contains invalid characters.)~~ + +CREATE LOGIN [babel\user*] from windows; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: 'babel\user*' is not a valid name because it contains invalid characters.)~~ + +CREATE LOGIN [babel\user*] from windows; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: 'babel\user*' is not a valid name because it contains invalid characters.)~~ + +CREATE LOGIN [babel\user?] from windows; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: 'babel\user?' is not a valid name because it contains invalid characters.)~~ + +CREATE LOGIN [babel\us?er] from windows; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: 'babel\us?er' is not a valid name because it contains invalid characters.)~~ + +CREATE LOGIN [babel\user>] from windows; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: 'babel\user>' is not a valid name because it contains invalid characters.)~~ + +CREATE LOGIN [babel\u>ser] from windows; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: 'babel\u>ser' is not a valid name because it contains invalid characters.)~~ + +CREATE LOGIN [babel\user<] from windows; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: 'babel\user<' is not a valid name because it contains invalid characters.)~~ + +CREATE LOGIN [babel\user] from windows; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: 'babel\us<>er' is not a valid name because it contains invalid characters.)~~ + + +-- test to show UPN format is not allowed +CREATE LOGIN [user@BABEL] from windows; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: 'user@BABEL' is not a valid Windows NT name. Give the complete name: .)~~ + + +-- test for duplicate login +CREATE LOGIN usErpassWord with PASSWORD = '123'; +GO + +CREATE LOGIN UserPassword with PASSWORD = '098'; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: The Server principal 'userpassword' already exists)~~ + + +CREATE LOGIN [babel\duplicate] from windows; +GO + +CREATE LOGIN [BabeL\DupLicate] from windows; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: The Server principal 'duplicate@BABEL' already exists)~~ + + +CREATE LOGIN [babel\duplicatedefaultdb] from windows with default_database=[master]; +GO + +CREATE LOGIN [Babel\DuplicateDefaultDB] from windows with default_database=[ad_db]; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: The Server principal 'duplicatedefaultdb@BABEL' already exists)~~ + + +-- test for empty domain name +CREATE LOGIN [\adnodomain] from windows; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: The login name '\adnodomain' is invalid. The domain can not be empty.)~~ + + + +-- test for login names with different language +-- Arabic +CREATE LOGIN [babel\كلب] from windows; +GO + +-- Mongolian +CREATE LOGIN [babel\өглөө] from windows; +GO + +-- Greek +CREATE LOGIN [babel\ελπίδα] from windows; +GO + +-- Chinese +CREATE LOGIN [babel\爱] from windows; +GO + +-- test for windows login with password --> should throw error +CREATE LOGIN [babel\adbabel] from windows with password='1234'; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: syntax error near '=' at line 2 and character position 55)~~ + + +-- test for when the domain name contains invalid characters +CREATE LOGIN [