Skip to content

Commit

Permalink
refactor: cleaning up csrf implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
thetutlage committed Mar 5, 2020
1 parent 4b45cf7 commit 214bd53
Show file tree
Hide file tree
Showing 6 changed files with 227 additions and 122 deletions.
37 changes: 15 additions & 22 deletions src/csrf.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* @adonisjs/shield
*
* (c) ? (Please advice before merge. Thanks !)
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
Expand All @@ -22,11 +22,10 @@ import { noop } from './noop'
const Csrf = new Tokens()

/**
* A wrapper around all the functionality
* for handling csrf verification
*
* A wrapper around all the functionality for handling
* csrf verification
*/
export class CsrfMiddleware {
export class CsrfFactory {
/**
* The session instance of the application.
* This would be injected from the
Expand Down Expand Up @@ -94,7 +93,6 @@ export class CsrfMiddleware {

if (!csrfSecret) {
csrfSecret = await Csrf.secret()

this.session.put('csrf-secret', csrfSecret)
}

Expand All @@ -114,20 +112,11 @@ export class CsrfMiddleware {
}

const encryptedToken = request.header('x-xsrf-token')
const unpackedToken = encryptedToken ? unpack(token, this.applicationKey) : null
const unpackedToken = encryptedToken ? unpack(encryptedToken, this.applicationKey) : null

return unpackedToken ? unpackedToken.value : null
}

/**
* Generate a new csrf token using
* the csrf secret extracted
* from session.
*/
public generateCsrfToken (csrfSecret): string {
return Csrf.create(csrfSecret)
}

/**
* Set the xsrf cookie on
* response
Expand Down Expand Up @@ -163,6 +152,15 @@ export class CsrfMiddleware {
})
}

/**
* Generate a new csrf token using
* the csrf secret extracted
* from session.
*/
public generateCsrfToken (csrfSecret: string): string {
return Csrf.create(csrfSecret)
}

/**
* Handle csrf verification. First, get the secret,
* next, check if the request method should be
Expand All @@ -171,21 +169,17 @@ export class CsrfMiddleware {
*/
public async handle (ctx: HttpContextContract): Promise<void> {
const { request } = ctx

const csrfSecret = await this.getCsrfSecret()

if (this.requestMethodShouldEnforceCsrf(request) && this.requestUrlShouldEnforceCsrf(ctx)) {
const csrfToken = this.getCsrfTokenFromRequest(request)

if (!csrfToken || !Csrf.verify(csrfSecret, csrfToken)) {
throw new Exception('Invalid CSRF Token', 403, 'E_BAD_CSRF_TOKEN')
}
}

this.setCsrfToken(ctx, csrfSecret)

this.setXsrfCookie(ctx)

this.shareCsrfViewLocals(ctx)
}
}
Expand All @@ -201,8 +195,7 @@ export function csrf (options: CsrfOptions, applicationKey: string) {
}

return async function csrfMiddlewareFn (ctx: HttpContextContract) {
const csrfMiddleware = new CsrfMiddleware(ctx.session, options, applicationKey)

const csrfMiddleware = new CsrfFactory(ctx.session, options, applicationKey)
return csrfMiddleware.handle(ctx)
}
}
73 changes: 26 additions & 47 deletions test-helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,10 @@
* file that was distributed with this source code.
*/

import { Socket } from 'net'
import { join } from 'path'
import { Ioc } from '@adonisjs/fold'
import { CsrfMiddleware } from '../src/csrf'
import { IncomingMessage } from 'http'
import { Filesystem } from '@poppinss/dev-utils'
import { CsrfOptions } from '@ioc:Adonis/Addons/Shield'
import { IncomingMessage, IncomingHttpHeaders } from 'http'
import { FakeLogger } from '@adonisjs/logger/build/standalone'
import { Profiler } from '@adonisjs/profiler/build/standalone'
import { SessionConfigContract } from '@ioc:Adonis/Addons/Session'
Expand All @@ -22,7 +19,7 @@ import { HttpContext } from '@adonisjs/http-server/build/standalone'
import ViewProvider from '@adonisjs/view/build/providers/ViewProvider'
import { SessionManager } from '@adonisjs/session/build/src/SessionManager'

export const Fs = new Filesystem(join(__dirname, 'views'))
export const fs = new Filesystem(join(__dirname, 'views'))

const logger = new FakeLogger({ level: 'trace', enabled: false, name: 'adonisjs' })
const profiler = new Profiler(__dirname, logger, {})
Expand All @@ -36,42 +33,37 @@ const sessionConfig: SessionConfigContract = {
},
}

export function getCtx () {
return HttpContext.create('/', {}, logger, profiler.create(''), {} as any) as HttpContextContract
}

function getContainerWithViews () {
const container = new Ioc()

const viewProvider = new ViewProvider(container)

viewProvider.register()
const ioc = new Ioc()
const viewProvider = new ViewProvider(ioc)

container.bind('Adonis/Core/Env', () => ({
get () {
return true
},
}))

container.bind('Adonis/Core/Application', () => ({
viewsPath () {
return Fs.basePath
},
}))

return container
}
ioc.bind('Adonis/Core/Env', () => ({
get () {
return true
},
}))

export async function getCtxWithSession (routePath: string = '/', routeParams = {}, request?: IncomingMessage) {
const container = getContainerWithViews()
ioc.bind('Adonis/Core/Application', () => ({
viewsPath () {
return fs.basePath
},
}))
viewProvider.register()

/**
* Returns HTTP context instance
*/
export function getCtx (
routePath: string = '/',
routeParams = {},
request?: IncomingMessage,
) {
HttpContext.getter('session', function session () {
const sessionManager = new SessionManager(container, sessionConfig)

const sessionManager = new SessionManager(ioc, sessionConfig)
return sessionManager.create(this)
}, true)

HttpContext.getter('view', function view () {
return container.use('Adonis/Core/View').share({ request: this.request, route: this.route })
return ioc.use('Adonis/Core/View').share({ request: this.request, route: this.route })
}, true)

const httpContext = HttpContext.create(
Expand All @@ -83,18 +75,5 @@ export async function getCtxWithSession (routePath: string = '/', routeParams =
request
) as HttpContextContract

await httpContext.session.initiate(false)

return httpContext
}

export function getCtxFromIncomingMessage (headers: IncomingHttpHeaders = {}, routePath = '/', routeParams = {}) {
const request = new IncomingMessage(new Socket())
request.headers = headers

return getCtxWithSession(routePath, routeParams, request)
}

export async function getCsrfMiddlewareInstance (options: CsrfOptions, applicationKey: string) {
return new CsrfMiddleware((await getCtxWithSession()).session, options, applicationKey)
}
1 change: 0 additions & 1 deletion test-helpers/views/token-function.edge

This file was deleted.

1 change: 0 additions & 1 deletion test-helpers/views/token-meta.edge

This file was deleted.

1 change: 0 additions & 1 deletion test-helpers/views/token.edge

This file was deleted.

0 comments on commit 214bd53

Please sign in to comment.