[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

@ -235,6 +235,11 @@ export class MigrateRichTextContentPatchCommand extends ActiveOrSuspendedWorkspa
const rows = await workspaceDataSource.query(
`SELECT id, "${richTextField.name}" FROM "${schemaName}"."${computeTableName(objectMetadata.nameSingular, objectMetadata.isCustom)}" WHERE "${richTextField.name}" IS NOT NULL`,
undefined, // parameters
undefined, // queryRunner
{
shouldBypassPermissionChecks: true,
},
);
this.logger.log(`Generating markdown for ${rows.length} records`);
@ -251,6 +256,10 @@ export class MigrateRichTextContentPatchCommand extends ActiveOrSuspendedWorkspa
await workspaceDataSource.query(
`UPDATE "${schemaName}"."${computeTableName(objectMetadata.nameSingular, objectMetadata.isCustom)}" SET "${richTextField.name}V2Blocknote" = $1, "${richTextField.name}V2Markdown" = $2 WHERE id = $3`,
[blocknoteFieldValue, markdownFieldValue, row.id],
undefined, // queryRunner
{
shouldBypassPermissionChecks: true,
},
);
} catch (error) {
this.logger.log(

View File

@ -1,20 +1,20 @@
import { InjectRepository } from '@nestjs/typeorm';
import { Command } from 'nest-commander';
import { Repository } from 'typeorm';
import { FieldMetadataType } from 'twenty-shared/types';
import { Repository } from 'typeorm';
import {
ActiveOrSuspendedWorkspacesMigrationCommandRunner,
RunOnWorkspaceArgs,
} from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { ActorMetadata } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type';
import { generateDefaultValue } from 'src/engine/metadata-modules/field-metadata/utils/generate-default-value';
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { computeTableName } from 'src/engine/utils/compute-table-name.util';
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
@Command({
name: 'upgrade:0-54:0-54-created-by-default-value',
@ -59,12 +59,19 @@ export class FixCreatedByDefaultValueCommand extends ActiveOrSuspendedWorkspaces
);
const actualDefaultValue = (
await dataSource.query(`
await dataSource.query(
`
SELECT column_default FROM information_schema.columns
WHERE table_schema = '${schemaName}'
AND table_name = '${tableName}'
AND column_name = 'createdBySource';
`)
`,
undefined, // parameters
undefined, // queryRunner
{
shouldBypassPermissionChecks: true,
},
)
)?.[0]?.column_default;
if (actualDefaultValue !== null) {
@ -75,12 +82,19 @@ export class FixCreatedByDefaultValueCommand extends ActiveOrSuspendedWorkspaces
FieldMetadataType.ACTOR,
) as ActorMetadata;
await dataSource.query(`
await dataSource.query(
`
ALTER TABLE "${schemaName}"."${tableName}"
ALTER COLUMN "createdBySource" SET DEFAULT ${createdByDefaultValues.source},
ALTER COLUMN "createdByName" SET DEFAULT ${createdByDefaultValues.name},
ALTER COLUMN "createdByContext" SET DEFAULT '${JSON.stringify(createdByDefaultValues.context)}';
`);
`,
undefined, // parameters
undefined, // queryRunner
{
shouldBypassPermissionChecks: true,
},
);
}
}
}

View File

