From af90ecc891762338504e51b4e66c6730a5ea9aa0 Mon Sep 17 00:00:00 2001 From: Dmitry Rodionov <d.rodionov@picodata.io> Date: Tue, 12 Sep 2023 14:45:45 +0300 Subject: [PATCH] feat: export box_access_check_space NO_DOC=picodata internal patch NO_CHANGELOG=picodata internal patch NO_TEST=picodata internal patch Co-authored-by: Yaroslav Dynnikov <yaroslav.dynnikov@gmail.com> --- extra/exports | 1 + src/box/box.cc | 9 ++++ src/box/box.h | 15 ++++++ src/box/space.h | 4 ++ test/app-tap/module_api.test.lua | 85 +++++++++++++++++++++++++++++++- 5 files changed, 113 insertions(+), 1 deletion(-) diff --git a/extra/exports b/extra/exports index c164164f28..c8f814b33a 100644 --- a/extra/exports +++ b/extra/exports @@ -12,6 +12,7 @@ base64_bufsize base64_decode base64_encode +box_access_check_space box_auth_data_prepare box_dd_version_id box_decimal_abs diff --git a/src/box/box.cc b/src/box/box.cc index 9aea8367ae..1b0541c88f 100644 --- a/src/box/box.cc +++ b/src/box/box.cc @@ -3649,6 +3649,15 @@ box_iproto_override(uint32_t req_type, iproto_handler_t handler, return iproto_override(req_type, handler, destroy, ctx); } +API_EXPORT int +box_access_check_space(uint32_t space_id, uint16_t access) +{ + struct space *space = space_cache_find(space_id); + if (space == NULL) + return -1; + return access_check_space(space, access); +} + static inline void box_register_replica(uint32_t id, const struct tt_uuid *uuid) { diff --git a/src/box/box.h b/src/box/box.h index 1df8996cdc..b52a387fb9 100644 --- a/src/box/box.h +++ b/src/box/box.h @@ -700,6 +700,21 @@ box_effective_user_id(void); API_EXPORT int box_user_id_by_name(const char *name, const char *name_end, uint32_t *uid); +/** + * Run access check for the current user against + * specified space and access type. + * While it is possible to pass bitmask in access + * parameter this function is intended to be used + * with only one permission at a time. + * Most relevant access types are read and write. + * \param space_id space id + * \param access type of access. See valid options in priv_type enum. + * \retval -1 on error (check box_error_last()) + * \retval 0 on success + */ +API_EXPORT int +box_access_check_space(uint32_t space_id, uint16_t access); + /** * Sends a packet with the given header and body over the IPROTO session's * socket. diff --git a/src/box/space.h b/src/box/space.h index 2fbea44f4b..93b197edac 100644 --- a/src/box/space.h +++ b/src/box/space.h @@ -434,6 +434,10 @@ index_name_by_id(struct space *space, uint32_t id); /** * Check whether or not the current user can be granted * the requested access to the space. + * @param space Space to run access check against + * @param access Requested access + * @retval 0 on success when access is granted + * @retval -1 on error (check box_error_last()) */ int access_check_space(struct space *space, user_access_t access); diff --git a/test/app-tap/module_api.test.lua b/test/app-tap/module_api.test.lua index 1788357c5b..29e28a765c 100755 --- a/test/app-tap/module_api.test.lua +++ b/test/app-tap/module_api.test.lua @@ -663,8 +663,90 @@ local function test_box_iproto_override(test, module) module.box_iproto_override_reset(box.iproto.type.PING) end +local function test_box_access_check_space(test) + test:plan(9) + + local user_name = "box_access_check_space_test_user" + box.schema.user.create(user_name, {password = 'foobar'}) + + local space_name = "test_box_access_check_space_ffi" + box.schema.space.create(space_name) + + ffi = require('ffi') + ffi.cdef('int box_access_check_space(uint32_t space_id, uint16_t access);') + local function test_access_check(test, access, expected_ret, expected_msg) + box.error.clear() + local r = ffi.C.box_access_check_space(box.space[space_name].id, access) + local e = tostring(box.error.last()) + test:plan(2) + test:is(r, expected_ret, "return value"); + test:like(e, expected_msg, "error message"); + end + + box.session.su(user_name, function() + test:test("no access - read fails", + test_access_check, box.priv.R, + -1, "Read access to space .+ is denied for user .+" + ) + + test:test("no access - write fails", + test_access_check, box.priv.W, + -1, "Write access to space .+ is denied for user .+" + ) + end) + + box.schema.user.grant(user_name, 'read', 'space', space_name) + + box.session.su(user_name, function() + test:test("grant read - read ok", + test_access_check, box.priv.R, + 0, "nil" + ) + + test:test("grant read - write fails", + test_access_check, box.priv.W, + -1, "Write access to space .+ is denied for user .+" + ) + end) + + box.schema.user.grant(user_name, 'write', 'space', space_name) + + box.session.su(user_name, function() + test:test("grant rw - read ok", + test_access_check, box.priv.R, + 0, "nil" + ) + + test:test("grant rw - write ok", + test_access_check, box.priv.W, + 0, "nil" + ) + + -- Execute accesses can't be granted to a space. This case is + -- artificial, but access check still fails. + test:test("grant rw - execute access check fails", + test_access_check, box.priv.X, + -1, "Execute access to space .+ is denied for user .+" + ) + end) + + box.schema.user.revoke(user_name, 'read', 'space', space_name) + + box.session.su(user_name, function() + test:test("grant write - read fails", + test_access_check, box.priv.R, + -1, "Read access to space .+ is denied for user .+" + ) + + test:test("grant write - write ok", + test_access_check, box.priv.W, + 0, "nil" + ) + end) +end + require('tap').test("module_api", function(test) - test:plan(56) + test:plan(57) local status, module = pcall(require, 'module_api') test:is(status, true, "module") test:ok(status, "module is loaded") @@ -701,6 +783,7 @@ require('tap').test("module_api", function(test) test:test("box_session_id_matches", test_box_session_id_matches, module) test:test("box_iproto_send", test_box_iproto_send, module) test:test("box_iproto_override", test_box_iproto_override, module) + test:test("box_access_check_space", test_box_access_check_space) space:drop() end) -- GitLab