Add on_authplayer callback and 'last_login' to on_joinplayer (#9574)
authorsorcerykid <rkrause@inbox.com>
Sat, 23 May 2020 11:24:06 +0000 (06:24 -0500)
committerSmallJoker <mk939@ymail.com>
Sat, 23 May 2020 11:25:29 +0000 (13:25 +0200)
    Replace on_auth_fail callback with more versatile on_authplayer
    Better clarify account login process in Lua API documentation
    Change initial timestamp for newly registered accounts to -1

builtin/game/auth.lua
builtin/game/chat.lua
builtin/game/deprecated.lua
builtin/game/register.lua
doc/lua_api.txt
src/network/serverpackethandler.cpp
src/script/cpp_api/s_player.cpp
src/script/cpp_api/s_player.h
src/script/cpp_api/s_server.cpp
src/script/cpp_api/s_server.h

index 7aedfc82ed490345b455d7818e22c2d9269b4720..fc061666c69052831aa1a64f286e1088b8cc6a81 100644 (file)
@@ -41,7 +41,6 @@ core.builtin_auth_handler = {
                return {
                        password = auth_entry.password,
                        privileges = privileges,
-                       -- Is set to nil if unknown
                        last_login = auth_entry.last_login,
                }
        end,
@@ -53,7 +52,7 @@ core.builtin_auth_handler = {
                        name = name,
                        password = password,
                        privileges = core.string_to_privs(core.settings:get("default_privs")),
-                       last_login = os.time(),
+                       last_login = -1,  -- Defer login time calculation until record_login (called by on_joinplayer)
                })
        end,
        delete_auth = function(name)
index b9f84e522a27445b89db5556c97b5a5b413f51ec..aae811794d7dee49a44dac0d85d5355c62d53265 100644 (file)
@@ -1068,7 +1068,7 @@ core.register_chatcommand("last-login", {
                        param = name
                end
                local pauth = core.get_auth_handler().get_auth(param)
-               if pauth and pauth.last_login then
+               if pauth and pauth.last_login and pauth.last_login ~= -1 then
                        -- Time in UTC, ISO 8601 format
                        return true, "Last login time was " ..
                                os.date("!%Y-%m-%dT%H:%M:%SZ", pauth.last_login)
index 73e105eb86c5720c0b1baff6b08c449d1c7c6cc0..20f0482eb374f11be65e073b01c8048d1e1bfa4f 100644 (file)
@@ -70,3 +70,19 @@ core.setting_get = setting_proxy("get")
 core.setting_setbool = setting_proxy("set_bool")
 core.setting_getbool = setting_proxy("get_bool")
 core.setting_save = setting_proxy("write")
+
+--
+-- core.register_on_auth_fail
+--
+
+function core.register_on_auth_fail(func)
+       core.log("deprecated", "core.register_on_auth_fail " ..
+               "is obsolete and should be replaced by " ..
+               "core.register_on_authplayer instead.")
+
+       core.register_on_authplayer(function (player_name, ip, is_success)
+               if not is_success then
+                       func(player_name, ip)
+               end
+       end)
+end
index eb6c2897ceb80c6552755bcd503335a08392b5c9..1034d4f2bd3bf8ddffc779231400d5b31eab763e 100644 (file)
@@ -607,9 +607,9 @@ core.registered_on_item_eats, core.register_on_item_eat = make_registration()
 core.registered_on_punchplayers, core.register_on_punchplayer = make_registration()
 core.registered_on_priv_grant, core.register_on_priv_grant = make_registration()
 core.registered_on_priv_revoke, core.register_on_priv_revoke = make_registration()
+core.registered_on_authplayers, core.register_on_authplayer = make_registration()
 core.registered_can_bypass_userlimit, core.register_can_bypass_userlimit = make_registration()
 core.registered_on_modchannel_message, core.register_on_modchannel_message = make_registration()
-core.registered_on_auth_fail, core.register_on_auth_fail = make_registration()
 core.registered_on_player_inventory_actions, core.register_on_player_inventory_action = make_registration()
 core.registered_allow_player_inventory_actions, core.register_allow_player_inventory_action = make_registration()
 
index bd0cb8acbf62468f18edc910b91a6b0d54844fb1..26061eccbe93430fc420b1bb0d7bf2cce336185f 100644 (file)
@@ -4374,7 +4374,7 @@ Call these functions only at load time!
     * Called after generating a piece of world. Modifying nodes inside the area
       is a bit faster than usually.
 * `minetest.register_on_newplayer(function(ObjectRef))`
-    * Called after a new player has been created
+    * Called when a new player enters the world for the first time
 * `minetest.register_on_punchplayer(function(player, hitter, time_from_last_punch, tool_capabilities, dir, damage))`
     * Called when a player is punched
     * Note: This callback is invoked even if the punched player is dead.
@@ -4415,19 +4415,23 @@ Call these functions only at load time!
     * Called _before_ repositioning of player occurs
     * return true in func to disable regular player placement
 * `minetest.register_on_prejoinplayer(function(name, ip))`
-    * Called before a player joins the game
-    * If it returns a string, the player is disconnected with that string as
+    * Called when a client connects to the server, prior to authentication
+    * If it returns a string, the client is disconnected with that string as
       reason.
-* `minetest.register_on_joinplayer(function(ObjectRef))`
+* `minetest.register_on_joinplayer(function(ObjectRef, last_login))`
     * Called when a player joins the game
+    * `last_login`: The timestamp of the previous login, or nil if player is new
 * `minetest.register_on_leaveplayer(function(ObjectRef, timed_out))`
     * Called when a player leaves the game
     * `timed_out`: True for timeout, false for other reasons.
+* `minetest.register_on_authplayer(function(name, ip, is_success))`
+    * Called when a client attempts to log into an account.
+    * `name`: The name of the account being authenticated.
+    * `ip`: The IP address of the client
+    * `is_success`: Whether the client was successfully authenticated
+    * For newly registered accounts, `is_success` will always be true
 * `minetest.register_on_auth_fail(function(name, ip))`
-    * Called when a client attempts to log into an account but supplies the
-      wrong password.
-    * `ip`: The IP address of the client.
-    * `name`: The account the client attempted to log into.
+    * Deprecated: use `minetest.register_on_authplayer(name, ip, is_success)` instead.
 * `minetest.register_on_cheat(function(ObjectRef, cheat))`
     * Called when a player cheats
     * `cheat`: `{type=<cheat_type>}`, where `<cheat_type>` is one of:
index 2fa9d4196f613ab5125d86c07d5390620240cc9b..fed3b6f851703d33e6ea89be5d20a3af66ec8c88 100644 (file)
@@ -409,9 +409,12 @@ void Server::handleCommand_ClientReady(NetworkPacket* pkt)
        // (u16) 1 + std::string represents a pseudo vector serialization representation
        notice_pkt << (u8) PLAYER_LIST_ADD << (u16) 1 << std::string(playersao->getPlayer()->getName());
        m_clients.sendToAll(&notice_pkt);
-
        m_clients.event(peer_id, CSE_SetClientReady);
-       m_script->on_joinplayer(playersao);
+
+       s64 last_login;
+       m_script->getAuth(playersao->getPlayer()->getName(), nullptr, nullptr, &last_login);
+       m_script->on_joinplayer(playersao, last_login);
+
        // Send shutdown timer if shutdown has been scheduled
        if (m_shutdown_state.isTimerRunning()) {
                SendChatMessage(peer_id, m_shutdown_state.getShutdownTimerMessage());
@@ -1512,6 +1515,7 @@ void Server::handleCommand_FirstSrp(NetworkPacket* pkt)
 
                initial_ver_key = encode_srp_verifier(verification_key, salt);
                m_script->createAuth(playername, initial_ver_key);
+               m_script->on_authplayer(playername, addr_s, true);
 
                acceptAuth(peer_id, false);
        } else {
@@ -1648,24 +1652,25 @@ void Server::handleCommand_SrpBytesM(NetworkPacket* pkt)
        session_t peer_id = pkt->getPeerId();
        RemoteClient *client = getClient(peer_id, CS_Invalid);
        ClientState cstate = client->getState();
+       std::string addr_s = getPeerAddress(pkt->getPeerId()).serializeString();
+       std::string playername = client->getName();
 
        bool wantSudo = (cstate == CS_Active);
 
        verbosestream << "Server: Received TOCLIENT_SRP_BYTES_M." << std::endl;
 
        if (!((cstate == CS_HelloSent) || (cstate == CS_Active))) {
-               actionstream << "Server: got SRP _M packet in wrong state " << cstate <<
-                       " from " << getPeerAddress(peer_id).serializeString() <<
-                       ". Ignoring." << std::endl;
+               actionstream << "Server: got SRP _M packet in wrong state "
+                       << cstate << " from " << addr_s
+                       << ". Ignoring." << std::endl;
                return;
        }
 
        if (client->chosen_mech != AUTH_MECHANISM_SRP &&
                        client->chosen_mech != AUTH_MECHANISM_LEGACY_PASSWORD) {
-               actionstream << "Server: got SRP _M packet, while auth is going on "
-                       "with mech " << client->chosen_mech << " from " <<
-                       getPeerAddress(peer_id).serializeString() <<
-                       " (wantSudo=" << wantSudo << "). Denying." << std::endl;
+               actionstream << "Server: got SRP _M packet, while auth"
+                       << "is going on with mech " << client->chosen_mech << " from " 
+                       << addr_s << " (wantSudo=" << wantSudo << "). Denying." << std::endl;
                if (wantSudo) {
                        DenySudoAccess(peer_id);
                        return;
@@ -1680,9 +1685,8 @@ void Server::handleCommand_SrpBytesM(NetworkPacket* pkt)
 
        if (srp_verifier_get_session_key_length((SRPVerifier *) client->auth_data)
                        != bytes_M.size()) {
-               actionstream << "Server: User " << client->getName() << " at " <<
-                       getPeerAddress(peer_id).serializeString() <<
-                       " sent bytes_M with invalid length " << bytes_M.size() << std::endl;
+               actionstream << "Server: User " << playername << " at " << addr_s
+                       << " sent bytes_M with invalid length " << bytes_M.size() << std::endl;
                DenyAccess(peer_id, SERVER_ACCESSDENIED_UNEXPECTED_DATA);
                return;
        }
@@ -1694,24 +1698,21 @@ void Server::handleCommand_SrpBytesM(NetworkPacket* pkt)
 
        if (!bytes_HAMK) {
                if (wantSudo) {
-                       actionstream << "Server: User " << client->getName() << " at " <<
-                               getPeerAddress(peer_id).serializeString() <<
-                               " tried to change their password, but supplied wrong (SRP) "
-                               "password for authentication." << std::endl;
+                       actionstream << "Server: User " << playername << " at " << addr_s
+                               << " tried to change their password, but supplied wrong"
+                               << " (SRP) password for authentication." << std::endl;
                        DenySudoAccess(peer_id);
                        return;
                }
 
-               std::string ip = getPeerAddress(peer_id).serializeString();
-               actionstream << "Server: User " << client->getName() << " at " << ip <<
-                       " supplied wrong password (auth mechanism: SRP)." << std::endl;
-               m_script->on_auth_failure(client->getName(), ip);
+               actionstream << "Server: User " << playername << " at " << addr_s
+                       << " supplied wrong password (auth mechanism: SRP)." << std::endl;
+               m_script->on_authplayer(playername, addr_s, false);
                DenyAccess(peer_id, SERVER_ACCESSDENIED_WRONG_PASSWORD);
                return;
        }
 
        if (client->create_player_on_auth_success) {
-               std::string playername = client->getName();
                m_script->createAuth(playername, client->enc_pwd);
 
                std::string checkpwd; // not used, but needed for passing something
@@ -1725,6 +1726,7 @@ void Server::handleCommand_SrpBytesM(NetworkPacket* pkt)
                client->create_player_on_auth_success = false;
        }
 
+       m_script->on_authplayer(playername, addr_s, true);
        acceptAuth(peer_id, wantSudo);
 }
 
index df67ea00c5fb789f3a149dbcdebd14423bb85c74..712120c61d5885ef9437e97aab3dccb4deda3a32 100644 (file)
@@ -147,7 +147,7 @@ bool ScriptApiPlayer::can_bypass_userlimit(const std::string &name, const std::s
        return lua_toboolean(L, -1);
 }
 
-void ScriptApiPlayer::on_joinplayer(ServerActiveObject *player)
+void ScriptApiPlayer::on_joinplayer(ServerActiveObject *player, s64 last_login)
 {
        SCRIPTAPI_PRECHECKHEADER
 
@@ -156,7 +156,11 @@ void ScriptApiPlayer::on_joinplayer(ServerActiveObject *player)
        lua_getfield(L, -1, "registered_on_joinplayers");
        // Call callbacks
        objectrefGetOrCreate(L, player);
-       runCallbacks(1, RUN_CALLBACKS_MODE_FIRST);
+       if (last_login != -1)
+               lua_pushinteger(L, last_login);
+       else
+               lua_pushnil(L);
+       runCallbacks(2, RUN_CALLBACKS_MODE_FIRST);
 }
 
 void ScriptApiPlayer::on_leaveplayer(ServerActiveObject *player,
@@ -216,16 +220,19 @@ void ScriptApiPlayer::on_playerReceiveFields(ServerActiveObject *player,
        runCallbacks(3, RUN_CALLBACKS_MODE_OR_SC);
 }
 
-void ScriptApiPlayer::on_auth_failure(const std::string &name, const std::string &ip)
+void ScriptApiPlayer::on_authplayer(const std::string &name, const std::string &ip, bool is_success)
 {
        SCRIPTAPI_PRECHECKHEADER
 
-       // Get core.registered_on_auth_failure
+       // Get core.registered_on_authplayers
        lua_getglobal(L, "core");
-       lua_getfield(L, -1, "registered_on_auth_fail");
+       lua_getfield(L, -1, "registered_on_authplayers");
+
+       // Call callbacks
        lua_pushstring(L, name.c_str());
        lua_pushstring(L, ip.c_str());
-       runCallbacks(2, RUN_CALLBACKS_MODE_FIRST);
+       lua_pushboolean(L, is_success);
+       runCallbacks(3, RUN_CALLBACKS_MODE_FIRST);
 }
 
 void ScriptApiPlayer::pushMoveArguments(
index 7ca3d8f30c5b610ecaad0017a754963cca6e29fa..a337f975bfd4437df338a003bff0afd9b48ec445 100644 (file)
@@ -41,7 +41,7 @@ public:
        bool on_prejoinplayer(const std::string &name, const std::string &ip,
                        std::string *reason);
        bool can_bypass_userlimit(const std::string &name, const std::string &ip);
-       void on_joinplayer(ServerActiveObject *player);
+       void on_joinplayer(ServerActiveObject *player, s64 last_login);
        void on_leaveplayer(ServerActiveObject *player, bool timeout);
        void on_cheat(ServerActiveObject *player, const std::string &cheat_type);
        bool on_punchplayer(ServerActiveObject *player, ServerActiveObject *hitter,
@@ -51,7 +51,7 @@ public:
                        const PlayerHPChangeReason &reason);
        void on_playerReceiveFields(ServerActiveObject *player,
                        const std::string &formname, const StringMap &fields);
-       void on_auth_failure(const std::string &name, const std::string &ip);
+       void on_authplayer(const std::string &name, const std::string &ip, bool is_success);
 
        // Player inventory callbacks
        // Return number of accepted items to be moved
index 1ce2f9d45d029fa8082364d966ba5544934971f6..96cb28b28936e3922997042f8badf6735541492f 100644 (file)
@@ -23,7 +23,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 bool ScriptApiServer::getAuth(const std::string &playername,
                std::string *dst_password,
-               std::set<std::string> *dst_privs)
+               std::set<std::string> *dst_privs,
+               s64 *dst_last_login)
 {
        SCRIPTAPI_PRECHECKHEADER
 
@@ -43,8 +44,7 @@ bool ScriptApiServer::getAuth(const std::string &playername,
        luaL_checktype(L, -1, LUA_TTABLE);
 
        std::string password;
-       bool found = getstringfield(L, -1, "password", password);
-       if (!found)
+       if (!getstringfield(L, -1, "password", password))
                throw LuaError("Authentication handler didn't return password");
        if (dst_password)
                *dst_password = password;
@@ -54,7 +54,13 @@ bool ScriptApiServer::getAuth(const std::string &playername,
                throw LuaError("Authentication handler didn't return privilege table");
        if (dst_privs)
                readPrivileges(-1, *dst_privs);
-       lua_pop(L, 1);
+       lua_pop(L, 1);  // Remove key from privs table
+
+       s64 last_login;
+       if(!getintfield(L, -1, "last_login", last_login))
+               throw LuaError("Authentication handler didn't return last_login");
+       if (dst_last_login)
+               *dst_last_login = (s64)last_login;
 
        return true;
 }
index a4cede84d4a60afc6a2a40e56d5d8e1b387ff3d2..d8639cba789e87f766db577cd0db18661077db7e 100644 (file)
@@ -43,7 +43,8 @@ public:
        /* auth */
        bool getAuth(const std::string &playername,
                std::string *dst_password,
-               std::set<std::string> *dst_privs);
+               std::set<std::string> *dst_privs,
+               s64 *dst_last_login = nullptr);
        void createAuth(const std::string &playername,
                const std::string &password);
        bool setPassword(const std::string &playername,