@ -3,11 +3,11 @@ import { DataSource } from 'typeorm';
const tableName = 'billingSubscription';
export const seedBillingSubscriptions = async (
workspaceDataSource: DataSource,
dataSource: DataSource,
schemaName: string,
workspaceId: string,
) => {
await workspaceDataSource
await dataSource
.createQueryBuilder()
.insert()
.into(`${schemaName}.${tableName}`, [

View File

@ -3,11 +3,11 @@ import { DataSource } from 'typeorm';
const tableName = 'featureFlag';
export const deleteFeatureFlags = async (
workspaceDataSource: DataSource,
dataSource: DataSource,
schemaName: string,
workspaceId: string,
) => {
await workspaceDataSource
await dataSource
.createQueryBuilder()
.delete()
.from(`${schemaName}.${tableName}`)

View File

@ -11,11 +11,11 @@ export const DEV_SEED_USER_WORKSPACE_IDS = {
};
export const seedUserWorkspaces = async (
workspaceDataSource: DataSource,
dataSource: DataSource,
schemaName: string,
workspaceId: string,
) => {
await workspaceDataSource
await dataSource
.createQueryBuilder()
.insert()
.into(`${schemaName}.${tableName}`, ['id', 'userId', 'workspaceId'])
@ -41,11 +41,11 @@ export const seedUserWorkspaces = async (
};
export const deleteUserWorkspaces = async (
workspaceDataSource: DataSource,
dataSource: DataSource,
schemaName: string,
workspaceId: string,
) => {
await workspaceDataSource
await dataSource
.createQueryBuilder()
.delete()
.from(`${schemaName}.${tableName}`)

View File

@ -10,11 +10,8 @@ export const DEMO_SEED_USER_IDS = {
TIM: '20202020-9e3b-46d4-a556-88b9ddc2b034',
};
export const seedUsers = async (
workspaceDataSource: DataSource,
schemaName: string,
) => {
await workspaceDataSource
export const seedUsers = async (dataSource: DataSource, schemaName: string) => {
await dataSource
.createQueryBuilder()
.insert()
.into(`${schemaName}.${tableName}`, [

View File

@ -1,14 +1,14 @@
import { DataSource } from 'typeorm';
import { WorkspaceActivationStatus } from 'twenty-shared/workspace';
import { DataSource } from 'typeorm';
const tableName = 'workspace';
export const seedWorkspaces = async (
workspaceDataSource: DataSource,
dataSource: DataSource,
schemaName: string,
workspaceId: string,
) => {
await workspaceDataSource
await dataSource
.createQueryBuilder()
.insert()
.into(`${schemaName}.${tableName}`, [
@ -36,11 +36,11 @@ export const seedWorkspaces = async (
};
export const deleteWorkspaces = async (
workspaceDataSource: DataSource,
dataSource: DataSource,
schemaName: string,
workspaceId: string,
) => {
await workspaceDataSource
await dataSource
.createQueryBuilder()
.delete()
.from(`${schemaName}.${tableName}`)

View File

@ -5,11 +5,11 @@ import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/featu
const tableName = 'featureFlag';
export const seedFeatureFlags = async (
workspaceDataSource: DataSource,
dataSource: DataSource,
schemaName: string,
workspaceId: string,
) => {
await workspaceDataSource
await dataSource
.createQueryBuilder()
.insert()
.into(`${schemaName}.${tableName}`, ['key', 'workspaceId', 'value'])
@ -45,11 +45,11 @@ export const seedFeatureFlags = async (
};
export const deleteFeatureFlags = async (
workspaceDataSource: DataSource,
dataSource: DataSource,
schemaName: string,
workspaceId: string,
) => {
await workspaceDataSource
await dataSource
.createQueryBuilder()
.delete()
.from(`${schemaName}.${tableName}`)

View File

@ -17,7 +17,7 @@ export const DEV_SEED_USER_WORKSPACE_IDS = {
};
export const seedUserWorkspaces = async (
workspaceDataSource: DataSource,
dataSource: DataSource,
schemaName: string,
workspaceId: string,
) => {
@ -53,7 +53,7 @@ export const seedUserWorkspaces = async (
},
];
}
await workspaceDataSource
await dataSource
.createQueryBuilder()
.insert()
.into(`${schemaName}.${tableName}`, ['id', 'userId', 'workspaceId'])
@ -63,11 +63,11 @@ export const seedUserWorkspaces = async (
};
export const deleteUserWorkspaces = async (
workspaceDataSource: DataSource,
dataSource: DataSource,
schemaName: string,
workspaceId: string,
) => {
await workspaceDataSource
await dataSource
.createQueryBuilder()
.delete()
.from(`${schemaName}.${tableName}`)

View File

@ -8,11 +8,8 @@ export const DEV_SEED_USER_IDS = {
PHIL: '20202020-7169-42cf-bc47-1cfef15264b8',
};
export const seedUsers = async (
workspaceDataSource: DataSource,
schemaName: string,
) => {
await workspaceDataSource
export const seedUsers = async (dataSource: DataSource, schemaName: string) => {
await dataSource
.createQueryBuilder()
.insert()
.into(`${schemaName}.${tableName}`, [