abilitiesUtils.ts 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. import {$accessProfile} from '~/services/profile/accessProfile'
  2. import {$organizationProfile} from '~/services/profile/organizationProfile'
  3. import {$roleUtils} from '~/services/rights/roleUtils'
  4. import {AbilitiesType} from '~/types/interfaces'
  5. import {useAccessProfileStore} from "~/store/profile/access";
  6. import {useProfileOrganizationStore} from "~/store/profile/organization";
  7. import YamlDenormalizer from "~/services/data/serializer/denormalizer/yamlDenormalizer";
  8. import {MongoAbility} from "@casl/ability/dist/types/Ability";
  9. import {AnyJson} from "~/types/data";
  10. /**
  11. * Classe permettant de mener des opérations sur les habilités
  12. */
  13. class AbilitiesUtils {
  14. private readonly $ability: MongoAbility = {} as MongoAbility
  15. private factory: AnyJson = {}
  16. /**
  17. * @constructor
  18. */
  19. constructor(ability: MongoAbility) {
  20. this.$ability = ability
  21. }
  22. /**
  23. * Retourne la factory des services
  24. *
  25. * @return {AnyJson} factory
  26. */
  27. getFactory() {
  28. return this.factory
  29. }
  30. /**
  31. * Initialise les services factories
  32. */
  33. initFactory() {
  34. this.factory = {
  35. access: $accessProfile,
  36. organization: $organizationProfile()
  37. }
  38. this.setAbilitiesAndStore()
  39. }
  40. /**
  41. * Initialise les Abilities pour le service AccessProfile
  42. */
  43. setAbilitiesAndStore() {
  44. this.factory.access.setAbility(this.$ability)
  45. this.factory.access.setPinia()
  46. }
  47. /**
  48. * Définit les abilities de l'utilisateur à chaque fois qu'on met à jour son profile
  49. */
  50. setAbilities() {
  51. const accessProfileStore = useAccessProfileStore()
  52. const organizationProfileStore = useProfileOrganizationStore()
  53. // Nécessaire pour que l'update des habilités soit correcte après la phase SSR
  54. this.$ability.update(accessProfileStore.abilities)
  55. // // Au moment où l'on effectue un SetProfile, il faut aller récupérer
  56. // // les différentes abilitées que l'utilisateur peut effectuer. (Tout cela se passe en SSR)
  57. const unsubscribe = organizationProfileStore.$onAction(({
  58. name, // name of the action
  59. store, // store instance, same as `someStore`
  60. args, // array of parameters passed to the action
  61. after, // hook after the action returns or resolves
  62. onError, // hook if the action throws or rejects
  63. }) => {
  64. after((result)=>{
  65. if(name === 'setProfile'){
  66. //On récupère les abilités
  67. const abilities: Array<AbilitiesType> = this.getAbilities();
  68. //On les store puis on update le service ability pour le mettre à jour.
  69. accessProfileStore.abilities = abilities
  70. this.$ability.update(abilities)
  71. // Unsubscribe pour éviter les memory leaks
  72. unsubscribe()
  73. }
  74. })
  75. })
  76. }
  77. /**
  78. * Récupération de l'ensemble des abilities, qu'elles soient par Roles ou par Config
  79. *
  80. * @return {Array<AbilitiesType>}
  81. */
  82. getAbilities(): Array<AbilitiesType> {
  83. const accessProfileStore = useAccessProfileStore()
  84. const abilitiesByRoles: Array<AbilitiesType> = this.getAbilitiesByRoles(accessProfileStore.roles)
  85. this.$ability.update(abilitiesByRoles)
  86. this.initFactory()
  87. return abilitiesByRoles.concat(this.getAbilitiesByConfig('./config/abilities/config.yaml'))
  88. }
  89. /**
  90. * Adaptation et transformations des roles en abilities
  91. *
  92. * @param {Array<string>} roles
  93. * @return {Array<AbilitiesType>}
  94. */
  95. getAbilitiesByRoles(roles: Array<string>): Array<AbilitiesType> {
  96. roles = $roleUtils.transformUnderscoreToHyphenBeforeCompleteMigration(roles)
  97. return $roleUtils.transformRoleToAbilities(roles)
  98. }
  99. /**
  100. * - Parcourt la config d'abilities en Yaml
  101. * - filtres la config pour ne garder que les abilities autorisées
  102. * - transform la config restante en Object Abilities
  103. * @param {string} configPath
  104. * @return {Array<AbilitiesType>}
  105. */
  106. getAbilitiesByConfig(configPath: string): Array<AbilitiesType> {
  107. let abilitiesByConfig: Array<AbilitiesType> = []
  108. try {
  109. const doc = YamlDenormalizer.denormalize({path: configPath})
  110. const abilitiesAvailable = doc.abilities
  111. const abilitiesFiltered = this.abilitiesAvailableFilter(abilitiesAvailable)
  112. abilitiesByConfig = this.transformAbilitiesConfigToAbility(abilitiesFiltered)
  113. } catch (e: any) {
  114. throw new Error(e.message)
  115. }
  116. return abilitiesByConfig
  117. }
  118. /**
  119. * Filtre toutes les abilities possible suivant si l'utilisateur est autorisé ou non à les posséder
  120. *
  121. * @param {AnyJson} abilitiesAvailable
  122. * @return {AnyJson}
  123. */
  124. abilitiesAvailableFilter(abilitiesAvailable: AnyJson): AnyJson {
  125. return usePickBy(abilitiesAvailable, (ability: any) => {
  126. const services = ability.services
  127. return this.canHaveTheAbility(services)
  128. })
  129. }
  130. /**
  131. * Transform une config d'abilities en un tableau d'Abilities
  132. *
  133. * @param {AnyJson} abilitiesAvailable
  134. * @return {Array<AbilitiesType>}
  135. */
  136. transformAbilitiesConfigToAbility(abilitiesAvailable: AnyJson): Array<AbilitiesType> {
  137. const abilitiesByConfig: Array<AbilitiesType> = []
  138. useEach(abilitiesAvailable, (ability, subject) => {
  139. const myAbility: AbilitiesType = {
  140. action: ability.action,
  141. subject
  142. }
  143. abilitiesByConfig.push(myAbility)
  144. })
  145. return abilitiesByConfig
  146. }
  147. /**
  148. * Parcourt les fonctions par services et établit si oui ou non l'habilité est autorisée
  149. *
  150. * @return {boolean}
  151. * @param functionByservices
  152. */
  153. canHaveTheAbility(functionByservices: AnyJson) {
  154. let hasAbility: boolean = true;
  155. useEach(functionByservices, (functions, service) => {
  156. if (hasAbility) {
  157. const nbFunctions: number = functions.length
  158. let cmpt: number = 0
  159. while (hasAbility && nbFunctions > cmpt) {
  160. const f: string = functions[cmpt]['function'];
  161. const parameters: any = functions[cmpt]['parameters'] ?? null;
  162. const result: boolean = functions[cmpt]['result'] ?? true;
  163. hasAbility = result !== null ? this.factory[service].handler()[f](parameters) == result : this.factory[service].handler()[f](parameters)
  164. cmpt++
  165. }
  166. }
  167. })
  168. return hasAbility
  169. }
  170. }
  171. export const $abilitiesUtils = (ability: MongoAbility) => new AbilitiesUtils(ability)