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) {
school: 'polytechnique',
parentuid: 'kes',
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',
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';
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.
* @function
......@@ -37,6 +82,7 @@ const quickPromise = (val) => {
* @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
* @rights super
*/
export const getGroupType = (user, groupUID) => {
return knex('simple_groups').select('uid').where('uid', groupUID).then( sg_res => {
......@@ -60,6 +106,7 @@ export const getGroupType = (user, groupUID) => {
* @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
* rights member(groupUID)
*/
export const getUsersWithAdminRights = (user, groupUID) => {
return getGroupType(user, groupUID).then( groupType => {
......@@ -98,6 +145,7 @@ export const getUsersWithAdminRights = (user, groupUID) => {
* @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.
* rights user
*/
export const hasAdminRights = (user, groupUID) => {
if(user.uid == "anatole.romon")
......@@ -114,6 +162,7 @@ export const hasAdminRights = (user, groupUID) => {
* @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.
* @rights user
*/
export const getVisibleGroupCallback = (user) => {
return listerGroupes(user, user.uid).then(group_ids => {
......@@ -166,6 +215,7 @@ function getGroupTableName(wantedType){
* @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.
* @rights user
*/
export async function getGroupIfVisible(user, groupUID, type="all"){
let group_table_name = getGroupTableName(type);
......@@ -199,6 +249,7 @@ export async function getAllVisibleMetaGroups (user){
* @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 getAllVisibleGroups(user){
let all_simple_groups = await getAllVisibleSimpleGroups(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} 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.
* @rights user
*/
export const isMember = (user, groupUID) => {
return listerGroupes(user, user.uid).then(group_ids => group_ids && group_ids.indexOf(groupUID) != -1);
......@@ -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
* @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
* @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){
let rasUID = initialUID.replace(' ', '_').replace(/\W/g, '').toLowerCase();
......@@ -243,6 +298,7 @@ export function getAvailablegroupUID(initialUID){
* @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.
* @rights admin (args.parentuid)
*/
export async function createSubgroup(user, args){
if (typeof args.parentuid != 'string')
......@@ -276,6 +332,7 @@ export async function createSubgroup(user, args){
* @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.
* @rights user
*/
export async function createGroupIfLegal(user, args){
if( await hasAdminRights(user, args.parentuid) ){
......@@ -292,6 +349,7 @@ export async function createGroupIfLegal(user, args){
* @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.
* @rights admin(recipientUID)
*/
export function getUserJoinGroupRequests(user, recipientUID){
return knex.select('id', 'useruid', 'message').from('user_join_group')
......@@ -307,6 +365,7 @@ export function getUserJoinGroupRequests(user, recipientUID){
* @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.
* @rights speaker(recipientUID)
*/
export function getGroupJoinEventRequests(user, recipientUID){
return knex.select('id', 'senderuid', 'eventuid', 'message').from('group_join_event')
......@@ -323,6 +382,7 @@ export function getGroupJoinEventRequests(user, recipientUID){
* @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.
* @rights speaker(recipientUID)
*/
export const getYourGroupHostEventRequests = (user, recipientUID) => {
return knex.select('id', 'senderuid', 'eventuid', 'message').from('your_group_host_event')
......@@ -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 {String} groupUID - Identifiant unique du groupe.
* @author manifold
* @rights super
*/
export const getGroup = (user, groupUID) => {
// Une sélection sur une table renvoie un tableau.
......@@ -374,14 +435,47 @@ export const getGroup = (user, groupUID) => {
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) => {
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) => {
return knex.select().from('meta_groups').where('uid',groupUID).then(results => results [0]);
};
export const getMetaGroupAdminMembers = (user, metaGroupUID) => {
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';
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";
});
asAdmin: async function (obj, args, context){
if(await connectors.hasAdminRights(context.user, args.groupUID))
return {groupUID : args.groupUID};
else
throw "You do not have admin rights over this group";
},
asMember: function (obj, args, context){
return {groupUID : args.groupUID};
},
accessGroups: (obj, args, context) => {
......@@ -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: {
userJoinGroup : (obj, args, context) => {
return connectors.getUserJoinGroupRequests(context.user, obj.groupUID);
......
......@@ -7,7 +7,7 @@ const RootTypes = `
asAdmin(groupUID: ID): AdminQuery
asSpeaker(groupUID: ID): AdminQuery
asMember(groupUID: ID): AdminQuery
asMember(groupUID: ID): MemberQuery
asViewer(groupUID: ID): AdminQuery
}
......@@ -124,6 +124,7 @@ const subQueries = `
type MemberQuery{
isMember: Boolean
allMembers : [Group]
}
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