| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323 |
- import * as _ from 'lodash-es'
- import type { MongoAbility } from '@casl/ability/dist/types/Ability'
- import RoleUtils from '~/services/rights/roleUtils'
- import type { AbilitiesType, AccessProfile } from '~/types/interfaces'
- import type { ABILITIES } from '~/types/enum/enums'
- import type OrganizationProfile from '~/models/Organization/OrganizationProfile'
- import abilitiesConfig from '~/config/abilities/config-precompiled'
- interface ConditionParameters {
- action: string
- subject: string
- }
- interface Condition {
- function: string
- parameters?: Array<string | ConditionParameters>
- expectedResult?: boolean
- }
- /**
- * Classe permettant de mener des opérations sur les habilités
- */
- class AbilityBuilder {
- private readonly ability: MongoAbility = {} as MongoAbility
- private readonly accessProfile: AccessProfile
- private readonly organizationProfile: OrganizationProfile
- private abilities: Array<AbilitiesType> = []
- /**
- * @constructor
- */
- constructor(
- ability: MongoAbility,
- accessProfile: AccessProfile,
- organizationProfile: OrganizationProfile,
- ) {
- this.ability = ability
- this.accessProfile = accessProfile
- this.organizationProfile = organizationProfile
- }
- /**
- * 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>}
- */
- buildAbilities(): Array<AbilitiesType> {
- // Build from roles
- this.abilities = this.buildAbilitiesFromRoles()
- this.ability.update(this.abilities)
- // Build from config
- this.abilities = this.abilities.concat(this.buildAbilitiesFromConfig())
- this.ability.update(this.abilities)
- return this.abilities
- }
- /**
- * Adaptation et transformations des roles symfony en abilities Casl
- */
- buildAbilitiesFromRoles() {
- return RoleUtils.rolesToAbilities(this.accessProfile.roles)
- }
- /**
- * Charge les habilités depuis les fichiers de configuration
- */
- buildAbilitiesFromConfig() {
- const abilitiesByConfig: Array<AbilitiesType> = []
- _.each(
- abilitiesConfig,
- (
- ability: { action: ABILITIES; conditions: Array<Condition> },
- subject: string,
- ) => {
- // eslint-disable-next-line prefer-const
- let { action, conditions } = ability
- if (!Array.isArray(conditions)) {
- // Special: la denormalization ne produit pas une array s'il n'y a qu'un seul élément
- conditions = [conditions]
- }
- const hasAbility = this.hasConfigAbility(
- conditions as Array<Condition>,
- subject,
- )
- abilitiesByConfig.push({ action, subject, inverted: !hasAbility })
- },
- )
- return abilitiesByConfig
- }
- /**
- * Parcourt les services définis dans la configuration, et établit si oui ou non l'habilité est autorisée
- *
- * @return {boolean}
- * @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(conditions: Array<Condition>, subject: string = '') {
- return conditions.every((condition) =>
- this.execAndValidateCondition(condition, subject),
- )
- }
- // noinspection JSUnusedGlobalSymbols
- /**
- * 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: Record<
- string,
- (parameters: Array<ConditionParameters | string>) => boolean
- > = {
- accessHasAllRoleAbilities: (
- parameters: Array<ConditionParameters | string>,
- ) => this.hasAllRoleAbilities(parameters),
- accessHasAnyRoleAbility: (
- parameters: Array<ConditionParameters | string>,
- ) => this.hasAnyRoleAbility(parameters),
- accessHasAnyProfile: (parameters: Array<ConditionParameters | string>) =>
- parameters === null || this.hasAnyProfile(parameters),
- organizationHasAllModules: (
- parameters: Array<ConditionParameters | string>,
- ) => this.hasAllModules(parameters),
- organizationHasAnyModule: (
- parameters: Array<ConditionParameters | string>,
- ) => this.hasAnyModule(parameters),
- accessIsAdminAccount: (_) => this.accessProfile.isAdminAccount,
- organizationIsSchool: (_) => this.organizationProfile.isSchool,
- organizationIsArtist: (_) => this.organizationProfile.isArtist,
- organizationIsManagerProduct: (_) =>
- this.organizationProfile.isManagerProduct,
- organizationHasChildren: (_) => this.organizationProfile.hasChildren,
- organizationIsAssociation: (_) => this.organizationProfile.isAssociation,
- organizationIsShowAdherentList: (_) =>
- this.organizationProfile.isShowAdherentList,
- organizationIsCmf: (_) => this.organizationProfile.isCmf,
- organizationHasWebsite: (_) => this.organizationProfile.getWebsite,
- }
- /**
- * 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
- */
- protected execAndValidateCondition(
- condition: Condition,
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- subject: string = '',
- ) {
- const expectedResult: boolean = condition.expectedResult ?? true
- const parameters = condition.parameters ?? []
- 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é en paramètre
- *
- * @return {boolean}
- * @param ability
- */
- hasRoleAbility(ability: ConditionParameters | string): boolean {
- if (!Object.prototype.hasOwnProperty.call(ability, 'action')) {
- throw new Error(
- 'hasRoleAbility except a ConditionParameters object, not a string',
- )
- }
- ability = ability as ConditionParameters
- return this.ability.can(ability.action, ability.subject)
- }
- /**
- * Est-ce que l'utilisateur possède toutes les habilités passées en paramètre
- *
- * @param {Array<AbilitiesType>} abilities Habilités à tester
- * @return {boolean}
- */
- hasAllRoleAbilities(abilities: Array<ConditionParameters | string>): boolean {
- 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<ConditionParameters | string>): boolean {
- return abilities.some((ability) => this.hasRoleAbility(ability))
- }
- /**
- * Teste si l'utilisateur possède le profil donné
- *
- * @param {string} profile Profil à tester
- * @return {boolean}
- */
- hasProfile(profile: ConditionParameters | string): boolean {
- if (Object.prototype.hasOwnProperty.call(profile, 'subject')) {
- profile = (profile as ConditionParameters).subject
- }
- return (
- {
- admin: this.accessProfile.isAdmin,
- administratifManager: this.accessProfile.isAdministratifManager,
- pedagogicManager: this.accessProfile.isPedagogicManager,
- financialManager: this.accessProfile.isFinancialManager,
- caMember: this.accessProfile.isCaMember,
- student: this.accessProfile.isStudent,
- teacher: this.accessProfile.isTeacher,
- member: this.accessProfile.isMember,
- other: this.accessProfile.isOther,
- guardian: this.accessProfile.isGuardian,
- payor: this.accessProfile.isPayer,
- }[profile as string] ?? false
- )
- }
- /**
- * Retourne vrai si l'utilisateur connecté possède l'un des profils passés en paramètre
- *
- * @param {Array<string>} profiles Profils à tester
- * @return {boolean}
- */
- hasAnyProfile(profiles: Array<ConditionParameters | string>): boolean {
- return profiles.some((p) => this.hasProfile(p))
- }
- /**
- * Retourne vrai si l'utilisateur connecté possède tous les profils passés en paramètre
- *
- * @param {Array<string>} profiles Profils à tester
- * @return {boolean}
- */
- hasAllProfiles(profiles: Array<ConditionParameters | string>): boolean {
- return profiles.every((p) => this.hasProfile(p))
- }
- /**
- * Est-ce que l'utilisateur possède le rôle donné ?
- *
- * @return {boolean}
- * @param role
- */
- hasRole(role: string): boolean {
- return this.accessProfile.hasRole(role)
- }
- /**
- * L'utilisateur possède-t-il au moins l'un des rôles donnés
- *
- * @return {boolean}
- * @param roles
- */
- hasAnyRole(roles: Array<string>): boolean {
- return roles.some((r) => this.hasRole(r))
- }
- /**
- * L'utilisateur possède-t-il tous les rôles donnés
- *
- * @return {boolean}
- * @param roles
- */
- hasAllRoles(roles: Array<string>): boolean {
- return roles.every((r) => this.hasRole(r))
- }
- /**
- * Est-ce que l'organisation possède le module donné
- *
- * @return {boolean}
- * @param module
- */
- hasModule(module: ConditionParameters | string): boolean {
- if (Object.prototype.hasOwnProperty.call(module, 'subject')) {
- module = (module as ConditionParameters).subject
- }
- return this.organizationProfile.hasModule(module)
- }
- /**
- * Est-ce que l'organisation possède au moins un des modules donnés
- *
- * @param modules
- * @return {boolean}
- */
- hasAnyModule(modules: Array<ConditionParameters | string>): boolean {
- return modules.some((r) => this.hasModule(r))
- }
- /**
- * Est-ce que l'organisation possède-t-il tous les modules donnés
- *
- * @param modules
- * @return {boolean}
- */
- hasAllModules(modules: Array<ConditionParameters | string>): boolean {
- return modules.every((r) => this.hasModule(r))
- }
- }
- export default AbilityBuilder
|