@flowerforce/flowerbase 1.0.1-beta.3

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 (292) hide show
  1. package/CHANGELOG.md +0 -0
  2. package/LICENSE +3 -0
  3. package/README.md +18 -0
  4. package/dist/auth/controller.d.ts +8 -0
  5. package/dist/auth/controller.d.ts.map +1 -0
  6. package/dist/auth/controller.js +76 -0
  7. package/dist/auth/dtos.d.ts +6 -0
  8. package/dist/auth/dtos.d.ts.map +1 -0
  9. package/dist/auth/dtos.js +2 -0
  10. package/dist/auth/plugins/jwt.d.ts +14 -0
  11. package/dist/auth/plugins/jwt.d.ts.map +1 -0
  12. package/dist/auth/plugins/jwt.js +68 -0
  13. package/dist/auth/providers/local-userpass/controller.d.ts +8 -0
  14. package/dist/auth/providers/local-userpass/controller.d.ts.map +1 -0
  15. package/dist/auth/providers/local-userpass/controller.js +184 -0
  16. package/dist/auth/providers/local-userpass/dtos.d.ts +35 -0
  17. package/dist/auth/providers/local-userpass/dtos.d.ts.map +1 -0
  18. package/dist/auth/providers/local-userpass/dtos.js +2 -0
  19. package/dist/auth/utils.d.ts +126 -0
  20. package/dist/auth/utils.d.ts.map +1 -0
  21. package/dist/auth/utils.js +122 -0
  22. package/dist/constants.d.ts +18 -0
  23. package/dist/constants.d.ts.map +1 -0
  24. package/dist/constants.js +34 -0
  25. package/dist/features/endpoints/index.d.ts +10 -0
  26. package/dist/features/endpoints/index.d.ts.map +1 -0
  27. package/dist/features/endpoints/index.js +31 -0
  28. package/dist/features/endpoints/interface.d.ts +27 -0
  29. package/dist/features/endpoints/interface.d.ts.map +1 -0
  30. package/dist/features/endpoints/interface.js +2 -0
  31. package/dist/features/endpoints/utils.d.ts +31 -0
  32. package/dist/features/endpoints/utils.d.ts.map +1 -0
  33. package/dist/features/endpoints/utils.js +85 -0
  34. package/dist/features/functions/controller.d.ts +9 -0
  35. package/dist/features/functions/controller.d.ts.map +1 -0
  36. package/dist/features/functions/controller.js +88 -0
  37. package/dist/features/functions/dtos.d.ts +34 -0
  38. package/dist/features/functions/dtos.d.ts.map +1 -0
  39. package/dist/features/functions/dtos.js +2 -0
  40. package/dist/features/functions/index.d.ts +9 -0
  41. package/dist/features/functions/index.d.ts.map +1 -0
  42. package/dist/features/functions/index.js +28 -0
  43. package/dist/features/functions/interface.d.ts +32 -0
  44. package/dist/features/functions/interface.d.ts.map +1 -0
  45. package/dist/features/functions/interface.js +2 -0
  46. package/dist/features/functions/utils.d.ts +23 -0
  47. package/dist/features/functions/utils.d.ts.map +1 -0
  48. package/dist/features/functions/utils.js +75 -0
  49. package/dist/features/rules/index.d.ts +1 -0
  50. package/dist/features/rules/index.d.ts.map +1 -0
  51. package/dist/features/rules/index.js +1 -0
  52. package/dist/features/rules/interface.d.ts +22 -0
  53. package/dist/features/rules/interface.d.ts.map +1 -0
  54. package/dist/features/rules/interface.js +2 -0
  55. package/dist/features/rules/utils.d.ts +3 -0
  56. package/dist/features/rules/utils.d.ts.map +1 -0
  57. package/dist/features/rules/utils.js +31 -0
  58. package/dist/features/triggers/dtos.d.ts +9 -0
  59. package/dist/features/triggers/dtos.d.ts.map +1 -0
  60. package/dist/features/triggers/dtos.js +2 -0
  61. package/dist/features/triggers/index.d.ts +10 -0
  62. package/dist/features/triggers/index.d.ts.map +1 -0
  63. package/dist/features/triggers/index.js +57 -0
  64. package/dist/features/triggers/interface.d.ts +44 -0
  65. package/dist/features/triggers/interface.d.ts.map +1 -0
  66. package/dist/features/triggers/interface.js +2 -0
  67. package/dist/features/triggers/utils.d.ts +16 -0
  68. package/dist/features/triggers/utils.d.ts.map +1 -0
  69. package/dist/features/triggers/utils.js +153 -0
  70. package/dist/index.d.ts +19 -0
  71. package/dist/index.d.ts.map +1 -0
  72. package/dist/index.js +84 -0
  73. package/dist/model.d.ts +2 -0
  74. package/dist/model.d.ts.map +1 -0
  75. package/dist/model.js +2 -0
  76. package/dist/services/api/index.d.ts +36 -0
  77. package/dist/services/api/index.d.ts.map +1 -0
  78. package/dist/services/api/index.js +36 -0
  79. package/dist/services/api/model.d.ts +33 -0
  80. package/dist/services/api/model.d.ts.map +1 -0
  81. package/dist/services/api/model.js +2 -0
  82. package/dist/services/api/utils.d.ts +16 -0
  83. package/dist/services/api/utils.d.ts.map +1 -0
  84. package/dist/services/api/utils.js +45 -0
  85. package/dist/services/aws/index.d.ts +13 -0
  86. package/dist/services/aws/index.d.ts.map +1 -0
  87. package/dist/services/aws/index.js +50 -0
  88. package/dist/services/index.d.ts +41 -0
  89. package/dist/services/index.d.ts.map +1 -0
  90. package/dist/services/index.js +14 -0
  91. package/dist/services/interface.d.ts +3 -0
  92. package/dist/services/interface.d.ts.map +1 -0
  93. package/dist/services/interface.js +2 -0
  94. package/dist/services/mongodb-atlas/index.d.ts +4 -0
  95. package/dist/services/mongodb-atlas/index.d.ts.map +1 -0
  96. package/dist/services/mongodb-atlas/index.js +483 -0
  97. package/dist/services/mongodb-atlas/model.d.ts +39 -0
  98. package/dist/services/mongodb-atlas/model.d.ts.map +1 -0
  99. package/dist/services/mongodb-atlas/model.js +2 -0
  100. package/dist/services/mongodb-atlas/utils.d.ts +8 -0
  101. package/dist/services/mongodb-atlas/utils.d.ts.map +1 -0
  102. package/dist/services/mongodb-atlas/utils.js +33 -0
  103. package/dist/state.d.ts +6 -0
  104. package/dist/state.d.ts.map +1 -0
  105. package/dist/state.js +18 -0
  106. package/dist/utils/context/helpers.d.ts +74 -0
  107. package/dist/utils/context/helpers.d.ts.map +1 -0
  108. package/dist/utils/context/helpers.js +60 -0
  109. package/dist/utils/context/index.d.ts +14 -0
  110. package/dist/utils/context/index.d.ts.map +1 -0
  111. package/dist/utils/context/index.js +50 -0
  112. package/dist/utils/context/interface.d.ts +18 -0
  113. package/dist/utils/context/interface.d.ts.map +1 -0
  114. package/dist/utils/context/interface.js +2 -0
  115. package/dist/utils/crypto/index.d.ts +19 -0
  116. package/dist/utils/crypto/index.d.ts.map +1 -0
  117. package/dist/utils/crypto/index.js +50 -0
  118. package/dist/utils/helpers/someAsync.d.ts +12 -0
  119. package/dist/utils/helpers/someAsync.d.ts.map +1 -0
  120. package/dist/utils/helpers/someAsync.js +56 -0
  121. package/dist/utils/index.d.ts +3 -0
  122. package/dist/utils/index.d.ts.map +1 -0
  123. package/dist/utils/index.js +11 -0
  124. package/dist/utils/initializer/exposeRoutes.d.ts +8 -0
  125. package/dist/utils/initializer/exposeRoutes.d.ts.map +1 -0
  126. package/dist/utils/initializer/exposeRoutes.js +41 -0
  127. package/dist/utils/initializer/registerPlugins.d.ts +19 -0
  128. package/dist/utils/initializer/registerPlugins.d.ts.map +1 -0
  129. package/dist/utils/initializer/registerPlugins.js +84 -0
  130. package/dist/utils/roles/helpers.d.ts +4 -0
  131. package/dist/utils/roles/helpers.d.ts.map +1 -0
  132. package/dist/utils/roles/helpers.js +47 -0
  133. package/dist/utils/roles/interface.d.ts +33 -0
  134. package/dist/utils/roles/interface.d.ts.map +1 -0
  135. package/dist/utils/roles/interface.js +2 -0
  136. package/dist/utils/roles/machines/commonValidators.d.ts +6 -0
  137. package/dist/utils/roles/machines/commonValidators.d.ts.map +1 -0
  138. package/dist/utils/roles/machines/commonValidators.js +34 -0
  139. package/dist/utils/roles/machines/index.d.ts +14 -0
  140. package/dist/utils/roles/machines/index.d.ts.map +1 -0
  141. package/dist/utils/roles/machines/index.js +27 -0
  142. package/dist/utils/roles/machines/interface.d.ts +46 -0
  143. package/dist/utils/roles/machines/interface.d.ts.map +1 -0
  144. package/dist/utils/roles/machines/interface.js +2 -0
  145. package/dist/utils/roles/machines/machine.d.ts +15 -0
  146. package/dist/utils/roles/machines/machine.d.ts.map +1 -0
  147. package/dist/utils/roles/machines/machine.js +97 -0
  148. package/dist/utils/roles/machines/read/A/index.d.ts +3 -0
  149. package/dist/utils/roles/machines/read/A/index.d.ts.map +1 -0
  150. package/dist/utils/roles/machines/read/A/index.js +27 -0
  151. package/dist/utils/roles/machines/read/B/index.d.ts +3 -0
  152. package/dist/utils/roles/machines/read/B/index.d.ts.map +1 -0
  153. package/dist/utils/roles/machines/read/B/index.js +36 -0
  154. package/dist/utils/roles/machines/read/C/index.d.ts +3 -0
  155. package/dist/utils/roles/machines/read/C/index.d.ts.map +1 -0
  156. package/dist/utils/roles/machines/read/C/index.js +38 -0
  157. package/dist/utils/roles/machines/read/D/index.d.ts +3 -0
  158. package/dist/utils/roles/machines/read/D/index.d.ts.map +1 -0
  159. package/dist/utils/roles/machines/read/D/index.js +26 -0
  160. package/dist/utils/roles/machines/read/D/validators.d.ts +4 -0
  161. package/dist/utils/roles/machines/read/D/validators.d.ts.map +1 -0
  162. package/dist/utils/roles/machines/read/D/validators.js +24 -0
  163. package/dist/utils/roles/machines/read/index.d.ts +2 -0
  164. package/dist/utils/roles/machines/read/index.d.ts.map +1 -0
  165. package/dist/utils/roles/machines/read/index.js +8 -0
  166. package/dist/utils/roles/machines/utils.d.ts +37 -0
  167. package/dist/utils/roles/machines/utils.d.ts.map +1 -0
  168. package/dist/utils/roles/machines/utils.js +54 -0
  169. package/dist/utils/roles/machines/write/A/index.d.ts +3 -0
  170. package/dist/utils/roles/machines/write/A/index.d.ts.map +1 -0
  171. package/dist/utils/roles/machines/write/A/index.js +29 -0
  172. package/dist/utils/roles/machines/write/B/index.d.ts +3 -0
  173. package/dist/utils/roles/machines/write/B/index.d.ts.map +1 -0
  174. package/dist/utils/roles/machines/write/B/index.js +47 -0
  175. package/dist/utils/roles/machines/write/C/index.d.ts +3 -0
  176. package/dist/utils/roles/machines/write/C/index.d.ts.map +1 -0
  177. package/dist/utils/roles/machines/write/C/index.js +26 -0
  178. package/dist/utils/roles/machines/write/C/validators.d.ts +4 -0
  179. package/dist/utils/roles/machines/write/C/validators.d.ts.map +1 -0
  180. package/dist/utils/roles/machines/write/C/validators.js +24 -0
  181. package/dist/utils/roles/machines/write/index.d.ts +2 -0
  182. package/dist/utils/roles/machines/write/index.d.ts.map +1 -0
  183. package/dist/utils/roles/machines/write/index.js +7 -0
  184. package/dist/utils/rules-matcher/interface.d.ts +338 -0
  185. package/dist/utils/rules-matcher/interface.d.ts.map +1 -0
  186. package/dist/utils/rules-matcher/interface.js +26 -0
  187. package/dist/utils/rules-matcher/utils.d.ts +11 -0
  188. package/dist/utils/rules-matcher/utils.d.ts.map +1 -0
  189. package/dist/utils/rules-matcher/utils.js +214 -0
  190. package/dist/utils/rules.d.ts +2 -0
  191. package/dist/utils/rules.d.ts.map +1 -0
  192. package/dist/utils/rules.js +22 -0
  193. package/jest.config.ts +24 -0
  194. package/package.json +63 -0
  195. package/project.json +10 -0
  196. package/rollup.config.js +17 -0
  197. package/src/auth/controller.ts +78 -0
  198. package/src/auth/dtos.ts +6 -0
  199. package/src/auth/plugins/jwt.ts +68 -0
  200. package/src/auth/providers/local-userpass/controller.ts +226 -0
  201. package/src/auth/providers/local-userpass/dtos.ts +40 -0
  202. package/src/auth/utils.ts +165 -0
  203. package/src/babel.config.json +3 -0
  204. package/src/constants.ts +22 -0
  205. package/src/fastify.d.ts +28 -0
  206. package/src/features/endpoints/index.ts +27 -0
  207. package/src/features/endpoints/interface.ts +29 -0
  208. package/src/features/endpoints/utils.ts +72 -0
  209. package/src/features/functions/controller.ts +102 -0
  210. package/src/features/functions/dtos.ts +41 -0
  211. package/src/features/functions/index.ts +21 -0
  212. package/src/features/functions/interface.ts +38 -0
  213. package/src/features/functions/utils.ts +82 -0
  214. package/src/features/rules/index.tsx +0 -0
  215. package/src/features/rules/interface.ts +24 -0
  216. package/src/features/rules/utils.ts +20 -0
  217. package/src/features/triggers/dtos.ts +9 -0
  218. package/src/features/triggers/index.ts +34 -0
  219. package/src/features/triggers/interface.ts +44 -0
  220. package/src/features/triggers/utils.ts +157 -0
  221. package/src/global.d.ts +0 -0
  222. package/src/index.ts +75 -0
  223. package/src/model.ts +1 -0
  224. package/src/services/api/index.ts +50 -0
  225. package/src/services/api/model.ts +38 -0
  226. package/src/services/api/utils.ts +39 -0
  227. package/src/services/aws/index.ts +48 -0
  228. package/src/services/index.ts +9 -0
  229. package/src/services/interface.ts +3 -0
  230. package/src/services/mongodb-atlas/index.ts +569 -0
  231. package/src/services/mongodb-atlas/model.ts +67 -0
  232. package/src/services/mongodb-atlas/utils.ts +44 -0
  233. package/src/state.ts +24 -0
  234. package/src/utils/__tests__/STEP_A_STATES.test.ts +54 -0
  235. package/src/utils/__tests__/STEP_B_STATES.test.ts +113 -0
  236. package/src/utils/__tests__/STEP_C_STATES.test.ts +87 -0
  237. package/src/utils/__tests__/STEP_D_STATES.test.ts +93 -0
  238. package/src/utils/__tests__/checkAdditionalFieldsFn.test.ts +45 -0
  239. package/src/utils/__tests__/checkApplyWhen.test.ts +49 -0
  240. package/src/utils/__tests__/checkFieldsPropertyExists.test.ts +47 -0
  241. package/src/utils/__tests__/checkIsValidFieldNameFn.test.ts +190 -0
  242. package/src/utils/__tests__/comparePassword.test.ts +38 -0
  243. package/src/utils/__tests__/evaluateDocumentsFiltersReadFn.test.ts +57 -0
  244. package/src/utils/__tests__/evaluateDocumentsFiltersWriteFn.test.ts +57 -0
  245. package/src/utils/__tests__/evaluateTopLevelReadFn.test.ts +58 -0
  246. package/src/utils/__tests__/evaluateTopLevelWriteFn.test.ts +66 -0
  247. package/src/utils/__tests__/exposeRoutes.test.ts +65 -0
  248. package/src/utils/__tests__/generateContextData.test.ts +75 -0
  249. package/src/utils/__tests__/getDefaultRule.test.ts +29 -0
  250. package/src/utils/__tests__/getKey.test.ts +12 -0
  251. package/src/utils/__tests__/getKeys.test.ts +11 -0
  252. package/src/utils/__tests__/getWinningRole.test.ts +66 -0
  253. package/src/utils/__tests__/hashPassword.test.ts +28 -0
  254. package/src/utils/__tests__/isEmpty.test.ts +17 -0
  255. package/src/utils/__tests__/logMachineInfo.test.ts +15 -0
  256. package/src/utils/__tests__/operators.test.ts +99 -0
  257. package/src/utils/__tests__/readFileContent.test.ts +35 -0
  258. package/src/utils/__tests__/registerPlugins.test.ts +59 -0
  259. package/src/utils/__tests__/rule.test.ts +51 -0
  260. package/src/utils/__tests__/rulesMatcherInterfaces.test.ts +57 -0
  261. package/src/utils/__tests__/rulesMatcherUtils.test.ts +56 -0
  262. package/src/utils/__tests__/someAsync.test.ts +55 -0
  263. package/src/utils/context/helpers.ts +71 -0
  264. package/src/utils/context/index.ts +52 -0
  265. package/src/utils/context/interface.ts +19 -0
  266. package/src/utils/crypto/index.ts +36 -0
  267. package/src/utils/helpers/someAsync.ts +24 -0
  268. package/src/utils/index.ts +5 -0
  269. package/src/utils/initializer/exposeRoutes.ts +26 -0
  270. package/src/utils/initializer/registerPlugins.ts +97 -0
  271. package/src/utils/roles/helpers.ts +47 -0
  272. package/src/utils/roles/interface.ts +42 -0
  273. package/src/utils/roles/machines/commonValidators.ts +24 -0
  274. package/src/utils/roles/machines/index.ts +20 -0
  275. package/src/utils/roles/machines/interface.ts +46 -0
  276. package/src/utils/roles/machines/machine.ts +85 -0
  277. package/src/utils/roles/machines/read/A/index.ts +19 -0
  278. package/src/utils/roles/machines/read/B/index.ts +31 -0
  279. package/src/utils/roles/machines/read/C/index.ts +30 -0
  280. package/src/utils/roles/machines/read/D/index.ts +20 -0
  281. package/src/utils/roles/machines/read/D/validators.ts +24 -0
  282. package/src/utils/roles/machines/read/index.ts +6 -0
  283. package/src/utils/roles/machines/utils.ts +54 -0
  284. package/src/utils/roles/machines/write/A/index.ts +25 -0
  285. package/src/utils/roles/machines/write/B/index.ts +43 -0
  286. package/src/utils/roles/machines/write/C/index.ts +20 -0
  287. package/src/utils/roles/machines/write/C/validators.ts +24 -0
  288. package/src/utils/roles/machines/write/index.ts +5 -0
  289. package/src/utils/rules-matcher/interface.ts +365 -0
  290. package/src/utils/rules-matcher/utils.ts +281 -0
  291. package/src/utils/rules.ts +19 -0
  292. package/tsconfig.json +28 -0
