From c6b563b499a385289711cff0f6dd3215b3316761 Mon Sep 17 00:00:00 2001
From: Oliver Facklam <oliver.facklam@polytechnique.edu>
Date: Tue, 26 Feb 2019 10:45:20 +0100
Subject: [PATCH] Delete child and supervisor ; new authorization

---
 ldap_config.json                    |   1 -
 src/app.ts                          |  16 +---
 src/graphql/models/authorization.ts |  47 ++---------
 src/graphql/models/tools.ts         | 119 ++++++++++++----------------
 4 files changed, 58 insertions(+), 125 deletions(-)

diff --git a/ldap_config.json b/ldap_config.json
index d981c3f..7eeba1c 100644
--- a/ldap_config.json
+++ b/ldap_config.json
@@ -53,7 +53,6 @@
 		"password": "userPassword",
 		"logo": "jpegPhoto",
 		"classes": "objectClass",
-		"childs": "child",
 		"parents": "parent"
 	}
 }
\ No newline at end of file
diff --git a/src/app.ts b/src/app.ts
index 3ad8748..1702ae6 100644
--- a/src/app.ts
+++ b/src/app.ts
@@ -209,7 +209,7 @@ const context = async ({ req }): Promise<Context> => {
         }
     }
     console.log(`Constructing context with uid = ${uid}`);
-    /*return {
+    let c = {
         request: req,
         user: { uid: uid },
         models: {
@@ -219,21 +219,9 @@ const context = async ({ req }): Promise<Context> => {
             message: new MessageModel(uid),
             request: new RequestModel(uid)
         }
-    };*/
-    let blah = {
-        request: req,
-        user: { uid: uid },
-        //models: null
-        models: {
-            auth: await AuthorizationModel.create(uid),
-            user: null,//new UserModel(uid),
-            group: null,//new GroupModel(uid),
-            message: null,//new MessageModel(uid),
-            request: null,//new RequestModel(uid)
-        }
     };
     console.log("finished constructing context");
