/** * @file Implémentation des requêtes GraphQL. * @author akka vodol, ofacklam * @memberof GraphQL */ 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, GroupSet } 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 = { Query: { // 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"); }, // 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"); }, // @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"); }, // @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"); }, // 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"); }, // @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"); }, // @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"); }, // @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"); }, // @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"); }, // Request queries de base //request -> not implemented -> request(rid: ID!): Request // @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"); }, // @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"); }, // @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); }, // @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 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 member of groups allPrivatePosts: async function (root, args, context: Context): Promise<PrivatePost[]> { let groups = context.models.auth.groupsMember(); return context.models.message.getAllPrivatePosts(groups); }, // 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); }, // @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); }, // @rights supervisor of groups supervisorOf: async function (root, args, context: Context): Promise<Group[]> { let supervisorGroupCollection = context.models.auth.groupsSupervisor(); return context.models.group.getAllGroupsByCollection(supervisorGroupCollection); }, // 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"); } }, Mutation: { // Groups - independent mutations // @rights authenticated editProfile: async function (root, args, context: Context): Promise<User> { if(context.models.auth.isAuthenticated()) { return context.models.user.editProfile(args); } throw new AuthenticationError("Not authenticated"); }, // Viewer mutations // @rights viewer likeGroup: async function (root, args, context: Context): Promise<boolean> { if (context.models.auth.isViewer(args.gid)) { return context.models.group.likeGroup(args.gid) } throw new AuthenticationError("Not a viewer"); }, // @rights viewer unlikeGroup: async function (root, args, context: Context): Promise<boolean> { if (context.models.auth.isViewer(args.gid)) { return context.models.group.unlikeGroup(args.gid) } throw new AuthenticationError("Not a viewer"); }, // @rights member d'un groupe author ou recipient userParticipate: async function (root, args, context: Context): Promise<boolean> { throw "Not implemented"; //TODO : Vérifier les autorisations return context.models.message.userParticipate(context.user.uid, args.forEvent); throw new AuthenticationError("Not a viewer"); }, // @rights member d'un groupe author ou recipient userUnparticipate: async function (root, args, context: Context): Promise<boolean> { throw "Not implemented"; //TODO : Vérifier les autorisations return context.models.message.userUnparticipate(context.user.uid, args.forEvent); throw new AuthenticationError("Not a viewer"); }, // @rights viewer userRequestJoinGroup: async function (root, args, context: Context): Promise<UserJoinGroup> { if(context.models.auth.isViewer(args.toGroup)) { return context.models.request.userRequestJoinGroup(args.toGroup, args.comment); } throw new AuthenticationError("Not a viewer"); }, // @rights viewer createQuestion: async function (root, args, context: Context): Promise<Question> { if (context.models.auth.isViewer(args.toGroup)) { return context.models.message.createQuestion(args.toGroup, args.title, args.content); } throw new AuthenticationError("Not a viewer"); }, // @rights viewer du groupe et author de la question editQuestion: async function (root, args, context: Context): Promise<Question> { throw "Not implemented"; // TODO : Vérifier qu'il est l'auteur de la question et viewer return context.models.message.editQuestion(args.questionToEdit, args.title, args.content); throw new AuthenticationError("Not a viewer"); }, // @rights viewer du groupe et author de la question removeQuestion: async function (root, args, context: Context): Promise<boolean> { throw "Not implemented"; // TODO : Vérifier qu'il est l'auteur de la question et viewer return context.models.message.removeQuestion(args.questionToRemove); throw new AuthenticationError("Not a viewer"); }, // Member mutations // @rights member userLeaveGroup: async function (root, args, context: Context): Promise<boolean> { if (context.models.auth.isMember(args.gid)) { return context.models.group.userLeaveGroup(args.gid); } throw new AuthenticationError("Not a member"); }, // @rights member createPrivatePost: async function (root, args, context: Context): Promise<PrivatePost> { if (context.models.auth.isMember(args.toGroup)) { return context.models.message.createPrivatePost(args.toGroup, args.title, args.content); } throw new AuthenticationError("Not a member"); }, // @rights member du groupe et author du post editPrivatePost: async function (root, args, context: Context): Promise<PrivatePost> { throw "Not implemented"; // TODO : Vérifier qu'il est l'auteur du post et member return context.models.message.editPrivatePost(args.privatePostToEdit, args.title, args.content); throw new AuthenticationError("Not a member"); }, // @rights member du groupe et author du post removePrivatePost: async function (root, args, context: Context): Promise<boolean> { throw "Not implemented"; // TODO : Vérifier qu'il est l'auteur du post et member return context.models.message.removePrivatePost(args.privatePostToRemove); throw new AuthenticationError("Not a member"); }, // Speaker mutations // @rights speaker writePostsSummary: async function (root, args, context: Context): Promise<boolean> { if (context.models.auth.isSpeaker(args.forGroup)) { return context.models.group.writePostsSummary(args.forGroup, args.content); } throw new AuthenticationError("Not a speaker"); }, // @rights speaker groupParticipate: async function (root, args, context: Context): Promise<boolean> { if (context.models.auth.isSpeaker(args.gid)) { return context.models.message.groupParticipate(args.gid, args.forEvent); } throw new AuthenticationError("Not a speaker"); }, // @rights speaker groupUnparticipate: async function (root, args, context: Context): Promise<boolean> { if (context.models.auth.isSpeaker(args.gid)) { return context.models.message.groupUnparticipate(args.gid, args.forEvent); } throw new AuthenticationError("Not a speaker"); }, // @rights speaker du groupe émetteur groupRequestCoauthorEvent: async function (root, args, context: Context): Promise<GroupCoauthorEvent> { if (context.models.auth.isSpeaker(args.fromGroup)) { return context.models.request.groupRequestCoauthorEvent(args.fromGroup, args.toGroup, args.forEvent, args.comment); } throw new AuthenticationError("Not a speaker"); }, // @rights speaker du groupe émetteur createAnnouncement: async function (root, args, context: Context): Promise<Announcement> { if (context.models.auth.isSpeaker(args.fromGroup)) { return context.models.message.createAnnouncement(args.fromGroup, new GroupSet(args.toGroups), args.title, args.content, args.forEvent); } throw new AuthenticationError("Not a speaker"); }, // @rights speaker du groupe émetteur editAnnouncement: async function (root, args, context: Context): Promise<Announcement> { throw "Not implemented"; // TODO : Vérifier les autorisations. return context.models.message.editAnnouncement(args.announcementToEdit, args.title, args.content, args.forEvent); throw new AuthenticationError("Not a speaker"); }, // @rights speaker du groupe émetteur removeAnnouncement: async function (root, args, context: Context): Promise<boolean> { throw "Not implemented"; // TODO : Vérifier les autorisations return context.models.message.removeAnnouncement(args.announcementToRemove); throw new AuthenticationError("Not a speaker"); }, // @rights speaker du groupe émetteur createEvent: async function (root, args, context: Context): Promise<Event> { if (context.models.auth.isSpeaker(args.fromGroup)) { return context.models.message.createEvent(args.fromGroup, new GroupSet(args.toGroups), args.title, args.content, args.location, args.startTime, args.endTime, args.forAnnouncement); } throw new AuthenticationError("Not a speaker"); }, // @rights speaker du groupe émetteur editEvent: async function (root, args, context: Context): Promise<Event> { throw "Not implemented"; // TODO : Vérifier les autorisations. return context.models.message.editEvent(args.eventToEdit, args.title, args.content, args.location, args.startTime, args.endTime, args.forAnnouncement); throw new AuthenticationError("Not a speaker"); }, // @rights speaker du groupe émetteur removeEvent: async function (root, args, context: Context): Promise<boolean> { throw "Not implemented"; // TODO : Vérifier les autorisations return context.models.message.removeEvent(args.eventToRemove); throw new AuthenticationError("Not a speaker"); }, // @rights speaker du groupe createAnswer: async function (root, args, context: Context): Promise<Answer> { throw "Not implemented"; // TODO : Vérifier les autorisations. return context.models.message.createAnswer(args.forQuestion, args.title, args.content); throw new AuthenticationError("Not a speaker"); }, // @rights speaker du groupe editAnswer: async function (root, args, context: Context): Promise<Answer> { throw "Not implemented"; // TODO : Vérifier les autorisations. return context.models.message.editAnswer(args.answerToEdit, args.title, args.content); throw new AuthenticationError("Not a speaker"); }, // @rights speaker du groupe removeAnswer: async function (root, args, context: Context): Promise<boolean> { throw "Not implemented"; // TODO : Vérifier les autorisations return context.models.message.removeAnswer(args.answerToRemove); throw new AuthenticationError("Not a speaker"); }, // Admin mutations // @rights admin of parent group createSubgroup: async function (root, args, context: Context): Promise<Group> { if(context.models.auth.isAdmin(args.fromGroup)) { return context.models.group.createSubgroup(args); } throw new AuthenticationError("Not an admin"); }, // @rights admin makeAdmin: async function (root, args, context: Context): Promise<User> { if (context.models.auth.isAdmin(args.forGroup)) { return context.models.group.makeAdmin(args.forGroup, args.uid); } throw new AuthenticationError("Not an admin"); }, // @rights admin unmakeAdmin: async function (root, args, context: Context): Promise<User> { if (context.models.auth.isAdmin(args.forGroup)) { return context.models.group.unmakeAdmin(args.forGroup, args.uid); } throw new AuthenticationError("Not an admin"); }, // @rights admin makeSpeaker: async function (root, args, context: Context): Promise<User> { if (context.models.auth.isAdmin(args.forGroup)) { return context.models.group.makeSpeaker(args.forGroup, args.uid); } throw new AuthenticationError("Not an admin"); }, // @rights admin unmakeSpeaker: async function (root, args, context: Context): Promise<User> { if (context.models.auth.isAdmin(args.forGroup)) { return context.models.group.unmakeSpeaker(args.forGroup, args.uid); } throw new AuthenticationError("Not an admin"); }, // @rights admin editGroup: async function (root, args, context: Context): Promise<Group> { if (context.models.auth.isAdmin(args.forGroup)) { return context.models.group.editGroup(args); } throw new AuthenticationError("Not an admin"); }, // @rights admin addVisibilityEdge: async function (root, args, context: Context): Promise<boolean> { if (context.models.auth.isAdmin(args.forGroup)) { return context.models.group.addVisibilityEdge(args.forGroup, args.visibleBy); } throw new AuthenticationError("Not an admin"); }, // @rights admin removeVisibilityEdge: async function (root, args, context: Context): Promise<boolean> { if (context.models.auth.isAdmin(args.forGroup)) { return context.models.group.removeVisibilityEdge(args.forGroup, args.visibleBy); } throw new AuthenticationError("Not an admin"); }, // @rights admin du groupe émetteur groupRequestJoinMetagroup: async function (root, args, context: Context): Promise<GroupJoinMetagroup> { if (context.models.auth.isAdmin(args.fromGroup)) { return context.models.request.groupRequestJoinMetagroup(args.fromGroup, args.toMetagroup, args.comment); } throw new AuthenticationError("Not an admin"); }, // @rights admin du groupe destinataire acceptUserJoinRequest: async function (root, args, context: Context): Promise<boolean> { let req = await UserJoinGroup.tryCreate(args.request); throw "Not implemented"; //TODO : Vérifier les autorisations //if (context.models.auth.isAdmin(req.to)) { return context.models.request.acceptUserJoinRequest(req, args.comment); throw new AuthenticationError("Not an admin"); }, // @rights admin du groupe destinataire acceptGroupJoinRequest: async function (root, args, context: Context): Promise<boolean> { let req = await GroupJoinMetagroup.tryCreate(args.request); throw "Not implemented"; //TODO : Vérifier les autorisations //if (context.models.auth.isAdmin(req.to)) { return context.models.request.acceptGroupJoinRequest(req, args.comment); throw new AuthenticationError("Not an admin"); }, // @rights admin du groupe destinataire refuseUserJoinRequest: async function (root, args, context: Context): Promise<boolean> { let req = await UserJoinGroup.tryCreate(args.request); throw "Not implemented"; //TODO : Vérifier les autorisations //if (context.models.auth.isAdmin(req.to)) { return context.models.request.refuseUserJoinRequest(req, args.comment); throw new AuthenticationError("Not an admin"); }, // @rights admin du groupe destinataire refuseGroupJoinRequest: async function (root, args, context: Context): Promise<boolean> { let req = await GroupJoinMetagroup.tryCreate(args.request); throw "Not implemented"; //TODO : Vérifier les autorisations //if (context.models.auth.isAdmin(req.to)) { return context.models.request.refuseGroupJoinRequest(req, args.comment); throw new AuthenticationError("Not an admin"); }, // @rights admin removeUser: async function (root, args, context: Context): Promise<User> { if(context.models.auth.isAdmin(args.fromGroup)) { return context.models.group.removeUser(args.fromGroup, args.uid); } throw new AuthenticationError("Not an admin"); }, // @rights admin du groupe destinataire acceptGroupCoauthorEventRequest: async function (root, args, context: Context): Promise<boolean> { // Pour l'instant, ce n'est pas a implémenter... let req = await GroupCoauthorEvent.tryCreate(args.request); throw "Not implemented"; //TODO : Vérifier les autorisations //if (context.models.auth.isAdmin(req.to)) { return context.models.request.acceptGroupCoauthorEventRequest(req, args.comment); throw new AuthenticationError("Not an admin"); }, // @rights admin du groupe destinataire refuseGroupCoauthorEventRequest: async function (root, args, context: Context): Promise<boolean> { // Pour l'instant, ce n'est pas a implémenter... let req = await GroupCoauthorEvent.tryCreate(args.request); throw "Not implemented"; //TODO : Vérifier les autorisations //if (context.models.auth.isAdmin(req.to)) { return context.models.request.refuseGroupCoauthorEventRequest(req, args.comment); throw new AuthenticationError("Not an admin"); }, // @rights admin du groupe censorQuestion: async function (root, args, context: Context): Promise<boolean> { throw "Not implemented"; //TODO : Vérifier les autorisations return context.models.message.censorQuestion(args.questionToCensor); throw new AuthenticationError("Not an admin"); }, // @rights admin du groupe censorAnswer: async function (root, args, context: Context): Promise<boolean> { throw "Not implemented"; //TODO : Vérifier les autorisations return context.models.message.censorAnswer(args.answerToCensor); throw new AuthenticationError("Not an admin"); }, // @rights admin du groupe censorPrivatePost: async function (root, args, context: Context): Promise<boolean> { throw "Not implemented"; //TODO : Vérifier les autorisations return context.models.message.censorPrivatePost(args.privatePostToCensor); throw new AuthenticationError("Not an admin"); }, // @rights admin d'un groupe author censorAnnouncement: async function (root, args, context: Context): Promise<boolean> { throw "Not implemented"; //TODO : Vérifier les autorisations return context.models.message.censorAnnouncement(args.announcementToCensor); throw new AuthenticationError("Not an admin"); }, // @rights admin d'un groupe author censorEvent: async function (root, args, context: Context): Promise<boolean> { throw "Not implemented"; //TODO : Vérifier les autorisations return context.models.message.censorEvent(args.eventToCensor); throw new AuthenticationError("Not an admin"); }, // Supervisor mutations // @rights supervisor takeAdminRights : async function(root, args, context: Context): Promise<boolean> { if (context.models.auth.isSupervisor(args.forGroup)) { return context.models.group.takeAdminRights(args.forGroup, context.user.uid); } throw new AuthenticationError("Not a supervisor"); }, // @rights supervisor releaseAdminRights: async function (root, args, context: Context): Promise<boolean> { if (context.models.auth.isSupervisor(args.forGroup)) { return context.models.group.releaseAdminRights(args.forGroup, context.user.uid); } throw new AuthenticationError("Not a supervisor"); } } };