@solidstarters/solid-core 1.2.32 → 1.2.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/dist/controllers/action-metadata.controller.d.ts +8 -7
  2. package/dist/controllers/action-metadata.controller.d.ts.map +1 -1
  3. package/dist/controllers/action-metadata.controller.js +30 -21
  4. package/dist/controllers/action-metadata.controller.js.map +1 -1
  5. package/dist/controllers/menu-item-metadata.controller.d.ts +8 -7
  6. package/dist/controllers/menu-item-metadata.controller.d.ts.map +1 -1
  7. package/dist/controllers/menu-item-metadata.controller.js +30 -21
  8. package/dist/controllers/menu-item-metadata.controller.js.map +1 -1
  9. package/dist/controllers/mq-message-queue.controller.d.ts +8 -7
  10. package/dist/controllers/mq-message-queue.controller.d.ts.map +1 -1
  11. package/dist/controllers/mq-message-queue.controller.js +30 -21
  12. package/dist/controllers/mq-message-queue.controller.js.map +1 -1
  13. package/dist/controllers/mq-message.controller.d.ts +8 -7
  14. package/dist/controllers/mq-message.controller.d.ts.map +1 -1
  15. package/dist/controllers/mq-message.controller.js +30 -21
  16. package/dist/controllers/mq-message.controller.js.map +1 -1
  17. package/dist/controllers/permission-metadata.controller.d.ts +8 -7
  18. package/dist/controllers/permission-metadata.controller.d.ts.map +1 -1
  19. package/dist/controllers/permission-metadata.controller.js +30 -21
  20. package/dist/controllers/permission-metadata.controller.js.map +1 -1
  21. package/dist/controllers/role-metadata.controller.d.ts +8 -7
  22. package/dist/controllers/role-metadata.controller.d.ts.map +1 -1
  23. package/dist/controllers/role-metadata.controller.js +30 -21
  24. package/dist/controllers/role-metadata.controller.js.map +1 -1
  25. package/dist/controllers/setting.controller.d.ts +9 -8
  26. package/dist/controllers/setting.controller.d.ts.map +1 -1
  27. package/dist/controllers/setting.controller.js +34 -24
  28. package/dist/controllers/setting.controller.js.map +1 -1
  29. package/dist/controllers/user.controller.d.ts +10 -9
  30. package/dist/controllers/user.controller.d.ts.map +1 -1
  31. package/dist/controllers/user.controller.js +38 -27
  32. package/dist/controllers/user.controller.js.map +1 -1
  33. package/dist/controllers/view-metadata.controller.d.ts +8 -7
  34. package/dist/controllers/view-metadata.controller.d.ts.map +1 -1
  35. package/dist/controllers/view-metadata.controller.js +30 -21
  36. package/dist/controllers/view-metadata.controller.js.map +1 -1
  37. package/dist/decorators/solid-request-context.decorator.d.ts +2 -0
  38. package/dist/decorators/solid-request-context.decorator.d.ts.map +1 -0
  39. package/dist/decorators/solid-request-context.decorator.js +14 -0
  40. package/dist/decorators/solid-request-context.decorator.js.map +1 -0
  41. package/dist/dtos/solid-request-context.dto.d.ts +5 -0
  42. package/dist/dtos/solid-request-context.dto.d.ts.map +1 -0
  43. package/dist/dtos/solid-request-context.dto.js +25 -0
  44. package/dist/dtos/solid-request-context.dto.js.map +1 -0
  45. package/dist/index.d.ts +2 -0
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +2 -0
  48. package/dist/index.js.map +1 -1
  49. package/dist/services/crud-helper.service.d.ts +6 -0
  50. package/dist/services/crud-helper.service.d.ts.map +1 -1
  51. package/dist/services/crud-helper.service.js +28 -1
  52. package/dist/services/crud-helper.service.js.map +1 -1
  53. package/dist/services/crud.service.d.ts +7 -7
  54. package/dist/services/crud.service.d.ts.map +1 -1
  55. package/dist/services/crud.service.js +55 -8
  56. package/dist/services/crud.service.js.map +1 -1
  57. package/dist/services/menu-item-metadata.service.d.ts.map +1 -1
  58. package/dist/services/menu-item-metadata.service.js +2 -4
  59. package/dist/services/menu-item-metadata.service.js.map +1 -1
  60. package/dist/services/permission-metadata.service.d.ts +0 -6
  61. package/dist/services/permission-metadata.service.d.ts.map +1 -1
  62. package/dist/services/permission-metadata.service.js +1 -32
  63. package/dist/services/permission-metadata.service.js.map +1 -1
  64. package/dist/services/user.service.d.ts +1 -1
  65. package/dist/services/user.service.d.ts.map +1 -1
  66. package/dist/services/user.service.js +1 -1
  67. package/dist/services/user.service.js.map +1 -1
  68. package/dist/tsconfig.tsbuildinfo +1 -1
  69. package/package.json +1 -1
  70. package/src/controllers/action-metadata.controller.ts +16 -14
  71. package/src/controllers/menu-item-metadata.controller.ts +16 -14
  72. package/src/controllers/mq-message-queue.controller.ts +16 -14
  73. package/src/controllers/mq-message.controller.ts +16 -14
  74. package/src/controllers/permission-metadata.controller.ts +16 -14
  75. package/src/controllers/role-metadata.controller.ts +16 -14
  76. package/src/controllers/setting.controller.ts +18 -16
  77. package/src/controllers/user.controller.ts +20 -18
  78. package/src/controllers/view-metadata.controller.ts +16 -14
  79. package/src/decorators/solid-request-context.decorator.ts +17 -0
  80. package/src/dtos/solid-request-context.dto.ts +10 -0
  81. package/src/index.ts +2 -1
  82. package/src/services/crud-helper.service.ts +45 -11
  83. package/src/services/crud.service.ts +117 -59
  84. package/src/services/menu-item-metadata.service.ts +7 -8
  85. package/src/services/permission-metadata.service.ts +0 -29
  86. package/src/services/user.service.ts +1 -1
