import { describe, test, expect } from 'vitest' import {MongoAbility} from "@casl/ability/dist/types/Ability"; import {AbilitiesType, AccessProfile, organizationState} from "~/types/interfaces"; import AbilityBuilder from "~/services/rights/abilityBuilder"; import {ABILITIES} from "~/types/enum/enums"; let ability: MongoAbility let accessProfile: AccessProfile let organizationProfile: organizationState let abilityBuilder: AbilityBuilder // Mock the content of the config yaml files // > This must be done in the global scope: https://vitest.dev/api/vi.html#vi-mock const doc = { abilities: { 'subject1': { action: ABILITIES.READ, conditions: [ { 'function': 'fct1', 'parameters': ['param1'], 'expectedResult': true } ] }, 'subject2': { action: ABILITIES.READ, conditions: { 'function': 'fct2', 'parameters': ['param2'], 'expectedResult': false } } } } vi.mock('yaml-import', async () => { return { default: { read: vi.fn((data: string) => doc) } } }) beforeEach(() => { ability = vi.fn() as any as MongoAbility accessProfile = vi.fn() as any as AccessProfile organizationProfile = vi.fn() as any as organizationState abilityBuilder = new AbilityBuilder(ability, accessProfile, organizationProfile) }) describe('buildAbilities', () => { test('base call', () => { const roleAbilities: Array = [ {action: ABILITIES.READ, subject: 'subject1'}, {action: ABILITIES.READ, subject: 'subject2'} ] const configAbilities: Array = [ {action: ABILITIES.READ, subject: 'subject3'}, {action: ABILITIES.READ, subject: 'subject4'} ] const allAbilities: Array = roleAbilities.concat(configAbilities) abilityBuilder.buildAbilitiesFromRoles = vi.fn(() => roleAbilities) abilityBuilder.buildAbilitiesFromConfig = vi.fn(() => configAbilities) ability.update = vi.fn() const result = abilityBuilder.buildAbilities() expect(ability.update).toHaveBeenCalledTimes(2) expect(ability.update).toHaveBeenCalledWith(roleAbilities) expect(ability.update).toHaveBeenCalledWith(allAbilities) expect(abilityBuilder.buildAbilitiesFromRoles).toHaveBeenCalledOnce() expect(abilityBuilder.buildAbilitiesFromConfig).toHaveBeenCalledOnce() expect(result).toEqual(allAbilities) }) }) describe('buildAbilitiesFromRoles', () => { test('calls roleUtils', () => { accessProfile.roles = ['ROLE_EVENTS_VIEW', 'ROLE_COURSES', 'ROLE_TEACHER_CORE', 'ROLE_OTHER'] const expected = [ { subject: 'events', action: 'read' }, { subject: 'courses', action: 'manage' }, { subject: 'other', action: 'manage' }, ] expect(abilityBuilder.buildAbilitiesFromRoles()).toEqual(expected) }) }) describe('buildAbilitiesFromConfig', () => { test('calls roleUtils', () => { abilityBuilder.hasConfigAbility = vi.fn(() => true) expect(abilityBuilder.buildAbilitiesFromConfig()).toEqual([ { action: 'read', subject: 'subject1' }, { action: 'read', subject: 'subject2' }, ]) }) }) describe('hasConfigAbility', () => { beforeEach(() => { accessProfile.isGuardian = true // @ts-ignore organizationProfile.isSchool = true // @ts-ignore organizationProfile.isCmf = false }) test('fulfill all conditions', () => { const conditions = [ {'function': 'accessHasAnyProfile', parameters: ['guardian', 'payer']}, {'function': 'organizationIsSchool'}, ] expect(abilityBuilder.hasConfigAbility(conditions)).toBeTruthy() }) test('fulfill at least one condition', () => { const conditions = [ {'function': 'accessHasAnyProfile', parameters: ['guardian', 'payer']}, {'function': 'organizationIsCmf'}, ] expect(abilityBuilder.hasConfigAbility(conditions)).toBeFalsy() }) test('fulfill none of the conditions', () => { const conditions = [ {'function': 'organizationIsCmf'}, ] expect(abilityBuilder.hasConfigAbility(conditions)).toBeFalsy() }) }) describe('execAndValidateCondition', () => { test('accessHasAllRoleAbilities', () => { ability.can = vi.fn((action: string, subject: string) => { return action === 'read' && (subject === 'subject1' || subject === 'subject2') }) expect( // @ts-ignore abilityBuilder.execAndValidateCondition( { 'function': 'accessHasAllRoleAbilities', parameters: [ {action: ABILITIES.READ, subject: 'subject1'}, {action: ABILITIES.READ, subject: 'subject2'}, ] }) ).toBeTruthy() expect( // @ts-ignore abilityBuilder.execAndValidateCondition( { 'function': 'accessHasAllRoleAbilities', parameters: [ {action: ABILITIES.READ, subject: 'subject1'}, {action: ABILITIES.READ, subject: 'subject3'} ] }) ).toBeFalsy() }) test('accessHasAnyRoleAbility', () => { ability.can = vi.fn((action: string, subject: string) => { return action === 'read' && subject === 'subject1' }) expect( // @ts-ignore abilityBuilder.execAndValidateCondition( { 'function': 'accessHasAnyRoleAbility', parameters: [ {action: ABILITIES.READ, subject: 'subject1'}, {action: ABILITIES.READ, subject: 'subject2'}, ] }) ).toBeTruthy() expect( // @ts-ignore abilityBuilder.execAndValidateCondition( {'function': 'accessHasAnyRoleAbility', parameters: [{action: ABILITIES.READ, subject: 'subject2'}]}) ).toBeFalsy() }) test('accessHasAnyProfile', () => { accessProfile.isMember = true accessProfile.isGuardian = true accessProfile.isPayer = true expect( // @ts-ignore abilityBuilder.execAndValidateCondition( {'function': 'accessHasAnyProfile', parameters: ['guardian', 'payer']} ) ).toBeTruthy() expect( // @ts-ignore abilityBuilder.execAndValidateCondition( {'function': 'accessHasAnyProfile', parameters: ['guardian', 'caMember']} ) ).toBeTruthy() expect( // @ts-ignore abilityBuilder.execAndValidateCondition( {'function': 'accessHasAnyProfile', parameters: ['caMember']} ) ).toBeFalsy() }) test('organizationHasAllModules', () => { // @ts-ignore organizationProfile.hasModule = vi.fn( (module: string) => module === 'module1' || module === 'module2' ) expect( // @ts-ignore abilityBuilder.execAndValidateCondition( {'function': 'organizationHasAllModules', parameters: ['module1', 'module2']} ) ).toBeTruthy() expect( // @ts-ignore abilityBuilder.execAndValidateCondition( {'function': 'organizationHasAllModules', parameters: ['module1', 'module3']} ) ).toBeFalsy() expect( // @ts-ignore abilityBuilder.execAndValidateCondition( {'function': 'organizationHasAllModules', parameters: ['module3']} ) ).toBeFalsy() }) test('organizationHasAnyModule', () => { // @ts-ignore organizationProfile.hasModule = vi.fn( (module: string) => module === 'module1' || module === 'module2' ) expect( // @ts-ignore abilityBuilder.execAndValidateCondition( {'function': 'organizationHasAnyModule', parameters: ['module1', 'module2']} ) ).toBeTruthy() expect( // @ts-ignore abilityBuilder.execAndValidateCondition( {'function': 'organizationHasAnyModule', parameters: ['module1', 'module3']} ) ).toBeTruthy() expect( // @ts-ignore abilityBuilder.execAndValidateCondition( {'function': 'organizationHasAnyModule', parameters: ['module3']} ) ).toBeFalsy() }) test('organizationHasAnyModule', () => { // @ts-ignore accessProfile.isAdminAccount = true // @ts-ignore expect(abilityBuilder.execAndValidateCondition({'function': 'accessIsAdminAccount'})).toBeTruthy() // @ts-ignore accessProfile.isAdminAccount = false // @ts-ignore expect(abilityBuilder.execAndValidateCondition({'function': 'accessIsAdminAccount'})).toBeFalsy() }) test('organizationIsSchool', () => { // @ts-ignore organizationProfile.isSchool = true // @ts-ignore expect(abilityBuilder.execAndValidateCondition({'function': 'organizationIsSchool'})).toBeTruthy() // @ts-ignore organizationProfile.isSchool = false // @ts-ignore expect(abilityBuilder.execAndValidateCondition({'function': 'organizationIsSchool'})).toBeFalsy() }) test('organizationIsArtist', () => { // @ts-ignore organizationProfile.isArtist = true // @ts-ignore expect(abilityBuilder.execAndValidateCondition({'function': 'organizationIsArtist'})).toBeTruthy() // @ts-ignore organizationProfile.isArtist = false // @ts-ignore expect(abilityBuilder.execAndValidateCondition({'function': 'organizationIsArtist'})).toBeFalsy() }) test('organizationIsManagerProduct', () => { // @ts-ignore organizationProfile.isManagerProduct = true // @ts-ignore expect(abilityBuilder.execAndValidateCondition({'function': 'organizationIsManagerProduct'})).toBeTruthy() // @ts-ignore organizationProfile.isManagerProduct = false // @ts-ignore expect(abilityBuilder.execAndValidateCondition({'function': 'organizationIsManagerProduct'})).toBeFalsy() }) test('organizationHasChildren', () => { // @ts-ignore organizationProfile.hasChildren = true // @ts-ignore expect(abilityBuilder.execAndValidateCondition({'function': 'organizationHasChildren'})).toBeTruthy() // @ts-ignore organizationProfile.hasChildren = false // @ts-ignore expect(abilityBuilder.execAndValidateCondition({'function': 'organizationHasChildren'})).toBeFalsy() }) test('organizationIsAssociation', () => { // @ts-ignore organizationProfile.isAssociation = true // @ts-ignore expect(abilityBuilder.execAndValidateCondition({'function': 'organizationIsAssociation'})).toBeTruthy() // @ts-ignore organizationProfile.isAssociation = false // @ts-ignore expect(abilityBuilder.execAndValidateCondition({'function': 'organizationIsAssociation'})).toBeFalsy() }) test('organizationIsShowAdherentList', () => { // @ts-ignore organizationProfile.isShowAdherentList = true // @ts-ignore expect(abilityBuilder.execAndValidateCondition({'function': 'organizationIsShowAdherentList'})).toBeTruthy() // @ts-ignore organizationProfile.isShowAdherentList = false // @ts-ignore expect(abilityBuilder.execAndValidateCondition({'function': 'organizationIsShowAdherentList'})).toBeFalsy() }) test('organizationIsCmf', () => { // @ts-ignore organizationProfile.isCmf = true // @ts-ignore expect(abilityBuilder.execAndValidateCondition({'function': 'organizationIsCmf'})).toBeTruthy() // @ts-ignore organizationProfile.isCmf = false // @ts-ignore expect(abilityBuilder.execAndValidateCondition({'function': 'organizationIsCmf'})).toBeFalsy() }) test('organizationHasWebsite', () => { // @ts-ignore organizationProfile.getWebsite = true // @ts-ignore expect(abilityBuilder.execAndValidateCondition({'function': 'organizationHasWebsite'})).toBeTruthy() // @ts-ignore organizationProfile.getWebsite = false // @ts-ignore expect(abilityBuilder.execAndValidateCondition({'function': 'organizationHasWebsite'})).toBeFalsy() }) test('with expected result', () => { // @ts-ignore organizationProfile.getWebsite = true expect( // @ts-ignore abilityBuilder.execAndValidateCondition({'function': 'organizationHasWebsite', expectedResult: true}) ).toBeTruthy() expect( // @ts-ignore abilityBuilder.execAndValidateCondition({'function': 'organizationHasWebsite', expectedResult: 'abc'}) ).toBeFalsy() }) test('invalid function', () => { expect( // @ts-ignore () => abilityBuilder.execAndValidateCondition({'function': 'invalid'}) ).toThrowError('unknown condition function : invalid') }) }) describe('hasRoleAbility', () => { beforeEach(() => { ability.can = vi.fn((action: string, subject: string) => { return action === 'read' && subject === 'a_subject' }) }) test('owned ability', () => { expect(abilityBuilder.hasRoleAbility({action: ABILITIES.READ, subject: 'a_subject'})).toBeTruthy() }) test('not owned ability', () => { expect(abilityBuilder.hasRoleAbility({action: ABILITIES.READ, subject: 'other_subject'})).toBeFalsy() }) }) describe('hasAllRoleAbilities', () => { beforeEach(() => { ability.can = vi.fn((action: string, subject: string) => { return action === 'read' && (subject === 'subject1' || subject === 'subject2') }) }) test('own all abilities', () => { const result = abilityBuilder.hasAllRoleAbilities( [ {action: ABILITIES.READ, subject: 'subject1'}, {action: ABILITIES.READ, subject: 'subject2'}, ] ) expect(result).toBeTruthy() }) test('own at least one ability', () => { const result = abilityBuilder.hasAllRoleAbilities( [ {action: ABILITIES.READ, subject: 'subject1'}, {action: ABILITIES.READ, subject: 'subject3'}, ] ) expect(result).toBeFalsy() }) test('own none of the abilities', () => { const result = abilityBuilder.hasAllRoleAbilities( [ {action: ABILITIES.READ, subject: 'subject3'}, {action: ABILITIES.READ, subject: 'subject4'}, ] ) expect(result).toBeFalsy() }) }) describe('hasAnyRoleAbility', () => { beforeEach(() => { ability.can = vi.fn((action: string, subject: string) => { return action === 'read' && (subject === 'subject1' || subject === 'subject2') }) }) test('has all abilities', () => { const result = abilityBuilder.hasAnyRoleAbility( [ {action: ABILITIES.READ, subject: 'subject1'}, {action: ABILITIES.READ, subject: 'subject2'}, ] ) expect(result).toBeTruthy() }) test('has at least one ability', () => { const result = abilityBuilder.hasAnyRoleAbility( [ {action: ABILITIES.READ, subject: 'subject1'}, {action: ABILITIES.READ, subject: 'subject3'}, ] ) expect(result).toBeTruthy() }) test('any none of the abilites', () => { const result = abilityBuilder.hasAnyRoleAbility( [ {action: ABILITIES.READ, subject: 'subject3'}, {action: ABILITIES.READ, subject: 'subject4'}, ] ) expect(result).toBeFalsy() }) }) describe('hasProfile', () => { test('owned profiles', () => { accessProfile.isAdmin = true accessProfile.isAdministratifManager = true accessProfile.isPedagogicManager = true accessProfile.isFinancialManager = true accessProfile.isCaMember = true accessProfile.isStudent = true accessProfile.isTeacher = true accessProfile.isMember = true accessProfile.isOther = true accessProfile.isGuardian = true accessProfile.isPayer = true expect(abilityBuilder.hasProfile('admin')).toBeTruthy() expect(abilityBuilder.hasProfile('administratifManager')).toBeTruthy() expect(abilityBuilder.hasProfile('pedagogicManager')).toBeTruthy() expect(abilityBuilder.hasProfile('financialManager')).toBeTruthy() expect(abilityBuilder.hasProfile('caMember')).toBeTruthy() expect(abilityBuilder.hasProfile('student')).toBeTruthy() expect(abilityBuilder.hasProfile('teacher')).toBeTruthy() expect(abilityBuilder.hasProfile('member')).toBeTruthy() expect(abilityBuilder.hasProfile('other')).toBeTruthy() expect(abilityBuilder.hasProfile('guardian')).toBeTruthy() expect(abilityBuilder.hasProfile('payor')).toBeTruthy() }) test('not owned profiles', () => { accessProfile.isAdmin = false accessProfile.isAdministratifManager = false accessProfile.isPedagogicManager = false accessProfile.isFinancialManager = false accessProfile.isCaMember = false accessProfile.isStudent = false accessProfile.isTeacher = false accessProfile.isMember = false accessProfile.isOther = false accessProfile.isGuardian = false accessProfile.isPayer = false expect(abilityBuilder.hasProfile('admin')).toBeFalsy() expect(abilityBuilder.hasProfile('administratifManager')).toBeFalsy() expect(abilityBuilder.hasProfile('pedagogicManager')).toBeFalsy() expect(abilityBuilder.hasProfile('financialManager')).toBeFalsy() expect(abilityBuilder.hasProfile('caMember')).toBeFalsy() expect(abilityBuilder.hasProfile('student')).toBeFalsy() expect(abilityBuilder.hasProfile('teacher')).toBeFalsy() expect(abilityBuilder.hasProfile('member')).toBeFalsy() expect(abilityBuilder.hasProfile('other')).toBeFalsy() expect(abilityBuilder.hasProfile('guardian')).toBeFalsy() expect(abilityBuilder.hasProfile('payor')).toBeFalsy() }) }) describe('hasAnyProfile', () => { beforeEach(() => { accessProfile.isMember = true accessProfile.isGuardian = true accessProfile.isPayer = true }) test('own all profiles', () => { expect(abilityBuilder.hasAnyProfile(['member', 'guardian', 'payor'])).toBeTruthy() }) test('own at least one profile', () => { expect(abilityBuilder.hasAnyProfile(['member', 'caMember'])).toBeTruthy() }) test('own none of the profiles', () => { expect(abilityBuilder.hasAnyProfile(['caMember', 'isFinancialManager'])).toBeFalsy() }) }) describe('hasAllProfiles', () => { beforeEach(() => { accessProfile.isMember = true accessProfile.isGuardian = true accessProfile.isPayer = true }) test('own all profiles', () => { expect(abilityBuilder.hasAllProfiles(['member', 'guardian', 'payor'])).toBeTruthy() }) test('own only one of the profiles', () => { expect(abilityBuilder.hasAllProfiles(['member', 'caMember'])).toBeFalsy() }) test('own none of the profiles', () => { expect(abilityBuilder.hasAllProfiles(['caMember', 'isFinancialManager'])).toBeFalsy() }) }) describe('hasRole', () => { beforeEach(() => { // @ts-ignore accessProfile.hasRole = vi.fn((role: string) => role === 'foo') }) test('has role', () => { expect(abilityBuilder.hasRole('foo')).toBeTruthy() }) test('has not role', () => { expect(abilityBuilder.hasRole('bar')).toBeFalsy() }) }) describe('hasAnyRole', () => { beforeEach(() => { // @ts-ignore accessProfile.hasRole = vi.fn((role: string) => role === 'role1' || role === 'role2') }) test('own all roles', () => { expect(abilityBuilder.hasAnyRole(['role1', 'role2'])).toBeTruthy() }) test('own at least one role', () => { expect(abilityBuilder.hasAnyRole(['role1', 'role3'])).toBeTruthy() }) test('own none of the roles', () => { expect(abilityBuilder.hasAnyRole(['role3'])).toBeFalsy() }) }) describe('hasAllRoles', () => { beforeEach(() => { // @ts-ignore accessProfile.hasRole = vi.fn((role: string) => role === 'role1' || role === 'role2') }) test('own all roles', () => { expect(abilityBuilder.hasAllRoles(['role1', 'role2'])).toBeTruthy() }) test('own at least one role', () => { expect(abilityBuilder.hasAllRoles(['role1', 'role3'])).toBeFalsy() }) test('own none of the roles', () => { expect(abilityBuilder.hasAllRoles(['role3'])).toBeFalsy() }) }) describe('hasModule', () => { beforeEach(() => { // @ts-ignore organizationProfile.hasModule = vi.fn((module: string) => module === 'foo') }) test('has module', () => { expect(abilityBuilder.hasModule('foo')).toBeTruthy() }) test('has not module', () => { expect(abilityBuilder.hasModule('bar')).toBeFalsy() }) }) describe('hasAnyModule', () => { beforeEach(() => { // @ts-ignore organizationProfile.hasModule = vi.fn((Module: string) => Module === 'Module1' || Module === 'Module2') }) test('own all modules', () => { expect(abilityBuilder.hasAnyModule(['Module1', 'Module2'])).toBeTruthy() }) test('own at least one module', () => { expect(abilityBuilder.hasAnyModule(['Module1', 'Module3'])).toBeTruthy() }) test('own none of the modules', () => { expect(abilityBuilder.hasAnyModule(['Module3'])).toBeFalsy() }) }) describe('hasAllModules', () => { beforeEach(() => { // @ts-ignore organizationProfile.hasModule = vi.fn((Module: string) => Module === 'Module1' || Module === 'Module2') }) test('own all modules', () => { expect(abilityBuilder.hasAllModules(['Module1', 'Module2'])).toBeTruthy() }) test('own at least one module', () => { expect(abilityBuilder.hasAllModules(['Module1', 'Module3'])).toBeFalsy() }) test('own none of the modules', () => { expect(abilityBuilder.hasAllModules(['Module3'])).toBeFalsy() }) })