Skip to content
Snippets Groups Projects
Commit d291a0e0 authored by Anatole ROMON's avatar Anatole ROMON
Browse files

docu + travail sur les meta-groupes

parent b37770f4
No related branches found
No related tags found
No related merge requests found
exports.up = function(knex, Promise) {
return knex.schema.createTable('meta_group_membership', function (table){
table.timestamp(true, true);
table.string('member_uid').notNullable();
table.string('union_uid').notNullable();
table.enum('status', ['admin', 'speaker', 'basic']).notNullable();
});
};
exports.down = function(knex, Promise) {
return knex.schema.dropTable('meta_group_membership');
};
...@@ -21,6 +21,14 @@ exports.seed = function(knex, Promise) { ...@@ -21,6 +21,14 @@ exports.seed = function(knex, Promise) {
school: 'polytechnique', school: 'polytechnique',
parentuid: 'kes', parentuid: 'kes',
type : 'simple' type : 'simple'
},{
name: 'Bôbar',
uid: 'bob',
description : "Viens. On est bien",
website: 'bôbar.binet.fr',
school: 'polytechnique',
parentuid: 'kes',
type : 'simple'
},{ },{
name: 'Kès', name: 'Kès',
uid: 'kes', uid: 'kes',
......
exports.seed = async function(knex, Promise) {
// Deletes ALL existing entries
await knex('meta_groups').del();
await knex('meta_group_membership').insert([
{
member_uid : "br",
union_uid : "federez",
status : "admin"
},
{
member_uid : "data",
union_uid : "federez",
status : "admin"
},
{
member_uid : "bob",
union_uid : "bsckbl",
status : "admin"
}
]);
return;
};
...@@ -10,6 +10,51 @@ import { exportAllDeclaration } from 'babel-types'; ...@@ -10,6 +10,51 @@ import { exportAllDeclaration } from 'babel-types';
export { renseignerSurUtilisateur, repliquerTOLdesIds, listerMembres }; 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.
*/
/** /**
* @summary Génère une promise. * @summary Génère une promise.
* @function * @function
...@@ -37,6 +82,7 @@ const quickPromise = (val) => { ...@@ -37,6 +82,7 @@ const quickPromise = (val) => {
* @arg {Object} groupUID - L'id du groupe dont on veut connaître le type. * @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. * @return {Promise(String)} Un string représentant le type du groupe.
* Peut être "SimpleGroup" ou "MetaGroup". Renvoie `Undefined` si le groupe n'existe pas * Peut être "SimpleGroup" ou "MetaGroup". Renvoie `Undefined` si le groupe n'existe pas
* @rights super
*/ */
export const getGroupType = (user, groupUID) => { export const getGroupType = (user, groupUID) => {
return knex('simple_groups').select('uid').where('uid', groupUID).then( sg_res => { return knex('simple_groups').select('uid').where('uid', groupUID).then( sg_res => {
...@@ -60,6 +106,7 @@ export const getGroupType = (user, groupUID) => { ...@@ -60,6 +106,7 @@ export const getGroupType = (user, groupUID) => {
* @arg {String} uid - L'uid du groupe dont on veut les administrateurs. * @arg {String} uid - L'uid du groupe dont on veut les administrateurs.
* @return {Promise} Retour de requête knex. Promise qui renvera une liste * @return {Promise} Retour de requête knex. Promise qui renvera une liste
* de tous les utilisateurs ayant droit d'admin sur le groupe * de tous les utilisateurs ayant droit d'admin sur le groupe
* rights member(groupUID)
*/ */
export const getUsersWithAdminRights = (user, groupUID) => { export const getUsersWithAdminRights = (user, groupUID) => {
return getGroupType(user, groupUID).then( groupType => { return getGroupType(user, groupUID).then( groupType => {
...@@ -98,6 +145,7 @@ export const getUsersWithAdminRights = (user, groupUID) => { ...@@ -98,6 +145,7 @@ export const getUsersWithAdminRights = (user, groupUID) => {
* @arg {Object} user - Objet contenant un attribut `uid` de type `string`. * @arg {Object} user - Objet contenant un attribut `uid` de type `string`.
* User représente l'utilisateur qui a effectué la requête. * 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. * @return {Promise} Retour de requête knex. Liste de tous les groupes que l'utilisateur a le droit de voire.
* rights user
*/ */
export const hasAdminRights = (user, groupUID) => { export const hasAdminRights = (user, groupUID) => {
if(user.uid == "anatole.romon") if(user.uid == "anatole.romon")
...@@ -114,6 +162,7 @@ export const hasAdminRights = (user, groupUID) => { ...@@ -114,6 +162,7 @@ export const hasAdminRights = (user, groupUID) => {
* @arg {Object} user - Objet contenant un attribut `uid` de type `string`. * @arg {Object} user - Objet contenant un attribut `uid` de type `string`.
* User représente l'utilisateur qui a effectué la requête. * 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. * @return {Promise(Callback)} callback contruisant une requête knex pour une table de tous les id visibles.
* @rights user
*/ */
export const getVisibleGroupCallback = (user) => { export const getVisibleGroupCallback = (user) => {
return listerGroupes(user, user.uid).then(group_ids => { return listerGroupes(user, user.uid).then(group_ids => {
...@@ -166,6 +215,7 @@ function getGroupTableName(wantedType){ ...@@ -166,6 +215,7 @@ function getGroupTableName(wantedType){
* @arg {String} uid - Identifiant du groupe voulu. * @arg {String} uid - Identifiant du groupe voulu.
* @arg {String} type - Type de groupe voulu. `"simple"`, `"meta"` ou `"all"`. * @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. * @return {Promise(group)} Retour de requête knex. Le groupe demandé, si l'utilisateur a le droit de la voire.
* @rights user
*/ */
export async function getGroupIfVisible(user, groupUID, type="all"){ export async function getGroupIfVisible(user, groupUID, type="all"){
let group_table_name = getGroupTableName(type); let group_table_name = getGroupTableName(type);
...@@ -199,6 +249,7 @@ export async function getAllVisibleMetaGroups (user){ ...@@ -199,6 +249,7 @@ export async function getAllVisibleMetaGroups (user){
* @arg {Object} user - Représente l'utilisateur qui a effectué la requête. * @arg {Object} user - Représente l'utilisateur qui a effectué la requête.
* @arg {String} wantedType - Type de groupe voulu : `"simple"`, `"meta"` ou `"all"`. * @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. * @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 getAllVisibleGroups(user){ export async function getAllVisibleGroups(user){
let all_simple_groups = await getAllVisibleSimpleGroups(user); let all_simple_groups = await getAllVisibleSimpleGroups(user);
...@@ -211,6 +262,7 @@ export async function getAllVisibleGroups(user){ ...@@ -211,6 +262,7 @@ export async function getAllVisibleGroups(user){
* @arg {Object} user - Représente l'utilisateur qui a effectué la requête. * @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. * @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. * @return {Promise(Boolean)} Boolean indiquant si l'utilisateur est membre du groupe.
* @rights user
*/ */
export const isMember = (user, groupUID) => { export const isMember = (user, groupUID) => {
return listerGroupes(user, user.uid).then(group_ids => group_ids && group_ids.indexOf(groupUID) != -1); return listerGroupes(user, user.uid).then(group_ids => group_ids && group_ids.indexOf(groupUID) != -1);
...@@ -221,6 +273,9 @@ export const isMember = (user, groupUID) => { ...@@ -221,6 +273,9 @@ export const isMember = (user, groupUID) => {
* @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 * @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. * @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 * @return {Promise} Retour de requête knex. Promise qui renvera une liste de tous les utilisateurs ayant droit d'admin sur le groupe
* @rights user
* remarque : n'importe qui peut tester si un groupe existe en demandant a créer un groupe avec ce nom la et en regardant si
* son UID a été modifié. Je ne vois pas comment contourner ce problème, c'est donc une faille permanente de sigma.
*/ */
export function getAvailablegroupUID(initialUID){ export function getAvailablegroupUID(initialUID){
let rasUID = initialUID.replace(' ', '_').replace(/\W/g, '').toLowerCase(); let rasUID = initialUID.replace(' ', '_').replace(/\W/g, '').toLowerCase();
...@@ -243,6 +298,7 @@ export function getAvailablegroupUID(initialUID){ ...@@ -243,6 +298,7 @@ export function getAvailablegroupUID(initialUID){
* @arg {Object} user - L'utilisateur qui effectue la requête. * @arg {Object} user - L'utilisateur qui effectue la requête.
* @arg {Object} args - Les arguments envoyés à la mutation. Cf le schéma GraphQL * @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. * @return {Promise} Retour de requête knex. Le groupe qui vient d'être créé. En cas d'echec, renvoie une erreur.
* @rights admin (args.parentuid)
*/ */
export async function createSubgroup(user, args){ export async function createSubgroup(user, args){
if (typeof args.parentuid != 'string') if (typeof args.parentuid != 'string')
...@@ -276,6 +332,7 @@ export async function createSubgroup(user, args){ ...@@ -276,6 +332,7 @@ export async function createSubgroup(user, args){
* @arg {Object} user - L'utilisateur qui effectue la requête. * @arg {Object} user - L'utilisateur qui effectue la requête.
* @arg {Object} args - Les arguments envoyés à la mutation. Cf le schéma GraphQL * @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. * @return {Promise} Retour de requête knex. Le groupe qui vient d'être créé. En cas d'echec, renvoie une erreur.
* @rights user
*/ */
export async function createGroupIfLegal(user, args){ export async function createGroupIfLegal(user, args){
if( await hasAdminRights(user, args.parentuid) ){ if( await hasAdminRights(user, args.parentuid) ){
...@@ -292,6 +349,7 @@ export async function createGroupIfLegal(user, args){ ...@@ -292,6 +349,7 @@ export async function createGroupIfLegal(user, args){
* @arg {Object} user - L'utilisateur qui effectue la requête. * @arg {Object} user - L'utilisateur qui effectue la requête.
* @arg {String} args - L'identifiant du groupe qui reçoit 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. * @return {Promise(Object)} Retour de requête knex. Toutes les requêtes destinées au groupe.
* @rights admin(recipientUID)
*/ */
export function getUserJoinGroupRequests(user, recipientUID){ export function getUserJoinGroupRequests(user, recipientUID){
return knex.select('id', 'useruid', 'message').from('user_join_group') return knex.select('id', 'useruid', 'message').from('user_join_group')
...@@ -307,6 +365,7 @@ export function getUserJoinGroupRequests(user, recipientUID){ ...@@ -307,6 +365,7 @@ export function getUserJoinGroupRequests(user, recipientUID){
* @arg {Object} user - L'utilisateur qui effectue la requête. * @arg {Object} user - L'utilisateur qui effectue la requête.
* @arg {String} args - L'identifiant du groupe qui reçoit 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. * @return {Promise(Object)} Retour de requête knex. Toutes les requêtes destinées au groupe.
* @rights speaker(recipientUID)
*/ */
export function getGroupJoinEventRequests(user, recipientUID){ export function getGroupJoinEventRequests(user, recipientUID){
return knex.select('id', 'senderuid', 'eventuid', 'message').from('group_join_event') return knex.select('id', 'senderuid', 'eventuid', 'message').from('group_join_event')
...@@ -323,6 +382,7 @@ export function getGroupJoinEventRequests(user, recipientUID){ ...@@ -323,6 +382,7 @@ export function getGroupJoinEventRequests(user, recipientUID){
* @arg {Object} user - L'utilisateur qui effectue la requête. * @arg {Object} user - L'utilisateur qui effectue la requête.
* @arg {String} args - L'identifiant du groupe qui reçoit 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. * @return {Promise(Object)} Retour de requête knex. Toutes les requêtes destinées au groupe.
* @rights speaker(recipientUID)
*/ */
export const getYourGroupHostEventRequests = (user, recipientUID) => { export const getYourGroupHostEventRequests = (user, recipientUID) => {
return knex.select('id', 'senderuid', 'eventuid', 'message').from('your_group_host_event') return knex.select('id', 'senderuid', 'eventuid', 'message').from('your_group_host_event')
...@@ -362,10 +422,11 @@ export const getEvent = (user, eventID) => { ...@@ -362,10 +422,11 @@ export const getEvent = (user, eventID) => {
}; };
/** /**
* @function Renvoie simplement un groupe en fonction de son identifiant. * @summary Renvoie simplement un groupe en fonction de son identifiant.
* @param {Object} user - Utilisateur effectuant la requête. * @param {Object} user - Utilisateur effectuant la requête.
* @param {String} groupUID - Identifiant unique du groupe. * @param {String} groupUID - Identifiant unique du groupe.
* @author manifold * @author manifold
* @rights super
*/ */
export const getGroup = (user, groupUID) => { export const getGroup = (user, groupUID) => {
// Une sélection sur une table renvoie un tableau. // Une sélection sur une table renvoie un tableau.
...@@ -374,14 +435,47 @@ export const getGroup = (user, groupUID) => { ...@@ -374,14 +435,47 @@ export const getGroup = (user, groupUID) => {
return knex.select().from('groups').where('uid',groupUID).then(results => results [0]); return knex.select().from('groups').where('uid',groupUID).then(results => results [0]);
}; };
/**
* @summary Renvoie simplement un groupe simple en fonction de son identifiant.
* @param {Object} user - Utilisateur effectuant la requête.
* @param {String} groupUID - Identifiant unique du groupe.
* @author manifold
* @rights super
*/
export const getSimpleGroup = (user, groupUID) => { export const getSimpleGroup = (user, groupUID) => {
return knex.select().from('simple_groups').where('uid',groupUID).then(results => results [0]); return knex.select().from('simple_groups').where('uid',groupUID).then(results => results [0]);
}; };
/**
* @summary Renvoie simplement un meta groupe en fonction de son identifiant.
* @param {Object} user - Utilisateur effectuant la requête.
* @param {String} groupUID - Identifiant unique du groupe.
* @author manifold
* @rights super
*/
export const getMetaGroup = (user, groupUID) => { export const getMetaGroup = (user, groupUID) => {
return knex.select().from('meta_groups').where('uid',groupUID).then(results => results [0]); return knex.select().from('meta_groups').where('uid',groupUID).then(results => results [0]);
}; };
export const getMetaGroupAdminMembers = (user, metaGroupUID) => { export const getMetaGroupAdminMembers = (user, metaGroupUID) => {
return quickPromise([]); return quickPromise([]);
};
/**
* @function getMetaGroupMembers
* @summary Renvoie tous les membres d'un meta-groupe.
* @param {Object} user - Utilisateur effectuant la requête.
* @param {String} metaGroupUID - Identifiant unique du groupe.
* @return {Promise(callback)} a callback to build a query for the members of a group
* It doesn't need to be a promise, but I figure having all of my functions return promises is
* easier than keeping track of which functions do and do not return promises.
* @author akka vodol
* @rights member(metaGroupUID)
*/
export async function getMetaGroupMembersCallback(user, metaGroupUID){
return function(query_builder){
return query_builder.distinct().select().from('groups')
.innerJoin('meta_group_membership', 'groups.uid', 'meta_group_membership.member_uid')
.where('meta_group_membership.union_uid', '=', metaGroupUID);
};
}; };
\ No newline at end of file
...@@ -16,13 +16,15 @@ import { connect } from 'http2'; ...@@ -16,13 +16,15 @@ import { connect } from 'http2';
export const resolvers = { export const resolvers = {
Query: { Query: {
asAdmin: (obj, args, context) => { asAdmin: async function (obj, args, context){
return connectors.hasAdminRights(context.user, args.groupUID).then(res => { if(await connectors.hasAdminRights(context.user, args.groupUID))
if(res) return {groupUID : args.groupUID};
return {groupUID : args.groupUID}; else
else throw "You do not have admin rights over this group";
throw "You do not have admin rights over this group"; },
});
asMember: function (obj, args, context){
return {groupUID : args.groupUID};
}, },
accessGroups: (obj, args, context) => { accessGroups: (obj, args, context) => {
...@@ -104,6 +106,17 @@ export const resolvers = { ...@@ -104,6 +106,17 @@ export const resolvers = {
} }
}, },
MemberQuery: {
isMember: (obj, args, context) => {
return true;
},
allMembers: async function (obj, args, context){
let cb = await connectors.getMetaGroupMembersCallback(context.user, obj.groupUID);
return cb(knex);
}
},
AllRequests: { AllRequests: {
userJoinGroup : (obj, args, context) => { userJoinGroup : (obj, args, context) => {
return connectors.getUserJoinGroupRequests(context.user, obj.groupUID); return connectors.getUserJoinGroupRequests(context.user, obj.groupUID);
......
...@@ -7,7 +7,7 @@ const RootTypes = ` ...@@ -7,7 +7,7 @@ const RootTypes = `
asAdmin(groupUID: ID): AdminQuery asAdmin(groupUID: ID): AdminQuery
asSpeaker(groupUID: ID): AdminQuery asSpeaker(groupUID: ID): AdminQuery
asMember(groupUID: ID): AdminQuery asMember(groupUID: ID): MemberQuery
asViewer(groupUID: ID): AdminQuery asViewer(groupUID: ID): AdminQuery
} }
...@@ -124,6 +124,7 @@ const subQueries = ` ...@@ -124,6 +124,7 @@ const subQueries = `
type MemberQuery{ type MemberQuery{
isMember: Boolean isMember: Boolean
allMembers : [Group]
} }
type ViewerQuery{ type ViewerQuery{
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment