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:
parent
ab49785f7a
commit
238fff3a2d
53
doc/sql.extensions/README.cumulative_roles.txt
Normal file
53
doc/sql.extensions/README.cumulative_roles.txt
Normal 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;
|
@ -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;
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
||||
//----------------------
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -112,7 +112,7 @@ public:
|
||||
Firebird::SortedArray<Firebird::MetaName> procedureNames;
|
||||
|
||||
private:
|
||||
Firebird::string owner;
|
||||
Firebird::MetaName owner;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
;
|
||||
|
||||
%%
|
||||
|
@ -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 };
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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());
|
||||
|
@ -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}
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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; }
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
131
src/jrd/scl.epp
131
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<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:
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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},
|
||||
|
Loading…
Reference in New Issue
Block a user