diff --git a/extra/exports b/extra/exports index 786a6087e7886f2b85ad36c504e6aea8e9526074..1c8f85e25103643b3b00388913297997a2b629a9 100644 --- a/extra/exports +++ b/extra/exports @@ -48,6 +48,7 @@ box_decimal_to_uint64 box_decimal_trim box_decimal_zero box_delete +box_effective_user_id box_error_clear box_error_code box_error_custom_type @@ -113,6 +114,8 @@ box_sequence_reset box_sequence_set box_session_id box_session_push +box_session_su +box_session_user_id box_space_id_by_name box_truncate box_tuple_bsize @@ -154,6 +157,7 @@ box_txn_set_isolation box_txn_set_timeout box_update box_upsert +box_user_id_by_name box_wait_ro clock_monotonic clock_monotonic64 diff --git a/src/box/box.cc b/src/box/box.cc index 588b7d29f4e8399e5f6c6a575d84bb447942e7a6..2ede8a8d41069465d227716ad6ee1199a8d01d54 100644 --- a/src/box/box.cc +++ b/src/box/box.cc @@ -3865,6 +3865,56 @@ box_session_id(void) return current_session()->id; } +API_EXPORT int +box_session_su(uint32_t uid) +{ + struct session *session = current_session(); + if (session == NULL) { + diag_set(ClientError, ER_SESSION_CLOSED); + return -1; + } + struct user *user = user_find(uid); + if (user == NULL) + return -1; + if (access_check_session(user) < 0) + return -1; + + credentials_reset(&session->credentials, user); + fiber_set_user(fiber(), &session->credentials); + return 0; +} + +API_EXPORT int +box_session_user_id(uint32_t *uid) +{ + struct session *session = current_session(); + if (session == NULL) { + diag_set(ClientError, ER_SESSION_CLOSED); + return -1; + } + struct user *user = user_find(session->credentials.uid); + if (user == NULL) + return -1; + *uid = user->def->uid; + return 0; +} + +API_EXPORT uint32_t +box_effective_user_id(void) +{ + return effective_user()->uid; +} + +API_EXPORT int +box_user_id_by_name(const char *name, const char *name_end, uint32_t *uid) +{ + struct user *user = user_find_by_name(name, name_end - name); + if (user == NULL) + return -1; + *uid = user->def->uid; + return 0; +} + API_EXPORT int box_iproto_send(uint64_t sid, const char *header, const char *header_end, diff --git a/src/box/box.h b/src/box/box.h index 5f2a70ad96e0460b6b21e81cf7b4032f1c2fea77..1e3b9fcedb295079037d82adface71028e1767b0 100644 --- a/src/box/box.h +++ b/src/box/box.h @@ -681,6 +681,46 @@ box_session_push(const char *data, const char *data_end); API_EXPORT uint64_t box_session_id(void); +/** + * Change the current session user. + * \param uid new user identifier + * \retval -1 on error (check box_error_last()) + * \retval 0 on success + */ +API_EXPORT int +box_session_su(uint32_t uid); + +/** + * Return the current session user identifier. + * Session user can be changed with box_session_su() + * or setuid functions. + * \param[out] uid pointer to a user identifier + * \retval -1 on error (check box_error_last()) + * \retval 0 on success + */ +API_EXPORT int +box_session_user_id(uint32_t *uid); + +/** + * Return the effective user identifier. Effective + * user is **not** affected by box_session_su() + * or setuid functions. + * \retval user identifier + */ +API_EXPORT uint32_t +box_effective_user_id(void); + +/** + * Return the user identifier by name. + * \param name user name + * \param name_end end of user name + * \param[out] uid pointer to a user identifier + * \retval -1 on error (check box_error_last()) + * \retval 0 on success + */ +API_EXPORT int +box_user_id_by_name(const char *name, const char *name_end, uint32_t *uid); + /** * Sends a packet with the given header and body over the IPROTO session's * socket. diff --git a/test/app-tap/module_api.c b/test/app-tap/module_api.c index 8c8a89d0adff22da13c7d63e7e17022103b0b34e..59b28b63a8f6042dd0d56ffd337afc70d176d1e9 100644 --- a/test/app-tap/module_api.c +++ b/test/app-tap/module_api.c @@ -3110,6 +3110,112 @@ test_box_session_id(struct lua_State *L) /* }}} Helpers for current session identifier Lua/C API test cases */ +static int +test_box_session_su(struct lua_State *L) +{ + size_t region_svp = box_region_used(); + + uint32_t guest_uid = 0; + uint32_t orig_uid = 0; + uint32_t uid = 0; + + /* Get current session user */ + int rc = box_session_user_id(&orig_uid); + fail_unless(rc == 0); + + /* Switch session to the guest user */ + const char *guest = "guest"; + rc = box_user_id_by_name(guest, guest + strlen(guest), &guest_uid); + fail_unless(rc == 0); + rc = box_session_su(guest_uid); + fail_unless(rc == 0); + rc = box_session_user_id(&uid); + fail_unless(rc == 0); + fail_unless(uid == guest_uid); + + /* Switch session back to original user */ + rc = box_session_su(orig_uid); + fail_unless(rc == 0); + rc = box_session_user_id(&uid); + fail_unless(rc == 0); + fail_unless(uid == orig_uid); + + box_region_truncate(region_svp); + lua_pushboolean(L, 1); + return 1; +} + +static int +test_box_session_user_id(struct lua_State *L) +{ + size_t region_svp = box_region_used(); + + uint32_t user_id = 42; + int rc = box_session_user_id(&user_id); + fail_unless(rc == 0); + fail_unless(user_id != 42); + + box_region_truncate(region_svp); + lua_pushboolean(L, 1); + return 1; +} + +static int +test_box_effective_user_id(struct lua_State *L) +{ + size_t region_svp = box_region_used(); + + /* Effective and session user ids are equal by default */ + uint32_t fiber_uid = box_effective_user_id(); + uint32_t session_uid = 0; + int rc = box_session_user_id(&session_uid); + fail_unless(rc == 0); + fail_unless(fiber_uid == session_uid); + + /** + * Switch the session user (effective and session users + * are **not** equal now) + */ + const char *guest = "guest"; + rc = box_user_id_by_name(guest, guest + strlen(guest), &session_uid); + fail_unless(rc == 0); + fiber_uid = box_effective_user_id(); + rc = box_session_su(session_uid); + fail_unless(rc == 0); + fail_unless(fiber_uid != session_uid); + + /** + * Switch back to original user (effective and session users + * become equal again) + */ + rc = box_session_su(fiber_uid); + fail_unless(rc == 0); + fiber_uid = box_effective_user_id(); + rc = box_session_user_id(&session_uid); + fail_unless(rc == 0); + fail_unless(session_uid == fiber_uid); + + box_region_truncate(region_svp); + lua_pushboolean(L, 1); + return 1; +} + +static int +test_box_user_id_by_name(struct lua_State *L) +{ + size_t region_svp = box_region_used(); + + uint32_t user_id = 42; + const char *guest = "guest"; + int rc = box_user_id_by_name(guest, guest + strlen(guest), &user_id); + fail_unless(rc == 0); + fail_unless(user_id != 42); + + box_region_truncate(region_svp); + lua_pushboolean(L, 1); + return 1; +} + /* {{{ Helpers for `box_iproto_send` Lua/C API test cases */ static int @@ -3265,6 +3371,10 @@ luaopen_module_api(lua_State *L) {"test_key_def_validate_key", test_key_def_validate_key}, {"test_key_def_hash", test_key_def_hash}, {"test_box_ibuf", test_box_ibuf}, + {"test_box_session_su", test_box_session_su}, + {"test_box_session_user_id", test_box_session_user_id}, + {"test_box_effective_user_id", test_box_effective_user_id}, + {"test_box_user_id_by_name", test_box_user_id_by_name}, {"tuple_validate_def", test_tuple_validate_default}, {"tuple_validate_fmt", test_tuple_validate_formatted}, {"test_key_def_dup", test_key_def_dup}, diff --git a/test/app-tap/module_api.test.lua b/test/app-tap/module_api.test.lua index 5642b9a59b2d0b656ca235326db4a34155f98487..e606d2b31be1b99aede1ab0fa01185c70eae2c62 100755 --- a/test/app-tap/module_api.test.lua +++ b/test/app-tap/module_api.test.lua @@ -664,7 +664,7 @@ local function test_box_iproto_override(test, module) end require('tap').test("module_api", function(test) - test:plan(50) + test:plan(54) local status, module = pcall(require, 'module_api') test:is(status, true, "module") test:ok(status, "module is loaded")