Skip to content

Commit

Permalink
Use built in test runner
Browse files Browse the repository at this point in the history
  • Loading branch information
voxpelli committed Jun 3, 2023
1 parent 1486e83 commit 7871e87
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 53 deletions.
9 changes: 5 additions & 4 deletions package.json
Expand Up @@ -19,9 +19,11 @@
"clean": "run-p clean:*",
"prepare": "husky install",
"prepublishOnly": "run-s build",
"test:tap": "tap 'test/**/*.test.js' --cov",
"test:node": "node --test test/*.test.js",
"test-ci": "run-s test:*",
"test": "run-s check test:*"
"test": "run-s check test:*",
"watch:test": "node --test --watch test/*.test.js",
"watch": "run-p watch:*"
},
"author": "Charles Read",
"license": "MIT",
Expand All @@ -34,7 +36,7 @@
"lib/**/*.d.ts.map"
],
"engines": {
"node": ">=16.0.0"
"node": ">=16.17.0"
},
"dependencies": {
"debug": "^4.3.1",
Expand Down Expand Up @@ -62,7 +64,6 @@
"installed-check": "^7.1.1",
"knip": "^2.13.0",
"npm-run-all2": "^6.0.5",
"tap": "^14.11.0",
"type-coverage": "^2.26.0",
"typescript": "~5.1.3"
},
Expand Down
35 changes: 24 additions & 11 deletions plugin.js
Expand Up @@ -12,21 +12,37 @@ const debug = createDebug('fastify-acl-auth:auth');
/** @typedef {string|ReadonlyArray<string>} Roles */

/**
* @callback ActualRolesCallback
* @callback RolesCallback
* @param {import('fastify').FastifyRequest} request
* @returns {Promise<Roles>|Roles|undefined}
*/

/**
* @typedef HookFactoryOptions
* @property {ActualRolesCallback} actualRoles
* @property {Roles} [allowedRoles]
* @property {ReadonlyArray<string>} [pathExempt]
* @typedef FastifyAclAuthBaseOptions
* @property {number} [httpErrorCode]
* @property {ReadonlyArray<string>} [pathExempt]
*/

/**
* @param {FastifyAclAuthOptions} options
* @typedef FastifyAclAuthOnlyOptions
* @property {RolesCallback} actualRoles
* @property {boolean} [all]
* @property {ReadonlyArray<string>} [hierarchy]
*/

/**
* @typedef FastifyAclAuthPluginOnlyOptions
* @property {RolesCallback} [actualRoles]
* @property {Roles} allowedRoles
*/

/** @typedef {FastifyAclAuthBaseOptions & FastifyAclAuthOnlyOptions} FastifyAclAuthOptions */
/** @typedef {FastifyAclAuthBaseOptions & FastifyAclAuthPluginOnlyOptions} FastifyAclAuthPluginOptions */

/** @typedef {import('fastify').FastifyPluginAsync<FastifyAclAuthPluginOptions>} FastifyAclAuthPlugin */

/**
* @param {FastifyAclAuthOptions & FastifyAclAuthPluginOptions} options
* @returns {import('fastify').preHandlerHookHandler}
*/
function createPreHandlerHook (options) {
Expand Down Expand Up @@ -70,22 +86,19 @@ function createPreHandlerHook (options) {
}
}

/** @typedef {HookFactoryOptions & import('./lib/auth.js').CheckRolesOptions} FastifyAclAuthOptions */

/** @typedef {import('fastify').FastifyPluginAsync<FastifyAclAuthOptions>} FastifyAclAuthPlugin */

