diff --git a/db/migrations/20180307213043_fixes_and_messages.js b/db/migrations/20180307213043_fixes_and_messages.js new file mode 100644 index 0000000000000000000000000000000000000000..7c46a723b9ad3ae6efb4f501eb215e97dd1b0488 --- /dev/null +++ b/db/migrations/20180307213043_fixes_and_messages.js @@ -0,0 +1,44 @@ + +exports.up = async function(knex, Promise) { + await knex.schema.renameTable("posts", "messages"); + await knex.schema.table('messages', function(table){ + table.renameColumn('description', 'content'); + }); + await knex.schema.createTable('announcements', function (table){ + table.inherits('messages'); + }); + await knex.schema.createTable('events', function (table){ + table.inherits('messages'); + table.string('location'); + table.dateTime('start_time'); + table.dateTime('end_time'); + table.boolean('is_announcement'); + }); + await knex.schema.createTable('group_message_relationships', function(table){ + table.integer('message').notNullable(); + table.string('group').notNullable(); + table.enum('status', ['join', 'host', 'publish']).notNullable(); + }); + await knex.schema.createTable('user_participation', function(table){ + table.integer('event').notNullable(); + table.string('user_uid').notNullable(); + table.enum('user_db', ['ldap']).notNullable().defaultTo('ldap'); + }); + return; +}; + +exports.down = async function(knex, Promise) { + await knex.schema.dropTable('group_message_relationships'); + await knex.schema.dropTable('user_participation'); + let event_posts = await knex.select().from('events'); + let announcment_posts = await knex.select().from('announcements'); + await knex.schema.dropTable('events'); + await knex.schema.dropTable('announcements'); + await knex.schema.renameTable('messages', 'posts'); + await knex.schema.table('posts', function(table){ + table.renameColumn('content', 'description'); + }); + await knex('messages').insert(event_posts); + await knex('messages').insert(announcment_posts); + return; +}; diff --git a/src/graphql/connectors/connectors.js b/src/graphql/connectors/connectors.js index ed0e679d7a4458831f1e52b673017ffd2a040d0b..9981605456b1e984d51bed623c261609ed8639ec 100644 --- a/src/graphql/connectors/connectors.js +++ b/src/graphql/connectors/connectors.js @@ -11,45 +11,45 @@ import { exportAllDeclaration } from 'babel-types'; export { renseignerSurUtilisateur, repliquerTOLdesIds, listerMembres }; /* -Le tag @rights et la gestion des authorisations - -Le système GraphQL est pensé comme l'interface par laquelle les utilisateurs -intéragissent avec sigma, les graphismes en moins. -Pour cette raison, et pour des questions de sécurité, il faut partir du principe que -l'utilisateir peut rédiger n'importe quelle requête, et que c'est au niveau des resolvers -que la sécurité ce déroule. C'est dans cette optique que j'utilise le tag @rights - -Commençons par un rappel sur le fonctionnement des droits. -Chaque utilisateur a un certain niveau de droit sur chaque groupe. Ce niveau de droit indique -ce qu'il a le droit de savoir et de faire. Chaque niveau est inclu dans les niveaus supérieur. -Les différends niveaux sont : -none - aucun droit -viewer : l'utilisateur a visibilité sur le groupe. Il sait que le groupe existe, et a accès à un certain nombre d'infos. -member : l'utilisateur est membre du groupe -speaker : l'utilisateur peut parler au nom du groupe. Il a le droit de publier des annonces et d'organiser des évènements -admin : l'utilisateur a tous les droits sur le groupe - -Certaines fonctions de connectors effectuent des vérifications d'authorisations avant -de renvoyer une réponse, d'autres non. Pour être sur qu'on ne renvoie jamais de réponse -sans avoir au préalable éffectué les bonnes vérifications, chaque fonction possède dans sa -description un attribut droit, qui décrit les droits que fournit cette fonction. - -La valeur de @rights peut être : -super - la fonction n'effectue aucune vérification, et renvoie le resultat demandé -admin( groupUID ) - la fonction ne fait que ce qu'un admin du groupe indiqué aurait le droit de faire -speaker( groupUID ), member( groupUID ), veiwer( groupUID ) - même chose -user - la fonction ne fait que ce que l'utiliateur a le droit de faire (vérifications via l'argument user) - -La procédure a suivre est la suivante : quand une fonction possède un certain niveau de droit, -elle ne peut appeler une fonction possédant un niveau de droit plus large que si -1 ) on a au préalable vérifié que l'utilisateur possédait effectivement ces droits. -ou -2 ) on s'est assuré que l'opération effectuée par cet appel particulier de la fonction était dans les droits -de l'utilisateur - -Les resolvers de base de mutation et query ont des droits user. - -Les fonctions qui ne modifient pas la BDD et ne renvoient pas de données sur la BDD n'ont pas de rights. + Le tag @rights et la gestion des authorisations + + Le système GraphQL est pensé comme l'interface par laquelle les utilisateurs + intéragissent avec sigma, les graphismes en moins. + Pour cette raison, et pour des questions de sécurité, il faut partir du principe que + l'utilisateir peut rédiger n'importe quelle requête, et que c'est au niveau des resolvers + que la sécurité ce déroule. C'est dans cette optique que j'utilise le tag @rights + + Commençons par un rappel sur le fonctionnement des droits. + Chaque utilisateur a un certain niveau de droit sur chaque groupe. Ce niveau de droit indique + ce qu'il a le droit de savoir et de faire. Chaque niveau est inclu dans les niveaus supérieur. + Les différends niveaux sont : + none - aucun droit + viewer : l'utilisateur a visibilité sur le groupe. Il sait que le groupe existe, et a accès à un certain nombre d'infos. + member : l'utilisateur est membre du groupe + speaker : l'utilisateur peut parler au nom du groupe. Il a le droit de publier des annonces et d'organiser des évènements + admin : l'utilisateur a tous les droits sur le groupe + + Certaines fonctions de connectors effectuent des vérifications d'authorisations avant + de renvoyer une réponse, d'autres non. Pour être sur qu'on ne renvoie jamais de réponse + sans avoir au préalable éffectué les bonnes vérifications, chaque fonction possède dans sa + description un attribut droit, qui décrit les droits que fournit cette fonction. + + La valeur de @rights peut être : + super - la fonction n'effectue aucune vérification, et renvoie le resultat demandé + admin( groupUID ) - la fonction ne fait que ce qu'un admin du groupe indiqué aurait le droit de faire + speaker( groupUID ), member( groupUID ), veiwer( groupUID ) - même chose + user - la fonction ne fait que ce que l'utiliateur a le droit de faire (vérifications via l'argument user) + + La procédure a suivre est la suivante : quand une fonction possède un certain niveau de droit, + elle ne peut appeler une fonction possédant un niveau de droit plus large que si + 1 ) on a au préalable vérifié que l'utilisateur possédait effectivement ces droits. + ou + 2 ) on s'est assuré que l'opération effectuée par cet appel particulier de la fonction était dans les droits + de l'utilisateur + + Les resolvers de base de mutation et query ont des droits user. + + Les fonctions qui ne modifient pas la BDD et ne renvoient pas de données sur la BDD n'ont pas de rights. */ @@ -420,15 +420,78 @@ export const getUser = (user, userUID) => { return result; }; +export async function getVisibleAnnouncementsCallback(user){ + return query_builder => { + return query_builder; + }; +}; + +export async function getVisibleEventsCallback(user){ + return query_builder => { + return query_builder; + }; +}; + /** - * @function Renvoie un événement en fonction de son identifiant. + * @summary Renvoie un message en fonction de son identifiant. * @param {*} user - Utilisateur effectuant la requête. * @param {*} eventID - Identifiant unique de l'événement. + * @rights super */ -export const getEvent = (user, eventID) => { - return quickPromise(null); +export async function getMessage(user, messageID){ + let announcement = await knex.select().from('announcements').where('id', messageID); + if(announcement){ + let res = announcement[0]; + res.type = "Announcement" + return res; + } + let event = await knex.select().from('events').where('id', messageID); + if(event){ + let res = event[0]; + res.type = "Event" + return res; + } + return undefined; }; +/** + * @summary Renvoie un message en fonction de son identifiant. + * @param {*} user - Utilisateur effectuant la requête. + * @param {*} eventID - Identifiant unique de l'événement. + * @rights super + */ +export async function getMessageIfVisible(user, messageID){ + let announcement = await knex + .with('visible_announcements', await getVisibleAnnouncementsCallback(user)) + .select().from('visible_announcements').where('id', messageID); + if(announcement){ + let res = announcement[0]; + res.type = "Announcement" + return res; + } + let event = await knex + .with('visible_events', await getVisibleEventsCallback(user)) + .select().from('visible_events').where('id', messageID); + if(event){ + let res = event[0]; + res.type = "Event" + return res; + } + return undefined; +}; + + +export async function getMessageHostsCallback(user, messageID){ + return function(query_builder){ + return query_builder.select('group').from('group_message_relationships') + .where('message', messageID); + } +} + +export async function getMessageHosts(user, messageID){ + +} + /** * @summary Renvoie simplement un groupe en fonction de son identifiant. * @param {Object} user - Utilisateur effectuant la requête. @@ -539,6 +602,55 @@ export async function getMetaGroupMemberUsers(user, metaGroupUID){ } }; +function recursive_callback_intersecter(callbackList){ + let cb = callbackList.pop(); + if(callbackList){ + let intersecter = recursive_callback_intersecter(callbackList); + return function(query_builder){ + return knex.with('callback_set', cb) + .with('intersection', intersection) + .select('intersection.uid').from('intersection') + .innerJoin('callback_set', function(){ + this.on('callback_set.uid', '=', 'intersection.uid'); + }); + } + }else{ + return cb; + } +} + +export async function getSimpleGroupsFromCallbacks (user, cbList){ + + let intersection = await recursive_callback_intersecter(cbList); + return knex.with('intersection', intersection).select().from("simple_groups") + .innerJoin('intersection', function (){ + this.on('intersection.uid', '=', 'simple_groups.uid'); + }); +} + +export async function getMetaGroupsFromCallbacks (user, cbList){ + + let intersection = await recursive_callback_intersecter(cbList); + return knex.with('intersection', intersection).select().from("meta_groups") + .innerJoin('intersection', function (){ + this.on('intersection.uid', '=', 'meta_groups.uid'); + }); +} + +/** + * @summary Renvoie tous les groupes dans l'intersection définie par les callbacks + * @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. + * @rights user + */ +export async function getGroupsFromCallbacks(user, cbList){ + let all_simple_groups = await getSimpleGroupsFromCallbacks(user, cbList); + let all_meta_groups = await getMetaGroupsFromCallbacks(user, cbList); + return all_simple_groups.concat(all_meta_groups); +} + /* * réflexion sur une façon possible de gérer les utilisateurs sans en demander trop à LDAP diff --git a/src/graphql/resolvers.js b/src/graphql/resolvers.js index 0ba5f15f2b999610a919638547628c8453d9ae42..5539fc0896185343ef3fb4058e33f4540b0dca47 100644 --- a/src/graphql/resolvers.js +++ b/src/graphql/resolvers.js @@ -261,6 +261,35 @@ export const resolvers = { } }, + Message: { + + __resolveType: function(obj){ + return obj.type; + } + + }, + + Announcement: { + forEvent : function(obj, args, context){ + // le champ is_announcement n'existe que sur les Events + // une ligne de la bdd events peut résoudre comme évènement ou comme annonce + if(obj.is_announcement) + return obj; + else + return null; + } + }, + + Event: { + asAnnouncement : function(obj, args, context){ + // le champ is_announcement indique si il existe une annonce qui va avec l'évènement + // une ligne de la bdd events peut résoudre comme évènement ou comme annonce + if(obj.is_announcement) + return obj; + else + return null; + } + }, // @rights viewer(obj.uid) Group: { diff --git a/src/graphql/typeDefs/actions.js b/src/graphql/typeDefs/actions.js index 53a982caeb0e23860aa0b4ba87f297f85cc7356c..6e0fad9e9fa94bb479455bdfe017373067f5ea4a 100644 --- a/src/graphql/typeDefs/actions.js +++ b/src/graphql/typeDefs/actions.js @@ -87,7 +87,7 @@ const subQueries = ` allMessages: [Message] allEvents: [Event] allPosts: [Post] - post(id: ID): Post + message(id: ID): Message allAnnouncements: [Announcement] } diff --git a/src/graphql/typeDefs/objects.js b/src/graphql/typeDefs/objects.js index 454dabc24c384d6330983356271208051557a7f7..5c580f7f582c5efc163f0d6d17233482a4d58ea2 100644 --- a/src/graphql/typeDefs/objects.js +++ b/src/graphql/typeDefs/objects.js @@ -84,6 +84,7 @@ const Message = ` id: ID! # Titre du message title: String! + content: String createdAt: String! updatedAt: String! # Auteur(s) du message @@ -99,7 +100,7 @@ const Post = ` createdAt: String! updatedAt: String! authors: [Group] - description: String! + content: String! } `; @@ -111,8 +112,10 @@ const Announcement = ` createdAt: String! updatedAt: String! authors: [Group] - description: String! + content: String! + importance: Int views: Int + forEvent : Event } `; @@ -134,6 +137,7 @@ const Event = ` participatingGroups: [Group] participatingUsers: [User] description: String + asAnnouncement : Announcment } `;