Added auth and permission check middleware
This commit is contained in:
parent
3aaec5a323
commit
951115fcce
74
apps/backend/src/middlewares/authInfo.ts
Normal file
74
apps/backend/src/middlewares/authInfo.ts
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
import { createMiddleware } from "hono/factory";
|
||||||
|
import HonoEnv from "../types/HonoEnv";
|
||||||
|
import db from "../drizzle";
|
||||||
|
import { users } from "../drizzle/schema/users";
|
||||||
|
import { permissionsToUsers } from "../drizzle/schema/permissionsToUsers";
|
||||||
|
import { rolesToUsers } from "../drizzle/schema/rolesToUsers";
|
||||||
|
import { and, eq, isNull, or } from "drizzle-orm";
|
||||||
|
import { rolesSchema } from "../drizzle/schema/roles";
|
||||||
|
import { permissionsToRoles } from "../drizzle/schema/permissionsToRoles";
|
||||||
|
import { permissionsSchema } from "../drizzle/schema/permissions";
|
||||||
|
import { RoleCode } from "../data/roles";
|
||||||
|
import { SpecificPermissionCode } from "../data/permissions";
|
||||||
|
|
||||||
|
const authInfo = createMiddleware<HonoEnv>(async (c, next) => {
|
||||||
|
const { uid, currentUser } = c.var;
|
||||||
|
|
||||||
|
if (uid && !currentUser) {
|
||||||
|
const user = await db
|
||||||
|
.select()
|
||||||
|
.from(users)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(users.id, uid),
|
||||||
|
eq(users.isEnabled, true),
|
||||||
|
isNull(users.deletedAt)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.leftJoin(
|
||||||
|
permissionsToUsers,
|
||||||
|
eq(permissionsToUsers.userId, users.id)
|
||||||
|
)
|
||||||
|
.leftJoin(rolesToUsers, eq(rolesToUsers.userId, users.id))
|
||||||
|
.leftJoin(rolesSchema, eq(rolesToUsers.roleId, rolesSchema.id))
|
||||||
|
.leftJoin(
|
||||||
|
permissionsToRoles,
|
||||||
|
eq(permissionsToRoles.roleId, rolesSchema.id)
|
||||||
|
)
|
||||||
|
.leftJoin(
|
||||||
|
permissionsSchema,
|
||||||
|
or(
|
||||||
|
eq(permissionsSchema.id, permissionsToUsers.permissionId),
|
||||||
|
eq(permissionsSchema.id, permissionsToRoles.permissionId)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const roles = new Set<RoleCode>();
|
||||||
|
const permissions = new Set<SpecificPermissionCode>();
|
||||||
|
|
||||||
|
user.forEach((user) => {
|
||||||
|
if (user.roles?.code) {
|
||||||
|
roles.add(user.roles.code as RoleCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.permissions?.code) {
|
||||||
|
permissions.add(
|
||||||
|
user.permissions.code as SpecificPermissionCode
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("roles", Array.from(roles));
|
||||||
|
console.log("permissions", Array.from(permissions));
|
||||||
|
|
||||||
|
c.set("currentUser", {
|
||||||
|
name: user[0].users.name,
|
||||||
|
permissions: Array.from(permissions),
|
||||||
|
roles: Array.from(roles),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await next();
|
||||||
|
});
|
||||||
|
|
||||||
|
export default authInfo;
|
||||||
21
apps/backend/src/middlewares/checkPermission.ts
Normal file
21
apps/backend/src/middlewares/checkPermission.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { createMiddleware } from "hono/factory";
|
||||||
|
import { PermissionCode } from "../data/permissions";
|
||||||
|
import HonoEnv from "../types/HonoEnv";
|
||||||
|
import { unauthorized } from "../errors/DashboardError";
|
||||||
|
|
||||||
|
const checkPermission = (...permissions: PermissionCode[]) =>
|
||||||
|
createMiddleware<HonoEnv>(async (c, next) => {
|
||||||
|
if (permissions.includes("*")) await next();
|
||||||
|
else if (c.var.currentUser) {
|
||||||
|
if (
|
||||||
|
c.var.currentUser.permissions.some((p) =>
|
||||||
|
permissions.includes(p)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
await next();
|
||||||
|
} else {
|
||||||
|
unauthorized();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default checkPermission;
|
||||||
|
|
@ -1,15 +1,13 @@
|
||||||
import { Hono } from "hono";
|
import { Hono } from "hono";
|
||||||
import DashboardError from "../../errors/DashboardError";
|
import DashboardError from "../../errors/DashboardError";
|
||||||
|
import authInfo from "../../middlewares/authInfo";
|
||||||
|
import HonoEnv from "../../types/HonoEnv";
|
||||||
|
|
||||||
const devRoutes = new Hono().get("/error", async () => {
|
const devRoutes = new Hono<HonoEnv>()
|
||||||
throw new DashboardError({
|
.use(authInfo)
|
||||||
errorCode: "TEST_ERROR",
|
.get("/middleware", async (c) => {
|
||||||
message: "Test error",
|
return c.json({
|
||||||
severity: "LOW",
|
message: "Middleware works!",
|
||||||
statusCode: 400,
|
|
||||||
formErrors: {
|
|
||||||
someField: "error",
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@ import { rolesToUsers } from "../../drizzle/schema/rolesToUsers";
|
||||||
import { rolesSchema } from "../../drizzle/schema/roles";
|
import { rolesSchema } from "../../drizzle/schema/roles";
|
||||||
import HonoEnv from "../../types/HonoEnv";
|
import HonoEnv from "../../types/HonoEnv";
|
||||||
import requestValidator from "../../utils/requestValidator";
|
import requestValidator from "../../utils/requestValidator";
|
||||||
|
import authInfo from "../../middlewares/authInfo";
|
||||||
|
import checkPermission from "../../middlewares/checkPermission";
|
||||||
|
|
||||||
const userFormSchema = z.object({
|
const userFormSchema = z.object({
|
||||||
name: z.string().min(1).max(255),
|
name: z.string().min(1).max(255),
|
||||||
|
|
@ -41,19 +43,10 @@ const userUpdateSchema = userFormSchema.extend({
|
||||||
});
|
});
|
||||||
|
|
||||||
const usersRoute = new Hono<HonoEnv>()
|
const usersRoute = new Hono<HonoEnv>()
|
||||||
.use(async (c, next) => {
|
.use(authInfo)
|
||||||
const uid = c.get("uid");
|
|
||||||
|
|
||||||
if (uid) {
|
|
||||||
await next();
|
|
||||||
} else {
|
|
||||||
throw new HTTPException(401, {
|
|
||||||
message: "Unauthorized",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.get(
|
.get(
|
||||||
"/",
|
"/",
|
||||||
|
checkPermission("users.readAll"),
|
||||||
requestValidator(
|
requestValidator(
|
||||||
"query",
|
"query",
|
||||||
z.object({
|
z.object({
|
||||||
|
|
@ -86,6 +79,7 @@ const usersRoute = new Hono<HonoEnv>()
|
||||||
//get user by id
|
//get user by id
|
||||||
.get(
|
.get(
|
||||||
"/:id",
|
"/:id",
|
||||||
|
checkPermission("users.readAll"),
|
||||||
requestValidator(
|
requestValidator(
|
||||||
"query",
|
"query",
|
||||||
z.object({
|
z.object({
|
||||||
|
|
@ -144,7 +138,11 @@ const usersRoute = new Hono<HonoEnv>()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
//create user
|
//create user
|
||||||
.post("/", requestValidator("form", userFormSchema), async (c) => {
|
.post(
|
||||||
|
"/",
|
||||||
|
checkPermission("users.create"),
|
||||||
|
requestValidator("form", userFormSchema),
|
||||||
|
async (c) => {
|
||||||
const userData = c.req.valid("form");
|
const userData = c.req.valid("form");
|
||||||
|
|
||||||
const user = await db
|
const user = await db
|
||||||
|
|
@ -178,10 +176,15 @@ const usersRoute = new Hono<HonoEnv>()
|
||||||
},
|
},
|
||||||
201
|
201
|
||||||
);
|
);
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
//update user
|
//update user
|
||||||
.patch("/:id", requestValidator("form", userUpdateSchema), async (c) => {
|
.patch(
|
||||||
|
"/:id",
|
||||||
|
checkPermission("users.update"),
|
||||||
|
requestValidator("form", userUpdateSchema),
|
||||||
|
async (c) => {
|
||||||
const userId = c.req.param("id");
|
const userId = c.req.param("id");
|
||||||
const userData = c.req.valid("form");
|
const userData = c.req.valid("form");
|
||||||
|
|
||||||
|
|
@ -207,11 +210,13 @@ const usersRoute = new Hono<HonoEnv>()
|
||||||
return c.json({
|
return c.json({
|
||||||
message: "User updated successfully",
|
message: "User updated successfully",
|
||||||
});
|
});
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
//delete user
|
//delete user
|
||||||
.delete(
|
.delete(
|
||||||
"/:id",
|
"/:id",
|
||||||
|
checkPermission("users.delete"),
|
||||||
requestValidator(
|
requestValidator(
|
||||||
"form",
|
"form",
|
||||||
z.object({
|
z.object({
|
||||||
|
|
@ -263,7 +268,7 @@ const usersRoute = new Hono<HonoEnv>()
|
||||||
)
|
)
|
||||||
|
|
||||||
//undo delete
|
//undo delete
|
||||||
.patch("/restore/:id", async (c) => {
|
.patch("/restore/:id", checkPermission("users.restore"), async (c) => {
|
||||||
const userId = c.req.param("id");
|
const userId = c.req.param("id");
|
||||||
|
|
||||||
const user = (
|
const user = (
|
||||||
|
|
|
||||||
8
apps/backend/src/types/HonoEnv.d.ts
vendored
8
apps/backend/src/types/HonoEnv.d.ts
vendored
|
|
@ -1,6 +1,14 @@
|
||||||
|
import { SpecificPermissionCode } from "../data/permissions";
|
||||||
|
import { RoleCode } from "../data/roles";
|
||||||
|
|
||||||
type HonoEnv = {
|
type HonoEnv = {
|
||||||
Variables: {
|
Variables: {
|
||||||
uid?: string;
|
uid?: string;
|
||||||
|
currentUser?: {
|
||||||
|
name: string;
|
||||||
|
permissions: SpecificPermissionCode[];
|
||||||
|
roles: RoleCode[];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user