From 153067e8e20dc072a160ce3a903a41d372633f98 Mon Sep 17 00:00:00 2001 From: Vladislav Shpilevoy Date: Tue, 7 Nov 2023 21:58:01 +0100 Subject: [PATCH] storage: introduce user_management_mode cfg Closes #435 @TarantoolBot document Title: vshard: `user_management_mode` cfg option The option can be specified at the root level and regulates how much vshard is involved into the user management. When specified to 'auto' (default), vshard will create the user as specified in the config URIs, and will grant it all needed rights to public and internal APIs. When specified to 'manual', vshard will neither create the user, nor grant it any rights. It is all expected to be done externally outside of vshard. --- test/luatest_helpers/vtest.lua | 8 + .../user_management_mode_test.lua | 138 ++++++++++++++++++ test/unit-luatest/config_test.lua | 10 ++ vshard/cfg.lua | 4 + vshard/storage/init.lua | 26 ++-- 5 files changed, 176 insertions(+), 10 deletions(-) create mode 100644 test/storage-luatest/user_management_mode_test.lua diff --git a/test/luatest_helpers/vtest.lua b/test/luatest_helpers/vtest.lua index bfe5980a..0a074689 100644 --- a/test/luatest_helpers/vtest.lua +++ b/test/luatest_helpers/vtest.lua @@ -211,6 +211,14 @@ local function cluster_new(g, cfg) ivtest.clear_test_cfg_options(cfg) ivshard.storage.cfg(cfg, box.info.uuid) + if cfg.user_management_mode == 'manual' then + box.schema.user.create('storage', { + password = 'storage', + -- Yes, intentionally false to ensure that the user wasn't + -- created by vshard. + if_not_exists = false, + }) + end if grant_range ~= nil then box.schema.user.grant('storage', grant_range, nil, nil, {if_not_exists = true}) diff --git a/test/storage-luatest/user_management_mode_test.lua b/test/storage-luatest/user_management_mode_test.lua new file mode 100644 index 00000000..bb022984 --- /dev/null +++ b/test/storage-luatest/user_management_mode_test.lua @@ -0,0 +1,138 @@ +local t = require('luatest') +local vtest = require('test.luatest_helpers.vtest') + +local test_group = t.group('storage') + +test_group.after_each(function(g) + g.cluster:drop() + g.cluster = nil +end) + +test_group.test_user_management_mode_boot_auto = function(g) + local cfg_template = { + sharding = { + { + replicas = { + replica_1_a = {master = true}, + replica_1_b = {}, + }, + }, + { + replicas = { + replica_2_a = {master = true}, + }, + }, + }, + bucket_count = 2, + user_management_mode = 'auto', + } + local cfg = vtest.config_new(cfg_template) + vtest.cluster_new(g, cfg) + vtest.cluster_bootstrap(g, cfg) + local bid = g.replica_1_a:exec(function(uuid) + local bid = _G.get_first_bucket() + local ok, err = ivshard.storage.bucket_send(bid, uuid, + {timeout = iwait_timeout}) + ilt.assert_equals(err, nil) + ilt.assert(ok) + _G.bucket_gc_wait() + return bid + end, {g.replica_2_a:replicaset_uuid()}) + -- + -- Switch to manual. Still works fine. + -- + cfg_template.user_management_mode = 'manual' + cfg = vtest.config_new(cfg_template) + vtest.cluster_cfg(g, cfg) + g.replica_2_a:exec(function(bid, uuid) + local ok, err = ivshard.storage.bucket_send(bid, uuid, + {timeout = iwait_timeout}) + ilt.assert_equals(err, nil) + ilt.assert(ok) + _G.bucket_gc_wait() + end, {bid, g.replica_1_a:replicaset_uuid()}) +end + +test_group.test_user_management_mode_boot_manual_only_replication = function(g) + local cfg_template = { + sharding = { + { + replicas = { + replica_1_a = {master = true}, + replica_1_b = {}, + }, + }, + { + replicas = { + replica_2_a = {master = true}, + }, + }, + }, + bucket_count = 2, + user_management_mode = 'manual', + test_user_grant_range = 'replication', + } + local cfg = vtest.config_new(cfg_template) + vtest.cluster_new(g, cfg) + vtest.cluster_bootstrap(g, cfg) + -- + -- Just replication rights are not enough for vshard operation. It will + -- boot, but any vshard operations between instances won't work. + -- + g.replica_1_a:exec(function(uuid) + local bid = _G.get_first_bucket() + local ok, err = ivshard.storage.bucket_send(bid, uuid, + {timeout = iwait_timeout}) + if ivutil.version_is_at_least(2, 7, 0, nil, 0, 0) then + ilt.assert_equals(err.type, 'AccessDeniedError') + end + ilt.assert_str_contains(err.message, 'bucket_recv') + ilt.assert(not ok) + end, {g.replica_2_a:replicaset_uuid()}) +end + +test_group.test_user_management_mode_boot_manual_super = function(g) + local cfg_template = { + sharding = { + { + replicas = { + replica_1_a = {master = true}, + replica_1_b = {}, + }, + }, + { + replicas = { + replica_2_a = {master = true}, + }, + }, + }, + bucket_count = 2, + user_management_mode = 'manual', + test_user_grant_range = 'super', + } + local cfg = vtest.config_new(cfg_template) + vtest.cluster_new(g, cfg) + vtest.cluster_bootstrap(g, cfg) + local bid = g.replica_1_a:exec(function(uuid) + local bid = _G.get_first_bucket() + local ok, err = ivshard.storage.bucket_send(bid, uuid, + {timeout = iwait_timeout}) + ilt.assert_equals(err, nil) + ilt.assert(ok) + _G.bucket_gc_wait() + return bid + end, {g.replica_2_a:replicaset_uuid()}) + -- + -- Switch to auto. Still works fine. + -- + cfg_template.user_management_mode = 'auto' + cfg = vtest.config_new(cfg_template) + vtest.cluster_cfg(g, cfg) + g.replica_2_a:exec(function(bid, uuid) + local ok, err = ivshard.storage.bucket_send(bid, uuid, + {timeout = iwait_timeout}) + ilt.assert_equals(err, nil) + ilt.assert(ok) + _G.bucket_gc_wait() + end, {bid, g.replica_1_a:replicaset_uuid()}) +end diff --git a/test/unit-luatest/config_test.lua b/test/unit-luatest/config_test.lua index 5bb4fdd2..13bf7b85 100644 --- a/test/unit-luatest/config_test.lua +++ b/test/unit-luatest/config_test.lua @@ -120,4 +120,14 @@ g.test_enum = function() "Discovery mode must be enum {'on', 'off', 'once', nil}", vcfg.check, config) config.discovery_mode = nil + + for _, v in pairs({'auto', 'manual'}) do + config.user_management_mode = v + t.assert(vcfg.check(config)) + end + config.user_management_mode = 'bad' + t.assert_error_msg_content_equals( + "User management mode must be enum {'auto', 'manual', nil}", + vcfg.check, config) + config.user_management_mode = nil end diff --git a/vshard/cfg.lua b/vshard/cfg.lua index da35f9b1..838fa092 100644 --- a/vshard/cfg.lua +++ b/vshard/cfg.lua @@ -376,6 +376,10 @@ local cfg_template = { name = 'Scheduler bucket move quota', type = 'non-negative number', is_optional = true, default = consts.DEFAULT_SCHED_MOVE_QUOTA }, + user_management_mode = { + name = 'User management mode', type = 'enum', is_optional = true, + default = 'auto', enum = {'auto', 'manual'}, + }, } -- diff --git a/vshard/storage/init.lua b/vshard/storage/init.lua index b0c0b716..f1f09eaf 100644 --- a/vshard/storage/init.lua +++ b/vshard/storage/init.lua @@ -755,13 +755,15 @@ end -- describes schema before that - 0.1.15. local function schema_init_0_1_15_0(ctx) log.info("Initializing schema %s", schema_version_make({0, 1, 15, 0})) - box.schema.user.create(ctx.username, { - password = ctx.password, - if_not_exists = true, - }) - -- Replication may has not been granted, if user exists. - box.schema.user.grant(ctx.username, 'replication', nil, nil, - {if_not_exists = true}) + if ctx.is_user_auto then + box.schema.user.create(ctx.username, { + password = ctx.password, + if_not_exists = true, + }) + -- Replication may has not been granted, if user exists. + box.schema.user.grant(ctx.username, 'replication', nil, nil, + {if_not_exists = true}) + end local bucket = box.schema.space.create('_bucket') bucket:format({ @@ -787,10 +789,11 @@ local function schema_init_0_1_15_0(ctx) 'vshard.storage.rebalancer_request_state', 'vshard.storage.rebalancer_apply_routes', } - for _, name in ipairs(storage_api) do box.schema.func.create(name, {setuid = true}) - box.schema.user.grant(ctx.username, 'execute', 'function', name) + if ctx.is_user_auto then + box.schema.user.grant(ctx.username, 'execute', 'function', name) + end end box.space._schema:replace({'vshard_version', 0, 1, 15, 0}) @@ -811,7 +814,9 @@ local function schema_upgrade_to_0_1_16_0(ctx) local func = 'vshard.storage._call' log.info('Create function %s()', func) box.schema.func.create(func, {setuid = true}) - box.schema.user.grant(ctx.username, 'execute', 'function', func) + if ctx.is_user_auto then + box.schema.user.grant(ctx.username, 'execute', 'function', func) + end -- Don't drop old functions in the same version. Removal can -- happen only after 0.1.16. Or there should appear support of -- rebalancing from too old versions. Drop of these functions @@ -3473,6 +3478,7 @@ local function storage_cfg(cfg, this_replica_uuid, is_reload) is_master = is_master, username = uri.login, password = uri.password, + is_user_auto = vshard_cfg.user_management_mode == 'auto', } schema_upgrade(upgrade_ctx)