Skip to content

Commit

Permalink
config: grant runtime access to lua_call from config
Browse files Browse the repository at this point in the history
This patch introduces the ability to grant execution privileges for Lua
functions through declarative configuration, even when the database is
in read-only mode or has an outdated schema version.

Users can specify  `lua_call: [<func_name>]`, enabling the execution of
specified Lua functions (e.g., `failover:execute()` when all instance
are in read-only mode). The `lua_call: [all]` option is also supported,
allowing access to all global Lua functions except built-in ones,
regardless of database mode or status.

Privileges are still written to the database when possible for consistency
and compatibility.

Closes tarantool#10310

@TarantoolBot document
Title: Grant runtime access to Lua functions via configuration

It is now possible to grant execution privileges for Lua functions
through the declarative configuration, even when the database is in
read-only mode or has an outdated schema version.

You can specify function permissions using the `lua_call` option in
the configuration, for example:

```yaml
credentials:
  users:
    alice:
      privileges:
        - permissions: [execute]
          lua_call: [my_func]
```

This grants the `alice` user permission to execute the `my_func` Lua
function, regardless of the database's mode or status. The special option
`lua_call: [all]` is also supported, granting access to all global Lua
functions except built-in ones, bypassing database restrictions.

Privileges will still be written to the database when possible to
maintain compatibility and consistency with other privilege types.
  • Loading branch information
mandesero authored and Totktonada committed Sep 25, 2024
1 parent db27af7 commit 38c6b0d
Show file tree
Hide file tree
Showing 6 changed files with 363 additions and 23 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## feature/config

* Now users can specify the `lua_call` option to allow calling Lua functions
even when the database is in read-only mode or has an outdated schema version
(gh-10310).
47 changes: 24 additions & 23 deletions src/box/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,29 +44,30 @@ lua_source(lua_sources lua/iproto.lua iproto_lua)
lua_source(lua_sources lua/mkversion.lua mkversion_lua)

# {{{ config
lua_source(lua_sources lua/config/applier/app.lua config_applier_app_lua)
lua_source(lua_sources lua/config/applier/box_cfg.lua config_applier_box_cfg_lua)
lua_source(lua_sources lua/config/applier/compat.lua config_applier_compat_lua)
lua_source(lua_sources lua/config/applier/console.lua config_applier_console_lua)
lua_source(lua_sources lua/config/applier/credentials.lua config_applier_credentials_lua)
lua_source(lua_sources lua/config/applier/fiber.lua config_applier_fiber_lua)
lua_source(lua_sources lua/config/applier/mkdir.lua config_applier_mkdir_lua)
lua_source(lua_sources lua/config/applier/roles.lua config_applier_roles_lua)
lua_source(lua_sources lua/config/applier/sharding.lua config_applier_sharding_lua)
lua_source(lua_sources lua/config/cluster_config.lua config_cluster_config_lua)
lua_source(lua_sources lua/config/configdata.lua config_configdata_lua)
lua_source(lua_sources lua/config/init.lua config_init_lua)
lua_source(lua_sources lua/config/instance_config.lua config_instance_config_lua)
lua_source(lua_sources lua/config/source/env.lua config_source_env_lua)
lua_source(lua_sources lua/config/source/file.lua config_source_file_lua)
lua_source(lua_sources lua/config/utils/aboard.lua config_utils_aboard_lua)
lua_source(lua_sources lua/config/utils/expression.lua config_utils_expression_lua)
lua_source(lua_sources lua/config/utils/file.lua config_utils_file_lua)
lua_source(lua_sources lua/config/utils/log.lua config_utils_log_lua)
lua_source(lua_sources lua/config/utils/odict.lua config_utils_odict_lua)
lua_source(lua_sources lua/config/utils/schema.lua config_utils_schema_lua)
lua_source(lua_sources lua/config/utils/snapshot.lua config_utils_snapshot_lua)
lua_source(lua_sources lua/config/utils/tabulate.lua config_utils_tabulate_lua)
lua_source(lua_sources lua/config/applier/app.lua config_applier_app_lua)
lua_source(lua_sources lua/config/applier/box_cfg.lua config_applier_box_cfg_lua)
lua_source(lua_sources lua/config/applier/runtime_priv.lua config_applier_runtime_priv_lua)
lua_source(lua_sources lua/config/applier/compat.lua config_applier_compat_lua)
lua_source(lua_sources lua/config/applier/console.lua config_applier_console_lua)
lua_source(lua_sources lua/config/applier/credentials.lua config_applier_credentials_lua)
lua_source(lua_sources lua/config/applier/fiber.lua config_applier_fiber_lua)
lua_source(lua_sources lua/config/applier/mkdir.lua config_applier_mkdir_lua)
lua_source(lua_sources lua/config/applier/roles.lua config_applier_roles_lua)
lua_source(lua_sources lua/config/applier/sharding.lua config_applier_sharding_lua)
lua_source(lua_sources lua/config/cluster_config.lua config_cluster_config_lua)
lua_source(lua_sources lua/config/configdata.lua config_configdata_lua)
lua_source(lua_sources lua/config/init.lua config_init_lua)
lua_source(lua_sources lua/config/instance_config.lua config_instance_config_lua)
lua_source(lua_sources lua/config/source/env.lua config_source_env_lua)
lua_source(lua_sources lua/config/source/file.lua config_source_file_lua)
lua_source(lua_sources lua/config/utils/aboard.lua config_utils_aboard_lua)
lua_source(lua_sources lua/config/utils/expression.lua config_utils_expression_lua)
lua_source(lua_sources lua/config/utils/file.lua config_utils_file_lua)
lua_source(lua_sources lua/config/utils/log.lua config_utils_log_lua)
lua_source(lua_sources lua/config/utils/odict.lua config_utils_odict_lua)
lua_source(lua_sources lua/config/utils/schema.lua config_utils_schema_lua)
lua_source(lua_sources lua/config/utils/snapshot.lua config_utils_snapshot_lua)
lua_source(lua_sources lua/config/utils/tabulate.lua config_utils_tabulate_lua)

