use hashed passwords in all commands (#4493)
* protocol changes * server changes * client changes for password reset and registration * add hashed password to change password in client * always use hashed password to log in * add warning to client when using plain text password * require real password for changing email on server this is backwards compatible as users logged in with a real password on older clients will not need this, only users logged in with a hashed password * implement password dialog when changing email * require min password length * use qstringlist to build query instead * use clear instead of = "" * add max to password dialog * use proper const ness in abstractclient * reject too long passwords instead of trimming
This commit is contained in:
parent
fcafcb340a
commit
2fc85e0c08
17 changed files with 330 additions and 96 deletions
|
@ -22,7 +22,8 @@
|
||||||
|
|
||||||
#include <google/protobuf/descriptor.h>
|
#include <google/protobuf/descriptor.h>
|
||||||
|
|
||||||
AbstractClient::AbstractClient(QObject *parent) : QObject(parent), nextCmdId(0), status(StatusDisconnected)
|
AbstractClient::AbstractClient(QObject *parent)
|
||||||
|
: QObject(parent), nextCmdId(0), status(StatusDisconnected), serverSupportsPasswordHash(false)
|
||||||
{
|
{
|
||||||
qRegisterMetaType<QVariant>("QVariant");
|
qRegisterMetaType<QVariant>("QVariant");
|
||||||
qRegisterMetaType<CommandContainer>("CommandContainer");
|
qRegisterMetaType<CommandContainer>("CommandContainer");
|
||||||
|
|
|
@ -88,6 +88,7 @@ protected slots:
|
||||||
protected:
|
protected:
|
||||||
QMap<int, PendingCommand *> pendingCommands;
|
QMap<int, PendingCommand *> pendingCommands;
|
||||||
QString userName, password, email, country, realName, token;
|
QString userName, password, email, country, realName, token;
|
||||||
|
bool serverSupportsPasswordHash;
|
||||||
void setStatus(ClientStatus _status);
|
void setStatus(ClientStatus _status);
|
||||||
int getNewCmdId()
|
int getNewCmdId()
|
||||||
{
|
{
|
||||||
|
@ -107,7 +108,11 @@ public:
|
||||||
void sendCommand(const CommandContainer &cont);
|
void sendCommand(const CommandContainer &cont);
|
||||||
void sendCommand(PendingCommand *pend);
|
void sendCommand(PendingCommand *pend);
|
||||||
|
|
||||||
const QString getUserName()
|
bool getServerSupportsPasswordHash() const
|
||||||
|
{
|
||||||
|
return serverSupportsPasswordHash;
|
||||||
|
}
|
||||||
|
const QString &getUserName() const
|
||||||
{
|
{
|
||||||
return userName;
|
return userName;
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,11 @@ DlgEditPassword::DlgEditPassword(QWidget *parent) : QDialog(parent)
|
||||||
|
|
||||||
void DlgEditPassword::actOk()
|
void DlgEditPassword::actOk()
|
||||||
{
|
{
|
||||||
if (newPasswordEdit->text() != newPasswordEdit2->text()) {
|
// TODO this stuff should be using qvalidators
|
||||||
|
if (newPasswordEdit->text().length() < 8) {
|
||||||
|
QMessageBox::critical(this, tr("Error"), tr("Your password is too short."));
|
||||||
|
return;
|
||||||
|
} else if (newPasswordEdit->text() != newPasswordEdit2->text()) {
|
||||||
QMessageBox::warning(this, tr("Error"), tr("The new passwords don't match."));
|
QMessageBox::warning(this, tr("Error"), tr("The new passwords don't match."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,7 +123,11 @@ void DlgForgotPasswordReset::actOk()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newpasswordEdit->text() != newpasswordverifyEdit->text()) {
|
// TODO this stuff should be using qvalidators
|
||||||
|
if (newpasswordEdit->text().length() < 8) {
|
||||||
|
QMessageBox::critical(this, tr("Error"), tr("Your password is too short."));
|
||||||
|
return;
|
||||||
|
} else if (newpasswordEdit->text() != newpasswordverifyEdit->text()) {
|
||||||
QMessageBox::critical(this, tr("Reset Password Error"), tr("The passwords do not match."));
|
QMessageBox::critical(this, tr("Reset Password Error"), tr("The passwords do not match."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -356,7 +356,11 @@ DlgRegister::DlgRegister(QWidget *parent) : QDialog(parent)
|
||||||
|
|
||||||
void DlgRegister::actOk()
|
void DlgRegister::actOk()
|
||||||
{
|
{
|
||||||
if (passwordEdit->text() != passwordConfirmationEdit->text()) {
|
// TODO this stuff should be using qvalidators
|
||||||
|
if (passwordEdit->text().length() < 8) {
|
||||||
|
QMessageBox::critical(this, tr("Registration Warning"), tr("Your password is too short."));
|
||||||
|
return;
|
||||||
|
} else if (passwordEdit->text() != passwordConfirmationEdit->text()) {
|
||||||
QMessageBox::critical(this, tr("Registration Warning"), tr("Your passwords do not match, please try again."));
|
QMessageBox::critical(this, tr("Registration Warning"), tr("Your passwords do not match, please try again."));
|
||||||
return;
|
return;
|
||||||
} else if (emailConfirmationEdit->text() != emailEdit->text()) {
|
} else if (emailConfirmationEdit->text() != emailEdit->text()) {
|
||||||
|
|
|
@ -27,7 +27,7 @@ static const unsigned int protocolVersion = 14;
|
||||||
|
|
||||||
RemoteClient::RemoteClient(QObject *parent)
|
RemoteClient::RemoteClient(QObject *parent)
|
||||||
: AbstractClient(parent), timeRunning(0), lastDataReceived(0), messageInProgress(false), handshakeStarted(false),
|
: AbstractClient(parent), timeRunning(0), lastDataReceived(0), messageInProgress(false), handshakeStarted(false),
|
||||||
usingWebSocket(false), messageLength(0)
|
usingWebSocket(false), messageLength(0), hashedPassword()
|
||||||
{
|
{
|
||||||
|
|
||||||
clearNewClientFeatures();
|
clearNewClientFeatures();
|
||||||
|
@ -110,6 +110,7 @@ void RemoteClient::processServerIdentificationEvent(const Event_ServerIdentifica
|
||||||
setStatus(StatusDisconnecting);
|
setStatus(StatusDisconnecting);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
serverSupportsPasswordHash = event.server_options() & Event_ServerIdentification::SupportsPasswordHash;
|
||||||
|
|
||||||
if (getStatus() == StatusRequestingForgotPassword) {
|
if (getStatus() == StatusRequestingForgotPassword) {
|
||||||
Command_ForgotPasswordRequest cmdForgotPasswordRequest;
|
Command_ForgotPasswordRequest cmdForgotPasswordRequest;
|
||||||
|
@ -127,7 +128,14 @@ void RemoteClient::processServerIdentificationEvent(const Event_ServerIdentifica
|
||||||
cmdForgotPasswordReset.set_user_name(userName.toStdString());
|
cmdForgotPasswordReset.set_user_name(userName.toStdString());
|
||||||
cmdForgotPasswordReset.set_clientid(getSrvClientID(lastHostname).toStdString());
|
cmdForgotPasswordReset.set_clientid(getSrvClientID(lastHostname).toStdString());
|
||||||
cmdForgotPasswordReset.set_token(token.toStdString());
|
cmdForgotPasswordReset.set_token(token.toStdString());
|
||||||
cmdForgotPasswordReset.set_new_password(password.toStdString());
|
if (!password.isEmpty() && serverSupportsPasswordHash) {
|
||||||
|
auto passwordSalt = PasswordHasher::generateRandomSalt();
|
||||||
|
hashedPassword = PasswordHasher::computeHash(password, passwordSalt);
|
||||||
|
cmdForgotPasswordReset.set_hashed_new_password(hashedPassword.toStdString());
|
||||||
|
} else if (!password.isEmpty()) {
|
||||||
|
qWarning() << "using plain text password to reset password";
|
||||||
|
cmdForgotPasswordReset.set_new_password(password.toStdString());
|
||||||
|
}
|
||||||
PendingCommand *pend = prepareSessionCommand(cmdForgotPasswordReset);
|
PendingCommand *pend = prepareSessionCommand(cmdForgotPasswordReset);
|
||||||
connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this,
|
connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this,
|
||||||
SLOT(submitForgotPasswordResetResponse(Response)));
|
SLOT(submitForgotPasswordResetResponse(Response)));
|
||||||
|
@ -150,7 +158,14 @@ void RemoteClient::processServerIdentificationEvent(const Event_ServerIdentifica
|
||||||
if (getStatus() == StatusRegistering) {
|
if (getStatus() == StatusRegistering) {
|
||||||
Command_Register cmdRegister;
|
Command_Register cmdRegister;
|
||||||
cmdRegister.set_user_name(userName.toStdString());
|
cmdRegister.set_user_name(userName.toStdString());
|
||||||
cmdRegister.set_password(password.toStdString());
|
if (!password.isEmpty() && serverSupportsPasswordHash) {
|
||||||
|
auto passwordSalt = PasswordHasher::generateRandomSalt();
|
||||||
|
hashedPassword = PasswordHasher::computeHash(password, passwordSalt);
|
||||||
|
cmdRegister.set_hashed_password(hashedPassword.toStdString());
|
||||||
|
} else if (!password.isEmpty()) {
|
||||||
|
qWarning() << "using plain text password to register";
|
||||||
|
cmdRegister.set_password(password.toStdString());
|
||||||
|
}
|
||||||
cmdRegister.set_email(email.toStdString());
|
cmdRegister.set_email(email.toStdString());
|
||||||
cmdRegister.set_country(country.toStdString());
|
cmdRegister.set_country(country.toStdString());
|
||||||
cmdRegister.set_real_name(realName.toStdString());
|
cmdRegister.set_real_name(realName.toStdString());
|
||||||
|
@ -175,13 +190,7 @@ void RemoteClient::processServerIdentificationEvent(const Event_ServerIdentifica
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!password.isEmpty() && event.server_options() & Event_ServerIdentification::SupportsPasswordHash) {
|
doLogin();
|
||||||
// TODO store and log in using stored hashed password
|
|
||||||
doRequestPasswordSalt(); // log in using password salt
|
|
||||||
} else {
|
|
||||||
// TODO add setting for client to reject unhashed logins
|
|
||||||
doLogin();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoteClient::doRequestPasswordSalt()
|
void RemoteClient::doRequestPasswordSalt()
|
||||||
|
@ -213,21 +222,33 @@ Command_Login RemoteClient::generateCommandLogin()
|
||||||
|
|
||||||
void RemoteClient::doLogin()
|
void RemoteClient::doLogin()
|
||||||
{
|
{
|
||||||
setStatus(StatusLoggingIn);
|
if (!password.isEmpty() && serverSupportsPasswordHash) {
|
||||||
Command_Login cmdLogin = generateCommandLogin();
|
// TODO store and log in using stored hashed password
|
||||||
cmdLogin.set_password(password.toStdString());
|
if (hashedPassword.isEmpty()) {
|
||||||
|
doRequestPasswordSalt(); // ask salt to create hashedPassword, then log in
|
||||||
|
} else {
|
||||||
|
doHashedLogin(); // log in using hashed password instead
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// TODO add setting for client to reject unhashed logins
|
||||||
|
setStatus(StatusLoggingIn);
|
||||||
|
Command_Login cmdLogin = generateCommandLogin();
|
||||||
|
if (!password.isEmpty()) {
|
||||||
|
qWarning() << "using plain text password to log in";
|
||||||
|
cmdLogin.set_password(password.toStdString());
|
||||||
|
}
|
||||||
|
|
||||||
PendingCommand *pend = prepareSessionCommand(cmdLogin);
|
PendingCommand *pend = prepareSessionCommand(cmdLogin);
|
||||||
connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(loginResponse(Response)));
|
connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(loginResponse(Response)));
|
||||||
sendCommand(pend);
|
sendCommand(pend);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoteClient::doLogin(const QString &passwordSalt)
|
void RemoteClient::doHashedLogin()
|
||||||
{
|
{
|
||||||
setStatus(StatusLoggingIn);
|
setStatus(StatusLoggingIn);
|
||||||
Command_Login cmdLogin = generateCommandLogin();
|
Command_Login cmdLogin = generateCommandLogin();
|
||||||
|
|
||||||
const auto hashedPassword = PasswordHasher::computeHash(password, passwordSalt);
|
|
||||||
cmdLogin.set_hashed_password(hashedPassword.toStdString());
|
cmdLogin.set_hashed_password(hashedPassword.toStdString());
|
||||||
|
|
||||||
PendingCommand *pend = prepareSessionCommand(cmdLogin);
|
PendingCommand *pend = prepareSessionCommand(cmdLogin);
|
||||||
|
@ -244,12 +265,13 @@ void RemoteClient::passwordSaltResponse(const Response &response)
|
||||||
{
|
{
|
||||||
if (response.response_code() == Response::RespOk) {
|
if (response.response_code() == Response::RespOk) {
|
||||||
const Response_PasswordSalt &resp = response.GetExtension(Response_PasswordSalt::ext);
|
const Response_PasswordSalt &resp = response.GetExtension(Response_PasswordSalt::ext);
|
||||||
QString salt = QString::fromStdString(resp.password_salt());
|
auto passwordSalt = QString::fromStdString(resp.password_salt());
|
||||||
if (salt.isEmpty()) { // the server does not recognize the user but allows them to enter unregistered
|
if (passwordSalt.isEmpty()) { // the server does not recognize the user but allows them to enter unregistered
|
||||||
password = ""; // the password will not be used
|
password.clear(); // the password will not be used
|
||||||
doLogin();
|
doLogin();
|
||||||
} else {
|
} else {
|
||||||
doLogin(salt);
|
hashedPassword = PasswordHasher::computeHash(password, passwordSalt);
|
||||||
|
doHashedLogin();
|
||||||
}
|
}
|
||||||
} else if (response.response_code() != Response::RespNotConnected) {
|
} else if (response.response_code() != Response::RespNotConnected) {
|
||||||
emit loginError(response.response_code(), {}, 0, {});
|
emit loginError(response.response_code(), {}, 0, {});
|
||||||
|
@ -438,6 +460,7 @@ void RemoteClient::doConnectToServer(const QString &hostname,
|
||||||
password = _password;
|
password = _password;
|
||||||
lastHostname = hostname;
|
lastHostname = hostname;
|
||||||
lastPort = port;
|
lastPort = port;
|
||||||
|
hashedPassword.clear();
|
||||||
|
|
||||||
connectToHost(hostname, port);
|
connectToHost(hostname, port);
|
||||||
setStatus(StatusConnecting);
|
setStatus(StatusConnecting);
|
||||||
|
@ -460,6 +483,7 @@ void RemoteClient::doRegisterToServer(const QString &hostname,
|
||||||
realName = _realname;
|
realName = _realname;
|
||||||
lastHostname = hostname;
|
lastHostname = hostname;
|
||||||
lastPort = port;
|
lastPort = port;
|
||||||
|
hashedPassword.clear();
|
||||||
|
|
||||||
connectToHost(hostname, port);
|
connectToHost(hostname, port);
|
||||||
setStatus(StatusRegistering);
|
setStatus(StatusRegistering);
|
||||||
|
@ -561,7 +585,7 @@ QString RemoteClient::getSrvClientID(const QString &_hostname)
|
||||||
QHostAddress hostAddress = hostInfo.addresses().first();
|
QHostAddress hostAddress = hostInfo.addresses().first();
|
||||||
srvClientID += hostAddress.toString();
|
srvClientID += hostAddress.toString();
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "Warning: ClientID generation host lookup failure [" << hostInfo.errorString() << "]";
|
qWarning() << "ClientID generation host lookup failure [" << hostInfo.errorString() << "]";
|
||||||
srvClientID += _hostname;
|
srvClientID += _hostname;
|
||||||
}
|
}
|
||||||
QString uniqueServerClientID =
|
QString uniqueServerClientID =
|
||||||
|
@ -648,6 +672,7 @@ void RemoteClient::doSubmitForgotPasswordResetToServer(const QString &hostname,
|
||||||
lastPort = port;
|
lastPort = port;
|
||||||
token = _token.trimmed();
|
token = _token.trimmed();
|
||||||
password = _newpassword;
|
password = _newpassword;
|
||||||
|
hashedPassword.clear();
|
||||||
|
|
||||||
connectToHost(lastHostname, lastPort);
|
connectToHost(lastHostname, lastPort);
|
||||||
setStatus(StatusSubmitForgotPasswordReset);
|
setStatus(StatusSubmitForgotPasswordReset);
|
||||||
|
|
|
@ -70,7 +70,7 @@ private slots:
|
||||||
const QString &_realname);
|
const QString &_realname);
|
||||||
void doRequestPasswordSalt();
|
void doRequestPasswordSalt();
|
||||||
void doLogin();
|
void doLogin();
|
||||||
void doLogin(const QString &passwordSalt);
|
void doHashedLogin();
|
||||||
Command_Login generateCommandLogin();
|
Command_Login generateCommandLogin();
|
||||||
void doDisconnectFromServer();
|
void doDisconnectFromServer();
|
||||||
void doActivateToServer(const QString &_token);
|
void doActivateToServer(const QString &_token);
|
||||||
|
@ -101,6 +101,7 @@ private:
|
||||||
QWebSocket *websocket;
|
QWebSocket *websocket;
|
||||||
QString lastHostname;
|
QString lastHostname;
|
||||||
unsigned int lastPort;
|
unsigned int lastPort;
|
||||||
|
QString hashedPassword;
|
||||||
|
|
||||||
QString getSrvClientID(const QString &_hostname);
|
QString getSrvClientID(const QString &_hostname);
|
||||||
bool newMissingFeatureFound(const QString &_serversMissingFeatures);
|
bool newMissingFeatureFound(const QString &_serversMissingFeatures);
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
#include "dlg_edit_avatar.h"
|
#include "dlg_edit_avatar.h"
|
||||||
#include "dlg_edit_password.h"
|
#include "dlg_edit_password.h"
|
||||||
#include "dlg_edit_user.h"
|
#include "dlg_edit_user.h"
|
||||||
|
#include "gettextwithmax.h"
|
||||||
|
#include "passwordhasher.h"
|
||||||
#include "pb/response_get_user_info.pb.h"
|
#include "pb/response_get_user_info.pb.h"
|
||||||
#include "pb/session_commands.pb.h"
|
#include "pb/session_commands.pb.h"
|
||||||
#include "pending_command.h"
|
#include "pending_command.h"
|
||||||
|
@ -11,6 +13,8 @@
|
||||||
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QGridLayout>
|
#include <QGridLayout>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QInputDialog>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
|
||||||
|
@ -200,7 +204,22 @@ void UserInfoBox::actEditInternal(const Response &r)
|
||||||
|
|
||||||
Command_AccountEdit cmd;
|
Command_AccountEdit cmd;
|
||||||
cmd.set_real_name(dlg.getRealName().toStdString());
|
cmd.set_real_name(dlg.getRealName().toStdString());
|
||||||
cmd.set_email(dlg.getEmail().toStdString());
|
if (client->getServerSupportsPasswordHash()) {
|
||||||
|
if (email != dlg.getEmail()) {
|
||||||
|
// real password is required to change email
|
||||||
|
bool ok = false;
|
||||||
|
QString password =
|
||||||
|
getTextWithMax(this, tr("Enter Password"),
|
||||||
|
tr("Password verification is required in order to change your email address"),
|
||||||
|
QLineEdit::Password, "", &ok);
|
||||||
|
if (!ok)
|
||||||
|
return;
|
||||||
|
cmd.set_password_check(password.toStdString());
|
||||||
|
cmd.set_email(dlg.getEmail().toStdString());
|
||||||
|
} // servers that support password hash do not require all fields to be filled anymore
|
||||||
|
} else {
|
||||||
|
cmd.set_email(dlg.getEmail().toStdString());
|
||||||
|
}
|
||||||
cmd.set_country(dlg.getCountry().toStdString());
|
cmd.set_country(dlg.getCountry().toStdString());
|
||||||
|
|
||||||
PendingCommand *pend = client->prepareSessionCommand(cmd);
|
PendingCommand *pend = client->prepareSessionCommand(cmd);
|
||||||
|
@ -216,9 +235,42 @@ void UserInfoBox::actPassword()
|
||||||
if (!dlg.exec())
|
if (!dlg.exec())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
auto oldPassword = dlg.getOldPassword();
|
||||||
|
auto newPassword = dlg.getNewPassword();
|
||||||
|
|
||||||
|
if (client->getServerSupportsPasswordHash()) {
|
||||||
|
Command_RequestPasswordSalt cmd;
|
||||||
|
cmd.set_user_name(client->getUserName().toStdString());
|
||||||
|
|
||||||
|
PendingCommand *pend = client->prepareSessionCommand(cmd);
|
||||||
|
connect(pend,
|
||||||
|
// we need qoverload here in order to select the right version of this function
|
||||||
|
QOverload<const Response &, const CommandContainer &, const QVariant &>::of(&PendingCommand::finished),
|
||||||
|
this, [=](const Response &response, const CommandContainer &, const QVariant &) {
|
||||||
|
if (response.response_code() == Response::RespOk) {
|
||||||
|
changePassword(oldPassword, newPassword);
|
||||||
|
} else {
|
||||||
|
QMessageBox::critical(this, tr("Error"),
|
||||||
|
tr("An error occurred while trying to update your user information."));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
client->sendCommand(pend);
|
||||||
|
} else {
|
||||||
|
changePassword(oldPassword, newPassword);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UserInfoBox::changePassword(const QString &oldPassword, const QString &newPassword)
|
||||||
|
{
|
||||||
Command_AccountPassword cmd;
|
Command_AccountPassword cmd;
|
||||||
cmd.set_old_password(dlg.getOldPassword().toStdString());
|
cmd.set_old_password(oldPassword.toStdString());
|
||||||
cmd.set_new_password(dlg.getNewPassword().toStdString());
|
if (client->getServerSupportsPasswordHash()) {
|
||||||
|
auto passwordSalt = PasswordHasher::generateRandomSalt();
|
||||||
|
QString hashedPassword = PasswordHasher::computeHash(newPassword, passwordSalt);
|
||||||
|
cmd.set_hashed_new_password(hashedPassword.toStdString());
|
||||||
|
} else {
|
||||||
|
cmd.set_new_password(newPassword.toStdString());
|
||||||
|
}
|
||||||
|
|
||||||
PendingCommand *pend = client->prepareSessionCommand(cmd);
|
PendingCommand *pend = client->prepareSessionCommand(cmd);
|
||||||
connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this,
|
connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this,
|
||||||
|
@ -254,6 +306,9 @@ void UserInfoBox::processEditResponse(const Response &r)
|
||||||
QMessageBox::critical(this, tr("Error"),
|
QMessageBox::critical(this, tr("Error"),
|
||||||
tr("This server does not permit you to update your user informations."));
|
tr("This server does not permit you to update your user informations."));
|
||||||
break;
|
break;
|
||||||
|
case Response::RespWrongPassword:
|
||||||
|
QMessageBox::critical(this, tr("Error"), tr("The entered password does not match your account."));
|
||||||
|
break;
|
||||||
case Response::RespInternalError:
|
case Response::RespInternalError:
|
||||||
default:
|
default:
|
||||||
QMessageBox::critical(this, tr("Error"),
|
QMessageBox::critical(this, tr("Error"),
|
||||||
|
@ -280,7 +335,7 @@ void UserInfoBox::processPasswordResponse(const Response &r)
|
||||||
case Response::RespInternalError:
|
case Response::RespInternalError:
|
||||||
default:
|
default:
|
||||||
QMessageBox::critical(this, tr("Error"),
|
QMessageBox::critical(this, tr("Error"),
|
||||||
tr("An error occured while trying to update your user informations."));
|
tr("An error occurred while trying to update your user information."));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,7 @@ public slots:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void resizeEvent(QResizeEvent *event) override;
|
void resizeEvent(QResizeEvent *event) override;
|
||||||
|
void changePassword(const QString &oldPassword, const QString &newPassword);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -119,7 +119,7 @@ message Command_Register {
|
||||||
// User name client wants to register
|
// User name client wants to register
|
||||||
required string user_name = 1;
|
required string user_name = 1;
|
||||||
// Hashed password to be inserted into database
|
// Hashed password to be inserted into database
|
||||||
required string password = 2;
|
optional string password = 2;
|
||||||
// Email address of the client for user validation
|
// Email address of the client for user validation
|
||||||
optional string email = 3;
|
optional string email = 3;
|
||||||
// gender = 4; // obsolete
|
// gender = 4; // obsolete
|
||||||
|
@ -127,6 +127,7 @@ message Command_Register {
|
||||||
optional string country = 5;
|
optional string country = 5;
|
||||||
optional string real_name = 6;
|
optional string real_name = 6;
|
||||||
optional string clientid = 7;
|
optional string clientid = 7;
|
||||||
|
optional string hashed_password = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
// User wants to activate an account
|
// User wants to activate an account
|
||||||
|
@ -149,6 +150,7 @@ message Command_AccountEdit {
|
||||||
optional string email = 2;
|
optional string email = 2;
|
||||||
// gender = 3; // obsolete
|
// gender = 3; // obsolete
|
||||||
optional string country = 4;
|
optional string country = 4;
|
||||||
|
optional string password_check = 100; // password is required to change sensitive information
|
||||||
}
|
}
|
||||||
|
|
||||||
message Command_AccountImage {
|
message Command_AccountImage {
|
||||||
|
@ -164,6 +166,8 @@ message Command_AccountPassword {
|
||||||
}
|
}
|
||||||
optional string old_password = 1;
|
optional string old_password = 1;
|
||||||
optional string new_password = 2;
|
optional string new_password = 2;
|
||||||
|
// optional string hashed_old_password = 3; // we don't want users to steal hashed passwords and change them
|
||||||
|
optional string hashed_new_password = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Command_ForgotPasswordRequest {
|
message Command_ForgotPasswordRequest {
|
||||||
|
@ -182,6 +186,7 @@ message Command_ForgotPasswordReset {
|
||||||
optional string clientid = 2;
|
optional string clientid = 2;
|
||||||
optional string token = 3;
|
optional string token = 3;
|
||||||
optional string new_password = 4;
|
optional string new_password = 4;
|
||||||
|
optional string hashed_new_password = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Command_ForgotPasswordChallenge {
|
message Command_ForgotPasswordChallenge {
|
||||||
|
|
|
@ -115,6 +115,7 @@ public:
|
||||||
virtual bool registerUser(const QString & /* userName */,
|
virtual bool registerUser(const QString & /* userName */,
|
||||||
const QString & /* realName */,
|
const QString & /* realName */,
|
||||||
const QString & /* password */,
|
const QString & /* password */,
|
||||||
|
bool /* passwordNeedsHash */,
|
||||||
const QString & /* emailAddress */,
|
const QString & /* emailAddress */,
|
||||||
const QString & /* country */,
|
const QString & /* country */,
|
||||||
bool /* active = false */)
|
bool /* active = false */)
|
||||||
|
@ -151,10 +152,16 @@ public:
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
|
virtual bool
|
||||||
|
changeUserPassword(const QString & /* user */, const QString & /* password */, bool /* passwordNeedsHash */)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
};
|
||||||
virtual bool changeUserPassword(const QString & /* user */,
|
virtual bool changeUserPassword(const QString & /* user */,
|
||||||
const QString & /* oldPassword */,
|
const QString & /* oldPassword */,
|
||||||
|
bool /* oldPasswordNeedsHash */,
|
||||||
const QString & /* newPassword */,
|
const QString & /* newPassword */,
|
||||||
const bool & /* force */)
|
bool /* newPasswordNeedsHash */)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
|
@ -31,7 +31,7 @@ Server_ProtocolHandler::Server_ProtocolHandler(Server *_server,
|
||||||
Server_DatabaseInterface *_databaseInterface,
|
Server_DatabaseInterface *_databaseInterface,
|
||||||
QObject *parent)
|
QObject *parent)
|
||||||
: QObject(parent), Server_AbstractUserInterface(_server), deleted(false), databaseInterface(_databaseInterface),
|
: QObject(parent), Server_AbstractUserInterface(_server), deleted(false), databaseInterface(_databaseInterface),
|
||||||
authState(NotLoggedIn), acceptsUserListChanges(false), acceptsRoomListChanges(false),
|
authState(NotLoggedIn), usingRealPassword(false), acceptsUserListChanges(false), acceptsRoomListChanges(false),
|
||||||
idleClientWarningSent(false), timeRunning(0), lastDataReceived(0), lastActionReceived(0)
|
idleClientWarningSent(false), timeRunning(0), lastDataReceived(0), lastActionReceived(0)
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -447,8 +447,12 @@ Response::ResponseCode Server_ProtocolHandler::cmdLogin(const Command_Login &cmd
|
||||||
QString password;
|
QString password;
|
||||||
bool needsHash = false;
|
bool needsHash = false;
|
||||||
if (cmd.has_password()) {
|
if (cmd.has_password()) {
|
||||||
password = nameFromStdString(cmd.password());
|
if (cmd.password().length() > MAX_NAME_LENGTH)
|
||||||
|
return Response::RespWrongPassword;
|
||||||
|
password = QString::fromStdString(cmd.password());
|
||||||
needsHash = true;
|
needsHash = true;
|
||||||
|
} else if (cmd.hashed_password().length() > MAX_NAME_LENGTH) {
|
||||||
|
return Response::RespContextError;
|
||||||
} else {
|
} else {
|
||||||
password = nameFromStdString(cmd.hashed_password());
|
password = nameFromStdString(cmd.hashed_password());
|
||||||
}
|
}
|
||||||
|
@ -515,6 +519,7 @@ Response::ResponseCode Server_ProtocolHandler::cmdLogin(const Command_Login &cmd
|
||||||
return Response::RespAccountNotActivated;
|
return Response::RespAccountNotActivated;
|
||||||
default:
|
default:
|
||||||
authState = res;
|
authState = res;
|
||||||
|
usingRealPassword = needsHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
// limit the number of non-privileged users that can connect to the server based on configuration settings
|
// limit the number of non-privileged users that can connect to the server based on configuration settings
|
||||||
|
@ -791,6 +796,8 @@ Server_ProtocolHandler::cmdCreateGame(const Command_CreateGame &cmd, Server_Room
|
||||||
const int gameId = databaseInterface->getNextGameId();
|
const int gameId = databaseInterface->getNextGameId();
|
||||||
if (gameId == -1)
|
if (gameId == -1)
|
||||||
return Response::RespInternalError;
|
return Response::RespInternalError;
|
||||||
|
if (cmd.password().length() > MAX_NAME_LENGTH)
|
||||||
|
return Response::RespContextError;
|
||||||
|
|
||||||
auto level = userInfo->user_level();
|
auto level = userInfo->user_level();
|
||||||
bool isJudge = level & ServerInfo_User::IsJudge;
|
bool isJudge = level & ServerInfo_User::IsJudge;
|
||||||
|
@ -818,10 +825,10 @@ Server_ProtocolHandler::cmdCreateGame(const Command_CreateGame &cmd, Server_Room
|
||||||
|
|
||||||
// When server doesn't permit registered users to exist, do not honor only-reg setting
|
// When server doesn't permit registered users to exist, do not honor only-reg setting
|
||||||
bool onlyRegisteredUsers = cmd.only_registered() && (server->permitUnregisteredUsers());
|
bool onlyRegisteredUsers = cmd.only_registered() && (server->permitUnregisteredUsers());
|
||||||
Server_Game *game = new Server_Game(copyUserInfo(false), gameId, description, nameFromStdString(cmd.password()),
|
Server_Game *game = new Server_Game(
|
||||||
cmd.max_players(), gameTypes, cmd.only_buddies(), onlyRegisteredUsers,
|
copyUserInfo(false), gameId, description, QString::fromStdString(cmd.password()), cmd.max_players(), gameTypes,
|
||||||
cmd.spectators_allowed(), cmd.spectators_need_password(),
|
cmd.only_buddies(), onlyRegisteredUsers, cmd.spectators_allowed(), cmd.spectators_need_password(),
|
||||||
cmd.spectators_can_talk(), cmd.spectators_see_everything(), room);
|
cmd.spectators_can_talk(), cmd.spectators_see_everything(), room);
|
||||||
|
|
||||||
game->addPlayer(this, rc, asSpectator, asJudge, false);
|
game->addPlayer(this, rc, asSpectator, asJudge, false);
|
||||||
room->addGame(game);
|
room->addGame(game);
|
||||||
|
|
|
@ -52,6 +52,7 @@ protected:
|
||||||
bool deleted;
|
bool deleted;
|
||||||
Server_DatabaseInterface *databaseInterface;
|
Server_DatabaseInterface *databaseInterface;
|
||||||
AuthenticationResult authState;
|
AuthenticationResult authState;
|
||||||
|
bool usingRealPassword;
|
||||||
bool acceptsUserListChanges;
|
bool acceptsUserListChanges;
|
||||||
bool acceptsRoomListChanges;
|
bool acceptsRoomListChanges;
|
||||||
bool idleClientWarningSent;
|
bool idleClientWarningSent;
|
||||||
|
|
|
@ -241,6 +241,8 @@ Response::ResponseCode Server_Room::processJoinGameCommand(const Command_JoinGam
|
||||||
ResponseContainer &rc,
|
ResponseContainer &rc,
|
||||||
Server_AbstractUserInterface *userInterface)
|
Server_AbstractUserInterface *userInterface)
|
||||||
{
|
{
|
||||||
|
if (cmd.password().length() > MAX_NAME_LENGTH)
|
||||||
|
return Response::RespWrongPassword;
|
||||||
// This function is called from the Server thread and from the S_PH thread.
|
// This function is called from the Server thread and from the S_PH thread.
|
||||||
// server->roomsMutex is always locked.
|
// server->roomsMutex is always locked.
|
||||||
|
|
||||||
|
@ -265,8 +267,9 @@ Response::ResponseCode Server_Room::processJoinGameCommand(const Command_JoinGam
|
||||||
|
|
||||||
QMutexLocker gameLocker(&game->gameMutex);
|
QMutexLocker gameLocker(&game->gameMutex);
|
||||||
|
|
||||||
Response::ResponseCode result = game->checkJoin(userInterface->getUserInfo(), nameFromStdString(cmd.password()),
|
Response::ResponseCode result =
|
||||||
cmd.spectator(), cmd.override_restrictions(), cmd.join_as_judge());
|
game->checkJoin(userInterface->getUserInfo(), QString::fromStdString(cmd.password()), cmd.spectator(),
|
||||||
|
cmd.override_restrictions(), cmd.join_as_judge());
|
||||||
if (result == Response::RespOk)
|
if (result == Response::RespOk)
|
||||||
game->addPlayer(userInterface, rc, cmd.spectator(), cmd.join_as_judge());
|
game->addPlayer(userInterface, rc, cmd.spectator(), cmd.join_as_judge());
|
||||||
|
|
||||||
|
|
|
@ -198,6 +198,7 @@ bool Servatrice_DatabaseInterface::usernameIsValid(const QString &user, QString
|
||||||
bool Servatrice_DatabaseInterface::registerUser(const QString &userName,
|
bool Servatrice_DatabaseInterface::registerUser(const QString &userName,
|
||||||
const QString &realName,
|
const QString &realName,
|
||||||
const QString &password,
|
const QString &password,
|
||||||
|
bool passwordNeedsHash,
|
||||||
const QString &emailAddress,
|
const QString &emailAddress,
|
||||||
const QString &country,
|
const QString &country,
|
||||||
bool active)
|
bool active)
|
||||||
|
@ -205,7 +206,12 @@ bool Servatrice_DatabaseInterface::registerUser(const QString &userName,
|
||||||
if (!checkSql())
|
if (!checkSql())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
QString passwordSha512 = PasswordHasher::computeHash(password, PasswordHasher::generateRandomSalt());
|
QString passwordSha512;
|
||||||
|
if (passwordNeedsHash) {
|
||||||
|
passwordSha512 = PasswordHasher::computeHash(password, PasswordHasher::generateRandomSalt());
|
||||||
|
} else {
|
||||||
|
passwordSha512 = password;
|
||||||
|
}
|
||||||
QString token = active ? QString() : PasswordHasher::generateActivationToken();
|
QString token = active ? QString() : PasswordHasher::generateActivationToken();
|
||||||
|
|
||||||
QSqlQuery *query =
|
QSqlQuery *query =
|
||||||
|
@ -303,7 +309,7 @@ AuthenticationResult Servatrice_DatabaseInterface::checkUserPassword(Server_Prot
|
||||||
}
|
}
|
||||||
|
|
||||||
if (passwordQuery->next()) {
|
if (passwordQuery->next()) {
|
||||||
const QString correctPassword = passwordQuery->value(0).toString();
|
const QString correctPasswordSha512 = passwordQuery->value(0).toString();
|
||||||
const bool userIsActive = passwordQuery->value(1).toBool();
|
const bool userIsActive = passwordQuery->value(1).toBool();
|
||||||
if (!userIsActive) {
|
if (!userIsActive) {
|
||||||
qDebug("Login denied: user not active");
|
qDebug("Login denied: user not active");
|
||||||
|
@ -311,11 +317,11 @@ AuthenticationResult Servatrice_DatabaseInterface::checkUserPassword(Server_Prot
|
||||||
}
|
}
|
||||||
QString hashedPassword;
|
QString hashedPassword;
|
||||||
if (passwordNeedsHash) {
|
if (passwordNeedsHash) {
|
||||||
hashedPassword = PasswordHasher::computeHash(password, correctPassword.left(16));
|
hashedPassword = PasswordHasher::computeHash(password, correctPasswordSha512.left(16));
|
||||||
} else {
|
} else {
|
||||||
hashedPassword = password;
|
hashedPassword = password;
|
||||||
}
|
}
|
||||||
if (correctPassword == hashedPassword) {
|
if (correctPasswordSha512 == hashedPassword) {
|
||||||
qDebug("Login accepted: password right");
|
qDebug("Login accepted: password right");
|
||||||
return PasswordRight;
|
return PasswordRight;
|
||||||
} else {
|
} else {
|
||||||
|
@ -935,10 +941,30 @@ void Servatrice_DatabaseInterface::logMessage(const int senderId,
|
||||||
execSqlQuery(query);
|
execSqlQuery(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Servatrice_DatabaseInterface::changeUserPassword(const QString &user,
|
||||||
|
const QString &password,
|
||||||
|
bool passwordNeedsHash)
|
||||||
|
{
|
||||||
|
QString passwordSha512 = password;
|
||||||
|
if (passwordNeedsHash) {
|
||||||
|
passwordSha512 = PasswordHasher::computeHash(password, PasswordHasher::generateRandomSalt());
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlQuery *passwordQuery = prepareQuery("update {prefix}_users set password_sha512=:password, "
|
||||||
|
"passwordLastChangedDate = NOW() where name = :name");
|
||||||
|
passwordQuery->bindValue(":password", passwordSha512);
|
||||||
|
passwordQuery->bindValue(":name", user);
|
||||||
|
if (execSqlQuery(passwordQuery))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool Servatrice_DatabaseInterface::changeUserPassword(const QString &user,
|
bool Servatrice_DatabaseInterface::changeUserPassword(const QString &user,
|
||||||
const QString &oldPassword,
|
const QString &oldPassword,
|
||||||
|
bool oldPasswordNeedsHash,
|
||||||
const QString &newPassword,
|
const QString &newPassword,
|
||||||
const bool &force = false)
|
bool newPasswordNeedsHash)
|
||||||
{
|
{
|
||||||
if (server->getAuthenticationMethod() != Servatrice::AuthenticationSql)
|
if (server->getAuthenticationMethod() != Servatrice::AuthenticationSql)
|
||||||
return false;
|
return false;
|
||||||
|
@ -953,30 +979,24 @@ bool Servatrice_DatabaseInterface::changeUserPassword(const QString &user,
|
||||||
QSqlQuery *passwordQuery = prepareQuery("select password_sha512 from {prefix}_users where name = :name");
|
QSqlQuery *passwordQuery = prepareQuery("select password_sha512 from {prefix}_users where name = :name");
|
||||||
passwordQuery->bindValue(":name", user);
|
passwordQuery->bindValue(":name", user);
|
||||||
|
|
||||||
if (!force) {
|
if (!execSqlQuery(passwordQuery)) {
|
||||||
if (!execSqlQuery(passwordQuery)) {
|
qDebug("Change password denied: SQL error");
|
||||||
qDebug("Change password denied: SQL error");
|
return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!passwordQuery->next())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const QString correctPassword = passwordQuery->value(0).toString();
|
|
||||||
if (correctPassword != PasswordHasher::computeHash(oldPassword, correctPassword.left(16)))
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString passwordSha512 = PasswordHasher::computeHash(newPassword, PasswordHasher::generateRandomSalt());
|
if (!passwordQuery->next())
|
||||||
|
return false;
|
||||||
|
|
||||||
passwordQuery = prepareQuery("update {prefix}_users set password_sha512=:password, "
|
const QString correctPasswordSha512 = passwordQuery->value(0).toString();
|
||||||
"passwordLastChangedDate = NOW() where name = :name");
|
QString oldPasswordSha512 = oldPassword;
|
||||||
passwordQuery->bindValue(":password", passwordSha512);
|
if (oldPasswordNeedsHash) {
|
||||||
passwordQuery->bindValue(":name", user);
|
QString salt = correctPasswordSha512.left(16);
|
||||||
if (execSqlQuery(passwordQuery))
|
oldPasswordSha512 = PasswordHasher::computeHash(oldPassword, salt);
|
||||||
return true;
|
}
|
||||||
|
if (correctPasswordSha512 != oldPasswordSha512)
|
||||||
|
return false;
|
||||||
|
|
||||||
return false;
|
return changeUserPassword(user, newPassword, newPasswordNeedsHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
int Servatrice_DatabaseInterface::getActiveUserCount(QString connectionType)
|
int Servatrice_DatabaseInterface::getActiveUserCount(QString connectionType)
|
||||||
|
|
|
@ -98,6 +98,7 @@ public:
|
||||||
bool registerUser(const QString &userName,
|
bool registerUser(const QString &userName,
|
||||||
const QString &realName,
|
const QString &realName,
|
||||||
const QString &password,
|
const QString &password,
|
||||||
|
bool passwordNeedsHash,
|
||||||
const QString &emailAddress,
|
const QString &emailAddress,
|
||||||
const QString &country,
|
const QString &country,
|
||||||
bool active = false);
|
bool active = false);
|
||||||
|
@ -111,8 +112,12 @@ public:
|
||||||
LogMessage_TargetType targetType,
|
LogMessage_TargetType targetType,
|
||||||
const int targetId,
|
const int targetId,
|
||||||
const QString &targetName);
|
const QString &targetName);
|
||||||
bool
|
bool changeUserPassword(const QString &user, const QString &password, bool passwordNeedsHash);
|
||||||
changeUserPassword(const QString &user, const QString &oldPassword, const QString &newPassword, const bool &force);
|
bool changeUserPassword(const QString &user,
|
||||||
|
const QString &oldPassword,
|
||||||
|
bool oldPasswordNeedsHash,
|
||||||
|
const QString &newPassword,
|
||||||
|
bool newPasswordNeedsHash);
|
||||||
QList<ServerInfo_Ban> getUserBanHistory(const QString userName);
|
QList<ServerInfo_Ban> getUserBanHistory(const QString userName);
|
||||||
bool
|
bool
|
||||||
addWarning(const QString userName, const QString adminName, const QString warningReason, const QString clientID);
|
addWarning(const QString userName, const QString adminName, const QString warningReason, const QString clientID);
|
||||||
|
|
|
@ -1164,19 +1164,29 @@ Response::ResponseCode AbstractServerSocketInterface::cmdRegisterAccount(const C
|
||||||
|
|
||||||
QString realName = nameFromStdString(cmd.real_name());
|
QString realName = nameFromStdString(cmd.real_name());
|
||||||
QString country = nameFromStdString(cmd.country());
|
QString country = nameFromStdString(cmd.country());
|
||||||
QString password = nameFromStdString(cmd.password());
|
QString password;
|
||||||
|
bool passwordNeedsHash = false;
|
||||||
if (!isPasswordLongEnough(password.length())) {
|
if (cmd.has_password()) {
|
||||||
if (servatrice->getEnableRegistrationAudit())
|
if (cmd.password().length() > MAX_NAME_LENGTH)
|
||||||
sqlInterface->addAuditRecord(userName.simplified(), this->getAddress(), clientId.simplified(),
|
return Response::RespRegistrationFailed;
|
||||||
"REGISTER_ACCOUNT", "Password is too short", false);
|
password = QString::fromStdString(cmd.password());
|
||||||
|
passwordNeedsHash = true;
|
||||||
return Response::RespPasswordTooShort;
|
if (!isPasswordLongEnough(password.length())) {
|
||||||
|
if (servatrice->getEnableRegistrationAudit()) {
|
||||||
|
sqlInterface->addAuditRecord(userName.simplified(), this->getAddress(), clientId.simplified(),
|
||||||
|
"REGISTER_ACCOUNT", "Password is too short", false);
|
||||||
|
}
|
||||||
|
return Response::RespPasswordTooShort;
|
||||||
|
}
|
||||||
|
} else if (cmd.hashed_password().length() > MAX_NAME_LENGTH) {
|
||||||
|
return Response::RespRegistrationFailed;
|
||||||
|
} else {
|
||||||
|
password = QString::fromStdString(cmd.hashed_password());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool requireEmailActivation = settingsCache->value("registration/requireemailactivation", true).toBool();
|
bool requireEmailActivation = settingsCache->value("registration/requireemailactivation", true).toBool();
|
||||||
bool regSucceeded =
|
bool regSucceeded = sqlInterface->registerUser(userName, realName, password, passwordNeedsHash, emailAddress,
|
||||||
sqlInterface->registerUser(userName, realName, password, emailAddress, country, !requireEmailActivation);
|
country, !requireEmailActivation);
|
||||||
|
|
||||||
if (regSucceeded) {
|
if (regSucceeded) {
|
||||||
qDebug() << "Accepted register command for user: " << userName;
|
qDebug() << "Accepted register command for user: " << userName;
|
||||||
|
@ -1256,20 +1266,72 @@ Response::ResponseCode AbstractServerSocketInterface::cmdAccountEdit(const Comma
|
||||||
QString emailAddress = nameFromStdString(cmd.email());
|
QString emailAddress = nameFromStdString(cmd.email());
|
||||||
QString country = nameFromStdString(cmd.country());
|
QString country = nameFromStdString(cmd.country());
|
||||||
|
|
||||||
|
bool checkedPassword = false;
|
||||||
QString userName = QString::fromStdString(userInfo->name());
|
QString userName = QString::fromStdString(userInfo->name());
|
||||||
|
if (cmd.has_password_check()) {
|
||||||
|
if (cmd.password_check().length() > MAX_NAME_LENGTH)
|
||||||
|
return Response::RespWrongPassword;
|
||||||
|
QString password = QString::fromStdString(cmd.password_check());
|
||||||
|
QString clientId = QString::fromStdString(userInfo->clientid());
|
||||||
|
QString reasonStr{};
|
||||||
|
int secondsLeft{};
|
||||||
|
AuthenticationResult checkStatus =
|
||||||
|
databaseInterface->checkUserPassword(this, userName, password, clientId, reasonStr, secondsLeft, true);
|
||||||
|
if (checkStatus == PasswordRight) {
|
||||||
|
checkedPassword = true;
|
||||||
|
} else {
|
||||||
|
// the user already logged in with this info, the only change is their password
|
||||||
|
return Response::RespWrongPassword;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QSqlQuery *query = sqlInterface->prepareQuery("update {prefix}_users set realname=:realName, email=:email, "
|
QStringList queryList({});
|
||||||
"country=:country where name=:userName");
|
if (cmd.has_real_name()) {
|
||||||
query->bindValue(":realName", realName);
|
queryList << "realname=:realName";
|
||||||
query->bindValue(":email", emailAddress);
|
}
|
||||||
query->bindValue(":country", country);
|
if (cmd.has_email()) {
|
||||||
|
// a real password is required in order to change the email address
|
||||||
|
if (usingRealPassword || checkedPassword) {
|
||||||
|
queryList << "email=:email";
|
||||||
|
} else {
|
||||||
|
return Response::RespFunctionNotAllowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cmd.has_country()) {
|
||||||
|
queryList << "country=:country";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (queryList.isEmpty())
|
||||||
|
return Response::RespOk;
|
||||||
|
|
||||||
|
QString queryText = QString("update {prefix}_users set %1 where name=:userName").arg(queryList.join(", "));
|
||||||
|
QSqlQuery *query = sqlInterface->prepareQuery(queryText);
|
||||||
|
if (cmd.has_real_name()) {
|
||||||
|
QString realName = nameFromStdString(cmd.real_name());
|
||||||
|
query->bindValue(":realName", realName);
|
||||||
|
}
|
||||||
|
if (cmd.has_email()) {
|
||||||
|
QString emailAddress = nameFromStdString(cmd.email());
|
||||||
|
query->bindValue(":email", emailAddress);
|
||||||
|
}
|
||||||
|
if (cmd.has_country()) {
|
||||||
|
QString country = nameFromStdString(cmd.country());
|
||||||
|
query->bindValue(":country", country);
|
||||||
|
}
|
||||||
query->bindValue(":userName", userName);
|
query->bindValue(":userName", userName);
|
||||||
|
|
||||||
if (!sqlInterface->execSqlQuery(query))
|
if (!sqlInterface->execSqlQuery(query))
|
||||||
return Response::RespInternalError;
|
return Response::RespInternalError;
|
||||||
|
|
||||||
userInfo->set_real_name(realName.toStdString());
|
if (cmd.has_real_name()) {
|
||||||
userInfo->set_email(emailAddress.toStdString());
|
userInfo->set_real_name(realName.toStdString());
|
||||||
userInfo->set_country(country.toStdString());
|
}
|
||||||
|
if (cmd.has_email()) {
|
||||||
|
userInfo->set_email(emailAddress.toStdString());
|
||||||
|
}
|
||||||
|
if (cmd.has_country()) {
|
||||||
|
userInfo->set_country(country.toStdString());
|
||||||
|
}
|
||||||
|
|
||||||
return Response::RespOk;
|
return Response::RespOk;
|
||||||
}
|
}
|
||||||
|
@ -1300,15 +1362,26 @@ Response::ResponseCode AbstractServerSocketInterface::cmdAccountPassword(const C
|
||||||
if (authState != PasswordRight)
|
if (authState != PasswordRight)
|
||||||
return Response::RespFunctionNotAllowed;
|
return Response::RespFunctionNotAllowed;
|
||||||
|
|
||||||
QString oldPassword = nameFromStdString(cmd.old_password());
|
if (cmd.old_password().length() > MAX_NAME_LENGTH)
|
||||||
QString newPassword = nameFromStdString(cmd.new_password());
|
return Response::RespWrongPassword;
|
||||||
|
QString oldPassword = QString::fromStdString(cmd.old_password());
|
||||||
if (!isPasswordLongEnough(newPassword.length()))
|
QString newPassword;
|
||||||
return Response::RespPasswordTooShort;
|
bool newPasswordNeedsHash = false;
|
||||||
|
if (cmd.has_new_password()) {
|
||||||
|
if (cmd.new_password().length() > MAX_NAME_LENGTH)
|
||||||
|
return Response::RespContextError;
|
||||||
|
newPassword = QString::fromStdString(cmd.new_password());
|
||||||
|
newPasswordNeedsHash = true;
|
||||||
|
if (!isPasswordLongEnough(newPassword.length()))
|
||||||
|
return Response::RespPasswordTooShort;
|
||||||
|
} else if (cmd.hashed_new_password().length() > MAX_NAME_LENGTH) {
|
||||||
|
return Response::RespContextError;
|
||||||
|
} else {
|
||||||
|
newPassword = QString::fromStdString(cmd.hashed_new_password());
|
||||||
|
}
|
||||||
|
|
||||||
QString userName = QString::fromStdString(userInfo->name());
|
QString userName = QString::fromStdString(userInfo->name());
|
||||||
|
if (!databaseInterface->changeUserPassword(userName, oldPassword, true, newPassword, newPasswordNeedsHash))
|
||||||
if (!databaseInterface->changeUserPassword(userName, oldPassword, newPassword, false))
|
|
||||||
return Response::RespWrongPassword;
|
return Response::RespWrongPassword;
|
||||||
|
|
||||||
return Response::RespOk;
|
return Response::RespOk;
|
||||||
|
@ -1421,7 +1494,20 @@ Response::ResponseCode AbstractServerSocketInterface::cmdForgotPasswordReset(con
|
||||||
return Response::RespFunctionNotAllowed;
|
return Response::RespFunctionNotAllowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sqlInterface->changeUserPassword(userName, "", nameFromStdString(cmd.new_password()), true)) {
|
QString password;
|
||||||
|
bool passwordNeedsHash = false;
|
||||||
|
if (cmd.has_new_password()) {
|
||||||
|
if (cmd.new_password().length() > MAX_NAME_LENGTH)
|
||||||
|
return Response::RespContextError;
|
||||||
|
password = QString::fromStdString(cmd.new_password());
|
||||||
|
passwordNeedsHash = true;
|
||||||
|
} else if (cmd.hashed_new_password().length() > MAX_NAME_LENGTH) {
|
||||||
|
return Response::RespContextError;
|
||||||
|
} else {
|
||||||
|
password = QString::fromStdString(cmd.hashed_new_password());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sqlInterface->changeUserPassword(nameFromStdString(cmd.user_name()), password, passwordNeedsHash)) {
|
||||||
if (servatrice->getEnableForgotPasswordAudit())
|
if (servatrice->getEnableForgotPasswordAudit())
|
||||||
sqlInterface->addAuditRecord(userName.simplified(), this->getAddress(), clientId.simplified(),
|
sqlInterface->addAuditRecord(userName.simplified(), this->getAddress(), clientId.simplified(),
|
||||||
"PASSWORD_RESET", "", true);
|
"PASSWORD_RESET", "", true);
|
||||||
|
|
Loading…
Reference in a new issue