diff --git a/ldap_config.json b/ldap_config.json
index 1e20d240cd3d988ff22b7aae9c35a19ab8f27383..80036ed11cdb513169810c8bbe37986a464bc180 100644
--- a/ldap_config.json
+++ b/ldap_config.json
@@ -1,34 +1,32 @@
 {
+	"COMMENT1": "Partie utilisée par ldap_auth",
 	"ldap": {
 		"server": "ldap://frankiz.eleves.polytechnique.fr:389",
 		"searchBase": "ou=eleves,dc=frankiz,dc=net",
 		"searchFilter": "(uid={{username}})"
 	},
+	
+	"COMMENT2": "Noms de domaines dans LDAP ; le niv d'après est en uid=, voir Wikipedia",
 	"dn_groups":"ou=groups,dc=frankiz,dc=net",
 	"dn_users": "ou=eleves,dc=frankiz,dc=net",
+	
+	"COMMENT3": "Anonymisation du champ id",
 	"key_id": "uid",
-	"lg": {
-		"filtre": "(uid=${id})",
-		"attributs": "brMemberOf"
-	},
-	"lm": {
-		"filtre": "(uid=${id})",
-		"attributs": "restrictedMemberUid"
-	},
-	"la": {
-		"filtre": "(uid=${id})",
-		"attributs": "memberUid"
-	},
+
+	"COMMENT4": "Anonymisation des attributs retournés par les différentes fonctions listerGroupes (lg), listerMembres (lm), etc...",
+	"lg": { "attributs": "brMemberOf" },
+	"lm": { "attributs": "restrictedMemberUid" },
+	"la": { "attributs": "memberUid" },
 	"rs": {
-		"filtre": "(uid=${id})",
 		"attributs": ["jpegPhoto","givenName", "sn", "brBirthdate", "brPromo","telephoneNumber","mail","brRoom","brIP","brMemberOf"]
 	},
 	"tgty": {
 		"filtre": "(brNS=${ty})",
 		"types": ["binet", "free"]
 	},
+	
 	"tolm": {
-		"input_names": ["givenName", "lastName", "nickname", "nationality", "promotion", "phone", "adress", "ip", "school", "groups","studies","sport","mail"],
+		"input_names": ["givenName", "lastName", "nickname", "nationality", "promotion", "phone", "adress", "ip", "school", "groups", "studies", "sport", "mail"],
 		"correspondance": {
 			"givenName": "givenName",
 			"lastName": "sn",
@@ -45,35 +43,60 @@
 	"tol": {
 		"attributes": ["jpegPhoto","givenName", "sn", "brBirthdate", "brPromo","telephoneNumber","mail","brRoom","brIP","brMemberOf"]
 	},
-	"cru": {
+
+	"cu": {
+		"COMMENT7": "Le détail des calculs des différents champs est ci-dessous",
 		"single_user_infos": ["uid","givenName","sn","displayName", "brBirthdate", "uidNumber","gidNumber", "homeDirectory", "userPassword","brPromo","brMemberOf","loginShell","email","telephoneNumber","jpegPhoto","brRoom","brNewsReadAccess","brNewsPostAccess","brAlias","brIP","cn","gecos"],
 		"expr_single_values_user": {
-			"uid": "data['hruid'];",
-			"givenName": "data['first_name'];",
-			"sn": "data['last_name'];",
-			"displayName": "data['nickname'];",
-			"brBirthdate": "data['birthdate'];",
-			"uidNumber": "(2*data['uid']+10000).toString();",
-			"gidNumber": "5000.toString();",
-			"homeDirectory": "'/hosting/users/' + data['hruid'];",
-			"userPassword": "data['password'];",
-			"brPromo": "data['promo'];",
-			"loginShell": "if (data['on_platal']==true) {'/bin/bash'; } else { '/sbin/nologin'; }",
-			"email": "data['email'];",
-			"telephoneNumber":"data['phone'];",
-			"jpegPhoto": "data['photo'];",
-			"brRoom": "data['room'];",
-			"brNewsReadAccess": "if (data['read_perm'].length>0) { 'br.*,public.*'; } else { 'br.*,public.*,'+data['read_perm']; }",
-			"brNewsPostAccess": "if (data['write_perm'].length>0) { 'br.*,!br.blague-du-jour,public.*,!br.campagnekes'; } else { 'br.*,!br.blague-du-jour,public.*,!br.campagnekes,'+data['read_perm']; }",
-			"brAlias": "data['forlifes'].split(' ');",
-			"brIP": "data['ips'].split(',');",
-			"cn": "data['first_name']+' '+data['last_name'].toUpperCase();",
-			"gecos": "btoa(data['first_name']+' '+data['last_name'].toUpperCase());"
+			"uid": "data['hruid']",
+			"givenName": "data['givenName']",
+			"sn": "data['lastName']",
+			"displayName": "data['nickname']",
+			"brBirthdate": "data['birthdate']",
+			"uidNumber": "(2*data['uid']+10000).toString()",
+			"gidNumber": "5000.toString()",
+			"homeDirectory": "'/hosting/users/' + data['hruid']",
+			"userPassword": "data['password']",
+			"brPromo": "data['promo']",
+			"loginShell": "if (data['onPlatal']==1) {'/bin/bash' } else { '/sbin/nologin' }",
+			"email": "data['email']",
+			"telephoneNumber":"data['phone']",
+			"jpegPhoto": "data['photo']",
+			"brRoom": "data['room']",
+			"brNewsReadAccess": "if (data['readPerm'].length>0) { 'br.*,public.*' } else { 'br.*,public.*,'+data['read_perm'] }",
+			"brNewsPostAccess": "if (data['writePerm'].length>0) { 'br.*,!br.blague-du-jour,public.*,!br.campagnekes' } else { 'br.*,!br.blague-du-jour,public.*,!br.campagnekes,'+data['read_perm'] }",
+			"brIP": "data['ips'].split(',')",
+			"cn": "data['givenName']+' '+data['sn'].toUpperCase()",
+			"gecos": "btoa(data['givenName']+' '+data['sn'].toUpperCase())"
 		},
-		"multiple_user_infos": ["objectClass", "brMemberOf"],
+		"multiple_user_infos": ["objectClass","brIP","brAlias"],
 		"expr_multiple_values_user": {
 			"objectClass": "['posixAccount', 'shadowAccount', 'inetOrgPerson', 'brAccount']",
-			"brMemberOf": "data['groups']"
+			"brIP": "data['ips']",
+			"brAlias": "data['forlifes']"
+		}
+	},
+	
+	"cg": {
+		"COMMENT10": "Détail des champs d'un groupe",
+		"single_user_infos": ["name","ns","gid","label"],
+		"expr_single_values_user": {
+			"uid": "btoa(data['name'].toLowerCase())",
+			"brAlias": "data['name']",
+			"brNS": "data['ns']",
+			"uidNumber": "(2*data['gid']+10001).toString()",
+			"gidNumber": "(2*data['gid']+10001).toString()",
+			"userPassword": "",
+			"loginShell": "/sbin/nologin",
+			"cn": "if (data['label']!= '') { data['label'] } else { data['name'] }",
+			"homeDirectory": "'/hosting/groups/'+btoa(data['name'].toLowerCase())",
+			"gecos": "btoa(data['name'].toLowerCase())",
+			"brNewsReadAccess": "!*",
+			"brNewsPostAccess": "!*"
+		},
+		"multiple_user_infos": ["objectClass"],
+		"expr_multiple_values_user": {
+			"objectClass": "[ 'posixGroup','posixAccount','brAccount' ]"
 		}
 	},
 	"sessionSecret":"change this"
diff --git a/src/ldap/ldap_data.js b/src/ldap/ldap_data.js
index b33dff8510264e64471c5e0dc1923422f90549d9..3abfb85bfd61564686f1451ef00714daeb3eef69 100644
--- a/src/ldap/ldap_data.js
+++ b/src/ldap/ldap_data.js
@@ -1,5 +1,5 @@
 /**
- * @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.
+ * @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. Ce fichier permet en outre de changer facilement les paramètres d'une fonction et de mieux comprendre les formats attendus en entrée. 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
  */
 
@@ -45,15 +45,15 @@ function connecterLDAP(user) {
 
 /**
  * @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).
+ * @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 - Filtre logique de la recherche (format [`RFC2254`](https://tools.ietf.org/search/rfc2254)) déjà passé au ldapEscape
+ * @arg {string} filter ["(objectClass=*)"] - 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) {
+function rechercherLDAP(user, base, attributes, filter="(objectClass=*)") {
     return new Promise(function(resolve, reject) {
         connecterLDAP(user);
         //Debug
@@ -145,54 +145,54 @@ function ajouterLDAP(user, dn, vals) {
 //------------------------------------------------------------------------------------------------------------------------
 
 /**
- * @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, entrée lg. Il faut l'appeler selon un schéma `listGroups(...).then((res) => { truc avec res });`. Elle utilise LDAPEscape pour éviter les injections.
+ * @summary Fonction qui retrouve les groupes dont un individu est membre.
+ * @desc Cette fonction utilise {@link rechercherLDAP} avec un dictionnaire prédéfini dans config.json, entrée lg. Elle va directement à la bonne feuille 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
  */
 function listerGroupes(user, uid) {
     return new Promise(function(resolve, reject) {
-        rechercherLDAP(user,config.key_id+"="+uid+","+config.dn_users, ldapEscape.filter(config.lg.filtre, { id : uid }), config.lg.attributs).then(res => resolve(res[0]));
+        rechercherLDAP(user,ldapEscape.filter(config.key_id+"=${id},"+config.dn_users, {id : uid}), config.lg.attributs).then(res => resolve(res[0]));
     });
 }
 
 /**
- * @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.
+ * @summary Fonction qui retrouve la liste des membres d'un groupe.
+ * @desc Cette fonction utilise {@link rechercherLDAP} avec un dictionnaire prédéfini dans config.json. Elle utilise LDAPEscape pour éviter les injections.
  * @arg {Object} user - Utilisateur de la forme nécessaire à {@link connecterLDAP}
  * @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.lm.filtre, { id: gid }), config.lm.attributs).then(res => resolve(res[0]));
+        rechercherLDAP(user,ldapEscape.filter(config.key_id+"=${id},"+config.dn_users, {id : gid}), config.lm.attributs).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.
+ * @summary Fonction qui retrouve la liste des admins d'un groupe.
+ * @desc Cette fonction utilise {@link rechercherLDAP} avec un dictionnaire prédéfini dans config.json. Elle utilise LDAPEscape pour éviter les injections.
  * @arg {Object} user - Utilisateur de la forme nécessaire à {@link connecterLDAP}
  * @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.la.filtre, { id: gid }), config.la.attributs).then(res => resolve(res[0]));
+        rechercherLDAP(user,ldapEscape.filter(config.key_id+"=${id},"+config.dn_users, {id : gid}), config.la.attributs).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.
+ * @summary Fonction qui renvoit toutes les infos relatives à un hruid particulier.
+ * @desc Cette fonction utilise {@link rechercherLDAP} avec un dictionnaire prédéfini dans config.json. 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 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.key_id+"="+uid+","+config.dn_users, ldapEscape.filter(config.rs.filtre, { id: uid }), config.rs.attributs).then(res => resolve(res));
+        rechercherLDAP(user,ldapEscape.filter(config.key_id+"=${id},"+config.dn_users, {id : uid}), config.rs.attributs).then(res => resolve(res));
     });
 }
 
@@ -202,7 +202,7 @@ function renseignerSurUtilisateur(user, uid) {
 
 /**
  * @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.
+ * @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
@@ -219,13 +219,13 @@ function trouverGroupes(user, input) {
                     "("+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.key_id).then(res => resolve(res));
+        rechercherLDAP(user, config.dn_groups, config.key_id, filter).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.
+ * @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.
@@ -238,7 +238,7 @@ function trouverGroupesParTypes(user, input, type) {
             let gidtyList = [];
             gidList.forEach(gid => {
                 // Nouvelle recherche à partir des résultats de la première
-                rechercherLDAP(config.key_id+"="+gid+","+config.dn_groups, ldapEscape.filter(config.tgty.filter, { ty: type }), config.key_id).then(res => {
+                rechercherLDAP(ldapEscape.filter(config.key_id+"=${id},"+config.dn_groups, {id : gid} ), config.key_id, ldapEscape.filter(config.tgty.filter, { ty: type })).then(res => {
                     if (res.length!=0) { gidtyList.push(res); }
                 });
             });
@@ -248,14 +248,24 @@ function trouverGroupesParTypes(user, input, type) {
 }
 
 /**
- * @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.
+ * @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.
  * @arg {Object} user - Utilisateur de la forme nécessaire à {@link connecterLDAP}
- * @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.
+ * @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.
+ * @arg {string} data[givenName] - Prénom 
+ * @arg {string} data[lastName] - Nom
+ * @arg {string} data[nickname] - Surnom
+ * @arg {string} data[nationality] - Nationalité (non implémentée pour l'instant, pas de format spécifique)
+ * @arg {string} data[promotion] - String de l'année de promo
+ * @arg {string} data[phone] - String du numéro de portable
+ * @arg {string} data[mail] - Adresse mail
+ * @arg {string} data[ip] - Une ou des adresses ip
+ * @arg {string} data[school] - Ecole d'appartenance (pour l'instant instable)
+ * @arg {string} data[groups] - Un ou plusieurs groupes (pas de différence entre membre simple et admin)
+ * @arg {string} data[studies] - PA ou autre
+ * @arg {string} data[sport] - Section sportive ou autre
+ * @arg {string[]} return_values - Liste d'attributs à renvoyer dans le résultat final
+ * @return {Promise(Object[])} Liste de dictionnaires de profils en cohérence avec l'input avec pour clés les attributs des profils.
  */
 function repliquerTOLModulable(user, data, return_values) {
     return new Promise(function(resolve, reject) {
@@ -279,19 +289,16 @@ function repliquerTOLModulable(user, data, return_values) {
             }
         });
         // Appel rechercheLDAP avec filtre de l'espace 
-        rechercherLDAP(user, config.dn_users, filter, return_values).then(res => resolve(res));
+        rechercherLDAP(user, config.dn_users, return_values, filter).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.
+ * @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 à 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.
+ * @arg {Object} data - Dictionnaire contenant les données nécessaires à {@link repliquerTOLModulable}
+ * @return {Promise(string[])} uids des profils qui "match" les critères proposés.
  */
 function repliquerTOLdesIds(user, data) {
     return new Promise( function(resolve, reject) {
@@ -300,23 +307,11 @@ function repliquerTOLdesIds(user, data) {
 }
 
 /**
- * @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.
+ * @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.
  * @arg {Object} user - Utilisateur de la forme nécessaire à {@link connecterLDAP}
- * @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.
+ * @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).
  */
 function repliquerTOL(user, data) {
     return new Promise( function(resolve, reject) {
@@ -329,77 +324,229 @@ function repliquerTOL(user, data) {
 //------------------------------------------------------------------------------------------------------------------------
 
 /**
- * @summary Fonction qui va plonger dans le LDAP et modifier un certain jeu de valeurs 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 add). Il faut l'appeler suivant un schéma `creerUtilisateur(...).then((res) => { truc avec res });`. 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.
+ * @summary Fonction qui créé un nouvel utilisateur dans le 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.
  * @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)
- * @return {boolean} `true` si la modification s'est bien déroulée, false sinon
+ * @arg {Object} data - Dictionnaire des informations utilisateurs
+ * @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
+ * @arg {string} data[birthdate] - Date d'anniversaire au format annee-mois-jour
+ * @arg {string} data[uid] - Numéro d'identifiant plus ou moins arbitraire
+ * @arg {string} data[password] - Mot de passe
+ * @arg {string} data[promo] - Simple année d'entrée école (pas d'X17)
+ * @arg {string} data[onPlatal] - "String booléen" valant 0 ou 1
+ * @arg {string} data[email] - Courriel supposé valide
+ * @arg {string} data[phone] - String du numéro de portable
+ * @arg {string} data[photo] - Photo jpeg directement en bytestring
+ * @arg {string} data[room] - Pour l'instant juste le numéro de casert ou rien, à terme l'adresse complète
+ * @arg {string} data[readPerm] [] - Permission spéciales BR sous la forme *truc,
+ * @arg {string} data[writePerm] [] - Permission spéciales BR sous la forme *truc,
+ * @arg {string[]} data[forlifes] [] - Liste d'alias spéciaux BR (attention le filtre .fkz n'est plus fonctionnel)
+ * @arg {string[]} data[ips] - Liste des ips connus de la personne
+ * @arg {string[]} data[groupsIsMember] [] - Liste des gid dont le pax est membre
+ * @arg {string[]} data[groupsIsAdmin] [] - Liste des gid dont le pax est admin ; supposé sous-liste du précédent
+ * @return {Promise(boolean)} `true` si la modification s'est bien déroulée, false sinon
  */
-/*function creerUtilisateur(user, data) {
+function creerUtilisateur(user, data) {
     return new Promise(function(resolve, reject) {
+        // Calcul d'un dictionnaire d'ajout
         let vals = {};
-        config.cru.single_user_infos.forEach(key => vals[key]=eval(config.cru.expr_single_values_user[key]));
-        ajouterLDAP(user, config.key_id+"="+data["uid"]+","+config.dn_users, vals).then( res => {
-            config.cru.multiple_user_infos.forEach(key => {
-                vals = {};
-                //vals[]
-                modifierLDAP(user, config.key_id+"="+data["uid"]+","+config.dn_users, ).then(res => {
-                    modifierLDAP(user, ).then(res => { resolve(true); });
+        // 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);
+            });
+            resolve(true);
         });
         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).
+ * @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.
  * @arg {Object} user - Utilisateur de la forme nécessaire à {@link connecterLDAP}
- * @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
+ * @arg {Object} data - Dictionnaire des informations utilisateurs (voir détail des champs dans config.json)
+ * @arg {string} data[name] - Nom du groupe
+ * @arg {string} data[ns] - Statut du groupe ; 'binet' ou 'free', càd ouvert à tous
+ * @arg {string} data[gid] - Numéro d'identifiant du groupe
+ * @arg {string} data[label] [] - Sorte de surnom ; remplacé par le nom si égal à ''
+ * @arg {string[]} data[members] - Liste des membres du groupe
+ * @arg {string[]} data[admins] - Liste des admins du groupe ; supposée être une sous-liste de la précédente
+ * @return {Promise(boolean)} `true` si la modification s'est bien déroulée, false sinon
  */
-function creerGroupeLDAP(user, gid, nom, status, admins, members) {
+function creerGroupe(user, data) {
     return new Promise(function(resolve, reject) {
-        let data = {};
-        data["uid"] = gid;
-        data["cn"] = nom;
-        data["brNS"] = status;
-        data[""];
+        // 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);
     });
 }
 
 //------------------------------------------------------------------------------------------------------------------------
-// Fonctions modificatrices
+// Fonctions d'édition
 //------------------------------------------------------------------------------------------------------------------------
 
 /**
- * @summary TBC Fonction qui va modifier un certain jeu de valeur en argument dans le LDAP.
- * @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).
+ * @summary Fonction qui permet de rajouter un membre déjà créé à un groupe.
+ * @desc Cette fonction fait essentiellement appel à {@link modifierLDAP} et {@link listerGroupes}.
  * @arg {Object} user - Utilisateur de la forme nécessaire à {@link connecterLDAP}
- * @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
+ * @arg {string} uid - Identifiant du futur membre
+ * @arg {string} gid - Identifiant du groupe
+ * @return {Promise(boolean)} `true` si la modification s'est bien déroulée, false sinon
+ */
+function ajouterMembreGroupe(user, uid, gid) {
+    return new Promise(function(resolve, reject) {
+        // Vérifie que l'utilisateur est pas déjà membre pour groupes
+        if (!listerMembres(user,gid).includes(uid)) {
+            let vals = {};
+            vals[config.am.key_gr] = uid;
+            modifierLDAP(user, ldapEscape(config.key_id+"=${id},"+config.dn_groups, { id: gid }), "add", vals).then(res => {
+                // Erreur si pb lors de la modification
+                if (!res) { reject(false); }
+            });
+        }
+        // Vérifie que l'utilisateur est pas déjà membre pour users
+        if (!listerGroupes(user,uid).includes(gid)) {
+            let vals2 = {};
+            vals2[config.am.key_u] = gid;
+            modifierLDAP(user, ldapEscape(config.key_id+"=${id},"+config.dn_users, { id: uid }), "add", vals2).then(res => {
+                // Erreur si pb lors de la modification
+                if (!res) { reject(false); }
+            });
+        }
+        resolve(true);
+    });
+}
+
+/**
+ * @summary Fonction qui permet de promouvoir membre au stade d'administrateur d'un groupe.
+ * @desc Cette fonction fait essentiellement appel à {@link modifierLDAP}.
+ * @arg {Object} user - Utilisateur de la forme nécessaire à {@link connecterLDAP}
+ * @arg {string} uid - Identifiant du futur membre
+ * @arg {string} gid - Identifiant du groupe
  * @return {boolean} `true` si la modification s'est bien déroulée, false sinon
  */
-function rajouterLDAP(user, gid, nom, status, admins, members) {
+function ajouterAdministrateurGroupe(user, uid, gid) {
     return new Promise(function(resolve, reject) {
-        let data = {};
-        data["uid"] = gid;
-        data["cn"] = nom;
-        data["brNS"] = status;
-        data[""];
+        // Vérifie que l'utilisateur est bien membre pour groupes et le cas échéant le rajoute
+        if (!(listerMembres(user,gid).includes(uid) & listerGroupes(user,uid).includes(gid))) {
+            ajouterMembreGroupe(user, uid, gid).then(res => {
+                // Gestion d'erreur
+                if (!res) { reject(false); }
+            });
+        }
+        // Vérifie que l'utilisateur n'est pas déjà admin
+        if (!listerAdministrateurs(user,gid).includes(uid)) {
+            let vals = {};
+            vals[config.am.key_gr] = uid;
+            modifierLDAP(user, ldapEscape(config.key_id+"=${id},"+config.dn_groups, { id: gid }), "add", vals).then(res => {
+                // Gestion d'erreur
+                if (!res) { reject(false); }
+            });
+        }
+        resolve(true);
+    });
+}
+
+/**
+ * @summary Fonction qui édite un utilisateur existant dans le LDAP. Très similaire à {@link creerUtilisateur}
+ * @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 au même format que pour {@link creerUtilisateur} avec tous les champs optionnels
+ * @return {Promise(boolean)} `true` si la modification s'est bien déroulée, false sinon
+ */
+function editerUtilisateur(user, data) {
+    return new Promise(function(resolve, reject) {
+        // Calcul d'un dictionnaire de modifs
+        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);
+            });
+            resolve(true);
+        });
         reject(false);
     });
 }
 
+/**
+ * @summary Fonction qui édite un groupe existant dans le LDAP. Très similaire à {@link creerGroupe}
+ * @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 au même format que pour {@link creerGroupe} avec tous les champs optionnels
+ * @return {Promise(boolean)} `true` si la modification s'est bien déroulée, false sinon
+ */
+function editerGroupe(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.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);
+    });
+}
 //listerGroupes({},"anatole.romon").then(res => console.log(res));
 //listerMembres({},"bda").then(res => console.log(res));
 //listerAdministrateurs({},"kes").then(res => console.log(res));
@@ -409,7 +556,10 @@ function rajouterLDAP(user, gid, nom, status, admins, members) {
 //repliquerTOL({},{"groups": ["faerix","br"]}).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));
-//trouverGroupesParTypes({},"fe","binet").then(res => console.log(res));
+//q^73
+
+
+trouverGroupesParTypes({},"fe","binet").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}; */