dmart 1.4.40.post8__py3-none-any.whl

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 (489) hide show
  1. dmart/__init__.py +7 -0
  2. dmart/alembic/README +1 -0
  3. dmart/alembic/__init__.py +0 -0
  4. dmart/alembic/env.py +91 -0
  5. dmart/alembic/notes.txt +11 -0
  6. dmart/alembic/script.py.mako +28 -0
  7. dmart/alembic/scripts/__init__.py +0 -0
  8. dmart/alembic/scripts/calculate_checksums.py +77 -0
  9. dmart/alembic/scripts/migration_f7a4949eed19.py +28 -0
  10. dmart/alembic/versions/0f3d2b1a7c21_add_authz_materialized_views.py +87 -0
  11. dmart/alembic/versions/10d2041b94d4_last_checksum_history.py +62 -0
  12. dmart/alembic/versions/1cf4e1ee3cb8_ext_permission_with_filter_fields_values.py +33 -0
  13. dmart/alembic/versions/26bfe19b49d4_rm_failedloginattempts.py +42 -0
  14. dmart/alembic/versions/3c8bca2219cc_add_otp_table.py +38 -0
  15. dmart/alembic/versions/6675fd9dfe42_remove_unique_from_sessions_table.py +36 -0
  16. dmart/alembic/versions/71bc1df82e6a_adding_user_last_login_at.py +43 -0
  17. dmart/alembic/versions/74288ccbd3b5_initial.py +264 -0
  18. dmart/alembic/versions/7520a89a8467_rm_activesession_table.py +39 -0
  19. dmart/alembic/versions/848b623755a4_make_created_nd_updated_at_required.py +138 -0
  20. dmart/alembic/versions/8640dcbebf85_add_notes_to_users.py +32 -0
  21. dmart/alembic/versions/91c94250232a_adding_fk_on_owner_shortname.py +104 -0
  22. dmart/alembic/versions/98ecd6f56f9a_ext_meta_with_owner_group_shortname.py +66 -0
  23. dmart/alembic/versions/9aae9138c4ef_indexing_created_at_updated_at.py +80 -0
  24. dmart/alembic/versions/__init__.py +0 -0
  25. dmart/alembic/versions/b53f916b3f6d_json_to_jsonb.py +492 -0
  26. dmart/alembic/versions/eb5f1ec65156_adding_user_locked_to_device.py +36 -0
  27. dmart/alembic/versions/f7a4949eed19_adding_query_policies_to_meta.py +60 -0
  28. dmart/alembic.ini +117 -0
  29. dmart/api/__init__.py +0 -0
  30. dmart/api/info/__init__.py +0 -0
  31. dmart/api/info/router.py +109 -0
  32. dmart/api/managed/__init__.py +0 -0
  33. dmart/api/managed/router.py +1541 -0
  34. dmart/api/managed/utils.py +1879 -0
  35. dmart/api/public/__init__.py +0 -0
  36. dmart/api/public/router.py +758 -0
  37. dmart/api/qr/__init__.py +0 -0
  38. dmart/api/qr/router.py +108 -0
  39. dmart/api/user/__init__.py +0 -0
  40. dmart/api/user/model/__init__.py +0 -0
  41. dmart/api/user/model/errors.py +14 -0
  42. dmart/api/user/model/requests.py +165 -0
  43. dmart/api/user/model/responses.py +11 -0
  44. dmart/api/user/router.py +1413 -0
  45. dmart/api/user/service.py +270 -0
  46. dmart/bundler.py +52 -0
  47. dmart/cli.py +1133 -0
  48. dmart/config/__init__.py +0 -0
  49. dmart/config/channels.json +11 -0
  50. dmart/config/notification.json +17 -0
  51. dmart/config.env.sample +27 -0
  52. dmart/config.ini.sample +7 -0
  53. dmart/conftest.py +13 -0
  54. dmart/curl.sh +196 -0
  55. dmart/cxb/__init__.py +0 -0
  56. dmart/cxb/assets/@codemirror-Rn7_6DkE.js +10 -0
  57. dmart/cxb/assets/@edraj-CS4NwVbD.js +1 -0
  58. dmart/cxb/assets/@floating-ui-BwwcF-xh.js +1 -0
  59. dmart/cxb/assets/@formatjs-yKEsAtjs.js +1 -0
  60. dmart/cxb/assets/@fortawesome-DRW1UCdr.js +9 -0
  61. dmart/cxb/assets/@jsonquerylang-laKNoFFq.js +12 -0
  62. dmart/cxb/assets/@lezer-za4Q-8Ew.js +1 -0
  63. dmart/cxb/assets/@marijn-DXwl3gUT.js +1 -0
  64. dmart/cxb/assets/@popperjs-l0sNRNKZ.js +1 -0
  65. dmart/cxb/assets/@replit--ERk53eB.js +1 -0
  66. dmart/cxb/assets/@roxi-CGMFK4i8.js +6 -0
  67. dmart/cxb/assets/@typewriter-cCzskkIv.js +17 -0
  68. dmart/cxb/assets/@zerodevx-BlBZjKxu.js +1 -0
  69. dmart/cxb/assets/@zerodevx-CVEpe6WZ.css +1 -0
  70. dmart/cxb/assets/BreadCrumbLite-DAhOx38v.js +1 -0
  71. dmart/cxb/assets/EntryRenderer-CCqV8Rkg.js +32 -0
  72. dmart/cxb/assets/EntryRenderer-DXytdFp9.css +1 -0
  73. dmart/cxb/assets/ListView-BQelo7vZ.js +16 -0
  74. dmart/cxb/assets/ListView-U8of-_c-.css +1 -0
  75. dmart/cxb/assets/Prism--hMplq-p.js +3 -0
  76. dmart/cxb/assets/Prism-Uh6uStUw.css +1 -0
  77. dmart/cxb/assets/Table2Cols-BsbwicQm.js +1 -0
  78. dmart/cxb/assets/_..-BvT6vdHa.css +1 -0
  79. dmart/cxb/assets/_...404_-fuLH_rX9.js +2 -0
  80. dmart/cxb/assets/_...fallback_-Ba_NLmAE.js +1 -0
  81. dmart/cxb/assets/_module-3HrtKAWo.js +3 -0
  82. dmart/cxb/assets/_module-DFKFq0AM.js +4 -0
  83. dmart/cxb/assets/_module-Dgq0ZVtz.js +1 -0
  84. dmart/cxb/assets/ajv-Cpj98o6Y.js +1 -0
  85. dmart/cxb/assets/axios-CG2WSiiR.js +6 -0
  86. dmart/cxb/assets/clsx-B-dksMZM.js +1 -0
  87. dmart/cxb/assets/codemirror-wrapped-line-indent-DPhKvljI.js +1 -0
  88. dmart/cxb/assets/compare-C3AjiGFR.js +1 -0
  89. dmart/cxb/assets/compute-scroll-into-view-Bl8rNFhg.js +1 -0
  90. dmart/cxb/assets/consolite-DlCuI0F9.js +1 -0
  91. dmart/cxb/assets/crelt-C8TCjufn.js +1 -0
  92. dmart/cxb/assets/date-fns-l0sNRNKZ.js +1 -0
  93. dmart/cxb/assets/deepmerge-rn4rBaHU.js +1 -0
  94. dmart/cxb/assets/dmart_services-AL6-IdDE.js +1 -0
  95. dmart/cxb/assets/downloadFile-D08i0YDh.js +1 -0
  96. dmart/cxb/assets/easy-signal-BiPFIK3O.js +1 -0
  97. dmart/cxb/assets/esm-env-rsSWfq8L.js +1 -0
  98. dmart/cxb/assets/export-OF_rTiXu.js +1 -0
  99. dmart/cxb/assets/fast-deep-equal-l0sNRNKZ.js +1 -0
  100. dmart/cxb/assets/fast-diff-C-IidNf4.js +1 -0
  101. dmart/cxb/assets/fast-uri-l0sNRNKZ.js +1 -0
  102. dmart/cxb/assets/flowbite-svelte-BLvjb-sa.js +1 -0
  103. dmart/cxb/assets/flowbite-svelte-CD54FDqW.css +1 -0
  104. dmart/cxb/assets/flowbite-svelte-icons-BI8GVhw_.js +1 -0
  105. dmart/cxb/assets/github-slugger-CQ4oX9Ud.js +1 -0
  106. dmart/cxb/assets/global-igKv-1g9.js +1 -0
  107. dmart/cxb/assets/hookar-BMRD9G9H.js +1 -0
  108. dmart/cxb/assets/immutable-json-patch-DtRO2E_S.js +1 -0
  109. dmart/cxb/assets/import-1vE3gBat.js +1 -0
  110. dmart/cxb/assets/index-B-eTh-ZX.js +1 -0
  111. dmart/cxb/assets/index-BSsK-X71.js +1 -0
  112. dmart/cxb/assets/index-BVyxzKtH.js +1 -0
  113. dmart/cxb/assets/index-BdeNM69f.js +1 -0
  114. dmart/cxb/assets/index-CC-A1ipE.js +1 -0
  115. dmart/cxb/assets/index-CQohGiYB.js +1 -0
  116. dmart/cxb/assets/index-ChjnkpdZ.js +4 -0
  117. dmart/cxb/assets/index-DLP7csA4.js +1 -0
  118. dmart/cxb/assets/index-DTfhnhwd.js +1 -0
  119. dmart/cxb/assets/index-DdXRK7n9.js +2 -0
  120. dmart/cxb/assets/index-DtiCmB4o.js +1 -0
  121. dmart/cxb/assets/index-NBrXBlLA.css +2 -0
  122. dmart/cxb/assets/index-X1uNehO7.js +1 -0
  123. dmart/cxb/assets/index-nrQW6Nrr.js +1 -0
  124. dmart/cxb/assets/info-B986lRiM.js +1 -0
  125. dmart/cxb/assets/intl-messageformat-Dc5UU-HB.js +3 -0
  126. dmart/cxb/assets/jmespath-l0sNRNKZ.js +1 -0
  127. dmart/cxb/assets/json-schema-traverse-l0sNRNKZ.js +1 -0
  128. dmart/cxb/assets/json-source-map-DRgZidqy.js +5 -0
  129. dmart/cxb/assets/jsonpath-plus-l0sNRNKZ.js +1 -0
  130. dmart/cxb/assets/jsonrepair-B30Dx381.js +8 -0
  131. dmart/cxb/assets/lodash-es-DZVAA2ox.js +1 -0
  132. dmart/cxb/assets/marked-DKjyhwJX.js +56 -0
  133. dmart/cxb/assets/marked-gfm-heading-id-U5zO829x.js +2 -0
  134. dmart/cxb/assets/marked-mangle-CDMeiHC6.js +1 -0
  135. dmart/cxb/assets/memoize-one-BdPwpGay.js +1 -0
  136. dmart/cxb/assets/natural-compare-lite-Bg2Xcf-o.js +7 -0
  137. dmart/cxb/assets/pagination-svelte-D5CyoiE_.js +13 -0
  138. dmart/cxb/assets/pagination-svelte-v10nAbbM.css +1 -0
  139. dmart/cxb/assets/plantuml-encoder-C47mzt9T.js +1 -0
  140. dmart/cxb/assets/prismjs-DTUiLGJu.js +9 -0
  141. dmart/cxb/assets/profile-BUf-tKMe.js +1 -0
  142. dmart/cxb/assets/query-CNmXTsgf.js +1 -0
  143. dmart/cxb/assets/queryHelpers-C9iBWwqe.js +1 -0
  144. dmart/cxb/assets/scroll-into-view-if-needed-KR58zyjF.js +1 -0
  145. dmart/cxb/assets/spaces-0oyGvpii.js +1 -0
  146. dmart/cxb/assets/style-mod-Bs6eFhZE.js +3 -0
  147. dmart/cxb/assets/svelte-B2XmcTi_.js +4 -0
  148. dmart/cxb/assets/svelte-awesome-COLlx0DN.css +1 -0
  149. dmart/cxb/assets/svelte-awesome-DhnMA6Q_.js +1 -0
  150. dmart/cxb/assets/svelte-datatables-net-CY7LBj6I.js +1 -0
  151. dmart/cxb/assets/svelte-floating-ui-BlS3sOAQ.js +1 -0
  152. dmart/cxb/assets/svelte-i18n-CT2KkQaN.js +3 -0
  153. dmart/cxb/assets/svelte-jsoneditor-BzfX6Usi.css +1 -0
  154. dmart/cxb/assets/svelte-jsoneditor-CUGSvWId.js +25 -0
  155. dmart/cxb/assets/svelte-select-CegQKzqH.css +1 -0
  156. dmart/cxb/assets/svelte-select-CjHAt_85.js +6 -0
  157. dmart/cxb/assets/tailwind-merge-CJvxXMcu.js +1 -0
  158. dmart/cxb/assets/tailwind-variants-Cj20BoQ3.js +1 -0
  159. dmart/cxb/assets/toast-B9WDyfyI.js +1 -0
  160. dmart/cxb/assets/tslib-pJfR_DrR.js +1 -0
  161. dmart/cxb/assets/typewriter-editor-DkTVIJdm.js +25 -0
  162. dmart/cxb/assets/user-DeK_NB5v.js +1 -0
  163. dmart/cxb/assets/vanilla-picker-l5rcX3cq.js +8 -0
  164. dmart/cxb/assets/w3c-keyname-Vcq4gwWv.js +1 -0
  165. dmart/cxb/config.json +11 -0
  166. dmart/cxb/config.sample.json +11 -0
  167. dmart/cxb/favicon.ico +0 -0
  168. dmart/cxb/favicon.png +0 -0
  169. dmart/cxb/index.html +28 -0
  170. dmart/data_adapters/__init__.py +0 -0
  171. dmart/data_adapters/adapter.py +16 -0
  172. dmart/data_adapters/base_data_adapter.py +467 -0
  173. dmart/data_adapters/file/__init__.py +0 -0
  174. dmart/data_adapters/file/adapter.py +2043 -0
  175. dmart/data_adapters/file/adapter_helpers.py +1013 -0
  176. dmart/data_adapters/file/archive.py +150 -0
  177. dmart/data_adapters/file/create_index.py +331 -0
  178. dmart/data_adapters/file/create_users_folders.py +52 -0
  179. dmart/data_adapters/file/custom_validations.py +68 -0
  180. dmart/data_adapters/file/drop_index.py +40 -0
  181. dmart/data_adapters/file/health_check.py +560 -0
  182. dmart/data_adapters/file/redis_services.py +1110 -0
  183. dmart/data_adapters/helpers.py +27 -0
  184. dmart/data_adapters/sql/__init__.py +0 -0
  185. dmart/data_adapters/sql/adapter.py +3218 -0
  186. dmart/data_adapters/sql/adapter_helpers.py +491 -0
  187. dmart/data_adapters/sql/create_tables.py +451 -0
  188. dmart/data_adapters/sql/create_users_folders.py +53 -0
  189. dmart/data_adapters/sql/db_to_json_migration.py +485 -0
  190. dmart/data_adapters/sql/health_check_sql.py +232 -0
  191. dmart/data_adapters/sql/json_to_db_migration.py +454 -0
  192. dmart/data_adapters/sql/update_query_policies.py +101 -0
  193. dmart/data_generator.py +81 -0
  194. dmart/dmart.py +761 -0
  195. dmart/get_settings.py +7 -0
  196. dmart/hypercorn_config.toml +3 -0
  197. dmart/info.json +1 -0
  198. dmart/languages/__init__.py +0 -0
  199. dmart/languages/arabic.json +15 -0
  200. dmart/languages/english.json +16 -0
  201. dmart/languages/kurdish.json +14 -0
  202. dmart/languages/loader.py +12 -0
  203. dmart/login_creds.sh +7 -0
  204. dmart/login_creds.sh.sample +7 -0
  205. dmart/main.py +563 -0
  206. dmart/manifest.sh +12 -0
  207. dmart/migrate.py +24 -0
  208. dmart/models/__init__.py +0 -0
  209. dmart/models/api.py +203 -0
  210. dmart/models/core.py +597 -0
  211. dmart/models/enums.py +255 -0
  212. dmart/password_gen.py +8 -0
  213. dmart/plugins/__init__.py +0 -0
  214. dmart/plugins/action_log/__init__.py +0 -0
  215. dmart/plugins/action_log/config.json +13 -0
  216. dmart/plugins/action_log/plugin.py +121 -0
  217. dmart/plugins/admin_notification_sender/__init__.py +0 -0
  218. dmart/plugins/admin_notification_sender/config.json +13 -0
  219. dmart/plugins/admin_notification_sender/plugin.py +124 -0
  220. dmart/plugins/ldap_manager/__init__.py +0 -0
  221. dmart/plugins/ldap_manager/config.json +12 -0
  222. dmart/plugins/ldap_manager/dmart.schema +146 -0
  223. dmart/plugins/ldap_manager/plugin.py +100 -0
  224. dmart/plugins/ldap_manager/slapd.conf +53 -0
  225. dmart/plugins/local_notification/__init__.py +0 -0
  226. dmart/plugins/local_notification/config.json +13 -0
  227. dmart/plugins/local_notification/plugin.py +123 -0
  228. dmart/plugins/realtime_updates_notifier/__init__.py +0 -0
  229. dmart/plugins/realtime_updates_notifier/config.json +12 -0
  230. dmart/plugins/realtime_updates_notifier/plugin.py +58 -0
  231. dmart/plugins/redis_db_update/__init__.py +0 -0
  232. dmart/plugins/redis_db_update/config.json +13 -0
  233. dmart/plugins/redis_db_update/plugin.py +188 -0
  234. dmart/plugins/resource_folders_creation/__init__.py +0 -0
  235. dmart/plugins/resource_folders_creation/config.json +12 -0
  236. dmart/plugins/resource_folders_creation/plugin.py +81 -0
  237. dmart/plugins/system_notification_sender/__init__.py +0 -0
  238. dmart/plugins/system_notification_sender/config.json +13 -0
  239. dmart/plugins/system_notification_sender/plugin.py +188 -0
  240. dmart/plugins/update_access_controls/__init__.py +0 -0
  241. dmart/plugins/update_access_controls/config.json +12 -0
  242. dmart/plugins/update_access_controls/plugin.py +9 -0
  243. dmart/publish.sh +57 -0
  244. dmart/pylint.sh +16 -0
  245. dmart/pyrightconfig.json +7 -0
  246. dmart/redis_connections.sh +13 -0
  247. dmart/reload.sh +56 -0
  248. dmart/run.sh +3 -0
  249. dmart/run_notification_campaign.py +85 -0
  250. dmart/sample/spaces/applications/.dm/meta.space.json +30 -0
  251. dmart/sample/spaces/applications/api/.dm/meta.folder.json +1 -0
  252. dmart/sample/spaces/applications/api/.dm/query_all_applications/meta.content.json +1 -0
  253. dmart/sample/spaces/applications/api/.dm/test_by_saad/attachments.media/meta.warframe.json +1 -0
  254. dmart/sample/spaces/applications/api/.dm/test_by_saad/attachments.media/warframe.png +0 -0
  255. dmart/sample/spaces/applications/api/.dm/test_by_saad/meta.content.json +1 -0
  256. dmart/sample/spaces/applications/api/.dm/user_profile/meta.content.json +1 -0
  257. dmart/sample/spaces/applications/api/applications/.dm/create_log/meta.content.json +1 -0
  258. dmart/sample/spaces/applications/api/applications/.dm/create_public_logs/meta.content.json +1 -0
  259. dmart/sample/spaces/applications/api/applications/.dm/meta.folder.json +1 -0
  260. dmart/sample/spaces/applications/api/applications/.dm/query_all_translated_data/meta.content.json +1 -0
  261. dmart/sample/spaces/applications/api/applications/.dm/query_logs/meta.content.json +1 -0
  262. dmart/sample/spaces/applications/api/applications/.dm/query_translated_enums/meta.content.json +1 -0
  263. dmart/sample/spaces/applications/api/applications/.dm/query_translated_others/meta.content.json +1 -0
  264. dmart/sample/spaces/applications/api/applications/.dm/query_translated_resolution/meta.content.json +1 -0
  265. dmart/sample/spaces/applications/api/applications/create_log.json +1 -0
  266. dmart/sample/spaces/applications/api/applications/create_public_logs.json +1 -0
  267. dmart/sample/spaces/applications/api/applications/query_all_translated_data.json +1 -0
  268. dmart/sample/spaces/applications/api/applications/query_logs.json +1 -0
  269. dmart/sample/spaces/applications/api/applications/query_translated_enums.json +1 -0
  270. dmart/sample/spaces/applications/api/applications/query_translated_others.json +1 -0
  271. dmart/sample/spaces/applications/api/applications/query_translated_resolution.json +1 -0
  272. dmart/sample/spaces/applications/api/applications.json +1 -0
  273. dmart/sample/spaces/applications/api/management/.dm/create_subaccount/meta.content.json +1 -0
  274. dmart/sample/spaces/applications/api/management/.dm/meta.folder.json +1 -0
  275. dmart/sample/spaces/applications/api/management/.dm/update_password/meta.content.json +1 -0
  276. dmart/sample/spaces/applications/api/management/create_subaccount.json +53 -0
  277. dmart/sample/spaces/applications/api/management/update_password.json +1 -0
  278. dmart/sample/spaces/applications/api/management.json +1 -0
  279. dmart/sample/spaces/applications/api/query_all_applications.json +15 -0
  280. dmart/sample/spaces/applications/api/test_by_saad.json +1 -0
  281. dmart/sample/spaces/applications/api/user/.dm/meta.folder.json +1 -0
  282. dmart/sample/spaces/applications/api/user/.dm/test_by_saad/meta.content.json +1 -0
  283. dmart/sample/spaces/applications/api/user/.dm/user_profile/meta.content.json +1 -0
  284. dmart/sample/spaces/applications/api/user/test_by_saad.json +1 -0
  285. dmart/sample/spaces/applications/api/user/user_profile.json +1 -0
  286. dmart/sample/spaces/applications/api/user_profile.json +1 -0
  287. dmart/sample/spaces/applications/api.json +1 -0
  288. dmart/sample/spaces/applications/collections/.dm/meta.folder.json +19 -0
  289. dmart/sample/spaces/applications/collections.json +1 -0
  290. dmart/sample/spaces/applications/configurations/.dm/meta.folder.json +1 -0
  291. dmart/sample/spaces/applications/configurations/time_out.json +1 -0
  292. dmart/sample/spaces/applications/configurations.json +19 -0
  293. dmart/sample/spaces/applications/errors.json +1 -0
  294. dmart/sample/spaces/applications/logs/.dm/meta.folder.json +1 -0
  295. dmart/sample/spaces/applications/logs.json +1 -0
  296. dmart/sample/spaces/applications/queries/.dm/meta.folder.json +1 -0
  297. dmart/sample/spaces/applications/queries/.dm/order/meta.content.json +1 -0
  298. dmart/sample/spaces/applications/queries/order.json +1 -0
  299. dmart/sample/spaces/applications/queries.json +1 -0
  300. dmart/sample/spaces/applications/schema/.dm/api/meta.schema.json +1 -0
  301. dmart/sample/spaces/applications/schema/.dm/configuration/meta.schema.json +1 -0
  302. dmart/sample/spaces/applications/schema/.dm/error/meta.schema.json +1 -0
  303. dmart/sample/spaces/applications/schema/.dm/log/meta.schema.json +1 -0
  304. dmart/sample/spaces/applications/schema/.dm/meta.folder.json +1 -0
  305. dmart/sample/spaces/applications/schema/.dm/query/meta.schema.json +16 -0
  306. dmart/sample/spaces/applications/schema/.dm/translation/meta.schema.json +1 -0
  307. dmart/sample/spaces/applications/schema/api.json +28 -0
  308. dmart/sample/spaces/applications/schema/configuration.json +1 -0
  309. dmart/sample/spaces/applications/schema/error.json +43 -0
  310. dmart/sample/spaces/applications/schema/log.json +1 -0
  311. dmart/sample/spaces/applications/schema/query.json +118 -0
  312. dmart/sample/spaces/applications/schema/translation.json +26 -0
  313. dmart/sample/spaces/applications/schema.json +1 -0
  314. dmart/sample/spaces/applications/translations/.dm/meta.folder.json +1 -0
  315. dmart/sample/spaces/applications/translations.json +1 -0
  316. dmart/sample/spaces/archive/.dm/meta.space.json +27 -0
  317. dmart/sample/spaces/custom_plugins/dummy/__pycache__/plugin.cpython-314.pyc +0 -0
  318. dmart/sample/spaces/custom_plugins/dummy/config.json +28 -0
  319. dmart/sample/spaces/custom_plugins/dummy/plugin.py +6 -0
  320. dmart/sample/spaces/custom_plugins/missed_entry/config.json +12 -0
  321. dmart/sample/spaces/custom_plugins/missed_entry/plugin.py +119 -0
  322. dmart/sample/spaces/custom_plugins/own_changed_notification/__pycache__/plugin.cpython-314.pyc +0 -0
  323. dmart/sample/spaces/custom_plugins/own_changed_notification/config.json +12 -0
  324. dmart/sample/spaces/custom_plugins/own_changed_notification/plugin.py +65 -0
  325. dmart/sample/spaces/custom_plugins/reports_stats/config.json +14 -0
  326. dmart/sample/spaces/custom_plugins/reports_stats/plugin.py +82 -0
  327. dmart/sample/spaces/custom_plugins/system_notification_sender/config.json +22 -0
  328. dmart/sample/spaces/custom_plugins/system_notification_sender/notification.py +268 -0
  329. dmart/sample/spaces/custom_plugins/system_notification_sender/plugin.py +98 -0
  330. dmart/sample/spaces/management/.dm/events.jsonl +32 -0
  331. dmart/sample/spaces/management/.dm/meta.space.json +48 -0
  332. dmart/sample/spaces/management/.dm/notifications/attachments.view.json/admin.json +36 -0
  333. dmart/sample/spaces/management/.dm/notifications/attachments.view.json/meta.admin.json +1 -0
  334. dmart/sample/spaces/management/.dm/notifications/attachments.view.json/meta.system.json +1 -0
  335. dmart/sample/spaces/management/.dm/notifications/attachments.view.json/system.json +32 -0
  336. dmart/sample/spaces/management/collections/.dm/meta.folder.json +1 -0
  337. dmart/sample/spaces/management/collections.json +1 -0
  338. dmart/sample/spaces/management/groups/.dm/meta.folder.json +1 -0
  339. dmart/sample/spaces/management/groups.json +1 -0
  340. dmart/sample/spaces/management/health_check/.dm/meta.folder.json +1 -0
  341. dmart/sample/spaces/management/health_check.json +1 -0
  342. dmart/sample/spaces/management/notifications/.dm/meta.folder.json +1 -0
  343. dmart/sample/spaces/management/notifications/admin/.dm/meta.folder.json +9 -0
  344. dmart/sample/spaces/management/notifications/system/.dm/meta.folder.json +9 -0
  345. dmart/sample/spaces/management/notifications.json +1 -0
  346. dmart/sample/spaces/management/permissions/.dm/access_applications/meta.permission.json +31 -0
  347. dmart/sample/spaces/management/permissions/.dm/access_applications_world/meta.permission.json +31 -0
  348. dmart/sample/spaces/management/permissions/.dm/access_messages/meta.permission.json +23 -0
  349. dmart/sample/spaces/management/permissions/.dm/access_personal/meta.permission.json +40 -0
  350. dmart/sample/spaces/management/permissions/.dm/access_protected/meta.permission.json +33 -0
  351. dmart/sample/spaces/management/permissions/.dm/access_public/meta.permission.json +24 -0
  352. dmart/sample/spaces/management/permissions/.dm/browse_all_folders/meta.permission.json +23 -0
  353. dmart/sample/spaces/management/permissions/.dm/create_log/meta.permission.json +24 -0
  354. dmart/sample/spaces/management/permissions/.dm/interviewer/meta.permission.json +1 -0
  355. dmart/sample/spaces/management/permissions/.dm/manage_applications/meta.permission.json +1 -0
  356. dmart/sample/spaces/management/permissions/.dm/manage_debug/meta.permission.json +25 -0
  357. dmart/sample/spaces/management/permissions/.dm/manage_spaces/meta.permission.json +24 -0
  358. dmart/sample/spaces/management/permissions/.dm/meta.folder.json +1 -0
  359. dmart/sample/spaces/management/permissions/.dm/rules_management_default/meta.permission.json +32 -0
  360. dmart/sample/spaces/management/permissions/.dm/super_manager/meta.permission.json +52 -0
  361. dmart/sample/spaces/management/permissions/.dm/view_activity_log/meta.permission.json +26 -0
  362. dmart/sample/spaces/management/permissions/.dm/view_collections/meta.permission.json +29 -0
  363. dmart/sample/spaces/management/permissions/.dm/view_logs/meta.permission.json +30 -0
  364. dmart/sample/spaces/management/permissions/.dm/view_roles/meta.permission.json +29 -0
  365. dmart/sample/spaces/management/permissions/.dm/view_users/meta.permission.json +25 -0
  366. dmart/sample/spaces/management/permissions/.dm/view_world/meta.permission.json +31 -0
  367. dmart/sample/spaces/management/permissions/.dm/world/meta.permission.json +35 -0
  368. dmart/sample/spaces/management/permissions.json +1 -0
  369. dmart/sample/spaces/management/requests.json +1 -0
  370. dmart/sample/spaces/management/roles/.dm/dummy/meta.role.json +12 -0
  371. dmart/sample/spaces/management/roles/.dm/logged_in/meta.role.json +18 -0
  372. dmart/sample/spaces/management/roles/.dm/manager/meta.role.json +13 -0
  373. dmart/sample/spaces/management/roles/.dm/meta.folder.json +1 -0
  374. dmart/sample/spaces/management/roles/.dm/moderator/meta.role.json +13 -0
  375. dmart/sample/spaces/management/roles/.dm/super_admin/meta.role.json +14 -0
  376. dmart/sample/spaces/management/roles/.dm/test_role/meta.role.json +13 -0
  377. dmart/sample/spaces/management/roles/.dm/world/meta.role.json +15 -0
  378. dmart/sample/spaces/management/roles.json +1 -0
  379. dmart/sample/spaces/management/schema/.dm/admin_notification_request/attachments.media/meta.ui_schema.json +10 -0
  380. dmart/sample/spaces/management/schema/.dm/admin_notification_request/attachments.media/ui_schema.json +32 -0
  381. dmart/sample/spaces/management/schema/.dm/admin_notification_request/meta.schema.json +1 -0
  382. dmart/sample/spaces/management/schema/.dm/api/meta.schema.json +1 -0
  383. dmart/sample/spaces/management/schema/.dm/folder_rendering/meta.schema.json +1 -0
  384. dmart/sample/spaces/management/schema/.dm/health_check/meta.schema.json +17 -0
  385. dmart/sample/spaces/management/schema/.dm/meta.folder.json +1 -0
  386. dmart/sample/spaces/management/schema/.dm/meta_schema/meta.schema.json +1 -0
  387. dmart/sample/spaces/management/schema/.dm/metafile/meta.schema.json +14 -0
  388. dmart/sample/spaces/management/schema/.dm/notification/meta.schema.json +1 -0
  389. dmart/sample/spaces/management/schema/.dm/system_notification_request/attachments.media/meta.ui_schema.json +10 -0
  390. dmart/sample/spaces/management/schema/.dm/system_notification_request/attachments.media/ui_schema.json +32 -0
  391. dmart/sample/spaces/management/schema/.dm/system_notification_request/meta.schema.json +1 -0
  392. dmart/sample/spaces/management/schema/.dm/view/meta.schema.json +1 -0
  393. dmart/sample/spaces/management/schema/.dm/workflow/meta.schema.json +1 -0
  394. dmart/sample/spaces/management/schema/admin_notification_request.json +89 -0
  395. dmart/sample/spaces/management/schema/api.json +1 -0
  396. dmart/sample/spaces/management/schema/folder_rendering.json +238 -0
  397. dmart/sample/spaces/management/schema/health_check.json +8 -0
  398. dmart/sample/spaces/management/schema/meta_schema.json +74 -0
  399. dmart/sample/spaces/management/schema/metafile.json +153 -0
  400. dmart/sample/spaces/management/schema/notification.json +28 -0
  401. dmart/sample/spaces/management/schema/system_notification_request.json +57 -0
  402. dmart/sample/spaces/management/schema/view.json +23 -0
  403. dmart/sample/spaces/management/schema/workflow.json +87 -0
  404. dmart/sample/spaces/management/schema.json +1 -0
  405. dmart/sample/spaces/management/users/.dm/alibaba/meta.user.json +23 -0
  406. dmart/sample/spaces/management/users/.dm/anonymous/meta.user.json +18 -0
  407. dmart/sample/spaces/management/users/.dm/dmart/meta.user.json +26 -0
  408. dmart/sample/spaces/management/users/.dm/meta.folder.json +14 -0
  409. dmart/sample/spaces/management/workflows/.dm/channel/meta.content.json +1 -0
  410. dmart/sample/spaces/management/workflows/.dm/meta.folder.json +1 -0
  411. dmart/sample/spaces/management/workflows/channel.json +148 -0
  412. dmart/sample/spaces/management/workflows.json +1 -0
  413. dmart/sample/spaces/maqola/.dm/meta.space.json +33 -0
  414. dmart/sample/spaces/personal/.dm/meta.space.json +24 -0
  415. dmart/sample/spaces/personal/people/.dm/meta.folder.json +1 -0
  416. dmart/sample/spaces/personal/people/dmart/.dm/meta.folder.json +1 -0
  417. dmart/sample/spaces/personal/people/dmart/messages/.dm/0b5f7e7f/meta.content.json +1 -0
  418. dmart/sample/spaces/personal/people/dmart/messages/.dm/meta.folder.json +1 -0
  419. dmart/sample/spaces/personal/people/dmart/messages/.dm/mytest/meta.content.json +1 -0
  420. dmart/sample/spaces/personal/people/dmart/messages/0b5f7e7f.json +1 -0
  421. dmart/sample/spaces/personal/people/dmart/messages/mytest.json +1 -0
  422. dmart/sample/spaces/personal/people/dmart/notifications/.dm/meta.folder.json +1 -0
  423. dmart/sample/spaces/personal/people/dmart/private/.dm/inner/meta.content.json +1 -0
  424. dmart/sample/spaces/personal/people/dmart/private/.dm/meta.folder.json +1 -0
  425. dmart/sample/spaces/personal/people/dmart/private/inner.json +1 -0
  426. dmart/sample/spaces/personal/people/dmart/protected/.dm/avatar/meta.content.json +1 -0
  427. dmart/sample/spaces/personal/people/dmart/protected/.dm/meta.folder.json +1 -0
  428. dmart/sample/spaces/personal/people/dmart/protected/avatar.png +0 -0
  429. dmart/sample/spaces/personal/people/dmart/public/.dm/meta.folder.json +1 -0
  430. dmart/sample/test/.gitignore +2 -0
  431. dmart/sample/test/createcontent.json +9 -0
  432. dmart/sample/test/createmedia.json +9 -0
  433. dmart/sample/test/createmedia_entry.json +6 -0
  434. dmart/sample/test/createschema.json +8 -0
  435. dmart/sample/test/createschemawork.json +11 -0
  436. dmart/sample/test/createticket.json +13 -0
  437. dmart/sample/test/data.json +4 -0
  438. dmart/sample/test/deletecontent.json +12 -0
  439. dmart/sample/test/logo.jpeg +0 -0
  440. dmart/sample/test/my.jpg +0 -0
  441. dmart/sample/test/myticket.json +23 -0
  442. dmart/sample/test/resources.csv +12 -0
  443. dmart/sample/test/schema.json +16 -0
  444. dmart/sample/test/temp.json +1 -0
  445. dmart/sample/test/test.dmart +45 -0
  446. dmart/sample/test/ticket_schema.json +23 -0
  447. dmart/sample/test/ticket_workflow.json +85 -0
  448. dmart/sample/test/ticketbody.json +4 -0
  449. dmart/sample/test/ticketcontent.json +14 -0
  450. dmart/sample/test/updatecontent.json +20 -0
  451. dmart/sample/test/workflow_schema.json +68 -0
  452. dmart/scheduled_notification_handler.py +121 -0
  453. dmart/schema_migration.py +208 -0
  454. dmart/schema_modulate.py +192 -0
  455. dmart/set_admin_passwd.py +75 -0
  456. dmart/sync.py +202 -0
  457. dmart/test_utils.py +34 -0
  458. dmart/utils/__init__.py +0 -0
  459. dmart/utils/access_control.py +306 -0
  460. dmart/utils/async_request.py +8 -0
  461. dmart/utils/exporter.py +309 -0
  462. dmart/utils/firebase_notifier.py +57 -0
  463. dmart/utils/generate_email.py +37 -0
  464. dmart/utils/helpers.py +352 -0
  465. dmart/utils/hypercorn_config.py +12 -0
  466. dmart/utils/internal_error_code.py +60 -0
  467. dmart/utils/jwt.py +124 -0
  468. dmart/utils/logger.py +167 -0
  469. dmart/utils/middleware.py +99 -0
  470. dmart/utils/notification.py +75 -0
  471. dmart/utils/password_hashing.py +16 -0
  472. dmart/utils/plugin_manager.py +202 -0
  473. dmart/utils/query_policies_helper.py +128 -0
  474. dmart/utils/regex.py +44 -0
  475. dmart/utils/repository.py +529 -0
  476. dmart/utils/router_helper.py +19 -0
  477. dmart/utils/settings.py +212 -0
  478. dmart/utils/sms_notifier.py +21 -0
  479. dmart/utils/social_sso.py +67 -0
  480. dmart/utils/templates/activation.html.j2 +26 -0
  481. dmart/utils/templates/reminder.html.j2 +17 -0
  482. dmart/utils/ticket_sys_utils.py +203 -0
  483. dmart/utils/web_notifier.py +29 -0
  484. dmart/websocket.py +231 -0
  485. dmart-1.4.40.post8.dist-info/METADATA +75 -0
  486. dmart-1.4.40.post8.dist-info/RECORD +489 -0
  487. dmart-1.4.40.post8.dist-info/WHEEL +5 -0
  488. dmart-1.4.40.post8.dist-info/entry_points.txt +2 -0
  489. dmart-1.4.40.post8.dist-info/top_level.txt +1 -0
