From 0ea08eb060b67f8bc55e37e28658e2c81eafb5a6 Mon Sep 17 00:00:00 2001 From: Thomas <4159106+dtslvr@users.noreply.github.com> Date: Tue, 7 Dec 2021 20:10:54 +0100 Subject: [PATCH] Update permissions dynamically --- apps/api/src/app/access/access.controller.ts | 16 ++------- .../api/src/app/account/account.controller.ts | 21 +++-------- apps/api/src/app/admin/admin.controller.ts | 20 +++++------ .../app/auth-device/auth-device.controller.ts | 8 ++--- apps/api/src/app/auth/auth.module.ts | 6 ++-- apps/api/src/app/order/order.controller.ts | 21 +++-------- apps/api/src/app/user/user.controller.ts | 36 ++++++++++++------- apps/api/src/app/user/user.module.ts | 2 ++ apps/api/src/app/user/user.service.ts | 33 +++++++++++++---- .../admin-overview.component.ts | 6 +++- .../components/header/header.component.html | 2 +- .../transactions-table.component.html | 7 +++- .../transactions-table.component.ts | 1 + .../src/app/core/http-response.interceptor.ts | 28 +++++++++++---- .../pages/accounts/accounts-page.component.ts | 2 +- .../transactions-page.component.ts | 4 +-- .../transactions/transactions-page.html | 1 + .../pages/register/register-page.component.ts | 4 +++ .../src/app/pages/register/register-page.html | 2 +- libs/common/src/lib/permissions.ts | 15 ++++---- 20 files changed, 127 insertions(+), 108 deletions(-) diff --git a/apps/api/src/app/access/access.controller.ts b/apps/api/src/app/access/access.controller.ts index bbf5423cc..405c53e68 100644 --- a/apps/api/src/app/access/access.controller.ts +++ b/apps/api/src/app/access/access.controller.ts @@ -1,9 +1,5 @@ import { Access } from '@ghostfolio/common/interfaces'; -import { - getPermissions, - hasPermission, - permissions -} from '@ghostfolio/common/permissions'; +import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import type { RequestWithUser } from '@ghostfolio/common/types'; import { Body, @@ -66,10 +62,7 @@ export class AccessController { @Body() data: CreateAccessDto ): Promise { if ( - !hasPermission( - getPermissions(this.request.user.role), - permissions.createAccess - ) + !hasPermission(this.request.user.permissions, permissions.createAccess) ) { throw new HttpException( getReasonPhrase(StatusCodes.FORBIDDEN), @@ -86,10 +79,7 @@ export class AccessController { @UseGuards(AuthGuard('jwt')) public async deleteAccess(@Param('id') id: string): Promise { if ( - !hasPermission( - getPermissions(this.request.user.role), - permissions.deleteAccess - ) + !hasPermission(this.request.user.permissions, permissions.deleteAccess) ) { throw new HttpException( getReasonPhrase(StatusCodes.FORBIDDEN), diff --git a/apps/api/src/app/account/account.controller.ts b/apps/api/src/app/account/account.controller.ts index a832bfca9..54fd912fd 100644 --- a/apps/api/src/app/account/account.controller.ts +++ b/apps/api/src/app/account/account.controller.ts @@ -6,11 +6,7 @@ import { } from '@ghostfolio/api/helper/object.helper'; import { ImpersonationService } from '@ghostfolio/api/services/impersonation.service'; import { Accounts } from '@ghostfolio/common/interfaces'; -import { - getPermissions, - hasPermission, - permissions -} from '@ghostfolio/common/permissions'; +import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import type { RequestWithUser } from '@ghostfolio/common/types'; import { Body, @@ -48,10 +44,7 @@ export class AccountController { @UseGuards(AuthGuard('jwt')) public async deleteAccount(@Param('id') id: string): Promise { if ( - !hasPermission( - getPermissions(this.request.user.role), - permissions.deleteAccount - ) + !hasPermission(this.request.user.permissions, permissions.deleteAccount) ) { throw new HttpException( getReasonPhrase(StatusCodes.FORBIDDEN), @@ -143,10 +136,7 @@ export class AccountController { @Body() data: CreateAccountDto ): Promise { if ( - !hasPermission( - getPermissions(this.request.user.role), - permissions.createAccount - ) + !hasPermission(this.request.user.permissions, permissions.createAccount) ) { throw new HttpException( getReasonPhrase(StatusCodes.FORBIDDEN), @@ -183,10 +173,7 @@ export class AccountController { @UseGuards(AuthGuard('jwt')) public async update(@Param('id') id: string, @Body() data: UpdateAccountDto) { if ( - !hasPermission( - getPermissions(this.request.user.role), - permissions.updateAccount - ) + !hasPermission(this.request.user.permissions, permissions.updateAccount) ) { throw new HttpException( getReasonPhrase(StatusCodes.FORBIDDEN), diff --git a/apps/api/src/app/admin/admin.controller.ts b/apps/api/src/app/admin/admin.controller.ts index 4ec509f04..ef6753894 100644 --- a/apps/api/src/app/admin/admin.controller.ts +++ b/apps/api/src/app/admin/admin.controller.ts @@ -6,11 +6,7 @@ import { AdminMarketData, AdminMarketDataDetails } from '@ghostfolio/common/interfaces'; -import { - getPermissions, - hasPermission, - permissions -} from '@ghostfolio/common/permissions'; +import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import type { RequestWithUser } from '@ghostfolio/common/types'; import { Body, @@ -44,7 +40,7 @@ export class AdminController { public async getAdminData(): Promise { if ( !hasPermission( - getPermissions(this.request.user.role), + this.request.user.permissions, permissions.accessAdminControl ) ) { @@ -62,7 +58,7 @@ export class AdminController { public async gatherMax(): Promise { if ( !hasPermission( - getPermissions(this.request.user.role), + this.request.user.permissions, permissions.accessAdminControl ) ) { @@ -86,7 +82,7 @@ export class AdminController { ): Promise { if ( !hasPermission( - getPermissions(this.request.user.role), + this.request.user.permissions, permissions.accessAdminControl ) ) { @@ -106,7 +102,7 @@ export class AdminController { public async gatherProfileData(): Promise { if ( !hasPermission( - getPermissions(this.request.user.role), + this.request.user.permissions, permissions.accessAdminControl ) ) { @@ -126,7 +122,7 @@ export class AdminController { public async getMarketData(): Promise { if ( !hasPermission( - getPermissions(this.request.user.role), + this.request.user.permissions, permissions.accessAdminControl ) ) { @@ -146,7 +142,7 @@ export class AdminController { ): Promise { if ( !hasPermission( - getPermissions(this.request.user.role), + this.request.user.permissions, permissions.accessAdminControl ) ) { @@ -167,7 +163,7 @@ export class AdminController { ) { if ( !hasPermission( - getPermissions(this.request.user.role), + this.request.user.permissions, permissions.accessAdminControl ) ) { diff --git a/apps/api/src/app/auth-device/auth-device.controller.ts b/apps/api/src/app/auth-device/auth-device.controller.ts index 89a44dd2f..33eae0cc0 100644 --- a/apps/api/src/app/auth-device/auth-device.controller.ts +++ b/apps/api/src/app/auth-device/auth-device.controller.ts @@ -1,9 +1,5 @@ import { AuthDeviceService } from '@ghostfolio/api/app/auth-device/auth-device.service'; -import { - getPermissions, - hasPermission, - permissions -} from '@ghostfolio/common/permissions'; +import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import type { RequestWithUser } from '@ghostfolio/common/types'; import { Controller, @@ -29,7 +25,7 @@ export class AuthDeviceController { public async deleteAuthDevice(@Param('id') id: string): Promise { if ( !hasPermission( - getPermissions(this.request.user.role), + this.request.user.permissions, permissions.deleteAuthDevice ) ) { diff --git a/apps/api/src/app/auth/auth.module.ts b/apps/api/src/app/auth/auth.module.ts index d573f91fe..8a59ff82a 100644 --- a/apps/api/src/app/auth/auth.module.ts +++ b/apps/api/src/app/auth/auth.module.ts @@ -1,7 +1,7 @@ import { AuthDeviceService } from '@ghostfolio/api/app/auth-device/auth-device.service'; import { WebAuthService } from '@ghostfolio/api/app/auth/web-auth.service'; import { SubscriptionModule } from '@ghostfolio/api/app/subscription/subscription.module'; -import { UserService } from '@ghostfolio/api/app/user/user.service'; +import { UserModule } from '@ghostfolio/api/app/user/user.module'; import { ConfigurationService } from '@ghostfolio/api/services/configuration.service'; import { PrismaService } from '@ghostfolio/api/services/prisma.service'; import { Module } from '@nestjs/common'; @@ -19,7 +19,8 @@ import { JwtStrategy } from './jwt.strategy'; secret: process.env.JWT_SECRET_KEY, signOptions: { expiresIn: '180 days' } }), - SubscriptionModule + SubscriptionModule, + UserModule ], providers: [ AuthDeviceService, @@ -28,7 +29,6 @@ import { JwtStrategy } from './jwt.strategy'; GoogleStrategy, JwtStrategy, PrismaService, - UserService, WebAuthService ] }) diff --git a/apps/api/src/app/order/order.controller.ts b/apps/api/src/app/order/order.controller.ts index d2c5d02e6..5281529b0 100644 --- a/apps/api/src/app/order/order.controller.ts +++ b/apps/api/src/app/order/order.controller.ts @@ -1,11 +1,7 @@ import { UserService } from '@ghostfolio/api/app/user/user.service'; import { nullifyValuesInObjects } from '@ghostfolio/api/helper/object.helper'; import { ImpersonationService } from '@ghostfolio/api/services/impersonation.service'; -import { - getPermissions, - hasPermission, - permissions -} from '@ghostfolio/common/permissions'; +import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import type { RequestWithUser } from '@ghostfolio/common/types'; import { Body, @@ -43,10 +39,7 @@ export class OrderController { @UseGuards(AuthGuard('jwt')) public async deleteOrder(@Param('id') id: string): Promise { if ( - !hasPermission( - getPermissions(this.request.user.role), - permissions.deleteOrder - ) + !hasPermission(this.request.user.permissions, permissions.deleteOrder) ) { throw new HttpException( getReasonPhrase(StatusCodes.FORBIDDEN), @@ -115,10 +108,7 @@ export class OrderController { @UseGuards(AuthGuard('jwt')) public async createOrder(@Body() data: CreateOrderDto): Promise { if ( - !hasPermission( - getPermissions(this.request.user.role), - permissions.createOrder - ) + !hasPermission(this.request.user.permissions, permissions.createOrder) ) { throw new HttpException( getReasonPhrase(StatusCodes.FORBIDDEN), @@ -161,10 +151,7 @@ export class OrderController { @UseGuards(AuthGuard('jwt')) public async update(@Param('id') id: string, @Body() data: UpdateOrderDto) { if ( - !hasPermission( - getPermissions(this.request.user.role), - permissions.updateOrder - ) + !hasPermission(this.request.user.permissions, permissions.updateOrder) ) { throw new HttpException( getReasonPhrase(StatusCodes.FORBIDDEN), diff --git a/apps/api/src/app/user/user.controller.ts b/apps/api/src/app/user/user.controller.ts index f62599dfd..0a7ed21cf 100644 --- a/apps/api/src/app/user/user.controller.ts +++ b/apps/api/src/app/user/user.controller.ts @@ -1,7 +1,10 @@ +import { ConfigurationService } from '@ghostfolio/api/services/configuration.service'; +import { PropertyService } from '@ghostfolio/api/services/property/property.service'; +import { PROPERTY_IS_READ_ONLY_MODE } from '@ghostfolio/common/config'; import { User } from '@ghostfolio/common/interfaces'; import { - getPermissions, hasPermission, + hasRole, permissions } from '@ghostfolio/common/permissions'; import type { RequestWithUser } from '@ghostfolio/common/types'; @@ -20,7 +23,7 @@ import { import { REQUEST } from '@nestjs/core'; import { JwtService } from '@nestjs/jwt'; import { AuthGuard } from '@nestjs/passport'; -import { Provider } from '@prisma/client'; +import { Provider, Role } from '@prisma/client'; import { User as UserModel } from '@prisma/client'; import { StatusCodes, getReasonPhrase } from 'http-status-codes'; @@ -34,7 +37,9 @@ import { UserService } from './user.service'; @Controller('user') export class UserController { public constructor( + private readonly configurationService: ConfigurationService, private jwtService: JwtService, + private readonly propertyService: PropertyService, @Inject(REQUEST) private readonly request: RequestWithUser, private readonly userService: UserService ) {} @@ -43,10 +48,7 @@ export class UserController { @UseGuards(AuthGuard('jwt')) public async deleteUser(@Param('id') id: string): Promise { if ( - !hasPermission( - getPermissions(this.request.user.role), - permissions.deleteUser - ) || + !hasPermission(this.request.user.permissions, permissions.deleteUser) || id === this.request.user.id ) { throw new HttpException( @@ -68,6 +70,19 @@ export class UserController { @Post() public async signupUser(): Promise { + if (this.configurationService.get('ENABLE_FEATURE_READ_ONLY_MODE')) { + const isReadOnlyMode = (await this.propertyService.getByKey( + PROPERTY_IS_READ_ONLY_MODE + )) as boolean; + + if (isReadOnlyMode) { + throw new HttpException( + getReasonPhrase(StatusCodes.FORBIDDEN), + StatusCodes.FORBIDDEN + ); + } + } + const { accessToken, id } = await this.userService.createUser({ provider: Provider.ANONYMOUS }); @@ -85,7 +100,7 @@ export class UserController { public async updateUserSetting(@Body() data: UpdateUserSettingDto) { if ( !hasPermission( - getPermissions(this.request.user.role), + this.request.user.permissions, permissions.updateUserSettings ) ) { @@ -111,7 +126,7 @@ export class UserController { public async updateUserSettings(@Body() data: UpdateUserSettingsDto) { if ( !hasPermission( - getPermissions(this.request.user.role), + this.request.user.permissions, permissions.updateUserSettings ) ) { @@ -127,10 +142,7 @@ export class UserController { }; if ( - hasPermission( - getPermissions(this.request.user.role), - permissions.updateViewMode - ) + hasPermission(this.request.user.permissions, permissions.updateViewMode) ) { userSettings.viewMode = data.viewMode; } diff --git a/apps/api/src/app/user/user.module.ts b/apps/api/src/app/user/user.module.ts index 7d2fc3d8e..ffbdc80db 100644 --- a/apps/api/src/app/user/user.module.ts +++ b/apps/api/src/app/user/user.module.ts @@ -1,6 +1,7 @@ import { SubscriptionModule } from '@ghostfolio/api/app/subscription/subscription.module'; import { ConfigurationService } from '@ghostfolio/api/services/configuration.service'; import { PrismaService } from '@ghostfolio/api/services/prisma.service'; +import { PropertyModule } from '@ghostfolio/api/services/property/property.module'; import { Module } from '@nestjs/common'; import { JwtModule } from '@nestjs/jwt'; @@ -13,6 +14,7 @@ import { UserService } from './user.service'; secret: process.env.JWT_SECRET_KEY, signOptions: { expiresIn: '30 days' } }), + PropertyModule, SubscriptionModule ], controllers: [UserController], diff --git a/apps/api/src/app/user/user.service.ts b/apps/api/src/app/user/user.service.ts index f163b9656..13a5b7d1d 100644 --- a/apps/api/src/app/user/user.service.ts +++ b/apps/api/src/app/user/user.service.ts @@ -1,7 +1,12 @@ import { SubscriptionService } from '@ghostfolio/api/app/subscription/subscription.service'; import { ConfigurationService } from '@ghostfolio/api/services/configuration.service'; import { PrismaService } from '@ghostfolio/api/services/prisma.service'; -import { baseCurrency, locale } from '@ghostfolio/common/config'; +import { PropertyService } from '@ghostfolio/api/services/property/property.service'; +import { + PROPERTY_IS_READ_ONLY_MODE, + baseCurrency, + locale +} from '@ghostfolio/common/config'; import { User as IUser, UserWithSettings } from '@ghostfolio/common/interfaces'; import { getPermissions, @@ -24,6 +29,7 @@ export class UserService { public constructor( private readonly configurationService: ConfigurationService, private readonly prismaService: PrismaService, + private readonly propertyService: PropertyService, private readonly subscriptionService: SubscriptionService ) {} @@ -78,17 +84,30 @@ export class UserService { const user: UserWithSettings = userFromDatabase; - const currentPermissions = getPermissions(userFromDatabase.role); + let currentPermissions = getPermissions(userFromDatabase.role); if (this.configurationService.get('ENABLE_FEATURE_FEAR_AND_GREED_INDEX')) { currentPermissions.push(permissions.accessFearAndGreedIndex); } - if ( - this.configurationService.get('ENABLE_FEATURE_READ_ONLY_MODE') && - hasRole(user, Role.ADMIN) - ) { - currentPermissions.push(permissions.toggleReadOnlyMode); + if (this.configurationService.get('ENABLE_FEATURE_READ_ONLY_MODE')) { + if (hasRole(user, Role.ADMIN)) { + currentPermissions.push(permissions.toggleReadOnlyMode); + } + + const isReadOnlyMode = (await this.propertyService.getByKey( + PROPERTY_IS_READ_ONLY_MODE + )) as boolean; + + if (isReadOnlyMode) { + currentPermissions = currentPermissions.filter((permission) => { + return !( + permission.startsWith('create') || + permission.startsWith('delete') || + permission.startsWith('update') + ); + }); + } } user.permissions = currentPermissions; diff --git a/apps/client/src/app/components/admin-overview/admin-overview.component.ts b/apps/client/src/app/components/admin-overview/admin-overview.component.ts index 3725dd269..51105632b 100644 --- a/apps/client/src/app/components/admin-overview/admin-overview.component.ts +++ b/apps/client/src/app/components/admin-overview/admin-overview.component.ts @@ -242,6 +242,10 @@ export class AdminOverviewComponent implements OnDestroy, OnInit { value: aValue ? 'true' : '' }) .pipe(takeUntil(this.unsubscribeSubject)) - .subscribe(() => {}); + .subscribe(() => { + setTimeout(() => { + window.location.reload(); + }, 300); + }); } } diff --git a/apps/client/src/app/components/header/header.component.html b/apps/client/src/app/components/header/header.component.html index 593ac23f3..4aeab5574 100644 --- a/apps/client/src/app/components/header/header.component.html +++ b/apps/client/src/app/components/header/header.component.html @@ -270,7 +270,7 @@ Sign In -
+
diff --git a/apps/client/src/app/components/transactions-table/transactions-table.component.ts b/apps/client/src/app/components/transactions-table/transactions-table.component.ts index e9a657406..ab2f18e10 100644 --- a/apps/client/src/app/components/transactions-table/transactions-table.component.ts +++ b/apps/client/src/app/components/transactions-table/transactions-table.component.ts @@ -43,6 +43,7 @@ export class TransactionsTableComponent { @Input() baseCurrency: string; @Input() deviceType: string; + @Input() hasPermissionToCreateOrder: boolean; @Input() hasPermissionToImportOrders: boolean; @Input() locale: string; @Input() showActions: boolean; diff --git a/apps/client/src/app/core/http-response.interceptor.ts b/apps/client/src/app/core/http-response.interceptor.ts index afed17f4e..ea0cf8d2e 100644 --- a/apps/client/src/app/core/http-response.interceptor.ts +++ b/apps/client/src/app/core/http-response.interceptor.ts @@ -15,22 +15,28 @@ import { } from '@angular/material/snack-bar'; import { Router } from '@angular/router'; import { WebAuthnService } from '@ghostfolio/client/services/web-authn.service'; +import { InfoItem } from '@ghostfolio/common/interfaces'; import { StatusCodes } from 'http-status-codes'; import { Observable, throwError } from 'rxjs'; import { catchError, tap } from 'rxjs/operators'; -import { TokenStorageService } from '../services/token-storage.service'; +import { DataService } from '@ghostfolio/client/services/data.service'; +import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service'; @Injectable() export class HttpResponseInterceptor implements HttpInterceptor { + public info: InfoItem; public snackBarRef: MatSnackBarRef; public constructor( + private dataService: DataService, private router: Router, private tokenStorageService: TokenStorageService, private snackBar: MatSnackBar, private webAuthnService: WebAuthnService - ) {} + ) { + this.info = this.dataService.fetchInfo(); + } public intercept( request: HttpRequest, @@ -63,11 +69,19 @@ export class HttpResponseInterceptor implements HttpInterceptor { catchError((error: HttpErrorResponse) => { if (error.status === StatusCodes.FORBIDDEN) { if (!this.snackBarRef) { - this.snackBarRef = this.snackBar.open( - 'This feature requires a subscription.', - 'Upgrade Plan', - { duration: 6000 } - ); + if (this.info.isReadOnlyMode) { + this.snackBarRef = this.snackBar.open( + 'This feature is currently unavailable. Please try again later.', + undefined, + { duration: 6000 } + ); + } else { + this.snackBarRef = this.snackBar.open( + 'This feature requires a subscription.', + 'Upgrade Plan', + { duration: 6000 } + ); + } this.snackBarRef.afterDismissed().subscribe(() => { this.snackBarRef = undefined; diff --git a/apps/client/src/app/pages/accounts/accounts-page.component.ts b/apps/client/src/app/pages/accounts/accounts-page.component.ts index 81ede2dc1..53191835c 100644 --- a/apps/client/src/app/pages/accounts/accounts-page.component.ts +++ b/apps/client/src/app/pages/accounts/accounts-page.component.ts @@ -51,7 +51,7 @@ export class AccountsPageComponent implements OnDestroy, OnInit { this.route.queryParams .pipe(takeUntil(this.unsubscribeSubject)) .subscribe((params) => { - if (params['createDialog']) { + if (params['createDialog'] && this.hasPermissionToCreateAccount) { this.openCreateAccountDialog(); } else if (params['editDialog']) { if (this.accounts) { diff --git a/apps/client/src/app/pages/portfolio/transactions/transactions-page.component.ts b/apps/client/src/app/pages/portfolio/transactions/transactions-page.component.ts index 0b79782a3..1827e156e 100644 --- a/apps/client/src/app/pages/portfolio/transactions/transactions-page.component.ts +++ b/apps/client/src/app/pages/portfolio/transactions/transactions-page.component.ts @@ -61,7 +61,7 @@ export class TransactionsPageComponent implements OnDestroy, OnInit { this.routeQueryParams = route.queryParams .pipe(takeUntil(this.unsubscribeSubject)) .subscribe((params) => { - if (params['createDialog']) { + if (params['createDialog'] && this.hasPermissionToCreateOrder) { this.openCreateTransactionDialog(); } else if (params['editDialog']) { if (this.transactions) { @@ -130,7 +130,7 @@ export class TransactionsPageComponent implements OnDestroy, OnInit { .subscribe((response) => { this.transactions = response; - if (this.transactions?.length <= 0) { + if (this.hasPermissionToCreateOrder && this.transactions?.length <= 0) { this.router.navigate([], { queryParams: { createDialog: true } }); } diff --git a/apps/client/src/app/pages/portfolio/transactions/transactions-page.html b/apps/client/src/app/pages/portfolio/transactions/transactions-page.html index 5288d7313..f97727e38 100644 --- a/apps/client/src/app/pages/portfolio/transactions/transactions-page.html +++ b/apps/client/src/app/pages/portfolio/transactions/transactions-page.html @@ -5,6 +5,7 @@ (); @@ -37,6 +39,8 @@ export class RegisterPageComponent implements OnDestroy, OnInit { private router: Router, private tokenStorageService: TokenStorageService ) { + this.info = this.dataService.fetchInfo(); + this.tokenStorageService.signOut(); } diff --git a/apps/client/src/app/pages/register/register-page.html b/apps/client/src/app/pages/register/register-page.html index 5806e8f4c..96d49251b 100644 --- a/apps/client/src/app/pages/register/register-page.html +++ b/apps/client/src/app/pages/register/register-page.html @@ -22,7 +22,7 @@ color="primary" i18n mat-flat-button - [disabled]="!demoAuthToken" + [disabled]="!demoAuthToken || info?.isReadOnlyMode" (click)="createAccount()" > Create Account diff --git a/libs/common/src/lib/permissions.ts b/libs/common/src/lib/permissions.ts index b09353bfa..dc574f7ef 100644 --- a/libs/common/src/lib/permissions.ts +++ b/libs/common/src/lib/permissions.ts @@ -1,4 +1,5 @@ import { Role } from '@prisma/client'; + import { UserWithSettings } from './interfaces'; export const permissions = { @@ -27,13 +28,6 @@ export const permissions = { updateViewMode: 'updateViewMode' }; -export function hasPermission( - aPermissions: string[] = [], - aPermission: string -) { - return aPermissions.includes(aPermission); -} - export function getPermissions(aRole: Role): string[] { switch (aRole) { case 'ADMIN': @@ -78,6 +72,13 @@ export function getPermissions(aRole: Role): string[] { } } +export function hasPermission( + aPermissions: string[] = [], + aPermission: string +) { + return aPermissions.includes(aPermission); +} + export function hasRole(aUser: UserWithSettings, aRole: Role): boolean { return aUser?.role === aRole; }