mage-ai 0.9.32__py3-none-any.whl → 0.9.34__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.

Potentially problematic release.


This version of mage-ai might be problematic. Click here for more details.

Files changed (365) hide show
  1. mage_ai/api/constants.py +37 -2
  2. mage_ai/api/errors.py +9 -5
  3. mage_ai/api/middleware.py +2 -2
  4. mage_ai/api/mixins/__init__.py +0 -0
  5. mage_ai/api/mixins/result_set.py +65 -0
  6. mage_ai/api/operations/base.py +41 -11
  7. mage_ai/api/operations/constants.py +7 -6
  8. mage_ai/api/parsers/BaseParser.py +6 -4
  9. mage_ai/api/parsers/PipelineScheduleParser.py +36 -2
  10. mage_ai/api/policies/BackfillPolicy.py +1 -0
  11. mage_ai/api/policies/BasePolicy.py +229 -40
  12. mage_ai/api/policies/PermissionPolicy.py +17 -0
  13. mage_ai/api/policies/PipelineSchedulePolicy.py +18 -2
  14. mage_ai/api/policies/RolePermissionPolicy.py +2 -1
  15. mage_ai/api/policies/RolePolicy.py +3 -0
  16. mage_ai/api/policies/SessionPolicy.py +36 -1
  17. mage_ai/api/policies/StatusPolicy.py +46 -3
  18. mage_ai/api/policies/UserPolicy.py +25 -2
  19. mage_ai/api/policies/mixins/__init__.py +0 -0
  20. mage_ai/api/policies/mixins/user_permissions.py +241 -0
  21. mage_ai/api/presenters/PermissionPresenter.py +35 -8
  22. mage_ai/api/presenters/RolePresenter.py +29 -12
  23. mage_ai/api/presenters/StatusPresenter.py +11 -0
  24. mage_ai/api/presenters/UserPresenter.py +21 -24
  25. mage_ai/api/resources/BaseResource.py +24 -9
  26. mage_ai/api/resources/BlockResource.py +3 -2
  27. mage_ai/api/resources/DatabaseResource.py +2 -2
  28. mage_ai/api/resources/PermissionResource.py +65 -3
  29. mage_ai/api/resources/Resource.py +8 -1
  30. mage_ai/api/resources/RolePermissionResource.py +23 -0
  31. mage_ai/api/resources/RoleResource.py +75 -4
  32. mage_ai/api/resources/StatusResource.py +34 -0
  33. mage_ai/api/resources/UserResource.py +35 -4
  34. mage_ai/api/result_set.py +8 -1
  35. mage_ai/authentication/permissions/constants.py +78 -0
  36. mage_ai/data_integrations/destinations/constants.py +1 -0
  37. mage_ai/data_integrations/utils/config.py +9 -0
  38. mage_ai/data_integrations/utils/scheduler.py +16 -7
  39. mage_ai/data_preparation/models/block/__init__.py +56 -16
  40. mage_ai/data_preparation/models/block/data_integration/mixins.py +92 -1
  41. mage_ai/data_preparation/models/block/data_integration/utils.py +5 -5
  42. mage_ai/data_preparation/models/block/integration/__init__.py +12 -7
  43. mage_ai/data_preparation/models/block/sql/__init__.py +5 -2
  44. mage_ai/data_preparation/models/block/sql/utils/shared.py +15 -1
  45. mage_ai/data_preparation/models/pipelines/interactions.py +15 -0
  46. mage_ai/data_preparation/shared/secrets.py +7 -17
  47. mage_ai/data_preparation/templates/data_exporters/streaming/google_cloud_pubsub.yaml +5 -0
  48. mage_ai/data_preparation/templates/repo/metadata.yaml +5 -0
  49. mage_ai/io/druid.py +9 -5
  50. mage_ai/io/mssql.py +52 -30
  51. mage_ai/io/postgres.py +1 -0
  52. mage_ai/io/sql.py +15 -1
  53. mage_ai/orchestration/db/models/oauth.py +181 -17
  54. mage_ai/orchestration/db/models/schedules.py +11 -0
  55. mage_ai/presenters/interactions/constants.py +1 -0
  56. mage_ai/presenters/pages/loaders/pipeline_schedules.py +9 -3
  57. mage_ai/server/api/base.py +16 -6
  58. mage_ai/server/api/v1.py +2 -1
  59. mage_ai/server/constants.py +1 -1
  60. mage_ai/server/frontend_dist/404.html +2 -2
  61. mage_ai/server/frontend_dist/_next/static/PBVuphyo_muEAj347ZP_b/_buildManifest.js +1 -0
  62. mage_ai/server/frontend_dist/_next/static/chunks/1124-d8fc76201b83b376.js +1 -0
  63. mage_ai/server/frontend_dist/_next/static/chunks/{1749-9ec0f4709f5a9284.js → 1749-a6bdce4ee8a09bce.js} +1 -1
  64. mage_ai/server/frontend_dist/_next/static/chunks/1769-613e23e361eb5bce.js +1 -0
  65. mage_ai/server/frontend_dist/_next/static/chunks/1821-953efd0da290d25f.js +1 -0
  66. mage_ai/server/frontend_dist/_next/static/chunks/2327-1a797c758f8b064a.js +1 -0
  67. mage_ai/server/frontend_dist/_next/static/chunks/2677-a85c5a72bb695304.js +1 -0
  68. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/3419-6c8ec8db8c398c12.js → frontend_dist/_next/static/chunks/3419-0df6c5ef72f2e672.js} +1 -1
  69. mage_ai/server/frontend_dist/_next/static/chunks/3684-e1a713b7c16f0151.js +1 -0
  70. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/3859-3501cdba0a33f9f2.js → frontend_dist/_next/static/chunks/3859-ba594d21a1260cd2.js} +1 -1
  71. mage_ai/server/frontend_dist/_next/static/chunks/4636-84f545d1d238df13.js +1 -0
  72. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/4839-963da65cea58054f.js → frontend_dist/_next/static/chunks/4839-e5fe343a369734bc.js} +1 -1
  73. mage_ai/server/frontend_dist/_next/static/chunks/5457-97433bc45b42a88a.js +1 -0
  74. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/5499-63667ad5a785dba5.js → frontend_dist/_next/static/chunks/5499-b74459f6be5f9229.js} +1 -1
  75. mage_ai/server/frontend_dist/_next/static/chunks/6043-6ea109833b88eb1d.js +1 -0
  76. mage_ai/server/frontend_dist/_next/static/chunks/6333-ca4cd6a73a597a40.js +1 -0
  77. mage_ai/server/frontend_dist/_next/static/chunks/7022-80d082a1d7fd1234.js +1 -0
  78. mage_ai/server/frontend_dist/_next/static/chunks/7361-25f211ef377e5958.js +1 -0
  79. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/8013-1d6b1f7c13264cb4.js → frontend_dist/_next/static/chunks/8013-e67c71ddea072a20.js} +1 -1
  80. mage_ai/server/frontend_dist/_next/static/chunks/8146-941c5155c3bfcc35.js +1 -0
  81. mage_ai/server/frontend_dist/_next/static/chunks/9161-837b653aa849a76f.js +1 -0
  82. mage_ai/server/frontend_dist/_next/static/chunks/9264-5730e4e059db40a8.js +1 -0
  83. mage_ai/server/frontend_dist/_next/static/chunks/pages/_app-752d991f239a128f.js +1 -0
  84. mage_ai/server/frontend_dist/_next/static/chunks/pages/{block-layout-a624972d126a3dbd.js → block-layout-a27a28d2a615e364.js} +1 -1
  85. mage_ai/server/frontend_dist/_next/static/chunks/pages/files-98e27a4d7bd0da47.js +1 -0
  86. mage_ai/server/frontend_dist/_next/static/chunks/pages/global-data-products/{[...slug]-a95787fe1695f493.js → [...slug]-9eb5dad57da13efd.js} +1 -1
  87. mage_ai/server/frontend_dist/_next/static/chunks/pages/{global-data-products-0d55711df91a78d0.js → global-data-products-26909dec66f00231.js} +1 -1
  88. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/settings-b9eea6abc676ca81.js +1 -0
  89. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/users/[user]-11c601c6ef07fe86.js +1 -0
  90. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/users/new-ae6083077c9c1c41.js +1 -0
  91. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/users-bfce0ee677d57206.js +1 -0
  92. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage-476c921d62f565fc.js +1 -0
  93. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/overview-9bc34ef66d84330a.js → frontend_dist/_next/static/chunks/pages/overview-2ec6b17e45a52be8.js} +1 -1
  94. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipeline-runs-605918f3a5c1aac4.js +1 -0
  95. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-84ec3ab0770bcd68.js → frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-ee65a62ed166bd85.js} +1 -1
  96. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills-2402004a19a6ad53.js +1 -0
  97. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/dashboard-9a5b4768a640cd68.js → frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/dashboard-8781db69f19759a1.js} +1 -1
  98. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/edit-d7fd4857579e2b00.js +1 -0
  99. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/logs-bd4bd009146bab36.js → frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/logs-234007c99efdccf6.js} +1 -1
  100. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/monitors/{block-runs-8405a83adeaa8ff5.js → block-runs-da7510d4b277e47b.js} +1 -1
  101. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/monitors/{block-runtime-98aa0840a00cc661.js → block-runtime-eae853ff34b09481.js} +1 -1
  102. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/{monitors-0fb48e1cc51f78b2.js → monitors-a057b17847b82468.js} +1 -1
  103. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/runs/{[run]-845c1f010d5ec380.js → [run]-99d11c86f8b15369.js} +1 -1
  104. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/runs-9255f85760e1f57a.js → frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/runs-3f0980d8810a540b.js} +1 -1
  105. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/{settings-187ac359020704e1.js → settings-9edf75d03460aaeb.js} +1 -1
  106. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/syncs-c52d0c49439ec620.js → frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/syncs-e7692e54979f037d.js} +1 -1
  107. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-7b3b57523b226a0e.js → frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-cb6a3bcaf4fa1a81.js} +1 -1
  108. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/triggers-5eff96c149584e87.js +1 -0
  109. mage_ai/server/frontend_dist/_next/static/chunks/pages/{pipelines-efc080913a247e99.js → pipelines-270b912e1ac189b5.js} +1 -1
  110. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/account/profile-2058d022972cdea4.js +1 -0
  111. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/permissions/[...slug]-1a95628ea8d0d846.js +1 -0
  112. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/permissions-cb1cdf5f8e5bf9c5.js +1 -0
  113. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/preferences-58978256db4efbda.js +1 -0
  114. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/roles/[...slug]-9ddd7eb842d5a911.js +1 -0
  115. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/roles-1694c5eb1acbcf30.js +1 -0
  116. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/sync-data-6850e854fbedbb61.js +1 -0
  117. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/users/[...slug]-5061c073e1c0de07.js +1 -0
  118. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/users-f551c5665bfd3494.js +1 -0
  119. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/sign-in-6fe3657070f98aca.js → frontend_dist/_next/static/chunks/pages/sign-in-e779dbab123e626e.js} +1 -1
  120. mage_ai/server/frontend_dist/_next/static/chunks/pages/templates/[...slug]-935113d252ada806.js +1 -0
  121. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/templates-25b6d24cfa81c306.js → frontend_dist/_next/static/chunks/pages/templates-7079d637e396f2a8.js} +1 -1
  122. mage_ai/server/frontend_dist/_next/static/chunks/pages/terminal-67fdb4a3be93aa14.js +1 -0
  123. mage_ai/server/frontend_dist/_next/static/chunks/pages/triggers-9c0374c7c783b34a.js +1 -0
  124. mage_ai/server/frontend_dist/_next/static/chunks/pages/version-control-ea3a0d48e5822b42.js +1 -0
  125. mage_ai/server/frontend_dist/block-layout.html +2 -2
  126. mage_ai/server/frontend_dist/files.html +2 -2
  127. mage_ai/server/frontend_dist/global-data-products/[...slug].html +2 -2
  128. mage_ai/server/frontend_dist/global-data-products.html +2 -2
  129. mage_ai/server/frontend_dist/index.html +2 -2
  130. mage_ai/server/frontend_dist/manage/settings.html +2 -2
  131. mage_ai/server/frontend_dist/manage/users/[user].html +2 -2
  132. mage_ai/server/frontend_dist/manage/users/new.html +2 -2
  133. mage_ai/server/frontend_dist/manage/users.html +2 -2
  134. mage_ai/server/frontend_dist/manage.html +2 -2
  135. mage_ai/server/frontend_dist/overview.html +2 -2
  136. mage_ai/server/frontend_dist/pipeline-runs.html +2 -2
  137. mage_ai/server/frontend_dist/pipelines/[pipeline]/backfills/[...slug].html +2 -2
  138. mage_ai/server/frontend_dist/pipelines/[pipeline]/backfills.html +2 -2
  139. mage_ai/server/frontend_dist/pipelines/[pipeline]/dashboard.html +2 -2
  140. mage_ai/server/frontend_dist/pipelines/[pipeline]/edit.html +2 -2
  141. mage_ai/server/frontend_dist/pipelines/[pipeline]/logs.html +2 -2
  142. mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors/block-runs.html +2 -2
  143. mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors/block-runtime.html +2 -2
  144. mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors.html +2 -2
  145. mage_ai/server/frontend_dist/pipelines/[pipeline]/runs/[run].html +2 -2
  146. mage_ai/server/frontend_dist/pipelines/[pipeline]/runs.html +2 -2
  147. mage_ai/server/frontend_dist/pipelines/[pipeline]/settings.html +2 -2
  148. mage_ai/server/frontend_dist/pipelines/[pipeline]/syncs.html +2 -2
  149. mage_ai/server/frontend_dist/pipelines/[pipeline]/triggers/[...slug].html +2 -2
  150. mage_ai/server/frontend_dist/pipelines/[pipeline]/triggers.html +2 -2
  151. mage_ai/server/frontend_dist/pipelines/[pipeline].html +2 -2
  152. mage_ai/server/frontend_dist/pipelines.html +2 -2
  153. mage_ai/server/frontend_dist/settings/account/profile.html +2 -2
  154. mage_ai/server/frontend_dist/settings/workspace/permissions/[...slug].html +24 -0
  155. mage_ai/server/frontend_dist/settings/workspace/permissions.html +24 -0
  156. mage_ai/server/frontend_dist/settings/workspace/preferences.html +2 -2
  157. mage_ai/server/frontend_dist/settings/workspace/roles/[...slug].html +24 -0
  158. mage_ai/server/frontend_dist/settings/workspace/roles.html +24 -0
  159. mage_ai/server/frontend_dist/settings/workspace/sync-data.html +2 -2
  160. mage_ai/server/frontend_dist/settings/workspace/users/[...slug].html +24 -0
  161. mage_ai/server/frontend_dist/settings/workspace/users.html +2 -2
  162. mage_ai/server/frontend_dist/settings.html +2 -2
  163. mage_ai/server/frontend_dist/sign-in.html +5 -5
  164. mage_ai/server/frontend_dist/templates/[...slug].html +2 -2
  165. mage_ai/server/frontend_dist/templates.html +2 -2
  166. mage_ai/server/frontend_dist/terminal.html +2 -2
  167. mage_ai/server/frontend_dist/test.html +2 -2
  168. mage_ai/server/frontend_dist/triggers.html +2 -2
  169. mage_ai/server/frontend_dist/version-control.html +2 -2
  170. mage_ai/server/frontend_dist_base_path_template/404.html +2 -2
  171. mage_ai/server/frontend_dist_base_path_template/_next/static/L-IKw5_bRZUs-wyjnpN_j/_buildManifest.js +1 -0
  172. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1124-d8fc76201b83b376.js +1 -0
  173. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/{1749-9ec0f4709f5a9284.js → 1749-a6bdce4ee8a09bce.js} +1 -1
  174. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1769-613e23e361eb5bce.js +1 -0
  175. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1821-953efd0da290d25f.js +1 -0
  176. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/2327-1a797c758f8b064a.js +1 -0
  177. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/2677-a85c5a72bb695304.js +1 -0
  178. mage_ai/server/{frontend_dist/_next/static/chunks/3419-6c8ec8db8c398c12.js → frontend_dist_base_path_template/_next/static/chunks/3419-0df6c5ef72f2e672.js} +1 -1
  179. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/3684-e1a713b7c16f0151.js +1 -0
  180. mage_ai/server/{frontend_dist/_next/static/chunks/3859-3501cdba0a33f9f2.js → frontend_dist_base_path_template/_next/static/chunks/3859-ba594d21a1260cd2.js} +1 -1
  181. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/4636-84f545d1d238df13.js +1 -0
  182. mage_ai/server/{frontend_dist/_next/static/chunks/4839-963da65cea58054f.js → frontend_dist_base_path_template/_next/static/chunks/4839-e5fe343a369734bc.js} +1 -1
  183. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/5457-97433bc45b42a88a.js +1 -0
  184. mage_ai/server/{frontend_dist/_next/static/chunks/5499-63667ad5a785dba5.js → frontend_dist_base_path_template/_next/static/chunks/5499-b74459f6be5f9229.js} +1 -1
  185. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/6043-6ea109833b88eb1d.js +1 -0
  186. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/6333-ca4cd6a73a597a40.js +1 -0
  187. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/7022-80d082a1d7fd1234.js +1 -0
  188. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/7361-25f211ef377e5958.js +1 -0
  189. mage_ai/server/{frontend_dist/_next/static/chunks/8013-1d6b1f7c13264cb4.js → frontend_dist_base_path_template/_next/static/chunks/8013-e67c71ddea072a20.js} +1 -1
  190. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/8146-941c5155c3bfcc35.js +1 -0
  191. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/9161-837b653aa849a76f.js +1 -0
  192. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/9264-5730e4e059db40a8.js +1 -0
  193. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/_app-752d991f239a128f.js +1 -0
  194. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/{block-layout-a624972d126a3dbd.js → block-layout-a27a28d2a615e364.js} +1 -1
  195. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/files-98e27a4d7bd0da47.js +1 -0
  196. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/global-data-products/{[...slug]-a95787fe1695f493.js → [...slug]-9eb5dad57da13efd.js} +1 -1
  197. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/{global-data-products-0d55711df91a78d0.js → global-data-products-26909dec66f00231.js} +1 -1
  198. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/settings-b9eea6abc676ca81.js +1 -0
  199. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/users/[user]-11c601c6ef07fe86.js +1 -0
  200. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/users/new-ae6083077c9c1c41.js +1 -0
  201. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/users-bfce0ee677d57206.js +1 -0
  202. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage-476c921d62f565fc.js +1 -0
  203. mage_ai/server/{frontend_dist/_next/static/chunks/pages/overview-9bc34ef66d84330a.js → frontend_dist_base_path_template/_next/static/chunks/pages/overview-2ec6b17e45a52be8.js} +1 -1
  204. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipeline-runs-605918f3a5c1aac4.js +1 -0
  205. mage_ai/server/{frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-84ec3ab0770bcd68.js → frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-ee65a62ed166bd85.js} +1 -1
  206. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills-2402004a19a6ad53.js +1 -0
  207. mage_ai/server/{frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/dashboard-9a5b4768a640cd68.js → frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/dashboard-8781db69f19759a1.js} +1 -1
  208. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/edit-d7fd4857579e2b00.js +1 -0
  209. mage_ai/server/{frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/logs-bd4bd009146bab36.js → frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/logs-234007c99efdccf6.js} +1 -1
  210. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/monitors/{block-runs-8405a83adeaa8ff5.js → block-runs-da7510d4b277e47b.js} +1 -1
  211. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/monitors/{block-runtime-98aa0840a00cc661.js → block-runtime-eae853ff34b09481.js} +1 -1
  212. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/{monitors-0fb48e1cc51f78b2.js → monitors-a057b17847b82468.js} +1 -1
  213. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/runs/{[run]-845c1f010d5ec380.js → [run]-99d11c86f8b15369.js} +1 -1
  214. mage_ai/server/{frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/runs-9255f85760e1f57a.js → frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/runs-3f0980d8810a540b.js} +1 -1
  215. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/{settings-187ac359020704e1.js → settings-9edf75d03460aaeb.js} +1 -1
  216. mage_ai/server/{frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/syncs-c52d0c49439ec620.js → frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/syncs-e7692e54979f037d.js} +1 -1
  217. mage_ai/server/{frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-7b3b57523b226a0e.js → frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-cb6a3bcaf4fa1a81.js} +1 -1
  218. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/triggers-5eff96c149584e87.js +1 -0
  219. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/{pipelines-efc080913a247e99.js → pipelines-270b912e1ac189b5.js} +1 -1
  220. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/account/profile-2058d022972cdea4.js +1 -0
  221. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/permissions/[...slug]-1a95628ea8d0d846.js +1 -0
  222. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/permissions-cb1cdf5f8e5bf9c5.js +1 -0
  223. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/preferences-58978256db4efbda.js +1 -0
  224. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/roles/[...slug]-9ddd7eb842d5a911.js +1 -0
  225. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/roles-1694c5eb1acbcf30.js +1 -0
  226. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/sync-data-6850e854fbedbb61.js +1 -0
  227. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/users/[...slug]-5061c073e1c0de07.js +1 -0
  228. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/users-f551c5665bfd3494.js +1 -0
  229. mage_ai/server/{frontend_dist/_next/static/chunks/pages/sign-in-6fe3657070f98aca.js → frontend_dist_base_path_template/_next/static/chunks/pages/sign-in-e779dbab123e626e.js} +1 -1
  230. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/templates/[...slug]-935113d252ada806.js +1 -0
  231. mage_ai/server/{frontend_dist/_next/static/chunks/pages/templates-25b6d24cfa81c306.js → frontend_dist_base_path_template/_next/static/chunks/pages/templates-7079d637e396f2a8.js} +1 -1
  232. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/terminal-67fdb4a3be93aa14.js +1 -0
  233. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/triggers-9c0374c7c783b34a.js +1 -0
  234. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/version-control-ea3a0d48e5822b42.js +1 -0
  235. mage_ai/server/frontend_dist_base_path_template/block-layout.html +2 -2
  236. mage_ai/server/frontend_dist_base_path_template/files.html +5 -5
  237. mage_ai/server/frontend_dist_base_path_template/global-data-products/[...slug].html +5 -5
  238. mage_ai/server/frontend_dist_base_path_template/global-data-products.html +5 -5
  239. mage_ai/server/frontend_dist_base_path_template/index.html +2 -2
  240. mage_ai/server/frontend_dist_base_path_template/manage/settings.html +5 -5
  241. mage_ai/server/frontend_dist_base_path_template/manage/users/[user].html +5 -5
  242. mage_ai/server/frontend_dist_base_path_template/manage/users/new.html +5 -5
  243. mage_ai/server/frontend_dist_base_path_template/manage/users.html +5 -5
  244. mage_ai/server/frontend_dist_base_path_template/manage.html +5 -5
  245. mage_ai/server/frontend_dist_base_path_template/overview.html +5 -5
  246. mage_ai/server/frontend_dist_base_path_template/pipeline-runs.html +5 -5
  247. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/backfills/[...slug].html +5 -5
  248. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/backfills.html +5 -5
  249. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/dashboard.html +5 -5
  250. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/edit.html +2 -2
  251. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/logs.html +5 -5
  252. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors/block-runs.html +5 -5
  253. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors/block-runtime.html +5 -5
  254. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors.html +5 -5
  255. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/runs/[run].html +5 -5
  256. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/runs.html +5 -5
  257. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/settings.html +5 -5
  258. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/syncs.html +5 -5
  259. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/triggers/[...slug].html +5 -5
  260. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/triggers.html +5 -5
  261. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline].html +2 -2
  262. mage_ai/server/frontend_dist_base_path_template/pipelines.html +5 -5
  263. mage_ai/server/frontend_dist_base_path_template/settings/account/profile.html +5 -5
  264. mage_ai/server/frontend_dist_base_path_template/settings/workspace/permissions/[...slug].html +24 -0
  265. mage_ai/server/frontend_dist_base_path_template/settings/workspace/permissions.html +24 -0
  266. mage_ai/server/frontend_dist_base_path_template/settings/workspace/preferences.html +5 -5
  267. mage_ai/server/frontend_dist_base_path_template/settings/workspace/roles/[...slug].html +24 -0
  268. mage_ai/server/frontend_dist_base_path_template/settings/workspace/roles.html +24 -0
  269. mage_ai/server/frontend_dist_base_path_template/settings/workspace/sync-data.html +5 -5
  270. mage_ai/server/frontend_dist_base_path_template/settings/workspace/users/[...slug].html +24 -0
  271. mage_ai/server/frontend_dist_base_path_template/settings/workspace/users.html +5 -5
  272. mage_ai/server/frontend_dist_base_path_template/settings.html +2 -2
  273. mage_ai/server/frontend_dist_base_path_template/sign-in.html +13 -13
  274. mage_ai/server/frontend_dist_base_path_template/templates/[...slug].html +5 -5
  275. mage_ai/server/frontend_dist_base_path_template/templates.html +5 -5
  276. mage_ai/server/frontend_dist_base_path_template/terminal.html +5 -5
  277. mage_ai/server/frontend_dist_base_path_template/test.html +4 -4
  278. mage_ai/server/frontend_dist_base_path_template/triggers.html +5 -5
  279. mage_ai/server/frontend_dist_base_path_template/version-control.html +5 -5
  280. mage_ai/server/server.py +37 -2
  281. mage_ai/services/aws/__init__.py +19 -8
  282. mage_ai/services/spark/config.py +5 -1
  283. mage_ai/services/spark/spark.py +5 -4
  284. mage_ai/settings/__init__.py +2 -0
  285. mage_ai/streaming/constants.py +1 -0
  286. mage_ai/streaming/sinks/google_cloud_pubsub.py +66 -0
  287. mage_ai/streaming/sinks/sink_factory.py +6 -0
  288. mage_ai/tests/api/operations/test_operations.py +8 -2
  289. mage_ai/tests/api/operations/test_pipeline_schedules.py +3 -0
  290. mage_ai/tests/data_preparation/models/block/sql/utils/test_shared.py +130 -5
  291. mage_ai/tests/factory.py +2 -0
  292. mage_ai/tests/orchestration/test_pipeline_scheduler.py +20 -3
  293. mage_ai/tests/streaming/sinks/test_google_cloud_pubsub.py +28 -0
  294. mage_ai/usage_statistics/logger.py +4 -1
  295. {mage_ai-0.9.32.dist-info → mage_ai-0.9.34.dist-info}/METADATA +1 -1
  296. {mage_ai-0.9.32.dist-info → mage_ai-0.9.34.dist-info}/RECORD +302 -268
  297. mage_ai/api/policies/utils.py +0 -14
  298. mage_ai/server/frontend_dist/_next/static/cbN7L4Ms6UUPoANozEh9w/_buildManifest.js +0 -1
  299. mage_ai/server/frontend_dist/_next/static/chunks/1769-e2efd4df8d09481f.js +0 -1
  300. mage_ai/server/frontend_dist/_next/static/chunks/2327-2d8a1555605cf4af.js +0 -1
  301. mage_ai/server/frontend_dist/_next/static/chunks/2369-cb9cd97052e18d27.js +0 -1
  302. mage_ai/server/frontend_dist/_next/static/chunks/2677-aea54e655c4a727f.js +0 -1
  303. mage_ai/server/frontend_dist/_next/static/chunks/5457-994e1044953f1425.js +0 -1
  304. mage_ai/server/frontend_dist/_next/static/chunks/5597-c034402ee26af3b4.js +0 -1
  305. mage_ai/server/frontend_dist/_next/static/chunks/6333-d1f8db4e7d9656a5.js +0 -1
  306. mage_ai/server/frontend_dist/_next/static/chunks/6648-9bd31397f1d1b551.js +0 -1
  307. mage_ai/server/frontend_dist/_next/static/chunks/9161-0101571a3635a938.js +0 -1
  308. mage_ai/server/frontend_dist/_next/static/chunks/9264-7c4fcfed1200046a.js +0 -1
  309. mage_ai/server/frontend_dist/_next/static/chunks/9696-3e8ab7786f0e3a0e.js +0 -1
  310. mage_ai/server/frontend_dist/_next/static/chunks/pages/_app-976b488b8aea327f.js +0 -1
  311. mage_ai/server/frontend_dist/_next/static/chunks/pages/files-1a6c6654bfc953ac.js +0 -1
  312. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/settings-49004d918b04c866.js +0 -1
  313. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/users/[user]-97177740fbb6eb9f.js +0 -1
  314. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/users/new-5b6411c9eb8c4ba4.js +0 -1
  315. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/users-0a7a746cc0998608.js +0 -1
  316. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage-606012a580245001.js +0 -1
  317. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipeline-runs-0801de3c9a2976ea.js +0 -1
  318. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills-0a5865d4cf012127.js +0 -1
  319. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/edit-87b1a0601c4236cd.js +0 -1
  320. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/triggers-77ffaa2d721a0a4e.js +0 -1
  321. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/account/profile-4e72d40881ac0605.js +0 -1
  322. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/preferences-8da8f8e2fdb1c99f.js +0 -1
  323. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/sync-data-be029347e99fcea2.js +0 -1
  324. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/users-d4b07df78b3d6037.js +0 -1
  325. mage_ai/server/frontend_dist/_next/static/chunks/pages/templates/[...slug]-87c492e9edc6064e.js +0 -1
  326. mage_ai/server/frontend_dist/_next/static/chunks/pages/terminal-2ab9f3d0c50cce81.js +0 -1
  327. mage_ai/server/frontend_dist/_next/static/chunks/pages/triggers-0ba40243a030ddf4.js +0 -1
  328. mage_ai/server/frontend_dist/_next/static/chunks/pages/version-control-28a3a43d65074394.js +0 -1
  329. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1769-e2efd4df8d09481f.js +0 -1
  330. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/2327-2d8a1555605cf4af.js +0 -1
  331. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/2369-cb9cd97052e18d27.js +0 -1
  332. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/2677-aea54e655c4a727f.js +0 -1
  333. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/5457-994e1044953f1425.js +0 -1
  334. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/5597-c034402ee26af3b4.js +0 -1
  335. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/6333-d1f8db4e7d9656a5.js +0 -1
  336. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/6648-9bd31397f1d1b551.js +0 -1
  337. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/9161-0101571a3635a938.js +0 -1
  338. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/9264-7c4fcfed1200046a.js +0 -1
  339. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/9696-3e8ab7786f0e3a0e.js +0 -1
  340. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/_app-976b488b8aea327f.js +0 -1
  341. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/files-1a6c6654bfc953ac.js +0 -1
  342. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/settings-49004d918b04c866.js +0 -1
  343. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/users/[user]-97177740fbb6eb9f.js +0 -1
  344. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/users/new-5b6411c9eb8c4ba4.js +0 -1
  345. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/users-0a7a746cc0998608.js +0 -1
  346. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage-606012a580245001.js +0 -1
  347. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipeline-runs-0801de3c9a2976ea.js +0 -1
  348. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills-0a5865d4cf012127.js +0 -1
  349. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/edit-87b1a0601c4236cd.js +0 -1
  350. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/triggers-77ffaa2d721a0a4e.js +0 -1
  351. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/account/profile-4e72d40881ac0605.js +0 -1
  352. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/preferences-8da8f8e2fdb1c99f.js +0 -1
  353. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/sync-data-be029347e99fcea2.js +0 -1
  354. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/users-d4b07df78b3d6037.js +0 -1
  355. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/templates/[...slug]-87c492e9edc6064e.js +0 -1
  356. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/terminal-2ab9f3d0c50cce81.js +0 -1
  357. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/triggers-0ba40243a030ddf4.js +0 -1
  358. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/version-control-28a3a43d65074394.js +0 -1
  359. mage_ai/server/frontend_dist_base_path_template/_next/static/o_Yc57jh8Te2SI-VoipGQ/_buildManifest.js +0 -1
  360. /mage_ai/server/frontend_dist/_next/static/{cbN7L4Ms6UUPoANozEh9w → PBVuphyo_muEAj347ZP_b}/_ssgManifest.js +0 -0
  361. /mage_ai/server/frontend_dist_base_path_template/_next/static/{o_Yc57jh8Te2SI-VoipGQ → L-IKw5_bRZUs-wyjnpN_j}/_ssgManifest.js +0 -0
  362. {mage_ai-0.9.32.dist-info → mage_ai-0.9.34.dist-info}/LICENSE +0 -0
  363. {mage_ai-0.9.32.dist-info → mage_ai-0.9.34.dist-info}/WHEEL +0 -0
  364. {mage_ai-0.9.32.dist-info → mage_ai-0.9.34.dist-info}/entry_points.txt +0 -0
  365. {mage_ai-0.9.32.dist-info → mage_ai-0.9.34.dist-info}/top_level.txt +0 -0
