Allow to change a password from the lobby page

Fixes #2543
This commit is contained in:
phosit 2024-10-21 19:17:25 +02:00 committed by phosit
parent 5a4180efb1
commit d0ebcbe038
13 changed files with 183 additions and 17 deletions

View file

@ -0,0 +1,107 @@
/**
* The account settings page allows the player to change some of their account settings.
*/
var AccountSettingsPage = {
async openPage(xmppMessages)
{
const pageElement = Engine.GetGUIObjectByName("accountSettingsPage");
const requestResult = Engine.GetGUIObjectByName("as_RequestResult");
try
{
pageElement.hidden = false;
pageElement.onTick = updateTimers;
await Promise.race([
new Promise(resolve => {
Engine.SetGlobalHotkey("cancel", "Press", resolve);
Engine.GetGUIObjectByName("as_Close").onPress = resolve;
}),
AccountSettingsPage._changePasswordLoop(requestResult, xmppMessages)
]);
}
finally
{
requestResult.caption = "";
pageElement.hidden = true;
}
},
async _changePasswordLoop(...args)
{
const changePasswordButton = Engine.GetGUIObjectByName("as_ChangePasswordBtn")
while (true)
{
await new Promise(resolve => {
changePasswordButton.onPress = resolve;
});
try
{
changePasswordButton.enabled = false;
await AccountSettingsPage._changePassword(...args);
}
finally
{
changePasswordButton.enabled = true;
}
}
},
async _changePassword(requestResult, xmppMessages)
{
const SetPasswordError = class extends Error{};
let timeout;
try
{
requestResult.caption = "Changing password…";
const encryptedPassword = AccountSettingsPage._readAndValidatePassword(SetPasswordError);
Engine.LobbyChangePassword(encryptedPassword);
await new Promise((resolve, reject) => {
xmppMessages.registerXmppMessageHandler("system", "registered", resolve);
xmppMessages.registerXmppMessageHandler("system", "error", message => {
reject(new SetPasswordError(message.text));
});
timeout = setTimeout(reject.bind(null,
new SetPasswordError(translate("Request timed out."))), 30000);
});
requestResult.caption = translate("Password changed successfully.");
const rememberPassword = Engine.ConfigDB_GetValue("user", "lobby.rememberpassword");
const functionSufix = rememberPassword === "true" ? "CreateValue" : "RemoveValue";
Engine["ConfigDB_" + functionSufix]("user", "lobby.password", encryptedPassword);
Engine.ConfigDB_SaveChanges("user");
}
catch (e)
{
if (e instanceof SetPasswordError)
requestResult.caption = e.message;
else
{
requestResult.caption = "";
error(uneval(e));
}
}
finally
{
clearTimeout(timeout);
}
},
_readAndValidatePassword(SetPasswordError)
{
const newPass = Engine.GetGUIObjectByName("as_PasswordInput").caption;
if (newPass.length < minimumPasswordLength)
throw new SetPasswordError(translate("Please choose a longer password"));
if (Engine.GetGUIObjectByName("as_PasswordInputConfirm").caption !== newPass)
throw new SetPasswordError(translate("Passwords do not match"));
let usn = Engine.LobbyGetJID();
let atIndex = usn.indexOf("@");
if (atIndex == -1)
{
// Probably can't happen too easily, so error out.
error("Invalid JID");
throw new SetPasswordError(translate("Invalid JID, cannot change password."));
}
return Engine.EncryptPassword(newPass, usn.substring(0, atIndex).toLowerCase());
}
};

