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

Allow to create database with different owner (#7718)

* Allow to create database with different owner via API

* Support for OWNER clause in executeCreateDatabase()

* squash! Support for OWNER clause in executeCreateDatabase()

Fix rebase error
This commit is contained in:
Dimitry Sibiryakov 2023-11-20 16:02:59 +01:00 committed by GitHub
parent a0ff6b90a3
commit b495b3f174
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 89 additions and 21 deletions

View File

@ -618,3 +618,9 @@ ALTER PROCEDURE <name> SQL SECURITY {DEFINER | INVOKER} | DROP SQL SECURITY
(Alexander Zhdanov)
ALTER PACKAGE <name> SQL SECURITY {DEFINER | INVOKER} | DROP SQL SECURITY
27) Added OWNER clause to CREATE DATABASE statement.
(Dmitry Sibiryakov)
<db_initial_option> list is expanded by "OWNER username" clause which allows to set an owner user name for the created database.
Only users with administrator rights can use this option.

View File

@ -0,0 +1,6 @@
New Database Parameter Block items:
1. isc_dpb_owner :
Used for createDatabase() call to set the owner for the new database
to be different from user set with isc_user_name.
Can be used only by users with admin rights.

View File

@ -194,6 +194,7 @@ IntlParametersBlock::TagType IntlDpb::checkTag(UCHAR tag, const char** tagName)
FB_IPB_TAG(isc_dpb_process_name);
FB_IPB_TAG(isc_dpb_host_name);
FB_IPB_TAG(isc_dpb_os_user);
FB_IPB_TAG(isc_dpb_owner);
return TAG_STRING;
}

View File

@ -350,6 +350,7 @@ PARSER_TOKEN(TOK_OVER, "OVER", false)
PARSER_TOKEN(TOK_OVERFLOW, "OVERFLOW", true)
PARSER_TOKEN(TOK_OVERLAY, "OVERLAY", true)
PARSER_TOKEN(TOK_OVERRIDING, "OVERRIDING", true)
PARSER_TOKEN(TOK_OWNER, "OWNER", true)
PARSER_TOKEN(TOK_PACKAGE, "PACKAGE", true)
PARSER_TOKEN(TOK_PAD, "PAD", true)
PARSER_TOKEN(TOK_PAGE, "PAGE", true)

View File

@ -695,6 +695,7 @@ using namespace Firebird;
%token <metaNamePtr> TIMEZONE_NAME
%token <metaNamePtr> UNICODE_CHAR
%token <metaNamePtr> UNICODE_VAL
%token <metaNamePtr> OWNER
// tokens added for Firebird 6.0
@ -2155,6 +2156,8 @@ db_initial_option($alterDatabaseNode)
: PAGE_SIZE equals NUMBER32BIT
| USER symbol_user_name
| USER utf_string
| OWNER symbol_user_name
| OWNER utf_string
| ROLE valid_symbol_name
| ROLE utf_string
| PASSWORD utf_string
@ -9464,6 +9467,7 @@ non_reserved_word
// added in FB 6.0
| ANY_VALUE
| FORMAT
| OWNER
;
%%

View File

@ -132,6 +132,7 @@
#define isc_dpb_upgrade_db 97
#define isc_dpb_parallel_workers 100
#define isc_dpb_worker_attach 101
#define isc_dpb_owner 102
/**************************************************/

View File

@ -4044,6 +4044,7 @@ const
isc_dpb_upgrade_db = byte(97);
isc_dpb_parallel_workers = byte(100);
isc_dpb_worker_attach = byte(101);
isc_dpb_owner = byte(102);
isc_dpb_address = byte(1);
isc_dpb_addr_protocol = byte(1);
isc_dpb_addr_endpoint = byte(2);

View File