/**
* @param {FastifyAclAuthOptions} options
* @returns {FastifyAclAuthPlugin}
*/
export function fastifyAclAuth (options) {
debug('aclFactory() called')

/** @satisfies {FastifyAclAuthOptions} */
const instanceOptions = { all: false, ...options };

debug('instanceOptions: %j', instanceOptions)

/** @type {import('fastify').FastifyPluginAsync<FastifyAclAuthOptions>} */
/** @type {import('fastify').FastifyPluginAsync<FastifyAclAuthPluginOptions>} */
const plugin = async (fastify, pluginOptions) => {
debug('plugin() called')

Expand Down
4 changes: 4 additions & 0 deletions test/.eslintrc
@@ -1,5 +1,9 @@
{
"rules": {
"mocha/handle-done-callback": 0,
"mocha/no-async-describe": 0,
"mocha/no-global-tests": 0,
"mocha/no-nested-tests": 0,
"node/no-unpublished-require": 0
}
}
52 changes: 39 additions & 13 deletions test/auth.test.js
@@ -1,17 +1,43 @@
import tap from 'tap'
import assert from 'node:assert/strict';
import test from 'node:test';

import { checkRoles } from '../lib/auth.js'

tap.test('auth.test.js', t => {
t.plan(10)
t.true(checkRoles('admin', 'admin'), 'admin can access admin')
t.false(checkRoles('user', 'admin'), 'user can\'t access admin')
t.true(checkRoles(['user'], 'user'), '[user] can access user')
t.true(checkRoles(['user'], ['user']), '[user] can access [user]')
t.true(checkRoles('user', ['user']), 'user can access [user]')
t.false(checkRoles(['a'], ['a', 'b'], { all: true }), 'all should cause a false return when not all roles are present')
t.true(checkRoles(['b', 'a'], ['a', 'b'], { all: true }), 'all should cause a true return when all roles are present')
t.true(checkRoles(['ham', 'b', 'x', 'a'], ['a', 'b'], { all: true }), 'all should cause a true return when all roles are present and user has more roles than are needed')
t.false(checkRoles('admin', 'user'), 'admin can\'t access user')
t.true(checkRoles('admin', 'user', { hierarchy: ['user', 'admin'] }), 'admin can access user with appropriate hierarchy')
test('checkRoles()', async t => {
await t.test('basic', () => {
assert.equal(checkRoles('admin', 'admin'), true, 'admin can access admin')
assert.equal(checkRoles('user', 'admin'), false, "user can't access admin")
assert.equal(checkRoles(['user'], 'user'), true, '[user] can access user')
assert.equal(checkRoles(['user'], ['user']), true, '[user] can access [user]')
assert.equal(checkRoles('user', ['user']), true, 'user can access [user]')
})

await t.test('all: true', () => {
assert.equal(
checkRoles(['a'], ['a', 'b'], { all: true }),
false,
'all should cause a false return when not all roles are present'
)

assert.equal(
checkRoles(['b', 'a'], ['a', 'b'], { all: true }),
true,
'all should cause a true return when all roles are present'
);

assert.equal(
checkRoles(['ham', 'b', 'x', 'a'], ['a', 'b'], { all: true }),
true,
'all should cause a true return when all roles are present and user has more roles than are needed'
)
});

await t.test('hierarchy', () => {
assert.equal(checkRoles('admin', 'user'), false, "admin can't access user")
assert.equal(
checkRoles('admin', 'user', { hierarchy: ['user', 'admin'] }),
true,
'admin can access user with appropriate hierarchy'
)
});
})
51 changes: 26 additions & 25 deletions test/plugin.test.js
@@ -1,42 +1,43 @@
import assert from 'node:assert/strict';
import { describe, it } from 'node:test';

import fastify from 'fastify'
import tap from 'tap'

import { fastifyAclAuth as plugin } from '../plugin.js'
import { fastifyAclAuth } from '../plugin.js'

/** @type {import('../plugin.js').ActualRolesCallback} */
const actualRoles = () => 'user';
const fastifyAclAuthPlugin = fastifyAclAuth({ actualRoles: () => 'user' });

tap.test('plugin.test.js', async t => {
const defaultPlugin = plugin({ actualRoles })
const fastifyInstance = fastify()
describe('plugin.test.js', () => {
it('should get actual route when accessing user', async () => {
const fastifyInstance = fastify()

t.ok(defaultPlugin, 'plugin exists')
fastifyInstance.register(function (f, _o, n) {
f.register(plugin({ allowedRoles: ['user'], actualRoles }))
f.get('/user', async function () {
return '/user'
})
n()
})
fastifyInstance.register(function (f, _o, n) {
f.register(plugin({ allowedRoles: ['admin'], actualRoles }))
f.get('/admin', async function () {
return '/admin'
fastifyInstance.register(async fastifyScope => {
fastifyScope.register(fastifyAclAuthPlugin, { allowedRoles: 'user' })
fastifyScope.get('/user', () => '/user')
})
n()
})
await t.test('get /user', async t => {

const response = await fastifyInstance.inject({
method: 'GET',
url: '/user',
})
t.is(response.body, '/user', 'body should be /user')

assert.equal(response.body, '/user', 'body should be /user')
})
await t.test('get /admin', async t => {

it('should get 403 when accessing admin', async () => {
const fastifyInstance = fastify()

fastifyInstance.register(async fastifyScope => {
fastifyScope.register(fastifyAclAuthPlugin, { allowedRoles: 'admin' })
fastifyScope.get('/admin', () => '/admin')
})

const response = await fastifyInstance.inject({
method: 'GET',
url: '/admin',
})
t.is(response.statusCode, 403, 'admin should return 403')

assert.equal(response.statusCode, 403, 'admin should return 403')
assert.notEqual(response.body, '/admin', 'body should not be /admin')
})
})

0 comments on commit 7871e87

Please sign in to comment.