@lenne.tech/nest-server 8.3.1 → 8.4.0

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 (54) hide show
  1. package/dist/core/common/decorators/restricted.decorator.d.ts +11 -5
  2. package/dist/core/common/decorators/restricted.decorator.js +64 -23
  3. package/dist/core/common/decorators/restricted.decorator.js.map +1 -1
  4. package/dist/core/common/enums/process-type.enum.d.ts +4 -0
  5. package/dist/core/common/enums/process-type.enum.js +9 -0
  6. package/dist/core/common/enums/process-type.enum.js.map +1 -0
  7. package/dist/core/common/enums/role.enum.d.ts +1 -2
  8. package/dist/core/common/enums/role.enum.js +0 -1
  9. package/dist/core/common/enums/role.enum.js.map +1 -1
  10. package/dist/core/common/helpers/db.helper.d.ts +3 -3
  11. package/dist/core/common/helpers/db.helper.js +25 -25
  12. package/dist/core/common/helpers/db.helper.js.map +1 -1
  13. package/dist/core/common/helpers/input.helper.d.ts +6 -5
  14. package/dist/core/common/helpers/input.helper.js +4 -15
  15. package/dist/core/common/helpers/input.helper.js.map +1 -1
  16. package/dist/core/common/interfaces/service-options.interface.d.ts +0 -2
  17. package/dist/core/common/models/core-persistence.model.d.ts +0 -1
  18. package/dist/core/common/models/core-persistence.model.js +0 -10
  19. package/dist/core/common/models/core-persistence.model.js.map +1 -1
  20. package/dist/core/common/services/module.service.d.ts +3 -3
  21. package/dist/core/common/services/module.service.js +13 -18
  22. package/dist/core/common/services/module.service.js.map +1 -1
  23. package/dist/core/common/types/require-only-one.type.d.ts +3 -0
  24. package/dist/core/common/types/require-only-one.type.js +3 -0
  25. package/dist/core/common/types/require-only-one.type.js.map +1 -0
  26. package/dist/core/common/types/required-at-least-one.type.d.ts +3 -0
  27. package/dist/core/common/types/required-at-least-one.type.js +3 -0
  28. package/dist/core/common/types/required-at-least-one.type.js.map +1 -0
  29. package/dist/core.module.js +0 -5
  30. package/dist/core.module.js.map +1 -1
  31. package/dist/index.d.ts +3 -0
  32. package/dist/index.js +3 -0
  33. package/dist/index.js.map +1 -1
  34. package/dist/server/modules/user/user.model.d.ts +1 -1
  35. package/dist/server/modules/user/user.model.js +1 -1
  36. package/dist/server/modules/user/user.model.js.map +1 -1
  37. package/dist/server/modules/user/user.service.js +1 -1
  38. package/dist/server/modules/user/user.service.js.map +1 -1
  39. package/dist/tsconfig.build.tsbuildinfo +1 -1
  40. package/package.json +3 -3
  41. package/src/core/common/decorators/restricted.decorator.ts +106 -45
  42. package/src/core/common/enums/process-type.enum.ts +7 -0
  43. package/src/core/common/enums/role.enum.ts +1 -4
  44. package/src/core/common/helpers/db.helper.ts +43 -51
  45. package/src/core/common/helpers/input.helper.ts +24 -15
  46. package/src/core/common/interfaces/service-options.interface.ts +2 -6
  47. package/src/core/common/models/core-persistence.model.ts +0 -11
  48. package/src/core/common/services/module.service.ts +15 -22
  49. package/src/core/common/types/require-only-one.type.ts +6 -0
  50. package/src/core/common/types/required-at-least-one.type.ts +6 -0
  51. package/src/core.module.ts +1 -19
  52. package/src/index.ts +3 -0
  53. package/src/server/modules/user/user.model.ts +2 -2
  54. package/src/server/modules/user/user.service.ts +3 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lenne.tech/nest-server",
