[permissions] Add permissions check layer in entityManager (#11818)

First and main step of
https://github.com/twentyhq/core-team-issues/issues/747

We are implementing a permission check layer in our custom
WorkspaceEntityManager by overriding all the db-executing methods (this
PR only overrides some as a POC, the rest will be done in the next PR).
Our custom repositories call entity managers under the hood to interact
with the db so this solves the repositories case too.
This is still behind the feature flag IsPermissionsV2Enabled.

In the next PR
- finish overriding all the methods required in WorkspaceEntityManager
- add tests
This commit is contained in:
Marie
2025-05-05 16:06:54 +02:00
committed by GitHub
parent 5f8040af5d
commit a9e73c6340
62 changed files with 1194 additions and 933 deletions

View File

@ -3,7 +3,7 @@ import { InjectRepository } from '@nestjs/typeorm';
import chunk from 'lodash.chunk';
import compact from 'lodash.compact';
import { Any, EntityManager, Repository } from 'typeorm';
import { Any, Repository } from 'typeorm';
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
import { ExceptionHandlerService } from 'src/engine/core-modules/exception-handler/exception-handler.service';
@ -45,7 +45,6 @@ export class CreateCompanyAndContactService {
contactsToCreate: Contact[],
workspaceId: string,
source: FieldActorSource,
transactionManager?: EntityManager,
): Promise<DeepPartial<PersonWorkspaceEntity>[]> {
if (!contactsToCreate || contactsToCreate.length === 0) {
return [];
@ -61,10 +60,7 @@ export class CreateCompanyAndContactService {
);
const workspaceMembers =
await this.workspaceMemberRepository.getAllByWorkspaceId(
workspaceId,
transactionManager,
);
await this.workspaceMemberRepository.getAllByWorkspaceId(workspaceId);
const contactsToCreateFromOtherCompanies =
filterOutSelfAndContactsFromCompanyOrWorkspace(
@ -137,7 +133,6 @@ export class CreateCompanyAndContactService {
const companiesObject = await this.createCompaniesService.createCompanies(
workDomainNamesToCreateFormatted,
workspaceId,
transactionManager,
);
const formattedContactsToCreate =
@ -158,7 +153,6 @@ export class CreateCompanyAndContactService {
return this.createContactService.createPeople(
formattedContactsToCreate,
workspaceId,
transactionManager,
);
}

View File

@ -4,7 +4,7 @@ import axios, { AxiosInstance } from 'axios';
import uniqBy from 'lodash.uniqby';
import { TWENTY_COMPANIES_BASE_URL } from 'twenty-shared/constants';
import { ConnectedAccountProvider } from 'twenty-shared/types';
import { DeepPartial, EntityManager, ILike } from 'typeorm';
import { DeepPartial, ILike } from 'typeorm';
import { FieldActorSource } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type';
import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
@ -37,7 +37,6 @@ export class CreateCompanyService {
async createCompanies(
companies: CompanyToCreate[],
workspaceId: string,
transactionManager?: EntityManager,
): Promise<{
[domainName: string]: string;
}> {
@ -63,12 +62,9 @@ export class CreateCompanyService {
}));
// Find existing companies
const existingCompanies = await companyRepository.find(
{
where: conditions,
},
transactionManager,
);
const existingCompanies = await companyRepository.find({
where: conditions,
});
const existingCompanyIdsMap = this.createCompanyMap(existingCompanies);
// Filter out companies that already exist
@ -87,10 +83,8 @@ export class CreateCompanyService {
}
// Retrieve the last company position
let lastCompanyPosition = await this.getLastCompanyPosition(
companyRepository,
transactionManager,
);
let lastCompanyPosition =
await this.getLastCompanyPosition(companyRepository);
const newCompaniesData = await Promise.all(
newCompaniesToCreate.map((company) =>
this.prepareCompanyData(company, ++lastCompanyPosition),
@ -156,12 +150,10 @@ export class CreateCompanyService {
private async getLastCompanyPosition(
companyRepository: WorkspaceRepository<CompanyWorkspaceEntity>,
transactionManager?: EntityManager,
): Promise<number> {
const lastCompanyPosition = await companyRepository.maximum(
'position',
undefined,
transactionManager,
);
return lastCompanyPosition ?? 0;

View File

@ -1,7 +1,7 @@
import { Injectable } from '@nestjs/common';
import { ConnectedAccountProvider } from 'twenty-shared/types';
import { DeepPartial, EntityManager } from 'typeorm';
import { DeepPartial } from 'typeorm';
import { v4 } from 'uuid';
import { FieldActorSource } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type';
@ -74,7 +74,6 @@ export class CreateContactService {
public async createPeople(
contactsToCreate: ContactToCreate[],
workspaceId: string,
transactionManager?: EntityManager,
): Promise<DeepPartial<PersonWorkspaceEntity>[]> {
if (contactsToCreate.length === 0) return [];
@ -87,31 +86,23 @@ export class CreateContactService {
},
);
const lastPersonPosition = await this.getLastPersonPosition(
personRepository,
transactionManager,
);
const lastPersonPosition =
await this.getLastPersonPosition(personRepository);
const formattedContacts = this.formatContacts(
contactsToCreate,
lastPersonPosition,
);
return personRepository.save(
formattedContacts,
undefined,
transactionManager,
);
return personRepository.save(formattedContacts, undefined);
}
private async getLastPersonPosition(
personRepository: WorkspaceRepository<PersonWorkspaceEntity>,
transactionManager?: EntityManager,
): Promise<number> {
const lastPersonPosition = await personRepository.maximum(
'position',
undefined,
transactionManager,
);
return lastPersonPosition ?? 0;