123 lines
No EOL
5.1 KiB
C++
123 lines
No EOL
5.1 KiB
C++
#include "DatabaseService.h"
|
|
#include "../services/RedisHelper.h"
|
|
#include <drogon/orm/DbClient.h>
|
|
#include <sstream>
|
|
#include <iomanip>
|
|
#include <openssl/rand.h>
|
|
|
|
using namespace drogon;
|
|
using namespace drogon::orm;
|
|
|
|
namespace {
|
|
void storeKeyInRedis(const std::string& streamKey) {
|
|
// Store the stream key in Redis for validation (24 hour TTL)
|
|
bool stored = RedisHelper::storeKey("stream_key:" + streamKey, "1", 86400);
|
|
|
|
if (stored) {
|
|
LOG_INFO << "Stored stream key in Redis: " << streamKey;
|
|
} else {
|
|
LOG_ERROR << "Failed to store key in Redis: " << streamKey;
|
|
}
|
|
}
|
|
|
|
// SECURITY FIX: Use cryptographically secure random bytes instead of mt19937
|
|
std::string generateStreamKey() {
|
|
unsigned char bytes[32]; // 32 bytes = 64 hex characters
|
|
if (RAND_bytes(bytes, sizeof(bytes)) != 1) {
|
|
LOG_ERROR << "Failed to generate cryptographically secure random bytes";
|
|
throw std::runtime_error("Failed to generate secure stream key");
|
|
}
|
|
|
|
std::stringstream ss;
|
|
for (size_t i = 0; i < sizeof(bytes); ++i) {
|
|
ss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(bytes[i]);
|
|
}
|
|
return ss.str();
|
|
}
|
|
}
|
|
|
|
void DatabaseService::initialize() {
|
|
LOG_INFO << "Initializing Database Service...";
|
|
}
|
|
|
|
void DatabaseService::getUserStreamKey(int64_t userId,
|
|
std::function<void(bool, const std::string&)> callback) {
|
|
auto dbClient = drogon::app().getDbClient();
|
|
|
|
*dbClient << "SELECT key FROM stream_keys WHERE user_id = $1 AND is_active = true"
|
|
<< userId
|
|
>> [callback, userId, dbClient](const Result &r) {
|
|
if (!r.empty()) {
|
|
std::string key = r[0]["key"].as<std::string>();
|
|
// Also store in Redis when retrieved
|
|
storeKeyInRedis(key);
|
|
callback(true, key);
|
|
} else {
|
|
// Generate new key for user
|
|
std::string newKey = generateStreamKey();
|
|
|
|
*dbClient << "INSERT INTO stream_keys (user_id, key, is_active) VALUES ($1, $2, true)"
|
|
<< userId << newKey
|
|
>> [callback, newKey](const Result &) {
|
|
storeKeyInRedis(newKey);
|
|
callback(true, newKey);
|
|
}
|
|
>> [callback](const DrogonDbException &e) {
|
|
LOG_ERROR << "Failed to create stream key: " << e.base().what();
|
|
callback(false, "");
|
|
};
|
|
}
|
|
}
|
|
>> [callback](const DrogonDbException &e) {
|
|
LOG_ERROR << "Database error: " << e.base().what();
|
|
callback(false, "");
|
|
};
|
|
}
|
|
|
|
void DatabaseService::updateUserStreamKey(int64_t userId,
|
|
const std::string& newKey,
|
|
std::function<void(bool)> callback) {
|
|
auto dbClient = drogon::app().getDbClient();
|
|
|
|
// Execute as separate queries instead of transaction for simplicity
|
|
*dbClient << "UPDATE stream_keys SET is_active = false WHERE user_id = $1"
|
|
<< userId
|
|
>> [dbClient, userId, newKey, callback](const Result &) {
|
|
// Insert new key
|
|
*dbClient << "INSERT INTO stream_keys (user_id, key, is_active) VALUES ($1, $2, true)"
|
|
<< userId << newKey
|
|
>> [callback, newKey](const Result &) {
|
|
// Store new key in Redis
|
|
storeKeyInRedis(newKey);
|
|
callback(true);
|
|
}
|
|
>> [callback](const DrogonDbException &e) {
|
|
LOG_ERROR << "Failed to insert new key: " << e.base().what();
|
|
callback(false);
|
|
};
|
|
}
|
|
>> [callback](const DrogonDbException &e) {
|
|
LOG_ERROR << "Failed to deactivate old keys: " << e.base().what();
|
|
callback(false);
|
|
};
|
|
}
|
|
|
|
void DatabaseService::validateStreamKey(const std::string& key,
|
|
std::function<void(bool)> callback) {
|
|
auto dbClient = drogon::app().getDbClient();
|
|
|
|
*dbClient << "SELECT 1 FROM stream_keys WHERE key = $1 AND is_active = true"
|
|
<< key
|
|
>> [callback, key](const Result &r) {
|
|
bool valid = !r.empty();
|
|
if (valid) {
|
|
// Also store in Redis when validated
|
|
storeKeyInRedis(key);
|
|
}
|
|
callback(valid);
|
|
}
|
|
>> [callback](const DrogonDbException &e) {
|
|
LOG_ERROR << "Database error: " << e.base().what();
|
|
callback(false);
|
|
};
|
|
} |