View file

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<object name="accountSettingsPage" hidden="true">
<script directory="gui/lobby/AccountSettingsPage/"/>
<!-- Translucent black background -->
<object type="image" z="100" sprite="ModernFade"/>
<object type="image" style="ModernDialog" size="50%-230 50%-150 50%+230 50%+150" z="101">
<object style="ModernLabelText" type="text" size="50%-128 -18 50%+128 14">
<translatableAttribute id="caption">Account settings</translatableAttribute>
</object>
<object size="24 32 100%-24 100%-50">
<object type="text" size="0 0 100% 24" style="ModernLeftLabelText">
<translatableAttribute id="caption">New password:</translatableAttribute>
</object>
<object name="as_PasswordInput" mask="true" mask_char="*" size="0 24 100% 48" type="input" style="ModernInput" font="sans-13"/>
<object type="text" size="0 50 100% 76" style="ModernLeftLabelText">
<translatableAttribute id="caption">Confirm new password:</translatableAttribute>
</object>
<object name="as_PasswordInputConfirm" mask="true" mask_char="*" size="0 76 100% 100" type="input" style="ModernInput" font="sans-13"/>
<object name="as_RequestResult" type="text" size="0 125 100% 150" style="ModernLeftLabelText"/>
<object size="18 100%-32 100%-18 100%">
<object type="button" name="as_Close" style="ModernButtonRed" size="0 0 50%-5 100%">
<translatableAttribute id="caption">Close</translatableAttribute>
</object>
<object type="button" name="as_ChangePasswordBtn" style="ModernButtonRed" size="50%+5 0 100% 100%">
<translatableAttribute id="caption">Change password</translatableAttribute>
</object>
</object>
</object>
</object>
</object>

View file

