diff --git a/src/box/alter.cc b/src/box/alter.cc index 3145e879bd78786d61150fc528b6ea5e70597ec5..80eff009816d159d5a6684a80ad7bff92f44f749 100644 --- a/src/box/alter.cc +++ b/src/box/alter.cc @@ -72,14 +72,15 @@ void access_check_ddl(uint32_t owner_uid) { - struct user_def *user = user(); + struct current_user *user = current_user(); /* * Only the creator of the space or superuser can modify * the space, since we don't have ALTER privilege. */ if (owner_uid != user->uid && user->uid != ADMIN) { + struct user_def *def = user_cache_find(user->uid); tnt_raise(ClientError, ER_ACCESS_DENIED, - "Create or drop", user->name); + "Create or drop", def->name); } } @@ -1321,6 +1322,7 @@ func_def_create_from_tuple(struct func_def *func, struct tuple *tuple) { func->fid = tuple_field_u32(tuple, ID); func->uid = tuple_field_u32(tuple, UID); + func->setuid = false; /* * Do not initialize the privilege cache right away since * when loading up a function definition during recovery, @@ -1332,8 +1334,7 @@ func_def_create_from_tuple(struct func_def *func, struct tuple *tuple) * Later on consistency of the cache is ensured by DDL * checks (see user_has_data()). */ - func->auth_token = BOX_USER_MAX; /* invalid value */ - func->setuid = false; + func->setuid_user.auth_token = BOX_USER_MAX; /* invalid value */ const char *name = tuple_field_cstr(tuple, NAME); uint32_t len = strlen(name); if (len >= sizeof(func->name)) { @@ -1498,8 +1499,14 @@ grant_or_revoke(struct priv_def *priv) struct access *access = NULL; switch (priv->object_type) { case SC_UNIVERSE: + { access = &grantee->universal_access; + /** Update cache at least in the current session. */ + struct current_user *user = current_user(); + if (grantee->uid == user->uid) + user->universal_access = priv->access; break; + } case SC_SPACE: { struct space *space = space_by_id(priv->object_id); diff --git a/src/box/authentication.cc b/src/box/authentication.cc index fa64b1e3ff1bd03276b02fecd353720fe3ff46fb..b35669ad642c769f7efb85b028c668abba3a12c4 100644 --- a/src/box/authentication.cc +++ b/src/box/authentication.cc @@ -35,7 +35,7 @@ authenticate(const char *user_name, uint32_t len, const char *tuple, const char * /* tuple_end */) { struct user_def *user = user_cache_find_by_name(user_name, len); - struct session *session = session(); + struct session *session = current_session(); uint32_t part_count = mp_decode_array(&tuple); if (part_count < 2) { /* Expected at least: authentication mechanism and data. */ @@ -54,6 +54,6 @@ authenticate(const char *user_name, uint32_t len, if (scramble_check(scramble, session->salt, user->hash2)) tnt_raise(ClientError, ER_PASSWORD_MISMATCH, user->name); - session_set_user(session, user); + current_user_init(&session->user, user); } diff --git a/src/box/box.cc b/src/box/box.cc index 559ce39a63265b03fce061c84d4446c2cdfca66d..de3f1c33ca59c2e526caaa0fb87562641edec796 100644 --- a/src/box/box.cc +++ b/src/box/box.cc @@ -331,7 +331,7 @@ box_on_cluster_join(const tt_uuid *server_uuid) } void -box_process_join(struct xrow_header *header) +box_process_join(int fd, struct xrow_header *header) { assert(header->type == IPROTO_JOIN); struct tt_uuid server_uuid = uuid_nil; @@ -339,15 +339,15 @@ box_process_join(struct xrow_header *header) box_on_cluster_join(&server_uuid); - /* process JOIN request via replication relay */ - replication_join(session()->fd, header); + /* Process JOIN request via replication relay */ + replication_join(fd, header); } void -box_process_subscribe(struct xrow_header *header) +box_process_subscribe(int fd, struct xrow_header *header) { /* process SUBSCRIBE request via replication relay */ - replication_subscribe(session()->fd, header); + replication_subscribe(fd, header); } /** Replace the current server id in _cluster */ @@ -384,6 +384,7 @@ box_free(void) { if (recovery == NULL) return; + session_free(); user_cache_free(); schema_free(); tuple_free(); @@ -391,7 +392,6 @@ box_free(void) recovery = NULL; engine_shutdown(); stat_free(); - session_free(); } static void @@ -411,7 +411,6 @@ box_init() box_check_config(); title("loading", NULL); - session_init(); replication_prefork(cfg_gets("snap_dir"), cfg_gets("wal_dir")); stat_init(); @@ -423,6 +422,12 @@ box_init() schema_init(); user_cache_init(); + /* + * The order is important: to initialize sessions, + * we need to access the admin user, which is used + * as a default session user when running triggers. + */ + session_init(); /* recovery initialization */ recovery = recovery_new(cfg_gets("snap_dir"), cfg_gets("wal_dir"), diff --git a/src/box/box.h b/src/box/box.h index a2ebc2f28912092ab442fe9d7ac43e815ab01b23..23ad5451e5a23465100b16f4a620431efac6e240 100644 --- a/src/box/box.h +++ b/src/box/box.h @@ -101,10 +101,10 @@ void box_leave_local_standby_mode(void *data __attribute__((unused))); void -box_process_join(struct xrow_header *header); +box_process_join(int fd, struct xrow_header *header); void -box_process_subscribe(struct xrow_header *header); +box_process_subscribe(int fd, struct xrow_header *header); /** * Check Lua configuration before initialization or diff --git a/src/box/iproto.cc b/src/box/iproto.cc index 866a3be5d3b48b63d6014582f9a8d108bba97fd8..4b561b5231bdf996ab227493c00148ce1e9f7b99 100644 --- a/src/box/iproto.cc +++ b/src/box/iproto.cc @@ -211,6 +211,7 @@ iproto_queue_handler(va_list ap) while ((request = iproto_queue_pop(i_queue))) { IprotoRequestGuard guard(request); fiber_set_session(fiber(), request->session); + fiber_set_user(fiber(), &request->session->user); request->process(request); } /** Put the current fiber into a queue fiber cache. */ @@ -347,8 +348,8 @@ iproto_connection_delete(struct iproto_connection *con) assert(iproto_connection_is_idle(con)); assert(!evio_is_active(&con->output)); if (con->session) { - fiber_set_session(fiber(), con->session); - session_run_on_disconnect_triggers(con->session); + if (! rlist_empty(&session_on_disconnect)) + session_run_on_disconnect_triggers(con->session); session_destroy(con->session); } iobuf_delete(con->iobuf[0]); @@ -710,12 +711,12 @@ iproto_process_admin(struct iproto_request *ireq) ireq->header.sync); break; case IPROTO_JOIN: - box_process_join(&ireq->header); + box_process_join(con->input.fd, &ireq->header); /* TODO: check requests in `con; queue */ iproto_connection_shutdown(con); return; case IPROTO_SUBSCRIBE: - box_process_subscribe(&ireq->header); + box_process_subscribe(con->input.fd, &ireq->header); /* TODO: check requests in `con; queue */ iproto_connection_shutdown(con); return; @@ -770,8 +771,8 @@ iproto_process_connect(struct iproto_request *request) con->session = session_create(fd, con->cookie); coio_write(&con->input, iproto_greeting(con->session->salt), IPROTO_GREETING_SIZE); - fiber_set_session(fiber(), con->session); - session_run_on_connect_triggers(con->session); + if (! rlist_empty(&session_on_connect)) + session_run_on_connect_triggers(con->session); } catch (ClientError *e) { iproto_reply_error(&iobuf->out, e, request->header.type); try { diff --git a/src/box/key_def.h b/src/box/key_def.h index 63e7251357d554a31174d1ddac1f06980d7d7769..91982d07ebcab8b110a859185a36b8a590752189 100644 --- a/src/box/key_def.h +++ b/src/box/key_def.h @@ -291,11 +291,28 @@ struct access { uint8_t effective; }; +/** + * Effective session user. A cache of user data + * and access stored in session and fiber local storage. + * Differs from the authenticated user when executing + * setuid functions. + */ +struct current_user { + /** A look up key to quickly find session user. */ + uint8_t auth_token; + /** + * Cached global grants, to avoid an extra look up + * when checking global grants. + */ + uint8_t universal_access; + /** User id of the authenticated user. */ + uint32_t uid; +}; + /** * Definition of a function. Function body is not stored * or replicated (yet). */ - struct func_def { /** Function id. */ uint32_t fid; @@ -303,14 +320,14 @@ struct func_def { uint32_t uid; /** * True if the function requires change of user id before - * invocaction. + * invocation. */ bool setuid; /** * Authentication id of the owner of the function, * used for set-user-id functions. */ - uint8_t auth_token; + struct current_user setuid_user; /** Function name. */ char name[BOX_NAME_MAX + 1]; /** diff --git a/src/box/lua/call.cc b/src/box/lua/call.cc index 6c661edcb2a394212d53c540018d1167408bf617..00a661a72c564257ff57303ceaccb12358b71bc3 100644 --- a/src/box/lua/call.cc +++ b/src/box/lua/call.cc @@ -471,21 +471,17 @@ struct SetuidGuard { /** True if the function was set-user-id one. */ bool setuid; - /** Original authentication token, only set if setuid = true. */ - uint8_t orig_auth_token; - /** Original user id, only set if setuid = true. */ - uint32_t orig_uid; + struct current_user *orig_user; inline SetuidGuard(const char *name, uint32_t name_len, - struct user_def *user, uint8_t access); + uint8_t access); inline ~SetuidGuard(); }; SetuidGuard::SetuidGuard(const char *name, uint32_t name_len, - struct user_def *user, uint8_t access) + uint8_t access) :setuid(false) - ,orig_auth_token(GUEST) /* silence gnu warning */ - ,orig_uid(GUEST) + ,orig_user(current_user()) { /* @@ -493,9 +489,9 @@ SetuidGuard::SetuidGuard(const char *name, uint32_t name_len, * No special check for ADMIN user is necessary * since ADMIN has universal access. */ - if (user->universal_access.effective & PRIV_ALL) + if (orig_user->universal_access & PRIV_ALL) return; - access &= ~user->universal_access.effective; + access &= ~orig_user->universal_access; /* * We need to look up the function by name even if * the user has access to it, since it could require @@ -511,40 +507,37 @@ SetuidGuard::SetuidGuard(const char *name, uint32_t name_len, */ return; } - if (func == NULL || (func->uid != user->uid && - access & ~func->access[user->auth_token].effective)) { + if (func == NULL || (func->uid != orig_user->uid && + access & ~func->access[orig_user->auth_token].effective)) { /* Access violation, report error. */ char name_buf[BOX_NAME_MAX + 1]; snprintf(name_buf, sizeof(name_buf), "%.*s", name_len, name); + struct user_def *def = user_cache_find(orig_user->uid); tnt_raise(ClientError, ER_FUNCTION_ACCESS_DENIED, - priv_name(access), user->name, name_buf); + priv_name(access), def->name, name_buf); } if (func->setuid) { /** Remember and change the current user id. */ - if (unlikely(func->auth_token >= BOX_USER_MAX)) { + if (unlikely(func->setuid_user.auth_token >= BOX_USER_MAX)) { /* * Fill the cache upon first access, since * when func_def is created, no user may * be around to fill it (recovery of * system spaces from a snapshot). */ - struct user_def *owner = user_by_id(func->uid); - assert(owner != NULL); /* checked by user_has_data() */ - func->auth_token = owner->auth_token; - assert(owner->auth_token < BOX_USER_MAX); + struct user_def *owner = user_cache_find(func->uid); + current_user_init(&func->setuid_user, owner); } setuid = true; - orig_auth_token = user->auth_token; - orig_uid = user->uid; - session_set_user(session(), user_by_token(func->auth_token)); + fiber_set_user(fiber(), &func->setuid_user); } } SetuidGuard::~SetuidGuard() { if (setuid) - session_set_user(session(), user_by_token(orig_auth_token)); + fiber_set_user(fiber(), orig_user); } /** @@ -554,7 +547,6 @@ SetuidGuard::~SetuidGuard() void box_lua_call(struct request *request, struct port *port) { - struct user_def *user = user(); lua_State *L = lua_newthread(tarantool_L); LuarefGuard coro_ref(tarantool_L); const char *name = request->key; @@ -569,7 +561,7 @@ box_lua_call(struct request *request, struct port *port) * https://github.com/tarantool/tarantool/issues/300 * - if a function does not exist, say it first. */ - SetuidGuard setuid(name, name_len, user, PRIV_X); + SetuidGuard setuid(name, name_len, PRIV_X); /* Push the rest of args (a tuple). */ const char *args = request->tuple; uint32_t arg_count = mp_decode_array(&args); diff --git a/src/box/lua/session.cc b/src/box/lua/session.cc index c6f1edff3168219a0a28e73e9b3688dfc2d1b8a2..c0ac116d54a6bd4861f59fa64721f127b307ebae 100644 --- a/src/box/lua/session.cc +++ b/src/box/lua/session.cc @@ -56,23 +56,31 @@ static const char *sessionlib_name = "box.session"; static int lbox_session_id(struct lua_State *L) { - lua_pushnumber(L, session()->id); + lua_pushnumber(L, current_session()->id); return 1; } -/** Session user id. */ +/** + * Session user id. + * Note: effective user id (current_user()->uid) + * may be different in a setuid function. + */ static int lbox_session_uid(struct lua_State *L) { - lua_pushnumber(L, session()->uid); + lua_pushnumber(L, current_session()->user.uid); return 1; } -/** Session user id. */ +/** + * Session user name. + * Note: effective user name may be different in + * a setuid function. + */ static int lbox_session_user(struct lua_State *L) { - struct user_def *user = user_by_id(session()->uid); + struct user_def *user = user_by_id(current_session()->user.uid); if (user) lua_pushstring(L, user->name); else @@ -86,7 +94,7 @@ lbox_session_su(struct lua_State *L) { if (lua_gettop(L) != 1) luaL_error(L, "session.su(): bad arguments"); - struct session *session = session(); + struct session *session = current_session(); if (session == NULL) luaL_error(L, "session.su(): session does not exit"); struct user_def *user; @@ -97,7 +105,7 @@ lbox_session_su(struct lua_State *L) } else { user = user_cache_find(lua_tointeger(L, 1)); } - session_set_user(session, user); + current_user_init(&session->user, user); return 0; } @@ -143,15 +151,14 @@ lbox_session_peer(struct lua_State *L) luaL_error(L, "session.peer(sid): bad arguments"); int fd; - if (lua_gettop(L) == 1) { - struct session *session = session_find(luaL_checkint(L, 1)); - if (session == NULL) - luaL_error(L, "session.peer(): session does not exit"); - fd = session->fd; - } else { - fd = session()->fd; - } - + struct session *session; + if (lua_gettop(L) == 1) + session = session_find(luaL_checkint(L, 1)); + else + session = current_session(); + if (session == NULL) + luaL_error(L, "session.peer(): session does not exit"); + fd = session->fd; if (fd < 0) { lua_pushnil(L); /* no associated peer */ return 1; diff --git a/src/box/recovery.cc b/src/box/recovery.cc index e39be264d3d208425eacaa356b4b01ce4263a7cc..cbfa110c2fbdac76f2c99454d1e66819b4b29fbe 100644 --- a/src/box/recovery.cc +++ b/src/box/recovery.cc @@ -532,7 +532,6 @@ recovery_finalize(struct recovery_state *r) * locally or send to the replica. */ struct wal_watcher { - struct session *session; /** * Rescan the WAL directory in search for new WAL files * every wal_dir_rescan_delay seconds. @@ -574,10 +573,15 @@ recovery_rescan_dir(ev_loop * loop, ev_timer *w, int /* revents */) struct wal_watcher *watcher = r->watcher; struct log_io *save_current_wal = r->current_wal; - /** To process transactions, we need a working session. */ - fiber_set_session(fiber(), r->watcher->session); + /** + * local hot standby is running from an ev + * watcher, without fiber infrastructure (todo: fix), + * but to run queries we need at least a current + * user. + */ + fiber_set_user(fiber(), &admin_user); int result = recover_remaining_wals(r); - fiber_set_session(fiber(), NULL); + fiber_set_user(fiber(), NULL); if (result < 0) panic("recover failed: %i", result); if (save_current_wal != r->current_wal) { @@ -593,9 +597,9 @@ recovery_rescan_file(ev_loop * loop, ev_stat *w, int /* revents */) { struct recovery_state *r = (struct recovery_state *) w->data; struct wal_watcher *watcher = r->watcher; - fiber_set_session(fiber(), r->watcher->session); + fiber_set_user(fiber(), &admin_user); int result = recover_wal(r, r->current_wal); - fiber_set_session(fiber(), NULL); + fiber_set_user(fiber(), NULL); if (result < 0) panic("recover failed"); if (result == LOG_EOF) { @@ -616,8 +620,6 @@ recovery_follow_local(struct recovery_state *r, ev_tstamp wal_dir_rescan_delay) struct wal_watcher *watcher = r->watcher= &wal_watcher; - r->watcher->session = session_create(-1, 0); - ev_timer_init(&watcher->dir_timer, recovery_rescan_dir, wal_dir_rescan_delay, wal_dir_rescan_delay); watcher->dir_timer.data = watcher->stat.data = r; @@ -638,8 +640,6 @@ recovery_stop_local(struct recovery_state *r) ev_timer_stop(loop(), &watcher->dir_timer); if (ev_is_active(&watcher->stat)) ev_stat_stop(loop(), &watcher->stat); - session_destroy(watcher->session); - watcher->session = NULL; r->watcher = NULL; } diff --git a/src/box/replica.cc b/src/box/replica.cc index deff8eafbd89714840e344855d6e5b671eda392e..727a70999bff45fbf09dce6d081872400f732b35 100644 --- a/src/box/replica.cc +++ b/src/box/replica.cc @@ -40,9 +40,9 @@ #include "recovery.h" #include "xrow.h" #include "msgpuck/msgpuck.h" -#include "session.h" #include "box/cluster.h" #include "iproto_constants.h" +#include "box/session.h" static const int RECONNECT_DELAY = 1.0; @@ -223,8 +223,6 @@ pull_from_remote(va_list ap) struct ev_io coio; struct iobuf *iobuf = NULL; ev_loop *loop = loop(); - /** This fiber executes transactions. */ - SessionGuard session_guard(-1, 0); coio_init(&coio); diff --git a/src/box/session.cc b/src/box/session.cc index e790da629edff2f27d85dd850aab55abdc2cb8d5..56f2324f8c1511312ac80f7ed49fb1a04a7ec378 100644 --- a/src/box/session.cc +++ b/src/box/session.cc @@ -55,17 +55,18 @@ sid_max() } static void -session_on_stop(struct trigger * trigger, void *event) +session_on_stop(struct trigger *trigger, void * /* event */) { - (void) event; - /* Remove on_stop trigger from fiber */ + /* + * Remove on_stop trigger from the fiber, otherwise the + * fiber will attempt to destroy the trigger eventually, + * after the trigger and its memory is long gone. + */ trigger_clear(trigger); - struct session *session = fiber_get_session(fiber()); - if (session == NULL) - return; - /* Destroy session */ + struct session *session = (struct session *) + fiber_get_key(fiber(), FIBER_KEY_SESSION); + /* Destroy the session */ session_destroy(session); - fiber_set_session(fiber(), NULL); } struct session * @@ -76,12 +77,10 @@ session_create(int fd, uint64_t cookie) session->id = sid_max(); session->fd = fd; session->cookie = cookie; - session->fiber_on_stop = { - rlist_nil, session_on_stop, NULL, NULL - }; /* For on_connect triggers. */ - session_set_user(session, user_by_token(ADMIN)); - random_bytes(session->salt, SESSION_SEED_SIZE); + current_user_init(&session->user, user_by_token(GUEST)); + if (fd >= 0) + random_bytes(session->salt, SESSION_SEED_SIZE); struct mh_i32ptr_node_t node; node.key = session->id; node.val = session; @@ -96,11 +95,35 @@ session_create(int fd, uint64_t cookie) return session; } +struct session * +session_create_on_demand() +{ + /* Create session on demand */ + struct session *s = session_create(-1, 0); + s->fiber_on_stop = { + rlist_nil, session_on_stop, NULL, NULL + }; + /* Add a trigger to destroy session on fiber stop */ + trigger_add(&fiber()->on_stop, &s->fiber_on_stop); + current_user_init(&s->user, user_by_token(ADMIN)); + fiber_set_session(fiber(), s); + fiber_set_user(fiber(), &s->user); + return s; +} + +/** + * To quickly switch to admin user when executing + * on_connect/on_disconnect triggers in iproto. + */ +struct current_user admin_user; + void session_run_on_disconnect_triggers(struct session *session) { + struct fiber *fiber = fiber(); /* For triggers. */ - session_set_user(session, user_by_token(ADMIN)); + fiber_set_session(fiber, session); + fiber_set_user(fiber, &admin_user); try { trigger_run(&session_on_disconnect, NULL); } catch (Exception *e) { @@ -114,9 +137,11 @@ session_run_on_disconnect_triggers(struct session *session) void session_run_on_connect_triggers(struct session *session) { + struct fiber *fiber = fiber(); + fiber_set_session(fiber, session); + fiber_set_user(fiber, &admin_user); trigger_run(&session_on_connect, NULL); /* Set session user to guest, until it is authenticated. */ - session_set_user(session, user_by_token(GUEST)); } void @@ -144,6 +169,7 @@ session_init() if (session_registry == NULL) panic("out of memory"); mempool_create(&session_pool, &cord()->slabc, sizeof(struct session)); + current_user_init(&admin_user, user_by_token(ADMIN)); } void @@ -152,27 +178,3 @@ session_free() if (session_registry) mh_i32ptr_delete(session_registry); } - -SessionGuard::SessionGuard(int fd, uint64_t cookie) -{ - session = session_create(fd, cookie); - fiber_set_session(fiber(), session); -} - -SessionGuard::~SessionGuard() -{ - assert(session == fiber_get_session(fiber())); - session_destroy(session); - fiber_set_session(fiber(), NULL); -} - -SessionGuardWithTriggers::SessionGuardWithTriggers(int fd, uint64_t cookie) - :SessionGuard(fd, cookie) -{ - session_run_on_connect_triggers(session); -} - -SessionGuardWithTriggers::~SessionGuardWithTriggers() -{ - session_run_on_disconnect_triggers(session); -} diff --git a/src/box/session.h b/src/box/session.h index 99d583f3b4f944077d9cab4f91b927c112630925..ff0c1a16361f1edd8a6c959e994b2f6714f3c9ed 100644 --- a/src/box/session.h +++ b/src/box/session.h @@ -40,11 +40,11 @@ enum { SESSION_SEED_SIZE = 32, SESSION_DELIM_SIZE = 16 }; * Abstraction of a single user session: * for now, only provides accounting of established * sessions and on-connect/on-disconnect event - * handling, in future: user credentials, protocol, etc. + * handling, user credentials. In future: the + * client/server protocol, etc. * Session identifiers grow monotonically. * 0 sid is reserved to mean 'no session'. */ - struct session { /** Session id. */ uint32_t id; @@ -54,10 +54,8 @@ struct session { uint64_t cookie; /** Authentication salt. */ char salt[SESSION_SEED_SIZE]; - /** A look up key to quickly find session user. */ - uint8_t auth_token; - /** User id of the authenticated user. */ - uint32_t uid; + /** Cached user id and global grants */ + struct current_user user; /** Trigger for fiber on_stop to cleanup created on-demand session */ struct trigger fiber_on_stop; }; @@ -94,14 +92,6 @@ session_destroy(struct session *); struct session * session_find(uint32_t sid); -/** Set session auth token and user id. */ -static inline void -session_set_user(struct session *session, struct user_def *user) -{ - session->auth_token = user->auth_token; - session->uid = user->uid; -} - /** Global on-connect triggers. */ extern struct rlist session_on_connect; @@ -125,10 +115,10 @@ session_free(); void session_storage_cleanup(int sid); -static inline struct session * -fiber_get_session(struct fiber *fiber) +static inline void +fiber_set_user(struct fiber *fiber, struct current_user *user) { - return (struct session *) fiber_get_key(fiber, FIBER_KEY_SESSION); + fiber_set_key(fiber, FIBER_KEY_USER, user); } static inline void @@ -137,29 +127,56 @@ fiber_set_session(struct fiber *fiber, struct session *session) fiber_set_key(fiber, FIBER_KEY_SESSION, session); } -#define session() ({\ - struct session *s = fiber_get_session(fiber()); \ - /* Create session on demand */ \ - if (s == NULL) { \ - s = session_create(-1, 0); \ - fiber_set_session(fiber(), s); \ - /* Add a trigger to destroy session on fiber stop */ \ - trigger_add(&fiber()->on_stop, &s->fiber_on_stop); \ - } \ - s; }) - -/** A helper class to create and set session in single-session fibers. */ -struct SessionGuard +/** + * Create a new session on demand, and set fiber on_stop + * trigger to destroy it when this fiber ends. + */ +struct session * +session_create_on_demand(); + +/* + * For use in local hot standby, which runs directly + * from ev watchers (without current fiber), but needs + * to execute transactions. + */ +extern struct current_user admin_user; + +/* + * When creating a new fiber, the database (box) + * may not be initialized yet. When later on + * this fiber attempts to access the database, + * we have no other choice but initialize fiber-specific + * database state (something like a database connection) + * on demand. This is why this function needs to + * check whether or not the current session exists + * and create it otherwise. + */ +static inline struct session * +current_session() { - struct session *session; - SessionGuard(int fd, uint64_t cookie); - ~SessionGuard(); -}; + struct session *s = (struct session *) + fiber_get_key(fiber(), FIBER_KEY_SESSION); + return s ? s : session_create_on_demand(); +} -struct SessionGuardWithTriggers: public SessionGuard +/* + * Return the current user. Create it if it doesn't + * exist yet. + * The same rationale for initializing the current + * user on demand as in current_session() applies. + */ +static inline struct current_user * +current_user() { - SessionGuardWithTriggers(int fd, uint64_t cookie); - ~SessionGuardWithTriggers(); -}; + struct current_user *u = + (struct current_user *) fiber_get_key(fiber(), + FIBER_KEY_USER); + if (u == NULL) { + session_create_on_demand(); + u = (struct current_user *) fiber_get_key(fiber(), + FIBER_KEY_USER); + } + return u; +} #endif /* INCLUDES_TARANTOOL_SESSION_H */ diff --git a/src/box/space.cc b/src/box/space.cc index 72f5f5490b66c3c618026cfa626fcaea8a2d5a07..0713814cdaa2fc40ddd2b41b4db6f80b8c0b330d 100644 --- a/src/box/space.cc +++ b/src/box/space.cc @@ -40,7 +40,7 @@ void access_check_space(struct space *space, uint8_t access) { - struct user_def *user = user(); + struct current_user *user = current_user(); /* * If a user has a global permission, clear the respective * privilege from the list of privileges required @@ -48,11 +48,12 @@ access_check_space(struct space *space, uint8_t access) * No special check for ADMIN user is necessary * since ADMIN has universal access. */ - access &= ~user->universal_access.effective; + access &= ~user->universal_access; if (access && space->def.uid != user->uid && access & ~space->access[user->auth_token].effective) { + struct user_def *def = user_cache_find(user->uid); tnt_raise(ClientError, ER_SPACE_ACCESS_DENIED, - priv_name(access), user->name, space->def.name); + priv_name(access), def->name, space->def.name); } } diff --git a/src/box/user_cache.h b/src/box/user_cache.h index aa5172cbb5bd0e246c47a7694e1920b910693ec5..d3da115278ce7b9998f10ea751955358b9225c69 100644 --- a/src/box/user_cache.h +++ b/src/box/user_cache.h @@ -79,21 +79,6 @@ user_cache_find(uint32_t uid); struct user_def * user_cache_find_by_name(const char *name, uint32_t len); -/** - * Return the current user. - */ -#define user() \ -({ \ - struct session *s = session(); \ - struct user_def *u = user_by_token(s->auth_token); \ - if (u->auth_token != s->auth_token || \ - u->uid != s->uid) { \ - tnt_raise(ClientError, ER_NO_SUCH_USER, \ - int2str(s->uid)); \ - } \ - u; \ -}) - /** Initialize the user cache and access control subsystem. */ void user_cache_init(); diff --git a/src/box/user_def.h b/src/box/user_def.h index 31ac5c49052a66c3f4de682b1f46fa84fbf62a9e..26de9fa1adbfac9bb419e225cb794cbe34426e04 100644 --- a/src/box/user_def.h +++ b/src/box/user_def.h @@ -73,4 +73,12 @@ struct user_def { /** Predefined user ids. */ enum { GUEST = 0, ADMIN = 1, PUBLIC = 2 /* role */ }; +static inline void +current_user_init(struct current_user *user, struct user_def *def) +{ + user->auth_token = def->auth_token; + user->universal_access = def->universal_access.effective; + user->uid = def->uid; +} + #endif /* TARANTOOL_BOX_USER_DEF_H_INCLUDED */ diff --git a/src/fiber.h b/src/fiber.h index eda96e01b620afe8c2c0625192ac77bf2668552a..9db3e82477ea74e53033749c42c43596dcc67ea8 100644 --- a/src/fiber.h +++ b/src/fiber.h @@ -85,7 +85,9 @@ enum fiber_key { FIBER_KEY_LUA_STORAGE = 1, /** transaction */ FIBER_KEY_TXN = 2, - FIBER_KEY_MAX = 3 + /** User global privilege and authentication token */ + FIBER_KEY_USER = 3, + FIBER_KEY_MAX = 4 }; struct fiber { diff --git a/src/lib/salad/rlist.h b/src/lib/salad/rlist.h index 7952d07cf5157732f5a6dea3e26b5415e4087947..b9a973b01d7147437bc592330c775e344e98ac1b 100644 --- a/src/lib/salad/rlist.h +++ b/src/lib/salad/rlist.h @@ -47,6 +47,7 @@ struct rlist { struct rlist *next; }; +/* Used for static initialization of an empty list. */ extern struct rlist rlist_nil; /** diff --git a/src/lua/fiber.cc b/src/lua/fiber.cc index 6d9e42201880a301fa87d2b898787edc349ffdb9..03a155d397fd71b4db07cd40128ead67b279917a 100644 --- a/src/lua/fiber.cc +++ b/src/lua/fiber.cc @@ -260,7 +260,7 @@ lbox_fiber_info(struct lua_State *L) } static void -box_lua_fiber_run_detached(va_list ap) +box_lua_fiber_run(va_list ap) { LuarefGuard coro_guard(va_arg(ap, int)); struct lua_State *L = va_arg(ap, struct lua_State *); @@ -270,7 +270,6 @@ box_lua_fiber_run_detached(va_list ap) fiber_get_key(fiber(), FIBER_KEY_LUA_STORAGE); if (storage_ref > 0) lua_unref(L, storage_ref); - fiber_set_key(fiber(), FIBER_KEY_LUA_STORAGE, NULL); }); try { @@ -293,7 +292,7 @@ lbox_fiber_create(struct lua_State *L) luaL_error(L, "fiber.create(function, ...): bad arguments"); fiber_checkstack(); - struct fiber *f = fiber_new("lua", box_lua_fiber_run_detached); + struct fiber *f = fiber_new("lua", box_lua_fiber_run); /* Not a system fiber. */ f->flags |= FIBER_USER_MODE; struct lua_State *child_L = lua_newthread(L); diff --git a/src/lua/init.cc b/src/lua/init.cc index 17d3cc78f5b90381bcd840056cee92a83c3d8d68..cab1410d5d5c13490e8204749756b7466ca81e68 100644 --- a/src/lua/init.cc +++ b/src/lua/init.cc @@ -57,10 +57,6 @@ extern "C" { #include "lua/pickle.h" #include "lua/fio.h" -#include <ctype.h> -#include "small/region.h" -#include <stdio.h> -#include <unistd.h> #include <readline/readline.h> #include <readline/history.h> diff --git a/test/app/console.test.lua b/test/app/console.test.lua index 5a611cc4e4b7c7961534b53c1d136311cdcef5f1..148926c580d8c8ed53df3fddcd5a33cfb9237ae3 100755 --- a/test/app/console.test.lua +++ b/test/app/console.test.lua @@ -72,7 +72,7 @@ test:ok(yaml.decode(client:read(EOL))[1].error:find('access denied'), box.schema.user.create('test', { password = 'pass' }) client:write(string.format("require('console').connect('test:pass@%s')\n", IPROTO_SOCKET)) --- error: Execute access denied for user 'tester' to function 'dostring +-- error: Execute access denied for user 'test' to function 'dostring test:ok(yaml.decode(client:read(EOL))[1].error:find('access denied'), 'remote access denied') diff --git a/test/box/session.storage.result b/test/box/session.storage.result index b10f215b103064ba95113155f248109c5c7250b8..a292e1b2f287f2e476444ae66b9637ac7f928e8b 100644 --- a/test/box/session.storage.result +++ b/test/box/session.storage.result @@ -31,7 +31,7 @@ all = getmetatable(session).aggregate_storage ... dump(all) --- -- '''[null,null,null,{"abc":"cde"}]''' +- '''[null,null,{"abc":"cde"}]''' ... --# create connection second to default --# set connection second