mage-ai 0.9.44__py3-none-any.whl → 0.9.46__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.
- mage_ai/api/constants.py +2 -0
- mage_ai/api/operations/base.py +208 -12
- mage_ai/api/policies/BasePolicy.py +3 -3
- mage_ai/api/policies/ComputeClusterPolicy.py +96 -0
- mage_ai/api/policies/ComputeConnectionPolicy.py +74 -0
- mage_ai/api/policies/ComputeServicePolicy.py +50 -0
- mage_ai/api/policies/DownloadPolicy.py +29 -0
- mage_ai/api/policies/GitCustomBranchPolicy.py +1 -0
- mage_ai/api/policies/GlobalHookPolicy.py +126 -0
- mage_ai/api/policies/OauthPolicy.py +56 -7
- mage_ai/api/policies/ProjectPolicy.py +1 -0
- mage_ai/api/policies/RolePolicy.py +12 -1
- mage_ai/api/presenters/ComputeClusterPresenter.py +19 -0
- mage_ai/api/presenters/ComputeConnectionPresenter.py +26 -0
- mage_ai/api/presenters/ComputeServicePresenter.py +15 -0
- mage_ai/api/presenters/DownloadPresenter.py +13 -0
- mage_ai/api/presenters/GlobalHookPresenter.py +74 -0
- mage_ai/api/presenters/OauthPresenter.py +1 -0
- mage_ai/api/presenters/PipelinePresenter.py +1 -0
- mage_ai/api/presenters/ProjectPresenter.py +1 -0
- mage_ai/api/presenters/SparkApplicationPresenter.py +2 -0
- mage_ai/api/presenters/WorkspacePresenter.py +22 -22
- mage_ai/api/resources/AsyncBaseResource.py +39 -0
- mage_ai/api/resources/BackfillResource.py +1 -0
- mage_ai/api/resources/BlockResource.py +57 -0
- mage_ai/api/resources/ClusterResource.py +1 -1
- mage_ai/api/resources/ComputeClusterResource.py +109 -0
- mage_ai/api/resources/ComputeConnectionResource.py +103 -0
- mage_ai/api/resources/ComputeServiceResource.py +35 -0
- mage_ai/api/resources/DownloadResource.py +56 -0
- mage_ai/api/resources/ExecutionStateResource.py +1 -1
- mage_ai/api/resources/GitBranchResource.py +35 -29
- mage_ai/api/resources/GitCustomBranchResource.py +9 -0
- mage_ai/api/resources/GlobalHookResource.py +192 -0
- mage_ai/api/resources/KernelResource.py +10 -0
- mage_ai/api/resources/OauthResource.py +60 -98
- mage_ai/api/resources/PipelineResource.py +4 -4
- mage_ai/api/resources/PipelineScheduleResource.py +37 -16
- mage_ai/api/resources/ProjectResource.py +5 -3
- mage_ai/api/resources/SessionResource.py +24 -9
- mage_ai/api/resources/SparkApplicationResource.py +5 -5
- mage_ai/api/resources/SparkEnvironmentResource.py +1 -2
- mage_ai/api/resources/SparkExecutorResource.py +1 -2
- mage_ai/api/resources/SparkJobResource.py +3 -6
- mage_ai/api/resources/SparkSqlResource.py +6 -11
- mage_ai/api/resources/SparkStageAttemptResource.py +2 -3
- mage_ai/api/resources/SparkStageAttemptTaskResource.py +1 -2
- mage_ai/api/resources/SparkStageAttemptTaskSummaryResource.py +1 -2
- mage_ai/api/resources/SparkStageResource.py +3 -6
- mage_ai/api/resources/SparkThreadResource.py +1 -2
- mage_ai/api/resources/UserResource.py +32 -31
- mage_ai/api/resources/mixins/spark.py +25 -4
- mage_ai/authentication/oauth/constants.py +4 -0
- mage_ai/authentication/oauth2.py +1 -3
- mage_ai/authentication/permissions/constants.py +4 -0
- mage_ai/authentication/providers/__init__.py +0 -0
- mage_ai/authentication/providers/active_directory.py +136 -0
- mage_ai/authentication/providers/constants.py +17 -0
- mage_ai/authentication/providers/ghe.py +81 -0
- mage_ai/authentication/providers/google.py +86 -0
- mage_ai/authentication/providers/oauth.py +60 -0
- mage_ai/authentication/providers/okta.py +101 -0
- mage_ai/authentication/providers/sso.py +20 -0
- mage_ai/authentication/providers/utils.py +12 -0
- mage_ai/cluster_manager/aws/emr_cluster_manager.py +2 -1
- mage_ai/cluster_manager/workspace/base.py +7 -3
- mage_ai/data_integrations/destinations/constants.py +2 -0
- mage_ai/data_preparation/executors/block_executor.py +3 -0
- mage_ai/data_preparation/executors/pipeline_executor.py +9 -0
- mage_ai/data_preparation/git/__init__.py +3 -3
- mage_ai/data_preparation/git/api.py +69 -35
- mage_ai/data_preparation/models/block/__init__.py +95 -60
- mage_ai/data_preparation/models/block/data_integration/data.py +1 -1
- mage_ai/data_preparation/models/block/dbt/block_sql.py +95 -1
- mage_ai/data_preparation/models/block/dbt/utils.py +6 -0
- mage_ai/data_preparation/models/block/spark/mixins.py +82 -34
- mage_ai/data_preparation/models/download/__init__.py +8 -0
- mage_ai/data_preparation/models/global_hooks/__init__.py +0 -0
- mage_ai/data_preparation/models/global_hooks/constants.py +44 -0
- mage_ai/data_preparation/models/global_hooks/models.py +928 -0
- mage_ai/data_preparation/models/global_hooks/utils.py +21 -0
- mage_ai/data_preparation/models/pipeline.py +82 -6
- mage_ai/data_preparation/models/pipelines/models.py +16 -0
- mage_ai/data_preparation/models/project/__init__.py +6 -0
- mage_ai/data_preparation/models/project/constants.py +1 -0
- mage_ai/data_preparation/models/project/models.py +12 -0
- mage_ai/data_preparation/repo_manager.py +23 -1
- mage_ai/data_preparation/templates/callbacks/base.jinja +4 -0
- mage_ai/data_preparation/templates/data_exporters/streaming/kafka.yaml +1 -0
- mage_ai/data_preparation/templates/data_loaders/streaming/activemq.yaml +6 -0
- mage_ai/data_preparation/templates/data_loaders/streaming/kafka.yaml +1 -0
- mage_ai/data_preparation/templates/data_loaders/streaming/nats.yaml +20 -0
- mage_ai/data_preparation/templates/repo/io_config.yaml +2 -2
- mage_ai/io/duckdb.py +0 -1
- mage_ai/orchestration/concurrency.py +8 -1
- mage_ai/orchestration/db/models/schedules.py +23 -2
- mage_ai/orchestration/pipeline_scheduler.py +168 -105
- mage_ai/orchestration/queue/process_queue.py +9 -1
- mage_ai/orchestration/triggers/api.py +11 -3
- mage_ai/orchestration/triggers/constants.py +1 -0
- mage_ai/orchestration/triggers/utils.py +23 -0
- mage_ai/server/active_kernel.py +37 -4
- mage_ai/server/api/downloads.py +76 -1
- mage_ai/server/api/triggers.py +1 -0
- mage_ai/server/constants.py +1 -1
- mage_ai/server/frontend_dist/404.html +2 -2
- mage_ai/server/frontend_dist/_next/static/9jB4XPuz6BzxBcG9VNao5/_buildManifest.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/1749-9a6276b2918fdae1.js +1 -0
- mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/7519-8c29bbb92e03cc77.js → frontend_dist/_next/static/chunks/1845-5ce774d5ab81ed57.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/1952-ac7722e8b1ab88fe.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/3419-f8d518d024e7b5c8.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/4267-fd4d8049e83178de.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/4366-93e09e5a4a7e182c.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/5457-949640f4037bf12f.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/5499-76cf8f023c6b0985.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/553-7f7919e14392ca67.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/5638-a65610405a70961c.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/5810-12eadc488265d55b.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/5820-28adeabb5cda2b96.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/595-0d174b1f9fbfce4f.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/600-705fe234320ec5de.js +1 -0
- mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/3122-f27de34d0b5426af.js → frontend_dist/_next/static/chunks/6285-e9b45335bfb9ccaf.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/6333-bc1b433b428a9095.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/{5397-b5f2e477acc6bd6b.js → 6965-c613d1834c8ed92d.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/{7022-18fde36eb46e1d65.js → 7022-070ec0144a4d029c.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/722-a1584445357a276c.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/{7858-c83d25349d0e980f.js → 7858-d9df72e95e438284.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/8264-0d582a6ca33c3dfa.js +1 -0
- mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/3684-cbda680fd8927d07.js → frontend_dist/_next/static/chunks/8432-f191e39f9b5893f2.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/90-a7308bae028d7001.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/9264-1d4f0327d42fed91.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/9618-2c5045255ac5a6e7.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/{976-27c4eed1c9b20c44.js → 976-18c98af60b76f1a7.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/framework-22b71764bf44ede4.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/_app-ebef928183f9a3bb.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/block-layout-a24cb24b6f08bbc9.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/compute-419775ca1293b354.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/files-0f2d4be6fdca86ca.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/global-data-products/{[...slug]-3f6fc312f67ff72e.js → [...slug]-cfd68e760ae00958.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/{global-data-products-f4cd03036c3e8723.js → global-data-products-c3b79ef31007f95b.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/global-hooks/[...slug]-77edfa32d000e88b.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/global-hooks-e561ae38cf5592e8.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/files-449a022f2f0f2d94.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/settings-60845f0b59142f32.js +1 -0
- mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/manage/users/[user]-1d9b298fdceabbf1.js → frontend_dist/_next/static/chunks/pages/manage/users/[user]-9384c5f1efa2ac18.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/users/new-abd8571907664fdf.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/{users-a7c970122a10afdc.js → users-28a930b148d99766.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/manage-f83deb790548693b.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/oauth-8bb62c4f6a511c43.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/overview-f0c40645f385f23f.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/{pipeline-runs-c79819d6826e5416.js → pipeline-runs-b35d37bfba8fbccc.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-10e9a2d19541caa2.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills-c8d3a5289ab93f88.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/dashboard-571c0962333b92f0.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/edit-ff7e9108502f5716.js +1 -0
- mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/logs-a52d2d3e0c2978f4.js → frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/logs-ef680455ae54ccbe.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/monitors/block-runs-ddddcddd2f74b4f6.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/monitors/block-runtime-4a238307feddb522.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/monitors-e051057d9fe94f23.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/runs/{[run]-4977276c8f84b5f4.js → [run]-0691711636fa95c7.js} +1 -1
- mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/runs-ce717786f31e8f04.js → frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/runs-2d20b2cd08907afd.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/settings-2914e326a5f1ffe0.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/syncs-b75bf17498e87354.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-3a7500e6e53084d3.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/triggers-c0e551d265a8d467.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/{[pipeline]-35fe7762cb83a733.js → [pipeline]-02c843b9c8418bb5.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines-e47db5c3eaf683af.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/account/profile-55ac955dfa9a5a8d.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/permissions/{[...slug]-d6a62284c7c99cb3.js → [...slug]-b78b1be5b9ed84b9.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/{permissions-b1389695f758c32b.js → permissions-37b78a436eeab258.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/preferences-29c92a9bc54ae5cd.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/roles/{[...slug]-0bdb66265286ab22.js → [...slug]-db05a80d18c168e5.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/{roles-e04e18fc295ca76a.js → roles-f55c77e4f46c8d33.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/sync-data-2a1f8737561fdd94.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/users/{[...slug]-0267358c91122109.js → [...slug]-e3bf6e5d8bb250c4.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/{users-50fed18bb9b8d142.js → users-20f0a050a42a015d.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/{settings-56f83205752b1323.js → settings-0f0121db7f5ff93d.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/sign-in-99e2748e3c1d57a3.js +1 -0
- mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/templates/[...slug]-9370551ffa462144.js → frontend_dist/_next/static/chunks/pages/templates/[...slug]-f44ccd1499ffd23a.js} +1 -1
- mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/templates-3cff90df13a1a755.js → frontend_dist/_next/static/chunks/pages/templates-1bfaa1c50e844813.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/terminal-ed121e305169cf1c.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/test-9ae68758102cc843.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/{triggers-ffaab4c013e62ba1.js → triggers-572d82d6eb7a5d43.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/version-control-2d26d80370a2e481.js +1 -0
- mage_ai/server/frontend_dist/block-layout.html +2 -2
- mage_ai/server/frontend_dist/compute.html +5 -5
- mage_ai/server/frontend_dist/files.html +5 -5
- mage_ai/server/frontend_dist/global-data-products/[...slug].html +5 -5
- mage_ai/server/frontend_dist/global-data-products.html +5 -5
- mage_ai/server/frontend_dist/global-hooks/[...slug].html +24 -0
- mage_ai/server/frontend_dist/global-hooks.html +24 -0
- mage_ai/server/frontend_dist/index.html +2 -2
- mage_ai/server/frontend_dist/manage/files.html +5 -5
- mage_ai/server/frontend_dist/manage/settings.html +5 -5
- mage_ai/server/frontend_dist/manage/users/[user].html +5 -5
- mage_ai/server/frontend_dist/manage/users/new.html +5 -5
- mage_ai/server/frontend_dist/manage/users.html +5 -5
- mage_ai/server/frontend_dist/manage.html +5 -5
- mage_ai/server/frontend_dist/oauth.html +4 -4
- mage_ai/server/frontend_dist/overview.html +5 -5
- mage_ai/server/frontend_dist/pipeline-runs.html +5 -5
- mage_ai/server/frontend_dist/pipelines/[pipeline]/backfills/[...slug].html +5 -5
- mage_ai/server/frontend_dist/pipelines/[pipeline]/backfills.html +5 -5
- mage_ai/server/frontend_dist/pipelines/[pipeline]/dashboard.html +5 -5
- mage_ai/server/frontend_dist/pipelines/[pipeline]/edit.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/logs.html +5 -5
- mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors/block-runs.html +5 -5
- mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors/block-runtime.html +5 -5
- mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors.html +5 -5
- mage_ai/server/frontend_dist/pipelines/[pipeline]/runs/[run].html +5 -5
- mage_ai/server/frontend_dist/pipelines/[pipeline]/runs.html +5 -5
- mage_ai/server/frontend_dist/pipelines/[pipeline]/settings.html +5 -5
- mage_ai/server/frontend_dist/pipelines/[pipeline]/syncs.html +5 -5
- mage_ai/server/frontend_dist/pipelines/[pipeline]/triggers/[...slug].html +5 -5
- mage_ai/server/frontend_dist/pipelines/[pipeline]/triggers.html +5 -5
- mage_ai/server/frontend_dist/pipelines/[pipeline].html +2 -2
- mage_ai/server/frontend_dist/pipelines.html +5 -5
- mage_ai/server/frontend_dist/settings/account/profile.html +5 -5
- mage_ai/server/frontend_dist/settings/workspace/permissions/[...slug].html +5 -5
- mage_ai/server/frontend_dist/settings/workspace/permissions.html +5 -5
- mage_ai/server/frontend_dist/settings/workspace/preferences.html +5 -5
- mage_ai/server/frontend_dist/settings/workspace/roles/[...slug].html +5 -5
- mage_ai/server/frontend_dist/settings/workspace/roles.html +5 -5
- mage_ai/server/frontend_dist/settings/workspace/sync-data.html +5 -5
- mage_ai/server/frontend_dist/settings/workspace/users/[...slug].html +5 -5
- mage_ai/server/frontend_dist/settings/workspace/users.html +5 -5
- mage_ai/server/frontend_dist/settings.html +2 -2
- mage_ai/server/frontend_dist/sign-in.html +17 -17
- mage_ai/server/frontend_dist/templates/[...slug].html +5 -5
- mage_ai/server/frontend_dist/templates.html +5 -5
- mage_ai/server/frontend_dist/terminal.html +5 -5
- mage_ai/server/frontend_dist/test.html +15 -15
- mage_ai/server/frontend_dist/triggers.html +5 -5
- mage_ai/server/frontend_dist/version-control.html +5 -5
- mage_ai/server/frontend_dist_base_path_template/404.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1749-9a6276b2918fdae1.js +1 -0
- mage_ai/server/{frontend_dist/_next/static/chunks/7519-8c29bbb92e03cc77.js → frontend_dist_base_path_template/_next/static/chunks/1845-5ce774d5ab81ed57.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1952-ac7722e8b1ab88fe.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/3419-f8d518d024e7b5c8.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/4267-fd4d8049e83178de.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/4366-93e09e5a4a7e182c.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/5457-949640f4037bf12f.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/5499-76cf8f023c6b0985.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/553-7f7919e14392ca67.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/5638-a65610405a70961c.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/5810-12eadc488265d55b.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/5820-28adeabb5cda2b96.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/595-0d174b1f9fbfce4f.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/600-705fe234320ec5de.js +1 -0
- mage_ai/server/{frontend_dist/_next/static/chunks/3122-f27de34d0b5426af.js → frontend_dist_base_path_template/_next/static/chunks/6285-e9b45335bfb9ccaf.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/6333-bc1b433b428a9095.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/{5397-b5f2e477acc6bd6b.js → 6965-c613d1834c8ed92d.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/{7022-18fde36eb46e1d65.js → 7022-070ec0144a4d029c.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/722-a1584445357a276c.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/{7858-c83d25349d0e980f.js → 7858-d9df72e95e438284.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/8264-0d582a6ca33c3dfa.js +1 -0
- mage_ai/server/{frontend_dist/_next/static/chunks/3684-cbda680fd8927d07.js → frontend_dist_base_path_template/_next/static/chunks/8432-f191e39f9b5893f2.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/90-a7308bae028d7001.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/9264-1d4f0327d42fed91.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/9618-2c5045255ac5a6e7.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/{976-27c4eed1c9b20c44.js → 976-18c98af60b76f1a7.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/framework-22b71764bf44ede4.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/_app-ebef928183f9a3bb.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/block-layout-a24cb24b6f08bbc9.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/compute-419775ca1293b354.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/files-0f2d4be6fdca86ca.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/global-data-products/{[...slug]-3f6fc312f67ff72e.js → [...slug]-cfd68e760ae00958.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/{global-data-products-f4cd03036c3e8723.js → global-data-products-c3b79ef31007f95b.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/global-hooks/[...slug]-77edfa32d000e88b.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/global-hooks-e561ae38cf5592e8.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/files-449a022f2f0f2d94.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/settings-60845f0b59142f32.js +1 -0
- mage_ai/server/{frontend_dist/_next/static/chunks/pages/manage/users/[user]-1d9b298fdceabbf1.js → frontend_dist_base_path_template/_next/static/chunks/pages/manage/users/[user]-9384c5f1efa2ac18.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/users/new-abd8571907664fdf.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/{users-a7c970122a10afdc.js → users-28a930b148d99766.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage-f83deb790548693b.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/oauth-8bb62c4f6a511c43.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/overview-f0c40645f385f23f.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/{pipeline-runs-c79819d6826e5416.js → pipeline-runs-b35d37bfba8fbccc.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-10e9a2d19541caa2.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills-c8d3a5289ab93f88.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/dashboard-571c0962333b92f0.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/edit-ff7e9108502f5716.js +1 -0
- mage_ai/server/{frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/logs-a52d2d3e0c2978f4.js → frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/logs-ef680455ae54ccbe.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/monitors/block-runs-ddddcddd2f74b4f6.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/monitors/block-runtime-4a238307feddb522.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/monitors-e051057d9fe94f23.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/runs/{[run]-4977276c8f84b5f4.js → [run]-0691711636fa95c7.js} +1 -1
- mage_ai/server/{frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/runs-ce717786f31e8f04.js → frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/runs-2d20b2cd08907afd.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/settings-2914e326a5f1ffe0.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/syncs-b75bf17498e87354.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-3a7500e6e53084d3.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/triggers-c0e551d265a8d467.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/{[pipeline]-35fe7762cb83a733.js → [pipeline]-02c843b9c8418bb5.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines-e47db5c3eaf683af.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/account/profile-55ac955dfa9a5a8d.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/permissions/{[...slug]-d6a62284c7c99cb3.js → [...slug]-b78b1be5b9ed84b9.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/{permissions-b1389695f758c32b.js → permissions-37b78a436eeab258.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/preferences-29c92a9bc54ae5cd.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/roles/{[...slug]-0bdb66265286ab22.js → [...slug]-db05a80d18c168e5.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/{roles-e04e18fc295ca76a.js → roles-f55c77e4f46c8d33.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/sync-data-2a1f8737561fdd94.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/users/{[...slug]-0267358c91122109.js → [...slug]-e3bf6e5d8bb250c4.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/{users-50fed18bb9b8d142.js → users-20f0a050a42a015d.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/{settings-56f83205752b1323.js → settings-0f0121db7f5ff93d.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/sign-in-99e2748e3c1d57a3.js +1 -0
- mage_ai/server/{frontend_dist/_next/static/chunks/pages/templates/[...slug]-9370551ffa462144.js → frontend_dist_base_path_template/_next/static/chunks/pages/templates/[...slug]-f44ccd1499ffd23a.js} +1 -1
- mage_ai/server/{frontend_dist/_next/static/chunks/pages/templates-3cff90df13a1a755.js → frontend_dist_base_path_template/_next/static/chunks/pages/templates-1bfaa1c50e844813.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/terminal-ed121e305169cf1c.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/test-9ae68758102cc843.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/{triggers-ffaab4c013e62ba1.js → triggers-572d82d6eb7a5d43.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/version-control-2d26d80370a2e481.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/uPDjJYpJMst1q6psbRyte/_buildManifest.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/block-layout.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/compute.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/files.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/global-data-products/[...slug].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/global-data-products.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/global-hooks/[...slug].html +24 -0
- mage_ai/server/frontend_dist_base_path_template/global-hooks.html +24 -0
- mage_ai/server/frontend_dist_base_path_template/index.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/manage/files.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/manage/settings.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/manage/users/[user].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/manage/users/new.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/manage/users.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/manage.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/oauth.html +3 -3
- mage_ai/server/frontend_dist_base_path_template/overview.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipeline-runs.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/backfills/[...slug].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/backfills.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/dashboard.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/edit.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/logs.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors/block-runs.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors/block-runtime.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/runs/[run].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/runs.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/settings.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/syncs.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/triggers/[...slug].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/triggers.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/account/profile.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/workspace/permissions/[...slug].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/workspace/permissions.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/workspace/preferences.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/workspace/roles/[...slug].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/workspace/roles.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/workspace/sync-data.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/workspace/users/[...slug].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/workspace/users.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/sign-in.html +9 -9
- mage_ai/server/frontend_dist_base_path_template/templates/[...slug].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/templates.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/terminal.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/test.html +14 -14
- mage_ai/server/frontend_dist_base_path_template/triggers.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/version-control.html +2 -2
- mage_ai/server/server.py +43 -15
- mage_ai/server/utils/output_display.py +6 -0
- mage_ai/server/websocket_server.py +14 -0
- mage_ai/services/aws/ecs/config.py +11 -8
- mage_ai/services/aws/emr/config.py +1 -0
- mage_ai/services/aws/emr/constants.py +1 -0
- mage_ai/services/aws/emr/emr.py +20 -7
- mage_ai/services/compute/__init__.py +0 -0
- mage_ai/services/compute/aws/__init__.py +0 -0
- mage_ai/services/compute/aws/constants.py +21 -0
- mage_ai/services/compute/aws/models.py +459 -0
- mage_ai/services/compute/aws/steps.py +482 -0
- mage_ai/services/compute/constants.py +27 -0
- mage_ai/services/compute/models.py +212 -0
- mage_ai/services/k8s/job_manager.py +3 -0
- mage_ai/services/spark/api/aws_emr.py +38 -0
- mage_ai/services/spark/api/base.py +7 -4
- mage_ai/services/spark/api/constants.py +4 -0
- mage_ai/services/spark/api/local.py +25 -24
- mage_ai/services/spark/api/service.py +15 -5
- mage_ai/services/spark/constants.py +1 -1
- mage_ai/services/spark/models/applications.py +45 -3
- mage_ai/services/spark/models/base.py +3 -19
- mage_ai/services/spark/models/environments.py +16 -11
- mage_ai/services/spark/models/executors.py +2 -2
- mage_ai/services/spark/models/sqls.py +46 -15
- mage_ai/services/spark/models/stages.py +55 -32
- mage_ai/services/spark/models/threads.py +2 -2
- mage_ai/services/spark/utils.py +22 -6
- mage_ai/services/ssh/__init__.py +0 -0
- mage_ai/services/ssh/aws/__init__.py +0 -0
- mage_ai/services/ssh/aws/emr/__init__.py +0 -0
- mage_ai/services/ssh/aws/emr/constants.py +10 -0
- mage_ai/services/ssh/aws/emr/models.py +326 -0
- mage_ai/services/ssh/aws/emr/utils.py +151 -0
- mage_ai/settings/__init__.py +16 -1
- mage_ai/settings/secret_generation.py +7 -0
- mage_ai/settings/sso.py +20 -0
- mage_ai/shared/hash.py +17 -1
- mage_ai/shared/models.py +253 -0
- mage_ai/streaming/constants.py +2 -0
- mage_ai/streaming/sources/activemq.py +89 -0
- mage_ai/streaming/sources/nats_js.py +182 -0
- mage_ai/streaming/sources/source_factory.py +8 -0
- mage_ai/tests/ai/test_ai_functions.py +53 -8
- mage_ai/tests/api/endpoints/test_oauths.py +33 -0
- mage_ai/tests/api/endpoints/test_projects.py +1 -0
- mage_ai/tests/api/endpoints/test_workspaces.py +55 -0
- mage_ai/tests/api/operations/test_base.py +7 -5
- mage_ai/tests/api/operations/test_operations.py +0 -1
- mage_ai/tests/api/operations/test_operations_with_hooks.py +577 -0
- mage_ai/tests/api/operations/test_syncs.py +0 -1
- mage_ai/tests/api/operations/test_users.py +13 -2
- mage_ai/tests/data_preparation/executors/test_block_executor.py +1 -0
- mage_ai/tests/data_preparation/models/global_hooks/__init__.py +0 -0
- mage_ai/tests/data_preparation/models/global_hooks/test_global_hooks.py +575 -0
- mage_ai/tests/data_preparation/models/global_hooks/test_hook.py +760 -0
- mage_ai/tests/data_preparation/models/global_hooks/test_utils.py +33 -0
- mage_ai/tests/data_preparation/models/test_pipeline.py +5 -0
- mage_ai/tests/data_preparation/test_repo_manager.py +11 -0
- mage_ai/tests/data_preparation/test_templates.py +1 -0
- mage_ai/tests/factory.py +132 -10
- mage_ai/tests/orchestration/queue/test_process_queue.py +15 -2
- mage_ai/tests/orchestration/test_pipeline_scheduler.py +447 -79
- mage_ai/tests/services/aws/ecs/__init__.py +0 -0
- mage_ai/tests/services/aws/ecs/test_config.py +144 -0
- mage_ai/tests/services/k8s/test_job_manager.py +22 -1
- mage_ai/tests/shared/mixins.py +291 -0
- mage_ai/tests/shared/test_hash.py +17 -1
- mage_ai/tests/streaming/sources/test_activemq.py +32 -0
- mage_ai/tests/streaming/sources/test_nats_js.py +32 -0
- mage_ai/tests/streaming/sources/test_source_factory.py +26 -1
- {mage_ai-0.9.44.dist-info → mage_ai-0.9.46.dist-info}/METADATA +28 -66
- {mage_ai-0.9.44.dist-info → mage_ai-0.9.46.dist-info}/RECORD +443 -364
- mage_ai/authentication/oauth/active_directory.py +0 -17
- mage_ai/server/frontend_dist/_next/static/RPXiX8RpZ7oO-yeaEtV2c/_buildManifest.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/1125-91d3ce33140ef041.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/1749-607014ecf28268bf.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/1952-573c7fc7ad84da6e.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/3004-231cad9039ae5dcb.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/3419-0873e170ef7d6303.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/3932-0ceca9599d6e6d00.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/4267-335766a915ee2fa9.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/5457-8be2e0a3fe0ba64b.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/5499-bca977f466e259e1.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/553-edf533e634e85192.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/5810-37c6091b29a1fe53.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/600-0733eb84f0a0a9e0.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/6333-53b6ebbef95a3691.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/722-3fff38a9019369fe.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/8224-39a93ee1058b6069.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/8264-6ef8fdb195694807.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/9264-1b5c4b071ed544c3.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/framework-7c365855dab1bf41.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/_app-a4a660fa14cf05a9.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/block-layout-c465c14d48392b11.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/compute-b1f8d5a7a9a30f2d.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/files-93c094bad0f299fb.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/files-891e5bd5935f7473.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/settings-d2e4ee4e68d36807.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/users/new-4a49126fcfe8ced0.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/manage-5f8c5d0bc6ad1113.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/oauth-09467512a85c96ed.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/overview-31bde04718d0d3a8.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-96718f95103d7844.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills-a9266d353f288e8c.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/dashboard-b5c29c852262312e.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/edit-5d525454ff7aeb3f.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/monitors/block-runs-883f4ae9a4a264c0.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/monitors/block-runtime-c65be964e6398cb7.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/monitors-1f5b4a974bd39740.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/settings-074c32397d341de9.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/syncs-50f5d8706ed0bc73.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-e151c1552fcb67bd.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/triggers-cc36f0f8c73fee4b.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines-8336c4326f1e7d96.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/account/profile-01dd679e4a21e0d9.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/preferences-4d9051c073a9b2ff.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/sync-data-88d41c33b8fcb300.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/sign-in-d859e07561eb9010.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/terminal-578d862f3e56e6e6.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/test-1c0588d685b909b9.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/version-control-e284cf1535d93798.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1125-91d3ce33140ef041.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1749-607014ecf28268bf.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1952-573c7fc7ad84da6e.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/3004-231cad9039ae5dcb.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/3419-0873e170ef7d6303.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/3932-0ceca9599d6e6d00.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/4267-335766a915ee2fa9.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/5457-8be2e0a3fe0ba64b.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/5499-bca977f466e259e1.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/553-edf533e634e85192.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/5810-37c6091b29a1fe53.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/600-0733eb84f0a0a9e0.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/6333-53b6ebbef95a3691.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/722-3fff38a9019369fe.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/8224-39a93ee1058b6069.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/8264-6ef8fdb195694807.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/9264-1b5c4b071ed544c3.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/framework-7c365855dab1bf41.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/_app-a4a660fa14cf05a9.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/block-layout-c465c14d48392b11.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/compute-b1f8d5a7a9a30f2d.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/files-93c094bad0f299fb.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/files-891e5bd5935f7473.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/settings-d2e4ee4e68d36807.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/users/new-4a49126fcfe8ced0.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage-5f8c5d0bc6ad1113.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/oauth-09467512a85c96ed.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/overview-31bde04718d0d3a8.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-96718f95103d7844.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills-a9266d353f288e8c.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/dashboard-b5c29c852262312e.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/edit-5d525454ff7aeb3f.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/monitors/block-runs-883f4ae9a4a264c0.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/monitors/block-runtime-c65be964e6398cb7.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/monitors-1f5b4a974bd39740.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/settings-074c32397d341de9.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/syncs-50f5d8706ed0bc73.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-e151c1552fcb67bd.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/triggers-cc36f0f8c73fee4b.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines-8336c4326f1e7d96.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/account/profile-01dd679e4a21e0d9.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/preferences-4d9051c073a9b2ff.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/sync-data-88d41c33b8fcb300.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/sign-in-d859e07561eb9010.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/terminal-578d862f3e56e6e6.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/test-1c0588d685b909b9.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/version-control-e284cf1535d93798.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/rNR5JgSxO3eA2BWGa_i7U/_buildManifest.js +0 -1
- /mage_ai/server/frontend_dist/_next/static/{RPXiX8RpZ7oO-yeaEtV2c → 9jB4XPuz6BzxBcG9VNao5}/_ssgManifest.js +0 -0
- /mage_ai/server/frontend_dist_base_path_template/_next/static/{rNR5JgSxO3eA2BWGa_i7U → uPDjJYpJMst1q6psbRyte}/_ssgManifest.js +0 -0
- {mage_ai-0.9.44.dist-info → mage_ai-0.9.46.dist-info}/LICENSE +0 -0
- {mage_ai-0.9.44.dist-info → mage_ai-0.9.46.dist-info}/WHEEL +0 -0
- {mage_ai-0.9.44.dist-info → mage_ai-0.9.46.dist-info}/entry_points.txt +0 -0
- {mage_ai-0.9.44.dist-info → mage_ai-0.9.46.dist-info}/top_level.txt +0 -0
|
@@ -176,37 +176,38 @@ class UserResource(DatabaseResource):
|
|
|
176
176
|
payload['password_hash'] = create_bcrypt_hash(password, password_salt)
|
|
177
177
|
payload['password_salt'] = password_salt
|
|
178
178
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
UserRole.
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
179
|
+
if 'role_ids' in payload:
|
|
180
|
+
role_ids = [int(i) for i in payload.get('role_ids') or []]
|
|
181
|
+
# Need to call roles_new directly on the model or else the resource will
|
|
182
|
+
# cache the result because of the collective loader.
|
|
183
|
+
role_mapping = index_by(lambda x: x.id, self.model.roles_new or [])
|
|
184
|
+
|
|
185
|
+
role_ids_create = []
|
|
186
|
+
role_ids_delete = []
|
|
187
|
+
|
|
188
|
+
for role_id in role_ids:
|
|
189
|
+
if role_id not in role_mapping:
|
|
190
|
+
role_ids_create.append(role_id)
|
|
191
|
+
|
|
192
|
+
for role_id in role_mapping.keys():
|
|
193
|
+
if role_id not in role_ids:
|
|
194
|
+
role_ids_delete.append(role_id)
|
|
195
|
+
|
|
196
|
+
if role_ids_create:
|
|
197
|
+
db_connection.session.bulk_save_objects(
|
|
198
|
+
[UserRole(
|
|
199
|
+
role_id=role_id,
|
|
200
|
+
user_id=self.id,
|
|
201
|
+
) for role_id in role_ids_create],
|
|
202
|
+
return_defaults=True,
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
if role_ids_delete:
|
|
206
|
+
delete_statement = UserRole.__table__.delete().where(
|
|
207
|
+
UserRole.role_id.in_(role_ids_delete),
|
|
208
|
+
UserRole.user_id == self.id,
|
|
209
|
+
)
|
|
210
|
+
db_connection.session.execute(delete_statement)
|
|
210
211
|
|
|
211
212
|
return super().update(ignore_keys(payload, [
|
|
212
213
|
'owner',
|
|
@@ -1,19 +1,26 @@
|
|
|
1
|
+
from typing import Dict
|
|
2
|
+
|
|
1
3
|
from mage_ai.api.errors import ApiError
|
|
2
|
-
from mage_ai.services.spark.api.
|
|
4
|
+
from mage_ai.services.spark.api.base import BaseAPI
|
|
5
|
+
from mage_ai.services.spark.api.service import API
|
|
3
6
|
from mage_ai.services.spark.models.applications import Application
|
|
4
7
|
|
|
5
8
|
|
|
6
9
|
class SparkApplicationChild:
|
|
10
|
+
@classmethod
|
|
11
|
+
def build_api(self) -> BaseAPI:
|
|
12
|
+
return API.build()
|
|
13
|
+
|
|
7
14
|
@classmethod
|
|
8
15
|
async def get_application_id(self, **kwargs) -> str:
|
|
9
16
|
application_id = None
|
|
10
17
|
parent_model = kwargs.get('parent_model')
|
|
11
18
|
if parent_model and isinstance(parent_model, Application):
|
|
12
|
-
application_id = parent_model.
|
|
19
|
+
application_id = parent_model.calculated_id()
|
|
13
20
|
else:
|
|
14
|
-
models = await
|
|
21
|
+
models = await self.build_api().applications()
|
|
15
22
|
if models and len(models) >= 1:
|
|
16
|
-
application_id = models[0].
|
|
23
|
+
application_id = models[0].calculated_id()
|
|
17
24
|
|
|
18
25
|
if not application_id:
|
|
19
26
|
error = ApiError(ApiError.RESOURCE_NOT_FOUND)
|
|
@@ -22,3 +29,17 @@ class SparkApplicationChild:
|
|
|
22
29
|
raise error
|
|
23
30
|
|
|
24
31
|
return application_id
|
|
32
|
+
|
|
33
|
+
@classmethod
|
|
34
|
+
def application_calculated_id_from_query(self, query: Dict = None) -> str:
|
|
35
|
+
if not query:
|
|
36
|
+
return None
|
|
37
|
+
|
|
38
|
+
application_id = query.get('application_id', [])
|
|
39
|
+
if application_id:
|
|
40
|
+
application_id = application_id[0]
|
|
41
|
+
|
|
42
|
+
if application_id:
|
|
43
|
+
application_id = Application.load(id=application_id).calculated_id()
|
|
44
|
+
|
|
45
|
+
return application_id
|
|
@@ -9,6 +9,8 @@ GITHUB_STATE = '1337'
|
|
|
9
9
|
OAUTH_PROVIDER_ACTIVE_DIRECTORY = 'active_directory'
|
|
10
10
|
OAUTH_PROVIDER_GITHUB = 'github'
|
|
11
11
|
OAUTH_PROVIDER_GHE = 'ghe'
|
|
12
|
+
OAUTH_PROVIDER_GOOGLE = 'google'
|
|
13
|
+
OAUTH_PROVIDER_OKTA = 'okta'
|
|
12
14
|
|
|
13
15
|
GHE_CLIENT_ID_ENV_VAR = 'GHE_CLIENT_ID'
|
|
14
16
|
GHE_CLIENT_SECRET_ENV_VAR = 'GHE_CLIENT_SECRET'
|
|
@@ -20,6 +22,8 @@ VALID_OAUTH_PROVIDERS = [
|
|
|
20
22
|
OAUTH_PROVIDER_ACTIVE_DIRECTORY,
|
|
21
23
|
OAUTH_PROVIDER_GHE,
|
|
22
24
|
OAUTH_PROVIDER_GITHUB,
|
|
25
|
+
OAUTH_PROVIDER_GOOGLE,
|
|
26
|
+
OAUTH_PROVIDER_OKTA,
|
|
23
27
|
]
|
|
24
28
|
|
|
25
29
|
|
mage_ai/authentication/oauth2.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import os
|
|
2
1
|
import secrets
|
|
3
2
|
from datetime import datetime, timedelta
|
|
4
3
|
from typing import Dict
|
|
@@ -10,10 +9,9 @@ from mage_ai.orchestration.db.models.oauth import (
|
|
|
10
9
|
Oauth2Application,
|
|
11
10
|
User,
|
|
12
11
|
)
|
|
13
|
-
from mage_ai.settings import MAGE_ACCESS_TOKEN_EXPIRY_TIME
|
|
12
|
+
from mage_ai.settings import JWT_SECRET, MAGE_ACCESS_TOKEN_EXPIRY_TIME
|
|
14
13
|
|
|
15
14
|
JWT_ALGORITHM = 'HS256'
|
|
16
|
-
JWT_SECRET = os.getenv('JWT_SECRET', 'materia')
|
|
17
15
|
|
|
18
16
|
|
|
19
17
|
def generate_access_token(
|
|
@@ -17,6 +17,9 @@ class EntityName(str, Enum):
|
|
|
17
17
|
Chart = 'Chart'
|
|
18
18
|
ClientPage = 'ClientPage'
|
|
19
19
|
Cluster = 'Cluster'
|
|
20
|
+
ComputeCluster = 'ComputeCluster'
|
|
21
|
+
ComputeConnection = 'ComputeConnection'
|
|
22
|
+
ComputeService = 'ComputeService'
|
|
20
23
|
CustomTemplate = 'CustomTemplate'
|
|
21
24
|
DataProvider = 'DataProvider'
|
|
22
25
|
Database = 'Database'
|
|
@@ -33,6 +36,7 @@ class EntityName(str, Enum):
|
|
|
33
36
|
GitCustomBranch = 'GitCustomBranch'
|
|
34
37
|
GitFile = 'GitFile'
|
|
35
38
|
GlobalDataProduct = 'GlobalDataProduct'
|
|
39
|
+
GlobalHook = 'GlobalHook'
|
|
36
40
|
IntegrationDestination = 'IntegrationDestination'
|
|
37
41
|
IntegrationSource = 'IntegrationSource'
|
|
38
42
|
IntegrationSourceStream = 'IntegrationSourceStream'
|
|
File without changes
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import uuid
|
|
3
|
+
from typing import Awaitable, Dict
|
|
4
|
+
from urllib.parse import quote_plus
|
|
5
|
+
|
|
6
|
+
import aiohttp
|
|
7
|
+
|
|
8
|
+
from mage_ai.authentication.oauth.constants import (
|
|
9
|
+
ACTIVE_DIRECTORY_CLIENT_ID as ACTIVE_DIRECTORY_MAGE_CLIENT_ID,
|
|
10
|
+
)
|
|
11
|
+
from mage_ai.authentication.oauth.constants import OAUTH_PROVIDER_ACTIVE_DIRECTORY
|
|
12
|
+
from mage_ai.authentication.providers.oauth import OauthProvider
|
|
13
|
+
from mage_ai.authentication.providers.sso import SsoProvider
|
|
14
|
+
from mage_ai.authentication.providers.utils import get_base_url
|
|
15
|
+
from mage_ai.settings import ACTIVE_DIRECTORY_DIRECTORY_ID
|
|
16
|
+
from mage_ai.settings.sso import (
|
|
17
|
+
ACTIVE_DIRECTORY_CLIENT_ID,
|
|
18
|
+
ACTIVE_DIRECTORY_CLIENT_SECRET,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ADProvider(SsoProvider, OauthProvider):
|
|
23
|
+
provider = OAUTH_PROVIDER_ACTIVE_DIRECTORY
|
|
24
|
+
|
|
25
|
+
def __init__(self):
|
|
26
|
+
self.__validate()
|
|
27
|
+
|
|
28
|
+
def __validate(self):
|
|
29
|
+
if not ACTIVE_DIRECTORY_DIRECTORY_ID:
|
|
30
|
+
raise Exception(
|
|
31
|
+
'AD directory id is empty. '
|
|
32
|
+
'Make sure the ACTIVE_DIRECTORY_DIRECTORY_ID environment variable is set.')
|
|
33
|
+
if ACTIVE_DIRECTORY_CLIENT_ID and not ACTIVE_DIRECTORY_CLIENT_SECRET:
|
|
34
|
+
raise Exception(
|
|
35
|
+
'AD client secret is empty. '
|
|
36
|
+
'Make sure the ACTIVE_DIRECTORY_CLIENT_SECRET environment variable is set.')
|
|
37
|
+
|
|
38
|
+
def get_auth_url_response(self, redirect_uri: str = None, **kwargs) -> Dict:
|
|
39
|
+
"""
|
|
40
|
+
For active directory, the user can set up the Oauth provider in two different ways. They
|
|
41
|
+
can either just set the ACTIVE_DIRECTORY_DIRECTORY_ID which will use the Mage application we
|
|
42
|
+
set up in Azure. They can additionally set the ACTIVE_DIRECTORY_CLIENT_ID and
|
|
43
|
+
ACTIVE_DIRECTORY_CLIENT_SECRET which will use their own Mage application in Azure.
|
|
44
|
+
"""
|
|
45
|
+
ad_directory_id = ACTIVE_DIRECTORY_DIRECTORY_ID
|
|
46
|
+
if ACTIVE_DIRECTORY_CLIENT_ID:
|
|
47
|
+
base_url = get_base_url(redirect_uri)
|
|
48
|
+
redirect_uri_query = dict(
|
|
49
|
+
provider=self.provider,
|
|
50
|
+
redirect_uri=redirect_uri,
|
|
51
|
+
)
|
|
52
|
+
query = dict(
|
|
53
|
+
client_id=ACTIVE_DIRECTORY_CLIENT_ID,
|
|
54
|
+
redirect_uri=quote_plus(
|
|
55
|
+
f'{base_url}/oauth',
|
|
56
|
+
),
|
|
57
|
+
response_type='code',
|
|
58
|
+
scope='User.Read',
|
|
59
|
+
state=uuid.uuid4().hex,
|
|
60
|
+
)
|
|
61
|
+
query_strings = []
|
|
62
|
+
for k, v in query.items():
|
|
63
|
+
query_strings.append(f'{k}={v}')
|
|
64
|
+
|
|
65
|
+
return dict(
|
|
66
|
+
url=f"https://login.microsoftonline.com/{ad_directory_id}/oauth2/v2.0/authorize?{'&'.join(query_strings)}", # noqa: E501
|
|
67
|
+
redirect_query_params=redirect_uri_query,
|
|
68
|
+
)
|
|
69
|
+
else:
|
|
70
|
+
from requests.models import PreparedRequest
|
|
71
|
+
|
|
72
|
+
req = PreparedRequest()
|
|
73
|
+
req.prepare_url(redirect_uri, dict(provider=self.provider))
|
|
74
|
+
query = dict(
|
|
75
|
+
client_id=ACTIVE_DIRECTORY_MAGE_CLIENT_ID,
|
|
76
|
+
redirect_uri=f'https://api.mage.ai/v1/oauth/{self.provider}',
|
|
77
|
+
response_type='code',
|
|
78
|
+
scope='User.Read',
|
|
79
|
+
state=quote_plus(
|
|
80
|
+
json.dumps(
|
|
81
|
+
dict(
|
|
82
|
+
redirect_uri=req.url,
|
|
83
|
+
tenant_id=ad_directory_id,
|
|
84
|
+
)
|
|
85
|
+
)
|
|
86
|
+
),
|
|
87
|
+
)
|
|
88
|
+
query_strings = []
|
|
89
|
+
for k, v in query.items():
|
|
90
|
+
query_strings.append(f'{k}={v}')
|
|
91
|
+
|
|
92
|
+
return dict(
|
|
93
|
+
url=f"https://login.microsoftonline.com/{ad_directory_id}/oauth2/v2.0/authorize?{'&'.join(query_strings)}", # noqa: E501
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
async def get_access_token_response(self, code: str, **kwargs) -> Awaitable[Dict]:
|
|
97
|
+
base_url = get_base_url(kwargs.get('redirect_uri'))
|
|
98
|
+
|
|
99
|
+
async with aiohttp.ClientSession() as session:
|
|
100
|
+
async with session.post(
|
|
101
|
+
'https://login.microsoftonline.com/organizations/oauth2/v2.0/token',
|
|
102
|
+
headers={
|
|
103
|
+
'Accept': 'application/json',
|
|
104
|
+
},
|
|
105
|
+
data=dict(
|
|
106
|
+
client_id=ACTIVE_DIRECTORY_CLIENT_ID,
|
|
107
|
+
client_secret=ACTIVE_DIRECTORY_CLIENT_SECRET,
|
|
108
|
+
code=code,
|
|
109
|
+
grant_type='authorization_code',
|
|
110
|
+
redirect_uri=f'{base_url}/oauth',
|
|
111
|
+
tenant=ACTIVE_DIRECTORY_DIRECTORY_ID,
|
|
112
|
+
),
|
|
113
|
+
timeout=20,
|
|
114
|
+
) as response:
|
|
115
|
+
data = await response.json()
|
|
116
|
+
|
|
117
|
+
return data
|
|
118
|
+
|
|
119
|
+
async def get_user_info(self, access_token: str = None, **kwargs) -> Awaitable[Dict]:
|
|
120
|
+
if access_token is None:
|
|
121
|
+
raise Exception('Access token is required to fetch user info.')
|
|
122
|
+
async with aiohttp.ClientSession() as session:
|
|
123
|
+
async with session.get(
|
|
124
|
+
'https://graph.microsoft.com/v1.0/me',
|
|
125
|
+
headers={
|
|
126
|
+
'Content-Type': 'application\\json',
|
|
127
|
+
'Authorization': f'Bearer {access_token}',
|
|
128
|
+
},
|
|
129
|
+
timeout=10,
|
|
130
|
+
) as response:
|
|
131
|
+
userinfo_resp = await response.json()
|
|
132
|
+
|
|
133
|
+
return dict(
|
|
134
|
+
email=userinfo_resp.get('userPrincipalName'),
|
|
135
|
+
username=userinfo_resp.get('userPrincipalName'),
|
|
136
|
+
)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from mage_ai.authentication.oauth.constants import (
|
|
2
|
+
OAUTH_PROVIDER_ACTIVE_DIRECTORY,
|
|
3
|
+
OAUTH_PROVIDER_GHE,
|
|
4
|
+
OAUTH_PROVIDER_GOOGLE,
|
|
5
|
+
OAUTH_PROVIDER_OKTA,
|
|
6
|
+
)
|
|
7
|
+
from mage_ai.authentication.providers.active_directory import ADProvider
|
|
8
|
+
from mage_ai.authentication.providers.ghe import GHEProvider
|
|
9
|
+
from mage_ai.authentication.providers.google import GoogleProvider
|
|
10
|
+
from mage_ai.authentication.providers.okta import OktaProvider
|
|
11
|
+
|
|
12
|
+
NAME_TO_PROVIDER = {
|
|
13
|
+
OAUTH_PROVIDER_ACTIVE_DIRECTORY: ADProvider,
|
|
14
|
+
OAUTH_PROVIDER_GHE: GHEProvider,
|
|
15
|
+
OAUTH_PROVIDER_GOOGLE: GoogleProvider,
|
|
16
|
+
OAUTH_PROVIDER_OKTA: OktaProvider,
|
|
17
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import urllib.parse
|
|
3
|
+
import uuid
|
|
4
|
+
from typing import Awaitable, Dict
|
|
5
|
+
|
|
6
|
+
import aiohttp
|
|
7
|
+
|
|
8
|
+
from mage_ai.authentication.oauth.constants import (
|
|
9
|
+
GHE_CLIENT_ID_ENV_VAR,
|
|
10
|
+
GHE_CLIENT_SECRET_ENV_VAR,
|
|
11
|
+
OAUTH_PROVIDER_GHE,
|
|
12
|
+
get_ghe_hostname,
|
|
13
|
+
)
|
|
14
|
+
from mage_ai.authentication.providers.oauth import OauthProvider
|
|
15
|
+
from mage_ai.authentication.providers.utils import get_base_url
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class GHEProvider(OauthProvider):
|
|
19
|
+
provider = OAUTH_PROVIDER_GHE
|
|
20
|
+
|
|
21
|
+
def __init__(self):
|
|
22
|
+
self.hostname = get_ghe_hostname()
|
|
23
|
+
self.client_id = os.getenv(GHE_CLIENT_ID_ENV_VAR)
|
|
24
|
+
self.client_secret = os.getenv(GHE_CLIENT_SECRET_ENV_VAR)
|
|
25
|
+
self.__validate()
|
|
26
|
+
|
|
27
|
+
def __validate(self):
|
|
28
|
+
if not self.hostname:
|
|
29
|
+
raise Exception(
|
|
30
|
+
'GHE hostname is empty. '
|
|
31
|
+
'Make sure the GHE_HOSTNAME environment variable is set.')
|
|
32
|
+
if not self.client_id:
|
|
33
|
+
raise Exception(
|
|
34
|
+
'GHE client id is empty. '
|
|
35
|
+
'Make sure the GHE_CLIENT_ID environment variable is set.')
|
|
36
|
+
if not self.client_secret:
|
|
37
|
+
raise Exception(
|
|
38
|
+
'GHE client secret is empty. '
|
|
39
|
+
'Make sure the GHE_CLIENT_SECRET environment variable is set.')
|
|
40
|
+
|
|
41
|
+
def get_auth_url_response(self, redirect_uri: str = None, **kwargs) -> Dict:
|
|
42
|
+
base_url = get_base_url(redirect_uri)
|
|
43
|
+
redirect_uri_query = dict(
|
|
44
|
+
provider=self.provider,
|
|
45
|
+
redirect_uri=redirect_uri,
|
|
46
|
+
)
|
|
47
|
+
query = dict(
|
|
48
|
+
client_id=self.client_id,
|
|
49
|
+
redirect_uri=urllib.parse.quote_plus(
|
|
50
|
+
f'{base_url}/oauth',
|
|
51
|
+
),
|
|
52
|
+
scope='repo',
|
|
53
|
+
state=uuid.uuid4().hex,
|
|
54
|
+
)
|
|
55
|
+
query_strings = []
|
|
56
|
+
for k, v in query.items():
|
|
57
|
+
query_strings.append(f'{k}={v}')
|
|
58
|
+
|
|
59
|
+
return dict(
|
|
60
|
+
url=f"{self.hostname}/login/oauth/authorize?{'&'.join(query_strings)}",
|
|
61
|
+
redirect_query_params=redirect_uri_query,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
async def get_access_token_response(self, code: str, **kwargs) -> Awaitable[Dict]:
|
|
65
|
+
data = dict()
|
|
66
|
+
async with aiohttp.ClientSession() as session:
|
|
67
|
+
async with session.post(
|
|
68
|
+
f'{self.hostname}/login/oauth/access_token',
|
|
69
|
+
headers={
|
|
70
|
+
'Accept': 'application/json',
|
|
71
|
+
},
|
|
72
|
+
data=dict(
|
|
73
|
+
client_id=self.client_id,
|
|
74
|
+
client_secret=self.client_secret,
|
|
75
|
+
code=code,
|
|
76
|
+
),
|
|
77
|
+
timeout=20,
|
|
78
|
+
) as response:
|
|
79
|
+
data = await response.json()
|
|
80
|
+
|
|
81
|
+
return data
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import urllib.parse
|
|
2
|
+
import uuid
|
|
3
|
+
from typing import Awaitable, Dict
|
|
4
|
+
|
|
5
|
+
import aiohttp
|
|
6
|
+
|
|
7
|
+
from mage_ai.authentication.oauth.constants import OAUTH_PROVIDER_GOOGLE
|
|
8
|
+
from mage_ai.authentication.providers.oauth import OauthProvider
|
|
9
|
+
from mage_ai.authentication.providers.sso import SsoProvider
|
|
10
|
+
from mage_ai.authentication.providers.utils import get_base_url
|
|
11
|
+
from mage_ai.settings.sso import GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class GoogleProvider(SsoProvider, OauthProvider):
|
|
15
|
+
provider = OAUTH_PROVIDER_GOOGLE
|
|
16
|
+
|
|
17
|
+
def __init__(self):
|
|
18
|
+
if not GOOGLE_CLIENT_ID:
|
|
19
|
+
raise Exception(
|
|
20
|
+
'Google client id is empty. '
|
|
21
|
+
'Make sure the GOOGLE_CLIENT_ID environment variable is set.')
|
|
22
|
+
if not GOOGLE_CLIENT_SECRET:
|
|
23
|
+
raise Exception(
|
|
24
|
+
'Google client secret is empty. '
|
|
25
|
+
'Make sure the GOOGLE_CLIENT_SECRET environment variable is set.')
|
|
26
|
+
|
|
27
|
+
def get_auth_url_response(self, redirect_uri: str = None, **kwargs) -> Dict:
|
|
28
|
+
base_url = get_base_url(redirect_uri)
|
|
29
|
+
redirect_uri_query = dict(
|
|
30
|
+
provider=self.provider,
|
|
31
|
+
redirect_uri=redirect_uri,
|
|
32
|
+
)
|
|
33
|
+
query = dict(
|
|
34
|
+
client_id=GOOGLE_CLIENT_ID,
|
|
35
|
+
prompt='select_account',
|
|
36
|
+
redirect_uri=urllib.parse.quote_plus(
|
|
37
|
+
f'{base_url}/oauth',
|
|
38
|
+
),
|
|
39
|
+
response_type='code',
|
|
40
|
+
scope='https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile', # noqa: E501
|
|
41
|
+
state=uuid.uuid4().hex,
|
|
42
|
+
)
|
|
43
|
+
query_strings = []
|
|
44
|
+
for k, v in query.items():
|
|
45
|
+
query_strings.append(f'{k}={v}')
|
|
46
|
+
|
|
47
|
+
return dict(
|
|
48
|
+
url=f"https://accounts.google.com/o/oauth2/v2/auth?{'&'.join(query_strings)}",
|
|
49
|
+
redirect_query_params=redirect_uri_query,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
async def get_access_token_response(self, code: str, **kwargs) -> Awaitable[Dict]:
|
|
53
|
+
base_url = get_base_url(kwargs.get('redirect_uri'))
|
|
54
|
+
|
|
55
|
+
data = dict()
|
|
56
|
+
async with aiohttp.ClientSession() as session:
|
|
57
|
+
async with session.post(
|
|
58
|
+
'https://oauth2.googleapis.com/token',
|
|
59
|
+
data=dict(
|
|
60
|
+
code=code,
|
|
61
|
+
client_id=GOOGLE_CLIENT_ID,
|
|
62
|
+
client_secret=GOOGLE_CLIENT_SECRET,
|
|
63
|
+
redirect_uri=f'{base_url}/oauth',
|
|
64
|
+
grant_type='authorization_code',
|
|
65
|
+
),
|
|
66
|
+
timeout=20,
|
|
67
|
+
) as response:
|
|
68
|
+
data = await response.json()
|
|
69
|
+
|
|
70
|
+
return data
|
|
71
|
+
|
|
72
|
+
async def get_user_info(self, access_token: str = None, **kwargs) -> Awaitable[Dict]:
|
|
73
|
+
if access_token is None:
|
|
74
|
+
raise Exception('Access token is required to fetch user info.')
|
|
75
|
+
async with aiohttp.ClientSession() as session:
|
|
76
|
+
async with session.get(
|
|
77
|
+
'https://www.googleapis.com/oauth2/v3/userinfo',
|
|
78
|
+
params=dict(access_token=access_token),
|
|
79
|
+
timeout=10,
|
|
80
|
+
) as response:
|
|
81
|
+
userinfo_resp = await response.json()
|
|
82
|
+
|
|
83
|
+
return dict(
|
|
84
|
+
email=userinfo_resp.get('email'),
|
|
85
|
+
username=userinfo_resp.get('email'),
|
|
86
|
+
)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from typing import Awaitable, Dict
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class OauthProvider(ABC):
|
|
6
|
+
"""
|
|
7
|
+
Base class for oauth authentication. This provider will be used for oauth authentication flows.
|
|
8
|
+
The current oauth for Mage is as follows:
|
|
9
|
+
|
|
10
|
+
1. Mage client will make a request to the server to get the auth url to get the authorization
|
|
11
|
+
code for the provider. (get_auth_url_response)
|
|
12
|
+
2. Mage client redirects to the auth url and the user will sign in to the provider and grant
|
|
13
|
+
Mage access to the user's profile.
|
|
14
|
+
3. The provider will redirect back to the Mage oauth route (/oauth) with the authorization code.
|
|
15
|
+
4. Mage client will make another request to the server with the authorization code to get the
|
|
16
|
+
access token. The server will make a request to the provider with the authorization code
|
|
17
|
+
and return the access token. (get_access_token_response)
|
|
18
|
+
5. Mage client will redirect to the redirect uri with the access token and provider in the
|
|
19
|
+
query params.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
@abstractmethod
|
|
23
|
+
def get_auth_url_response(self, **kwargs) -> Dict:
|
|
24
|
+
"""
|
|
25
|
+
This method should construct the auth url to start the oauth flow.
|
|
26
|
+
|
|
27
|
+
Kwargs:
|
|
28
|
+
redirect_uri (str): the redirect uri to be used after the oauth provider returns
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
Dict: a dict with the following keys:
|
|
32
|
+
url: auth url to redirect to start oauth flow
|
|
33
|
+
redirect_query_params (optional): query params to be added to the redirect url
|
|
34
|
+
after the oauth provider returns the authorization code. This is used when
|
|
35
|
+
the provider expects an exact redirect_uri match and query params cannot
|
|
36
|
+
be included in the url.
|
|
37
|
+
"""
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
@abstractmethod
|
|
41
|
+
async def get_access_token_response(self, code: str, **kwargs) -> Awaitable[Dict]:
|
|
42
|
+
"""
|
|
43
|
+
This method should call the oauth provider with the authorization code and return
|
|
44
|
+
a dictionary containing an access token for the user.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
code (str): the authorization code returned by the oauth provider
|
|
48
|
+
|
|
49
|
+
Kwargs:
|
|
50
|
+
redirect_uri (str): the redirect uri to be used after the access token is fetched
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
Dict: a dict with the following keys:
|
|
54
|
+
url: auth url to redirect to start oauth flow
|
|
55
|
+
redirect_query_params (optional): query params to be added to the redirect url
|
|
56
|
+
after the oauth provider returns the authorization code. This is used when
|
|
57
|
+
the provider expects an exact redirect_uri match and query params cannot
|
|
58
|
+
be included in the url.
|
|
59
|
+
"""
|
|
60
|
+
pass
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import urllib.parse
|
|
2
|
+
import uuid
|
|
3
|
+
from typing import Awaitable, Dict
|
|
4
|
+
|
|
5
|
+
import aiohttp
|
|
6
|
+
from aiohttp import BasicAuth
|
|
7
|
+
|
|
8
|
+
from mage_ai.authentication.oauth.constants import OAUTH_PROVIDER_OKTA
|
|
9
|
+
from mage_ai.authentication.providers.oauth import OauthProvider
|
|
10
|
+
from mage_ai.authentication.providers.sso import SsoProvider
|
|
11
|
+
from mage_ai.authentication.providers.utils import get_base_url
|
|
12
|
+
from mage_ai.settings.sso import OKTA_CLIENT_ID, OKTA_CLIENT_SECRET, OKTA_DOMAIN_URL
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class OktaProvider(SsoProvider, OauthProvider):
|
|
16
|
+
provider = OAUTH_PROVIDER_OKTA
|
|
17
|
+
|
|
18
|
+
def __init__(self):
|
|
19
|
+
self.hostname = OKTA_DOMAIN_URL
|
|
20
|
+
self.__validate()
|
|
21
|
+
|
|
22
|
+
if not self.hostname.startswith('https'):
|
|
23
|
+
self.hostname = f'https://{self.hostname}'
|
|
24
|
+
|
|
25
|
+
def __validate(self):
|
|
26
|
+
if not self.hostname:
|
|
27
|
+
raise Exception(
|
|
28
|
+
'Okta hostname is empty. '
|
|
29
|
+
'Make sure the OKTA_DOMAIN_URL environment variable is set.')
|
|
30
|
+
if not OKTA_CLIENT_ID:
|
|
31
|
+
raise Exception(
|
|
32
|
+
'Okta client id is empty. '
|
|
33
|
+
'Make sure the OKTA_CLIENT_ID environment variable is set.')
|
|
34
|
+
if not OKTA_CLIENT_SECRET:
|
|
35
|
+
raise Exception(
|
|
36
|
+
'Okta client secret is empty. '
|
|
37
|
+
'Make sure the OKTA_CLIENT_SECRET environment variable is set.')
|
|
38
|
+
|
|
39
|
+
def get_auth_url_response(self, redirect_uri: str = None, **kwargs) -> Dict:
|
|
40
|
+
base_url = get_base_url(redirect_uri)
|
|
41
|
+
redirect_uri_query = dict(
|
|
42
|
+
provider=self.provider,
|
|
43
|
+
redirect_uri=redirect_uri,
|
|
44
|
+
)
|
|
45
|
+
query = dict(
|
|
46
|
+
client_id=OKTA_CLIENT_ID,
|
|
47
|
+
redirect_uri=urllib.parse.quote_plus(
|
|
48
|
+
f'{base_url}/oauth',
|
|
49
|
+
),
|
|
50
|
+
response_mode='query',
|
|
51
|
+
response_type='code',
|
|
52
|
+
scope='openid email profile',
|
|
53
|
+
state=uuid.uuid4().hex,
|
|
54
|
+
)
|
|
55
|
+
query_strings = []
|
|
56
|
+
for k, v in query.items():
|
|
57
|
+
query_strings.append(f'{k}={v}')
|
|
58
|
+
|
|
59
|
+
return dict(
|
|
60
|
+
url=f"{self.hostname}/oauth2/default/v1/authorize?{'&'.join(query_strings)}",
|
|
61
|
+
redirect_query_params=redirect_uri_query,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
async def get_access_token_response(self, code: str, **kwargs) -> Awaitable[Dict]:
|
|
65
|
+
base_url = get_base_url(kwargs.get('redirect_uri'))
|
|
66
|
+
data = dict()
|
|
67
|
+
async with aiohttp.ClientSession() as session:
|
|
68
|
+
async with session.post(
|
|
69
|
+
f'{self.hostname}/oauth2/default/v1/token',
|
|
70
|
+
headers={
|
|
71
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
72
|
+
},
|
|
73
|
+
data=dict(
|
|
74
|
+
grant_type='authorization_code',
|
|
75
|
+
code=code,
|
|
76
|
+
redirect_uri=f'{base_url}/oauth',
|
|
77
|
+
),
|
|
78
|
+
auth=BasicAuth(OKTA_CLIENT_ID, OKTA_CLIENT_SECRET),
|
|
79
|
+
timeout=20,
|
|
80
|
+
) as response:
|
|
81
|
+
data = await response.json()
|
|
82
|
+
|
|
83
|
+
return data
|
|
84
|
+
|
|
85
|
+
async def get_user_info(self, access_token: str = None, **kwargs) -> Awaitable[Dict]:
|
|
86
|
+
if access_token is None:
|
|
87
|
+
raise Exception('Access token is required to fetch user info.')
|
|
88
|
+
async with aiohttp.ClientSession() as session:
|
|
89
|
+
async with session.get(
|
|
90
|
+
f'{self.hostname}/oauth2/default/v1/userinfo',
|
|
91
|
+
headers={
|
|
92
|
+
'Authorization': f'Bearer {access_token}'
|
|
93
|
+
},
|
|
94
|
+
timeout=10,
|
|
95
|
+
) as response:
|
|
96
|
+
userinfo_resp = await response.json()
|
|
97
|
+
|
|
98
|
+
return dict(
|
|
99
|
+
email=userinfo_resp.get('email'),
|
|
100
|
+
username=userinfo_resp.get('sub'),
|
|
101
|
+
)
|