Manuel-Antunes / adonis5-audit

A functional audit library for Adonisjs V5 πŸ”– - embend into your base model and audit everthing

Home Page:https://www.npmjs.com/package/adonis5-audit

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Table of contents

πŸ”– Adonis5 Audit

test

Functional helper functions to Audit Lucid Models with easily! After setup the steps below you only need call that

await MyAuditedModel.save({ ctx })

And voila your model has been audited.

How to use

  1. Install the package npm i adonis5-audit

  2. Copy migration file, add provider to your project node ace invoke adonis5-audit

  3. Define your Audit model like repo sample ./templates/Audit.txt

  4. Add it on your models replacing save() and delete() implementations and adding the auxiliary methods superSave and superDelete used by reference

    Unfourtunely because some issues during development we cant make it works out of box as mixin, because during db:seed commands the typescript import causes some errors like saying that imported Audit model is a undefined type, so to avoid this we need import our models manualy to the lib we recommend create a "BaseModel" fo your project

// you need this to extend your models
import { compose } from '@ioc:Adonis/Core/Helpers'
import { auditSave, auditDelete, createAudit } from '@ioc:Adonis/Addons/Audit'
class MyCRUDModel extends BaseModel {
// we need this to call the BaseModel original behaviour
// when dont have changes in code
  public superSave(): any {
    return super.save()
  }
  public superDelete(): any { // and this too
    return super.delete()
  }

  /** @ts-ignore */
  public async save(param?: { ctx }): Promise<this> {
// you could use HttpContext.get() here !!
    const Audit = (await import('../Audit')).default // this type of import avoid weird behaviour
    return auditSave(this, arguments, Audit)
  }
  /** @ts-ignore */
  public async delete(param?: { ctx }): Promise<void> {
// you could use HttpContext.get() here !!
    const Audit = (await import('App/Models/Audit')).default // this type of import avoid weird behaviour
    return auditSave(this, arguments, Audit)
  }

}


export default class MyAuditedModel extends CRUDModel { // now all we need is inherit from CRUDModel in all our classes
    // ... your custom implementation
}
  1. Audit your model from anywhere sending the HttpContext object !! (you could try use HttpContext.get() and enabling useAsyncLocalStorage: true,it onapp.ts` to get current request context (but i prefer pass explicitly))
export default class MyAuditedModelController {

  public async store(ctx: HttpContextContract) {
    const { request } = ctx
    const auditedModel = new MyAuditedModel()
    auditedModel.cleanAndMerge(request.body()) // <= custom function
    await auditedModel.save({ ctx }) // <= This will make an CREATE event entry on audits, since model was not saved before
    await auditedModel.load('storehouse')
    return auditedModel
  }

  public async update(ctx: HttpContextContract) {
    const { request } = ctx
    const auditedModel = await MyAuditedModel.findOrFail(request.params().id)
    await auditedModel.cleanAndMerge(request.body()) // <= custom Code ehehe

    await auditedModel.save({ ctx }) // <= This will make an UPDATE event entry on audits table
    return auditedModel
  }

  public async destroy(ctx: HttpContextContract) {
   const { request } = ctx // <= We need the ctx here because the lib use it to log IP Addresses, User and more !
   const auditedModel = await MyAuditedModel.findOrFail(request.params().id)
   await auditedModel.delete({ ctx }) // <- this will make an DELETE event entry on audits table
   return auditedModel
  }
}

Oh you can audit custom events also (they are strings)!!

import { auditSave, auditDelete, createAudit } from '@ioc:Adonis/Addons/Audit'
// we recommend define your app custom events
// in some centralized file as Enum
// this will ensure dont have event name change
import { CustomAuditEvents } from '<you_types:App/Types/AuditEvents>'

// the following code will create your custom event .toString()
await createAudit({
  auth: ctx.auth,
  request,
  event: 'CUSTOM_INTEGRATION', // this should be an String
  auditable_id: manifest.id,
  auditable: 'Manifest',
  newData: customData.toJSON(),
  auditType: Audit,
})

Template Files

Audit Migration Example

import BaseSchema from '@ioc:Adonis/Lucid/Schema'
export default class Audits extends BaseSchema {
  protected tableName = 'audits'

  public async up() {
    this.schema.createTable(this.tableName, (table) => {
      table.increments('id')

      // User id from ctx auth obj
      table
        .integer('user_id')
        .unsigned()
        .nullable()
        .defaultTo(null)
        .references('id')
        .inTable('users')
        .onDelete('SET NULL')
      // Entity Id
      table.string('auditable_id').nullable()
      // Entity Name
      table.string('auditable').nullable()
      // Event name saved
      table.string('event').nullable()
      // IP from ctx
      table.string('ip').nullable()
      // URL request from ctx
      table.string('url').nullable()
      // Data Before
      table.json('old_data').nullable()
      // Data After
      table.json('new_data').nullable()

      table.timestamp('deleted_at', { useTz: true }).nullable().defaultTo(null)
      table.timestamps(true, true)
    })
  }

  public async down() {
    this.schema.dropTable(this.tableName)
  }
}

Audit Model Example

import { BaseModel, BelongsTo, belongsTo, column } from '@ioc:Adonis/Lucid/Orm'
import { DateTime } from 'luxon'

import User from './User'

export default class Audit extends BaseModel {
  @column({ isPrimary: true })
  public id: number

  @column()
  public userId: number | null = null
  /** User Id */
  @belongsTo(() => User, {
    foreignKey: 'userId',
  })
  public user: BelongsTo<typeof User>

  /** Entity Id */
  @column()
  public auditableId: string | null

  /** Entity Name */
  @column()
  public auditable: string | null

  /** Event Name */
  @column()
  public event: string

  /** IP from CTX */
  @column()
  public ip: string | null

  /** URL from CTX */
  @column()
  public url: string | null

  /** toJson() Data before*/
  @column()
  public oldData: any

  /** toJson() Data After*/
  @column()
  public newData: any

  @column.dateTime({ autoCreate: true })
  public createdAt: DateTime

  @column.dateTime({ autoCreate: true, autoUpdate: true })
  public updatedAt: DateTime
}

About

A functional audit library for Adonisjs V5 πŸ”– - embend into your base model and audit everthing

https://www.npmjs.com/package/adonis5-audit

License:MIT License


Languages

Language:TypeScript 75.8%Language:HTML 17.0%Language:JavaScript 7.2%