mage-ai 0.9.65__py3-none-any.whl → 0.9.67__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/monitors/BaseMonitor.py +38 -16
- mage_ai/api/policies/BackfillPolicy.py +1 -0
- mage_ai/api/resources/OauthResource.py +13 -5
- mage_ai/api/resources/SessionResource.py +6 -4
- mage_ai/authentication/ldap.py +19 -9
- mage_ai/authentication/oauth/constants.py +8 -14
- mage_ai/authentication/oauth/utils.py +18 -6
- mage_ai/authentication/providers/active_directory.py +21 -16
- mage_ai/authentication/providers/azure_devops.py +18 -0
- mage_ai/authentication/providers/bitbucket.py +10 -9
- mage_ai/authentication/providers/constants.py +2 -0
- mage_ai/authentication/providers/ghe.py +5 -9
- mage_ai/authentication/providers/gitlab.py +6 -9
- mage_ai/authentication/providers/google.py +9 -6
- mage_ai/authentication/providers/oidc.py +6 -4
- mage_ai/authentication/providers/okta.py +9 -6
- mage_ai/cluster_manager/kubernetes/workload_manager.py +10 -0
- mage_ai/cluster_manager/workspace/base.py +6 -1
- mage_ai/cluster_manager/workspace/kubernetes.py +3 -0
- mage_ai/data_preparation/decorators.py +15 -0
- mage_ai/data_preparation/executors/streaming_pipeline_executor.py +22 -12
- mage_ai/data_preparation/git/__init__.py +10 -1
- mage_ai/data_preparation/git/api.py +3 -0
- mage_ai/data_preparation/git/clients/azure_devops.py +106 -0
- mage_ai/data_preparation/git/clients/base.py +6 -0
- mage_ai/data_preparation/git/clients/gitlab.py +3 -2
- mage_ai/data_preparation/git/utils.py +31 -29
- mage_ai/data_preparation/models/block/__init__.py +27 -18
- mage_ai/data_preparation/models/block/dbt/block_sql.py +164 -0
- mage_ai/data_preparation/models/block/dynamic/variables.py +1 -2
- mage_ai/data_preparation/models/pipeline.py +3 -3
- mage_ai/data_preparation/models/triggers/__init__.py +6 -1
- mage_ai/data_preparation/preferences.py +42 -37
- mage_ai/data_preparation/repo_manager.py +21 -0
- mage_ai/data_preparation/storage/gcs_storage.py +27 -2
- mage_ai/data_preparation/storage/local_storage.py +18 -3
- mage_ai/data_preparation/storage/s3_storage.py +7 -2
- mage_ai/data_preparation/templates/data_loaders/streaming/generic_python.py +23 -0
- mage_ai/data_preparation/templates/main/metadata.yaml +6 -0
- mage_ai/data_preparation/templates/template.py +6 -2
- mage_ai/data_preparation/variable_manager.py +2 -1
- mage_ai/io/base.py +3 -0
- mage_ai/io/bigquery.py +2 -0
- mage_ai/io/export_utils.py +14 -9
- mage_ai/io/mssql.py +104 -25
- mage_ai/io/mysql.py +10 -9
- mage_ai/io/oracledb.py +14 -2
- mage_ai/io/postgres.py +3 -0
- mage_ai/io/sql.py +14 -6
- mage_ai/io/trino.py +10 -8
- mage_ai/orchestration/db/migrations/versions/90d978a8aef8_update_unique_constraint_for_secret.py +11 -5
- mage_ai/orchestration/db/models/schedules.py +25 -1
- mage_ai/orchestration/db/models/schedules_project_platform.py +24 -1
- mage_ai/orchestration/job_manager.py +6 -1
- mage_ai/orchestration/pipeline_scheduler_original.py +16 -10
- mage_ai/server/constants.py +1 -1
- mage_ai/server/file_observer.py +10 -0
- mage_ai/server/frontend_dist/404.html +2 -2
- mage_ai/server/frontend_dist/_next/static/chunks/{1557-a754b04510d50b80.js → 1557-01f0843dc6ac4971.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/9440-4069842b90d4b801.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/_app-1c1ffd928f5a00f7.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/index-b7b8695a7f9efde2.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/oauth-30e34ee15d410331.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-ff4bd7a8ec3bab40.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills-aaf393c86fc1bda3.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/{edit-8d32ac7e8f023779.js → edit-36377e679da2cd91.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/logs-3f5c14076ddde20e.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/{triggers-fe08120d9a6fb1b0.js → triggers-f508c2f261297724.js} +1 -1
- mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/pipelines-bcdb4ad41dd4c7d5.js → frontend_dist/_next/static/chunks/pages/pipelines-f99e99aa8f45529c.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/{sign-in-19b36600d908b711.js → sign-in-7d38b2f7c3e918a1.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/version-control-5753fac7c1bfdc88.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/{webpack-ac7fdc472bedf682.js → webpack-d079359c241db804.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/{ZMrJfDouIX5AMb_RteRbL → vPsMu6Fi2zrHaf2fRXKRO}/_buildManifest.js +1 -1
- mage_ai/server/frontend_dist/block-layout.html +2 -2
- mage_ai/server/frontend_dist/compute.html +2 -2
- mage_ai/server/frontend_dist/files.html +2 -2
- mage_ai/server/frontend_dist/global-data-products/[...slug].html +2 -2
- mage_ai/server/frontend_dist/global-data-products.html +2 -2
- mage_ai/server/frontend_dist/global-hooks/[...slug].html +2 -2
- mage_ai/server/frontend_dist/global-hooks.html +2 -2
- mage_ai/server/frontend_dist/index.html +2 -2
- mage_ai/server/frontend_dist/manage/files.html +2 -2
- mage_ai/server/frontend_dist/manage/settings.html +2 -2
- mage_ai/server/frontend_dist/manage/users/[user].html +2 -2
- mage_ai/server/frontend_dist/manage/users/new.html +2 -2
- mage_ai/server/frontend_dist/manage/users.html +2 -2
- mage_ai/server/frontend_dist/manage.html +2 -2
- mage_ai/server/frontend_dist/oauth.html +2 -2
- mage_ai/server/frontend_dist/overview.html +2 -2
- mage_ai/server/frontend_dist/pipeline-runs.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/backfills/[...slug].html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/backfills.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/dashboard.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/edit.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/logs.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors/block-runs.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors/block-runtime.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/runs/[run].html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/runs.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/settings.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/syncs.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/triggers/[...slug].html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/triggers.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline].html +2 -2
- mage_ai/server/frontend_dist/pipelines.html +2 -2
- mage_ai/server/frontend_dist/platform/global-hooks/[...slug].html +2 -2
- mage_ai/server/frontend_dist/platform/global-hooks.html +2 -2
- mage_ai/server/frontend_dist/settings/account/profile.html +2 -2
- mage_ai/server/frontend_dist/settings/platform/preferences.html +2 -2
- mage_ai/server/frontend_dist/settings/platform/settings.html +2 -2
- mage_ai/server/frontend_dist/settings/workspace/permissions/[...slug].html +2 -2
- mage_ai/server/frontend_dist/settings/workspace/permissions.html +2 -2
- mage_ai/server/frontend_dist/settings/workspace/preferences.html +2 -2
- mage_ai/server/frontend_dist/settings/workspace/roles/[...slug].html +2 -2
- mage_ai/server/frontend_dist/settings/workspace/roles.html +2 -2
- mage_ai/server/frontend_dist/settings/workspace/sync-data.html +2 -2
- mage_ai/server/frontend_dist/settings/workspace/users/[...slug].html +2 -2
- mage_ai/server/frontend_dist/settings/workspace/users.html +2 -2
- mage_ai/server/frontend_dist/settings.html +2 -2
- mage_ai/server/frontend_dist/sign-in.html +2 -2
- mage_ai/server/frontend_dist/templates/[...slug].html +2 -2
- mage_ai/server/frontend_dist/templates.html +2 -2
- mage_ai/server/frontend_dist/terminal.html +2 -2
- mage_ai/server/frontend_dist/test.html +2 -2
- mage_ai/server/frontend_dist/triggers.html +2 -2
- mage_ai/server/frontend_dist/version-control.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/404.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/{1557-a754b04510d50b80.js → 1557-01f0843dc6ac4971.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/9440-4069842b90d4b801.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/_app-1c1ffd928f5a00f7.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/index-b7b8695a7f9efde2.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/oauth-30e34ee15d410331.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-ff4bd7a8ec3bab40.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills-aaf393c86fc1bda3.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/{edit-8d32ac7e8f023779.js → edit-36377e679da2cd91.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/logs-3f5c14076ddde20e.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/{triggers-fe08120d9a6fb1b0.js → triggers-f508c2f261297724.js} +1 -1
- mage_ai/server/{frontend_dist/_next/static/chunks/pages/pipelines-bcdb4ad41dd4c7d5.js → frontend_dist_base_path_template/_next/static/chunks/pages/pipelines-f99e99aa8f45529c.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/{sign-in-19b36600d908b711.js → sign-in-7d38b2f7c3e918a1.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/version-control-5753fac7c1bfdc88.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/{webpack-481689d9989710cd.js → webpack-68c003fb6a175cd7.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/{QYwFH4sievaq5XyUjRriy → khKiaJtwrslgMmp4YSa1f}/_buildManifest.js +1 -1
- 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 +2 -2
- mage_ai/server/frontend_dist_base_path_template/global-hooks.html +2 -2
- 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 +2 -2
- 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/platform/global-hooks/[...slug].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/platform/global-hooks.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/platform/preferences.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/platform/settings.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 +2 -2
- 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 +2 -2
- 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 +117 -87
- mage_ai/server/utils/output_display.py +6 -1
- mage_ai/services/aws/s3/s3.py +8 -2
- mage_ai/services/slack/slack.py +8 -8
- mage_ai/settings/__init__.py +36 -186
- mage_ai/settings/backends.py +95 -0
- mage_ai/settings/keys/__init__.py +1 -0
- mage_ai/settings/keys/auth.py +76 -0
- mage_ai/settings/server.py +187 -0
- mage_ai/shared/io.py +2 -2
- mage_ai/shared/logger.py +12 -6
- mage_ai/streaming/sources/base_python.py +30 -0
- mage_ai/streaming/sources/source_factory.py +25 -0
- mage_ai/tests/api/endpoints/test_oauths.py +13 -5
- mage_ai/tests/api/operations/test_operations.py +2 -2
- mage_ai/tests/api/operations/test_sessions.py +83 -48
- mage_ai/tests/authentication/oauth/test_utils.py +56 -6
- mage_ai/tests/authentication/providers/test_active_directory.py +9 -15
- mage_ai/tests/data_preparation/models/test_block.py +39 -2
- mage_ai/tests/orchestration/db/models/test_schedules.py +33 -1
- {mage_ai-0.9.65.dist-info → mage_ai-0.9.67.dist-info}/METADATA +2 -1
- {mage_ai-0.9.65.dist-info → mage_ai-0.9.67.dist-info}/RECORD +225 -217
- {mage_ai-0.9.65.dist-info → mage_ai-0.9.67.dist-info}/WHEEL +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/9440-2bcbdc765ed82062.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/_app-2ae1d919333f01fe.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/index-64851458dde54ad9.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/oauth-abe5ba687cb93509.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-22e49726eeed16ae.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills-c74507dce89b41a2.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/logs-cf656cbe37ecaacc.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/version-control-690206d30d8b412b.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/9440-2bcbdc765ed82062.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/_app-2ae1d919333f01fe.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/index-64851458dde54ad9.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/oauth-abe5ba687cb93509.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-22e49726eeed16ae.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills-c74507dce89b41a2.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/logs-cf656cbe37ecaacc.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/version-control-690206d30d8b412b.js +0 -1
- mage_ai/settings/sso.py +0 -27
- /mage_ai/server/frontend_dist/_next/static/{ZMrJfDouIX5AMb_RteRbL → vPsMu6Fi2zrHaf2fRXKRO}/_ssgManifest.js +0 -0
- /mage_ai/server/frontend_dist_base_path_template/_next/static/{QYwFH4sievaq5XyUjRriy → khKiaJtwrslgMmp4YSa1f}/_ssgManifest.js +0 -0
- {mage_ai-0.9.65.dist-info → mage_ai-0.9.67.dist-info}/LICENSE +0 -0
- {mage_ai-0.9.65.dist-info → mage_ai-0.9.67.dist-info}/entry_points.txt +0 -0
- {mage_ai-0.9.65.dist-info → mage_ai-0.9.67.dist-info}/top_level.txt +0 -0
|
@@ -6,7 +6,7 @@ from mage_ai.usage_statistics.constants import EventNameType
|
|
|
6
6
|
from mage_ai.usage_statistics.logger import UsageStatisticLogger
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
class BaseMonitor
|
|
9
|
+
class BaseMonitor:
|
|
10
10
|
def __init__(self, resource, user, error, **kwargs):
|
|
11
11
|
self.error = error
|
|
12
12
|
self.options = kwargs
|
|
@@ -24,21 +24,43 @@ class BaseMonitor():
|
|
|
24
24
|
if self.error.type:
|
|
25
25
|
data['type'] = self.error.type
|
|
26
26
|
|
|
27
|
-
asyncio.
|
|
28
|
-
|
|
27
|
+
loop = asyncio.get_event_loop()
|
|
28
|
+
|
|
29
|
+
kwargs = dict(
|
|
29
30
|
resource=self.resource,
|
|
30
|
-
**extract(
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
31
|
+
**extract(
|
|
32
|
+
data,
|
|
33
|
+
[
|
|
34
|
+
'code',
|
|
35
|
+
'errors',
|
|
36
|
+
'message',
|
|
37
|
+
'type',
|
|
38
|
+
],
|
|
39
|
+
),
|
|
40
|
+
**extract(
|
|
41
|
+
self.options,
|
|
42
|
+
[
|
|
43
|
+
'operation',
|
|
44
|
+
'resource_id',
|
|
45
|
+
'resource_parent',
|
|
46
|
+
'resource_parent_id',
|
|
47
|
+
],
|
|
48
|
+
),
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
if loop is not None:
|
|
52
|
+
loop.create_task(
|
|
53
|
+
UsageStatisticLogger().error(
|
|
54
|
+
EventNameType.API_ERROR,
|
|
55
|
+
**kwargs
|
|
56
|
+
)
|
|
57
|
+
)
|
|
58
|
+
else:
|
|
59
|
+
asyncio.run(
|
|
60
|
+
UsageStatisticLogger().error(
|
|
61
|
+
EventNameType.API_ERROR,
|
|
62
|
+
**kwargs
|
|
63
|
+
)
|
|
64
|
+
)
|
|
43
65
|
|
|
44
66
|
return data
|
|
@@ -69,7 +69,7 @@ class OauthResource(GenericResource):
|
|
|
69
69
|
model.update(**auth_url_response)
|
|
70
70
|
|
|
71
71
|
if authenticated or new_token:
|
|
72
|
-
model['authenticated'] =
|
|
72
|
+
model['authenticated'] = True
|
|
73
73
|
if new_token:
|
|
74
74
|
access_tokens = [new_token]
|
|
75
75
|
model['expires'] = max(
|
|
@@ -78,7 +78,7 @@ class OauthResource(GenericResource):
|
|
|
78
78
|
|
|
79
79
|
oauths.append(model)
|
|
80
80
|
except Exception:
|
|
81
|
-
|
|
81
|
+
pass
|
|
82
82
|
|
|
83
83
|
return self.build_result_set(oauths, user, **kwargs)
|
|
84
84
|
|
|
@@ -110,12 +110,20 @@ class OauthResource(GenericResource):
|
|
|
110
110
|
client_id=client_id,
|
|
111
111
|
client_type=Oauth2Application.ClientType.PRIVATE,
|
|
112
112
|
name=provider,
|
|
113
|
-
user_id=user.id if user else None,
|
|
114
113
|
)
|
|
115
114
|
|
|
116
|
-
|
|
115
|
+
access_token_query = Oauth2AccessToken.query.filter(
|
|
117
116
|
Oauth2AccessToken.token == token,
|
|
118
|
-
)
|
|
117
|
+
)
|
|
118
|
+
if user:
|
|
119
|
+
access_token_query = access_token_query.filter(
|
|
120
|
+
Oauth2AccessToken.user_id == user.id,
|
|
121
|
+
)
|
|
122
|
+
else:
|
|
123
|
+
access_token_query = access_token_query.filter(
|
|
124
|
+
Oauth2AccessToken.user_id.is_(None),
|
|
125
|
+
)
|
|
126
|
+
access_token = access_token_query.first()
|
|
119
127
|
|
|
120
128
|
if expires_in:
|
|
121
129
|
expire_timedelta = timedelta(seconds=int(expires_in))
|
|
@@ -10,9 +10,10 @@ from mage_ai.orchestration.db import safe_db_query
|
|
|
10
10
|
from mage_ai.orchestration.db.models.oauth import Role, User
|
|
11
11
|
from mage_ai.settings import (
|
|
12
12
|
AUTHENTICATION_MODE,
|
|
13
|
-
LDAP_DEFAULT_ACCESS,
|
|
14
13
|
OAUTH_DEFAULT_ACCESS,
|
|
14
|
+
get_settings_value,
|
|
15
15
|
)
|
|
16
|
+
from mage_ai.settings.keys import LDAP_DEFAULT_ACCESS
|
|
16
17
|
from mage_ai.usage_statistics.logger import UsageStatisticLogger
|
|
17
18
|
|
|
18
19
|
|
|
@@ -97,10 +98,11 @@ class SessionResource(BaseResource):
|
|
|
97
98
|
role_names = conn.get_user_roles(user_attributes)
|
|
98
99
|
if role_names:
|
|
99
100
|
roles = Role.query.filter(Role.name.in_(role_names)).all()
|
|
101
|
+
ldap_default_access = get_settings_value(LDAP_DEFAULT_ACCESS)
|
|
100
102
|
if not roles and \
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
default_role = Role.get_role(
|
|
103
|
+
ldap_default_access is not None and \
|
|
104
|
+
ldap_default_access in [r for r in Role.DefaultRole]:
|
|
105
|
+
default_role = Role.get_role(ldap_default_access)
|
|
104
106
|
if default_role:
|
|
105
107
|
roles.append(default_role)
|
|
106
108
|
user = User.create(
|
mage_ai/authentication/ldap.py
CHANGED
|
@@ -6,7 +6,8 @@ from ldap3 import Connection, Server
|
|
|
6
6
|
from ldap3.core.exceptions import LDAPException
|
|
7
7
|
|
|
8
8
|
from mage_ai.data_preparation.repo_manager import get_repo_config
|
|
9
|
-
from mage_ai.settings import
|
|
9
|
+
from mage_ai.settings import get_settings_value
|
|
10
|
+
from mage_ai.settings.keys import (
|
|
10
11
|
LDAP_AUTHENTICATION_FILTER,
|
|
11
12
|
LDAP_AUTHORIZATION_FILTER,
|
|
12
13
|
LDAP_BASE_DN,
|
|
@@ -120,14 +121,23 @@ class LDAPConnection(LDAPAuthenticator):
|
|
|
120
121
|
|
|
121
122
|
def new_ldap_connection() -> LDAPConnection:
|
|
122
123
|
ldap_config_from_env_vars = dict(
|
|
123
|
-
server_url=LDAP_SERVER,
|
|
124
|
-
bind_dn=LDAP_BIND_DN,
|
|
125
|
-
bind_password=LDAP_BIND_PASSWORD,
|
|
126
|
-
base_dn=LDAP_BASE_DN,
|
|
127
|
-
authentication_filter=
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
124
|
+
server_url=get_settings_value(LDAP_SERVER, 'ldaps://127.0.0.1:1636'),
|
|
125
|
+
bind_dn=get_settings_value(LDAP_BIND_DN, 'cd=admin,dc=example,dc=org'),
|
|
126
|
+
bind_password=get_settings_value(LDAP_BIND_PASSWORD, 'admin_password'),
|
|
127
|
+
base_dn=get_settings_value(LDAP_BASE_DN, 'dc=example,dc=org'),
|
|
128
|
+
authentication_filter=get_settings_value(
|
|
129
|
+
LDAP_AUTHENTICATION_FILTER,
|
|
130
|
+
'(&(|(objectClass=Pers)(objectClass=gro))(cn={username}))'
|
|
131
|
+
),
|
|
132
|
+
authorization_filter=get_settings_value(
|
|
133
|
+
LDAP_AUTHORIZATION_FILTER,
|
|
134
|
+
'(&(objectClass=groupOfNames)(cn=group)(member={user_dn}))'
|
|
135
|
+
),
|
|
136
|
+
group_field=get_settings_value(
|
|
137
|
+
LDAP_GROUP_FIELD,
|
|
138
|
+
'memberOf',
|
|
139
|
+
),
|
|
140
|
+
roles_mapping=get_settings_value(LDAP_ROLES_MAPPING),
|
|
131
141
|
)
|
|
132
142
|
try:
|
|
133
143
|
ldap_config = get_repo_config().ldap_config
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import os
|
|
2
1
|
from enum import Enum
|
|
3
2
|
from typing import Optional
|
|
4
3
|
|
|
4
|
+
from mage_ai.settings import get_settings_value
|
|
5
|
+
from mage_ai.settings.keys import GHE_HOSTNAME
|
|
6
|
+
|
|
5
7
|
ACTIVE_DIRECTORY_CLIENT_ID = '51aec820-9d49-40a9-b046-17c1f28f620d'
|
|
6
8
|
|
|
7
9
|
GITHUB_CLIENT_ID = '8577f13ddc81e2848b07'
|
|
@@ -10,6 +12,7 @@ GITHUB_STATE = '1337'
|
|
|
10
12
|
|
|
11
13
|
class ProviderName(str, Enum):
|
|
12
14
|
ACTIVE_DIRECTORY = 'active_directory'
|
|
15
|
+
AZURE_DEVOPS = 'azure_devops'
|
|
13
16
|
BITBUCKET = 'bitbucket'
|
|
14
17
|
GITHUB = 'github'
|
|
15
18
|
GITLAB = 'gitlab'
|
|
@@ -21,28 +24,19 @@ class ProviderName(str, Enum):
|
|
|
21
24
|
|
|
22
25
|
VALID_OAUTH_PROVIDERS = [e.value for e in ProviderName]
|
|
23
26
|
|
|
24
|
-
GHE_CLIENT_ID_ENV_VAR = 'GHE_CLIENT_ID'
|
|
25
|
-
GHE_CLIENT_SECRET_ENV_VAR = 'GHE_CLIENT_SECRET'
|
|
26
|
-
GHE_HOSTNAME_ENV_VAR = 'GHE_HOSTNAME'
|
|
27
|
-
|
|
28
|
-
GITLAB_HOST = os.getenv('GITLAB_HOST')
|
|
29
|
-
GITLAB_CLIENT_ID = os.getenv('GITLAB_CLIENT_ID')
|
|
30
|
-
GITLAB_CLIENT_SECRET = os.getenv('GITLAB_CLIENT_SECRET')
|
|
31
|
-
|
|
32
|
-
BITBUCKET_HOST = os.getenv('BITBUCKET_HOST')
|
|
33
|
-
BITBUCKET_OAUTH_KEY = os.getenv('BITBUCKET_OAUTH_KEY')
|
|
34
|
-
BITBUCKET_OAUTH_SECRET = os.getenv('BITBUCKET_OAUTH_SECRET')
|
|
35
|
-
|
|
36
27
|
DEFAULT_GITHUB_HOSTNAME = 'https://github.com'
|
|
37
28
|
|
|
29
|
+
# Github and GHE don't need to be added to this list because they are handled
|
|
30
|
+
# separately for now.
|
|
38
31
|
GIT_OAUTH_PROVIDERS = [
|
|
32
|
+
ProviderName.AZURE_DEVOPS,
|
|
39
33
|
ProviderName.BITBUCKET,
|
|
40
34
|
ProviderName.GITLAB,
|
|
41
35
|
]
|
|
42
36
|
|
|
43
37
|
|
|
44
38
|
def get_ghe_hostname() -> Optional[str]:
|
|
45
|
-
ghe_hostname =
|
|
39
|
+
ghe_hostname = get_settings_value(GHE_HOSTNAME)
|
|
46
40
|
if ghe_hostname and not ghe_hostname.startswith('http'):
|
|
47
41
|
ghe_hostname = f'https://{ghe_hostname}'
|
|
48
42
|
|
|
@@ -22,8 +22,6 @@ def access_tokens_for_client(
|
|
|
22
22
|
) -> List[Oauth2AccessToken]:
|
|
23
23
|
query = Oauth2Application.query.filter(Oauth2Application.client_id == client_id)
|
|
24
24
|
query.cache = True
|
|
25
|
-
if user:
|
|
26
|
-
query.filter(Oauth2Application.user_id == user.id)
|
|
27
25
|
|
|
28
26
|
oauth_client = query.first()
|
|
29
27
|
|
|
@@ -31,10 +29,18 @@ def access_tokens_for_client(
|
|
|
31
29
|
if oauth_client:
|
|
32
30
|
access_tokens_query = Oauth2AccessToken.query
|
|
33
31
|
access_tokens_query.cache = True
|
|
34
|
-
|
|
32
|
+
access_tokens_query = access_tokens_query.filter(
|
|
35
33
|
Oauth2AccessToken.expires > datetime.utcnow(),
|
|
36
34
|
Oauth2AccessToken.oauth2_application_id == oauth_client.id,
|
|
37
35
|
)
|
|
36
|
+
if user:
|
|
37
|
+
access_tokens = access_tokens_query.filter(
|
|
38
|
+
Oauth2AccessToken.user_id == user.id
|
|
39
|
+
)
|
|
40
|
+
else:
|
|
41
|
+
access_tokens = access_tokens_query.filter(
|
|
42
|
+
Oauth2AccessToken.user_id.is_(None)
|
|
43
|
+
)
|
|
38
44
|
|
|
39
45
|
return [row for row in access_tokens]
|
|
40
46
|
|
|
@@ -47,8 +53,6 @@ async def refresh_token_for_client(
|
|
|
47
53
|
) -> Awaitable[Optional[Oauth2AccessToken]]:
|
|
48
54
|
query = Oauth2Application.query.filter(Oauth2Application.client_id == client_id)
|
|
49
55
|
query.cache = True
|
|
50
|
-
if user:
|
|
51
|
-
query.filter(Oauth2Application.user_id == user.id)
|
|
52
56
|
|
|
53
57
|
oauth_client = query.first()
|
|
54
58
|
|
|
@@ -56,10 +60,18 @@ async def refresh_token_for_client(
|
|
|
56
60
|
if oauth_client:
|
|
57
61
|
access_tokens_query = Oauth2AccessToken.query
|
|
58
62
|
access_tokens_query.cache = True
|
|
59
|
-
|
|
63
|
+
access_tokens_query = access_tokens_query.filter(
|
|
60
64
|
Oauth2AccessToken.refresh_token.is_not(None),
|
|
61
65
|
Oauth2AccessToken.oauth2_application_id == oauth_client.id,
|
|
62
66
|
)
|
|
67
|
+
if user:
|
|
68
|
+
refreshable_tokens = access_tokens_query.filter(
|
|
69
|
+
Oauth2AccessToken.user_id == user.id
|
|
70
|
+
)
|
|
71
|
+
else:
|
|
72
|
+
refreshable_tokens = access_tokens_query.filter(
|
|
73
|
+
Oauth2AccessToken.user_id.is_(None)
|
|
74
|
+
)
|
|
63
75
|
|
|
64
76
|
new_token = None
|
|
65
77
|
if refreshable_tokens:
|
|
@@ -13,10 +13,11 @@ from mage_ai.authentication.providers.oauth import OauthProvider
|
|
|
13
13
|
from mage_ai.authentication.providers.sso import SsoProvider
|
|
14
14
|
from mage_ai.authentication.providers.utils import get_base_url
|
|
15
15
|
from mage_ai.server.logger import Logger
|
|
16
|
-
from mage_ai.settings import
|
|
17
|
-
from mage_ai.settings.
|
|
16
|
+
from mage_ai.settings import get_settings_value
|
|
17
|
+
from mage_ai.settings.keys import (
|
|
18
18
|
ACTIVE_DIRECTORY_CLIENT_ID,
|
|
19
19
|
ACTIVE_DIRECTORY_CLIENT_SECRET,
|
|
20
|
+
ACTIVE_DIRECTORY_DIRECTORY_ID,
|
|
20
21
|
ACTIVE_DIRECTORY_ROLES_MAPPING,
|
|
21
22
|
)
|
|
22
23
|
|
|
@@ -25,24 +26,28 @@ logger = Logger().new_server_logger(__name__)
|
|
|
25
26
|
|
|
26
27
|
class ADProvider(SsoProvider, OauthProvider):
|
|
27
28
|
provider = ProviderName.ACTIVE_DIRECTORY
|
|
29
|
+
scope = 'User.Read'
|
|
28
30
|
|
|
29
31
|
def __init__(self):
|
|
32
|
+
self.directory_id = get_settings_value(ACTIVE_DIRECTORY_DIRECTORY_ID)
|
|
33
|
+
self.client_id = get_settings_value(ACTIVE_DIRECTORY_CLIENT_ID)
|
|
34
|
+
self.client_secret = get_settings_value(ACTIVE_DIRECTORY_CLIENT_SECRET)
|
|
30
35
|
self.__validate()
|
|
31
36
|
|
|
32
|
-
self.roles_mapping =
|
|
33
|
-
if
|
|
37
|
+
self.roles_mapping = get_settings_value(ACTIVE_DIRECTORY_ROLES_MAPPING)
|
|
38
|
+
if self.roles_mapping:
|
|
34
39
|
try:
|
|
35
|
-
self.roles_mapping = json.loads(
|
|
40
|
+
self.roles_mapping = json.loads(self.roles_mapping)
|
|
36
41
|
except Exception:
|
|
37
42
|
logger.exception('Failed to parse roles mapping.')
|
|
38
43
|
|
|
39
44
|
def __validate(self):
|
|
40
|
-
if not
|
|
45
|
+
if not self.directory_id:
|
|
41
46
|
raise Exception(
|
|
42
47
|
'AD directory id is empty. '
|
|
43
48
|
'Make sure the ACTIVE_DIRECTORY_DIRECTORY_ID environment variable is set.'
|
|
44
49
|
)
|
|
45
|
-
if
|
|
50
|
+
if self.client_id and not self.client_secret:
|
|
46
51
|
raise Exception(
|
|
47
52
|
'AD client secret is empty. '
|
|
48
53
|
'Make sure the ACTIVE_DIRECTORY_CLIENT_SECRET environment variable is set.'
|
|
@@ -55,20 +60,20 @@ class ADProvider(SsoProvider, OauthProvider):
|
|
|
55
60
|
set up in Azure. They can additionally set the ACTIVE_DIRECTORY_CLIENT_ID and
|
|
56
61
|
ACTIVE_DIRECTORY_CLIENT_SECRET which will use their own Mage application in Azure.
|
|
57
62
|
"""
|
|
58
|
-
ad_directory_id =
|
|
59
|
-
if
|
|
63
|
+
ad_directory_id = self.directory_id
|
|
64
|
+
if self.client_id:
|
|
60
65
|
base_url = get_base_url(redirect_uri)
|
|
61
66
|
redirect_uri_query = dict(
|
|
62
67
|
provider=self.provider,
|
|
63
68
|
redirect_uri=redirect_uri,
|
|
64
69
|
)
|
|
65
70
|
query = dict(
|
|
66
|
-
client_id=
|
|
71
|
+
client_id=self.client_id,
|
|
67
72
|
redirect_uri=quote_plus(
|
|
68
73
|
f'{base_url}/oauth',
|
|
69
74
|
),
|
|
70
75
|
response_type='code',
|
|
71
|
-
scope=
|
|
76
|
+
scope=self.scope,
|
|
72
77
|
state=uuid.uuid4().hex,
|
|
73
78
|
)
|
|
74
79
|
query_strings = []
|
|
@@ -88,7 +93,7 @@ class ADProvider(SsoProvider, OauthProvider):
|
|
|
88
93
|
client_id=ACTIVE_DIRECTORY_MAGE_CLIENT_ID,
|
|
89
94
|
redirect_uri=f'https://api.mage.ai/v1/oauth/{self.provider}',
|
|
90
95
|
response_type='code',
|
|
91
|
-
scope=
|
|
96
|
+
scope=self.scope,
|
|
92
97
|
state=quote_plus(
|
|
93
98
|
json.dumps(
|
|
94
99
|
dict(
|
|
@@ -116,12 +121,12 @@ class ADProvider(SsoProvider, OauthProvider):
|
|
|
116
121
|
'Accept': 'application/json',
|
|
117
122
|
},
|
|
118
123
|
data=dict(
|
|
119
|
-
client_id=
|
|
120
|
-
client_secret=
|
|
124
|
+
client_id=self.client_id,
|
|
125
|
+
client_secret=self.client_secret,
|
|
121
126
|
code=code,
|
|
122
127
|
grant_type='authorization_code',
|
|
123
128
|
redirect_uri=f'{base_url}/oauth',
|
|
124
|
-
tenant=
|
|
129
|
+
tenant=self.directory_id,
|
|
125
130
|
),
|
|
126
131
|
timeout=20,
|
|
127
132
|
) as response:
|
|
@@ -150,7 +155,7 @@ class ADProvider(SsoProvider, OauthProvider):
|
|
|
150
155
|
if self.roles_mapping:
|
|
151
156
|
try:
|
|
152
157
|
async with session.get(
|
|
153
|
-
f'https://graph.microsoft.com/v1.0/servicePrincipals?$filter=appId eq \'{
|
|
158
|
+
f'https://graph.microsoft.com/v1.0/servicePrincipals?$filter=appId eq \'{self.client_id}\'&$select=id', # noqa: E501
|
|
154
159
|
headers={
|
|
155
160
|
'Content-Type': 'application\\json',
|
|
156
161
|
'Authorization': f'Bearer {access_token}',
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from mage_ai.authentication.oauth.constants import ProviderName
|
|
2
|
+
from mage_ai.authentication.providers.active_directory import ADProvider
|
|
3
|
+
from mage_ai.settings import get_settings_value
|
|
4
|
+
from mage_ai.settings.keys import AZURE_DEVOPS_ORGANIZATION
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AzureDevopsProvider(ADProvider):
|
|
8
|
+
provider = ProviderName.AZURE_DEVOPS
|
|
9
|
+
# This is a hardcoded scope value that is used to get Git access to the Azure DevOps project.
|
|
10
|
+
# This should not be changed unless this scope stops working properly.
|
|
11
|
+
scope = '499b84ac-1321-427f-aa17-267ca6975798/.default'
|
|
12
|
+
|
|
13
|
+
def __init__(self):
|
|
14
|
+
super().__init__()
|
|
15
|
+
if not get_settings_value(AZURE_DEVOPS_ORGANIZATION):
|
|
16
|
+
raise Exception(
|
|
17
|
+
'Azure DevOps organization is empty. '
|
|
18
|
+
'Make sure the AZURE_DEVOPS_ORGANIZATION environment variable is set.')
|
|
@@ -5,23 +5,24 @@ from typing import Awaitable, Dict
|
|
|
5
5
|
import aiohttp
|
|
6
6
|
from aiohttp import BasicAuth
|
|
7
7
|
|
|
8
|
-
from mage_ai.authentication.oauth.constants import
|
|
8
|
+
from mage_ai.authentication.oauth.constants import ProviderName
|
|
9
|
+
from mage_ai.authentication.providers.oauth import OauthProvider
|
|
10
|
+
from mage_ai.authentication.providers.utils import get_base_url
|
|
11
|
+
from mage_ai.settings import get_settings_value
|
|
12
|
+
from mage_ai.settings.keys import (
|
|
9
13
|
BITBUCKET_HOST,
|
|
10
14
|
BITBUCKET_OAUTH_KEY,
|
|
11
15
|
BITBUCKET_OAUTH_SECRET,
|
|
12
|
-
ProviderName,
|
|
13
16
|
)
|
|
14
|
-
from mage_ai.authentication.providers.oauth import OauthProvider
|
|
15
|
-
from mage_ai.authentication.providers.utils import get_base_url
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
class BitbucketProvider(OauthProvider):
|
|
19
20
|
provider = ProviderName.BITBUCKET
|
|
20
21
|
|
|
21
22
|
def __init__(self):
|
|
22
|
-
self.hostname = BITBUCKET_HOST or 'https://bitbucket.org'
|
|
23
|
-
self.key = BITBUCKET_OAUTH_KEY
|
|
24
|
-
self.secret = BITBUCKET_OAUTH_SECRET
|
|
23
|
+
self.hostname = get_settings_value(BITBUCKET_HOST) or 'https://bitbucket.org'
|
|
24
|
+
self.key = get_settings_value(BITBUCKET_OAUTH_KEY)
|
|
25
|
+
self.secret = get_settings_value(BITBUCKET_OAUTH_SECRET)
|
|
25
26
|
self.__validate()
|
|
26
27
|
|
|
27
28
|
def __validate(self):
|
|
@@ -71,7 +72,7 @@ class BitbucketProvider(OauthProvider):
|
|
|
71
72
|
code=code,
|
|
72
73
|
redirect_uri=f'{base_url}/oauth',
|
|
73
74
|
),
|
|
74
|
-
auth=BasicAuth(
|
|
75
|
+
auth=BasicAuth(self.key, self.secret),
|
|
75
76
|
timeout=20,
|
|
76
77
|
) as response:
|
|
77
78
|
data = await response.json()
|
|
@@ -91,7 +92,7 @@ class BitbucketProvider(OauthProvider):
|
|
|
91
92
|
grant_type='refresh_token',
|
|
92
93
|
refresh_token=refresh_token,
|
|
93
94
|
),
|
|
94
|
-
auth=BasicAuth(
|
|
95
|
+
auth=BasicAuth(self.key, self.secret),
|
|
95
96
|
timeout=20,
|
|
96
97
|
) as response:
|
|
97
98
|
data = await response.json()
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from mage_ai.authentication.oauth.constants import ProviderName
|
|
2
2
|
from mage_ai.authentication.providers.active_directory import ADProvider
|
|
3
|
+
from mage_ai.authentication.providers.azure_devops import AzureDevopsProvider
|
|
3
4
|
from mage_ai.authentication.providers.bitbucket import BitbucketProvider
|
|
4
5
|
from mage_ai.authentication.providers.ghe import GHEProvider
|
|
5
6
|
from mage_ai.authentication.providers.gitlab import GitlabProvider
|
|
@@ -9,6 +10,7 @@ from mage_ai.authentication.providers.okta import OktaProvider
|
|
|
9
10
|
|
|
10
11
|
NAME_TO_PROVIDER = {
|
|
11
12
|
ProviderName.ACTIVE_DIRECTORY: ADProvider,
|
|
13
|
+
ProviderName.AZURE_DEVOPS: AzureDevopsProvider,
|
|
12
14
|
ProviderName.BITBUCKET: BitbucketProvider,
|
|
13
15
|
ProviderName.GHE: GHEProvider,
|
|
14
16
|
ProviderName.GITLAB: GitlabProvider,
|
|
@@ -1,18 +1,14 @@
|
|
|
1
|
-
import os
|
|
2
1
|
import urllib.parse
|
|
3
2
|
import uuid
|
|
4
3
|
from typing import Awaitable, Dict
|
|
5
4
|
|
|
6
5
|
import aiohttp
|
|
7
6
|
|
|
8
|
-
from mage_ai.authentication.oauth.constants import
|
|
9
|
-
GHE_CLIENT_ID_ENV_VAR,
|
|
10
|
-
GHE_CLIENT_SECRET_ENV_VAR,
|
|
11
|
-
ProviderName,
|
|
12
|
-
get_ghe_hostname,
|
|
13
|
-
)
|
|
7
|
+
from mage_ai.authentication.oauth.constants import ProviderName, get_ghe_hostname
|
|
14
8
|
from mage_ai.authentication.providers.oauth import OauthProvider
|
|
15
9
|
from mage_ai.authentication.providers.utils import get_base_url
|
|
10
|
+
from mage_ai.settings import get_settings_value
|
|
11
|
+
from mage_ai.settings.keys import GHE_CLIENT_ID, GHE_CLIENT_SECRET
|
|
16
12
|
|
|
17
13
|
|
|
18
14
|
class GHEProvider(OauthProvider):
|
|
@@ -20,8 +16,8 @@ class GHEProvider(OauthProvider):
|
|
|
20
16
|
|
|
21
17
|
def __init__(self):
|
|
22
18
|
self.hostname = get_ghe_hostname()
|
|
23
|
-
self.client_id =
|
|
24
|
-
self.client_secret =
|
|
19
|
+
self.client_id = get_settings_value(GHE_CLIENT_ID)
|
|
20
|
+
self.client_secret = get_settings_value(GHE_CLIENT_SECRET)
|
|
25
21
|
self.__validate()
|
|
26
22
|
|
|
27
23
|
def __validate(self):
|
|
@@ -4,25 +4,22 @@ from typing import Awaitable, Dict
|
|
|
4
4
|
|
|
5
5
|
import aiohttp
|
|
6
6
|
|
|
7
|
-
from mage_ai.authentication.oauth.constants import
|
|
8
|
-
GITLAB_CLIENT_ID,
|
|
9
|
-
GITLAB_CLIENT_SECRET,
|
|
10
|
-
GITLAB_HOST,
|
|
11
|
-
ProviderName,
|
|
12
|
-
)
|
|
7
|
+
from mage_ai.authentication.oauth.constants import ProviderName
|
|
13
8
|
from mage_ai.authentication.providers.oauth import OauthProvider
|
|
14
9
|
from mage_ai.authentication.providers.utils import get_base_url
|
|
10
|
+
from mage_ai.settings import get_settings_value
|
|
11
|
+
from mage_ai.settings.keys import GITLAB_CLIENT_ID, GITLAB_CLIENT_SECRET, GITLAB_HOST
|
|
15
12
|
|
|
16
13
|
|
|
17
14
|
class GitlabProvider(OauthProvider):
|
|
18
15
|
provider = ProviderName.GITLAB
|
|
19
16
|
|
|
20
17
|
def __init__(self):
|
|
21
|
-
self.hostname = GITLAB_HOST or 'https://gitlab.com'
|
|
18
|
+
self.hostname = get_settings_value(GITLAB_HOST) or 'https://gitlab.com'
|
|
22
19
|
if not self.hostname.startswith('http'):
|
|
23
20
|
self.hostname = f'https://{self.hostname}'
|
|
24
|
-
self.client_id = GITLAB_CLIENT_ID
|
|
25
|
-
self.client_secret = GITLAB_CLIENT_SECRET
|
|
21
|
+
self.client_id = get_settings_value(GITLAB_CLIENT_ID)
|
|
22
|
+
self.client_secret = get_settings_value(GITLAB_CLIENT_SECRET)
|
|
26
23
|
self.__validate()
|
|
27
24
|
|
|
28
25
|
def __validate(self):
|
|
@@ -8,18 +8,21 @@ from mage_ai.authentication.oauth.constants import ProviderName
|
|
|
8
8
|
from mage_ai.authentication.providers.oauth import OauthProvider
|
|
9
9
|
from mage_ai.authentication.providers.sso import SsoProvider
|
|
10
10
|
from mage_ai.authentication.providers.utils import get_base_url
|
|
11
|
-
from mage_ai.settings
|
|
11
|
+
from mage_ai.settings import get_settings_value
|
|
12
|
+
from mage_ai.settings.keys import GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
class GoogleProvider(SsoProvider, OauthProvider):
|
|
15
16
|
provider = ProviderName.GOOGLE
|
|
16
17
|
|
|
17
18
|
def __init__(self):
|
|
18
|
-
|
|
19
|
+
self.client_id = get_settings_value(GOOGLE_CLIENT_ID)
|
|
20
|
+
self.client_secret = get_settings_value(GOOGLE_CLIENT_SECRET)
|
|
21
|
+
if not self.client_id:
|
|
19
22
|
raise Exception(
|
|
20
23
|
'Google client id is empty. '
|
|
21
24
|
'Make sure the GOOGLE_CLIENT_ID environment variable is set.')
|
|
22
|
-
if not
|
|
25
|
+
if not self.client_secret:
|
|
23
26
|
raise Exception(
|
|
24
27
|
'Google client secret is empty. '
|
|
25
28
|
'Make sure the GOOGLE_CLIENT_SECRET environment variable is set.')
|
|
@@ -31,7 +34,7 @@ class GoogleProvider(SsoProvider, OauthProvider):
|
|
|
31
34
|
redirect_uri=redirect_uri,
|
|
32
35
|
)
|
|
33
36
|
query = dict(
|
|
34
|
-
client_id=
|
|
37
|
+
client_id=self.client_id,
|
|
35
38
|
prompt='select_account',
|
|
36
39
|
redirect_uri=urllib.parse.quote_plus(
|
|
37
40
|
f'{base_url}/oauth',
|
|
@@ -58,8 +61,8 @@ class GoogleProvider(SsoProvider, OauthProvider):
|
|
|
58
61
|
'https://oauth2.googleapis.com/token',
|
|
59
62
|
data=dict(
|
|
60
63
|
code=code,
|
|
61
|
-
client_id=
|
|
62
|
-
client_secret=
|
|
64
|
+
client_id=self.client_id,
|
|
65
|
+
client_secret=self.client_secret,
|
|
63
66
|
redirect_uri=f'{base_url}/oauth',
|
|
64
67
|
grant_type='authorization_code',
|
|
65
68
|
),
|
|
@@ -10,7 +10,8 @@ from mage_ai.authentication.providers.oauth import OauthProvider
|
|
|
10
10
|
from mage_ai.authentication.providers.sso import SsoProvider
|
|
11
11
|
from mage_ai.authentication.providers.utils import get_base_url
|
|
12
12
|
from mage_ai.server.logger import Logger
|
|
13
|
-
from mage_ai.settings
|
|
13
|
+
from mage_ai.settings import get_settings_value
|
|
14
|
+
from mage_ai.settings.keys import OIDC_CLIENT_ID, OIDC_CLIENT_SECRET, OIDC_DISCOVERY_URL
|
|
14
15
|
|
|
15
16
|
logger = Logger().new_server_logger(__name__)
|
|
16
17
|
|
|
@@ -19,9 +20,9 @@ class OidcProvider(OauthProvider, SsoProvider):
|
|
|
19
20
|
provider = ProviderName.OIDC_GENERIC
|
|
20
21
|
|
|
21
22
|
def __init__(self):
|
|
22
|
-
self.discovery_url = OIDC_DISCOVERY_URL
|
|
23
|
-
self.client_id = OIDC_CLIENT_ID
|
|
24
|
-
self.client_secret = OIDC_CLIENT_SECRET
|
|
23
|
+
self.discovery_url = get_settings_value(OIDC_DISCOVERY_URL)
|
|
24
|
+
self.client_id = get_settings_value(OIDC_CLIENT_ID)
|
|
25
|
+
self.client_secret = get_settings_value(OIDC_CLIENT_SECRET)
|
|
25
26
|
self.__validate()
|
|
26
27
|
|
|
27
28
|
self.discover()
|
|
@@ -122,6 +123,7 @@ class OidcProvider(OauthProvider, SsoProvider):
|
|
|
122
123
|
},
|
|
123
124
|
timeout=10,
|
|
124
125
|
) as response:
|
|
126
|
+
response.raise_for_status()
|
|
125
127
|
userinfo_resp = await response.json()
|
|
126
128
|
|
|
127
129
|
email = userinfo_resp.get('email')
|