/
Agustangel
/
calculator
Обзор
Документация
Войти
/
Agustangel
/
calculator
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
include/database.h
311 строк
9 KB
yvladimirovmelnikova
Add file database encryption; Add security logger, password policy and global IP blocking
05 окт 2025, 11:01
05 окт 2025, 11:01
e138c05
Код
Авторство
О чём код?
#ifndef DATABASE_H #define DATABASE_H #include <sys/stat.h> #include <ctime> #include <fstream> #include <iostream> #include <map> #include <sstream> #include <string> #include <vector> #include "hash_generator.h" using namespace std; // Роли пользователей enum class Role { GUEST, // Только базовые операции USER, // Базовые операции + дополнительные ADMIN // Все операции + управление пользователями }; // Структура для хранения информации о пользователе struct UserInfo { string passwordHash; Role role; bool isActive; }; // Структура для IP-блокировки struct IPLockInfo { int attempts; time_t unlockTime; time_t lastAttemptTime; }; // Класс для работы с базой данных пользователей class UserDatabase { private: string dbFilename; map<string, UserInfo> users; map<string, IPLockInfo> ipLocks; // Блокировки по IP const int MAX_GLOBAL_ATTEMPTS = 10; // Максимум попыток с IP const int GLOBAL_LOCK_TIME = 60; // Блокировка на 1 минуту // Inline static константа для ключа шифрования inline static const string DEFAULT_ENCRYPTION_KEY = "secure_calc_key_2024!@#"; string simpleEncrypt(const string& data, const string& key) { string encrypted = data; for (size_t i = 0; i < data.length(); ++i) { encrypted[i] = data[i] ^ key[i % key.length()]; } return encrypted; } string simpleDecrypt(const string& data, const string& key) { return simpleEncrypt(data, key); // XOR обратим } void setFilePermissions() { chmod(dbFilename.c_str(), S_IRUSR | S_IWUSR); // Только владелец может читать/писать } // Очистка старых блокировок (старше 24 часов) void cleanupOldLocks() { time_t now = time(nullptr); vector<string> toRemove; for (const auto& [ip, lockInfo] : ipLocks) { if (now - lockInfo.lastAttemptTime > 86400) { // 24 часа toRemove.push_back(ip); } } for (const auto& ip : toRemove) { ipLocks.erase(ip); } } public: UserDatabase(const string& filename = "../users.dat") : dbFilename(filename) {} // Проверка блокировки IP bool isIPLocked(const string& ip) { cleanupOldLocks(); auto it = ipLocks.find(ip); if (it != ipLocks.end()) { IPLockInfo& info = it->second; if (info.attempts >= MAX_GLOBAL_ATTEMPTS) { time_t now = time(nullptr); if (now < info.unlockTime) { return true; } else { // Время блокировки истекло, сбрасываем счетчик info.attempts = 0; } } } return false; } // Получение времени разблокировки IP time_t getIPUnlockTime(const string& ip) { auto it = ipLocks.find(ip); return it != ipLocks.end() ? it->second.unlockTime : 0; } // Регистрация неудачной попытки входа с IP void registerFailedAttempt(const string& ip) { IPLockInfo& info = ipLocks[ip]; time_t now = time(nullptr); info.attempts++; info.lastAttemptTime = now; if (info.attempts >= MAX_GLOBAL_ATTEMPTS) { info.unlockTime = now + GLOBAL_LOCK_TIME; } } // Сброс счетчика попыток для IP (при успешном входе) void resetIPAttempts(const string& ip) { auto it = ipLocks.find(ip); if (it != ipLocks.end()) { it->second.attempts = 0; } } // Получение информации о блокировке IP IPLockInfo getIPLockInfo(const string& ip) { return ipLocks[ip]; } bool loadUsers(const string& encryptionKey = "") { string key = encryptionKey.empty() ? DEFAULT_ENCRYPTION_KEY : encryptionKey; ifstream file(dbFilename, ios::binary); if (!file.is_open()) { cout << "База пользователей не найдена. Создана новая." << endl; createDefaultUsers(); return saveUsers(key); } string encryptedData((istreambuf_iterator<char>(file)), istreambuf_iterator<char>()); file.close(); if (encryptedData.empty()) { cout << "База пользователей пуста." << endl; createDefaultUsers(); return saveUsers(key); } // ДЕШИФРОВКА данных string data = simpleDecrypt(encryptedData, key); // Парсинг данных stringstream ss(data); string line; users.clear(); int loadedCount = 0; while (getline(ss, line)) { if (line.empty()) continue; // Разбираем строку вручную, учитывая экранирование vector<string> parts; string part; bool escaped = false; for (char c : line) { if (escaped) { part += c; escaped = false; } else if (c == '\\') { escaped = true; } else if (c == ':') { parts.push_back(part); part.clear(); } else { part += c; } } parts.push_back(part); if (parts.size() == 4) { try { string login = parts[0]; int role = stoi(parts[1]); int active = stoi(parts[2]); string passwordHash = parts[3]; if (role < 0 || role > 2) { cout << "Некорректная роль для пользователя " << login << ": " << role << endl; continue; } users[login] = {passwordHash, static_cast<Role>(role), static_cast<bool>(active)}; loadedCount++; } catch (const exception& e) { cout << "Ошибка при загрузке пользователя: " << e.what() << " (данные: " << line << ")" << endl; } } else { cout << "Некорректный формат строки (ожидалось 4 части, получили " << parts.size() << "): " << line << endl; } } cout << "Загружено пользователей: " << loadedCount << endl; if (users.empty()) { cout << "Создана новая база пользователей по умолчанию." << endl; createDefaultUsers(); return saveUsers(key); } return true; } bool saveUsers(const string& encryptionKey = "") { string key = encryptionKey.empty() ? DEFAULT_ENCRYPTION_KEY : encryptionKey; ofstream file(dbFilename, ios::binary); if (!file.is_open()) { cerr << "Ошибка: Не удалось открыть файл для записи: " << dbFilename << endl; return false; } // Сериализация данных stringstream data; for (const auto& [login, userInfo] : users) { string escapedLogin = login; size_t pos = 0; while ((pos = escapedLogin.find(':', pos)) != string::npos) { escapedLogin.replace(pos, 1, "\\:"); pos += 2; } data << escapedLogin << ":" << static_cast<int>(userInfo.role) << ":" << (userInfo.isActive ? "1" : "0") << ":" << userInfo.passwordHash << "\n"; } string dataStr = data.str(); // ШИФРОВАНИЕ данных перед записью string encryptedOutput = simpleEncrypt(dataStr, key); file << encryptedOutput; if (!file.good()) { cerr << "Ошибка при записи в файл!" << endl; return false; } file.close(); setFilePermissions(); cout << "База данных успешно сохранена (" << users.size() << " пользователей)" << endl; return true; } void createDefaultUsers() { users = { {"admin", {SecurePasswordHasher::hashPassword("Admin123!"), Role::ADMIN, true}}, {"user1", {SecurePasswordHasher::hashPassword("User123!"), Role::USER, true}}, {"guest", {SecurePasswordHasher::hashPassword("Guest123!"), Role::GUEST, true}}}; } // Методы доступа к пользователям bool userExists(const string& login) const { return users.find(login) != users.end(); } UserInfo* getUser(const string& login) { auto it = users.find(login); return it != users.end() ? &it->second : nullptr; } const map<string, UserInfo>& getAllUsers() const { return users; } void addUser(const string& login, const string& password, Role role) { users[login] = {SecurePasswordHasher::hashPassword(password), role, true}; } bool updateUserRole(const string& login, Role newRole) { auto it = users.find(login); if (it != users.end()) { it->second.role = newRole; return true; } return false; } bool toggleUserActive(const string& login) { auto it = users.find(login); if (it != users.end()) { it->second.isActive = !it->second.isActive; return true; } return false; } bool deleteUser(const string& login) { return users.erase(login) > 0; } }; #endif