initial files
This commit is contained in:
302
src/services/identityManager.js
Normal file
302
src/services/identityManager.js
Normal file
@@ -0,0 +1,302 @@
|
||||
const { Op } = require('sequelize');
|
||||
|
||||
class IdentityManager {
|
||||
constructor(adManager, AuthenticationModel) {
|
||||
this.ad = adManager || null;
|
||||
this.Authentication = AuthenticationModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* -----------------------------------------------------
|
||||
* REQUIRED FIELDS (MANUAL USER)
|
||||
* -----------------------------------------------------
|
||||
*/
|
||||
REQUIRED_FIELDS = [
|
||||
'sAMAccountName',
|
||||
'mail',
|
||||
'givenName',
|
||||
'sn',
|
||||
'password'
|
||||
];
|
||||
|
||||
/**
|
||||
* -----------------------------------------------------
|
||||
* VALIDATE MANUAL USER
|
||||
* -----------------------------------------------------
|
||||
*/
|
||||
validateManualUser(user) {
|
||||
const missing = [];
|
||||
|
||||
for (const field of this.REQUIRED_FIELDS) {
|
||||
if (
|
||||
user[field] === undefined ||
|
||||
user[field] === null ||
|
||||
user[field] === ''
|
||||
) {
|
||||
missing.push(field);
|
||||
}
|
||||
}
|
||||
|
||||
if (missing.length) {
|
||||
throw new Error(
|
||||
`Fehlende Pflichtfelder: ${missing.join(', ')}`
|
||||
);
|
||||
}
|
||||
|
||||
// 🔍 Optional: einfache Zusatzvalidierungen
|
||||
if (user.mail && !user.mail.includes('@')) {
|
||||
throw new Error('Ungültige E-Mail-Adresse');
|
||||
}
|
||||
|
||||
if (user.password && user.password.length < 6) {
|
||||
throw new Error('Passwort muss mindestens 6 Zeichen lang sein');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* -----------------------------------------------------
|
||||
* FIXED MAPPING (AD → Authentication)
|
||||
* -----------------------------------------------------
|
||||
*/
|
||||
mapAdObject(obj) {
|
||||
if (!obj || !obj.objectGUID) return null;
|
||||
|
||||
return {
|
||||
ObjectGUID: obj.objectGUID,
|
||||
sAMAccountName: obj.sAMAccountName || obj.cn || null,
|
||||
mail: obj.mail || null,
|
||||
givenName: obj.givenName || null,
|
||||
sn: obj.sn || null,
|
||||
employeeID: obj.employeeID || null,
|
||||
title: obj.title || null,
|
||||
department: obj.department || null,
|
||||
streetAddress: obj.streetAddress || null,
|
||||
userAccountControl_ID: obj.userAccountControl || null,
|
||||
authenticationType_ID: 1,
|
||||
telephoneNumber: obj.telephoneNumber || null,
|
||||
physicalDeliveryOfficeName: obj.physicalDeliveryOfficeName || null,
|
||||
distinguishedName: obj.dn || null,
|
||||
password: null,
|
||||
refreshtoken: null,
|
||||
active: true,
|
||||
online: false
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* -----------------------------------------------------
|
||||
* DEDUP (wie SQL UNION)
|
||||
* -----------------------------------------------------
|
||||
*/
|
||||
deduplicateByGUID(items) {
|
||||
const map = new Map();
|
||||
|
||||
for (const item of items) {
|
||||
if (!item?.ObjectGUID) continue;
|
||||
map.set(item.ObjectGUID, item);
|
||||
}
|
||||
|
||||
return Array.from(map.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* -----------------------------------------------------
|
||||
* TABLE CHECK / CREATE
|
||||
* -----------------------------------------------------
|
||||
*/
|
||||
async ensureTable() {
|
||||
const qi = this.Authentication.sequelize.getQueryInterface();
|
||||
const tables = await qi.showAllTables();
|
||||
|
||||
const exists = tables.includes('Authentication');
|
||||
|
||||
if (!exists) {
|
||||
await this.Authentication.sync();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* -----------------------------------------------------
|
||||
* CORE SYNC (INTELLIGENT)
|
||||
* -----------------------------------------------------
|
||||
*/
|
||||
async syncFromAD() {
|
||||
if (!this.ad) {
|
||||
throw new Error('AD nicht konfiguriert');
|
||||
}
|
||||
|
||||
const [users, groups, computers] = await Promise.all([
|
||||
this.ad.findUsers('*'),
|
||||
this.ad.findGroups('*'),
|
||||
this.ad.getComputers()
|
||||
]);
|
||||
|
||||
const mapped = this.deduplicateByGUID([
|
||||
...users.map(u => this.mapAdObject(u)),
|
||||
...groups.map(g => this.mapAdObject(g)),
|
||||
...computers.map(c => this.mapAdObject(c))
|
||||
].filter(Boolean));
|
||||
|
||||
if (!mapped.length) {
|
||||
return { total: 0, deactivated: 0 };
|
||||
}
|
||||
|
||||
await this.Authentication.bulkCreate(mapped, {
|
||||
updateOnDuplicate: [
|
||||
'mail',
|
||||
'givenName',
|
||||
'sn',
|
||||
'employeeID',
|
||||
'title',
|
||||
'department',
|
||||
'streetAddress',
|
||||
'userAccountControl_ID',
|
||||
'telephoneNumber',
|
||||
'physicalDeliveryOfficeName',
|
||||
'distinguishedName',
|
||||
'active'
|
||||
]
|
||||
});
|
||||
|
||||
const existing = await this.Authentication.findAll({
|
||||
where: { authenticationType_ID: 1 },
|
||||
attributes: ['ObjectGUID']
|
||||
});
|
||||
|
||||
const adGuids = new Set(mapped.map(u => u.ObjectGUID));
|
||||
|
||||
const toDeactivate = existing
|
||||
.filter(e => !adGuids.has(e.ObjectGUID))
|
||||
.map(e => e.ObjectGUID);
|
||||
|
||||
if (toDeactivate.length) {
|
||||
await this.Authentication.update(
|
||||
{ active: false },
|
||||
{
|
||||
where: {
|
||||
ObjectGUID: toDeactivate
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
total: mapped.length,
|
||||
deactivated: toDeactivate.length,
|
||||
adGuids: Array.from(adGuids)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* -----------------------------------------------------
|
||||
* OPTIONAL: HARD DELETE
|
||||
* -----------------------------------------------------
|
||||
*/
|
||||
async removeDeletedADObjects(adGuids) {
|
||||
return this.Authentication.destroy({
|
||||
where: {
|
||||
authenticationType_ID: 1,
|
||||
ObjectGUID: {
|
||||
[Op.notIn]: adGuids
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* -----------------------------------------------------
|
||||
* RECREATE
|
||||
* -----------------------------------------------------
|
||||
*/
|
||||
async recreateAuthentications(hardReset = false) {
|
||||
let message = '';
|
||||
|
||||
const exists = await this.ensureTable();
|
||||
|
||||
if (!exists) {
|
||||
message = 'Tabelle wurde neu erstellt ';
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await this.syncFromAD();
|
||||
|
||||
message += `Sync abgeschlossen (${result.total} Objekte)`;
|
||||
|
||||
if (result.deactivated) {
|
||||
message += `, ${result.deactivated} deaktiviert`;
|
||||
}
|
||||
|
||||
if (hardReset) {
|
||||
const deleted = await this.removeDeletedADObjects(result.adGuids);
|
||||
message += `, ${deleted} gelöscht`;
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
message += 'Fehler: ' + err.message;
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* -----------------------------------------------------
|
||||
* MANUAL USER (MIT VALIDATION)
|
||||
* -----------------------------------------------------
|
||||
*/
|
||||
async createManualUser(user) {
|
||||
this.validateManualUser(user);
|
||||
|
||||
return this.Authentication.create({
|
||||
...user,
|
||||
authenticationType_ID: 2,
|
||||
active: true,
|
||||
online: false
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* -----------------------------------------------------
|
||||
* MANUAL BULK (MIT VALIDATION)
|
||||
* -----------------------------------------------------
|
||||
*/
|
||||
async createManualUsers(users) {
|
||||
const errors = [];
|
||||
|
||||
users.forEach((user, index) => {
|
||||
try {
|
||||
this.validateManualUser(user);
|
||||
} catch (err) {
|
||||
errors.push(`User ${index}: ${err.message}`);
|
||||
}
|
||||
});
|
||||
|
||||
if (errors.length) {
|
||||
throw new Error(errors.join(' | '));
|
||||
}
|
||||
|
||||
return this.Authentication.bulkCreate(
|
||||
users.map(user => ({
|
||||
...user,
|
||||
authenticationType_ID: 2,
|
||||
active: true,
|
||||
online: false
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* -----------------------------------------------------
|
||||
* GET USER
|
||||
* -----------------------------------------------------
|
||||
*/
|
||||
async getUser(username) {
|
||||
return this.Authentication.findOne({
|
||||
where: { sAMAccountName: username }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = IdentityManager;
|
||||
Reference in New Issue
Block a user