@ -104,11 +104,11 @@ bool openDb(const char* securityDb, RefPtr<IAttachment>& att, RefPtr<ITransactio
namespace Jrd {
bool checkCreateDatabaseGrant(const MetaString& userName, const MetaString& trustedRole,
CreateGrant checkCreateDatabaseGrant(const MetaString& userName, const MetaString& trustedRole,
const MetaString& sqlRole, const char* securityDb)
{
if (userName == DBA_USER_NAME)
return true;
return CreateGrant::ASSUMED;
RefPtr<IAttachment> att;
RefPtr<ITransaction> tra;
@ -169,10 +169,10 @@ bool checkCreateDatabaseGrant(const MetaString& userName, const MetaString& trus
role = trustedRole;
if (role == ADMIN_ROLE)
return true;
return CreateGrant::ASSUMED;
if (!hasDb)
return false;
return CreateGrant::NONE;
// check db creators table
Message gr;
@ -198,13 +198,13 @@ bool checkCreateDatabaseGrant(const MetaString& userName, const MetaString& trus
{
// isc_dsql_relation_err when exec SQL - i.e. table RDB$DB_CREATORS
// is missing due to non-FB3 security DB
return false;
return CreateGrant::NONE;
}
check("IAttachment::execute", &st);
}
if (cnt > 0)
return true;
return CreateGrant::GRANTED;
if (!role.hasData())
role = "NONE";
@ -247,7 +247,7 @@ bool checkCreateDatabaseGrant(const MetaString& userName, const MetaString& trus
check("IResultSet::fetchNext", &st);
return wrk.test(CREATE_DATABASE);
return wrk.test(CREATE_DATABASE) ? CreateGrant::GRANTED : CreateGrant::NONE;
}

View File

@ -36,7 +36,14 @@
namespace Jrd {
bool checkCreateDatabaseGrant(const Firebird::MetaString& userName, const Firebird::MetaString& trustedRole,
enum class CreateGrant
{
NONE, // This user cannot create databases
GRANTED, // User was granted privilege to create database
ASSUMED // User is admin and has all rights
};
CreateGrant checkCreateDatabaseGrant(const Firebird::MetaString& userName, const Firebird::MetaString& trustedRole,
const Firebird::MetaString& sqlRole, const char* securityDb);
class DbCreatorsScan: public VirtualTableScan

View File

@ -1111,6 +1111,7 @@ namespace Jrd
PathName dpb_set_bind;
string dpb_decfloat_round;
string dpb_decfloat_traps;
string dpb_owner;
public:
static const ULONG DPB_FLAGS_MASK = DBB_damaged;
@ -1337,7 +1338,7 @@ static void start_transaction(thread_db* tdbb, bool transliterate, jrd_tra** tr
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 RefPtr<const Config>*, bool, Mapping& mapping, bool);
const RefPtr<const Config>*, Mapping& mapping, bool);
static void waitForShutdown(Semaphore&);
static THREAD_ENTRY_DECLARE shutdown_thread(THREAD_ENTRY_PARAM);
@ -1353,7 +1354,7 @@ TraceFailedConnection::TraceFailedConnection(const char* filename, const Databas
{
Mapping mapping(Mapping::MAP_ERROR_HANDLER, NULL);
mapping.setAuthBlock(m_options->dpb_auth_block);
getUserInfo(m_id, *m_options, m_filename, NULL, false, mapping, false);
getUserInfo(m_id, *m_options, m_filename, NULL, mapping, false);
}
@ -1949,7 +1950,7 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch
try
{
mapping.setDb(filename, expanded_name.c_str(), jAtt);
getUserInfo(userId, options, filename, &config, false, mapping, options.dpb_reset_icu);
getUserInfo(userId, options, filename, &config, mapping, options.dpb_reset_icu);
}
catch(const Exception&)
{
@ -2847,7 +2848,41 @@ JAttachment* JProvider::createDatabase(CheckStatusWrapper* user_status, const ch
// Check for correct credentials supplied
mapping.setSecurityDbAlias(config->getSecurityDatabase(), nullptr);
getUserInfo(userId, options, filename, &config, true, mapping, false);
getUserInfo(userId, options, filename, &config, mapping, false);
// Check user's power level
CreateGrant powerLevel = CreateGrant::ASSUMED; // By default it is a boot build or embedded mode where everything is allowed
if (options.dpb_auth_block.hasData())
{
powerLevel = checkCreateDatabaseGrant(userId.getUserName(), userId.getTrustedRole(), userId.getSqlRole(), config->getSecurityDatabase());
}
switch (powerLevel)
{
case CreateGrant::NONE:
(Arg::Gds(isc_no_priv) << "CREATE" << "DATABASE" << filename).raise();
case CreateGrant::ASSUMED:
if (options.dpb_owner.hasData())
{
// Superuser can create databases for anyone other
fb_utils::dpbItemUpper(options.dpb_owner);
userId.setUserName(options.dpb_owner);
}
break;
case CreateGrant::GRANTED:
if (options.dpb_owner.hasData())
{
// Superuser can create databases for anyone other
fb_utils::dpbItemUpper(options.dpb_owner);
if (userId.getUserName() != options.dpb_owner)
{
(Arg::Gds(isc_no_priv) << "IMPERSONATE USER" << "DATABASE" << filename).raise();
}
}
break;
}
#ifdef WIN_NT
guardDbInit.enter(); // Required to correctly expand name of just created database
@ -7274,6 +7309,10 @@ void DatabaseOptions::get(const UCHAR* dpb, USHORT dpb_length, bool& invalid_cli
dpb_upgrade_db = true;
break;
case isc_dpb_owner:
getString(rdr, dpb_owner);
break;
default:
break;
}
@ -8543,13 +8582,12 @@ static VdnResult verifyDatabaseName(const PathName& name, FbStatusVector* status
@param aliasName
@param dbName
@param config
@param creating
@param iAtt
@param cryptCb
**/
static void getUserInfo(UserId& user, const DatabaseOptions& options, const char* aliasName,
const RefPtr<const Config>* config, bool creating, Mapping& mapping, bool icuReset)
const RefPtr<const Config>* config, Mapping& mapping, bool icuReset)
{
bool wheel = false;
int id = -1, group = -1; // CVC: This var contained trash
@ -8580,12 +8618,6 @@ static void getUserInfo(UserId& user, const DatabaseOptions& options, const char
if (mapping.mapUser(name, trusted_role) & Mapping::MAP_DOWN)
user.setFlag(USR_mapdown);
if (creating && config) // when config is NULL we are in error handler
{
if (!checkCreateDatabaseGrant(name, trusted_role, options.dpb_role_name, (*config)->getSecurityDatabase()))
(Arg::Gds(isc_no_priv) << "CREATE" << "DATABASE" << aliasName).raise();
}
}
else
{

View File

@ -47,7 +47,8 @@ enum pp_vals {
PP_PAGE = 9,
PP_SET = 10,
PP_NAMES = 11,
PP_ROLE = 12
PP_ROLE = 12,
PP_OWNER = 13
};
@ -76,6 +77,7 @@ static const pp_table pp_symbols[] =
{"SET", PP_SET},
{"NAMES", PP_NAMES},
{"ROLE", PP_ROLE},
{"OWNER", PP_OWNER},
{"", 0}
};
@ -310,6 +312,13 @@ bool PREPARSE_execute(CheckStatusWrapper* status, Why::YAttachment** ptrAtt,
case PP_PAGES:
matched = true;
break;
case PP_OWNER:
token = getToken(pos, tks);
dpb.insertString(isc_dpb_owner, token);
matched = true;
break;
} // switch
} // if
} // for