8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-22 17:23:03 +01:00

Fixed CORE-1815: Ability to grant role to another role (#23)

* Initial patch for cumulative roles

* Fixed multiple records in USER_PRIVILEGES and reworked logic on additional grant default role and admin option
This commit is contained in:
Roman Simakov 2016-05-12 17:03:54 +03:00 committed by Alexander Peshkov
parent ab49785f7a
commit 238fff3a2d
27 changed files with 396 additions and 153 deletions

View File

@ -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] <role name> TO [USER | ROLE] <user/role name> [WITH ADMIN OPTION];
REVOKE [DEFAULT] <role name> FROM [USER | ROLE] <user/role name> [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;

View File

@ -332,6 +332,12 @@ namespace Firebird
{
}
ObjectsArray(const ObjectsArray<T, A>& o)
: A()
{
add(o);
}
ObjectsArray() :
A()
{
@ -436,6 +442,12 @@ namespace Firebird
ObjectCmp> >(p)
{ }
explicit SortedObjectsArray() :
ObjectsArray <ObjectValue, SortedArray<ObjectValue*,
ObjectStorage, const ObjectKey*, ObjectKeyOfValue,
ObjectCmp> >()
{ }
bool find(const ObjectKey& item, size_type& pos) const
{
const ObjectKey* const pItem = &item;

View File

@ -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<Firebird::MetaName> 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
}
//----------------------

View File

@ -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<PrivilegeClause> privileges;
Firebird::Array<GranteeClause> roles;
Firebird::Array<bool> defaultRoles;
NestConst<GranteeClause> object;
Firebird::Array<GranteeClause> users;
bool grantAdminOption;

View File

@ -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);

View File

@ -112,7 +112,7 @@ public:
Firebird::SortedArray<Firebird::MetaName> procedureNames;
private:
Firebird::string owner;
Firebird::MetaName owner;
};

View File

@ -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;

View File

@ -589,6 +589,10 @@ using namespace Firebird;
%token <metaNamePtr> REGR_SXY
%token <metaNamePtr> REGR_SYY
// tokens added for Firebird 4.0
%token <metaNamePtr> 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(<granteeArray>)
role_name_list($granteeArray)
: role_name($granteeArray)
| role_name_list ',' role_name($granteeArray)
%type role_name_list(<grantRevokeNode>)
role_name_list($grantRevokeNode)
: role_name($grantRevokeNode)
| role_name_list ',' role_name($grantRevokeNode)
;
%type role_name(<granteeArray>)
role_name($granteeArray)
%type role_name(<grantRevokeNode>)
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(<granteeArray>)
@ -1211,10 +1223,11 @@ role_grantee_list($granteeArray)
%type role_grantee(<granteeArray>)
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 <ddlNode> declare
@ -7093,6 +7106,7 @@ system_function_std_syntax
| TANH
| TRUNC
| UUID_TO_CHAR
| RDB_ROLE_IN_USE
;
%type <sysFuncCallNode> system_function_special_syntax
@ -7783,6 +7797,7 @@ non_reserved_word
| SERVERWIDE
| INCREMENT
| TRUSTED
| RDB_ROLE_IN_USE // added in FB 4.0
;
%%

View File

@ -102,8 +102,8 @@ bool openDb(const char* securityDb, RefPtr<IAttachment>& att, RefPtr<ITransactio
namespace Jrd {
bool checkCreateDatabaseGrant(const string& userName, const string& trustedRole,
const string& sqlRole, const char* securityDb)
bool checkCreateDatabaseGrant(const MetaName& userName, const MetaName& trustedRole,
const MetaName& sqlRole, const char* securityDb)
{
if (userName == SYSDBA_USER_NAME)
return true;
@ -114,7 +114,7 @@ bool checkCreateDatabaseGrant(const string& userName, const string& trustedRole,
return false;
FbLocalStatus st;
string role(sqlRole);
MetaName role(sqlRole);
if (role.hasData())
{
const UCHAR info[] = { isc_info_db_sql_dialect, isc_info_end };

View File

@ -36,8 +36,8 @@
namespace Jrd {
bool checkCreateDatabaseGrant(const Firebird::string& userName, const Firebird::string& trustedRole,
const Firebird::string& sqlRole, const char* securityDb);
bool checkCreateDatabaseGrant(const Firebird::MetaName &userName, const Firebird::MetaName &trustedRole,
const Firebird::MetaName &sqlRole, const char* securityDb);
class DbCreatorsScan: public VirtualTableScan
{

View File

@ -391,7 +391,7 @@ MonitoringSnapshot::MonitoringSnapshot(thread_db* tdbb, MemoryPool& pool)
// Enumerate active sessions
const string& user_name = attachment->att_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());

View File

@ -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}
};

View File

@ -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
};

View File

@ -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;

View File

@ -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;

View File

@ -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)
{

View File

@ -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; }

View File

@ -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);

View File

@ -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();

View File

@ -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

View File

@ -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<Config>*, 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)
{

View File

@ -42,6 +42,10 @@ namespace Jrd {
class dsql_req;
}
namespace Firebird {
class MetaName;
}
void jrd_vtof(const char*, char*, SSHORT);
typedef Firebird::SortedObjectsArray<Firebird::PathName> 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

View File

@ -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<Firebird::MetaName>& 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<Firebird::MetaName>& 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:

View File

@ -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<Firebird::MetaName> 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;

View File

@ -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<Firebird::MetaName> &grantedRoles, bool defaultOnly);
bool SCL_admin_role(Jrd::thread_db* tdbb, const Firebird::SortedArray<Firebird::MetaName> &roles);
Jrd::SecurityClass::flags_t SCL_get_object_mask(const int object_type);
namespace Jrd {

View File

@ -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);

View File

@ -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},