From 97f73c68caf039d549df859cefaec12b6320522a Mon Sep 17 00:00:00 2001
From: hawkspar <quentin.chevalier@polytechnique.edu>
Date: Sat, 2 Mar 2019 19:27:10 +0100
Subject: [PATCH] split add & rem into subfunctions

---
 src/ldap/internal/basics.ts |   8 +-
 src/ldap/internal/config.ts |   5 -
 src/ldap/internal/tools.ts  | 256 +++++++++++++++++++++---------------
 3 files changed, 157 insertions(+), 112 deletions(-)

diff --git a/src/ldap/internal/basics.ts b/src/ldap/internal/basics.ts
index c738443..089d74b 100644
--- a/src/ldap/internal/basics.ts
+++ b/src/ldap/internal/basics.ts
@@ -255,4 +255,10 @@ export class Basics {
         });
         return true;
     }
-}
\ No newline at end of file
+}
+
+// Bind
+Basics.unbind();
+Basics.adminBind();
+
+console.log("Binding with LDAP client completed successfully, looking good !");
\ No newline at end of file
diff --git a/src/ldap/internal/config.ts b/src/ldap/internal/config.ts
index 14a3d4f..3b00495 100644
--- a/src/ldap/internal/config.ts
+++ b/src/ldap/internal/config.ts
@@ -13,7 +13,6 @@ import fs from 'fs';
 import path from 'path';
 import colors from 'colors';
 import dotenv from 'dotenv';
-import { Basics } from './basics';
 
 // Chargement de l'environnement
 let path_env = path.resolve(__dirname, '..', '..', '..', './.env');
