[permissions] Remove raw queries and restrict its usage (#12360)

Closes https://github.com/twentyhq/core-team-issues/issues/748

In the frame of the work on permissions we

- remove all raw queries possible to use repositories instead
- forbid usage workspaceDataSource.executeRawQueries()
- restrict usage of workspaceDataSource.query() to force developers to
pass on shouldBypassPermissionChecks to use it.

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
This commit is contained in:
Marie
2025-06-02 10:53:51 +02:00
committed by GitHub
parent 1ef7b7a474
commit 9706f0df13
49 changed files with 495 additions and 754 deletions

View File

@ -1,130 +0,0 @@
import { Logger } from '@nestjs/common';
import { InjectDataSource } from '@nestjs/typeorm';
import { Command, CommandRunner, Option } from 'nest-commander';
import { DataSource } from 'typeorm';
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
interface RunCommandOptions {
workspaceId?: string;
}
@Command({
name: 'workspace:convert-record-positions-to-integers',
description: 'Convert record positions to integers',
})
export class ConvertRecordPositionsToIntegers extends CommandRunner {
private readonly logger = new Logger(ConvertRecordPositionsToIntegers.name);
constructor(
@InjectDataSource('metadata')
private readonly metadataDataSource: DataSource,
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
) {
super();
}
async run(_passedParam: string[], options: RunCommandOptions): Promise<void> {
const queryRunner = this.metadataDataSource.createQueryRunner();
const workspaceId = options.workspaceId;
if (!workspaceId || typeof workspaceId !== 'string') {
this.logger.error('Workspace id is required');
return;
}
const customObjectMetadataCollection = await this.metadataDataSource
.getRepository(ObjectMetadataEntity)
.findBy({
workspaceId,
isCustom: true,
});
const customObjectTableNames = customObjectMetadataCollection.map(
(metadata) => metadata.nameSingular,
);
await queryRunner.connect();
await queryRunner.startTransaction();
const transactionManager = queryRunner.manager;
this.logger.log('Converting record positions to integers');
try {
await this.convertRecordPositionsToIntegers(
customObjectTableNames,
workspaceId,
transactionManager,
);
await queryRunner.commitTransaction();
} catch (error) {
await queryRunner.rollbackTransaction();
this.logger.error('Error converting record positions to integers', error);
} finally {
await queryRunner.release();
this.logger.log('Record positions converted to integers');
}
}
private async convertRecordPositionsToIntegers(
customObjectTableNames: string[],
workspaceId: string,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
transactionManager: any,
): Promise<void> {
const dataSourceSchema =
this.workspaceDataSourceService.getSchemaName(workspaceId);
for (const tableName of ['company', 'person', 'opportunity']) {
await this.convertRecordPositionsToIntegersForTable(
tableName,
dataSourceSchema,
workspaceId,
transactionManager,
);
}
for (const tableName of customObjectTableNames) {
await this.convertRecordPositionsToIntegersForTable(
`_${tableName}`,
dataSourceSchema,
workspaceId,
transactionManager,
);
}
}
private async convertRecordPositionsToIntegersForTable(
tableName: string,
dataSourceSchema: string,
workspaceId: string,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
transactionManager: any,
): Promise<void> {
await this.workspaceDataSourceService.executeRawQuery(
`UPDATE ${dataSourceSchema}."${tableName}" SET position = subquery.position
FROM (
SELECT id, ROW_NUMBER() OVER (ORDER BY position) as position
FROM ${dataSourceSchema}."${tableName}"
) as subquery
WHERE ${dataSourceSchema}."${tableName}".id = subquery.id`,
[],
workspaceId,
transactionManager,
);
}
@Option({
flags: '-w, --workspace-id [workspace_id]',
description: 'workspace id',
required: true,
})
parseWorkspaceId(value: string): string {
return value;
}
}

View File

@ -7,9 +7,8 @@ import { WorkspaceModule } from 'src/engine/core-modules/workspace/workspace.mod
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
import { WorkspaceHealthModule } from 'src/engine/workspace-manager/workspace-health/workspace-health.module';
import { ConvertRecordPositionsToIntegers } from 'src/engine/workspace-manager/workspace-sync-metadata/commands/convert-record-positions-to-integers.command';
import { WorkspaceSyncMetadataModule } from 'src/engine/workspace-manager/workspace-sync-metadata/workspace-sync-metadata.module';
import { SyncWorkspaceLoggerModule } from 'src/engine/workspace-manager/workspace-sync-metadata/commands/services/sync-workspace-logger.module';
import { WorkspaceSyncMetadataModule } from 'src/engine/workspace-manager/workspace-sync-metadata/workspace-sync-metadata.module';
import { SyncWorkspaceMetadataCommand } from './sync-workspace-metadata.command';
@ -24,7 +23,7 @@ import { SyncWorkspaceMetadataCommand } from './sync-workspace-metadata.command'
TypeOrmModule.forFeature([Workspace], 'core'),
SyncWorkspaceLoggerModule,
],
providers: [SyncWorkspaceMetadataCommand, ConvertRecordPositionsToIntegers],
providers: [SyncWorkspaceMetadataCommand],
exports: [SyncWorkspaceMetadataCommand],
})
export class WorkspaceSyncMetadataCommandsModule {}