diff --git a/src/graphql/connectors/authorisationModel.ts b/src/graphql/connectors/authorisationModel.ts index 7e13d67fc7a79fbf4a673a2cecbfa239db933bfc..010ba6ead4dce52d5dade249162d9715bcbd8cdd 100644 --- a/src/graphql/connectors/authorisationModel.ts +++ b/src/graphql/connectors/authorisationModel.ts @@ -121,6 +121,20 @@ export class AuthorisationModel { return false; } + /** + * @memberof GraphQL.AuthorisationModel# + * @function groupsViewer + * @summary Fonction qui renvoit les groupes dont l'utilisateur est viewer. + * @return {GroupCollection} Renvoie la collection de groupes dont l'utilisateur est viewer. + */ + groupsViewer(): GroupCollection { + //ensure uid is valid !!!!!!!! + if (this.isAuthenticated()) { + return this.viewerOf; + } + return null; + } + /** * @memberof GraphQL.AuthorisationModel# * @function isMember @@ -158,6 +172,20 @@ export class AuthorisationModel { return false; } + /** + * @memberof GraphQL.AuthorisationModel# + * @function groupsMember + * @summary Fonction qui renvoit les groupes dont l'utilisateur est member. + * @return {GroupCollection} Renvoie la collection de groupes dont l'utilisateur est member. + */ + groupsMember(): GroupCollection { + //ensure uid is valid !!!!!!!! + if (this.isAuthenticated()) { + return this.memberOf; + } + return null; + } + /** * @memberof GraphQL.AuthorisationModel# * @function isSpeaker @@ -174,6 +202,20 @@ export class AuthorisationModel { return false; } + /** + * @memberof GraphQL.AuthorisationModel# + * @function groupsSpeaker + * @summary Fonction qui renvoit les groupes dont l'utilisateur est speaker. + * @return {GroupCollection} Renvoie la collection de groupes dont l'utilisateur est speaker. + */ + groupsSpeaker(): GroupCollection { + //ensure uid is valid !!!!!!!! + if (this.isAuthenticated()) { + return this.speakerOf; + } + return null; + } + /** * @memberof GraphQL.AuthorisationModel# * @function isAdmin @@ -195,6 +237,20 @@ export class AuthorisationModel { return false; } + /** + * @memberof GraphQL.AuthorisationModel# + * @function groupsAdmin + * @summary Fonction qui renvoit les groupes dont l'utilisateur est admin. + * @return {GroupCollection} Renvoie la collection de groupes dont l'utilisateur est admin. + */ + groupsAdmin(): GroupCollection { + //ensure uid is valid !!!!!!!! + if (this.isAuthenticated()) { + return this.adminOf; + } + return null; + } + /** * @memberof GraphQL.AuthorisationModel# * @function isSupervisor @@ -211,4 +267,18 @@ export class AuthorisationModel { return false; } + /** + * @memberof GraphQL.AuthorisationModel# + * @function groupsSupervisor + * @summary Fonction qui renvoit les groupes dont l'utilisateur est supervisor. + * @return {GroupCollection} Renvoie la collection de groupes dont l'utilisateur est supervisor. + */ + groupsSupervisor(): GroupCollection { + //ensure uid is valid !!!!!!!! + if (this.isAuthenticated()) { + return this.supervisorOf; + } + return null; + } + } \ No newline at end of file diff --git a/src/graphql/connectors/groupModel.ts b/src/graphql/connectors/groupModel.ts index 67b0852c1c1e03e82aecdf75345efc1589e86d53..24431454ad110fe2986861d0b29e3c4f29d82d0f 100644 --- a/src/graphql/connectors/groupModel.ts +++ b/src/graphql/connectors/groupModel.ts @@ -123,11 +123,11 @@ export class GroupModel { * @function getAllSimpleGroups * @summary Fonction qui renvoie tous les groupes simples donnés en argument. * @arg {GroupSet} groups - Ensemble d'identifiants, supposés valides. - * @return {Group[]} Renvoie le tableau de groupes correspondant + * @return {SimpleGroup[]} Renvoie le tableau de groupes correspondant * @rights connectedOrOnplatal */ - getAllSimpleGroups(groups: GroupSet): Group[] { - let res: Group[] + getAllSimpleGroups(groups: GroupSet): SimpleGroup[] { + let res: SimpleGroup[] for (let g of groups) { res.push(new SimpleGroup(g)); diff --git a/src/graphql/resolvers.ts b/src/graphql/resolvers.ts index fcc78cafc0d9a0e609d25979b2368662d4715d44..4285c874340c8d30d15a90dd2d53e57ea25b2491 100644 --- a/src/graphql/resolvers.ts +++ b/src/graphql/resolvers.ts @@ -1,171 +1,251 @@ /** * @file Implémentation des requêtes GraphQL. - * @author akka vodol + * @author akka vodol, ofacklam * @memberof GraphQL */ -import _ from 'lodash'; -import { assertBinaryExpression } from 'babel-types'; //hawkspar->akka ASKIP useless -import knex from '../../db/knex_router'; - -import '../config_passport'; - -import * as auth from './connectors/authorisation'; -import * as conn from './connectors/connection'; - -import MessageResolvers from './resolvers/messages'; -import GroupResolvers from './resolvers/groups'; - -class ChevalierError { - /** - * @memberof GraphQL - * @class ChevalierError - * @summary Erreur dans le code de hawkspar. - * @classdesc Une erreur dans le code de hawkspar. - */ - - returned_error: any - name: String - message: String - constructor(err) { - this.returned_error = err; - this.name = "chevalier error"; - this.message = "Error encountered while running ldap access code : " + err.message; - } - - toString(): String { - return this.message; - } -} +import { AuthenticationError } from "apollo-server-core"; +import { Context } from "./typeDefs/queries"; +import { User } from "./resolvers/users"; +import { Group, SimpleGroup, MetaGroup } from "./resolvers/groups"; +import { Announcement, Event, PrivatePost, Question, Answer, Message } from "./resolvers/messages"; +import { UserJoinGroup, GroupJoinMetagroup, GroupCoauthorEvent, Request } from "./resolvers/requests"; +import { GroupCollection } from "./connectors/tools"; + +/* + Le tag @rights est la gestion des autorisations. + + Le système GraphQL est pensé comme l'interface par laquelle les utilisateurs + intéragissent avec sigma, les graphismes en moins. + Le client peut envoyer tout type de requête. C'est au niveau des resolvers + que les permissions sont gérées. D'où le @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 inclus dans les niveaus supérieur. + Les différents 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éri-fication, 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. +*/ /** * Résolveurs des différentes requêtes GraphQL */ export const resolvers = { - // @rights user Query: { - // group queries - - allGroups: async function(root, args, context) : Promise<conn.Group[]>{ - const visibleGroupSet = await auth.forViewer(context.user); - return conn.getGroups(visibleGroupSet); + // User queries de base + // @rights connectedOrOnplatal + user: async function (root, args, context: Context): Promise<User> { + if(context.models.auth.isConnectedOrOnplatal()) { + return context.models.user.getUser(args.uid); + } + throw new AuthenticationError("Not connected or on-platal"); }, - allSimpleGroups: async function (root, args, context) : Promise<conn.Group[]>{ - const visibleGroupSet = await auth.forViewer(context.user); - return conn.getSimpleGroups(visibleGroupSet); + // Group queries de base + // @rights connectedOrOnplatal + group: async function (root, args, context: Context): Promise<Group> { + if (context.models.auth.isConnectedOrOnplatal()) { + return context.models.group.getGroup(args.gid); + } + throw new AuthenticationError("Not connected or on-platal"); }, - allMetaGroups: async function(root, args, context) : Promise<conn.Group[]>{ - const visibleGroupSet = await auth.forViewer(context.user); - return conn.getMetaGroups(visibleGroupSet); + // @rights connectedOrOnplatal + simpleGroup: async function (obj, args, context: Context): Promise<SimpleGroup> { + if (context.models.auth.isConnectedOrOnplatal()) { + return context.models.group.getSimpleGroup(args.gid); + } + throw new AuthenticationError("Not connected or on-platal"); }, - group: async function(root, args, context) : Promise<conn.Group>{ - if(await auth.isViewer(context.user, args.gid)){ - return conn.getGroup(args.gid); - }else{ - return null; + // @rights connectedOrOnplatal + metaGroup: async function (obj, args, context: Context): Promise<MetaGroup> { + if (context.models.auth.isConnectedOrOnplatal()) { + return context.models.group.getMetaGroup(args.gid); } + throw new AuthenticationError("Not connected or on-platal"); }, - simpleGroup: async function(obj, args, context) : Promise<conn.Group>{ - if(await auth.isViewer(context.user, args.gid)){ - return conn.getSimpleGroup(args.gid); - }else{ - return null; - } + // Message queries de base + //message -> not implemented -> message(mid: ID!): Message + + // @rights membre d'un groupe author ou d'un groupe recipient + announcement: async function (root, args, context: Context): Promise<Announcement> { + //TODO : verifier les autorisations + throw "Not implemented"; + return context.models.message.getAnnouncement(args.mid); + throw new AuthenticationError("Not connected or on-platal"); }, - metaGroup: async function(obj, args, context) : Promise<conn.Group>{ - if(await auth.isViewer(context.user, args.gid)){ - return conn.getMetaGroup(args.gid); - }else{ - return null; - } + // @rights membre d'un groupe author ou d'un groupe recipient + event: async function (root, args, context: Context): Promise<Event> { + //TODO : verifier les autorisations + throw "Not implemented"; + return context.models.message.getEvent(args.mid); + throw new AuthenticationError("Not connected or on-platal"); }, - /* - * Message queries. - */ + // @rights membre du groupe recipient + privatePost: async function (root, args, context: Context): Promise<PrivatePost> { + //TODO : verifier les autorisations + throw "Not implemented"; + return context.models.message.getPrivatePost(args.mid); + throw new AuthenticationError("Not connected or on-platal"); + }, - allAnnouncements: async function(obj, args, context) : Promise<Object[]> { - const visibleGroupSet = await auth.forViewer(context.user); - return conn.allAnnouncements(visibleGroupSet); + // @rights viewer du groupe recipient + question: async function (root, args, context: Context): Promise<Question> { + //TODO : verifier les autorisations + throw "Not implemented"; + return context.models.message.getQuestion(args.mid); + throw new AuthenticationError("Not connected or on-platal"); }, - allEvents: async function (root, args, context) : Promise<Object[]> { - const visibleGroupSet = await auth.forViewer(context.user); - return conn.allAnnouncements(visibleGroupSet); + // @rights viewer du groupe author + answer: async function (root, args, context: Context): Promise<Answer> { + //TODO : verifier les autorisations + throw "Not implemented"; + return context.models.message.getAnswer(args.mid); + throw new AuthenticationError("Not connected or on-platal"); }, - // user queries + // Request queries de base + //request -> not implemented -> request(rid: ID!): Request - user: async function(root, args, context): Promise<conn.User> { - return conn.getUser(args.uid); + // @rights admin du groupe destinaire ou le user émetteur + userJoinGroupRequest: async function (root, args, context: Context): Promise<UserJoinGroup> { + //TODO : verifier les autorisations + throw "Not implemented"; + return context.models.request.getUserJoinGroupRequest(args.rid); + throw new AuthenticationError("Not connected or on-platal"); }, - searchTOL: (root, args, context): Promise<conn.User[]> => { - // TODO : filter - return conn.searchUsers(args); + // @rights admin du groupe émetteur ou destinataire + groupJoinMetagroupRequest: async function (root, args, context: Context): Promise<GroupJoinMetagroup> { + //TODO : verifier les autorisations + throw "Not implemented"; + return context.models.request.getGroupJoinMetagroupRequest(args.rid); + throw new AuthenticationError("Not connected or on-platal"); }, - test: async function(root, args, context){ - return conn.getSimpleGroup("br"); - } - }, + // @rights admin du groupe émetteur ou destinataire + groupCoauthorEventRequest: async function (root, args, context: Context): Promise<GroupCoauthorEvent> { + //TODO : verifier les autorisations + throw "Not implemented"; + return context.models.request.getGroupCoauthorEventRequest(args.rid); + throw new AuthenticationError("Not connected or on-platal"); + }, + // Tous les Messages visibles par un utilisateur(dont le uid, et donc les autorisations, est passé par context) + // @rights member of groups + allMessages: async function (root, args, context: Context): Promise<Message[]> { + let groups = context.models.auth.groupsMember(); + return context.models.message.getAllMessages(groups); + }, - Request : { - __resolveType : function(obj){ - return obj.type; - } - }, + // @rights member of groups + allAnnouncements: async function (root, args, context: Context): Promise<Announcement[]> { + let groups = context.models.auth.groupsMember(); + return context.models.message.getAllAnnouncements(groups); + }, - // @rights admin(obj.groupUID) - UserJoinGroup: { - user : (obj, args, context) => { - return conn.getUser(obj.useruid); - } - }, + // @rights member of groups + allEvents: async function (root, args, context: Context): Promise<Event[]> { + let groups = context.models.auth.groupsMember(); + return context.models.message.getAllEvents(groups); + }, - // @rights speaker(obj.groupUID) - GroupJoinEvent : { - event: (obj, args, context) => { - return conn.getEvent(obj.eventuid); + // @rights member of groups + allPrivatePosts: async function (root, args, context: Context): Promise<PrivatePost[]> { + let groups = context.models.auth.groupsMember(); + return context.models.message.getAllPrivatePosts(groups); }, - groupWantingToJoin: (obj, args, context) => { - return conn.getGroup(obj.senderuid); - } - }, - // @rights speaker(obj.groupUID) - YourGroupHostEvent : { - event: (obj, args, context) => { - return conn.getEvent(obj.eventuid); + // Tous les Groupes visibles par un utilisateur. + // Correspondrait au sous - champ "viewerOf" de User, volontairement non - défini comme tel.Tous les autres cas de figure sont couverts par les sous - champs "<permission>Of" de User + // @rights viewer of groups + allGroups: async function (root, args, context: Context): Promise<Group[]> { + let visibleGroupCollection = context.models.auth.groupsViewer(); + return context.models.group.getAllGroupsByCollection(visibleGroupCollection); }, - sender: (obj, args, context) => { - return conn.getGroup(obj.senderuid); - } - }, - // @rights user - User : { - groups : (obj, args, context) => { - let result = Promise.all(obj.groups.map((grid) => { - return conn.getSimpleGroup(grid); - })); + // @rights viewer of groups + allSimpleGroups: async function (root, args, context: Context): Promise<SimpleGroup[]> { + let visibleGroupCollection = context.models.auth.groupsViewer(); + return context.models.group.getAllSimpleGroups(visibleGroupCollection.simpleGroups); + }, + + // Toutes les Requests auxquelles un groupe doit répondre + // @rights admin + requestsToGroup: async function (root, args, context: Context): Promise<Request[]> { + if(context.models.auth.isAdmin(args.gid)) { + return context.models.request.getRequestsToGroup(args.gid); + } + throw new AuthenticationError("Not an admin"); + }, - return result.then(groups => { - return _.filter(groups,(o) => !_.isUndefined(o)); - }); + // @rights admin + userJoinGroupRequestsToGroup: async function (root, args, context: Context): Promise<UserJoinGroup[]> { + if (context.models.auth.isAdmin(args.gid)) { + return context.models.request.getUserJoinGroupRequestsToGroup(args.gid); + } + throw new AuthenticationError("Not an admin"); }, + // @rights admin + groupJoinMetagroupRequestsToGroup: async function (root, args, context: Context): Promise<GroupJoinMetagroup[]> { + if (context.models.auth.isAdmin(args.gid)) { + return context.models.request.getGroupJoinMetagroupRequestsToGroup(args.gid); + } + throw new AuthenticationError("Not an admin"); + }, + + // @rights admin + groupCoauthorEventRequestsToGroup: async function (root, args, context: Context): Promise<GroupCoauthorEvent[]> { + if (context.models.auth.isAdmin(args.gid)) { + return context.models.request.getGroupCoauthorEventRequestsToGroup(args.gid); + } + throw new AuthenticationError("Not an admin"); + }, + + // TOL + // @rights connectedOrOnplatal + searchTOL: async function (root, args, context: Context): Promise<User[]> { + if (context.models.auth.isConnectedOrOnplatal()) { + return context.models.user.searchTOL(args); + } + throw new AuthenticationError("Not connected or on-platal"); + } }, - // @rights user Mutation: { // Superviser mutations @@ -188,10 +268,5 @@ export const resolvers = { createSubgroup: async function (obj, args, context){ throw new Error('Not implemented'); }, - }, - - - ...MessageResolvers, - - ...GroupResolvers, + } };