if (ENABLE_CONFIG_EXTRAS)
lua_source(lua_sources ${CONFIG_EXTRAS_DIR}/source/etcd.lua config_source_etcd_lua)
Expand Down
125 changes: 125 additions & 0 deletions src/box/lua/config/applier/runtime_priv.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
-- Extract and add functions from a user or a role definition to
-- the `{[func_name] = true, <...>}` mapping `res`.
--
-- The source is `lua_call` declarations.
local function add_funcs(res, user_or_role_def)
local privileges = user_or_role_def.privileges or {}

for _, privilege in ipairs(privileges) do
local permissions = privilege.permissions or {}
local has_execute = false
for _, perm in ipairs(permissions) do
if perm == 'execute' then
has_execute = true
break
end
end
if has_execute and privilege.lua_call ~= nil then
for _, func_name in ipairs(privilege.lua_call) do
res[func_name] = true
end
end
end
end

-- Extract and add roles for the given user to the
-- `{[role_name] = true, <...>}` mapping `res`, including
-- transitively assigned (when a role is assigned to a role).
local function add_roles(res, user_or_role_def, ctx)
for _, role_name in ipairs(user_or_role_def.roles or {}) do
-- Detect a recursion.
if ctx.visited[role_name] then
error(('Recursion detected: credentials.roles.%s depends on ' ..
'itself'):format(role_name), 0)
end

-- Add the role into the result.
res[role_name] = true

-- Add the nested roles.
--
-- Ignore unknown roles. For example, there is a
-- built-in role 'super' that doesn't have to be
-- configured.
local role_def = ctx.roles[role_name]
if role_def ~= nil then
ctx.visited[role_name] = true
add_roles(res, role_def, ctx)
ctx.visited[role_name] = nil
end
end
end

-- Extract all the user's functions listed in the `lua_call`
-- directives in the user definition or its roles assigned
-- directly or transitively over the other roles.
local function extract_funcs(user_name, ctx)
local user_def = ctx.users[user_name]

local roles = {}
ctx.visited = {}
add_roles(roles, user_def, ctx)
ctx.visited = nil

-- Collect a full set of functions for the given user.
--
-- {
-- [func_name] = true,
-- <...>,
-- }
local funcs = {}
add_funcs(funcs, user_def)
for role_name, _ in pairs(roles) do
local role_def = ctx.roles[role_name]
if role_def ~= nil then
add_funcs(funcs, role_def)
end
end

return funcs
end

local function apply(config_module)
-- Prepare a context with the configuration information to
-- transform.
local configdata = config_module._configdata
local ctx = {
roles = configdata:get('credentials.roles') or {},
users = configdata:get('credentials.users') or {},
}

-- Collect a mapping from users to their granted functions.
--
-- {
-- [user_name] = {
-- [func_name] = true,
-- <...>,
-- },
-- <...>
-- }
local res = {}
for user_name, _ in pairs(ctx.users) do
local funcs = extract_funcs(user_name, ctx)
if next(funcs) ~= nil then
res[user_name] = funcs
end
end

-- Reset the runtime privileges and grant all the configured
-- ones.
box.internal.lua_call_runtime_priv_reset()
for user_name, funcs in pairs(res) do
for func_name, _ in pairs(funcs) do
if func_name == 'all' then
box.internal.lua_call_runtime_priv_grant(user_name, '')
else
box.internal.lua_call_runtime_priv_grant(user_name, func_name)
end
end
end
end

return {
name = 'runtime_priv',
apply = apply,
}
1 change: 1 addition & 0 deletions src/box/lua/config/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ function methods._initialize(self)
self:_register_applier(require('internal.config.applier.compat'))
self:_register_applier(require('internal.config.applier.mkdir'))
self:_register_applier(require('internal.config.applier.console'))
self:_register_applier(require('internal.config.applier.runtime_priv'))
self:_register_applier(require('internal.config.applier.box_cfg'))
self:_register_applier(require('internal.config.applier.credentials'))
self:_register_applier(require('internal.config.applier.fiber'))
Expand Down
5 changes: 5 additions & 0 deletions src/box/lua/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ extern char session_lua[],
/* {{{ config */
config_applier_app_lua[],
config_applier_box_cfg_lua[],
config_applier_runtime_priv_lua[],
config_applier_compat_lua[],
config_applier_console_lua[],
config_applier_credentials_lua[],
Expand Down Expand Up @@ -426,6 +427,10 @@ static const char *lua_sources[] = {
"internal.config.applier.console",
config_applier_console_lua,

"config/applier/runtime_priv",
"internal.config.applier.runtime_priv",
config_applier_runtime_priv_lua,

"config/applier/credentials",
"internal.config.applier.credentials",
config_applier_credentials_lua,
Expand Down
Loading

0 comments on commit 38c6b0d

Please sign in to comment.