@ -9,12 +9,15 @@ class LobbyPage
Engine.ProfileStart("Create LobbyPage");
let mapCache = new MapCache();
let buddyButton = new BuddyButton(xmppMessages);
const accountSettingsButton = Engine.GetGUIObjectByName("accountSettingsButton");
accountSettingsButton.onPress = AccountSettingsPage.openPage.bind(null, xmppMessages);
let gameList = new GameList(xmppMessages, buddyButton, mapCache);
let playerList = new PlayerList(xmppMessages, buddyButton, gameList);
this.lobbyPage = {
"buttons": {
"buddyButton": buddyButton,
"accountSettingsButton": accountSettingsButton,
"hostButton": new HostButton(dialog, xmppMessages),
"joinButton": new JoinButton(dialog, gameList),
"leaderboardButton": new LeaderboardButton(xmppMessages, leaderboardPage),

View file

@ -16,16 +16,19 @@
<object name="lobbyPanels" size="0 40 100% 100%-20">
<object name="leftPanel" size="20 0 20% 100%+20">
<object size="0 0 100% 100%-315">
<object size="0 0 100% 100%-342">
<include file="gui/lobby/LobbyPage/PlayerList.xml"/>
</object>
<object size="0 100%-310 100% 100%-104">
<object size="0 100%-337 100% 100%-131">
<include file="gui/lobby/LobbyPage/ProfilePanel.xml"/>
</object>
<object size="0 100%-98 100% 100%">
<object size="0 100%-125 100% 100%">
<object name="toggleBuddyButton" type="button" style="ModernButtonRed" size="0 0 100% 25"/>
<object name="leaderboardButton" type="button" style="ModernButtonRed" size="0 27 100% 52"/>
<object name="profileButton" type="button" style="ModernButtonRed" size="0 54 100% 79"/>
<object name="accountSettingsButton" type="button" style="ModernButtonRed" size="0 27 100% 52">
<translatableAttribute id="caption">Account settings</translatableAttribute>
</object>
<object name="leaderboardButton" type="button" style="ModernButtonRed" size="0 54 100% 79"/>
<object name="profileButton" type="button" style="ModernButtonRed" size="0 81 100% 106"/>
</object>
</object>

View file

@ -9,6 +9,7 @@
<object>
<include file="gui/lobby/LobbyPage/LobbyPage.xml"/>
<include file="gui/lobby/AccountSettingsPage/AccountSettingsPage.xml"/>
<include file="gui/lobby/LeaderboardPage/LeaderboardPage.xml"/>
<include file="gui/lobby/ProfilePage/ProfilePage.xml"/>

View file

@ -0,0 +1,4 @@
/**
* Common definitions for the lobby password.
*/
const minimumPasswordLength = 8;

View file

@ -19,7 +19,7 @@ function checkPassword(register)
translateWithContext("register", "Please enter your password") :
translateWithContext("login", "Please enter your password");
if (register && password.length < 8)
if (register && password.length < minimumPasswordLength)
return translate("Please choose a longer password");
return "";

View file

@ -6,6 +6,7 @@
<script directory="gui/prelobby/common/feedback/"/>
<script directory="gui/prelobby/common/terms/"/>
<script directory="gui/prelobby/login/"/>
<script file="gui/lobby/password.js"/>
<object type="image" style="ModernDialog" size="50%-230 50%-190 50%+230 50%+190">

View file

@ -6,6 +6,7 @@
<script directory="gui/prelobby/common/feedback/"/>
<script directory="gui/prelobby/common/terms/"/>
<script directory="gui/prelobby/register/"/>
<script file="gui/lobby/password.js"/>
<object type="image" style="ModernDialog" size="50%-230 50%-210 50%+230 50%+210">

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2021 Wildfire Games.
/* Copyright (C) 2024 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -43,6 +43,7 @@ public:
virtual void SetNick(const std::string& nick) = 0;
virtual std::string GetNick() const = 0;
virtual std::string GetJID() const = 0;
virtual void ChangePassword(const std::string& newPassword) = 0;
virtual void kick(const std::string& nick, const std::string& reason) = 0;
virtual void ban(const std::string& nick, const std::string& reason) = 0;
virtual void SetPresence(const std::string& presence) = 0;

View file

@ -73,6 +73,7 @@ XmppClient::XmppClient(const ScriptInterface* scriptInterface, const std::string
m_client(nullptr),
m_mucRoom(nullptr),
m_registration(nullptr),
m_regOpt(regOpt),
m_username(sUsername),
m_password(sPassword),
m_room(sRoom),
@ -143,6 +144,9 @@ XmppClient::XmppClient(const ScriptInterface* scriptInterface, const std::string
m_client->registerMessageHandler(this);
m_registration = new gloox::Registration(m_client);
m_registration->registerRegistrationHandler(this);
// Uncomment to see the raw stanzas
// m_client->logInstance().registerLogHandler(gloox::LogLevelDebug, gloox::LogAreaAll, this);
@ -153,12 +157,6 @@ XmppClient::XmppClient(const ScriptInterface* scriptInterface, const std::string
// Get room history.
m_mucRoom->setRequestHistory(historyRequestSize, gloox::MUCRoom::HistoryMaxStanzas);
}
else
{
// Registration
m_registration = new gloox::Registration(m_client);
m_registration->registerRegistrationHandler(this);
}
m_sessionManager = new gloox::Jingle::SessionManager(m_client, this);
// Register plugins to allow gloox parse them in incoming sessions
@ -247,7 +245,7 @@ void XmppClient::onConnect()
m_mucRoom->join();
}
if (m_registration)
if (m_regOpt)
m_registration->fetchRegistrationFields();
}
@ -535,8 +533,6 @@ void XmppClient::handleRegistrationResult(const gloox::JID&, gloox::Registration
CreateGUIMessage("system", "registered", std::time(nullptr));
else
CreateGUIMessage("system", "error", std::time(nullptr), "text", result);
disconnect();
}
void XmppClient::handleAlreadyRegistered(const gloox::JID&)
@ -1209,6 +1205,16 @@ std::string XmppClient::GetJID() const
return m_client->jid().full();
}
/**
* Change password for authenticated user.
*
* @param newPassword New password
*/
void XmppClient::ChangePassword(const std::string& newPassword)
{
m_registration->changePassword(m_username, newPassword);
}
/**
* Kick a player from the current room.
*

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2021 Wildfire Games.
/* Copyright (C) 2024 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -57,6 +57,7 @@ private:
gloox::CertStatus m_certStatus;
bool m_initialLoadComplete;
bool m_isConnected;
bool m_regOpt;
public:
// Basic
@ -87,6 +88,7 @@ public:
void SetNick(const std::string& nick) override;
std::string GetNick() const override;
std::string GetJID() const override;
void ChangePassword(const std::string& newPassword) override;
void kick(const std::string& nick, const std::string& reason) override;
void ban(const std::string& nick, const std::string& reason) override;
void SetPresence(const std::string& presence) override;

View file

@ -218,6 +218,7 @@ void RegisterScriptFunctions(const ScriptRequest& rq)
REGISTER_XMPP(SetNick, "LobbySetNick");
REGISTER_XMPP(GetNick, "LobbyGetNick");
REGISTER_XMPP(GetJID, "LobbyGetJID");
REGISTER_XMPP(ChangePassword, "LobbyChangePassword");
REGISTER_XMPP(kick, "LobbyKick");
REGISTER_XMPP(ban, "LobbyBan");
REGISTER_XMPP(GetPresence, "LobbyGetPlayerPresence");