const bcrypt = require('bcryptjs'); const fetch = require('node-fetch'); class AuthService { constructor() { this.db = null; this.initialized = false; } setDatabase(database) { this.db = database; this.initialized = true; console.log('✅ База данных установлена в AuthService'); this.initUsers(); } async initUsers() { if (!this.db) { console.log('⚠️ База данных не установлена, откладываем создание пользователей'); return; } try { // Создаем пользователей из .env const users = [ { login: process.env.USER_1_LOGIN, password: process.env.USER_1_PASSWORD, name: process.env.USER_1_NAME, email: process.env.USER_1_EMAIL, auth_type: 'local' }, { login: process.env.USER_2_LOGIN, password: process.env.USER_2_PASSWORD, name: process.env.USER_2_NAME, email: process.env.USER_2_EMAIL, auth_type: 'local' }, { login: process.env.USER_3_LOGIN, password: process.env.USER_3_PASSWORD, name: process.env.USER_3_NAME, email: process.env.USER_3_EMAIL, auth_type: 'local' } ]; for (const userData of users) { if (userData.login && userData.password) { await this.createUserIfNotExists(userData); } } } catch (error) { console.error('❌ Ошибка инициализации пользователей:', error.message); } } async createUserIfNotExists(userData) { return new Promise((resolve, reject) => { if (!this.db) { console.error('❌ База данных не доступна в createUserIfNotExists'); reject(new Error('База данных не инициализирована')); return; } this.db.get("SELECT id FROM users WHERE login = ?", [userData.login], async (err, row) => { if (err) { reject(err); return; } if (!row) { const hashedPassword = await bcrypt.hash(userData.password, 10); this.db.run( "INSERT INTO users (login, password, name, email, role, auth_type, created_at) VALUES (?, ?, ?, ?, ?, ?, datetime('now'))", [ userData.login, hashedPassword, userData.name, userData.email, 'teacher', userData.auth_type || 'local' ], function(err) { if (err) { reject(err); } else { console.log(`✅ Создан пользователь: ${userData.name}`); resolve(this.lastID); } } ); } else { resolve(row.id); } }); }); } async authenticateLocal(login, password) { if (!this.db) { throw new Error('База данных не инициализирована в AuthService'); } return new Promise((resolve, reject) => { this.db.get("SELECT * FROM users WHERE login = ? AND auth_type = 'local'", [login], async (err, user) => { if (err) { reject(err); return; } if (!user) { resolve(null); return; } try { const isValid = await bcrypt.compare(password, user.password); if (isValid) { // Обновляем last_login this.db.run("UPDATE users SET last_login = datetime('now') WHERE id = ?", [user.id]); // Не возвращаем пароль const { password, ...userWithoutPassword } = user; resolve(userWithoutPassword); } else { resolve(null); } } catch (error) { reject(error); } }); }); } async authenticateLDAP(username, password) { if (!this.db) { throw new Error('База данных не инициализирована в AuthService'); } try { // Проверяем наличие URL для LDAP if (!process.env.LDAP_AUTH_URL) { console.log('⚠️ LDAP_AUTH_URL не задан в .env'); return null; } const response = await fetch(process.env.LDAP_AUTH_URL, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ username, password }) }); if (!response.ok) { console.log(`⚠️ LDAP сервер вернул ошибку: ${response.status}`); return null; } const data = await response.json(); if (data.success) { return this.processLDAPUser(data); } else { return null; } } catch (error) { console.error('❌ Ошибка LDAP аутентификации:', error.message); return null; } } async processLDAPUser(ldapData) { if (!this.db) { throw new Error('База данных не инициализирована в AuthService'); } const { username, full_name, groups, description } = ldapData; // Определяем роль пользователя на основе групп const allowedGroups = process.env.ALLOWED_GROUPS ? process.env.ALLOWED_GROUPS.split(',').map(g => g.trim()) : []; // ВАЖНО: Проверяем актуальные группы при каждом входе const isAdmin = groups && groups.some(group => allowedGroups.includes(group) ); const role = isAdmin ? 'admin' : 'teacher'; // Сохраняем/обновляем пользователя в базе return new Promise((resolve, reject) => { this.db.get("SELECT * FROM users WHERE login = ? AND auth_type = 'ldap'", [username], async (err, existingUser) => { if (err) { reject(err); return; } const userData = { login: username, name: full_name || username, email: `${username}@school25.ru`, role: role, // Всегда обновляем роль из актуальных групп auth_type: 'ldap', groups: groups ? JSON.stringify(groups) : '[]', description: description || '', last_login: new Date().toISOString() }; if (existingUser) { // Всегда обновляем роль, даже если пользователь уже существует this.db.run( `UPDATE users SET name = ?, email = ?, role = ?, groups = ?, description = ?, last_login = datetime('now'), updated_at = datetime('now') WHERE id = ?`, [userData.name, userData.email, userData.role, userData.groups, userData.description, existingUser.id], function(err) { if (err) { reject(err); } else { console.log(`✅ Обновлены данные LDAP пользователя ${username}. Роль: ${userData.role}, Группы: ${groups}`); resolve({ id: existingUser.id, login: userData.login, name: userData.name, email: userData.email, role: userData.role, auth_type: userData.auth_type, groups: userData.groups, description: userData.description, last_login: new Date().toISOString() }); } } ); } else { // Создаем нового пользователя this.db.run( `INSERT INTO users (login, name, email, role, auth_type, groups, description, created_at, last_login) VALUES (?, ?, ?, ?, ?, ?, ?, datetime('now'), datetime('now'))`, [userData.login, userData.name, userData.email, userData.role, userData.auth_type, userData.groups, userData.description], function(err) { if (err) { reject(err); } else { console.log(`✅ Создан новый LDAP пользователь ${username}. Роль: ${userData.role}, Группы: ${groups}`); resolve({ id: this.lastID, login: userData.login, name: userData.name, email: userData.email, role: userData.role, auth_type: userData.auth_type, groups: userData.groups, description: userData.description, last_login: new Date().toISOString() }); } } ); } }); }); } async authenticate(login, password) { if (!this.db) { throw new Error('База данных не инициализирована в AuthService'); } // Сначала пробуем локальную авторизацию try { const localUser = await this.authenticateLocal(login, password); if (localUser) { return localUser; } } catch (error) { console.error('Ошибка локальной аутентификации:', error.message); } // Если локальная не сработала, пробуем LDAP try { const ldapUser = await this.authenticateLDAP(login, password); if (ldapUser) { return ldapUser; } } catch (error) { console.error('Ошибка LDAP аутентификации:', error.message); } return null; } getUserById(id) { if (!this.db) { throw new Error('База данных не инициализирована в AuthService'); } return new Promise((resolve, reject) => { this.db.get("SELECT id, login, name, email, role, auth_type, groups, description, last_login FROM users WHERE id = ?", [id], (err, user) => { if (err) { reject(err); } else { resolve(user); } }); }); } // Метод для проверки готовности сервиса isReady() { return this.db !== null; } } // Создаем singleton экземпляр const authService = new AuthService(); module.exports = authService;