abilitiesUtils.ts 6.8 KB

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