Files
minicrm/auth.js
2026-02-01 22:03:01 +05:00

358 lines
15 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
// Определяем роль пользователя на основе групп
// Получаем все группы из .env
const allowedGroups = process.env.ALLOWED_GROUPS ?
process.env.ALLOWED_GROUPS.split(',').map(g => g.trim()) : [];
const secretaryGroups = process.env.SECRETARY_GROUPS ?
process.env.SECRETARY_GROUPS.split(',').map(g => g.trim()) : [];
const helpGroups = process.env.HELP_GROUPS ?
process.env.HELP_GROUPS.split(',').map(g => g.trim()) : [];
const itHelpGroups = process.env.ITHELP_GROUPS ?
process.env.ITHELP_GROUPS.split(',').map(g => g.trim()) : [];
const requestGroups = process.env.REQUEST_GROUPS ?
process.env.REQUEST_GROUPS.split(',').map(g => g.trim()) : [];
const tasksGroups = process.env.TASKS_GROUPS ?
process.env.TASKS_GROUPS.split(',').map(g => g.trim()) : [];
// Все LDAP пользователи являются "teacher" по умолчанию
let userGroups = groups ? [...groups] : [];
// Проверяем все группы пользователя и определяем роль
// Если пользователь состоит в нескольких группах, приоритет определяется порядком проверки
let role = 'teacher'; // Роль по умолчанию для всех пользователей
if (userGroups.length > 0) {
// Определяем наивысшую роль пользователя
// Порядок приоритета: admin > secretary > help > ithelp > request > tasks > teacher
if (userGroups.some(group => allowedGroups.includes(group))) {
role = 'admin';
} else if (userGroups.some(group => secretaryGroups.includes(group))) {
role = 'secretary';
} else if (userGroups.some(group => helpGroups.includes(group))) {
role = 'help';
} else if (userGroups.some(group => itHelpGroups.includes(group))) {
role = 'ithelp';
} else if (userGroups.some(group => requestGroups.includes(group))) {
role = 'request';
} else if (userGroups.some(group => tasksGroups.includes(group))) {
role = 'tasks';
}
// Если ни одна из специальных групп не найдена, остается '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: JSON.stringify(userGroups),
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}, Группы: ${userGroups.length}`);
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}, Группы: ${userGroups.length}`);
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;