3
- "version": "8.3.1",
3
+ "version": "8.4.0",
4
4
  "description": "Modern, fast, powerful Node.js web framework in TypeScript based on Nest with a GraphQL API and a connection to MongoDB (or other databases).",
5
5
  "keywords": [
6
6
  "node",
@@ -98,7 +98,7 @@
98
98
  "@typescript-eslint/eslint-plugin": "5.22.0",
99
99
  "@typescript-eslint/parser": "5.22.0",
100
100
  "coffeescript": "2.7.0",
101
- "eslint": "8.14.0",
101
+ "eslint": "8.15.0",
102
102
  "eslint-config-prettier": "8.5.0",
103
103
  "find-file-up": "2.0.1",
104
104
  "grunt": "1.5.2",
@@ -107,7 +107,7 @@
107
107
  "grunt-contrib-watch": "1.1.0",
108
108
  "grunt-sync": "0.8.2",
109
109
  "husky": "7.0.4",
110
- "jest": "28.0.3",
110
+ "jest": "28.1.0",
111
111
  "pm2": "5.2.0",
112
112
  "prettier": "2.6.2",
113
113
  "pretty-quick": "3.1.3",
@@ -1,43 +1,55 @@
1
1
  import 'reflect-metadata';
2
2
  import { UnauthorizedException } from '@nestjs/common';
3
+ import { ProcessType } from '../enums/process-type.enum';
3
4
  import { RoleEnum } from '../enums/role.enum';
4
- import { equalIds, getStringIds } from '../helpers/db.helper';
5
- import { IdsType } from '../types/ids.type';
5
+ import { getIncludedIds } from '../helpers/db.helper';
6
+ import { RequireAtLeastOne } from '../types/required-at-least-one.type';
6
7
 
7
8
  /**
8
9
  * Restricted meta key
9
10
  */
10
11
  const restrictedMetaKey = Symbol('restricted');
12
+ export type RestrictedType = (
13
+ | string
14
+ | RequireAtLeastOne<
15
+ { memberOf?: string | string[]; roles?: string | string[]; processType?: ProcessType },
16
+ 'memberOf' | 'roles'
17
+ >
18
+ )[];
11
19
 
12
20
  /**
13
21
  * Decorator for restricted properties
14
22
  *
15
- * If the decorator is used it will be checked if the current user has one of the included roles.
16
- * If this is not the case, the property is removed from the return object.
23
+ * The restricted decorator can include roles as strings or object with property `roles`
24
+ * and memberships as objects with property `memberOf`.
17
25
  *
18
- * Activation of the CheckResponseInterceptor is necessary for use.
26
+ * Roles:
27
+ * If one or more Role(Enum)s are set, the user must have at least one of it in his `role` property.
28
+ *
29
+ * Memberships:
30
+ * If one or more membership objects are set, the ID of the user must be included in one of the
31
+ * properties of the processed item, which is specified by the value of `memberOf`
32
+ * Via processType the restriction can be set for input or output only
19
33
  */
20
- export const Restricted = (...roles: string[]): PropertyDecorator => {
21
- return Reflect.metadata(restrictedMetaKey, roles);
34
+ export const Restricted = (...rolesOrMember: RestrictedType): PropertyDecorator => {
35
+ return Reflect.metadata(restrictedMetaKey, rolesOrMember);
22
36
  };
23
37
 
24
38
  /**
25
39
  * Get restricted
26
40
  */
27
- export const getRestricted = (object: unknown, propertyKey: string) => {
41
+ export const getRestricted = (object: unknown, propertyKey: string): RestrictedType => {
28
42
  return Reflect.getMetadata(restrictedMetaKey, object, propertyKey);
29
43
  };
30
44
 
31
45
  /**
32
46
  * Check data for restricted properties (properties with `Restricted` decorator)
33
- *
34
- * If restricted roles includes RoleEnum.S_CREATOR, `creator` (createdBy) from current (DB) data must be set in options
35
- * If restricted roles includes RoleEnum.S_OWNER, `ownerIds` from current (DB) data must be set in options
47
+ * For special Roles and member of group checking the dbObject must be set in options
36
48
  */
37
49
  export const checkRestricted = (
38
50
  data: any,
39
51
  user: { id: any; hasRole: (roles: string[]) => boolean },
40
- options: { creator?: IdsType; ignoreUndefined?: boolean; ownerIds?: IdsType; throwError?: boolean } = {},
52
+ options: { dbObject?: any; ignoreUndefined?: boolean; processType?: ProcessType; throwError?: boolean } = {},
41
53
  processedObjects: any[] = []
42
54
  ) => {
43
55
  const config = {
@@ -70,50 +82,99 @@ export const checkRestricted = (
70
82
  continue;
71
83
  }
72
84
 
73
- // Get roles
74
- const roles = getRestricted(data, propertyKey);
85
+ // Check restricted
86
+ const restricted = getRestricted(data, propertyKey);
87
+ let valid = true;
88
+ if (restricted?.length) {
89
+ valid = false;
75
90
 
76
- // If roles are specified
77
- if (roles && roles.some((value) => !!value)) {
78
- // Check user and user roles
79
- if (!user || !user.hasRole(roles)) {
80
- // Check special creator role
81
- if (user?.id && roles.includes(RoleEnum.S_CREATOR) && equalIds(user.id, config.creator)) {
82
- continue;
91
+ // Get roles
92
+ const roles: string[] = [];
93
+ restricted.forEach((item) => {
94
+ if (typeof item === 'string') {
95
+ roles.push(item);
96
+ } else if (
97
+ item?.roles?.length &&
98
+ (config.processType && item.processType ? config.processType === item.processType : true)
99
+ ) {
100
+ if (Array.isArray(item.roles)) {
101
+ roles.push(...item.roles);
102
+ } else {
103
+ roles.push(item.roles);
104
+ }
83
105
  }
106
+ });
107
+
108
+ // Check roles
109
+ if (roles.length) {
110
+ // Check roles
111
+ if (
112
+ user?.hasRole(roles) ||
113
+ (roles.includes(RoleEnum.S_CREATOR) && getIncludedIds(config.dbObject?.createdBy, user))
114
+ ) {
115
+ valid = true;
116
+ }
117
+ }
118
+
119
+ if (!valid) {
120
+ // Get groups
121
+ const groups = restricted.filter((item) => {
122
+ return (
123
+ typeof item === 'object' &&
124
+ // Check if object is valid
125
+ item.memberOf.length &&
126
+ // Check if processType is specified and is valid for current process
127
+ (config.processType && item.processType ? config.processType === item.processType : true)
128
+ );
129
+ }) as { memberOf: string | string[] }[];
84
130
 
85
- // Check special owner role
86
- else if (user && roles.includes(RoleEnum.S_OWNER)) {
87
- const userId = getStringIds(user);
88
- const ownerIds = config.ownerIds ? getStringIds(config.ownerIds) : null;
89
-
90
- if (
91
- // No owner IDs
92
- !ownerIds ||
93
- // User is not the owner
94
- !(ownerIds === userId || (Array.isArray(ownerIds) && ownerIds.includes(userId)))
95
- ) {
96
- // The user does not have the required rights and is not the owner
97
- if (config.throwError) {
98
- if (!config.ownerIds) {
99
- throw new UnauthorizedException('Lack of ownerIds to verify ownership of ' + propertyKey);
131
+ // Check groups
132
+ if (groups.length) {
133
+ // Get members from groups
134
+ const members = [];
135
+ for (const group of groups) {
136
+ let properties: string[] = group.memberOf as string[];
137
+ if (!Array.isArray(group.memberOf)) {
138
+ properties = [group.memberOf];
139
+ }
140
+ for (const property of properties) {
141
+ const items = config.dbObject?.[property];
142
+ if (items) {
143
+ if (Array.isArray(items)) {
144
+ members.concat(items);
145
+ } else {
146
+ members.push(items);
147
+ }
100
148
  }
101
- throw new UnauthorizedException('Current user is not allowed to set ' + propertyKey);
102
149
  }
103
- continue;
104
150
  }
105
- } else {
106
- // The user does not have the required rights
107
- if (config.throwError) {
108
- throw new UnauthorizedException('Current user is not allowed to set ' + propertyKey);
151
+
152
+ // Check if user is a member
153
+ if (getIncludedIds(members, user)) {
154
+ valid = true;
109
155
  }
110
- continue;
156
+ }
157
+
158
+ // Check if there are no limitations
159
+ if (!roles.length && !groups.length) {
160
+ valid = true;
111
161
  }
112
162
  }
113
163
  }
114
164
 
115
- // Check property data
116
- data[propertyKey] = checkRestricted(data[propertyKey], user, config, processedObjects);
165
+ // Check rights
166
+ if (valid) {
167
+ // Check deep
168
+ data[propertyKey] = checkRestricted(data[propertyKey], user, config, processedObjects);
169
+ } else {
170
+ // Throw error
171
+ if (config.throwError) {
172
+ throw new UnauthorizedException('The current user has no access rights for ' + propertyKey);
173
+ }
174
+
175
+ // Remove property
176
+ delete data[propertyKey];
177
+ }
117
178
  }
118
179
 
119
180
  // Return processed data
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Current process type for data input or data output
3
+ */
4
+ export enum ProcessType {
5
+ INPUT = 'input',
6
+ OUTPUT = 'output',
7
+ }
@@ -15,7 +15,7 @@ export enum RoleEnum {
15
15
  // and via ServiceOptions for Resolver methods. This roles should not be integrated into user.roles!
16
16
  // ===================================================================================================================
17
17
 
18
- // User must be signed in (see context user, e.g. @GraphQLUser)
18
+ // User must be logged in (see context user, e.g. @GraphQLUser)
19
19
  S_USER = 's_user',
20
20
 
21
21
  // ===================================================================================================================
@@ -25,7 +25,4 @@ export enum RoleEnum {
25
25
 
26
26
  // User must be the creator of the processed object(s) (see createdBy property of object(s))
27
27
  S_CREATOR = 's_creator',
28
-
29
- // User must be an owner of the processed object(s) (see owners property of object(s))
30
- S_OWNER = 's_owner',
31
28
  }
@@ -84,7 +84,7 @@ export function addIds(
84
84
  */
85
85
  export function equalIds(...ids: IdsType[]): boolean {
86
86
  if (!ids) {
87
- return true;
87
+ return false;
88
88
  }
89
89
  const compare = getStringIds(ids[0]);
90
90
  if (!compare) {
@@ -93,10 +93,51 @@ export function equalIds(...ids: IdsType[]): boolean {
93
93
  return ids.every((id) => getStringIds(id) === compare);
94
94
  }
95
95
 
96
+ /**
97
+ * Get included ids
98
+ * @param includes IdsType, which should be checked if it contains the ID
99
+ * @param ids IdsType, which should be included
100
+ * @param convert If set the result array will be converted to pure type String array or ObjectId array
101
+ * @return IdsType with IDs which are included, undefined if includes or ids are missing or null if none is included
102
+ */
103
+ export function getIncludedIds(includes: IdsType, ids: IdsType, convert?: 'string'): string[];
104
+ export function getIncludedIds(includes: IdsType, ids: IdsType, convert?: 'object'): Types.ObjectId[];
105
+ export function getIncludedIds<T = IdsType>(
106
+ includes: IdsType,
107
+ ids: T | IdsType,
108
+ convert?: 'string' | 'object'
109
+ ): T[] | null | undefined {
110
+ if (!includes || !ids) {
111
+ return undefined;
112
+ }
113
+
114
+ if (!Array.isArray(includes)) {
115
+ includes = [includes];
116
+ }
117
+
118
+ if (!Array.isArray(ids)) {
119
+ ids = [ids];
120
+ }
121
+
122
+ let result = [];
123
+ const includesStrings = getStringIds(includes);
124
+ for (const id of ids) {
125
+ if (includesStrings.includes(getStringIds(id))) {
126
+ result.push(id);
127
+ }
128
+ }
129
+
130
+ if (convert) {
131
+ result = convert === 'string' ? getStringIds(result) : getObjectIds(result);
132
+ }
133
+
134
+ return result.length ? result : null;
135
+ }
136
+
96
137
  /**
97
138
  * Get indexes of IDs in an array
98
139
  */
99
- export function getIndexesViaIds(ids: any | any[], array: any[]): number[] {
140
+ export function getIndexesViaIds(ids: IdsType, array: IdsType): number[] {
100
141
  // Check and prepare parameters
101
142
  if (!ids) {
102
143
  return [];
@@ -301,55 +342,6 @@ export function getJSONClone<T = any>(obj: T): Partial<T> {
301
342
  return JSON.parse(JSON.stringify(obj));
302
343
  }
303
344
 
304
- /**
305
- * Check if ID is included
306
- * @param includes String, ObjectId or array with both, which should be checked if it contains the ID
307
- * @param ids String, ObjectId or array with both, which should be included
308
- * @param convert If set the result array will be converted to pure type String array or ObjectId array
309
- * @return StringOrObjectId[] Array with IDs which are included or null if none is included
310
- */
311
- export function includesIds(
312
- includes: StringOrObjectId | StringOrObjectId[],
313
- ids: StringOrObjectId | StringOrObjectId[],
314
- convert?: 'string'
315
- ): string[];
316
- export function includesIds(
317
- includes: StringOrObjectId | StringOrObjectId[],
318
- ids: StringOrObjectId | StringOrObjectId[],
319
- convert?: 'object'
320
- ): Types.ObjectId[];
321
- export function includesIds<T = StringOrObjectId>(
322
- includes: StringOrObjectId | StringOrObjectId[] | any | any[],
323
- ids: T | StringOrObjectId[],
324
- convert?: 'string' | 'object'
325
- ): T[] | null {
326
- if (!includes || !ids) {
327
- return null;
328
- }
329
-
330
- if (!Array.isArray(includes)) {
331
- includes = [includes];
332
- }
333
-
334
- if (!Array.isArray(ids)) {
335
- ids = [ids] as any;
336
- }
337
-
338
- let result = [];
339
- const includesStrings = getStringIds(includes);
340
- for (const id of ids as StringOrObjectId[]) {
341
- if (includesStrings.includes(getStringIds(id))) {
342
- result.push(id);
343
- }
344
- }
345
-
346
- if (convert) {
347
- result = convert === 'string' ? getStringIds(result) : getObjectIds(result);
348
- }
349
-
350
- return result.length ? result : null;
351
- }
352
-
353
345
  /**
354
346
  * Convert all ObjectIds to strings
355
347
  */
@@ -3,9 +3,9 @@ import { plainToInstance } from 'class-transformer';
3
3
  import { validate } from 'class-validator';
4
4
  import * as _ from 'lodash';
5
5
  import { checkRestricted } from '../decorators/restricted.decorator';
6
+ import { ProcessType } from '../enums/process-type.enum';
6
7
  import { RoleEnum } from '../enums/role.enum';
7
- import { IdsType } from '../types/ids.type';
8
- import { equalIds, getStringIds } from './db.helper';
8
+ import { equalIds } from './db.helper';
9
9
 
10
10
  /**
11
11
  * Helper class for inputs
@@ -18,7 +18,13 @@ export default class InputHelper {
18
18
  public static async check(
19
19
  value: any,
20
20
  user: { id: any; hasRole: (roles: string[]) => boolean },
21
- options?: { creator?: IdsType; metatype?: any; ownerIds?: IdsType; roles?: string | string[] }
21
+ options?: {
22
+ dbObject?: any;
23
+ metatype?: any;
24
+ processType?: ProcessType;
25
+ roles?: string | string[];
26
+ throwError?: boolean;
27
+ }
22
28
  ): Promise<any> {
23
29
  return check(value, user, options);
24
30
  }
@@ -182,7 +188,13 @@ export default class InputHelper {
182
188
  export async function check(
183
189
  value: any,
184
190
  user: { id: any; hasRole: (roles: string[]) => boolean },
185
- options?: { creator?: IdsType; metatype?: any; ownerIds?: IdsType; roles?: string | string[]; throwError?: boolean }
191
+ options?: {
192
+ dbObject?: any;
193
+ metatype?: any;
194
+ processType?: ProcessType;
195
+ roles?: string | string[];
196
+ throwError?: boolean;
197
+ }
186
198
  ): Promise<any> {
187
199
  const config = {
188
200
  throwError: true,
@@ -196,18 +208,15 @@ export async function check(
196
208
  roles = [roles];
197
209
  }
198
210
  let valid = false;
199
- if (roles.includes(RoleEnum.S_USER) && user?.id) {
200
- valid = true;
201
- } else if (user.hasRole(roles)) {
211
+ if (
212
+ // check if user is logged in
213
+ (roles.includes(RoleEnum.S_USER) && user?.id) ||
214
+ // check if the user has at least one of the required roles
215
+ user.hasRole(roles) ||
216
+ // check if the user is the creator
217
+ (roles.includes(RoleEnum.S_CREATOR) && equalIds(config.dbObject?.createdBy, user))
218
+ ) {
202
219
  valid = true;
203
- } else if (roles.includes(RoleEnum.S_CREATOR) && user?.id && equalIds(user.id, config.creator)) {
204
- valid = true;
205
- } else if (roles.includes(RoleEnum.S_OWNER) && user?.id && config.ownerIds) {
206
- let ownerIds: string | string[] = getStringIds(config.ownerIds);
207
- if (!Array.isArray(ownerIds)) {
208
- ownerIds = [ownerIds];
209
- }
210
- valid = ownerIds.includes(getStringIds(user.id));
211
220
  }
212
221
  if (!valid) {
213
222
  throw new UnauthorizedException('Missing rights');
@@ -1,6 +1,5 @@
1
- import { Model, Types } from 'mongoose';
1
+ import { Model } from 'mongoose';
2
2
  import { FieldSelection } from '../types/field-selection.type';
3
- import { IdsType } from '../types/ids.type';
4
3
 
5
4
  /**
6
5
  * General service options
@@ -14,7 +13,7 @@ export interface ServiceOptions {
14
13
  // If truly (default): input data will be checked
15
14
  checkRights?: boolean;
16
15
 
17
- // Current user to set ownership, check roles and other things
16
+ // Current user to set ownership, check rights and other things
18
17
  currentUser?: {
19
18
  [key: string]: any;
20
19
  id: string;
@@ -27,9 +26,6 @@ export interface ServiceOptions {
27
26
  // Overwrites type of input (array items)
28
27
  inputType?: new (...params: any[]) => any;
29
28
 
30
- // Owner IDs
31
- ownerIds?: IdsType;
32
-
33
29
  // Process field selection
34
30
  // If {} or not set, then the field selection runs with defaults
35
31
  // If falsy, then the field selection will not be automatically executed
@@ -59,16 +59,6 @@ export abstract class CorePersistenceModel extends CoreModel {
59
59
  @Prop([String])
60
60
  labels: string[] = undefined;
61
61
 
62
- /**
63
- * IDs of the Owners
64
- */
65
- @Field((type) => [String], {
66
- description: 'Users who own the object',
67
- nullable: true,
68
- })
69
- @Prop([String])
70
- ownerIds: string[] = undefined;
71
-
72
62
  /**
73
63
  * Tags for the object
74
64
  */
@@ -97,7 +87,6 @@ export abstract class CorePersistenceModel extends CoreModel {
97
87
  super.init();
98
88
  this.createdAt = this.createdAt === undefined ? new Date() : this.createdAt;
99
89
  this.labels = this.labels === undefined ? [] : this.labels;
100
- this.ownerIds = this.ownerIds === undefined ? [] : this.ownerIds;
101
90
  this.tags = this.tags === undefined ? [] : this.tags;
102
91
  this.updatedAt = this.tags === undefined ? this.createdAt : this.updatedAt;
103
92
  return this;
@@ -1,11 +1,11 @@
1
1
  import { Document, Model, Types } from 'mongoose';
2
+ import { ProcessType } from '../enums/process-type.enum';
2
3
  import { getStringIds, popAndMap } from '../helpers/db.helper';
3
4
  import { check } from '../helpers/input.helper';
4
5
  import { prepareInput, prepareOutput } from '../helpers/service.helper';
5
6
  import { ServiceOptions } from '../interfaces/service-options.interface';
6
7
  import { CoreModel } from '../models/core-model.model';
7
8
  import { FieldSelection } from '../types/field-selection.type';
8
- import { IdsType } from '../types/ids.type';
9
9
 
10
10
  /**
11
11
  * Module service class to be extended by concrete module services
@@ -39,9 +39,9 @@ export abstract class ModuleService<T extends CoreModel = any> {
39
39
  input: any,
40
40
  currentUser: { id: any; hasRole: (roles: string[]) => boolean },
41
41
  options?: {
42
- creator?: IdsType;
42
+ dbObject?: any;
43
43
  metatype?: any;
44
- ownerIds?: IdsType;
44
+ processType?: ProcessType;
45
45
  roles?: string | string[];
46
46
  throwError?: boolean;
47
47
  }
@@ -88,30 +88,18 @@ export abstract class ModuleService<T extends CoreModel = any> {
88
88
  }
89
89
 
90
90
  // Get DB object
91
- const getDbObject = async () => {
92
- if (config.dbObject) {
93
- if (typeof config.dbObject === 'string' || config.dbObject instanceof Types.ObjectId) {
94
- const dbObject = await this.get(getStringIds(config.dbObject));
95
- if (dbObject) {
96
- config.dbObject = dbObject;
97
- }
91
+ if (config.dbObject && config.checkRights && this.checkRights) {
92
+ if (typeof config.dbObject === 'string' || config.dbObject instanceof Types.ObjectId) {
93
+ const dbObject = await this.get(getStringIds(config.dbObject));
94
+ if (dbObject) {
95
+ config.dbObject = dbObject;
98
96
  }
99
97
  }
100
- return config.dbObject;
101
- };
102
-
103
- // Get owner IDs
104
- let ownerIds = undefined;
105
- if (config.checkRights && this.checkRights) {
106
- ownerIds = getStringIds(config.ownerIds);
107
- if (!ownerIds?.length) {
108
- ownerIds = (await getDbObject())?.ownerIds;
109
- }
110
98
  }
111
99
 
112
100
  // Check rights for input
113
101
  if (config.input && config.checkRights && this.checkRights) {
114
- const opts: any = { creator: (await getDbObject())?.createdBy, ownerIds, roles: config.roles };
102
+ const opts: any = { dbObject: config.dbObject, processType: ProcessType.INPUT, roles: config.roles };
115
103
  if (config.inputType) {
116
104
  opts.metatype = config.resultType;
117
105
  }
@@ -137,7 +125,12 @@ export abstract class ModuleService<T extends CoreModel = any> {
137
125
 
138
126
  // Check output rights
139
127
  if (config.checkRights && this.checkRights) {
140
- const opts: any = { creator: (await getDbObject())?.createdBy, ownerIds, roles: config.roles, throwError: false };
128
+ const opts: any = {
129
+ dbObject: config.dbObject,
130
+ processType: ProcessType.OUTPUT,
131
+ roles: config.roles,
132
+ throwError: false,
133
+ };
141
134
  if (config.resultType) {
142
135
  opts.metatype = config.resultType;
143
136
  }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Require only one of the optional properties
3
+ * See https://stackoverflow.com/a/49725198
4
+ */
5
+ export type RequireOnlyOne<T, Keys extends keyof T = keyof T> = Pick<T, Exclude<keyof T, Keys>> &
6
+ { [K in Keys]-?: Required<Pick<T, K>> & Partial<Record<Exclude<Keys, K>, undefined>> }[Keys];
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Require at least on of optional properties
3
+ * See https://stackoverflow.com/a/49725198
4
+ */
5
+ export type RequireAtLeastOne<T, Keys extends keyof T = keyof T> = Pick<T, Exclude<keyof T, Keys>> &
6
+ { [K in Keys]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<Keys, K>>> }[Keys];
@@ -1,8 +1,7 @@
1
1
  import { DynamicModule, Global, Module, UnauthorizedException } from '@nestjs/common';
2
- import { APP_INTERCEPTOR, APP_PIPE } from '@nestjs/core';
2
+ import { APP_PIPE } from '@nestjs/core';
3
3
  import { GraphQLModule } from '@nestjs/graphql';
4
4
  import { merge } from './core/common/helpers/config.helper';
5
- import { CheckResponseInterceptor } from './core/common/interceptors/check-response.interceptor';
6
5
  import { IServerOptions } from './core/common/interfaces/server-options.interface';
7
6
  import { MapAndValidatePipe } from './core/common/pipes/map-and-validate.pipe';
8
7
  import { ConfigService } from './core/common/services/config.service';
@@ -19,8 +18,6 @@ import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
19
18
  * - MongooseModule
20
19
  * - GraphQL
21
20
  * - ConfigService
22
- * - CheckInput
23
- * - CheckResponse
24
21
  *
25
22
  * and sets the following services as globals:
26
23
  * - ConfigService
@@ -99,21 +96,6 @@ export class CoreModule {
99
96
  useValue: new ConfigService(config),
100
97
  },
101
98
 
102
- // [Global] The CheckResponseInterceptor restricts the response to the properties
103
- // that are permitted for the current user
104
- {
105
- provide: APP_INTERCEPTOR,
106
- useClass: CheckResponseInterceptor,
107
- },
108
-
109
- // [Global] The CheckInputPipe checks the permissibility of individual properties of inputs for the resolvers
110
- // in relation to the current user, replaces MapAndValidatePipe
111
- // Does not work yet, because context is missing: https://github.com/nestjs/graphql/issues/325
112
- // {
113
- // provide: APP_PIPE,
114
- // useClass: CheckInputPipe,
115
- // },
116
-
117
99
  // [Global] Map plain objects to metatype and validate
118
100
  {
119
101
  provide: APP_PIPE,
package/src/index.ts CHANGED
@@ -15,6 +15,7 @@ export * from './core/common/decorators/restricted.decorator';
15
15
  export * from './core/common/decorators/roles.decorator';
16
16
  export * from './core/common/enums/comparison-operator.enum';
17
17
  export * from './core/common/enums/logical-operator.enum';
18
+ export * from './core/common/enums/process-type.enum';
18
19
  export * from './core/common/enums/role.enum';
19
20
  export * from './core/common/enums/sort-order.emum';
20
21
  export * from './core/common/helpers/config.helper';
@@ -54,6 +55,8 @@ export * from './core/common/types/core-model-constructor.type';
54
55
  export * from './core/common/types/field-selection.type';
55
56
  export * from './core/common/types/ids.type';
56
57
  export * from './core/common/types/plain-input.type';
58
+ export * from './core/common/types/require-only-one.type';
59
+ export * from './core/common/types/required-at-least-one.type';
57
60
  export * from './core/common/types/string-or-object-id.type';
58
61
 
59
62
  // =====================================================================================================================
@@ -1,8 +1,8 @@
1
1
  import { Field, ObjectType } from '@nestjs/graphql';
2
+ import { Prop, Schema as MongooseSchema, SchemaFactory } from '@nestjs/mongoose';
3
+ import { Document, Schema } from 'mongoose';
2
4
  import { CoreUserModel } from '../../../core/modules/user/core-user.model';
3
5
  import { PersistenceModel } from '../../common/models/persistence.model';
4
- import { Prop, Schema as MongooseSchema, SchemaFactory } from '@nestjs/mongoose';
5
- import { Schema, Document } from 'mongoose';
6
6
 
7
7
  export type UserDocument = User & Document;
8
8