diff --git a/sigap-website/app/(pages)/(admin)/dashboard/user-management/_components/table/cells/actions-cell.tsx b/sigap-website/app/(pages)/(admin)/dashboard/user-management/_components/table/cells/actions-cell.tsx index 67a0041..a0333d1 100644 --- a/sigap-website/app/(pages)/(admin)/dashboard/user-management/_components/table/cells/actions-cell.tsx +++ b/sigap-website/app/(pages)/(admin)/dashboard/user-management/_components/table/cells/actions-cell.tsx @@ -15,10 +15,10 @@ import { IUserSchema } from "@/src/entities/models/users/users.model" import { useUserActionsHandler } from "../../../_handlers/actions/use-user-actions" import { BanUserDialog } from "../../dialogs/ban-user-dialog" import { useCreateUserColumn } from "../../../_handlers/use-create-user-column" -import { useCheckPermissionsHandler } from "@/app/(pages)/(auth)/_handlers/use-check-permissions" -import { useCheckPermissionsNewQuery } from "@/app/(pages)/(auth)/_queries/mutations" + import { useGetCurrentUserQuery } from "../../../_queries/queries" import { Badge } from "@/app/_components/ui/badge" +import { useCheckPermissionsQuery } from "@/app/(pages)/(auth)/_queries/queries" interface ActionsCellProps { user: IUserSchema @@ -43,12 +43,12 @@ export const ActionsCell: React.FC = ({ user, onUpdate }) => { setSelectedUser, } = useCreateUserColumn() - const { data: currentUser, isPending } = useGetCurrentUserQuery() + const { data: currentUser } = useGetCurrentUserQuery() if (!currentUser) return user not found - let { data: isAllowedToDelete } = useCheckPermissionsNewQuery(currentUser.email, "delete", "users") - let { data: isAllowedToUpdate } = useCheckPermissionsNewQuery(currentUser.email, "update", "users") + let { data: isAllowedToDelete } = useCheckPermissionsQuery(currentUser.email, "delete", "users") + let { data: isAllowedToUpdate } = useCheckPermissionsQuery(currentUser.email, "update", "users") return (
e.stopPropagation()}> @@ -76,7 +76,7 @@ export const ActionsCell: React.FC = ({ user, onUpdate }) => { Delete )} - {isAllowedToUpdate && user.banned_until != null && ( + {isAllowedToUpdate && ( { if (user.banned_until != null) { diff --git a/sigap-website/app/(pages)/(auth)/_handlers/use-check-permissions.ts b/sigap-website/app/(pages)/(auth)/_handlers/use-check-permissions.ts index d3d8c36..e69de29 100644 --- a/sigap-website/app/(pages)/(auth)/_handlers/use-check-permissions.ts +++ b/sigap-website/app/(pages)/(auth)/_handlers/use-check-permissions.ts @@ -1,14 +0,0 @@ -import { useCheckPermissionsMutation } from "../_queries/mutations" - -export const useCheckPermissionsHandler = () => { - const { mutateAsync: checkPermissions, isPending } = useCheckPermissionsMutation() - - const handleCheckPermissions = async (userId: string, action: string, resource: string) => { - return await checkPermissions({ userId, action, resource }) - } - - return { - checkPermissions: handleCheckPermissions, - isPending, - } -} \ No newline at end of file diff --git a/sigap-website/app/(pages)/(auth)/_queries/mutations.ts b/sigap-website/app/(pages)/(auth)/_queries/mutations.ts index 7fff4c2..3b23d39 100644 --- a/sigap-website/app/(pages)/(auth)/_queries/mutations.ts +++ b/sigap-website/app/(pages)/(auth)/_queries/mutations.ts @@ -1,5 +1,5 @@ import { useMutation, useQueries, useQuery } from "@tanstack/react-query" -import { checkPermissionNew, checkPermissions, sendMagicLink, sendPasswordRecovery, signInPasswordless, signInWithPassword, signOut, verifyOtp } from "../action" +import { checkPermissions, sendMagicLink, sendPasswordRecovery, signInPasswordless, signInWithPassword, signOut, verifyOtp } from "../action" export const useSignInPasswordlessMutation = () => { return useMutation({ @@ -42,17 +42,3 @@ export const useVerifyOtpMutation = () => { mutationFn: async (formData: FormData) => await verifyOtp(formData), }) } - -export const useCheckPermissionsMutation = () => { - return useMutation({ - mutationKey: ["check-permissions"], - mutationFn: async ({ userId, action, resource }: { userId: string; action: string; resource: string }) => await checkPermissions(userId, action, resource), - }) -} - -export const useCheckPermissionsNewQuery = (email: string, action: string, resource: string) => { - return useQuery({ - queryKey: ["check-permissions", email, action, resource], - queryFn: async () => await checkPermissionNew(email, action, resource), - }) -} diff --git a/sigap-website/app/(pages)/(auth)/_queries/queries.ts b/sigap-website/app/(pages)/(auth)/_queries/queries.ts new file mode 100644 index 0000000..64b54f5 --- /dev/null +++ b/sigap-website/app/(pages)/(auth)/_queries/queries.ts @@ -0,0 +1,9 @@ +import { useQuery } from "@tanstack/react-query" +import { checkPermissions } from "../action" + +export const useCheckPermissionsQuery = (email: string, action: string, resource: string) => { + return useQuery({ + queryKey: ["check-permissions", email, action, resource], + queryFn: async () => await checkPermissions(email, action, resource), + }) +} diff --git a/sigap-website/app/(pages)/(auth)/action.ts b/sigap-website/app/(pages)/(auth)/action.ts index 145fa9e..fbc23b1 100644 --- a/sigap-website/app/(pages)/(auth)/action.ts +++ b/sigap-website/app/(pages)/(auth)/action.ts @@ -229,82 +229,42 @@ export async function sendPasswordRecovery(email: string) { }) } -export async function checkPermissions(userId: string, action: string, resource: string) { - const instrumentationService = getInjection("IInstrumentationService") - return await instrumentationService.instrumentServerAction("checkPermissions", { - recordResponse: true - }, async () => { - try { - - const user = await db.users.findUnique({ - where: { id: userId }, - include: { - role: true - } - }) - - console.log("Checking permissions for user:", user?.role.name, "action:", action, "resource:", resource) - - const permission = await db.permissions.findFirst({ - where: { - role_id: user?.role.id, - action: action, - resource: { - name: resource - } - } - }) - - return !!permission - - } catch (err) { - if (err instanceof InputParseError) { - return { error: err.message } - } - - const crashReporterService = getInjection("ICrashReporterService") - crashReporterService.report(err) - - return { - error: "An error occurred during permissions check. Please try again later.", - } - } - }) -} - -export async function checkPermissionNew(email: string, action: string, resource: string) { +export async function checkPermissions(email: string, action: string, resource: string) { const instrumentationService = getInjection("IInstrumentationService") return await instrumentationService.instrumentServerAction("checkPermissionNew", { recordResponse: true }, async () => { try { - const user = await db.users.findUnique({ - where: { email }, - include: { role: true } - }) + // const user = await db.users.findUnique({ + // where: { email }, + // include: { role: true } + // }) - if (!user) { - return { error: "User not found" } - } + // if (!user) { + // return { error: "User not found" } + // } - console.log("Checking permissions for user:", user?.role.name, "action:", action, "resource:", resource) + // console.log("Checking permissions for user:", user?.role.name, "action:", action, "resource:", resource) - const permission = await db.permissions.findFirst({ - where: { - role_id: user.role.id, - action: action, - resource: { - name: resource - } - } - }) + // const permission = await db.permissions.findFirst({ + // where: { + // role_id: user.role.id, + // action: action, + // resource: { + // name: resource + // } + // } + // }) - if (!permission) { - return false - } + // if (!permission) { + // return false + // } - return !!permission + // return !!permission + + const checkPermissionsController = getInjection("ICheckPermissionsController") + return await checkPermissionsController({ email, action, resource }) } catch (err) { if (err instanceof InputParseError) { @@ -315,7 +275,7 @@ export async function checkPermissionNew(email: string, action: string, resource crashReporterService.report(err) return { - error: "An error occurred during permissions check. Please try again later.", + error: err instanceof Error ? err.message : "An unknown error occurred", } } }) diff --git a/sigap-website/di/modules/authentication.module.ts b/sigap-website/di/modules/authentication.module.ts index 3234494..b61ad74 100644 --- a/sigap-website/di/modules/authentication.module.ts +++ b/sigap-website/di/modules/authentication.module.ts @@ -17,6 +17,8 @@ import { sendMagicLinkController } from '@/src/interface-adapters/controllers/au import { sendPasswordRecoveryController } from '@/src/interface-adapters/controllers/auth/send-password-recovery.controller'; import { signInWithPasswordUseCase } from '@/src/application/use-cases/auth/sign-in-with-password.use-case'; import { signInWithPasswordController } from '@/src/interface-adapters/controllers/auth/sign-in-with-password.controller'; +import { checkPermissionsUseCase } from '@/src/application/use-cases/auth/check-permissions.use-case'; +import { checkPermissionsController } from '@/src/interface-adapters/controllers/auth/check-permissions.controller'; export function createAuthenticationModule() { const authenticationModule = createModule(); @@ -28,15 +30,19 @@ export function createAuthenticationModule() { authenticationModule .bind(DI_SYMBOLS.IAuthenticationService) .toClass(AuthenticationService, [ - DI_SYMBOLS.IUsersRepository, DI_SYMBOLS.IInstrumentationService, + DI_SYMBOLS.ICrashReporterService, + DI_SYMBOLS.IPermissionsRepository, + DI_SYMBOLS.IUsersRepository, ]); } else { authenticationModule .bind(DI_SYMBOLS.IAuthenticationService) .toClass(AuthenticationService, [ - DI_SYMBOLS.IUsersRepository, DI_SYMBOLS.IInstrumentationService, + DI_SYMBOLS.ICrashReporterService, + DI_SYMBOLS.IPermissionsRepository, + DI_SYMBOLS.IUsersRepository, ]); } @@ -98,7 +104,7 @@ export function createAuthenticationModule() { authenticationModule .bind(DI_SYMBOLS.ICheckPermissionsUseCase) - .toHigherOrderFunction(signUpUseCase, [ + .toHigherOrderFunction(checkPermissionsUseCase, [ DI_SYMBOLS.IInstrumentationService, DI_SYMBOLS.IAuthenticationService, ]); @@ -150,9 +156,9 @@ export function createAuthenticationModule() { authenticationModule .bind(DI_SYMBOLS.ICheckPermissionsController) - .toHigherOrderFunction(signUpUseCase, [ + .toHigherOrderFunction(checkPermissionsController, [ DI_SYMBOLS.IInstrumentationService, - DI_SYMBOLS.IAuthenticationService, + DI_SYMBOLS.ICheckPermissionsUseCase, DI_SYMBOLS.IUsersRepository, ]); diff --git a/sigap-website/di/modules/permissions.module.ts b/sigap-website/di/modules/permissions.module.ts index 024508e..a0db9c2 100644 --- a/sigap-website/di/modules/permissions.module.ts +++ b/sigap-website/di/modules/permissions.module.ts @@ -13,10 +13,27 @@ import { getPermissionByIdController } from '@/src/interface-adapters/controller import { getPermissionByRoleController } from '@/src/interface-adapters/controllers/permissions/get-permission-by-role.controller'; import { updatePermissionController } from '@/src/interface-adapters/controllers/permissions/update-permission.controller'; import { deletePermissionController } from '@/src/interface-adapters/controllers/permissions/delete-permission.controller'; +import { PermissionsRepository } from '@/src/infrastructure/repositories/permissions.repository'; export function createPermissionsModule() { const permissionsModule = createModule(); + if (process.env.NODE_ENV === 'test') { + permissionsModule + .bind(DI_SYMBOLS.IPermissionsRepository) + .toClass(PermissionsRepository, [ + DI_SYMBOLS.IInstrumentationService, + DI_SYMBOLS.ICrashReporterService, + ]); + } else { + permissionsModule + .bind(DI_SYMBOLS.IPermissionsRepository) + .toClass(PermissionsRepository, [ + DI_SYMBOLS.IInstrumentationService, + DI_SYMBOLS.ICrashReporterService, + ]); + } + // Use Cases permissionsModule .bind(DI_SYMBOLS.ICreatePermissionUseCase) diff --git a/sigap-website/di/modules/resources.module.ts b/sigap-website/di/modules/resources.module.ts index 9042c3e..6f238a3 100644 --- a/sigap-website/di/modules/resources.module.ts +++ b/sigap-website/di/modules/resources.module.ts @@ -11,10 +11,23 @@ import { getResourceByIdController } from '@/src/interface-adapters/controllers/ import { getResourcesByTypeController } from '@/src/interface-adapters/controllers/resources/get-resources-by-type.controller'; import { updateResourceController } from '@/src/interface-adapters/controllers/resources/update-resource.controller'; import { deleteResourceController } from '@/src/interface-adapters/controllers/resources/delete-resource.controller'; +import { ResourcesRepository } from '@/src/infrastructure/repositories/resources.repository'; export function createResourcesModule() { const resourcesModule = createModule(); + if (process.env.NODE_ENV === 'test') { + // resourcesModule + // .bind(DI_SYMBOLS.IResourcesRepository) + // .toClass(MockResourcesRepository, [DI_SYMBOLS.IInstrumentationService]); + } else { + resourcesModule + .bind(DI_SYMBOLS.IResourcesRepository) + .toClass(ResourcesRepository, [ + DI_SYMBOLS.IInstrumentationService, + ]); + } + // Use Cases resourcesModule .bind(DI_SYMBOLS.ICreateResourceUseCase) diff --git a/sigap-website/di/modules/roles.module.ts b/sigap-website/di/modules/roles.module.ts index b55f7fc..05958b8 100644 --- a/sigap-website/di/modules/roles.module.ts +++ b/sigap-website/di/modules/roles.module.ts @@ -9,10 +9,23 @@ import { createRoleController } from '@/src/interface-adapters/controllers/roles import { getRoleByIdController } from '@/src/interface-adapters/controllers/roles/get-role-by-id.controller'; import { updateRoleController } from '@/src/interface-adapters/controllers/roles/update-role.controller'; import { deleteRoleController } from '@/src/interface-adapters/controllers/roles/delete-role.controller'; +import { RolesRepository } from '@/src/infrastructure/repositories/roles.repository'; export function createRolesModule() { const rolesModule = createModule(); + if (process.env.NODE_ENV === 'test') { + // rolesModule + // .bind(DI_SYMBOLS.IRolesRepository) + // .toClass(MockRolesRepository, [DI_SYMBOLS.IInstrumentationService]); + } else { + rolesModule + .bind(DI_SYMBOLS.IRolesRepository) + .toClass(RolesRepository, [ + DI_SYMBOLS.IInstrumentationService, + ]); + } + // Use Cases rolesModule .bind(DI_SYMBOLS.ICreateRoleUseCase) diff --git a/sigap-website/src/application/services/authentication.service.interface.ts b/sigap-website/src/application/services/authentication.service.interface.ts index 8345ac0..bc3c34a 100644 --- a/sigap-website/src/application/services/authentication.service.interface.ts +++ b/sigap-website/src/application/services/authentication.service.interface.ts @@ -17,5 +17,5 @@ export interface IAuthenticationService { sendMagicLink(credentials: ISendMagicLinkSchema): Promise sendPasswordRecovery(credentials: ISendPasswordRecoverySchema): Promise verifyOtp(credentials: IVerifyOtpSchema): Promise - checkPermission(userId: string, action: string, resource: string): Promise; + checkPermission(email: string, action: string, resource: string): Promise; } \ No newline at end of file diff --git a/sigap-website/src/application/use-cases/auth/check-permissions.use-case.ts b/sigap-website/src/application/use-cases/auth/check-permissions.use-case.ts index 395c59b..8d7c7cb 100644 --- a/sigap-website/src/application/use-cases/auth/check-permissions.use-case.ts +++ b/sigap-website/src/application/use-cases/auth/check-permissions.use-case.ts @@ -1,21 +1,19 @@ import { IInstrumentationService } from "../../services/instrumentation.service.interface"; -import { NotFoundError } from "@/src/entities/errors/common"; import { IAuthenticationService } from "../../services/authentication.service.interface"; -import { IUsersRepository } from "../../repositories/users.repository.interface"; export type ICheckPermissionsUseCase = ReturnType; export const checkPermissionsUseCase = ( instrumentationService: IInstrumentationService, authenticationService: IAuthenticationService, -) => async (userId: string, action: string, resource: string): Promise => { +) => async (email: string, action: string, resource: string): Promise => { return await instrumentationService.startSpan({ name: "Check Permission Use Case", op: "function" }, async () => { - const permission = await authenticationService.checkPermission(userId, action, resource); + const permission = await authenticationService.checkPermission(email, action, resource); if (!permission) { - throw new NotFoundError("Permission not found"); + return false; } return permission; diff --git a/sigap-website/src/infrastructure/repositories/permissions.repository.ts b/sigap-website/src/infrastructure/repositories/permissions.repository.ts index 97d99c2..474a6ba 100644 --- a/sigap-website/src/infrastructure/repositories/permissions.repository.ts +++ b/sigap-website/src/infrastructure/repositories/permissions.repository.ts @@ -73,19 +73,22 @@ export class PermissionsRepository implements IPermissionsRepository { async () => { try { - const result = await db.permissions.findFirst({ + const permission = await db.permissions.findFirst({ where: { - role: { - name: role - }, - action: action, - resource: { - name: resource - } + role: { name: role }, + action, + resource: { name: resource } } - }); + }) + + if (!permission) { + return false + } + + console.log("Permission", permission) + + return true - return !!result; } catch (err) { this.crashReporterService.report(err); throw err; diff --git a/sigap-website/src/infrastructure/services/authentication.service.ts b/sigap-website/src/infrastructure/services/authentication.service.ts index dc778c4..0b96ca7 100644 --- a/sigap-website/src/infrastructure/services/authentication.service.ts +++ b/sigap-website/src/infrastructure/services/authentication.service.ts @@ -17,10 +17,9 @@ import { IUserSchema } from "@/src/entities/models/users/users.model"; export class AuthenticationService implements IAuthenticationService { constructor( - private readonly usersRepository: IUsersRepository, private readonly instrumentationService: IInstrumentationService, private readonly crashReporterService: ICrashReporterService, - private readonly permissionRepository: IPermissionsRepository, + private readonly permissionsRepository: IPermissionsRepository, private readonly supabaseAdmin = createAdminClient(), private readonly supabaseServer = createClient() ) { } @@ -277,35 +276,22 @@ export class AuthenticationService implements IAuthenticationService { }) } - async checkPermission(userId: string, action: string, resource: string): Promise { + async checkPermission(email: string, action: string, resource: string): Promise { return await this.instrumentationService.startSpan({ - name: "", + name: "Check Permission Use Case", }, async () => { try { + const user = await db.users.findUnique({ + where: { email }, + include: { role: true } + }); - // const user = await db.users.findUnique({ - // where: { id: userId }, - // include: { role: true } - // }); + if (!user) { + return false; + } - // if (!user) { - // return false; - // } - - // const role = user.role.name; - - // const permission = await db.permissions.findFirst({ - // where: { - // role: { name: role }, - // action, - // resource: { - // name: resource - // } - // } - // }) - - return true + return await this.permissionsRepository.checkPermission(user.role.name, action, resource); } catch (err) { this.crashReporterService.report(err) diff --git a/sigap-website/src/interface-adapters/controllers/auth/check-permissions.controller.ts b/sigap-website/src/interface-adapters/controllers/auth/check-permissions.controller.ts index 700f923..1f1754e 100644 --- a/sigap-website/src/interface-adapters/controllers/auth/check-permissions.controller.ts +++ b/sigap-website/src/interface-adapters/controllers/auth/check-permissions.controller.ts @@ -6,7 +6,7 @@ import { z } from "zod"; const checkPermissionInputSchema = z.object({ - userId: z.string().uuid("Please enter a valid user ID"), + email: z.string().email("Please enter a valid email"), action: z.string().nonempty("Please enter an action"), resource: z.string().nonempty("Please enter a resource"), }) @@ -23,11 +23,11 @@ export const checkPermissionsController = return await instrumentationService.startSpan({ name: "checkPermission Controller" }, async () => { - // const session = await usersRpository.getCurrentUser() + const session = await usersRpository.getCurrentUser() - // if (!session) { - // throw new InputParseError("User not found") - // } + if (!session) { + throw new InputParseError("User not found") + } const { data, error: inputParseError } = checkPermissionInputSchema.safeParse(input) @@ -36,7 +36,7 @@ export const checkPermissionsController = } return await checkPermissionUseCase( - data.userId, + data.email, data.action, data.resource )