From 99e1be282a97dcea2cfd68bed118cb8735fb92ed Mon Sep 17 00:00:00 2001
From: hawkspar <quentin.chevalier@polytechnique.edu>
Date: Sat, 20 Oct 2018 12:30:51 +0200
Subject: [PATCH] Refacto de l'infini

---
 ldap_config.json                          |   5 +-
 src/graphql/typeDefs/objects_ldap.graphql |   2 +-
 src/ldap/basics.ts                        |   3 +-
 src/ldap/group.ts                         |  22 ++---
 src/ldap/user.ts                          | 114 +++++++++++++---------
 src/ldap/utilities.ts                     |  47 ++++-----
 6 files changed, 102 insertions(+), 91 deletions(-)

diff --git a/ldap_config.json b/ldap_config.json
index 60790a9..dbb5dc3 100644
--- a/ldap_config.json
+++ b/ldap_config.json
@@ -20,9 +20,8 @@
 		"nationality": "country",
 		"promotion": "brPromo",
 		"phone": "telephoneNumber",
-		"adress": "brRoom",
+		"adresses": "brRoom",
 		"id": "uidNumber",
-		"sport": "brMemberOf",
 		"password": "userPassword",
 		"idNum": "gidNumber",
 		"directory": "homeDirectory",
@@ -33,8 +32,6 @@
 		"ips": "brIP",
 		"forlifes": "brAlias",
 		"groups": "brMemberOf",
-		"schools": "brMemberOf",
-		"courses": "brMemberOf",
 		"classes": "objectClass"
 	},
 	"comment_4": "Placeholders et indications de contenu de certains champs du LDAP généré par frankiz pour les groupes",
diff --git a/src/graphql/typeDefs/objects_ldap.graphql b/src/graphql/typeDefs/objects_ldap.graphql
index 577b693..3efc076 100644
--- a/src/graphql/typeDefs/objects_ldap.graphql
+++ b/src/graphql/typeDefs/objects_ldap.graphql
@@ -19,7 +19,7 @@ type User {
     likes: [Group]
     # A terme rajouter aussi admin
     # Adresse(s) de l'utilisateur.
-    address: [String]
+    addresses: [String]
     # Promotion
     promotion: String
     photo: String
diff --git a/src/ldap/basics.ts b/src/ldap/basics.ts
index 85cf2d6..e2ab6e0 100644
--- a/src/ldap/basics.ts
+++ b/src/ldap/basics.ts
@@ -116,10 +116,11 @@ export class LDAP {
                 });
                 // Si la recherche renvoie une erreur, on renvoit
                 res.on('error', resErr => { throw resErr; });
-                // Si la recherche est finie on se déconnecte et on renvoit la liste
+                // Si la recherche est finie on se déconnecte
                 res.on('end', res => { LDAP.unbind(); });
             }
         });
+        // On renvoit le résultat
         return vals;
     }
 
diff --git a/src/ldap/group.ts b/src/ldap/group.ts
index ab04387..2b7005a 100644
--- a/src/ldap/group.ts
+++ b/src/ldap/group.ts
@@ -3,9 +3,9 @@
  * @author hawkspar
  */
 
-import { ldapConfig } from './config';
+import {ldapConfig} from './config';
 import {LDAP} from './basics';
