abilitiesUtils.ts 5.9 KB

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