diff --git a/changelogs/unreleased/gh-6199-schema-priv-resolve-existence-check.md b/changelogs/unreleased/gh-6199-schema-priv-resolve-existence-check.md new file mode 100644 index 0000000000000000000000000000000000000000..3390d312b07fac95f28d6f7e69fe65f9d7df40ff --- /dev/null +++ b/changelogs/unreleased/gh-6199-schema-priv-resolve-existence-check.md @@ -0,0 +1,4 @@ +## bugfix/box + +* We now check that all privileges passed to box.schema.grant are resolved + (gh-6199). diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua index 99b65b0f51e30c3eeb53d3a6ef3442bcc83495f8..2c02949c507cb2b14d5fe46403fca0f467408f00 100644 --- a/src/box/lua/schema.lua +++ b/src/box/lua/schema.lua @@ -2380,53 +2380,56 @@ box.schema.sequence.drop = function(name, opts) _sequence:delete{id} end -local function privilege_resolve(privilege) - local numeric = 0 - if type(privilege) == 'string' then - privilege = string.lower(privilege) - if string.find(privilege, 'read') then - numeric = numeric + box.priv.R - end - if string.find(privilege, 'write') then - numeric = numeric + box.priv.W - end - if string.find(privilege, 'execute') then - numeric = numeric + box.priv.X - end - if string.find(privilege, 'session') then - numeric = numeric + box.priv.S - end - if string.find(privilege, 'usage') then - numeric = numeric + box.priv.U - end - if string.find(privilege, 'create') then - numeric = numeric + box.priv.C - end - if string.find(privilege, 'drop') then - numeric = numeric + box.priv.D - end - if string.find(privilege, 'alter') then - numeric = numeric + box.priv.A - end - if string.find(privilege, 'reference') then - numeric = numeric + box.priv.REFERENCE - end - if string.find(privilege, 'trigger') then - numeric = numeric + box.priv.TRIGGER - end - if string.find(privilege, 'insert') then - numeric = numeric + box.priv.INSERT - end - if string.find(privilege, 'update') then - numeric = numeric + box.priv.UPDATE - end - if string.find(privilege, 'delete') then - numeric = numeric + box.priv.DELETE +local function privilege_parse(privs) + -- TODO: introduce a global privilege -> bit mapping? + local privs_map = { + read = box.priv.R, + write = box.priv.W, + execute = box.priv.X, + session = box.priv.S, + usage = box.priv.U, + create = box.priv.C, + drop = box.priv.D, + alter = box.priv.A, + reference = box.priv.REFERENECE, + trigger = box.priv.TRIGGER, + insert = box.priv.INSERT, + update = box.priv.UPDATE, + delete = box.priv.DELETE + } + local privs_cp = string.lower(privs):gsub('^[%A]*', '') + + local mask = 0 + -- TODO: prove correctness formally (e.g. via a FSA)? + repeat + local matched = false + -- TODO: replace this with one group pattern when Lua patterns start + -- supporting disjunction (e.g. '|') + for priv, bit in pairs(privs_map) do + privs_cp = string.gsub(privs_cp, '^' .. priv .. '[%A]*', + function() + matched = true + mask = mask + bit + privs_map[priv] = 0 + return '' + end) end - else - numeric = privilege + until (not matched) + + if privs_cp ~= '' then + mask = 0 + end + + return mask +end + +local function privilege_resolve(privs) + if type(privs) == 'string' then + return privilege_parse(privs) + elseif type(privs) == 'number' then -- TODO: assert type(privs)? + return privs end - return numeric + return 0 end -- allowed combination of privilege bits for object diff --git a/test/box-luatest/gh_6199_schema_priv_resolve_existence_check_test.lua b/test/box-luatest/gh_6199_schema_priv_resolve_existence_check_test.lua new file mode 100644 index 0000000000000000000000000000000000000000..acd229f78f046e82dc12239d1cb0f745d31f6398 --- /dev/null +++ b/test/box-luatest/gh_6199_schema_priv_resolve_existence_check_test.lua @@ -0,0 +1,49 @@ +local cluster = require('test.luatest_helpers.cluster') +local t = require('luatest') + +local g = t.group('gh-6199-schema-priv-resolve-existence-check') + +g.before_all(function() + local helpers = require('test.luatest_helpers') + + g.cluster = cluster:new({}) + + local default_box_cfg = { + listen = helpers.instance_uri('default'), + } + g.default = g.cluster:build_and_add_server({alias = 'default', + box_cfg = default_box_cfg}) + g.cluster:start() + g.default:exec(function() + box.session.su('admin') + end) +end) + +g.after_all(function() + g.cluster:drop() +end) + +g.test_priv_resolve_existence_check = function() + g.default:exec(function() + local t = require('luatest') + + local msg = 'role cannot be granted together with a privilege' + t.assert_error(box.schema.user.grant, 'guest', 'read,replication', + 'universe', msg) + + msg = 'when privileges are resolved, we check that all of them have' .. + 'been resolved' + local privs = {'read', 'write', 'execute', 'session', 'usage', 'read', + 'create', 'drop', 'alter', 'reference', 'trigger', + 'update', 'delete'} + for _, priv in pairs(privs) do + local invalid_priv = ('%s, unknown,%s'):format(priv, priv) + t.assert_error(box.schema.user.grant, 'guest', invalid_priv, + 'universe', msg) + end + local invalid_all_privs = table.concat(privs, ',') .. ',unknown' .. + table.concat(privs, ',') + t.assert_error(box.schema.user.grant, 'guest', invalid_all_privs, + 'universe', msg) + end) +end