ken_nogi/NodeJS/LDAP/ldapUpdate.js
Kenichiro NOGI 88a402ce0f up
2026-07-10 18:13:30 +09:00

278 lines
9.7 KiB
JavaScript

const ldap = require('ldapjs');
const fs = require('fs');
const csv = require('csv-parser');
const { Parser } = require('json2csv');
const iconv = require('iconv-lite'); // 追加
const LDAP_URL = 'ldap://192.168.4.225';
const BASE_DN = 'dc=ldap,dc=next-hd,dc=co,dc=jp'; // ルートに変更
const now = new Date();
const pad = (n) => n.toString().padStart(2, '0');
const timestamp = `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}_${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
const EXPORT_CSV = `ldap_users_export_${timestamp}.csv`;
const IMPORT_CSV = 'ldap_users_import.csv';
const BIND_DN = 'uid=root,cn=users,dc=ldap,dc=next-hd,dc=co,dc=jp'; // 管理者DN
const BIND_PASSWORD = 'il7DXhKA'; // 管理者パスワード
// 管理者権限でLDAP接続
function getLdapClientAndBind(callback) {
const client = ldap.createClient({ url: LDAP_URL });
client.bind(BIND_DN, BIND_PASSWORD, err => {
callback(client, err);
});
}
// LDAPからデータを取得してCSV出力
function exportLdapToCsv() {
getLdapClientAndBind((client, err) => {
if (err) {
console.error('LDAP bind error:', err);
client.unbind();
return;
}
const entries = [];
// filterでcn=users配下のユーザーのみ取得
client.search(BASE_DN, {
scope: 'sub',
filter: 'objectClass=person',
attributes: []
}, (err, res) => {
if (err) {
console.error('LDAP search error:', err);
client.unbind();
return;
}
res.on('searchEntry', entry => {
const obj = {};
if (Array.isArray(entry.attributes)) {
entry.attributes.forEach(attr => {
obj[attr.type] = Array.isArray(attr.vals) ? attr.vals.join(',') : attr.vals;
});
entries.push(obj);
}
});
res.on('end', () => {
if (entries.length === 0) {
console.log('エントリが見つかりませんでした。');
} else {
console.log(`取得したエントリ数: ${entries.length}`);
console.log('エントリの例:', entries[100]);
// 属性値が配列の場合は文字列化
const normalized = entries.map(obj => {
const n = {};
if (obj && typeof obj === 'object') {
Object.keys(obj).forEach(k => {
if (Array.isArray(obj[k])) {
n[k] = obj[k].join(','); // カンマ区切りで連結
} else {
n[k] = obj[k];
}
});
}
return n;
});
// 取得できた属性名を自動で抽出
const allFields = Array.from(
normalized.reduce((set, obj) => {
if (obj && typeof obj === 'object') {
Object.keys(obj).forEach(k => set.add(k));
}
return set;
}, new Set())
);
const parser = new Parser({ fields: allFields });
const csvData = parser.parse(normalized);
// Shift-JISに変換して保存
const sjisData = iconv.encode(csvData, 'Shift_JIS');
fs.writeFileSync(EXPORT_CSV, sjisData);
console.log('LDAPデータをShift-JISでCSVにエクスポートしました:', EXPORT_CSV);
}
client.unbind();
});
});
});
}
// CSVを読み込んでLDAPを更新
function importCsvToLdap() {
const updates = [];
fs.createReadStream(IMPORT_CSV)
.pipe(csv())
.on('data', (row) => {
updates.push(row);
})
.on('end', () => {
getLdapClientAndBind((client, err) => {
if (err) {
console.error('LDAP bind error:', err);
client.unbind();
return;
}
let completed = 0;
updates.forEach(update => {
const uidNumber = update.uidNumber;
if (uidNumber) {
// uidNumberがある場合は検索して更新
client.search(BASE_DN, {
scope: 'sub',
filter: `(uidNumber=${uidNumber})`,
attributes: ['dn']
}, (err, res) => {
if (err) {
console.error('LDAP search error:', err);
completed++;
if (completed === updates.length) client.unbind();
return;
}
let found = false;
res.on('searchEntry', entry => {
found = true;
const dn = entry.objectName || entry.dn || (entry.object && entry.object.dn);
if (!dn) {
console.error('dnが取得できませんでした:', entry);
completed++;
if (completed === updates.length) client.unbind();
return;
}
// dnとobjectClass以外の属性を更新
const changes = [];
Object.keys(update).forEach(attr => {
if (attr !== 'dn' && attr !== 'objectClass' && update[attr]) {
changes.push(new ldap.Change({
operation: 'replace',
modification: { [attr]: update[attr] }
}));
}
});
if (changes.length > 0) {
client.modify(dn, changes, err => {
if (err) {
console.error(`LDAP modify error for ${dn}:`, err);
} else {
console.log(`LDAPエントリを更新しました: ${dn}`);
}
completed++;
if (completed === updates.length) client.unbind();
});
} else {
completed++;
if (completed === updates.length) client.unbind();
}
});
res.on('end', () => {
if (!found) {
// uidNumberはあるが該当エントリがない場合は新規登録
addLdapEntry(client, update, () => {
completed++;
if (completed === updates.length) client.unbind();
});
}
});
});
} else {
// uidNumberがない場合は新規登録
addLdapEntry(client, update, () => {
completed++;
if (completed === updates.length) client.unbind();
});
}
});
});
});
}
// 新規登録用ヘルパー関数
function addLdapEntry(client, entry, callback) {
// 必要な属性を設定
const dn = entry.dn || `uid=${entry.uid},cn=users,${BASE_DN}`;
const objectClass = entry.objectClass ? entry.objectClass.split(',') : [
'top', 'person', 'organizationalPerson', 'inetOrgPerson', 'posixAccount', 'shadowAccount'
];
const attrs = { ...entry, objectClass };
delete attrs.dn;
client.add(dn, attrs, err => {
if (err) {
console.error(`LDAPエントリ新規登録失敗: ${dn}`, err);
} else {
console.log(`LDAPエントリ新規登録: ${dn}`);
}
if (callback) callback();
});
}
// 指定したuidNumberのエントリーを削除する関数
function deleteEntriesByUidNumbers(uidNumbers) {
if (!Array.isArray(uidNumbers) || uidNumbers.length === 0) {
console.log('uidNumberの配列を指定してください。');
return;
}
getLdapClientAndBind((client, err) => {
if (err) {
console.error('LDAP bind error:', err);
client.unbind();
return;
}
// 各uidNumberごとに削除処理
let processed = 0;
uidNumbers.forEach(uidNumber => {
// uidNumberでエントリーを検索
client.search(BASE_DN, {
scope: 'sub',
filter: `(uidNumber=${uidNumber})`,
attributes: ['dn']
}, (err, res) => {
if (err) {
console.error('LDAP search error:', err);
processed++;
if (processed === uidNumbers.length) client.unbind();
return;
}
let found = false;
res.on('searchEntry', entry => {
found = true;
const dn = entry.objectName || entry.dn || (entry.object && entry.object.dn);
if (dn) {
client.del(dn, err => {
if (err) {
console.error(`削除失敗: ${dn}`, err);
} else {
console.log(`削除成功: ${dn}`);
}
processed++;
if (processed === uidNumbers.length) client.unbind();
});
} else {
console.error('dnが取得できませんでした:', entry);
processed++;
if (processed === uidNumbers.length) client.unbind();
}
});
res.on('end', () => {
if (!found) {
console.log(`uidNumber=${uidNumber} のエントリーが見つかりませんでした。`);
processed++;
if (processed === uidNumbers.length) client.unbind();
}
});
});
});
});
}
// コマンドライン引数で処理を切り替え
const mode = process.argv[2];
if (mode === 'export') {
exportLdapToCsv();
} else if (mode === 'import') {
importCsvToLdap();
} else if (mode === 'delete-uids') {
// 例: node ldapUpdate.js delete-uids 1000003,1000004
const uidNumbers = (process.argv[3] || '').split(',').map(s => s.trim()).filter(Boolean);
deleteEntriesByUidNumbers(uidNumbers);
} else {
console.log('使い方:');
console.log(' node ldapUpdate.js export # cn=users配下のLDAPデータをCSVエクスポート');
console.log(' node ldapUpdate.js import # CSVからLDAPを更新');
console.log(' node ldapUpdate.js delete-uids 1000003,1000004 # 指定uidNumberのエントリーを削除');
}