@liminalfunctions/framework 1.0.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 (181) hide show
  1. package/.mocharc.json +5 -0
  2. package/dist/F_Client_Collection_Registry.d.ts +18 -0
  3. package/dist/F_Client_Collection_Registry.js +36 -0
  4. package/dist/F_Client_Collection_Registry.js.map +1 -0
  5. package/dist/F_Collection.d.ts +21 -0
  6. package/dist/F_Collection.js +36 -0
  7. package/dist/F_Collection.js.map +1 -0
  8. package/dist/F_Collection_Registry.d.ts +11 -0
  9. package/dist/F_Collection_Registry.js +18 -0
  10. package/dist/F_Collection_Registry.js.map +1 -0
  11. package/dist/F_Compile.d.ts +4 -0
  12. package/dist/F_Compile.js +298 -0
  13. package/dist/F_Compile.js.map +1 -0
  14. package/dist/F_Security_Models/F_SM_Open_Access.d.ts +11 -0
  15. package/dist/F_Security_Models/F_SM_Open_Access.js +14 -0
  16. package/dist/F_Security_Models/F_SM_Open_Access.js.map +1 -0
  17. package/dist/F_Security_Models/F_SM_Ownership.d.ts +12 -0
  18. package/dist/F_Security_Models/F_SM_Ownership.js +46 -0
  19. package/dist/F_Security_Models/F_SM_Ownership.js.map +1 -0
  20. package/dist/F_Security_Models/F_SM_Role_Membership.d.ts +19 -0
  21. package/dist/F_Security_Models/F_SM_Role_Membership.js +73 -0
  22. package/dist/F_Security_Models/F_SM_Role_Membership.js.map +1 -0
  23. package/dist/F_Security_Models/F_Security_Model.d.ts +41 -0
  24. package/dist/F_Security_Models/F_Security_Model.js +29 -0
  25. package/dist/F_Security_Models/F_Security_Model.js.map +1 -0
  26. package/dist/code_generation/generate_client_library.d.ts +4 -0
  27. package/dist/code_generation/generate_client_library.js +158 -0
  28. package/dist/code_generation/generate_client_library.js.map +1 -0
  29. package/dist/code_generation/templates/.gitignore.mustache +383 -0
  30. package/dist/code_generation/templates/collection.mustache +106 -0
  31. package/dist/code_generation/templates/main.mustache +24 -0
  32. package/dist/code_generation/templates/package.json.mustache +18 -0
  33. package/dist/code_generation/templates/tsconfig.json.mustache +14 -0
  34. package/dist/code_generation/templates/types.mustache +4 -0
  35. package/dist/code_generation/templates/utils.ts.mustache +17 -0
  36. package/dist/code_generation/utils/tab_indent.d.ts +1 -0
  37. package/dist/code_generation/utils/tab_indent.js +4 -0
  38. package/dist/code_generation/utils/tab_indent.js.map +1 -0
  39. package/dist/code_generation/utils/type_from_zod.d.ts +2 -0
  40. package/dist/code_generation/utils/type_from_zod.js +102 -0
  41. package/dist/code_generation/utils/type_from_zod.js.map +1 -0
  42. package/dist/utils/cache.d.ts +13 -0
  43. package/dist/utils/cache.js +101 -0
  44. package/dist/utils/cache.js.map +1 -0
  45. package/dist/utils/mongoose_from_zod.d.ts +13 -0
  46. package/dist/utils/mongoose_from_zod.js +164 -0
  47. package/dist/utils/mongoose_from_zod.js.map +1 -0
  48. package/dist/utils/pretty_print_zod.d.ts +2 -0
  49. package/dist/utils/pretty_print_zod.js +63 -0
  50. package/dist/utils/pretty_print_zod.js.map +1 -0
  51. package/dist/utils/query_object_to_mongodb_query.d.ts +3 -0
  52. package/dist/utils/query_object_to_mongodb_query.js +61 -0
  53. package/dist/utils/query_object_to_mongodb_query.js.map +1 -0
  54. package/dist/utils/query_validator_from_zod.d.ts +6 -0
  55. package/dist/utils/query_validator_from_zod.js +216 -0
  56. package/dist/utils/query_validator_from_zod.js.map +1 -0
  57. package/package.json +36 -0
  58. package/src/F_Collection.ts +50 -0
  59. package/src/F_Collection_Registry.ts +29 -0
  60. package/src/F_Compile.ts +368 -0
  61. package/src/F_Security_Models/F_SM_Open_Access.ts +21 -0
  62. package/src/F_Security_Models/F_SM_Ownership.ts +72 -0
  63. package/src/F_Security_Models/F_SM_Role_Membership.ts +87 -0
  64. package/src/F_Security_Models/F_Security_Model.ts +85 -0
  65. package/src/code_generation/generate_client_library.ts +197 -0
  66. package/src/code_generation/templates/.gitignore.mustache +383 -0
  67. package/src/code_generation/templates/collection.mustache +106 -0
  68. package/src/code_generation/templates/main.mustache +24 -0
  69. package/src/code_generation/templates/package.json.mustache +18 -0
  70. package/src/code_generation/templates/tsconfig.json.mustache +14 -0
  71. package/src/code_generation/templates/types.mustache +4 -0
  72. package/src/code_generation/templates/utils.ts.mustache +17 -0
  73. package/src/code_generation/utils/tab_indent.ts +3 -0
  74. package/src/code_generation/utils/type_from_zod.ts +140 -0
  75. package/src/utils/cache.ts +149 -0
  76. package/src/utils/mongoose_from_zod.ts +191 -0
  77. package/src/utils/pretty_print_zod.ts +75 -0
  78. package/src/utils/query_object_to_mongodb_query.ts +73 -0
  79. package/src/utils/query_validator_from_zod.ts +246 -0
  80. package/test/0_0_mongoose_from_zod.test.ts +260 -0
  81. package/test/0_1_query_validator_from_zod.test.ts +518 -0
  82. package/test/0_2_query_validator_to_mongodb_query.test.ts +365 -0
  83. package/test/0_3_cache.test.ts +204 -0
  84. package/test/1_0_basic_server.test.ts +530 -0
  85. package/test/1_1_security_ownership.test.ts +328 -0
  86. package/test/1_2_role_membership.test.ts +731 -0
  87. package/test/2_0_client_library_basic_type_generation.test.ts +444 -0
  88. package/test/2_0_client_library_query_type_generation.test.ts +352 -0
  89. package/test/2_1_client_library_generation.test.ts +255 -0
  90. package/test/tmp/dist/Brief_News_Category.d.ts +16 -0
  91. package/test/tmp/dist/Brief_News_Category.js +85 -0
  92. package/test/tmp/dist/Brief_News_Category.js.map +1 -0
  93. package/test/tmp/dist/Client.d.ts +19 -0
  94. package/test/tmp/dist/Client.js +97 -0
  95. package/test/tmp/dist/Client.js.map +1 -0
  96. package/test/tmp/dist/Institution.d.ts +18 -0
  97. package/test/tmp/dist/Institution.js +94 -0
  98. package/test/tmp/dist/Institution.js.map +1 -0
  99. package/test/tmp/dist/Project.d.ts +16 -0
  100. package/test/tmp/dist/Project.js +85 -0
  101. package/test/tmp/dist/Project.js.map +1 -0
  102. package/test/tmp/dist/index.d.ts +4 -0
  103. package/test/tmp/dist/index.js +14 -0
  104. package/test/tmp/dist/index.js.map +1 -0
  105. package/test/tmp/dist/types/brief_news_category.d.ts +7 -0
  106. package/test/tmp/dist/types/brief_news_category.js +2 -0
  107. package/test/tmp/dist/types/brief_news_category.js.map +1 -0
  108. package/test/tmp/dist/types/brief_news_category_post.d.ts +7 -0
  109. package/test/tmp/dist/types/brief_news_category_post.js +2 -0
  110. package/test/tmp/dist/types/brief_news_category_post.js.map +1 -0
  111. package/test/tmp/dist/types/brief_news_category_put.d.ts +7 -0
  112. package/test/tmp/dist/types/brief_news_category_put.js +2 -0
  113. package/test/tmp/dist/types/brief_news_category_put.js.map +1 -0
  114. package/test/tmp/dist/types/brief_news_category_query.d.ts +26 -0
  115. package/test/tmp/dist/types/brief_news_category_query.js +2 -0
  116. package/test/tmp/dist/types/brief_news_category_query.js.map +1 -0
  117. package/test/tmp/dist/types/client.d.ts +5 -0
  118. package/test/tmp/dist/types/client.js +2 -0
  119. package/test/tmp/dist/types/client.js.map +1 -0
  120. package/test/tmp/dist/types/client_post.d.ts +5 -0
  121. package/test/tmp/dist/types/client_post.js +2 -0
  122. package/test/tmp/dist/types/client_post.js.map +1 -0
  123. package/test/tmp/dist/types/client_put.d.ts +5 -0
  124. package/test/tmp/dist/types/client_put.js +2 -0
  125. package/test/tmp/dist/types/client_put.js.map +1 -0
  126. package/test/tmp/dist/types/client_query.d.ts +18 -0
  127. package/test/tmp/dist/types/client_query.js +2 -0
  128. package/test/tmp/dist/types/client_query.js.map +1 -0
  129. package/test/tmp/dist/types/institution.d.ts +4 -0
  130. package/test/tmp/dist/types/institution.js +2 -0
  131. package/test/tmp/dist/types/institution.js.map +1 -0
  132. package/test/tmp/dist/types/institution_post.d.ts +4 -0
  133. package/test/tmp/dist/types/institution_post.js +2 -0
  134. package/test/tmp/dist/types/institution_post.js.map +1 -0
  135. package/test/tmp/dist/types/institution_put.d.ts +4 -0
  136. package/test/tmp/dist/types/institution_put.js +2 -0
  137. package/test/tmp/dist/types/institution_put.js.map +1 -0
  138. package/test/tmp/dist/types/institution_query.d.ts +14 -0
  139. package/test/tmp/dist/types/institution_query.js +2 -0
  140. package/test/tmp/dist/types/institution_query.js.map +1 -0
  141. package/test/tmp/dist/types/project.d.ts +7 -0
  142. package/test/tmp/dist/types/project.js +2 -0
  143. package/test/tmp/dist/types/project.js.map +1 -0
  144. package/test/tmp/dist/types/project_post.d.ts +7 -0
  145. package/test/tmp/dist/types/project_post.js +2 -0
  146. package/test/tmp/dist/types/project_post.js.map +1 -0
  147. package/test/tmp/dist/types/project_put.d.ts +7 -0
  148. package/test/tmp/dist/types/project_put.js +2 -0
  149. package/test/tmp/dist/types/project_put.js.map +1 -0
  150. package/test/tmp/dist/types/project_query.d.ts +27 -0
  151. package/test/tmp/dist/types/project_query.js +2 -0
  152. package/test/tmp/dist/types/project_query.js.map +1 -0
  153. package/test/tmp/dist/utils/utils.d.ts +11 -0
  154. package/test/tmp/dist/utils/utils.js +13 -0
  155. package/test/tmp/dist/utils/utils.js.map +1 -0
  156. package/test/tmp/package-lock.json +573 -0
  157. package/test/tmp/package.json +18 -0
  158. package/test/tmp/src/Brief_News_Category.ts +94 -0
  159. package/test/tmp/src/Client.ts +106 -0
  160. package/test/tmp/src/Institution.ts +103 -0
  161. package/test/tmp/src/Project.ts +94 -0
  162. package/test/tmp/src/index.ts +20 -0
  163. package/test/tmp/src/types/brief_news_category.ts +7 -0
  164. package/test/tmp/src/types/brief_news_category_post.ts +7 -0
  165. package/test/tmp/src/types/brief_news_category_put.ts +7 -0
  166. package/test/tmp/src/types/brief_news_category_query.ts +26 -0
  167. package/test/tmp/src/types/client.ts +5 -0
  168. package/test/tmp/src/types/client_post.ts +5 -0
  169. package/test/tmp/src/types/client_put.ts +5 -0
  170. package/test/tmp/src/types/client_query.ts +18 -0
  171. package/test/tmp/src/types/institution.ts +4 -0
  172. package/test/tmp/src/types/institution_post.ts +4 -0
  173. package/test/tmp/src/types/institution_put.ts +4 -0
  174. package/test/tmp/src/types/institution_query.ts +14 -0
  175. package/test/tmp/src/types/project.ts +7 -0
  176. package/test/tmp/src/types/project_post.ts +7 -0
  177. package/test/tmp/src/types/project_put.ts +7 -0
  178. package/test/tmp/src/types/project_query.ts +27 -0
  179. package/test/tmp/src/utils/utils.ts +17 -0
  180. package/test/tmp/tsconfig.json +14 -0
  181. package/tsconfig.json +14 -0
