|
|
@@ -2,10 +2,16 @@ import RoleUtils from '~/services/rights/roleUtils'
|
|
|
import {AbilitiesType} from '~/types/interfaces'
|
|
|
import YamlDenormalizer from "~/services/data/serializer/denormalizer/yamlDenormalizer";
|
|
|
import {MongoAbility} from "@casl/ability/dist/types/Ability";
|
|
|
-import {AnyJson} from "~/types/data";
|
|
|
import {useEach} from "#imports";
|
|
|
import {ABILITIES} from "~/types/enum/enums";
|
|
|
|
|
|
+interface Condition {
|
|
|
+ function: string
|
|
|
+ parameters?: Array<any>
|
|
|
+ expectedResult?: any
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
/**
|
|
|
* Classe permettant de mener des opérations sur les habilités
|
|
|
*/
|
|
|
@@ -33,7 +39,7 @@ class AbilityUtils {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Définit les abilities de l'utilisateur selon son profil
|
|
|
+ * Construit les habilités de l'utilisateur selon son profil et met à jour MongoAbility en fonction
|
|
|
*/
|
|
|
setupAbilities() {
|
|
|
// Nécessaire pour que l'update des habilités soit correcte après la phase SSR
|
|
|
@@ -55,6 +61,8 @@ class AbilityUtils {
|
|
|
|
|
|
//On les store puis on update le service ability pour le mettre à jour.
|
|
|
this.accessProfile.abilities = abilities
|
|
|
+ console.log(abilities)
|
|
|
+
|
|
|
this.ability.update(abilities)
|
|
|
|
|
|
// Unsubscribe pour éviter les memory leaks
|
|
|
@@ -65,7 +73,8 @@ class AbilityUtils {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Récupération de l'ensemble des habilités de l'utilisateur, qu'elles soient par Roles ou par Config
|
|
|
+ * Construit et renvoie l'ensemble des habilités de l'utilisateur, qu'elles soient issues de ses roles
|
|
|
+ * ou de la configuration
|
|
|
*
|
|
|
* @return {Array<AbilitiesType>}
|
|
|
*/
|
|
|
@@ -94,10 +103,15 @@ class AbilityUtils {
|
|
|
const doc = YamlDenormalizer.denormalize({path: this.configDir})
|
|
|
const fromConfig = doc.abilities
|
|
|
|
|
|
- useEach(fromConfig, (ability: { action: ABILITIES, services: object }, subject: string) => {
|
|
|
- const { action, services } = ability
|
|
|
+ useEach(fromConfig, (ability: { action: ABILITIES, conditions: Array<Condition> }, subject: string) => {
|
|
|
+ let { action, conditions } = ability
|
|
|
|
|
|
- if (this.hasConfigAbility(services)) {
|
|
|
+ if (!Array.isArray(conditions)) {
|
|
|
+ // Special: la denormalization ne produit pas une array s'il n'y a qu'un seul élément
|
|
|
+ conditions = [conditions]
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this.hasConfigAbility(conditions as Array<Condition>, subject)) {
|
|
|
this.abilitiesByConfig.push({ action, subject })
|
|
|
}
|
|
|
})
|
|
|
@@ -106,56 +120,74 @@ class AbilityUtils {
|
|
|
/**
|
|
|
* Parcourt les services définis dans la configuration, et établit si oui ou non l'habilité est autorisée
|
|
|
*
|
|
|
- * TODO: voir pourquoi on a besoin d'accepter un param null pour le hasProfile?
|
|
|
- *
|
|
|
* @return {boolean}
|
|
|
- * @param services
|
|
|
+ * @param conditions Les conditions à l'obtention de l'habileté, telles que définies dans les fichiers de config
|
|
|
+ * @param subject For debugging purpose only
|
|
|
*/
|
|
|
- hasConfigAbility(services: AnyJson) {
|
|
|
- for (const service in services) {
|
|
|
- let handlers = services[service] as Array<{ function: string, parameters?: Array<any>, result?: any }>
|
|
|
-
|
|
|
- if (handlers.some((handler) => !this.testConfigService(handler))) {
|
|
|
- return false
|
|
|
- }
|
|
|
- }
|
|
|
- return true
|
|
|
+ hasConfigAbility(conditions: Array<Condition>, subject: string = '') {
|
|
|
+ return conditions.every((condition) => this.execAndValidateCondition(condition, subject))
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Correspondances entre les noms des fonctions définies dans les conditions des fichiers de configuration et
|
|
|
+ * les méthodes correspondantes
|
|
|
+ *
|
|
|
+ * TODO: voir pourquoi on a besoin d'accepter un param null pour le hasProfile?
|
|
|
+ */
|
|
|
handlerMap: any = {
|
|
|
- hasAllRoleAbilities: (parameters: any) => this.hasAllRoleAbilities(parameters),
|
|
|
- hasAnyProfile: (parameters: any) => parameters === null || this.hasAnyProfile(parameters),
|
|
|
- hasAllModules: (parameters: any) => this.hasAllModules(parameters),
|
|
|
- isAdminAccount: (parameters: any) => this.accessProfile.isAdminAccount,
|
|
|
- isSchool: (parameters: any) => this.organizationProfile.isSchool,
|
|
|
- isArtist: (parameters: any) => this.organizationProfile.isArtist,
|
|
|
- isManagerProduct: (parameters: any) => this.organizationProfile.isManagerProduct,
|
|
|
- isOrganizationWithChildren: (parameters: any) => this.organizationProfile.hasChildren,
|
|
|
- isAssociation: (parameters: any) => this.organizationProfile.isAssociation,
|
|
|
- isShowAdherentList: (parameters: any) => this.organizationProfile.isShowAdherentList,
|
|
|
- isCmf: (parameters: any) => this.organizationProfile.isCmf,
|
|
|
- getWebsite: (parameters: any) => this.organizationProfile.getWebsite,
|
|
|
+ accessHasAllRoleAbilities: (parameters: any) => this.hasAllRoleAbilities(parameters),
|
|
|
+ accessHasAnyRoleAbility: (parameters: any) => this.hasAnyRoleAbility(parameters),
|
|
|
+ accessHasAnyProfile: (parameters: any) => parameters === null || this.hasAnyProfile(parameters),
|
|
|
+ accessHasAllModules: (parameters: any) => this.hasAllModules(parameters),
|
|
|
+ organizationHasAnyModule: (parameters: any) => this.hasAnyModule(parameters),
|
|
|
+ accessIsAdminAccount: (parameters: any) => this.accessProfile.isAdminAccount,
|
|
|
+ organizationIsSchool: (parameters: any) => this.organizationProfile.isSchool,
|
|
|
+ organizationIsArtist: (parameters: any) => this.organizationProfile.isArtist,
|
|
|
+ organizationIsManagerProduct: (parameters: any) => this.organizationProfile.isManagerProduct,
|
|
|
+ organizationHasChildren: (parameters: any) => this.organizationProfile.hasChildren,
|
|
|
+ organizationIsAssociation: (parameters: any) => this.organizationProfile.isAssociation,
|
|
|
+ organizationIsShowAdherentList: (parameters: any) => this.organizationProfile.isShowAdherentList,
|
|
|
+ organizationIsCmf: (parameters: any) => this.organizationProfile.isCmf,
|
|
|
+ organizationHasWebsite: (parameters: any) => this.organizationProfile.getWebsite,
|
|
|
}
|
|
|
|
|
|
- private testConfigService(handler: { function: string, parameters?: Array<any>, result?: any }) {
|
|
|
- const expectedResult: boolean = handler.result ?? true;
|
|
|
- const parameters = handler.parameters ?? []
|
|
|
+ /**
|
|
|
+ * Exécute la fonction associée à la condition, et compare le résultat obtenu au résultat attendu (true par défaut)
|
|
|
+ *
|
|
|
+ * @param condition Un condition à la possession d'une habilité, telle que définie dans les fichiers de config
|
|
|
+ * @param subject For debugging purpose only
|
|
|
+ * @private
|
|
|
+ */
|
|
|
+ private execAndValidateCondition(
|
|
|
+ condition: Condition,
|
|
|
+ subject: string = ''
|
|
|
+ ) {
|
|
|
+ const expectedResult: boolean = condition.expectedResult ?? true;
|
|
|
+ const parameters = condition.parameters ?? []
|
|
|
|
|
|
- const actualResult = this.handlerMap[handler.function](parameters ?? null)
|
|
|
+ if (!(condition.function in this.handlerMap)) {
|
|
|
+ throw new Error('unknown condition function : ' + condition.function)
|
|
|
+ }
|
|
|
+ const actualResult = this.handlerMap[condition.function](parameters ?? null)
|
|
|
|
|
|
return actualResult === expectedResult
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Est-ce que l'utilisateur possède l'habilité passée en paramètre
|
|
|
+ * Est-ce que l'utilisateur possède l'habilité en paramètre
|
|
|
*
|
|
|
* @return {boolean}
|
|
|
* @param ability
|
|
|
*/
|
|
|
- hasRoleAbility(ability: AbilitiesType) {
|
|
|
- return this.abilitiesByRoles.some(
|
|
|
- (candidate) => candidate.action === ability.action && candidate.subject === ability.subject
|
|
|
- )
|
|
|
+ hasRoleAbility(ability: AbilitiesType): boolean {
|
|
|
+ // console.log(ability, this.abilitiesByRoles.some((candidate: AbilitiesType) => {
|
|
|
+ // return candidate.subject === ability.subject && (candidate.action === ability.action || candidate.action === 'manage')
|
|
|
+ // }))
|
|
|
+
|
|
|
+ return this.abilitiesByRoles.some((candidate: AbilitiesType) => {
|
|
|
+ return candidate.subject === ability.subject &&
|
|
|
+ (candidate.action === ability.action || candidate.action === 'manage') // 'manage' permet aussi l'action 'read'
|
|
|
+ })
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -165,9 +197,17 @@ class AbilityUtils {
|
|
|
* @return {boolean}
|
|
|
*/
|
|
|
hasAllRoleAbilities(abilities: Array<AbilitiesType>): boolean {
|
|
|
- return abilities.every(
|
|
|
- ability => this.hasRoleAbility(ability)
|
|
|
- )
|
|
|
+ return abilities.every(ability => this.hasRoleAbility(ability))
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Est-ce que l'utilisateur possède au moins l'une des habilités passées en paramètre
|
|
|
+ *
|
|
|
+ * @param {Array<AbilitiesType>} abilities Habilités à tester
|
|
|
+ * @return {boolean}
|
|
|
+ */
|
|
|
+ hasAnyRoleAbility(abilities: Array<AbilitiesType>): boolean {
|
|
|
+ return abilities.some(ability => this.hasRoleAbility(ability))
|
|
|
}
|
|
|
|
|
|
/**
|