Skip to content
Snippets Groups Projects
Commit f929ecd5 authored by Wilson JALLET's avatar Wilson JALLET :money_with_wings:
Browse files

Ldap refactoring

parent ec062a0a
No related branches found
No related tags found
No related merge requests found
......@@ -9,52 +9,55 @@
"comment_3": "Placeholders et indications de contenu de certains champs du LDAP généré par frankiz pour les utilisateurs",
"user": {
"direct_input": ["givenName","lastName","birthdate", "promotion", "mail","phone","photo","adress"],
"multiple_input": ["ips","forlifes"],
"profil": ["jpegPhoto","displayName","givenName", "sn", "brBirthdate", "brPromo","telephoneNumber","mail","brRoom","brIP","brMemberOf"],
"photo": "jpegPhoto",
"givenName": "givenName",
"lastName": "sn",
"nickname": "displayName",
"birthdate": "brBirthdate",
"nationality": "country",
"promotion": "brPromo",
"phone": "telephoneNumber",
"mail": "mail",
"adress": "brRoom",
"ip": "brIP",
"id": "uidNumber",
"sport": "brMemberOf",
"password": "userPassword",
"forlifes": "brAlias",
"idNum": "gidNumber",
"directory": "homeDirectory",
"readPerm": "brNewsReadAccess",
"writePerm": "brNewsPostAccess",
"fullName": "cn",
"login": "loginShell",
"groups": "brMemberOf",
"school": "brMemberOf",
"course": "brMemberOf",
"cleanFullName": "gecos",
"class": "objectClass"
"single": {
"photo": "jpegPhoto",
"givenName": "givenName",
"lastName": "sn",
"fullName": "cn",
"cleanFullName": "gecos",
"nickname": "displayName",
"birthdate": "brBirthdate",
"nationality": "country",
"promotion": "brPromo",
"phone": "telephoneNumber",
"adress": "brRoom",
"id": "uidNumber",
"sport": "brMemberOf",
"password": "userPassword",
"idNum": "gidNumber",
"directory": "homeDirectory",
"login": "loginShell",
"readPerm": "brNewsReadAccess",
"writePerm": "brNewsPostAccess"
},
"multiple": {
"mail": "mail",
"ip": "brIP",
"forlifes": "brAlias",
"groups": "brMemberOf",
"school": "brMemberOf",
"course": "brMemberOf",
"class": "objectClass"
}
},
"comment_4": "Placeholders et indications de contenu de certains champs du LDAP généré par frankiz pour les groupes",
"group": {
"direct_input": ["name","type"],
"profil": ["cn","restrictedMemberUid","memberUid", "brNS"],
"name": "cn",
"member": "restrictedMemberUid",
"admin": "memberUid",
"type": "brNS",
"idNumber": "uidNumber",
"idNumber2": "gidNumber",
"password": "userPassword",
"login": "loginShell",
"directory": "homeDirectory",
"cleanFullName": "gecos",
"readPerm": "brNewsReadAccess",
"writePerm": "brNewsPostAccess"
"single": {
"name": "cn",
"nickname": "brAlias",
"type": "brNS",
"idNumber": "uidNumber",
"idNumber2": "gidNumber",
"login": "loginShell",
"password": "userPassword",
"directory": "homeDirectory",
"cleanFullName": "gecos"
},
"multiple": {
"member": "restrictedMemberUid",
"admin": "memberUid",
"class": "objectClass"
}
},
"sessionSecret":"ozyNMHdT,WFTu|t"
}
\ No newline at end of file
......@@ -32,7 +32,7 @@ import morgan from 'morgan';
// packages pour pouvoir importer depuis des fichiers de config
import path from 'path';
import { ldapConfig, credentialsLdapConfig } from './ldap/config';
import {ldapConfig, credentialsLdapConfig} from './ldap/config';
const { dn, passwd } = credentialsLdapConfig;
......
......@@ -7,24 +7,7 @@
import {ldapConfig} from './config';
import {LDAP} from './basics';
import {Tests} from './utilities';
import { Open, User, userData, groupData } from './users';
/**
* @interface userFullData
* @desc Interface avec toutes les données d'un utilisateur. Etend {@link userData}.
* @var {string} password - Mot de passe généré en amont
* @arg {string} readPerm - Permissions spéciales BR
* @var {string} writePerm - Permissions spéciales BR
* @var {string[]} forlifes - Alias BR (attention le filtre .fkz n'est plus fonctionnel)
* @var {string[]} groupsIsAdmin - Liste des gid dont le pax est admin ; supposé sous-liste de groups
*/
export interface userFullData extends userData {
password: string,
readPerm: string,
writePerm: string,
forlifes: string[],
groupsIsAdmin: string[]
}
import {Open, User, userData, groupData} from './users';
export class Admin extends User {
/**
......@@ -48,7 +31,7 @@ export class Admin extends User {
* @async
* @static
*/
static async addGroupMember(uid: string, gid: string) {
static async addGroupMember(uid: string, gid: string) : Promise<boolean> {
try {
// Vérifie que l'utilisateur est pas déjà membre pour groupes
let lm = await Open.getMembers(gid);
......@@ -91,7 +74,7 @@ export class Admin extends User {
* @async
* @static
*/
static async delGroupMember(uid: string, gid: string) {
static async delGroupMember(uid: string, gid: string): Promise<boolean> {
try {
// Vérifie que l'utilisateur est pas déjà viré pour groupes
let lm = await Open.getMembers(gid);
......@@ -147,7 +130,7 @@ export class Admin extends User {
* @async
* @static
*/
static async addGroupAdmin(uid: string, gid: string){
static async addGroupAdmin(uid: string, gid: string): Promise<boolean> {
// Ajoute le membre au groupe avant d'en faire un admin
if (!await Admin.addGroupMember(uid,gid)) { throw "Erreur lors de l'ajout du futur admin en tant que membre."; }
try {
......@@ -176,7 +159,7 @@ export class Admin extends User {
* @async
* @static
*/
static async delGroupAdmin(uid: string, gid: string) {
static async delGroupAdmin(uid: string, gid: string): Promise<boolean> {
// Peut paraître absurde mais permet de s'assurer que le membre est bien présent et que ses champs sont comme il faut
if (!(await Admin.delGroupMember(uid, gid)&&Admin.addGroupMember(uid,gid))) { throw "Erreur dans l'éjection/réadmission du futur admin."; }
try {
......@@ -212,7 +195,7 @@ export class Admin extends User {
* @async
* @static
*/
static async editGroup(gid: string, data: groupData) {
static async editGroup(gid: string, data: groupData) : Promise<boolean> {
try {
// Récupération des anciennes données
let profil = await Open.peekGroup(gid);
......@@ -249,7 +232,7 @@ export class Admin extends User {
* @async
* @static
*/
static async delGroup(gid) {
static async delGroup(gid): Promise<boolean> {
try {
// Gestion des membres et administrateurs d'abord
let profil = await Open.peekGroup(gid);
......@@ -285,12 +268,12 @@ export class Supervisor extends Admin {
/**
* @summary Fonction qui créé un nouvel utilisateur dans le LDAP.
* @desc Appelle {@link LDAP.add} bien sûr, mais aussi {@link User.addGroupMember} et {@link Admin.addGroupAdmin} pour gérer les groupes du nouvel utilisateur.
* @arg {userFullData} data - Dictionnaire des informations utilisateurs. Des erreurs peuvent apparaître si tous les champs ne sont pas remplis.
* @arg {userData} data - Dictionnaire des informations utilisateurs. Des erreurs peuvent apparaître si tous les champs ne sont pas remplis.
* @return {Promise(boolean)} `true` si la modification s'est bien déroulée, false sinon
* @async
* @static
*/
static async addUser(data: userFullData) {
static async addUser(data: userData): Promise<boolean> {
// Calcul d'un dictionnaire d'ajout
let vals = {};
......@@ -303,20 +286,22 @@ export class Supervisor extends Admin {
throw "Erreur lors de la génération d'un hruid pour un nouvel utilisateur.";
}
let uid = vals[ldapConfig.key_id];
// Ecriture de toutes les valeurs directement inscrites dans le LDAP (in pour input)
// Génère une erreur si un champ n'est pas rempli
ldapConfig.user.direct_input.forEach(key_att => vals[ldapConfig.user[key_att]]=data[key_att]);
ldapConfig.user.single.forEach(key_att => vals[ldapConfig.user.single[key_att]]=data[key_att]);
// Appel à la fonction de base
if (!await LDAP.add(ldapConfig.key_id+"="+vals[ldapConfig.key_id]+","+ldapConfig.dn_users, vals)) { throw "Erreur de l'ajout de la feuille à l'arbre utilisateur."; }
if (!await LDAP.add(ldapConfig.key_id+"="+uid+","+ldapConfig.dn_users, vals)) { throw "Erreur de l'ajout de la feuille à l'arbre utilisateur."; }
// Modifications multiples pour avoir plusieurs champs de même type ; boucle sur les attributs multiples
ldapConfig.user.multiple_input.forEach(key_att => {
ldapConfig.user.multiple.forEach(key_att => {
// On rajoute chaque valeur en entrée
data[key_att].forEach(val => {
let vals2 = {};
vals2[ldapConfig.user[key_att]]=val;
LDAP.change(ldapConfig.key_id+"="+vals[ldapConfig.key_id]+","+ldapConfig.dn_users, "add", vals2).then(res => {
vals2[ldapConfig.user.multiple[key_att]]=val;
LDAP.change(ldapConfig.key_id+"="+uid+","+ldapConfig.dn_users, "add", vals2).then(res => {
if (!res) { throw "Erreur lors de l'ajout d'une valeur pour un champ à valeurs multiples à la feuille du nouvel utilisateur."; }
});
});
......@@ -326,19 +311,19 @@ export class Supervisor extends Admin {
let vals3={};
// Création d'un nom complet lisible
vals3[ldapConfig.user['fullName']]=data['givenName']+' '+data['lastName'].toUpperCase();
vals3[ldapConfig.user.single['fullName']]=data['givenName']+' '+data['lastName'].toUpperCase();
// ldapConfiguration du mot de passe utilisateur
// Le préfixe {CRYPT} signifie que le mdp est hashé dans OpenLDAP voir : https://www.openldap.org/doc/admin24/security.html
vals3[ldapConfig.user['password']] = "{CRYPT}"+data['password'];
vals3[ldapConfig.user.single['password']] = "{CRYPT}"+data['password'];
// Ecriture d'un surnom s'il y a lieu
if ((data['nickname']!=undefined) && (data['nickname']!='')) {
vals3[ldapConfig.user['nickname']]=data['nickname'];
vals3[ldapConfig.user.single['nickname']]=data['nickname'];
}
try {
// Génération id aléatoire unique
vals3[ldapConfig.user['id']]= await Tests.generateId(ldapConfig.user['id'], ldapConfig.dn_users);
vals3[ldapConfig.user.single['id']]= await Tests.generateId(ldapConfig.user.single['id'], ldapConfig.dn_users);
}
catch(err) {
throw "Erreur lors de la génération d'un id numérique pour un nouvel utilisateur.";
......@@ -348,42 +333,42 @@ export class Supervisor extends Admin {
vals3[ldapConfig.user['directory']] = '/hosting/users/' + data['givenName'][0];
// Code root
vals3[ldapConfig.user['cleanFullName']]=data['fullName'].replace(':', ';').toLowerCase().normalize('UFD');
vals3[ldapConfig.user.single['cleanFullName']]=data['fullName'].replace(':', ';').toLowerCase().normalize('UFD');
// Adressage root
if (data['groups'].includes("on_platal")) { vals3[ldapConfig.user['login']] = "/bin/bash"; }
else { vals3[ldapConfig.user['login']] = "/sbin/nologin"; }
if (data['groups'].includes("on_platal")) { vals3[ldapConfig.user.single['login']] = "/bin/bash"; }
else { vals3[ldapConfig.user.single['login']] = "/sbin/nologin"; }
// Permissions BR
vals3[ldapConfig.user['readPerm']] = 'br.*,public.*';
if (data['readPerm'].length>0) { vals3[ldapConfig.user['readPerm']] += ',' + data['readPerm']; }
vals3[ldapConfig.user['writePerm']] = 'br.*,!br.blague-du-jour,public.*,!br.campagnekes';
if (data['writePerm'].length>0) { vals3[ldapConfig.user['readPerm']] += ',' + data['writePerm']; }
vals3[ldapConfig.user.single['readPerm']] = 'br.*,public.*';
if (data['readPerm'].length>0) { vals3[ldapConfig.user.single['readPerm']] += ',' + data['readPerm']; }
vals3[ldapConfig.user.single['writePerm']] = 'br.*,!br.blague-du-jour,public.*,!br.campagnekes';
if (data['writePerm'].length>0) { vals3[ldapConfig.user.single['readPerm']] += ',' + data['writePerm']; }
// Valeur nécessaire ASKIP mais inutile
vals3[ldapConfig.user['idNum']] ='5000';
vals3[ldapConfig.user.single['idNum']] ='5000';
// Inscription des valeurs calculées
if (!await LDAP.change(ldapConfig.key_id+"="+vals[ldapConfig.user['hruid']]+","+ldapConfig.dn_users, "add", vals3)) {
if (!await LDAP.change(ldapConfig.key_id+"="+uid+","+ldapConfig.dn_users, "add", vals3)) {
throw "Erreur lors de l'ajout des valeurs calculées à la feuille du nouvel utilisateur.";
}
["posixAccount", "shadowAccount", "inetOrgPerson", "brAccount"].forEach(cst => {
let val3={};
vals3[ldapConfig.user['class']]=cst;
LDAP.change(ldapConfig.key_id+"="+vals[ldapConfig.user['hruid']]+","+ldapConfig.dn_users, "add", vals3).then(res => {
vals3[ldapConfig.user.multiple['class']]=cst;
LDAP.change(ldapConfig.key_id+"="+uid+","+ldapConfig.dn_users, "add", vals3).then(res => {
if (!res) { throw "Erreur lors de l'ajout d'une valeur constante à la feuille du nouvel utilisateur."; }
});
});
// Utilisation des fonctions adaptées pour assurer la cohérence de l'ensemble
data['groupsIsMember'].forEach(gid => {
Admin.addGroupMember(vals[ldapConfig.key_id], gid).then(res => {
Admin.addGroupMember(uid, gid).then(res => {
if (!res) { throw "Erreur lors de l'ajout du nouvel utilisateur à un groupe."; }
});
});
data['groupsIsAdmin'].forEach(gid => {
Admin.addGroupAdmin( vals[ldapConfig.key_id], gid).then(res => {
Admin.addGroupAdmin(uid, gid).then(res => {
if (!res) { throw "Erreur lors de l'ajout du nouvel utilisateur à un groupe en tant qu'admin."; }
});
});
......@@ -404,15 +389,15 @@ export class Supervisor extends Admin {
* @async
* @static
*/
static async delUser(uid: string) {
static async delUser(uid: string): Promise<boolean> {
try {
// Gestion des groupes d'abord
let profil = await Open.peekUser(uid);
profil[ldapConfig.user['groups']].forEach(gid => {
profil[ldapConfig.user.multiple['groups']].forEach(gid => {
if (Open.isGroupAdmin(uid,gid)) {
if (!Admin.delGroupAdmin(uid, gid)) { throw "Erreur lors de la suppression des droits d'admin de l'utilisateur."; }
}
if (!Admin.delGroupMember(uid, gid)) { throw "Erreur lors de la suppression de l'appartenace à un groupe de l'utilisateur."; }
if (!Admin.delGroupMember(uid, gid)) { throw "Erreur lors de la suppression de l'appartenance à un groupe de l'utilisateur."; }
});
}
catch(err) {
......@@ -420,5 +405,6 @@ export class Supervisor extends Admin {
}
// Elimination
if (!LDAP.clear(ldapConfig.key_id+"="+uid+","+ldapConfig.dn_users)) { throw "Erreur lors de la suppression de l'utilisateur."; }
return true;
}
}
\ No newline at end of file
......@@ -40,20 +40,17 @@ export class LDAP {
* @static
* @async
*/
static async bind(dn: string, password: string) {
return new Promise((resolve, reject) => {
// Escape DN as everywhere in this file, but password is taken as is
client.bind(dn, password, res => {
// Gestion erreur
try { res; }
catch(err) {
reject(err);
throw "Erreur lors de la connection au LDAP.";
}
});
// End with a boolean
resolve(true);
static async bind(dn: string, password: string) : Promise<boolean> {
// Escape DN as everywhere in this file, but password is taken as is
client.bind(dn, password, res => {
// Gestion erreur
try { res; }
catch(err) {
throw "Erreur lors de la connection au LDAP.";
}
});
// End with a boolean
return true;
}
/**
......@@ -63,7 +60,7 @@ export class LDAP {
* @static
* @async
*/
static async adminBind() { return this.bind(credentialsLdapConfig.dn, credentialsLdapConfig.password); }
static async adminBind() : Promise<boolean> { return LDAP.bind(credentialsLdapConfig.dn, credentialsLdapConfig.password); }
/**
* @summary Fonction qui sert à se déconnecter du LDAP.
......@@ -73,7 +70,7 @@ export class LDAP {
* @static
* @async
*/
static async unbind() { return this.bind("", ""); }
static async unbind() : Promise<boolean> { return LDAP.bind("", ""); }
/**
* @summary Fonction qui interroge le LDAP selon un protocole spécifié en argument et renvoit les valeurs trouvées.
......@@ -87,44 +84,39 @@ export class LDAP {
* @static
* @async
*/
static async search(dn: string, attributes: string[], filter="(objectClass=*)") {
this.adminBind();
return new Promise((resolve, reject) => {
let vals=[];
// Interrogation LDAP selon ldapConfiguration fournie en argument
client.search(ldapEscape.dn("${txt}", { txt: dn}), {
"scope": "sub",
"filter": ldapEscape.filter("${txt}", { txt: filter}),
"attributes": attributes
}, (err, res) => {
// Gestion erreur ; pb car pas simple true / autre en sortie
if (err) {
reject(err);
throw "Erreur lors de la recherche sur le LDAP.";
} else {
// Dès que la recherche renvoit une entrée, on stocke les attributs qui nous intéresse
res.on('searchEntry', entry => {
// Cas un seul attribut où le résultat est une liste directement
if (!Array.isArray(attributes)) { vals.push(entry.object[attributes]); }
else if (attributes.length == 1) { vals.push(entry.object[attributes[0]]); }
// Cas plusieurs attributs donc résultat dictionnaire
else {
vals.push({});
attributes.forEach(attribute => {
vals.slice(-1)[0][attribute]=entry.object[attribute];
});
}
});
// Si la recherche renvoie une erreur, on renvoit
res.on('error', resErr => { reject(resErr); });
// Si la recherche est finie on se déconnecte et on renvoit la liste
res.on('end', res => {
this.unbind();
resolve(vals);
});
}
});
static async search(dn: string, attributes: string[], filter="(objectClass=*)") : Promise<Array<any>> {
LDAP.adminBind();
let vals=[];
// Interrogation LDAP selon ldapConfiguration fournie en argument
client.search(ldapEscape.dn("${txt}", { txt: dn}), {
"scope": "sub",
"filter": ldapEscape.filter("${txt}", { txt: filter}),
"attributes": attributes
}, (err, res) => {
// Gestion erreur ; pb car pas simple true / autre en sortie
if (err) {
throw "Erreur lors de la recherche sur le LDAP.";
} else {
// Dès que la recherche renvoit une entrée, on stocke les attributs qui nous intéresse
res.on('searchEntry', entry => {
// Cas un seul attribut où le résultat est une liste directement
if (!Array.isArray(attributes)) { vals.push(entry.object[attributes]); }
else if (attributes.length == 1) { vals.push(entry.object[attributes[0]]); }
// Cas plusieurs attributs donc résultat dictionnaire
else {
vals.push({});
attributes.forEach(attribute => {
vals.slice(-1)[0][attribute]=entry.object[attribute];
});
}
});
// Si la recherche renvoie une erreur, on renvoit
res.on('error', resErr => { throw resErr; });
// Si la recherche est finie on se déconnecte et on renvoit la liste
res.on('end', res => { LDAP.unbind(); });
}
});
return vals;
}
//TBT
......@@ -140,21 +132,18 @@ export class LDAP {
* @static
* @async
*/
static async change(dn: string, op: string, mod) {
this.adminBind();
return new Promise((resolve, reject) => {
// Modification LDAP selon ldapConfiguration en argument (pourrait prendre une liste de Changes)
client.modify(ldapEscape.dn("${txt}", {txt: dn}), new ldap.Change({
operation: ldapEscape.dn("${txt}", {txt: op}),
modification: mod,
// Gestion erreur
}), err => {
reject(err);
throw "Erreur lors d'une opération de modification sur le LDAP.";
});
this.unbind();
resolve(true);
static async change(dn: string, op: string, mod) : Promise<boolean> {
LDAP.adminBind();
// Modification LDAP selon ldapConfiguration en argument (pourrait prendre une liste de Changes)
client.modify(ldapEscape.dn("${txt}", {txt: dn}), new ldap.Change({
operation: ldapEscape.dn("${txt}", {txt: op}),
modification: mod,
// Gestion erreur
}), err => {
throw "Erreur lors d'une opération de modification sur le LDAP.";
});
LDAP.unbind();
return true;
}
//TBT
......@@ -168,17 +157,14 @@ export class LDAP {
* @static
* @async
*/
static async add(dn: string, vals) {
this.adminBind();
return new Promise((resolve, reject) => {
// Ajout LDAP selon la ldapConfiguration en argument
client.add(ldapEscape.dn(ldapConfig.key_id+"="+vals[ldapConfig.key_id]+",${txt}", { txt: dn}), vals, err => {
reject(err);
throw "Erreur lors d'une opération d'ajout sur le LDAP.";
});
this.unbind();
resolve(true);
static async add(dn: string, vals) : Promise<boolean> {
LDAP.adminBind();
// Ajout LDAP selon la ldapConfiguration en argument
client.add(ldapEscape.dn(ldapConfig.key_id+"="+vals[ldapConfig.key_id]+",${txt}", { txt: dn}), vals, err => {
throw "Erreur lors d'une opération d'ajout sur le LDAP.";
});
LDAP.unbind();
return true;
}
//TBT
......@@ -191,16 +177,13 @@ export class LDAP {
* @static
* @async
*/
static async clear(dn: string) {
this.adminBind();
return new Promise((resolve, reject) => {
// Suppression LDAP
client.del(ldapEscape.dn("${txt}", {txt: dn}), err => {
reject(err);
throw "Erreur lors d'une opération de suppression sur le LDAP.";
});
this.unbind();
resolve(true);
static async clear(dn: string) : Promise<boolean> {
LDAP.adminBind();
// Suppression LDAP
client.del(ldapEscape.dn("${txt}", {txt: dn}), err => {
throw "Erreur lors d'une opération de suppression sur le LDAP.";
});
LDAP.unbind();
return true;
}
}
\ No newline at end of file
......@@ -4,10 +4,10 @@
* @author hawkspar
*/
import {ldapConfig} from './config';
import { ldapConfig } from './config';
import {LDAP} from './basics';
import {searchUserFields, SmartSearch, Tests} from './utilities';
import { Admin, Supervisor, userFullData } from './admins';
import {Admin, Supervisor} from './admins';
import ldap from 'ldapjs';
/**
......@@ -18,10 +18,8 @@ import ldap from 'ldapjs';
* @var {string[]} admins - Liste des admins du groupe ; supposée être une sous-liste de la précédente
*/
let groupDataTemplate = {}
for (let key in ldapConfig.group.profil) {
if (ldapConfig.group.direct_input.includes(key)) { groupDataTemplate[key] = ""; }
else { groupDataTemplate[key] = [""]; }
}
for (let key in ldapConfig.group.single) { groupDataTemplate[key] = ""; }
for (let key in ldapConfig.group.multiple) { groupDataTemplate[key] = [""]; }
/**
* @type groupData
......@@ -43,12 +41,15 @@ export type groupData = typeof groupDataTemplate;
* @var {string} ip - Adresse(s) ip
* @var {string} adress - Adresse(s)
* @var {string} groups - Un ou plusieurs groupes dont l'utilisateur est membre (inclus section sportive, binet, PA...)
* @var {string} password - Mot de passe généré en amont
* @arg {string} readPerm - Permissions spéciales BR
* @var {string} writePerm - Permissions spéciales BR
* @var {string[]} forlifes - Alias BR (attention le filtre .fkz n'est plus fonctionnel)
* @var {string[]} groupsIsAdmin - Liste des gid dont le pax est admin ; supposé sous-liste de groups
*/
let userDataTemplate = {}
for (let key in ldapConfig.user.profil) {
if (ldapConfig.user.direct_input.includes(key)) { userDataTemplate[key] = ""; }
else { userDataTemplate[key] = [""]; }
}
for (let key in ldapConfig.user.single) { userDataTemplate[key] = ""; }
for (let key in ldapConfig.user.multiple) { userDataTemplate[key] = [""]; }
/**
* @type groupData
......@@ -175,7 +176,10 @@ export class Open {
*/
static async peekUser(uid: string) : Promise<userData> {
try {
let LDAPUserData = await LDAP.search(ldapConfig.key_id+uid+ldapConfig.dn_users, ldapConfig.user.profil);
let fields = [];
fields.push(ldapConfig.user.single.values());
fields.push(ldapConfig.user.multiple.values());
let LDAPUserData = await LDAP.search(ldapConfig.key_id+'='+uid+','+ldapConfig.dn_users, fields);
let cleanUserData = userDataTemplate;
// Rename output
for (let uncleanKey in LDAPUserData) {
......@@ -201,7 +205,10 @@ export class Open {
*/
static async peekGroup(gid: string) : Promise<groupData> {
try {
let LDAPGroupData = await LDAP.search(ldapConfig.key_id+gid+ldapConfig.dn_groups, ldapConfig.group.profil);
let fields = [];
fields.push(ldapConfig.user.single.values());
fields.push(ldapConfig.user.multiple.values());
let LDAPGroupData = await LDAP.search(ldapConfig.key_id+'='+gid+','+ldapConfig.dn_groups, fields);
let cleanGroupData=groupDataTemplate;
// Rename output
for (let uncleanKey in LDAPGroupData) {
......@@ -228,10 +235,10 @@ export class Open {
* @static
* @async
*/
static async findGroups(input: string) {
static async findGroups(input: string) : Promise<string[]> {
try {
// Trucs intelligents faits dans ./utilities
return SmartSearch.groups(input, [ldapConfig.key_id, ldapConfig.group.type]);
return SmartSearch.groups(input, ldapConfig.key_id);
}
catch(err) {
throw "Erreur lors de la recherche approximative d'un groupe.";
......@@ -247,7 +254,7 @@ export class Open {
* @static
* @async
*/
static async findUsers(data: searchUserFields) {
static async findUsers(data: searchUserFields) : Promise<string[]> {
try {
return SmartSearch.users(data, ldapConfig.key_id);
}
......@@ -280,78 +287,80 @@ export class User extends Open {
* @async
* @static
*/
static async addGroup(data: groupData) {
static async addGroup(data: groupData) : Promise<boolean> {
// Calcul d'un dictionnaire d'ajout
let vals = {};
// uid de base généré à partir du nom standardisé
try {
Tests.generateReadableId(data['name']).then(id => { vals[ldapConfig.group['name']]=id; });
Tests.generateReadableId(data['name']).then(id => {
vals[ldapConfig.key_id]=id;
vals[ldapConfig.group.single['name']]=id;
});
}
catch(err) {
throw "Erreur lors de la génération d'un hruid pour créer un nouveau groupe.";
}
let gid = vals[ldapConfig.key_id];
// Ecriture de toutes les valeurs directement inscrites dans le LDAP (in pour input)
ldapConfig.group.direct_input.forEach(key_att => vals[ldapConfig.group[key_att]]=data[key_att]);
ldapConfig.group.single.forEach(key_att => vals[ldapConfig.group.single[key_att]]=data[key_att]);
// Appel à la fonction de base
if (!await LDAP.add(ldapConfig.key_id+"="+vals[ldapConfig.group['name']]+","+ldapConfig.dn_groups, vals)) {
if (!await LDAP.add(ldapConfig.key_id+"="+gid+","+ldapConfig.dn_groups, vals)) {
throw "Erreur lors de la création d'une nouvelle feuille dans l'arbre des groupes.";
}
// Certains champs nécessitent de petits calculs
let vals2={};
// Sauvegarde du nom (pour le cas où gid != data['name'])
vals2[ldapConfig.group["name"]]=data['name'];
// ?!
vals2[ldapConfig.user['password']] = '';
vals2[ldapConfig.group.single['password']] = '';
// Génération id aléatoire et test contre le LDAP
try {
Tests.generateId(ldapConfig.groups["idNumber"], ldapConfig.dn_groups).then(id => { vals2[ldapConfig.group['idNumber']]=id; });
Tests.generateId(ldapConfig.group.single["idNumber"], ldapConfig.dn_groups).then(id => { vals2[ldapConfig.group.single['idNumber']]=id; });
}
catch(err) {
throw "Erreur lors de la génération d'un id numérique pour créer un nouveau groupe.";
}
// FOIREUX : Hypothèse sur la structure du reste des données mais évite un test.assurerUnicite à deux variables
vals2[ldapConfig.group['idNumber2']]=vals2[ldapConfig.group['idNumber']];
vals2[ldapConfig.group.single['idNumber2']]=vals2[ldapConfig.group.single['idNumber']];
// Stockage machine ; dépend du prénom
vals2[ldapConfig.group['directory']] = '/hosting/groups/'+vals[ldapConfig.key_id];
vals2[ldapConfig.group.single['directory']] = '/hosting/groups/'+gid;
// Code root
vals2[ldapConfig.group['cleanFullName']]=data['name'].replace(':', ';').toLowerCase().normalize('UFD');
vals2[ldapConfig.group.single['cleanFullName']]=data['name'].replace(':', ';').toLowerCase().normalize('UFD');
// Adressage root
vals2[ldapConfig.group['login']] = "/sbin/nologin";
vals2[ldapConfig.group.single['login']] = "/sbin/nologin";
// Permissions BR
vals2[ldapConfig.group['readPerm']] = '!*';
vals2[ldapConfig.group['writePerm']] = '!*';
vals2[ldapConfig.group.single['readPerm']] = '!*';
vals2[ldapConfig.group.single['writePerm']] = '!*';
// Inscription des valeurs calculées par effet de bord
if (!await LDAP.change(ldapConfig.key_id+"="+vals[ldapConfig.key_id]+","+ldapConfig.dn_groups, "add", vals2)) {
if (!await LDAP.change(ldapConfig.key_id+"="+gid+","+ldapConfig.dn_groups, "add", vals2)) {
throw "Erreur lors de l'ajout des valeurs intelligentes du nouveau groupe.";
}
["posixAccount", "posixGroup", "brAccount"].forEach(cst => {
let vals3={};
vals3[ldapConfig.group['class']]=cst;
LDAP.change(ldapConfig.key_id+"="+vals[ldapConfig.key_id]+","+ldapConfig.dn_groups, "add", vals3).then(res => {
vals3[ldapConfig.group.multiple['class']]=cst;
LDAP.change(ldapConfig.key_id+"="+gid+","+ldapConfig.dn_groups, "add", vals3).then(res => {
if (!res) { throw "Erreur lors de l'ajout des valeurs constantes du nouveau groupe."; }
});
});
// Utilisation des fonctions adaptées pour assurer la cohérence de l'ensemble
data['members'].forEach(uid => {
Admin.addGroupMember(uid, vals[ldapConfig.key_att]).then(res => {
Admin.addGroupMember(uid, gid).then(res => {
if (!res) { throw "Erreur de l'ajout d'un membre au groupe."; }
});
});
data['admins'].forEach(uid => {
Admin.addGroupAdmin( uid, vals[ldapConfig.key_att]).then(res => {
Admin.addGroupAdmin(uid, gid).then(res => {
if (!res) { throw "Erreur de l'ajout d'un admin au groupe."; }
});
});
......@@ -376,18 +385,11 @@ export class User extends Open {
* @async
* @static
*/
static async editUser(uid : string, data : userFullData) {
static async editUser(uid : string, data : userData) : Promise<boolean> {
// Récupération des anciennes données
let profil = await Open.peekUser(uid);
// Reecriture de profil avec les bons champs
Object.keys(profil).forEach(keyLDAP => {
Object.keys(ldapConfig.user).forEach(keyAlias => {
ldapConfig.user[keyAlias]=keyLDAP;
profil[keyAlias]=profil[keyLDAP];
});
});
// Régénération du champ manquant dans profil
try {
// Régénération du champ manquant dans profil
let lg = await Open.getGroups(uid);
profil['groupsIsAdmin']=[];
lg.forEach(gid => {
......@@ -396,15 +398,11 @@ export class User extends Open {
});
});
// Surcharge des champs à modifier selon data
let unchangeables = ['readPerm','writePerm','forlifes','ips','groups','groupsIsAdmin'];
Object.keys(data).forEach(function(key: string) {
// Some fields the user cannot change (groups and groupsIsAdmin must be changed through addGroupMember and addGroupAdmin in Admin)
if (!unchangeables.includes(key)) { profil[key]=data[key]; }
if (!['readPerm','writePerm','forlifes','ips','groups','groupsIsAdmin'].includes(key)) { profil[key]=data[key]; }
});
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// TBM : rajouter god passwd. Moche mais bonne façon de faire
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// Passage en godmode,modification propre par effet de bord
// Modification propre par effet de bord
if (!(await Supervisor.delUser(uid) && await Supervisor.addUser(profil))) {
throw "Erreur dans la destruction/création du compte.";
} else {
......@@ -415,6 +413,4 @@ export class User extends Open {
throw "Erreur lors de la modification des groupes où un utilisateur est admin.";
}
}
destr() { LDAP.unbind(); }
}
\ No newline at end of file
......@@ -66,7 +66,7 @@ export class SmartSearch {
* @static
* @async
*/
static async groups(input: string, return_attributes: string[]) {
static async groups(input: string, return_attributes: string[]) : Promise<string[]> {
// Construction du filtre custom
let filter= "(|("+ldapConfig.key_id+"="+ input+")" + // On cherche la valeur exacte
"(|("+ldapConfig.key_id+"=*"+input+")" + // La valeur finale avec des trucs avant ; wildcard *
......@@ -95,7 +95,7 @@ export class SmartSearch {
* @static
* @async
*/
static async users(data: searchUserFields, return_attributes: string[]) {
static async users(data: searchUserFields, return_attributes: string[]) : Promise<string[]> {
let filter="";
// Iteration pour chaque champ, alourdissement du filtre selon des trucs prédéfinis dans ldapConfig encore
for (var key in data) {
......@@ -156,7 +156,7 @@ export class Tests {
* @static
* @async
*/
static async ensureUnique(value: string, attribute: string, dn: string, changeValue: (string, number) => string, n=0) {
static async ensureUnique(value: string, attribute: string, dn: string, changeValue: (string, number) => string, n=0) : Promise<string> {
// Recherche d'autres occurences de l'id
try {
return LDAP.search(dn, ldapConfig.key_id, "("+attribute+"="+value+")").then(function (matches: string[]) {
......@@ -164,7 +164,7 @@ export class Tests {
// On renvoit la valeur si elle est bien unique
else if (matches.length==0) { return value; }
// Sinon, on tente de nouveau notre chance avec la valeur suivante
else { return this.ensureUnique(changeValue(value, n+1), dn, changeValue, n+1); }
else { return Tests.ensureUnique(changeValue(value, n+1), attribute, dn, changeValue, n+1); }
});
}
catch(err) {
......@@ -182,7 +182,7 @@ export class Tests {
* @static
* @async
*/
static async generateUid(givenName: string, lastName: string, promotion: string) {
static async generateUid(givenName: string, lastName: string, promotion: string) : Promise<string> {
try {
// normalize et lowerCase standardisent le format
return this.ensureUnique((givenName+'.'+lastName).toLowerCase().normalize('UFD'), ldapConfig.key_id, ldapConfig.dn_users, (id: string, n: number) => {
......@@ -205,7 +205,7 @@ export class Tests {
* @static
* @async
*/
static async generateReadableId(name: string) {
static async generateReadableId(name: string) : Promise<string> {
try {
// normalize et lowerCase standardisent le format
return this.ensureUnique(name.toLowerCase().normalize('UFD'), ldapConfig.key_id, ldapConfig.dn_groups, (id: string, n: number) => {
......@@ -227,7 +227,7 @@ export class Tests {
* @static
* @async
*/
static async generateId(attribut: string, dn: string) {
static async generateId(attribut: string, dn: string) : Promise<string> {
try {
return this.ensureUnique("0", attribut, dn, (id,n) => { return Math.floor((Math.random() * 100000) + 1).toString(); });
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment