diff --git a/ldap_config.json b/ldap_config.json index 11d58ea68cbba5786eb8f0907fbb76b101717b6b..f19e61a42b4b6ebcde0d7a86a60054e8b3ba9045 100644 --- a/ldap_config.json +++ b/ldap_config.json @@ -14,36 +14,37 @@ "user": { "direct_input": ["givenName","lastName","birthdate", "promotion", "mail","phone","photo","adress"], "multiple_input": ["ips","forlifes"], - "profil": ["jpegPhoto","givenName", "sn", "brBirthdate", "brPromo","telephoneNumber","mail","brRoom","brIP","brMemberOf"], + "profil": ["jpegPhoto","displayName","givenName", "sn", "brBirthdate", "brPromo","telephoneNumber","mail","brRoom","brIP","brMemberOf","brNewsReadAccess","brNewsPostAccess","brAlias"], "photo": "jpegPhoto", "givenName": "givenName", "lastName": "sn", "nickname": "displayName", + "birthdate": "brBirthdate", "nationality": "country", "promotion": "brPromo", "phone": "telephoneNumber", "mail": "mail", "adress": "brRoom", - "ips": "brIP", - "school": "brMemberOf", - "groups": "brMemberOf", - "studies": "brMemberOf", - "birthdate": "brBirthdate", - "sport": "brMemberOf", + "ips": "brIP", "id": "uidNumber", + "sport": "brMemberOf", "password": "userPassword", "forlifes": "brAlias", "idNum": "gidNumber", "directory": "homeDirectory", - "login": "loginShell", "readPerm": "brNewsReadAccess", "writePerm": "brNewsPostAccess", "fullName": "cn", + "login": "loginShell", + "groups": "brMemberOf", + "school": "brMemberOf", + "studies": "brMemberOf", "cleanFullName": "gecos", "class": "objectClass" }, "group": { "direct_input": ["name","ns"], + "profil": ["cn","restrictedMemberUid","memberUid", "brNS"], "name": "cn", "member": "restrictedMemberUid", "admin": "memberUid", diff --git a/src/ldap/ldap_data.js b/src/ldap/ldap_data.js index 76b28af83a261400781d0cd3cd8f3b9746cdee8e..7f2c65085ed5bb4bee22b6f0cdb5f32c0dc89477 100644 --- a/src/ldap/ldap_data.js +++ b/src/ldap/ldap_data.js @@ -1,5 +1,9 @@ /** - * @file Ce fichier gère les requêtes LDAP de type données ; liste des groupe d'un individu, liste des membres d'un groupe... A ne pas confondre avec ldap_auth qui lui gère l'authentification. Il est à lire conjointement avec [`ldap_config.json`](..\..\config.json) qui détaille les champs spécifiques à chaque LDAP, anonymisés ici. Il est fortement conseillé d'utiliser [`JXplorer`](http://jxplorer.org/) pour naviguer sur le LDAP en parallèle du dévelopement. Toutes les fonctions écrites ici sont asynchrones et renvoient des Promises ce qui nécessite de les appeler avec la synthaxe un peu particulière `f(args).then(res => ...)` pour exploiter leur résultat. + * @file Ce fichier gère les requêtes LDAP de type données ; liste des groupe d'un individu, liste des membres d'un groupe... + * A ne pas confondre avec ldap_auth qui lui gère l'authentification. Il est à lire conjointement avec [`ldap_config.json`](..\..\config.json) qui détaille + * les champs spécifiques à chaque LDAP, anonymisés ici. Il est fortement conseillé d'utiliser [`JXplorer`](http://jxplorer.org/) pour naviguer sur le LDAP + * en parallèle du dévelopement. Toutes les fonctions écrites ici sont asynchrones et renvoient des Promises ce qui nécessite de les appeler avec la synthaxe + * un peu particulière `f(args).then(res => ...)` pour exploiter leur résultat. * @author hawkspar */ @@ -43,7 +47,10 @@ function connecterLDAP(user) { client.bind(connect.dn, connect.passwd, (err, res /** * @summary Fonction qui interroge le LDAP selon un protocole spécifié en argument et renvoit les valeurs trouvées. - * @desc Cette fonction appelle {@link bind} pour authentifier l'utilisateur pour authentifier l'utilisateur puis rompts la connexion. Cette fonction utilise ldapjs (voir [`Client API`](http://ldapjs.org/client.html) méthode search). Il faut l'appeler suivant un schéma `rechercherLDAP(user, ...).then((res) => { truc avec res });`. Cette fonction fait une demande au LDAP qu'elle filtre selon un schéma prédéfini dans `dic` et à chaque résultat (event SearchEntry) le met dans une liste, et renvoit la liste à l'issue (event end). + * @desc Cette fonction appelle {@link bind} pour authentifier l'utilisateur pour authentifier l'utilisateur puis rompts la connexion. + * Cette fonction utilise ldapjs (voir [`Client API`](http://ldapjs.org/client.html) méthode search). Il faut l'appeler suivant un schéma + * `rechercherLDAP(user, ...).then((res) => { truc avec res });`. Cette fonction fait une demande au LDAP qu'elle filtre selon un schéma prédéfini dans `dic` + * et à chaque résultat (event SearchEntry) le met dans une liste, et renvoit la liste à l'issue (event end). * @arg {Object} user - Utilisateur de la forme nécessaire à {@link connecterLDAP} * @arg {string} base - DN de l'emplacement de la requête * @arg {string} filter ["(objectClass=*)"] - Filtre logique de la recherche (format [`RFC2254`](https://tools.ietf.org/search/rfc2254)) déjà passé au ldapEscape @@ -93,11 +100,15 @@ function rechercherLDAP(user, base, attributes, filter="(objectClass=*)") { //TBT /** * @summary Fonction qui permet de modifier un élément sur le LDAP. Gestion intelligente de l'appartenance à un binet. - * @desc Cette fonction appelle {@link bind} pour authentifier l'utilisateur pour authentifier l'utilisateur puis rompts la connexion. Cette fonction utilise une Promise pour être asynchrone ; elle renvoit la promesse d'une réponse puis traite la demande avec ldapjs (voir [`Client API`](http://ldapjs.org/client.html) méthode modify). Il faut l'appeler suivant un schéma `modifierLDAP(...).then((res) => { truc avec res });`. Cette fonction fait une demande au LDAP qu'elle filtre selon un schéma prédéfini dans `dic` et à chaque résultat (event SearchEntry) le met dans une liste, et renvoit la liste à l'issue (event end). + * @desc Cette fonction appelle {@link bind} pour authentifier l'utilisateur pour authentifier l'utilisateur puis rompts la connexion. + * Cette fonction utilise une Promise pour être asynchrone ; elle renvoit la promesse d'une réponse puis traite la demande avec ldapjs + * (voir [`Client API`](http://ldapjs.org/client.html) méthode modify). Il faut l'appeler suivant un schéma `modifierLDAP(...).then((res) => { truc avec res });`. * @arg {Object} user - Utilisateur de la forme nécessaire à {@link connecterLDAP} * @arg {string} name - DN de l'endroit à modifier - * @arg {string} op - Operation à réaliser sur le LDAP. Trois opération sont possibles ; "add", qui rajoute des attributs et qui peut créer des doublons, "del" qui en supprime, et "replace" qui remplace du contenu par un autre. - * @arg {Object} mod - Dictionnaire contenant les attributs à modifier et les nouvelles valeurs des attributs. Une nouvelle valeur vide ("") est équivalent à la suppression de cet attribut. + * @arg {string} op - Operation à réaliser sur le LDAP. Trois opération sont possibles ; "add", qui rajoute des attributs et qui peut créer des doublons, + * "del" qui en supprime, et "replace" qui remplace du contenu par un autre. + * @arg {Object} mod - Dictionnaire contenant les attributs à modifier et les nouvelles valeurs des attributs. Une nouvelle valeur vide ("") est équivalent à la suppression + * de cet attribut. * @return {Promise(boolean)} `true` si la modification s'est bien déroulée, false sinon */ function modifierLDAP(user, name, op, mod) { @@ -118,7 +129,9 @@ function modifierLDAP(user, name, op, mod) { //TBT /** * @summary Fonction qui permet de rajouter un élément sur le LDAP. - * @desc Cette fonction appelle {@link connecterLDAP} pour authentifier l'utilisateur puis rompts la connexion. Cette fonction utilise une Promise pour être asynchrone ; elle renvoit la promesse d'une réponse puis traite la demande avec ldapjs (voir [`Client API`](http://ldapjs.org/client.html) méthode add). Il faut l'appeler suivant un schéma `modifierLDAP(...).then((res) => { truc avec res });`. Cette fonction fait une demande au LDAP qu'elle filtre selon un schéma prédéfini dans `dic` et à chaque résultat (event SearchEntry) le met dans une liste, et renvoit la liste à l'issue (event end). + * @desc Cette fonction appelle {@link connecterLDAP} pour authentifier l'utilisateur puis rompts la connexion. Cette fonction utilise une Promise pour être asynchrone ; + * elle renvoit la promesse d'une réponse puis traite la demande avec ldapjs (voir [`Client API`](http://ldapjs.org/client.html) méthode add). + * Il faut l'appeler suivant un schéma `modifierLDAP(...).then((res) => { truc avec res });`. * @arg {Object} user - Utilisateur de la forme nécessaire au {@link connecterLDAP} * @arg {string} dn - Adresse du parent * @arg {Object} vals - Dictionnaire contenant les valeurs à créer @@ -137,13 +150,38 @@ function ajouterLDAP(user, dn, vals) { }); } +//TBT +/** + * @summary Fonction qui permet de supprimer une feuille du LDAP. + * @desc Cette fonction appelle {@link connecterLDAP} pour authentifier l'utilisateur puis rompts la connexion. Cette fonction utilise une Promise pour être asynchrone ; + * elle renvoit la promesse d'une réponse puis traite la demande avec ldapjs (voir [`Client API`](http://ldapjs.org/client.html) méthode del). + * Il faut l'appeler suivant un schéma `modifierLDAP(...).then((res) => { truc avec res });`. Cette fonction fait une demande au LDAP qu'elle filtre selon un + * schéma prédéfini dans `dic` et à chaque résultat (event SearchEntry) le met dans une liste, et renvoit la liste à l'issue (event end). + * Elle est différente de modify avec "del" car elle affecte directement une feuille et pas un attribut. + * @arg {Object} user - Utilisateur de la forme nécessaire au {@link connecterLDAP} + * @arg {string} dn - Adresse de la cible + * @return {Promise(boolean)} `true` si la modification s'est bien déroulée, false sinon + */ +function supprimerLDAP(user, dn) { + return new Promise((resolve, reject) => { + connecterLDAP(user); + // Suppression LDAP + client.del(dn, function(err) { + reject(err); + }); + client.bind("", "", (err, res) => {}); + resolve(true); + }); +} + //------------------------------------------------------------------------------------------------------------------------ // Fonctions de lecture //------------------------------------------------------------------------------------------------------------------------ /** * @summary Fonction qui retrouve les groupes dont un individu est membre. - * @desc Cette fonction utilise {@link rechercherLDAP} ; elle va directement à la feuille de l'utilisateur et n'a donc pas de filtre. Elle utilise ldapEscape pour éviter les injections. + * @desc Cette fonction utilise {@link rechercherLDAP} ; elle va directement à la feuille de l'utilisateur et n'a donc pas de filtre. Elle utilise ldapEscape pour éviter + * les injections. * @arg {Object} user - Utilisateur de la forme nécessaire à {@link connecterLDAP} * @arg {string} uid - Identifiant de l'individu à interroger (le plus souvent prenom.nom, parfois l'année, supposé valide) * @return {Promise(string[])} Liste des uid de groupes (noms flat des groupes) où l'id fourni est membre @@ -181,18 +219,32 @@ function listerAdministrateurs(user, gid) { } /** - * @summary Fonction qui renvoit toutes les infos relatives à un hruid particulier. + * @summary Fonction qui renvoit toutes les infos relatives à un utilisateur particulier. * @desc Cette fonction utilise {@link rechercherLDAP} avec des attributs prédéfinis. Elle utilise LDAPEscape pour éviter les injections. * @arg {Object} user - Utilisateur de la forme nécessaire à {@link connecterLDAP} * @arg {string} uid - Identifiant de l'utilisateur - * @return {Promise(Object[])} Informations recueillies ; renvoie une liste de dictionnaire avec le profil complet de l'utilisateur ; voir `ldap_config.json`(..\..\ldap_config.json) pour les clés exactes. + * @return {Promise(Object[])} Informations recueillies ; renvoie une liste de dictionnaire avec le profil complet de l'utilisateur ; + * voir `ldap_config.json`(..\..\ldap_config.json) pour les clés exactes. */ function renseignerSurUtilisateur(user, uid) { return new Promise((resolve, reject) => { - let attributes =[]; rechercherLDAP(user,ldapEscape.filter(config.key_id+"=${id},"+config.dn_users, {id : uid}), config.user.profil).then(res => resolve(res)); }); } + +/** + * @summary Fonction qui renvoit toutes les infos relatives à un groupe particulier. + * @desc Cette fonction utilise {@link rechercherLDAP} avec des attributs prédéfinis. Elle utilise LDAPEscape pour éviter les injections. + * @arg {Object} user - Utilisateur de la forme nécessaire à {@link connecterLDAP} + * @arg {string} gid - Identifiant du groupe + * @return {Promise(Object[])} Informations recueillies ; renvoie une liste de dictionnaire avec le profil complet du groupe ; + * voir `ldap_config.json`(..\..\ldap_config.json) pour les clés exactes. + */ +function renseignerSurGroupe(user, gid) { + return new Promise((resolve, reject) => { + rechercherLDAP(user,ldapEscape.filter(config.key_id+"=${id},"+config.dn_groups, {id : gid}), config.group.profil).then(res => resolve(res)); + }); +} //------------------------------------------------------------------------------------------------------------------------ // Fonctions de recherche @@ -201,7 +253,9 @@ function renseignerSurUtilisateur(user, uid) { /** * @summary Fonction qui interroge le LDAP et retrouve les groupes (voir LDAP) qui ressemblent * à l'entrée. Etape 0 vers un vrai TOL (Trombino On Line). - * @desc Cette fonction utilise {@link rechercherLDAP} mais avec un filtre généré à la volée. Accepte des champs exacts ou incomplets mais pas approximatifs et ne gère pas l'auto-complete. Cette fonction utilise aussi config.json. MEF Timeout pour des recherches trop vagues. Renvoit une liste d'uid. Elle utilise LDAPEscape pour éviter les injections. + * @desc Cette fonction utilise {@link rechercherLDAP} mais avec un filtre généré à la volée. Accepte des champs exacts ou incomplets mais pas approximatifs + * et ne gère pas l'auto-complete. Cette fonction utilise aussi config.json. MEF Timeout pour des recherches trop vagues. Renvoit une liste d'uid. + * Elle utilise LDAPEscape pour éviter les injections. * @arg {Object} user - Utilisateur de la forme nécessaire à {@link connecterLDAP} * @arg {string} input - String entré par l'utilisateur qui ressemble au nom du groupe. * @return {Promise(string[])} Liste des uid de groupes dont le nom ressemble à l'input @@ -225,7 +279,9 @@ function trouverGroupes(user, input) { // TBT /** * @summary Fonction qui interroge le LDAP et retrouve le groupe qui ressemblent à l'input et qui correspond. Etape 0 vers un vrai TOL (Trombino On Line). - * @desc Cette fonction utilise {@link rechercherLDAP} mais avec un filtre généré à la volée. Accepte des champs exacts ou incomplets mais pas approximatifs et ne gère pas l'auto-complete. Cette fonction utilise aussi config.json. MEF Timeout pour des recherches trop vagues. Renvoit une liste d'uid. Elle utilise LDAPEscape pour éviter les injections. + * @desc Cette fonction utilise {@link rechercherLDAP} mais avec un filtre généré à la volée. Accepte des champs exacts ou incomplets mais pas approximatifs et + * ne gère pas l'auto-complete. Cette fonction utilise aussi config.json. MEF Timeout pour des recherches trop vagues. Renvoit une liste d'uid. Elle utilise LDAPEscape + * pour éviter les injections. * @arg {Object} user - Utilisateur de la forme nécessaire à {@link connecterLDAP} * @arg {string} input - String entré par l'utilisateur qui ressemble au nom du groupe. * @arg {string} type - String aux valeurs prédéfinies dans ldap_config. @@ -252,9 +308,12 @@ function trouverGroupesParTypes(user, input, type) { /** * @summary Fonction qui renvoit les attributs demandés des paxs validant les critères de recherche. Première étape vers vrai TOL (Trombino On Line). - * @desc Cette fonction utilise {@link rechercherLDAP} mais avec un filtre généré à la volée. Accepte des champs exacts ou incomplets pour la plupart des champs mais pas approximatifs et ne gère pas l'auto-complete. MEF Timeout pour des recherches trop vagues. Elle utilise LDAPEscape pour éviter les injections. + * @desc Cette fonction utilise {@link rechercherLDAP} mais avec un filtre généré à la volée. Accepte des champs exacts ou incomplets pour la plupart des champs + * mais pas approximatifs et ne gère pas l'auto-complete. MEF Timeout pour des recherches trop vagues. Elle utilise LDAPEscape pour éviter les injections. * @arg {Object} user - Utilisateur de la forme nécessaire à {@link connecterLDAP} - * @arg {Object} data - Dictionnaire contenant les données nécessaires à la recherche. Les valeurs sont celles entrées par l'utilisateur et sont par hypothèse comme des sous-parties compactes des valeurs renvoyées. Tous les champs ci-dessous peuvent être indifféremment des listes (par exempl pour chercher un membre de plusieurs groupes) ou des éléments isolés. Si un champ n'est pas pertinent, le mettre à '' ou undefined. + * @arg {Object} data - Dictionnaire contenant les données nécessaires à la recherche. Les valeurs sont celles entrées par l'utilisateur et sont par hypothèse + * comme des sous-parties compactes des valeurs renvoyées. Tous les champs ci-dessous peuvent être indifféremment des listes (par exempl pour chercher un membre + * de plusieurs groupes) ou des éléments isolés. Si un champ n'est pas pertinent, le mettre à '' ou undefined. * @arg {string} data[givenName] - Prénom * @arg {string} data[lastName] - Nom * @arg {string} data[nickname] - Surnom @@ -273,16 +332,16 @@ function trouverGroupesParTypes(user, input, type) { function repliquerTOLModulable(user, data, return_values) { return new Promise((resolve, reject) => { let filter=""; - // Iteration pour chaque champ, alourdissement du filtre selon des trucs prédéfini dans config encore - config.user.keys().forEach(name => { - if ((data[name]!= undefined) & (data[name] != '')) { // Si il y a qque chose à chercher pour ce filtre - if (!Array.isArray(data[name])) { data[name]=[data[name]]; } // Gestion d'une liste de valeurs à rechercher + // Iteration pour chaque champ, alourdissement du filtre selon des trucs prédéfinis dans config encore + for (var key in data) { + if ((data[key]!= undefined) & (data[key] != '')) { // Si il y a qque chose à chercher pour ce filtre + if (!Array.isArray(data[key])) { data[key]=[data[key]]; } // Gestion d'une liste de valeurs à rechercher // Iteration pour chaque valeur fournie par l'utilisateur - data[name].forEach(val => { + data[key].forEach(val => { // Escape de l'input utilisateur let str=ldapEscape.filter("${input}", { input: val}); // Traduction en language LDAP - let attribute = config.user[name]; + let attribute = config.user[key]; // Creation du filtre étape par étape filter="(&"+filter+ "(|("+attribute+"="+str+")"+ // On cherche la valeur exacte "(|("+attribute+"=*"+str+")"+ // La valeur finale avec des trucs avant ; wildcard * (MEF la wildcart ne marche pas pour tous les attributs) @@ -290,14 +349,15 @@ function repliquerTOLModulable(user, data, return_values) { "("+attribute+"="+str+"*)))))"; // La valeur du début avec des trucs après }); } - }); + } // Appel rechercheLDAP avec filtre de l'espace rechercherLDAP(user, config.dn_users, return_values, filter).then(res => resolve(res)); }); } /** - * @summary Fonction qui retrouve les uid des paxs validant les critères de recherche. Autre étape vers vrai TOL (Trombino On Line). Doit être préféré à repliquerTOL car moins gourmande envers le LDAP (utiliser {@link renseignerSurUtilisateur} au cas par cas après pour obtenir les vraies infos). + * @summary Fonction qui retrouve les uid des paxs validant les critères de recherche. Autre étape vers vrai TOL (Trombino On Line). Doit être préféré à repliquerTOL + * car moins gourmande envers le LDAP (utiliser {@link renseignerSurUtilisateur} au cas par cas après pour obtenir les vraies infos). * @desc Cette fonction utilise {@link repliquerTOLModulable}. * @arg {Object} user - Utilisateur de la forme nécessaire à {@link connecterLDAP} * @arg {Object} data - Dictionnaire contenant les données nécessaires à {@link repliquerTOLModulable} @@ -310,8 +370,10 @@ function repliquerTOLdesIds(user, data) { } /** - * @summary Fonction qui retrouve les paxs validant les critères de recherche. Bien mais vite inutilisable car demande trop au LDAP et renvoie des erreurs de type size limit. Préférer {@link repliquerTOLdesIds} puis {@link renseignerSurUtilisateur} au cas par cas. - * @desc Cette fonction utilise {@link repliquerTOLModulable} avec un filtre généré à la volée. Accepte des champs incomplets mais pas approximatifs et ne gère pas l'auto-complete. + * @summary Fonction qui retrouve les paxs validant les critères de recherche. Bien mais vite inutilisable car demande trop au LDAP et renvoie des erreurs de type size limit. + * Préférer {@link repliquerTOLdesIds} puis {@link renseignerSurUtilisateur} au cas par cas. + * @desc Cette fonction utilise {@link repliquerTOLModulable} avec un filtre généré à la volée. Accepte des champs incomplets mais pas approximatifs et ne gère pa + * l'auto-complete. * @arg {Object} user - Utilisateur de la forme nécessaire à {@link connecterLDAP} * @arg {Object} data - Dictionnaire contenant les données nécessaires à {@link repliquerTOLModulable} * @return {Promise(Object[])} Liste de dictionnaires de profils en cohérence avec l'input avec pour clés tous les attributs disponibles ou presque (voir config). @@ -328,7 +390,8 @@ function repliquerTOL(user, data) { /** * @summary Cette fonction teste une valeur d'un attribut (typiquement un identifiant) et le fait évoluer jusqu'à ce qu'il soit unique. - * @desc Librement adapté de Stack Overflow. Appelle {@link rechercherLDAP} pour vérifier qu'il n'y a pas d'autres occurences de cette valeur pour cette attribut dans le dn fourni. + * @desc Librement adapté de Stack Overflow. Appelle {@link rechercherLDAP} pour vérifier qu'il n'y a pas d'autres occurences de cette valeur pour cette attribut + * dans le dn fourni. * @param {Object} user - Utilisateur de la forme nécessaire à {@link connecterLDAP}. * @param {string} valeur - Valeur de l'attribut (le plus souvent un identifiant) à tester à cette itération * @param {string} attribut - Attribut à tester @@ -409,7 +472,6 @@ function genererIdNum(user, attribut, dn) { * @desc Appelle {@link ajouterLDAP} bien sûr, mais aussi {@link ajouterMembreGroupe} et {@link ajouterAdministrateurGroupe} pour gérer les groupes du nouvel utilisateur. * @arg {Object} user - Utilisateur de la forme nécessaire à {@link connecterLDAP}. * @arg {Object} data - Dictionnaire des informations utilisateurs. Des erreurs peuvent apparaître si tous les champs ne sont pas remplis. - * @arg {string} data[hruid] - prenom.nom ou nom.promo * @arg {string} data[givenName] - Prénom * @arg {string} data[lastName] - Nom * @arg {string} data[nickname] [] - Surnom @@ -513,7 +575,9 @@ function creerUtilisateur(user, data) { /** * @summary Fonction qui créé un nouveau groupe dans le LDAP. - * @desc Cette fonction fait une utilisation massive d'eval pour anonymiser son code ; c'est mal et cela suppose que beaucoup de soins ont été pris lors de l'escape de ses paramètres. Appelle {@link ajouterLDAP} et {@link modifierLDAP}, mais aussi {@link ajouterMembreGroupe} et {@link ajouterAdministrateurGroupe} pour gérer les groupes du nouvel utilisateur. Attention une manip FOIREUSE est cachée dedans. + * @desc Cette fonction fait une utilisation massive d'eval pour anonymiser son code ; c'est mal et cela suppose que beaucoup de soins ont été pris lors de + * l'escape de ses paramètres. Appelle {@link ajouterLDAP} et {@link modifierLDAP}, mais aussi {@link ajouterMembreGroupe} et {@link ajouterAdministrateurGroupe} + * pour gérer les groupes du nouvel utilisateur. Attention une manip FOIREUSE est cachée dedans. * @arg {Object} user - Utilisateur de la forme nécessaire à {@link connecterLDAP} * @arg {Object} data - Dictionnaire des informations utilisateurs (voir détail des champs dans config.json) * @arg {string} data[name] - Nom du groupe @@ -591,7 +655,8 @@ function creerGroupe(user, data) { /** * @summary Fonction qui permet de rajouter un membre déjà créé à un groupe. - * @desc Cette fonction fait essentiellement appel à {@link modifierLDAP} et {@link listerGroupes}. Elle n'autorise pas les doublons et opère dans les deux dns users et groups. + * @desc Cette fonction fait essentiellement appel à {@link modifierLDAP} et {@link listerGroupes}. Elle n'autorise pas les doublons et opère dans les deux dns users + * et groups. * @arg {Object} user - Utilisateur de la forme nécessaire à {@link connecterLDAP} * @arg {string} uid - Identifiant du futur membre * @arg {string} gid - Identifiant du groupe @@ -626,7 +691,8 @@ function ajouterMembreGroupe(user, uid, gid) { /** * @summary Fonction qui permet de promouvoir membre au stade d'administrateur d'un groupe. - * @desc Cette fonction fait essentiellement appel à {@link ajouterMembreGroupe} {@link modifierLDAP} et {@link listerAdministrateurs}. Elle n'autorise pas les doublons et opère dans les deux dns users et groups. + * @desc Cette fonction fait essentiellement appel à {@link ajouterMembreGroupe} {@link modifierLDAP} et {@link listerAdministrateurs}. Elle n'autorise pas + * les doublons et opère dans les deux dns users et groups. * @arg {Object} user - Utilisateur de la forme nécessaire à {@link connecterLDAP} * @arg {string} uid - Identifiant du futur membre * @arg {string} gid - Identifiant du groupe @@ -722,112 +788,45 @@ function supprimerAdministrateurGroupe(user, uid, gid) { /** * @summary Fonction qui édite un utilisateur existant dans le LDAP. Très similaire à {@link creerUtilisateur} - * @desc Appelle {@link modifierLDAP} bien sûr, mais aussi {@link ajouterMembreGroupe} et {@link ajouterAdministrateurGroupe} pour gérer les groupes du nouvel utilisateur. Peut aussi être utilisé + * @desc Appelle simplement {@link creerUtilisateur} et {@link supprimerUtilisateur}, plus {@link renseignerSurUtilisateur} pour les champs non fournis. + * Ce choix a pour conséquence que l'ordre du dictionnaire de correspondance dans ldap_config est important. * @arg {Object} user - Utilisateur de la forme nécessaire à {@link connecterLDAP} * @arg {string} uid - Utilisateur à modifier (le plus souvent le même, mais root possible) - * @arg {Object} data - Dictionnaire des informations utilisateurs au même format que pour {@link creerUtilisateur} avec tous les champs optionnels ; attention toutes les clés de cette entrée seront modifiées dans le LDAP + * @arg {Object} data - Dictionnaire des informations utilisateurs au même format que pour {@link creerUtilisateur} avec tous les champs optionnels ; + * MEF ces valeurs vont écraser les précédentes. + * attention toutes les clés de cette entrée seront modifiées dans le LDAP * @return {Promise(boolean)} `true` si la modification s'est bien déroulée, false sinon */ function editerUtilisateur(user, uid, data) { return new Promise((resolve, reject) => { - let data_keys =data.keys(); - - // Calcul d'un dictionnaire d'ajout - let vals = {}; - - // Iteration plus souple qu'à la création puisqu'on accepte que certains champs soient vides - data_keys.forEach(key => { - // Attribution directe des champs pour lesquels c'est possible - if (config.user.direct_input.includes(key)) { vals[config.user[key]]=data[key]; } - }); - - // Modifications multiples pour avoir plusieurs champs de même type ; boucle sur les attributs multiples (d'où mul) - config.user.muliple_input.forEach(key_att => { - if (data_keys.includes(key_att)) { - // On rajoute chaque valeur en entrée - data[key_att].forEach(val => { - vals[config.user[key_att]]=val; + // Récupération des anciennes données + renseignerSurUtilisateur(user, uid).then(profil => { + // Reecriture de profil avec les bons champs + Object.keys(profil).forEach(keyLDAP => { + Object.keys(config.user).forEach(keyAlias => { + config.user[keyAlias]=keyLDAP; + profil[keyAlias]=profil[keyLDAP]; }); - } - }); - - // Manipulation sur noms et prénoms (nécessité de gérer ce qui est à changer et de rechercher les valeurs courantes) - let gnb = data_keys.includes("givenName"); - let lnb = data_keys.includes("lastName"); - if (gnb | lnb) { - // Création de variables locales instantiées selon les cas par data ou rechercheLDAP - let givenName; - let lastName; - let promo; - // A ce niveau dans le code la promo a déjà été changée si elle été fausse - rechercherLDAP(user, config.key_id+"="+uid, config.user['promotion']).then(pr => { - promo=pr; - if (gnb & !lnb) { - rechercherLDAP(user, config.key_id+"="+uid, config.user['lastName']).then(ln => { - givenName = data['giveName']; - lastName = ln; - }); - } - else if (!gnb & lnb) { - rechercherLDAP(user, config.key_id+"="+uid, config.user['givenName']).then(gn => { - givenName = gn; - lastName = data['lastName']; + }); + // Régénération du champ manquant dans profil + listerGroupes(user, uid).then(lg => { + profil['groupsIsAdmin']=[]; + lg.forEach(gid =>{ + listerAdministrateurs(gid).then(la =>{ + if (la.includes(uid)) { profil['groupsIsAdmin'].push(gid); } + }).then(res => { + // Surcharge des champs à modifier selon data + Object.keys(data).forEach(key => { + profil[key]=data[key]; + }); + // Modification propre + supprimerUtilisateur(user, uid).then(r => { + creerUtilisateur(user, profil).then(res => { if (!res) {reject(false); }}); + }).then (res => { resolve(true); }); }); - } - else { - givenName = data['giveName']; - lastName = data['lastName']; - } + }); }); - // Régénération de l'uid (FOIREUX) - genererUid(user, givenName, lastName, promo).then(id => { vals[config.key_id]=id; }); - // Nom complet - vals[config.user['fullName']]=givenName+' '+lastName.toUpperCase(); - // Stockage machine ; dépend du prénom - vals[config.user['directory']] = '/hosting/users/' + givenName[0]; - // Code root - vals[config.user['cleanFullName']]=vals[config.user['fullName']].replace(':', ';').toLowerCase().normalize('UFD'); - } - - // ?! - if (data_keys.includes('password')) { vals[config.user['password']] = '{CRYPT}' + data['password']; } - - // Ecriture d'un surnom s'il y a lieu, évaluation paresseuse & - if (data_keys.includes('nickname')&(data['nickname']!=undefined)&(data['nickname']!='')) { - vals[config.user['nickname']]=data['nickname']; - } - - // Adressage root - if (data.keys().includes('groups')){ - if (data['groups'].includes("on_platal")) { vals[config.user['login']] = "/bin/bash"; } - else { vals[config.user['login']] = "/sbin/nologin"; } - } - - // Permissions BR - if (data_keys.includes('readPerm')){ - vals[config.user['readPerm']] = 'br.*,public.*'; - if (data['readPerm'].length>0) { vals[config.user['readPerm']] += ',' + data['readPerm']; } - } - - if (data_keys.includes('writePerm')){ - vals[config.user['writePerm']] = 'br.*,!br.blague-du-jour,public.*,!br.campagnekes'; - if (data['writePerm'].length>0) { vals[config.user['readPerm']] += ',' + data['writePerm']; } - } - - // Inscription des valeurs calculées - modifierLDAP(user, config.key_id+"="+uid+","+config.dn_users, "modify", vals).then(res => { - if (!res) { reject(false); } }); - - // Utilisation des fonctions adaptées pour assurer la cohérence de l'ensemble - if (data_keys.includes('groupsIsMember')){ - data['groupsIsMember'].forEach(gid => { ajouterMembreGroupe(user, vals[config.key_id], gid); }); - } - if (data_keys.includes('groupsIsAdmin')){ - data['groupsIsAdmin'].forEach(gid => { ajouterAdministrateurGroupe(user, vals[config.key_id], gid); }); - } - - resolve(true); }); } @@ -841,125 +840,74 @@ function editerUtilisateur(user, uid, data) { */ function editerGroupe(user, gid, data) { return new Promise((resolve, reject) => { - let data_keys =data.keys(); - - // Calcul d'un dictionnaire d'ajout - let vals = {}; - - // Ecriture de toutes les valeurs directement inscrites dans le LDAP - data_keys.forEach(key_att => vals[config.group[key_att]]=data[key_att]); - - if (data_keys.includes('name')) { - // uid de base généré à partir du nom standardisé - genererGid(user, data['name']).then(id => { vals[config.group['name']]=id; }); - - // Sauvegarde du nom (pour le cas où gid != data['name']) - vals[config.group["name"]]=data['name']; - - // Stockage machine ; dépend du prénom - vals[config.group['directory']] = '/hosting/groups/'+vals[config.key_id]; - - // Code root - vals[config.group['cleanFullName']]=data['name'].replace(':', ';').toLowerCase().normalize('UFD'); - } - - if (data_keys.includes('password')) { - // ?! - vals[config.user['password']] = ''; - } - - // Inscription des valeurs calculées - modifierLDAP(user, config.key_id+"="+vals[config.key_id]+","+config.dn_groups, "modify", vals).then(res => { - if (!res) { reject(false); } + // Récupération des anciennes données + renseignerSurGroupe(user, gid).then(profil => { + // Reecriture de profil avec les bons champs + Object.keys(profil).forEach(keyLDAP => { + Object.keys(config.group).forEach(keyAlias => { + config.group[keyAlias]=keyLDAP; + profil[keyAlias]=profil[keyLDAP]; + }); + }); + // Surcharge des champs à modifier selon data + Object.keys(data).forEach(key => { + profil[key]=data[key]; + }); + // Modification propre + supprimerGroupe(user, gid).then(r => { + creerGroupe(user, profil).then(res => { if (!res) { reject(false); }}); + }).then(res => { resolve(true); }); }); - - // Utilisation des fonctions adaptées pour assurer la cohérence de l'ensemble - data['members'].forEach(uid => { ajouterMembreGroupe(user, uid, vals[config.key_att]); }); - data['admins'].forEach(uid => { ajouterAdministrateurGroupe(user, uid, vals[config.key_att]); }); - - resolve(true); }); } //------------------------------------------------------------------------------------------------------------------------ -// Fonctions de suppression +// Fonctions de suppression TBT //------------------------------------------------------------------------------------------------------------------------ -//TBC /** * @summary Fonction qui supprime un utilisateur du LDAP. - * @desc Cette fonction fait une utilisation massive d'eval pour anonymiser son code ; - * voir ldap_config.json pour le détail de la génération des champs finaux. - * Appelle {@link ajouterLDAP} bien sûr, mais aussi {@link ajouterMembreGroupe} et {@link ajouterAdministrateurGroupe} pour gérer les groupes du nouvel utilisateur. + * @desc Cette fonction commence par gérer les groupes du membre puis le supprime entièrement. + * Appelle {@link supprimerLDAP} bien sûr, mais aussi {@link supprimerMembreGroupe} et {@link supprimerAdministrateurGroupe} pour gérer les groupes de l'utilisateur sortant. * @arg {Object} user - Utilisateur de la forme nécessaire à {@link connecterLDAP} * @arg {string} uid - uid de la victime * @return {Promise(boolean)} `true` si la modification s'est bien déroulée, false sinon */ -function supprimerUtilisateur(user, data) { - return new Promise(function(resolve, reject) { - // Calcul d'un dictionnaire d'ajout - let vals = {}; - // EVAL DIRECTE D'UN STRING UTILISATEUR ! FAILLE DE SECURITE MAJEURE ! - config.cu.single_user_infos.forEach(key_att => vals[key_att]=eval(config.cu.expr_single_values_user[key_att])); - // Appel à la fonction de base - ajouterLDAP(user, ldapEscape.filter(config.key_id+"=${id},"+config.dn_users, { id: data["uid"]}), vals).then( res => { - // Modifications multiples pour avoir plusieurs champs de même type ; boucle sur les attributs multiples - config.cu.multiple_user_infos.forEach(key_att => { - // Evaluation des expressions des liste de valeurs des attributs - // EVAL DIRECTE D'UN STRING UTILISATEUR ! FAILLE DE SECURITE MAJEURE ! - eval(config.cu.expr_multiple_values_user[key_att]).forEach(val => { - // Nouvel ajout attribut par attribut pour démultiplier les champs - modifierLDAP(user, ldapEscape.filter(config.key_id+"=${id},"+config.dn_users, { id: data["uid"]}), "add", { key_att: val }); - }); - }); - // Utilisation des fonctions adaptées pour assurer la cohérence de l'ensemble - data['groupsIsMember'].forEach(gid => { ajouterMembreGroupe(user, data['uid'], gid); }); - data['groupsIsAdmin'].forEach(gid => { ajouterAdministrateurGroupe(user, data['uid'], gid); +function supprimerUtilisateur(user, uid) { + return new Promise((resolve, reject) => { + // Gestion des groupes d'abord + renseignerSurUtilisateur(user, uid).then(profil => { + profil[config.user['groups']].forEach(gid => { + listerAdministrateurs(user, gid).then(la => { + if (la.includes(uid)) { supprimerAdministrateurGroupe(user, uid, gid); } + }).then(res => { supprimerMembreGroupe(user, uid, gid); }); }); - resolve(true); - }); - reject(false); + // Elimination + }).then(res => { supprimerLDAP(user, config.key_id+"="+uid+","+config.dn_users); }); }); } -//TBC /** - * @summary Fonction qui supprime un groupe dans le LDAP. - * @desc Cette fonction fait une utilisation massive d'eval pour anonymiser son code ; c'est mal et cela - * suppose que beaucoup de soins ont été pris lors de l'escape de ses paramètres. Appelle {@link ajouterLDAP} - * et {@link modifierLDAP}, mais aussi {@link ajouterMembreGroupe} et - * {@link ajouterAdministrateurGroupe} pour gérer les groupes du nouvel utilisateur. + * @summary Fonction qui supprime un groupe du LDAP. + * @desc Cette fonction commence par gérer les groupes du membre puis le supprime entièrement. + * Appelle {@link supprimerLDAP} bien sûr, mais aussi {@link supprimerMembreGroupe} et {@link supprimerAdministrateurGroupe} pour gérer les groupes de l'utilisateur sortant. * @arg {Object} user - Utilisateur de la forme nécessaire à {@link connecterLDAP} - * @arg {string} gid - Identifiant du groupe à supprimer + * @arg {string} gid - gid de la victime * @return {Promise(boolean)} `true` si la modification s'est bien déroulée, false sinon */ -function supprimerGroupe(user, data) { +function supprimerGroupe(user, gid) { return new Promise(function(resolve, reject) { - // Calcul d'un dictionnaire d'ajout - let vals = {}; - // EVAL DIRECTE D'UN STRING UTILISATEUR ! FAILLE DE SECURITE MAJEURE ! - config.cru.single_user_infos.forEach(key_att => vals[key_att]=eval(config.cru.expr_single_values_user[key_att])); - // Appel à la fonction de base - ajouterLDAP(user, ldapEscape.filter(config.key_id+"=${id},"+config.dn_users, {id: data['uid']}), vals).then( res => { - // Modifications multiples pour avoir plusieurs champs de même type - config.cru.multiple_user_infos.forEach(key_att => { - // EVAL DIRECTE D'UN STRING UTILISATEUR ! FAILLE DE SECURITE MAJEURE ! - eval(config.cru.expr_multiple_values_user[key_att]).forEach(key_val => { - // Nouveau dictionnaire d'ajout - let vals2 = { key_att: key_val }; - modifierLDAP(user, ldapEscape.filter(config.key_id+"=${id},"+config.dn_users, {id: data['uid']}), "add", vals2); - }); - }); - // Utilisation des fonctions adaptées pour assurer la cohérence de l'ensemble - data["members"].forEach(gid => { ajouterMembreGroupe(user, data['uid'], gid); }); - data["admins"].forEach(gid => { ajouterAdministrateurGroupe(user, data['uid'], gid); }); - resolve(true); - }); - reject(false); + // Gestion des membres et administrateurs d'abord + renseignerSurGroupe(user, gid).then(profil => { + profil[config.group['admin']].forEach(id => { supprimerAdministrateurGroupe(user, id, gid); }); + profil[config.group['member']].forEach(id => { supprimerMembreGroupe(user, id, gid); }); + // Elimination + }).then(res => { + supprimerLDAP(user, config.key_id+"="+gid+","+config.dn_groups); + }).then(res => { if (res) {resolve(true); }}); }); } - //trouverGroupesParTypes({},"fe","binet").then(res => console.log(res)); /* Partage pour le reste du monde ; même remarque synthaxe que pour l'import