dataflow-core 2.1.28__tar.gz → 2.1.29__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.
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/PKG-INFO +1 -1
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/authenticator/dataflowhubauthenticator.py +60 -59
- dataflow_core-2.1.29/authenticator/utils/verify_email_domain.py +67 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/models/__init__.py +5 -1
- dataflow_core-2.1.29/dataflow/models/active_server_view.py +27 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/models/app_types.py +2 -0
- dataflow_core-2.1.29/dataflow/models/container.py +54 -0
- dataflow_core-2.1.29/dataflow/models/inference_service.py +59 -0
- dataflow_core-2.1.29/dataflow/models/onboarding_project.py +54 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/models/org_associations.py +15 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/models/organization.py +2 -1
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/models/project_details.py +1 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/models/recent_project_studio.py +15 -1
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/models/role.py +1 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/models/server_config.py +3 -1
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/models/user.py +8 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/secrets_manager/providers/gcp_manager.py +3 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/utils/get_current_user.py +4 -2
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow_core.egg-info/PKG-INFO +1 -1
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow_core.egg-info/SOURCES.txt +15 -0
- dataflow_core-2.1.29/dfmigration/versions/012_app_type_public_and_project_description.py +24 -0
- dataflow_core-2.1.29/dfmigration/versions/013_add_container_table.py +52 -0
- dataflow_core-2.1.29/dfmigration/versions/014_active_server_view_can_spawn.py +56 -0
- dataflow_core-2.1.29/dfmigration/versions/015_add_agent_enabled_org.py +27 -0
- dataflow_core-2.1.29/dfmigration/versions/016_org_server_visibility_controls.py +32 -0
- dataflow_core-2.1.29/dfmigration/versions/017_add_created_at_to_user.py +32 -0
- dataflow_core-2.1.29/dfmigration/versions/018_add_inference_service_table.py +67 -0
- dataflow_core-2.1.29/dfmigration/versions/019_user_onboarding_demo_selection.py +110 -0
- dataflow_core-2.1.29/dfmigration/versions/020_create_onboarding_project_table.py +129 -0
- dataflow_core-2.1.29/dfmigration/versions/__init__.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/setup.py +1 -1
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/README.md +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/authenticator/__init__.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/authenticator/dataflowairflowauthenticator.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/authenticator/dataflowsupersetauthenticator.py +0 -0
- {dataflow_core-2.1.28/dataflow/schemas → dataflow_core-2.1.29/authenticator/utils}/__init__.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/__init__.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/configuration.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/database_manager.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/dataflow.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/db.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/environment.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/models/blacklist_library.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/models/connection.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/models/connection_type.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/models/dataflow_setting.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/models/dataflow_zone.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/models/environment.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/models/environment_status.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/models/git_ssh.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/models/otp.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/models/pinned_projects.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/models/pod_activity.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/models/pod_session_history.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/models/recent_projects.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/models/role_server.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/models/role_zone.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/models/session.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/models/subscription.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/models/team.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/models/user_team.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/models/variables.py +0 -0
- {dataflow_core-2.1.28/dataflow/secrets_manager/providers → dataflow_core-2.1.29/dataflow/schemas}/__init__.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/schemas/connection.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/schemas/git_ssh.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/schemas/secret.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/scripts/clone_environment.sh +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/scripts/create_environment.sh +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/secrets_manager/__init__.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/secrets_manager/factory.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/secrets_manager/interface.py +0 -0
- {dataflow_core-2.1.28/dataflow/utils → dataflow_core-2.1.29/dataflow/secrets_manager/providers}/__init__.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/secrets_manager/providers/aws_manager.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/secrets_manager/providers/azure_manager.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/secrets_manager/service.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/secrets_manager/utils.py +0 -0
- {dataflow_core-2.1.28/dfmigration → dataflow_core-2.1.29/dataflow/utils}/__init__.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/utils/blocked_domains.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/utils/exceptions.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/utils/logger.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow_core.egg-info/dependency_links.txt +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow_core.egg-info/entry_points.txt +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow_core.egg-info/requires.txt +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow_core.egg-info/top_level.txt +0 -0
- {dataflow_core-2.1.28/dfmigration/versions → dataflow_core-2.1.29/dfmigration}/__init__.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dfmigration/env.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dfmigration/versions/001_initial_baseline_migration.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dfmigration/versions/002_user_onboarding_migration.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dfmigration/versions/003_runtime_visibility.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dfmigration/versions/004_runtime_custom_image.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dfmigration/versions/005_session_table_migration.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dfmigration/versions/006_super_admin_migration.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dfmigration/versions/007_show_walkthrough_and_org_email_migration.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dfmigration/versions/008_costing_migration.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dfmigration/versions/009_session_migration.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dfmigration/versions/010_add_environment_job_table.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/dfmigration/versions/011_connection_type_migration.py +0 -0
- {dataflow_core-2.1.28 → dataflow_core-2.1.29}/setup.cfg +0 -0
|
@@ -12,6 +12,8 @@ from dataflow.configuration import ConfigurationManager
|
|
|
12
12
|
import asyncio
|
|
13
13
|
import httpx
|
|
14
14
|
|
|
15
|
+
from .utils.verify_email_domain import is_disposable_email
|
|
16
|
+
|
|
15
17
|
class DataflowBaseAuthenticator(Authenticator):
|
|
16
18
|
"""Base Authenticator to handle Dataflow authentication and session management.
|
|
17
19
|
Provides methods to authenticate users via Dataflow credentials, manage sessions.
|
|
@@ -177,7 +179,7 @@ class DataflowBaseAuthenticator(Authenticator):
|
|
|
177
179
|
except Exception as e:
|
|
178
180
|
self.log.warning(f"Error sending user creation email for {email}: {str(e)}")
|
|
179
181
|
|
|
180
|
-
def create_new_user(self, email, first_name=None, last_name=None):
|
|
182
|
+
def create_new_user(self, email, first_name=None, last_name=None, handler=None):
|
|
181
183
|
|
|
182
184
|
"""Create a new user with Applicant role
|
|
183
185
|
|
|
@@ -185,6 +187,7 @@ class DataflowBaseAuthenticator(Authenticator):
|
|
|
185
187
|
email (str): User's email address
|
|
186
188
|
first_name (str): User's first name
|
|
187
189
|
last_name (str): User's last name
|
|
190
|
+
handler: optional request handler to show a login_error page when signup is refused
|
|
188
191
|
|
|
189
192
|
Returns:
|
|
190
193
|
new_user (m_user.User|None): Created user object or None if creation failed
|
|
@@ -193,6 +196,16 @@ class DataflowBaseAuthenticator(Authenticator):
|
|
|
193
196
|
try:
|
|
194
197
|
# Normalize email to lowercase for consistency
|
|
195
198
|
email = email.lower()
|
|
199
|
+
|
|
200
|
+
# Disallow disposable / temporary email addresses at signup
|
|
201
|
+
if is_disposable_email(email):
|
|
202
|
+
self.log.warning(f"Rejected signup for disposable email: {email}")
|
|
203
|
+
if handler is not None:
|
|
204
|
+
try:
|
|
205
|
+
self.login_error(handler, "Disposable or temporary email addresses cannot be used. Please use a personal or work email.", "Invalid Email")
|
|
206
|
+
except Exception as e:
|
|
207
|
+
self.log.warning(f"Failed to render disposable email error page: {e}")
|
|
208
|
+
return None
|
|
196
209
|
|
|
197
210
|
username = self.extract_username_from_email(email)
|
|
198
211
|
username = re.sub(r'[^a-z0-9]', '', username.lower())
|
|
@@ -229,6 +242,22 @@ class DataflowBaseAuthenticator(Authenticator):
|
|
|
229
242
|
self.db.add(new_user)
|
|
230
243
|
self.db.commit()
|
|
231
244
|
self.db.refresh(new_user)
|
|
245
|
+
|
|
246
|
+
# Create initial USER_ONBOARDING entry with demo_launch_initialized = False
|
|
247
|
+
try:
|
|
248
|
+
if hasattr(m_user, "UserOnboarding"):
|
|
249
|
+
onboarding = m_user.UserOnboarding(
|
|
250
|
+
user_id=new_user.user_id,
|
|
251
|
+
demo_launch_initialized=False
|
|
252
|
+
)
|
|
253
|
+
self.db.add(onboarding)
|
|
254
|
+
self.db.commit()
|
|
255
|
+
else:
|
|
256
|
+
self.log.warning("UserOnboarding model not found in m_user; skipping onboarding entry creation")
|
|
257
|
+
|
|
258
|
+
except Exception as e:
|
|
259
|
+
self.log.warning(f"Failed to create USER_ONBOARDING entry for user {username}: {e}")
|
|
260
|
+
self.db.rollback()
|
|
232
261
|
|
|
233
262
|
self.log.info(f"Created new user: {username} with email: {email}")
|
|
234
263
|
|
|
@@ -276,6 +305,33 @@ class DataflowBaseAuthenticator(Authenticator):
|
|
|
276
305
|
# return False
|
|
277
306
|
|
|
278
307
|
return True
|
|
308
|
+
|
|
309
|
+
def login_error(self, handler, message, title="Authentication Failed"):
|
|
310
|
+
"""Custom error handler with simple centered design"""
|
|
311
|
+
html = f"""
|
|
312
|
+
<!DOCTYPE html>
|
|
313
|
+
<html>
|
|
314
|
+
<head>
|
|
315
|
+
<meta charset="UTF-8">
|
|
316
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
317
|
+
<title>{title}</title>
|
|
318
|
+
<link href='https://fonts.googleapis.com/css?family=Lato:400,600' rel='stylesheet'>
|
|
319
|
+
</head>
|
|
320
|
+
<body style="margin: 0; padding: 20px; font-family: 'Lato', Arial, sans-serif; background-color: #f8fafc; min-height: 100vh; display: flex; align-items: center; justify-content: center;">
|
|
321
|
+
<div style="background: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); max-width: 450px; width: 100%; padding: 40px; text-align: center;">
|
|
322
|
+
<div style="width: 60px; height: 60px; background-color: #ffebee; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin: 0 auto 20px; font-size: 30px; color: #d32f2f;">!</div>
|
|
323
|
+
|
|
324
|
+
<h1 style="font-size: 24px; font-weight: 600; color: #121926; margin: 0 0 12px 0;">{title}</h1>
|
|
325
|
+
|
|
326
|
+
<p style="font-size: 15px; color: #697586; line-height: 1.5; margin: 0 0 24px 0;">{message}</p>
|
|
327
|
+
|
|
328
|
+
<a href="/hub/login" style="display: inline-block; padding: 12px 32px; background-color: #30baba; color: white; text-decoration: none; border-radius: 6px; font-size: 14px; font-weight: 600; transition: background-color 0.3s;">Try Again</a>
|
|
329
|
+
</div>
|
|
330
|
+
</body>
|
|
331
|
+
</html>
|
|
332
|
+
"""
|
|
333
|
+
handler.set_status(403)
|
|
334
|
+
handler.finish(html)
|
|
279
335
|
|
|
280
336
|
async def authenticate_dataflow(self, handler, data):
|
|
281
337
|
|
|
@@ -328,7 +384,7 @@ class DataflowBaseAuthenticator(Authenticator):
|
|
|
328
384
|
self.log.info(f"OTP validated and deleted for user: {user_email}")
|
|
329
385
|
if not user:
|
|
330
386
|
try:
|
|
331
|
-
db_user = self.create_new_user(user_email)
|
|
387
|
+
db_user = self.create_new_user(user_email, handler=handler)
|
|
332
388
|
if not db_user:
|
|
333
389
|
self.log.error(f"Failed to create new user for email: {user_email}")
|
|
334
390
|
self.login_error(handler, "Authentication error occurred. Please try again.")
|
|
@@ -389,35 +445,6 @@ class DataflowGoogleAuthenticator(DataflowBaseAuthenticator, GoogleOAuthenticato
|
|
|
389
445
|
f"oauth_callback_url={self.oauth_callback_url}, "
|
|
390
446
|
f"enable_dataflow_auth={self.enable_dataflow_auth}")
|
|
391
447
|
|
|
392
|
-
def login_error(self, handler, message, title="Authentication Failed"):
|
|
393
|
-
"""Custom error handler with simple centered design"""
|
|
394
|
-
html = f"""
|
|
395
|
-
<!DOCTYPE html>
|
|
396
|
-
<html>
|
|
397
|
-
<head>
|
|
398
|
-
<meta charset="UTF-8">
|
|
399
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
400
|
-
<title>{title}</title>
|
|
401
|
-
<link href='https://fonts.googleapis.com/css?family=Lato:400,600' rel='stylesheet'>
|
|
402
|
-
</head>
|
|
403
|
-
<body style="margin: 0; padding: 20px; font-family: 'Lato', Arial, sans-serif; background-color: #f8fafc; min-height: 100vh; display: flex; align-items: center; justify-content: center;">
|
|
404
|
-
<div style="background: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); max-width: 450px; width: 100%; padding: 40px; text-align: center;">
|
|
405
|
-
<div style="width: 60px; height: 60px; background-color: #ffebee; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin: 0 auto 20px; font-size: 30px; color: #d32f2f;">!</div>
|
|
406
|
-
|
|
407
|
-
<h1 style="font-size: 24px; font-weight: 600; color: #121926; margin: 0 0 12px 0;">{title}</h1>
|
|
408
|
-
|
|
409
|
-
<p style="font-size: 15px; color: #697586; line-height: 1.5; margin: 0 0 24px 0;">{message}</p>
|
|
410
|
-
|
|
411
|
-
<a href="/hub/login" style="display: inline-block; padding: 12px 32px; background-color: #30baba; color: white; text-decoration: none; border-radius: 6px; font-size: 14px; font-weight: 600; transition: background-color 0.3s;">Try Again</a>
|
|
412
|
-
</div>
|
|
413
|
-
</body>
|
|
414
|
-
</html>
|
|
415
|
-
"""
|
|
416
|
-
handler.set_status(403)
|
|
417
|
-
handler.finish(html)
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
448
|
async def authenticate(self, handler, data):
|
|
422
449
|
|
|
423
450
|
"""Authenticate user using Google OAuth with Dataflow integration.
|
|
@@ -474,7 +501,7 @@ class DataflowGoogleAuthenticator(DataflowBaseAuthenticator, GoogleOAuthenticato
|
|
|
474
501
|
self.log.info(f"Creating user with first_name='{first_name}', last_name='{last_name}' from Google data: {user_info}")
|
|
475
502
|
|
|
476
503
|
try:
|
|
477
|
-
db_user = self.create_new_user(email, first_name, last_name)
|
|
504
|
+
db_user = self.create_new_user(email, first_name, last_name, handler)
|
|
478
505
|
if not db_user:
|
|
479
506
|
self.log.error(f"Failed to create new user for email: {email}")
|
|
480
507
|
self.login_error(handler, "Authentication error occurred. Please try again.")
|
|
@@ -543,32 +570,6 @@ class DataflowAzureAuthenticator(DataflowBaseAuthenticator, AzureAdOAuthenticato
|
|
|
543
570
|
f"oauth_callback_url={self.oauth_callback_url}, "
|
|
544
571
|
f"enable_dataflow_auth={self.enable_dataflow_auth}")
|
|
545
572
|
|
|
546
|
-
def login_error(self, handler, message, title="Authentication Failed"):
|
|
547
|
-
"""Custom error handler with simple centered design"""
|
|
548
|
-
html = f"""
|
|
549
|
-
<!DOCTYPE html>
|
|
550
|
-
<html>
|
|
551
|
-
<head>
|
|
552
|
-
<meta charset="UTF-8">
|
|
553
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
554
|
-
<title>{title}</title>
|
|
555
|
-
<link href='https://fonts.googleapis.com/css?family=Lato:400,600' rel='stylesheet'>
|
|
556
|
-
</head>
|
|
557
|
-
<body style="margin: 0; padding: 20px; font-family: 'Lato', Arial, sans-serif; background-color: #f8fafc; min-height: 100vh; display: flex; align-items: center; justify-content: center;">
|
|
558
|
-
<div style="background: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); max-width: 450px; width: 100%; padding: 40px; text-align: center;">
|
|
559
|
-
<div style="width: 60px; height: 60px; background-color: #ffebee; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin: 0 auto 20px; font-size: 30px; color: #d32f2f;">!</div>
|
|
560
|
-
|
|
561
|
-
<h1 style="font-size: 24px; font-weight: 600; color: #121926; margin: 0 0 12px 0;">{title}</h1>
|
|
562
|
-
|
|
563
|
-
<p style="font-size: 15px; color: #697586; line-height: 1.5; margin: 0 0 24px 0;">{message}</p>
|
|
564
|
-
|
|
565
|
-
<a href="/hub/login" style="display: inline-block; padding: 12px 32px; background-color: #30baba; color: white; text-decoration: none; border-radius: 6px; font-size: 14px; font-weight: 600; transition: background-color 0.3s;">Try Again</a>
|
|
566
|
-
</div>
|
|
567
|
-
</body>
|
|
568
|
-
</html>
|
|
569
|
-
"""
|
|
570
|
-
handler.set_status(403)
|
|
571
|
-
handler.finish(html)
|
|
572
573
|
|
|
573
574
|
async def authenticate(self, handler, data):
|
|
574
575
|
"""Authenticate user using Azure AD OAuth with Dataflow integration.
|
|
@@ -626,7 +627,7 @@ class DataflowAzureAuthenticator(DataflowBaseAuthenticator, AzureAdOAuthenticato
|
|
|
626
627
|
self.log.info(f"Creating user with first_name='{first_name}', last_name='{last_name}' from Azure data: {user_info}")
|
|
627
628
|
|
|
628
629
|
try:
|
|
629
|
-
db_user = self.create_new_user(email, first_name, last_name)
|
|
630
|
+
db_user = self.create_new_user(email, first_name, last_name, handler)
|
|
630
631
|
if not db_user:
|
|
631
632
|
self.log.error(f"Failed to create new user for email: {email}")
|
|
632
633
|
self.login_error(handler, "Authentication error occurred. Please try again.")
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""Simple cached-file disposable email checker.
|
|
2
|
+
|
|
3
|
+
Behavior:
|
|
4
|
+
- Read domains from a local cache file (`CACHE`).
|
|
5
|
+
- Cache contents in memory and reload only when the file modification time changes.
|
|
6
|
+
- If the cache file is missing or unreadable, treat all emails as valid (returns False).
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
from typing import Set, Optional
|
|
12
|
+
|
|
13
|
+
CACHE = '/dataflow/app/constants/domains.txt'
|
|
14
|
+
|
|
15
|
+
_domains: Set[str] = set()
|
|
16
|
+
_last_mtime: Optional[float] = None
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _load_cache(path: str) -> bool:
|
|
20
|
+
"""Load domains from `path` into the in-memory set.
|
|
21
|
+
|
|
22
|
+
Returns True if the cache file existed and was loaded (or already up-to-date),
|
|
23
|
+
False if the file did not exist or could not be read.
|
|
24
|
+
"""
|
|
25
|
+
global _domains, _last_mtime
|
|
26
|
+
try:
|
|
27
|
+
if not os.path.exists(path):
|
|
28
|
+
_domains = set()
|
|
29
|
+
_last_mtime = None
|
|
30
|
+
return False
|
|
31
|
+
|
|
32
|
+
mtime = os.path.getmtime(path)
|
|
33
|
+
if _last_mtime is None or mtime != _last_mtime:
|
|
34
|
+
# (re)load file
|
|
35
|
+
domains: Set[str] = set()
|
|
36
|
+
with open(path, 'r') as fh:
|
|
37
|
+
for ln in fh:
|
|
38
|
+
ln = ln.strip()
|
|
39
|
+
if not ln or ln.startswith('#'):
|
|
40
|
+
continue
|
|
41
|
+
domains.add(ln.lower())
|
|
42
|
+
_domains = domains
|
|
43
|
+
_last_mtime = mtime
|
|
44
|
+
return True
|
|
45
|
+
except Exception:
|
|
46
|
+
# On any error treat as missing so callers can decide (we return False)
|
|
47
|
+
_domains = set()
|
|
48
|
+
_last_mtime = None
|
|
49
|
+
return False
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def is_disposable_email(email: str) -> bool:
|
|
53
|
+
"""Check whether `email` belongs to a disposable domain.
|
|
54
|
+
|
|
55
|
+
- If the cache file is missing/unreadable: return True (treat as disposable).
|
|
56
|
+
- If email is invalid (no '@'): return False.
|
|
57
|
+
"""
|
|
58
|
+
if not email or '@' not in email:
|
|
59
|
+
return False
|
|
60
|
+
|
|
61
|
+
cache_ok = _load_cache(CACHE)
|
|
62
|
+
if not cache_ok:
|
|
63
|
+
# No reliable domain list available allow all
|
|
64
|
+
return False
|
|
65
|
+
|
|
66
|
+
domain = email.split('@', 1)[1].lower()
|
|
67
|
+
return domain in _domains
|
|
@@ -18,7 +18,7 @@ from .environment_status import EnvironmentStatus
|
|
|
18
18
|
from .user_team import UserTeam
|
|
19
19
|
from .role_server import RoleServer
|
|
20
20
|
from .variables import Variable
|
|
21
|
-
from .recent_project_studio import RecentProjectStudio
|
|
21
|
+
from .recent_project_studio import RecentProjectStudio, AppLaunchStatus
|
|
22
22
|
from .connection import Connection
|
|
23
23
|
from .git_ssh import GitSSH
|
|
24
24
|
from .pod_activity import PodActivity
|
|
@@ -29,3 +29,7 @@ from .dataflow_setting import DataflowSetting
|
|
|
29
29
|
from .subscription import Subscription, SubscriptionServer, Price, OrganizationCreditTransaction, OrganizationSubscription
|
|
30
30
|
from .otp import UserOtp
|
|
31
31
|
from .connection_type import ConnectionType
|
|
32
|
+
from .container import Container, ContainerStatus
|
|
33
|
+
from .active_server_view import ActiveServerView
|
|
34
|
+
from .inference_service import InferenceService, InferenceStatus
|
|
35
|
+
from .onboarding_project import OnboardingProject
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""active_server_view.py"""
|
|
2
|
+
from sqlalchemy import Column, Integer, String, Boolean, ForeignKey, DateTime, func
|
|
3
|
+
from dataflow.db import Base
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ActiveServerView(Base):
|
|
7
|
+
"""Table ACTIVE_SERVER_VIEW
|
|
8
|
+
|
|
9
|
+
Tracks which user's server the current user is viewing/accessing.
|
|
10
|
+
When a user switches to viewing a shared server, this record is upserted.
|
|
11
|
+
When viewing their own server, the record is deleted.
|
|
12
|
+
|
|
13
|
+
Attributes:
|
|
14
|
+
id (int): Primary key.
|
|
15
|
+
user_name (str): The logged-in user (the viewer). Unique — one active view per user.
|
|
16
|
+
viewing_user_name (str): The server owner whose server is being viewed.
|
|
17
|
+
can_spawn (bool): Whether the viewer has start/stop (spawn) permission on the viewed server.
|
|
18
|
+
updated_at (datetime): When this was last changed.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
__tablename__ = 'ACTIVE_SERVER_VIEW'
|
|
22
|
+
|
|
23
|
+
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
24
|
+
user_name = Column(String, ForeignKey('USER.user_name', ondelete='CASCADE'), unique=True, nullable=False, index=True)
|
|
25
|
+
viewing_user_name = Column(String, ForeignKey('USER.user_name', ondelete='CASCADE'), nullable=False)
|
|
26
|
+
can_spawn = Column(Boolean, nullable=False, default=True, server_default='true')
|
|
27
|
+
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
|
@@ -11,6 +11,7 @@ class AppType(Base):
|
|
|
11
11
|
name (str): Unique name identifier for the app type.
|
|
12
12
|
display_name (str): Human-readable name for the app type.
|
|
13
13
|
code_based (bool): Indicates if the app type is code-based.
|
|
14
|
+
is_public_allowed (bool): Indicates if the app type is allowed for public use.
|
|
14
15
|
studio (bool): Indicates if the app type is associated with Dataflow Studio.
|
|
15
16
|
runtime (bool): Indicates if the app type is a runtime application.
|
|
16
17
|
organizations (list): Relationship to organizations using this app type.
|
|
@@ -25,6 +26,7 @@ class AppType(Base):
|
|
|
25
26
|
name = Column(String, unique=True, nullable=False)
|
|
26
27
|
display_name = Column(String, nullable=False)
|
|
27
28
|
code_based = Column(Boolean, nullable=False)
|
|
29
|
+
is_public_allowed = Column(Boolean, nullable=False, default=True, server_default='true')
|
|
28
30
|
studio = Column(Boolean, nullable=False, default=False, server_default='false')
|
|
29
31
|
runtime = Column(Boolean, nullable=False, default=False, server_default='false')
|
|
30
32
|
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from sqlalchemy import Column, Enum, Integer, String, ForeignKey, UniqueConstraint, Text
|
|
2
|
+
from dataflow.db import Base
|
|
3
|
+
import enum
|
|
4
|
+
|
|
5
|
+
class ContainerStatus(enum.Enum):
|
|
6
|
+
"""Enum-like class for container deployment status."""
|
|
7
|
+
PENDING = "PENDING"
|
|
8
|
+
DEPLOYED = "DEPLOYED"
|
|
9
|
+
FAILED = "FAILED"
|
|
10
|
+
DELETING = "DELETING"
|
|
11
|
+
STOPPED = "STOPPED"
|
|
12
|
+
|
|
13
|
+
class Container(Base):
|
|
14
|
+
"""TABLE 'CONTAINER'.
|
|
15
|
+
|
|
16
|
+
Attributes:
|
|
17
|
+
id (int): Primary key for the container.
|
|
18
|
+
name (str): Unique name identifier for the container.
|
|
19
|
+
image (str): The container image to deploy.
|
|
20
|
+
port (int): The port to expose.
|
|
21
|
+
env_vars (str): JSON string of environment variables.
|
|
22
|
+
command (str): JSON string of container command override.
|
|
23
|
+
cpu_limit (str): CPU resource limit (e.g., '1', '500m').
|
|
24
|
+
memory_limit (str): Memory resource limit (e.g., '2Gi', '512Mi').
|
|
25
|
+
status (ContainerStatus): Deployment status of the container.
|
|
26
|
+
url (str): URL where the container is accessible after deployment.
|
|
27
|
+
|
|
28
|
+
Relationships:
|
|
29
|
+
created_by (str): Foreign key referencing the USER table.
|
|
30
|
+
org_id (int): Foreign key referencing the ORGANIZATION table.
|
|
31
|
+
|
|
32
|
+
Constraints:
|
|
33
|
+
UniqueConstraint: Ensures unique combination of name and org_id.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
__tablename__ = "CONTAINER"
|
|
37
|
+
|
|
38
|
+
id = Column(Integer, primary_key=True, autoincrement=True, unique=True)
|
|
39
|
+
name = Column(String, nullable=False)
|
|
40
|
+
slug = Column(String, nullable=False)
|
|
41
|
+
image = Column(String, nullable=False)
|
|
42
|
+
port = Column(Integer, nullable=True)
|
|
43
|
+
env_vars = Column(Text, nullable=True)
|
|
44
|
+
command = Column(Text, nullable=True)
|
|
45
|
+
cpu_limit = Column(String, nullable=True, default="1", server_default="1")
|
|
46
|
+
memory_limit = Column(String, nullable=True, default="2Gi", server_default="2Gi")
|
|
47
|
+
created_by = Column(String, ForeignKey('USER.user_name', ondelete="CASCADE"), nullable=False)
|
|
48
|
+
org_id = Column(Integer, ForeignKey("ORGANIZATION.id"), index=True, nullable=False)
|
|
49
|
+
status = Column(Enum(ContainerStatus), default=ContainerStatus.PENDING, server_default="PENDING", nullable=False)
|
|
50
|
+
url = Column(String, nullable=True)
|
|
51
|
+
|
|
52
|
+
__table_args__ = (
|
|
53
|
+
UniqueConstraint(slug, org_id, name='name_org_id_unique'),
|
|
54
|
+
)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, Enum
|
|
2
|
+
from dataflow.db import Base
|
|
3
|
+
import enum
|
|
4
|
+
|
|
5
|
+
class InferenceStatus(enum.Enum):
|
|
6
|
+
"""Enum-like class for inference service status."""
|
|
7
|
+
PENDING = "PENDING"
|
|
8
|
+
RUNNING = "RUNNING"
|
|
9
|
+
SUCCEEDED = "SUCCEEDED"
|
|
10
|
+
FAILED = "FAILED"
|
|
11
|
+
|
|
12
|
+
class InferenceService(Base):
|
|
13
|
+
"""TABLE 'INFERENCE_SERVICE'
|
|
14
|
+
|
|
15
|
+
Attributes:
|
|
16
|
+
id (int): Primary key for the model.
|
|
17
|
+
slug (str): Unique identifier for the model.
|
|
18
|
+
model_name (str): Name of the model.
|
|
19
|
+
model_version (str): Version of the model.
|
|
20
|
+
description (str): Description of the model.
|
|
21
|
+
runtime (str): Runtime in which the model is deployed.
|
|
22
|
+
org_id (int): Foreign key referencing the ORGANIZATION table.
|
|
23
|
+
storage_uri (str): URI where the model artifacts are stored.
|
|
24
|
+
service_url (str): User-facing URL to access the inference service.
|
|
25
|
+
concurrency (int): Number of concurrent requests the model can handle.
|
|
26
|
+
min_replicas (int): Minimum number of replicas for the model deployment.
|
|
27
|
+
max_replicas (int): Maximum number of replicas for the model deployment.
|
|
28
|
+
cpu_limit (str): CPU resource limit for the model deployment
|
|
29
|
+
memory_limit (str): Memory resource limit for the model deployment
|
|
30
|
+
gpu_type (str): Type of GPU to use for the model deployment (if any)
|
|
31
|
+
gpu_count (str): Number of GPUs to use for the model deployment (if any)
|
|
32
|
+
status (InferenceStatus): Deployment status of the model inference service.
|
|
33
|
+
|
|
34
|
+
Constraints:
|
|
35
|
+
UniqueConstraint: Ensures unique combination of slug and runtime.
|
|
36
|
+
"""
|
|
37
|
+
__tablename__ = "INFERENCE_SERVICE"
|
|
38
|
+
|
|
39
|
+
id = Column(Integer, primary_key=True, autoincrement=True, unique=True)
|
|
40
|
+
slug = Column(String, nullable=False)
|
|
41
|
+
model_name = Column(String, nullable=False)
|
|
42
|
+
model_version = Column(String, nullable=False)
|
|
43
|
+
description = Column(String, nullable=True)
|
|
44
|
+
runtime = Column(String, nullable=False)
|
|
45
|
+
org_id = Column(Integer, ForeignKey("ORGANIZATION.id"), index=True, nullable=False)
|
|
46
|
+
storage_uri = Column(String, nullable=False)
|
|
47
|
+
service_url = Column(String, nullable=True)
|
|
48
|
+
concurrency = Column(Integer, nullable=True, default=80)
|
|
49
|
+
min_replicas = Column(Integer, nullable=True, default=1)
|
|
50
|
+
max_replicas = Column(Integer, nullable=True, default=1)
|
|
51
|
+
cpu_limit = Column(String, nullable=True, default="500m", server_default="500m")
|
|
52
|
+
memory_limit = Column(String, nullable=True, default="2Gi", server_default="2Gi")
|
|
53
|
+
gpu_type = Column(String, nullable=True)
|
|
54
|
+
gpu_count = Column(String, nullable=True, default="0", server_default="0")
|
|
55
|
+
status = Column(Enum(InferenceStatus, name="inference_status"), default=InferenceStatus.PENDING, server_default="PENDING", nullable=False)
|
|
56
|
+
|
|
57
|
+
__table_args__ = (
|
|
58
|
+
UniqueConstraint('slug', 'runtime', name='slug_runtime_unique'),
|
|
59
|
+
)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from sqlalchemy import Column, String, Integer, DateTime, func
|
|
2
|
+
from sqlalchemy.dialects.postgresql import JSONB, ARRAY
|
|
3
|
+
from dataflow.db import Base
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class OnboardingProject(Base):
|
|
7
|
+
|
|
8
|
+
"""Represents an onboarding project template.
|
|
9
|
+
|
|
10
|
+
Attributes:
|
|
11
|
+
project_id (int): Primary key.
|
|
12
|
+
name (str): Human-friendly name for the onboarding project.
|
|
13
|
+
description (str): Optional description.
|
|
14
|
+
apps (dict): JSON map of app name -> relative path in project.
|
|
15
|
+
path_to_clone (str): Base path where the project is cloned inside Dataflow.
|
|
16
|
+
git_url (str): Repository URL used to clone onboarding project content.
|
|
17
|
+
git_branch (str): Branch to checkout for onboarding project content.
|
|
18
|
+
display_order (int): Card ordering priority in onboarding selection UI.
|
|
19
|
+
connections (dict): JSON map of connection id -> relative path in project.
|
|
20
|
+
variables (dict): JSON map of variable key -> value.
|
|
21
|
+
secrets (dict): JSON map of secret key -> value.
|
|
22
|
+
guide_steps (list[dict]): Ordered guide metadata with route, label and hint per step.
|
|
23
|
+
steps (list[str]): Ordered short onboarding journey steps.
|
|
24
|
+
created_at (datetime): Creation timestamp.
|
|
25
|
+
created_by (str): User who created the template.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
__tablename__ = "ONBOARDING_PROJECT"
|
|
29
|
+
|
|
30
|
+
project_id = Column(Integer, primary_key=True, autoincrement=True)
|
|
31
|
+
name = Column(String, nullable=False, unique=True)
|
|
32
|
+
description = Column(String, nullable=True)
|
|
33
|
+
path_to_clone = Column(String, nullable=False)
|
|
34
|
+
git_url = Column(String, nullable=True)
|
|
35
|
+
git_branch = Column(String, nullable=True)
|
|
36
|
+
display_order = Column(Integer, nullable=False, server_default='100')
|
|
37
|
+
|
|
38
|
+
# Key-value configuration for onboarding resources
|
|
39
|
+
# apps example: {"streamlit": "Demo 1 - Flight Delay Analysis/streamlit"}
|
|
40
|
+
apps = Column(JSONB, nullable=True, server_default='{}')
|
|
41
|
+
# connections example: {"conn_id": "relative/path/to/db.sqlite"}
|
|
42
|
+
connections = Column(JSONB, nullable=True, server_default='{}')
|
|
43
|
+
variables = Column(JSONB, nullable=True, server_default='{}')
|
|
44
|
+
secrets = Column(JSONB, nullable=True, server_default='{}')
|
|
45
|
+
config_step = Column(ARRAY(String), nullable=True, server_default='{}')
|
|
46
|
+
environment = Column(String, nullable=True)
|
|
47
|
+
guide_steps = Column(JSONB, nullable=True, server_default='[]')
|
|
48
|
+
steps = Column(ARRAY(String), nullable=True, server_default='{}')
|
|
49
|
+
|
|
50
|
+
created_at = Column(DateTime, nullable=False, server_default=func.now())
|
|
51
|
+
created_by = Column(String, nullable=False)
|
|
52
|
+
|
|
53
|
+
def __repr__(self):
|
|
54
|
+
return f"<OnboardingProject(id={self.project_id}, name={self.name})>"
|
|
@@ -57,6 +57,21 @@ class OrganizationAppType(Base):
|
|
|
57
57
|
org_id = Column(Integer, ForeignKey('ORGANIZATION.id', ondelete="CASCADE"), primary_key=True, nullable=False)
|
|
58
58
|
app_type_id = Column(Integer, ForeignKey('APP_TYPE.id', ondelete="CASCADE"), primary_key=True, nullable=False)
|
|
59
59
|
|
|
60
|
+
|
|
61
|
+
class OrganizationServer(Base):
|
|
62
|
+
|
|
63
|
+
"""TABLE 'ORGANIZATION_SERVER'
|
|
64
|
+
|
|
65
|
+
Attributes:
|
|
66
|
+
org_id (int): Foreign key referencing the ORGANIZATION table, also part of the primary key.
|
|
67
|
+
server_id (int): Foreign key referencing the SERVER_CONFIG table, also part of the primary key.
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
__tablename__ = "ORGANIZATION_SERVER"
|
|
71
|
+
|
|
72
|
+
org_id = Column(Integer, ForeignKey('ORGANIZATION.id', ondelete="CASCADE"), primary_key=True, nullable=False)
|
|
73
|
+
server_id = Column(Integer, ForeignKey('SERVER_CONFIG.id', ondelete="CASCADE"), primary_key=True, nullable=False)
|
|
74
|
+
|
|
60
75
|
# class OrganizationSubscription(Base):
|
|
61
76
|
|
|
62
77
|
# """TABLE 'ORGANIZATION_SUBSCRIPTION'
|
|
@@ -42,6 +42,7 @@ class Organization(Base):
|
|
|
42
42
|
email_domain = Column(String(255), nullable=False)
|
|
43
43
|
spark_enabled_zones = Column(JSONB, default=func.json([]), server_default=text("'[]'::jsonb")) # List of zone IDs where Spark is enabled
|
|
44
44
|
agent_enabled = Column(Boolean, default=False, server_default='false')
|
|
45
|
+
agent_enabled_org = Column(Boolean, default=False, server_default='false')
|
|
45
46
|
created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc), server_default=func.now())
|
|
46
47
|
allow_auto_user_onboarding = Column(Boolean, nullable=False, default=True, server_default="true")
|
|
47
48
|
active = Column(Boolean, nullable=False, default=True, server_default="true")
|
|
@@ -54,7 +55,7 @@ class Organization(Base):
|
|
|
54
55
|
# subscriptions = relationship("Subscription", secondary="ORGANIZATION_SUBSCRIPTION", back_populates="organizations")
|
|
55
56
|
onboarding_requests = relationship("UserOnboarding", back_populates="organization", cascade="all, delete-orphan")
|
|
56
57
|
invited_users = relationship("InvitedUser", back_populates="organization", cascade="all, delete-orphan")
|
|
57
|
-
|
|
58
|
+
servers = relationship("ServerConfig", secondary="ORGANIZATION_SERVER", back_populates="organizations")
|
|
58
59
|
apps = relationship("AppType", secondary="ORGANIZATION_APP_TYPE", back_populates="organizations")
|
|
59
60
|
roles = relationship("Role", cascade="all, delete-orphan")
|
|
60
61
|
environments = relationship("Environment", back_populates="organization")
|
|
@@ -50,6 +50,7 @@ class ProjectDetails(Base):
|
|
|
50
50
|
git_folder = Column(String, nullable=True)
|
|
51
51
|
type = Column(String, ForeignKey('APP_TYPE.name'), nullable=False)
|
|
52
52
|
slug = Column(String, nullable=False)
|
|
53
|
+
description = Column(String, nullable=True)
|
|
53
54
|
runtime = Column(String, nullable=False)
|
|
54
55
|
py_env = Column(Integer, nullable=True)
|
|
55
56
|
launch_url = Column(String, nullable=True)
|
|
@@ -32,4 +32,18 @@ class RecentProjectStudio(Base):
|
|
|
32
32
|
|
|
33
33
|
__table_args__ = (
|
|
34
34
|
UniqueConstraint(user_name, project_path, app_name, name='user_name_project_path_app_name_unique'),
|
|
35
|
-
)
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class AppLaunchStatus(Base):
|
|
39
|
+
"""TABLE 'APP_LAUNCH_STATUS'
|
|
40
|
+
|
|
41
|
+
Tracks the last launch attempt for each application.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
__tablename__ = 'APP_LAUNCH_STATUS'
|
|
45
|
+
|
|
46
|
+
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
|
|
47
|
+
app_name = Column(String, nullable=False, unique=True, index=True)
|
|
48
|
+
launch_status = Column(Boolean, nullable=False, default=False, server_default='false')
|
|
49
|
+
last_launch_time = Column(DateTime, server_default=func.now(), nullable=False)
|
|
@@ -35,10 +35,12 @@ class ServerConfig(Base):
|
|
|
35
35
|
cpu = Column(String, nullable=False)
|
|
36
36
|
gpu = Column(String)
|
|
37
37
|
default = Column(Boolean, default=False, server_default='false')
|
|
38
|
+
available_to_all_orgs = Column(Boolean, default=True, server_default='true', nullable=False)
|
|
38
39
|
tags = Column(JSONB, default=func.json([]), server_default=text("'[]'::jsonb"))
|
|
39
40
|
description = Column(Text, nullable=True)
|
|
40
41
|
kubespawner_override = Column(JSONB, default=func.json({}), server_default=text("'{}'::jsonb"))
|
|
41
42
|
|
|
42
43
|
# Relationships
|
|
43
44
|
subscriptions = relationship("Subscription", secondary="SUBSCRIPTION_SERVER", back_populates="servers")
|
|
44
|
-
roles = relationship("Role", secondary="ROLE_SERVER", back_populates="servers")
|
|
45
|
+
roles = relationship("Role", secondary="ROLE_SERVER", back_populates="servers")
|
|
46
|
+
organizations = relationship("Organization", secondary="ORGANIZATION_SERVER", back_populates="servers")
|
|
@@ -21,6 +21,7 @@ class User(Base):
|
|
|
21
21
|
active (bool): Flag indicating whether the user is active.
|
|
22
22
|
password (str): Hashed password for the user.
|
|
23
23
|
active_org_id (int): Foreign key referencing the active organization of the user.
|
|
24
|
+
created_at (datetime): Timestamp of when the user record was created.
|
|
24
25
|
|
|
25
26
|
Relationships:
|
|
26
27
|
org_user_assocs: Relationship to OrganizationUser model.
|
|
@@ -43,6 +44,7 @@ class User(Base):
|
|
|
43
44
|
password = Column(String, nullable=True)
|
|
44
45
|
active_org_id = Column(Integer, ForeignKey('ORGANIZATION.id'))
|
|
45
46
|
is_super_admin = Column(Boolean, nullable=False, default=False, server_default='false')
|
|
47
|
+
created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc), nullable=False, server_default=func.now())
|
|
46
48
|
|
|
47
49
|
# Relationships
|
|
48
50
|
org_user_assocs = relationship("OrganizationUser", back_populates="user", cascade="all, delete-orphan")
|
|
@@ -97,6 +99,12 @@ class UserOnboarding(Base):
|
|
|
97
99
|
user_id = Column(Integer, ForeignKey("USER.user_id", ondelete="CASCADE"), nullable=False)
|
|
98
100
|
org_id = Column(Integer, ForeignKey("ORGANIZATION.id", ondelete="CASCADE"), nullable=False)
|
|
99
101
|
status = Column(Enum(OnboardingStatus, name='onboarding_status'), nullable=False, default=OnboardingStatus.pending.value, server_default='pending')
|
|
102
|
+
selected_role = Column(String, nullable=True)
|
|
103
|
+
selected_demo_project = Column(String, nullable=True)
|
|
104
|
+
skipped = Column(Boolean, nullable=False, default=False, server_default='false')
|
|
105
|
+
demo_launch_initialized = Column(Boolean, nullable=False, default=False, server_default='false')
|
|
106
|
+
demo_current_step_id = Column(String, nullable=True)
|
|
107
|
+
demo_current_step_index = Column(Integer, nullable=True)
|
|
100
108
|
created_at = Column(DateTime, default=func.now(), nullable=False, server_default=func.now())
|
|
101
109
|
updated_at = Column(DateTime, default=func.now(), onupdate=func.now(), nullable=False, server_default=func.now())
|
|
102
110
|
# Relationships
|
{dataflow_core-2.1.28 → dataflow_core-2.1.29}/dataflow/secrets_manager/providers/gcp_manager.py
RENAMED
|
@@ -233,6 +233,9 @@ class GCPSecretsManager(SecretManager):
|
|
|
233
233
|
if version.name == new_version_name:
|
|
234
234
|
continue
|
|
235
235
|
|
|
236
|
+
if version.state.name == "DESTROYED":
|
|
237
|
+
continue
|
|
238
|
+
|
|
236
239
|
# Disable the version before destroying
|
|
237
240
|
if version.state.name != "DISABLED":
|
|
238
241
|
self.client.disable_secret_version(
|