diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c5c956f4b285c701b0cb02ecd4b4607a1fee55e1..d89c523accdb6e884c1fe8c5baa5b314d4b578f3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -34,7 +34,7 @@ build: - ldap_connexion_config.json - build/ - node_modules/ - expire_in: 240 min + expire_in: 240 min tags: - database - build @@ -49,10 +49,18 @@ test:lint: deploy_staging: stage: deploy + only: + - master + before_script: + - eval $(ssh-agent -s) + - ssh-add <(echo "$STAGING_PRIVATE_KEY") + - mkdir -p ~/.ssh + - echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config script: - npm run build - - mkdir -p /opt/sigma-back-dev - - cp -r build/ /opt/sigma-back-dev/build/ + - ssh -p22 sigma-dev@roued "mkdir -p /opt/sigma-back-dev" + - ssh -p22 sigma-dev@roued "rm -rf /opt/sigma-back-dev/build" + - scp -p22 -r build/ sigma-dev@roued:/opt/sigma-back-dev/build environment: name: staging url: http://129.104.210.10:3000 diff --git a/db/migrations/20180303192411_create_metaGroups.js b/db/migrations/20180303192411_create_metaGroups.js new file mode 100644 index 0000000000000000000000000000000000000000..90df1a877c7fe944429a21ce190b856bb0ac3cfd --- /dev/null +++ b/db/migrations/20180303192411_create_metaGroups.js @@ -0,0 +1,49 @@ + +/* +Une mutation pour rajouter dans la BDD les types SimpleGroup et MetaGroup +(cf la documentation de l'interface GraphQL) +Les objets qui étaient jusqu''à présent stoqués dans la table groups +sont déplacés dans la table simple_groups +En cas de rollback, le déplacement se fait dans l'autre sens +(et les meta groupes sont perdus) + +Après cette mutation, plus rien n'est sensé être mis directement dans la table groups +*/ + +exports.up = function(knex, Promise) { + return knex('groups').select().then( groups_content => { + return knex('groups').del().then(function () { + return knex.schema.table('groups', function (table){ + table.dropColumn('school'); + table.dropColumn('parentuid'); + }).then(()=> { + return knex.schema.createTable('simple_groups', function (table){ + table.inherits('groups'); + table.string('parentuid'); + table.enum('school', ['polytechnique', 'ensta', 'supoptique']).notNullable(); + }).then(() => { + return knex.schema.createTable('meta_groups', function (table){ + table.inherits('groups'); + }).then(function(){ + return knex('simple_groups').insert(groups_content); + }); + }); + }); + }); + }); +}; + +exports.down = function(knex, Promise) { + return knex('simple_groups').select().then(simple_groups_content => { + return knex.schema.dropTable('simple_groups').then(function (){ + return knex.schema.dropTable('meta_groups').then(function (){ + return knex.schema.table('groups', function (table){ + table.string('parentuid'); + table.enum('school', ['polytechnique', 'ensta', 'supoptique']).notNullable(); + }).then(function (){ + return knex('groups').insert(simple_groups_content); + }); + }); + }); + }); +}; diff --git a/db/migrations/20180304154225_group_type_column.js b/db/migrations/20180304154225_group_type_column.js new file mode 100644 index 0000000000000000000000000000000000000000..f178a0a9ddf63ec1de5e2e6e050eb5ab4ea6af1c --- /dev/null +++ b/db/migrations/20180304154225_group_type_column.js @@ -0,0 +1,17 @@ + +exports.up = function(knex, Promise) { + return knex.schema.table('groups', function(table) { + table.enum('type', ['simple', 'meta', 'error']).notNullable().defaultTo('error'); + }).then( () => { + return knex('simple_groups').update({type : "simple"}).then(() => { + return knex('meta_groups').update({type : "meta"}); + }); + }); + +}; + +exports.down = function(knex, Promise) { + return knex.schema.table('groups', function(table) { + table.dropColumn('type'); + }); +}; diff --git a/db/seeds/01_create_groups.js b/db/seeds/01_create_groups.js index 1eb823722ccc15ba7e2b75ae5dda990b632c4a71..ee9c1ce9463b9c3d697978667f06c15da9e955b1 100644 --- a/db/seeds/01_create_groups.js +++ b/db/seeds/01_create_groups.js @@ -3,44 +3,61 @@ exports.seed = function(knex, Promise) { // Deletes ALL existing entries return knex('groups').del() .then(function () { + knex('simple_groups').del() + .then(function() { // Inserts seed entries - const groups = [{ + const simple_groups = [{ name: 'BR', uid: 'br', website: 'br.binets.fr', description: 'Le Binet Réseau est responsable du réseau internet des élèves sur le campus de l\'Ecole polytechnique.', school: 'polytechnique', - parentuid: 'kes' + parentuid: 'kes', + type : 'simple' },{ name: 'JTX', uid: 'jtx', website: 'binet-jtx.com', school: 'polytechnique', - parentuid: 'kes' + parentuid: 'kes', + type : 'simple' },{ name: 'Kès', uid: 'kes', website: 'kes.binets.fr', - school: 'polytechnique' + school: 'polytechnique', + type : 'simple' },{ name: 'DaTA', uid: 'data', website: 'data-ensta.fr', school: 'ensta', - parentuid: 'bdeensta' + parentuid: 'bdeensta', + type : 'simple' },{ name: 'Laser Wave', uid: 'laserwave', website: 'laserwave.fr', - school: 'supoptique' + school: 'supoptique', + type : 'simple' },{ name: 'BDE Ensta', uid: 'bdeensta', website: 'http://bde.ensta-paristech.fr/', - school: 'ensta' - } + school: 'ensta', + type : 'simple' + }, + { + name: 'Subaïsse', + uid: 'subaisse', + description: 'Le Binet de ceux qui subissent', + school: 'polytechnique', + parentuid: 'kes', + type : 'simple' + }, ]; - return knex('groups').insert(groups); + return knex('simple_groups').insert(simple_groups); }); + }); }; diff --git a/db/seeds/03_make_requests.js b/db/seeds/03_make_requests.js index 2cb07fa44bcd80687b11bacb6099acb4d5c2b87e..aceaf4721893cb157cd6d5197384e49ff99b0e36 100644 --- a/db/seeds/03_make_requests.js +++ b/db/seeds/03_make_requests.js @@ -2,19 +2,37 @@ exports.seed = function(knex, Promise) { // Deletes ALL existing entries return knex('user_join_group').del() - .then(function () { - // Inserts seed entries - return knex('user_join_group').insert([ - { id: 1, - recipient: 'br', - message: "C'est ici pour développer sigma ?", - useruid: "anatole.romon" - }, - { id: 2, - recipient: 'br', - message: "Bonjour, je cherche le binet subaisse", - useruid: "quentin.gendre" - } - ]); - }); + .then(function () { + // Inserts seed entries + return knex('user_join_group').insert([ + { id: 1, + recipient: 'br', + message: "C'est ici pour développer sigma ?", + useruid: "anatole.romon" + }, + { id: 2, + recipient: 'br', + message: "Bonjour, je cherche le binet subaisse", + useruid: "quentin.gendre" + }, + {id : 3, + recipient: 'jtx', + message: "Quand je serais grand je serais cinéaste !", + useruid: "anatole.romon" + } + ]).then(() => { + return knex('group_join_event').del() + .then(function (){ + return knex('group_join_event').insert([ + { + id : 4, + recipient : "br", + message : "nous aussi on veut coder sigma", + eventuid : 42, + senderuid : "subaisse" + } + ]); + }); + }); + }); }; diff --git a/db/seeds/04_make_metagroups.js b/db/seeds/04_make_metagroups.js new file mode 100644 index 0000000000000000000000000000000000000000..432bb33d1b96b6f4fda099d16cbf35d471193c6a --- /dev/null +++ b/db/seeds/04_make_metagroups.js @@ -0,0 +1,24 @@ + +exports.seed = function(knex, Promise) { + // Deletes ALL existing entries + return knex('meta_groups').del() + .then(function () { + // Inserts seed entries + return knex('meta_groups').insert([ + { + name: 'Fédérez', + uid: 'federez', + website: 'federez.io', + description: "L'association de toutes les associations de réseau des écoles", + type : 'meta' + }, + { + name: 'BSCkBl', + uid: 'bsckbl', + website: 'bsckbl.binets.fr', + type : 'meta' + } + ]); + }); + }; + \ No newline at end of file diff --git a/package.json b/package.json index cd95995964c78a2258e982a934fd227fbba57a7e..3fb5e2449494c39592716faed3543c5ace252152 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "knex": "^0.14.4", "ldap-escape": "^1.1.5", "ldapjs": "^1.0.2", + "lodash": "^4.17.5", "morgan": "^1.9.0", "passport": "^0.4.0", "passport-ldapauth": "^2.0.0", diff --git a/src/graphql/connectors/connectors.js b/src/graphql/connectors/connectors.js new file mode 100644 index 0000000000000000000000000000000000000000..0aea999c8b86c9b16380d9ecc67a98c28dbe84b5 --- /dev/null +++ b/src/graphql/connectors/connectors.js @@ -0,0 +1,378 @@ +/** + * @file Fonctions pour interagir avec la BDD sigma et le LDAP. + * @author akka vodol + */ +import knex from '../../../db/knex_router'; +import { renseignerSurUtilisateur, repliquerTOLdesIds, + listerGroupes, listerMembres, listerAdministrateurs +} from '../../ldap/ldap_data'; +import { exportAllDeclaration } from 'babel-types'; + +export { renseignerSurUtilisateur, repliquerTOLdesIds, listerMembres }; + +/** + * @summary Génère une promise. + * @function + * @desc Les fonctions ici sont toutes supposées renvoyer une promise. + * Si on veut renvoyer une valeur directement, cette fonction permet de construire + * une Promise qui renvoie cette valeur facilement. + * @arg {Object} val - La valeur qu'on veut renvoyer. + * @return {Promise(Object)} Une promise qui renvoi val + */ +const quickPromise = (val) => { + return new Promise( (resolve, reject) => { + resolve(val); + }); +}; + +/** + * @summary Renvoie le type d'un groupe. + * @function + * @desc Parcours les BDD pour savoir dans laquelle se trouve le groupe ayant l'UID donné. + * Cette opération nécéssite un parcours de base de donnée, et il est préférable de ne pas + * sans servir si on a un autre moyend de connaitre le typed d'un groupe + * (Par exemple, si on dispose d'un retour de BDD pour ce groupe. Le champ 'type' indique alors son type.) + * @arg {Object} user - Objet contenant un attribut `uid` de type `string`. + * User représente l'utilisateur qui a effectué la requête. + * @arg {Object} groupUID - L'id du groupe dont on veut connaître le type. + * @return {Promise(String)} Un string représentant le type du groupe. + * Peut être "SimpleGroup" ou "MetaGroup". Renvoie `Undefined` si le groupe n'existe pas + */ +export const getGroupType = (user, groupUID) => { + return knex('simple_groups').select('uid').where('uid', groupUID).then( sg_res => { + if(sg_res) + return "SimpleGroup"; + return knex('meta_groups').select('uid').where('uid', groupUID).then(mg_res => { + if(mg_res) + return "MetaGroup"; + return undefined; + }); + }); +}; + +/** + * @summary Renvoie tous les utilisateurs ayant des droits d'administrateur sur un groupe. + * @function + * @desc Les utilisateurs qui ont un droit d'administrateur sur un groupe simple sont ses administrateurs + * et les utilisateurs ayant droit d'admin sur son parent. + * Les utilisateurs ayant droit d'admin sur un meta-groupe sont les utilisateurs + * ayant droit d'admin sur un des groupes membres + * @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 + */ +export const getUsersWithAdminRights = (user, groupUID) => { + return getGroupType(user, groupUID).then( groupType => { + + if(groupType == "SimpleGroup"){ + return listerAdministrateurs(user, groupUID).then(adminList => { + if (typeof adminList == "undefined") + return undefined; + else + return knex('simple_groups').select('parentuid').where('uid', groupUID).then(req => { + if (req[0].parentuid) + return getUsersWithAdminRights(user, req[0].parentuid).then(parentAdmins => { + return adminList.concat(parentAdmins); + }); + else + // pour les besoins des tests, anatole romon a tout les droits + return adminList.concat(['anatole.romon']); + }); + }); + }else{ + return getMetaGroupAdminMembers(user, groupUID).then(mg_members => { + function fuse (l){ + if(l) + return getUsersWithAdminRights(l.pop()).then(usrs => fuse(l).concat(usrs) ); + else + return []; + } + }); + } + }); +}; + +/** + * @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. + */ +export const hasAdminRights = (user, groupUID) => { + if(user.uid == "anatole.romon") + return quickPromise(true); + return getUsersWithAdminRights(user, groupUID).then(adminList => { + return (typeof adminList != "undefined" && adminList.indexOf(user.uid) != -1); + }); +}; + + +/** + * @summary Renvoie une liste des id de tous les groupes visibles par l'utilisateur + * @desc Cette fonction génère un callback qui créé une table contenant les uid de tous les groupes visibles + * @arg {Object} user - Objet contenant un attribut `uid` de type `string`. + * User représente l'utilisateur qui a effectué la requête. + * @return {Promise(Callback)} callback contruisant une requête knex pour une table de tous les id visibles. + */ +export const getVisibleGroupCallback = (user) => { + return listerGroupes(user, user.uid).then(group_ids => { + return function (global_query_builder){ + if (typeof group_ids == "undefined") + throw "invalid user"; + var membered_groups = qb => qb.select('simple_groups.uid').from('simple_groups').whereIn('uid', group_ids.concat(['kes'])); + var directly_visible_simple_groups = qb => qb.with('membered_groups', membered_groups).select('simple_groups.uid').from('simple_groups').distinct() + .innerJoin('membered_groups', + function () { + this.on('simple_groups.uid', '=', 'membered_groups.uid') + .orOn('simple_groups.parentuid', '=', 'membered_groups.uid'); + } + ); + return directly_visible_simple_groups(global_query_builder); + }; + }); + +}; + +/** + * @summary Renvoie le nom de la table dans laquelle il faut chercher en fonction de ce qu'on veut + * @desc a remplir + * @arg {String} wantedType - Un string indiquant le type de groupe qu'on veut. Peut être `"simple"`, `"meta"` ou `"all"`. + * @return {String} Le nom de la table dans laquelle la requète doit être effectuée. + */ +function getGroupTableName(wantedType){ + switch(wantedType){ + case "simple": + return "simple_groups"; + case "SimpleGroup": + return "simple_groups"; + case "meta": + return "meta_groups"; + case "MetaGroup": + return "meta_groups"; + case "all": + return "groups"; + default: + throw "invalid type request : " + wantedType + "is not a valid group type"; + } +} + +/** + * @summary Renvoie un unique groupe, ssi ce groupe est visible par l'utilisateur + * @desc Pour l'instant, la fonction effectue la même requête que `getAllVisibleGroups` + * et restreint au groupe demandé. Cette fonction peut être implémentée de manière + * plus efficace et plus chiante. + * @arg {Object} user - Utilisateur effectuant la requête. + * @arg {String} uid - Identifiant du groupe voulu. + * @arg {String} type - Type de groupe voulu. `"simple"`, `"meta"` ou `"all"`. + * @return {Promise(group)} Retour de requête knex. Le groupe demandé, si l'utilisateur a le droit de la voire. + */ +export async function getGroupIfVisible(user, groupUID, type="all"){ + let group_table_name = getGroupTableName(type); + let visible_groups = await getVisibleGroupCallback(user); + return knex.with('visible_groups', visible_groups).select() + .from(group_table_name).innerJoin('visible_groups', function (){ + this.on('visible_groups.uid', '=', group_table_name + '.uid'); + }).where(group_table_name + '.uid', groupUID).then(res => res[0]); +}; + +export const getSimpleGroupIfVisible = (user, groupUID) => getGroupIfVisible(user, groupUID, "simple"); +export const getMetaGroupIfVisible = (user, groupUID) => getGroupIfVisible(user, groupUID, "meta"); + +/** + * @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 - Représente l'utilisateur qui a effectué la requête. + * @arg {String} wantedType - Type de groupe voulu : `"simple"`, `"meta"` ou `"all"`. + * @return {Promise} Retour de requête knex. Liste de tous les groupes que l'utilisateur a le droit de voire. + */ +export async function getAllVisibleGroups(user, wantedType="all"){ + let group_table_name = getGroupTableName(wantedType); + let visible_groups = await getVisibleGroupCallback(user); + return knex.with('visible_groups', visible_groups).select().from(group_table_name).innerJoin('visible_groups', function (){ + this.on('visible_groups.uid', '=', group_table_name + '.uid'); + }); +}; + +export const getAllVisibleSimpleGroups = (user) => getAllVisibleGroups(user,"simple"); +export const getAllVisibleMetaGroups = (user) => getAllVisibleGroups(user,"meta"); + +/** + * @summary Teste si un utilisateur est membre d'un groupe + * @arg {Object} user - Représente l'utilisateur qui a effectué la requête. + * @arg {Object} groupUID - L'id du groupe dont on veu savoir si l'utilisateur est membre. + * @return {Promise(Boolean)} Boolean indiquant si l'utilisateur est membre du groupe. + */ +export const isMember = (user, groupUID) => { + return listerGroupes(user, user.uid).then(group_ids => group_ids && group_ids.indexOf(groupUID) != -1); +}; + +/** + * @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 + */ +export function 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 + * @desc Les arguments doivent être valides, sauf pour uid. Une clé uid valide sera générée dans tous les cas. + * Les authorisations de l'utilisateur ne sont pas vérifiées + * 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. + */ +export async function createSubgroup(user, args){ + if (typeof args.parentuid != 'string') + throw "Illegal argument : parentuid must be a non null string"; + if (typeof args.name != 'string') + throw "Illegal argument : name must be a non null string"; + + let rasUID = await getAvailablegroupUID(args.uid); + + // TODO : appeller une fonction de ldap_data pour y créer un groupe. + await knex('simple_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, + type : "simple" + }); + + return getGroupIfVisible(user, rasUID); +}; + +/** + * @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. + */ +export async function createGroupIfLegal(user, args){ + if( await hasAdminRights(user, args.parentuid) ){ + return createSubgroup(user, args); + }else{ + throw "illegal request : you must have admin rights over a group to create a subgroup of that group"; + } +}; + +/** + * @summary Renvoie toues les requêtes de type UserJoinGroup + * @desc Une requête UserJoinGroup est envoyée par un utilisateur à un groupe, + * pour demander à rejoindre ce groupe + * @arg {Object} user - L'utilisateur qui effectue la requête. + * @arg {String} args - L'identifiant du groupe qui reçoit la requête. + * @return {Promise(Object)} Retour de requête knex. Toutes les requêtes destinées au groupe. + */ +export function getUserJoinGroupRequests(user, recipientUID){ + return knex.select('id', 'useruid', 'message').from('user_join_group') + .where('recipient', recipientUID); +}; + +/** + * @summary Renvoie toues les requêtes de type GroupJoinEvent + * @desc Une requête UserJoinGroup est envoyée par un groupe à un évènement (donc aux administrateurs de l'évènement), + * pour demander à rejoindre cet évènement. + * Remarque : toutes les requêtes ont pour le moment un attribut recipient, + * mais ici il ne sera a terme pas utilisé. + * @arg {Object} user - L'utilisateur qui effectue la requête. + * @arg {String} args - L'identifiant du groupe qui reçoit la requête. + * @return {Promise(Object)} Retour de requête knex. Toutes les requêtes destinées au groupe. + */ +export function getGroupJoinEventRequests(user, recipientUID){ + return knex.select('id', 'senderuid', 'eventuid', 'message').from('group_join_event') + .where('recipient', recipientUID); +}; + + +/** + * @summary Renvoie toues les requêtes de type GroupJoinEvent + * @desc Une requête UserJoinGroup est envoyée par un groupe à un évènement (donc aux administrateurs de l'évènement), + * pour demander à rejoindre cet évènement. + * Remarque : toutes les requêtes ont pour le moment un attribut recipient, + * mais ici il ne sera a terme pas utilisé. + * @arg {Object} user - L'utilisateur qui effectue la requête. + * @arg {String} args - L'identifiant du groupe qui reçoit la requête. + * @return {Promise(Object)} Retour de requête knex. Toutes les requêtes destinées au groupe. + */ +export const getYourGroupHostEventRequests = (user, recipientUID) => { + return knex.select('id', 'senderuid', 'eventuid', 'message').from('your_group_host_event') + .where('recipient', recipientUID); +}; + + +//Don't forget the argument user is the guy who makes the request, not the user we want +export const getUser = (user, userUID) => { + const refactorer = (data) => { + return { + uid: userUID, + lastName: data.sn, + givenName: data.givenName, + birthdate: data.brBirthdate, + groups: data.brMemberOf, + mail: data.mail, + phone: data.telephoneNumber, + room: data.brRoom + }; + }; + + const result = renseignerSurUtilisateur(user, userUID).then(res => { + return refactorer(res[0]); + }); + + return result; +}; + +/** + * @function Renvoie un événement en fonction de son identifiant. + * @param {*} user - Utilisateur effectuant la requête. + * @param {*} eventID - Identifiant unique de l'événement. + */ +export const getEvent = (user, eventID) => { + return quickPromise(null); +}; + +/** + * @function Renvoie simplement un groupe en fonction de son identifiant. + * @param {Object} user - Utilisateur effectuant la requête. + * @param {String} groupUID - Identifiant unique du groupe. + * @author manifold + */ +export const getGroup = (user, groupUID) => { + // Une sélection sur une table renvoie un tableau. + // Knex renvoie une promesse, qui se résout en le tableau sélectionné. + // On récupère son unique valeur, puisqu'on filtre sur l'identifiant unique. + return knex.select().from('groups').where('uid',groupUID).then(results => results [0]); +}; + +export const getSimpleGroup = (user, groupUID) => { + return knex.select().from('simple_groups').where('uid',groupUID).then(results => results [0]); +}; + +export const getMetaGroup = (user, groupUID) => { + return knex.select().from('meta_groups').where('uid',groupUID).then(results => results [0]); +}; + +export const getMetaGroupAdminMembers = (user, metaGroupUID) => { + return quickPromise([]); +}; \ No newline at end of file diff --git a/src/graphql/db_utils.js b/src/graphql/db_utils.js deleted file mode 100644 index f9330dc1b8caf03ab84702effd9a207cbc4fb836..0000000000000000000000000000000000000000 --- a/src/graphql/db_utils.js +++ /dev/null @@ -1,212 +0,0 @@ -/** - * @file Fonctions pour interagir avec la BDD sigma et le LDAP. - * @author akka vodol - */ -import knex from '../../db/knex_router'; -import { renseignerSurUtilisateur, repliquerTOLdesIds, listerGroupes, listerMembres, listerAdministrateurs } from '../ldap/ldap_data'; -import { exportAllDeclaration } from 'babel-types'; - -export { renseignerSurUtilisateur, repliquerTOLdesIds, listerMembres }; - -/** - * @summary Renvoie tous les utilisateurs ayant des droits d'administrateur sur un groupe. - * @function - * @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 - */ -export const getUsersWithAdminRights = (user, groupUID) => { - return listerAdministrateurs(user, groupUID).then(adminList => { - if (typeof adminList == "undefined") - return undefined; - else - return knex('groups').select('parentuid').where('uid', groupUID).then(req => { - if (req[0].parentuid) - return getUsersWithAdminRights(user, req[0].parentuid).then(parentAdmins => { - return adminList.concat(parentAdmins); - }); - else - return adminList.concat(['anatole.romon']); // pour les besoins des tests, anatole romon a tout les droits - }); - }); -}; - -/** - * @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. - */ -export 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. - */ -export const getAllVisibleGroups = (user) => { - - return listerGroupes(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 Teste si un utilisateur est membre d'un groupe - * @arg {Object} user - Objet contenant un attribut *uid* de type *string*. User représente l'utilisateur qui a effectué la requête. - * @arg {Object} groupUID - L'id du groupe dont on veu savoir si l'utilisateur est membre. - * @return {Promise(Boolean)} Boolean indiquant si l'utilisateur est membre du groupe. - */ -export const isMember = (user, groupUID) => { - return listerGroupes(user, user.uid).then(group_ids => group_ids && group_ids.indexOf(groupUID) != -1); -}; - -/** - * @summary Renvoie un unique groupe, ssi ce groupe est visible par l'utilisateur - * @desc Actuellement, la fonction effectue la même requête que *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(group)} Retour de requête knex. Le groupe demandé, si l'utilisateur a le droit de la voire. - */ -export const getGroupIfVisible = (user, groupUID) => { - return listerGroupes(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'])); - 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'); - } - ).where('groups.uid', groupUID).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 - */ -export 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 - * @desc Les arguments doivent être valides, sauf pour uid. Une clé uid valide sera générée dans tous les cas. - * Les authorisations de l'utilisateur ne sont pas vérifiées - * 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. - */ -export const createSubgroup = (user, args) => { - if (typeof args.parentuid != 'string') - throw "Illegal argument : parentuid must be a non null string"; - if (typeof args.name != 'string') - throw "Illegal argument : name must be a non null string"; - - 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); - }); - })); -}; - -/** - * @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. - */ -export const createGroupIfLegal = (user, args) => { - return hasAdminRights(user, args.parentuid).then(answer => { - if (!answer) - throw "illegal request : you must have admin rights over a group to create a subgroup of that group"; - return createSubgroup(user, args); - }); -}; - -export const getUserJoinGroupRequests = (user, recipientUID) => { - return knex('user_join_group').select(/*'id', 'useruid', 'message'*/)/*.where('recipient', recipientUID)*/; -}; - -//Don't forget the argument user is the guy who makes the request, not the user we want -export const getUser = (user, userUID) => { - return new Promise( (resolve, reject) => { - resolve({ - givenName : "Jean-Michel", - lastName : "Samarchepas", - uid : "jm", - birthdate : "dans le turfu", - mail : "email.email@email.com", - phone : "06 60 06 60 06", - groups : [], - likes : [], - address : "aussi dans le turfu" - }); - }); - /*return renseignerSurUtilisateur(user, userUID).then(res => { - return { - givenName : res[0].givenName, - lastName : res[0].lastName, - uid : userUID, - birthdate : res[0].brBirthdate, - mail : res[0].mail, - phone : res[0].telephoneNumber, - groups : [], - likes : [], - address : res[0].brRoom - }; - });*/ -}; - -export const getEvent = (user, eventID) => { - return null; -}; - -export const getGroup = (user, groupUID) => { - return null; -}; \ No newline at end of file diff --git a/src/graphql/resolvers.js b/src/graphql/resolvers.js new file mode 100644 index 0000000000000000000000000000000000000000..24c05c153eccf64fa35833b5466292eb07aff507 --- /dev/null +++ b/src/graphql/resolvers.js @@ -0,0 +1,258 @@ +/** + * @file Implémentation des requêtes GraphQL. + * @author akka vodol +*/ +import { request } from 'https'; +import _ from 'lodash'; +import { assertBinaryExpression } from 'babel-types'; +import knex from '../../db/knex_router'; + +import * as connectors from './connectors/connectors.js'; +import { connect } from 'http2'; + +/** + * @description Résolveurs des différentes requêtes GraphQL +*/ +export const resolvers = { + Query: { + + asAdmin: (obj, args, context) => { + return connectors.hasAdminRights(context.user, args.groupUID).then(res => { + if(res) + return {groupUID : args.groupUID}; + else + throw "You do not have admin rights over this group"; + }); + }, + + accessGroups: (obj, args, context) => { + return {}; + }, + + accessPosts: (obj, args, context) => { + return {}; + }, + + accessUsers: (obj, args, context) => { + return {}; + }, + }, + + GroupQuery : { + allGroups: (obj, args, context) => { + return connectors.getAllVisibleGroups(context.user); + }, + group: (obj, args, context) => { + return connectors.getGroupIfVisible(context.user, args.uid); + }, + simpleGroup: (obj, args, context) => { + return connectors.getSimpleGroupIfVisible(context.user, args.uid); + }, + metaGroup: (obj, args, context) => { + return connectors.getMetaGroupIfVisible(context.user, args.uid); + } + }, + + MessageQuery : { + allPosts: (obj, args, context) => { + return knex.select().from('posts'); + }, + post: (obj, args, context) => { + const result = knex.select().from('posts').where('id','=',args.id); + return result.then((res) => res[0]); + }, + }, + + UserQuery: { + user: (obj, args, context) => { + + return connectors.getUser(context.user,args.uid); + }, + + searchTOL: (obj, args, context) => { + console.log(args); + return connectors.repliquerTOLdesIds(context.user, { + givenName: args.givenName, + lastName: args.lastName, + nickname: args.nickname, + nationality: args.nationality, + school: args.school, + promotion: args.promotion, + groups: args.groups, + studies: args.studies, + sport: args.sport, + phone: args.phone, + mail: args.mail, + adress: args.adress, + ip: args.ip + }); + } + }, + + AdminQuery: { + isAdmin: (obj, args, context) => { + return true; + }, + + allRequests: (obj, args, context) => { + console.log(obj); + return obj; + } + }, + + AllRequests: { + userJoinGroup : (obj, args, context) => { + return connectors.getUserJoinGroupRequests(context.user, obj.groupUID); + }, + + groupJoinEvent : (obj, args, context) => { + return connectors.getGroupJoinEventRequests(context.user, obj.groupUID); + }, + + yourGroupHostEvent : (obj, args, context) => { + return connectors.getYourGroupHostEventRequests(context.user, obj.groupUID); + }, + }, + + UserJoinGroup: { + user : (obj, args, context) => { + return connectors.getUser(context.user, obj.useruid); + /*return connectors.getUser(context.user, "quentin.gendre"); + if(obj.useruid === "anatole.romon"){ + return connectors.getUser(context.user, "anatole.romon").then(res => { + return connectors.getUser(context.user, "quentin.gendre"); + }); + }else{ + return new Promise( (resolve, reject) => { + resolve({givenName : "patrick"}); + }); + }*/ + } + }, + + GroupJoinEvent : { + event: (obj, args, context) => { + return connectors.getEvent(context.user, obj.eventuid); + }, + groupWantingToJoin: (obj, args, context) => { + return connectors.getGroup(context.user, obj.senderuid); + } + }, + + YourGroupHostEvent : { + event: (obj, args, context) => { + return connectors.getEvent(context.user, obj.eventuid); + }, + sender: (obj, args, context) => { + return connectors.getGroup(context.user, obj.senderuid); + } + }, + + User : { + groups : (obj, args, context) => { + let result = Promise.all(obj.groups.map((grid) => { + return connectors.getSimpleGroup(context.user,grid); + })); + + return result.then(groups => { + return _.filter(groups,(o) => !_.isUndefined(o)); + }); + }, + + }, + + Mutation: { + asAdmin: (obj, args, context) => { + return connectors.hasAdminRights(context.user, args.groupUID).then(res => { + if(res) + return {groupUID : args.groupUID}; + else + throw "You do not have admin rights over this group"; + }); + }, + + asSpeaker: (obj, args, context) => { + return connectors.hasAdminRights(context.user, args.groupUID).then(res => { + if(res) + return {groupUID : args.groupUID}; + else + throw "You do not have publishing rights in this group"; + }); + }, + + asMember: (obj, args, context) => { + return connectors.isMember(context.user, args.groupUID).then(res => { + if(res) + return {groupUID : args.groupUID}; + else + throw "You are not a member of this group"; + }); + }, + asViewer: (obj, args, context) => { + return connectors.getGroupIfVisible(context.user, args.groupUID).then(res => { + if(res) + return {groupUID : args.groupUID}; + else + throw "You are not a member of this group"; + }); + } + }, + + AdminMutation: { + isAdmin: (obj, args, context) => { + return true; + + }, + createSubgroup: (obj, args, context) => { + args.parentuid = obj.groupUID; + return connectors.createSubgroup(context.user, args); + }, + + editGroup: (obj, args, context) => { + return null; + } + }, + + ViewerMutation: { + requestJoin: (obj, args, context) => { + console.log(obj); + return null; + } + }, + + Post: { + authors: (obj, args, context) => { + return knex.select().from('groups').whereIn('uid',obj.authors); + } + }, + + Group: { + __resolveType: (obj) => { + + switch(obj.type){ + case "simple": + return "SimpleGroup"; + case "meta": + return "MetaGroup"; + default: + return undefined; + } + } + }, + + SimpleGroup: { + + members: (obj, args, context) => { + console.log("Current group is",obj.uid); + console.log("\tMembers of the group are:",obj); + return connectors.listerMembres(context.user,obj.uid); + } + }, + + MetaGroup: { + + members: (obj, args, context) => { + } + + } +}; diff --git a/src/graphql/schema.js b/src/graphql/schema.js index 0d96a92c93b7c5b5cedc73eb121e56ed899561a6..844298716a5bdcbb534a56d292732df1110c513f 100644 --- a/src/graphql/schema.js +++ b/src/graphql/schema.js @@ -2,249 +2,10 @@ * @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 { makeExecutableSchema } from 'graphql-tools'; -import { request } from 'https'; -import { assertBinaryExpression } from 'babel-types'; -import knex from '../../db/knex_router'; import typeDefs from './typeDefs'; - -import * as db_utils from './db_utils'; - -/** - * @description Résolveurs des différentes requêtes GraphQL -*/ -const resolvers = { - Query: { - - asAdmin: (obj, args, context) => { - return db_utils.hasAdminRights(context.user, args.groupUID).then(res => { - if(res) - return {groupUID : args.groupUID}; - else - throw "You do not have admin rights over this group"; - }); - }, - - allGroups: (obj, args, context) => { - return db_utils.getAllVisibleGroups(context.user); - }, - group: (obj, args, context) => { - return db_utils.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 - }; - }; - - const result = db_utils.renseignerSurUtilisateur(context.user, args.uid).then(res => { - return refactorer(res[0]); - }); - - return result; - }, - - allPosts: (obj, args, context) => { - return knex.select().from('posts'); - }, - post: (obj, args, context) => { - const result = knex.select().from('posts').where('id','=',args.id); - return result.then((res) => res[0]); - }, - - searchTOL: (obj, args, context) => { - console.log(args); - return db_utils.repliquerTOLdesIds(context.user, { - givenName: args.givenName, - lastName: args.lastName, - nickname: args.nickname, - nationality: args.nationality, - school: args.school, - promotion: args.promotion, - groups: args.groups, - studies: args.studies, - sport: args.sport, - phone: args.phone, - mail: args.mail, - adress: args.adress, - ip: args.ip - }); - } - }, - - AdminQuery: { - isAdmin: (obj, args, context) => { - return true; - }, - - allRequests: (obj, args, context) => { - console.log(obj); - return obj; - } - }, - - AllRequests: { - userJoinGroup : (obj, args, context) => { - return db_utils.getUserJoinGroupRequests(context.user, args.groupUID); - }, - }, - - UserJoinGroup: { - user : (obj, args, context) => { - return db_utils.getUser(context.user, obj.useruid); - /*return db_utils.getUser(context.user, "quentin.gendre"); - if(obj.useruid === "anatole.romon"){ - return db_utils.getUser(context.user, "anatole.romon").then(res => { - return db_utils.getUser(context.user, "quentin.gendre"); - }); - }else{ - return new Promise( (resolve, reject) => { - resolve({givenName : "patrick"}); - }); - }*/ - } - }, - - GroupJoinEvent : { - event: (obj, args, context) => { - return db_utils.getEvent(context.user, obj.eventuid); - }, - groupWantingToJoin: (obj, args, context) => { - return db_utils.getGroup(context.user, obj.senderuid); - } - }, - - YourGroupHostEvent : { - event: (obj, args, context) => { - return db_utils.getEvent(context.user, obj.eventuid); - }, - sender: (obj, args, context) => { - return db_utils.getGroup(context.user, obj.senderuid); - } - }, - - /*User : { - - givenName : (obj, args, context) => { - console.log("I got this") - return null; - }, - - lastName : (obj, args, context) => { - return null; - }, - - uid : (obj, args, context) => { - return null; - }, - - birthdate : (obj, args, context) => { - return null; - }, - - mail : (obj, args, context) => { - return null; - }, - - phone : (obj, args, context) => { - return null; - }, - - groups : (obj, args, context) => { - return null; - }, - - likes : (obj, args, context) => { - return null; - }, - - address : (obj, args, context) => { - return null; - } - - },*/ - - Mutation: { - asAdmin: (obj, args, context) => { - return db_utils.hasAdminRights(context.user, args.groupUID).then(res => { - if(res) - return {groupUID : args.groupUID}; - else - throw "You do not have admin rights over this group"; - }); - }, - - asSpeaker: (obj, args, context) => { - return db_utils.hasAdminRights(context.user, args.groupUID).then(res => { - if(res) - return {groupUID : args.groupUID}; - else - throw "You do not have publishing rights in this group"; - }); - }, - - asMember: (obj, args, context) => { - return db_utils.isMember(context.user, args.groupUID).then(res => { - if(res) - return {groupUID : args.groupUID}; - else - throw "You are not a member of this group"; - }); - }, - asViewer: (obj, args, context) => { - return db_utils.getGroupIfVisible(context.user, args.groupUID).then(res => { - if(res) - return {groupUID : args.groupUID}; - else - throw "You are not a member of this group"; - }); - } - }, - - AdminMutation: { - isAdmin: (obj, args, context) => { - return true; - - }, - createSubgroup: (obj, args, context) => { - args.parentuid = obj.groupUID; - return db_utils.createSubgroup(context.user, args); - }, - - editGroup: (obj, args, context) => { - return null; - } - }, - - ViewerMutation: { - requestJoin: (obj, args, context) => { - console.log(obj); - return null; - } - }, - - Post: { - authors: (obj, args, context) => { - return knex.select().from('groups').whereIn('uid',obj.authors); - } - }, - - Group: { - members: (obj, args, context) => { - console.log("Current group is",obj.uid); - console.log("\tMembers of the group are:",obj); - return db_utils.listerMembres(context.user,obj.uid); - } - } -}; +import { resolvers } from './resolvers'; const schema = makeExecutableSchema({ typeDefs, diff --git a/src/graphql/typeDefs.js b/src/graphql/typeDefs.js index 71686030bbe6969b47a8bcf0ed99ae41b5e56387..b77e9ea4bd531601dfba749d4aed7d7056145596 100644 --- a/src/graphql/typeDefs.js +++ b/src/graphql/typeDefs.js @@ -1,35 +1,15 @@ const RootTypes = ` # Requêtes type Query { - allGroups: [Group] - group(uid: ID) : Group - user(uid: ID) : User - allMessages: [Message] - allEvents: [Event] - allPosts: [Post] - post(id: ID): Post - allAnnouncements: [Announcement] + accessGroups : GroupQuery + accessPosts : MessageQuery + accessUsers : UserQuery asAdmin(groupUID: ID): AdminQuery asSpeaker(groupUID: ID): AdminQuery asMember(groupUID: ID): AdminQuery asViewer(groupUID: ID): AdminQuery - searchTOL( - givenName: String, - lastName: String, - nickname: String, - nationality: String, - school: String, - promotion: String, - groups: String, - studies: String, - sport: String, - phone: String, - mail: String, - adress: String, - ip: String - ): [String] } type Mutation { @@ -86,6 +66,50 @@ const subMutations = ` `; const subQueries = ` + + """ + Requête pour obtenir un groupe. + """ + type GroupQuery{ + allGroups: [Group] + group(uid: ID) : Group + simpleGroup(uid : ID) : SimpleGroup + metaGroup(uid : ID) : MetaGroup + + } + + """ + Requête pour obtenir un message. + """ + type MessageQuery{ + allMessages: [Message] + allEvents: [Event] + allPosts: [Post] + post(id: ID): Post + allAnnouncements: [Announcement] + } + + type UserQuery{ + + user(uid: ID) : User + + searchTOL( + givenName: String, + lastName: String, + nickname: String, + nationality: String, + school: String, + promotion: String, + groups: String, + studies: String, + sport: String, + phone: String, + mail: String, + adress: String, + ip: String + ): [String] + } + # Requête à la base de donnée nécessitant d'être administrateur. type AdminQuery{ isAdmin: Boolean @@ -117,7 +141,7 @@ const User = ` mail: String phone: String # Groupes dont l'utilisateur est membre. - groups: [Group] + groups: [SimpleGroup] # Groupes que l'utilisateur aime. likes: [Group] address: String @@ -126,24 +150,56 @@ const User = ` const Group = ` # Groupes associatifs - type Group { - # Identifiant unique du groupe + + """ + L'interface Group représente les deux types de groupes implémentés dans Sigma : les groupes + simples, dont les membres sont des utilisateurs, et les métagroupes, dont les membres sont + des groupes simples (tel que Federez, dont les membres incluent le BR et DaTA). + """ + interface Group { + # Identifiant unique de l'union uid: ID - # Nom du groupe + # Nom de l'union name: String # Site du groupe website: String + # Le groupe est-il un spiritualGroup (sinon c'est un materialGroup) + createdAt: String! + updatedAt: String! + + } + + # Le groupe de base, dont les membres sont des utilisateurs. Binets, Kes, etc... + type SimpleGroup implements Group{ + uid: ID + name: String + website: String + createdAt: String! + updatedAt: String! + # Membres du groupe members: [User] # Jour et heure de création du groupe - createdAt: String! - updatedAt: String! description: String # École d'origine du groupe school: String # Groupe parent parent: Group } + + # Anciennement appelé "interface dans les discussions" + # Un groupe dont les membre sont d'autres groupes + type MetaGroup implements Group { + uid: ID + name: String + website: String + createdAt: String! + updatedAt: String! + + # Les groupes constitutifs du méta-groupe. + members: [Group]! + } + `; const Message = ` diff --git a/src/ldap/ldap_data.js b/src/ldap/ldap_data.js index dd10885b44566b24216210285f7d9b9537d7292e..76b28af83a261400781d0cd3cd8f3b9746cdee8e 100644 --- a/src/ldap/ldap_data.js +++ b/src/ldap/ldap_data.js @@ -32,7 +32,7 @@ var client = ldap.createClient({ url: config.ldap.server}); //------------------------------------------------------------------------------------------------------------------------ /** - * @summary Fonction qui sert à s'identifier sur le LDAP. Renvoit rien. + * @summary Fonction qui sert à s'identifier sur le LDAP. Ne renvoie rien. * @desc Assez important en terme de sécurité, de gestion de conflit, et de droit d'accès. Méthode ldapjs * (voir [`Client API`](http://ldapjs.org/client.html) méthode bind). * @arg {Object} user - Utilisateur de la forme suivante : @@ -159,7 +159,7 @@ function listerGroupes(user, uid) { * @desc Cette fonction utilise {@link rechercherLDAP} avec un dictionnaire prédéfini dans config.json. Elle utilise LDAPEscape pour éviter les injections. * @arg {Object} user - Utilisateur de la forme nécessaire à {@link connecterLDAP} * @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) */ function listerMembres(user, gid) { return new Promise((resolve, reject) => { @@ -199,8 +199,9 @@ function renseignerSurUtilisateur(user, uid) { //------------------------------------------------------------------------------------------------------------------------ /** - * @summary Fonction qui interroge le LDAP et retrouve les groupes (voir LDAP) qui ressemblent à l'input. Etape 0 vers un vrai TOL (Trombino On Line). - * @desc Cette fonction utilise {@link rechercherLDAP} mais avec un filtre généré à la volée. Accepte des champs exacts ou incomplets mais pas approximatifs et ne gère pas l'auto-complete. Cette fonction utilise aussi [`ldap_config.json`](..\..\ldap_config.json). MEF Timeout pour des recherches trop vagues. Renvoit une liste d'uid. Elle utilise LDAPEscape pour éviter les injections. + * @summary Fonction qui interroge le LDAP et retrouve les groupes (voir LDAP) qui ressemblent + * à l'entrée. Etape 0 vers un vrai TOL (Trombino On Line). + * @desc Cette fonction utilise {@link rechercherLDAP} mais avec un filtre généré à la volée. Accepte des champs exacts ou incomplets mais pas approximatifs et ne gère pas l'auto-complete. Cette fonction utilise aussi config.json. MEF Timeout pour des recherches trop vagues. Renvoit une liste d'uid. Elle utilise LDAPEscape pour éviter les injections. * @arg {Object} user - Utilisateur de la forme nécessaire à {@link connecterLDAP} * @arg {string} input - String entré par l'utilisateur qui ressemble au nom du groupe. * @return {Promise(string[])} Liste des uid de groupes dont le nom ressemble à l'input @@ -887,7 +888,9 @@ function editerGroupe(user, gid, data) { //TBC /** * @summary Fonction qui supprime un utilisateur du LDAP. - * @desc Cette fonction fait une utilisation massive d'eval pour anonymiser son code ; voir [`ldap_config.json`](..\..\ldap_config.json) pour le détail de la génération des champs finaux. Appelle {@link ajouterLDAP} bien sûr, mais aussi {@link ajouterMembreGroupe} et {@link ajouterAdministrateurGroupe} pour gérer les groupes du nouvel utilisateur. + * @desc Cette fonction fait une utilisation massive d'eval pour anonymiser son code ; + * voir ldap_config.json pour le détail de la génération des champs finaux. + * Appelle {@link ajouterLDAP} bien sûr, mais aussi {@link ajouterMembreGroupe} et {@link ajouterAdministrateurGroupe} pour gérer les groupes du nouvel utilisateur. * @arg {Object} user - Utilisateur de la forme nécessaire à {@link connecterLDAP} * @arg {string} uid - uid de la victime * @return {Promise(boolean)} `true` si la modification s'est bien déroulée, false sinon @@ -922,7 +925,10 @@ function supprimerUtilisateur(user, data) { //TBC /** * @summary Fonction qui supprime un groupe dans le LDAP. - * @desc Cette fonction fait une utilisation massive d'eval pour anonymiser son code ; c'est mal et cela suppose que beaucoup de soins ont été pris lors de l'escape de ses paramètres. Appelle {@link ajouterLDAP} et {@link modifierLDAP}, mais aussi {@link ajouterMembreGroupe} et {@link ajouterAdministrateurGroupe} pour gérer les groupes du nouvel utilisateur. + * @desc Cette fonction fait une utilisation massive d'eval pour anonymiser son code ; c'est mal et cela + * suppose que beaucoup de soins ont été pris lors de l'escape de ses paramètres. Appelle {@link ajouterLDAP} + * et {@link modifierLDAP}, mais aussi {@link ajouterMembreGroupe} et + * {@link ajouterAdministrateurGroupe} pour gérer les groupes du nouvel utilisateur. * @arg {Object} user - Utilisateur de la forme nécessaire à {@link connecterLDAP} * @arg {string} gid - Identifiant du groupe à supprimer * @return {Promise(boolean)} `true` si la modification s'est bien déroulée, false sinon diff --git a/src/server.js b/src/server.js index c0019c071114f463a7fc15d1d9e677eee224b54a..af64a856f58d649243d5718806047a07a6b4e248 100644 --- a/src/server.js +++ b/src/server.js @@ -22,7 +22,6 @@ import path from 'path'; import cors from 'cors'; const server = express(); - // on sait pas a quoi ca sert mais il parait que c'est utile server.use(bodyParser.json()); server.use(bodyParser.urlencoded({ @@ -114,7 +113,7 @@ server.use('/graphql', bodyParser.json(), })); // GraphiQL est une console interactive pour faire des requêtes au schéma GraphQL -server.use('/graphiql', ensureLoggedIn('/login'), graphiqlExpress({endpointURL: '/graphql'})); +server.use('/graphiql', /*ensureLoggedIn('/login'),*/ graphiqlExpress({endpointURL: '/graphql'})); // GraphQL voyager server.use('/voyager', graphqlVoyager({ endpointUrl: '/graphql' }));