dataflow-core 2.1.8__tar.gz → 2.1.18rc3__tar.gz
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 dataflow-core might be problematic. Click here for more details.
- {dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/PKG-INFO +4 -1
- {dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/authenticator/dataflowairflowauthenticator.py +10 -2
- {dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/authenticator/dataflowhubauthenticator.py +153 -47
- {dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/authenticator/dataflowsupersetauthenticator.py +11 -6
- {dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/dataflow/configuration.py +7 -0
- {dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/dataflow/database_manager.py +23 -0
- {dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/dataflow/dataflow.py +162 -62
- {dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/dataflow/db.py +4 -3
- {dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/dataflow/environment.py +82 -51
- {dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/dataflow/models/__init__.py +7 -3
- dataflow_core-2.1.18rc3/dataflow/models/app_types.py +31 -0
- {dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/dataflow/models/blacklist_library.py +3 -3
- dataflow_core-2.1.18rc3/dataflow/models/connection.py +43 -0
- dataflow_core-2.1.18rc3/dataflow/models/dataflow_zone.py +33 -0
- dataflow_core-2.1.18rc3/dataflow/models/environment.py +246 -0
- {dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/dataflow/models/environment_status.py +6 -0
- dataflow_core-2.1.18rc3/dataflow/models/git_ssh.py +35 -0
- dataflow_core-2.1.18rc3/dataflow/models/org_associations.py +71 -0
- dataflow_core-2.1.18rc3/dataflow/models/organization.py +123 -0
- dataflow_core-2.1.18rc3/dataflow/models/pinned_projects.py +28 -0
- dataflow_core-2.1.18rc3/dataflow/models/pod_activity.py +30 -0
- dataflow_core-2.1.18rc3/dataflow/models/pod_session_history.py +29 -0
- dataflow_core-2.1.18rc3/dataflow/models/project_details.py +52 -0
- {dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/dataflow/models/recent_project_studio.py +17 -1
- {dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/dataflow/models/recent_projects.py +7 -0
- dataflow_core-2.1.18rc3/dataflow/models/role.py +54 -0
- dataflow_core-2.1.18rc3/dataflow/models/role_server.py +18 -0
- dataflow_core-2.1.18rc3/dataflow/models/role_zone.py +37 -0
- dataflow_core-2.1.18rc3/dataflow/models/server_config.py +77 -0
- dataflow_core-2.1.18rc3/dataflow/models/session.py +22 -0
- dataflow_core-2.1.18rc3/dataflow/models/team.py +32 -0
- dataflow_core-2.1.18rc3/dataflow/models/user.py +101 -0
- {dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/dataflow/models/user_team.py +12 -4
- dataflow_core-2.1.18rc3/dataflow/models/variables.py +60 -0
- dataflow_core-2.1.18rc3/dataflow/schemas/connection.py +129 -0
- dataflow_core-2.1.18rc3/dataflow/schemas/git_ssh.py +84 -0
- dataflow_core-2.1.18rc3/dataflow/schemas/secret.py +75 -0
- {dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/dataflow/scripts/clone_environment.sh +2 -1
- {dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/dataflow/scripts/create_environment.sh +4 -0
- {dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/dataflow/secrets_manager/factory.py +20 -10
- {dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/dataflow/secrets_manager/interface.py +3 -0
- {dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/dataflow/secrets_manager/providers/aws_manager.py +55 -0
- {dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/dataflow/secrets_manager/providers/azure_manager.py +55 -0
- dataflow_core-2.1.18rc3/dataflow/secrets_manager/providers/gcp_manager.py +332 -0
- {dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/dataflow/secrets_manager/service.py +17 -9
- dataflow_core-2.1.18rc3/dataflow/secrets_manager/utils.py +58 -0
- dataflow_core-2.1.18rc3/dataflow/utils/get_current_user.py +74 -0
- {dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/dataflow_core.egg-info/PKG-INFO +4 -1
- {dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/dataflow_core.egg-info/SOURCES.txt +11 -2
- {dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/dataflow_core.egg-info/requires.txt +3 -0
- {dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/dataflow_core.egg-info/top_level.txt +1 -0
- dataflow_core-2.1.18rc3/dfmigration/__init__.py +0 -0
- dataflow_core-2.1.18rc3/dfmigration/env.py +55 -0
- dataflow_core-2.1.18rc3/dfmigration/versions/001_initial_baseline_migration.py +20 -0
- dataflow_core-2.1.18rc3/dfmigration/versions/__init__.py +0 -0
- {dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/setup.py +6 -3
- dataflow_core-2.1.8/dataflow/models/app_types.py +0 -10
- dataflow_core-2.1.8/dataflow/models/connection.py +0 -25
- dataflow_core-2.1.8/dataflow/models/dataflow_zone.py +0 -19
- dataflow_core-2.1.8/dataflow/models/environment.py +0 -75
- dataflow_core-2.1.8/dataflow/models/git_ssh.py +0 -18
- dataflow_core-2.1.8/dataflow/models/pinned_projects.py +0 -15
- dataflow_core-2.1.8/dataflow/models/project_details.py +0 -23
- dataflow_core-2.1.8/dataflow/models/role.py +0 -29
- dataflow_core-2.1.8/dataflow/models/role_server.py +0 -14
- dataflow_core-2.1.8/dataflow/models/role_zone.py +0 -17
- dataflow_core-2.1.8/dataflow/models/server_config.py +0 -33
- dataflow_core-2.1.8/dataflow/models/session.py +0 -17
- dataflow_core-2.1.8/dataflow/models/team.py +0 -17
- dataflow_core-2.1.8/dataflow/models/user.py +0 -30
- dataflow_core-2.1.8/dataflow/models/user_environment.py +0 -16
- dataflow_core-2.1.8/dataflow/models/variables.py +0 -27
- dataflow_core-2.1.8/dataflow/schemas/connection.py +0 -84
- dataflow_core-2.1.8/dataflow/schemas/git_ssh.py +0 -50
- dataflow_core-2.1.8/dataflow/schemas/secret.py +0 -44
- dataflow_core-2.1.8/dataflow/utils/get_current_user.py +0 -37
- {dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/README.md +0 -0
- {dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/authenticator/__init__.py +0 -0
- {dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/dataflow/__init__.py +0 -0
- {dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/dataflow/schemas/__init__.py +0 -0
- {dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/dataflow/scripts/update_environment.sh +0 -0
- {dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/dataflow/secrets_manager/__init__.py +0 -0
- {dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/dataflow/secrets_manager/providers/__init__.py +0 -0
- {dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/dataflow/utils/__init__.py +0 -0
- {dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/dataflow/utils/exceptions.py +0 -0
- {dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/dataflow/utils/logger.py +0 -0
- {dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/dataflow_core.egg-info/dependency_links.txt +0 -0
- {dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/dataflow_core.egg-info/entry_points.txt +0 -0
- {dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/setup.cfg +0 -0
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dataflow-core
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.18rc3
|
|
4
4
|
Summary: Dataflow core package
|
|
5
5
|
Author: Dataflow
|
|
6
6
|
Author-email:
|
|
7
7
|
Requires-Dist: sqlalchemy
|
|
8
|
+
Requires-Dist: alembic
|
|
8
9
|
Requires-Dist: boto3
|
|
9
10
|
Requires-Dist: psycopg2-binary
|
|
10
11
|
Requires-Dist: pymysql
|
|
11
12
|
Requires-Dist: requests
|
|
12
13
|
Requires-Dist: azure-identity
|
|
13
14
|
Requires-Dist: azure-keyvault-secrets
|
|
15
|
+
Requires-Dist: google-auth
|
|
16
|
+
Requires-Dist: google-cloud-secret-manager
|
|
14
17
|
Dynamic: author
|
|
15
18
|
Dynamic: requires-dist
|
|
16
19
|
Dynamic: summary
|
{dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/authenticator/dataflowairflowauthenticator.py
RENAMED
|
@@ -18,9 +18,14 @@ dataflow = Dataflow()
|
|
|
18
18
|
class DataflowAuthDBView(AuthDBView):
|
|
19
19
|
@expose('/login/', methods=['GET', 'POST'])
|
|
20
20
|
def login(self):
|
|
21
|
+
|
|
22
|
+
"""This method checks for a 'dataflow_session' cookie, retrieves user details from Dataflow,
|
|
23
|
+
and logs in or creates the user in Airflow accordingly.
|
|
24
|
+
If the cookie is not present, it falls back to the standard login process.
|
|
25
|
+
|
|
26
|
+
Overrides the default login method to integrate with Dataflow authentication.
|
|
21
27
|
"""
|
|
22
|
-
|
|
23
|
-
"""
|
|
28
|
+
|
|
24
29
|
try:
|
|
25
30
|
session_id = request.cookies.get('dataflow_session')
|
|
26
31
|
if not session_id:
|
|
@@ -52,6 +57,9 @@ class DataflowAuthDBView(AuthDBView):
|
|
|
52
57
|
return super().login()
|
|
53
58
|
|
|
54
59
|
class DataflowAirflowAuthenticator(FabAirflowSecurityManagerOverride):
|
|
60
|
+
|
|
61
|
+
"""Custom Security Manager to integrate Airflow authentication with Dataflow."""
|
|
62
|
+
|
|
55
63
|
authdbview = DataflowAuthDBView
|
|
56
64
|
|
|
57
65
|
def __init__(self, appbuilder):
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import uuid
|
|
3
|
-
import re
|
|
1
|
+
import os, uuid, re, hashlib, secrets
|
|
4
2
|
from datetime import datetime, timedelta
|
|
5
3
|
from zoneinfo import ZoneInfo
|
|
6
4
|
from traitlets import Bool, Unicode
|
|
@@ -8,9 +6,17 @@ from jupyterhub.auth import Authenticator
|
|
|
8
6
|
from oauthenticator.google import GoogleOAuthenticator
|
|
9
7
|
from oauthenticator.azuread import AzureAdOAuthenticator
|
|
10
8
|
from dataflow.db import get_db
|
|
11
|
-
from dataflow.models import user as m_user, session as m_session
|
|
9
|
+
from dataflow.models import user as m_user, session as m_session
|
|
10
|
+
from sqlalchemy import or_
|
|
12
11
|
|
|
13
12
|
class DataflowBaseAuthenticator(Authenticator):
|
|
13
|
+
|
|
14
|
+
"""Base Authenticator to handle Dataflow authentication and session management.
|
|
15
|
+
Provides methods to authenticate users via Dataflow credentials, manage sessions.
|
|
16
|
+
|
|
17
|
+
Overrides JupyterHub's Authenticator class.
|
|
18
|
+
"""
|
|
19
|
+
|
|
14
20
|
enable_dataflow_auth = Bool(True, config=True, help="Enable username/password authentication")
|
|
15
21
|
|
|
16
22
|
def __init__(self, **kwargs):
|
|
@@ -25,10 +31,20 @@ class DataflowBaseAuthenticator(Authenticator):
|
|
|
25
31
|
raise
|
|
26
32
|
|
|
27
33
|
def generate_session_id(self):
|
|
34
|
+
|
|
35
|
+
"""Generate and return a unique session ID using UUID4."""
|
|
36
|
+
|
|
28
37
|
return str(uuid.uuid4())
|
|
29
38
|
|
|
30
39
|
def set_session_cookie(self, handler, session_id):
|
|
31
|
-
|
|
40
|
+
|
|
41
|
+
"""Set the dataflow_session cookie in the user's browser.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
handler: The request handler to set the cookie on.
|
|
45
|
+
session_id: The session ID to set in the cookie."""
|
|
46
|
+
|
|
47
|
+
expires = datetime.now(ZoneInfo("UTC")) + timedelta(days=60)
|
|
32
48
|
host = handler.request.host
|
|
33
49
|
domain = '.'.join(host.split('.')[-2:]) if len(host.split('.')) >= 2 else host
|
|
34
50
|
handler.set_cookie(
|
|
@@ -44,19 +60,22 @@ class DataflowBaseAuthenticator(Authenticator):
|
|
|
44
60
|
self.log.info(f"Set session cookie: dataflow_session={session_id} for host={host}")
|
|
45
61
|
|
|
46
62
|
def get_or_create_session(self, user_id):
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
63
|
+
|
|
64
|
+
"""Retrieve existing session ID for user or create a new one.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
user_id: The ID of the user to get or create a session for.
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
session_id (str): The existing or newly created session ID.
|
|
71
|
+
"""
|
|
72
|
+
|
|
55
73
|
session_id = self.generate_session_id()
|
|
56
74
|
while self.db.query(m_session.Session).filter(
|
|
57
75
|
m_session.Session.session_id == session_id
|
|
58
76
|
).first():
|
|
59
77
|
session_id = self.generate_session_id()
|
|
78
|
+
|
|
60
79
|
db_item = m_session.Session(user_id=user_id, session_id=session_id)
|
|
61
80
|
self.db.add(db_item)
|
|
62
81
|
self.db.commit()
|
|
@@ -65,6 +84,16 @@ class DataflowBaseAuthenticator(Authenticator):
|
|
|
65
84
|
return session_id
|
|
66
85
|
|
|
67
86
|
def check_blocked_users(self, username, authenticated):
|
|
87
|
+
|
|
88
|
+
"""Check if the authenticated user is blocked based on allowed_users list.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
username (str): The username of the authenticated user.
|
|
92
|
+
authenticated (dict|None): The authentication data returned from authenticate method.
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
username (str|None): The username if not blocked, else None."""
|
|
96
|
+
|
|
68
97
|
self.log.info(f"Checking blocked users for {username}: authenticated={authenticated}, allowed_users={self.allowed_users}")
|
|
69
98
|
|
|
70
99
|
if not authenticated:
|
|
@@ -77,42 +106,54 @@ class DataflowBaseAuthenticator(Authenticator):
|
|
|
77
106
|
|
|
78
107
|
return super().check_blocked_users(username, authenticated)
|
|
79
108
|
|
|
80
|
-
def get_applicant_role_id(self):
|
|
81
|
-
"""Get the role ID for 'Applicant' role"""
|
|
82
|
-
try:
|
|
83
|
-
applicant_role = (
|
|
84
|
-
self.db.query(m_role.Role)
|
|
85
|
-
.filter(m_role.Role.name == "Applicant")
|
|
86
|
-
.first()
|
|
87
|
-
)
|
|
88
|
-
if applicant_role:
|
|
89
|
-
return applicant_role.id
|
|
90
|
-
else:
|
|
91
|
-
self.log.warning("Applicant role not found in database")
|
|
92
|
-
return None
|
|
93
|
-
except Exception as e:
|
|
94
|
-
self.log.error(f"Error getting Applicant role: {str(e)}")
|
|
95
|
-
return None
|
|
96
|
-
|
|
97
109
|
def extract_username_from_email(self, email):
|
|
98
|
-
|
|
110
|
+
|
|
111
|
+
"""Extract username from email by removing domain
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
email (str): User's email address
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
username (str): Extracted username after removing domain
|
|
118
|
+
"""
|
|
119
|
+
|
|
99
120
|
if '@' in email:
|
|
100
121
|
return email.split('@')[0]
|
|
101
122
|
return email
|
|
102
123
|
|
|
124
|
+
def generate_secure_password(self):
|
|
125
|
+
|
|
126
|
+
"""Generate secure random password hash
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
password_hash (str): Securely hashed password
|
|
130
|
+
"""
|
|
131
|
+
|
|
132
|
+
salt = secrets.token_hex(16)
|
|
133
|
+
random_uuid = str(uuid.uuid4())
|
|
134
|
+
hash_obj = hashlib.sha256((random_uuid + salt).encode())
|
|
135
|
+
return hash_obj.hexdigest()
|
|
136
|
+
|
|
103
137
|
def create_new_user(self, email, first_name=None, last_name=None):
|
|
104
|
-
|
|
138
|
+
|
|
139
|
+
"""Create a new user with Applicant role
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
email (str): User's email address
|
|
143
|
+
first_name (str): User's first name
|
|
144
|
+
last_name (str): User's last name
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
new_user (m_user.User|None): Created user object or None if creation failed
|
|
148
|
+
"""
|
|
149
|
+
|
|
105
150
|
try:
|
|
106
|
-
role_id = self.get_applicant_role_id()
|
|
107
|
-
if not role_id:
|
|
108
|
-
self.log.error("Cannot create user: Applicant role not found")
|
|
109
|
-
return None
|
|
110
|
-
|
|
111
151
|
username = self.extract_username_from_email(email)
|
|
112
152
|
username = re.sub(r'[^a-z0-9]', '', username.lower())
|
|
113
153
|
if not username:
|
|
114
154
|
self.log.error("Cannot create user: Username is empty")
|
|
115
155
|
return None
|
|
156
|
+
|
|
116
157
|
existing_user = (
|
|
117
158
|
self.db.query(m_user.User)
|
|
118
159
|
.filter(m_user.User.user_name == username)
|
|
@@ -122,7 +163,7 @@ class DataflowBaseAuthenticator(Authenticator):
|
|
|
122
163
|
counter = 1
|
|
123
164
|
original_username = username
|
|
124
165
|
while existing_user:
|
|
125
|
-
username = f"{original_username}
|
|
166
|
+
username = f"{original_username}{counter}"
|
|
126
167
|
existing_user = (
|
|
127
168
|
self.db.query(m_user.User)
|
|
128
169
|
.filter(m_user.User.user_name == username)
|
|
@@ -130,13 +171,13 @@ class DataflowBaseAuthenticator(Authenticator):
|
|
|
130
171
|
)
|
|
131
172
|
counter += 1
|
|
132
173
|
|
|
174
|
+
secure_password = self.generate_secure_password()
|
|
133
175
|
new_user = m_user.User(
|
|
134
176
|
user_name=username,
|
|
135
177
|
first_name=first_name or username,
|
|
136
178
|
last_name=last_name or "",
|
|
137
179
|
email=email,
|
|
138
|
-
|
|
139
|
-
password='user@123',
|
|
180
|
+
password=secure_password,
|
|
140
181
|
)
|
|
141
182
|
|
|
142
183
|
self.db.add(new_user)
|
|
@@ -152,24 +193,42 @@ class DataflowBaseAuthenticator(Authenticator):
|
|
|
152
193
|
return None
|
|
153
194
|
|
|
154
195
|
async def authenticate_dataflow(self, handler, data):
|
|
196
|
+
|
|
197
|
+
"""Authenticate user using Dataflow username/password.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
handler: The request handler.
|
|
201
|
+
data: The authentication data containing username and password.
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
dict|None: Authentication result with username and session_id if successful, else None.
|
|
205
|
+
"""
|
|
206
|
+
|
|
155
207
|
if not (self.enable_dataflow_auth and isinstance(data, dict) and data.get("username") and data.get("password")):
|
|
156
208
|
return None
|
|
157
|
-
|
|
209
|
+
user_name_or_email = data["username"]
|
|
158
210
|
password = data["password"]
|
|
159
|
-
self.log.info(f"Attempting Dataflow authentication for user: {
|
|
211
|
+
self.log.info(f"Attempting Dataflow authentication for user: {user_name_or_email}")
|
|
160
212
|
try:
|
|
161
213
|
user = (
|
|
162
214
|
self.db.query(m_user.User)
|
|
163
|
-
.filter(
|
|
215
|
+
.filter(
|
|
216
|
+
or_(
|
|
217
|
+
m_user.User.email == user_name_or_email,
|
|
218
|
+
m_user.User.user_name == user_name_or_email
|
|
219
|
+
)
|
|
220
|
+
)
|
|
164
221
|
.first()
|
|
165
222
|
)
|
|
223
|
+
|
|
166
224
|
if not user or user.password != password:
|
|
167
|
-
self.log.warning(f"Dataflow authentication failed for user: {
|
|
225
|
+
self.log.warning(f"Dataflow authentication failed for user: {user_name_or_email}")
|
|
168
226
|
return None
|
|
227
|
+
|
|
169
228
|
session_id = self.get_or_create_session(user.user_id)
|
|
170
229
|
self.set_session_cookie(handler, session_id)
|
|
171
|
-
self.log.info(f"Dataflow authentication successful for user: {
|
|
172
|
-
return {"name":
|
|
230
|
+
self.log.info(f"Dataflow authentication successful for user: {user.user_name}")
|
|
231
|
+
return {"name": user.user_name, "session_id": session_id, "auth_state": {}}
|
|
173
232
|
except Exception as e:
|
|
174
233
|
self.log.error(f"Dataflow authentication error: {str(e)}")
|
|
175
234
|
return None
|
|
@@ -177,6 +236,18 @@ class DataflowBaseAuthenticator(Authenticator):
|
|
|
177
236
|
self.db.close()
|
|
178
237
|
|
|
179
238
|
class DataflowGoogleAuthenticator(DataflowBaseAuthenticator, GoogleOAuthenticator):
|
|
239
|
+
|
|
240
|
+
"""Authenticator to handle Google OAuth authentication with Dataflow integration.
|
|
241
|
+
|
|
242
|
+
Overrides
|
|
243
|
+
- DataflowBaseAuthenticator
|
|
244
|
+
- GoogleOAuthenticator
|
|
245
|
+
|
|
246
|
+
Requires Google OAuth credentials.
|
|
247
|
+
- google_client_id
|
|
248
|
+
- google_client_secret
|
|
249
|
+
"""
|
|
250
|
+
|
|
180
251
|
dataflow_oauth_type = Unicode(
|
|
181
252
|
default_value="google",
|
|
182
253
|
config=True,
|
|
@@ -195,6 +266,17 @@ class DataflowGoogleAuthenticator(DataflowBaseAuthenticator, GoogleOAuthenticato
|
|
|
195
266
|
f"enable_dataflow_auth={self.enable_dataflow_auth}")
|
|
196
267
|
|
|
197
268
|
async def authenticate(self, handler, data):
|
|
269
|
+
|
|
270
|
+
"""Authenticate user using Google OAuth with Dataflow integration.
|
|
271
|
+
|
|
272
|
+
Args:
|
|
273
|
+
handler: The request handler.
|
|
274
|
+
data: The authentication data.
|
|
275
|
+
|
|
276
|
+
Returns:
|
|
277
|
+
dict|None: Authentication result with username and session_id if successful, else None.
|
|
278
|
+
"""
|
|
279
|
+
|
|
198
280
|
self.log.info(f"Authenticate called with data: {data}, request_uri: {handler.request.uri}")
|
|
199
281
|
result = await self.authenticate_dataflow(handler, data)
|
|
200
282
|
if result:
|
|
@@ -243,6 +325,19 @@ class DataflowGoogleAuthenticator(DataflowBaseAuthenticator, GoogleOAuthenticato
|
|
|
243
325
|
self.db.close()
|
|
244
326
|
|
|
245
327
|
class DataflowAzureAuthenticator(DataflowBaseAuthenticator, AzureAdOAuthenticator):
|
|
328
|
+
|
|
329
|
+
"""Authenticator to handle Azure AD OAuth authentication with Dataflow integration.
|
|
330
|
+
|
|
331
|
+
Overrides
|
|
332
|
+
- DataflowBaseAuthenticator
|
|
333
|
+
- AzureAdOAuthenticator
|
|
334
|
+
|
|
335
|
+
Requires Azure AD OAuth credentials.
|
|
336
|
+
- azure_client_id
|
|
337
|
+
- azure_client_secret
|
|
338
|
+
- azure_tenant_id
|
|
339
|
+
"""
|
|
340
|
+
|
|
246
341
|
azure_client_id = Unicode(config=True, help="Azure AD OAuth client ID")
|
|
247
342
|
azure_client_secret = Unicode(config=True, help="Azure AD OAuth client secret")
|
|
248
343
|
azure_tenant_id = Unicode(config=True, help="Azure AD tenant ID")
|
|
@@ -264,6 +359,17 @@ class DataflowAzureAuthenticator(DataflowBaseAuthenticator, AzureAdOAuthenticato
|
|
|
264
359
|
f"enable_dataflow_auth={self.enable_dataflow_auth}")
|
|
265
360
|
|
|
266
361
|
async def authenticate(self, handler, data):
|
|
362
|
+
|
|
363
|
+
"""Authenticate user using Azure AD OAuth with Dataflow integration.
|
|
364
|
+
|
|
365
|
+
Args:
|
|
366
|
+
handler: The request handler.
|
|
367
|
+
data: The authentication data.
|
|
368
|
+
|
|
369
|
+
Returns:
|
|
370
|
+
dict|None: Authentication result with username and session_id if successful, else None.
|
|
371
|
+
"""
|
|
372
|
+
|
|
267
373
|
result = await self.authenticate_dataflow(handler, data)
|
|
268
374
|
if result:
|
|
269
375
|
return result
|
|
@@ -302,7 +408,7 @@ class DataflowAzureAuthenticator(DataflowBaseAuthenticator, AzureAdOAuthenticato
|
|
|
302
408
|
self.set_session_cookie(handler, session_id)
|
|
303
409
|
self.log.info(f"Azure AD OAuth completed for user: {username}, session_id={session_id}")
|
|
304
410
|
return {
|
|
305
|
-
"name":
|
|
411
|
+
"name": username,
|
|
306
412
|
"session_id": session_id,
|
|
307
413
|
"auth_state": user.get("auth_state", {})
|
|
308
414
|
}
|
{dataflow_core-2.1.8 → dataflow_core-2.1.18rc3}/authenticator/dataflowsupersetauthenticator.py
RENAMED
|
@@ -12,6 +12,7 @@ from superset.security import SupersetSecurityManager
|
|
|
12
12
|
from dataflow.dataflow import Dataflow
|
|
13
13
|
|
|
14
14
|
class DataflowAuthDBView(AuthDBView):
|
|
15
|
+
|
|
15
16
|
def __init__(self):
|
|
16
17
|
self.dataflow = Dataflow()
|
|
17
18
|
|
|
@@ -33,14 +34,15 @@ class DataflowAuthDBView(AuthDBView):
|
|
|
33
34
|
@expose('/login/', methods=['GET', "POST"])
|
|
34
35
|
def login(self):
|
|
35
36
|
"""
|
|
36
|
-
|
|
37
|
+
This method handles authentication for superset in Dataflow.
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
-
|
|
39
|
+
Methods:
|
|
40
|
+
- GET:
|
|
41
|
+
Used for browser-based login. Authenticates using session cookie and redirects to home.
|
|
40
42
|
|
|
41
|
-
|
|
42
|
-
-
|
|
43
|
-
|
|
43
|
+
- POST:
|
|
44
|
+
Used for API-based login. Returns JWT access token for programmatic access.
|
|
45
|
+
Returns JSON response with access token
|
|
44
46
|
"""
|
|
45
47
|
if request.method == "GET":
|
|
46
48
|
session_id = request.cookies.get('dataflow_session')
|
|
@@ -71,6 +73,9 @@ class DataflowAuthDBView(AuthDBView):
|
|
|
71
73
|
return jsonify(resp)
|
|
72
74
|
|
|
73
75
|
class DataflowSecurityManager(SupersetSecurityManager):
|
|
76
|
+
|
|
77
|
+
"""Custom Security Manager integrating Dataflow authentication with superset."""
|
|
78
|
+
|
|
74
79
|
authdbview = DataflowAuthDBView
|
|
75
80
|
def __init__(self, appbuilder):
|
|
76
81
|
super(DataflowSecurityManager, self).__init__(appbuilder)
|
|
@@ -20,6 +20,13 @@ class ConfigurationManager:
|
|
|
20
20
|
def get_config_value(self, section, option):
|
|
21
21
|
"""
|
|
22
22
|
Get configuration value
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
section (str): The section in the config file.
|
|
26
|
+
option (str): The option within the section.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
str | None: The configuration value or None if not found.
|
|
23
30
|
"""
|
|
24
31
|
try:
|
|
25
32
|
return self.config.get(section, option)
|
|
@@ -5,11 +5,22 @@ from sqlalchemy import create_engine
|
|
|
5
5
|
from sqlalchemy.orm import sessionmaker
|
|
6
6
|
|
|
7
7
|
class DatabaseManager:
|
|
8
|
+
|
|
9
|
+
"""Manages database connections and sessions."""
|
|
10
|
+
|
|
8
11
|
def __init__(self, db_url):
|
|
9
12
|
self.db_url = db_url
|
|
10
13
|
self.engine = self.get_engine()
|
|
11
14
|
|
|
12
15
|
def get_engine(self):
|
|
16
|
+
|
|
17
|
+
"""
|
|
18
|
+
Create a new SQLAlchemy engine instance.
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
Engine: The SQLAlchemy engine instance.
|
|
22
|
+
"""
|
|
23
|
+
|
|
13
24
|
try:
|
|
14
25
|
engine = create_engine(self.db_url)
|
|
15
26
|
return engine
|
|
@@ -17,6 +28,12 @@ class DatabaseManager:
|
|
|
17
28
|
raise e
|
|
18
29
|
|
|
19
30
|
def get_session(self):
|
|
31
|
+
"""
|
|
32
|
+
Create a new SQLAlchemy session.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
Session: The SQLAlchemy session instance.
|
|
36
|
+
"""
|
|
20
37
|
try:
|
|
21
38
|
engine = self.engine
|
|
22
39
|
session = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
|
@@ -30,4 +47,10 @@ class DatabaseManager:
|
|
|
30
47
|
raise e
|
|
31
48
|
|
|
32
49
|
def get_base(self):
|
|
50
|
+
"""
|
|
51
|
+
Get the declarative base class for the ORM models.
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
Base: The declarative base class.
|
|
55
|
+
"""
|
|
33
56
|
return declarative_base()
|