/** * @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 config.json qui détaille les paxs et champs spécifique à un LDAP. Il est fortement conseillé d'utiliser [`JXplorer`](http://jxplorer.org/) pour baviguer sur le LDAP en parallèle du dévelopement. * @author hawkspar */ /* Ne pas hésiter à repasser en synthaxe ES5... Plus simple pour tester en solo avec node directement import ldap from 'ldapjs'; import fs from 'fs'; import ldapEscape from 'ldap-escape'; import path from 'path'; */ /**/var ldap = require('ldapjs'); var fs = require('fs'); var ldapEscape = require('ldap-escape'); var path = require('path'); // Important ; permet de vérifier que l'utilisateur reste connecté. var ensureLoggedin = require('connect-ensure-login').ensureLoggedIn; // Point central ; tous les champs de la BDD sont 'cachés' dans config.json et pas visibles directement var configPath = path.resolve('./','ldap_config.json'); var config = JSON.parse(fs.readFileSync(configPath, 'utf8')); // Idem pour les ids de connexion temporaires var connectPath = path.resolve('./','ldap_connexion_config.json'); var connect = JSON.parse(fs.readFileSync(connectPath, 'utf8')); // Connection au serveur LDAP avec des temps de timeout arbitraires var client = ldap.createClient({ url: config.ldap.server}); //------------------------------------------------------------------------------------------------------------------------ // Fonctions de base //------------------------------------------------------------------------------------------------------------------------ /** * @summary Fonction qui sert à s'identifier sur le LDAP. Renvoit rien. * @desc Assez important en terme de sécurité, de gestion de conflit, et de droit d'accès. Méthode ldapjs (voir [`Client API`](http://ldapjs.org/client.html) méthode bind). * @arg {Object} user - Utilisateur de la forme nécessaire au bind * @arg {string} user[uid] - User identifier * @arg {string} user[password] - Mot de passe */ function connecterLDAP(user) { // TBM utiliser 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 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 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 au bind * @arg {string} base - DN de l'emplacement de la requête * @arg {string} filter - Filtre logique de la recherche (format [`RFC2254`](https://tools.ietf.org/search/rfc2254)) déjà passé au ldapEscape * @arg {string} filter_dic[key] - Vraie valeur pertinente de la recherche * @arg {string[]} attributes - Liste des attributs qui figureront dans le résultat final * @return {Promise(Object[])} Résultats de la recherche ; soit une liste de valeurs d'attributs, soit une liste de dictionnaires si on veut plus d'un attribut (les clés du dictionnaire sont celles du LDAP) */ function rechercherLDAP(user, base, filter, attributes) { return new Promise(function(resolve, reject) { connecterLDAP(user); //Debug /*console.log(base); console.log(filter); console.log(attributes);*/ let vals=[]; // Interrogation LDAP selon configuration fournie en argument client.search(base, { "scope": "sub", "filter": filter, "attributes": attributes }, function(err, res) { // Gestion erreur if (err) { reject(err); } else { // Dès que la recherche renvoit une entrée, on stocke les attributs qui nous intéresse res.on('searchEntry', function(entry) { // Cas un seul attribut où le résultat est une liste 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 est finie on se déconnecte et on renvoit la liste res.on('end', function(res) { client.bind("", "", (err, res) => {}); resolve(vals); }); } }); }); } /** * @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). * @arg {Object} user - Utilisateur de la forme nécessaire au bind * @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. * @return {Promise(boolean)} `true` si la modification s'est bien déroulée, false sinon */ function modifierLDAP(user, name, op, mod) { return new Promise(function(resolve, reject) { connecterLDAP(user); // Modification LDAP selon configuration en argument (pourrait prendre une liste de Changes) client.modify(name, new ldap.Change({ operation: op, modification: mod, }), function(err) { reject(err); }); client.bind("", "", (err, res) => {}); resolve(true); }); } /** * @summary Fonction qui permet de rajouter un élément sur le LDAP. * @desc Cette fonction appelle {@link bind} 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). * @arg {Object} user - Utilisateur de la forme nécessaire au bind * @arg {string} dn - Adresse du parent * @arg {Object} vals - Dictionnaire contenant les valeurs à créer * @arg {string} vals[key] - Nouvelle valeur pour le champ key * @return {Promise(boolean)} `true` si la modification s'est bien déroulée, false sinon */ function ajouterLDAP(user, dn, vals) { return new Promise(function(resolve, reject) { connecterLDAP(user); // Ajout LDAP selon la configuration en argument client.add(config.key_id+"="+vals["uid"]+","+dn, vals, function(err) { reject(err); }); client.bind("", "", (err, res) => {}); resolve(true); }); } //------------------------------------------------------------------------------------------------------------------------ // Fonctions de recherche //------------------------------------------------------------------------------------------------------------------------ /** * @summary Fonction qui interroge le LDAP et retrouve les groupes dont un individu est membre. * @desc Cette fonction utilise {@link rechercherLDAP} avec un dictionnaire prédéfini dans config.json. Il faut l'appeler selon un schéma `listGroups(...).then((res) => { truc avec res });`. Elle utilise LDAPEscape pour éviter les injections. * @arg {Object} user - Utilisateur de la forme nécessaire au bind * @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 */ function listerGroupes(user, uid) { return new Promise(function(resolve, reject) { rechercherLDAP(user,config.dn_users, ldapEscape.filter(config.filter_id, { id : uid }), config.attributes_lg).then(res => resolve(res[0])); }); } /** * @summary Fonction qui interroge le LDAP et retrouve les groupes dont un individu est membre. Assez différente de la précédente en terme d'implémentation, plus lente mais renvoir moins de résultats plus pertinents, probablement plus pérenne. * @desc Cette fonction utilise {@link rechercherLDAP} avec un dictionnaire prédéfini dans config.json. Il faut l'appeler selon un schéma `listGroups2(...).then((res) => { truc avec res });`. Elle utilise LDAPEscape pour éviter les injections. * @arg {Object} user - Utilisateur de la forme nécessaire au bind * @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 */ function listerGroupes2(user, uid) { return new Promise(function(resolve, reject) { rechercherLDAP(user, config.dn_groups, ldapEscape.filter(config.filter_lg, { id : uid }), config.attributes_id).then(res => resolve(res)); }); } /** * @summary Fonction qui interroge le LDAP et retrouve la liste des membres d'un groupe. * @desc Cette fonction utilise {@link rechercherLDAP} avec un dictionnaire prédéfini dans config.json. Il faut l'appeler selon un schéma `listMembers(...).then((res) => { truc avec res });`. Elle utilise LDAPEscape pour éviter les injections. * @arg {Object} user - Utilisateur de la forme nécessaire au bind * @arg {string} user[key] - uid et password * @arg {string} gid - Identifiant du groupe à interroger (le plus souvent nom du groupe en minuscule) * @return {Promise(string[])} Liste des uid des membres où l'id fournie est membre (noms flat des groupes) */ function listerMembres(user, gid) { return new Promise(function(resolve, reject) { rechercherLDAP(user, config.key_id+"="+gid+","+config.dn_groups, ldapEscape.filter(config.filter_id, { id: gid }), config.attributes_lm).then(res => resolve(res[0])); }); } /** * @summary Fonction qui interroge le LDAP et retrouve la liste des admins d'un groupe. * @desc Cette fonction utilise {@link rechercherLDAP} avec un dictionnaire prédéfini dans config.json. Il faut l'appeler selon un schéma `listAdmins(...).then((res) => { truc avec res });`. Elle utilise LDAPEscape pour éviter les injections. * @arg {Object} user - Utilisateur de la forme nécessaire au bind * @arg {string} user[key] - uid et password * @arg {string} gid - Identifiant du groupe à interroger (le plus souvent nom du groupe en minuscule) * @return {Promise(string[])} Liste des uid des membres où l'id fournie est membre (noms flat des groupes) */ function listerAdministrateurs(user, gid) { return new Promise(function(resolve, reject) { rechercherLDAP(user, config.key_id+"="+gid+","+config.dn_groups, ldapEscape.filter(config.filter_id, { id: gid }), config.attributes_al).then(res => resolve(res[0])); }); } /** * @summary Fonction qui interroge le LDAP au sujet d'un uid particulier et qui renvoit toutes ses infos. * @desc Cette fonction utilise {@link rechercherLDAP} avec un dictionnaire prédéfini dans config.json. Il faut l'appeler selon un schéma `rens(...).then((res) => { truc avec res });`. Elle utilise LDAPEscape pour éviter les injections. * @arg {Object} user - Utilisateur de la forme nécessaire au bind * @arg {string} uid - Identifiant de l'utilisateur * @return {Promise(Object[])} Informations recueillies ; renvoie une liste de dictionnaire avec les mêmes clés que dans le TOL, voir config.json. */ function renseignerSurUtilisateur(user, uid) { return new Promise(function(resolve, reject) { rechercherLDAP(user, config.dn_users, ldapEscape.filter(config.filter_id, { id: uid }), config.attributes_all).then(res => resolve(res)); }); } /** * @summary Fonction qui interroge le LDAP et retrouve les groupes (voir LDAP) qui ressemblent à l'input. 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 une Promise pour être asynchrone ; elle renvoit la promesse d'une réponse puis traite la demande. Il faut l'appeler suivant un schéma `trouverGroupe(user, input).then((res) => { truc avec res });`. 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 au bind * @arg {string} user[key] - uid et password * @arg {string} input - String entré par l'utilisateur. * @return {Promise(string[])} Liste des uid de groupes dont le nom ressemble à l'input */ function trouverGroupes(user, input) { return new Promise(function(resolve, reject) { // Escape de l'input utilisateur par sécurité let str=ldapEscape.filter("${txt}", { txt: input}); // Construction du filtre custom let filter= "(|("+config.key_id+"="+str+")"+ // On cherche la valeur exacte "(|("+config.key_id+"=*"+str+")"+ // La valeur finale avec des trucs avant ; wildcard * "(|("+config.key_id+"=*"+str+"*)"+ // La valeur du milieu avec des trucs avant et après "("+config.key_id+"="+str+"*))))"; // La valeur du début avec des trucs après // Appel rechercheLDAP avec filtre de l'espace rechercherLDAP(user, config.dn_groups, filter, config.attributes_id).then(res => resolve(res)); }); } /** * @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 une Promise pour être asynchrone ; elle renvoit la promesse d'une réponse puis traite la demande. Il faut l'appeler suivant un schéma `trouverGroupe(user, input).then((res) => { truc avec res });`. 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 au bind * @arg {string} user[key] - uid et password * @arg {string} input - String entré par l'utilisateur. * @arg {string} input - String entré par l'utilisateur. * @return {Promise(string[])} Liste des uid de groupes dont le nom ressemble à l'input */ function trouverGroupesParTypes(user, input, typer) { return new Promise(function(resolve, reject) { // Escape de l'input utilisateur par sécurité let str=ldapEscape.filter("${txt}", { txt: input}); // Construction du filtre custom let filter= "(|("+config.key_id+"="+str+")"+ // On cherche la valeur exacte "(|("+config.key_id+"=*"+str+")"+ // La valeur finale avec des trucs avant ; wildcard * "(|("+config.key_id+"=*"+str+"*)"+ // La valeur du milieu avec des trucs avant et après "("+config.key_id+"="+str+"*))))"; // La valeur du début avec des trucs après // Appel rechercheLDAP avec filtre de l'espace rechercherLDAP(user, config.dn_groups, filter, config.attributes_id).then(res => resolve(res)); }); } /** * @summary Fonction qui interroge le LDAP et retrouve certains attributs 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. Cette fonction utilise une Promise pour être asynchrone ; elle renvoit la promesse d'une réponse puis traite la demande. Il faut l'appeler suivant un schéma `TOL(uid).then((res) => { truc avec res });`.Cette fonction fait une demande au LDAP qu'elle filtre selon un schéma généré à la volée à partir de config.json et à chaque résultat (event SearchEntry) le met dans une liste, et renvoit la liste à l'issue (event end). MEF Timeout pour des recherches trop vagues. Renvoit aussi une liste de dictionnaire. Elle utilise LDAPEscape pour éviter les injections. * @arg {Object} user - Utilisateur de la forme nécessaire au bind * @arg {string} user[key] - uid et password * @arg {Object} data - Dictionnaire contenant les données nécessaires à la recherche avec des champs lisibles (voir config). * @arg {string} data[key] - La plupart des champs sont directement des string. * @arg {string[]} data[key] - Mais certains des champs peuvent être des listes, permettant une recherche sur plusieurs valeurs du même critère. * @arg {string[]} return_values - Liste d'attributs à renvoyer * @return {string[]} Attributs des profils qui "match" les critères proposés. */ function repliquerTOLModulable(user, data, return_values) { return new Promise(function(resolve, reject) { let filter=""; // Iteration pour chaque champ, alourdissement du filtre selon des trucs prédéfini dans config.json encore config.search_attributes_tol.forEach((attribute, index) => { let input_name=config.input_search_names[index]; if (data[input_name]!= undefined & data[input_name] != '') { // Si il y a qque chose à chercher pour ce filtre if (!Array.isArray(data[input_name])) { data[input_name]=[data[input_name]]; } // Gestion d'une liste de valeurs à rechercher // Iteration pour chaque valeur fournie par l'utilisateur data[input_name].forEach(val => { // Escape de l'input utilisateur let str=ldapEscape.filter("${input}", { input: val}); 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) "(|("+attribute+"=*"+str+"*)"+ // La valeur du milieu avec des trucs avant et après "("+attribute+"="+str+"*)))))"; // La valeur du début avec des trucs après }); } }); // Appel rechercheLDAP avec filtre de l'espace rechercherLDAP(user, config.dn_users, filter, config.attributes_id).then(res => resolve(res)); }); } /** * @summary Fonction qui interroge le LDAP et retrouve les uid des paxs validant les critères de recherche. Première étape vers vrai TOL (Trombino On Line). * @desc Cette fonction utilise {@link repliquerTOLModulable} 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. Cette fonction utilise une Promise pour être asynchrone ; elle renvoit la promesse d'une réponse puis traite la demande. Il faut l'appeler suivant un schéma `TOL(uid).then((res) => { truc avec res });`.Cette fonction fait une demande au LDAP qu'elle filtre selon un schéma généré à la volée à partir de config.json et à chaque résultat (event SearchEntry) le met dans une liste, et renvoit la liste à l'issue (event end). MEF Timeout pour des recherches trop vagues. Renvoit aussi une liste de dictionnaire. Elle utilise LDAPEscape pour éviter les injections. * @arg {Object} user - Utilisateur de la forme nécessaire au bind * @arg {string} user[key] - uid et password * @arg {Object} data - Dictionnaire contenant les données nécessaires à la recherche avec des champs lisibles (voir config). * @arg {string} data[key] - La plupart des champs sont directement des string. * @arg {string[]} data[key] - Mais certains des champs peuvent être des listes, permettant une recherche sur plusieurs valeurs du même critère. * @arg {string[]} return_values - Liste d'attributs à renvoyer * @return {string[]} uids des profils qui "match" les critères proposés. */ function repliquerTOLdesIds(user, data) { return new Promise( function(resolve, reject) { repliquerTOLModulable(user, data, config.attributes_id).then(res => resolve(res)); }); } /** * @summary Fonction qui interroge le LDAP et 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 idTOL puis stalk 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. Cette fonction utilise une Promise pour être asynchrone ; elle renvoit la promesse d'une réponse puis traite la demande. Il faut l'appeler suivant un schéma `TOL(uid).then((res) => { truc avec res });`. Aucun bind n'est nécessaire donc pas d'identifiant ou de mot de passe à passer. Cette fonction fait une demande au LDAP qu'elle filtre selon un schéma généré à la volée à partir de config.json et à chaque résultat (event SearchEntry) le met dans une liste, et renvoit la liste à l'issue (event end). MEF Timeout pour des recherches trop vagues. Renvoit aussi une liste de dictionnaire. Utilise aussi requeteLDAP. Elle utilise LDAPEscape pour éviter les injections. * @arg {Object} user - Utilisateur de la forme nécessaire au bind * @arg {string} user[key] - uid et password * @arg {string} c0 - Prénom * @arg {string} c1 - Nom * @arg {string} c2 - Surnom * @arg {string} c3 - Nationalité * @arg {string} c4 - Ecole ou université d'origine * @arg {string} c5 - Promotion * @arg {string} c6 - Groupe * @arg {string} c7 - Cours * @arg {string} c8 - Sport pratiqué * @arg {string} c9 - Numéro de téléphone * @arg {string} c10 - Adresse courriel * @arg {string} c11 - Adresse physique * @arg {string} c12 - Adresse ip * @return {string[]} Informations recueillies ; voir config.json. */ function repliquerTOL(user, data) { return new Promise( function(resolve, reject) { repliquerTOLModulable(user, data, config.attributes_all).then(res => resolve(res)); }); } //------------------------------------------------------------------------------------------------------------------------ // Fonctions créatrices //------------------------------------------------------------------------------------------------------------------------ /** * @summary TBC Fonction qui va plonger dans le LDAP et modifier un certain jeu de valeur en argument. * @desc 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 search). 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). * @arg {Object} user - Utilisateur de la forme nécessaire au bind * @arg {Object} data - Dictionnaire des informations utilisateurs (voir détail des champs dans config.json) * @return {boolean} `true` si la modification s'est bien déroulée, false sinon */ function creerUtilisateurLDAP(user, data) { return new Promise(function(resolve, reject) { let data = {}; reject(false); }); } /** * @summary TBC Fonction qui va plonger dans le LDAP et modifier un certain jeu de valeur en argument. * @desc 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 search). 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). * @arg {Object} user - Utilisateur de la forme nécessaire au bind * @arg {string} gid - Identifiant du group * @arg {string} nom - Nom commun du group * @arg {string} status - Statut du groupe (binet, autre ?) * @arg {string[]} admins - Admins du groupe * @arg {string[]} members - Nouvelle valeur des attributs * @return {boolean} `true` si la modification s'est bien déroulée, false sinon */ function creerGroupeLDAP(user, gid, nom, status, admins, members) { return new Promise(function(resolve, reject) { let data = {}; data["uid"] = gid; data["cn"] = nom; data["brNS"] = status; data[""]; reject(false); }); } //TOL({},{"groups": ["br","faerix"]}).then(res => console.log(res)); //rechercherLDAP({},"ou=groups,dc=frankiz,dc=net","(cn=f*)",["uid"]).then(res => console.log(res)); //listeAdmins({},"zqfnsflswndf").then(res => console.log(res)); /* Partage pour le reste du monde ; même remarque synthaxe que pour l'import export { listeGroupes, listeMembres, listeAdministrateurs, renseignementsSurUtilisateur, trouverGroupes, repliquerTOLdesIds, repliquerTOL }; */ /**/module.exports ={ listerGroupes, listerMembres, listerAdministrateurs, renseignerSurUtilisateur, trouverGroupes, repliquerTOLdesIds, repliquerTOL };