Skip to content

Commit a8ea343

Browse files
Merge branch 'main' into automation-empty-state
2 parents 3f3de48 + db43a36 commit a8ea343

30 files changed

Lines changed: 2595 additions & 86 deletions

backend/src/entities/cedar-authorization/cedar-action-map.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,17 @@ export enum CedarAction {
77
TableAdd = 'table:add',
88
TableEdit = 'table:edit',
99
TableDelete = 'table:delete',
10+
DashboardRead = 'dashboard:read',
11+
DashboardCreate = 'dashboard:create',
12+
DashboardEdit = 'dashboard:edit',
13+
DashboardDelete = 'dashboard:delete',
1014
}
1115

1216
export enum CedarResourceType {
1317
Connection = 'RocketAdmin::Connection',
1418
Group = 'RocketAdmin::Group',
1519
Table = 'RocketAdmin::Table',
20+
Dashboard = 'RocketAdmin::Dashboard',
1621
}
1722

1823
export const CEDAR_ACTION_TYPE = 'RocketAdmin::Action';
@@ -25,4 +30,5 @@ export interface CedarValidationRequest {
2530
connectionId?: string;
2631
groupId?: string;
2732
tableName?: string;
33+
dashboardId?: string;
2834
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import {
2+
Body,
3+
Controller,
4+
Get,
5+
HttpException,
6+
HttpStatus,
7+
Injectable,
8+
Post,
9+
UseGuards,
10+
UseInterceptors,
11+
} from '@nestjs/common';
12+
import { ApiBearerAuth, ApiBody, ApiOperation, ApiParam, ApiResponse, ApiTags } from '@nestjs/swagger';
13+
import { SlugUuid } from '../../decorators/index.js';
14+
import { Timeout } from '../../decorators/timeout.decorator.js';
15+
import { Messages } from '../../exceptions/text/messages.js';
16+
import { ConnectionEditGuard } from '../../guards/connection-edit.guard.js';
17+
import { ConnectionReadGuard } from '../../guards/connection-read.guard.js';
18+
import { SentryInterceptor } from '../../interceptors/index.js';
19+
import { IComplexPermission } from '../permission/permission.interface.js';
20+
import { CedarAuthorizationService } from './cedar-authorization.service.js';
21+
import { SaveCedarPolicyDto } from './dto/save-cedar-policy.dto.js';
22+
import { ValidateCedarSchemaDto } from './dto/validate-cedar-schema.dto.js';
23+
24+
@UseInterceptors(SentryInterceptor)
25+
@Timeout()
26+
@Controller()
27+
@ApiBearerAuth()
28+
@ApiTags('Cedar Authorization')
29+
@Injectable()
30+
export class CedarAuthorizationController {
31+
constructor(private readonly cedarAuthService: CedarAuthorizationService) {}
32+
33+
@ApiOperation({ summary: 'Get the current cedar schema used for authorization' })
34+
@ApiResponse({
35+
status: 200,
36+
description: 'Cedar schema returned.',
37+
})
38+
@ApiParam({ name: 'connectionId', required: true })
39+
@UseGuards(ConnectionReadGuard)
40+
@Get('/connection/cedar-schema/:connectionId')
41+
async getCedarSchema(
42+
@SlugUuid('connectionId') connectionId: string,
43+
): Promise<{ cedarSchema: Record<string, unknown> }> {
44+
if (!connectionId) {
45+
throw new HttpException({ message: Messages.CONNECTION_ID_MISSING }, HttpStatus.BAD_REQUEST);
46+
}
47+
return { cedarSchema: this.cedarAuthService.getSchema() };
48+
}
49+
50+
@ApiOperation({ summary: 'Validate a cedar schema against the Cedar engine' })
51+
@ApiResponse({
52+
status: 200,
53+
description: 'Cedar schema is valid.',
54+
})
55+
@ApiBody({ type: ValidateCedarSchemaDto })
56+
@ApiParam({ name: 'connectionId', required: true })
57+
@UseGuards(ConnectionReadGuard)
58+
@Post('/connection/cedar-schema/validate/:connectionId')
59+
async validateCedarSchema(
60+
@SlugUuid('connectionId') connectionId: string,
61+
@Body() dto: ValidateCedarSchemaDto,
62+
): Promise<{ valid: boolean }> {
63+
if (!connectionId) {
64+
throw new HttpException({ message: Messages.CONNECTION_ID_MISSING }, HttpStatus.BAD_REQUEST);
65+
}
66+
this.cedarAuthService.validateCedarSchema(dto.cedarSchema);
67+
return { valid: true };
68+
}
69+
70+
@ApiOperation({ summary: 'Save a cedar policy for a group, generating classical permissions for backward compatibility' })
71+
@ApiResponse({
72+
status: 200,
73+
description: 'Cedar policy saved and classical permissions generated.',
74+
})
75+
@ApiBody({ type: SaveCedarPolicyDto })
76+
@ApiParam({ name: 'connectionId', required: true })
77+
@UseGuards(ConnectionEditGuard)
78+
@Post('/connection/cedar-policy/:connectionId')
79+
async saveCedarPolicy(
80+
@SlugUuid('connectionId') connectionId: string,
81+
@Body() dto: SaveCedarPolicyDto,
82+
): Promise<{ cedarPolicy: string; classicalPermissions: IComplexPermission }> {
83+
if (!connectionId) {
84+
throw new HttpException({ message: Messages.CONNECTION_ID_MISSING }, HttpStatus.BAD_REQUEST);
85+
}
86+
return this.cedarAuthService.saveCedarPolicy(connectionId, dto.groupId, dto.cedarPolicy);
87+
}
88+
}
Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,34 @@
1-
import { Global, Module } from '@nestjs/common';
1+
import { Global, MiddlewareConsumer, Module, NestModule, RequestMethod } from '@nestjs/common';
2+
import { TypeOrmModule } from '@nestjs/typeorm';
3+
import { AuthMiddleware } from '../../authorization/auth.middleware.js';
4+
import { GlobalDatabaseContext } from '../../common/application/global-database-context.js';
5+
import { BaseType } from '../../common/data-injection.tokens.js';
6+
import { LogOutEntity } from '../log-out/log-out.entity.js';
7+
import { UserEntity } from '../user/user.entity.js';
8+
import { CedarAuthorizationController } from './cedar-authorization.controller.js';
29
import { CedarAuthorizationService } from './cedar-authorization.service.js';
310

411
@Global()
512
@Module({
6-
providers: [CedarAuthorizationService],
13+
imports: [TypeOrmModule.forFeature([UserEntity, LogOutEntity])],
14+
providers: [
15+
{
16+
provide: BaseType.GLOBAL_DB_CONTEXT,
17+
useClass: GlobalDatabaseContext,
18+
},
19+
CedarAuthorizationService,
20+
],
21+
controllers: [CedarAuthorizationController],
722
exports: [CedarAuthorizationService],
823
})
9-
export class CedarAuthorizationModule {}
24+
export class CedarAuthorizationModule implements NestModule {
25+
public configure(consumer: MiddlewareConsumer): void {
26+
consumer
27+
.apply(AuthMiddleware)
28+
.forRoutes(
29+
{ path: '/connection/cedar-schema/:connectionId', method: RequestMethod.GET },
30+
{ path: '/connection/cedar-schema/validate/:connectionId', method: RequestMethod.POST },
31+
{ path: '/connection/cedar-policy/:connectionId', method: RequestMethod.POST },
32+
);
33+
}
34+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
1+
import { IComplexPermission } from '../permission/permission.interface.js';
12
import { CedarValidationRequest } from './cedar-action-map.js';
23

34
export interface ICedarAuthorizationService {
45
isFeatureEnabled(): boolean;
56
validate(request: CedarValidationRequest): Promise<boolean>;
67
invalidatePolicyCacheForConnection(connectionId: string): void;
8+
getSchema(): Record<string, unknown>;
9+
validateCedarSchema(schema: Record<string, unknown>): void;
10+
saveCedarPolicy(
11+
connectionId: string,
12+
groupId: string,
13+
cedarPolicy: string,
14+
): Promise<{ cedarPolicy: string; classicalPermissions: IComplexPermission }>;
715
}

0 commit comments

Comments
 (0)