Skip to content

Commit bc7e6cf

Browse files
authored
Merge pull request #1712 from rocket-admin/backend_dashboards_permissions
Implement dashboard permissions validation and add end-to-end tests for dashboard access
2 parents 2678ae6 + 19596fb commit bc7e6cf

3 files changed

Lines changed: 379 additions & 2 deletions

File tree

backend/src/entities/visualizations/dashboard/use-cases/find-all-dashboards.use.case.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { Inject, Injectable, NotFoundException, Scope } from '@nestjs/common';
22
import AbstractUseCase from '../../../../common/abstract-use.case.js';
33
import { IGlobalDatabaseContext } from '../../../../common/application/global-database-context.interface.js';
44
import { BaseType } from '../../../../common/data-injection.tokens.js';
5+
import { CedarAction } from '../../../cedar-authorization/cedar-action-map.js';
6+
import { CedarAuthorizationService } from '../../../cedar-authorization/cedar-authorization.service.js';
57
import { Messages } from '../../../../exceptions/text/messages.js';
68
import { FindAllDashboardsDs } from '../data-structures/find-all-dashboards.ds.js';
79
import { FoundDashboardDto } from '../dto/found-dashboard.dto.js';
@@ -16,12 +18,13 @@ export class FindAllDashboardsUseCase
1618
constructor(
1719
@Inject(BaseType.GLOBAL_DB_CONTEXT)
1820
protected _dbContext: IGlobalDatabaseContext,
21+
private readonly cedarAuthService: CedarAuthorizationService,
1922
) {
2023
super();
2124
}
2225

2326
public async implementation(inputData: FindAllDashboardsDs): Promise<FoundDashboardDto[]> {
24-
const { connectionId, masterPassword } = inputData;
27+
const { connectionId, masterPassword, userId } = inputData;
2528

2629
const foundConnection = await this._dbContext.connectionRepository.findAndDecryptConnection(
2730
connectionId,
@@ -35,6 +38,17 @@ export class FindAllDashboardsUseCase
3538
const dashboards =
3639
await this._dbContext.dashboardRepository.findAllDashboardsWithWidgetsByConnectionId(connectionId);
3740

38-
return dashboards.map(buildFoundDashboardDto);
41+
const accessChecks = await Promise.all(
42+
dashboards.map((dashboard) =>
43+
this.cedarAuthService.validate({
44+
userId,
45+
action: CedarAction.DashboardRead,
46+
connectionId,
47+
dashboardId: dashboard.id,
48+
}),
49+
),
50+
);
51+
52+
return dashboards.filter((_, index) => accessChecks[index]).map(buildFoundDashboardDto);
3953
}
4054
}

backend/src/guards/dashboard-read.guard.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@ import {
33
CanActivate,
44
ExecutionContext,
55
ForbiddenException,
6+
Inject,
67
Injectable,
78
} from '@nestjs/common';
89
import { Observable } from 'rxjs';
910
import { IRequestWithCognitoInfo } from '../authorization/index.js';
11+
import { IGlobalDatabaseContext } from '../common/application/global-database-context.interface.js';
12+
import { BaseType } from '../common/data-injection.tokens.js';
1013
import { CedarAction } from '../entities/cedar-authorization/cedar-action-map.js';
1114
import { CedarAuthorizationService } from '../entities/cedar-authorization/cedar-authorization.service.js';
1215
import { Messages } from '../exceptions/text/messages.js';
@@ -17,6 +20,8 @@ import { validateUuidByRegex } from './utils/validate-uuid-by-regex.js';
1720
export class DashboardReadGuard implements CanActivate {
1821
constructor(
1922
private readonly cedarAuthService: CedarAuthorizationService,
23+
@Inject(BaseType.GLOBAL_DB_CONTEXT)
24+
private readonly _dbContext: IGlobalDatabaseContext,
2025
) {}
2126

2227
canActivate(context: ExecutionContext): boolean | Promise<boolean> | Observable<boolean> {
@@ -35,6 +40,19 @@ export class DashboardReadGuard implements CanActivate {
3540
const dashboardId: string = request.params?.dashboardId;
3641

3742
try {
43+
if (!dashboardId) {
44+
const isUserInConnection = await this._dbContext.connectionRepository.isUserFromConnection(
45+
cognitoUserName,
46+
connectionId,
47+
);
48+
if (isUserInConnection) {
49+
resolve(true);
50+
return;
51+
}
52+
reject(new ForbiddenException(Messages.DONT_HAVE_PERMISSIONS));
53+
return;
54+
}
55+
3856
const allowed = await this.cedarAuthService.validate({
3957
userId: cognitoUserName,
4058
action: CedarAction.DashboardRead,

0 commit comments

Comments
 (0)