@@ -0,0 +1,731 @@
1
+
2
+ import assert from "assert";
3
+
4
+ import { z_mongodb_id } from '../dist/utils/mongoose_from_zod.js';
5
+ import { F_Collection } from '../dist/f_collection.js';
6
+ import { F_Collection_Registry } from '../dist/F_Collection_Registry.js'
7
+ import { F_SM_Open_Access } from '../dist/F_Security_Models/F_SM_Open_Access.js'
8
+ import { F_SM_Role_Membership } from '../dist/F_Security_Models/F_SM_Role_Membership.js'
9
+ import { F_Security_Model } from '../dist/F_Security_Models/F_Security_Model.js'
10
+ import { Cache } from '../dist/utils/cache.js'
11
+ import { z, ZodBoolean, ZodDate, ZodNumber, ZodString } from 'zod'
12
+
13
+ import got from 'got'
14
+ import express, { Express, Request, Response, NextFunction } from 'express'
15
+ import mongoose, { Mongoose } from "mongoose";
16
+ import { Server } from "http";
17
+
18
+
19
+
20
+ describe('Security Model Role Membership', function () {
21
+ const port = 4601;
22
+ let express_app: Express;
23
+ let server: Server;
24
+ let db_connection: Mongoose;
25
+
26
+ const validate_institution = z.object({
27
+ _id: z_mongodb_id,
28
+ name: z.string(),
29
+ });
30
+ const validate_client = z.object({
31
+ _id: z_mongodb_id,
32
+ institution_id: z_mongodb_id,
33
+ name: z.string(),
34
+ });
35
+ const validate_project = z.object({
36
+ _id: z_mongodb_id,
37
+ institution_id: z_mongodb_id,
38
+ client_id: z_mongodb_id,
39
+ name: z.string(),
40
+ });
41
+ let validate_user = z.object({
42
+ _id: z_mongodb_id,
43
+ auth_id: z.string(),
44
+ });
45
+ let validate_role = z.object({
46
+ _id: z_mongodb_id,
47
+ name: z.string(),
48
+ institution_id: z_mongodb_id,
49
+ permissions: z.object({
50
+ institution: z.array(z.enum(['read', 'create', 'update', 'delete'])),
51
+ client: z.array(z.enum(['read', 'create', 'update', 'delete'])),
52
+ project: z.array(z.enum(['read', 'create', 'update', 'delete'])),
53
+ role: z.array(z.enum(['read', 'create', 'update', 'delete'])),
54
+ })
55
+ })
56
+ let validate_institution_role_membership = z.object({
57
+ _id: z_mongodb_id,
58
+ institution_id: z_mongodb_id,
59
+ user_id: z_mongodb_id,
60
+ role_id: z_mongodb_id,
61
+ })
62
+ let validate_client_role_membership = z.object({
63
+ _id: z_mongodb_id,
64
+ institution_id: z_mongodb_id,
65
+ client_id: z_mongodb_id,
66
+ user_id: z_mongodb_id,
67
+ role_id: z_mongodb_id,
68
+ })
69
+
70
+
71
+ let collection_institution: F_Collection<'institution', typeof validate_institution>;
72
+ let collection_client: F_Collection<'client', typeof validate_client>;
73
+ let collection_project: F_Collection<'project', typeof validate_project>;
74
+ let collection_user: F_Collection<'user', typeof validate_user>;
75
+ let collection_role: F_Collection<'role', typeof validate_role>;
76
+ let collection_institution_role_membership: F_Collection<'institution_role_membership', typeof validate_institution_role_membership>;
77
+ let collection_client_role_membership: F_Collection<'client_role_membership', typeof validate_client_role_membership>;
78
+
79
+
80
+
81
+ // build registry
82
+ let registry: F_Collection_Registry;
83
+
84
+ // before any tests run, set up the server and the db connection
85
+ before(async function() {
86
+ express_app = express();
87
+ express_app.use(express.json());
88
+ db_connection = await mongoose.connect('mongodb://127.0.0.1:27017/');
89
+
90
+ let cache_role = new Cache(60);
91
+ let cache_institution_role_membership = new Cache(60);
92
+ let cache_client_role_membership = new Cache(60);
93
+
94
+ collection_institution = new F_Collection('institution', validate_institution);
95
+ collection_client = new F_Collection('client', validate_client);
96
+ collection_project = new F_Collection('project', validate_project);
97
+ collection_user = new F_Collection('user', validate_user);
98
+ collection_role = new F_Collection('role', validate_role);
99
+ collection_institution_role_membership = new F_Collection('institution_role_membership', validate_institution_role_membership);
100
+ collection_client_role_membership = new F_Collection('client_role_membership', validate_client_role_membership);
101
+
102
+ collection_institution.add_layers([], [new F_SM_Role_Membership(
103
+ collection_institution,
104
+ collection_institution,
105
+ collection_institution_role_membership,
106
+ collection_role,
107
+ cache_institution_role_membership,
108
+ cache_role,
109
+ 'user_id',
110
+ 'role_id'
111
+ )]);
112
+
113
+ collection_client.add_layers(['institution'], [new F_SM_Role_Membership(
114
+ collection_client,
115
+ collection_institution,
116
+ collection_institution_role_membership,
117
+ collection_role,
118
+ cache_institution_role_membership,
119
+ cache_role,
120
+ 'user_id',
121
+ 'role_id'
122
+ )]);
123
+
124
+ collection_project.add_layers(['institution', 'client'], [
125
+ new F_SM_Role_Membership(
126
+ collection_project,
127
+ collection_institution,
128
+ collection_institution_role_membership,
129
+ collection_role,
130
+ cache_institution_role_membership,
131
+ cache_role,
132
+ 'user_id',
133
+ 'role_id'
134
+ ),
135
+ new F_SM_Role_Membership(
136
+ collection_project,
137
+ collection_client,
138
+ collection_client_role_membership,
139
+ collection_role,
140
+ cache_client_role_membership,
141
+ cache_role,
142
+ 'user_id',
143
+ 'role_id'
144
+ )
145
+ ]);
146
+
147
+ let proto_registry = new F_Collection_Registry();
148
+ registry = proto_registry.register(collection_user)
149
+ .register(collection_institution)
150
+ .register(collection_client)
151
+ .register(collection_project)
152
+ .register(collection_user)
153
+ .register(collection_role)
154
+ .register(collection_institution_role_membership)
155
+ .register(collection_client_role_membership);
156
+
157
+ F_Security_Model.set_auth_fetcher(async (req: Request) => {
158
+ if(!req.headers.authorization){ return undefined; }
159
+
160
+ let user_record = await collection_user.mongoose_model.findOne({auth_id: req.headers.authorization})
161
+ if(!user_record){ return undefined; }
162
+
163
+ return { user_id: user_record._id, layers: [] };
164
+ })
165
+ registry.compile(express_app, '/api');
166
+
167
+ server = express_app.listen(port);
168
+
169
+ // wait for a moment because otherwise stuff breaks for no reason
170
+ await new Promise(resolve => setTimeout(resolve, 200))
171
+ })
172
+
173
+ after(async function (){
174
+ await server.close();
175
+ mongoose.connection.modelNames().forEach(ele => mongoose.connection.deleteModel(ele));
176
+ db_connection.modelNames().forEach(ele => db_connection.deleteModel(ele));
177
+ await db_connection.disconnect()
178
+ });
179
+
180
+ beforeEach(async function(){
181
+ for(let collection of Object.values(registry.collections)){
182
+ //@ts-ignore
183
+ await collection.mongoose_model.collection.drop();
184
+ }
185
+ })
186
+
187
+ /**
188
+ * generates a default DB with the following structure:
189
+ * - steve institution
190
+ * - - steve client
191
+ * - - - steve project
192
+ * - - joe client
193
+ * - - - steve project
194
+ * - edwin institution
195
+ * - - nathan client
196
+ * - - - nathan project
197
+ * - - edna client
198
+ * - - - edna project
199
+ *
200
+ * The user steve has project read, write, update, and create access for the institution "steve institution".
201
+ * Steve also has a client role that grants him RWUC access to the nathan client, and a client role that grants
202
+ * him no project permissions to the edna client.
203
+ *
204
+ * The user edwin has project RWUC access for the "edwin institution". He does not have any client permissions in the steve institution
205
+ * @returns
206
+ */
207
+ async function generate_test_setup(){
208
+ let user_steve = await collection_user.mongoose_model.create({
209
+ auth_id: 'steve'
210
+ });
211
+
212
+ let user_edwin = await collection_user.mongoose_model.create({
213
+ auth_id: 'edwin'
214
+ });
215
+
216
+ let steve_institution = await collection_institution.mongoose_model.create({
217
+ name: `steve institution`
218
+ });
219
+
220
+ let edwin_institution = await collection_institution.mongoose_model.create({
221
+ name: `edwin institution`
222
+ });
223
+
224
+ let steve_client = await collection_client.mongoose_model.create({
225
+ institution_id: steve_institution._id,
226
+ name: 'steve client'
227
+ })
228
+
229
+ let joe_client = await collection_client.mongoose_model.create({
230
+ institution_id: steve_institution._id,
231
+ name: 'joe client'
232
+ })
233
+
234
+ let nathan_client = await collection_client.mongoose_model.create({
235
+ institution_id: edwin_institution._id,
236
+ name: 'nathan client'
237
+ })
238
+
239
+ let edna_client = await collection_client.mongoose_model.create({
240
+ institution_id: edwin_institution._id,
241
+ name: 'edna client'
242
+ })
243
+
244
+ let steve_project = await collection_project.mongoose_model.create({
245
+ institution_id: steve_institution._id,
246
+ client_id: steve_client,
247
+ name: 'steve project'
248
+ })
249
+
250
+ let joe_project = await collection_project.mongoose_model.create({
251
+ institution_id: steve_institution._id,
252
+ client_id: joe_client,
253
+ name: 'joe project'
254
+ })
255
+
256
+ let nathan_project = await collection_project.mongoose_model.create({
257
+ institution_id: edwin_institution._id,
258
+ client_id: nathan_client,
259
+ name: 'nathan project'
260
+ })
261
+
262
+ let edna_project = await collection_project.mongoose_model.create({
263
+ institution_id: edwin_institution._id,
264
+ client_id: edna_client,
265
+ name: 'edna project'
266
+ })
267
+
268
+ let access_role_steve_institution_grants_project = await collection_role.mongoose_model.create({
269
+ name: 'steve full access',
270
+ institution_id: steve_institution._id,
271
+ permissions: {
272
+ institution: ['read', 'create', 'update', 'delete'],
273
+ client: ['read', 'create', 'update', 'delete'],
274
+ project: ['read', 'create', 'update', 'delete'],
275
+ role: ['read', 'create', 'update', 'delete'],
276
+ }
277
+ });
278
+
279
+ let access_role_steve_institution_grants_minimal = await collection_role.mongoose_model.create({
280
+ name: 'steve limited access',
281
+ institution_id: steve_institution._id,
282
+ permissions: {
283
+ institution: ['read'],
284
+ client: ['read'],
285
+ project: [],
286
+ role: ['read'],
287
+ }
288
+ });
289
+
290
+ let access_role_edwin_institution_grants_project = await collection_role.mongoose_model.create({
291
+ name: 'edwin full access',
292
+ institution_id: edwin_institution._id,
293
+ permissions: {
294
+ institution: ['read', 'create', 'update', 'delete'],
295
+ client: ['read', 'create', 'update', 'delete'],
296
+ project: ['read', 'create', 'update', 'delete'],
297
+ role: ['read', 'create', 'update', 'delete'],
298
+ }
299
+ });
300
+
301
+ let access_role_edwin_institution_grants_minimal = await collection_role.mongoose_model.create({
302
+ name: 'edwin limited access',
303
+ institution_id: edwin_institution._id,
304
+ permissions: {
305
+ institution: ['read'],
306
+ client: ['read'],
307
+ project: [],
308
+ role: ['read'],
309
+ }
310
+ });
311
+
312
+ let steve_steve_institution_role_membership = await collection_institution_role_membership.mongoose_model.create({
313
+ role_id: access_role_steve_institution_grants_project._id,
314
+ user_id: user_steve._id,
315
+ institution_id: steve_institution._id,
316
+ })
317
+
318
+ let steve_edwin_institution_role_membership = await collection_institution_role_membership.mongoose_model.create({
319
+ role_id: access_role_edwin_institution_grants_minimal._id,
320
+ user_id: user_steve._id,
321
+ institution_id: edwin_institution._id,
322
+ })
323
+
324
+ let steve_nathan_client_role_membership = await collection_client_role_membership.mongoose_model.create({
325
+ role_id: access_role_edwin_institution_grants_project._id,
326
+ user_id: user_steve._id,
327
+ institution_id: edwin_institution._id,
328
+ client_id: nathan_client._id
329
+ })
330
+
331
+ let steve_edna_client_role_membership = await collection_client_role_membership.mongoose_model.create({
332
+ role_id: access_role_edwin_institution_grants_minimal._id,
333
+ user_id: user_steve._id,
334
+ institution_id: edwin_institution._id,
335
+ client_id: edna_client._id
336
+ })
337
+
338
+ let edwin_edwin_institution_role_membership = await collection_institution_role_membership.mongoose_model.create({
339
+ role_id: access_role_edwin_institution_grants_project._id,
340
+ user_id: user_edwin._id,
341
+ institution_id: edwin_institution._id,
342
+ })
343
+
344
+ return {
345
+ user_steve,
346
+ user_edwin,
347
+ steve_institution,
348
+ edwin_institution,
349
+ steve_client,
350
+ joe_client,
351
+ nathan_client,
352
+ edna_client,
353
+ steve_project,
354
+ joe_project,
355
+ nathan_project,
356
+ edna_project,
357
+ access_role_steve_institution_grants_project,
358
+ access_role_steve_institution_grants_minimal,
359
+ access_role_edwin_institution_grants_project,
360
+ access_role_edwin_institution_grants_minimal,
361
+ steve_steve_institution_role_membership,
362
+ steve_edwin_institution_role_membership,
363
+ steve_nathan_client_role_membership,
364
+ steve_edna_client_role_membership,
365
+ edwin_edwin_institution_role_membership,
366
+ }
367
+ }
368
+
369
+
370
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
371
+ ///////////////////////////////////////////////////////////// GET one ////////////////////////////////////////////////////////////////////////////////
372
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
373
+
374
+ it(`should authorize a basic GET operation on a document where the user has a T1 role membership`, async function () {
375
+ let { steve_institution, steve_client, steve_project } = await generate_test_setup();
376
+
377
+ let results = await got.get(`http://localhost:${port}/api/institution/${steve_institution._id}/client/${steve_client._id}/project/${steve_project._id}`, {
378
+ headers: {
379
+ authorization: 'steve'
380
+ }
381
+ }).json();
382
+
383
+ //@ts-ignore
384
+ assert.deepEqual(JSON.parse(JSON.stringify(steve_project)), results.data);
385
+ });
386
+
387
+ it(`should authorize a basic GET operation on a document where the user has a T2 role membership`, async function () {
388
+ let { edwin_institution, nathan_client, nathan_project } = await generate_test_setup();
389
+
390
+ let results = await got.get(`http://localhost:${port}/api/institution/${edwin_institution._id}/client/${nathan_client._id}/project/${nathan_project._id}`, {
391
+ headers: {
392
+ authorization: 'steve'
393
+ }
394
+ }).json();
395
+
396
+ //@ts-ignore
397
+ assert.deepEqual(JSON.parse(JSON.stringify(nathan_project)), results.data);
398
+ });
399
+
400
+ it(`should reject a basic GET operation on a document where the user has a role membership without permission`, async function () {
401
+ let { edwin_institution, edna_client, edna_project } = await generate_test_setup();
402
+
403
+ assert.rejects(async () => {
404
+ let results = await got.get(`http://localhost:${port}/api/institution/${edwin_institution._id}/client/${edna_client._id}/project/${edna_project._id}`, {
405
+ headers: {
406
+ authorization: 'steve'
407
+ }
408
+ }).json();
409
+ },
410
+ { message: 'HTTPError: Response code 403 (Forbidden)' })
411
+ });
412
+
413
+ it(`should reject a basic GET operation on a document where the user has no role membership`, async function () {
414
+ let { steve_institution, steve_client, steve_project } = await generate_test_setup();
415
+
416
+ assert.rejects(async () => {
417
+ let results = await got.get(`http://localhost:${port}/api/institution/${steve_institution._id}/client/${steve_client._id}/project/${steve_project._id}`, {
418
+ headers: {
419
+ authorization: 'edwin'
420
+ }
421
+ }).json();
422
+ },
423
+ { message: 'HTTPError: Response code 403 (Forbidden)' })
424
+ });
425
+
426
+
427
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
428
+ ///////////////////////////////////////////////////////////// GET multiple ///////////////////////////////////////////////////////////////////////////
429
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
430
+
431
+ it(`should authorize a basic GET multiple operation where the user has a T1 role membership`, async function () {
432
+ let { steve_institution, steve_client, steve_project } = await generate_test_setup();
433
+
434
+ let projects = [steve_project];
435
+ for(let q = 0; q < 5; q++){
436
+ projects.push(await collection_project.mongoose_model.create({
437
+ institution_id: steve_institution._id,
438
+ client_id: steve_client,
439
+ name: `additional project ${q}`
440
+ }))
441
+ }
442
+
443
+ let results = await got.get(`http://localhost:${port}/api/institution/${steve_institution._id}/client/${steve_client._id}/project`, {
444
+ headers: {
445
+ authorization: 'steve'
446
+ }
447
+ }).json();
448
+
449
+ //@ts-ignore
450
+ assert.deepEqual(JSON.parse(JSON.stringify(projects)), results.data);
451
+ });
452
+
453
+ it(`should authorize a basic GET multiple operation where the user has a T2 role membership`, async function () {
454
+ let { edwin_institution, nathan_client, nathan_project } = await generate_test_setup();
455
+
456
+ let projects = [nathan_project];
457
+ for(let q = 0; q < 5; q++){
458
+ projects.push(await collection_project.mongoose_model.create({
459
+ institution_id: edwin_institution._id,
460
+ client_id: nathan_client,
461
+ name: `additional project ${q}`
462
+ }))
463
+ }
464
+
465
+ let results = await got.get(`http://localhost:${port}/api/institution/${edwin_institution._id}/client/${nathan_client._id}/project`, {
466
+ headers: {
467
+ authorization: 'steve'
468
+ }
469
+ }).json();
470
+
471
+ //@ts-ignore
472
+ assert.deepEqual(JSON.parse(JSON.stringify(projects)), results.data);
473
+ });
474
+
475
+ it(`should reject a basic GET multiple operation where the user has a role membership without permission`, async function () {
476
+ let { edwin_institution, edna_client, edna_project } = await generate_test_setup();
477
+
478
+ let projects = [edna_project];
479
+ for(let q = 0; q < 5; q++){
480
+ projects.push(await collection_project.mongoose_model.create({
481
+ institution_id: edwin_institution._id,
482
+ client_id: edna_client,
483
+ name: `additional project ${q}`
484
+ }))
485
+ }
486
+
487
+ assert.rejects(async () => {
488
+ let results = await got.get(`http://localhost:${port}/api/institution/${edwin_institution._id}/client/${edna_client._id}/project`, {
489
+ headers: {
490
+ authorization: 'steve'
491
+ }
492
+ }).json();
493
+ },
494
+ { message: 'HTTPError: Response code 403 (Forbidden)' })
495
+ });
496
+
497
+ it(`should reject a basic GET multiple operation where the user has no role membership`, async function () {
498
+ let { steve_institution, steve_client, steve_project } = await generate_test_setup();
499
+
500
+ let projects = [steve_project];
501
+ for(let q = 0; q < 5; q++){
502
+ projects.push(await collection_project.mongoose_model.create({
503
+ institution_id: steve_institution._id,
504
+ client_id: steve_client,
505
+ name: `additional project ${q}`
506
+ }))
507
+ }
508
+
509
+ assert.rejects(async () => {
510
+ let results = await got.get(`http://localhost:${port}/api/institution/${steve_institution._id}/client/${steve_client._id}/project`, {
511
+ headers: {
512
+ authorization: 'edwin'
513
+ }
514
+ }).json();
515
+ },
516
+ { message: 'HTTPError: Response code 403 (Forbidden)' })
517
+ });
518
+
519
+
520
+
521
+
522
+
523
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
524
+ ///////////////////////////////////////////////////////////// PUT ////////////////////////////////////////////////////////////////////////////////////
525
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
526
+
527
+ it(`should authorize a basic PUT operation on a document where the user has a T1 role membership`, async function () {
528
+ let { steve_institution, steve_client, steve_project } = await generate_test_setup();
529
+
530
+ let results = await got.put(`http://localhost:${port}/api/institution/${steve_institution._id}/client/${steve_client._id}/project/${steve_project._id}`, {
531
+ headers: {
532
+ authorization: 'steve'
533
+ },
534
+ json: {
535
+ name: 'Flammable Project'
536
+ }
537
+ }).json();
538
+
539
+ //@ts-ignore
540
+ assert.notDeepEqual(JSON.parse(JSON.stringify(steve_project)), results.data);
541
+ //@ts-ignore
542
+ assert.deepEqual(JSON.parse(JSON.stringify(await collection_project.mongoose_model.findById(steve_project._id))), results.data);
543
+ });
544
+
545
+ it(`should authorize a basic PUT operation on a document where the user has a T2 role membership`, async function () {
546
+ let { edwin_institution, nathan_client, nathan_project } = await generate_test_setup();
547
+
548
+ let results = await got.put(`http://localhost:${port}/api/institution/${edwin_institution._id}/client/${nathan_client._id}/project/${nathan_project._id}`, {
549
+ headers: {
550
+ authorization: 'steve'
551
+ },
552
+ json: {
553
+ name: 'Flammable Project'
554
+ }
555
+ }).json();
556
+
557
+ //@ts-ignore
558
+ assert.notDeepEqual(JSON.parse(JSON.stringify(nathan_project)), results.data);
559
+ //@ts-ignore
560
+ assert.deepEqual(JSON.parse(JSON.stringify(await collection_project.mongoose_model.findById(nathan_project._id))), results.data);
561
+ });
562
+
563
+ it(`should reject a basic PUT operation on a document where the user has a role membership without permission`, async function () {
564
+ let { edwin_institution, edna_client, edna_project } = await generate_test_setup();
565
+
566
+ assert.rejects(async () => {
567
+ let results = await got.put(`http://localhost:${port}/api/institution/${edwin_institution._id}/client/${edna_client._id}/project/${edna_project._id}`, {
568
+ headers: {
569
+ authorization: 'steve'
570
+ },
571
+ json: {
572
+ name: 'Flammable Project'
573
+ }
574
+ }).json();
575
+ },
576
+ { message: 'HTTPError: Response code 403 (Forbidden)' })
577
+ });
578
+
579
+ it(`should reject a basic PUT operation on a document where the user has no role membership`, async function () {
580
+ let { steve_institution, steve_client, steve_project } = await generate_test_setup();
581
+
582
+ assert.rejects(async () => {
583
+ let results = await got.put(`http://localhost:${port}/api/institution/${steve_institution._id}/client/${steve_client._id}/project/${steve_project._id}`, {
584
+ headers: {
585
+ authorization: 'edwin'
586
+ },
587
+ json: {
588
+ name: 'Flammable Project'
589
+ }
590
+ }).json();
591
+ },
592
+ { message: 'HTTPError: Response code 403 (Forbidden)' })
593
+ });
594
+
595
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
596
+ ///////////////////////////////////////////////////////////// POST ///////////////////////////////////////////////////////////////////////////////////
597
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
598
+
599
+ it(`should authorize a basic POST operation on a document where the user has a T1 role membership`, async function () {
600
+ let { steve_institution, steve_client, steve_project } = await generate_test_setup();
601
+
602
+ let results = await got.post(`http://localhost:${port}/api/institution/${steve_institution._id}/client/${steve_client._id}/project`, {
603
+ headers: {
604
+ authorization: 'steve'
605
+ },
606
+ json: {
607
+ name: 'Flammable Project',
608
+ institution_id: steve_institution._id,
609
+ client_id: steve_client._id,
610
+ }
611
+ }).json();
612
+
613
+ //@ts-ignore
614
+ assert.deepEqual(JSON.parse(JSON.stringify(await collection_project.mongoose_model.findById(results.data._id))), results.data);
615
+ });
616
+
617
+ it(`should authorize a basic POST operation on a document where the user has a T2 role membership`, async function () {
618
+ let { edwin_institution, nathan_client, nathan_project } = await generate_test_setup();
619
+
620
+ let results = await got.post(`http://localhost:${port}/api/institution/${edwin_institution._id}/client/${nathan_client._id}/project`, {
621
+ headers: {
622
+ authorization: 'steve'
623
+ },
624
+ json: {
625
+ name: 'Flammable Project',
626
+ institution_id: edwin_institution._id,
627
+ client_id: nathan_client._id,
628
+ }
629
+ }).json();
630
+
631
+ //@ts-ignore
632
+ assert.deepEqual(JSON.parse(JSON.stringify(await collection_project.mongoose_model.findById(results.data._id))), results.data);
633
+ });
634
+
635
+ it(`should reject a basic POST operation on a document where the user has a role membership without permission`, async function () {
636
+ let { edwin_institution, edna_client, edna_project } = await generate_test_setup();
637
+
638
+ assert.rejects(async () => {
639
+ let results = await got.post(`http://localhost:${port}/api/institution/${edwin_institution._id}/client/${edna_client._id}/project`, {
640
+ headers: {
641
+ authorization: 'steve'
642
+ },
643
+ json: {
644
+ name: 'Flammable Project',
645
+ institution_id: edwin_institution._id,
646
+ client_id: edna_client._id,
647
+ }
648
+ }).json();
649
+ },
650
+ { message: 'HTTPError: Response code 403 (Forbidden)' })
651
+ });
652
+
653
+ it(`should reject a basic POST operation on a document where the user has no role membership`, async function () {
654
+ let { steve_institution, steve_client, steve_project } = await generate_test_setup();
655
+
656
+ assert.rejects(async () => {
657
+ let results = await got.post(`http://localhost:${port}/api/institution/${steve_institution._id}/client/${steve_client._id}/project`, {
658
+ headers: {
659
+ authorization: 'edwin'
660
+ },
661
+ json: {
662
+ name: 'Flammable Project',
663
+ institution_id: steve_institution._id,
664
+ client_id: steve_client._id,
665
+ }
666
+ }).json();
667
+ },
668
+ { message: 'HTTPError: Response code 403 (Forbidden)' })
669
+ });
670
+
671
+
672
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
673
+ ///////////////////////////////////////////////////////////// DELETE /////////////////////////////////////////////////////////////////////////////////
674
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
675
+
676
+ it(`should authorize a basic DELETE operation on a document where the user has a T1 role membership`, async function () {
677
+ let { steve_institution, steve_client, steve_project } = await generate_test_setup();
678
+
679
+ let results = await got.delete(`http://localhost:${port}/api/institution/${steve_institution._id}/client/${steve_client._id}/project/${steve_project._id}`, {
680
+ headers: {
681
+ authorization: 'steve'
682
+ }
683
+ }).json();
684
+
685
+ //@ts-ignore
686
+ assert.deepEqual(JSON.parse(JSON.stringify(steve_project)), results.data);
687
+ //@ts-ignore
688
+ assert.deepEqual(JSON.parse(JSON.stringify(await collection_project.mongoose_model.findById(steve_project._id))), undefined);
689
+ });
690
+
691
+ it(`should authorize a basic DELETE operation on a document where the user has a T2 role membership`, async function () {
692
+ let { edwin_institution, nathan_client, nathan_project } = await generate_test_setup();
693
+
694
+ let results = await got.delete(`http://localhost:${port}/api/institution/${edwin_institution._id}/client/${nathan_client._id}/project/${nathan_project._id}`, {
695
+ headers: {
696
+ authorization: 'steve'
697
+ }
698
+ }).json();
699
+
700
+ //@ts-ignore
701
+ assert.deepEqual(JSON.parse(JSON.stringify(nathan_project)), results.data);
702
+ //@ts-ignore
703
+ assert.deepEqual(JSON.parse(JSON.stringify(await collection_project.mongoose_model.findById(nathan_project._id))), undefined);
704
+ });
705
+
706
+ it(`should reject a basic DELETE operation on a document where the user has a role membership without permission`, async function () {
707
+ let { edwin_institution, edna_client, edna_project } = await generate_test_setup();
708
+
709
+ assert.rejects(async () => {
710
+ let results = await got.delete(`http://localhost:${port}/api/institution/${edwin_institution._id}/client/${edna_client._id}/project/${edna_project._id}`, {
711
+ headers: {
712
+ authorization: 'steve'
713
+ }
714
+ }).json();
715
+ },
716
+ { message: 'HTTPError: Response code 403 (Forbidden)' })
717
+ });
718
+
719
+ it(`should reject a basic DELETE operation on a document where the user has no role membership`, async function () {
720
+ let { steve_institution, steve_client, steve_project } = await generate_test_setup();
721
+
722
+ assert.rejects(async () => {
723
+ let results = await got.delete(`http://localhost:${port}/api/institution/${steve_institution._id}/client/${steve_client._id}/project/${steve_project._id}`, {
724
+ headers: {
725
+ authorization: 'edwin'
726
+ }
727
+ }).json();
728
+ },
729
+ { message: 'HTTPError: Response code 403 (Forbidden)' })
730
+ });
731
+ });