This commit is contained in:
Ben Irvin 2022-08-01 13:40:43 +02:00
parent 20fe90de93
commit a8d6c215d2

View File

@ -281,7 +281,7 @@ describe('Permissions Engine', () => {
expect(registerFunctions[1]).toBeCalledWith(permissions[1]); expect(registerFunctions[1]).toBeCalledWith(permissions[1]);
}); });
it('does not register action when conditions not met', async () => { it(`doesn't register action when conditions not met`, async () => {
const permissions = [ const permissions = [
{ {
action: 'read', action: 'read',
@ -309,7 +309,7 @@ describe('Permissions Engine', () => {
expect(registerFunctions[0]).toBeCalledTimes(0); expect(registerFunctions[0]).toBeCalledTimes(0);
}); });
it('register action when conditions are met', async () => { it('registers an action when conditions are met', async () => {
const permissions = [ const permissions = [
{ {
action: 'read', action: 'read',
@ -336,163 +336,183 @@ describe('Permissions Engine', () => {
}); });
describe('hooks', () => { describe('hooks', () => {
it('format.permission can modify permissions', async () => { describe('format.permission', () => {
const permissions = [{ action: 'read', subject: 'article' }]; it('modifies permissions correctly', async () => {
const newPermissions = [{ action: 'view', subject: 'article' }]; const permissions = [{ action: 'read', subject: 'article' }];
const { ability, registerFunctions } = await buildEngineWithAbility({ const newPermissions = [{ action: 'view', subject: 'article' }];
permissions, const { ability, registerFunctions } = await buildEngineWithAbility({
engineHooks: [ permissions,
{ engineHooks: [
name: 'format.permission', {
// eslint-disable-next-line no-unused-vars name: 'format.permission',
fn(permission) { // eslint-disable-next-line no-unused-vars
return newPermissions[0]; fn(permission) {
return newPermissions[0];
},
}, },
}, ],
], });
expect(ability.rules).toMatchObject(expectedAbilityRules(newPermissions));
expect(ability.can('read')).toBeFalsy();
expect(ability.can('read')).toBeFalsy();
expect(ability.can('view', 'article')).toBeTruthy();
expect(registerFunctions[0]).toBeCalledWith(newPermissions[0]);
}); });
expect(ability.rules).toMatchObject(expectedAbilityRules(newPermissions));
expect(ability.can('read')).toBeFalsy();
expect(ability.can('read')).toBeFalsy();
expect(ability.can('view', 'article')).toBeTruthy();
expect(registerFunctions[0]).toBeCalledWith(newPermissions[0]);
}); });
it('validate hooks are called in the right order', async () => { describe('before-format::validate.permission', () => {
const permissions = [{ action: 'update' }, { action: 'delete' }, { action: 'view' }]; it('before-format::validate.permission can prevent action register', async () => {
const newPermissions = [{ action: 'modify' }, { action: 'remove' }]; const permissions = [{ action: 'read', subject: 'article' }];
const newPermissions = [];
const { ability, registerFunctions, createRegisterFunction } = await buildEngineWithAbility(
{
permissions,
engineHooks: [
{
name: 'before-format::validate.permission',
fn: generateInvalidateActionHook('read'),
},
],
}
);
const { ability } = await buildEngineWithAbility({ expect(ability.rules).toMatchObject(expectedAbilityRules(newPermissions));
permissions,
engineHooks: [ expect(ability.can('read', 'article')).toBeFalsy();
{ expect(ability.can('read', 'user')).toBeFalsy();
name: 'format.permission', expect(createRegisterFunction).toBeCalledTimes(1);
fn(permission) { expect(registerFunctions[0]).toBeCalledTimes(0);
if (permission.action === 'update') {
return {
...permission,
action: 'modify',
};
}
if (permission.action === 'delete') {
return {
...permission,
action: 'remove',
};
}
if (permission.action === 'view') {
return {
...permission,
action: 'read',
};
}
return permission;
},
},
{
name: 'before-format::validate.permission',
fn: generateInvalidateActionHook('modify'),
},
{
name: 'before-format::validate.permission',
fn: generateInvalidateActionHook('view'),
},
{
name: 'post-format::validate.permission',
fn: generateInvalidateActionHook('update'),
},
],
}); });
expect(ability.rules).toMatchObject(expectedAbilityRules(newPermissions));
expect(ability.can('update')).toBeFalsy();
expect(ability.can('modify')).toBeTruthy();
expect(ability.can('delete')).toBeFalsy();
expect(ability.can('remove')).toBeTruthy();
expect(ability.can('view')).toBeFalsy();
}); });
it('before-format::validate.permission can prevent action register', async () => { describe('post-format::validate.permission', () => {
const permissions = [{ action: 'read', subject: 'article' }]; it('can prevent action register', async () => {
const newPermissions = []; const permissions = [
const { ability, registerFunctions, createRegisterFunction } = await buildEngineWithAbility({ { action: 'read', subject: 'article' },
permissions, { action: 'read', subject: 'user' },
engineHooks: [ { action: 'write', subject: 'article' },
{ name: 'before-format::validate.permission', fn: generateInvalidateActionHook('read') }, ];
], const newPermissions = [{ action: 'write', subject: 'article' }];
const { ability, registerFunctions, createRegisterFunction } = await buildEngineWithAbility(
{
permissions,
engineHooks: [
{
name: 'post-format::validate.permission',
fn: generateInvalidateActionHook('read'),
},
],
}
);
expect(ability.rules).toMatchObject(expectedAbilityRules(newPermissions));
expect(ability.can('read', 'article')).toBeFalsy();
expect(ability.can('read', 'user')).toBeFalsy();
expect(ability.can('write', 'article')).toBeTruthy();
expect(createRegisterFunction).toBeCalledTimes(3);
expect(registerFunctions[0]).toBeCalledTimes(0);
expect(registerFunctions[1]).toBeCalledTimes(0);
expect(registerFunctions[2]).toBeCalledTimes(1);
}); });
});
expect(ability.rules).toMatchObject(expectedAbilityRules(newPermissions)); describe('*validate* hooks', () => {
it('execute in the correct order', async () => {
const permissions = [{ action: 'update' }, { action: 'delete' }, { action: 'view' }];
const newPermissions = [{ action: 'modify' }, { action: 'remove' }];
expect(ability.can('read', 'article')).toBeFalsy(); const { ability } = await buildEngineWithAbility({
expect(ability.can('read', 'user')).toBeFalsy(); permissions,
expect(createRegisterFunction).toBeCalledTimes(1); engineHooks: [
expect(registerFunctions[0]).toBeCalledTimes(0); {
name: 'format.permission',
fn(permission) {
if (permission.action === 'update') {
return {
...permission,
action: 'modify',
};
}
if (permission.action === 'delete') {
return {
...permission,
action: 'remove',
};
}
if (permission.action === 'view') {
return {
...permission,
action: 'read',
};
}
return permission;
},
},
{
name: 'before-format::validate.permission',
fn: generateInvalidateActionHook('modify'),
},
{
name: 'before-format::validate.permission',
fn: generateInvalidateActionHook('view'),
},
{
name: 'post-format::validate.permission',
fn: generateInvalidateActionHook('update'),
},
],
});
expect(ability.rules).toMatchObject(expectedAbilityRules(newPermissions));
expect(ability.can('update')).toBeFalsy();
expect(ability.can('modify')).toBeTruthy();
expect(ability.can('delete')).toBeFalsy();
expect(ability.can('remove')).toBeTruthy();
expect(ability.can('view')).toBeFalsy();
});
}); });
}); });
it('post-format::validate.permission can prevent action register', async () => { describe('before-* hooks', () => {
const permissions = [ it('execute in the correct order', async () => {
{ action: 'read', subject: 'article' }, let called = '';
{ action: 'read', subject: 'user' }, const beforeEvaluateFn = jest.fn(() => {
{ action: 'write', subject: 'article' }, called = 'beforeEvaluate';
]; });
const newPermissions = [{ action: 'write', subject: 'article' }]; const beforeRegisterFn = jest.fn(() => {
const { ability, registerFunctions, createRegisterFunction } = await buildEngineWithAbility({ expect(called).toEqual('beforeEvaluate');
permissions, called = 'beforeRegister';
engineHooks: [ });
{ name: 'post-format::validate.permission', fn: generateInvalidateActionHook('read') }, const permissions = [{ action: 'read', subject: 'article', conditions: [allowedCondition] }];
], await buildEngineWithAbility({
}); permissions,
engineHooks: [
{
name: 'before-evaluate.permission',
fn: beforeEvaluateFn,
},
{
name: 'before-register.permission',
fn: beforeRegisterFn,
},
],
});
expect(ability.rules).toMatchObject(expectedAbilityRules(newPermissions)); expect(beforeEvaluateFn).toBeCalledTimes(1);
expect(beforeEvaluateFn).toBeCalledWith({
expect(ability.can('read', 'article')).toBeFalsy(); addCondition: expect.any(Function),
expect(ability.can('read', 'user')).toBeFalsy(); permission: permissions[0],
expect(ability.can('write', 'article')).toBeTruthy(); });
expect(createRegisterFunction).toBeCalledTimes(3); expect(beforeRegisterFn).toBeCalledTimes(1);
expect(registerFunctions[0]).toBeCalledTimes(0); expect(beforeRegisterFn).toBeCalledWith({
expect(registerFunctions[1]).toBeCalledTimes(0); condition: expect.any(Object),
expect(registerFunctions[2]).toBeCalledTimes(1); permission: { ...permissions[0], conditions: undefined, properties: undefined },
}); });
expect(called).toEqual('beforeRegister');
it('before-evaluate and before-register are called in the right order', async () => {
let called = '';
const beforeEvaluateFn = jest.fn(() => {
called = 'beforeEvaluate';
}); });
const beforeRegisterFn = jest.fn(() => {
expect(called).toEqual('beforeEvaluate');
called = 'beforeRegister';
});
const permissions = [{ action: 'read', subject: 'article', conditions: [allowedCondition] }];
await buildEngineWithAbility({
permissions,
engineHooks: [
{
name: 'before-evaluate.permission',
fn: beforeEvaluateFn,
},
{
name: 'before-register.permission',
fn: beforeRegisterFn,
},
],
});
expect(beforeEvaluateFn).toBeCalledTimes(1);
expect(beforeEvaluateFn).toBeCalledWith({
addCondition: expect.any(Function),
permission: permissions[0],
});
expect(beforeRegisterFn).toBeCalledTimes(1);
expect(beforeRegisterFn).toBeCalledWith({
condition: expect.any(Object),
permission: { ...permissions[0], conditions: undefined, properties: undefined },
});
expect(called).toEqual('beforeRegister');
}); });
}); });