dmart/test_utils.py ADDED
@@ -0,0 +1,34 @@
1
+ from fastapi import status
2
+
3
+
4
+ def check_repeated_shortname(response):
5
+ json_response = response.json()
6
+ assert response.status_code == status.HTTP_400_BAD_REQUEST
7
+ assert "failed" == json_response.get("status")
8
+ assert "request" == json_response.get("error", {}).get("type")
9
+
10
+
11
+ def check_not_found(response):
12
+ json_response = response.json()
13
+ assert response.status_code == status.HTTP_404_NOT_FOUND
14
+ assert "failed" == json_response.get("status")
15
+ assert "db" == json_response.get("error").get("type")
16
+
17
+
18
+ def check_unauthorized(response):
19
+ json_response = response.json()
20
+ assert response.status_code == status.HTTP_401_UNAUTHORIZED
21
+ assert "failed" == json_response.get("status")
22
+ assert "auth" == json_response.get("error", {}).get("type")
23
+
24
+
25
+ def assert_code_and_status_success(response):
26
+ if response.status_code != status.HTTP_200_OK:
27
+ print(
28
+ "\n\n\n\n\n========================= ERROR RESPONSE: =========================n:",
29
+ response.json(),
30
+ "\n\n\n\n\n",
31
+ )
32
+ assert response.status_code == status.HTTP_200_OK
33
+ json_response = response.json()
34
+ assert json_response["status"] == "success"
File without changes
@@ -0,0 +1,306 @@
1
+ import re
2
+ import sys
3
+ from models.core import Meta, ACL, ActionType, ConditionType, Group, Permission, Role, User
4
+ from models.enums import ResourceType
5
+ from utils.helpers import camel_case, flatten_dict
6
+ from utils.settings import settings
7
+ from data_adapters.adapter import data_adapter as db
8
+ from utils.regex import FILE_PATTERN
9
+
10
+
11
+ class AccessControl:
12
+ permissions: dict[str, Permission] = {}
13
+ groups: dict[str, Group] = {}
14
+ roles: dict[str, Role] = {}
15
+ users: dict[str, User] = {}
16
+
17
+ async def load_permissions_and_roles(self) -> None:
18
+ if settings.active_data_db == "file":
19
+ management_path = settings.spaces_folder / settings.management_space
20
+
21
+ management_modules: dict[str, type[Meta]] = {
22
+ "groups": Group,
23
+ "roles": Role,
24
+ "permissions": Permission
25
+ }
26
+
27
+ for module_name, module_value in management_modules.items():
28
+ self_module = getattr(self, module_name)
29
+ self_module = {}
30
+ path = management_path / module_name
31
+ entries_glob = ".dm/*/meta.*.json"
32
+ if path.exists():
33
+ for one in path.glob(entries_glob):
34
+ match = FILE_PATTERN.search(str(one))
35
+ if not match or not one.is_file():
36
+ continue
37
+ shortname = match.group(1)
38
+ try:
39
+ resource_obj: Meta = await db.load(
40
+ settings.management_space,
41
+ module_name,
42
+ shortname,
43
+ module_value,
44
+ "anonymous",
45
+ )
46
+ if resource_obj.is_active:
47
+ self_module[shortname] = resource_obj # store in redis doc
48
+ except Exception as ex:
49
+ # print(f"Error processing @{settings.management_space}/{module_name}/{shortname} ... ", ex)
50
+ raise ex
51
+
52
+ await db.create_user_premission_index()
53
+ await db.store_modules_to_redis(self.roles, self.groups, self.permissions)
54
+ await db.delete_user_permissions_map_in_redis()
55
+
56
+
57
+ async def check_access(
58
+ self,
59
+ user_shortname: str,
60
+ space_name: str,
61
+ subpath: str,
62
+ resource_type: ResourceType,
63
+ action_type: ActionType,
64
+ resource_is_active: bool = False,
65
+ resource_owner_shortname: str | None = None,
66
+ resource_owner_group: str | None = None,
67
+ record_attributes: dict = {},
68
+ entry_shortname: str | None = None
69
+ ):
70
+ # print("Checking access for", user_shortname, space_name, subpath, resource_type, action_type)
71
+ if resource_type == ResourceType.space and entry_shortname:
72
+ return await self.check_space_access(
73
+ user_shortname,
74
+ entry_shortname
75
+ )
76
+
77
+ if entry_shortname:
78
+ acl_access = await self.check_access_control_list(
79
+ space_name,
80
+ subpath,
81
+ resource_type,
82
+ entry_shortname,
83
+ action_type,
84
+ user_shortname
85
+ )
86
+ if acl_access:
87
+ return True
88
+ # print("Checking check_space_access access")
89
+ user_permissions = await db.get_user_permissions(user_shortname)
90
+
91
+ user_groups = (await db.load_user_meta(user_shortname)).groups or []
92
+
93
+ # Generate set of achevied conditions on the resource
94
+ # ex: {"is_active", "own"}
95
+ resource_achieved_conditions: set[ConditionType] = set()
96
+ if resource_is_active:
97
+ resource_achieved_conditions.add(ConditionType.is_active)
98
+ if resource_owner_shortname == user_shortname or resource_owner_group in user_groups:
99
+ resource_achieved_conditions.add(ConditionType.own)
100
+
101
+ # Allow checking for root permissions
102
+ subpath_parts = ["/"]
103
+ subpath_parts += list(filter(None, subpath.strip("/").split("/")))
104
+ if resource_type == ResourceType.folder and entry_shortname:
105
+ subpath_parts.append(entry_shortname)
106
+
107
+ search_subpath = ""
108
+ for subpath_part in subpath_parts:
109
+ search_subpath += subpath_part
110
+ # Check if the user has global access
111
+ global_access = self.has_global_access(
112
+ space_name,
113
+ user_permissions,
114
+ search_subpath,
115
+ action_type,
116
+ resource_type,
117
+ resource_achieved_conditions,
118
+ record_attributes
119
+ )
120
+ if global_access:
121
+ return True
122
+
123
+ permission_key = f"{space_name}:{search_subpath}:{resource_type}"
124
+ if (
125
+ permission_key in user_permissions
126
+ and action_type in user_permissions[permission_key]["allowed_actions"]
127
+ and self.check_access_conditions(
128
+ set(user_permissions[permission_key]["conditions"]),
129
+ set(resource_achieved_conditions),
130
+ action_type,
131
+ )
132
+ and self.check_access_restriction(
133
+ user_permissions[permission_key]["restricted_fields"],
134
+ user_permissions[permission_key]["allowed_fields_values"],
135
+ action_type,
136
+ record_attributes
137
+ )
138
+ ):
139
+ return True
140
+
141
+ if search_subpath == "/":
142
+ search_subpath = ""
143
+ else:
144
+ search_subpath += "/"
145
+
146
+ return False
147
+
148
+ async def check_access_control_list(
149
+ self,
150
+ space_name: str,
151
+ subpath: str,
152
+ resource_type: ResourceType,
153
+ entry_shortname: str,
154
+ action_type: ActionType,
155
+ user_shortname: str,
156
+ ) -> bool:
157
+ resource_cls = getattr(
158
+ sys.modules["models.core"], camel_case(resource_type)
159
+ )
160
+
161
+ try:
162
+ entry = await db.load(
163
+ space_name=space_name,
164
+ subpath=subpath,
165
+ shortname=entry_shortname,
166
+ class_type=resource_cls
167
+ )
168
+ except Exception:
169
+ return False
170
+
171
+ if not entry.acl:
172
+ return False
173
+
174
+ user_acl: ACL | None = None
175
+ for access in entry.acl:
176
+ if access.user_shortname == user_shortname:
177
+ user_acl = access
178
+ break
179
+
180
+ if not user_acl:
181
+ return False
182
+
183
+ return action_type in user_acl.allowed_actions
184
+
185
+ def has_global_access(
186
+ self,
187
+ space_name: str,
188
+ user_permissions: dict,
189
+ search_subpath: str,
190
+ action_type: ActionType,
191
+ resource_type: str,
192
+ resource_achieved_conditions: set,
193
+ record_attributes: dict
194
+ ) -> bool:
195
+ """
196
+ check if has access to global subpath by replacing the following
197
+ subpath = / => __all_subpaths__
198
+ subpath = {subpath} => __all_subpaths__
199
+ subpath = {subpath}/protected => __all_subpaths__/protected
200
+ subpath = {subpath}/protected/mine => {subpath}/__all_subpaths__/mine
201
+ """
202
+ original_subpath = search_subpath
203
+ search_subpath_parts = search_subpath.split("/")
204
+ if len(search_subpath_parts) > 1:
205
+ search_subpath_parts[-2] = settings.all_subpaths_mw
206
+ search_subpath = "/".join(search_subpath_parts)
207
+ elif len(search_subpath_parts) == 1:
208
+ search_subpath = settings.all_subpaths_mw
209
+ if search_subpath[-1] == "/" and len(search_subpath) > 1:
210
+ search_subpath = search_subpath[:-1]
211
+
212
+ permission_key = None
213
+ # check if has access to all spaces
214
+ if f"{settings.all_spaces_mw}:{search_subpath}:{resource_type}" in user_permissions:
215
+ permission_key = f"{settings.all_spaces_mw}:{search_subpath}:{resource_type}"
216
+
217
+ # check if has access to current spaces
218
+ if f"{space_name}:{search_subpath}:{resource_type}" in user_permissions:
219
+ permission_key = f"{space_name}:{search_subpath}:{resource_type}"
220
+
221
+
222
+ # check if has access to current subpath
223
+ if f"{settings.all_spaces_mw}:{original_subpath}:{resource_type}" in user_permissions:
224
+ permission_key = f"{settings.all_spaces_mw}:{original_subpath}:{resource_type}"
225
+
226
+ if not permission_key:
227
+ return False
228
+
229
+ if (
230
+ action_type in user_permissions[permission_key]["allowed_actions"]
231
+ and self.check_access_conditions(
232
+ set(user_permissions[permission_key]["conditions"]),
233
+ set(resource_achieved_conditions),
234
+ action_type,
235
+ )
236
+ and self.check_access_restriction(
237
+ user_permissions[permission_key]["restricted_fields"],
238
+ user_permissions[permission_key]["allowed_fields_values"],
239
+ action_type,
240
+ record_attributes
241
+ )
242
+ ):
243
+ return True
244
+
245
+ return False
246
+
247
+ def check_access_conditions(
248
+ self,
249
+ premission_conditions: set,
250
+ resource_achieved_conditions: set,
251
+ action_type: ActionType,
252
+ ):
253
+ # actions of type query will be handled in the query function
254
+ # actions of type create shouldn't check for permission conditions
255
+ if action_type in [ActionType.create, ActionType.query]:
256
+ return True
257
+
258
+ return premission_conditions.issubset(resource_achieved_conditions)
259
+
260
+ def check_access_restriction(
261
+ self,
262
+ restricted_fields: list,
263
+ allowed_fields_values: dict,
264
+ action_type: ActionType,
265
+ record_attributes: dict
266
+ ):
267
+ """
268
+ in case of create or update action, check access for the record fields
269
+ via permission.restricted_fields and permission.allowed_fields_values
270
+ """
271
+ if action_type not in [ActionType.create, ActionType.update]:
272
+ return True
273
+
274
+ flattened_attributes = flatten_dict(record_attributes)
275
+
276
+ for restricted_field in restricted_fields:
277
+ if restricted_field in flattened_attributes:
278
+ return False
279
+ for flattened_key in flattened_attributes.keys():
280
+ if flattened_key == restricted_field or flattened_key.startswith(f"{restricted_field}."):
281
+ return False
282
+
283
+ for field_name, field_values in allowed_fields_values.items():
284
+ if field_name not in flattened_attributes:
285
+ continue
286
+ if (
287
+ isinstance(flattened_attributes[field_name], list) and
288
+ isinstance(field_values[0], list) and
289
+ not any(all(i in allowed_values for i in flattened_attributes[field_name]) for allowed_values in field_values)
290
+ ):
291
+ return False
292
+ elif (
293
+ not isinstance(flattened_attributes[field_name], list) and
294
+ flattened_attributes[field_name] not in field_values
295
+ ):
296
+ return False
297
+
298
+ return True
299
+
300
+ async def check_space_access(self, user_shortname: str, space_name: str) -> bool:
301
+ user_permissions = await db.get_user_permissions(user_shortname)
302
+ prog = re.compile(f"{space_name}:*|{settings.all_spaces_mw}:*")
303
+ return bool(list(filter(prog.match, user_permissions.keys())))
304
+
305
+
306
+ access_control = AccessControl()
@@ -0,0 +1,8 @@
1
+ import aiohttp
2
+ #import json_logging
3
+
4
+
5
+ def AsyncRequest(headers=None):
6
+ # corr_id = {"X-Correlation-ID": json_logging.get_correlation_id()}
7
+ # headers = {**headers, **corr_id} if headers else corr_id
8
+ return aiohttp.ClientSession(headers=headers)
@@ -0,0 +1,309 @@
1
+ #!/usr/bin/env python3
2
+ import argparse
3
+ import asyncio
4
+ from hashlib import blake2b, md5
5
+ import json
6
+ import os
7
+ import shutil
8
+ import sys
9
+ import copy
10
+ import jsonschema
11
+ from aiofiles import open as aopen
12
+ from pathlib import Path
13
+
14
+ # from pydantic import config
15
+
16
+
17
+ def hashing_data(data: str, hashed_data: dict):
18
+ hash = blake2b(salt=md5(data.encode()).digest())
19
+ hash.update(data.encode())
20
+ hashed_val = md5(hash.digest()).hexdigest()
21
+ hashed_data[hashed_val] = data
22
+ return hashed_val
23
+
24
+
25
+ def exit_with_error(msg: str):
26
+ print("ERROR!!", msg)
27
+ sys.exit(1)
28
+
29
+
30
+ SECURED_FIELDS = [
31
+ "name",
32
+ "email",
33
+ "ip",
34
+ "pin",
35
+ "RechargeNumber",
36
+ "CallingNumber",
37
+ "shortname",
38
+ "contact_number",
39
+ "pin",
40
+ "msisdn",
41
+ "imsi",
42
+ "sender_msisdn",
43
+ "old_username",
44
+ "firstname",
45
+ "lastname",
46
+ ]
47
+ OUTPUT_FOLDER_NAME = "spaces_data"
48
+
49
+ def meta_path(
50
+ space_path: Path,
51
+ subpath: str,
52
+ file_path: str,
53
+ resource_type: str
54
+ ) -> Path:
55
+ return space_path / f"{subpath}/.dm/{file_path}/meta.{resource_type}.json"
56
+
57
+ async def get_meta(
58
+ *,
59
+ space_path: Path,
60
+ subpath: str,
61
+ file_path: str,
62
+ resource_type: str
63
+ ):
64
+ meta_content = meta_path(space_path, subpath, file_path, resource_type)
65
+ async with aopen(meta_content, "r") as f:
66
+ return json.loads(await f.read())
67
+
68
+
69
+ def validate_config(config_obj: dict):
70
+ if (
71
+ not config_obj.get("space")
72
+ or not config_obj.get("subpath")
73
+ or not config_obj.get("resource_type")
74
+ or not config_obj.get("schema_shortname")
75
+ ):
76
+ return False
77
+ return True
78
+
79
+
80
+ def remove_fields(src: dict, restricted_keys: list):
81
+ for k in list(src.keys()):
82
+ if isinstance(src[k], list):
83
+ for item in src[k]:
84
+ if isinstance(item, dict):
85
+ item = remove_fields(item, restricted_keys)
86
+ elif isinstance(src[k], dict):
87
+ src[k] = remove_fields(src[k], restricted_keys)
88
+
89
+ if k in restricted_keys:
90
+ del src[k]
91
+
92
+ return src
93
+
94
+ def enc_dict(d: dict, hashed_data: dict):
95
+ for k, v in d.items():
96
+ if isinstance(v, dict):
97
+ d[k] = enc_dict(v, hashed_data)
98
+ elif isinstance(d[k], list):
99
+ for item in d[k]:
100
+ if isinstance(item, dict):
101
+ item = enc_dict(item, hashed_data)
102
+
103
+ # if k == "msisdn":
104
+ # d[k] = hashing_data(str(v))
105
+ # try:
106
+ # print("ADD TO SQLLITE")
107
+ # cur.execute(f"INSERT INTO migrated_data VALUES('{v}', '{d[k]}')")
108
+ # con.commit()
109
+ # print("FINSIHED")
110
+ # except sqlite3.IntegrityError as e:
111
+ # if "migrated_data.hash" in str(e): # msisdn already in db
112
+ # raise Exception(f"Collision on: msisdn {v}, hash {d[k]}")
113
+ elif k in SECURED_FIELDS:
114
+ d[k] = hashing_data(str(v), hashed_data)
115
+
116
+ return d
117
+
118
+
119
+ def prepare_output(
120
+ meta: dict,
121
+ payload: dict,
122
+ included_meta_fields: dict,
123
+ excluded_payload_fields: dict,
124
+ ):
125
+ out = payload
126
+ for field_meta in included_meta_fields:
127
+ field_name = field_meta.get("field_name")
128
+ rename_to = field_meta.get("rename_to")
129
+ if not field_name:
130
+ continue
131
+ if rename_to:
132
+ out[rename_to] = meta.get(field_name, "")
133
+ else:
134
+ out[field_name] = meta.get(field_name, "")
135
+
136
+ out = remove_fields(
137
+ out,
138
+ [field["field_name"] for field in excluded_payload_fields]
139
+ )
140
+ return out
141
+
142
+
143
+
144
+
145
+ async def extract(
146
+ space : str,
147
+ subpath : str, # = config_obj.get("subpath")
148
+ resource_type : str, # = config_obj.get("resource_type")
149
+ schema_shortname : str, # = config_obj.get("schema_shortname")
150
+ included_meta_fields : dict, # = config_obj.get("included_meta_fields", [])
151
+ excluded_payload_fields : dict, # = config_obj.get("excluded_payload_fields", [])
152
+ spaces_path: str,
153
+ output_path : str,
154
+ entries_since = None
155
+ ) -> None:
156
+ hashed_data: dict[str, str] = {}
157
+
158
+ space_path = Path(f"{spaces_path}/{space}")
159
+ subpath_schema_obj = None
160
+ with open(space_path / f"schema/{schema_shortname}.json", "r") as f:
161
+ subpath_schema_obj = json.load(f)
162
+ input_subpath_schema_obj = copy.deepcopy(subpath_schema_obj)
163
+
164
+ output_subpath = Path(f"{output_path}/{OUTPUT_FOLDER_NAME}/{space}/{subpath}")
165
+ if not output_subpath.is_dir():
166
+ os.makedirs(output_subpath)
167
+
168
+ # Generat output schema
169
+ schema_fil = output_subpath / "schema.json"
170
+ for field in included_meta_fields:
171
+ if "oneOf" in subpath_schema_obj:
172
+ for schema in subpath_schema_obj["oneOf"]:
173
+ schema["properties"][field["field_name"]] = field["schema_entry"]
174
+ if field.get("rename_to"):
175
+ schema["properties"][field["rename_to"]] = schema[
176
+ "properties"
177
+ ].pop(field["field_name"])
178
+ else:
179
+ subpath_schema_obj["properties"][field["field_name"]] = field["schema_entry"]
180
+ if field.get("rename_to"):
181
+ subpath_schema_obj["properties"][field["rename_to"]] = subpath_schema_obj[
182
+ "properties"
183
+ ].pop(field["field_name"])
184
+ if "oneOf" in subpath_schema_obj:
185
+ for schema in subpath_schema_obj["oneOf"]:
186
+ schema["properties"] = remove_fields(
187
+ schema["properties"],
188
+ [field["field_name"] for field in excluded_payload_fields]
189
+ )
190
+ else:
191
+ subpath_schema_obj["properties"] = remove_fields(
192
+ subpath_schema_obj["properties"],
193
+ [field["field_name"] for field in excluded_payload_fields]
194
+ )
195
+ open(schema_fil, "w").write(json.dumps(subpath_schema_obj) + "\n")
196
+
197
+ # Generat output content file
198
+ data_file = output_subpath / "data.ljson"
199
+ path = os.path.join(spaces_path, space, subpath)
200
+ for file_name in os.listdir(path):
201
+ if not file_name.endswith(".json"):
202
+ continue
203
+ if entries_since:
204
+ payload_ts = int(round(
205
+ os.path.getmtime(os.path.join(path, file_name)) * 1000
206
+ ))
207
+ meta_ts = int(round(os.path.getmtime(meta_path(
208
+ space_path,
209
+ subpath,
210
+ file_name.split(".")[0],
211
+ resource_type
212
+ )) * 1000))
213
+ if payload_ts <= entries_since and meta_ts <= entries_since:
214
+ continue
215
+
216
+ async with aopen(os.path.join(path, file_name), "r") as f:
217
+ content = await f.read()
218
+ try:
219
+ payload = json.loads(content)
220
+ jsonschema.validate(
221
+ instance=payload, schema=input_subpath_schema_obj
222
+ )
223
+ meta = await get_meta(
224
+ space_path=space_path,
225
+ subpath=subpath,
226
+ file_path=file_name.split(".")[0],
227
+ resource_type=resource_type,
228
+ )
229
+ except Exception:
230
+ # print(f"ERROR: {error.args = }")
231
+ continue
232
+
233
+ out = prepare_output(
234
+ meta, payload, included_meta_fields, excluded_payload_fields
235
+ )
236
+
237
+ # jsonschema.validate(instance=out, schema=subpath_schema_obj)
238
+
239
+ encrypted_out = enc_dict(out, hashed_data)
240
+ open(data_file, "a").write(json.dumps(encrypted_out) + "\n")
241
+
242
+ open(f"{output_subpath}/hashed_data.json", "w").write(json.dumps(hashed_data))
243
+
244
+
245
+
246
+ async def main(tasks):
247
+ await asyncio.gather(*tasks)
248
+
249
+
250
+ if __name__ == "__main__":
251
+ parser = argparse.ArgumentParser()
252
+ parser.add_argument(
253
+ "--config", required=True, help="Json config relative path from the script"
254
+ )
255
+ parser.add_argument(
256
+ "--spaces", required=True, help="Spaces relative path from the script"
257
+ )
258
+ parser.add_argument(
259
+ "--output",
260
+ help="Output relative path from the script (the default path is the current script path",
261
+ )
262
+ parser.add_argument(
263
+ "--since",
264
+ help="Export entries created/updated since the provided timestamp",
265
+ )
266
+ args = parser.parse_args()
267
+ since = None
268
+ output_path = ""
269
+ if args.output:
270
+ output_path = args.output
271
+
272
+ if args.since:
273
+ since = int(round(float(args.since) * 1000))
274
+
275
+ if not os.path.isdir(args.spaces):
276
+ exit_with_error(f"The spaces folder {args.spaces} is not found.")
277
+
278
+ out_path = os.path.join(output_path, OUTPUT_FOLDER_NAME)
279
+ if os.path.isdir(out_path):
280
+ shutil.rmtree(out_path)
281
+
282
+ # con = sqlite3.connect(f"../../exporter_data/output/data.db")
283
+ # cur = con.cursor()
284
+ # cur.execute("DROP TABLE IF EXISTS migrated_data")
285
+ # cur.execute(
286
+ # "CREATE TABLE migrated_data(hash VARCHAR UNIQUE, msisdn VARCHAR UNIQUE)"
287
+ # )
288
+ # print(con)
289
+
290
+ tasks = []
291
+ with open(args.config, "r") as f:
292
+ config_objs = json.load(f)
293
+
294
+ for config_obj in config_objs:
295
+ if not validate_config(config_obj):
296
+ continue
297
+ tasks.append(extract(config_obj.get("space", ""),
298
+ config_obj.get("subpath", ""),
299
+ config_obj.get("resource_type", ""),
300
+ config_obj.get("schema_shortname", ""),
301
+ config_obj.get("included_meta_fields", {}),
302
+ config_obj.get("excluded_payload_fields", {}),
303
+ args.spaces, output_path, since))
304
+
305
+ asyncio.run(main(tasks))
306
+
307
+ print(
308
+ f"Output path: {os.path.abspath(os.path.join(output_path, OUTPUT_FOLDER_NAME))}"
309
+ )