-import {Tests} from './utilities';
+import {Tools} from './utilities';
 
 /**
  * @interface groupData
@@ -94,7 +94,7 @@ export class Group {
     static async addMember(uid: string, gid: string) : Promise<boolean> {
         try {
             // Vérifie que l'utilisateur est pas déjà membre pour groupes
-            let lm = await Tests.getMembers(gid);
+            let lm = await Tools.getMembers(gid);
             if (!lm.includes(uid)) {
                 let vals = {};
                 vals[ldapConfig.group.members] = uid;
@@ -109,7 +109,7 @@ export class Group {
         }
         try {
             // Vérifie que l'utilisateur est pas déjà membre pour users
-            let lg = await Tests.getGroups(uid);
+            let lg = await Tools.getGroups(uid);
             if (!lg.includes(gid)) {
                 let vals2 = {};
                 vals2[ldapConfig.user.groups] = gid;
@@ -137,7 +137,7 @@ export class Group {
     static async delMember(uid: string, gid: string): Promise<boolean> {
         try {
             // Vérifie que l'utilisateur est pas déjà viré pour groupes
-            let lm = await Tests.getMembers(gid);
+            let lm = await Tools.getMembers(gid);
             if (lm.includes(uid)) {
                 // Supprime tous les utilisateurs
                 if (!await LDAP.change("gr", gid, "del", ldapConfig.group.members)) {
@@ -157,7 +157,7 @@ export class Group {
             throw "Erreur pour obtenir une liste de membres d'un groupe pour supprimer un membre du groupe.";
         }
         try {
-            let lg = await Tests.getGroups(uid);
+            let lg = await Tools.getGroups(uid);
             // Vérifie que l'utilisateur est pas déjà viré pour users
             if (lg.includes(gid)) {
                 // Supprime tous les groupes
@@ -194,7 +194,7 @@ export class Group {
         // Ajoute le membre au groupe avant d'en faire un admin
         if (!await Group.addMember(uid,gid)) { throw "Erreur lors de l'ajout du futur admin en tant que membre."; }
         try {
-            let la = await Tests.getAdmins(gid);
+            let la = await Tools.getAdmins(gid);
             if (!la.includes(uid)) {
                 // Finalement modification, uniquement dans groups
                 let vals = {};
@@ -224,7 +224,7 @@ export class Group {
         if (!(await Group.delMember(uid, gid) && Group.addMember(uid,gid))) { throw "Erreur dans l'éjection/réadmission du futur admin."; }
         try {
             // Vérifie que l'utilisateur est bien admin (comme dans delGroupMember)
-            let la = await Tests.getAdmins(gid);
+            let la = await Tools.getAdmins(gid);
             if (la.includes(uid)) {
                 // Supprime tous les administrateurs
                 if (!await LDAP.change("gr", gid, "del", ldapConfig.group.admins)) { throw "Erreur dans la suppression de tous les admins pour en supprimer un."; }
@@ -257,7 +257,7 @@ export class Group {
 
         // gid de base généré à partir du nom standardisé, pas à partir de l'entrée 'gid' !
         try {
-            Tests.generateReadableId(data['name']).then(id => {
+            Tools.generateReadableId(data['name']).then(id => {
                 vals[ldapConfig.key_id]=id;
                 vals[ldapConfig.group['name']]=id;
             });
@@ -278,7 +278,7 @@ export class Group {
         // Certains champs nécessitent de petits calculs
         let vals2={};
 
-        // Encore un cahmp redondant
+        // Encore un champ redondant
         vals2[ldapConfig.group['adress']] = gid;
 
         // ?!
@@ -286,7 +286,7 @@ export class Group {
 
         // Génération id aléatoire et test contre le LDAP
         try {
-            Tests.generateId(ldapConfig.group["idNumber"], "gr").then(id => { vals2[ldapConfig.group['idNumber']]=id; });
+            Tools.generateId(ldapConfig.group["idNumber"], "gr").then(id => { vals2[ldapConfig.group['idNumber']]=id; });
         }
         catch(err) {
             throw "Erreur lors de la génération d'un id numérique pour créer un nouveau groupe.";
diff --git a/src/ldap/user.ts b/src/ldap/user.ts
index 3652c9e..7f1ab31 100644
--- a/src/ldap/user.ts
+++ b/src/ldap/user.ts
@@ -4,60 +4,73 @@
  * @author hawkspar
  */
 
-import { ldapConfig } from './config';
+import {ldapConfig} from './config';
 import {LDAP} from './basics';
