#include "DatabaseService.h" #include "../services/RedisHelper.h" #include #include #include #include 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(bytes[i]); } return ss.str(); } } void DatabaseService::initialize() { LOG_INFO << "Initializing Database Service..."; } void DatabaseService::getUserStreamKey(int64_t userId, std::function 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(); // 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 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 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); }; }