From 5a56b3554d45020f9bf480b0f31b9ce69254279d Mon Sep 17 00:00:00 2001 From: Andreas Knuth Date: Sat, 8 Mar 2025 11:18:31 +0100 Subject: [PATCH] einbau von rollen, neue Admin Ansicht --- bizmatch-server/package.json | 3 +- bizmatch-server/src/app.module.ts | 4 +- bizmatch-server/src/auth/auth.controller.ts | 94 +++++- bizmatch-server/src/auth/auth.module.ts | 2 + bizmatch-server/src/auth/auth.service.ts | 113 +++++++ .../src/jwt-auth/admin-auth.guard.ts | 20 ++ bizmatch-server/src/jwt-auth/auth.guard.ts | 56 ++-- .../src/jwt-auth/firebase-admin.ts | 16 - .../src/jwt-auth/localhost.guard.ts | 21 ++ .../src/jwt-auth/optional-auth.guard.ts | 56 ++-- bizmatch-server/src/models/main.model.ts | 20 ++ bizmatch-server/src/setup-admin.command.ts | 27 ++ bizmatch-server/src/user/user.controller.ts | 11 +- .../components/header/header.component.html | 13 +- .../app/components/header/header.component.ts | 11 +- .../admin/user-list/user-list.component.html | 286 +++++++++--------- .../admin/user-list/user-list.component.ts | 185 +++++------ .../details-business-listing.component.html | 2 +- .../details-business-listing.component.ts | 8 +- ...commercial-property-listing.component.html | 2 +- ...s-commercial-property-listing.component.ts | 7 +- .../details-user/details-user.component.html | 2 +- .../details-user/details-user.component.ts | 8 +- bizmatch/src/app/pages/home/home.component.ts | 2 +- .../account/account.component.html | 4 +- .../subscription/account/account.component.ts | 8 +- bizmatch/src/app/services/auth.service.ts | 188 +++++++++--- bizmatch/src/app/services/user.service.ts | 39 ++- bizmatch/src/app/utils/utils.ts | 6 +- 29 files changed, 788 insertions(+), 426 deletions(-) create mode 100644 bizmatch-server/src/auth/auth.service.ts create mode 100644 bizmatch-server/src/jwt-auth/admin-auth.guard.ts delete mode 100644 bizmatch-server/src/jwt-auth/firebase-admin.ts create mode 100644 bizmatch-server/src/jwt-auth/localhost.guard.ts create mode 100644 bizmatch-server/src/setup-admin.command.ts diff --git a/bizmatch-server/package.json b/bizmatch-server/package.json index 69f0ee4..445be18 100644 --- a/bizmatch-server/package.json +++ b/bizmatch-server/package.json @@ -75,6 +75,7 @@ "eslint-config-prettier": "^9.0.0", "eslint-plugin-prettier": "^5.0.0", "kysely-codegen": "^0.15.0", + "nest-commander": "^3.16.1", "pg-to-ts": "^4.1.1", "prettier": "^3.0.0", "rimraf": "^5.0.5", @@ -103,4 +104,4 @@ "coverageDirectory": "../coverage", "testEnvironment": "node" } -} \ No newline at end of file +} diff --git a/bizmatch-server/src/app.module.ts b/bizmatch-server/src/app.module.ts index 94c5ba6..4198a2e 100644 --- a/bizmatch-server/src/app.module.ts +++ b/bizmatch-server/src/app.module.ts @@ -19,12 +19,13 @@ import { ConfigModule } from '@nestjs/config'; import { APP_INTERCEPTOR } from '@nestjs/core'; import { ClsMiddleware, ClsModule } from 'nestjs-cls'; import path from 'path'; +import { AuthService } from './auth/auth.service'; +import { FirebaseAdminModule } from './firebase-admin/firebase-admin.module'; import { LoggingInterceptor } from './interceptors/logging.interceptor'; import { UserInterceptor } from './interceptors/user.interceptor'; import { RequestDurationMiddleware } from './request-duration/request-duration.middleware'; import { SelectOptionsModule } from './select-options/select-options.module'; import { UserModule } from './user/user.module'; -import { FirebaseAdminModule } from './firebase-admin/firebase-admin.module'; //loadEnvFiles(); console.log('Loaded environment variables:'); @@ -82,6 +83,7 @@ console.log('Loaded environment variables:'); provide: APP_INTERCEPTOR, useClass: LoggingInterceptor, // Registriere den LoggingInterceptor global }, + AuthService, ], }) export class AppModule implements NestModule { diff --git a/bizmatch-server/src/auth/auth.controller.ts b/bizmatch-server/src/auth/auth.controller.ts index febfb94..090bfb2 100644 --- a/bizmatch-server/src/auth/auth.controller.ts +++ b/bizmatch-server/src/auth/auth.controller.ts @@ -1,11 +1,17 @@ -import { Body, Controller, HttpException, HttpStatus, Inject, Post } from '@nestjs/common'; +import { Body, Controller, Get, HttpException, HttpStatus, Inject, Param, Post, Query, Req, UseGuards } from '@nestjs/common'; import * as admin from 'firebase-admin'; +import { AdminGuard } from 'src/jwt-auth/admin-auth.guard'; +import { AuthGuard } from 'src/jwt-auth/auth.guard'; +import { LocalhostGuard } from 'src/jwt-auth/localhost.guard'; +import { UserRole, UsersResponse } from 'src/models/main.model'; +import { AuthService } from './auth.service'; @Controller('auth') export class AuthController { constructor( @Inject('FIREBASE_ADMIN') private readonly firebaseAdmin: typeof admin, + private readonly authService: AuthService, ) {} @Post('verify-email') async verifyEmail(@Body('oobCode') oobCode: string, @Body('email') email: string) { @@ -33,5 +39,91 @@ export class AuthController { throw new HttpException(error.message || 'Failed to verify email', HttpStatus.BAD_REQUEST); } } + @Post(':uid/role') + @UseGuards(AuthGuard, AdminGuard) // Only admins can change roles + async setUserRole(@Param('uid') uid: string, @Body('role') role: UserRole): Promise<{ success: boolean }> { + await this.authService.setUserRole(uid, role); + return { success: true }; + } + @Get('me/role') + @UseGuards(AuthGuard) + async getMyRole(@Req() req: any): Promise<{ role: UserRole | null }> { + console.log('->', req.user); + console.log('-->', req.user.uid); + const uid = req.user.uid; // From FirebaseAuthGuard + const role = await this.authService.getUserRole(uid); + return { role }; + } + + @Get(':uid/role') + @UseGuards(AuthGuard) + async getUserRole(@Param('uid') uid: string): Promise<{ role: UserRole | null }> { + const role = await this.authService.getUserRole(uid); + return { role }; + } + + @Get('role/:role') + @UseGuards(AuthGuard, AdminGuard) // Only admins can list users by role + async getUsersByRole(@Param('role') role: UserRole): Promise<{ users: any[] }> { + const users = await this.authService.getUsersByRole(role); + // Map to simpler objects to avoid circular references + const simplifiedUsers = users.map(user => ({ + uid: user.uid, + email: user.email, + displayName: user.displayName, + })); + return { users: simplifiedUsers }; + } + + /** + * Ruft alle Firebase-Benutzer mit ihren Rollen ab + * @param maxResults Maximale Anzahl an zurückzugebenden Benutzern (optional, Standard: 1000) + * @param pageToken Token für die Paginierung (optional) + * @returns Eine Liste von Benutzern mit ihren Rollen und Metadaten + */ + @Get() + @UseGuards(AuthGuard, AdminGuard) // Only admins can list all users + async getAllUsers(@Query('maxResults') maxResults?: number, @Query('pageToken') pageToken?: string): Promise { + const result = await this.authService.getAllUsers(maxResults ? parseInt(maxResults.toString(), 10) : undefined, pageToken); + + return { + users: result.users, + totalCount: result.users.length, + ...(result.pageToken && { pageToken: result.pageToken }), + }; + } + + /** + * Endpoint zum direkten Einstellen einer Rolle für Debug-Zwecke + * WARNUNG: Dieser Endpoint sollte in der Produktion entfernt oder stark gesichert werden + */ + @Post('set-role') + @UseGuards(AuthGuard, LocalhostGuard) + async setUserRoleOnLocalhost(@Req() req: any, @Body('role') role: UserRole): Promise<{ success: boolean; message: string }> { + try { + const uid = req.user.uid; + + // Aktuelle Rolle protokollieren + const currentUser = await this.authService.getUserRole(uid); + console.log(`Changing role for user ${uid} from ${currentUser} to ${role}`); + + // Neue Rolle setzen + await this.authService.setUserRole(uid, role); + + // Rolle erneut prüfen, um zu bestätigen + const newRole = await this.authService.getUserRole(uid); + + return { + success: true, + message: `Rolle für Benutzer ${uid} von ${currentUser} zu ${newRole} geändert`, + }; + } catch (error) { + console.error('Fehler beim Setzen der Rolle:', error); + return { + success: false, + message: `Fehler: ${error.message}`, + }; + } + } } diff --git a/bizmatch-server/src/auth/auth.module.ts b/bizmatch-server/src/auth/auth.module.ts index 13a2bf1..155c147 100644 --- a/bizmatch-server/src/auth/auth.module.ts +++ b/bizmatch-server/src/auth/auth.module.ts @@ -2,11 +2,13 @@ import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { FirebaseAdminModule } from 'src/firebase-admin/firebase-admin.module'; import { AuthController } from './auth.controller'; +import { AuthService } from './auth.service'; @Module({ imports: [ConfigModule.forRoot({ envFilePath: '.env' }),FirebaseAdminModule], controllers: [AuthController], + providers: [AuthService], exports: [], }) export class AuthModule {} diff --git a/bizmatch-server/src/auth/auth.service.ts b/bizmatch-server/src/auth/auth.service.ts new file mode 100644 index 0000000..ee0a26c --- /dev/null +++ b/bizmatch-server/src/auth/auth.service.ts @@ -0,0 +1,113 @@ +import { Inject, Injectable } from '@nestjs/common'; +import * as admin from 'firebase-admin'; +import { FirebaseUserInfo, UserRole } from 'src/models/main.model'; + +@Injectable() +export class AuthService { + constructor(@Inject('FIREBASE_ADMIN') private firebaseAdmin: admin.app.App) {} + + /** + * Set a user's role via Firebase custom claims + */ + async setUserRole(uid: string, role: UserRole): Promise { + try { + // Get the current custom claims + const user = await this.firebaseAdmin.auth().getUser(uid); + const currentClaims = user.customClaims || {}; + + // Set the new role + await this.firebaseAdmin.auth().setCustomUserClaims(uid, { + ...currentClaims, + role: role, + }); + } catch (error) { + console.error('Error setting user role:', error); + throw error; + } + } + + /** + * Get a user's current role + */ + async getUserRole(uid: string): Promise { + try { + const user = await this.firebaseAdmin.auth().getUser(uid); + const claims = user.customClaims || {}; + return (claims.role as UserRole) || null; + } catch (error) { + console.error('Error getting user role:', error); + throw error; + } + } + + /** + * Get all users with a specific role + */ + async getUsersByRole(role: UserRole): Promise { + // Note: Firebase Admin doesn't provide a direct way to query users by custom claims + // For a production app, you might want to store role information in Firestore as well + // This is a simple implementation that lists all users and filters them + try { + const listUsersResult = await this.firebaseAdmin.auth().listUsers(); + return listUsersResult.users.filter(user => user.customClaims && user.customClaims.role === role); + } catch (error) { + console.error('Error getting users by role:', error); + throw error; + } + } + + /** + * Get all Firebase users with their roles + * @param maxResults Maximum number of users to return (optional, default 1000) + * @param pageToken Token for pagination (optional) + */ + async getAllUsers(maxResults: number = 1000, pageToken?: string): Promise<{ users: FirebaseUserInfo[]; pageToken?: string }> { + try { + const listUsersResult = await this.firebaseAdmin.auth().listUsers(maxResults, pageToken); + + const users = listUsersResult.users.map(user => this.mapUserRecord(user)); + + return { + users, + pageToken: listUsersResult.pageToken, + }; + } catch (error) { + console.error('Error getting all users:', error); + throw error; + } + } + /** + * Maps a Firebase UserRecord to our FirebaseUserInfo interface + */ + private mapUserRecord(user: admin.auth.UserRecord): FirebaseUserInfo { + return { + uid: user.uid, + email: user.email || null, + displayName: user.displayName || null, + photoURL: user.photoURL || null, + phoneNumber: user.phoneNumber || null, + disabled: user.disabled, + emailVerified: user.emailVerified, + role: user.customClaims?.role || null, + creationTime: user.metadata.creationTime, + lastSignInTime: user.metadata.lastSignInTime, + // Optionally include other customClaims if needed + customClaims: user.customClaims, + }; + } + + /** + * Set default role for a new user + */ + async setDefaultRole(uid: string): Promise { + return this.setUserRole(uid, 'guest'); + } + + /** + * Verify if a user has a specific role + */ + async hasRole(uid: string, role: UserRole): Promise { + const userRole = await this.getUserRole(uid); + return userRole === role; + } +} diff --git a/bizmatch-server/src/jwt-auth/admin-auth.guard.ts b/bizmatch-server/src/jwt-auth/admin-auth.guard.ts new file mode 100644 index 0000000..f6b7b9a --- /dev/null +++ b/bizmatch-server/src/jwt-auth/admin-auth.guard.ts @@ -0,0 +1,20 @@ +import { CanActivate, ExecutionContext, ForbiddenException, Injectable } from '@nestjs/common'; + +@Injectable() +export class AdminGuard implements CanActivate { + canActivate(context: ExecutionContext): boolean { + const request = context.switchToHttp().getRequest(); + + // The FirebaseAuthGuard should run before this guard + // and populate the request.user object + if (!request.user) { + throw new ForbiddenException('User not authenticated'); + } + + if (request.user.role !== 'admin') { + throw new ForbiddenException('Requires admin privileges'); + } + + return true; + } +} \ No newline at end of file diff --git a/bizmatch-server/src/jwt-auth/auth.guard.ts b/bizmatch-server/src/jwt-auth/auth.guard.ts index 3b4d646..b2e723b 100644 --- a/bizmatch-server/src/jwt-auth/auth.guard.ts +++ b/bizmatch-server/src/jwt-auth/auth.guard.ts @@ -4,53 +4,39 @@ import * as admin from 'firebase-admin'; @Injectable() export class AuthGuard implements CanActivate { constructor( - @Inject('FIREBASE_ADMIN') - private readonly firebaseAdmin: typeof admin, + @Inject('FIREBASE_ADMIN') private firebaseAdmin: admin.app.App, ) {} async canActivate(context: ExecutionContext): Promise { - const request = context.switchToHttp().getRequest(); - const token = this.extractTokenFromHeader(request); + const request = context.switchToHttp().getRequest(); + const authHeader = request.headers.authorization; - if (!token) { - throw new UnauthorizedException('No token provided'); + if (!authHeader || !authHeader.startsWith('Bearer ')) { + throw new UnauthorizedException('Missing or invalid authorization token'); } + const token = authHeader.split('Bearer ')[1]; + try { const decodedToken = await this.firebaseAdmin.auth().verifyIdToken(token); - request['user'] = decodedToken; + + // Check if email is verified (optional but recommended) + if (!decodedToken.email_verified) { + throw new UnauthorizedException('Email not verified'); + } + + // Add the user to the request + request.user = { + uid: decodedToken.uid, + email: decodedToken.email, + role: decodedToken.role || null, + // Add other user info as needed + }; + return true; } catch (error) { throw new UnauthorizedException('Invalid token'); } } - - private extractTokenFromHeader(request: Request): string | undefined { - const [type, token] = request.headers['authorization']?.split(' ') ?? []; - return type === 'Bearer' ? token : undefined; - } } -// @Injectable() -// export class AuthGuard implements CanActivate { -// async canActivate(context: ExecutionContext): Promise { -// const request = context.switchToHttp().getRequest(); -// const token = this.extractTokenFromHeader(request); -// if (!token) { -// throw new UnauthorizedException('No token provided'); -// } - -// try { -// const decodedToken = await admin.auth().verifyIdToken(token); -// request['user'] = decodedToken; // Fügen Sie die Benutzerdaten dem Request-Objekt hinzu -// return true; -// } catch (error) { -// throw new UnauthorizedException('Invalid token'); -// } -// } - -// private extractTokenFromHeader(request: Request): string | undefined { -// const [type, token] = request.headers['authorization']?.split(' ') ?? []; -// return type === 'Bearer' ? token : undefined; -// } -// } diff --git a/bizmatch-server/src/jwt-auth/firebase-admin.ts b/bizmatch-server/src/jwt-auth/firebase-admin.ts deleted file mode 100644 index faf842b..0000000 --- a/bizmatch-server/src/jwt-auth/firebase-admin.ts +++ /dev/null @@ -1,16 +0,0 @@ -// import * as admin from 'firebase-admin'; -// import { ServiceAccount } from 'firebase-admin'; -// console.log('--> '+process.env['FIREBASE_PROJECT_ID']) -// const serviceAccount: ServiceAccount = { -// projectId: process.env['FIREBASE_PROJECT_ID'], -// clientEmail: process.env['FIREBASE_CLIENT_EMAIL'], -// privateKey: process.env['FIREBASE_PRIVATE_KEY']?.replace(/\\n/g, '\n'), // Ersetzen Sie escaped newlines -// }; - -// if (!admin.apps.length) { -// admin.initializeApp({ -// credential: admin.credential.cert(serviceAccount), -// }); -// } - -// export default admin; diff --git a/bizmatch-server/src/jwt-auth/localhost.guard.ts b/bizmatch-server/src/jwt-auth/localhost.guard.ts new file mode 100644 index 0000000..4a26ffd --- /dev/null +++ b/bizmatch-server/src/jwt-auth/localhost.guard.ts @@ -0,0 +1,21 @@ +import { CanActivate, ExecutionContext, ForbiddenException, Injectable } from '@nestjs/common'; +import { Request } from 'express'; +import { Observable } from 'rxjs'; + +@Injectable() +export class LocalhostGuard implements CanActivate { + canActivate(context: ExecutionContext): boolean | Promise | Observable { + const request = context.switchToHttp().getRequest(); + const ip = request.ip; + + // Liste der erlaubten IPs + const allowedIPs = ['127.0.0.1', '::1', 'localhost', '::ffff:127.0.0.1']; + + if (!allowedIPs.includes(ip)) { + console.warn(`Versuchter Zugriff von unerlaubter IP: ${ip}`); + throw new ForbiddenException('Dieser Endpunkt kann nur lokal aufgerufen werden'); + } + + return true; + } +} diff --git a/bizmatch-server/src/jwt-auth/optional-auth.guard.ts b/bizmatch-server/src/jwt-auth/optional-auth.guard.ts index e442e50..9426683 100644 --- a/bizmatch-server/src/jwt-auth/optional-auth.guard.ts +++ b/bizmatch-server/src/jwt-auth/optional-auth.guard.ts @@ -3,54 +3,70 @@ import * as admin from 'firebase-admin'; @Injectable() export class OptionalAuthGuard implements CanActivate { - constructor( - @Inject('FIREBASE_ADMIN') - private readonly firebaseAdmin: typeof admin, - ) {} + constructor(@Inject('FIREBASE_ADMIN') private firebaseAdmin: admin.app.App) {} async canActivate(context: ExecutionContext): Promise { - const request = context.switchToHttp().getRequest(); - const token = this.extractTokenFromHeader(request); + const request = context.switchToHttp().getRequest(); + const authHeader = request.headers.authorization; - if (!token) { + if (!authHeader || !authHeader.startsWith('Bearer ')) { + //throw new UnauthorizedException('Missing or invalid authorization token'); return true; } + const token = authHeader.split('Bearer ')[1]; + try { const decodedToken = await this.firebaseAdmin.auth().verifyIdToken(token); - request['user'] = decodedToken; + + // Check if email is verified (optional but recommended) + if (!decodedToken.email_verified) { + //throw new UnauthorizedException('Email not verified'); + return true; + } + + // Add the user to the request + request.user = { + uid: decodedToken.uid, + email: decodedToken.email, + role: decodedToken.role || null, + // Add other user info as needed + }; + return true; } catch (error) { //throw new UnauthorizedException('Invalid token'); - request['user'] = null; return true; } } - - private extractTokenFromHeader(request: Request): string | undefined { - const [type, token] = request.headers['authorization']?.split(' ') ?? []; - return type === 'Bearer' ? token : undefined; - } } +// import { CanActivate, ExecutionContext, Inject, Injectable } from '@nestjs/common'; +// import * as admin from 'firebase-admin'; + // @Injectable() // export class OptionalAuthGuard implements CanActivate { +// constructor( +// @Inject('FIREBASE_ADMIN') +// private readonly firebaseAdmin: typeof admin, +// ) {} + // async canActivate(context: ExecutionContext): Promise { // const request = context.switchToHttp().getRequest(); // const token = this.extractTokenFromHeader(request); // if (!token) { -// return true; // Kein Token vorhanden, aber Zugriff erlaubt +// return true; // } // try { -// const decodedToken = await admin.auth().verifyIdToken(token); -// request['user'] = decodedToken; // Benutzerdaten zum Request hinzufügen, wenn Token gültig +// const decodedToken = await this.firebaseAdmin.auth().verifyIdToken(token); +// request['user'] = decodedToken; +// return true; // } catch (error) { -// // Bei ungültigem Token wird kein Fehler geworfen, sondern einfach kein User gesetzt +// //throw new UnauthorizedException('Invalid token'); // request['user'] = null; +// return true; // } - -// return true; // Zugriff wird immer erlaubt // } // private extractTokenFromHeader(request: Request): string | undefined { diff --git a/bizmatch-server/src/models/main.model.ts b/bizmatch-server/src/models/main.model.ts index 1899a7c..c6f4f6c 100644 --- a/bizmatch-server/src/models/main.model.ts +++ b/bizmatch-server/src/models/main.model.ts @@ -278,6 +278,26 @@ export interface Checkout { email: string; name: string; } +export type UserRole = 'admin' | 'pro' | 'guest' | null; +export interface FirebaseUserInfo { + uid: string; + email: string | null; + displayName: string | null; + photoURL: string | null; + phoneNumber: string | null; + disabled: boolean; + emailVerified: boolean; + role: UserRole; + creationTime?: string; + lastSignInTime?: string; + customClaims?: Record; +} + +export interface UsersResponse { + users: FirebaseUserInfo[]; + totalCount: number; + pageToken?: string; +} export function isEmpty(value: any): boolean { // Check for undefined or null if (value === undefined || value === null) { diff --git a/bizmatch-server/src/setup-admin.command.ts b/bizmatch-server/src/setup-admin.command.ts new file mode 100644 index 0000000..e84b60f --- /dev/null +++ b/bizmatch-server/src/setup-admin.command.ts @@ -0,0 +1,27 @@ +import { Injectable } from '@nestjs/common'; +import { Command, CommandRunner } from 'nest-commander'; +import { AuthService } from './auth/auth.service'; + +@Injectable() +@Command({ name: 'setup-admin', description: 'Set up the first admin user' }) +export class SetupAdminCommand extends CommandRunner { + constructor(private readonly authService: AuthService) { + super(); + } + + async run(passedParams: string[]): Promise { + if (passedParams.length < 1) { + console.error('Please provide a user UID'); + return; + } + + const uid = passedParams[0]; + + try { + await this.authService.setUserRole(uid, 'admin'); + console.log(`User ${uid} has been set as admin`); + } catch (error) { + console.error('Error setting admin role:', error); + } + } +} diff --git a/bizmatch-server/src/user/user.controller.ts b/bizmatch-server/src/user/user.controller.ts index 8500330..d399df9 100644 --- a/bizmatch-server/src/user/user.controller.ts +++ b/bizmatch-server/src/user/user.controller.ts @@ -4,6 +4,7 @@ import { Logger } from 'winston'; import { ZodError } from 'zod'; import { FileService } from '../file/file.service'; +import { AdminGuard } from 'src/jwt-auth/admin-auth.guard'; import { AuthGuard } from 'src/jwt-auth/auth.guard'; import { OptionalAuthGuard } from 'src/jwt-auth/optional-auth.guard'; import { User } from '../models/db.model'; @@ -31,11 +32,11 @@ export class UserController { const user = await this.userService.getUserById(id); return user; } - // @UseGuards(AdminAuthGuard) - // @Get('user/all') - // async getAllUser(): Promise { - // return await this.userService.getAllUser(); - // } + @UseGuards(AdminGuard) + @Get('user/all') + async getAllUser(): Promise { + return await this.userService.getAllUser(); + } @UseGuards(OptionalAuthGuard) @Post() diff --git a/bizmatch/src/app/components/header/header.component.html b/bizmatch/src/app/components/header/header.component.html index 5eae35a..e087b4a 100644 --- a/bizmatch/src/app/components/header/header.component.html +++ b/bizmatch/src/app/components/header/header.component.html @@ -68,7 +68,7 @@
  • Account
  • - @if(user.customerType==='professional' || user.customerType==='seller' || isAdmin()){ + @if(user.customerType==='professional' || user.customerType==='seller' || (authService.isAdmin() | async)){
  • @if(user.customerSubType==='broker'){ Logout
  • - @if(isAdmin()){ + @if(authService.isAdmin() | async){