@@ -37,10 +36,6 @@ let path_credentials = path.resolve(__dirname, '..', '..', '..', 'ldap_credentia
 console.log(colors.green("Loading LDAP credentials from "+path_credentials));
 export const credentialsLdapConfig = JSON.parse(fs.readFileSync(path_credentials).toString());
 
-// Bind
-Basics.unbind();
-Basics.adminBind();
-
 // Data formats and useful constants
 export const categories = ["admins","speakers","members","followers"];
 
diff --git a/src/ldap/internal/tools.ts b/src/ldap/internal/tools.ts
index 967523d..cb5b6b6 100644
--- a/src/ldap/internal/tools.ts
+++ b/src/ldap/internal/tools.ts
@@ -157,14 +157,16 @@ export class Tools {
      * @async
      * @static
      */
-    static async addIfNotPresent(id1: string, domain1: "group"|"user", id2: string, domain2: "group"|"user", category: string): Promise<boolean> {
+    static async addIfAbsent(id1: string, domain1: "group"|"user", id2: string, domain2: "group"|"user", category: string): Promise<boolean> {
         try {
             // Vérifie que l'utilisateur est pas déjà membre pour groupes
             let l = await Tools.get(id1, domain1, category);
-            var catName = ldapConfig[domain2][category];
+            var catName = ldapConfig[domain1][category];
             if (!l.includes(id2)) {
                 // Ajoute l'utilisateur dans la catégorie concernée
-                if (!await Basics.change(domain2, id2, "add", catName)) throw "Erreur lors de la modification dans l'arbre "+domain2+" pour ajouter une entrée dans la catégorie voulue.";
+                if (domain2 == "group") var id = ldapConfig[domain2].gid;
+                else                    var id = ldapConfig[domain2].uid;
+                if (!await Basics.change(domain1, id1, "add", {catName: id+"="+id2+","+ldapConfig.dn[domain2]})) throw "Erreur lors de la modification dans l'arbre "+domain2+" pour ajouter une entrée dans la catégorie voulue.";
             }
         }
         catch(err) { throw "Erreur pour obtenir une liste d'entrées d'une catégorie d'un "+domain1+"."; }
@@ -183,16 +185,16 @@ export class Tools {
      * @async
      * @static
      */
-    static async manageInclusions(uid: string, gid: string, category: string): Promise<boolean> {
-        var adm = ldapConfig.group.admins;
+    static async addIncluded(uid: string, gid: string, category: string): Promise<boolean> {
         var spk = ldapConfig.group.speakers;
+        var mem = ldapConfig.group.members;
         switch (category) {
             case "admins":
                 // Admin => speaker
-                if (!Basics.change("user", uid, "add", { spk: ldapConfig.group.gid+"="+gid+","+ldapConfig.dn.group })) throw "Erreur à l'ajout d'un nouvel admin parmi les portes-paroles d'un groupe";
+                if (!await Basics.change("user", uid, "add", { spk: ldapConfig.group.gid+"="+gid+","+ldapConfig.dn.group })) throw "Erreur à l'ajout d'un nouvel admin parmi les portes-paroles d'un groupe";
             case "speakers":
                 // Speaker & admin => member
-                if (!Basics.change("user", uid, "add", { mem: ldapConfig.group.gid+"="+gid+","+ldapConfig.dn.group })) throw "Erreur à l'ajout d'un nouvel admin ou porte-parole parmi les membres d'un groupe";
+                if (!await Basics.change("user", uid, "add", { mem: ldapConfig.group.gid+"="+gid+","+ldapConfig.dn.group })) throw "Erreur à l'ajout d'un nouvel admin ou porte-parole parmi les membres d'un groupe";
             case "members":
             case "followers":
                 break;
@@ -212,7 +214,7 @@ export class Tools {
      * @async
      * @static
      */
-    static async DFS(uid: string, gid: string, direction: boolean): Promise<boolean> {
+    static async addDFS(uid: string, gid: string, direction: boolean): Promise<boolean> {
         // Cas récursif ascendant (admins)
         if (direction) {
             var rol = ldapConfig.group.admins;
@@ -247,130 +249,172 @@ export class Tools {
      */
     static async add(uid: string, gid: string, category: string): Promise<boolean> {
         // Gestion naïve
-        Tools.addIfNotPresent(uid, "user", gid, "group", category);
-        Tools.addIfNotPresent(gid, "group", uid, "user", category);
+        Tools.addIfAbsent(uid, "user", gid, "group", category);
+        Tools.addIfAbsent(gid, "group", uid, "user", category);
         // Gestion des droits par inclusion
-        Tools.manageInclusions(uid, gid, category);
+        Tools.addIncluded(uid, gid, category);
         // Gestion des droits récursive
-        if (category != "followers")    Tools.DFS(uid, gid, false);
-        if (category == "admins")       Tools.DFS(uid, gid, true);
+        if (category != "followers")    Tools.addDFS(uid, gid, false);
+        if (category == "admins")       Tools.addDFS(uid, gid, true);
         return true;
     }
-
+    
     /**
      * @memberof LDAP
-     * @summary Fonction qui permet de supprimer un membre d'une catégorie existant d'un groupe.
-     * @desc Cette fonction fait essentiellement appel à d'autres fonctions de {@link Tools} passées en argument et {@link LDAP.change}.
-     * Elle essaie d'assurer les propriétés d'inclusion et de récursion du LDAP. Elle est sans effet pour un statut hérité.
-     * @arg {string} uid - Identifiant de l'ex-membre
-     * @arg {string} gid - Identifiant du groupe
+     * @summary Fonction intermédiaire naïve.
+     * @desc Cette fonction enlève un utilisateur d'un groupe pour un des deux arbres si cette entrée n'était pas déjà absente.
+     * Elle ne donne pas de précédence particulière à un administrateur strict, ou de protection à une administrateur hérité.
+     * @arg {string} id1 - uid/gid
+     * @arg {"group"|"user"} domain1 - Arbre concerné pour l'id1
+     * @arg {string} id2 - gid/uid
+     * @arg {"group"|"user"} domain2 - Arbre concerné pour l'id2
      * @arg {"admins"|"speakers"|"members"|"followers"} category - Categorie de l'utilisateur concerné (admin, speaker, member ou follower)
      * @return {Promise(boolean)} `true` si la modification s'est bien déroulée, false sinon
      * @async
      * @static
      */
-    static async remove(uid: string, gid: string, category : string): Promise<boolean> {
+    static async remIfPresent(id1: string, domain1: "group"|"user", id2: string, domain2: "group"|"user", category: string): Promise<boolean> {
         try {
-            // Vérifie que l'utilisateur est pas déjà viré pour groupes
-            let lu = await Tools.get(gid, "group", category);
-            let catName = ldapConfig.group[category];
-            // Statut strict
-            if (lu.includes(uid)) {
-                // Supprime tous les utilisateurs
-                if (!await Basics.change("group", gid, "del", catName)) throw "Erreur lors de la suppression de tous les membres d'une catégorie du groupe.";
+            let l = await Tools.get(id1, domain1, category);
+            var catName = ldapConfig[domain1][category];
+            // Vérifie que l'identifiant est bien présent
+            if (l.includes(id2)) {
+                // Supprime tous les identifiants
+                if (!await Basics.change(domain1, id1, "del", catName)) throw "Erreur lors de la suppression de tous les "+category+" de l'identifiant "+id1+".";
                 // Les rajoute un par un, sauf pour le supprimé
-                lu.forEach(id => {
-                    if (id!=uid) {
-                        Tools.add(id, gid, category).then(res => {
-                            if (!res) throw "Erreur lors du ré-ajout d'un autre membre d'une catégorie.";
+                l.forEach(id => {
+                    if (id!=id2) {
+                        if (domain2 == "group") var id_n = ldapConfig[domain2].gid;
+                        else                    var id_n = ldapConfig[domain2].uid;
+                        var catName = ldapConfig[domain1][category];
+                        Basics.change(domain1, id1, "add", {catName: id_n+'='+id+','+ldapConfig[domain2].dn}).then(res => {
+                            if (!res) throw "Erreur lors du ré-ajout d'un autre "+domain1+" de la catégorie "+category+".";
                         });
                     }
                 });
             }
-            // Si hérité, appel sans effet
-            else return true;
-        }
-        catch(err) {
-            throw "Erreur pour obtenir une liste de membres d'une catégorie d'un groupe pour supprimer un membre de cette categorie du groupe.";
         }
-        try {
-            // Vérifie que l'utilisateur est pas déjà viré pour user
-            let lg = await Tools.get(uid, "user", category);
-            let catName = ldapConfig.user[category];
-            if (lg.includes(gid)) {
-                // Supprime tous les groupes de la catégorie pour l'utilisateur
-                if (!await Basics.change("user", uid, "del", catName)) throw "Erreur lors de la suppression de tous les groupes d'un membre.";
+        catch(err) { throw "Erreur pour obtenir une liste d'entrées d'une catégorie d'un "+domain1+"."; }
+        return true;
+    }
+
+    /**
+     * @memberof LDAP
+     * @summary Fonction intermédiaire.
+     * @desc Cette fonction gère les inclusions de droits. Elle ne rajoute pas un admin pour les admins, mais elle le rajoute en tant que speaker.
+     * Cette fonction agit uniquement sur l'arbre User.
+     * @arg {string} uid - uid de l'utilisateur à ajouter
+     * @arg {string} gid - gid du groupe concerné
+     * @arg {"admins"|"speakers"|"members"|"followers"} category - Categorie de l'utilisateur concerné (admin, speaker, member ou follower)
+     * @return {Promise(boolean)} `true` si la modification s'est bien déroulée, false sinon
+     * @async
+     * @static
+     */
+    static async remIncluded(uid: string, gid: string, category: string): Promise<boolean> {
+        var spk = ldapConfig.group.speakers;
+        var mem = ldapConfig.group.members;
+        switch (category) {
+            case "admins":
+                // Admin => speaker
+                let l = await Tools.get(gid, "group", spk);
+                // Supprime tous les identifiants
+                if (!await Basics.change("group", gid, "del", spk)) throw "Erreur lors de la suppression de tous les portes-paroles de l'identifiant "+gid+".";
+                // Les rajoute un par un, sauf pour le supprimé
+                l.forEach(id => {
+                    if (id!=uid) {
+                        let tmp = ldapConfig.user.uid;
+                        Basics.change("group", gid, "add", {spk: tmp+'='+uid+','+ldapConfig.user.dn}).then(res => {
+                            if (!res) throw "Erreur lors du ré-ajout d'un autre membre de la catégorie porte-parole.";
+                        });
+                    }
+                });
+            case "speakers":
+                // Speaker & admin => member
+                let lm = await Tools.get(gid, "group", mem);
+                // Supprime tous les identifiants
+                if (!await Basics.change("group", gid, "del", mem)) throw "Erreur lors de la suppression de tous les portes-paroles de l'identifiant "+gid+".";
                 // Les rajoute un par un, sauf pour le supprimé
-                lg.forEach(id => {
-                    if (id!=gid) {
-                        Tools.add(uid, id, category).then(res => {
-                            if (!res) throw "Erreur lors du ré-ajout d'un autre groupe.";
+                l.forEach(id => {
+                    if (id!=uid) {
+                        let tmp = ldapConfig.user.uid;
+                        Basics.change("group", gid, "add", {mem: tmp+'='+uid+','+ldapConfig.user.dn}).then(res => {
+                            if (!res) throw "Erreur lors du ré-ajout d'un autre membre de la catégorie membre.";
                         });
                     }
                 });
-            }
+            case "members":
+            case "followers":
+                break;
         }
-        catch(err) {
-            throw "Erreur pour obtenir une liste de groupes d'une categorie d'un membre pour supprimer un groupe de cette category pour le membre.";
+        return true;
+    }
+
+    /**
+     * @memberof LDAP
+     * @summary Fonction intermédiaire de récursion.
+     * @desc Cette fonction gère les droits par récursion par une classique Depth First Search.
+     * Cette fonction agit uniquement sur l'arbre User. TBM
+     * @arg {string} uid - uid de l'utilisateur à ajouter
+     * @arg {string} gid - gid du groupe concerné
+     * @arg {boolean} direction - direction de la recursion
+     * @return {Promise(boolean)} `true` si la modification s'est bien déroulée, false sinon
+     * @async
+     * @static
+     */
+    static async remDFS(uid: string, gid: string, direction: boolean): Promise<boolean> {
+        // Cas récursif ascendant (admins)
+        if (direction) {
+            var rol = ldapConfig.group.admins;
+            var tov = ldapConfig.group.childs;
         }
-        
-        // Partie interne à l'X non propre à Sigma 
-        // Seule catégorie non récursive
-        if (category != "followers") {
-            if (category in ["admins", "members"]) {
-                // Cas récursif descendant
-                let to_visit = [gid];
-                let adm = ldapConfig.group.admins;
-                while (to_visit.length > 0) {
-                    let cur_gid = to_visit.pop();
-                    // Si uid était un admin hérité
-                    if (!(uid in await Tools.get(cur_gid, "group", "admins"))) {
-                        let lg = await Tools.get(uid, "user", "admins");
-                        if (!await Basics.change("user", uid, "del", adm)) {
-                            throw "Erreur lors de la suppression de tous les groupes d'un membre spé X.";
-                        }
-                        // Les rajoute un par un, sauf pour le supprimé
-                        lg.forEach(id => {
-                            if (id!=gid) Tools.add(uid, id, "admins").then(res => {
-                                if (!res) throw "Erreur lors du ré-ajout d'un autre groupe spé X.";
-                            });
-                        });                    
-                        to_visit.concat(await Basics.searchSingle("group", ldapConfig.group.childs, cur_gid));
-                    }
-                }
-            }
-            // Très violent ; virer un membre c'est aussi le virer de ses postes de porte-parole, d'admin et de groupes hérités
-            if (category == "members") {
-                let spk = ldapConfig.group.speakers;
-                let lg = await Tools.get(uid, "user", "speakers");
-                if (!await Basics.change("user", uid, "del", spk)) throw "Erreur lors de la suppression de tous les groupes d'un membre spé X.";
-                // Les rajoute un par un, sauf pour le supprimé
-                lg.forEach(id => {
-                    if (id!=gid) Tools.add(uid, id, "speakers").then(res => {
-                        if (!res) throw "Erreur lors du ré-ajout d'un autre groupe spé X.";
+        // Cas récursif descendant (membres)
+        else {
+            var rol = ldapConfig.group.members;
+            var tov = ldapConfig.group.parents;
+        }
+        // Classic DFS
+        var to_visit = [gid];
+        while (to_visit.length > 0) {
+            let cur_gid = to_visit.pop();
+            let l = await Tools.get(gid, "group", rol);
+            // Supprime tous les identifiants
+            if (!await Basics.change("group", gid, "del", rol)) throw "Erreur lors de la suppression de tous les "+rol+" de l'identifiant "+gid+".";
+            // Les rajoute un par un, sauf pour le supprimé
+            l.forEach(id => {
+                if (id!=uid) {
+                    let tmp = ldapConfig.user.uid;
+                    Basics.change("group", gid, "add", {rol: tmp+'='+uid+','+ldapConfig.user.dn}).then(res => {
+                        if (!res) throw "Erreur lors du ré-ajout d'un autre membre de la catégorie "+rol+".";
                     });
-                });
-                // Cas récursif montant
-                let to_visit = [gid];
-                let mem = ldapConfig.group.members;
-                while (to_visit.length > 0) {
-                    let cur_gid = to_visit.pop();
-                    // Si uid était un membre hérité
-                    if (!(uid in await Tools.get(cur_gid, "group", "admins"))
-                     && !(uid in await Tools.get(cur_gid, "group", "speakers"))
-                     && !(uid in await Tools.get(cur_gid, "group", "members"))) {
-                        let lg = await Tools.get(uid, "user", "members");
-                        if (!await Basics.change("user", uid, "del", mem)) throw "Erreur lors de la suppression de tous les groupes d'un membre spé X.";
-                        // Les rajoute un par un, sauf pour le supprimé
-                        lg.forEach(id => {
-                            if (id!=gid) Tools.add(uid, id, "admins").then(res => {
-                                if (!res) throw "Erreur lors du ré-ajout d'un autre groupe spé X.";
-                            });
-                        });                    
-                        to_visit.concat(await Basics.searchSingle("group", ldapConfig.group.parents, cur_gid));
-                    }
                 }
-            }
+            });
+            to_visit.concat(await Basics.searchSingle("group", tov, cur_gid));
+        }
+        return true;
+    }
+
+    /**
+     * @memberof LDAP
+     * @summary Fonction qui permet de supprimer un membre d'une catégorie existant d'un groupe.
+     * @desc Cette fonction fait essentiellement appel à d'autres fonctions de {@link Tools} passées en argument et {@link LDAP.change}.
+     * Elle essaie d'assurer les propriétés d'inclusion et de récursion du LDAP. Elle est sans effet pour un statut hérité.
+     * @arg {string} uid - Identifiant de l'ex-membre
+     * @arg {string} gid - Identifiant du groupe
+     * @arg {"admins"|"speakers"|"members"|"followers"} category - Categorie de l'utilisateur concerné (admin, speaker, member ou follower)
+     * @return {Promise(boolean)} `true` si la modification s'est bien déroulée, false sinon
+     * @async
+     * @static
+     */
+    static async remove(uid: string, gid: string, category : string): Promise<boolean> {
+        // Invulnérabilité pour les admins hérités
+        if ((await Tools.get(gid, "group", category)).includes(uid)) {
+            Tools.remIfPresent(uid, "user", gid, "group", category);
+            Tools.remIfPresent(gid, "group", uid, "user", category);
+            // Gestion des droits par inclusion
+            Tools.remIncluded(uid, gid, category);
+            // Gestion des droits récursive
+            if (category != "followers")    Tools.remDFS(uid, gid, false);
+            if (category == "admins")       Tools.remDFS(uid, gid, true);
         }
         return true;
     }
-- 
GitLab