From 3059741b1a5eef8efd3dc9c3757785d4355516aa Mon Sep 17 00:00:00 2001 From: kalugin66 Date: Tue, 25 Aug 2026 00:44:10 +0500 Subject: [PATCH] zup2 --- lib/ldapClient.js | 68 ++++++++++++++++++++++++++++++++++------------- lib/zupApi.js | 29 +++++++++++++++++--- public/zup.html | 27 ++++++++++++++----- 3 files changed, 96 insertions(+), 28 deletions(-) diff --git a/lib/ldapClient.js b/lib/ldapClient.js index fdf233d..955cd97 100644 --- a/lib/ldapClient.js +++ b/lib/ldapClient.js @@ -30,6 +30,7 @@ function mapLdapError(err) { case 50: return new LdapError('Недостаточно прав для выполнения операции', code, 403); case 53: return new LdapError('Операция запрещена сервером', code, 500); case 19: return new LdapError('Нарушение ограничений (например, слишком короткий пароль)', code, 400); + case 21: return new LdapError('Недопустимое значение атрибута (проверьте ФИО, телефон, почту)', code, 400); case 14: return new LdapError('Операция недоступна', code, 500); case 52: return new LdapError('Сервер недоступен', code, 500); case 80: return new LdapError('Не удалось подключиться к LDAP-серверу', code, 502); @@ -383,6 +384,7 @@ function parseUser(entry) { full_name: entry.displayName || entry.cn || '', given_name: entry.givenName || '', sn: entry.sn || '', + middle_name: entry.middleName || '', email: entry.mail || '', phone: entry.telephoneNumber || '', position: entry.description || '', @@ -400,7 +402,7 @@ async function listUsers() { const entries = await searchEntries(client, config.baseDN, { scope: 'sub', filter: '(&(objectClass=user)(objectCategory=person))', - attributes: ['dn', 'sAMAccountName', 'displayName', 'cn', 'givenName', 'sn', + attributes: ['dn', 'sAMAccountName', 'displayName', 'cn', 'givenName', 'sn', 'middleName', 'mail', 'telephoneNumber', 'description', 'userPrincipalName', 'userAccountControl', 'memberOf'] }); return entries.map((e) => parseUser(entryToObject(e))); @@ -557,7 +559,24 @@ async function createUser({ ouDn, fio, login, password, email, phone, position, } } -async function updateUser(login, { fio, email, phone, position, logonScript, homeDrive, homeBasePath, password, groupCNs }) { +function cleanValue(value, maxLen) { + return String(value || '') + .replace(/[\u0000-\u001f\u007f]/g, ' ') + .replace(/\s+/g, ' ') + .trim() + .slice(0, maxLen || 200); +} + +function splitFioParts(fio) { + const parts = cleanValue(fio).split(' '); + return { + lastName: parts[0] || '', + firstName: parts[1] || '', + middleName: parts.slice(2).join(' ') || '' + }; +} + +async function updateUser(login, { fio, lastName, firstName, middleName, email, phone, position, logonScript, homeDrive, homeBasePath, password, groupCNs }) { const client = await connect(); try { await bind(client, config.adminLogin, config.adminPassword); @@ -566,37 +585,50 @@ async function updateUser(login, { fio, email, phone, position, logonScript, hom const userDN = entry.dn; const changes = []; - if (fio !== undefined) { - changes.push({ operation: 'replace', modification: { displayName: [fio] } }); - const nameParts = (fio || '').trim().split(/\s+/); - changes.push({ - operation: 'replace', - modification: { givenName: [nameParts[0] || ''] } - }); - changes.push({ - operation: 'replace', - modification: { sn: [nameParts.slice(1).join(' ') || ''] } - }); + const hasNameFields = lastName !== undefined || firstName !== undefined || middleName !== undefined; + if (fio !== undefined || hasNameFields) { + let lname = lastName !== undefined ? cleanValue(lastName, 64) : null; + let fname = firstName !== undefined ? cleanValue(firstName, 64) : null; + let mname = middleName !== undefined ? cleanValue(middleName, 64) : null; + if (fio !== undefined && !hasNameFields) { + const parts = splitFioParts(fio); + if (lname === null) lname = parts.lastName; + if (fname === null) fname = parts.firstName; + if (mname === null) mname = parts.middleName; + } + const display = [lname, fname, mname].filter(Boolean).join(' '); + if (display) { + changes.push({ operation: 'replace', modification: { displayName: [display] } }); + } + if (lname !== null && lname) { + changes.push({ operation: 'replace', modification: { sn: [lname] } }); + } + if (fname !== null && fname) { + changes.push({ operation: 'replace', modification: { givenName: [fname] } }); + } + if (mname !== null && mname) { + changes.push({ operation: 'replace', modification: { middleName: [mname] } }); + } } if (email !== undefined) { - const emailVal = String(email || '').trim(); + const emailVal = cleanValue(email, 254); if (emailVal) { changes.push({ operation: 'replace', modification: { mail: [emailVal] } }); } } if (phone !== undefined) { - changes.push({ operation: 'replace', modification: { telephoneNumber: [phone || ''] } }); + changes.push({ operation: 'replace', modification: { telephoneNumber: [cleanValue(phone, 60)] } }); } if (position !== undefined) { - const positionVal = String(position).trim() || ''; + const positionVal = cleanValue(position, 120); changes.push({ operation: 'replace', modification: { description: [positionVal] } }); changes.push({ operation: 'replace', modification: { title: [positionVal] } }); } if (logonScript !== undefined) { - changes.push({ operation: 'replace', modification: { scriptPath: [String(logonScript).trim() || ''] } }); + changes.push({ operation: 'replace', modification: { scriptPath: [cleanValue(logonScript, 255)] } }); } if (homeDrive !== undefined) { - changes.push({ operation: 'replace', modification: { homeDrive: [String(homeDrive).trim() || ''] } }); + changes.push({ operation: 'replace', modification: { homeDrive: [cleanValue(homeDrive, 3)] } }); } if (homeBasePath !== undefined) { changes.push({ diff --git a/lib/zupApi.js b/lib/zupApi.js index 89170bc..ff77ecb 100644 --- a/lib/zupApi.js +++ b/lib/zupApi.js @@ -98,6 +98,9 @@ router.get('/users', requireZupAuth, async (req, res) => { users: users.map((u) => ({ login: u.login, fio: u.full_name || '', + first_name: u.given_name || '', + last_name: u.sn || '', + middle_name: u.middle_name || '', phone: u.phone || '', email: u.email || '', position: u.position || '', @@ -113,7 +116,7 @@ router.get('/users', requireZupAuth, async (req, res) => { router.put('/users/:login', requireZupAuth, async (req, res) => { const login = String(req.params.login || '').trim(); const actor = (req.session.user && req.session.user.username) || ''; - const { fio, position, phone, email } = req.body || {}; + const { fio, lastName, firstName, middleName, position, phone, email } = req.body || {}; try { if (!login) return res.status(400).json({ success: false, error: 'Логин не указан' }); if (String(login).toLowerCase() === String(actor).toLowerCase()) { @@ -122,8 +125,24 @@ router.put('/users/:login', requireZupAuth, async (req, res) => { if (zupAccess.isAdminLogin(login)) { return res.status(400).json({ success: false, error: 'Нельзя редактировать учётную запись администратора' }); } - const newFio = String(fio || '').trim(); - if (!newFio) return res.status(400).json({ success: false, error: 'ФИО обязательно' }); + const hasNameFields = lastName !== undefined || firstName !== undefined || middleName !== undefined; + let newLastName = String(lastName !== undefined ? lastName : '').trim().slice(0, 64); + let newFirstName = String(firstName !== undefined ? firstName : '').trim().slice(0, 64); + let newMiddleName = String(middleName !== undefined ? middleName : '').trim().slice(0, 64); + if (fio !== undefined && !hasNameFields) { + // обратная совместимость: строка «Фамилия Имя Отчество» + const parts = String(fio).trim().split(/\s+/); + newLastName = parts[0] || ''; + newFirstName = parts[1] || ''; + newMiddleName = parts.slice(2).join(' ') || ''; + } + if (hasNameFields) { + if (!newLastName || !newFirstName) { + return res.status(400).json({ success: false, error: 'Укажите фамилию и имя' }); + } + } else if (!newLastName) { + return res.status(400).json({ success: false, error: 'ФИО обязательно' }); + } const newEmail = String(email || '').trim(); if (newEmail && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(newEmail)) { return res.status(400).json({ success: false, error: 'Некорректный email' }); @@ -133,7 +152,9 @@ router.put('/users/:login', requireZupAuth, async (req, res) => { await ldapClient.getUser(login); const result = await ldapClient.updateUser(login, { - fio: newFio, + lastName: newLastName, + firstName: newFirstName, + middleName: newMiddleName, position: newPosition, email: newEmail, phone: newPhone diff --git a/public/zup.html b/public/zup.html index 909c3f2..1986fba 100644 --- a/public/zup.html +++ b/public/zup.html @@ -171,9 +171,19 @@