/** * 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) }; } }