diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index 4e4ce05..fe77d90 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -1,6 +1,7 @@ import { Routes } from "@angular/router"; import { Home } from "./features/home/home"; import { authGuard } from "./core/guards/auth-guard"; +import { roleGuard } from "./core/guards/role-guard"; export const routes: Routes = [ { @@ -16,6 +17,7 @@ export const routes: Routes = [ path: "products", loadChildren: () => import("./features/product/product.routes").then((routes) => routes.productRoutes), - // canActivate: [authGuard] + canActivate: [authGuard, roleGuard], + data: { roles: ["broker"] }, }, ]; diff --git a/src/app/core/guards/role-guard.spec.ts b/src/app/core/guards/role-guard.spec.ts new file mode 100644 index 0000000..61e5eb7 --- /dev/null +++ b/src/app/core/guards/role-guard.spec.ts @@ -0,0 +1,17 @@ +import { TestBed } from "@angular/core/testing"; +import { CanActivateFn } from "@angular/router"; + +import { roleGuard } from "./role-guard"; + +describe("roleGuard", () => { + const executeGuard: CanActivateFn = (...guardParameters) => + TestBed.runInInjectionContext(() => roleGuard(...guardParameters)); + + beforeEach(() => { + TestBed.configureTestingModule({}); + }); + + it("should be created", () => { + expect(executeGuard).toBeTruthy(); + }); +}); diff --git a/src/app/core/guards/role-guard.ts b/src/app/core/guards/role-guard.ts new file mode 100644 index 0000000..dfd7a3c --- /dev/null +++ b/src/app/core/guards/role-guard.ts @@ -0,0 +1,12 @@ +import { CanActivateFn } from "@angular/router"; +import { inject } from "@angular/core"; +import { AuthService } from "../../features/auth/services/auth-service"; + +export const roleGuard: CanActivateFn = (route, state) => { + const authService = inject(AuthService); + + // get role from route data passed in route config. + const roles = route.data["roles"] as string[]; + + return roles && authService.hasRoles(roles); +}; diff --git a/src/app/core/models/user.model.ts b/src/app/core/models/user.model.ts index aa61525..086b340 100644 --- a/src/app/core/models/user.model.ts +++ b/src/app/core/models/user.model.ts @@ -13,4 +13,5 @@ export interface User { email: string; mobileNumber: string; city: string; + role: string; } diff --git a/src/app/features/auth/services/auth-service.ts b/src/app/features/auth/services/auth-service.ts index 20f195f..d973e8c 100644 --- a/src/app/features/auth/services/auth-service.ts +++ b/src/app/features/auth/services/auth-service.ts @@ -27,6 +27,7 @@ export class AuthService { // User states readonly authState: WritableSignal; readonly user: WritableSignal; + readonly userRole: Signal; // Computed state for easy checking readonly isAuthenticated: Signal; @@ -45,6 +46,7 @@ export class AuthService { ); this.user = signal(cachedUser); this.isAuthenticated = computed(() => !!this.user()); + this.userRole = computed(() => this.user()?.role || null); } register(userRequest: RegisterUserRequest) { @@ -74,6 +76,16 @@ export class AuthService { ); } + /** + * Check if current user has the role. + * Mostly used in role guard. + */ + hasRoles(roles: string[]) { + const role = this.userRole(); + if (!role) return false; + return roles.includes(role); + } + logout() { return this.http.post(`${this.backendURL}/logout`, {}).pipe( tap({