diff --git a/doc/sql.extensions/README.cumulative_roles.txt b/doc/sql.extensions/README.cumulative_roles.txt new file mode 100644 index 0000000000..fb6bab125c --- /dev/null +++ b/doc/sql.extensions/README.cumulative_roles.txt @@ -0,0 +1,53 @@ +Cumulative roles. + + Implements capability to grant role to role. + +Author: + Red Soft Corporation, roman.simakov(at)red-soft.biz + +Syntax is: + +GRANT [DEFAULT] TO [USER | ROLE] [WITH ADMIN OPTION]; +REVOKE [DEFAULT] FROM [USER | ROLE] [WITH ADMIN OPTION]; + +Description: + +Makes it possible to grant a role to user or another role. + +If DEFAULT keyword is used the role will be used every time for user even if it's not specified explicitly. +While connecting user will get permissions of all roles which were granted to him with DEFAULT keyword and +permissions of all roles also granted to them with DEFAULT keyword specified. +If user specify a role in connection he will also get permissions of this role (if granted of course) and +permissions of all roles granted to it, etc. + +When some user want go grant a role to another user or role ADMIN OPTION will be checked. In this case user can grant +a role cumulatively granted to him only if every role in sequence has ADMIN OPTION. + +REVOKE works as usual except if DEFAULT is specified only default option will be revoked. In other words +role skill be granted but like without DEFAULT. + +Let: +"->" grant without ADMIN OPTION +"=>" grant with ADMIN OPTION + +Consider 3 options: +1) WORKER->MANAGER->Joe +2) WORKER->MANAGER=>Joe +3) WORKER=>MANAGER->Joe +4) WORKER=>MANAGER=>Joe + +Joe can grant role MANAGER in 2 and 4 options and role WORKER only in 4 option. In 1 and 3 options Joe cannot grant +nothing even in 3 option WORKER granted to MANAGER with ADMIN OPTION. + +Sample: + +CREATE DATABASE 'LOCALHOST:/TMP/CUMROLES.FDB'; +CREATE TABLE T(I INTEGER); +CREATE ROLE TINS; +CREATE ROLE CUMR; +GRANT INSERT ON T TO TINS; +GRANT DEFAULT TINS ROLE TO CUMR WITH ADMIN OPTION; +GRANT CUMR TO USER US WITH ADMIN OPTION; +CONNECT 'LOCALHOST:/TMP/CUMROLES.FDB' USER 'US' PASSWORD 'PAS'; +INSERT INTO T VALUES (1); +GRANT TINS TO US2; diff --git a/src/common/classes/objects_array.h b/src/common/classes/objects_array.h index 367b76f73d..35786b3093 100644 --- a/src/common/classes/objects_array.h +++ b/src/common/classes/objects_array.h @@ -332,6 +332,12 @@ namespace Firebird { } + ObjectsArray(const ObjectsArray& o) + : A() + { + add(o); + } + ObjectsArray() : A() { @@ -436,6 +442,12 @@ namespace Firebird ObjectCmp> >(p) { } + explicit SortedObjectsArray() : + ObjectsArray >() + { } + bool find(const ObjectKey& item, size_type& pos) const { const ObjectKey* const pItem = &item; diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp index 9d4b71f08d..b796b1dfb8 100644 --- a/src/dsql/DdlNodes.epp +++ b/src/dsql/DdlNodes.epp @@ -903,7 +903,7 @@ void DdlNode::storePrivileges(thread_db* tdbb, jrd_tra* transaction, const char* privileges) { Attachment* const attachment = transaction->tra_attachment; - const string& userName = attachment->att_user->usr_user_name; + const MetaName& userName = attachment->att_user->usr_user_name; AutoCacheRequest request(tdbb, drq_s_usr_prvs, DYN_REQUESTS); @@ -966,7 +966,7 @@ void DdlNode::storeGlobalField(thread_db* tdbb, jrd_tra* transaction, MetaName& const TypeClause* field, const string& computedSource, const BlrDebugWriter::BlrData& computedValue) { Attachment* const attachment = transaction->tra_attachment; - const string& userName = attachment->att_user->usr_user_name; + const MetaName& userName = attachment->att_user->usr_user_name; const ValueListNode* elements = field->ranges; const USHORT dims = elements ? elements->items.getCount() / 2 : 0; @@ -1674,7 +1674,7 @@ void CreateAlterFunctionNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch jrd_tra* transaction) { Attachment* const attachment = transaction->getAttachment(); - const string& userName = attachment->att_user->usr_user_name; + const MetaName& userName = attachment->att_user->usr_user_name; if (package.isEmpty()) { @@ -2661,7 +2661,7 @@ void CreateAlterProcedureNode::executeCreate(thread_db* tdbb, DsqlCompilerScratc jrd_tra* transaction) { Attachment* const attachment = transaction->getAttachment(); - const string& userName = attachment->att_user->usr_user_name; + const MetaName& userName = attachment->att_user->usr_user_name; if (package.isEmpty()) { @@ -3801,7 +3801,7 @@ void CreateCollationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra jrd_tra* transaction) { Attachment* const attachment = transaction->tra_attachment; - const string& userName = attachment->att_user->usr_user_name; + const MetaName& userName = attachment->att_user->usr_user_name; // run all statements under savepoint control AutoSavePoint savePoint(tdbb, transaction); @@ -5264,7 +5264,7 @@ void CreateAlterExceptionNode::executeCreate(thread_db* tdbb, DsqlCompilerScratc jrd_tra* transaction) { Attachment* const attachment = transaction->getAttachment(); - const string& userName = attachment->att_user->usr_user_name; + const MetaName& userName = attachment->att_user->usr_user_name; executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_EXCEPTION, name, NULL); @@ -5637,7 +5637,7 @@ SSHORT CreateAlterSequenceNode::store(thread_db* tdbb, jrd_tra* transaction, con fb_sysflag sysFlag, SINT64 val, SLONG step) { Attachment* const attachment = transaction->tra_attachment; - const string& userName = attachment->att_user->usr_user_name; + const MetaName& userName = attachment->att_user->usr_user_name; DYN_UTIL_check_unique_name(tdbb, transaction, name, obj_generator); @@ -8304,7 +8304,7 @@ void CreateAlterViewNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra jrd_tra* transaction) { Attachment* const attachment = transaction->tra_attachment; - const string& userName = attachment->att_user->usr_user_name; + const MetaName& userName = attachment->att_user->usr_user_name; const dsql_rel* modifyingView = NULL; @@ -10624,7 +10624,7 @@ void CreateAlterUserNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra Auth::DynamicUserData* userData = FB_NEW_POOL(*transaction->tra_pool) Auth::DynamicUserData; - string text = name.c_str(); + MetaName text = name.c_str(); if (text.isEmpty() && mode == USER_MOD) { // alter current user @@ -10825,12 +10825,15 @@ void GrantRevokeNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, if (grantAdminOption) option = 2; // with admin option + const GranteeClause* rolesPtr = roles.begin(); const GranteeClause* rolesEnd = roles.end(); - for (const GranteeClause* rolesPtr = roles.begin(); rolesPtr != rolesEnd; ++rolesPtr) + const bool* defaultRolesPtr = defaultRoles.begin(); + for (; rolesPtr != rolesEnd; ++rolesPtr, ++defaultRolesPtr) { usersEnd = users.end(); + const bool defaultRole = *defaultRolesPtr; for (usersPtr = users.begin(); usersPtr != usersEnd; ++usersPtr) - grantRevoke(tdbb, transaction, rolesPtr, usersPtr, "M", NULL, option); + grantRevoke(tdbb, transaction, rolesPtr, usersPtr, "M", defaultRole ? "D" : NULL, option); } } } @@ -10964,13 +10967,13 @@ void GrantRevokeNode::modifyPrivileges(thread_db* tdbb, jrd_tra* transaction, SS // Execute SQL grant/revoke operation. void GrantRevokeNode::grantRevoke(thread_db* tdbb, jrd_tra* transaction, const GranteeClause* object, const GranteeClause* userNod, const char* privs, - const MetaName& field, int options) + MetaName field, int options) { SSHORT userType = userNod->first; MetaName user(userNod->second); - MetaName dummyName; + MetaName dummyName; const SSHORT objType = object ? object->first : obj_type_MAX; - bool crdb = false; + bool crdb = false; char privileges[16]; strcpy(privileges, privs ? privs : ""); @@ -11136,7 +11139,8 @@ void GrantRevokeNode::grantRevoke(thread_db* tdbb, jrd_tra* transaction, const G PRIV.RDB$USER = user.c_str() AND PRIV.RDB$USER_TYPE = userType AND PRIV.RDB$GRANTOR EQ grantorRevoker.c_str() AND - PRIV.RDB$FIELD_NAME EQUIV NULLIF(field.c_str(), '') + (PRIV.RDB$FIELD_NAME EQUIV NULLIF(field.c_str(), '') OR + (PRIV.RDB$OBJECT_TYPE EQ obj_sql_role)) { if (PRIV.RDB$GRANT_OPTION.NULL || PRIV.RDB$GRANT_OPTION || @@ -11144,8 +11148,22 @@ void GrantRevokeNode::grantRevoke(thread_db* tdbb, jrd_tra* transaction, const G { duplicate = true; } - else - ERASE PRIV; // has to be 0 and options == 1 + else if (!PRIV.RDB$FIELD_NAME.NULL) + { + field = PRIV.RDB$FIELD_NAME; // Keep DEFAULT ROLE while adding ADMIN OPTION + } + + if (duplicate && objType == obj_sql_role && field == "D" && + PRIV.RDB$FIELD_NAME.NULL) + { + // We have to reset duplicate to add DEFAULT ROLE and keep options to prevent reset of ADMIN OPTION + duplicate = false; + options = PRIV.RDB$GRANT_OPTION; + } + + + if (!duplicate) + ERASE PRIV; } END_FOR @@ -11158,9 +11176,12 @@ void GrantRevokeNode::grantRevoke(thread_db* tdbb, jrd_tra* transaction, const G if (userType == obj_sql_role) { - // Temporary restriction. This should be removed once GRANT role1 TO rolex is - // supported and this message could be reused for blocking cycles of role grants. - status_exception::raise(Arg::PrivateDyn(192) << user.c_str()); + // Check for blocking cycles of role grants. + Firebird::SortedArray grantedRoles; + SCL_find_granted_roles(tdbb, objName, true, grantedRoles, false); + // 292: role @1 can not be granted to role @2 + if (grantedRoles.exist(user.c_str())) + status_exception::raise(Arg::PrivateDyn(292) << objName.c_str() << user.c_str()); } } else @@ -11196,6 +11217,10 @@ void GrantRevokeNode::grantRevoke(thread_db* tdbb, jrd_tra* transaction, const G } else // REVOKE { + const bool revokeRoleDefault = (objType == obj_sql_role) && field.hasData(); + MetaName curField; + int curOptions = options; + AutoCacheRequest request(tdbb, (field.hasData() ? drq_e_grant1 : drq_e_grant2), DYN_REQUESTS); for (const char* pr = privileges; (priv[0] = *pr); ++pr) @@ -11203,7 +11228,7 @@ void GrantRevokeNode::grantRevoke(thread_db* tdbb, jrd_tra* transaction, const G bool grantErased = false; bool badGrantor = false; - if (field.hasData()) + if (field.hasData() && objType != obj_sql_role) { FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) PRIV IN RDB$USER_PRIVILEGES @@ -11242,6 +11267,8 @@ void GrantRevokeNode::grantRevoke(thread_db* tdbb, jrd_tra* transaction, const G if (grantorRevoker == PRIV.RDB$GRANTOR) { + curOptions = PRIV.RDB$GRANT_OPTION; + curField = PRIV.RDB$FIELD_NAME; ERASE PRIV; grantErased = true; } @@ -11251,13 +11278,19 @@ void GrantRevokeNode::grantRevoke(thread_db* tdbb, jrd_tra* transaction, const G END_FOR } - if (options && grantErased) + if (revokeRoleDefault) + { + // Add role grant again without default + storePrivilege(tdbb, transaction, objName, user, NULL, pr, userType, objType, + curOptions, grantorRevoker); + } + else if (options && grantErased) { // Add the privilege without the grant option. There is a modify trigger on the // rdb$user_privileges which disallows the table from being updated. It would have // to be changed such that only the grant_option field can be updated. - storePrivilege(tdbb, transaction, objName, user, field, pr, userType, objType, + storePrivilege(tdbb, transaction, objName, user, curField, pr, userType, objType, 0, grantorRevoker); } @@ -11478,7 +11511,57 @@ void GrantRevokeNode::checkGrantorCanGrant(thread_db* tdbb, jrd_tra* transaction END_FOR } -// Check if the grantor has admin privilege on the role. +/* + * Function takes a role and grantor name and go through all roles granted to specified one. + * If found target role with admin option returns 2, without admin option - 1. + * Otherwise returns 0; + * + */ +int getGrantorOption(thread_db* tdbb, jrd_tra* transaction, const MetaName& grantor, int grantorType, const MetaName& roleName) +{ + AutoCacheRequest request(tdbb, drq_get_role_au, DYN_REQUESTS); + + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + PRV IN RDB$USER_PRIVILEGES WITH + PRV.RDB$USER = UPPERCASE(grantor.c_str()) AND + PRV.RDB$USER_TYPE = grantorType AND + PRV.RDB$OBJECT_TYPE = obj_sql_role AND + PRV.RDB$PRIVILEGE EQ "M" + { + const Firebird::MetaName role = PRV.RDB$RELATION_NAME; + const bool grantable = PRV.RDB$GRANT_OPTION == 2; + + if (role == roleName) + { + return grantable ? 2 : 1; + } + else + { + switch (getGrantorOption(tdbb, transaction, role, obj_sql_role, roleName)) + { + case 0: + continue; + case 1: // call found roleName we should stop searching + return 1; + case 2: // call found roleName with admin option but have we admin option of intermediate roles? + return grantable ? 2 : 1; + } + } + } + END_FOR + + // we and calls did not found granted roleName and have to return 0 + return 0; +} + +/* + * Check if the grantor has admin privilege on the role or admin privilege on another role + * which has admin privilege on it, etc + * If recoursive call of getGrantorOption returns: + * 0 - role does not grant to grantor. + * 1 - found but without admin option. + * 2 - with admin option. +*/ void GrantRevokeNode::checkGrantorCanGrantRole(thread_db* tdbb, jrd_tra* transaction, const MetaName& grantor, const MetaName& roleName) { @@ -11496,32 +11579,13 @@ void GrantRevokeNode::checkGrantorCanGrantRole(thread_db* tdbb, jrd_tra* transac status_exception::raise(Arg::PrivateDyn(188) << roleName.c_str()); } - AutoCacheRequest request(tdbb, drq_get_role_au, DYN_REQUESTS); - bool grantable = false; - bool noAdmin = false; + const int r = getGrantorOption(tdbb, transaction, grantor, obj_user, roleName); - // The 'grantor' is not the owner of the ROLE, see if they have admin privilege on the role. - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) - PRV IN RDB$USER_PRIVILEGES WITH - PRV.RDB$USER = UPPERCASE(grantor.c_str()) AND - PRV.RDB$USER_TYPE = obj_user AND - PRV.RDB$RELATION_NAME EQ roleName.c_str() AND - PRV.RDB$OBJECT_TYPE = obj_sql_role AND - PRV.RDB$PRIVILEGE EQ "M" - { - if (PRV.RDB$GRANT_OPTION == 2) - grantable = true; - else - noAdmin = true; - } - END_FOR - - if (!grantable) + if (r < 2) { // 189: user have no admin option. // 190: user is not a member of the role. - status_exception::raise(Arg::PrivateDyn(noAdmin ? 189 : 190) << - grantor.c_str() << roleName.c_str()); + status_exception::raise(Arg::PrivateDyn(r ? 189 : 190) << grantor.c_str() << roleName.c_str()); } } @@ -11620,7 +11684,6 @@ void GrantRevokeNode::setFieldClassName(thread_db* tdbb, jrd_tra* transaction, END_FOR } - //---------------------- diff --git a/src/dsql/DdlNodes.h b/src/dsql/DdlNodes.h index 24d232e8f8..af4b5e5810 100644 --- a/src/dsql/DdlNodes.h +++ b/src/dsql/DdlNodes.h @@ -405,7 +405,7 @@ public: bool compiled; bool invalid; Firebird::MetaName package; - Firebird::string packageOwner; + Firebird::MetaName packageOwner; bool privateScope; bool preserveDefaults; SLONG udfReturnPos; @@ -539,7 +539,7 @@ public: bool compiled; bool invalid; Firebird::MetaName package; - Firebird::string packageOwner; + Firebird::MetaName packageOwner; bool privateScope; bool preserveDefaults; }; @@ -2118,6 +2118,7 @@ public: isGrant(aIsGrant), privileges(p), roles(p), + defaultRoles(p), object(NULL), users(p), grantAdminOption(false), @@ -2142,7 +2143,7 @@ protected: private: void modifyPrivileges(thread_db* tdbb, jrd_tra* transaction, SSHORT option, const GranteeClause* user); void grantRevoke(thread_db* tdbb, jrd_tra* transaction, const GranteeClause* object, - const GranteeClause* userNod, const char* privs, const Firebird::MetaName& field, int options); + const GranteeClause* userNod, const char* privs, Firebird::MetaName field, int options); static void checkGrantorCanGrant(thread_db* tdbb, jrd_tra* transaction, const char* grantor, const char* privilege, const Firebird::MetaName& relationName, const Firebird::MetaName& fieldName, bool topLevel); @@ -2197,6 +2198,7 @@ public: bool isGrant; Firebird::Array privileges; Firebird::Array roles; + Firebird::Array defaultRoles; NestConst object; Firebird::Array users; bool grantAdminOption; diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index f3cb83947a..876b58f24b 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -491,7 +491,7 @@ void CreateAlterPackageNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch* jrd_tra* transaction) { Attachment* attachment = transaction->getAttachment(); - const string& userName = attachment->att_user->usr_user_name; + const MetaName& userName = attachment->att_user->usr_user_name; executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_PACKAGE, name, NULL); diff --git a/src/dsql/PackageNodes.h b/src/dsql/PackageNodes.h index 6f2ef5781b..1b20255d1b 100644 --- a/src/dsql/PackageNodes.h +++ b/src/dsql/PackageNodes.h @@ -112,7 +112,7 @@ public: Firebird::SortedArray procedureNames; private: - Firebird::string owner; + Firebird::MetaName owner; }; diff --git a/src/dsql/StmtNodes.cpp b/src/dsql/StmtNodes.cpp index 2289e748ea..a71eb33ec5 100644 --- a/src/dsql/StmtNodes.cpp +++ b/src/dsql/StmtNodes.cpp @@ -7964,6 +7964,8 @@ void SetRoleNode::execute(thread_db* tdbb, dsql_req* request, jrd_tra** transact UserId* user = attachment->att_user; fb_assert(user); + user->usr_granted_roles.clear(); + if (trusted) { if (!user->usr_trusted_role.hasData()) @@ -7977,7 +7979,17 @@ void SetRoleNode::execute(thread_db* tdbb, dsql_req* request, jrd_tra** transact user->usr_sql_role_name = roleName.c_str(); } - if (SCL_admin_role(tdbb, user->usr_sql_role_name.c_str())) + if (!user->usr_sql_role_name.isEmpty() || user->usr_sql_role_name != NULL_ROLE) + { + // Add role itself and all roles cumulatively granted to it. Not only default. + user->usr_granted_roles.add(user->usr_sql_role_name); + SCL_find_granted_roles(tdbb, user->usr_sql_role_name, true, user->usr_granted_roles, false); + } + // Add all default roles granted to user + SCL_find_granted_roles(tdbb, user->usr_user_name, false, user->usr_granted_roles, true); + + + if (SCL_admin_role(tdbb, user->usr_granted_roles)) user->usr_flags |= USR_dba; else user->usr_flags &= ~USR_dba; diff --git a/src/dsql/parse.y b/src/dsql/parse.y index c9a9aa8481..c0e10b503f 100644 --- a/src/dsql/parse.y +++ b/src/dsql/parse.y @@ -589,6 +589,10 @@ using namespace Firebird; %token REGR_SXY %token REGR_SYY +// tokens added for Firebird 4.0 + +%token RDB_ROLE_IN_USE + // precedence declarations for expression evaluation %left OR @@ -876,7 +880,7 @@ grant0($node) $node->grantor = $6; $node->isDdl = true; } - | role_name_list(NOTRIAL(&$node->roles)) TO role_grantee_list(NOTRIAL(&$node->users)) + | role_name_list(NOTRIAL($node)) TO role_grantee_list(NOTRIAL(&$node->users)) role_admin_option granted_by { $node->grantAdminOption = $4; @@ -1133,7 +1137,7 @@ revoke0($node) $node->grantor = $6; $node->isDdl = true; } - | rev_admin_option role_name_list(NOTRIAL(&$node->roles)) + | rev_admin_option role_name_list(NOTRIAL($node)) FROM role_grantee_list(NOTRIAL(&$node->users)) granted_by { $node->grantAdminOption = $1; @@ -1191,16 +1195,24 @@ user_grantee($granteeArray) { $granteeArray->add(GranteeClause(obj_user_group, *$2)); } ; -%type role_name_list() -role_name_list($granteeArray) - : role_name($granteeArray) - | role_name_list ',' role_name($granteeArray) +%type role_name_list() +role_name_list($grantRevokeNode) + : role_name($grantRevokeNode) + | role_name_list ',' role_name($grantRevokeNode) ; -%type role_name() -role_name($granteeArray) +%type role_name() +role_name($grantRevokeNode) : symbol_role_name - { $granteeArray->add(GranteeClause(obj_sql_role, *$1)); } + { + $grantRevokeNode->roles.add(GranteeClause(obj_sql_role, *$1)); + $grantRevokeNode->defaultRoles.add(false); + } + | DEFAULT symbol_role_name + { + $grantRevokeNode->roles.add(GranteeClause(obj_sql_role, *$2)); + $grantRevokeNode->defaultRoles.add(true); + } ; %type role_grantee_list() @@ -1211,10 +1223,11 @@ role_grantee_list($granteeArray) %type role_grantee() role_grantee($granteeArray) - : grantor { $granteeArray->add(GranteeClause(obj_user, *$1)); } + : symbol_user_name { $granteeArray->add(GranteeClause(obj_user_or_role, *$1)); } + | USER symbol_user_name { $granteeArray->add(GranteeClause(obj_user, *$2)); } + | ROLE symbol_user_name { $granteeArray->add(GranteeClause(obj_sql_role, *$2)); } ; - // DECLARE operations %type declare @@ -7093,6 +7106,7 @@ system_function_std_syntax | TANH | TRUNC | UUID_TO_CHAR + | RDB_ROLE_IN_USE ; %type system_function_special_syntax @@ -7783,6 +7797,7 @@ non_reserved_word | SERVERWIDE | INCREMENT | TRUSTED + | RDB_ROLE_IN_USE // added in FB 4.0 ; %% diff --git a/src/jrd/DbCreators.cpp b/src/jrd/DbCreators.cpp index 1508901039..18b72e5d94 100644 --- a/src/jrd/DbCreators.cpp +++ b/src/jrd/DbCreators.cpp @@ -102,8 +102,8 @@ bool openDb(const char* securityDb, RefPtr& att, RefPtratt_user->usr_user_name; + const MetaName& user_name = attachment->att_user->usr_user_name; const bool locksmith = attachment->locksmith(); const char* user_name_ptr = locksmith ? NULL : user_name.c_str(); @@ -1240,7 +1240,7 @@ void Monitoring::dumpAttachment(thread_db* tdbb, Attachment* attachment) attachment->mergeStats(); const AttNumber att_id = attachment->att_attachment_id; - const string& user_name = attachment->att_user->usr_user_name; + const MetaName& user_name = attachment->att_user->usr_user_name; if (!dbb->dbb_monitoring_data) dbb->dbb_monitoring_data = FB_NEW_POOL(pool) MonitoringData(dbb); @@ -1309,7 +1309,7 @@ void Monitoring::publishAttachment(thread_db* tdbb) if (!dbb->dbb_monitoring_data) dbb->dbb_monitoring_data = FB_NEW_POOL(*dbb->dbb_permanent) MonitoringData(dbb); - const string& user_name = attachment->att_user->usr_user_name; + const MetaName& user_name = attachment->att_user->usr_user_name; MonitoringData::Guard guard(dbb->dbb_monitoring_data); dbb->dbb_monitoring_data->setup(attachment->att_attachment_id, user_name.c_str()); diff --git a/src/jrd/SysFunction.cpp b/src/jrd/SysFunction.cpp index 1d95df9224..d512f84b14 100644 --- a/src/jrd/SysFunction.cpp +++ b/src/jrd/SysFunction.cpp @@ -187,6 +187,7 @@ dsc* evlSign(thread_db* tdbb, const SysFunction* function, const NestValueArray& dsc* evlSqrt(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure); dsc* evlTrunc(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure); dsc* evlUuidToChar(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure); +dsc* evlRoleInUse(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure); // System context function names @@ -521,6 +522,17 @@ void makeLongResult(DataTypeUtilBase*, const SysFunction*, dsc* result, result->setNullable(isNullable); } +void makeBooleanResult(DataTypeUtilBase*, const SysFunction*, dsc* result, + int argsCount, const dsc** args) +{ + result->makeBoolean(0); + + bool isNullable; + if (initResult(result, argsCount, args, &isNullable)) + return; + + result->setNullable(isNullable); +} /*** * This function doesn't work yet, because makeFromListResult isn't totally prepared for blobs vs strings. @@ -2222,14 +2234,14 @@ dsc* evlGetContext(thread_db* tdbb, const SysFunction*, const NestValueArray& ar if (!attachment->att_user || attachment->att_user->usr_user_name.isEmpty()) return NULL; - resultStr = attachment->att_user->usr_user_name; + resultStr = attachment->att_user->usr_user_name.c_str(); } else if (nameStr == CURRENT_ROLE_NAME) { if (!attachment->att_user || attachment->att_user->usr_sql_role_name.isEmpty()) return NULL; - resultStr = attachment->att_user->usr_sql_role_name; + resultStr = attachment->att_user->usr_sql_role_name.c_str(); } else if (nameStr == TRANSACTION_ID_NAME) resultStr.printf("%" SQUADFORMAT, transaction->tra_number); @@ -3776,6 +3788,29 @@ dsc* evlUuidToChar(thread_db* tdbb, const SysFunction* function, const NestValue return &impure->vlu_desc; } +dsc* evlRoleInUse(thread_db* tdbb, const SysFunction*, const NestValueArray& args, + impure_value* impure) +{ + fb_assert(args.getCount() == 1); + + jrd_req* request = tdbb->getRequest(); + Jrd::Attachment* attachment = tdbb->getAttachment(); + + const dsc* value = EVL_expr(tdbb, request, args[0]); + if (request->req_flags & req_null) // return NULL if value is NULL + return NULL; + + string roleStr(MOV_make_string2(tdbb, value, ttype_none)); + roleStr.upper(); + + impure->vlu_misc.vlu_uchar = attachment->att_user->usr_granted_roles.exist(roleStr) ? 1 : 0; + + impure->vlu_desc.makeBoolean(&impure->vlu_misc.vlu_uchar); + + return &impure->vlu_desc; +} + + } // anonymous namespace @@ -3840,6 +3875,7 @@ const SysFunction SysFunction::functions[] = {"TANH", 1, 1, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfTanh}, {"TRUNC", 1, 2, setParamsRoundTrunc, makeTrunc, evlTrunc, NULL}, {"UUID_TO_CHAR", 1, 1, setParamsUuidToChar, makeUuidToChar, evlUuidToChar, NULL}, + {"RDB$ROLE_IN_USE", 1, 1, setParamsAsciiVal, makeBooleanResult, evlRoleInUse, NULL}, {"", 0, 0, NULL, NULL, NULL, NULL} }; diff --git a/src/jrd/drq.h b/src/jrd/drq.h index 03f2f31d3e..803208d1d5 100644 --- a/src/jrd/drq.h +++ b/src/jrd/drq.h @@ -232,8 +232,9 @@ enum drq_type_t drq_e_gfld_prvs, // erase domain privileges drq_g_nxt_nbakhist_id, // generate next history ID for nbackup drq_l_index_relname, // lookup relation name for index - drq_l_trigger_relname, // loopup relation name for trigger - drq_l_grant_option, // loopup grant option for privilege + drq_l_trigger_relname, // lookup relation name for trigger + drq_l_grant_option, // lookup grant option for privilege + drq_l_granted_roles, // lookup granted roles drq_MAX }; diff --git a/src/jrd/extds/ExtDS.cpp b/src/jrd/extds/ExtDS.cpp index 473d9d582f..216c2579d6 100644 --- a/src/jrd/extds/ExtDS.cpp +++ b/src/jrd/extds/ExtDS.cpp @@ -312,7 +312,7 @@ Connection::~Connection() } void Connection::generateDPB(thread_db* tdbb, ClumpletWriter& dpb, - const string& user, const string& pwd, const string& role) const + const MetaName& user, const string& pwd, const MetaName& role) const { dpb.reset(isc_dpb_version1); @@ -346,7 +346,7 @@ void Connection::generateDPB(thread_db* tdbb, ClumpletWriter& dpb, } bool Connection::isSameDatabase(thread_db* tdbb, const PathName& dbName, - const string& user, const string& pwd, const string& role) const + const MetaName &user, const string& pwd, const MetaName &role) const { if (m_dbName != dbName) return false; diff --git a/src/jrd/extds/ExtDS.h b/src/jrd/extds/ExtDS.h index 765929b14c..12cbadfe2c 100644 --- a/src/jrd/extds/ExtDS.h +++ b/src/jrd/extds/ExtDS.h @@ -156,8 +156,8 @@ public: Provider* getProvider() { return &m_provider; } virtual void attach(Jrd::thread_db* tdbb, const Firebird::PathName& dbName, - const Firebird::string& user, const Firebird::string& pwd, - const Firebird::string& role) = 0; + const Firebird::MetaName& user, const Firebird::string& pwd, + const Firebird::MetaName& role) = 0; virtual void detach(Jrd::thread_db* tdbb); virtual bool cancelExecution() = 0; @@ -173,8 +173,8 @@ public: virtual bool isConnected() const = 0; virtual bool isSameDatabase(Jrd::thread_db* tdbb, const Firebird::PathName& dbName, - const Firebird::string& user, const Firebird::string& pwd, - const Firebird::string& role) const; + const Firebird::MetaName& user, const Firebird::string& pwd, + const Firebird::MetaName& role) const; bool isBroken() const { @@ -214,8 +214,8 @@ public: protected: void generateDPB(Jrd::thread_db* tdbb, Firebird::ClumpletWriter& dpb, - const Firebird::string& user, const Firebird::string& pwd, - const Firebird::string& role) const; + const Firebird::MetaName &user, const Firebird::string& pwd, + const Firebird::MetaName &role) const; virtual Transaction* doCreateTransaction() = 0; virtual Statement* doCreateStatement() = 0; diff --git a/src/jrd/extds/InternalDS.cpp b/src/jrd/extds/InternalDS.cpp index a92dd48239..dc8ee70fa4 100644 --- a/src/jrd/extds/InternalDS.cpp +++ b/src/jrd/extds/InternalDS.cpp @@ -136,8 +136,8 @@ private: }; void InternalConnection::attach(thread_db* tdbb, const PathName& dbName, - const string& user, const string& pwd, - const string& role) + const MetaName& user, const string& pwd, + const MetaName& role) { fb_assert(!m_attachment); Database* dbb = tdbb->getDatabase(); @@ -243,8 +243,8 @@ bool InternalConnection::isAvailable(thread_db* tdbb, TraScope /*traScope*/) con } bool InternalConnection::isSameDatabase(thread_db* tdbb, const PathName& dbName, - const string& user, const string& pwd, - const string& role) const + const MetaName& user, const string& pwd, + const MetaName& role) const { if (m_isCurrent) { diff --git a/src/jrd/extds/InternalDS.h b/src/jrd/extds/InternalDS.h index 94d9884f92..46304d4857 100644 --- a/src/jrd/extds/InternalDS.h +++ b/src/jrd/extds/InternalDS.h @@ -64,8 +64,8 @@ protected: public: virtual void attach(Jrd::thread_db* tdbb, const Firebird::PathName& dbName, - const Firebird::string& user, const Firebird::string& pwd, - const Firebird::string& role); + const Firebird::MetaName &user, const Firebird::string& pwd, + const Firebird::MetaName &role); virtual bool cancelExecution(); @@ -74,8 +74,8 @@ public: virtual bool isConnected() const { return (m_attachment != 0); } virtual bool isSameDatabase(Jrd::thread_db* tdbb, const Firebird::PathName& dbName, - const Firebird::string& user, const Firebird::string& pwd, - const Firebird::string& role) const; + const Firebird::MetaName &user, const Firebird::string& pwd, + const Firebird::MetaName &role) const; bool isCurrent() const { return m_isCurrent; } diff --git a/src/jrd/extds/IscDS.cpp b/src/jrd/extds/IscDS.cpp index 6065d4645f..38257b6e05 100644 --- a/src/jrd/extds/IscDS.cpp +++ b/src/jrd/extds/IscDS.cpp @@ -111,8 +111,8 @@ IscConnection::~IscConnection() { } -void IscConnection::attach(thread_db* tdbb, const PathName& dbName, const string& user, - const string& pwd, const string& role) +void IscConnection::attach(thread_db* tdbb, const PathName& dbName, const MetaName &user, + const string& pwd, const MetaName &role) { m_dbName = dbName; generateDPB(tdbb, m_dpb, user, pwd, role); diff --git a/src/jrd/extds/IscDS.h b/src/jrd/extds/IscDS.h index 19600f164b..dd3abb95e5 100644 --- a/src/jrd/extds/IscDS.h +++ b/src/jrd/extds/IscDS.h @@ -511,8 +511,8 @@ public: FB_API_HANDLE& getAPIHandle() { return m_handle; } virtual void attach(Jrd::thread_db* tdbb, const Firebird::PathName& dbName, - const Firebird::string& user, const Firebird::string& pwd, - const Firebird::string& role); + const Firebird::MetaName& user, const Firebird::string& pwd, + const Firebird::MetaName& role); virtual bool cancelExecution(); diff --git a/src/jrd/irq.h b/src/jrd/irq.h index 33571ca053..f21ca71951 100644 --- a/src/jrd/irq.h +++ b/src/jrd/irq.h @@ -105,7 +105,6 @@ enum irq_type_t irq_l_check, // lookup check constraint for trigger irq_l_check2, // lookup constraint for index irq_c_trg_perm, // check if trig can ignore perm. checks - irq_get_role_mem, // get SQL role membership irq_get_role_name, // get SQL role name irq_is_admin_role, // check is current role admin or not irq_get_att_class, // get security class for current attachment diff --git a/src/jrd/jrd.cpp b/src/jrd/jrd.cpp index 9afbeeb722..27fa45a274 100644 --- a/src/jrd/jrd.cpp +++ b/src/jrd/jrd.cpp @@ -1027,7 +1027,7 @@ static void rollback(thread_db*, jrd_tra*, const bool); static void purge_attachment(thread_db* tdbb, StableAttachmentPart* sAtt, unsigned flags = 0); static void getUserInfo(UserId&, const DatabaseOptions&, const char*, const char*, const RefPtr*, bool, ICryptKeyCallback*); -static void makeRoleName(Database*, string&, DatabaseOptions&); +static void makeRoleName(Database*, MetaName &, DatabaseOptions&); static THREAD_ENTRY_DECLARE shutdown_thread(THREAD_ENTRY_PARAM); @@ -1098,7 +1098,7 @@ static void successful_completion(CheckStatusWrapper* s, ISC_STATUS acceptCode = } -static void makeRoleName(Database* dbb, string& userIdRole, DatabaseOptions& options) +static void makeRoleName(Database* dbb, MetaName& userIdRole, DatabaseOptions& options) { if (userIdRole.isEmpty()) return; @@ -1300,7 +1300,7 @@ static void trace_failed_attach(TraceManager* traceManager, const char* filename } -void JRD_make_role_name(string& userIdRole, const int dialect) +void JRD_make_role_name(MetaName& userIdRole, const int dialect) { switch (dialect) { diff --git a/src/jrd/jrd_proto.h b/src/jrd/jrd_proto.h index 79751a2cf1..d7f660d06a 100644 --- a/src/jrd/jrd_proto.h +++ b/src/jrd/jrd_proto.h @@ -42,6 +42,10 @@ namespace Jrd { class dsql_req; } +namespace Firebird { + class MetaName; +} + void jrd_vtof(const char*, char*, SSHORT); typedef Firebird::SortedObjectsArray PathNameList; @@ -75,7 +79,7 @@ bool JRD_verify_database_access(const Firebird::PathName&); void JRD_shutdown_attachment(Jrd::Attachment* attachment); void JRD_shutdown_attachments(Jrd::Database* dbb); void JRD_cancel_operation(Jrd::thread_db* tdbb, Jrd::Attachment* attachment, int option); -void JRD_make_role_name(Firebird::string& userIdRole, const int dialect); +void JRD_make_role_name(Firebird::MetaName &userIdRole, const int dialect); bool JRD_shutdown_database(Jrd::Database* dbb, const unsigned flags = 0); // JRD_shutdown_database() flags diff --git a/src/jrd/scl.epp b/src/jrd/scl.epp index 27ba244eac..1672967ea9 100644 --- a/src/jrd/scl.epp +++ b/src/jrd/scl.epp @@ -1003,7 +1003,7 @@ bool SCL_role_granted(thread_db* tdbb, const UserId& usr, const TEXT* sql_role) return true; } - Firebird::string loginName(usr.usr_user_name); + Firebird::MetaName loginName(usr.usr_user_name); const TEXT* login_name = loginName.c_str(); bool found = false; @@ -1036,7 +1036,38 @@ bool SCL_role_granted(thread_db* tdbb, const UserId& usr, const TEXT* sql_role) } -bool SCL_admin_role(thread_db* tdbb, const TEXT* sql_role) +void SCL_find_granted_roles(thread_db* tdbb, const Firebird::MetaName& object, bool isRole, + Firebird::SortedArray& grantedRoles, bool defaultOnly) +{ + SET_TDBB(tdbb); + + Jrd::Attachment* const attachment = tdbb->getAttachment(); + + const int obj_type = isRole ? obj_sql_role : obj_user; + + AutoCacheRequest request(tdbb, drq_l_granted_roles, DYN_REQUESTS); + + FOR(REQUEST_HANDLE request) + U IN RDB$USER_PRIVILEGES WITH + U.RDB$USER EQ object.c_str() AND + U.RDB$USER_TYPE EQ obj_type AND + U.RDB$OBJECT_TYPE EQ obj_sql_role AND + U.RDB$PRIVILEGE EQ "M" + + const Firebird::MetaName roleName = U.RDB$RELATION_NAME; + const bool defaultRole = !defaultOnly || !U.RDB$FIELD_NAME.NULL && (U.RDB$FIELD_NAME[0] == 'D'); + + if (defaultRole && !grantedRoles.exist(roleName)) + { + grantedRoles.add(roleName); + SCL_find_granted_roles(tdbb, roleName, true, grantedRoles, defaultOnly); + } + + END_FOR +} + + +bool SCL_admin_role(thread_db* tdbb, const Firebird::SortedArray& roles) { /************************************** * @@ -1045,7 +1076,7 @@ bool SCL_admin_role(thread_db* tdbb, const TEXT* sql_role) ************************************** * * Functional description - * Check is sql_role is an admin role. + * Check if roles has an admin role. * **************************************/ SET_TDBB(tdbb); @@ -1053,15 +1084,19 @@ bool SCL_admin_role(thread_db* tdbb, const TEXT* sql_role) bool adminRole = false; - AutoCacheRequest request(tdbb, irq_is_admin_role, IRQ_REQUESTS); - - FOR(REQUEST_HANDLE request) R IN RDB$ROLES - WITH R.RDB$ROLE_NAME EQ sql_role - AND R.RDB$SYSTEM_FLAG != 0 + for (int i = 0; !adminRole && (i < roles.getCount()); i++) { - adminRole = true; + AutoCacheRequest request(tdbb, irq_is_admin_role, IRQ_REQUESTS); + + FOR(REQUEST_HANDLE request) R IN RDB$ROLES + WITH R.RDB$ROLE_NAME EQ roles[i].c_str() + AND R.RDB$SYSTEM_FLAG != 0 + { + adminRole = true; + break; + } + END_FOR } - END_FOR return adminRole; } @@ -1094,7 +1129,7 @@ void SCL_init(thread_db* tdbb, bool create, const UserId& tempId) { if (!create) { - Firebird::string loginName(tempId.usr_user_name); + Firebird::MetaName loginName(tempId.usr_user_name); const TEXT* login_name = loginName.c_str(); AutoCacheRequest request(tdbb, irq_get_role_name, IRQ_REQUESTS); @@ -1129,6 +1164,16 @@ void SCL_init(thread_db* tdbb, bool create, const UserId& tempId) if (!create) { + const bool isRole = role_name != NULL_ROLE; + if (isRole) + { + // Add role itself and all roles cumulatively granted to it. Not only default. + user->usr_granted_roles.add(user->usr_sql_role_name); + SCL_find_granted_roles(tdbb, user->usr_sql_role_name, true, user->usr_granted_roles, false); + } + // Add all default roles granted to user + SCL_find_granted_roles(tdbb, user->usr_user_name, false, user->usr_granted_roles, true); + AutoCacheRequest request(tdbb, irq_get_att_class, IRQ_REQUESTS); FOR(REQUEST_HANDLE request) X IN RDB$DATABASE @@ -1155,7 +1200,7 @@ void SCL_init(thread_db* tdbb, bool create, const UserId& tempId) if (dbb->dbb_owner == user->usr_user_name) user->usr_flags |= USR_owner; - if (sql_role && SCL_admin_role(tdbb, role_name.c_str())) + if (sql_role && SCL_admin_role(tdbb, user->usr_granted_roles)) user->usr_flags |= USR_dba; } else @@ -1388,9 +1433,27 @@ static bool check_string(const UCHAR* acl, const Firebird::MetaName& string) const FB_SIZE_T length = *acl++; const TEXT* const ptr = (TEXT*) acl; - return (string.compare(ptr, length) != 0); + return (string.compare(ptr, length) != 0); } +static void get_string(const UCHAR* acl, Firebird::MetaName& string) +{ +/************************************** + * + * g e t _ s t r i n g + * + ************************************** + * + * Functional description + * Get a string from acl string. + * + **************************************/ + fb_assert(acl); + + const size_t length = *acl++; + const TEXT* const ptr = (TEXT*) acl; + string.assign(ptr, length); +} static SecurityClass::flags_t compute_access(thread_db* tdbb, const SecurityClass* s_class, @@ -1477,8 +1540,7 @@ static SecurityClass::flags_t walk_acl(thread_db* tdbb, // Munch ACL. If we find a hit, eat up privileges. - UserId user = *tdbb->getAttachment()->att_user; - const TEXT* role_name = user.usr_sql_role_name.nullStr(); + UserId user = *attachment->att_user; if (view && (view->rel_flags & REL_sql_relation)) { @@ -1535,38 +1597,17 @@ static SecurityClass::flags_t walk_acl(thread_db* tdbb, case id_group: if (check_user_group(tdbb, a, user.usr_group_id)) - { hit = false; - } break; case id_sql_role: - if (!role_name || check_string(a, role_name)) + { + Firebird::MetaName role_name; + get_string(a, role_name); + if (!user.usr_granted_roles.exist(role_name)) hit = false; - else if (user.usr_sql_role_name != user.usr_trusted_role) - { - Firebird::string loginName(user.usr_user_name); - const TEXT* login_name = loginName.c_str(); - bool roleHit = false; - AutoCacheRequest request(tdbb, irq_get_role_mem, IRQ_REQUESTS); - - FOR(REQUEST_HANDLE request) U IN RDB$USER_PRIVILEGES WITH - (U.RDB$USER EQ login_name OR - U.RDB$USER EQ "PUBLIC") AND - U.RDB$USER_TYPE EQ obj_user AND - U.RDB$RELATION_NAME EQ user.usr_sql_role_name.c_str() AND - U.RDB$OBJECT_TYPE EQ obj_sql_role AND - U.RDB$PRIVILEGE EQ "M" - { - if (!U.RDB$USER.NULL) - roleHit = true; - } - END_FOR - - if (!roleHit) - hit = false; - } break; + } case id_view: if (!view || check_string(a, view->rel_name)) @@ -1578,9 +1619,7 @@ static SecurityClass::flags_t walk_acl(thread_db* tdbb, case id_trigger: case id_function: if (c != obj_type || check_string(a, obj_name)) - { hit = false; - } break; case id_views: @@ -1590,15 +1629,13 @@ static SecurityClass::flags_t walk_acl(thread_db* tdbb, // generated for SQL. hit = false; - if (!view) { + if (!view) hit = false; - } break; case id_user: - if (check_number(a, user.usr_user_id)) { + if (check_number(a, user.usr_user_id)) hit = false; - } break; case id_node: diff --git a/src/jrd/scl.h b/src/jrd/scl.h index 439b766f92..5869c66060 100644 --- a/src/jrd/scl.h +++ b/src/jrd/scl.h @@ -90,9 +90,10 @@ const USHORT USR_mapdown = 8; // Mapping failed when getting context class UserId { public: - Firebird::string usr_user_name; // User name - Firebird::string usr_sql_role_name; // Role name - Firebird::string usr_trusted_role; // Trusted role if set + Firebird::MetaName usr_user_name; // User name + Firebird::MetaName usr_sql_role_name; // Role name + Firebird::SortedArray usr_granted_roles; // Granted roles list + Firebird::MetaName usr_trusted_role; // Trusted role if set Firebird::string usr_project_name; // Project name Firebird::string usr_org_name; // Organization name Firebird::string usr_auth_method; // Authentication method @@ -108,11 +109,12 @@ public: UserId() : usr_user_id(0), usr_group_id(0), usr_flags(0) - { } + {} UserId(Firebird::MemoryPool& p, const UserId& ui) : usr_user_name(p, ui.usr_user_name), usr_sql_role_name(p, ui.usr_sql_role_name), + usr_granted_roles(p), usr_trusted_role(p, ui.usr_trusted_role), usr_project_name(p, ui.usr_project_name), usr_org_name(p, ui.usr_org_name), @@ -123,11 +125,13 @@ public: usr_flags(ui.usr_flags) { usr_auth_block.assign(ui.usr_auth_block); + usr_granted_roles = ui.usr_granted_roles; } UserId(const UserId& ui) : usr_user_name(ui.usr_user_name), usr_sql_role_name(ui.usr_sql_role_name), + usr_granted_roles(ui.usr_granted_roles), usr_trusted_role(ui.usr_trusted_role), usr_project_name(ui.usr_project_name), usr_org_name(ui.usr_org_name), @@ -143,6 +147,7 @@ public: { usr_user_name = ui.usr_user_name; usr_sql_role_name = ui.usr_sql_role_name; + usr_granted_roles = ui.usr_granted_roles; usr_trusted_role = ui.usr_trusted_role; usr_project_name = ui.usr_project_name; usr_org_name = ui.usr_org_name; diff --git a/src/jrd/scl_proto.h b/src/jrd/scl_proto.h index 5f8481f678..7363b48e99 100644 --- a/src/jrd/scl_proto.h +++ b/src/jrd/scl_proto.h @@ -57,7 +57,9 @@ void SCL_init(Jrd::thread_db* tdbb, bool, const Jrd::UserId& tempId); Jrd::SecurityClass* SCL_recompute_class(Jrd::thread_db*, const TEXT*); void SCL_release_all(Jrd::SecurityClassList*&); bool SCL_role_granted(Jrd::thread_db* tdbb, const Jrd::UserId& usr, const TEXT* sql_role); -bool SCL_admin_role(Jrd::thread_db* tdbb, const TEXT* sql_role); +void SCL_find_granted_roles(Jrd::thread_db* tdbb, const Firebird::MetaName& object, bool isRole, + Firebird::SortedArray &grantedRoles, bool defaultOnly); +bool SCL_admin_role(Jrd::thread_db* tdbb, const Firebird::SortedArray &roles); Jrd::SecurityClass::flags_t SCL_get_object_mask(const int object_type); namespace Jrd { diff --git a/src/msgs/messages2.sql b/src/msgs/messages2.sql index f7338e9a46..49ad036ae2 100644 --- a/src/msgs/messages2.sql +++ b/src/msgs/messages2.sql @@ -1970,6 +1970,7 @@ COMMIT WORK; ('dyn_cant_use_in_foreignkey', NULL, 'DdlNodes.epp', NULL, 8, 289, NULL, 'Can''t use @1 in FOREIGN KEY constraint', NULL, NULL); ('dyn_defvaldecl_package_func', 'CreatePackageBodyNode::execute', 'PackageNodes.epp', NULL, 8, 290, NULL, 'Default values for parameters are allowed only in declaration of packaged function @1.@2', NULL, NULL); ('dyn_create_user_no_password', 'CreateAlterUserNode', 'DdlNodes.epp', NULL, 8, 291, NULL, 'Password must be specified when creating user', NULL, NULL); +('dyn_cyclic_role', 'GrantRevokeNode::grantRevoke', 'DdlNodes.epp', NULL, 8, 292, NULL, 'role @1 can not be granted to role @2', NULL, NULL); COMMIT WORK; -- TEST (NULL, 'main', 'test.c', NULL, 11, 0, NULL, 'This is a modified text message', NULL, NULL); diff --git a/src/yvalve/keywords.cpp b/src/yvalve/keywords.cpp index 474ea391ad..e30d2468af 100644 --- a/src/yvalve/keywords.cpp +++ b/src/yvalve/keywords.cpp @@ -359,6 +359,7 @@ static const TOK tokens[] = {REVOKE, "REVOKE", 1, false}, {RIGHT, "RIGHT", 1, false}, {ROLE, "ROLE", 1, true}, + {RDB_ROLE_IN_USE, "RDB$ROLE_IN_USE", 2, false}, {ROLLBACK, "ROLLBACK", 1, false}, {ROUND, "ROUND", 2, false}, {ROW, "ROW", 2, false},