diff --git a/src/graphql/connectors/selectors.js b/src/graphql/connectors/selectors.js
index 9cde12e5625bc99a05d960142b131c300b24633d..5e4720dfb6c98bb56f6f83f77ac2460dd8f453a8 100644
--- a/src/graphql/connectors/selectors.js
+++ b/src/graphql/connectors/selectors.js
@@ -3,7 +3,6 @@
  * @author akka vodol
  */
 
-
 //Give a user, get the groups linked to that user
 
 /**
diff --git a/src/ldap/admins.js b/src/ldap/admins.js
index e0b9b80fd82e85afd765f6188eebd3188afa8f2c..2da5cd4a73c02a6d1e92ea60b521b41d9019e444 100644
--- a/src/ldap/admins.js
+++ b/src/ldap/admins.js
@@ -35,29 +35,30 @@ class Admin extends User {
      * @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
+     * @async
      */
-    static addGroupMember(uid, gid) {
+    async addGroupMember(uid, gid) {
         // Vérifie que l'utilisateur est pas déjà membre pour groupes
-        return this.getMembers(gid).then(lm => {
-            if (!lm.includes(uid)) {
-                let vals = {};
-                vals[config.groups.member] = uid;
-                LDAP.change(config.key_id+gid+config.dn_groups, "add", vals).then(res => {
-                    // Erreur si pb lors de la modification
-                    if (!res) { throw Error; }
-                });
+        let lm = await this.getMembers(gid);
+        if (!lm.includes(uid)) {
+            let vals = {};
+            vals[config.groups.member] = uid;
+            // Erreur si pb lors de la modification
+            if (!await LDAP.change(config.key_id+gid+config.dn_groups, "add", vals)) {
+                throw "Erreur lors de la modification dans l'arbre des groupes";
             }
+        }
         // Vérifie que l'utilisateur est pas déjà membre pour users
-        }).then(res => this.getGroups(uid)).then( lg => {
-            if (!lg.includes(gid)) {
-                let vals2 = {};
-                vals2[config.users.groups] = gid;
-                LDAP.change(config.key_id+uid+config.dn_users, "add", vals2).then(res => {
-                    // Erreur si pb lors de la modification
-                    if (!res) { throw Error; } else { return true; }
-                });
+        let lg = await this.getGroups(uid);
+        if (!lg.includes(gid)) {
+            let vals2 = {};
+            vals2[config.users.groups] = gid;
+            // Erreur si pb lors de la modification
+            if (!await LDAP.change(config.key_id+uid+config.dn_users, "add", vals2)) {
+                throw "Erreur lors de la modification dans l'arbre des utilisateurs";
             }
-        });
+        }
+        return true;
     }
 
     /**
@@ -66,31 +67,42 @@ class Admin extends User {
      * @arg {string} uid - Identifiant de l'ex-membre
      * @arg {string} gid - Identifiant du groupe
      * @return {Promise(boolean)} `true` si la modification s'est bien déroulée, false sinon
+     * @async
      */
-    static delGroupMember(uid, gid) {
+    async delGroupMember(uid, gid) {
         // Vérifie que l'utilisateur est pas déjà viré pour groupes
-        return this.getMembers(gid).then(lm => {
-            if (lm.includes(uid)) {
-                // Supprime tous les utilisateurs
-                LDAP.change(config.key_id+gid+config.dn_groups, "del", config.group.member).then(res => {
-                    // Les rajoute un par un, sauf pour le supprimé
-                    lm.forEach(id => {
-                        if (id!=uid) { this.addGroupMember(id, gid); }
-                    });
-                });
+        let lm = await this.getMembers(gid);
+        if (lm.includes(uid)) {
+            // Supprime tous les utilisateurs
+            if (!await LDAP.change(config.key_id+gid+config.dn_groups, "del", config.group.member)) {
+                throw "Erreur lors de la suppression de tous les membres du groupe.";
             }
-        }).then(res => this.getGroups(uid)).then(lg => {
-            // Vérifie que l'utilisateur est pas déjà viré pour users
-            if (lg.includes(gid)) {
-                // Supprime tous les groupes
-                LDAP.change(config.key_id+uid+config.dn_users, "del", config.member.groups).then(res => {
-                    // Les rajoute un par un, sauf pour le supprimé
-                    lg.forEach(id => {
-                        if (id!=gid) { this.addGroupMember(uid, id).then(res => { if (res) { return true; } else { throw Error; }}); }
+            // Les rajoute un par un, sauf pour le supprimé
+            lm.forEach(id => {
+                if (id!=uid) {
+                    this.addGroupMember(id, gid).then(res => {
+                        if (!res) { throw "Erreur lors du ré-ajout des autres membres"; }
                     });
-                });
+                }
+            });
+        }
+        let lg = await this.getGroups(uid);
+        // Vérifie que l'utilisateur est pas déjà viré pour users
+        if (lg.includes(gid)) {
+            // Supprime tous les groupes
+            if (!await LDAP.change(config.key_id+uid+config.dn_users, "del", config.member.groups)) {
+                throw "Erreur lors de la suppression de tous les groupes du membre.";
             }
-        });
+            // Les rajoute un par un, sauf pour le supprimé
+            lg.forEach(id => {
+                if (id!=gid) {
+                    this.addGroupMember(uid, id).then(res => {
+                        if (!res) { throw "Erreur lors du ré-ajout des autres groupes"; }
+                    });
+                }
+            });
+        }
+        return true;
     }
 
     /**
@@ -100,22 +112,21 @@ class Admin extends User {
      * @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
+     * @async
      */
-    static addGroupAdmin(uid, gid){
+    async addGroupAdmin(uid, gid){
         // Ajoute le membre au groupe avant d'en faire un admin
-        return this.addGroupMember(uid,gid).then(res => {
-            this.getAdmins(gid).then(la => {
-                if (!la.includes(uid)) {
-                    // Finalement modification, uniquement dans groups
-                    let vals = {};
-                    vals[config.groups.admin] = uid;
-                    LDAP.change(config.key_id+gid+config.dn_groups, "add", vals).then(res => {
-                        // Gestion d'erreur
-                        if (!res) { throw Error; } else { return true; }
-                    });
-                }
-            });
-        });
+        if (!await this.addGroupMember(uid,gid)) { throw "Erreur lors de l'ajout du futur admin en tant que membre."; }
+        let la = await this.getAdmins(gid);
+        if (!la.includes(uid)) {
+            // Finalement modification, uniquement dans groups
+            let vals = {};
+            vals[config.groups.admin] = uid;
+            if (!await LDAP.change(config.key_id+gid+config.dn_groups, "add", vals)) {
+                throw "Erreur lors de l'ajout de l'admin dans l'arbre des groupes";
+            }
+        }
+        return true;
     }
 
     /**
@@ -124,23 +135,24 @@ class Admin extends User {
      * @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
+     * @async
      */
-    delGroupAdmin(uid, gid) {
+    async delGroupAdmin(uid, gid) {
         // Peut paraître absurde mais permet de s'assurer que le membre est bien présent et que ses champs sont comme il faut
-        return this.delGroupMember(uid, gid).then( res => { this.addGroupMember(uid,gid).then( res =>
-            // Vérifie que l'utilisateur est bien admin (comme dans delGroupMember)
-            this.getAdmins(gid).then(la => {
-                if (la.includes(uid)) {
-                    // Supprime tous les administrateurs
-                    LDAP.change(config.key_id+gid+config.dn_groups, "del", config.group.admin).then(res => {
-                        // Les rajoute un par un, sauf pour le supprimé
-                        la.forEach(id => {
-                            if (id!=uid) { this.addGroupAdmin(id, gid); }
-                        });
-                    });
-                }
-            }).then(res => { return true; }));
-        });
+        if (!(await this.delGroupMember(uid, gid)&&this.addGroupMember(uid,gid))) { throw "Erreur dans l'éjection/réadmission du futur admin."; }
+        // Vérifie que l'utilisateur est bien admin (comme dans delGroupMember)
+        let la = await this.getAdmins(gid);
+        if (la.includes(uid)) {
+            // Supprime tous les administrateurs
+            if (!await LDAP.change(config.key_id+gid+config.dn_groups, "del", config.group.admin)) { throw "Erreur dans la suppression de tous les admins."; }
+            // Les rajoute un par un, sauf pour le supprimé
+            la.forEach(id => {
+                if (id!=uid) { this.addGroupAdmin(id, gid).then(res => {
+                    if (!res) { throw "Erreur dans le réajout d'un des autres admins."; }
+                }); }
+            });
+        }
+        return true;
     }
     
     //------------------------------------------------------------------------------------------------------------------------
@@ -153,26 +165,25 @@ class Admin extends User {
      * @arg {string} gid - Identifiant du groupe à modifier
      * @arg {Object} data - Dictionnaire des informations utilisateurs au même format que pour {@link User.addGroup} avec tous les champs optionnels.
      * @return {Promise(boolean)} `true` si la modification s'est bien déroulée, false sinon
+     * @async
      */
-    editGroup(gid, data) {
+    async editGroup(gid, data) {
         // Récupération des anciennes données
-        return this.peekGroup(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
-            this.delGroup(gid).then(r => {
-                this.addGroup(profil).then(res => { if (!res) { throw Error; } else { return true; }});
+        let profil = await this.peekGroup(gid);
+        // 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
+        if (!await this.delGroup(gid)&&await this.addGroup(profil)) { throw "Erreur de la destruction/recréation du groupe."; }
+        return true;
     }
     
     //------------------------------------------------------------------------------------------------------------------------
@@ -185,17 +196,21 @@ class Admin extends User {
      * Appelle {@link LDAP.clear} bien sûr, mais aussi {@link Admin.delGroupMember} et {@link Admin.delGroupAdmin} pour gérer les groupes de l'utilisateur sortant.
      * @arg {string} gid - gid de la victime
      * @return {Promise(boolean)} `true` si la modification s'est bien déroulée, false sinon
+     * @async
      */
-    delGroup(gid) {
+    async delGroup(gid) {
         // Gestion des membres et administrateurs d'abord
-        return this.constructor.peekGroup(gid).then(profil => {
-            // Ordre important
-            profil[config.group['admin']].forEach( id => { this.delGroupAdmin( id, gid); });
-            profil[config.group['member']].forEach(id => { this.delGroupMember(id, gid); });
-        // Elimination
-        }).then(res => {
-            LDAP.clear(config.key_id+"="+gid+","+config.dn_groups);
+        let profil = await this.constructor.peekGroup(gid);
+        // Ordre important
+        profil[config.group['admin']].forEach( id => {
+            this.delGroupAdmin( id, gid).then(res => { if (!res) { throw "Erreur lors de la suppression d'un admin."; } });
+        });
+        profil[config.group['member']].forEach(id => {
+            this.delGroupMember(id, gid).then(res => { if (!res) { throw "Erreur lors de la suppression d'un membre."; } });
         });
+        // Elimination
+        if (!await LDAP.clear(config.key_id+"="+gid+","+config.dn_groups)) { throw "Erreur lors de la suppression de la feuille dans l'arbre des groupes."; }
+        return true;
     }
 }
 
@@ -233,8 +248,9 @@ class SuperAdmin extends Admin {
      * @arg {string[]} data[groups] [] - Liste des gid dont le pax est membre (doit contenir "on_platal" si il y a lieu)
      * @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
+     * @async
      */
-    addUser(data) {
+    async addUser(data) {
         // Calcul d'un dictionnaire d'ajout
         let vals = {};
 
@@ -247,71 +263,83 @@ class SuperAdmin extends Admin {
         config.user.direct_input.forEach(key_att => vals[config.user[key_att]]=data[key_att]);
 
         // Appel à la fonction de base
-        return LDAP.add(config.key_id+"="+vals[config.key_id]+","+config.dn_users, vals).then( res => {
-            // 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 => {
-                // On rajoute chaque valeur en entrée
-                data[key_att].forEach(val => {
-                    let vals2 = {};
-                    vals2[config.user[key_att]]=val;
-                    LDAP.change(config.key_id+"="+vals[config.key_id]+","+config.dn_users, "add", vals2).then(res => {
-                        if (!res) { throw Error; }
-                    });
+        if (!await LDAP.add(config.key_id+"="+vals[config.key_id]+","+config.dn_users, vals)) { throw "Erreur de l'ajout de la feuille à l'arbre utilisateur."; }
+        
+        // Modifications multiples pour avoir plusieurs champs de même type ; boucle sur les attributs multiples
+        config.user.multiple_input.forEach(key_att => {
+            // On rajoute chaque valeur en entrée
+            data[key_att].forEach(val => {
+                let vals2 = {};
+                vals2[config.user[key_att]]=val;
+                LDAP.change(config.key_id+"="+vals[config.key_id]+","+config.dn_users, "add", vals2).then(res => {
+                    if (!res) { throw "Erreur lors de l'ajout d'une valeur pour un champ à valeurs multiples à la feuille du nouvel utilisateur."; }
                 });
             });
+        });
 
-            // Certains champs nécessitent de petits calculs
-            let vals3={};
+        // Certains champs nécessitent de petits calculs
+        let vals3={};
 
-            // Création d'un nom complet lisible
-            vals3[config.user['fullName']]=data['givenName']+' '+data['lastName'].toUpperCase();
-            // ?!
-            vals3[config.user['password']] =  '{CRYPT}' + data['password'];
-            // Ecriture d'un surnom s'il y a lieu
-            if ((data['nickname']!=undefined)&(data['nickname']!='')) {
-                vals3[config.user['nickname']]=data['nickname'];
-            }
-            // Génération id aléatoire unique
-            Tests.generateId(config.user['id'], config.dn_users).then(id => { vals3[config.user['id']]=id; });
-            
-            // Stockage machine ; dépend du prénom
-            vals3[config.user['directory']] = '/hosting/users/' + data['givenName'][0];
+        // Création d'un nom complet lisible
+        vals3[config.user['fullName']]=data['givenName']+' '+data['lastName'].toUpperCase();
+
+        // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+        // TBM CA MARCHERA PB PAS
+        // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+        vals3[config.user['password']] =  '{CRYPT}' + data['password'];
+        
+        // Ecriture d'un surnom s'il y a lieu
+        if ((data['nickname']!=undefined)&&(data['nickname']!='')) {
+            vals3[config.user['nickname']]=data['nickname'];
+        }
+        // Génération id aléatoire unique
+        vals3[config.user['id']]= await Tests.generateId(config.user['id'], config.dn_users);
+        
+        // Stockage machine ; dépend du prénom
+        vals3[config.user['directory']] = '/hosting/users/' + data['givenName'][0];
 
-            // Code root
-            vals3[config.user['cleanFullName']]=data['fullName'].replace(':', ';').toLowerCase().normalize('UFD');
-            
-            // Adressage root
-            if (data['groups'].includes("on_platal")) { vals3[config.user['login']] = "/bin/bash"; }
-            else  { vals3[config.user['login']] = "/sbin/nologin"; }
-            
-            // Permissions BR
-            vals3[config.user['readPerm']] = 'br.*,public.*';
-            if (data['readPerm'].length>0) { vals3[config.user['readPerm']] += ',' + data['readPerm']; }
-            vals3[config.user['writePerm']] = 'br.*,!br.blague-du-jour,public.*,!br.campagnekes';
-            if (data['writePerm'].length>0) { vals3[config.user['readPerm']] += ',' + data['writePerm']; }
+        // Code root
+        vals3[config.user['cleanFullName']]=data['fullName'].replace(':', ';').toLowerCase().normalize('UFD');
+        
+        // Adressage root
+        if (data['groups'].includes("on_platal")) { vals3[config.user['login']] = "/bin/bash"; }
+        else  { vals3[config.user['login']] = "/sbin/nologin"; }
+        
+        // Permissions BR
+        vals3[config.user['readPerm']] = 'br.*,public.*';
+        if (data['readPerm'].length>0) { vals3[config.user['readPerm']] += ',' + data['readPerm']; }
+        vals3[config.user['writePerm']] = 'br.*,!br.blague-du-jour,public.*,!br.campagnekes';
+        if (data['writePerm'].length>0) { vals3[config.user['readPerm']] += ',' + data['writePerm']; }
 
-            // Valeur nécessaire mais inutile
-            vals3[config.user['idNum']] ='5000';
+        // Valeur nécessaire mais inutile
+        vals3[config.user['idNum']] ='5000';
 
-            // Inscription des valeurs calculées
+        // Inscription des valeurs calculées
+        if (!await LDAP.change(config.key_id+"="+vals[config.user['hruid']]+","+config.dn_users, "add", vals3)) {
+            throw "Erreur lors de l'ajout des valeurs calculées à la feuille du nouvel utilisateur.";
+        }
+
+        ["posixAccount", "shadowAccount", "inetOrgPerson", "brAccount"].forEach(cst => {
+            let val3={};
+            vals3[config.user['class']]=cst;
             LDAP.change(config.key_id+"="+vals[config.user['hruid']]+","+config.dn_users, "add", vals3).then(res => {
-                if (!res) { throw Error; }
+                if (!res) { throw "Erreur lors de l'ajout d'une valeur constante à la feuille du nouvel utilisateur."; }
             });
+        });
 
-            ["posixAccount", "shadowAccount", "inetOrgPerson", "brAccount"].forEach(cst => {
-                let val3={};
-                vals3[config.user['class']]=cst;
-                LDAP.change(config.key_id+"="+vals[config.user['hruid']]+","+config.dn_users, "add", vals3).then(res => {
-                    if (!res) { throw Error; }
-                });
+        // Utilisation des fonctions adaptées pour assurer la cohérence de l'ensemble
+        data['groupsIsMember'].forEach(gid => {
+            this.addGroupMember(vals[config.key_id], gid).then(res => {
+                if (!res) { throw "Erreur lors de l'ajout du nouvel utilisateur à un groupe."; }
             });
-
-            // Utilisation des fonctions adaptées pour assurer la cohérence de l'ensemble
-            data['groupsIsMember'].forEach(gid => { this.addGroupMember(vals[config.key_id], gid); });
-            data['groupsIsAdmin'].forEach(gid => {  this.addGroupAdmin( vals[config.key_id], gid); });
-
-            return true;
         });
+        data['groupsIsAdmin'].forEach(gid => { 
+            this.addGroupAdmin( vals[config.key_id], gid).then(res => {
+                if (!res) { throw "Erreur lors de l'ajout du nouvel utilisateur à un groupe en tant qu'admin."; }
+            });
+        });
+
+        return true;
     }
 
     //------------------------------------------------------------------------------------------------------------------------
@@ -324,17 +352,19 @@ class SuperAdmin extends Admin {
      * Appelle {@link LDAP.clear} bien sûr, mais aussi {@link Admin.delGroupMember} et {@link Admin.delGroupAdmin} pour gérer les groupes de l'utilisateur sortant.
      * @arg {string} uid - uid de la victime
      * @return {Promise(boolean)} `true` si la modification s'est bien déroulée, false sinon
+     * @async
      */
-    delUser(uid) {
+    async delUser(uid) {
         // Gestion des groupes d'abord
-        return super.peekUser(uid).then(profil => {
-            profil[config.user['groups']].forEach(gid => {
-                this.isGroupAdmin(gid).then(res => {
-                    this.delGroupAdmin(uid, gid);
-                }).then(res => { this.delGroupMember(uid, gid); });
-            });
+        let profil = await this.peekUser(uid);
+        profil[config.user['groups']].forEach(gid => {
+            if (this.isGroupAdmin(gid)) {
+                if (!this.delGroupAdmin(uid, gid)) { throw "Erreur lors de la suppression des droits d'admin de l'utilisateur."; }
+            }
+            if (!this.delGroupMember(uid, gid)) { throw "Erreur lors de la suppression de l'appartenace à un groupe de l'utilisateur."; }
+        });
         // Elimination
-        }).then(res => { this.LDAP.clear(config.key_id+"="+uid+","+config.dn_users); });
+        if (!this.LDAP.clear(config.key_id+"="+uid+","+config.dn_users)) { throw "Erreur lors de la suppression de l'utilisateur."; }
     }
 }
 
diff --git a/src/ldap/basics.js b/src/ldap/basics.js
index 4780a20af90cb382503e21add8f68aeaf2740fb9..5ec3415d16b52edeb93792f5f3e46a95a5e12643 100644
--- a/src/ldap/basics.js
+++ b/src/ldap/basics.js
@@ -42,17 +42,32 @@ class LDAP {
      * @arg {Object} user - Utilisateur de la forme suivante :
      * @arg {string} user[uid] - User identifier
      * @arg {string} user[password] - Mot de passe
-     * @static
+     * @returns {Promise(boolean)} `true` si l'opération s'est bien déroulée, `false` sinon.
+     * @static @async
      */
-    static bind(user) { client.bind(ldapEscape.dn("${txt}", { txt: user["dn"]}), ldapEscape.filter("${txt}", { txt: user["password"]}), (err, res) => {}); } 
+    static async bind(user) {
+        return new Promise((resolve, reject) => {
+            // Escape DN as everywhere in this file, but password is taken as is
+            client.bind(ldapEscape.dn("${txt}", { txt: user["dn"]}), user["password"], res => {
+                // Gestion erreur
+                try { res; }
+                catch(err) {
+                    reject(err);
+                    throw "Erreur lors de la connection au LDAP.";
+                }
+            });
+            // End with a boolean
+            resolve(true);
+        });
+    }
 
     /**
      * @summary Fonction qui sert à se déconnecter du LDAP. Ne renvoie 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).
-     * @static
+     * @desc Assez important en terme de sécurité, de gestion de conflit, et de droit d'accès. Fait appel à {@link LDAP.bind}
+     * @returns {Promise(boolean)} `true` si l'opération s'est bien déroulée, `false` sinon.
+     * @static @async
      */
-    static unbind() { client.bind("", "", (err, res) => {}); }
+    static async unbind() { return this.bind({"uid":"", "password":""}); }
 
     /**
      * @summary Fonction qui interroge le LDAP selon un protocole spécifié en argument et renvoit les valeurs trouvées.
@@ -65,9 +80,9 @@ class LDAP {
      * @arg {string[]} attributes - Liste des attributs qui figureront dans le résultat final ; peut aussi être un seul élément
      * @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)
-     * @static
+     * @static @async
      */
-    static search(dn, attributes, filter="(objectClass=*)") {
+    static async search(dn, attributes, filter="(objectClass=*)") {
         return new Promise((resolve, reject) => {
             let vals=[];
             // Interrogation LDAP selon configuration fournie en argument
@@ -75,26 +90,27 @@ class LDAP {
                 "scope": "sub",
                 "filter": ldapEscape.filter("${txt}", { txt: filter}),
                 "attributes": attributes
-            }, function(err, res) {
+            }, (err, res) => {
                 // Gestion erreur
                 if (err) {
                     reject(err);
+                    throw "Erreur lors de la recherche sur le LDAP.";
                 } else {
                     // Dès que la recherche renvoit une entrée, on stocke les attributs qui nous intéresse
-                    res.on('searchEntry', function(entry) {
+                    res.on('searchEntry', entry => {
                         // Cas un seul attribut où le résultat est une liste directement
-                        if (!Array.isArray(attributes)) { vals.push(entry.object[attributes]); }
+                        if (!Array.isArray(attributes)) {  vals.push(entry.object[attributes]); }
                         else if (attributes.length == 1) { vals.push(entry.object[attributes[0]]); }
                         // Cas plusieurs attributs donc résultat dictionnaire
                         else {
                             vals.push({});
-                            attributes.forEach((attribute) => {
+                            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) { resolve(vals); });
+                    res.on('end', res => { resolve(vals); });
                 }
             });
         });
@@ -111,17 +127,19 @@ class LDAP {
      * "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.
-     * @static
+     * @return {Promise(boolean)} `true` si la modification s'est bien déroulée, `false` sinon.
+     * @static @async
      */
-    static change(dn, op, mod) {
+    static async change(dn, op, mod) {
         return new Promise((resolve, reject) => {
             // Modification LDAP selon configuration en argument (pourrait prendre une liste de Changes)
             client.modify(ldapEscape.dn("${txt}", {txt: dn}), new ldap.Change({
                 operation: ldapEscape.dn("${txt}", {txt: op}),
                 modification: mod,
+            // Gestion erreur 
             }), err => {
                 reject(err);
+                throw "Erreur lors d'une opération de modification sur le LDAP.";
             });
             resolve(true);
         });
@@ -138,13 +156,14 @@ class LDAP {
      * @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.
-     * @static
+     * @static @async
      */
-    static add(dn, vals) {
+    static async add(dn, vals) {
         return new Promise((resolve, reject) => {
             // Ajout LDAP selon la configuration en argument
-            client.add(ldapEscape.dn(config.key_id+"="+vals[config.key_id]+",${txt}", { txt: dn}), vals, function(err) {
+            client.add(ldapEscape.dn(config.key_id+"="+vals[config.key_id]+",${txt}", { txt: dn}), vals, err => {
                 reject(err);
+                throw "Erreur lors d'une opération d'ajout sur le LDAP.";
             });
             resolve(true);
         });
@@ -161,13 +180,14 @@ class LDAP {
      * @arg {Object} user - Utilisateur de la forme nécessaire au {@link LDAP.connecter}
      * @arg {string} dn - Adresse de la cible
      * @return {Promise(boolean)} `true` si la modification s'est bien déroulée, false sinon
-     * @static
+     * @static @async
      */
-    static clear(dn) {
+    static async clear(dn) {
         return new Promise((resolve, reject) => {
             // Suppression LDAP
-            client.del(ldapEscape.dn("${txt}", {txt: dn}), function(err) {
+            client.del(ldapEscape.dn("${txt}", {txt: dn}), err => {
                 reject(err);
+                throw "Erreur lors d'une opération de suppression sur le LDAP.";
             });
             resolve(true);
         });
diff --git a/src/ldap/users.js b/src/ldap/users.js
index f36729b1be62a323bfcd935515e5b48a65c865e8..8fd2f2e0a95393fd4681a668da34621d4932182b 100644
--- a/src/ldap/users.js
+++ b/src/ldap/users.js
@@ -23,7 +23,6 @@ class Open {
     /**
      * @class Cette classe est la classe exportable de base permettant à un utilisateur non connecté de faire des petites recherches simples.
      * @summary Constructeur vide.
-     * @author hawkspar
     */
     constructor(config) {}
 
@@ -35,9 +34,9 @@ class Open {
      * @desc Cette fonction utilise {@link LDAP.search} va directement à la feuille de l'utilisateur.
      * @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
-     * @static
+     * @static @async
      */
-    static getGroups(uid) {
+    static async getGroups(uid) {
         return LDAP.search(config.key_id+uid+config.dn_users, config.user.groups)[0];
     }
     
@@ -46,9 +45,9 @@ class Open {
      * @desc Cette fonction utilise {@link LDAP.search} avec un dictionnaire prédéfini dans config.json.
      * @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)
-     * @static
+     * @static @async
      */
-    static getMembers(gid) {
+    static async getMembers(gid) {
         return LDAP.search(config.key_id+gid+config.dn_users, config.group.member)[0];
     }
     
@@ -57,9 +56,9 @@ class Open {
      * @desc Cette fonction utilise {@link LDAP.search} avec un dictionnaire prédéfini dans config.json.
      * @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)
-     * @static
+     * @static @async
      */
-    static getAdmins(gid) {
+    static async getAdmins(gid) {
         return LDAP.search(config.key_id+gid+config.dn_users, config.group.admin)[0];
     }
 
@@ -69,19 +68,17 @@ class Open {
      * @param {string} uid - Identifiant de l'utilisateur à tester 
      * @param {string} gid  - Identification du groupe à tester
      * @returns {Promise(boolean)} True si l'utilisateur est membre
-     * @static
+     * @static @async
      */
-    static isGroupMember(uid, gid) {
-        return this.getGroups(uid).then(lg => {
-            this.getMembers(gid).then(lm => {
-                if (lg.includes(gid) && lm.includes(uid)) {
-                    return true;
-                }
-                else {
-                    return false;
-                }
-            });
-        });
+    static async isGroupMember(uid, gid) {
+        let lg = await this.getGroups(uid);
+        let lm = await this.getMembers(gid);
+        if (lg.includes(gid) && lm.includes(uid)) {
+            return true;
+        }
+        else {
+            return false;
+        }
     }
 
     /**
@@ -90,17 +87,16 @@ class Open {
      * @param {string} uid - Identifiant de l'utilisateur à tester 
      * @param {string} gid  - Identification du groupe à tester
      * @returns {Promise(boolean)} True si l'utilisateur est administrateur
-     * @static
+     * @static @async
      */
-    static isGroupAdmin(uid, gid) {
-        return this.getAdmins(gid).then(la => {
-            if (la.includes(uid)) {
-                return true;
-            }
-            else {
-                return false;
-            }
-        });
+    static async isGroupAdmin(uid, gid) {
+        let la = await this.getAdmins(gid);
+        if (la.includes(uid)) {
+            return true;
+        }
+        else {
+            return false;
+        }
     }
      
     /**
@@ -137,18 +133,17 @@ class Open {
      * @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.
      * @return {Promise(string[])} Liste des gid dont le nom ressemble à l'input.
-     * @static
+     * @static @async
     */
-    static findGroups(input, type) {
+    static async findGroups(input, type) {
         // Trucs intelligents faits dans ./utilities
-        return SmartSearch.groups(input, [config.key_id, config.group.type]).then(gList => {
-            let gidtyList = [];
-            gList.forEach(g => {
-                // Si le groupe est du bon type on rajoute son gid
-                if (g[config.group.type]==type) { gidtyList.push(g[config.key_id]); }
-            });
-            return gidtyList;
+        let gList = await SmartSearch.groups(input, [config.key_id, config.group.type]);
+        let gidtyList = [];
+        gList.forEach(g => {
+            // Si le groupe est du bon type on rajoute son gid
+            if (g[config.group.type]==type) { gidtyList.push(g[config.key_id]); }
         });
+        return gidtyList;
     }
 
     /**
@@ -157,9 +152,9 @@ class Open {
      * @desc Cette fonction utilise {@link SmartSearch.users}.
      * @arg {Object} data - Dictionnaire contenant les données nécessaires à {@link SmartSearch.groups}
      * @return {Promise(string[])} gids des profils qui "match" les critères proposés.
-     * @static
+     * @static @async
      */
-    static findUsers(data) {
+    static async findUsers(data) {
         return SmartSearch.users(data, config.key_id);
     }
 }
@@ -167,9 +162,11 @@ class Open {
 class User extends Open {
     /**
      * @class Cette classe est la classe de l'utilisateur connecté qui peut déjà créer un groupe et changer son profil.
+     * Techniquement, c'est la première classe qui a vraiment besoin de méthodes dynamiques dans l'arborescence, puisque c'est à partir du niveau User
+     * qu'on peut commencer à vouloir tracer les actions de l'utilisateur. 
      * @summary Ce constructeur appelle simplement {@link LDAP.bind}.
      * @arg {Object} user - Utilisateur de la forme nécessaire à {@link LDAP.bind}.
-     * @author hawkspar
+     * @async
     */
     constructor(user) {
         super();
@@ -191,8 +188,9 @@ class User extends Open {
      * @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
+     * @async
      */
-    addGroup(data) {
+    async addGroup(data) {
         // Calcul d'un dictionnaire d'ajout
         let vals = {};
 
@@ -203,53 +201,54 @@ class User extends Open {
         config.group.direct_input.forEach(key_att => vals[config.group[key_att]]=data[key_att]);
 
         // Appel à la fonction de base
-        return LDAP.add(config.key_id+"="+vals[config.group['name']]+","+config.dn_groups, vals).then( res => {
-            // Certains champs nécessitent de petits calculs
-            let vals2={};
+        if (!await LDAP.add(config.key_id+"="+vals[config.group['name']]+","+config.dn_groups, vals)) {
+            throw "Erreur lors de la création d'une nouvelle feuille dans l'arbre des groupes.";
+        }
+        // Certains champs nécessitent de petits calculs
+        let vals2={};
 
-            // Sauvegarde du nom (pour le cas où gid != data['name'])
-            vals2[config.group["name"]]=data['name'];
+        // Sauvegarde du nom (pour le cas où gid != data['name'])
+        vals2[config.group["name"]]=data['name'];
 
-            // ?!
-            vals2[config.user['password']] =  '';
+        // ?!
+        vals2[config.user['password']] =  '';
 
-            // Génération id aléatoire et test contre le LDAP
-            Tests.generateId(config.groups["idNumber"], config.dn_groups).then(id => { vals2[config.group['idNumber']]=id; });
-            // FOIREUX : Hypothèse sur la structure du reste des données mais évite un test.assurerUnicite à deux variables
-            vals2[config.group['idNumber2']]=vals2[config.group['idNumber']];
-            
-            // Stockage machine ; dépend du prénom
-            vals2[config.group['directory']] = '/hosting/groups/'+vals[config.key_id];
+        // Génération id aléatoire et test contre le LDAP
+        Tests.generateId(config.groups["idNumber"], config.dn_groups).then(id => { vals2[config.group['idNumber']]=id; });
+        // FOIREUX : Hypothèse sur la structure du reste des données mais évite un test.assurerUnicite à deux variables
+        vals2[config.group['idNumber2']]=vals2[config.group['idNumber']];
+        
+        // Stockage machine ; dépend du prénom
+        vals2[config.group['directory']] = '/hosting/groups/'+vals[config.key_id];
 
-            // Code root
-            vals2[config.group['cleanFullName']]=data['name'].replace(':', ';').toLowerCase().normalize('UFD');
-            
-            // Adressage root
-            vals2[config.group['login']] = "/sbin/nologin";
-            
-            // Permissions BR
-            vals2[config.group['readPerm']] = '!*';
-            vals2[config.group['writePerm']] = '!*';
+        // Code root
+        vals2[config.group['cleanFullName']]=data['name'].replace(':', ';').toLowerCase().normalize('UFD');
+        
+        // Adressage root
+        vals2[config.group['login']] = "/sbin/nologin";
+        
+        // Permissions BR
+        vals2[config.group['readPerm']] = '!*';
+        vals2[config.group['writePerm']] = '!*';
 
-            // Inscription des valeurs calculées
-            LDAP.change(config.key_id+"="+vals[config.key_id]+","+config.dn_groups, "add", vals2).then(res => {
-                if (!res) { throw Error; }
-            });
+        // Inscription des valeurs calculées par effet de bord
+        if (!await LDAP.change(config.key_id+"="+vals[config.key_id]+","+config.dn_groups, "add", vals2)) {
+            throw "Erreur lors de l'ajout des valeurs intelligentes du nouveau groupe.";
+        }
 
-            ["posixAccount", "posixGroup", "brAccount"].forEach(cst => {
-                let vals3={};
-                vals3[config.group['class']]=cst;
-                LDAP.change(config.key_id+"="+vals[config.key_id]+","+config.dn_groups, "add", vals3).then(res => {
-                    if (!res) { throw Error; }
-                });
+        ["posixAccount", "posixGroup", "brAccount"].forEach(cst => {
+            let vals3={};
+            vals3[config.group['class']]=cst;
+            LDAP.change(config.key_id+"="+vals[config.key_id]+","+config.dn_groups, "add", vals3).then(res => {
+                if (!res) { throw "Erreur lors de l'ajout des valeurs constantes du nouveau groupe."; }
             });
+        });
 
-            // Utilisation des fonctions adaptées pour assurer la cohérence de l'ensemble
-            data['members'].forEach(uid => { Admin.addGroupMember(uid, vals[config.key_att]); });
-            data['admins'].forEach(uid => {  Admin.addGroupAdmin( uid, vals[config.key_att]); });
+        // Utilisation des fonctions adaptées pour assurer la cohérence de l'ensemble
+        data['members'].forEach(uid => { Admin.addGroupMember(uid, vals[config.key_att]); });
+        data['admins'].forEach(uid => {  Admin.addGroupAdmin( uid, vals[config.key_att]); });
 
-            return true;
-        });
+        return true;
     }
 
     //------------------------------------------------------------------------------------------------------------------------
@@ -265,38 +264,38 @@ class User extends Open {
      * 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
+     * @async
      */
-    editUser(uid, data) {
+    async editUser(uid, data) {
         // Récupération des anciennes données
-        return this.peekUser(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];
-                });
+        let profil = await this.peekUser(uid);
+        // 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];
             });
-            // Régénération du champ manquant dans profil
-            this.getGroups(uid).then(lg => {
-                profil['groupsIsAdmin']=[];
-                lg.forEach(gid =>{
-                    this.isGroupAdmin(uid, gid).then(res =>{
-                        if (res) { profil['groupsIsAdmin'].push(gid); }
-                    }).then(res => {
-                        // Surcharge des champs à modifier selon data
-                        Object.keys(data).forEach(key => {
-                            profil[key]=data[key];
-                        });
-                        // Passage en godmode
-                        var god = SuperAdmin({"uid":"", "password":""});
-                        // Modification propre
-                        god.delUser(uid).then(r => {
-                            god.createUser(profil).then(res => { if (!res) { throw Error; } else { return true; }});
-                        });
-                    });
-                });
+        });
+        // Régénération du champ manquant dans profil
+        let lg = await this.getGroups(uid);
+        profil['groupsIsAdmin']=[];
+        lg.forEach(gid => {
+            this.isGroupAdmin(uid, gid).then(res => {
+                if (res) { profil['groupsIsAdmin'].push(gid); }
             });
         });
+        // Surcharge des champs à modifier selon data
+        Object.keys(data).forEach(key => {
+            profil[key]=data[key];
+        });
+        // Passage en godmode
+        var god = SuperAdmin({"uid":"", "password":""});
+        // Modification propre par effet de bord
+        if (!(await god.delUser(uid)&&await god.createUser(profil))) {
+            throw "Erreur dans la destruction/création du compte.";
+        } else {
+            return true;
+        }
     }
     
     destr() { LDAP.unbind(); }
diff --git a/src/ldap/utilities.js b/src/ldap/utilities.js
index 71f38a1229b53dc6c8e8370f70527570229b2af0..2a18cebae91aa9b15de22628eecdc83217f81ddb 100644
--- a/src/ldap/utilities.js
+++ b/src/ldap/utilities.js
@@ -36,14 +36,14 @@ class SmartSearch {
      * @arg {string} input - String entré par l'utilisateur qui ressemble au nom du groupe.
      * @arg {string[]} return_attributes - Liste d'attributs à renvoyer dans le résultat final
      * @return {Promise(string[])} Liste des uid de groupes dont le nom ressemble à l'input
-     * @static
+     * @static @async
      */
-    static groups(input, return_attributes) {
+    static async groups(input, return_attributes) {
         // Construction du filtre custom
-        let filter= "(|("+config.key_id+"="+input+")"+      // On cherche la valeur exacte
-                    "(|("+config.key_id+"=*"+input+")"+     // La valeur finale avec des trucs avant ; wildcard *
+        let filter= "(|("+config.key_id+"="+ input+")" +    // On cherche la valeur exacte
+                    "(|("+config.key_id+"=*"+input+")" +    // La valeur finale avec des trucs avant ; wildcard *
                     "(|("+config.key_id+"=*"+input+"*)"+    // La valeur du milieu avec des trucs avant et après
-                    "("+config.key_id+"="+input+"*))))";   // La valeur du début avec des trucs après
+                    "("+  config.key_id+"="+ input+"*))))"; // La valeur du début avec des trucs après
 
         // Appel rechercheLDAP avec filtre de l'espace 
         return LDAP.search(config.dn_groups, return_attributes, filter);
@@ -71,9 +71,9 @@ class SmartSearch {
      * @arg {string} data[sport] - Section sportive ou autre Doit être exact.
      * @arg {string[]} return_attributes - 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.
-     * @static
+     * @static @async
      */
-    static users(data, return_attributes) {
+    static async users(data, return_attributes) {
         let filter="";
         // Iteration pour chaque champ, alourdissement du filtre selon des trucs prédéfinis dans config encore
         for (var key in data) {
@@ -113,11 +113,14 @@ class Tests {
      * le nombre d'itérations et qui renvoit la prochaine valeur de l'attribut 
      * @param {int} n [0] - Nombre d'itérations (à initialiser à 0)
      * @return {Promise(string)} Valeur unique dans le domaine spécifié de l'attribut spécifié
-     * @static
+     * @static @async
      */
-    static ensureUnique(value, attribute, dn, changeValue, n=0) {
+    // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+    // TBM PB DE THROW ; PB ABSENCE TOTAL DE DEBUG
+    // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+    static async ensureUnique(value, attribute, dn, changeValue, n=0) {
         // Recherche d'autres occurences de l'id
-        LDAP.search(dn, config.key_id, "("+attribute+"="+value+")").then(matches => {
+        return LDAP.search(dn, config.key_id, "("+attribute+"="+value+")").then(matches => {
             // On renvoit la valeur si elle est bien unique
             if (matches.length==0) { return value; }
             // Sinon, on tente de nouveau notre chance avec la valeur suivante
@@ -132,9 +135,9 @@ class Tests {
      * @param {string} lastName - Nom
      * @param {string} promotion - Année de promotion
      * @return {Promise(string)} Valeur unique dans le domaine spécifié de l'attribut spécifié
-     * @static
+     * @static @async
      */
-    static generateUid(givenName, lastName, promotion) {
+    static async generateUid(givenName, lastName, promotion) {
         // normalize et lowerCase standardisent le format
         return this.ensureUnique((givenName+'.'+lastName).toLowerCase().normalize('UFD'), config.key_id, config.dn_users, (id,n) => {
             if (n==1) { id+='.'+promotion; }    // Si prénom.nom existe déjà, on rajoute la promo
@@ -149,9 +152,9 @@ class Tests {
      * @desc Limité à un appel à {@link Tests.ensureUnique} avec les bons paramètres, et quelques opérations sur l'uid pour qu'il soit valide (escape, normalisation).
      * @param {string} name - Nom
      * @return {Promise(string)} Valeur unique dans le domaine spécifié de l'attribut spécifié
-     * @static
+     * @static @async
      */
-    static generateReadableId(name) {
+    static async generateReadableId(name) {
         // normalize et lowerCase standardisent le format
         return this.ensureUnique(name.toLowerCase().normalize('UFD'), config.key_id, config.dn_groups, (id,n) => {
             if (n==1) { id+='.'+n; }    // Si nom existe déjà, on essaie nom.1
@@ -166,9 +169,9 @@ class Tests {
      * @param {string} attribut - Intitulé exact de l'id concerné
      * @param {string} dn - *Domain Name* dans lequel l'attribut doit être unique
      * @return {Promise(string)} Valeur unique dans le domaine spécifié de l'attribut spécifié
-     * @static
+     * @static @async
      */
-    static generateId(attribut, dn) {
+    static async generateId(attribut, dn) {
         return this.ensureUnique("0", attribut, dn, (id,n) => { Math.floor((Math.random() * 100000) + 1).toString(); });
     }
 }