From 2c0b82a97c8aa82b1cb99e8f0a0d6c2b0f373f95 Mon Sep 17 00:00:00 2001 From: Harminder Virk Date: Tue, 5 Dec 2023 21:25:47 +0530 Subject: [PATCH] feat: create initial policies and abilities files --- configure.ts | 7 +++ stubs/abilities.stub | 26 +++++++++ stubs/initialize_bouncer_middleware.stub | 38 ++++++++++---- stubs/policies.stub | 18 +++++++ tests/configure.spec.ts | 67 ++++++++++++++++++++++++ 5 files changed, 146 insertions(+), 10 deletions(-) create mode 100644 stubs/abilities.stub create mode 100644 stubs/policies.stub create mode 100644 tests/configure.spec.ts diff --git a/configure.ts b/configure.ts index 311bec2..98f849c 100644 --- a/configure.ts +++ b/configure.ts @@ -15,6 +15,13 @@ import type Configure from '@adonisjs/core/commands/configure' export async function configure(command: Configure) { const codemods = await command.createCodemods() + /** + * Publish stubs to define abilities and collect + * policies + */ + await command.publishStub('abilities.stub', {}) + await command.publishStub('policies.stub', {}) + /** * Register provider */ diff --git a/stubs/abilities.stub b/stubs/abilities.stub new file mode 100644 index 0000000..f915f6a --- /dev/null +++ b/stubs/abilities.stub @@ -0,0 +1,26 @@ +{{{ + exports({ to: app.makePath('app/abilities/main.ts') }) +}}} +/* +|-------------------------------------------------------------------------- +| Bouncer abilities +|-------------------------------------------------------------------------- +| +| You may export multiple abilities from this file and pre-register them +| when creating the Bouncer instance. +| +| Pre-registered policies and abilities can be referenced as a string by their +| name. Also they are must if want to perform authorization inside Edge +| templates. +| +*/ + +import { Bouncer } from '@adonisjs/bouncer' + +/** + * Delete the following ability to start from + * scratch + */ +export const editUser = Bouncer.ability(() => { + return true +}) diff --git a/stubs/initialize_bouncer_middleware.stub b/stubs/initialize_bouncer_middleware.stub index cf35e0c..7d8d17f 100644 --- a/stubs/initialize_bouncer_middleware.stub +++ b/stubs/initialize_bouncer_middleware.stub @@ -5,9 +5,8 @@ }}} import { policies } from '#policies/main' import * as abilities from '#abilities/main' -import { Bouncer } from '@adonisjs/bouncer' -import emitter from '@adonisjs/core/services/emitter' +import { Bouncer } from '@adonisjs/bouncer' import type { HttpContext } from '@adonisjs/core/http' import type { NextFn } from '@adonisjs/core/types/http' @@ -16,15 +15,34 @@ import type { NextFn } from '@adonisjs/core/types/http' * during an HTTP request */ export default class {{ middlewareName }} { - async handle( - ctx: HttpContext, - next: NextFn, - ) { - const bouncer = Bouncer - .create(() => ctx.auth.user, abilities, policies) - .setContainerResolver(ctx.containerResolver) - .setEmitter(emitter) + async handle(ctx: HttpContext, next: NextFn) { + /** + * Create bouncer instance for the ongoing HTTP request. + * We will pull the user from the HTTP context. + */ + ctx.bouncer = new Bouncer( + () => ctx.auth.user || null, + abilities, + policies + ).setContainerResolver(ctx.containerResolver) + + /** + * Share bouncer helpers with Edge templates. + */ + if ('view' in ctx) { + ctx.view.share(ctx.bouncer.edgeHelpers) + } return next() } } + +declare module '@adonisjs/core/http' { + export interface HttpContext { + bouncer: Bouncer< + Exclude, + typeof abilities, + typeof policies + > + } +} diff --git a/stubs/policies.stub b/stubs/policies.stub new file mode 100644 index 0000000..fde6b47 --- /dev/null +++ b/stubs/policies.stub @@ -0,0 +1,18 @@ +{{{ + exports({ to: app.policiesPath('main.ts') }) +}}} +/* +|-------------------------------------------------------------------------- +| Bouncer policies +|-------------------------------------------------------------------------- +| +| You may define a collection of policies inside this file and pre-register +| them when creating a new bouncer instance. +| +| Pre-registered policies and abilities can be referenced as a string by their +| name. Also they are must if want to perform authorization inside Edge +| templates. +| +*/ + +export const policies = {} diff --git a/tests/configure.spec.ts b/tests/configure.spec.ts new file mode 100644 index 0000000..3daac73 --- /dev/null +++ b/tests/configure.spec.ts @@ -0,0 +1,67 @@ +/* + * @adonisjs/session + * + * (c) AdonisJS + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import { test } from '@japa/runner' +import { fileURLToPath } from 'node:url' +import { IgnitorFactory } from '@adonisjs/core/factories' +import Configure from '@adonisjs/core/commands/configure' + +import { stubsRoot } from '../index.js' +const BASE_URL = new URL('./tmp/', import.meta.url) + +test.group('Configure', (group) => { + group.each.setup(({ context }) => { + context.fs.baseUrl = BASE_URL + context.fs.basePath = fileURLToPath(BASE_URL) + }) + + test('register provider and publish stubs', async ({ fs, assert }) => { + const ignitor = new IgnitorFactory() + .withCoreProviders() + .withCoreConfig() + .create(BASE_URL, { + importer: (filePath) => { + if (filePath.startsWith('./') || filePath.startsWith('../')) { + return import(new URL(filePath, BASE_URL).href) + } + + return import(filePath) + }, + }) + + await fs.createJson('tsconfig.json', {}) + await fs.create('start/kernel.ts', `router.use([])`) + await fs.create('adonisrc.ts', `export default defineConfig({}) {}`) + + const app = ignitor.createApp('web') + await app.init() + await app.boot() + + const ace = await app.container.make('ace') + const command = await ace.create(Configure, ['../../index.js']) + await command.exec() + + const stubsManager = await app.stubs.create() + const abilitiesStub = await stubsManager + .build('abilities.stub', { source: stubsRoot }) + .then((stub) => stub.prepare({})) + + const policiesStub = await stubsManager + .build('policies.stub', { source: stubsRoot }) + .then((stub) => stub.prepare({})) + + await assert.fileContains('adonisrc.ts', '@adonisjs/bouncer/bouncer_provider') + await assert.fileContains('app/abilities/main.ts', abilitiesStub.contents) + await assert.fileContains('app/policies/main.ts', policiesStub.contents) + await assert.fileContains( + 'app/middleware/initialize_bouncer_middleware.ts', + `export default class InitializeBouncerMiddleware {` + ) + }).disableTimeout() +})