abilitiesUtils.ts 6.9 KB

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