Skip to content

Latest commit

 

History

History
350 lines (296 loc) · 10.1 KB

File metadata and controls

350 lines (296 loc) · 10.1 KB

Audit Log

Guide to the Audit Log plugin, including installation, log table setup, resource wiring, and tracking of admin changes, custom actions, and client metadata.

AuditLog plugin allows to log all changes in the resources done from the admin panel. It will allow you to figure out who and when made changes in the data. Requires separate table in the database to store logs.

Installation

pnpm add @adminforth/audit-log --save

Create auditLogs.ts in resources folder:

import AuditLogPlugin from '@adminforth/audit-log';
import { AdminForthDataTypes } from 'adminforth'

Getting Started will be used as base for this example.

Creating table for storing activity data

For the first, to track records changes, we need to set up the database and table with certain fields inside where tracked data will be stored.

First of all you should create this table in your own database ./schema.prisma:

model audit_logs {
  id                String      @id
  created_at        DateTime  /// timestamp of applied change
  resource_id       String    /// identifier of resource where change were applied
  user_id           String    /// identifier of user who made the changes
  action            String    /// type of change (create, edit, delete)
  diff              String?   /// delta betwen before/after versions
  record_id         String?   /// identifier of record that been changed
}

And prisma migrate:

pnpm makemigration --name add-audit-logs ; pnpm migrate:local

Also to make this code start

Setting up the resource and dataSource for plugin

Logger sets up for all the resources by default. But you can exclude unwanted resources with option "excludeResourceIds". In this example, we'll exclude resource "adminuser" from logging.

Also, it excludes itself to avoid infinte logging loop.

Add this code in auditLogs.ts:

import AuditLogPlugin from "@adminforth/audit-log/index.js";
import { AdminForthDataTypes } from "adminforth";
import { randomUUID } from "crypto";

export default {
  dataSource: 'maindb', 
  table: 'audit_logs',
  columns: [
    { name: 'id', primaryKey: true, required: false, fillOnCreate: ({initialRecord}: any) => randomUUID(),
      showIn: {
        list: false,
        edit: false,
        create: false,
        filter: false,
      } },
    { name: 'created_at', required: false },
    { name: 'resource_id', required: false },
    { name: 'user_id', required: false, 
        foreignResource: {
        resourceId: 'adminuser',
      } },
    { name: 'action', required: false },
    { name: 'diff', required: false, type: AdminForthDataTypes.JSON, showIn: {
        list: false,
        edit: false,
        create: false,
        filter: false,
      } },
    { name: 'record_id', required: false },
  ],
  options: {
    allowedActions: {
      edit: false,
      delete: false,
      create: false
    }
  },
  plugins: [
    new AuditLogPlugin({
      // if you want to exclude some resources from logging
      //excludeResourceIds: ['adminuser'],
      resourceColumns: {
        resourceIdColumnName: 'resource_id',
        resourceActionColumnName: 'action',
        resourceDataColumnName: 'diff',
        resourceUserIdColumnName: 'user_id',
        resourceRecordIdColumnName: 'record_id',
        resourceCreatedColumnName: 'created_at'
      }
    }),
  ],
}

Then you need to import ./resources/auditLogs:

//diff-add
import auditLogsResource from "./resources/auditLogs"

