From 60253c3a3b35ebffb34950901dbd10a222c1b2da Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sun, 15 Dec 2024 10:28:49 +0100 Subject: [PATCH] Extend permissions --- .../market-data/market-data.controller.ts | 72 +++++++++++++++---- .../market-data/market-data.module.ts | 3 +- apps/api/src/app/import/import.service.ts | 3 +- .../enhanced-symbol-profile.interface.ts | 1 + libs/common/src/lib/permissions.ts | 9 +++ 5 files changed, 74 insertions(+), 14 deletions(-) diff --git a/apps/api/src/app/endpoints/market-data/market-data.controller.ts b/apps/api/src/app/endpoints/market-data/market-data.controller.ts index 7f669cd28..b4aef807a 100644 --- a/apps/api/src/app/endpoints/market-data/market-data.controller.ts +++ b/apps/api/src/app/endpoints/market-data/market-data.controller.ts @@ -1,5 +1,6 @@ import { AdminService } from '@ghostfolio/api/app/admin/admin.service'; import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; +import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; import { MarketDataDetailsResponse } from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { RequestWithUser } from '@ghostfolio/common/types'; @@ -27,7 +28,8 @@ export class MarketDataController { public constructor( private readonly adminService: AdminService, private readonly marketDataService: MarketDataService, - @Inject(REQUEST) private readonly request: RequestWithUser + @Inject(REQUEST) private readonly request: RequestWithUser, + private readonly symbolProfileService: SymbolProfileService ) {} @Get(':dataSource/:symbol') @@ -36,12 +38,35 @@ export class MarketDataController { @Param('dataSource') dataSource: DataSource, @Param('symbol') symbol: string ): Promise { - if ( - !hasPermission(this.request.user.permissions, permissions.readMarketData) - ) { + const [assetProfile] = await this.symbolProfileService.getSymbolProfiles([ + { dataSource, symbol } + ]); + + if (!assetProfile) { throw new HttpException( - getReasonPhrase(StatusCodes.FORBIDDEN), - StatusCodes.FORBIDDEN + getReasonPhrase(StatusCodes.NOT_FOUND), + StatusCodes.NOT_FOUND + ); + } + + const canReadAllAssetProfiles = hasPermission( + this.request.user.permissions, + permissions.readMarketData + ); + + const canReadOwnAssetProfile = + assetProfile.userId === this.request.user.id && + hasPermission( + this.request.user.permissions, + permissions.readMarketDataOfOwnAssetProfile + ); + + if (!canReadAllAssetProfiles && !canReadOwnAssetProfile) { + throw new HttpException( + assetProfile.userId + ? getReasonPhrase(StatusCodes.NOT_FOUND) + : getReasonPhrase(StatusCodes.FORBIDDEN), + assetProfile.userId ? StatusCodes.NOT_FOUND : StatusCodes.FORBIDDEN ); } @@ -55,16 +80,39 @@ export class MarketDataController { @Param('dataSource') dataSource: DataSource, @Param('symbol') symbol: string ) { - if ( - !hasPermission( + const [assetProfile] = await this.symbolProfileService.getSymbolProfiles([ + { dataSource, symbol } + ]); + + if (!assetProfile) { + throw new HttpException( + getReasonPhrase(StatusCodes.NOT_FOUND), + StatusCodes.NOT_FOUND + ); + } + + const canUpsertAllAssetProfiles = + hasPermission( this.request.user.permissions, permissions.createMarketData - ) || - !hasPermission( + ) && + hasPermission( this.request.user.permissions, permissions.updateMarketData - ) - ) { + ); + + const canUpsertOwnAssetProfile = + assetProfile.userId === this.request.user.id && + hasPermission( + this.request.user.permissions, + permissions.createMarketDataOfOwnAssetProfile + ) && + hasPermission( + this.request.user.permissions, + permissions.updateMarketDataOfOwnAssetProfile + ); + + if (!canUpsertAllAssetProfiles && !canUpsertOwnAssetProfile) { throw new HttpException( getReasonPhrase(StatusCodes.FORBIDDEN), StatusCodes.FORBIDDEN diff --git a/apps/api/src/app/endpoints/market-data/market-data.module.ts b/apps/api/src/app/endpoints/market-data/market-data.module.ts index 2421020ec..2050889fd 100644 --- a/apps/api/src/app/endpoints/market-data/market-data.module.ts +++ b/apps/api/src/app/endpoints/market-data/market-data.module.ts @@ -1,5 +1,6 @@ import { AdminModule } from '@ghostfolio/api/app/admin/admin.module'; import { MarketDataModule as MarketDataServiceModule } from '@ghostfolio/api/services/market-data/market-data.module'; +import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile/symbol-profile.module'; import { Module } from '@nestjs/common'; @@ -7,6 +8,6 @@ import { MarketDataController } from './market-data.controller'; @Module({ controllers: [MarketDataController], - imports: [AdminModule, MarketDataServiceModule] + imports: [AdminModule, MarketDataServiceModule, SymbolProfileModule] }) export class MarketDataModule {} diff --git a/apps/api/src/app/import/import.service.ts b/apps/api/src/app/import/import.service.ts index 3b7290b43..7f1f90724 100644 --- a/apps/api/src/app/import/import.service.ts +++ b/apps/api/src/app/import/import.service.ts @@ -551,7 +551,8 @@ export class ImportService { holdings: undefined, id: undefined, sectors: undefined, - updatedAt: undefined + updatedAt: undefined, + userId: undefined } }; } diff --git a/libs/common/src/lib/interfaces/enhanced-symbol-profile.interface.ts b/libs/common/src/lib/interfaces/enhanced-symbol-profile.interface.ts index e7fc4c5b5..ce585c6a3 100644 --- a/libs/common/src/lib/interfaces/enhanced-symbol-profile.interface.ts +++ b/libs/common/src/lib/interfaces/enhanced-symbol-profile.interface.ts @@ -30,4 +30,5 @@ export interface EnhancedSymbolProfile { symbolMapping?: { [key: string]: string }; updatedAt: Date; url?: string; + userId?: string; } diff --git a/libs/common/src/lib/permissions.ts b/libs/common/src/lib/permissions.ts index eeab226ec..d6676ec4e 100644 --- a/libs/common/src/lib/permissions.ts +++ b/libs/common/src/lib/permissions.ts @@ -11,6 +11,7 @@ export const permissions = { createAccountBalance: 'createAccountBalance', createApiKey: 'createApiKey', createMarketData: 'createMarketData', + createMarketDataOfOwnAssetProfile: 'createMarketDataOfOwnAssetProfile', createOrder: 'createOrder', createPlatform: 'createPlatform', createTag: 'createTag', @@ -35,6 +36,7 @@ export const permissions = { enableSystemMessage: 'enableSystemMessage', impersonateAllUsers: 'impersonateAllUsers', readMarketData: 'readMarketData', + readMarketDataOfOwnAssetProfile: 'readMarketDataOfOwnAssetProfile', readPlatforms: 'readPlatforms', readTags: 'readTags', reportDataGlitch: 'reportDataGlitch', @@ -42,6 +44,7 @@ export const permissions = { updateAccount: 'updateAccount', updateAuthDevice: 'updateAuthDevice', updateMarketData: 'updateMarketData', + updateMarketDataOfOwnAssetProfile: 'updateMarketDataOfOwnAssetProfile', updateOrder: 'updateOrder', updatePlatform: 'updatePlatform', updateTag: 'updateTag', @@ -61,6 +64,7 @@ export function getPermissions(aRole: Role): string[] { permissions.createAccountBalance, permissions.deleteAccountBalance, permissions.createMarketData, + permissions.createMarketDataOfOwnAssetProfile, permissions.createOrder, permissions.createPlatform, permissions.createTag, @@ -73,11 +77,13 @@ export function getPermissions(aRole: Role): string[] { permissions.deleteTag, permissions.deleteUser, permissions.readMarketData, + permissions.readMarketDataOfOwnAssetProfile, permissions.readPlatforms, permissions.readTags, permissions.updateAccount, permissions.updateAuthDevice, permissions.updateMarketData, + permissions.updateMarketDataOfOwnAssetProfile, permissions.updateOrder, permissions.updatePlatform, permissions.updateTag, @@ -99,6 +105,7 @@ export function getPermissions(aRole: Role): string[] { permissions.createAccess, permissions.createAccount, permissions.createAccountBalance, + permissions.createMarketDataOfOwnAssetProfile, permissions.createOrder, permissions.deleteAccess, permissions.deleteAccount, @@ -106,8 +113,10 @@ export function getPermissions(aRole: Role): string[] { permissions.deleteAuthDevice, permissions.deleteOrder, permissions.deleteOwnUser, + permissions.readMarketDataOfOwnAssetProfile, permissions.updateAccount, permissions.updateAuthDevice, + permissions.updateMarketDataOfOwnAssetProfile, permissions.updateOrder, permissions.updateUserSettings, permissions.updateViewMode