@@ -1,11 +1,50 @@
1
+ from typing import Dict
2
+
3
+ from mage_ai.api.constants import AttributeOperationType
1
4
  from mage_ai.api.oauth_scope import OauthScope
2
5
  from mage_ai.api.operations import constants
6
+ from mage_ai.api.operations.constants import OperationType
3
7
  from mage_ai.api.policies.BasePolicy import BasePolicy
8
+ from mage_ai.api.policies.mixins.user_permissions import UserPermissionMixIn
4
9
  from mage_ai.api.presenters.StatusPresenter import StatusPresenter
10
+ from mage_ai.shared.hash import merge_dict
11
+
5
12
 
13
+ class StatusPolicy(BasePolicy, UserPermissionMixIn):
14
+ @classmethod
15
+ def action_rule_with_permissions(self, operation: OperationType) -> Dict:
16
+ return merge_dict(super().action_rule_with_permissions(operation), {
17
+ OauthScope.CLIENT_PRIVATE: dict(
18
+ condition=lambda _policy: OperationType.LIST == operation,
19
+ ),
20
+ OauthScope.CLIENT_PUBLIC: dict(
21
+ condition=lambda _policy: OperationType.LIST == operation,
22
+ ),
23
+ })
6
24
 
7
- class StatusPolicy(BasePolicy):
8
- pass
25
+ @classmethod
26
+ def attribute_rule_with_permissions(
27
+ self,
28
+ attribute_operation_type: AttributeOperationType,
29
+ resource_attribute: str,
30
+ ) -> Dict:
31
+ config = {}
32
+ if AttributeOperationType.READ == attribute_operation_type:
33
+ config = self.read_rules[self.__name__].get(resource_attribute)
34
+ else:
35
+ config = self.write_rules[self.__name__].get(resource_attribute)
36
+
37
+ return merge_dict(super().attribute_rule_with_permissions(
38
+ attribute_operation_type,
39
+ resource_attribute,
40
+ ), {
41
+ OauthScope.CLIENT_PRIVATE: {
42
+ OperationType.LIST: config[OauthScope.CLIENT_PRIVATE][OperationType.LIST],
43
+ },
44
+ OauthScope.CLIENT_PUBLIC: {
45
+ OperationType.LIST: config[OauthScope.CLIENT_PUBLIC][OperationType.LIST],
46
+ },
47
+ })
9
48
 
