mirror of
https://github.com/kunkundi/crossdesk-server.git
synced 2026-10-26 01:34:33 +08:00
[fix] make password changes idempotent
This commit is contained in:
1 parent
78a5fb98d2
commit
d8f48ebad6
2 files changed
+65
-6
No files matched your search
@@ -1,5 +1,9 @@
|
||||
#include "signal_negotiation.h"
|
||||
|
||||
#include <openssl/sha.h>
|
||||
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
|
||||
#include "log.h"
|
||||
@@ -14,6 +18,18 @@ bool GetStringField(const json& j, const char* key, std::string& value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string PasswordFingerprint(const std::string& password) {
|
||||
unsigned char digest[SHA256_DIGEST_LENGTH];
|
||||
SHA256(reinterpret_cast<const unsigned char*>(password.data()),
|
||||
password.size(), digest);
|
||||
std::ostringstream stream;
|
||||
stream << std::hex << std::setfill('0');
|
||||
for (unsigned char byte : digest) {
|
||||
stream << std::setw(2) << static_cast<unsigned int>(byte);
|
||||
}
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
SignalNegotiation::SignalNegotiation(
|
||||
@@ -433,9 +449,12 @@ bool SignalNegotiation::new_candidate_mid(websocketpp::connection_hdl hdl,
|
||||
|
||||
bool SignalNegotiation::change_password(websocketpp::connection_hdl hdl,
|
||||
const json& j) {
|
||||
constexpr size_t kMaxRememberedPasswordChanges = 4096;
|
||||
constexpr size_t kMaxPasswordChangeRequestIdLength = 128;
|
||||
json message = {{"type", "change_password"}};
|
||||
std::string request_id;
|
||||
if (!GetStringField(j, "request_id", request_id) || request_id.empty()) {
|
||||
if (!GetStringField(j, "request_id", request_id) || request_id.empty() ||
|
||||
request_id.size() > kMaxPasswordChangeRequestIdLength) {
|
||||
message["status"] = "fail";
|
||||
message["reason"] = "Missing or invalid request ID";
|
||||
send_msg_(hdl, message);
|
||||
@@ -461,12 +480,39 @@ bool SignalNegotiation::change_password(websocketpp::connection_hdl hdl,
|
||||
new_password.size() != 6) {
|
||||
message["status"] = "fail";
|
||||
message["reason"] = "Password must contain exactly 6 characters";
|
||||
} else if (!device_db_manager_->UpdatePassword(user_id, new_password)) {
|
||||
message["status"] = "fail";
|
||||
message["reason"] = "Failed to update password";
|
||||
} else {
|
||||
message["status"] = "success";
|
||||
LOG_INFO("Authenticated client [{}] changed its device password", user_id);
|
||||
const std::string cache_key = user_id + "\n" + request_id;
|
||||
const std::string password_fingerprint =
|
||||
PasswordFingerprint(new_password);
|
||||
std::lock_guard<std::mutex> lock(password_change_mutex_);
|
||||
const auto cached = password_change_results_.find(cache_key);
|
||||
if (cached != password_change_results_.end()) {
|
||||
if (cached->second.password_fingerprint != password_fingerprint) {
|
||||
message["status"] = "fail";
|
||||
message["reason"] = "Request ID was already used";
|
||||
} else {
|
||||
message = cached->second.response;
|
||||
LOG_INFO("Replay password change result for authenticated client "
|
||||
"[{}] request [{}]",
|
||||
user_id, request_id);
|
||||
}
|
||||
} else if (!device_db_manager_->UpdatePassword(user_id, new_password)) {
|
||||
message["status"] = "fail";
|
||||
message["reason"] = "Failed to update password";
|
||||
} else {
|
||||
message["status"] = "success";
|
||||
password_change_results_.emplace(
|
||||
cache_key, PasswordChangeResult{password_fingerprint, message});
|
||||
password_change_result_order_.push_back(cache_key);
|
||||
while (password_change_result_order_.size() >
|
||||
kMaxRememberedPasswordChanges) {
|
||||
password_change_results_.erase(
|
||||
password_change_result_order_.front());
|
||||
password_change_result_order_.pop_front();
|
||||
}
|
||||
LOG_INFO("Authenticated client [{}] changed its device password",
|
||||
user_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,11 @@
|
||||
#ifndef _SIGNAL_NEGOTIATION_H_
|
||||
#define _SIGNAL_NEGOTIATION_H_
|
||||
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "device_db_manager.h"
|
||||
#include "transmission_manager.h"
|
||||
@@ -41,12 +45,21 @@ class SignalNegotiation {
|
||||
void OnWebClientDisconnect(const std::string& user_id);
|
||||
|
||||
private:
|
||||
struct PasswordChangeResult {
|
||||
std::string password_fingerprint;
|
||||
json response;
|
||||
};
|
||||
|
||||
void AddTurnCredentials(json& message, const std::string& user_id) const;
|
||||
|
||||
std::shared_ptr<TransmissionManager> transmission_manager_;
|
||||
DeviceDBManager* device_db_manager_;
|
||||
std::shared_ptr<TurnCredentialIssuer> turn_credential_issuer_;
|
||||
std::function<void(websocketpp::connection_hdl, json)> send_msg_;
|
||||
std::mutex password_change_mutex_;
|
||||
std::unordered_map<std::string, PasswordChangeResult>
|
||||
password_change_results_;
|
||||
std::deque<std::string> password_change_result_order_;
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in new issue
Block a user