Skip to content
Snippets Groups Projects
Forked from an inaccessible project.
tools.ts 8.85 KiB
/**
 * Namespace qui regroupe toutes les classes et toutes les fonctions qui permettent l'implémentation des resolvers du schéma GraphQL
 * @namespace GraphQL
 */

/** 
 * @file Fonctions génériques de recherche dans la BDD et le LDAP réutilisables dans l'authorization et/ou dans les resolvers
 * @author ofacklam
 * @memberof GraphQL
 */

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> {
    addList(l: string[]) {
        for(let elt of l) this.add(elt);
    }
}
export interface GroupCollection {
    simpleGroups: GroupSet,
    metaGroups: GroupSet
}

export class Tools {

    /**
     * @memberof GraphQL
     * @class Tools
     * @summary Outils intermédiaires LDAP / BDD.
     * @classdesc Cette classe contient des fonctions intermédiaires de recherche dans le LDAP / la BDD.
     */
    constructor() { }

    /**
     * @memberof GraphQL
     * @summary Fonction qui escape l'id donné en argument
     * @arg {string} id - L'identifiant a escape.
     * @return {Promise(Group)} Renvoie l'identifiant escapé.
     * @static
     */
    static escapeID(id: string) {
        return String(id).replace(' ', '_').replace(/\W/g, '').toLowerCase(); //the REGEXP matches all non-word characters (\W) in a global search (/../g)
    }

    /**
     * @memberof GraphQL
     * @summary Fonction qui renvoit l'union de deux ensembles
     * @arg {GroupSet} A - Le premier ensemble.
     * @arg {GroupSet} B - Le deuxieme ensemble.
     * @return {Promise(GroupSet)} Renvoie un nouveau GroupSet contenant l'union de A et B.
     * @static
     */
    static union(A: GroupSet, B: GroupSet): GroupSet {
        let union = new GroupSet(A);
        for(let b of B) {
            union.add(b);
        }
        return union;
    }

    /**
     * @memberof GraphQL
     * @summary Fonction qui renvoit tous les groupes simples dont le user est membre.
     * @arg {userData} data - Données de l'utilisateur.
     * @return {Promise(GroupSet)} Renvoie un GroupSet contenant le nom des groupes simples.
     * @static
     * @async
     */
    static async memberOfSimple(data: userData): Promise<GroupSet> {
        //Do a DFS from data.members to find all parents
        //return Tools.DFS(data.members, 'parent');

        //No need to do DFS
        return new GroupSet(data.members);
    }

    /**
     * @memberof GraphQL
     * @summary Fonction qui renvoit tous les groupes simples dont le user est speaker.
     * @arg {userData} data - Données de l'utilisateur.
     * @return {Promise(GroupSet)} Renvoie un GroupSet contenant le nom des groupes simples.
     * @static
     * @async
     */
    static async speakerOfSimple(data: userData): Promise<GroupSet> {
        return new GroupSet(data.speakers);
    }

    /**
     * @memberof GraphQL
     * @summary Fonction qui renvoit tous les groupes simples dont le user est administrateur.
     * @arg {userData} data - Données de l'utilisateur.
     * @return {Promise(GroupSet)} Renvoie un GroupSet contenant le nom des groupes simples.
     * @static
     * @async
     */
    static async adminOfSimple(data: userData): Promise<GroupSet> {
        //Do a DFS from data.admins to find all children
        //return Tools.DFS(data.admins, 'child');

        //No need to do DFS
        return new GroupSet(data.admins);
    }

    /**
     * @memberof GraphQL
     * @summary Fonction qui renvoit tous les méta-groupes dont ces groupes sont membres.
     * @arg {GroupSet} groups - Un ensemble de gid des groupes a considérer.
     * @return {Promise(GroupSet)} Renvoie un GroupSet contenant le nom des méta-groupes.
     * @static
     * @async
     */
    static async metaGroupsOfGroups(groups: GroupSet): Promise<GroupSet> {
        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;
        }));
    }

    /**
     * @memberof GraphQL
     * @summary Fonction qui renvoit tous les groupes (simples ou méta) dont le user est membre.
     * @arg {userData} data - Données de l'utilisateur.
     * @return {Promise(GroupCollection)} Renvoie une GroupCollection contenant le nom des groupes. 
     * @static
     * @async
     */
    static async memberOf(data: userData): Promise<GroupCollection> {
        let simple = await Tools.memberOfSimple(data);
        return { simpleGroups: simple, metaGroups: await Tools.metaGroupsOfGroups(simple) };
    }

    /**
     * @memberof GraphQL
     * @summary Fonction qui renvoit tous les groupes (simples ou méta) dont le user est speaker.
     * @arg {userData} data - Données de l'utilisateur.
     * @return {Promise(GroupCollection)} Renvoie une GroupCollection contenant le nom des groupes. 
     * @static
     * @async
     */
    static async speakerOf(data: userData): Promise<GroupCollection> {
        let simple = await Tools.speakerOfSimple(data);
        return { simpleGroups: simple, metaGroups: await Tools.metaGroupsOfGroups(simple) };
    }

    /**
     * @memberof GraphQL
     * @summary Fonction qui renvoit tous les groupes (simples ou méta) dont le user est administrateur.
     * @arg {userData} data - Données de l'utilisateur.
     * @return {Promise(GroupCollection)} Renvoie une GroupCollection contenant le nom des groupes.
     * @static
     * @async
     */
    static async adminOf(data: userData): Promise<GroupCollection> {
        let simple = await Tools.adminOfSimple(data);
        return { simpleGroups: simple, metaGroups: await Tools.metaGroupsOfGroups(simple) };
    }

    /**
     * @memberof GraphQL
     * @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(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);
                    stack.push(...data.parents)
                }                  
                
            }
        }

        return res;
    }

    /**
     * @memberof GraphQL
     * @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(roots: GroupSet): Promise<string[]> {
        let res = [];

        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 viewer.
     * @desc Utilise {@link Tools.oneDownSearch} pour avoir la profondeur 1 des arbres enracinés en chacun des groupes dont il est membre.
     * @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(groups: GroupCollection): Promise<GroupCollection> {
        let simple = groups.simpleGroups;

        //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]);
        simple.addList(s.map( elt => {
            return elt.simple_group_gid;
        }));

        //TODO : rajouter les VisibilityEdges

        return { simpleGroups: simple, metaGroups: await Tools.metaGroupsOfGroups(simple) };
    }
}