... new AdminForth({
  dataSources: [...],
    ...
  resources: [
    apartmentsResource,
    usersResource,
//diff-add
    auditLogsResource
  ],
    ...
]

Also, we need to add it to menu:

menu: [
  ...
//diff-add
  {
//diff-add
      label: 'Audit Logs',
//diff-add
      icon: 'flowbite:search-outline',
//diff-add
      resourceId: 'audit_logs',
//diff-add
  }
]

That's it! Now you can see the logs in the table

alt text

Logging custom actions

Audit log is able to catch only standard actions like create, update, delete or custom bulk actions.

If you have a custom, self coded actions in your API, you can log them by calling logCustomAction method of AuditLogPlugin instance:

import type { IAdminUserExpressRequest, ITranslateExpressRequest } from 'adminforth';
import express from 'express';

....

app.get(`${ADMIN_BASE_URL}/api/dashboard/`,
  admin.express.authorize(
    async (req: IAdminUserExpressRequest, res: express.Response) => {

      admin.getPluginByClassName<AuditLogPlugin>('AuditLogPlugin').logCustomAction({
        resourceId: 'aparts',
        recordId: null, // recordId can be null if not applicable
        actionId: 'visitedDashboard', // any random string you want to useto identify this action 
        oldData: null, // old data, can be null if not applicable
        data: { dashboard: 'main' }, // new data or any data you want to log
        user: req.adminUser,
        headers: req.headers //required if you want log client ip
      })

      ....

Logging client ip address

Audit log can also log the client's IP address if needed.

First, you need to migrate the audit_logs table in ./schema.prisma:

model audit_logs {
  id                String      @id
  created_at        DateTime  /// timestamp of applied change
  resource_id       String    /// identifier of resource where change were applied
  user_id           String    /// identifier of user who made the changes
  action            String    /// type of change (create, edit, delete)
  diff              String?   /// delta betwen before/after versions
  record_id         String?   /// identifier of record that been changed
//diff-add
  ip_address        String?   /// client ip address
}

And prisma migrate:

pnpm makemigration --name add-ip-address-to-audit-logs ; pnpm migrate:local

Also, update the resource configuration in ./resources/auditLogs.ts:

  export default {
    dataSource: 'maindb', 
    table: 'audit_logs',
    columns: [
      ...
      { name: 'action', required: false },
      { name: 'diff', required: false, type: AdminForthDataTypes.JSON, showIn: {
          list: false,
          edit: false,
          create: false,
          filter: false,
        } },
      { name: 'record_id', required: false },
      //diff-add
      { name: 'ip_address', required: false },
    ],
    ...
    plugins: [
      new AuditLogPlugin({
        resourceColumns: {
          resourceIdColumnName: 'resource_id',
          resourceActionColumnName: 'action',
          resourceDataColumnName: 'diff',
          resourceUserIdColumnName: 'user_id',
          resourceRecordIdColumnName: 'record_id',
          resourceCreatedColumnName: 'created_at'
//diff-add
          resourceIpColumnName: "ip_address",
        }
      }),
    ],
  }

Logging client ip country

Audit log can also log the client's country if needed.

First, you need to migrate the audit_logs table in ./schema.prisma:

model audit_logs {
  id                String      @id
  created_at        DateTime  /// timestamp of applied change
  resource_id       String    /// identifier of resource where change were applied
  user_id           String    /// identifier of user who made the changes
  action            String    /// type of change (create, edit, delete)
  diff              String?   /// delta betwen before/after versions
  record_id         String?   /// identifier of record that been changed
  ip_address        String?   /// client ip address
//diff-add
  country           String?   /// client country

//diff-add
  @@index([ip_address])       /// index for fast lookups by IP
}

And prisma migrate:

pnpm makemigration --name add-ip-address-to-audit-logs ; pnpm migrate:local

Update the resource configuration in ./resources/auditLogs.ts:

  export default {
    dataSource: 'maindb', 
    table: 'audit_logs',
    columns: [
      ...
      { name: 'action', required: false },
      { name: 'diff', required: false, type: AdminForthDataTypes.JSON, showIn: {
          list: false,
          edit: false,
          create: false,
          filter: false,
        } },
      { name: 'record_id', required: false },
      { name: 'ip_address', required: false },
       //diff-add
      { 
        //diff-add
        name: "country",
        //diff-add
        required: false,
        //diff-add
        components: {
        //diff-add
          list: '@/renderers/CountryFlag.vue'
        //diff-add
          show: '@/renderers/CountryFlag.vue'
        //diff-add
        }, 
         //diff-add
      },
    ],
    ...
    plugins: [
      new AuditLogPlugin({
        resourceColumns: {
          resourceIdColumnName: 'resource_id',
          resourceActionColumnName: 'action',
          resourceDataColumnName: 'diff',
          resourceUserIdColumnName: 'user_id',
          resourceRecordIdColumnName: 'record_id',
          resourceCreatedColumnName: 'created_at'
          resourceIpColumnName: "ip_address",
//diff-add
          resourceCountryColumnName: "country",
        }
      }),
    ],
  }

Providing Country Headers

If your deployed app has header with user country in ISO 3166-1 alpha-2 format, you can specify this header, so country will be taken from it:

plugins: [
      new AuditLogPlugin({
        //diff-add
        isoCountryCodeRequestHeader: 'CF-IPCountry',
        resourceColumns: {
          resourceIdColumnName: 'resource_id',
          resourceActionColumnName: 'action',
          resourceDataColumnName: 'diff',
          resourceUserIdColumnName: 'user_id',
          resourceRecordIdColumnName: 'record_id',
          resourceCreatedColumnName: 'created_at'
          resourceIpColumnName: "ip_address",
          resourceCountryColumnName: "country",
        }
      }),
    ],