-import {Tests} from './utilities';
+import {Tools} from './utilities';
 import {Group} from './group';
 
 /**
  * @interface userData
  * @desc Interface avec toutes les données extractables pour un utilisateur.
+ * @var {string} uid - Identifiant utilisateur
  * @var {string} givenName - Prénom
  * @var {string} lastName - Nom
  * @var {string} nickname - Surnom
  * @var {string} photo - Bytestring de la photo de l'utilisateur
  * @var {string} birthdate - Date d'anniversaire
+ * TBA @var {string} nationality - Nationalité d'origine
  * @var {string} promotion - Année(s) de promo
  * @var {string} phone - Numéro(s) de téléphone
- * @var {string[]} mail - Adresse(s) courriel
- * @var {string[]} adress - Adresses
- * @var {string} groups - Un ou plusieurs groupes dont l'utilisateur est membre (inclus section sportive, binet, PA...)
- * @var {string} password - Mot de passe généré en amont
- * @var {string[]} ips - Adresse(s) ip
- * @arg {string} readPerm - Permissions spéciales BR
- * @var {string} writePerm - Permissions spéciales BR
- * @var {string[]} forlifes - Alias BR (attention le filtre .fkz n'est plus fonctionnel)
- * @var {string[]} groupsIsAdmin - Liste des gid dont le pax est admin ; supposé sous-liste de groups
+ * @var {string[]} adresses - Adresse(s)
+ * @var {string[]} mails - Adresse(s) courriel
+ * @var {string[]} groups - Un ou plusieurs groupes dont l'utilisateur est membre (inclus section sportive, binet, PA...)
+ * @var {string[]} admins - Liste des gid dont l'utilisateur est admin ; supposé sous-liste de groups
+ * TBA @var {string[]} likes - Liste des gid dont l'utilisateur est sympathisant
  */
 export interface userData {
-    "uid": string,
-    "photo"?: string,
+    "uid"?: string,
     "givenName"?: string,
     "lastName"?: string,
-    "fullName"?: string,
-    "cleanFullName": string,
     "nickname"?: string,
+    "photo"?: string,
     "birthdate"?: string,
-    "nationality"?: string,
-    "promotion": string,
+    //"nationality"?: string,
+    "promotion"?: string,
     "phone"?: string,
-    "adress"?: string,
-    "id"?: string,
-    "sport"?: string,
+    "adresses"?: string,
+    "mails"?: string[],
+    "groups"?: string[],
+    "admins"?: string[]
+    //"likes"?: string[]
+}
+
+/**
+ * @interface fullUserData
+ * @desc Contient les champs utilisateurs intéressants pour le LDAP de l'X, pas pour sigma
+ * @var {string} fullName - Nom complet
+ * @var {string} password - Mot de passe généré en amont
+ * @var {string[]} ips - Adresse(s) ip
+ * @var {string} directory - Adresse soft des données utilisateurs
+ * @var {string} login - Astuce de root flemmard
+ * @arg {string} readPerm - Permissions spéciales BR
+ * @var {string} writePerm - Permissions spéciales BR
+ * @var {string[]} forlifes - Alias BR (attention le filtre .fkz n'est plus fonctionnel)
+ * @var {string[]} classes - Classes du LDAP, pas de sens particulier
+ * @var {string} id - Identifiant plus complet
+ * @var {string} idNum - Identifiant numérique
+ */
+export interface fullUserData extends userData {
+    "fullName"?: string,
     "password"?: string,
-    "idNum"?: string,
+    "ips"?: string[],
     "directory"?: string,
     "login"?: string,
     "readPerm"?: string,
     "writePerm"?: string,
-    "mails"?: string[],
-    "ips"?: string[],
     "forlifes"?: string[],
-    "groups": string[],
-    "groupsIsAdmin": string[],
-    "schools"?: string[],
-    "courses"?: string[],
-    "classes"?: string[]
+    "classes"?: string[],
+    "id"?: string,
+    "idNum"?: string
 }
 
 //------------------------------------------------------------------------------------------------------------------------