@@ -0,0 +1,19 @@
1
+ import { FastifyInstance } from 'fastify'
2
+ import { Arguments, User } from '../../auth/dtos'
3
+ import { Function, Functions } from '../../features/functions/interface'
4
+ import { Rules } from '../../features/rules/interface'
5
+ import { Services } from '../../services/interface'
6
+
7
+ export interface GenerateContextParams {
8
+ app: FastifyInstance
9
+ currentFunction: Function
10
+ functionsList: Functions
11
+ rules: Rules
12
+ user: User
13
+ services: Services
14
+ args: Arguments
15
+ }
16
+
17
+ export interface GenerateContextDataParams extends Omit<GenerateContextParams, 'args'> {
18
+ GenerateContext: (params: GenerateContextParams) => Promise<void>
19
+ }
@@ -0,0 +1,36 @@
1
+ import crypto from 'node:crypto'
2
+ import { promisify } from 'node:util'
3
+
4
+ const scrypt = promisify(crypto.scrypt)
5
+
6
+ /**
7
+ * > Creates the hash for a string
8
+ * @param plaintext -> the string that should be encrypted
9
+ * @tested
10
+ */
11
+ export const hashPassword = async (plaintext: string) => {
12
+ const salt = crypto.randomBytes(128).toString('hex')
13
+ const buffer = (await scrypt(plaintext, salt, 64)) as Buffer
14
+ return `${buffer.toString('hex')}.${salt}`
15
+ }
16
+
17
+ /**
18
+ * > Compares two strings
19
+ * @param plaintext -> the first string
20
+ * @param storedPassword -> the second string
21
+ * @tested
22
+ */
23
+ export const comparePassword = async (plaintext: string, storedPassword: string) => {
24
+ const [storedHash, storedSalt] = storedPassword.split('.')
25
+ const storedBuffer = Buffer.from(storedHash, 'hex')
26
+ const buffer = (await scrypt(plaintext, storedSalt, 64)) as Buffer
27
+ return crypto.timingSafeEqual(buffer, storedBuffer)
28
+ }
29
+
30
+ /**
31
+ * > Generate a random token
32
+ * @param length -> the token length
33
+ */
34
+ export const generateToken = (length = 32) => {
35
+ return crypto.randomBytes(length).toString('hex');
36
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Asynchronously checks if at least one element in the array satisfies the given asynchronous callback function.
3
+ *
4
+ * @template T - The type of elements in the array.
5
+ * @param {T[]} array - The array to iterate over.
6
+ * @param {(element: T, index: number, arr: T[]) => Promise<boolean>} callback - An asynchronous function that takes an element, its index, and the original array,
7
+ * and returns a Promise resolving to `true` if the condition is met, otherwise `false`.
8
+ * @returns {Promise<boolean>} A promise that resolves to `true` if at least one element satisfies the callback condition, otherwise `false`.
9
+ * @tested
10
+ */
11
+ export async function someAsync<T>(
12
+ array: T[],
13
+ callback: (element: T, index: number, arr: T[]) => Promise<boolean>
14
+ ): Promise<boolean> {
15
+ let i = 0;
16
+ for await (const el of array) {
17
+ const isValid = await callback(el, i, array);
18
+ if (isValid) {
19
+ return true;
20
+ }
21
+ i++;
22
+ }
23
+ return false;
24
+ }
@@ -0,0 +1,5 @@
1
+ import fs from 'fs'
2
+
3
+ export const readFileContent = (filePath: string) => fs.readFileSync(filePath, 'utf-8')
4
+ export const readJsonContent = (filePath: string) =>
5
+ JSON.parse(readFileContent(filePath)) as unknown
@@ -0,0 +1,26 @@
1
+ import { uptime } from 'node:process'
2
+ import { FastifyInstance } from 'fastify'
3
+ import { API_VERSION } from '../../constants'
4
+
5
+ /**
6
+ * > Used to expose all app routes
7
+ * @param fastify -> the fastify instance
8
+ * @tested
9
+ */
10
+ export const exposeRoutes = async (fastify: FastifyInstance) => {
11
+ try {
12
+ fastify.get(`${API_VERSION}/app/:appId/location`, async (req) => ({
13
+ deployment_model: 'LOCAL',
14
+ location: 'IE',
15
+ hostname: `http://${req.headers.host}`,
16
+ ws_hostname: `wss://${req.headers.host}`
17
+ }))
18
+
19
+ fastify.get('/health', async () => ({
20
+ status: 'ok',
21
+ uptime: uptime()
22
+ }))
23
+ } catch (e) {
24
+ console.error('Error while exposing routes', (e as Error).message)
25
+ }
26
+ }
@@ -0,0 +1,97 @@
1
+ import cors from '@fastify/cors'
2
+ import fastifyMongodb from '@fastify/mongodb'
3
+ import { FastifyInstance } from 'fastify'
4
+ import { authController } from '../../auth/controller'
5
+ import jwtAuthPlugin from '../../auth/plugins/jwt'
6
+ import { localUserPassController } from '../../auth/providers/local-userpass/controller'
7
+ import { API_VERSION } from '../../constants'
8
+ import { Functions } from '../../features/functions/interface'
9
+
10
+ type RegisterFunction = FastifyInstance['register']
11
+ type RegisterParameters = Parameters<RegisterFunction>
12
+
13
+ type RegisterPluginsParams = {
14
+ register: RegisterFunction
15
+ mongodbUrl: string
16
+ jwtSecret: string
17
+ functionsList: Functions
18
+ }
19
+
20
+ type RegisterConfig = {
21
+ plugin: RegisterParameters[0]
22
+ options: RegisterParameters[1]
23
+ }
24
+
25
+ /**
26
+ * > Used to register all plugins
27
+ * @param register -> the fastify register method
28
+ * @param mongodbUrl -> the database connection string
29
+ * @param jwtSecret -> connection jwt
30
+ * @tested
31
+ */
32
+ export const registerPlugins = async ({
33
+ register,
34
+ mongodbUrl,
35
+ jwtSecret,
36
+ functionsList
37
+ }: RegisterPluginsParams) => {
38
+ try {
39
+ const registersConfig = await getRegisterConfig({
40
+ mongodbUrl,
41
+ jwtSecret,
42
+ functionsList
43
+ })
44
+
45
+ registersConfig.forEach(({ plugin, options }) => {
46
+ register(plugin, options)
47
+ })
48
+ } catch (e) {
49
+ console.error('Error while registering plugins', (e as Error).message)
50
+ }
51
+ }
52
+
53
+ /**
54
+ * > Used to generate the register congig
55
+ * @param mongodbUrl -> the database connection string
56
+ * @param jwtSecret -> connection jwt
57
+ * @testable
58
+ */
59
+ const getRegisterConfig = async ({
60
+ mongodbUrl,
61
+ jwtSecret
62
+ }: Pick<RegisterPluginsParams, 'jwtSecret' | 'mongodbUrl' | 'functionsList'>): Promise<
63
+ RegisterConfig[]
64
+ > => {
65
+ return [
66
+ {
67
+ plugin: cors,
68
+ options: {
69
+ origin: '*',
70
+ methods: ['POST', 'GET']
71
+ }
72
+ },
73
+ {
74
+ plugin: fastifyMongodb,
75
+ options: {
76
+ forceClose: true,
77
+ url: mongodbUrl
78
+ }
79
+ },
80
+ {
81
+ plugin: jwtAuthPlugin,
82
+ options: {
83
+ secret: jwtSecret
84
+ }
85
+ },
86
+ {
87
+ plugin: authController,
88
+ options: { prefix: `${API_VERSION}/auth` }
89
+ },
90
+ {
91
+ plugin: localUserPassController,
92
+ options: {
93
+ prefix: `${API_VERSION}/app/:appId/auth/providers/local-userpass`
94
+ }
95
+ }
96
+ ] as RegisterConfig[]
97
+ }
@@ -0,0 +1,47 @@
1
+ import { services } from '../../services'
2
+ import { StateManager } from '../../state'
3
+ import { GenerateContext } from '../context'
4
+ import { expandQuery } from '../rules'
5
+ import rulesMatcherUtils from '../rules-matcher/utils'
6
+ import { PermissionExpression } from './interface'
7
+ import { MachineContext } from './machines/interface'
8
+
9
+ const functionsConditions = ["%%true", "%%false"]
10
+
11
+ export const evaluateExpression = async (params: MachineContext["params"], expression?: PermissionExpression, user?: MachineContext["user"]): Promise<boolean> => {
12
+ if (!expression || typeof expression === 'boolean') return !!expression
13
+
14
+ const value = {
15
+ ...params.expansions,
16
+ ...params.cursor,
17
+ "%%user": user,
18
+ '%%true': true
19
+ }
20
+ const conditions = expandQuery(expression, value)
21
+ const complexCondition = Object.entries<Record<string, any>>(conditions).find(([key]) => functionsConditions.includes(key))
22
+ return complexCondition ? await evaluateComplexExpression(complexCondition, params, user) : rulesMatcherUtils.checkRule(conditions, value, {})
23
+
24
+ }
25
+
26
+ const evaluateComplexExpression = async (condition: [string, Record<string, any>], params: MachineContext["params"], user: MachineContext["user"]) => {
27
+ const [key, config] = condition
28
+
29
+ const { name } = config["%function"]
30
+ const functionsList = StateManager.select("functions")
31
+ const app = StateManager.select("app")
32
+ const currentFunction = functionsList[name]
33
+ const response = await GenerateContext({
34
+ args: [params.cursor],
35
+ app,
36
+ rules: {},
37
+ user,
38
+ currentFunction,
39
+ functionsList,
40
+ services
41
+ })
42
+ return key === "%%true" ? response : !response
43
+
44
+ }
45
+
46
+
47
+
@@ -0,0 +1,42 @@
1
+ export type PermissionExpression = boolean // TODO: add complex condition (%%true: %function)
2
+
3
+ export type FieldPermissionExpression = {
4
+ read?: boolean,
5
+ write?: boolean
6
+ }
7
+
8
+ export interface DocumentFiltersPermissions {
9
+ read?: PermissionExpression
10
+ write?: PermissionExpression
11
+ }
12
+
13
+ export interface Role {
14
+ name: string
15
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
16
+ apply_when: Record<string, any> // TODO -> define this type
17
+ search?: PermissionExpression
18
+ document_filters?: DocumentFiltersPermissions
19
+ read?: PermissionExpression
20
+ write?: PermissionExpression
21
+ insert?: PermissionExpression
22
+ delete?: PermissionExpression
23
+ fields?: {
24
+ [K: string]: FieldPermissionExpression
25
+ }
26
+ additional_fields?: {
27
+ [K: string]: FieldPermissionExpression
28
+ }
29
+ }
30
+
31
+ export interface Params {
32
+ roles: Role[]
33
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
34
+ cursor: any // TODO -> define this type
35
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
36
+ expansions: Record<string, any> // TODO -> define this type
37
+ type: 'insert' | 'read' | 'delete' | 'search' | 'write'
38
+ }
39
+
40
+
41
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
42
+ export type Condition = Record<string, any>
@@ -0,0 +1,24 @@
1
+ import { someAsync } from "../../helpers/someAsync"
2
+ import { evaluateExpression } from "../helpers"
3
+ import { DocumentFiltersPermissions } from "../interface"
4
+ import { MachineContext } from "./interface"
5
+
6
+ const readOnlyPermissions = ['read']
7
+ const readWritePermissions = ['write', 'delete', 'insert', ...readOnlyPermissions]
8
+
9
+ export const evaluateDocumentFiltersFn = async ({ params, role, user }: MachineContext, currentType: keyof DocumentFiltersPermissions) => {
10
+ const permissions = currentType === "read" ? readOnlyPermissions : readWritePermissions
11
+ return await someAsync([
12
+ permissions.includes(params.type) && role.document_filters?.[currentType]
13
+ ]
14
+ .filter(Boolean), async (expr) => evaluateExpression(params, expr, user))
15
+ }
16
+
17
+
18
+ export const evaluateTopLevelPermissionsFn = async ({ params, role, user }: MachineContext, currentType: MachineContext["params"]["type"]) => {
19
+ return role[currentType] ? await evaluateExpression(params, role[currentType], user) : undefined
20
+ }
21
+
22
+ export const checkFieldsPropertyExists = ({ role }: MachineContext) => {
23
+ return !!Object.keys(role.fields ?? {}).length
24
+ }
@@ -0,0 +1,20 @@
1
+ import { User } from '../../../auth/dtos'
2
+ import { Params, Role } from '../interface'
3
+ import { StepResult } from './interface'
4
+ import { StateMachine } from './machine'
5
+
6
+ /**
7
+ * Executes the validation process using the `StateMachine` for the given role, parameters, and user.
8
+ *
9
+ * @param {Role} role - The role configuration for validation.
10
+ * @param {Params} params - The parameters relevant to the validation process.
11
+ * @param {User} user - The user for whom the validation is being performed.
12
+ *
13
+ * @returns {Promise<StepResult>} - The result of the state machine's validation process.
14
+ */
15
+ export const checkValidation = async (role: Role, params: Params, user: User, enableLog?: boolean): Promise<StepResult> => {
16
+ const stateMachine = new StateMachine(role, params, user, enableLog)
17
+ return await stateMachine.runValidation()
18
+ }
19
+
20
+
@@ -0,0 +1,46 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { Document } from 'mongodb'
3
+ import { User } from '../../../auth/dtos'
4
+ import { Params, Role } from '../interface'
5
+
6
+ export type PrevParams = Record<string, any>
7
+ export interface MachineContext {
8
+ user: User
9
+ role: Role
10
+ params: Params
11
+ prevParams?: PrevParams
12
+ enableLog?: boolean
13
+ }
14
+
15
+ type StateFunction = (
16
+ params: RunParams & {
17
+ context: MachineContext
18
+ } & {
19
+ next: (step: string, params?: Record<string, any>) => void
20
+ endValidation: ({ success, document }: { success: boolean, document?: Document }) => void
21
+ goToNextValidationStage: (initialStep?: string | null) => void
22
+ }
23
+ ) => Promise<void>
24
+
25
+ export type States = Record<string, StateFunction>
26
+
27
+ export interface RunParams {
28
+ initialStep: string | null
29
+ }
30
+
31
+ export interface ValidationStatus {
32
+ status: boolean | null
33
+ document?: Document
34
+ }
35
+
36
+
37
+ export interface StepResult { status: boolean | null; nextInitialStep: string | null; document?: Document }
38
+
39
+ export interface EndValidationParams { success: boolean, document?: Document }
40
+
41
+ export type LogMachineInfoParams = {
42
+ enabled?: boolean
43
+ machine: string
44
+ step: number
45
+ stepName: string
46
+ }
@@ -0,0 +1,85 @@
1
+ import { Document } from "mongodb"
2
+ import { User } from "../../../auth/dtos";
3
+ import { Params, Role } from "../interface";
4
+ import { MachineContext, PrevParams, States, StepResult, ValidationStatus } from "./interface";
5
+ import { READ_MACHINE } from "./read";
6
+ import { WRITE_MACHINE } from "./write";
7
+
8
+ export class StateMachine {
9
+ private _context: MachineContext
10
+ private _validation: StepResult = {
11
+ status: null,
12
+ nextInitialStep: null
13
+ }
14
+ private _machines: States[]
15
+ private _currentStep: {
16
+ names: readonly string[]
17
+ states: States
18
+ validation: ValidationStatus,
19
+ completed?: boolean,
20
+ initialStep: string | null
21
+ }
22
+ constructor(role: Role, params: Params, user: User, enableLog?: boolean) {
23
+ this._context = { role, params, user, enableLog }
24
+ this._machines = params.type === "read" ? READ_MACHINE : WRITE_MACHINE
25
+ this._currentStep = {
26
+ names: [],
27
+ states: {},
28
+ validation: {
29
+ status: null
30
+ },
31
+ initialStep: null
32
+ }
33
+ }
34
+
35
+ async runValidation() {
36
+ for await (const machine of this._machines) {
37
+ this._currentStep = { names: Object.freeze(Object.keys(machine)) as readonly (keyof typeof machine)[], states: machine, validation: { status: null }, initialStep: null }
38
+ await this.runMachine(this._validation.nextInitialStep);
39
+
40
+ this._validation.nextInitialStep = this._currentStep.initialStep;
41
+ if (this._currentStep.validation.status !== null) {
42
+ this._validation.status = this._currentStep.validation.status;
43
+ this._validation.document = this._currentStep.validation.document;
44
+ break;
45
+ }
46
+ }
47
+ return this._validation;
48
+ }
49
+
50
+ private async runMachine(initialStep: string | null) {
51
+ const executeStep = async (step: keyof typeof this._currentStep.states, params?: PrevParams) => {
52
+ const currentStep = this._currentStep.states[step]
53
+ const next = (nextStep: keyof typeof this._currentStep.states, params?: PrevParams) =>
54
+ executeStep(nextStep, params)
55
+
56
+ await currentStep({
57
+ context: { ...this._context, prevParams: params },
58
+ next,
59
+ endValidation: this.endValidation.bind(this),
60
+ goToNextValidationStage: this.goToNextValidationStage.bind(this),
61
+ initialStep
62
+ })
63
+
64
+ if (this._currentStep.validation.status !== null || this._currentStep.completed !== undefined)
65
+ return { isValid: this._currentStep.validation.status, ...this._currentStep }
66
+ }
67
+ const nextStep = initialStep && this._currentStep.states[initialStep] ? initialStep : this._currentStep.names[0]
68
+ await executeStep(nextStep)
69
+ }
70
+
71
+ private endValidation({ success, document }: { success: boolean, document?: Document }) {
72
+ this._currentStep.validation.status = success
73
+ if (success) {
74
+ this._currentStep.validation.document = document || this._context.params.cursor
75
+ }
76
+ }
77
+
78
+
79
+ private goToNextValidationStage(initialStep: string | null = null) {
80
+ this._currentStep.completed = true
81
+ this._currentStep.initialStep = initialStep
82
+ }
83
+
84
+
85
+ }
@@ -0,0 +1,19 @@
1
+ import { States } from '../../interface'
2
+ import { logMachineInfo } from '../../utils'
3
+
4
+ export const STEP_A_STATES: States = {
5
+ checkSearchRequest: async ({ context, next, goToNextValidationStage }) => {
6
+ logMachineInfo({ enabled: context.enableLog, machine: "A", step: 1, stepName: "checkSearchRequest" })
7
+ if (context.params.type === 'search') {
8
+ return next('evaluateSearch')
9
+ }
10
+ return goToNextValidationStage()
11
+ },
12
+ evaluateSearch: async ({ context, endValidation }) => {
13
+ logMachineInfo({ enabled: context.enableLog, machine: "A", step: 2, stepName: "evaluateSearch" })
14
+ // NOTE -> we don't support search operations
15
+ return endValidation({ success: false })
16
+ }
17
+ }
18
+
19
+
@@ -0,0 +1,31 @@
1
+ import { evaluateDocumentFiltersFn } from '../../commonValidators'
2
+ import { States } from '../../interface'
3
+ import { logMachineInfo } from '../../utils'
4
+
5
+ export const STEP_B_STATES: States = {
6
+ checkDocumentsFilters: async ({ context, next, goToNextValidationStage }) => {
7
+ logMachineInfo({ enabled: context.enableLog, machine: "B", step: 1, stepName: "checkDocumentsFilters" })
8
+ const { role } = context
9
+ if (role.document_filters) {
10
+ return next('evaluateDocumentsFiltersRead')
11
+ }
12
+ return goToNextValidationStage()
13
+ },
14
+ evaluateDocumentsFiltersRead: async ({ context, next, goToNextValidationStage }) => {
15
+ logMachineInfo({ enabled: context.enableLog, machine: "B", step: 2, stepName: "evaluateDocumentsFiltersRead" })
16
+ const hasDocumentFiltersRead = await evaluateDocumentFiltersFn(context, "read")
17
+ if (!hasDocumentFiltersRead) return next('evaluateDocumentsFiltersWrite')
18
+ return goToNextValidationStage()
19
+ },
20
+ evaluateDocumentsFiltersWrite: async ({
21
+ context,
22
+ endValidation,
23
+ goToNextValidationStage
24
+ }) => {
25
+ logMachineInfo({ enabled: context.enableLog, machine: "B", step: 3, stepName: "evaluateDocumentsFiltersWrite" })
26
+ const check = await evaluateDocumentFiltersFn(context, "write")
27
+ return check ? goToNextValidationStage() : endValidation({ success: false })
28
+ }
29
+ }
30
+
31
+
@@ -0,0 +1,30 @@
1
+ import { checkFieldsPropertyExists, evaluateTopLevelPermissionsFn } from '../../commonValidators'
2
+ import { States } from '../../interface'
3
+ import { logMachineInfo } from '../../utils'
4
+
5
+
6
+ export const STEP_C_STATES: States = {
7
+ evaluateTopLevelRead: async ({ context, next, endValidation }) => {
8
+ logMachineInfo({ enabled: context.enableLog, machine: "C", step: 1, stepName: "evaluateTopLevelRead" })
9
+ const check = await evaluateTopLevelPermissionsFn(context, "read")
10
+ return check
11
+ ? endValidation({ success: true })
12
+ : next('evaluateTopLevelWrite', { check })
13
+ },
14
+ evaluateTopLevelWrite: async ({ context, next, endValidation }) => {
15
+ logMachineInfo({ enabled: context.enableLog, machine: "C", step: 2, stepName: "evaluateTopLevelWrite" })
16
+ const check = await evaluateTopLevelPermissionsFn(context, "write")
17
+ if (check) return endValidation({ success: true })
18
+ return context?.prevParams?.check === false
19
+ ? endValidation({ success: false })
20
+ : next('checkFieldsProperty')
21
+ },
22
+ checkFieldsProperty: async ({ context, goToNextValidationStage }) => {
23
+ logMachineInfo({ enabled: context.enableLog, machine: "C", step: 3, stepName: "checkFieldsProperty" })
24
+ const check = checkFieldsPropertyExists(context)
25
+ return goToNextValidationStage(
26
+ check ? 'checkIsValidFieldName' : 'checkAdditionalFields'
27
+ )
28
+ }
29
+ }
30
+
@@ -0,0 +1,20 @@
1
+ import { States } from '../../interface'
2
+ import { logMachineInfo } from '../../utils'
3
+ import {
4
+ checkAdditionalFieldsFn,
5
+ checkIsValidFieldNameFn,
6
+ } from './validators'
7
+
8
+ export const STEP_D_STATES: States = {
9
+ checkAdditionalFields: async ({ context, next, endValidation }) => {
10
+ logMachineInfo({ enabled: context.enableLog, machine: "D", step: 1, stepName: "checkAdditionalFields" })
11
+ const check = checkAdditionalFieldsFn(context)
12
+ return check ? next('checkIsValidFieldName') : endValidation({ success: false })
13
+ },
14
+ checkIsValidFieldName: async ({ context, endValidation }) => {
15
+ logMachineInfo({ enabled: context.enableLog, machine: "D", step: 2, stepName: "checkIsValidFieldName" })
16
+ const document = checkIsValidFieldNameFn(context)
17
+ return endValidation({ success: !!Object.keys(document).length, document })
18
+ },
19
+ }
20
+
@@ -0,0 +1,24 @@
1
+ import { MachineContext } from "../../interface"
2
+
3
+
4
+ export const checkAdditionalFieldsFn = ({ role }: MachineContext) => {
5
+ return !!Object.keys(role.additional_fields || {}).length
6
+ }
7
+
8
+ export const checkIsValidFieldNameFn = ({ role, params }: MachineContext) => {
9
+ const { cursor } = params
10
+
11
+ const { fields = {}, additional_fields = {} } = role
12
+ const rulesOnId = !!(fields["_id"] || additional_fields["_id"])
13
+ const filteredDocument = Object.entries(cursor).reduce((filteredDocument, [key, value]) => {
14
+ if (fields![key]) {
15
+ return (role.fields![key].read || role.fields![key].write) ? { ...filteredDocument, [key]: value } : filteredDocument
16
+ }
17
+ if (additional_fields[key]) {
18
+ return (additional_fields[key]?.read || additional_fields[key]?.write) ? { ...filteredDocument, [key]: value } : filteredDocument
19
+ }
20
+ return { ...filteredDocument, [key]: value }
21
+ }, {})
22
+
23
+ return (rulesOnId || cursor._id === undefined) ? filteredDocument : { ...filteredDocument, "_id": cursor._id }
24
+ }
@@ -0,0 +1,6 @@
1
+ import { STEP_A_STATES } from "./A";
2
+ import { STEP_B_STATES } from "./B";
3
+ import { STEP_C_STATES } from "./C";
4
+ import { STEP_D_STATES } from "./D";
5
+
6
+ export const READ_MACHINE = [STEP_A_STATES, STEP_B_STATES, STEP_C_STATES, STEP_D_STATES]
@@ -0,0 +1,54 @@
1
+ import { Document, OptionalId } from "mongodb"
2
+ import { User } from "../../../auth/dtos";
3
+ import { Filter } from "../../../features/rules/interface";
4
+ import { getValidRule } from "../../../services/mongodb-atlas/utils";
5
+ import { Role } from "../interface"
6
+ import { LogMachineInfoParams } from "./interface";
7
+
8
+ /**
9
+ * Determines the first applicable role for a given user and document.
10
+ *
11
+ * @param {OptionalId<Document> | null} document - The document to check against role conditions.
12
+ * @param {User} user - The user for whom the role is being determined.
13
+ * @param {Role[]} [roles=[]] - The list of available roles to evaluate.
14
+ *
15
+ * @returns {Role | null} - Returns the first role that matches the `apply_when` condition, or `null` if none match.
16
+ */
17
+ export const getWinningRole = (document: OptionalId<Document> | null, user: User, roles: Role[] = []): Role | null => {
18
+ if (!roles.length) return null
19
+ for (const role of roles) {
20
+ if (checkApplyWhen(role.apply_when, user, document)) {
21
+ return role;
22
+ }
23
+ }
24
+ return null
25
+ }
26
+
27
+ /**
28
+ * Checks if the `apply_when` condition is valid for the given user and document.
29
+ *
30
+ * @param {Role["apply_when"]} apply_when - The rule condition to evaluate.
31
+ * @param {User} user - The user for whom the condition is being checked.
32
+ * @param {WithId<Document> | null} document - The document to check against the condition.
33
+ *
34
+ * @returns {boolean} - Returns `true` if at least one valid rule is found, otherwise `false`.
35
+ */
36
+ export const checkApplyWhen = (apply_when: Role["apply_when"], user: User, document: OptionalId<Document> | null) => {
37
+ const validRule = getValidRule({ filters: [{ apply_when } as Filter], user, record: document })
38
+ return !!validRule.length
39
+ }
40
+
41
+ /**
42
+ * Logs machine step information if logging is enabled.
43
+ *
44
+ * @param {Object} params - The parameters for logging machine info.
45
+ * @param {boolean} params.enabled - Whether logging is enabled.
46
+ * @param {string} params.machine - The name of the machine.
47
+ * @param {number} params.step - The current step number.
48
+ * @param {string} params.stepName - The name of the current step.
49
+ *
50
+ * @returns {void}
51
+ */
52
+ export const logMachineInfo = ({ enabled, machine, step, stepName }: LogMachineInfoParams) => {
53
+ if (enabled) console.log(`MACHINE ${machine} -> STEP ${step}: ${stepName}`)
54
+ }