-    return blah;
+    return c;
 };
 
 const server = new ApolloServer({
diff --git a/src/graphql/models/authorization.ts b/src/graphql/models/authorization.ts
index a4dc65c..97a18d7 100644
--- a/src/graphql/models/authorization.ts
+++ b/src/graphql/models/authorization.ts
@@ -8,14 +8,13 @@ import { Tools, GroupSet, GroupCollection } from "./tools";
 import { User as UT } from '../../ldap/export/user'
 
 /*
- * There are 7 levels of authorization
+ * There are 6 levels of authorization
  *  none : doesn't know the group / the user exists
  *  connectedOrOnplatal : knows the group exists, can use TOL
  *  viewer : can see the group
  *  member : part of the group
  *  speaker : allowed to speak for the group
  *  admin : admin of the group
- *  supervisor : allowed to take control of the group
  */
 
 export class AuthorizationModel {
@@ -55,7 +54,6 @@ export class AuthorizationModel {
     protected memberOf: GroupCollection;
     protected speakerOf: GroupCollection;
     protected adminOf: GroupCollection;
-    protected supervisorOf: GroupCollection;
 
     static PUBLICUSER = "public_user";
     static ONPLATALUSER = "public_onplatal_user";
@@ -72,11 +70,11 @@ export class AuthorizationModel {
         console.log("UT.peek returned with data:");
         //console.log(data);
 
-        this.viewerOf = await Tools.viewerOf(data);
         this.memberOf = await Tools.memberOf(data);
-        this.speakerOf = await Tools.viewerOf(data);
+        this.speakerOf = await Tools.speakerOf(data);
         this.adminOf = await Tools.adminOf(data);
-        this.supervisorOf = await Tools.supervisorOf(data);
+        this.viewerOf = await Tools.viewerOf(this.memberOf);
+        //console.log(this.viewerOf);
     }
 
     /**
@@ -232,12 +230,7 @@ export class AuthorizationModel {
         //ensure uid is valid !!!!!!!!
         if (this.isAuthenticated()) {
             let groups = this.adminOf;
-            let is_admin = groups.simpleGroups.has(gid) || groups.metaGroups.has(gid);
-            
-            //TODO : Vérifier s'il a pris les droits d'admin en étant supervisor
-            let took_admin_rights = false;
-
-            return (is_admin || took_admin_rights);
+            return (groups.simpleGroups.has(gid) || groups.metaGroups.has(gid));
         }
         return false;
     }
@@ -256,34 +249,4 @@ export class AuthorizationModel {
         return null;
     }
 
-    /**
-     * @memberof GraphQL.AuthorizationModel#
-     * @function isSupervisor
-     * @summary Fonction qui renvoit si l'utilisateur est supervisor du groupe.
-     * @arg {string} gid - Identifiant du groupe.
-     * @return {boolean} Renvoie true si l'utilisateur est supervisor du groupe.
-     */
-    isSupervisor(gid: string): boolean {
-        //ensure uid is valid !!!!!!!!
-        if (this.isAuthenticated()) {
-            let groups = this.supervisorOf;
-            return (groups.simpleGroups.has(gid) || groups.metaGroups.has(gid));
-        }
-        return false;
-    }
-
-    /**
-     * @memberof GraphQL.AuthorizationModel#
-     * @function groupsSupervisor
-     * @summary Fonction qui renvoit les groupes dont l'utilisateur est supervisor.
-     * @return {GroupCollection} Renvoie la collection de groupes dont l'utilisateur est supervisor.
-     */
-    groupsSupervisor(): GroupCollection {
-        //ensure uid is valid !!!!!!!!
-        if (this.isAuthenticated()) {
-            return this.supervisorOf;
-        }
-        return null;
-    }
-
 }
\ No newline at end of file
diff --git a/src/graphql/models/tools.ts b/src/graphql/models/tools.ts
index 1e01268..1b24e6b 100644
--- a/src/graphql/models/tools.ts
+++ b/src/graphql/models/tools.ts
@@ -10,6 +10,7 @@
  */
 
 import { userData } from '../../ldap/export/user'
+import { groupData, Group as GT } from '../../ldap/export/group';
 import knex from '../../../db/knex_router';
 
 export class GroupSet extends Set<string> {
@@ -70,8 +71,8 @@ export class Tools {
      * @async
      */
     static async memberOfSimple(data: userData): Promise<GroupSet> {
-        throw "Not implemented";
-        return new GroupSet(data.members);
+        //Do a DFS from data.members to find all parents
+        return Tools.DFS(data.members, 'parent');
     }
 
     /**
@@ -83,9 +84,7 @@ export class Tools {
      * @async
      */
     static async speakerOfSimple(data: userData): Promise<GroupSet> {
-        let speaker = new GroupSet(data.speakers);
-        speaker.addList(data.admins);
-        return speaker;
+        return new GroupSet(data.speakers);
     }
 
     /**
@@ -97,8 +96,8 @@ export class Tools {
      * @async
      */
     static async adminOfSimple(data: userData): Promise<GroupSet> {
-        throw "Not implemented";
-        return new GroupSet(data.admins);
+        //Do a DFS from data.admins to find all children
+        return Tools.DFS(data.admins, 'child');
     }
 
     /**
@@ -110,7 +109,7 @@ export class Tools {
      * @async
      */
     static async metaGroupsOfGroups(groups: GroupSet): Promise<GroupSet> {
-        let metas = await knex.select('meta_group_gid').from('metagroup_memberships').whereIn('simple_group_gid', groups);
+        let metas = await knex.select('meta_group_gid').from('metagroup_memberships').whereIn('simple_group_gid', [...groups]);
         return new GroupSet(metas.map( elt => {
             return elt.meta_group_gid;
         }));
@@ -139,8 +138,7 @@ export class Tools {
      */
     static async speakerOf(data: userData): Promise<GroupCollection> {
         let speaker = await Tools.speakerOfSimple(data);
-        let admin = await Tools.adminOfSimple(data);
-        return { simpleGroups: speaker, metaGroups: await Tools.metaGroupsOfGroups(admin) };
+        return { simpleGroups: speaker, metaGroups: await Tools.metaGroupsOfGroups(new GroupSet(data.admins)) };
     }
 
     /**
@@ -158,19 +156,36 @@ export class Tools {
 
     /**
      * @memberof GraphQL
-     * @summary Fonction qui fait un parcours en profondeur de l'arbre de racine `gid` et renvoie la liste de tous les noeuds.
-     * @arg {string} gid - Identifiant du groupe, supposé valide.
-     * @return {Promise(string[])} Renvoie une liste contenant le nom des groupes dans cet arbre.
+     * @summary Fonction qui fait un parcours en profondeur du graphe a partir de 'roots' et renvoie la liste de tous les noeuds dans la direction donnée.
+     * @arg {string[]} roots - Identifiants des groupes racine, supposés valides.
+     * @arg {'parent'|'child'} direction - Direction de la recherche. 'parent' cherche tous les noeuds ascendants. 'child' cherche tous les noeuds descendants.
+     * @return {Promise(GroupSet)} Renvoie un ensemble contenant le nom des groupes dans cette direction.
      * @static
      * @async
      */
-    static async DFS(gid: string): Promise<string[]> {
-        let res = [gid];
-        let children = await knex.select('gid').from('groups').where('parent_gid', gid);
-
-        for(let child of children) {
-            let sub_tree = await Tools.DFS(child.gid);
-            res = res.concat(sub_tree);
+    static async DFS(roots : string[], direction : 'parent'|'child'): Promise<GroupSet> {
+        let stack = roots.slice();
+        let visited = {};
+        let res = new GroupSet();
+
+        while(stack.length > 0) {
+            let gid = stack.pop();
+            if(visited[gid] !== true) {
+                visited[gid] = true;
+                res.add(gid);
+
+                //console.log(gid);
+                if(direction === 'child') {
+                    let gd = new groupData();
+                    gd.parents = [gid]; //on cherche les enfants, ie tous les groupes qui ont `gid` comme parent
+                    stack.push(...await GT.search(gd));
+                }
+                else {
+                    let data = await GT.peek(gid);
+                    if(data.parents !== undefined) stack.push(...data.parents)
+                }                  
+                
+            }
         }
 
         return res;
@@ -178,79 +193,47 @@ export class Tools {
 
     /**
      * @memberof GraphQL
-     * @summary Fonction qui fait un parcours a profondeur 1 de l'arbre de racine `gid` et renvoie la liste des noeuds.
-     * @arg {string} gid - Identifiant du groupe, supposé valide.
-     * @return {Promise(string[])} Renvoie une liste contenant le nom des groupes a profondeur 1.
+     * @summary Fonction qui renvoie la liste des enfants des noeuds de `roots`.
+     * @arg {GroupSet} roots - Identifiants des groupes racine, supposés valides.
+     * @return {Promise(string[])} Renvoie une liste contenant le nom des groupes enfants (!! doublons !!).
      * @static
      * @async
      */
-    static async oneDownSearch(gid: string): Promise<string[]> {
-        let res = [gid];
-        let children = await knex.select('gid').from('groups').where('parent_gid', gid);
+    static async oneDownSearch(roots: GroupSet): Promise<string[]> {
+        let res = [];
 
-        for (let child of children) {
-            res.push(child.gid);
+        for(let r of roots) {
+            let gd = new groupData();
+            gd.parents = [r]; //on cherche les enfants, ie tous les groupes qui ont `r` comme parent
+            res.push(...await GT.search(gd));
         }
 
         return res;
     }
 
-    /**
-     * @memberof GraphQL
-     * @summary Fonction qui renvoie tous les groupes dont l'utilisateur est supervisor.
-     * @desc Utilise {@link Tools.oneDownSearch} pour avoir la profondeur 1 des arbres enracinés en chacun des groupes dont il est admin.
-     * @arg {userData} data - Données du user.
-     * @return {Promise(GroupCollection)} Renvoie une GroupCollection contenant le nom des groupes.
-     * @static
-     * @async
-     */
-    static async supervisorOf(data: userData): Promise<GroupCollection> {
-        let groups = await Tools.adminOf(data);
-
-        let simple = new GroupSet();
-        let meta = new GroupSet();
-
-        for(let g of groups.simpleGroups) {
-            simple.addList(await Tools.oneDownSearch(g));
-        }
-        for(let g of groups.metaGroups) {
-            meta.addList(await Tools.oneDownSearch(g));
-        }
-    
-        return { simpleGroups: simple, metaGroups: meta };
-    }
-
     /**
      * @memberof GraphQL
      * @summary Fonction qui renvoie tous les groupes dont l'utilisateur est viewer.
      * @desc Utilise {@link Tools.oneDownSearch} pour avoir la profondeur 1 des arbres enracinés en chacun des groupes dont il est membre.
-     * @arg {userData} data - Données du user.
+     * @arg {GroupCollection} groups - Groupes dont le user est membre hérité.
      * @return {Promise(GroupCollection)} Renvoie une GroupCollection contenant le nom des groupes.
      * @static
      * @async
      */
-    static async viewerOf(data: userData): Promise<GroupCollection> {
-        let groups = await Tools.memberOf(data);
+    static async viewerOf(groups: GroupCollection): Promise<GroupCollection> {
+        let simple = groups.simpleGroups;
 
-        let simple = new GroupSet();
-        let meta = new GroupSet();
-
-        //Ajouter les groupes dont il est membre + les fils a profondeur 1
-        for(let g of groups.simpleGroups) {
-            simple.addList(await Tools.oneDownSearch(g));
-        }
-        for(let g of groups.metaGroups) {
-            meta.addList(await Tools.oneDownSearch(g));
-        }
+        //Ajouter les groupes enfants
+        simple.addList(await Tools.oneDownSearch(simple));
 
         //Ajouter les groupes simples qui sont dans ses métagroupes
-        let s = await knex.select('simple_group_gid').from('metagroup_memberships').whereIn('meta_group_gid', groups.metaGroups);
+        let s = await knex.select('simple_group_gid').from('metagroup_memberships').whereIn('meta_group_gid', [...groups.metaGroups]);
         simple.addList(s.map( elt => {
             return elt.simple_group_gid;
         }));
 
         //TODO : rajouter les VisibilityEdges
 
-        return { simpleGroups: simple, metaGroups: meta };
+        return { simpleGroups: simple, metaGroups: await Tools.metaGroupsOfGroups(simple) };
     }
 }
\ No newline at end of file
-- 
GitLab