8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-22 16:03: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) (Alexander Zhdanov)
ALTER PACKAGE <name> SQL SECURITY {DEFINER | INVOKER} | DROP SQL SECURITY 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_process_name);
FB_IPB_TAG(isc_dpb_host_name); FB_IPB_TAG(isc_dpb_host_name);
FB_IPB_TAG(isc_dpb_os_user); FB_IPB_TAG(isc_dpb_os_user);
FB_IPB_TAG(isc_dpb_owner);
return TAG_STRING; return TAG_STRING;
} }

View File

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

View File

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

View File

@ -132,6 +132,7 @@
#define isc_dpb_upgrade_db 97 #define isc_dpb_upgrade_db 97
#define isc_dpb_parallel_workers 100 #define isc_dpb_parallel_workers 100
#define isc_dpb_worker_attach 101 #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_upgrade_db = byte(97);
isc_dpb_parallel_workers = byte(100); isc_dpb_parallel_workers = byte(100);
isc_dpb_worker_attach = byte(101); isc_dpb_worker_attach = byte(101);
isc_dpb_owner = byte(102);
isc_dpb_address = byte(1); isc_dpb_address = byte(1);
isc_dpb_addr_protocol = byte(1); isc_dpb_addr_protocol = byte(1);
isc_dpb_addr_endpoint = byte(2); isc_dpb_addr_endpoint = byte(2);

View File

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

View File

@ -1111,6 +1111,7 @@ namespace Jrd
PathName dpb_set_bind; PathName dpb_set_bind;
string dpb_decfloat_round; string dpb_decfloat_round;
string dpb_decfloat_traps; string dpb_decfloat_traps;
string dpb_owner;
public: public:
static const ULONG DPB_FLAGS_MASK = DBB_damaged; 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 rollback(thread_db*, jrd_tra*, const bool);
static void purge_attachment(thread_db* tdbb, StableAttachmentPart* sAtt, unsigned flags = 0); static void purge_attachment(thread_db* tdbb, StableAttachmentPart* sAtt, unsigned flags = 0);
static void getUserInfo(UserId&, const DatabaseOptions&, const char*, 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 void waitForShutdown(Semaphore&);
static THREAD_ENTRY_DECLARE shutdown_thread(THREAD_ENTRY_PARAM); 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 mapping(Mapping::MAP_ERROR_HANDLER, NULL);
mapping.setAuthBlock(m_options->dpb_auth_block); 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 try
{ {
mapping.setDb(filename, expanded_name.c_str(), jAtt); 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&) catch(const Exception&)
{ {
@ -2847,7 +2848,41 @@ JAttachment* JProvider::createDatabase(CheckStatusWrapper* user_status, const ch
// Check for correct credentials supplied // Check for correct credentials supplied
mapping.setSecurityDbAlias(config->getSecurityDatabase(), nullptr); 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 #ifdef WIN_NT
guardDbInit.enter(); // Required to correctly expand name of just created database 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; dpb_upgrade_db = true;
break; break;
case isc_dpb_owner:
getString(rdr, dpb_owner);
break;
default: default:
break; break;
} }
@ -8543,13 +8582,12 @@ static VdnResult verifyDatabaseName(const PathName& name, FbStatusVector* status
@param aliasName @param aliasName
@param dbName @param dbName
@param config @param config
@param creating
@param iAtt @param iAtt
@param cryptCb @param cryptCb
**/ **/
static void getUserInfo(UserId& user, const DatabaseOptions& options, const char* aliasName, 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; bool wheel = false;
int id = -1, group = -1; // CVC: This var contained trash 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) if (mapping.mapUser(name, trusted_role) & Mapping::MAP_DOWN)
user.setFlag(USR_mapdown); 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 else
{ {

View File

@ -47,7 +47,8 @@ enum pp_vals {
PP_PAGE = 9, PP_PAGE = 9,
PP_SET = 10, PP_SET = 10,
PP_NAMES = 11, 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}, {"SET", PP_SET},
{"NAMES", PP_NAMES}, {"NAMES", PP_NAMES},
{"ROLE", PP_ROLE}, {"ROLE", PP_ROLE},
{"OWNER", PP_OWNER},
{"", 0} {"", 0}
}; };
@ -310,6 +312,13 @@ bool PREPARSE_execute(CheckStatusWrapper* status, Why::YAttachment** ptrAtt,
case PP_PAGES: case PP_PAGES:
matched = true; matched = true;
break; break;
case PP_OWNER:
token = getToken(pos, tks);
dpb.insertString(isc_dpb_owner, token);
matched = true;
break;
} // switch } // switch
} // if } // if
} // for } // for