Группы 2.1
This commit is contained in:
@@ -49,7 +49,7 @@ module.exports = function(app, db) {
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
service_type TEXT NOT NULL, -- 'sberbank', 'yandex', 'ldap', 'other'
|
||||
external_id TEXT NOT NULL,
|
||||
external_id TEXT, -- СДЕЛАЛИ НЕОБЯЗАТЕЛЬНЫМ
|
||||
login TEXT, -- Логин LDAP (если есть)
|
||||
ldap_group TEXT, -- Группа LDAP (если есть)
|
||||
group_id INTEGER, -- Ссылка на группу в idgroups
|
||||
@@ -59,7 +59,7 @@ module.exports = function(app, db) {
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (group_id) REFERENCES idgroups (id) ON DELETE SET NULL,
|
||||
UNIQUE(user_id, service_type, external_id)
|
||||
UNIQUE(user_id, service_type, external_id) -- Теперь external_id может быть NULL
|
||||
)
|
||||
`, (err) => {
|
||||
if (err) {
|
||||
@@ -522,10 +522,10 @@ module.exports = function(app, db) {
|
||||
is_active
|
||||
} = req.body;
|
||||
|
||||
// Валидация обязательных полей
|
||||
if (!user_id || !service_type || !external_id) {
|
||||
// Валидация обязательных полей - СДЕЛАЕМ external_id НЕОБЯЗАТЕЛЬНЫМ
|
||||
if (!user_id || !service_type) {
|
||||
return res.status(400).json({
|
||||
error: 'Обязательные поля: user_id, service_type, external_id'
|
||||
error: 'Обязательные поля: user_id, service_type'
|
||||
});
|
||||
}
|
||||
|
||||
@@ -559,14 +559,8 @@ module.exports = function(app, db) {
|
||||
return res.status(404).json({ error: 'Указанная группа не найдена' });
|
||||
}
|
||||
|
||||
// Проверяем соответствие типа сервиса
|
||||
if (group.service_type !== service_type) {
|
||||
return res.status(400).json({
|
||||
error: `Тип сервиса группы (${group.service_type}) не соответствует типу сервиса идентификатора (${service_type})`
|
||||
});
|
||||
}
|
||||
|
||||
// Создаем идентификатор
|
||||
// УБИРАЕМ проверку соответствия типа сервиса
|
||||
// Теперь группы независимы от сервиса
|
||||
createIdUser();
|
||||
});
|
||||
} else {
|
||||
@@ -577,6 +571,9 @@ module.exports = function(app, db) {
|
||||
function createIdUser() {
|
||||
const metadataJson = metadata ? JSON.stringify(metadata) : null;
|
||||
|
||||
// Генерируем уникальный external_id если он не предоставлен
|
||||
const finalExternalId = external_id || `temp_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
||||
|
||||
db.run(
|
||||
`INSERT INTO idusers
|
||||
(user_id, service_type, external_id, login, ldap_group, group_id, metadata, is_active)
|
||||
@@ -584,7 +581,7 @@ module.exports = function(app, db) {
|
||||
[
|
||||
user_id,
|
||||
service_type,
|
||||
external_id,
|
||||
finalExternalId,
|
||||
login || null,
|
||||
ldap_group || null,
|
||||
group_id || null,
|
||||
@@ -638,6 +635,31 @@ module.exports = function(app, db) {
|
||||
return res.status(404).json({ error: 'Идентификатор не найден' });
|
||||
}
|
||||
|
||||
// Если указана группа и она меняется, проверяем существование новой группы
|
||||
if (group_id !== undefined && group_id !== existing.group_id) {
|
||||
if (group_id) {
|
||||
db.get('SELECT id FROM idgroups WHERE id = ?', [group_id], (err, group) => {
|
||||
if (err) {
|
||||
console.error('❌ Ошибка проверки группы:', err);
|
||||
return res.status(500).json({ error: 'Ошибка проверки группы' });
|
||||
}
|
||||
|
||||
if (!group) {
|
||||
return res.status(404).json({ error: 'Указанная группа не найдена' });
|
||||
}
|
||||
|
||||
// УБИРАЕМ проверку соответствия типа сервиса
|
||||
// Теперь группы независимы от сервиса
|
||||
updateIdUser();
|
||||
});
|
||||
} else {
|
||||
updateIdUser();
|
||||
}
|
||||
} else {
|
||||
updateIdUser();
|
||||
}
|
||||
|
||||
function updateIdUser() {
|
||||
// Собираем поля для обновления
|
||||
const updates = [];
|
||||
const params = [];
|
||||
@@ -660,7 +682,9 @@ module.exports = function(app, db) {
|
||||
|
||||
if (external_id !== undefined) {
|
||||
updates.push('external_id = ?');
|
||||
params.push(external_id);
|
||||
// Генерируем временный external_id если он пустой
|
||||
const finalExternalId = external_id || `temp_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
||||
params.push(finalExternalId);
|
||||
}
|
||||
|
||||
if (login !== undefined) {
|
||||
@@ -714,6 +738,7 @@ module.exports = function(app, db) {
|
||||
message: 'Идентификатор успешно обновлен'
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
910
public/doc.html
910
public/doc.html
File diff suppressed because it is too large
Load Diff
133
public/doc.js
133
public/doc.js
@@ -78,6 +78,11 @@
|
||||
document.getElementById('iduserServiceTypeFilter').addEventListener('change', filterIdUsers);
|
||||
document.getElementById('iduserGroupFilter').addEventListener('change', filterIdUsers);
|
||||
document.getElementById('iduserStatusFilter').addEventListener('change', filterIdUsers);
|
||||
|
||||
// Обновление опций групп при изменении типа сервиса в форме
|
||||
document.getElementById('iduserServiceType').addEventListener('change', function() {
|
||||
updateGroupOptions();
|
||||
});
|
||||
}
|
||||
|
||||
// Дебаунс для поиска
|
||||
@@ -338,7 +343,7 @@
|
||||
<small>${iduser.user_login || '-'}</small>
|
||||
</td>
|
||||
<td>${serviceTypeBadge}</td>
|
||||
<td><strong>${iduser.external_id}</strong></td>
|
||||
<td><strong>${iduser.external_id || '-'}</strong></td>
|
||||
<td>${iduser.login || '-'}</td>
|
||||
<td>${iduser.ldap_group || '-'}</td>
|
||||
<td>${iduser.group_name || '-'}</td>
|
||||
@@ -374,7 +379,7 @@
|
||||
const filtered = idusers.filter(iduser => {
|
||||
// Поиск по тексту
|
||||
const matchesSearch = !searchText ||
|
||||
iduser.external_id.toLowerCase().includes(searchText) ||
|
||||
(iduser.external_id && iduser.external_id.toLowerCase().includes(searchText)) ||
|
||||
(iduser.login && iduser.login.toLowerCase().includes(searchText)) ||
|
||||
(iduser.user_name && iduser.user_name.toLowerCase().includes(searchText)) ||
|
||||
(iduser.user_login && iduser.user_login.toLowerCase().includes(searchText));
|
||||
@@ -643,10 +648,26 @@
|
||||
document.getElementById('iduserModalTitle').textContent = 'Добавить идентификатор';
|
||||
document.getElementById('iduserId').value = '';
|
||||
document.getElementById('iduserForm').reset();
|
||||
|
||||
// Устанавливаем тип сервиса по умолчанию как LDAP
|
||||
document.getElementById('iduserServiceType').value = 'ldap';
|
||||
|
||||
document.getElementById('iduserIsActive').checked = true;
|
||||
document.getElementById('iduserMessage').innerHTML = '';
|
||||
|
||||
// Обновляем опции групп
|
||||
// Добавляем обработчик для автозаполнения LDAP полей
|
||||
const userIdSelect = document.getElementById('iduserUserId');
|
||||
|
||||
// Удаляем существующие обработчики чтобы избежать дублирования
|
||||
const newUserIdSelect = userIdSelect.cloneNode(true);
|
||||
userIdSelect.parentNode.replaceChild(newUserIdSelect, userIdSelect);
|
||||
|
||||
// Добавляем новый обработчик
|
||||
document.getElementById('iduserUserId').addEventListener('change', function() {
|
||||
autoFillLdapFields(this.value);
|
||||
});
|
||||
|
||||
// Обновляем опции групп (все группы, независимо от сервиса)
|
||||
updateGroupOptions();
|
||||
|
||||
document.getElementById('iduserModal').classList.add('active');
|
||||
@@ -668,7 +689,7 @@
|
||||
document.getElementById('iduserModalTitle').textContent = 'Редактировать идентификатор';
|
||||
document.getElementById('iduserId').value = iduser.id;
|
||||
document.getElementById('iduserUserId').value = iduser.user_id;
|
||||
document.getElementById('iduserExternalId').value = iduser.external_id;
|
||||
document.getElementById('iduserExternalId').value = iduser.external_id || '';
|
||||
document.getElementById('iduserLogin').value = iduser.login || '';
|
||||
document.getElementById('iduserLdapGroup').value = iduser.ldap_group || '';
|
||||
document.getElementById('iduserServiceType').value = iduser.service_type;
|
||||
@@ -681,9 +702,20 @@
|
||||
document.getElementById('iduserMetadata').value = '';
|
||||
}
|
||||
|
||||
// Обновляем опции групп и выбираем текущую
|
||||
updateGroupOptions().then(() => {
|
||||
// Обновляем опции групп (все группы) и выбираем текущую
|
||||
await updateGroupOptions();
|
||||
document.getElementById('iduserGroupId').value = iduser.group_id || '';
|
||||
|
||||
// Добавляем обработчик для автозаполнения LDAP полей
|
||||
const userIdSelect = document.getElementById('iduserUserId');
|
||||
|
||||
// Удаляем существующие обработчики чтобы избежать дублирования
|
||||
const newUserIdSelect = userIdSelect.cloneNode(true);
|
||||
userIdSelect.parentNode.replaceChild(newUserIdSelect, userIdSelect);
|
||||
|
||||
// Добавляем новый обработчик
|
||||
document.getElementById('iduserUserId').addEventListener('change', function() {
|
||||
autoFillLdapFields(this.value);
|
||||
});
|
||||
|
||||
document.getElementById('iduserMessage').innerHTML = '';
|
||||
@@ -695,9 +727,48 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Обновление опций групп в зависимости от типа сервиса
|
||||
// Автоматическое заполнение полей LDAP на основе выбранного пользователя
|
||||
function autoFillLdapFields(userId) {
|
||||
if (!userId) return;
|
||||
|
||||
// Находим пользователя в списке всех пользователей
|
||||
const user = allUsers.find(u => u.id == userId);
|
||||
if (!user) return;
|
||||
|
||||
// Проверяем, является ли пользователь LDAP пользователем
|
||||
// Предполагаем, что LDAP пользователи имеют логин в определенном формате
|
||||
// или у них есть специальный атрибут в метаданных
|
||||
const isLdapUser = user.login && (
|
||||
user.login.includes('@') || // LDAP логин часто содержит домен
|
||||
user.service_type === 'ldap' ||
|
||||
(user.metadata && user.metadata.dn) // Имеет Distinguished Name
|
||||
);
|
||||
|
||||
if (isLdapUser) {
|
||||
// Автоматически заполняем логин LDAP если поле пустое
|
||||
if (!document.getElementById('iduserLogin').value.trim()) {
|
||||
document.getElementById('iduserLogin').value = user.login;
|
||||
}
|
||||
|
||||
// Автоматически заполняем группу LDAP из атрибутов пользователя
|
||||
if (!document.getElementById('iduserLdapGroup').value.trim()) {
|
||||
// Можно извлекать из различных мест:
|
||||
// 1. Из метаданных пользователя
|
||||
// 2. Из атрибута department или ou
|
||||
// 3. Из других полей
|
||||
if (user.metadata && user.metadata.ou) {
|
||||
document.getElementById('iduserLdapGroup').value = user.metadata.ou;
|
||||
} else if (user.department) {
|
||||
document.getElementById('iduserLdapGroup').value = user.department;
|
||||
} else if (user.metadata && user.metadata.memberOf) {
|
||||
document.getElementById('iduserLdapGroup').value = user.metadata.memberOf;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Обновление опций групп - теперь показывает все группы независимо от типа сервиса
|
||||
async function updateGroupOptions() {
|
||||
const serviceType = document.getElementById('iduserServiceType').value;
|
||||
const groupSelect = document.getElementById('iduserGroupId');
|
||||
|
||||
// Загружаем группы, если еще не загружены
|
||||
@@ -705,25 +776,23 @@
|
||||
await loadGroups();
|
||||
}
|
||||
|
||||
// Фильтруем группы по типу сервиса
|
||||
const filteredGroups = serviceType ?
|
||||
groups.filter(g => g.service_type === serviceType && g.is_active) :
|
||||
groups.filter(g => g.is_active);
|
||||
// Берем все активные группы (без фильтрации по типу сервиса)
|
||||
const activeGroups = groups.filter(g => g.is_active);
|
||||
|
||||
// Сохраняем текущее значение
|
||||
const currentValue = groupSelect.value;
|
||||
|
||||
// Обновляем опции
|
||||
groupSelect.innerHTML = '<option value="">Без группы</option>';
|
||||
filteredGroups.forEach(group => {
|
||||
activeGroups.forEach(group => {
|
||||
const option = document.createElement('option');
|
||||
option.value = group.id;
|
||||
option.textContent = group.name;
|
||||
option.textContent = `${group.name} (${getServiceTypeName(group.service_type)})`;
|
||||
groupSelect.appendChild(option);
|
||||
});
|
||||
|
||||
// Восстанавливаем значение, если оно есть в новых опциях
|
||||
if (currentValue && filteredGroups.some(g => g.id == currentValue)) {
|
||||
if (currentValue && activeGroups.some(g => g.id == currentValue)) {
|
||||
groupSelect.value = currentValue;
|
||||
}
|
||||
|
||||
@@ -747,6 +816,9 @@
|
||||
const iduserId = document.getElementById('iduserId').value;
|
||||
const isEdit = !!iduserId;
|
||||
|
||||
// Валидация - External ID теперь необязательный
|
||||
const externalId = document.getElementById('iduserExternalId').value.trim();
|
||||
|
||||
// Парсим метаданные
|
||||
let metadata = null;
|
||||
const metadataText = document.getElementById('iduserMetadata').value.trim();
|
||||
@@ -767,14 +839,35 @@
|
||||
const iduserData = {
|
||||
user_id: parseInt(document.getElementById('iduserUserId').value),
|
||||
service_type: document.getElementById('iduserServiceType').value,
|
||||
external_id: document.getElementById('iduserExternalId').value,
|
||||
login: document.getElementById('iduserLogin').value || undefined,
|
||||
ldap_group: document.getElementById('iduserLdapGroup').value || undefined,
|
||||
group_id: document.getElementById('iduserGroupId').value || undefined,
|
||||
external_id: externalId || null, // Разрешаем null для external_id
|
||||
login: document.getElementById('iduserLogin').value.trim() || null,
|
||||
ldap_group: document.getElementById('iduserLdapGroup').value.trim() || null,
|
||||
group_id: document.getElementById('iduserGroupId').value || null,
|
||||
metadata: metadata,
|
||||
is_active: document.getElementById('iduserIsActive').checked
|
||||
};
|
||||
|
||||
// Проверяем обязательные поля
|
||||
if (!iduserData.user_id) {
|
||||
document.getElementById('iduserMessage').innerHTML = `
|
||||
<div class="error">
|
||||
<i class="fas fa-exclamation-circle"></i>
|
||||
Пожалуйста, выберите пользователя
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!iduserData.service_type) {
|
||||
document.getElementById('iduserMessage').innerHTML = `
|
||||
<div class="error">
|
||||
<i class="fas fa-exclamation-circle"></i>
|
||||
Пожалуйста, выберите тип сервиса
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const url = isEdit ? `/api2/idusers/${iduserId}` : '/api2/idusers';
|
||||
const method = isEdit ? 'PUT' : 'POST';
|
||||
@@ -830,7 +923,7 @@
|
||||
if (!iduser) return;
|
||||
|
||||
document.getElementById('confirmModalTitle').textContent = 'Удаление идентификатора';
|
||||
document.getElementById('confirmMessage').textContent = `Вы уверены, что хотите удалить идентификатор "${iduser.external_id}" пользователя "${iduser.user_name}"?`;
|
||||
document.getElementById('confirmMessage').textContent = `Вы уверены, что хотите удалить идентификатор "${iduser.external_id || 'без внешнего ID'}" пользователя "${iduser.user_name}"?`;
|
||||
|
||||
deleteCallback = deleteIdUser;
|
||||
deleteParams = { id };
|
||||
|
||||
Reference in New Issue
Block a user