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

Implemented CORE-4538: Access rights for CREATE DATABASE operator

This commit is contained in:
alexpeshkoff 2014-09-02 16:55:14 +00:00
parent 45a5d66a32
commit 2a293c2691
24 changed files with 869 additions and 275 deletions

View File

@ -16,12 +16,12 @@ REVOKE [GRANT OPTION FOR] ALTER ANY <OBJECT> FROM [USER | ROLE] <user/role name>
REVOKE [GRANT OPTION FOR] DROP ANY <OBJECT> FROM [USER | ROLE] <user/role name>;
Where <OBJECT> could be:
TABLE, VIEW, PROCEDURE, FUNCTION, PACKAGE, GENERATOR, SEQUENCE, DOMAIN,
EXCEPTION, ROLE, DATABASE, CHARACTER SET, COLLATION, FILTER
TABLE, VIEW, PROCEDURE, FUNCTION, PACKAGE, GENERATOR, SEQUENCE, DOMAIN,
EXCEPTION, ROLE, CHARACTER SET, COLLATION, FILTER
Description:
Makes it possible to grant and revoke privileges on DDL operations.
Makes it possible to grant and revoke privileges on DDL operations.
DDL operations for managing triggers and indices re-use table privileges.
@ -31,5 +31,15 @@ If ANY keyword was used due GRANT operation it also must be used in according RE
Sample:
GRANT CREATE TABLE TO Joe;
GRANT ALTER ANY TABLE TO Joe;
GRANT ALTER ANY TABLE TO Joe;
REVOKE CREATE TABLE FROM Joe;
For database access special form is supported:
GRANT CREATE DATABASE TO [USER | ROLE] <user/role name>;
GRANT ALTER DATABASE TO [USER | ROLE] <user/role name> [WITH GRANT OPTION];
GRANT DROP DATABASE TO [USER | ROLE] <user/role name> [WITH GRANT OPTION];
REVOKE CREATE DATABASE FROM [USER | ROLE] <user/role name>;
REVOKE [GRANT OPTION FOR] ALTER DATABASE FROM [USER | ROLE] <user/role name>;
REVOKE [GRANT OPTION FOR] DROP DATABASE FROM [USER | ROLE] <user/role name>;

View File

@ -1604,6 +1604,12 @@ C --
PARAMETER (GDS__dsql_cant_grant_option = 335545095)
INTEGER*4 GDS__read_conflict
PARAMETER (GDS__read_conflict = 335545096)
INTEGER*4 GDS__crdb_load
PARAMETER (GDS__crdb_load = 335545097)
INTEGER*4 GDS__crdb_nodb
PARAMETER (GDS__crdb_nodb = 335545098)
INTEGER*4 GDS__crdb_notable
PARAMETER (GDS__crdb_notable = 335545099)
INTEGER*4 GDS__gfix_db_name
PARAMETER (GDS__gfix_db_name = 335740929)
INTEGER*4 GDS__gfix_invalid_sw

View File

@ -809,6 +809,9 @@ const
gds_dyn_no_priv = 335545094;
gds_dsql_cant_grant_option = 335545095;
gds_read_conflict = 335545096;
gds_crdb_load = 335545097;
gds_crdb_nodb = 335545098;
gds_crdb_notable = 335545099;
gds_gfix_db_name = 335740929;
gds_gfix_invalid_sw = 335740930;
gds_gfix_incmp_sw = 335740932;

View File

