#include #include #include #include #include #include #include #include using namespace drogon; using namespace drogon::orm; // SECURITY FIX #32: Add audit logging for admin CLI operations namespace { void writeAuditLog(const std::string& action, const std::string& target, const std::string& status, const std::string& details = "") { // Get current timestamp auto now = std::time(nullptr); auto tm = *std::localtime(&now); std::ostringstream timestamp; timestamp << std::put_time(&tm, "%Y-%m-%d %H:%M:%S"); // Get hostname for audit trail char hostname[256] = "unknown"; gethostname(hostname, sizeof(hostname)); // Build log entry std::ostringstream logEntry; logEntry << "[" << timestamp.str() << "] " << "HOST=" << hostname << " " << "ACTION=" << action << " " << "TARGET=" << target << " " << "STATUS=" << status; if (!details.empty()) { logEntry << " DETAILS=" << details; } logEntry << std::endl; // Write to audit log file std::string logPath = "/var/log/admin_tool_audit.log"; std::ofstream logFile(logPath, std::ios::app); if (logFile.is_open()) { logFile << logEntry.str(); logFile.close(); } // Also output to stderr for immediate visibility std::cerr << "[AUDIT] " << logEntry.str(); } } int main(int argc, char* argv[]) { if (argc < 2) { std::cerr << "Usage: " << argv[0] << " -promote-admin " << std::endl; return 1; } std::string command = argv[1]; if (command != "-promote-admin" || argc != 3) { std::cerr << "Usage: " << argv[0] << " -promote-admin " << std::endl; return 1; } std::string username = argv[2]; // Get database config from environment or use defaults std::string dbHost = std::getenv("DB_HOST") ? std::getenv("DB_HOST") : "postgres"; std::string dbName = std::getenv("DB_NAME") ? std::getenv("DB_NAME") : "streaming"; std::string dbUser = std::getenv("DB_USER") ? std::getenv("DB_USER") : "streamuser"; std::string dbPass = std::getenv("DB_PASS") ? std::getenv("DB_PASS") : "streampass"; // Create database client directly auto dbClient = DbClient::newPgClient( "host=" + dbHost + " port=5432 dbname=" + dbName + " user=" + dbUser + " password=" + dbPass, 1 // connection number ); writeAuditLog("PROMOTE_ADMIN_ATTEMPT", username, "STARTED"); try { // Check if user exists auto result = dbClient->execSqlSync( "SELECT id, username, is_admin FROM users WHERE username = $1", username ); if (result.empty()) { writeAuditLog("PROMOTE_ADMIN", username, "FAILED", "user_not_found"); std::cerr << "Error: User '" << username << "' not found." << std::endl; return 1; } bool isAdmin = result[0]["is_admin"].as(); if (isAdmin) { writeAuditLog("PROMOTE_ADMIN", username, "SKIPPED", "already_admin"); std::cout << "User '" << username << "' is already an admin." << std::endl; return 0; } // Promote to admin dbClient->execSqlSync( "UPDATE users SET is_admin = true WHERE username = $1", username ); writeAuditLog("PROMOTE_ADMIN", username, "SUCCESS"); std::cout << "Successfully promoted '" << username << "' to admin." << std::endl; return 0; } catch (const DrogonDbException& e) { writeAuditLog("PROMOTE_ADMIN", username, "ERROR", e.base().what()); std::cerr << "Database error: " << e.base().what() << std::endl; return 1; } catch (const std::exception& e) { writeAuditLog("PROMOTE_ADMIN", username, "ERROR", e.what()); std::cerr << "Error: " << e.what() << std::endl; return 1; } }