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")