abilitiesUtils.ts 5.6 KB

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