10
49
 
11
50
  StatusPolicy.allow_actions([
@@ -15,7 +54,11 @@ StatusPolicy.allow_actions([
15
54
  OauthScope.CLIENT_PUBLIC,
16
55
  ])
17
56
 
18
- StatusPolicy.allow_read(StatusPresenter.default_attributes, scopes=[
57
+ StatusPolicy.allow_read(StatusPresenter.default_attributes + [
58
+ 'active_pipeline_run_count',
59
+ 'last_scheduler_activity',
60
+ 'last_user_request',
61
+ ], scopes=[
19
62
  OauthScope.CLIENT_PRIVATE,
20
63
  OauthScope.CLIENT_PUBLIC,
21
64
  ], on_action=[
@@ -16,6 +16,7 @@ UserPolicy.allow_actions([
16
16
  OauthScope.CLIENT_PRIVATE,
17
17
  ], condition=lambda policy: policy.is_owner())
18
18
 
19
+
19
20
  UserPolicy.allow_actions([
20
21
  constants.DETAIL,
21
22
  constants.UPDATE,
@@ -23,24 +24,40 @@ UserPolicy.allow_actions([
23
24
  OauthScope.CLIENT_PRIVATE,
24
25
  ], condition=lambda policy: policy.is_current_user() or policy.has_at_least_admin_role())
25
26
 
27
+
26
28
  UserPolicy.allow_actions([
27
29
  constants.LIST,
28
30
  ], scopes=[
29
31
  OauthScope.CLIENT_PRIVATE,
30
32
  ], condition=lambda policy: policy.has_at_least_admin_role())
31
33
 
34
+
32
35
  UserPolicy.allow_read(UserPresenter.default_attributes, scopes=[
33
36
  OauthScope.CLIENT_PRIVATE,
34
37
  ], condition=lambda policy: policy.is_current_user() or policy.has_at_least_admin_role())
35
38
 
39
+
36
40
  UserPolicy.allow_read(UserPresenter.default_attributes + [
41
+ 'permissions',
42
+ 'token',
43
+ ], scopes=[
44
+ OauthScope.CLIENT_PRIVATE,
45
+ ], on_action=[
46
+ constants.DETAIL,
47
+ constants.UPDATE,
48
+ ], condition=lambda policy: policy.is_current_user() or policy.has_at_least_admin_role())
49
+
50
+
51
+ UserPolicy.allow_read(UserPresenter.default_attributes + [
52
+ 'permissions',
37
53
  'token',
38
54
  ], scopes=[
39
55
  OauthScope.CLIENT_PRIVATE,
40
56
  ], on_action=[
41
57
  constants.CREATE,
42
58
  constants.DELETE,
43
- ], condition=lambda policy: policy.is_current_user() or policy.is_owner())
59
+ ], condition=lambda policy: policy.is_owner())
60
+
44
61
 
45
62
  UserPolicy.allow_write([
46
63
  'avatar',
@@ -57,15 +74,19 @@ UserPolicy.allow_write([
57
74
  constants.UPDATE,
58
75
  ], condition=lambda policy: policy.is_current_user() or policy.has_at_least_admin_role())
59
76
 
77
+
60
78
  UserPolicy.allow_write([
79
+ 'role_ids',
61
80
  'roles',
62
- 'roles_new'
81
+ 'roles_new',
63
82
  ], scopes=[
64
83
  OauthScope.CLIENT_PRIVATE,
65
84
  ], on_action=[
85
+ constants.DELETE,
66
86
  constants.UPDATE,
67
87
  ], condition=lambda policy: policy.has_at_least_admin_role())
68
88
 
89
+
69
90
  UserPolicy.allow_write([
70
91
  'owner',
71
92
  ], scopes=[
@@ -74,6 +95,7 @@ UserPolicy.allow_write([
74
95
  constants.UPDATE,
75
96
  ], condition=lambda policy: policy.is_owner())
76
97
 
98
+
77
99
  UserPolicy.allow_write([
78
100
  'avatar',
79
101
  'email',
@@ -81,6 +103,7 @@ UserPolicy.allow_write([
81
103
  'last_name',
82
104
  'password',
83
105
  'password_confirmation',
106
+ 'role_ids',
84
107
  'roles',
85
108
  'roles_new',
86
109
  'username',
File without changes
@@ -0,0 +1,241 @@
1
+ import asyncio
2
+ from typing import Any, Callable, Dict, Tuple
3
+
4
+ from mage_ai.api.constants import (
5
+ ATTRIBUTE_OPERATION_TYPE_DISABLE_TO_ACCESS_MAPPING,
6
+ ATTRIBUTE_OPERATION_TYPE_TO_ACCESS_MAPPING,
7
+ OPERATION_TYPE_DISABLE_TO_ACCESS_MAPPING,
8
+ OPERATION_TYPE_TO_ACCESS_MAPPING,
9
+ AttributeOperationType,
10
+ )
11
+ from mage_ai.api.oauth_scope import OauthScope
12
+ from mage_ai.api.operations.constants import OperationType
13
+ from mage_ai.authentication.permissions.constants import (
14
+ PERMISSION_ACCESS_WITH_MULTIPLE_ACCESS,
15
+ RESERVED_ENTITY_NAMES,
16
+ EntityName,
17
+ PermissionAccess,
18
+ )
19
+ from mage_ai.orchestration.db.models.oauth import Permission
20
+
21
+
22
+ def get_entity_name_from_policy(policy) -> EntityName:
23
+ model_name = policy.model_name()
24
+ if model_name in EntityName._value2member_map_:
25
+ return EntityName(model_name)
26
+
27
+ return None
28
+
29
+
30
+ async def validate_condition_with_permissions(
31
+ policy,
32
+ operation: OperationType,
33
+ attribute_operation_type: AttributeOperationType = None,
34
+ resource_attribute: str = None,
35
+ ) -> bool:
36
+ entity_name = get_entity_name_from_policy(policy)
37
+ access = OPERATION_TYPE_TO_ACCESS_MAPPING.get(operation) or Permission.Access.OWNER
38
+ disable_access = OPERATION_TYPE_DISABLE_TO_ACCESS_MAPPING.get(operation)
39
+
40
+ access_for_attribute_operation = None
41
+ disable_access_for_attribute_operation = None
42
+ if attribute_operation_type:
43
+ access_for_attribute_operation = ATTRIBUTE_OPERATION_TYPE_TO_ACCESS_MAPPING.get(
44
+ attribute_operation_type,
45
+ )
46
+ disable_access_for_attribute_operation = \
47
+ ATTRIBUTE_OPERATION_TYPE_DISABLE_TO_ACCESS_MAPPING.get(
48
+ attribute_operation_type,
49
+ )
50
+
51
+ permissions = await (policy.resource or policy).load_and_cache_user_permissions()
52
+
53
+ async def __permission_grants_access(
54
+ permission: Permission,
55
+ entity_name=entity_name,
56
+ access=access,
57
+ disable_access=disable_access,
58
+ access_for_attribute_operation=access_for_attribute_operation,
59
+ disable_access_for_attribute_operation=disable_access_for_attribute_operation,
60
+ attribute_operation_type=attribute_operation_type,
61
+ resource_attribute=resource_attribute,
62
+ ) -> Tuple[bool, bool]:
63
+ if permission.access is None:
64
+ return (False, False)
65
+
66
+ # Check if user is an owner
67
+ if permission.access & PermissionAccess.OWNER:
68
+ return (True, False)
69
+
70
+ # 1. Get permissions for current entity_name for roles belonging to current user.
71
+ # Include permissions where entity_name is ALL or ALL_EXCEPT_RESERVED.
72
+ correct_entity_name = permission.entity_name == entity_name or \
73
+ permission.entity_name == EntityName.ALL or \
74
+ (
75
+ permission.entity_name == EntityName.ALL_EXCEPT_RESERVED and
76
+ entity_name not in RESERVED_ENTITY_NAMES
77
+ )
78
+
79
+ if not correct_entity_name:
80
+ return (False, False)
81
+
82
+ # 2a. Don’t grant access if permission disables access to this entity for this operation.
83
+ if disable_access is not None and permission.access & disable_access:
84
+ return (False, True)
85
+
86
+ # 3. Add additional permission access (e.g. read, list, detail for viewer)
87
+ # to grant access to this entity and its attributes
88
+ permission_accesses = PERMISSION_ACCESS_WITH_MULTIPLE_ACCESS.get(f'{permission.access}')
89
+ if not permission_accesses:
90
+ permission_accesses = [permission.access]
91
+
92
+ permission_granted = False
93
+ permission_disabled = False
94
+
95
+ for permission_access in permission_accesses:
96
+ if permission_granted:
97
+ break
98
+
99
+ # Access to all operations and attribute operations
100
+ if permission_access & PermissionAccess.ALL.value:
101
+ permission_granted = True
102
+ break
103
+
104
+ # Disable all operations
105
+ if permission_access & PermissionAccess.DISABLE_OPERATION_ALL.value:
106
+ permission_disabled = True
107
+ break
108
+
109
+ has_access_for_all_operations = permission_access & PermissionAccess.OPERATION_ALL
110
+ valid_for_operation = has_access_for_all_operations or permission_access & access
111
+
112
+ # If this condition is validating attribute operations for an attribute:
113
+ if access_for_attribute_operation:
114
+ access_for_all = 0
115
+ disable_access_for_all = 0
116
+
117
+ if AttributeOperationType.QUERY == attribute_operation_type:
118
+ access_for_all = PermissionAccess.QUERY_ALL
119
+ disable_access_for_all = PermissionAccess.DISABLE_QUERY_ALL
120
+ elif AttributeOperationType.READ == attribute_operation_type:
121
+ access_for_all = PermissionAccess.READ_ALL
122
+ disable_access_for_all = PermissionAccess.DISABLE_READ_ALL
123
+ elif AttributeOperationType.WRITE == attribute_operation_type:
124
+ access_for_all = PermissionAccess.WRITE_ALL
125
+ disable_access_for_all = PermissionAccess.DISABLE_WRITE_ALL
126
+
127
+ if permission_access & disable_access_for_all:
128
+ permission_disabled = True
129
+ break
130
+
131
+ has_access_for_all_attributes = permission_access & access_for_all
132
+ valid_for_operation = valid_for_operation and \
133
+ (
134
+ permission_access & access_for_attribute_operation or
135
+ has_access_for_all_attributes
136
+ )
137
+
138
+ # Don’t grant access if permission disables access to this entity’s attributes
139
+ # for this attribute operation.
140
+ if disable_access_for_attribute_operation is not None and \
141
+ permission.access & disable_access_for_attribute_operation:
142
+
143
+ disabled_attributes = []
144
+ if AttributeOperationType.QUERY == attribute_operation_type:
145
+ disabled_attributes = permission.query_attributes
146
+ elif AttributeOperationType.READ == attribute_operation_type:
147
+ disabled_attributes = permission.read_attributes
148
+ elif AttributeOperationType.WRITE == attribute_operation_type:
149
+ disabled_attributes = permission.write_attributes
150
+
151
+ if resource_attribute in (disabled_attributes or []):
152
+ permission_disabled = True
153
+ break
154
+
155
+ if not has_access_for_all_attributes:
156
+ if valid_for_operation and attribute_operation_type and resource_attribute:
157
+ permitted_attributes = []
158
+ if AttributeOperationType.QUERY == attribute_operation_type:
159
+ permitted_attributes = permission.query_attributes
160
+ elif AttributeOperationType.READ == attribute_operation_type:
161
+ permitted_attributes = permission.read_attributes
162
+ elif AttributeOperationType.WRITE == attribute_operation_type:
163
+ permitted_attributes = permission.write_attributes
164
+
165
+ valid_for_operation = resource_attribute in (permitted_attributes or [])
166
+
167
+ if valid_for_operation:
168
+ permission_granted = True
169
+ break
170
+
171
+ return (permission_granted, permission_disabled)
172
+
173
+ validation_results = await asyncio.gather(
174
+ *[__permission_grants_access(p) for p in permissions]
175
+ )
176
+
177
+ authorized = False
178
+ unauthorized = False
179
+ for permission_granted, permission_disabled in validation_results:
180
+ if permission_disabled:
181
+ unauthorized = True
182
+ break
183
+
184
+ if permission_granted and not authorized:
185
+ authorized = True
186
+
187
+ return authorized and not unauthorized
188
+
189
+
190
+ class UserPermissionMixIn:
191
+ @classmethod
192
+ def action_rule_with_permissions(self, operation: OperationType) -> Dict:
193
+ return {
194
+ OauthScope.CLIENT_PRIVATE: dict(
195
+ condition=self.build_validate_condition(operation),
196
+ ),
197
+ }
198
+
199
+ @classmethod
200
+ def attribute_rule_with_permissions(
201
+ self,
202
+ attribute_operation_type: AttributeOperationType,
203
+ resource_attribute: str,
204
+ ) -> Dict:
205
+ conditions = {}
206
+ for operation in OperationType:
207
+ conditions[operation.value] = dict(
208
+ condition=self.build_validate_attribute(
209
+ operation,
210
+ attribute_operation_type,
211
+ resource_attribute,
212
+ ),
213
+ )
214
+
215
+ return {
216
+ OauthScope.CLIENT_PRIVATE: conditions,
217
+ }
218
+
219
+ @classmethod
220
+ def build_validate_attribute(
221
+ self,
222
+ operation: OperationType,
223
+ attribute_operation_type: AttributeOperationType,
224
+ resource_attribute: str,
225
+ ) -> Callable[[Any], bool]:
226
+ def _validate_condition(policy) -> bool:
227
+ return validate_condition_with_permissions(
228
+ policy,
229
+ operation,
230
+ attribute_operation_type=attribute_operation_type,
231
+ resource_attribute=resource_attribute,
232
+ )
233
+
234
+ return _validate_condition
235
+
236
+ @classmethod
237
+ def build_validate_condition(self, operation: OperationType) -> Callable[[Any], bool]:
238
+ def _validate_condition(policy) -> bool:
239
+ return validate_condition_with_permissions(policy, operation)
240
+
241
+ return _validate_condition
@@ -1,5 +1,12 @@
1
+ from typing import List
2
+
1
3
  from mage_ai.api.operations import constants
2
4
  from mage_ai.api.presenters.BasePresenter import BasePresenter
5
+ from mage_ai.authentication.permissions.constants import (
6
+ BlockEntityType,
7
+ EntityName,
8
+ PipelineEntityType,
9
+ )
3
10
 
4
11
 
5
12
  class PermissionPresenter(BasePresenter):
@@ -14,6 +21,14 @@ class PermissionPresenter(BasePresenter):
14
21
  'updated_at',
15
22
  ]
16
23
 
24
+ def entity_names(self, **kwargs) -> List[str]:
25
+ return sorted([n for n in EntityName])
26
+
27
+ def entity_types(self, **kwargs) -> List[str]:
28
+ return sorted(
29
+ [n for n in BlockEntityType] + [n for n in PipelineEntityType],
30
+ )
31
+
17
32
 
18
33
  PermissionPresenter.register_format(
19
34
  f'role/{constants.DETAIL}',
@@ -27,14 +42,26 @@ PermissionPresenter.register_format(
27
42
 
28
43
  PermissionPresenter.register_formats([
29
44
  constants.CREATE,
45
+ constants.DELETE,
30
46
  constants.DETAIL,
31
47
  constants.UPDATE,
32
48
  ], PermissionPresenter.default_attributes + [
33
- 'query_attributes',
34
- 'read_attributes',
35
- 'role',
36
- 'roles',
37
- 'user_id',
38
- 'write_attributes',
39
- ],
40
- )
49
+ 'entity_names',
50
+ 'entity_types',
51
+ 'query_attributes',
52
+ 'read_attributes',
53
+ 'role',
54
+ 'roles',
55
+ 'user',
56
+ 'user_id',
57
+ 'users',
58
+ 'write_attributes',
59
+ ])
60
+
61
+
62
+ PermissionPresenter.register_formats([
63
+ 'with_only_entity_options',
64
+ ], [
65
+ 'entity_names',
66
+ 'entity_types',
67
+ ])
@@ -10,6 +10,7 @@ class RolePresenter(BasePresenter):
10
10
  'permissions',
11
11
  'role_permissions',
12
12
  'updated_at',
13
+ 'user',
13
14
  ]
14
15
 
15
16
 
@@ -25,12 +26,14 @@ RolePresenter.register_format(
25
26
  )
26
27
 
27
28
 
28
- RolePresenter.register_format(
29
+ RolePresenter.register_formats([
29
30
  constants.DETAIL,
30
- RolePresenter.default_attributes + [
31
- 'users',
32
- ],
33
- )
31
+ constants.CREATE,
32
+ constants.UPDATE,
33
+ constants.DELETE,
34
+ ], RolePresenter.default_attributes + [
35
+ 'users',
36
+ ])
34
37
 
35
38
 
36
39
  RolePresenter.register_formats([
@@ -38,10 +41,24 @@ RolePresenter.register_formats([
38
41
  f'permission/{constants.DETAIL}',
39
42
  f'permission/{constants.UPDATE}',
40
43
  ], [
41
- 'created_at',
42
- 'id',
43
- 'name',
44
- 'updated_at',
45
- 'users',
46
- ],
47
- )
44
+ 'created_at',
45
+ 'id',
46
+ 'name',
47
+ 'updated_at',
48
+ 'users',
49
+ ])
50
+
51
+
52
+ RolePresenter.register_formats([
53
+ f'user/{constants.CREATE}',
54
+ f'user/{constants.DELETE}',
55
+ f'user/{constants.DETAIL}',
56
+ f'user/{constants.LIST}',
57
+ f'user/{constants.UPDATE}',
58
+ ], [
59
+ 'created_at',
60
+ 'id',
61
+ 'name',
62
+ 'permissions',
63
+ 'updated_at',
64
+ ])
@@ -10,6 +10,17 @@ class StatusPresenter(BasePresenter):
10
10
  'instance_type',
11
11
  'disable_pipeline_edit_access',
12
12
  'require_user_authentication',
13
+ 'require_user_permissions',
13
14
  'project_type',
14
15
  'project_uuid',
15
16
  ]
17
+
18
+
19
+ StatusPresenter.register_format(
20
+ 'with_activity_details',
21
+ StatusPresenter.default_attributes + [
22
+ 'active_pipeline_run_count',
23
+ 'last_scheduler_activity',
24
+ 'last_user_request',
25
+ ],
26
+ )
@@ -1,46 +1,43 @@
1
1
  from mage_ai.api.operations import constants
2
2
  from mage_ai.api.presenters.BasePresenter import BasePresenter
3
- from mage_ai.shared.hash import extract
4
3
 
5
4
 
6
5
  class UserPresenter(BasePresenter):
7
6
  default_attributes = [
8
7
  'avatar',
8
+ 'created_at',
9
9
  'email',
10
10
  'first_name',
11
11
  'id',
12
12
  'last_name',
13
13
  'owner',
14
+ 'project_access',
14
15
  'roles',
15
16
  'roles_display',
16
17
  'roles_new',
17
- 'project_access',
18
+ 'updated_at',
18
19
  'username',
19
20
  ]
20
21
 
21
- async def prepare_present(self, **kwargs):
22
- data = self.model.to_dict(include_attributes=self.default_attributes)
23
- data = extract(data, self.default_attributes, include_blank_values=True)
24
-
25
- roles_new = self.model.roles_new
26
- data['roles_new'] = [
27
- role.to_dict(include_attributes=['permissions'])
28
- for role in roles_new
29
- ]
30
-
31
- return data
32
-
33
22
 
34
- UserPresenter.register_format(
35
- constants.CREATE, UserPresenter.default_attributes + ['token', ])
23
+ UserPresenter.register_formats([
24
+ constants.CREATE,
25
+ constants.DELETE,
26
+ constants.DETAIL,
27
+ constants.UPDATE,
28
+ ], UserPresenter.default_attributes + [
29
+ 'permissions',
30
+ 'token',
31
+ ])
36
32
 
37
33
 
38
- UserPresenter.register_format(
34
+ UserPresenter.register_formats([
35
+ f'permission/{constants.DETAIL}',
39
36
  f'role/{constants.DETAIL}',
40
- [
41
- 'first_name',
42
- 'id',
43
- 'last_name',
44
- 'username',
45
- ],
46
- )
37
+ f'role/{constants.LIST}',
38
+ ], [
39
+ 'first_name',
40
+ 'id',
41
+ 'last_name',
42
+ 'username',
43
+ ])
@@ -5,6 +5,7 @@ import inflection
5
5
 
6
6
  from mage_ai import settings
7
7
  from mage_ai.api.errors import ApiError
8
+ from mage_ai.api.mixins.result_set import ResultSetMixIn
8
9
  from mage_ai.api.resources.Resource import Resource
9
10
  from mage_ai.api.resources.shared import collective_loaders
10
11
  from mage_ai.api.result_set import ResultSet
@@ -12,7 +13,7 @@ from mage_ai.orchestration.db.errors import DoesNotExistError
12
13
  from mage_ai.shared.hash import merge_dict
13
14
 
14
15
 
15
- class BaseResource(Resource):
16
+ class BaseResource(Resource, ResultSetMixIn):
16
17
  collective_loader_attr = {}
17
18
  datetime_keys = []
18
19
 
@@ -25,11 +26,15 @@ class BaseResource(Resource):
25
26
  parent_models_attr = {}
26
27
  parent_resource_attr = {}
27
28
 
29
+ @classmethod
30
+ def model_name(self) -> str:
31
+ return self.__name__.replace('Resource', '')
32
+
28
33
  @classmethod
29
34
  def parser_class(self):
30
- klass = self.__name__.replace('Resource', '')
31
- module_name = f'mage_ai.api.parsers.{klass}Parser'
32
- class_name = f'{klass}Parser'
35
+ model_name = self.model_name()
36
+ module_name = f'mage_ai.api.parsers.{model_name}Parser'
37
+ class_name = f'{model_name}Parser'
33
38
 
34
39
  try:
35
40
  return getattr(importlib.import_module(module_name), class_name)
@@ -38,7 +43,7 @@ class BaseResource(Resource):
38
43
 
39
44
  @classmethod
40
45
  def policy_class(self):
41
- model_name = self.__name__.replace('Resource', '')
46
+ model_name = self.model_name()
42
47
  return getattr(
43
48
  importlib.import_module(
44
49
  'mage_ai.api.policies.{}Policy'.format(model_name)),
@@ -47,7 +52,7 @@ class BaseResource(Resource):
47
52
 
48
53
  @classmethod
49
54
  def presenter_class(self):
50
- model_name = self.__name__.replace('Resource', '')
55
+ model_name = self.model_name()
51
56
  return getattr(
52
57
  importlib.import_module(
53
58
  'mage_ai.api.presenters.{}Presenter'.format(model_name)),
@@ -151,7 +156,12 @@ class BaseResource(Resource):
151
156
  pass
152
157
 
153
158
  @classmethod
154
- async def process_create(self, payload, user, **kwargs):
159
+ async def process_create(
160
+ self,
161
+ payload,
162
+ user,
163
+ **kwargs,
164
+ ):
155
165
  self.on_create_callback = None
156
166
  self.on_create_failure_callback = None
157
167
  before_create = self.before_create(payload, user, **kwargs)
@@ -259,11 +269,16 @@ class BaseResource(Resource):
259
269
 
260
270
  raise err
261
271
 
262
- def result_set(self):
272
+ def result_set(self) -> ResultSet:
263
273
  if self.__result_sets().get(self.__class__.__name__, None):
264
274
  return self.__result_sets()[self.__class__.__name__]
265
275
  elif not self.result_set_attr:
266
- self.result_set_attr = ResultSet([self])
276
+ if self.result_set_from_external is not None:
277
+ self.result_set_attr = self.result_set_from_external
278
+ if self not in self.result_set_attr:
279
+ self.result_set_attr.add_results([self])
280
+ else:
281
+ self.result_set_attr = ResultSet([self])
267
282
  return self.result_set_attr
268
283
 
269
284
  def update(self, payload, **kwargs):