| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065 |
- #include "user_manager.h"
- #include "jwt_auth.h"
- #include <nlohmann/json.hpp>
- #include <fstream>
- #include <filesystem>
- #include <sstream>
- #include <iomanip>
- #include <algorithm>
- #include <regex>
- #include <crypt.h>
- #include <unistd.h>
- #include <pwd.h>
- #include <shadow.h>
- #include <sys/types.h>
- #include <openssl/sha.h>
- // Define static permission constants
- const std::string UserManager::Permissions::READ = "read";
- const std::string UserManager::Permissions::GENERATE = "generate";
- const std::string UserManager::Permissions::QUEUE_MANAGE = "queue_manage";
- const std::string UserManager::Permissions::MODEL_MANAGE = "model_manage";
- const std::string UserManager::Permissions::USER_MANAGE = "user_manage";
- const std::string UserManager::Permissions::ADMIN = "admin";
- UserManager::UserManager(const std::string& dataDir,
- AuthMethod authMethod)
- : m_dataDir(dataDir)
- , m_authMethod(authMethod)
- , m_pamAuthEnabled(false)
- {
- #ifdef ENABLE_PAM_AUTH
- m_pamAuth = std::make_unique<PamAuth>();
- #endif
- }
- UserManager::~UserManager() {
- shutdown();
- }
- bool UserManager::initialize() {
- try {
- // Create data directory if it doesn't exist
- std::filesystem::create_directories(m_dataDir);
- // Load existing user data
- if (!loadUserData()) {
- // Create default admin user if no users exist
- if (m_users.empty()) {
- auto [success, adminId] = createUser("admin", "admin123", "admin@localhost", UserRole::ADMIN, "system");
- if (!success) {
- return false;
- }
- }
- }
- // Load existing API key data
- loadApiKeyData();
- return true;
- } catch (const std::exception& e) {
- return false;
- }
- }
- void UserManager::shutdown() {
- // Save data before shutdown
- saveUserData();
- saveApiKeyData();
- }
- AuthResult UserManager::authenticateUser(const std::string& username, const std::string& password) {
- AuthResult result;
- result.success = false;
- try {
- // Find user by username
- auto it = m_users.find(username);
- if (it == m_users.end()) {
- result.errorMessage = "User not found";
- result.errorCode = "USER_NOT_FOUND";
- return result;
- }
- const UserInfo& user = it->second;
- // Check if user is active
- if (!user.active) {
- result.errorMessage = "User account is disabled";
- result.errorCode = "ACCOUNT_DISABLED";
- return result;
- }
- // Verify password
- if (!verifyPassword(password, user.passwordHash)) {
- result.errorMessage = "Invalid password";
- result.errorCode = "INVALID_PASSWORD";
- return result;
- }
- // Authentication successful
- result.success = true;
- result.userId = user.id;
- result.username = user.username;
- result.role = user.role;
- result.permissions = user.permissions;
- // Update last login time
- m_users[username].lastLoginAt = getCurrentTimestamp();
- saveUserData();
- } catch (const std::exception& e) {
- result.errorMessage = "Authentication failed: " + std::string(e.what());
- result.errorCode = "AUTH_ERROR";
- }
- return result;
- }
- AuthResult UserManager::authenticateUnix(const std::string& username, const std::string& password) {
- AuthResult result;
- result.success = false;
- // Check if Unix authentication is the configured method
- if (m_authMethod != AuthMethod::UNIX) {
- result.errorMessage = "Unix authentication is not enabled";
- result.errorCode = "UNIX_AUTH_DISABLED";
- return result;
- }
- try {
- // If PAM is enabled, delegate to PAM authentication
- if (m_pamAuthEnabled) {
- return authenticatePam(username, password);
- }
- // Traditional Unix auth without PAM - just check if user exists
- // This is a fallback for when PAM is not available
- struct passwd* pw = getpwnam(username.c_str());
- if (!pw) {
- result.errorMessage = "Unix user not found";
- result.errorCode = "UNIX_USER_NOT_FOUND";
- return result;
- }
- // Check if user exists in our system or create guest user
- UserInfo user;
- auto it = m_users.find(username);
- if (it != m_users.end()) {
- user = it->second;
- } else {
- // Create guest user for Unix authentication
- user.id = generateUserId();
- user.username = username;
- user.email = username + "@localhost";
- user.role = roleToString(UserRole::USER);
- user.permissions = getDefaultPermissions(UserRole::USER);
- user.active = true;
- user.createdAt = getCurrentTimestamp();
- user.createdBy = "system";
- m_users[username] = user;
- saveUserData();
- }
- // Authentication successful
- result.success = true;
- result.userId = user.id;
- result.username = user.username;
- result.role = user.role;
- result.permissions = user.permissions;
- } catch (const std::exception& e) {
- result.errorMessage = "Unix authentication failed: " + std::string(e.what());
- result.errorCode = "UNIX_AUTH_ERROR";
- }
- return result;
- }
- AuthResult UserManager::authenticatePam(const std::string& username, const std::string& password) {
- AuthResult result;
- result.success = false;
- // Check if PAM authentication is the configured method
- if (m_authMethod != AuthMethod::PAM) {
- result.errorMessage = "PAM authentication is not enabled";
- result.errorCode = "PAM_AUTH_DISABLED";
- return result;
- }
- #ifdef ENABLE_PAM_AUTH
- if (!m_pamAuth || !m_pamAuth->isAvailable()) {
- result.errorMessage = "PAM authentication not available";
- result.errorCode = "PAM_AUTH_UNAVAILABLE";
- return result;
- }
- try {
- // Authenticate with PAM
- PamAuthResult pamResult = m_pamAuth->authenticate(username, password);
- if (!pamResult.success) {
- result.errorMessage = pamResult.errorMessage;
- result.errorCode = pamResult.errorCode;
- return result;
- }
- // Check if user exists in our system or create guest user
- UserInfo user;
- auto it = m_users.find(username);
- if (it != m_users.end()) {
- user = it->second;
- } else {
- // Create guest user for PAM authentication
- user.id = generateUserId();
- user.username = username;
- user.email = username + "@localhost";
- user.role = roleToString(UserRole::USER);
- user.permissions = getDefaultPermissions(UserRole::USER);
- user.active = true;
- user.createdAt = getCurrentTimestamp();
- user.createdBy = "system";
- m_users[username] = user;
- saveUserData();
- }
- // Authentication successful
- result.success = true;
- result.userId = user.id;
- result.username = user.username;
- result.role = user.role;
- result.permissions = user.permissions;
- } catch (const std::exception& e) {
- result.errorMessage = "PAM authentication failed: " + std::string(e.what());
- result.errorCode = "PAM_AUTH_ERROR";
- }
- #else
- result.errorMessage = "PAM authentication not available (compiled without PAM support)";
- result.errorCode = "PAM_NOT_AVAILABLE";
- #endif
- return result;
- }
- AuthResult UserManager::authenticateApiKey(const std::string& apiKey) {
- AuthResult result;
- result.success = false;
- try {
- // Hash the API key to compare with stored hashes
- std::string keyHash = hashApiKey(apiKey);
- auto it = m_apiKeyMap.find(keyHash);
- if (it == m_apiKeyMap.end()) {
- result.errorMessage = "Invalid API key";
- result.errorCode = "INVALID_API_KEY";
- return result;
- }
- const std::string& keyId = it->second;
- const ApiKeyInfo& keyInfo = m_apiKeys[keyId];
- // Check if key is active
- if (!keyInfo.active) {
- result.errorMessage = "API key is disabled";
- result.errorCode = "API_KEY_DISABLED";
- return result;
- }
- // Check expiration
- if (keyInfo.expiresAt > 0 && getCurrentTimestamp() >= keyInfo.expiresAt) {
- result.errorMessage = "API key has expired";
- result.errorCode = "API_KEY_EXPIRED";
- return result;
- }
- // Get user information
- auto userIt = m_users.find(keyInfo.userId);
- if (userIt == m_users.end()) {
- result.errorMessage = "API key owner not found";
- result.errorCode = "USER_NOT_FOUND";
- return result;
- }
- const UserInfo& user = userIt->second;
- if (!user.active) {
- result.errorMessage = "API key owner account is disabled";
- result.errorCode = "ACCOUNT_DISABLED";
- return result;
- }
- // Authentication successful
- result.success = true;
- result.userId = user.id;
- result.username = user.username;
- result.role = user.role;
- result.permissions = keyInfo.permissions.empty() ? user.permissions : keyInfo.permissions;
- // Update last used timestamp
- m_apiKeys[keyId].lastUsedAt = getCurrentTimestamp();
- saveApiKeyData();
- } catch (const std::exception& e) {
- result.errorMessage = "API key authentication failed: " + std::string(e.what());
- result.errorCode = "API_KEY_AUTH_ERROR";
- }
- return result;
- }
- std::pair<bool, std::string> UserManager::createUser(const std::string& username,
- const std::string& password,
- const std::string& email,
- UserRole role,
- const std::string& createdBy) {
- try {
- // Validate inputs
- if (!validateUsername(username)) {
- return {false, "Invalid username format"};
- }
- if (!validatePassword(password)) {
- return {false, "Password does not meet requirements"};
- }
- if (!validateEmail(email)) {
- return {false, "Invalid email format"};
- }
- // Check if user already exists
- if (m_users.find(username) != m_users.end()) {
- return {false, "User already exists"};
- }
- // Create user
- UserInfo user;
- user.id = generateUserId();
- user.username = username;
- user.email = email;
- user.passwordHash = hashPassword(password);
- user.role = roleToString(role);
- user.permissions = getDefaultPermissions(role);
- user.active = true;
- user.createdAt = getCurrentTimestamp();
- user.passwordChangedAt = getCurrentTimestamp();
- user.createdBy = createdBy;
- m_users[username] = user;
- saveUserData();
- return {true, user.id};
- } catch (const std::exception& e) {
- return {false, "Failed to create user: " + std::string(e.what())};
- }
- }
- std::pair<bool, std::string> UserManager::updateUser(const std::string& userId,
- const std::map<std::string, std::string>& updates) {
- try {
- // Find user by ID
- std::string username;
- for (const auto& pair : m_users) {
- if (pair.second.id == userId) {
- username = pair.first;
- break;
- }
- }
- if (username.empty()) {
- return {false, "User not found"};
- }
- UserInfo& user = m_users[username];
- // Update allowed fields
- for (const auto& update : updates) {
- const std::string& field = update.first;
- const std::string& value = update.second;
- if (field == "email") {
- if (!validateEmail(value)) {
- return {false, "Invalid email format"};
- }
- user.email = value;
- } else if (field == "role") {
- user.role = value;
- user.permissions = getDefaultPermissions(stringToRole(value));
- } else if (field == "active") {
- user.active = (value == "true" || value == "1");
- }
- }
- saveUserData();
- return {true, "User updated successfully"};
- } catch (const std::exception& e) {
- return {false, "Failed to update user: " + std::string(e.what())};
- }
- }
- std::pair<bool, std::string> UserManager::deleteUser(const std::string& userId,
- const std::string& requestingUserId) {
- try {
- // Find user by ID
- std::string username;
- for (const auto& pair : m_users) {
- if (pair.second.id == userId) {
- username = pair.first;
- break;
- }
- }
- if (username.empty()) {
- return {false, "User not found"};
- }
- // Check permissions
- if (!canManageUser(requestingUserId, userId)) {
- return {false, "Insufficient permissions to delete user"};
- }
- // Delete user's API keys
- auto it = m_apiKeys.begin();
- while (it != m_apiKeys.end()) {
- if (it->second.userId == userId) {
- m_apiKeyMap.erase(it->second.keyHash);
- it = m_apiKeys.erase(it);
- } else {
- ++it;
- }
- }
- // Delete user
- m_users.erase(username);
- saveUserData();
- saveApiKeyData();
- return {true, "User deleted successfully"};
- } catch (const std::exception& e) {
- return {false, "Failed to delete user: " + std::string(e.what())};
- }
- }
- std::pair<bool, std::string> UserManager::changePassword(const std::string& userId,
- const std::string& oldPassword,
- const std::string& newPassword,
- const std::string& requestingUserId) {
- try {
- // Find user by ID
- std::string username;
- for (const auto& pair : m_users) {
- if (pair.second.id == userId) {
- username = pair.first;
- break;
- }
- }
- if (username.empty()) {
- return {false, "User not found"};
- }
- UserInfo& user = m_users[username];
- // Check permissions (admin can change without old password)
- if (requestingUserId != userId) {
- if (!canManageUser(requestingUserId, userId)) {
- return {false, "Insufficient permissions to change password"};
- }
- } else {
- // User changing own password - verify old password
- if (!verifyPassword(oldPassword, user.passwordHash)) {
- return {false, "Current password is incorrect"};
- }
- }
- // Validate new password
- if (!validatePassword(newPassword)) {
- return {false, "New password does not meet requirements"};
- }
- // Update password
- user.passwordHash = hashPassword(newPassword);
- user.passwordChangedAt = getCurrentTimestamp();
- saveUserData();
- return {true, "Password changed successfully"};
- } catch (const std::exception& e) {
- return {false, "Failed to change password: " + std::string(e.what())};
- }
- }
- UserInfo UserManager::getUserInfo(const std::string& userId) {
- for (const auto& pair : m_users) {
- if (pair.second.id == userId) {
- return pair.second;
- }
- }
- return UserInfo{};
- }
- UserInfo UserManager::getUserInfoByUsername(const std::string& username) {
- auto it = m_users.find(username);
- if (it != m_users.end()) {
- return it->second;
- }
- return UserInfo{};
- }
- std::vector<UserInfo> UserManager::listUsers(const std::string& requestingUserId) {
- std::vector<UserInfo> users;
- // Check if requester is admin
- UserInfo requester = getUserInfo(requestingUserId);
- bool isAdmin = (requester.role == roleToString(UserRole::ADMIN));
- for (const auto& pair : m_users) {
- const UserInfo& user = pair.second;
- // Non-admins can only see themselves
- if (!isAdmin && user.id != requestingUserId) {
- continue;
- }
- // Don't include sensitive information for non-admins
- UserInfo userInfo = user;
- if (!isAdmin) {
- userInfo.passwordHash = "";
- userInfo.apiKeys.clear();
- }
- users.push_back(userInfo);
- }
- return users;
- }
- std::pair<bool, std::string> UserManager::createApiKey(const std::string& userId,
- const std::string& name,
- const std::vector<std::string>& permissions,
- int64_t expiresAt,
- const std::string& createdBy) {
- try {
- // Check if user exists
- bool userExists = false;
- for (const auto& pair : m_users) {
- if (pair.second.id == userId) {
- userExists = true;
- break;
- }
- }
- if (!userExists) {
- return {false, "User not found"};
- }
- // Generate API key
- std::string apiKey = JWTAuth::generateApiKey(32);
- std::string keyId = generateKeyId();
- std::string keyHash = hashApiKey(apiKey);
- // Create API key info
- ApiKeyInfo keyInfo;
- keyInfo.keyId = keyId;
- keyInfo.keyHash = keyHash;
- keyInfo.name = name;
- keyInfo.userId = userId;
- keyInfo.permissions = permissions;
- keyInfo.active = true;
- keyInfo.createdAt = getCurrentTimestamp();
- keyInfo.lastUsedAt = 0;
- keyInfo.expiresAt = expiresAt;
- keyInfo.createdBy = createdBy;
- m_apiKeys[keyId] = keyInfo;
- m_apiKeyMap[keyHash] = keyId;
- saveApiKeyData();
- return {true, apiKey};
- } catch (const std::exception& e) {
- return {false, "Failed to create API key: " + std::string(e.what())};
- }
- }
- std::pair<bool, std::string> UserManager::revokeApiKey(const std::string& keyId,
- const std::string& requestingUserId) {
- try {
- auto it = m_apiKeys.find(keyId);
- if (it == m_apiKeys.end()) {
- return {false, "API key not found"};
- }
- const ApiKeyInfo& keyInfo = it->second;
- // Check permissions
- if (keyInfo.userId != requestingUserId) {
- if (!canManageUser(requestingUserId, keyInfo.userId)) {
- return {false, "Insufficient permissions to revoke API key"};
- }
- }
- // Remove API key
- m_apiKeyMap.erase(keyInfo.keyHash);
- m_apiKeys.erase(it);
- saveApiKeyData();
- return {true, "API key revoked successfully"};
- } catch (const std::exception& e) {
- return {false, "Failed to revoke API key: " + std::string(e.what())};
- }
- }
- std::vector<ApiKeyInfo> UserManager::listApiKeys(const std::string& userId,
- const std::string& requestingUserId) {
- std::vector<ApiKeyInfo> apiKeys;
- // Check if requester is admin or owner
- UserInfo requester = getUserInfo(requestingUserId);
- bool isAdmin = (requester.role == roleToString(UserRole::ADMIN));
- bool isOwner = (requestingUserId == userId);
- for (const auto& pair : m_apiKeys) {
- const ApiKeyInfo& keyInfo = pair.second;
- // Filter by user ID if specified
- if (!userId.empty() && keyInfo.userId != userId) {
- continue;
- }
- // Check permissions
- if (!isAdmin && keyInfo.userId != requestingUserId) {
- continue;
- }
- // Don't include hash for non-owners
- ApiKeyInfo keyInfoCopy = keyInfo;
- if (!isOwner && !isAdmin) {
- keyInfoCopy.keyHash = "";
- }
- apiKeys.push_back(keyInfoCopy);
- }
- return apiKeys;
- }
- ApiKeyInfo UserManager::getApiKeyInfo(const std::string& keyId,
- const std::string& requestingUserId) {
- auto it = m_apiKeys.find(keyId);
- if (it == m_apiKeys.end()) {
- return ApiKeyInfo{};
- }
- const ApiKeyInfo& keyInfo = it->second;
- // Check permissions
- UserInfo requester = getUserInfo(requestingUserId);
- bool isAdmin = (requester.role == roleToString(UserRole::ADMIN));
- bool isOwner = (keyInfo.userId == requestingUserId);
- if (!isAdmin && !isOwner) {
- return ApiKeyInfo{};
- }
- // Don't include hash for non-owners
- ApiKeyInfo keyInfoCopy = keyInfo;
- if (!isOwner) {
- keyInfoCopy.keyHash = "";
- }
- return keyInfoCopy;
- }
- void UserManager::updateApiKeyLastUsed(const std::string& keyId) {
- auto it = m_apiKeys.find(keyId);
- if (it != m_apiKeys.end()) {
- it->second.lastUsedAt = getCurrentTimestamp();
- saveApiKeyData();
- }
- }
- bool UserManager::hasPermission(const std::string& userId, const std::string& permission) {
- UserInfo user = getUserInfo(userId);
- if (user.id.empty()) {
- return false;
- }
- return JWTAuth::hasPermission(user.permissions, permission);
- }
- bool UserManager::hasAnyPermission(const std::string& userId,
- const std::vector<std::string>& permissions) {
- UserInfo user = getUserInfo(userId);
- if (user.id.empty()) {
- return false;
- }
- return JWTAuth::hasAnyPermission(user.permissions, permissions);
- }
- std::string UserManager::roleToString(UserRole role) {
- switch (role) {
- case UserRole::GUEST: return "guest";
- case UserRole::USER: return "user";
- case UserRole::ADMIN: return "admin";
- case UserRole::SERVICE: return "service";
- default: return "unknown";
- }
- }
- UserManager::UserRole UserManager::stringToRole(const std::string& roleStr) {
- if (roleStr == "guest") return UserRole::GUEST;
- if (roleStr == "user") return UserRole::USER;
- if (roleStr == "admin") return UserRole::ADMIN;
- if (roleStr == "service") return UserRole::SERVICE;
- return UserRole::USER; // Default
- }
- std::vector<std::string> UserManager::getDefaultPermissions(UserRole role) {
- switch (role) {
- case UserRole::GUEST:
- return {Permissions::READ};
- case UserRole::USER:
- return {Permissions::READ, Permissions::GENERATE};
- case UserRole::ADMIN:
- return {Permissions::READ, Permissions::GENERATE, Permissions::QUEUE_MANAGE,
- Permissions::MODEL_MANAGE, Permissions::USER_MANAGE, Permissions::ADMIN};
- case UserRole::SERVICE:
- return {Permissions::READ, Permissions::GENERATE, Permissions::QUEUE_MANAGE};
- default:
- return {};
- }
- }
- void UserManager::setAuthMethod(AuthMethod method) {
- m_authMethod = method;
- }
- UserManager::AuthMethod UserManager::getAuthMethod() const {
- return m_authMethod;
- }
- void UserManager::setPamAuthEnabled(bool enable) {
- m_pamAuthEnabled = enable;
- #ifdef ENABLE_PAM_AUTH
- if (enable && m_pamAuth && !m_pamAuth->isAvailable()) {
- if (!m_pamAuth->initialize()) {
- m_pamAuthEnabled = false;
- }
- }
- #endif
- }
- bool UserManager::isPamAuthEnabled() const {
- return m_pamAuthEnabled;
- }
- std::map<std::string, int> UserManager::getStatistics() {
- std::map<std::string, int> stats;
- stats["total_users"] = m_users.size();
- stats["active_users"] = 0;
- stats["admin_users"] = 0;
- stats["total_api_keys"] = m_apiKeys.size();
- stats["active_api_keys"] = 0;
- stats["expired_api_keys"] = 0;
- int64_t currentTime = getCurrentTimestamp();
- for (const auto& pair : m_users) {
- const UserInfo& user = pair.second;
- if (user.active) {
- stats["active_users"]++;
- }
- if (user.role == roleToString(UserRole::ADMIN)) {
- stats["admin_users"]++;
- }
- }
- for (const auto& pair : m_apiKeys) {
- const ApiKeyInfo& keyInfo = pair.second;
- if (keyInfo.active) {
- stats["active_api_keys"]++;
- }
- if (keyInfo.expiresAt > 0 && currentTime >= keyInfo.expiresAt) {
- stats["expired_api_keys"]++;
- }
- }
- return stats;
- }
- std::string UserManager::hashPassword(const std::string& password) {
- // Simple SHA256 hash for now (in production, use proper password hashing like bcrypt/argon2)
- unsigned char hash[SHA256_DIGEST_LENGTH];
- SHA256((unsigned char*)password.c_str(), password.length(), hash);
- std::stringstream ss;
- for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
- ss << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i];
- }
- return "sha256:" + ss.str();
- }
- bool UserManager::verifyPassword(const std::string& password, const std::string& storedHash) {
- // Simple SHA256 verification (in production, use proper password hashing)
- if (storedHash.substr(0, 7) != "sha256:") {
- return false;
- }
- unsigned char hash[SHA256_DIGEST_LENGTH];
- SHA256((unsigned char*)password.c_str(), password.length(), hash);
- std::stringstream ss;
- for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
- ss << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i];
- }
- return "sha256:" + ss.str() == storedHash;
- }
- std::string UserManager::hashApiKey(const std::string& apiKey) {
- // Use SHA256 for API key hashing
- unsigned char hash[SHA256_DIGEST_LENGTH];
- SHA256((unsigned char*)apiKey.c_str(), apiKey.length(), hash);
- std::stringstream ss;
- for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
- ss << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i];
- }
- return ss.str();
- }
- std::string UserManager::generateUserId() {
- return "user_" + std::to_string(getCurrentTimestamp()) + "_" +
- std::to_string(rand() % 10000);
- }
- std::string UserManager::generateKeyId() {
- return "key_" + std::to_string(getCurrentTimestamp()) + "_" +
- std::to_string(rand() % 10000);
- }
- bool UserManager::saveUserData() {
- try {
- nlohmann::json usersData = nlohmann::json::object();
- for (const auto& pair : m_users) {
- const UserInfo& user = pair.second;
- nlohmann::json userData = {
- {"id", user.id},
- {"username", user.username},
- {"email", user.email},
- {"password_hash", user.passwordHash},
- {"role", user.role},
- {"permissions", user.permissions},
- {"api_keys", user.apiKeys},
- {"active", user.active},
- {"created_at", user.createdAt},
- {"last_login_at", user.lastLoginAt},
- {"password_changed_at", user.passwordChangedAt},
- {"created_by", user.createdBy}
- };
- usersData[user.username] = userData;
- }
- std::string filename = m_dataDir + "/users.json";
- std::ofstream file(filename);
- if (!file.is_open()) {
- return false;
- }
- file << usersData.dump(2);
- file.close();
- return true;
- } catch (const std::exception& e) {
- return false;
- }
- }
- bool UserManager::loadUserData() {
- try {
- std::string filename = m_dataDir + "/users.json";
- std::ifstream file(filename);
- if (!file.is_open()) {
- return false; // File doesn't exist is OK for first run
- }
- nlohmann::json usersJson;
- file >> usersJson;
- file.close();
- m_users.clear();
- for (auto& item : usersJson.items()) {
- const std::string& username = item.key();
- nlohmann::json& userJson = item.value();
- UserInfo user;
- user.id = userJson.value("id", "");
- user.username = userJson.value("username", username);
- user.email = userJson.value("email", "");
- user.passwordHash = userJson.value("password_hash", "");
- user.role = userJson.value("role", "user");
- user.permissions = userJson.value("permissions", std::vector<std::string>{});
- user.apiKeys = userJson.value("api_keys", std::vector<std::string>{});
- user.active = userJson.value("active", true);
- user.createdAt = userJson.value("created_at", 0);
- user.lastLoginAt = userJson.value("last_login_at", 0);
- user.passwordChangedAt = userJson.value("password_changed_at", 0);
- user.createdBy = userJson.value("created_by", "system");
- m_users[username] = user;
- }
- return true;
- } catch (const std::exception& e) {
- return false;
- }
- }
- bool UserManager::saveApiKeyData() {
- try {
- nlohmann::json apiKeysData = nlohmann::json::object();
- for (const auto& pair : m_apiKeys) {
- const ApiKeyInfo& keyInfo = pair.second;
- nlohmann::json keyData = {
- {"key_id", keyInfo.keyId},
- {"key_hash", keyInfo.keyHash},
- {"name", keyInfo.name},
- {"user_id", keyInfo.userId},
- {"permissions", keyInfo.permissions},
- {"active", keyInfo.active},
- {"created_at", keyInfo.createdAt},
- {"last_used_at", keyInfo.lastUsedAt},
- {"expires_at", keyInfo.expiresAt},
- {"created_by", keyInfo.createdBy}
- };
- apiKeysData[keyInfo.keyId] = keyData;
- }
- std::string filename = m_dataDir + "/api_keys.json";
- std::ofstream file(filename);
- if (!file.is_open()) {
- return false;
- }
- file << apiKeysData.dump(2);
- file.close();
- return true;
- } catch (const std::exception& e) {
- return false;
- }
- }
- bool UserManager::loadApiKeyData() {
- try {
- std::string filename = m_dataDir + "/api_keys.json";
- std::ifstream file(filename);
- if (!file.is_open()) {
- return false; // File doesn't exist is OK for first run
- }
- nlohmann::json apiKeysJson;
- file >> apiKeysJson;
- file.close();
- m_apiKeys.clear();
- m_apiKeyMap.clear();
- for (auto& item : apiKeysJson.items()) {
- const std::string& keyId = item.key();
- nlohmann::json& keyJson = item.value();
- ApiKeyInfo keyInfo;
- keyInfo.keyId = keyJson.value("key_id", keyId);
- keyInfo.keyHash = keyJson.value("key_hash", "");
- keyInfo.name = keyJson.value("name", "");
- keyInfo.userId = keyJson.value("user_id", "");
- keyInfo.permissions = keyJson.value("permissions", std::vector<std::string>{});
- keyInfo.active = keyJson.value("active", true);
- keyInfo.createdAt = keyJson.value("created_at", 0);
- keyInfo.lastUsedAt = keyJson.value("last_used_at", 0);
- keyInfo.expiresAt = keyJson.value("expires_at", 0);
- keyInfo.createdBy = keyJson.value("created_by", "system");
- m_apiKeys[keyId] = keyInfo;
- m_apiKeyMap[keyInfo.keyHash] = keyId;
- }
- return true;
- } catch (const std::exception& e) {
- return false;
- }
- }
- int64_t UserManager::getCurrentTimestamp() {
- return std::chrono::duration_cast<std::chrono::seconds>(
- std::chrono::system_clock::now().time_since_epoch()).count();
- }
- bool UserManager::validateUsername(const std::string& username) {
- if (username.length() < 3 || username.length() > 32) {
- return false;
- }
- // Username should contain only alphanumeric characters, underscores, and hyphens
- std::regex pattern("^[a-zA-Z0-9_-]+$");
- return std::regex_match(username, pattern);
- }
- bool UserManager::validatePassword(const std::string& password) {
- if (password.length() < 8 || password.length() > 128) {
- return false;
- }
- // Password should contain at least one letter and one digit
- bool hasLetter = false;
- bool hasDigit = false;
- for (char c : password) {
- if (std::isalpha(c)) hasLetter = true;
- if (std::isdigit(c)) hasDigit = true;
- }
- return hasLetter && hasDigit;
- }
- bool UserManager::validateEmail(const std::string& email) {
- if (email.length() < 5 || email.length() > 254) {
- return false;
- }
- // Basic email validation
- std::regex pattern("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$");
- return std::regex_match(email, pattern);
- }
- bool UserManager::canManageUser(const std::string& requestingUserId, const std::string& targetUserId) {
- // Users can always manage themselves
- if (requestingUserId == targetUserId) {
- return true;
- }
- // Check if requester is admin
- UserInfo requester = getUserInfo(requestingUserId);
- return requester.role == roleToString(UserRole::ADMIN);
- }
|