@ -113,6 +113,60 @@ static const char* const CHECK_CONSTRAINT_EXCEPTION = "check_constraint";
DATABASE DB = STATIC "ODS.RDB";
//----------------------
void ExecInSecurityDb::executeInSecurityDb(jrd_tra* localTransaction)
{
LocalStatus st;
SecDbContext* secDbContext = localTransaction->getSecDbContext();
if (!secDbContext)
{
Attachment* lAtt = localTransaction->getAttachment();
const char* secDb = lAtt->att_database->dbb_config->getSecurityDatabase();
ClumpletWriter dpb(ClumpletWriter::WideTagged, MAX_DPB_SIZE, isc_dpb_version2);
if (lAtt->att_user)
lAtt->att_user->populateDpb(dpb);
IAttachment* att = DispatcherPtr()->attachDatabase(&st, secDb,
dpb.getBufferLength(), dpb.getBuffer());
check(&st);
ITransaction* tra = att->startTransaction(&st, 0, NULL);
check(&st);
secDbContext = localTransaction->setSecDbContext(att, tra);
}
// run all statements under savepoint control
string savePoint;
savePoint.printf("ExecInSecurityDb%d", secDbContext->savePoint++);
secDbContext->att->execute(&st, secDbContext->tra, 0, ("SAVEPOINT " + savePoint).c_str(),
SQL_DIALECT_V6, NULL, NULL, NULL, NULL);
check(&st);
try
{
runInSecurityDb(secDbContext);
secDbContext->att->execute(&st, secDbContext->tra, 0, ("RELEASE SAVEPOINT " + savePoint).c_str(),
SQL_DIALECT_V6, NULL, NULL, NULL, NULL);
savePoint.erase();
check(&st);
}
catch (const Exception&)
{
if (savePoint.hasData())
{
LocalStatus tmp;
secDbContext->att->execute(&tmp, secDbContext->tra, 0, ("ROLLBACK TO SAVEPOINT " + savePoint).c_str(),
SQL_DIALECT_V6, NULL, NULL, NULL, NULL);
}
throw;
}
}
//----------------------
@ -9662,6 +9716,204 @@ bool MappingNode::checkPermission(thread_db* tdbb, jrd_tra* transaction)
return false;
}
void MappingNode::runInSecurityDb(SecDbContext* secDbContext)
{
Firebird::LocalStatus st;
IStatus* s = &st;
// first of all try to use regenerated DDL statement
// that's the best way if security database is FB3 or higher fb version
string ddl;
switch(op)
{
case MAP_ADD:
ddl = "CREATE MAPPING ";
break;
case MAP_MOD:
ddl = "ALTER MAPPING ";
break;
case MAP_DROP:
ddl = "DROP MAPPING ";
break;
case MAP_RPL:
ddl = "CREATE OR ALTER MAPPING ";
break;
}
addItem(ddl, name.c_str());
if (op != MAP_DROP)
{
ddl += " USING ";
switch (mode)
{
case 'P':
if (!plugin)
ddl += "ANY PLUGIN ";
else
{
ddl += "PLUGIN ";
addItem(ddl, plugin->c_str());
ddl += ' ';
}
break;
case 'S':
ddl += "ANY PLUGIN SERVERWIDE ";
break;
case '*':
ddl += "* ";
break;
case 'M':
ddl += "MAPPING ";
break;
}
if (db)
{
ddl += "IN ";
addItem(ddl, db->c_str());
ddl += ' ';
}
if (fromType)
{
ddl += "FROM ";
if (!from)
ddl += "ANY ";
addItem(ddl, fromType->c_str());
ddl += ' ';
if (from)
{
addItem(ddl, from->getString().c_str());
ddl += ' ';
}
}
ddl += "TO ";
ddl += (role ? "ROLE" : "USER");
if (to)
{
ddl += ' ';
addItem(ddl, to->c_str());
}
}
// Now try to run DDL
secDbContext->att->execute(s, secDbContext->tra, 0, ddl.c_str(), SQL_DIALECT_V6,
NULL, NULL, NULL, NULL);
if (s->getStatus() & IStatus::FB_HAS_ERRORS)
{
try
{
// try direct access to rdb$auth_mapping table in secure db
LocalStatus s2;
s = &s2;
// check presence of such record in the table
Message msgCheck;
Field<Varying> nm(msgCheck, 1);
nm = name.c_str();
Message result;
Field<ISC_INT64> cnt(result);
const char* checkSql = "select count(*) from RDB$AUTH_MAPPING where RDB$MAP_NAME = ?";
secDbContext->att->execute(s, secDbContext->tra, 0, checkSql, SQL_DIALECT_V6,
msgCheck.getMetadata(), msgCheck.getBuffer(), result.getMetadata(), result.getBuffer());
check(s);
if (cnt > 1 && op != MAP_DROP)
ERRD_bugcheck("Database mapping misconfigured");
bool hasLine = cnt > 0;
switch(op)
{
case MAP_ADD:
if (hasLine)
(Arg::Gds(isc_map_already_exists) << name).raise();
break;
case MAP_MOD:
case MAP_DROP:
if (!hasLine)
(Arg::Gds(isc_map_not_exists) << name).raise();
break;
case MAP_RPL:
op = hasLine ? MAP_MOD : MAP_DROP;
break;
}
// Get ready to modify table
Message full;
Field<ISC_SHORT> toType(full);
Field<Varying> t(full, MAX_SQL_IDENTIFIER_LEN);
Field<Varying> usng2(full, 1);
Field<Varying> plug2(full, MAX_SQL_IDENTIFIER_LEN);
Field<Varying> d2(full, MAX_SQL_IDENTIFIER_LEN);
Field<Varying> type2(full, MAX_SQL_IDENTIFIER_LEN);
Field<Varying> f2(full, 255);
Field<Varying> nm2(full, MAX_SQL_IDENTIFIER_LEN);
toType = role ? 1 : 0;
if (to)
t = to->c_str();
usng2.set(1, &mode);
if (plugin)
plug2 = plugin->c_str();
if (db)
d2 = db->c_str();
if (fromType)
type2 = fromType->c_str();
if (from)
f2 = from->getString().c_str();
nm2 = name.c_str();
Message* msg = NULL;
const char* sql = NULL;
switch(op)
{
case MAP_ADD:
sql = "insert into RDB$AUTH_MAPPING(RDB$MAP_TO_TYPE, RDB$MAP_TO, RDB$MAP_USING, "
"RDB$MAP_PLUGIN, RDB$MAP_DB, RDB$MAP_FROM_TYPE, RDB$MAP_FROM, RDB$MAP_NAME) "
"values (?, ?, ?, ?, ?, ?, ?, ?)";
msg = &full;
break;
case MAP_MOD:
sql = "update RDB$AUTH_MAPPING set RDB$MAP_TO_TYPE = ?, RDB$MAP_TO = ?, "
"RDB$MAP_USING = ?, RDB$MAP_PLUGIN = ?, RDB$MAP_DB = ?, "
"RDB$MAP_FROM_TYPE = ?, RDB$MAP_FROM = ? "
"where RDB$MAP_NAME = ?";
msg = &full;
break;
case MAP_DROP:
sql = "delete from RDBAUTH_MAPPING where RDB$MAP_NAME = ?";
msg = &msgCheck;
break;
}
// Actual modification
fb_assert(sql && msg);
secDbContext->att->execute(s, secDbContext->tra, 0, sql, SQL_DIALECT_V6,
msg->getMetadata(), msg->getBuffer(), NULL, NULL);
check(s);
}
catch(const Exception& ex)
{
if (st.getStatus() & IStatus::FB_HAS_ERRORS)
{
const ISC_STATUS* stat2 = st.getErrors();
if (stat2[1] != isc_dsql_token_unk_err)
status_exception::raise(&st);
}
throw;
}
}
}
// It's purpose is to add/drop mapping from any security name to DB security object.
void MappingNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction)
{
@ -9670,235 +9922,7 @@ void MappingNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd
if (global)
{
LocalStatus st;
LocalStatus s2; // we will use it in DDL case and remember
IStatus* s = &st;
SecDbContext* secDbContext = transaction->getSecDbContext();
if (!secDbContext)
{
const char* secDb = tdbb->getDatabase()->dbb_config->getSecurityDatabase();
ClumpletWriter dpb(ClumpletWriter::WideTagged, MAX_DPB_SIZE, isc_dpb_version2);
if (tdbb->getAttachment()->att_user)
tdbb->getAttachment()->att_user->populateDpb(dpb);
IAttachment* att = DispatcherPtr()->attachDatabase(s, secDb,
dpb.getBufferLength(), dpb.getBuffer());
check(s);
ITransaction* tra = att->startTransaction(s, 0, NULL);
check(s);
secDbContext = transaction->setSecDbContext(att, tra);
}
// run all statements under savepoint control
string savePoint;
savePoint.printf("GLOBALMAP%d", secDbContext->savePoint++);
secDbContext->att->execute(s, secDbContext->tra, 0, ("SAVEPOINT " + savePoint).c_str(),
SQL_DIALECT_V6, NULL, NULL, NULL, NULL);
check(s);
try
{
// first of all try to use regenerated DDL statement
// that's the best way if security database is FB3 or higher fb version
string ddl;
switch(op)
{
case MAP_ADD:
ddl = "CREATE MAPPING ";
break;
case MAP_MOD:
ddl = "ALTER MAPPING ";
break;
case MAP_DROP:
ddl = "DROP MAPPING ";
break;
case MAP_RPL:
ddl = "CREATE OR ALTER MAPPING ";
break;
}
addItem(ddl, name.c_str());
if (op != MAP_DROP)
{
ddl += " USING ";
switch (mode)
{
case 'P':
if (!plugin)
ddl += "ANY PLUGIN ";
else
{
ddl += "PLUGIN ";
addItem(ddl, plugin->c_str());
ddl += ' ';
}
break;
case 'S':
ddl += "ANY PLUGIN SERVERWIDE ";
break;
case '*':
ddl += "* ";
break;
case 'M':
ddl += "MAPPING ";
break;
}
if (db)
{
ddl += "IN ";
addItem(ddl, db->c_str());
ddl += ' ';
}
if (fromType)
{
ddl += "FROM ";
if (!from)
ddl += "ANY ";
addItem(ddl, fromType->c_str());
ddl += ' ';
if (from)
{
addItem(ddl, from->getString().c_str());
ddl += ' ';
}
}
ddl += "TO ";
ddl += (role ? "ROLE" : "USER");
if (to)
{
ddl += ' ';
addItem(ddl, to->c_str());
}
}
// Now try to run DDL
secDbContext->att->execute(&s2, secDbContext->tra, 0, ddl.c_str(), SQL_DIALECT_V6,
NULL, NULL, NULL, NULL);
if (s2.getStatus() & IStatus::FB_HAS_ERRORS)
{
// try direct access to rdb$auth_mapping table in secure db
// check presence of such record in the table
Message msgCheck;
Field<Varying> nm(msgCheck, 1);
nm = name.c_str();
Message result;
Field<ISC_INT64> cnt(result);
const char* checkSql = "select count(*) from RDB$AUTH_MAPPING where RDB$MAP_NAME = ?";
secDbContext->att->execute(s, secDbContext->tra, 0, checkSql, SQL_DIALECT_V6,
msgCheck.getMetadata(), msgCheck.getBuffer(), result.getMetadata(), result.getBuffer());
check(s);
if (cnt > 1 && op != MAP_DROP)
ERRD_bugcheck("Database mapping misconfigured");
bool hasLine = cnt > 0;
switch(op)
{
case MAP_ADD:
if (hasLine)
(Arg::Gds(isc_map_already_exists) << name).raise();
break;
case MAP_MOD:
case MAP_DROP:
if (!hasLine)
(Arg::Gds(isc_map_not_exists) << name).raise();
break;
case MAP_RPL:
op = hasLine ? MAP_MOD : MAP_DROP;
break;
}
// Get ready to modify table
Message full;
Field<ISC_SHORT> toType(full);
Field<Varying> t(full, MAX_SQL_IDENTIFIER_LEN);
Field<Varying> usng2(full, 1);
Field<Varying> plug2(full, MAX_SQL_IDENTIFIER_LEN);
Field<Varying> d2(full, MAX_SQL_IDENTIFIER_LEN);
Field<Varying> type2(full, MAX_SQL_IDENTIFIER_LEN);
Field<Varying> f2(full, 255);
Field<Varying> nm2(full, MAX_SQL_IDENTIFIER_LEN);
toType = role ? 1 : 0;
if (to)
t = to->c_str();
usng2.set(1, &mode);
if (plugin)
plug2 = plugin->c_str();
if (db)
d2 = db->c_str();
if (fromType)
type2 = fromType->c_str();
if (from)
f2 = from->getString().c_str();
nm2 = name.c_str();
Message* msg = NULL;
const char* sql = NULL;
switch(op)
{
case MAP_ADD:
sql = "insert into RDB$AUTH_MAPPING(RDB$MAP_TO_TYPE, RDB$MAP_TO, RDB$MAP_USING, "
"RDB$MAP_PLUGIN, RDB$MAP_DB, RDB$MAP_FROM_TYPE, RDB$MAP_FROM, RDB$MAP_NAME) "
"values (?, ?, ?, ?, ?, ?, ?, ?)";
msg = &full;
break;
case MAP_MOD:
sql = "update RDB$AUTH_MAPPING set RDB$MAP_TO_TYPE = ?, RDB$MAP_TO = ?, "
"RDB$MAP_USING = ?, RDB$MAP_PLUGIN = ?, RDB$MAP_DB = ?, "
"RDB$MAP_FROM_TYPE = ?, RDB$MAP_FROM = ? "
"where RDB$MAP_NAME = ?";
msg = &full;
break;
case MAP_DROP:
sql = "delete from RDBAUTH_MAPPING where RDB$MAP_NAME = ?";
msg = &msgCheck;
break;
}
// Actual modification
fb_assert(sql && msg);
secDbContext->att->execute(s, secDbContext->tra, 0, sql, SQL_DIALECT_V6,
msg->getMetadata(), msg->getBuffer(), NULL, NULL);
check(s);
secDbContext->att->execute(s, secDbContext->tra, 0, ("RELEASE SAVEPOINT " + savePoint).c_str(),
SQL_DIALECT_V6, NULL, NULL, NULL, NULL);
savePoint.erase();
check(s);
}
}
catch (const Exception&)
{
if (savePoint.hasData())
{
secDbContext->att->execute(s, secDbContext->tra, 0, ("ROLLBACK TO SAVEPOINT " + savePoint).c_str(),
SQL_DIALECT_V6, NULL, NULL, NULL, NULL);
}
if (s2.getStatus() & IStatus::FB_HAS_ERRORS)
{
const ISC_STATUS* stat2 = s2.getErrors();
if (stat2[1] != isc_dsql_token_unk_err)
status_exception::raise(&s2);
}
throw;
}
executeInSecurityDb(transaction);
return;
}
@ -10319,6 +10343,8 @@ void GrantRevokeNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
// run all statements under savepoint control
AutoSavePoint savePoint(tdbb, transaction);
createDbJobs.clear();
const GranteeClause* usersPtr;
const GranteeClause* usersEnd;
@ -10356,9 +10382,83 @@ void GrantRevokeNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
}
}
if (createDbJobs.hasData())
executeInSecurityDb(transaction);
savePoint.release(); // everything is ok
}
void GrantRevokeNode::runInSecurityDb(SecDbContext* secDbContext)
{
for (unsigned n = 0; n < createDbJobs.getCount(); ++n)
{
CreateDbJob& j = createDbJobs[n];
LocalStatus st;
IStatus* s = &st;
Message gr;
Field<ISC_SHORT> uType(gr);
Field<Varying> u(gr, MAX_SQL_IDENTIFIER_LEN);
uType = j.userType;
u = j.user.c_str();
Message result;
Field<ISC_INT64> cnt(result);
const char* checkSql = "select count(*) from RDB$DB_CREATORS where RDB$USER_TYPE = ? and RDB$USER = ?";
secDbContext->att->execute(s, secDbContext->tra, 0, checkSql, SQL_DIALECT_V6,
gr.getMetadata(), gr.getBuffer(), result.getMetadata(), result.getBuffer());
check(s);
if (isGrant)
{
if (!cnt)
{
const char* insertSql = "insert into RDB$DB_CREATORS(RDB$USER_TYPE, RDB$USER) values(?, ?)";
secDbContext->att->execute(s, secDbContext->tra, 0, insertSql, SQL_DIALECT_V6,
gr.getMetadata(), gr.getBuffer(), NULL, NULL);
check(s);
}
}
else
{
if (cnt)
{
const char* deleteSql = "delete from RDB$DB_CREATORS where RDB$USER_TYPE = ? and RDB$USER = ?";
secDbContext->att->execute(s, secDbContext->tra, 0, deleteSql, SQL_DIALECT_V6,
gr.getMetadata(), gr.getBuffer(), NULL, NULL);
j.grantErased = true;
}
if (!j.grantErased)
{
if (j.allOnAll)
{
const char* all = "ALL";
if (j.badGrantor)
{
// msg 246: @1 is not grantor of @2 on @3 to @4.
(Arg::PrivateDyn(246) << j.revoker.c_str() << all << all << j.user).raise();
}
// msg 247: Warning: @1 on @2 is not granted to @3.
ERR_post_warning(
Arg::Warning(isc_dyn_miss_priv_warning) <<
all << all << j.user);
}
else
{
// msg 247: Warning: @1 on @2 is not granted to @3.
ERR_post_warning(Arg::Warning(isc_dyn_miss_priv_warning) <<
privilegeName('C') << "DATABASE" << j.user);
}
}
}
}
}
void GrantRevokeNode::modifyPrivileges(thread_db* tdbb, jrd_tra* transaction, SSHORT option,
const GranteeClause* user)
{
@ -10467,8 +10567,9 @@ void GrantRevokeNode::grantRevoke(thread_db* tdbb, jrd_tra* transaction, const G
if (!isGrant && !privs) // REVOKE ALL ON ALL
{
AutoCacheRequest request(tdbb, drq_e_grant3, DYN_REQUESTS);
bool grantErased = false;
bool badGrantor = false;
CreateDbJob all(userType, user);
all.allOnAll = true;
all.revoker = grantorRevoker;
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
PRIV IN RDB$USER_PRIVILEGES
@ -10478,29 +10579,14 @@ void GrantRevokeNode::grantRevoke(thread_db* tdbb, jrd_tra* transaction, const G
if (tdbb->getAttachment()->att_user->locksmith() || grantorRevoker == PRIV.RDB$GRANTOR)
{
ERASE PRIV;
grantErased = true;
all.grantErased = true;
}
else
badGrantor = true;
all.badGrantor = true;
}
END_FOR
const char* all = "ALL";
if (badGrantor && !grantErased)
{
// msg 246: @1 is not grantor of @2 on @3 to @4.
status_exception::raise(Arg::PrivateDyn(246) <<
grantorRevoker.c_str() << all << all << user.c_str());
}
if (!grantErased)
{
// msg 247: Warning: @1 on @2 is not granted to @3.
ERR_post_warning(
Arg::Warning(isc_dyn_miss_priv_warning) <<
all << all << Arg::Str(user));
}
createDbJobs.push(all);
return;
}
@ -10527,6 +10613,30 @@ void GrantRevokeNode::grantRevoke(thread_db* tdbb, jrd_tra* transaction, const G
}
}
if (objType == obj_database && strchr(privileges, 'C'))
{
if (options || grantor)
{
(Arg::Gds(isc_wish_list) << Arg::Gds(isc_random) <<
"GRANT/ADMIN OPTION and GRANTED BY not supported for CREATE DATABASE grants").raise();
}
if (userType != obj_sql_role && userType != obj_user)
{
(Arg::Gds(isc_wish_list) << Arg::Gds(isc_random) <<
"Only grants to USER or ROLE are supported for CREATE DATABASE").raise();
}
CreateDbJob job(userType, user);
createDbJobs.push(job);
char* cPtr = strchr(privileges, 'C');
size_t len = strlen(cPtr);
memmove(cPtr, cPtr + 1, len);
if (!privileges[0])
return;
}
char priv[2];
priv[1] = '\0';

View File

@ -41,7 +41,7 @@ namespace Jrd {
class CompoundStmtNode;
class RelationSourceNode;
class ValueListNode;
class SecDbContext;
struct BoolSourceClause
{
@ -135,6 +135,18 @@ public:
};
class ExecInSecurityDb
{
public:
virtual ~ExecInSecurityDb() { }
void executeInSecurityDb(jrd_tra* tra);
protected:
virtual void runInSecurityDb(SecDbContext* secDbContext) = 0;
};
template <typename CreateNode, typename DropNode, ISC_STATUS ERROR_CODE>
class RecreateNode : public DdlNode
{
@ -1802,7 +1814,7 @@ public:
};
class MappingNode : public DdlNode
class MappingNode : public DdlNode, private ExecInSecurityDb
{
public:
enum OP {MAP_ADD, MAP_MOD, MAP_RPL, MAP_DROP};
@ -1836,6 +1848,7 @@ protected:
(op == MAP_ADD ? "CREATE" : op == MAP_MOD ?
"ALTER" : op == MAP_RPL ? "CREATE OR ALTER" : "DROP");
}
void runInSecurityDb(SecDbContext* secDbContext);
private:
void addItem(Firebird::string& ddl, const char* text);
@ -1974,11 +1987,12 @@ public:
typedef Firebird::Pair<Firebird::NonPooled<char, ValueListNode*> > PrivilegeClause;
typedef Firebird::Pair<Firebird::NonPooled<SSHORT, Firebird::MetaName> > GranteeClause;
class GrantRevokeNode : public DdlNode
class GrantRevokeNode : public DdlNode, private ExecInSecurityDb
{
public:
GrantRevokeNode(MemoryPool& p, bool aIsGrant)
: DdlNode(p),
createDbJobs(p),
isGrant(aIsGrant),
privileges(p),
roles(p),
@ -2001,6 +2015,7 @@ protected:
statusVector <<
Firebird::Arg::Gds(isGrant ? isc_dsql_grant_failed : isc_dsql_revoke_failed);
}
void runInSecurityDb(SecDbContext* secDbContext);
private:
void modifyPrivileges(thread_db* tdbb, jrd_tra* transaction, SSHORT option, const GranteeClause* user);
@ -2043,6 +2058,19 @@ private:
return "<Unknown>";
}
struct CreateDbJob
{
CreateDbJob(SSHORT a_userType, const Firebird::MetaName& a_user)
: allOnAll(false), grantErased(false), badGrantor(false),
userType(a_userType), user(a_user)
{ }
bool allOnAll, grantErased, badGrantor;
SSHORT userType;
Firebird::MetaName user, revoker;
};
Firebird::Array<CreateDbJob> createDbJobs;
public:
bool isGrant;
Firebird::Array<PrivilegeClause> privileges;

View File

@ -976,6 +976,7 @@ db_ddl_privilege_list($privilegeArray)
%type db_ddl_privilege(<privilegeArray>)
db_ddl_privilege($privilegeArray)
| CREATE { $privilegeArray->add(PrivilegeClause('C', NULL)); }
| ALTER { $privilegeArray->add(PrivilegeClause('L', NULL)); }
| DROP { $privilegeArray->add(PrivilegeClause('O', NULL)); }
;

View File

@ -798,6 +798,9 @@ static const struct {
{"dyn_no_priv", 335545094},
{"dsql_cant_grant_option", 335545095},
{"read_conflict", 335545096},
{"crdb_load", 335545097},
{"crdb_nodb", 335545098},
{"crdb_notable", 335545099},
{"gfix_db_name", 335740929},
{"gfix_invalid_sw", 335740930},
{"gfix_incmp_sw", 335740932},

View File

@ -832,6 +832,9 @@ const ISC_STATUS isc_dup_attribute = 335545093L;
const ISC_STATUS isc_dyn_no_priv = 335545094L;
const ISC_STATUS isc_dsql_cant_grant_option = 335545095L;
const ISC_STATUS isc_read_conflict = 335545096L;
const ISC_STATUS isc_crdb_load = 335545097L;
const ISC_STATUS isc_crdb_nodb = 335545098L;
const ISC_STATUS isc_crdb_notable = 335545099L;
const ISC_STATUS isc_gfix_db_name = 335740929L;
const ISC_STATUS isc_gfix_invalid_sw = 335740930L;
const ISC_STATUS isc_gfix_incmp_sw = 335740932L;
@ -1290,7 +1293,7 @@ const ISC_STATUS isc_trace_switch_user_only = 337182757L;
const ISC_STATUS isc_trace_switch_param_miss = 337182758L;
const ISC_STATUS isc_trace_param_act_notcompat = 337182759L;
const ISC_STATUS isc_trace_mandatory_switch_miss = 337182760L;
const ISC_STATUS isc_err_max = 1234;
const ISC_STATUS isc_err_max = 1237;
#else /* c definitions */
@ -2092,6 +2095,9 @@ const ISC_STATUS isc_err_max = 1234;
#define isc_dyn_no_priv 335545094L
#define isc_dsql_cant_grant_option 335545095L
#define isc_read_conflict 335545096L
#define isc_crdb_load 335545097L
#define isc_crdb_nodb 335545098L
#define isc_crdb_notable 335545099L
#define isc_gfix_db_name 335740929L
#define isc_gfix_invalid_sw 335740930L
#define isc_gfix_incmp_sw 335740932L
@ -2550,7 +2556,7 @@ const ISC_STATUS isc_err_max = 1234;
#define isc_trace_switch_param_miss 337182758L
#define isc_trace_param_act_notcompat 337182759L
#define isc_trace_mandatory_switch_miss 337182760L
#define isc_err_max 1234
#define isc_err_max 1237
#endif

View File

@ -660,3 +660,15 @@
const USHORT f_sec_map_to = 7;
// Relation 47 (RDB$DB_CREATORS)
const USHORT f_crt_user = 0;
const USHORT f_crt_u_type = 1;
// Relation 48 (SEC$DB_CREATORS)
const USHORT f_sec_crt_user = 0;
const USHORT f_sec_crt_u_type = 1;

View File

@ -801,6 +801,9 @@ Data source : @4"}, /* eds_statement */
{335545094, "There is no privilege for this operation"}, /* dyn_no_priv */
{335545095, "Using GRANT OPTION on @1 not allowed"}, /* dsql_cant_grant_option */
{335545096, "read conflicts with concurrent update"}, /* read_conflict */
{335545097, "@1 failed when working with CREATE DATABASE grants"}, /* crdb_load */
{335545098, "CREATE DATABASE grants check is not possible when database @1 is not present"}, /* crdb_nodb */
{335545099, "CREATE DATABASE grants check is not possible when table RDB$DB_CREATORS is not present in database @1"}, /* crdb_notable */
{335740929, "data base file name (@1) already given"}, /* gfix_db_name */
{335740930, "invalid switch @1"}, /* gfix_invalid_sw */
{335740932, "incompatible switch combination"}, /* gfix_incmp_sw */

View File

@ -797,6 +797,9 @@ static const struct {
{335545094, -901}, /* 774 dyn_no_priv */
{335545095, -901}, /* 775 dsql_cant_grant_option */
{335545096, -904}, /* 776 read_conflict */
{335545097, -901}, /* 777 crdb_load */
{335545098, -901}, /* 778 crdb_nodb */
{335545099, -901}, /* 779 crdb_notable */
{335740929, -901}, /* 1 gfix_db_name */
{335740930, -901}, /* 2 gfix_invalid_sw */
{335740932, -901}, /* 4 gfix_incmp_sw */

View File

@ -797,6 +797,9 @@ static const struct {
{335545094, "42000"}, // 774 dyn_no_priv
{335545095, "42000"}, // 775 dsql_cant_grant_option
{335545096, "40001"}, // 776 read_conflict
{335545097, "08004"}, // 777 crdb_load
{335545098, "0A000"}, // 778 crdb_nodb
{335545099, "0A000"}, // 779 crdb_notable
{335740929, "00000"}, // 1 gfix_db_name
{335740930, "00000"}, // 2 gfix_invalid_sw
{335740932, "00000"}, // 4 gfix_incmp_sw

View File

@ -1452,6 +1452,44 @@ processing_state SHOW_grants2 (const SCHAR* object,
return ps_ERR;
END_ERROR
if (obj_type == obj_database || obj_type == 255)
{
FOR PRV IN SEC$DB_CREATORS
SORTED BY PRV.SEC$USER_TYPE, PRV.SEC$USER
if (first && optional_msg)
isqlGlob.prints(optional_msg);
first = false;
fb_utils::exact_name(PRV.SEC$USER);
switch (PRV.SEC$USER_TYPE)
{
case obj_sql_role:
case obj_user:
if (mangle && isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
IUTILS_copy_SQL_id(PRV.SEC$USER, SQL_identifier, DBL_QUOTE);
else
strcpy(SQL_identifier, PRV.SEC$USER);
break;
default:
fb_assert(false);
strcpy(SQL_identifier, PRV.SEC$USER);
break;
}
set_grantee(PRV.SEC$USER_TYPE, SQL_identifier, user_string);
isqlGlob.printf("GRANT CREATE DATABASE TO %s%s%s",
user_string, terminator, NEWLINE);
END_FOR
ON_ERROR
ISQL_errmsg(fbStatus);
return ps_ERR;
END_ERROR
}
if (!first)
return (SKIP);
}

249
src/jrd/DbCreators.cpp Normal file
View File

@ -0,0 +1,249 @@
/*
* PROGRAM: JRD access method
* MODULE: DbCreators.cpp
* DESCRIPTION: Checks CREATE DATABASE right (in security.db)
*
* The contents of this file are subject to the Initial
* Developer's Public License Version 1.0 (the "License");
* you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
* http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
*
* Software distributed under the License is distributed AS IS,
* WITHOUT WARRANTY OF ANY KIND, either express or implied.
* See the License for the specific language governing rights
* and limitations under the License.
*
* The Original Code was created by Alex Peshkov
* for the Firebird Open Source RDBMS project.
*
* Copyright (c) 2014 Alex Peshkov <peshkoff at mail.ru>
* and all contributors signed below.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
*
*
*/
#include "firebird.h"
#include "firebird/Provider.h"
#include "../auth/SecureRemotePassword/Message.h"
#include "gen/iberror.h"
#include "../jrd/constants.h"
#include "../common/classes/ClumpletWriter.h"
#include "../common/classes/init.h"
#include "../common/classes/Hash.h"
#include "../common/classes/GenericMap.h"
#include "../common/classes/RefMutex.h"
#include "../common/classes/SyncObject.h"
#include "../common/classes/MetaName.h"
#include "../common/isc_s_proto.h"
#include "../common/isc_proto.h"
#include "../common/ThreadStart.h"
#include "../common/db_alias.h"
#include "../jrd/DbCreators.h"
#include "../jrd/tra.h"
#include "../jrd/ini.h"
#include "gen/ids.h"
#define DBC_DEBUG(A)
using namespace Firebird;
namespace {
void check(const char* s, IStatus* st)
{
if (!(st->getStatus() & IStatus::FB_HAS_ERRORS))
return;
Arg::StatusVector newStatus(st);
newStatus << Arg::Gds(isc_crdb_load) << s;
newStatus.raise();
}
bool openDb(const char* securityDb, RefPtr<IAttachment>& att, RefPtr<ITransaction>& tra)
{
DispatcherPtr prov;
ClumpletWriter embeddedSysdba(ClumpletWriter::Tagged, MAX_DPB_SIZE, isc_dpb_version1);
embeddedSysdba.insertString(isc_dpb_user_name, SYSDBA_USER_NAME, fb_strlen(SYSDBA_USER_NAME));
embeddedSysdba.insertByte(isc_dpb_sec_attach, TRUE);
embeddedSysdba.insertByte(isc_dpb_no_db_triggers, TRUE);
LocalStatus st;
att = prov->attachDatabase(&st, securityDb,
embeddedSysdba.getBufferLength(), embeddedSysdba.getBuffer());
if (st.getStatus() & IStatus::FB_HAS_ERRORS)
{
if (!fb_utils::containsErrorCode(st.getErrors(), isc_io_error))
check("IProvider::attachDatabase", &st);
// missing security DB - checking granted rights not possible
return false;
}
ClumpletWriter readOnly(ClumpletWriter::Tpb, MAX_DPB_SIZE, isc_tpb_version1);
readOnly.insertTag(isc_tpb_read);
readOnly.insertTag(isc_tpb_wait);
tra = att->startTransaction(&st, readOnly.getBufferLength(), readOnly.getBuffer());
check("IAttachment::startTransaction", &st);
return true;
}
} // anonymous namespace
namespace Jrd {
bool checkCreateDatabaseGrant(Firebird::string& userName, Firebird::string& trustedRole,
const char* securityDb)
{
if (userName == SYSDBA_USER_NAME || trustedRole == ADMIN_ROLE)
return true;
RefPtr<IAttachment> att;
RefPtr<ITransaction> tra;
if (!openDb(securityDb, att, tra))
return false;
Message gr;
Field<ISC_SHORT> uType(gr);
Field<Varying> u(gr, MAX_SQL_IDENTIFIER_LEN);
Field<ISC_SHORT> rType(gr);
Field<Varying> r(gr, MAX_SQL_IDENTIFIER_LEN);
uType = obj_user;
u = userName.c_str();
rType = trustedRole.hasData() ? obj_sql_role : 255;
r = trustedRole.c_str();
Message result;
Field<ISC_INT64> cnt(result);
LocalStatus st;
att->execute(&st, tra, 0,
"select count(*) from RDB$DB_CREATORS"
" where (RDB$USER_TYPE = ? and RDB$USER = ?) or (RDB$USER_TYPE = ? and RDB$USER = ?)",
SQL_DIALECT_V6, gr.getMetadata(), gr.getBuffer(), result.getMetadata(), result.getBuffer());
if (st.getStatus() & IStatus::FB_HAS_ERRORS)
{
if (fb_utils::containsErrorCode(st.getErrors(), isc_dsql_relation_err))
{
// isc_dsql_relation_err when exec SQL - i.e. table RDB$DB_CREATORS
// is missing due to non-FB3 security DB
return false;
}
check("IAttachment::execute", &st);
}
return cnt > 0;
}
const Format* DbCreatorsScan::getFormat(thread_db* tdbb, jrd_rel* relation) const
{
jrd_tra* const transaction = tdbb->getTransaction();
return transaction->getDbCreatorsList()->getList(tdbb, relation)->getFormat();
}
bool DbCreatorsScan::retrieveRecord(thread_db* tdbb, jrd_rel* relation,
FB_UINT64 position, Record* record) const
{
jrd_tra* const transaction = tdbb->getTransaction();
return transaction->getDbCreatorsList()->getList(tdbb, relation)->fetch(position, record);
}
DbCreatorsList::DbCreatorsList(jrd_tra* tra)
: SnapshotData(*tra->tra_pool)
{ }
RecordBuffer* DbCreatorsList::makeBuffer(thread_db* tdbb)
{
MemoryPool* const pool = tdbb->getTransaction()->tra_pool;
allocBuffer(tdbb, *pool, rel_sec_db_creators);
return getData(rel_sec_db_creators);
}
RecordBuffer* DbCreatorsList::getList(thread_db* tdbb, jrd_rel* relation)
{
fb_assert(relation);
fb_assert(relation->rel_id == rel_sec_db_creators);
RecordBuffer* buffer = getData(relation);
if (buffer)
{
return buffer;
}
RefPtr<IAttachment> att;
RefPtr<ITransaction> tra;
const char* dbName = tdbb->getDatabase()->dbb_config->getSecurityDatabase();
if (!openDb(dbName, att, tra))
{
// In embedded mode we are not raising any errors - silent return
if (MasterInterfacePtr()->serverMode(-1) < 0)
return makeBuffer(tdbb);
(Arg::Gds(isc_crdb_nodb) << dbName).raise();
}
Message gr;
Field<ISC_SHORT> uType(gr);
Field<Varying> u(gr, MAX_SQL_IDENTIFIER_LEN);
LocalStatus st;
RefPtr<IResultSet> curs(att->openCursor(&st, tra, 0,
"select RDB$USER_TYPE, RDB$USER from RDB$DB_CREATORS",
SQL_DIALECT_V6, NULL, NULL, gr.getMetadata(), NULL));
if (st.getStatus() & IStatus::FB_HAS_ERRORS)
{
if (!fb_utils::containsErrorCode(st.getErrors(), isc_dsql_relation_err))
check("IAttachment::openCursor", &st);
// isc_dsql_relation_err when opening cursor - i.e. table
// is missing due to non-FB3 security DB
// In embedded mode we are not raising any errors - silent return
if (MasterInterfacePtr()->serverMode(-1) < 0)
return makeBuffer(tdbb);
(Arg::Gds(isc_crdb_notable) << dbName).raise();
}
try
{
buffer = makeBuffer(tdbb);
while (curs->fetchNext(&st, gr.getBuffer()) == IStatus::FB_OK)
{
int charset = CS_METADATA;
Record* record = buffer->getTempRecord();
record->nullify();
putField(tdbb, record,
DumpField(f_sec_crt_user, VALUE_STRING, u->len, u->data),
charset);
SINT64 v = uType;
putField(tdbb, record,
DumpField(f_sec_crt_u_type, VALUE_INTEGER, sizeof(v), &v),
charset);
buffer->store(record);
}
check("IResultSet::fetchNext", &st);
}
catch (const Exception&)
{
clearSnapshot();
throw;
}
return getData(relation);
}
} // namespace Jrd

69
src/jrd/DbCreators.h Normal file
View File

@ -0,0 +1,69 @@
/*
* PROGRAM: JRD access method
* MODULE: DbCreators.h
* DESCRIPTION: GRANT CREATE DATABASE right (in security.db)
*
* The contents of this file are subject to the Initial
* Developer's Public License Version 1.0 (the "License");
* you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
* http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
*
* Software distributed under the License is distributed AS IS,
* WITHOUT WARRANTY OF ANY KIND, either express or implied.
* See the License for the specific language governing rights
* and limitations under the License.
*
* The Original Code was created by Alex Peshkov
* for the Firebird Open Source RDBMS project.
*
* Copyright (c) 2014 Alex Peshkov <peshkoff at mail.ru>
* and all contributors signed below.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
*
*
*/
#ifndef JRD_DB_CREATORS
#define JRD_DB_CREATORS
#include "../common/classes/alloc.h"
#include "../common/classes/fb_string.h"
#include "../jrd/recsrc/RecordSource.h"
#include "../jrd/Monitoring.h"
namespace Jrd {
bool checkCreateDatabaseGrant(Firebird::string& userName, Firebird::string& trustedRole,
const char* securityDb);
class DbCreatorsScan: public VirtualTableScan
{
public:
DbCreatorsScan(CompilerScratch* csb, const Firebird::string& alias,
StreamType stream, jrd_rel* relation)
: VirtualTableScan(csb, alias, stream, relation)
{}
protected:
const Format* getFormat(thread_db* tdbb, jrd_rel* relation) const;
bool retrieveRecord(thread_db* tdbb, jrd_rel* relation, FB_UINT64 position, Record* record) const;
};
class DbCreatorsList : public SnapshotData
{
public:
explicit DbCreatorsList(jrd_tra* tra);
RecordBuffer* getList(thread_db* tdbb, jrd_rel* relation);
private:
RecordBuffer* makeBuffer(thread_db* tdbb);
};
} // namespace Jrd
#endif // JRD_DB_CREATORS

View File

@ -126,6 +126,7 @@
#include "../jrd/DebugInterface.h"
#include "../jrd/EngineInterface.h"
#include "../jrd/CryptoManager.h"
#include "../jrd/DbCreators.h"
#include "../dsql/dsql.h"
#include "../dsql/dsql_proto.h"
@ -995,7 +996,7 @@ static void release_attachment(thread_db*, Jrd::Attachment*);
static void rollback(thread_db*, jrd_tra*, const bool);
static void strip_quotes(string&);
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>*);
static void getUserInfo(UserId&, const DatabaseOptions&, const char*, const char*, const RefPtr<Config>*, bool);
static THREAD_ENTRY_DECLARE shutdown_thread(THREAD_ENTRY_PARAM);
@ -1007,7 +1008,7 @@ TraceFailedConnection::TraceFailedConnection(const char* filename, const Databas
m_filename(filename),
m_options(options)
{
getUserInfo(m_id, *m_options, m_filename, NULL, NULL);
getUserInfo(m_id, *m_options, m_filename, NULL, NULL, false);
}
@ -1397,7 +1398,7 @@ JAttachment* FB_CARG JProvider::attachDatabase(IStatus* user_status, const char*
}
// Check for correct credentials supplied
getUserInfo(userId, options, org_filename.c_str(), expanded_name.c_str(), &config);
getUserInfo(userId, options, org_filename.c_str(), expanded_name.c_str(), &config, false);
#ifdef WIN_NT
guardDbInit.enter(); // Required to correctly expand name of just created database
@ -2409,7 +2410,7 @@ JAttachment* FB_CARG JProvider::createDatabase(IStatus* user_status, const char*
}
// Check for correct credentials supplied
getUserInfo(userId, options, org_filename.c_str(), NULL, &config);
getUserInfo(userId, options, org_filename.c_str(), NULL, &config, true);
#ifdef WIN_NT
guardDbInit.enter(); // Required to correctly expand name of just created database
@ -7055,9 +7056,8 @@ static VdnResult verifyDatabaseName(const PathName& name, ISC_STATUS* status, bo
getUserInfo
@brief Almost stub-like now.
Planned to take into an account mapping of users and groups.
Fills UserId structure with resulting values.
@brief Fills UserId structure with resulting values.
Takes into an account mapping of users and groups.
@param user
@param options
@ -7065,7 +7065,7 @@ static VdnResult verifyDatabaseName(const PathName& name, ISC_STATUS* status, bo
**/
static void getUserInfo(UserId& user, const DatabaseOptions& options,
const char* aliasName, const char* dbName, const RefPtr<Config>* config)
const char* aliasName, const char* dbName, const RefPtr<Config>* config, bool creating)
{
bool wheel = false;
int id = -1, group = -1; // CVC: This var contained trash
@ -7092,6 +7092,12 @@ static void getUserInfo(UserId& user, const DatabaseOptions& options,
aliasName, dbName, (config ? (*config)->getSecurityDatabase() : NULL));
ISC_systemToUtf8(name);
ISC_systemToUtf8(trusted_role);
if (creating && config) // when config is NULL we are in error handler
{
if (!checkCreateDatabaseGrant(name, trusted_role, (*config)->getSecurityDatabase()))
(Arg::Gds(isc_no_priv) << "CREATE" << "DATABASE" << aliasName).raise();
}
}
else
{

View File

@ -394,3 +394,8 @@ NAME("SEC$MAP_FROM_TYPE", nam_sec_map_from_type)
NAME("SEC$MAP_FROM", nam_sec_map_from)
NAME("SEC$MAP_TO_TYPE", nam_sec_map_to_type)
NAME("SEC$MAP_TO", nam_sec_map_to)
NAME("RDB$DB_CREATORS", nam_db_creators)
NAME("SEC$DB_CREATORS", nam_sec_db_creators)
NAME("SEC$USER", nam_sec_user)
NAME("SEC$USER_TYPE", nam_sec_user_type)

View File

@ -85,6 +85,7 @@
#include "../jrd/recsrc/RecordSource.h"
#include "../jrd/recsrc/Cursor.h"
#include "../jrd/Mapping.h"
#include "../jrd/DbCreators.h"
#include "../jrd/Optimizer.h"
#include "../dsql/BoolNodes.h"
@ -2264,17 +2265,24 @@ static RecordSource* gen_retrieval(thread_db* tdbb,
else if (relation->isVirtual())
{
// Virtual table: monitoring or security
if (relation->rel_id == rel_global_auth_mapping)
switch(relation->rel_id)
{
case rel_global_auth_mapping:
rsb = FB_NEW(*tdbb->getDefaultPool()) GlobalMappingScan(csb, alias, stream, relation);
}
else if (relation->rel_id == rel_sec_users || relation->rel_id == rel_sec_user_attributes)
{
break;
case rel_sec_users:
case rel_sec_user_attributes:
rsb = FB_NEW(*tdbb->getDefaultPool()) UsersTableScan(csb, alias, stream, relation);
}
else
{
break;
case rel_sec_db_creators:
rsb = FB_NEW(*tdbb->getDefaultPool()) DbCreatorsScan(csb, alias, stream, relation);
break;
default:
rsb = FB_NEW(*tdbb->getDefaultPool()) MonitoringTableScan(csb, alias, stream, relation);
break;
}
}
else

View File

@ -649,7 +649,7 @@ END_RELATION
// Relation 46 (SEC$GLOBAL_AUTH_MAPPING)
RELATION(nam_sec_global_auth_mapping, rel_global_auth_mapping, ODS_12_0, rel_virtual)
FIELD(f_sec_map_name, nam_sec_map_name, fld_map_name, 1, ODS_12_0)
FIELD(f_sec_map_name, nam_sec_map_name, fld_map_name, 0, ODS_12_0)
FIELD(f_sec_map_using, nam_sec_map_using, fld_map_using, 0, ODS_12_0)
FIELD(f_sec_map_plugin, nam_sec_map_plugin, fld_map_plugin, 0, ODS_12_0)
FIELD(f_sec_map_db, nam_sec_map_db, fld_map_db, 0, ODS_12_0)
@ -658,3 +658,15 @@ RELATION(nam_sec_global_auth_mapping, rel_global_auth_mapping, ODS_12_0, rel_vir
FIELD(f_sec_map_to_type, nam_sec_map_to_type, fld_obj_type, 0, ODS_12_0)
FIELD(f_sec_map_to, nam_sec_map_to, fld_map_to, 0, ODS_12_0)
END_RELATION
// Relation 47 (RDB$DB_CREATORS)
RELATION(nam_db_creators, rel_db_creators, ODS_12_0, rel_persistent)
FIELD(f_crt_user, nam_user, fld_user, 1, ODS_12_0)
FIELD(f_crt_u_type, nam_user_type, fld_obj_type, 1, ODS_12_0)
END_RELATION
// Relation 48 (SEC$DB_CREATORS)
RELATION(nam_sec_db_creators, rel_sec_db_creators, ODS_12_0, rel_virtual)
FIELD(f_sec_crt_user, nam_sec_user, fld_user, 0, ODS_12_0)
FIELD(f_sec_crt_u_type, nam_sec_user_type, fld_obj_type, 0, ODS_12_0)
END_RELATION

View File

@ -73,6 +73,7 @@
#include "../jrd/Function.h"
#include "../jrd/Collation.h"
#include "../jrd/Mapping.h"
#include "../jrd/DbCreators.h"
const int DYN_MSG_FAC = 8;
@ -3401,6 +3402,15 @@ MappingList* jrd_tra::getMappingList()
return tra_mapping_list;
}
DbCreatorsList* jrd_tra::getDbCreatorsList()
{
if (!tra_dbcreators_list)
{
tra_dbcreators_list = FB_NEW(*tra_pool) DbCreatorsList(this);
}
return tra_dbcreators_list;
}
jrd_tra* jrd_tra::getOuter()
{

View File

@ -71,6 +71,7 @@ class DeferredJob;
class dsql_opn;
class UserManagement;
class MappingList;
class DbCreatorsList;
class thread_db;
class SecDbContext
@ -303,6 +304,7 @@ private:
UserManagement* tra_user_management;
SecDbContext* tra_sec_db_context;
MappingList* tra_mapping_list;
DbCreatorsList* tra_dbcreators_list;
MemoryPool* tra_autonomous_pool;
USHORT tra_autonomous_cnt;
static const USHORT TRA_AUTONOMOUS_PER_POOL = 64;
@ -359,6 +361,7 @@ public:
SecDbContext* setSecDbContext(Firebird::IAttachment* att, Firebird::ITransaction* tra);
void eraseSecDbContext();
MappingList* getMappingList();
DbCreatorsList* getDbCreatorsList();
GenIdCache* getGenIdCache()
{

View File

@ -1,7 +1,7 @@
/* MAX_NUMBER is the next number to be used, always one more than the highest message number. */
set bulk_insert INSERT INTO FACILITIES (LAST_CHANGE, FACILITY, FAC_CODE, MAX_NUMBER) VALUES (?, ?, ?, ?);
--
('2014-08-27 16:38:00', 'JRD', 0, 777)
('2014-09-02 19:54:57', 'JRD', 0, 780)
('2012-01-23 20:10:30', 'QLI', 1, 532)
('2013-11-13 15:59:10', 'GFIX', 3, 122)
('1996-11-07 13:39:40', 'GPRE', 4, 1)

View File

@ -884,6 +884,9 @@ Data source : @4', NULL, NULL)
('dyn_no_priv', NULL, 'scl.epp', NULL, 0, 774, NULL, 'There is no privilege for this operation', NULL, NULL);
('dsql_cant_grant_option', NULL, 'DdlNodes.epp', NULL, 0, 775, NULL, 'Using GRANT OPTION on @1 not allowed', NULL, NULL);
('read_conflict', NULL, NULL, 'vio.cpp', 0, 776, NULL, 'read conflicts with concurrent update', NULL, NULL);
('crdb_load', 'check', 'DbCreators.cpp', NULL, 0, 777, NULL, '@1 failed when working with CREATE DATABASE grants', NULL, NULL);
('crdb_nodb', 'DbCreatorsList::getList', 'DbCreators.cpp', NULL, 0, 778, NULL, 'CREATE DATABASE grants check is not possible when database @1 is not present', NULL, NULL);
('crdb_notable', 'DbCreatorsList::getList', 'DbCreators.cpp', NULL, 0, 779, NULL, 'CREATE DATABASE grants check is not possible when table RDB$DB_CREATORS is not present in database @1', NULL, NULL);
-- QLI
(NULL, NULL, NULL, NULL, 1, 0, NULL, 'expected type', NULL, NULL);
(NULL, NULL, NULL, NULL, 1, 1, NULL, 'bad block type', NULL, NULL);

View File

@ -783,6 +783,9 @@ set bulk_insert INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, FA
(-901, '42', '000', 0, 774, 'dyn_no_priv', NULL, NULL)
(-901, '42', '000', 0, 775, 'dsql_cant_grant_option', NULL, NULL);
(-904, '40', '001', 0, 776, 'read_conflict', NULL, NULL);
(-901, '08', '004', 0, 777, 'crdb_load', NULL, NULL);
(-901, '0A', '000', 0, 778, 'crdb_nodb', NULL, NULL);
(-901, '0A', '000', 0, 779, 'crdb_notable', NULL, NULL);
-- GFIX
(-901, '00', '000', 3, 1, 'gfix_db_name', NULL, NULL)
(-901, '00', '000', 3, 2, 'gfix_invalid_sw', NULL, NULL)