refactor: extracted auth as middleware to be added manually to endpoints

This commit is contained in:
dswbx
2025-01-07 13:32:50 +01:00
parent 064bbba8aa
commit 7d3d1e811f
13 changed files with 211 additions and 178 deletions

View File

@@ -1,11 +1,11 @@
/** @jsxImportSource hono/jsx */
import type { App } from "App";
import { type ClassController, isDebug } from "core";
import { isDebug } from "core";
import { addFlashMessage } from "core/server/flash";
import { Hono } from "hono";
import { html } from "hono/html";
import { Fragment } from "hono/jsx";
import { Controller } from "modules/Controller";
import * as SystemPermissions from "modules/permissions";
const htmlBkndContextReplace = "<!-- BKND_CONTEXT -->";
@@ -17,11 +17,13 @@ export type AdminControllerOptions = {
forceDev?: boolean | { mainPath: string };
};
export class AdminController implements ClassController {
export class AdminController extends Controller {
constructor(
private readonly app: App,
private options: AdminControllerOptions = {}
) {}
) {
super();
}
get ctx() {
return this.app.modules.ctx();
@@ -32,19 +34,16 @@ export class AdminController implements ClassController {
}
private withBasePath(route: string = "") {
return (this.basepath + route).replace(/\/+$/, "/");
return (this.basepath + route).replace(/(?<!:)\/+/g, "/");
}
getController(): Hono<any> {
override getController() {
const hono = this.create().basePath(this.withBasePath());
const auth = this.app.module.auth;
const configs = this.app.modules.configs();
// if auth is not enabled, authenticator is undefined
const auth_enabled = configs.auth.enabled;
const hono = new Hono<{
Variables: {
html: string;
};
}>().basePath(this.withBasePath());
const authRoutes = {
root: "/",
success: configs.auth.cookie.pathSuccess ?? "/",
@@ -80,8 +79,7 @@ export class AdminController implements ClassController {
return c.redirect(authRoutes.success);
}
const html = c.get("html");
return c.html(html);
return c.html(c.get("html")!);
});
hono.get(authRoutes.logout, async (c) => {
@@ -96,8 +94,7 @@ export class AdminController implements ClassController {
return c.redirect(authRoutes.login);
}
const html = c.get("html");
return c.html(html);
return c.html(c.get("html")!);
});
return hono;

View File

@@ -1,10 +1,11 @@
/// <reference types="@cloudflare/workers-types" />
import type { App } from "App";
import type { ClassController } from "core";
import { tbValidator as tb } from "core";
import { StringEnum, Type, TypeInvalidError } from "core/utils";
import { type Context, Hono } from "hono";
import type { Context, Hono } from "hono";
import { Controller } from "modules/Controller";
import {
MODULE_NAMES,
type ModuleConfigs,
@@ -27,21 +28,20 @@ export type ConfigUpdateResponse<Key extends ModuleKey = ModuleKey> =
| ConfigUpdate<Key>
| { success: false; type: "type-invalid" | "error" | "unknown"; error?: any; errors?: any };
export class SystemController implements ClassController {
constructor(private readonly app: App) {}
export class SystemController extends Controller {
constructor(private readonly app: App) {
super();
}
get ctx() {
return this.app.modules.ctx();
}
private registerConfigController(client: Hono<any>): void {
const hono = new Hono();
const hono = this.create();
const { permission } = this.middlewares;
/*hono.use("*", async (c, next) => {
//this.ctx.guard.throwUnlessGranted(SystemPermissions.configRead);
console.log("perm?", this.ctx.guard.hasPermission(SystemPermissions.configRead));
return next();
});*/
hono.use(permission(SystemPermissions.configRead));
hono.get(
"/:module?",
@@ -57,7 +57,6 @@ export class SystemController implements ClassController {
const { secrets } = c.req.valid("query");
const { module } = c.req.valid("param");
this.ctx.guard.throwUnlessGranted(SystemPermissions.configRead);
secrets && this.ctx.guard.throwUnlessGranted(SystemPermissions.configReadSecrets);
const config = this.app.toJSON(secrets);
@@ -96,6 +95,7 @@ export class SystemController implements ClassController {
hono.post(
"/set/:module",
permission(SystemPermissions.configWrite),
tb(
"query",
Type.Object({
@@ -107,8 +107,6 @@ export class SystemController implements ClassController {
const { force } = c.req.valid("query");
const value = await c.req.json();
this.ctx.guard.throwUnlessGranted(SystemPermissions.configWrite);
return await handleConfigUpdateResponse(c, async () => {
// you must explicitly set force to override existing values
// because omitted values gets removed
@@ -131,14 +129,12 @@ export class SystemController implements ClassController {
}
);
hono.post("/add/:module/:path", async (c) => {
hono.post("/add/:module/:path", permission(SystemPermissions.configWrite), async (c) => {
// @todo: require auth (admin)
const module = c.req.param("module") as any;
const value = await c.req.json();
const path = c.req.param("path") as string;
this.ctx.guard.throwUnlessGranted(SystemPermissions.configWrite);
const moduleConfig = this.app.mutateConfig(module);
if (moduleConfig.has(path)) {
return c.json({ success: false, path, error: "Path already exists" }, { status: 400 });
@@ -155,14 +151,12 @@ export class SystemController implements ClassController {
});
});
hono.patch("/patch/:module/:path", async (c) => {
hono.patch("/patch/:module/:path", permission(SystemPermissions.configWrite), async (c) => {
// @todo: require auth (admin)
const module = c.req.param("module") as any;
const value = await c.req.json();
const path = c.req.param("path");
this.ctx.guard.throwUnlessGranted(SystemPermissions.configWrite);
return await handleConfigUpdateResponse(c, async () => {
await this.app.mutateConfig(module).patch(path, value);
return {
@@ -173,14 +167,12 @@ export class SystemController implements ClassController {
});
});
hono.put("/overwrite/:module/:path", async (c) => {
hono.put("/overwrite/:module/:path", permission(SystemPermissions.configWrite), async (c) => {
// @todo: require auth (admin)
const module = c.req.param("module") as any;
const value = await c.req.json();
const path = c.req.param("path");
this.ctx.guard.throwUnlessGranted(SystemPermissions.configWrite);
return await handleConfigUpdateResponse(c, async () => {
await this.app.mutateConfig(module).overwrite(path, value);
return {
@@ -191,13 +183,11 @@ export class SystemController implements ClassController {
});
});
hono.delete("/remove/:module/:path", async (c) => {
hono.delete("/remove/:module/:path", permission(SystemPermissions.configWrite), async (c) => {
// @todo: require auth (admin)
const module = c.req.param("module") as any;
const path = c.req.param("path")!;
this.ctx.guard.throwUnlessGranted(SystemPermissions.configWrite);
return await handleConfigUpdateResponse(c, async () => {
await this.app.mutateConfig(module).remove(path);
return {
@@ -211,13 +201,15 @@ export class SystemController implements ClassController {
client.route("/config", hono);
}
getController(): Hono {
const hono = new Hono();
override getController() {
const hono = this.create();
const { permission } = this.middlewares;
this.registerConfigController(hono);
hono.get(
"/schema/:module?",
permission(SystemPermissions.schemaRead),
tb(
"query",
Type.Object({
@@ -228,7 +220,7 @@ export class SystemController implements ClassController {
async (c) => {
const module = c.req.param("module") as ModuleKey | undefined;
const { config, secrets } = c.req.valid("query");
this.ctx.guard.throwUnlessGranted(SystemPermissions.schemaRead);
config && this.ctx.guard.throwUnlessGranted(SystemPermissions.configRead);
secrets && this.ctx.guard.throwUnlessGranted(SystemPermissions.configReadSecrets);
@@ -300,8 +292,7 @@ export class SystemController implements ClassController {
return c.json({
version: this.app.version(),
test: 2,
// @ts-ignore
app: !!c.var.app
app: c.get("app").version()
});
});