[permissions] Adapt field permissions to connect + createMany (#13655)
In this PR we adapt field permission to two things 1. Connect: the recent insertion of the connect feature introduces the possibility to have "connect" objects in typeORM's expressionMap's valueSet, where we previously only had column names (if i followed correctly). For instance for a person object that a N - 1 relationship to company, person will have both companyId and company as possible valueSet keys for the upsert. We need to reflect that in our `getColumnNameToFieldMetadataIdMap` util that returns a map containing every possible value we could encounter in valueSet. In an attempt to tie this to the schema generation where this is introduced, I created the shallow util extractGraphQLRelationFieldNames (probably ill-named - im willing to update the name if @etiennejouan has a better idea?) to remind us that these two are linked. 2. CreateMany: When calling query builders methods directly on custom object (like we do in graphql-create-many-resolver), we need to be careful to call them with a selection of readable fields. We had a debate on whether we should compute this selection containing all readable fields by default under the hood when no selected fields are indicated. I am still not 100% convinced as I think it should remain the caller's responsibility, but this case reminds us that it could easily be forgotten by developers - although it is all the more the case as we don't have seeds yet that help us realize that ([PR on the way](https://github.com/twentyhq/twenty/pull/13646)).
This commit is contained in:
+2
-1
@@ -5,6 +5,7 @@ import { Request } from 'express';
|
||||
import { RestApiBaseHandler } from 'src/engine/api/rest/core/interfaces/rest-api-base.handler';
|
||||
|
||||
import { parseCorePath } from 'src/engine/api/rest/core/query-builder/utils/path-parsers/parse-core-path.utils';
|
||||
import { getAllSelectableFields } from 'src/engine/api/utils/get-all-selectable-fields.utils';
|
||||
|
||||
@Injectable()
|
||||
export class RestApiDeleteOneHandler extends RestApiBaseHandler {
|
||||
@@ -18,7 +19,7 @@ export class RestApiDeleteOneHandler extends RestApiBaseHandler {
|
||||
const { objectMetadata, repository, restrictedFields } =
|
||||
await this.getRepositoryAndMetadataOrFail(request);
|
||||
|
||||
const selectOptions = this.getAllSelectableFields({
|
||||
const selectOptions = getAllSelectableFields({
|
||||
restrictedFields,
|
||||
objectMetadata,
|
||||
});
|
||||
|
||||
+2
-32
@@ -24,6 +24,7 @@ import {
|
||||
MAX_DEPTH,
|
||||
} from 'src/engine/api/rest/input-factories/depth-input.factory';
|
||||
import { computeCursorArgFilter } from 'src/engine/api/utils/compute-cursor-arg-filter.utils';
|
||||
import { getAllSelectableFields } from 'src/engine/api/utils/get-all-selectable-fields.utils';
|
||||
import { CreatedByFromAuthContextService } from 'src/engine/core-modules/actor/services/created-by-from-auth-context.service';
|
||||
import { ApiKeyRoleService } from 'src/engine/core-modules/api-key/api-key-role.service';
|
||||
import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
@@ -44,7 +45,6 @@ import { WorkspaceSelectQueryBuilder } from 'src/engine/twenty-orm/repository/wo
|
||||
import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { formatResult as formatGetManyData } from 'src/engine/twenty-orm/utils/format-result.util';
|
||||
import { getFieldMetadataIdToColumnNamesMap } from 'src/engine/twenty-orm/utils/get-field-metadata-id-to-column-names-map.util';
|
||||
import { isFieldMetadataEntityOfType } from 'src/engine/utils/is-field-metadata-of-type.util';
|
||||
|
||||
export interface PageInfo {
|
||||
@@ -310,7 +310,7 @@ export abstract class RestApiBaseHandler {
|
||||
let selectOptions = undefined;
|
||||
|
||||
if (!isEmpty(restrictedFields)) {
|
||||
selectOptions = this.getAllSelectableFields({
|
||||
selectOptions = getAllSelectableFields({
|
||||
restrictedFields,
|
||||
objectMetadata,
|
||||
});
|
||||
@@ -355,36 +355,6 @@ export abstract class RestApiBaseHandler {
|
||||
};
|
||||
}
|
||||
|
||||
public getAllSelectableFields({
|
||||
restrictedFields,
|
||||
objectMetadata,
|
||||
}: {
|
||||
restrictedFields: RestrictedFields;
|
||||
objectMetadata: { objectMetadataMapItem: ObjectMetadataItemWithFieldMaps };
|
||||
}) {
|
||||
const restrictedFieldsIds = Object.entries(restrictedFields)
|
||||
.filter(([_, value]) => value.canRead === false)
|
||||
.map(([key]) => key);
|
||||
|
||||
const fieldMetadataIdToColumnNamesMap = getFieldMetadataIdToColumnNamesMap(
|
||||
objectMetadata.objectMetadataMapItem,
|
||||
);
|
||||
|
||||
const restrictedFieldsColumnNames: string[] = restrictedFieldsIds
|
||||
.map((fieldId) => fieldMetadataIdToColumnNamesMap.get(fieldId))
|
||||
.filter(isDefined)
|
||||
.flat();
|
||||
|
||||
const allColumnNames = [...fieldMetadataIdToColumnNamesMap.values()].flat();
|
||||
|
||||
return Object.fromEntries(
|
||||
allColumnNames.map((columnName) => [
|
||||
columnName,
|
||||
!restrictedFieldsColumnNames.includes(columnName),
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
public formatResult<T>({
|
||||
operation,
|
||||
objectNameSingular,
|
||||
|
||||
Reference in New Issue
Block a user