Browse Source

Fix percentage values in impersonation mode with unrestricted access

pull/7522/head
Thomas Kaul 4 weeks ago
parent
commit
38e8e299d0
  1. 10
      apps/api/src/app/portfolio/portfolio.controller.ts
  2. 2
      apps/api/src/interceptors/redact-values-in-response/redact-values-in-response.interceptor.ts
  3. 11
      apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts
  4. 4
      apps/client/src/app/components/account-detail-dialog/account-detail-dialog.html
  5. 2
      apps/client/src/app/components/account-detail-dialog/interfaces/interfaces.ts
  6. 6
      apps/client/src/app/pages/accounts/accounts-page.component.ts
  7. 19
      apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts
  8. 17
      apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts
  9. 12
      apps/client/src/app/pages/portfolio/analysis/analysis-page.html
  10. 12
      libs/common/src/lib/permissions.ts

10
apps/api/src/app/portfolio/portfolio.controller.ts

@ -136,7 +136,7 @@ export class PortfolioController {
if (
hasReadRestrictedAccessPermission({
impersonationId,
user: this.request.user
accesses: this.request.user?.accessesGet
}) ||
isRestrictedView(this.request.user)
) {
@ -180,7 +180,7 @@ export class PortfolioController {
hasDetails === false ||
hasReadRestrictedAccessPermission({
impersonationId,
user: this.request.user
accesses: this.request.user?.accessesGet
}) ||
isRestrictedView(this.request.user)
) {
@ -374,7 +374,7 @@ export class PortfolioController {
if (
hasReadRestrictedAccessPermission({
impersonationId,
user: this.request.user
accesses: this.request.user?.accessesGet
}) ||
isRestrictedView(this.request.user)
) {
@ -503,7 +503,7 @@ export class PortfolioController {
if (
hasReadRestrictedAccessPermission({
impersonationId,
user: this.request.user
accesses: this.request.user?.accessesGet
}) ||
isRestrictedView(this.request.user)
) {
@ -578,7 +578,7 @@ export class PortfolioController {
if (
hasReadRestrictedAccessPermission({
impersonationId,
user: this.request.user
accesses: this.request.user?.accessesGet
}) ||
isRestrictedView(this.request.user) ||
this.request.user.settings.settings.viewMode === 'ZEN'

2
apps/api/src/interceptors/redact-values-in-response/redact-values-in-response.interceptor.ts

@ -38,7 +38,7 @@ export class RedactValuesInResponseInterceptor<T> implements NestInterceptor<
if (
hasReadRestrictedAccessPermission({
impersonationId,
user
accesses: user?.accessesGet
}) ||
isRestrictedView(user)
) {

11
apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts

@ -14,7 +14,11 @@ import {
PortfolioPosition,
User
} from '@ghostfolio/common/interfaces';
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import {
hasPermission,
hasReadRestrictedAccessPermission,
permissions
} from '@ghostfolio/common/permissions';
import { GfAccountBalancesComponent } from '@ghostfolio/ui/account-balances';
import { GfActivitiesTableComponent } from '@ghostfolio/ui/activities-table';
import { GfDialogFooterComponent } from '@ghostfolio/ui/dialog-footer';
@ -225,7 +229,10 @@ export class GfAccountDetailDialogComponent implements OnInit {
protected showValuesInPercentage() {
return (
this.data.hasImpersonationId || this.user?.settings?.isRestrictedView
hasReadRestrictedAccessPermission({
accesses: this.user?.access,
impersonationId: this.data.impersonationId
}) || this.user?.settings?.isRestrictedView
);
}

4
apps/client/src/app/components/account-detail-dialog/account-detail-dialog.html

@ -158,8 +158,8 @@
[pageSize]="pageSize"
[showAccountColumn]="false"
[showActions]="
!data.hasImpersonationId &&
data.hasPermissionToCreateActivity &&
!data.impersonationId &&
user?.settings?.isExperimentalFeatures &&
!user?.settings?.isRestrictedView
"
@ -183,8 +183,8 @@
[currentBalance]="balance"
[locale]="user?.settings?.locale"
[showActions]="
!data.hasImpersonationId &&
hasPermissionToDeleteAccountBalance &&
!data.impersonationId &&
!user.settings.isRestrictedView
"
(accountBalanceCreated)="onAddAccountBalance($event)"

2
apps/client/src/app/components/account-detail-dialog/interfaces/interfaces.ts

@ -1,8 +1,8 @@
export interface AccountDetailDialogParams {
accountId: string;
deviceType: string;
hasImpersonationId: boolean;
hasPermissionToCreateActivity: boolean;
impersonationId: string | null;
}
export interface AccountDetailDialogResult {

6
apps/client/src/app/pages/accounts/accounts-page.component.ts

@ -54,6 +54,7 @@ export class GfAccountsPageComponent implements OnInit {
protected hasImpersonationId: boolean;
protected hasPermissionToCreateAccount: boolean;
protected hasPermissionToUpdateAccount: boolean;
protected impersonationId: string | null;
protected totalBalanceInBaseCurrency = 0;
protected totalValueInBaseCurrency = 0;
protected user: User;
@ -110,6 +111,7 @@ export class GfAccountsPageComponent implements OnInit {
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((impersonationId) => {
this.hasImpersonationId = !!impersonationId;
this.impersonationId = impersonationId;
});
this.userService.stateChanged
@ -252,11 +254,11 @@ export class GfAccountsPageComponent implements OnInit {
data: {
accountId: aAccountId,
deviceType: this.deviceType(),
hasImpersonationId: this.hasImpersonationId,
hasPermissionToCreateActivity:
!this.hasImpersonationId &&
hasPermission(this.user?.permissions, permissions.createActivity) &&
!this.user?.settings?.isRestrictedView
!this.user?.settings?.isRestrictedView,
impersonationId: this.impersonationId
},
height: this.deviceType() === 'mobile' ? '98vh' : '80vh',
width: this.deviceType() === 'mobile' ? '100vw' : '50rem'

19
apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts

@ -17,7 +17,11 @@ import {
PortfolioPosition,
User
} from '@ghostfolio/common/interfaces';
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import {
hasPermission,
hasReadRestrictedAccessPermission,
permissions
} from '@ghostfolio/common/permissions';
import { MarketAdvanced } from '@ghostfolio/common/types';
import { translate } from '@ghostfolio/ui/i18n';
import { GfPortfolioProportionChartComponent } from '@ghostfolio/ui/portfolio-proportion-chart';
@ -97,6 +101,7 @@ export class GfAllocationsPageComponent implements OnInit {
| 'name'
> & { etfProvider: string; value: number };
};
protected impersonationId: string | null;
protected isLoading = false;
protected markets: PortfolioDetails['markets'];
protected marketsAdvanced: {
@ -170,6 +175,7 @@ export class GfAllocationsPageComponent implements OnInit {
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((impersonationId) => {
this.hasImpersonationId = !!impersonationId;
this.impersonationId = impersonationId;
this.changeDetectorRef.markForCheck();
});
@ -224,7 +230,12 @@ export class GfAllocationsPageComponent implements OnInit {
}
protected showValuesInPercentage() {
return this.hasImpersonationId || this.user?.settings?.isRestrictedView;
return (
hasReadRestrictedAccessPermission({
accesses: this.user?.access,
impersonationId: this.impersonationId
}) || this.user?.settings?.isRestrictedView
);
}
private extractCurrency({
@ -618,11 +629,11 @@ export class GfAllocationsPageComponent implements OnInit {
data: {
accountId: aAccountId,
deviceType: this.deviceType(),
hasImpersonationId: this.hasImpersonationId,
hasPermissionToCreateActivity:
!this.hasImpersonationId &&
hasPermission(this.user?.permissions, permissions.createActivity) &&
!this.user?.settings?.isRestrictedView
!this.user?.settings?.isRestrictedView,
impersonationId: this.impersonationId
},
height: this.deviceType() === 'mobile' ? '98vh' : '80vh',
width: this.deviceType() === 'mobile' ? '100vw' : '50rem'

17
apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts

@ -16,7 +16,11 @@ import {
ToggleOption,
User
} from '@ghostfolio/common/interfaces';
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import {
hasPermission,
hasReadRestrictedAccessPermission,
permissions
} from '@ghostfolio/common/permissions';
import type { AiPromptMode, GroupBy } from '@ghostfolio/common/types';
import { translate } from '@ghostfolio/ui/i18n';
import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator';
@ -81,6 +85,7 @@ export class GfAnalysisPageComponent implements OnInit {
protected readonly dividendTimelineDataLabel = $localize`Dividend`;
protected hasImpersonationId: boolean;
protected hasPermissionToReadAiPrompt: boolean;
protected impersonationId: string | null;
protected investments: InvestmentItem[];
protected readonly investmentTimelineDataLabel = $localize`Invested Capital`;
protected investmentsByGroup: InvestmentItem[];
@ -151,6 +156,7 @@ export class GfAnalysisPageComponent implements OnInit {
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((impersonationId) => {
this.hasImpersonationId = !!impersonationId;
this.impersonationId = impersonationId;
this.changeDetectorRef.markForCheck();
});
@ -241,6 +247,15 @@ export class GfAnalysisPageComponent implements OnInit {
});
}
protected showValuesInPercentage() {
return (
hasReadRestrictedAccessPermission({
accesses: this.user?.access,
impersonationId: this.impersonationId
}) || this.user?.settings?.isRestrictedView
);
}
private fetchDividendsAndInvestments() {
this.isLoadingDividendTimelineChart = true;
this.isLoadingInvestmentTimelineChart = true;

12
apps/client/src/app/pages/portfolio/analysis/analysis-page.html

@ -398,9 +398,7 @@
[benchmarkDataLabel]="portfolioEvolutionDataLabel"
[currency]="user?.settings?.baseCurrency"
[historicalDataItems]="performanceDataItems"
[isInPercentage]="
hasImpersonationId || user.settings.isRestrictedView
"
[isInPercentage]="showValuesInPercentage()"
[isLoading]="isLoadingInvestmentChart"
[locale]="user?.settings?.locale"
/>
@ -456,9 +454,7 @@
[benchmarkDataLabel]="investmentTimelineDataLabel"
[currency]="user?.settings?.baseCurrency"
[groupBy]="mode()"
[isInPercentage]="
hasImpersonationId || user.settings.isRestrictedView
"
[isInPercentage]="showValuesInPercentage()"
[isLoading]="isLoadingInvestmentTimelineChart"
[locale]="user?.settings?.locale"
[savingsRate]="savingsRate"
@ -493,9 +489,7 @@
[benchmarkDataLabel]="dividendTimelineDataLabel"
[currency]="user?.settings?.baseCurrency"
[groupBy]="mode()"
[isInPercentage]="
hasImpersonationId || user.settings.isRestrictedView
"
[isInPercentage]="showValuesInPercentage()"
[isLoading]="isLoadingDividendTimelineChart"
[locale]="user?.settings?.locale"
/>

12
libs/common/src/lib/permissions.ts

@ -1,6 +1,6 @@
import { UserWithSettings } from '@ghostfolio/common/types';
import { Role } from '@prisma/client';
import { Access, Role } from '@prisma/client';
export const permissions = {
accessAdminControl: 'accessAdminControl',
@ -198,17 +198,17 @@ export function hasPermission(
}
export function hasReadRestrictedAccessPermission({
impersonationId,
user
accesses = [],
impersonationId
}: {
impersonationId: string;
user: UserWithSettings;
accesses: Pick<Access, 'id' | 'permissions'>[];
impersonationId: string | null;
}) {
if (!impersonationId) {
return false;
}
const access = user?.accessesGet?.find(({ id }) => {
const access = accesses?.find(({ id }) => {
return id === impersonationId;
});

Loading…
Cancel
Save