mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-09 03:23:24 -08:00
[Server/Client] Enforce and complete the forced password change at login (#7680)
checkUserPassword ignored force_password_change on the challenge-response path, so a reset password stayed valid. Returning PasswordChangeRequired was not enough on its own: the session was dropped and the client told the user to ask the administrator for the temporary password they had just used. The pending session now keeps its identity without registering a user session or announcing a join, may only ping and change its password, and the client completes the change in-session before reconnecting. Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
parent
ef4574d2b9
commit
50a2ae9cc8
16 changed files with 575 additions and 28 deletions
|
|
@ -125,6 +125,7 @@ RemoteClient::RemoteClient(QObject *parent, INetworkSettingsProvider *_networkSe
|
|||
&RemoteClient::doSubmitForgotPasswordResetToServer);
|
||||
connect(this, &RemoteClient::sigSubmitForgotPasswordChallengeToServer, this,
|
||||
&RemoteClient::doSubmitForgotPasswordChallengeToServer);
|
||||
connect(this, &RemoteClient::sigChangePasswordToServer, this, &RemoteClient::doChangePasswordToServer);
|
||||
}
|
||||
|
||||
RemoteClient::~RemoteClient()
|
||||
|
|
@ -354,6 +355,29 @@ void RemoteClient::submitPasswordVerifierResponse(const Response &response)
|
|||
}
|
||||
}
|
||||
|
||||
void RemoteClient::changePasswordToServer(const QString &oldPassword, const QString &newPassword)
|
||||
{
|
||||
emit sigChangePasswordToServer(oldPassword, newPassword);
|
||||
}
|
||||
|
||||
void RemoteClient::doChangePasswordToServer(const QString &oldPassword, const QString &newPassword)
|
||||
{
|
||||
const Command_AccountPassword cmd = makeAccountPasswordCommand(oldPassword, newPassword);
|
||||
|
||||
PendingCommand *pend = prepareSessionCommand(cmd);
|
||||
connect(pend, &PendingCommand::finished, this, &RemoteClient::accountPasswordResponse);
|
||||
sendCommand(pend);
|
||||
}
|
||||
|
||||
void RemoteClient::accountPasswordResponse(const Response &response)
|
||||
{
|
||||
if (response.response_code() == Response::RespOk) {
|
||||
emit sigPasswordChangeSuccess();
|
||||
} else {
|
||||
emit sigPasswordChangeError(response.response_code());
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteClient::processConnectionClosedEvent(const Event_ConnectionClosed & /*event*/)
|
||||
{
|
||||
doDisconnectFromServer();
|
||||
|
|
@ -475,6 +499,23 @@ void RemoteClient::loginResponse(const Response &response)
|
|||
emit notifyUserAboutUpdate();
|
||||
}
|
||||
|
||||
} else if (response.response_code() == Response::RespPasswordChangeRequired) {
|
||||
// The credentials were accepted: the server keeps this session open and serves
|
||||
// nothing but ping and Command_AccountPassword until the password is replaced.
|
||||
// Treating it as a failed login would disconnect before the dialog that collects
|
||||
// the new password (queued to the UI thread behind this slot) can send anything:
|
||||
// the command would sit in pendingCommands forever, because the ping timer that
|
||||
// times it out stops with the connection. Keep the session and let the controller
|
||||
// report the outcome through accountPasswordResponse().
|
||||
QList<QString> missingFeatures;
|
||||
if (resp.missing_features_size() > 0) {
|
||||
for (int i = 0; i < resp.missing_features_size(); ++i) {
|
||||
missingFeatures << QString::fromStdString(resp.missing_features(i));
|
||||
}
|
||||
}
|
||||
setStatus(StatusPasswordChangeRequired);
|
||||
emit loginError(response.response_code(), QString::fromStdString(resp.denied_reason_str()),
|
||||
static_cast<quint32>(resp.denied_end_time()), missingFeatures);
|
||||
} else if (response.response_code() != Response::RespNotConnected) {
|
||||
QList<QString> missingFeatures;
|
||||
if (resp.missing_features_size() > 0) {
|
||||
|
|
|
|||
|
|
@ -71,6 +71,11 @@ signals:
|
|||
//! should only store it for the connection it is currently negotiating, so
|
||||
//! the hostname and user name are intentionally not part of the signal.
|
||||
void sigPasswordVerifierReady(const QString &verifier);
|
||||
//! \brief Outcome of the password change the server demanded at login.
|
||||
void sigPasswordChangeSuccess();
|
||||
void sigPasswordChangeError(int responseCode);
|
||||
//! \brief Request a password change; queued to the client thread like the other requests.
|
||||
void sigChangePasswordToServer(const QString &oldPassword, const QString &newPassword);
|
||||
private slots:
|
||||
void slotConnected();
|
||||
void readData();
|
||||
|
|
@ -99,6 +104,8 @@ private slots:
|
|||
Command_Login generateCommandLogin();
|
||||
void doSubmitPasswordVerifier();
|
||||
void submitPasswordVerifierResponse(const Response &response);
|
||||
void accountPasswordResponse(const Response &response);
|
||||
void doChangePasswordToServer(const QString &oldPassword, const QString &newPassword);
|
||||
void doDisconnectFromServer();
|
||||
void doActivateToServer(const QString &_token);
|
||||
void doRequestForgotPasswordToServer(const QString &hostname, unsigned int port, const QString &_userName);
|
||||
|
|
@ -201,6 +208,10 @@ public:
|
|||
unsigned int port,
|
||||
const QString &_userName,
|
||||
const QString &_email);
|
||||
//! \brief Change the password of the account that just logged in, following the
|
||||
//! server's forced-password-change reply. Emits sigPasswordChangeSuccess or
|
||||
//! sigPasswordChangeError when the server answers.
|
||||
void changePasswordToServer(const QString &oldPassword, const QString &newPassword);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue