return {
password = auth_entry.password,
privileges = privileges,
- -- Is set to nil if unknown
last_login = auth_entry.last_login,
}
end,
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)
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)
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
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()
* 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.
* 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:
// (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(¬ice_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());
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 {
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;
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;
}
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
client->create_player_on_auth_success = false;
}
+ m_script->on_authplayer(playername, addr_s, true);
acceptAuth(peer_id, wantSudo);
}
return lua_toboolean(L, -1);
}
-void ScriptApiPlayer::on_joinplayer(ServerActiveObject *player)
+void ScriptApiPlayer::on_joinplayer(ServerActiveObject *player, s64 last_login)
{
SCRIPTAPI_PRECHECKHEADER
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,
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(
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,
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
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
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;
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;
}
/* 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,