|
@@ -14,6 +14,11 @@ class AbilityUtils {
|
|
|
private readonly accessProfile: any
|
|
private readonly accessProfile: any
|
|
|
private readonly organizationProfile: any
|
|
private readonly organizationProfile: any
|
|
|
|
|
|
|
|
|
|
+ private readonly configDir = './config/abilities/config.yaml'
|
|
|
|
|
+
|
|
|
|
|
+ private abilitiesByRoles: Array<AbilitiesType> = []
|
|
|
|
|
+ private abilitiesByConfig: Array<AbilitiesType> = []
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* @constructor
|
|
* @constructor
|
|
|
*/
|
|
*/
|
|
@@ -34,10 +39,6 @@ class AbilityUtils {
|
|
|
// Nécessaire pour que l'update des habilités soit correcte après la phase SSR
|
|
// Nécessaire pour que l'update des habilités soit correcte après la phase SSR
|
|
|
this.ability.update(this.accessProfile.abilities)
|
|
this.ability.update(this.accessProfile.abilities)
|
|
|
|
|
|
|
|
- // const abilities: Array<AbilitiesType> = this.buildAbilities();
|
|
|
|
|
- // this.accessProfile.abilities = abilities
|
|
|
|
|
- // this.ability.update(abilities)
|
|
|
|
|
-
|
|
|
|
|
// Au moment où l'on effectue une action organizationProfileStore.setProfile, il faut aller récupérer
|
|
// Au moment où l'on effectue une action organizationProfileStore.setProfile, il faut aller récupérer
|
|
|
// les différentes habilités que l'utilisateur peut effectuer. (Tout cela se passe en SSR)
|
|
// les différentes habilités que l'utilisateur peut effectuer. (Tout cela se passe en SSR)
|
|
|
const unsubscribe = this.organizationProfile.$onAction(({
|
|
const unsubscribe = this.organizationProfile.$onAction(({
|
|
@@ -50,7 +51,7 @@ class AbilityUtils {
|
|
|
after((result: any)=>{
|
|
after((result: any)=>{
|
|
|
if (name === 'setProfile'){
|
|
if (name === 'setProfile'){
|
|
|
//On récupère les habilités
|
|
//On récupère les habilités
|
|
|
- const abilities: Array<AbilitiesType> = this.buildAbilities();
|
|
|
|
|
|
|
+ const abilities = this.buildAbilities();
|
|
|
|
|
|
|
|
//On les store puis on update le service ability pour le mettre à jour.
|
|
//On les store puis on update le service ability pour le mettre à jour.
|
|
|
this.accessProfile.abilities = abilities
|
|
this.accessProfile.abilities = abilities
|
|
@@ -69,90 +70,92 @@ class AbilityUtils {
|
|
|
* @return {Array<AbilitiesType>}
|
|
* @return {Array<AbilitiesType>}
|
|
|
*/
|
|
*/
|
|
|
buildAbilities(): Array<AbilitiesType> {
|
|
buildAbilities(): Array<AbilitiesType> {
|
|
|
- const abilitiesByRoles: Array<AbilitiesType> = this.buildAbilitiesFromRoles(this.accessProfile.roles)
|
|
|
|
|
- const abilitiesByConfig = this.buildAbilitiesFromConfig('./config/abilities/config.yaml')
|
|
|
|
|
- return abilitiesByRoles.concat(abilitiesByConfig)
|
|
|
|
|
|
|
+ this.buildAbilitiesFromRoles()
|
|
|
|
|
+ this.buildAbilitiesFromConfig()
|
|
|
|
|
+
|
|
|
|
|
+ return this.abilities()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ abilities(): Array<AbilitiesType> {
|
|
|
|
|
+ return ([] as Array<AbilitiesType>).concat(this.abilitiesByRoles).concat(this.abilitiesByConfig)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* Adaptation et transformations des roles symfony en abilities Casl
|
|
* Adaptation et transformations des roles symfony en abilities Casl
|
|
|
- *
|
|
|
|
|
- * @param {Array<string>} roles
|
|
|
|
|
- * @return {Array<AbilitiesType>}
|
|
|
|
|
*/
|
|
*/
|
|
|
- buildAbilitiesFromRoles(roles: Array<string>): Array<AbilitiesType> {
|
|
|
|
|
- return RoleUtils.rolesToAbilities(roles)
|
|
|
|
|
|
|
+ buildAbilitiesFromRoles() {
|
|
|
|
|
+ this.abilitiesByRoles = RoleUtils.rolesToAbilities(this.accessProfile.roles)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* Charge les habilités depuis les fichiers de configuration
|
|
* Charge les habilités depuis les fichiers de configuration
|
|
|
- *
|
|
|
|
|
- * @param {string} configPath
|
|
|
|
|
- * @return {Array<AbilitiesType>}
|
|
|
|
|
*/
|
|
*/
|
|
|
- buildAbilitiesFromConfig(configPath: string): Array<AbilitiesType> {
|
|
|
|
|
- const doc = YamlDenormalizer.denormalize({path: configPath})
|
|
|
|
|
|
|
+ buildAbilitiesFromConfig() {
|
|
|
|
|
+ const doc = YamlDenormalizer.denormalize({path: this.configDir})
|
|
|
const fromConfig = doc.abilities
|
|
const fromConfig = doc.abilities
|
|
|
|
|
|
|
|
- const abilities: Array<AbilitiesType> = []
|
|
|
|
|
-
|
|
|
|
|
useEach(fromConfig, (ability: { action: ABILITIES, services: object }, subject: string) => {
|
|
useEach(fromConfig, (ability: { action: ABILITIES, services: object }, subject: string) => {
|
|
|
const { action, services } = ability
|
|
const { action, services } = ability
|
|
|
|
|
+
|
|
|
if (this.hasConfigAbility(services)) {
|
|
if (this.hasConfigAbility(services)) {
|
|
|
- abilities.push({ action, subject })
|
|
|
|
|
|
|
+ this.abilitiesByConfig.push({ action, subject })
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
|
- return abilities
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* Parcourt les services définis dans la configuration, et établit si oui ou non l'habilité est autorisée
|
|
* Parcourt les services définis dans la configuration, et établit si oui ou non l'habilité est autorisée
|
|
|
*
|
|
*
|
|
|
|
|
+ * TODO: voir pourquoi on a besoin d'accepter un param null pour le hasProfile?
|
|
|
|
|
+ *
|
|
|
* @return {boolean}
|
|
* @return {boolean}
|
|
|
* @param services
|
|
* @param services
|
|
|
*/
|
|
*/
|
|
|
hasConfigAbility(services: AnyJson) {
|
|
hasConfigAbility(services: AnyJson) {
|
|
|
- const handlerMap: any = {
|
|
|
|
|
- hasAbility: (parameters: any) => this.hasAllAbilities(parameters),
|
|
|
|
|
- hasProfile: (parameters: any) => parameters === null || this.hasAnyProfile(parameters),
|
|
|
|
|
- hasModule: (parameters: any) => this.hasModule(parameters),
|
|
|
|
|
- isAdminAccount: (parameters: any) => this.accessProfile.isAdminAccount,
|
|
|
|
|
- isSchool: (parameters: any) => this.organizationProfile.isSchool,
|
|
|
|
|
- isArtist: (parameters: any) => this.organizationProfile.isArtist,
|
|
|
|
|
- isManagerProduct: (parameters: any) => this.organizationProfile.isManagerProduct,
|
|
|
|
|
- isOrganizationWithChildren: (parameters: any) => this.organizationProfile.hasChildren,
|
|
|
|
|
- isAssociation: (parameters: any) => this.organizationProfile.isAssociation,
|
|
|
|
|
- isShowAdherentList: (parameters: any) => this.organizationProfile.isShowAdherentList,
|
|
|
|
|
- isCmf: (parameters: any) => this.organizationProfile.isCmf,
|
|
|
|
|
- getWebsite: (parameters: any) => this.organizationProfile.getWebsite,
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ for (const service in services) {
|
|
|
|
|
+ let handlers = services[service] as Array<{ function: string, parameters?: Array<any>, result?: any }>
|
|
|
|
|
|
|
|
- // TODO: renommer les fonctions du handlerMap de la même façon que les fonctions hasAnyXxx, hasAllXxx, etc.
|
|
|
|
|
- // TODO: revoir les fichiers yaml en fonction
|
|
|
|
|
- // TODO: clarifier le fonctionnement du hasAbility (c'est très bizarre de tester des habilités alors qu'on est en train de les construire)
|
|
|
|
|
- // TODO: extraire l'intérieur du useEach dans une méthode séparée pour clarifier l'algo
|
|
|
|
|
- // TODO: voir pourquoi on a besoin d'accepter un param null pour le hasProfile?
|
|
|
|
|
- // TODO: créer un outil pour simplifier le déboguage des droits
|
|
|
|
|
-
|
|
|
|
|
- let hasAbility = true
|
|
|
|
|
|
|
+ if (handlers.some((handler) => !this.testConfigService(handler))) {
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return true
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- useEach(services, (handlers: Array<{ function: string, parameters?: Array<any>, result?: any }>, service: string) => {
|
|
|
|
|
|
|
+ handlerMap: any = {
|
|
|
|
|
+ hasAllRoleAbilities: (parameters: any) => this.hasAllRoleAbilities(parameters),
|
|
|
|
|
+ hasAnyProfile: (parameters: any) => parameters === null || this.hasAnyProfile(parameters),
|
|
|
|
|
+ hasAllModules: (parameters: any) => this.hasAllModules(parameters),
|
|
|
|
|
+ isAdminAccount: (parameters: any) => this.accessProfile.isAdminAccount,
|
|
|
|
|
+ isSchool: (parameters: any) => this.organizationProfile.isSchool,
|
|
|
|
|
+ isArtist: (parameters: any) => this.organizationProfile.isArtist,
|
|
|
|
|
+ isManagerProduct: (parameters: any) => this.organizationProfile.isManagerProduct,
|
|
|
|
|
+ isOrganizationWithChildren: (parameters: any) => this.organizationProfile.hasChildren,
|
|
|
|
|
+ isAssociation: (parameters: any) => this.organizationProfile.isAssociation,
|
|
|
|
|
+ isShowAdherentList: (parameters: any) => this.organizationProfile.isShowAdherentList,
|
|
|
|
|
+ isCmf: (parameters: any) => this.organizationProfile.isCmf,
|
|
|
|
|
+ getWebsite: (parameters: any) => this.organizationProfile.getWebsite,
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- useEach(handlers, (handler: { function: string, parameters?: Array<any>, result?: any }) => {
|
|
|
|
|
|
|
+ private testConfigService(handler: { function: string, parameters?: Array<any>, result?: any }) {
|
|
|
|
|
+ const expectedResult: boolean = handler.result ?? true;
|
|
|
|
|
+ const parameters = handler.parameters ?? []
|
|
|
|
|
|
|
|
- const expectedResult: boolean = handler.result ?? true;
|
|
|
|
|
- const parameters = handler.parameters ?? []
|
|
|
|
|
|
|
+ const actualResult = this.handlerMap[handler.function](parameters ?? null)
|
|
|
|
|
|
|
|
- const actualResult = handlerMap[handler.function](parameters ?? null)
|
|
|
|
|
|
|
+ return actualResult === expectedResult
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- if (actualResult !== expectedResult) {
|
|
|
|
|
- hasAbility = false
|
|
|
|
|
- return false
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
- if (!hasAbility) { return false }
|
|
|
|
|
- })
|
|
|
|
|
- return hasAbility
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Est-ce que l'utilisateur possède l'habilité passée en paramètre
|
|
|
|
|
+ *
|
|
|
|
|
+ * @return {boolean}
|
|
|
|
|
+ * @param ability
|
|
|
|
|
+ */
|
|
|
|
|
+ hasRoleAbility(ability: AbilitiesType) {
|
|
|
|
|
+ return this.abilitiesByRoles.some(
|
|
|
|
|
+ (candidate) => candidate.action === ability.action && candidate.subject === ability.subject
|
|
|
|
|
+ )
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -161,8 +164,10 @@ class AbilityUtils {
|
|
|
* @param {Array<AbilitiesType>} abilities Habilités à tester
|
|
* @param {Array<AbilitiesType>} abilities Habilités à tester
|
|
|
* @return {boolean}
|
|
* @return {boolean}
|
|
|
*/
|
|
*/
|
|
|
- hasAllAbilities(abilities: Array<AbilitiesType>): boolean {
|
|
|
|
|
- return abilities.every(ability => this.ability.can(ability.action, ability.subject))
|
|
|
|
|
|
|
+ hasAllRoleAbilities(abilities: Array<AbilitiesType>): boolean {
|
|
|
|
|
+ return abilities.every(
|
|
|
|
|
+ ability => this.hasRoleAbility(ability)
|
|
|
|
|
+ )
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -172,7 +177,7 @@ class AbilityUtils {
|
|
|
* @return {boolean}
|
|
* @return {boolean}
|
|
|
*/
|
|
*/
|
|
|
hasProfile(profile: string): boolean {
|
|
hasProfile(profile: string): boolean {
|
|
|
- const factory: {[key: string]: boolean|null} = {
|
|
|
|
|
|
|
+ return {
|
|
|
'admin': this.accessProfile.isAdmin,
|
|
'admin': this.accessProfile.isAdmin,
|
|
|
'administratifManager': this.accessProfile.isAdministratifManager,
|
|
'administratifManager': this.accessProfile.isAdministratifManager,
|
|
|
'pedagogicManager': this.accessProfile.isPedagogicManager,
|
|
'pedagogicManager': this.accessProfile.isPedagogicManager,
|
|
@@ -184,8 +189,7 @@ class AbilityUtils {
|
|
|
'other': this.accessProfile.isOther,
|
|
'other': this.accessProfile.isOther,
|
|
|
'guardian': this.accessProfile.isGuardian,
|
|
'guardian': this.accessProfile.isGuardian,
|
|
|
'payor': this.accessProfile.isPayer,
|
|
'payor': this.accessProfile.isPayer,
|
|
|
- }
|
|
|
|
|
- return factory[profile] ?? false
|
|
|
|
|
|
|
+ }[profile] ?? false
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -215,7 +219,7 @@ class AbilityUtils {
|
|
|
* @param role
|
|
* @param role
|
|
|
*/
|
|
*/
|
|
|
hasRole(role: string): boolean {
|
|
hasRole(role: string): boolean {
|
|
|
- return this.accessProfile.roles.includes(role)
|
|
|
|
|
|
|
+ return this.accessProfile.hasRole(role)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -245,7 +249,7 @@ class AbilityUtils {
|
|
|
* @param module
|
|
* @param module
|
|
|
*/
|
|
*/
|
|
|
hasModule(module: string): boolean {
|
|
hasModule(module: string): boolean {
|
|
|
- return this.organizationProfile.modules.includes(module)
|
|
|
|
|
|
|
+ return this.organizationProfile.hasModule(module)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|