/** * @file Ce fichier génère le schéma GraphQL. C'est ici que les requêtes GraphQl sont résolues. * @author akka vodol */ import knex from '../../db/knex_router'; import { rens, idTOL, listGroups, listMembers, listAdmins } from '../ldap/ldap_data'; import { makeExecutableSchema } from 'graphql-tools'; import { request } from 'https'; import { assertBinaryExpression } from 'babel-types'; import { Group, User } from './user_and_group'; const typeDefs = ` type Query { allGroups: [Group] group(uid: ID) : Group user(uid: ID) : [User] } type Mutation { asAdmin(groupid: String): AdminMutation asMember(groupid: String): MemberMutation asViewer(groupid: String): ViewerMutation createGroup( uid: ID = null name: String website: String description: String school: String parentuid : String ): Group } type AdminMutation { createSubgroup( uid: ID = null, name: String, website: String, description: String, school: String, ): Group addUser(userid : String): User removeUser(userid : String): User addAdmin(userid : String): User removeAdmin(userid : String): User editGroup( name: String, website: String, description: String, school: String, ): Group } type MemberMutation { leave: Group } type ViewerMutation { requestJoin: Group } `; /** * @summary Renvoie tous les utilisateurs ayant des droits d'administrateur sur un groupe. * @desc Les utilisateurs qui ont un droit d'administrateur sur un groupe sont ses administrateurs et les utilisateurs ayant droit d'admin sur son parent * @arg {String} uid - L'uid du groupe dont on veut les administrateurs. * @return {Promise} Retour de requête knex. Promise qui renvera une liste de tous les utilisateurs ayant droit d'admin sur le groupe */ const getUsersWithAdminRights = (user, groupUID) => { return listAdmins(user, groupUID).then(adminList => { if(typeof adminList == "undefined") return undefined; else return knex('groups').select('parentuid').where('uid', groupUID).then(reqResult => { if(reqResult[0].parentuid) return getUsersWithAdminRights(user, reqResult[0].parentuid).then(parentAdmins => { return adminList.concat(parentAdmins); }); else return adminList; }); }); }; /** * @summary teste si un utilisateur a des droits * @desc Cette fonction effectue une requête knex. Elle gère l'arête de parenté. * @arg {Object} user - Objet contenant un attribut *uid* de type *string*. User représente l'utilisateur qui a effectué la requête. * @return {Promise} Retour de requête knex. Liste de tous les groupes que l'utilisateur a le droit de voire. */ const hasAdminRights = (user, groupuid) => { return getUsersWithAdminRights(user, groupuid).then(adminList => { return (typeof adminList != "undefined" && adminList.indexOf(user.uid) != -1); }); }; /** * @summary Renvoie tous les groupes visibles par l'utilisateur user * @desc Cette fonction effectue une requête knex. Elle gère l'arête de parenté. * @arg {Object} user - Objet contenant un attribut *uid* de type *string*. User représente l'utilisateur qui a effectué la requête. * @return {Promise} Retour de requête knex. Liste de tous les groupes que l'utilisateur a le droit de voire. */ const getAllVisibleGroups = (user) => { return listGroups(user, user.uid).then(group_ids => { if(typeof group_ids == "undefined") throw "invalid user"; var membered_groups = qb => qb.select().from('groups').whereIn('uid', group_ids.concat(['kes'])); /* membered_groups est un callback qui reçoit un argument qb représentant un objet de requête (un truc qui s'utilise comme knex()) * et effectue dessus la requête pour obtenir une table de tous les groupes dont user est membre * with est ensuite utilisé pour donner à ce retour de requête l'alias 'membered_groups'. * Cette table peut ainsi être JOIN avec la table groups. La condition de Join est donnée selon une syntaxe recopiée depuis la Doc Knex * On récupère tous les groupes tels que le groupe est dans membered_groups ou le parent est dans membered_groups */ return knex.with('membered_groups', membered_groups).distinct('groups.*').select().from('groups').innerJoin('membered_groups', function() { this.on('groups.uid', '=', 'membered_groups.uid').orOn('groups.parentuid', '=', 'membered_groups.uid'); } ); }); }; /** * @summary Renvoie un unique groupe, ssi ce groupe est visible par l'utilisateur * @desc Actuellement, la fonction appelle *getAllVisibleGroups* et restreint au groupe demandé. Cette fonction peut être implémentée de manière plus efficace et plus chiante. * @arg {Object} user - Objet contenant un attribut *uid* de type *string*. User représente l'utilisateur qui a effectué la requête. * @arg {String} uid - uid du groupe que l'on veut voire. * @return {Promise} Retour de requête knex. Le groupe demandé, si l'utilisateur a le droit de la voire. */ const getGroupIfVisible = (user, uid) => { return /*getAllVisibleGroups(user)*/knex('groups').where('groups.uid', uid).then(res => { return res[0]; }); }; /** * @summary Attribue un UID qui n'a pas encore été utilisé à un groupe * @desc RASifie le string initialUID si necessaire (ramené à de l'ASCCI sans espace), puis si l'uid est deja pris rajoute un n a la fin et reteste * @arg {String} uid - L'uid du groupe dont on veut les administrateurs. * @return {Promise} Retour de requête knex. Promise qui renvera une liste de tous les utilisateurs ayant droit d'admin sur le groupe */ const getAvailableGroupUID = (initialUID) => { let rasUID = initialUID.replace(' ', '_').replace(/\W/g, '').toLowerCase(); return knex.from('groups').where('uid', rasUID).then(res =>{ if(res.length == 0){ return(rasUID); }else{ return(getAvailableGroupUID(rasUID + 'n')); } }); }; /** * @summary Créé un groupe si les arguments sont tous valides et l'utilisateur est authorisé * @desc Les arguments doivent être valides, sauf pour uid. Une clé uid valide sera générée dans tous les cas. * On teste si l'utilisateur qui envoie la requête a des droits d'admin sur le parent du groupe qui doit être créé, avec la fonction *getUsersWithAdminRights* * Si un argument est invalide ou si l'utilisateur n'a pas les droits, la fonction renvoie une erreur * @arg {Object} user - L'utilisateur qui effectue la requête. * @arg {Object} args - Les arguments envoyés à la mutation. Cf le schéma GraphQL * @return {Promise} Retour de requête knex. Le groupe qui vient d'être créé. En cas d'echec, renvoie une erreur. */ const createGroupIfLegal = (user, args) => { if(typeof args.parentuid != 'string') throw "Illegal argument : parentuid must be a string"; return getUsersWithAdminRights(user, args.parentuid).then( admin_list => { if(typeof admin_list == undefined) //Cela arrive si arg.parentuid n'est pas un id de groupe valable (ou éventuellement si Chevalier fait de la merde) throw "invalid argument : no group with id " + args.parentuid; admin_list = admin_list.concat(['anatole.romon']); // pour les besoins des tests, anatole romon a tout les droits if(admin_list.indexOf(user.uid) == -1) throw "illegal request : you must have admin rights over a group to create a subgroup of that group"; if(typeof args.uid != "string") args.uid = args.name; return(getAvailableGroupUID(args.uid).then(rasUID => { // TODO : appeller une fonction de ldap_data pour y créer un groupe. return knex('groups').insert({ uid : rasUID, parentuid : args.parentuid, createdAt : knex.fn.now(), updatedAt : this.createdAt, name : args.name, website : args.website, description : args.description, school : args.school }).then( res => { return getGroupIfVisible(user, rasUID); }); })); }); }; /** * @description Résolveurs des différentes requêtes GraphQL */ const resolvers = { Query: { allGroups: (obj, args, context) => { return getAllVisibleGroups(context.user); }, group: (obj, args, context) => { return getGroupIfVisible(context.user, args.uid); }, user: (obj, args, context) => { const refactorer = (data) => { return { uid: args.uid, lastName: data.sn, givenName: data.givenName, birthdate: data.brBirthdate, groups: data.brMemberOf, mail: data.mail, phone: data.telephoneNumber, room: data.brRoom }; }; console.log("Logged in as:",context.user); const result = rens(context.user, args.uid).then(res => { const output = res.map(entry => refactorer(entry)); return output; }); return result; } }, Mutation: { asAdmin: (obj, args, context) => { }, createGroup: (obj, args, context) => { return createGroupIfLegal(context.user, args); } } }; const schema = makeExecutableSchema({ typeDefs: [typeDefs, Group, User], resolvers }); export default schema;