From 152d4440670b7406552e8606517708dcc671e151 Mon Sep 17 00:00:00 2001 From: Sianida26 Date: Mon, 29 Jan 2024 00:51:28 +0700 Subject: [PATCH] Add proper permission check --- src/features/auth/authUtils.ts | 2 + .../auth/tools/checkMultiplePermissions.ts | 40 +++++++-------- src/features/auth/tools/checkPermission.ts | 49 +++++++++++++------ 3 files changed, 56 insertions(+), 35 deletions(-) diff --git a/src/features/auth/authUtils.ts b/src/features/auth/authUtils.ts index 17535e7..bf31119 100644 --- a/src/features/auth/authUtils.ts +++ b/src/features/auth/authUtils.ts @@ -73,6 +73,8 @@ export async function getUserFromToken(token: string) { const user = await prisma.user.findFirst({ include:{ photoProfile: true, + roles: true, + directPermissions: true }, where: { id: decodedToken.id, diff --git a/src/features/auth/tools/checkMultiplePermissions.ts b/src/features/auth/tools/checkMultiplePermissions.ts index afa6fba..e783df0 100644 --- a/src/features/auth/tools/checkMultiplePermissions.ts +++ b/src/features/auth/tools/checkMultiplePermissions.ts @@ -1,31 +1,31 @@ -import { getUserFromToken } from "../authUtils"; +import checkPermission from "./checkPermission"; import getCurrentUser from "./getCurrentUser"; -type PermissionsInput = Record; - -type PermissionsOutput = Record; - /** * Checks multiple permissions for the current user and returns an object indicating * whether each permission is granted. - * - * @param {PermissionsInput} permissions - An object with keys as permission names and values as the required roles/permissions. - * @returns {Promise} An object with keys as permission names and boolean values indicating whether the permission is granted. + * + * @param permissions - An object with keys as permission names and values as the required roles/permissions. + * @returns An object with keys as permission names and boolean values indicating whether the permission is granted. */ -async function checkMultiplePermissions>(permissions: T): Promise<{ [K in keyof T]: boolean }> { - const permissionResults: Partial<{ [K in keyof T]: boolean }> = {}; - const currentUser = await getCurrentUser(); +async function checkMultiplePermissions>( + permissions: T +): Promise<{ [K in keyof T]: boolean }> { + const permissionResults: Partial<{ [K in keyof T]: boolean }> = {}; + const currentUser = await getCurrentUser(); - for (const permissionKey in permissions) { - if (permissions.hasOwnProperty(permissionKey)) { - const requiredPermission = permissions[permissionKey]; - // const isPermissionGranted: boolean = currentUser ? currentUser.roles.includes(requiredPermission) : false; - const isPermissionGranted = true; - permissionResults[permissionKey] = isPermissionGranted; - } - } + for (const permissionKey in permissions) { + if (permissions.hasOwnProperty(permissionKey)) { + const requiredPermission = permissions[permissionKey]; + const isPermissionGranted = await checkPermission( + requiredPermission, + currentUser + ); + permissionResults[permissionKey] = isPermissionGranted; + } + } - return permissionResults as { [K in keyof T]: boolean }; + return permissionResults as { [K in keyof T]: boolean }; } export default checkMultiplePermissions; diff --git a/src/features/auth/tools/checkPermission.ts b/src/features/auth/tools/checkPermission.ts index d73d483..3dd63c2 100644 --- a/src/features/auth/tools/checkPermission.ts +++ b/src/features/auth/tools/checkPermission.ts @@ -1,23 +1,42 @@ -import { cookies } from "next/headers" -import "server-only" -import { getUserFromToken } from "../authUtils"; import getCurrentUser from "./getCurrentUser"; +import "server-only"; -export default async function checkPermission(permission?: "guest-only" | "authenticated-only" | string & {}){ - //TODO: Add permission check +/** + * Checks if the current user has the specified permissions. + * + * @param permission - The specific permission to check. If it's "guest-only", the function returns true if the user is not authenticated. If it's "authenticated-only", it returns true if the user is authenticated. For other permissions, it checks against the user's roles and direct permissions. + * @param currentUser - Optional. The current user object. If not provided, the function retrieves the current user. + * @returns true if the user has the required permission, otherwise false. + */ +export default async function checkPermission( + permission?: "guest-only" | "authenticated-only" | (string & {}), + currentUser?: Awaited> +): Promise { + // Allow if no specific permission is required. + if (!permission) return true; + // Retrieve current user if not provided. + const user = currentUser ?? (await getCurrentUser()); - if (!permission) return true; //allow if no permission supplied + // Handle non-authenticated users. + if (!user) { + return permission === "guest-only"; + } - const user = await getCurrentUser() + // Allow authenticated users if the permission is 'authenticated-only'. + if (permission === "authenticated-only") { + return true; + } - if (!user && permission === "guest-only"){ - return true; - } + // Short-circuit for super-admin role to allow all permissions. + if (user.roles.some((role) => role.code === "super-admin")) return true; - if (user && permission === "authenticated-only"){ - return true; - } + // Aggregate all role codes and direct permissions into a set for efficient lookup. + const permissions = new Set([ + ...user.roles.map((role) => role.code), + ...user.directPermissions.map((dp) => dp.code), + ]); - return true; -} \ No newline at end of file + // Check if the user has the required permission. + return permissions.has(permission); +}