@@ -4,6 +4,8 @@ import { ApiBearerAuth, ApiQuery, ApiTags } from '@nestjs/swagger';
4
4
  import { ViewMetadataService } from '../services/view-metadata.service';
5
5
  import { CreateViewMetadataDto } from '../dtos/create-view-metadata.dto';
6
6
  import { UpdateViewMetadataDto } from '../dtos/update-view-metadata.dto';
7
+ import { SolidRequestContextDecorator } from 'src/decorators/solid-request-context.decorator';
8
+ import { SolidRequestContextDto } from 'src/dtos/solid-request-context.dto';
7
9
 
8
10
  @ApiTags('App')
9
11
  @Controller('view-metadata') //FIXME: Change this to the model plural name
@@ -13,23 +15,23 @@ export class ViewMetadataController {
13
15
  @ApiBearerAuth("jwt")
14
16
  @Post()
15
17
  @UseInterceptors(AnyFilesInterceptor())
16
- create(@Body() createDto: CreateViewMetadataDto, @UploadedFiles() files: Array<Express.Multer.File>) {
17
- return this.service.create(createDto, files);
18
+ create(@Body() createDto: CreateViewMetadataDto, @UploadedFiles() files: Array<Express.Multer.File>,@SolidRequestContextDecorator() solidRequestContext:SolidRequestContextDto) {
19
+ return this.service.create(createDto, files,solidRequestContext);
18
20
  }
19
21
 
20
22
  @ApiBearerAuth("jwt")
21
23
  @Post('/bulk')
22
24
  @UseInterceptors(AnyFilesInterceptor())
23
- insertMany(@Body() createDtos: CreateViewMetadataDto[], @UploadedFiles() filesArray: Express.Multer.File[][] = []) {
24
- return this.service.insertMany(createDtos, filesArray);
25
+ insertMany(@Body() createDtos: CreateViewMetadataDto[], @UploadedFiles() filesArray: Express.Multer.File[][] = [],@SolidRequestContextDecorator() solidRequestContext:SolidRequestContextDto) {
26
+ return this.service.insertMany(createDtos, filesArray,solidRequestContext);
25
27
  }
26
28
 
27
29
 
28
30
  @ApiBearerAuth("jwt")
29
31
  @Put(':id')
30
32
  @UseInterceptors(AnyFilesInterceptor())
31
- update(@Param('id') id: number, @Body() updateDto: UpdateViewMetadataDto, @UploadedFiles() files: Array<Express.Multer.File>) {
32
- return this.service.update(id, updateDto, files);
33
+ update(@Param('id') id: number, @Body() updateDto: UpdateViewMetadataDto, @UploadedFiles() files: Array<Express.Multer.File>,@SolidRequestContextDecorator() solidRequestContext:SolidRequestContextDto) {
34
+ return this.service.update(id, updateDto, files,false,solidRequestContext);
33
35
  }
34
36
 
35
37
  @ApiBearerAuth("jwt")
@@ -44,25 +46,25 @@ export class ViewMetadataController {
44
46
  @ApiQuery({ name: 'populateMedia', required: false, type: Array })
45
47
  @ApiQuery({ name: 'filters', required: false, type: Array })
46
48
  @Get()
47
- async findMany(@Query() query: any) {
48
- return this.service.find(query);
49
+ async findMany(@Query() query: any,@SolidRequestContextDecorator() solidRequestContext:SolidRequestContextDto) {
50
+ return this.service.find(query,solidRequestContext);
49
51
  }
50
52
 
51
53
  @ApiBearerAuth("jwt")
52
54
  @Get(':id')
53
- async findOne(@Param('id') id: string, @Query() query: any) {
54
- return this.service.findOne(+id, query);
55
+ async findOne(@Param('id') id: string, @Query() query: any,@SolidRequestContextDecorator() solidRequestContext:SolidRequestContextDto) {
56
+ return this.service.findOne(+id, query,solidRequestContext);
55
57
  }
56
58
 
57
59
  @Delete('/bulk')
58
- async deleteMany(@Body() ids: number[]) {
59
- return this.service.deleteMany(ids);
60
+ async deleteMany(@Body() ids: number[],@SolidRequestContextDecorator() solidRequestContext:SolidRequestContextDto) {
61
+ return this.service.deleteMany(ids,solidRequestContext);
60
62
  }
61
63
 
62
64
  @ApiBearerAuth("jwt")
63
65
  @Delete(':id')
64
- async delete(@Param('id') id: number) {
65
- return this.service.delete(id);
66
+ async delete(@Param('id') id: number,@SolidRequestContextDecorator() solidRequestContext:SolidRequestContextDto) {
67
+ return this.service.delete(id,solidRequestContext);
66
68
  }
67
69
 
68
70
  @ApiBearerAuth("jwt")
@@ -0,0 +1,17 @@
1
+ import { createParamDecorator, ExecutionContext } from '@nestjs/common';
2
+ import { ActiveUserData } from '../interfaces/active-user-data.interface';
3
+ import { REQUEST_USER_KEY } from '../constants';
4
+ import { SolidRequestContextDto } from 'src/dtos/solid-request-context.dto';
5
+
6
+ export const SolidRequestContextDecorator = createParamDecorator(
7
+ (data: unknown, ctx: ExecutionContext): SolidRequestContextDto => {
8
+ const request = ctx.switchToHttp().getRequest();
9
+ const activeUser: ActiveUserData | undefined = request[REQUEST_USER_KEY]; // Get active user
10
+
11
+ // Create a new instance of SolidRequestContextDto and stamp user data
12
+ const solidRequestContext = new SolidRequestContextDto();
13
+ solidRequestContext.activeUser = activeUser;
14
+
15
+ return solidRequestContext; // Return modified request context DTO
16
+ },
17
+ );
@@ -0,0 +1,10 @@
1
+ import { IsOptional } from 'class-validator';
2
+ import { ActiveUserData } from 'src/interfaces/active-user-data.interface';
3
+
4
+ export class SolidRequestContextDto {
5
+
6
+
7
+ @IsOptional()
8
+ activeUser: ActiveUserData;
9
+
10
+ }
package/src/index.ts CHANGED
@@ -10,6 +10,7 @@ export * from './config/iam.config'
10
10
  export * from './config/cache.options'
11
11
 
12
12
  export * from './decorators/active-user.decorator'
13
+ export * from './decorators/solid-request-context.decorator'
13
14
  export * from './decorators/auth.decorator'
14
15
  export * from './decorators/computed-field-provider.decorator'
15
16
  export * from './decorators/is-not-in-enum.decorator'
@@ -19,8 +20,8 @@ export * from './decorators/roles.decorator'
19
20
  export * from './decorators/selection-provider.decorator'
20
21
  export * from './decorators/solid-database-module.decorator'
21
22
  export * from './decorators/solid-service.decorator'
22
-
23
23
  export * from './dtos/basic-filters.dto'
24
+ export * from './dtos/solid-request-context.dto'
24
25
  export * from './dtos/change-password.dto'
25
26
  export * from './dtos/confirm-forgot-password.dto'
26
27
  export * from './dtos/create-action-metadata.dto'
@@ -1,12 +1,14 @@
1
1
  import { Brackets, SelectQueryBuilder, WhereExpressionBuilder } from "typeorm";
2
2
  import { BasicFilterDto } from "../dtos/basic-filters.dto";
3
+ import { classify } from "@angular-devkit/core/src/utils/strings";
4
+ import { ActiveUserData } from "src/interfaces/active-user-data.interface";
3
5
 
4
6
  export class CrudHelperService {
5
7
  constructor(
6
8
 
7
9
  ) { }
8
10
 
9
-
11
+
10
12
  private orderOptions(sort: any[] = []) {
11
13
  const orderOptions = {};
12
14
  sort.forEach((s: string) => {
@@ -51,7 +53,7 @@ export class CrudHelperService {
51
53
  }
52
54
  else { // Recursively call the applyFilters method to handle nested conditions
53
55
  selectQb.leftJoin(`${alias}.${key}`, key);
54
- this.applyFilters(qb, primaryFilterObj, key, selectQb);
56
+ this.applyFilters(qb, primaryFilterObj, key, selectQb);
55
57
  }
56
58
  });
57
59
  }
@@ -139,7 +141,7 @@ export class CrudHelperService {
139
141
  }, {});
140
142
  }
141
143
 
142
- private normalize(value: string|string[]): string[] {
144
+ private normalize(value: string | string[]): string[] {
143
145
  if (!value) return [];// if the value is nullish, then return an empty array
144
146
  return Array.isArray(value) ? value : [value]; // if the value is an array, return it as is, otherwise return it as an array
145
147
  }
@@ -150,7 +152,7 @@ export class CrudHelperService {
150
152
 
151
153
  buildFilterQuery(qb: SelectQueryBuilder<any>, basicFilterDto: BasicFilterDto, entityAlias: string): SelectQueryBuilder<any> { //TODO : Check how to pass a type to SelectQueryBuilder instead of any
152
154
  let { limit, offset, showSoftDeleted, filters } = basicFilterDto;
153
- const { fields, sort, groupBy, populate = [] } = basicFilterDto;
155
+ const { fields, sort, groupBy, populate = [] } = basicFilterDto;
154
156
 
155
157
  // Normalize the fields, sort, groupBy and populate options i.e (since they can be either a string or an array of strings, when coming from the request)
156
158
  const normalizedFields = this.normalize(fields);
@@ -158,7 +160,7 @@ export class CrudHelperService {
158
160
  const normalizedSort = this.normalize(sort);
159
161
  const normalizedGroupBy = this.normalize(groupBy);
160
162
  if (normalizedGroupBy.length > 1) {
161
- throw new Error('buildFilterQuery: Only 1 Group by field is supported currently');
163
+ throw new Error('buildFilterQuery: Only 1 Group by field is supported currently');
162
164
  }
163
165
 
164
166
  if (filters) {
@@ -171,7 +173,7 @@ export class CrudHelperService {
171
173
  if (normalizedFields && normalizedFields.length) {
172
174
  qb.select(normalizedFields.map(field => {
173
175
  // If the field contains a (, do not prefix the entity alias
174
- return this.isAggregateField(field) ? field : `${entityAlias}.${field}`;
176
+ return this.isAggregateField(field) ? field : `${entityAlias}.${field}`;
175
177
  }));
176
178
  }
177
179
 
@@ -200,11 +202,11 @@ export class CrudHelperService {
200
202
  if (showSoftDeleted === 'inclusive') {
201
203
  qb.withDeleted();
202
204
  }
203
-
205
+
204
206
  if (showSoftDeleted === 'exclusive') {
205
207
  qb.withDeleted();
206
208
  qb.where(`${entityAlias}.deletedAt IS NOT NULL`);
207
- }
209
+ }
208
210
 
209
211
  // Apply the group by options
210
212
  if (normalizedGroupBy && normalizedGroupBy.length) {
@@ -245,9 +247,9 @@ export class CrudHelperService {
245
247
 
246
248
  getGroupName(group: any, alias: string): string {
247
249
  return Object.keys(group)
248
- .filter(key => !this.isAggregateFieldKey(key, alias))
249
- .map(key => group[key])
250
- .join('_');
250
+ .filter(key => !this.isAggregateFieldKey(key, alias))
251
+ .map(key => group[key])
252
+ .join('_');
251
253
  }
252
254
 
253
255
  createGroupRecords(group: any, alias: string, groupData: any) {
@@ -272,4 +274,36 @@ export class CrudHelperService {
272
274
  };
273
275
  }
274
276
 
277
+
278
+ hasReadPermissionOnModel = (activeUser: ActiveUserData, modelName: string) => {
279
+ const permissionNames = [`${classify(modelName)}Controller.findOne`, `${classify(modelName)}Controller.findMany`];
280
+ const matchingPermssions = activeUser.permissions.filter((p) => permissionNames.includes(p));
281
+ return matchingPermssions.length > 0
282
+ }
283
+
284
+ hasWritePermissionOnModel = (activeUser: ActiveUserData, modelName: string) => {
285
+ const permissionNames = [`${classify(modelName)}Controller.create`, `${classify(modelName)}Controller.insertMany`, `${classify(modelName)}Controller.update`];
286
+ const matchingPermssions = activeUser.permissions.filter((p) => permissionNames.includes(p));
287
+ return matchingPermssions.length > 0
288
+ }
289
+
290
+ hasUpdatePermissionOnModel = (activeUser: ActiveUserData, modelName: string) => {
291
+ const permissionNames = [`${classify(modelName)}Controller.update`];
292
+ const matchingPermssions = activeUser.permissions.filter((p) => permissionNames.includes(p));
293
+ return matchingPermssions.length > 0
294
+ }
295
+
296
+ hasDeletePermissionOnModel = (activeUser: ActiveUserData, modelName: string) => {
297
+ const permissionNames = [`${classify(modelName)}Controller.delete`, `${classify(modelName)}Controller.deleteMany`];
298
+ const matchingPermssions = activeUser.permissions.filter((p) => permissionNames.includes(p));
299
+ return matchingPermssions.length > 0
300
+ }
301
+ hasCreatePermissionOnModel = (activeUser: ActiveUserData, modelName: string) => {
302
+ const permissionNames = [`${classify(modelName)}Controller.create`];
303
+ const matchingPermssions = activeUser.permissions.filter((p) => permissionNames.includes(p));
304
+ return matchingPermssions.length > 0
305
+ }
306
+
307
+
308
+
275
309
  }
@@ -36,6 +36,7 @@ import { MediaService } from "./media.service";
36
36
  import { getMediaStorageProvider } from "./mediaStorageProviders";
37
37
  import { ModelMetadataService } from "./model-metadata.service";
38
38
  import { ModuleMetadataService } from "./module-metadata.service";
39
+ import { SolidRequestContextDto } from "src/dtos/solid-request-context.dto";
39
40
 
40
41
  const DEFAULT_LIMIT = 10;
41
42
  const DEFAULT_OFFSET = 0;
@@ -57,7 +58,7 @@ export class CRUDService<T> { //Add two generic value i.e Person,CreatePersonDto
57
58
  //We can just have the Model Entity here
58
59
  ) { }
59
60
 
60
- async create(createDto: any, files: Express.Multer.File[] = []): Promise<T> {
61
+ async create(createDto: any, files: Express.Multer.File[] = [],solidRequestContext :any = {}): Promise<T> {
61
62
  // This class will be extended by the generated service class i.e PersonService
62
63
  // The data required to identify the model and module name will be passed from the generate CrudService subclass
63
64
  //TODO: Algorithm to create the entity
@@ -66,6 +67,13 @@ export class CRUDService<T> { //Add two generic value i.e Person,CreatePersonDto
66
67
  let hasMediaFields = false;
67
68
 
68
69
  const model = await this.loadModel();
70
+ // Check wheather user has create permission for model
71
+ if (solidRequestContext.activeUser) {
72
+ const hasPermission = this.crudHelperService.hasCreatePermissionOnModel(solidRequestContext.activeUser, model.singularName);
73
+ if (!hasPermission) {
74
+ throw new BadRequestException('Forbidden');
75
+ }
76
+ }
69
77
  // const inverseRelationFields = await this.loadInverseRelationFields();
70
78
  const fieldsToProcess = [...model.fields];
71
79
 
@@ -162,10 +170,18 @@ export class CRUDService<T> { //Add two generic value i.e Person,CreatePersonDto
162
170
  }
163
171
 
164
172
  //TODO: Will the updates be partial i.e PATCH or full i.e PUT
165
- async update(id: number, updateDto: any, files: Express.Multer.File[] = [], isPartialUpdate: boolean = false): Promise<T> {
173
+ async update(id: number, updateDto: any, files: Express.Multer.File[] = [], isPartialUpdate: boolean = false, solidRequestContext: any = {}): Promise<T> {
166
174
  if (!id) {
167
175
  throw new Error('Id is required for update');
168
176
  }
177
+ const model = await this.loadModel();
178
+ // Check wheather user has update permission for model
179
+ if (solidRequestContext.activeUser) {
180
+ const hasPermission = this.crudHelperService.hasUpdatePermissionOnModel(solidRequestContext.activeUser, model.singularName);
181
+ if (!hasPermission) {
182
+ throw new BadRequestException('Forbidden');
183
+ }
184
+ }
169
185
  const entity = await this.repo.findOne({
170
186
  where: {
171
187
  //@ts-ignore
@@ -184,7 +200,6 @@ export class CRUDService<T> { //Add two generic value i.e Person,CreatePersonDto
184
200
  // FIXME This can be optimized to take in module name i.e (handle scenario wherein same model exists in multiple modules)
185
201
  let hasMediaFields = false;
186
202
 
187
- const model = await this.loadModel();
188
203
  const fieldsToProcess = [...model.fields];
189
204
 
190
205
  // 2. Loop through the fields with a switch statement
@@ -208,11 +223,19 @@ export class CRUDService<T> { //Add two generic value i.e Person,CreatePersonDto
208
223
  }
209
224
 
210
225
  //TODO: Will the updates be partial i.e PATCH or full i.e PUT
211
- async delete(id: number) {
226
+ async delete(id: number, solidRequestContext: any = {}) {
212
227
  if (!id) {
213
228
  throw new Error('Id is required for update');
214
229
  }
215
-
230
+ const loadedmodel = await this.loadModel();
231
+ // Check wheather user has update permission for model
232
+ if (solidRequestContext.activeUser) {
233
+ const hasPermission = this.crudHelperService.hasDeletePermissionOnModel(solidRequestContext.activeUser, loadedmodel.singularName);
234
+ if (!hasPermission) {
235
+ throw new BadRequestException('Forbidden');
236
+ }
237
+ }
238
+
216
239
  const model = await this.modelMetadataService.findOneBySingularName(this.modelName, {
217
240
  fields: {
218
241
  model: true,
@@ -305,25 +328,25 @@ export class CRUDService<T> { //Add two generic value i.e Person,CreatePersonDto
305
328
  case SolidFieldType.relation: {
306
329
  // Identify if the field is for the inverse side or not
307
330
  if (fieldMetadata.relationType === RelationType.manyToOne) {
308
- const manyToOneOptions: ManyToOneRelationFieldOptions = {
309
- ...commonOptions,
310
- relationCoModelSingularName: fieldMetadata.relationCoModelSingularName,
311
- modelUserKeyFieldName: fieldMetadata.model.userKeyField?.name,
312
- modelSingularName: fieldMetadata.model.singularName,
313
- entityManager,
314
- }
315
- return new ManyToOneRelationFieldCrudManager(manyToOneOptions);
331
+ const manyToOneOptions: ManyToOneRelationFieldOptions = {
332
+ ...commonOptions,
333
+ relationCoModelSingularName: fieldMetadata.relationCoModelSingularName,
334
+ modelUserKeyFieldName: fieldMetadata.model.userKeyField?.name,
335
+ modelSingularName: fieldMetadata.model.singularName,
336
+ entityManager,
337
+ }
338
+ return new ManyToOneRelationFieldCrudManager(manyToOneOptions);
316
339
  }
317
340
  else if (fieldMetadata.relationType === RelationType.oneToMany) {
318
- const oneToManyOptions: OneToManyRelationFieldOptions = {
319
- ...commonOptions,
320
- relationCoModelSingularName: fieldMetadata.relationCoModelSingularName,
321
- modelSingularName: fieldMetadata.model.singularName,
322
- entityManager,
323
- inverseFieldName: fieldMetadata.relationCoModelFieldName,
324
- inverseRelationCoModelFieldName: fieldMetadata.name,
325
- }
326
- return new OneToManyRelationFieldCrudManager(oneToManyOptions);
341
+ const oneToManyOptions: OneToManyRelationFieldOptions = {
342
+ ...commonOptions,
343
+ relationCoModelSingularName: fieldMetadata.relationCoModelSingularName,
344
+ modelSingularName: fieldMetadata.model.singularName,
345
+ entityManager,
346
+ inverseFieldName: fieldMetadata.relationCoModelFieldName,
347
+ inverseRelationCoModelFieldName: fieldMetadata.name,
348
+ }
349
+ return new OneToManyRelationFieldCrudManager(oneToManyOptions);
327
350
  }
328
351
  else if (fieldMetadata.relationType === RelationType.manyTomany) {
329
352
  if (fieldMetadata.isRelationManyToManyOwner) {
@@ -335,7 +358,7 @@ export class CRUDService<T> { //Add two generic value i.e Person,CreatePersonDto
335
358
  entityManager,
336
359
  fieldName: fieldMetadata.name,
337
360
  }
338
- return new ManyToManyRelationFieldCrudManager(manyToManyOptions);
361
+ return new ManyToManyRelationFieldCrudManager(manyToManyOptions);
339
362
  }
340
363
  else {
341
364
  const inverseManyToManyOptions: ManyToManyRelationFieldOptions = {
@@ -392,10 +415,18 @@ export class CRUDService<T> { //Add two generic value i.e Person,CreatePersonDto
392
415
  }
393
416
 
394
417
 
395
- async find(basicFilterDto: BasicFilterDto) {
418
+ async find(basicFilterDto: BasicFilterDto, solidRequestContext: any = {}) {
396
419
  const alias = 'entity';
397
420
  // Extract the required keys from the input query
398
- let { limit, offset, populateMedia, populateGroup,groupFilter } = basicFilterDto;
421
+ let { limit, offset, populateMedia, populateGroup, groupFilter } = basicFilterDto;
422
+ const model = await this.loadModel();
423
+ // Check wheather user has update permission for model
424
+ if (solidRequestContext.activeUser) {
425
+ const hasPermission = this.crudHelperService.hasReadPermissionOnModel(solidRequestContext.activeUser, model.singularName);
426
+ if (!hasPermission) {
427
+ throw new BadRequestException('Forbidden');
428
+ }
429
+ }
399
430
 
400
431
  // Create above query on pincode table using query builder
401
432
  var qb: SelectQueryBuilder<T> = this.repo.createQueryBuilder(alias)
@@ -403,7 +434,7 @@ export class CRUDService<T> { //Add two generic value i.e Person,CreatePersonDto
403
434
 
404
435
  if (basicFilterDto.groupBy) {
405
436
  // Get the records and the count
406
- const { groupMeta, groupRecords } = await this.handleGroupFind(qb, groupFilter,populateGroup, alias, populateMedia);
437
+ const { groupMeta, groupRecords } = await this.handleGroupFind(qb, groupFilter, populateGroup, alias, populateMedia);
407
438
  return {
408
439
  groupMeta,
409
440
  groupRecords,
@@ -430,7 +461,7 @@ export class CRUDService<T> { //Add two generic value i.e Person,CreatePersonDto
430
461
  return this.wrapFindResponse(offset, limit, count, entities);
431
462
  }
432
463
 
433
- private async handleGroupFind(qb: SelectQueryBuilder<T>, groupFilter: BasicFilterDto,populateGroup: boolean, alias: string, populateMedia: string[]) {
464
+ private async handleGroupFind(qb: SelectQueryBuilder<T>, groupFilter: BasicFilterDto, populateGroup: boolean, alias: string, populateMedia: string[]) {
434
465
  const groupByResult = await qb.getRawMany();
435
466
 
436
467
  const groupMeta = [];
@@ -515,8 +546,18 @@ export class CRUDService<T> { //Add two generic value i.e Person,CreatePersonDto
515
546
  }
516
547
  }
517
548
 
518
- async findOne(id: number, query: any) {
549
+ async findOne(id: number, query: any, solidRequestContext: any = {}) {
519
550
  const { populate = [], fields = [], populateMedia = [] } = query;
551
+
552
+ const model = await this.loadModel();
553
+ // Check wheather user has update permission for model
554
+ if (solidRequestContext.activeUser) {
555
+ const hasPermission = this.crudHelperService.hasReadPermissionOnModel(solidRequestContext.activeUser, model.singularName);
556
+ if (!hasPermission) {
557
+ throw new BadRequestException('Forbidden');
558
+ }
559
+ }
560
+
520
561
  const entity = await this.repo.findOne({
521
562
  where: {
522
563
  //@ts-ignore
@@ -557,13 +598,22 @@ export class CRUDService<T> { //Add two generic value i.e Person,CreatePersonDto
557
598
  return entity;
558
599
  }
559
600
 
560
- async insertMany(createDtos: any[], filesArray: Express.Multer.File[][] = []): Promise<T[]> {
601
+ async insertMany(createDtos: any[], filesArray: Express.Multer.File[][] = [],solidRequestContext: any = {}): Promise<T[]> {
561
602
 
562
603
 
563
604
  // if (createDtos.length !== filesArray.length) {
564
605
  // throw new BadRequestException('Mismatch between data objects and file arrays.');
565
606
  // }
566
607
 
608
+ const loadedmodel = await this.loadModel();
609
+ // Check wheather user has create permission for model
610
+ if (solidRequestContext.activeUser) {
611
+ const hasPermission = this.crudHelperService.hasCreatePermissionOnModel(solidRequestContext.activeUser, loadedmodel.singularName);
612
+ if (!hasPermission) {
613
+ throw new BadRequestException('Forbidden');
614
+ }
615
+ }
616
+
567
617
  // Fetch model metadata once
568
618
  const model = await this.modelMetadataService.findOneBySingularName(this.modelName, {
569
619
  fields: {
@@ -607,12 +657,20 @@ export class CRUDService<T> { //Add two generic value i.e Person,CreatePersonDto
607
657
  return savedEntities;
608
658
  }
609
659
 
610
- async deleteMany(ids: number[]): Promise<any> {
660
+ async deleteMany(ids: number[],solidRequestContext: any = {}): Promise<any> {
611
661
 
612
662
  if (!ids || ids.length === 0) {
613
663
  throw new Error('At least one ID is required for deletion');
614
664
  }
615
665
 
666
+ const loadedmodel = await this.loadModel();
667
+ // Check wheather user has update permission for model
668
+ if (solidRequestContext.activeUser) {
669
+ const hasPermission = this.crudHelperService.hasDeletePermissionOnModel(solidRequestContext.activeUser, loadedmodel.singularName);
670
+ if (!hasPermission) {
671
+ throw new BadRequestException('Forbidden');
672
+ }
673
+ }
616
674
  const model = await this.modelMetadataService.findOneBySingularName(this.modelName, {
617
675
  fields: {
618
676
  model: true,
@@ -645,41 +703,41 @@ export class CRUDService<T> { //Add two generic value i.e Person,CreatePersonDto
645
703
 
646
704
  async recover(id: number) {
647
705
  try {
648
- const softDeletedRows = await this.repo.findOne({
649
- where: {
650
- //@ts-ignore
651
- id, deletedAt: Not(IsNull())
652
- },
653
- withDeleted: true,
654
- });
655
-
656
- if (!softDeletedRows) {
657
- throw new Error('No soft-deleted record found with the given ID.');
658
- }
659
-
660
- await this.repo.update(id, {
706
+ const softDeletedRows = await this.repo.findOne({
707
+ where: {
708
+ //@ts-ignore
709
+ id, deletedAt: Not(IsNull())
710
+ },
711
+ withDeleted: true,
712
+ });
713
+
714
+ if (!softDeletedRows) {
715
+ throw new Error('No soft-deleted record found with the given ID.');
716
+ }
717
+
718
+ await this.repo.update(id, {
661
719
  //@ts-ignore
662
- deletedAt: null, deletedTracker: "not-deleted"
720
+ deletedAt: null, deletedTracker: "not-deleted"
663
721
  });
664
-
665
- return { message: 'Record successfully recovered', data: softDeletedRows };
722
+
723
+ return { message: 'Record successfully recovered', data: softDeletedRows };
666
724
  } catch (error) {
667
- if (error instanceof QueryFailedError) {
668
- if ((error as any).code === '23505') {
669
- throw new Error('Another record is conflicting with the record you are attempting to Un-Archive, either delete or change the other record so as to avoid this conflict.');
725
+ if (error instanceof QueryFailedError) {
726
+ if ((error as any).code === '23505') {
727
+ throw new Error('Another record is conflicting with the record you are attempting to Un-Archive, either delete or change the other record so as to avoid this conflict.');
728
+ }
670
729
  }
671
- }
672
-
673
- throw new Error(error);
730
+
731
+ throw new Error(error);
674
732
  }
675
733
  }
676
-
734
+
677
735
  async recoverMany(ids: number[]) {
678
736
  try {
679
737
  if (!ids || ids.length === 0) {
680
738
  throw new Error("No IDs provided for recovery.");
681
739
  }
682
-
740
+
683
741
  // Find soft-deleted records matching the given IDs
684
742
  const softDeletedRows = await this.repo.find({
685
743
  where: {
@@ -689,18 +747,18 @@ export class CRUDService<T> { //Add two generic value i.e Person,CreatePersonDto
689
747
  },
690
748
  withDeleted: true,
691
749
  });
692
-
750
+
693
751
  if (softDeletedRows.length === 0) {
694
752
  throw new Error("No matching soft-deleted records found.");
695
753
  }
696
-
754
+
697
755
  // Recover the specific records by setting deletedAt to null
698
756
  await this.repo.update(
699
757
  //@ts-ignore
700
758
  { id: In(ids) },
701
759
  { deletedAt: null, deletedTracker: "not-deleted" }
702
760
  );
703
-
761
+
704
762
  return { message: "Selected records successfully recovered", recoveredIds: ids };
705
763
  } catch (error) {
706
764
  if (error instanceof QueryFailedError) {
@@ -710,10 +768,10 @@ export class CRUDService<T> { //Add two generic value i.e Person,CreatePersonDto
710
768
  );
711
769
  }
712
770
  }
713
-
771
+
714
772
  throw new Error(error);
715
773
  }
716
774
  }
717
-
775
+
718
776
 
719
777
  }
@@ -17,7 +17,6 @@ import { UpdateMenuItemMetadataDto } from '../dtos/update-menu-item-metadata.dto
17
17
  import { ActiveUserData } from 'src/interfaces/active-user-data.interface';
18
18
  import { ModuleMetadata } from '../entities/module-metadata.entity';
19
19
  import { dasherize } from '@angular-devkit/core/src/utils/strings';
20
- import { hasReadPermissionOnModel } from './permission-metadata.service';
21
20
 
22
21
  @Injectable()
23
22
  export class MenuItemMetadataService extends CRUDService<MenuItemMetadata> {
@@ -148,13 +147,13 @@ export class MenuItemMetadataService extends CRUDService<MenuItemMetadata> {
148
147
  // Recursive function to build the tree
149
148
  private buildMenuTree(rootItems: MenuItemMetadata[], allMenuItems: MenuItemMetadata[], activeUser: ActiveUserData): any[] {
150
149
  const menuItemsData = rootItems.map(rootItem => {
151
- const allowedMenuItems =allMenuItems.filter(i => {
152
- if(!i.parentMenuItem){
150
+ const allowedMenuItems = allMenuItems.filter(i => {
151
+ if (!i.parentMenuItem) {
153
152
  return true
154
- }else{
155
- const hasPermission = hasReadPermissionOnModel(activeUser, i.action.model.singularName);
156
- return hasReadPermissionOnModel(activeUser, i.action.model.singularName)
157
- }});
153
+ } else {
154
+ return this.crudHelperService.hasReadPermissionOnModel(activeUser, i.action.model.singularName)
155
+ }
156
+ });
158
157
  // Get immediate children of the current loop variable menuItem.
159
158
  const children = allowedMenuItems.filter(item => item.parentMenuItem && item.parentMenuItem.id === rootItem.id);
160
159
 
@@ -166,7 +165,7 @@ export class MenuItemMetadataService extends CRUDService<MenuItemMetadata> {
166
165
  path = rootItem.action.customComponent;
167
166
  }
168
167
  if (rootItem.action && rootItem.action.type === 'solid') {
169
- if (hasReadPermissionOnModel(activeUser, rootItem.action.model.singularName)) {
168
+ if (this.crudHelperService.hasReadPermissionOnModel(activeUser, rootItem.action.model.singularName)) {
170
169
 
171
170
 
172
171
  // TODO: Here we are assuming that we will always take the user to collection view of a model.
@@ -61,33 +61,4 @@ export class PermissionMetadataService extends CRUDService<PermissionMetadata> {
61
61
 
62
62
  }
63
63
 
64
- export const hasReadPermissionOnModel = (activeUser: ActiveUserData, modelName: string) => {
65
- const permissionNames = [`${classify(modelName)}Controller.findOne`, `${classify(modelName)}Controller.findMany`];
66
- const matchingPermssions = activeUser.permissions.filter((p) => permissionNames.includes(p));
67
- return matchingPermssions.length > 0
68
- }
69
-
70
- export const hasWritePermissionOnModel = (activeUser: ActiveUserData, modelName: string) => {
71
- const permissionNames = [`${classify(modelName)}Controller.create`, `${classify(modelName)}Controller.insertMany`, `${classify(modelName)}Controller.update`];
72
- const matchingPermssions = activeUser.permissions.filter((p) => permissionNames.includes(p));
73
- return matchingPermssions.length > 0
74
- }
75
-
76
- export const hasUpdatePermissionOnModel = (activeUser: ActiveUserData, modelName: string) => {
77
- const permissionNames = [`${classify(modelName)}Controller.update`];
78
- const matchingPermssions = activeUser.permissions.filter((p) => permissionNames.includes(p));
79
- return matchingPermssions.length > 0
80
- }
81
-
82
- export const hasDeletePermissionOnModel = (activeUser: ActiveUserData, modelName: string) => {
83
- const permissionNames = [`${classify(modelName)}Controller.delete`, `${classify(modelName)}Controller.deleteMany`];
84
- const matchingPermssions = activeUser.permissions.filter((p) => permissionNames.includes(p));
85
- return matchingPermssions.length > 0
86
- }
87
- export const hasCreatePermissionOnModel = (activeUser: ActiveUserData, modelName: string) => {
88
- const permissionNames = [`${classify(modelName)}Controller.create`];
89
- const matchingPermssions = activeUser.permissions.filter((p) => permissionNames.includes(p));
90
- return matchingPermssions.length > 0
91
- }
92
-
93
64
 
@@ -75,7 +75,7 @@ export class UserService extends CRUDService<User> {
75
75
  // return entity;
76
76
  }
77
77
 
78
- async updateUser(id: any, updateDto, files) {
78
+ async updateUser(id: any, updateDto, files,solidRequestContext :any = {}) {
79
79
  const user = await this.repo.findOne({
80
80
  where: { id: id },
81
81
  relations: {