From bf4e3110771d36e29d740c2315f558ee539e52da Mon Sep 17 00:00:00 2001 From: yaroslav8765 Date: Thu, 16 Oct 2025 16:19:18 +0300 Subject: [PATCH] feat: add "adminUserAuthorize" hook --- adminforth/servers/express.ts | 30 ++++++++++++++++++++++++++++-- adminforth/types/Back.ts | 18 ++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/adminforth/servers/express.ts b/adminforth/servers/express.ts index b8e38b8d..b732ceb6 100644 --- a/adminforth/servers/express.ts +++ b/adminforth/servers/express.ts @@ -3,12 +3,13 @@ import path from 'path'; import fs from 'fs'; import { Express } from 'express'; import fetch from 'node-fetch'; -import { IAdminForth, IExpressHttpServer } from '../types/Back.js'; +import { AdminUserAuthorizeFunction, IAdminForth, IExpressHttpServer, HttpExtra } from '../types/Back.js'; import { WebSocketServer } from 'ws'; import { WebSocketClient } from './common.js'; import { AdminUser } from '../types/Common.js'; import http from 'http'; import { randomUUID } from 'crypto'; +import { listify } from '../modules/utils.js'; function replaceAtStart(string, substring) { if (string.startsWith(substring)) { @@ -216,6 +217,25 @@ class ExpressServer implements IExpressHttpServer { this.server.listen(...args); } + async processAuthorizeCallbacks(adminUser: AdminUser, toReturn: { error?: string, allowed: boolean }, response: Response, extra: any) { + const adminUserAuthorize = this.adminforth.config.auth.adminUserAuthorize as (AdminUserAuthorizeFunction[] | undefined); + + for (const hook of listify(adminUserAuthorize)) { + const resp = await hook({ + adminUser, + response, + adminforth: this.adminforth, + extra, + }); + if (resp?.allowed === false || resp?.error) { + // delete all items from toReturn and add these: + toReturn.allowed = resp?.allowed; + toReturn.error = resp?.error; + break; + } + } + } + authorize(handler) { return async (req, res, next) => { @@ -248,7 +268,13 @@ class ExpressServer implements IExpressHttpServer { res.status(401).send('Unauthorized by AdminForth'); } else { req.adminUser = adminforthUser; - handler(req, res, next); + const toReturn: { error?: string, allowed: boolean } = { allowed: true }; + await this.processAuthorizeCallbacks(adminforthUser, toReturn, res, {}); + if (!toReturn.allowed) { + res.status(401).send('Unauthorized by AdminForth'); + } else { + handler(req, res, next); + } } }; } diff --git a/adminforth/types/Back.ts b/adminforth/types/Back.ts index 40efe691..035a7b3d 100644 --- a/adminforth/types/Back.ts +++ b/adminforth/types/Back.ts @@ -611,6 +611,19 @@ export type BeforeLoginConfirmationFunction = (params?: { } }>; +/** + * Allow to make extra authorization + */ +export type AdminUserAuthorizeFunction = ((params?: { + adminUser: AdminUser, + response: IAdminForthHttpResponse, + adminforth: IAdminForth, + extra?: HttpExtra, +}) => Promise<{ + error?: string, + allowed?: boolean, +}>); + /** * Data source describes database connection which will be used to fetch data for resources. @@ -1009,6 +1022,11 @@ export interface AdminForthInputConfig { */ beforeLoginConfirmation?: BeforeLoginConfirmationFunction | Array, + /** + * Array of functions which will be called before any request to AdminForth API. + */ + adminUserAuthorize?: AdminUserAuthorizeFunction | Array, + /** * Optionally if your users table has a field(column) with full name, you can set it here. * This field will be used to display user name in the top right corner of the admin panel.