@@ -68,24 +81,25 @@ export class User {
     /**
      * @class Cette classe est une des deux classes exportables permettant de faire des opérations sur les utilisateurs.
      * @summary Constructeur vide.
-    */
+     */
     constructor() {}
-     
+    
     /**
-     * @summary Fonction qui renvoit toutes les infos relatives à un utilisateur particulier.
+     * @summary Fonction qui renvoit certaines informations relatives à un utilisateur particulier.
      * @desc Cette fonction utilise {@link LDAP.search} avec des attributs prédéfinis.
      * @arg {string} uid - Identifiant de l'utilisateur
-     * @return {Promise(userData)} Informations recueillies ; renvoie une liste de dictionnaire avec le profil complet de l'utilisateur ;
-     * voir `ldap_ldapConfig.json`(..\..\ldap_ldapConfig.json) pour les clés exactes.
+     * @return {Promise(T)} Informations recueillies ; renvoie une partie du profil de l'utilisateur selon le format choisi.
+     * Voir `ldap_ldapConfig.json`(..\..\ldap_ldapConfig.json) pour les clés exactes.
      * @static
      * @async
+     * @private
      */
-    static async peek(uid: string) : Promise<userData> {
+    private static async genericPeek<T>(uid: string) : Promise<T> {
         try {
             let fields = [];
             fields.push(ldapConfig.user.values());
             let LDAPUserData = await LDAP.search("us", fields, uid);
-            let cleanUserData : userData;
+            let cleanUserData : T;
             // Rename output
             for (let uncleanKey in LDAPUserData) {
                 for (let cleanKey in cleanUserData) {
@@ -98,6 +112,16 @@ export class User {
             throw "Erreur lors d'une recherche d'informations sur un individu.";
         }
     }
+     
+    /**
+     * @summary Fonction qui renvoit les infos de base relatives à un utilisateur particulier.
+     * @desc Cette fonction utilise {@link User.genericPeek} avec l'interface {@link userData}.
+     * @arg {string} uid - Identifiant de l'utilisateur
+     * @return {Promise(userData)} Informations recueillies.
+     * @static
+     * @async
+     */
+    static async peek(uid: string) : Promise<userData> { return User.genericPeek<userData>(uid); }
     
     /**
      * @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é à repliquer TOL
@@ -142,19 +166,19 @@ export class User {
     /**
      * @summary Fonction qui créé un nouvel utilisateur dans le LDAP.
      * @desc Appelle {@link LDAP.add} bien sûr, mais aussi {@link User.addGroupMember} et {@link Admin.addGroupAdmin} pour gérer les groupes du nouvel utilisateur.
-     * @arg {userData} data - Dictionnaire des informations utilisateurs. Des erreurs peuvent apparaître si tous les champs ne sont pas remplis.
+     * @arg {fullUserData} data - Dictionnaire des informations utilisateurs. Des erreurs peuvent apparaître si tous les champs ne sont pas remplis.
      * @return {Promise(boolean)} `true` si la modification s'est bien déroulée, false sinon
      * @async
      * @static
      */
-    static async create(data: userData): Promise<boolean> {
+    static async create(data: fullUserData): Promise<boolean> {
         // Calcul d'un dictionnaire d'ajout
         let vals = {};
 
         // uid de base généré à partir de nom et prénom, plus potentiellement promo et un offset
         // MEF mélange de Promise et de fonction standard
         try {
-            Tests.generateUid(data['givenName'],data['lastName'],data['promotion']).then(id => { vals[ldapConfig.key_id]=id; } );
+            Tools.generateUid(data['givenName'],data['lastName'],data['promotion']).then(id => { vals[ldapConfig.key_id]=id; } );
         }
         catch(err) {
             throw "Erreur lors de la génération d'un hruid pour un nouvel utilisateur.";
@@ -201,7 +225,7 @@ export class User {
         }
         try {
             // Génération id aléatoire unique
-            vals3[ldapConfig.user['id']]= await Tests.generateId(ldapConfig.user['id'], "us");
+            vals3[ldapConfig.user['id']]= await Tools.generateId(ldapConfig.user['id'], "us");
         }
         catch(err) {
             throw "Erreur lors de la génération d'un id numérique pour un nouvel utilisateur.";
@@ -273,7 +297,7 @@ export class User {
             let profil = await User.peek(uid);
             profil[ldapConfig.user['groups']].forEach(gid => {
                 // Opérations effectuées par effet de bord
-                if (Tests.isGroupAdmin(uid,gid)) {
+                if (Tools.isGroupAdmin(uid,gid)) {
                     if (!Group.delAdmin(uid, gid)) { throw "Erreur lors de la suppression des droits d'admin de l'utilisateur."; }
                 }
                 if (!Group.delMember(uid, gid)) { throw "Erreur lors de la suppression de l'appartenance à un groupe de l'utilisateur."; }
@@ -292,23 +316,23 @@ export class User {
      * @desc Appelle simplement {@link creerUtilisateur} et {@link supprimerUtilisateur} en godmode, plus {@link renseignerSurUtilisateur} pour les champs non fournis.
      * Ce choix a pour conséquence que l'ordre du dictionnaire de correspondance dans ldap_ldapConfig est important.
      * Une version "nerfée" de cette fonction est envisageable ; elle donne bcp de pouvoir à l'utilisateur.
-     * @arg {userData} data - Dictionnaire des informations utilisateurs au même format que pour {@link creerUtilisateur} avec tous les champs optionnels sauf 'uid',
+     * @arg {fullUserData} data - Dictionnaire des informations utilisateurs au même format que pour {@link creerUtilisateur} avec tous les champs optionnels sauf 'uid',
      * qui permet de savoir qui modifier. Attention toutes les clés de cette entrée seront modifiées dans le LDAP ; les nouveaux résultats écrasant les précédents,
      * sauf 'readPerm','writePerm', 'forlifes','ips','groups' et 'groupsIsAdmin' qui sont censurés pour cette fonction).
      * @return {Promise(boolean)} `true` si la modification s'est bien déroulée, false sinon
      * @async
      * @static
      */
-    static async edit(data : userData) : Promise<boolean> {
+    static async edit(data : fullUserData) : Promise<boolean> {
         let uid = data['uid'];
         // Récupération des anciennes données
-        let profil = await User.peek(uid);
+        let profil = await User.genericPeek<fullUserData>(uid);
         try {
             // Régénération du champ manquant dans profil
-            let lg = await Tests.getGroups(uid);
+            let lg = await Tools.getGroups(uid);
             profil['groupsIsAdmin']=[];
             lg.forEach(gid => {
-                Tests.isGroupAdmin(uid, gid).then(res => {
+                Tools.isGroupAdmin(uid, gid).then(res => {
                     if (res) { profil['groupsIsAdmin'].push(gid); }
                 });
             });
diff --git a/src/ldap/utilities.ts b/src/ldap/utilities.ts
index f336794..0791636 100644
--- a/src/ldap/utilities.ts
+++ b/src/ldap/utilities.ts
@@ -6,15 +6,14 @@
 
 import {ldapConfig} from './config';
 import {LDAP} from './basics';
-import {Group} from './group';
 
 //------------------------------------------------------------------------------------------------------------------------
 // Fonctions intermédiaires TBT
 //------------------------------------------------------------------------------------------------------------------------
 
-export class Tests {
+export class Tools {
     /**
-     * @class Cette classe contient des fonctions de test d'unicité trop puissantes pour être exportées tel quel.
+     * @class Cette classe contient des fonctions intermédiaires qui ne sont pas destinées à être utilisées dans les resolvers.
      * @summary Constructeur vide.
      * @author hawkspar
     */
@@ -49,7 +48,7 @@ export class Tests {
                 // On renvoit la valeur si elle est bien unique
                 else if (matches.length==0) { return value; }
                 // Sinon, on tente de nouveau notre chance avec la valeur suivante
-                else { return Tests.ensureUnique(changeValue(value, n+1), attribute, domain, changeValue, n+1); }
+                else { return Tools.ensureUnique(changeValue(value, n+1), attribute, domain, changeValue, n+1); }
             });
         }
         catch(err) {
@@ -59,7 +58,7 @@ export class Tests {
 
     /**
      * @summary Cette fonction génère un uid standard, puis le fait évoluer jusqu'à ce qu'il soit unique.
-     * @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).
+     * @desc Limité à un appel à {@link Tools.ensureUnique} avec les bons paramètres, et quelques opérations sur l'uid pour qu'il soit valide (escape, normalisation).
      * @param {string} givenName - Prénom
      * @param {string} lastName - Nom
      * @param {string} promotion - Année de promotion
@@ -70,7 +69,7 @@ export class Tests {
     static async generateUid(givenName: string, lastName: string, promotion: string) : Promise<string> {
         try {
             // normalize et lowerCase standardisent le format
-            return this.ensureUnique((givenName+'.'+lastName).toLowerCase().normalize('UFD'), ldapConfig.key_id, "us", (id: string, n: number) => {
+            return Tools.ensureUnique((givenName+'.'+lastName).toLowerCase().normalize('UFD'), ldapConfig.key_id, "us", (id: string, n: number) => {
                 if (n==1) { id+='.'+promotion; }                // Si prénom.nom existe déjà, on rajoute la promo
                 else if (n==2) { id+='.'+(n-1).toString(); }    // Puis si prénom.nom.promo existe déjà on passe à nom.prenom.promo .1
                 else if (n>2) { id+=n; }                        // Ensuite on continue .123, .1234, etc...
@@ -84,7 +83,7 @@ export class Tests {
 
     /**
      * @summary Cette fonction génère un id lisible, puis le fait évoluer jusqu'à ce qu'il soit unique.
-     * @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).
+     * @desc Limité à un appel à {@link Tools.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
@@ -93,7 +92,7 @@ export class Tests {
     static async generateReadableId(name: string) : Promise<string> {
         try {
             // normalize et lowerCase standardisent le format
-            return this.ensureUnique(name.toLowerCase().normalize('UFD'), ldapConfig.key_id, "gr", (id: string, n: number) => {
+            return Tools.ensureUnique(name.toLowerCase().normalize('UFD'), ldapConfig.key_id, "gr", (id: string, n: number) => {
                 if (n==1) { id+='.'+n.toString(); }   // Si nom existe déjà, on essaie nom.1
                 else if (n>1) { id+=n.toString(); }   // Ensuite on continue .12, .123, etc...
                 return id;
@@ -114,7 +113,7 @@ export class Tests {
      */
     static async generateId(attribut: string, domain: "gr"|"us") : Promise<string> {
         try {
-            return this.ensureUnique("0", attribut, domain, (id,n) => { return Math.floor((Math.random() * 100000) + 1).toString(); });
+            return Tools.ensureUnique("0", attribut, domain, (id,n) => { return Math.floor((Math.random() * 100000) + 1).toString(); });
         }
         catch(err) {
             throw "Erreur lors de l'assurance de l'unicité d'un unique identifier numérique.";
@@ -129,7 +128,7 @@ export class Tests {
      * @static
      * @async
      */
-    static async getGroups(uid: string) {
+    static async getGroups(uid: string) : Promise<string[]> {
         try {
             return LDAP.search("us", [ldapConfig.user.groups], uid)[0];
         }
@@ -142,11 +141,11 @@ export class Tests {
      * @summary Fonction qui retrouve la liste des membres d'un groupe.
      * @desc Cette fonction utilise {@link LDAP.search} avec un dictionnaire prédéfini dans ldapConfig.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)
+     * @return {Promise(string[])} Liste des uid des membres où l'id fournie est membre (noms flat des groupes)
      * @static
      * @async
      */
-    static async getMembers(gid: string) {
+    static async getMembers(gid: string) : Promise<string[]> {
         try {
             return LDAP.search("gr", [ldapConfig.group.members], gid)[0];
         }
@@ -163,7 +162,7 @@ export class Tests {
      * @static
      * @async
      */
-    static async getAdmins(gid: string) {
+    static async getAdmins(gid: string) : Promise<string[]> {
         try {
             return LDAP.search("gr", [ldapConfig.group.admins], gid)[0];
         }
@@ -181,18 +180,13 @@ export class Tests {
      * @static
      * @async
      */
-    static async isGroupMember(uid: string, gid: string) {
+    static async isGroupMember(uid: string, gid: string) : Promise<boolean> {
         try {
-            let lg = await Tests.getGroups(uid);
-            let lm = await Tests.getMembers(gid);
+            let lg = await Tools.getGroups(uid);
+            let lm = await Tools.getMembers(gid);
             if (lg.includes(gid) && lm.includes(uid)) {
                 return true;
             }
-            // Réalignement forcé
-            else if (lg.includes(gid) || lm.includes(uid)) {
-                Group.addMember(uid, gid);
-                return true;
-            }
             else { return false; }
         }
         catch(err) {
@@ -209,16 +203,11 @@ export class Tests {
      * @static
      * @async
      */
-    static async isGroupAdmin(uid: string, gid: string) {
+    static async isGroupAdmin(uid: string, gid: string) : Promise<boolean> {
         try {
-            let lm = await Tests.getMembers(gid);
-            let la = await Tests.getAdmins(gid);
+            let lm = await Tools.getMembers(gid);
+            let la = await Tools.getAdmins(gid);
             if (la.includes(uid) && lm.includes(uid)) { return true; }
-            // Réalignement forcé
-            else if (la.includes(uid)) {
-                Group.addAdmin(uid, gid);
-                return true;
-            }
             else { return false; }
         }
         catch(err) {
-- 
GitLab