Skip to content

Commit

Permalink
feat: create initial policies and abilities files
Browse files Browse the repository at this point in the history
  • Loading branch information
thetutlage committed Dec 5, 2023
1 parent 70acc43 commit 2c0b82a
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 10 deletions.
7 changes: 7 additions & 0 deletions configure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down
26 changes: 26 additions & 0 deletions stubs/abilities.stub
Original file line number Diff line number Diff line change
@@ -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
})
38 changes: 28 additions & 10 deletions stubs/initialize_bouncer_middleware.stub
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand All @@ -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<HttpContext['auth']['user'], undefined>,
typeof abilities,
typeof policies
>
}
}
18 changes: 18 additions & 0 deletions stubs/policies.stub
Original file line number Diff line number Diff line change
@@ -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 = {}
67 changes: 67 additions & 0 deletions tests/configure.spec.ts
Original file line number Diff line number Diff line change
@@ -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()
})

0 comments on commit 2c0b82a

Please sign in to comment.