This commit is contained in:
2025-12-08 12:15:20 +01:00
parent 1d3bf94b88
commit 428a002e9f
7 changed files with 495 additions and 427 deletions

View File

@@ -1,103 +1,115 @@
import { FastifyInstance } from "fastify";
import fp from "fastify-plugin";
import jwt from "jsonwebtoken";
import { secrets } from "../utils/secrets";
import { FastifyInstance } from "fastify"
import fp from "fastify-plugin"
import jwt from "jsonwebtoken"
import { secrets } from "../utils/secrets"
import {
authUserRoles,
authRolePermissions,
} from "../../db/schema"
import { eq, and } from "drizzle-orm"
export default fp(async (server: FastifyInstance) => {
server.addHook("preHandler", async (req, reply) => {
// 1⃣ Token holen (Header oder Cookie)
const cookieToken = req.cookies?.token;
const authHeader = req.headers.authorization;
const headerToken = authHeader?.startsWith("Bearer ")
? authHeader.slice(7)
: null;
// 1⃣ Token aus Header oder Cookie lesen
const cookieToken = req.cookies?.token
const authHeader = req.headers.authorization
const headerToken =
authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : null
const token =
headerToken && headerToken.length > 10 ? headerToken : cookieToken || null;
/*let token = null
if(headerToken !== null && headerToken.length > 10){
token = headerToken
} else if(cookieToken ){
token = cookieToken
}*/
headerToken && headerToken.length > 10
? headerToken
: cookieToken || null
if (!token) {
return reply.code(401).send({ error: "Authentication required" });
return reply.code(401).send({ error: "Authentication required" })
}
try {
// 2⃣ JWT verifizieren
const payload = jwt.verify(token, secrets.JWT_SECRET!) as {
user_id: string;
email: string;
tenant_id: number;
};
user_id: string
email: string
tenant_id: number | null
}
if (!payload?.user_id) {
return reply.code(401).send({ error: "Invalid token" });
return reply.code(401).send({ error: "Invalid token" })
}
req.user = payload;
// Payload an Request hängen
req.user = payload
if(req.user.tenant_id) {
// 3⃣ Rolle des Nutzers im Tenant laden
const { data: roleData, error: roleError } = await server.supabase
.from("auth_user_roles")
.select("role_id")
.eq("user_id", payload.user_id)
.eq("tenant_id", payload.tenant_id)
.maybeSingle();
if (roleError) {
console.log("Error fetching user role", roleError);
return reply.code(500).send({ error: "Failed to load user role" });
}
if (!roleData) {
return reply.code(403).send({ error: "No role assigned for this tenant" });
}
const roleId = roleData.role_id;
// 4⃣ Berechtigungen der Rolle laden
const { data: permissions, error: permsError } = await server.supabase
.from("auth_role_permissions")
.select("permission")
.eq("role_id", roleId);
if (permsError) {
console.log("Failed to load permissions", permsError);
return reply.code(500).send({ error: "Permission lookup failed" });
}
const perms = permissions?.map((p) => p.permission) ?? [];
// 5⃣ An Request hängen
req.role = roleId;
req.permissions = perms;
req.hasPermission = (perm: string) => perms.includes(perm);
// Multi-Tenant Modus ohne ausgewählten Tenant → keine Rollenprüfung
if (!req.user.tenant_id) {
return
}
const tenantId = req.user.tenant_id
const userId = req.user.user_id
// --------------------------------------------------------
// 3⃣ Rolle des Nutzers im Tenant holen
// --------------------------------------------------------
const roleRows = await server.db
.select()
.from(authUserRoles)
.where(
and(
eq(authUserRoles.user_id, userId),
eq(authUserRoles.tenant_id, tenantId)
)
)
.limit(1)
if (roleRows.length === 0) {
return reply
.code(403)
.send({ error: "No role assigned for this tenant" })
}
const roleId = roleRows[0].role_id
// --------------------------------------------------------
// 4⃣ Berechtigungen der Rolle laden
// --------------------------------------------------------
const permissionRows = await server.db
.select()
.from(authRolePermissions)
.where(eq(authRolePermissions.role_id, roleId))
const permissions = permissionRows.map((p) => p.permission)
// --------------------------------------------------------
// 5⃣ An Request hängen für spätere Nutzung
// --------------------------------------------------------
req.role = roleId
req.permissions = permissions
req.hasPermission = (perm: string) => permissions.includes(perm)
} catch (err) {
return reply.code(401).send({ error: "Invalid or expired token" });
console.error("JWT verification error:", err)
return reply.code(401).send({ error: "Invalid or expired token" })
}
});
});
})
})
// ---------------------------------------------------------------------------
// Fastify TypeScript Erweiterungen
// ---------------------------------------------------------------------------
// 🧩 Fastify Type Declarations
declare module "fastify" {
interface FastifyRequest {
user: {
user_id: string;
email: string;
tenant_id: number;
};
role: string;
permissions: string[];
hasPermission: (permission: string) => boolean;
user_id: string
email: string
tenant_id: number | null
}
role: string
permissions: string[]
hasPermission: (permission: string) => boolean
}
}