Skip to content

Commit

Permalink
refactor: add type casting protect route, separate processing functio…
Browse files Browse the repository at this point in the history
…n to each file

fix: fix protect route conflict with role
  • Loading branch information
lcaohoanq committed Jul 8, 2024
1 parent 26814bc commit 4ccf6af
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 129 deletions.
39 changes: 0 additions & 39 deletions src/modules/protectRouting/mapRouteWithRole.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,16 @@
"login": {
"api": "/admin/login",
"method": "post",
"role": 0,
"access_token": false
},
"createEmployee": {
"api": "/admin/createEmployee",
"method": "post",
"role": 0,
"access_token": true
},
":id": {
"api": "/admin/:id",
"method": "patch",
"role": 0,
"access_token": true
}
}
Expand All @@ -28,30 +25,21 @@
"/": {
"api": "/",
"method": "get",
"role": 0,
"access_token": false
},
"verify": {
"api": "/captcha/verify",
"method": "post",
"role": 0,
"access_token": false
}
}
},
{
"module": "employee",
"route": "",
"role": 0,
"access_token": false
},
{
"module": "menu",
"route": {
":language": {
"api": "/menu/:language",
"method": "get",
"role": 0,
"access_token": false
}
}
Expand All @@ -62,37 +50,31 @@
"google": {
"api": "/oauth/google",
"method": "get",
"role": 0,
"access_token": false
},
"google/callback": {
"api": "/oauth/google/callback",
"method": "get",
"role": 0,
"access_token": false
},
"facebook": {
"api": "/oauth/facebook",
"method": "get",
"role": 0,
"access_token": false
},
"facebook/callback": {
"api": "/oauth/facebook/callback",
"method": "get",
"role": 0,
"access_token": false
},
"login-success": {
"api": "/oauth/login-success",
"method": "post",
"role": 0,
"access_token": false
},
"login-fail": {
"api": "/oauth/login-fail",
"method": "post",
"role": 0,
"access_token": false
}
}
Expand All @@ -103,13 +85,11 @@
"send-otp-phone": {
"api": "/otp/send-otp-phone",
"method": "post",
"role": 0,
"access_token": true
},
"send-otp-email": {
"api": "/otp/send-otp-email",
"method": "post",
"role": 0,
"access_token": true
}
}
Expand All @@ -120,102 +100,83 @@
"updatePass": {
"api": "/pass/updatePass",
"method": "post",
"role": 0,
"access_token": true
}
}
},
{
"module": "refreshToken",
"route": ""
},
{
"module": "user",
"route": {
"register": {
"api": "/user/register",
"method": "post",
"role": 0,
"access_token": false
},
"login": {
"api": "/user/login",
"method": "post",
"role": 0,
"access_token": false
},
"forgot-password": {
"api": "/user/forgot-password",
"method": "post",
"role": 0,
"access_token": false
},
"verify-otp": {
"api": "/user/verify-otp",
"method": "post",
"role": 0,
"access_token": false
},
"reset-password": {
"api": "/user/reset-password",
"method": "post",
"role": 0,
"access_token": false
},
"send-verify-account-otp": {
"api": "/user/send-verify-account-otp",
"method": "post",
"role": 0,
"access_token": true
},
"verify-account": {
"api": "/user/verify-account",
"method": "post",
"role": 0,
"access_token": true
},
"change-password": {
"api": "/user/change-password",
"method": "post",
"role": 0,
"access_token": true
},
"me": [
{
"api": "/user/me",
"method": "get",
"role": 0,
"access_token": true
},
{
"api": "/user/me",
"method": "patch",
"role": 0,
"access_token": true
}
],
"search": {
"api": "/user/search",
"method": "post",
"role": 0,
"access_token": true
},
"logout": {
"api": "/user/logout",
"method": "post",
"role": 0,
"access_token": true
},
"refresh-token": {
"api": "/user/refresh-token",
"method": "post",
"role": 0,
"access_token": false
},
"list-account": {
"api": "/user/list-account",
"method": "get",
"role": 0,
"access_token": false
}
}
Expand Down
11 changes: 11 additions & 0 deletions src/modules/protectRouting/protect.configs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { UserRole } from "../user/user.enum";
import { RouteConfig } from "./protect.schemas";

export const routesConfig: RouteConfig[] = [
{
contextPath: "/admin",
roles: [UserRole.Admin],
}, //Admin can access all
{ contextPath: "/user", roles: [UserRole.Customer, UserRole.Admin] }, // User only access to Customer
{ contextPath: "/employee", roles: [UserRole.Employee, UserRole.Admin] }, // Employee only access to Employee
];
4 changes: 3 additions & 1 deletion src/modules/protectRouting/protect.messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ export const PROTECT_MESSAGES = {
ROLE_CUSTOMER: "You are customer",
ROLE_EMPLOYEE: "You are employee",
ROLE_NOT_FOUND: "Role not found",
ROLE_NOT_VALID: "Role not valid",
ACCESS_DENIED: "Access denied",
UNAUTHORIZED: "Unauthorized",
ROUTE_AND_ROLE_MIS_MATCH: "Route and role mismatch",
ROUTE_AND_ROLE_MIS_MATCH_SEEM_WRONG_AT_ROUTES_CONFIG:
"Route and role mismatch seem wrong at routes config",
ROUTES_CONFIG_WRONG: "Routes config is wrong",
MISSING_ACCESS_TOKEN: "Missing access token",
} as const;
20 changes: 20 additions & 0 deletions src/modules/protectRouting/protect.schemas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { UserRole } from "../user/user.enum";

export interface Route {
api: string;
method: string;
access_token: boolean;
}

// Interface for a Module object with nested routes
export interface Module {
module: string;
route: Record<string, Route | Route[]>;
}

export interface RouteConfig {
contextPath: string;
roles: UserRole[];
}

export type RequestPath = string;
63 changes: 29 additions & 34 deletions src/modules/protectRouting/protect.utils.ts
Original file line number Diff line number Diff line change
@@ -1,55 +1,50 @@
import { UserRole } from "../user/user.enum";
const routes: Module[] = require("./mapRouteWithRole.json");
import rawRoutes from "./mapRouteWithRole.json";
import { routesConfig } from "./protect.configs";
import { Module, RouteConfig } from "./protect.schemas";

// Interface for a Route object within a module
interface Route {
api: string;
method: string;
role: number;
access_token: boolean;
}

// Interface for a Module object with nested routes
interface Module {
module: string;
route: { [key: string]: Route };
}

interface RouteConfig {
path: string;
roles: UserRole[];
}

export const routesConfig: RouteConfig[] = [
{
path: "/admin",
roles: [UserRole.Employee, UserRole.Customer, UserRole.Admin],
}, //Admin can access all
{ path: "/user", roles: [UserRole.Customer] }, // User only access to Customer
{ path: "/employee", roles: [UserRole.Employee] }, // Employee only access to Employee
];
// Cast rawRoutes to unknown first, then to Module[]
const routes: Module[] = rawRoutes as unknown as Module[];

export function getOpenRoutes(): string[] {
const openRoutes: string[] = [];

// Filter routes where access_token is false
routes.forEach((module: Module) => {
for (const key in module.route) {
if (!module.route[key].access_token) {
openRoutes.push(module.route[key].api);
const route = module.route[key];
//if in case of multiple routes
if (Array.isArray(route)) {
route.forEach((r) => {
if (!r.access_token) {
openRoutes.push(r.api);
}
});
} else {
if (!route.access_token) {
openRoutes.push(route.api);
}
}
}
});

return openRoutes;
}

// math the route.path with the req.path (/user/login) but take the first part only (/user)
// match the route.contextPath with the req.path (/user/login) but take the first part only (/user)
// ex: /user/login -> /user : { contextPath: '/user', roles: [ 0,1 ]}
// ex: /user/login -> /admin : undefined
export function checkRole(
path: string,
pattern: string,
requestPath: string,
delimiter: string,
): RouteConfig | undefined {
return routesConfig.find(
(route) => route.path === pattern + path.split(pattern)[1],
(route) =>
route.contextPath === delimiter + requestPath.split(delimiter)[1],
);
}

// Type guard to check if a number is a valid UserRole
export function isValidUserRole(role: UserRole): boolean {
return Object.values(UserRole).includes(role);
}
3 changes: 2 additions & 1 deletion src/modules/user/user.messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ export const USER_MESSAGES = {
// refresh token
REFRESH_TOKEN_NOT_FOUND: "Refresh token is not found",
REFRESH_TOKEN_SUCCESSFULLY: "Refresh token successfully!",
REFRESH_TOKEN_NOT_VALID: "Refresh token is not valid",

//logout
LOGOUT_SUCCESSFULLY: "Logout successfully!",
Expand All @@ -107,7 +108,7 @@ export const USER_MESSAGES = {
//token
REFRESH_TOKEN_IS_REQUIRED: "Refresh token is required",
OTP_IS_INCORRECT: "OTP is incorrect",

// block
USER_HAS_BEEN_BLOCKED: "user has been blocked",
USER_UNBLOCK_SUCCESSFULLY: "user unblock successfully",
Expand Down
Loading

0 comments on commit 4ccf6af

Please sign in to comment.