dataflow-core 2.1.8__tar.gz → 2.1.18rc2__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.

Files changed (77) hide show
  1. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/PKG-INFO +4 -1
  2. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/authenticator/dataflowhubauthenticator.py +30 -45
  3. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow/dataflow.py +162 -62
  4. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow/environment.py +62 -33
  5. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow/models/__init__.py +7 -3
  6. dataflow_core-2.1.18rc2/dataflow/models/app_types.py +15 -0
  7. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow/models/connection.py +5 -4
  8. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow/models/dataflow_zone.py +2 -3
  9. dataflow_core-2.1.18rc2/dataflow/models/environment.py +125 -0
  10. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow/models/git_ssh.py +2 -1
  11. dataflow_core-2.1.18rc2/dataflow/models/org_associations.py +38 -0
  12. dataflow_core-2.1.18rc2/dataflow/models/organization.py +78 -0
  13. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow/models/pinned_projects.py +2 -2
  14. dataflow_core-2.1.18rc2/dataflow/models/pod_activity.py +17 -0
  15. dataflow_core-2.1.18rc2/dataflow/models/pod_session_history.py +16 -0
  16. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow/models/project_details.py +9 -6
  17. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow/models/recent_project_studio.py +1 -1
  18. dataflow_core-2.1.18rc2/dataflow/models/role.py +35 -0
  19. dataflow_core-2.1.18rc2/dataflow/models/role_server.py +11 -0
  20. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow/models/role_zone.py +8 -3
  21. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow/models/server_config.py +16 -8
  22. dataflow_core-2.1.18rc2/dataflow/models/team.py +23 -0
  23. dataflow_core-2.1.18rc2/dataflow/models/user.py +68 -0
  24. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow/models/user_team.py +1 -4
  25. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow/models/variables.py +13 -4
  26. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow/schemas/connection.py +7 -4
  27. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow/schemas/git_ssh.py +5 -5
  28. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow/schemas/secret.py +7 -4
  29. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow/scripts/clone_environment.sh +2 -1
  30. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow/scripts/create_environment.sh +4 -0
  31. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow/secrets_manager/factory.py +14 -8
  32. dataflow_core-2.1.18rc2/dataflow/secrets_manager/providers/gcp_manager.py +332 -0
  33. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow/secrets_manager/service.py +17 -9
  34. dataflow_core-2.1.18rc2/dataflow/secrets_manager/utils.py +58 -0
  35. dataflow_core-2.1.18rc2/dataflow/utils/get_current_user.py +60 -0
  36. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow_core.egg-info/PKG-INFO +4 -1
  37. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow_core.egg-info/SOURCES.txt +11 -2
  38. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow_core.egg-info/requires.txt +3 -0
  39. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow_core.egg-info/top_level.txt +1 -0
  40. dataflow_core-2.1.18rc2/dfmigration/__init__.py +0 -0
  41. dataflow_core-2.1.18rc2/dfmigration/env.py +45 -0
  42. dataflow_core-2.1.18rc2/dfmigration/versions/001_initial_baseline_migration.py +20 -0
  43. dataflow_core-2.1.18rc2/dfmigration/versions/__init__.py +0 -0
  44. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/setup.py +6 -3
  45. dataflow_core-2.1.8/dataflow/models/app_types.py +0 -10
  46. dataflow_core-2.1.8/dataflow/models/environment.py +0 -75
  47. dataflow_core-2.1.8/dataflow/models/role.py +0 -29
  48. dataflow_core-2.1.8/dataflow/models/role_server.py +0 -14
  49. dataflow_core-2.1.8/dataflow/models/team.py +0 -17
  50. dataflow_core-2.1.8/dataflow/models/user.py +0 -30
  51. dataflow_core-2.1.8/dataflow/models/user_environment.py +0 -16
  52. dataflow_core-2.1.8/dataflow/utils/get_current_user.py +0 -37
  53. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/README.md +0 -0
  54. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/authenticator/__init__.py +0 -0
  55. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/authenticator/dataflowairflowauthenticator.py +0 -0
  56. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/authenticator/dataflowsupersetauthenticator.py +0 -0
  57. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow/__init__.py +0 -0
  58. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow/configuration.py +0 -0
  59. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow/database_manager.py +0 -0
  60. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow/db.py +0 -0
  61. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow/models/blacklist_library.py +0 -0
  62. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow/models/environment_status.py +0 -0
  63. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow/models/recent_projects.py +0 -0
  64. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow/models/session.py +0 -0
  65. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow/schemas/__init__.py +0 -0
  66. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow/scripts/update_environment.sh +0 -0
  67. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow/secrets_manager/__init__.py +0 -0
  68. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow/secrets_manager/interface.py +0 -0
  69. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow/secrets_manager/providers/__init__.py +0 -0
  70. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow/secrets_manager/providers/aws_manager.py +0 -0
  71. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow/secrets_manager/providers/azure_manager.py +0 -0
  72. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow/utils/__init__.py +0 -0
  73. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow/utils/exceptions.py +0 -0
  74. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow/utils/logger.py +0 -0
  75. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow_core.egg-info/dependency_links.txt +0 -0
  76. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/dataflow_core.egg-info/entry_points.txt +0 -0
  77. {dataflow_core-2.1.8 → dataflow_core-2.1.18rc2}/setup.cfg +0 -0
@@ -1,16 +1,19 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dataflow-core
3
- Version: 2.1.8
3
+ Version: 2.1.18rc2
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
@@ -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,7 +6,8 @@ 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, role as m_role
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):
14
13
  enable_dataflow_auth = Bool(True, config=True, help="Enable username/password authentication")
@@ -28,7 +27,7 @@ class DataflowBaseAuthenticator(Authenticator):
28
27
  return str(uuid.uuid4())
29
28
 
30
29
  def set_session_cookie(self, handler, session_id):
31
- expires = datetime.now(ZoneInfo("UTC")) + timedelta(days=365)
30
+ expires = datetime.now(ZoneInfo("UTC")) + timedelta(days=60)
32
31
  host = handler.request.host
33
32
  domain = '.'.join(host.split('.')[-2:]) if len(host.split('.')) >= 2 else host
34
33
  handler.set_cookie(
@@ -44,19 +43,12 @@ class DataflowBaseAuthenticator(Authenticator):
44
43
  self.log.info(f"Set session cookie: dataflow_session={session_id} for host={host}")
45
44
 
46
45
  def get_or_create_session(self, user_id):
47
- existing_session = (
48
- self.db.query(m_session.Session)
49
- .filter(m_session.Session.user_id == str(user_id))
50
- .first()
51
- )
52
- if existing_session:
53
- self.log.info(f"Reusing existing session: {existing_session.session_id}")
54
- return existing_session.session_id
55
46
  session_id = self.generate_session_id()
56
47
  while self.db.query(m_session.Session).filter(
57
48
  m_session.Session.session_id == session_id
58
49
  ).first():
59
50
  session_id = self.generate_session_id()
51
+
60
52
  db_item = m_session.Session(user_id=user_id, session_id=session_id)
61
53
  self.db.add(db_item)
62
54
  self.db.commit()
@@ -77,42 +69,28 @@ class DataflowBaseAuthenticator(Authenticator):
77
69
 
78
70
  return super().check_blocked_users(username, authenticated)
79
71
 
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
72
  def extract_username_from_email(self, email):
98
73
  """Extract username from email by removing domain"""
99
74
  if '@' in email:
100
75
  return email.split('@')[0]
101
76
  return email
102
77
 
78
+ def generate_secure_password(self):
79
+ """Generate a secure random password hash"""
80
+ salt = secrets.token_hex(16)
81
+ random_uuid = str(uuid.uuid4())
82
+ hash_obj = hashlib.sha256((random_uuid + salt).encode())
83
+ return hash_obj.hexdigest()
84
+
103
85
  def create_new_user(self, email, first_name=None, last_name=None):
104
86
  """Create a new user with Applicant role"""
105
87
  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
88
  username = self.extract_username_from_email(email)
112
89
  username = re.sub(r'[^a-z0-9]', '', username.lower())
113
90
  if not username:
114
91
  self.log.error("Cannot create user: Username is empty")
115
92
  return None
93
+
116
94
  existing_user = (
117
95
  self.db.query(m_user.User)
118
96
  .filter(m_user.User.user_name == username)
@@ -122,7 +100,7 @@ class DataflowBaseAuthenticator(Authenticator):
122
100
  counter = 1
123
101
  original_username = username
124
102
  while existing_user:
125
- username = f"{original_username}_{counter}"
103
+ username = f"{original_username}{counter}"
126
104
  existing_user = (
127
105
  self.db.query(m_user.User)
128
106
  .filter(m_user.User.user_name == username)
@@ -130,13 +108,13 @@ class DataflowBaseAuthenticator(Authenticator):
130
108
  )
131
109
  counter += 1
132
110
 
111
+ secure_password = self.generate_secure_password()
133
112
  new_user = m_user.User(
134
113
  user_name=username,
135
114
  first_name=first_name or username,
136
115
  last_name=last_name or "",
137
116
  email=email,
138
- role_id=role_id,
139
- password='user@123',
117
+ password=secure_password,
140
118
  )
141
119
 
142
120
  self.db.add(new_user)
@@ -154,22 +132,29 @@ class DataflowBaseAuthenticator(Authenticator):
154
132
  async def authenticate_dataflow(self, handler, data):
155
133
  if not (self.enable_dataflow_auth and isinstance(data, dict) and data.get("username") and data.get("password")):
156
134
  return None
157
- username = data["username"]
135
+ user_name_or_email = data["username"]
158
136
  password = data["password"]
159
- self.log.info(f"Attempting Dataflow authentication for user: {username}")
137
+ self.log.info(f"Attempting Dataflow authentication for user: {user_name_or_email}")
160
138
  try:
161
139
  user = (
162
140
  self.db.query(m_user.User)
163
- .filter(m_user.User.user_name == username)
141
+ .filter(
142
+ or_(
143
+ m_user.User.email == user_name_or_email,
144
+ m_user.User.user_name == user_name_or_email
145
+ )
146
+ )
164
147
  .first()
165
148
  )
149
+
166
150
  if not user or user.password != password:
167
- self.log.warning(f"Dataflow authentication failed for user: {username}")
151
+ self.log.warning(f"Dataflow authentication failed for user: {user_name_or_email}")
168
152
  return None
153
+
169
154
  session_id = self.get_or_create_session(user.user_id)
170
155
  self.set_session_cookie(handler, session_id)
171
- self.log.info(f"Dataflow authentication successful for user: {username}")
172
- return {"name": username, "session_id": session_id, "auth_state": {}}
156
+ self.log.info(f"Dataflow authentication successful for user: {user.user_name}")
157
+ return {"name": user.user_name, "session_id": session_id, "auth_state": {}}
173
158
  except Exception as e:
174
159
  self.log.error(f"Dataflow authentication error: {str(e)}")
175
160
  return None
@@ -302,7 +287,7 @@ class DataflowAzureAuthenticator(DataflowBaseAuthenticator, AzureAdOAuthenticato
302
287
  self.set_session_cookie(handler, session_id)
303
288
  self.log.info(f"Azure AD OAuth completed for user: {username}, session_id={session_id}")
304
289
  return {
305
- "name": db_user.first_name,
290
+ "name": username,
306
291
  "session_id": session_id,
307
292
  "auth_state": user.get("auth_state", {})
308
293
  }
@@ -1,6 +1,7 @@
1
1
  import os, requests
2
2
  from .database_manager import DatabaseManager
3
3
  import json
4
+ import base64
4
5
  from .configuration import ConfigurationManager
5
6
 
6
7
 
@@ -8,6 +9,31 @@ class Dataflow:
8
9
  """
9
10
  Dataflow class to interact with Dataflow services.
10
11
  """
12
+
13
+ @staticmethod
14
+ def _json_parse(value):
15
+ try:
16
+ result = json.loads(value)
17
+ if isinstance(result, str):
18
+ try:
19
+ return json.loads(result)
20
+ except json.JSONDecodeError:
21
+ return result
22
+ return result
23
+ except (json.JSONDecodeError, TypeError):
24
+ return value
25
+
26
+ def _parse_response_data(self, response):
27
+ """Parse response data based on datatype field or fallback to JSON parsing."""
28
+ data = response.json()
29
+ if not isinstance(data, dict):
30
+ raise ValueError("Internal Dataflow Error!")
31
+ value = data.get('value', '')
32
+ if data.get('datatype') == 'json':
33
+ return self._json_parse(value)
34
+ else:
35
+ return value
36
+
11
37
  def auth(self, session_id: str):
12
38
  """
13
39
  Retrieve and return user information using their session ID.
@@ -41,7 +67,7 @@ class Dataflow:
41
67
 
42
68
  except Exception as e:
43
69
  return e
44
-
70
+
45
71
  def variable(self, variable_name: str):
46
72
  """
47
73
  Retrieve a Dataflow variable.
@@ -56,12 +82,19 @@ class Dataflow:
56
82
  host_name = os.environ.get("HOSTNAME", "")
57
83
  runtime = os.environ.get("RUNTIME")
58
84
  slug = os.environ.get("SLUG")
85
+ org_id = os.environ.get("ORGANIZATION")
59
86
 
60
87
  dataflow_config = ConfigurationManager('/dataflow/app/auth_config/dataflow_auth.cfg')
88
+ query_params = {
89
+ "key": variable_name,
90
+ }
61
91
 
62
92
  variable_api = None
63
93
  if runtime and slug:
64
94
  variable_api = dataflow_config.get_config_value("auth", "variable_ui_api")
95
+ query_params["runtime"] = runtime
96
+ query_params["slug"] = slug
97
+ query_params["org_id"] = org_id
65
98
  elif host_name:
66
99
  variable_api = dataflow_config.get_config_value("auth", "variable_manager_api")
67
100
  else:
@@ -70,45 +103,22 @@ class Dataflow:
70
103
  if not variable_api:
71
104
  print("[Dataflow.variable] Variable Unreachable")
72
105
  return None
73
-
74
- if runtime:
75
- query_params = {
76
- "key": variable_name,
77
- "runtime": runtime,
78
- "slug": slug
79
- }
80
- response = requests.get(variable_api, params=query_params)
81
- if response.status_code == 200:
82
- response_text = response.text.strip().strip('"')
83
- return response_text
84
-
85
- query_params["slug"] = "global"
86
- response = requests.get(variable_api, params=query_params)
87
- if response.status_code == 200:
88
- response_text = response.text.strip().strip('"')
89
- return response_text
90
- else:
91
- return None
92
-
93
- query_params = {
94
- "key": variable_name,
95
- }
106
+
96
107
  response = requests.get(variable_api, params=query_params)
97
108
 
98
- # Handle different HTTP status codes gracefully
99
109
  if response.status_code == 404:
100
- return None # Variable not found
110
+ return None
101
111
  elif response.status_code >= 500:
102
- response.raise_for_status() # Let server errors propagate
112
+ response.raise_for_status()
103
113
  elif response.status_code >= 400:
104
114
  print(f"[Dataflow.variable] Client error {response.status_code} for variable '{variable_name}'")
105
115
  return None
106
116
  elif response.status_code != 200:
107
117
  print(f"[Dataflow.variable] Unexpected status {response.status_code} for variable '{variable_name}'")
108
118
  return None
119
+
120
+ return self._parse_response_data(response)
109
121
 
110
- return response.text.strip().strip('"')
111
-
112
122
  except requests.exceptions.RequestException as e:
113
123
  raise RuntimeError(f"[Dataflow.variable] Failed to fetch variable '{variable_name}'") from e
114
124
 
@@ -130,40 +140,137 @@ class Dataflow:
130
140
  host_name = os.environ.get("HOSTNAME", "")
131
141
  runtime = os.environ.get("RUNTIME")
132
142
  slug = os.environ.get("SLUG")
143
+ org_id = os.environ.get("ORGANIZATION")
133
144
 
134
145
  dataflow_config = ConfigurationManager('/dataflow/app/auth_config/dataflow_auth.cfg')
146
+ query_params = {
147
+ "key": secret_name
148
+ }
149
+
135
150
  if runtime:
136
151
  secret_api = dataflow_config.get_config_value("auth", "secret_ui_api")
152
+ query_params["runtime"] = runtime
153
+ query_params["slug"] = slug
154
+ query_params["org_id"] = org_id
137
155
  else:
138
156
  secret_api = dataflow_config.get_config_value("auth", "secret_manager_api")
139
157
  if not secret_api:
140
158
  print("[Dataflow.secret] Secret API Unreachable")
141
159
  return None
142
160
 
161
+ response = requests.get(secret_api, params=query_params)
162
+
163
+ if response.status_code == 404:
164
+ return None
165
+ elif response.status_code >= 500:
166
+ response.raise_for_status()
167
+ elif response.status_code >= 400:
168
+ print(f"[Dataflow.secret] Client error {response.status_code} for secret '{secret_name}'")
169
+ return None
170
+ elif response.status_code != 200:
171
+ print(f"[Dataflow.secret] Unexpected status {response.status_code} for secret '{secret_name}'")
172
+ return None
173
+
174
+ return self._parse_response_data(response)
175
+
176
+ except requests.exceptions.RequestException as e:
177
+ raise RuntimeError(f"[Dataflow.secret] Failed to fetch secret '{secret_name}'") from e
178
+ except Exception as e:
179
+ print(f"[Dataflow.secret] Exception occurred: {e}")
180
+ return None
181
+
182
+ def secret_file(self, secret_name: str):
183
+ """
184
+ Retrieve a Dataflow secret file.
185
+
186
+ Args:
187
+ secret_name (str): Name of the secret to retrieve
188
+
189
+ Returns:
190
+ str or None: Secret value if found, None otherwise
191
+ """
192
+ try:
193
+ host_name = os.environ.get("HOSTNAME", "")
194
+ runtime = os.environ.get("RUNTIME")
195
+ slug = os.environ.get("SLUG")
196
+ org_id = os.environ.get("ORGANIZATION")
197
+
198
+ dataflow_config = ConfigurationManager('/dataflow/app/auth_config/dataflow_auth.cfg')
143
199
  query_params = {
144
200
  "key": secret_name
145
201
  }
146
202
 
147
203
  if runtime:
204
+ secret_api = dataflow_config.get_config_value("auth", "secret_ui_api")
148
205
  query_params["runtime"] = runtime
149
- if slug:
150
206
  query_params["slug"] = slug
207
+ query_params["org_id"] = org_id
208
+ else:
209
+ secret_api = dataflow_config.get_config_value("auth", "secret_manager_api")
210
+ if not secret_api:
211
+ print("[Dataflow.secret] Secret API Unreachable")
212
+ return None
151
213
 
152
214
  response = requests.get(secret_api, params=query_params)
153
-
154
- # Handle different HTTP status codes gracefully
215
+
155
216
  if response.status_code == 404:
156
- return None # Secret not found
217
+ return None
157
218
  elif response.status_code >= 500:
158
- response.raise_for_status() # Let server errors propagate
219
+ response.raise_for_status()
159
220
  elif response.status_code >= 400:
160
221
  print(f"[Dataflow.secret] Client error {response.status_code} for secret '{secret_name}'")
161
222
  return None
162
223
  elif response.status_code != 200:
163
224
  print(f"[Dataflow.secret] Unexpected status {response.status_code} for secret '{secret_name}'")
164
225
  return None
226
+
227
+ response_data = response.json()
228
+ if response.status_code == 200 and response_data.get('filename'):
229
+ # For runtime mode, create file and return filepath
230
+ if runtime:
231
+ import tempfile
232
+ from pathlib import Path
165
233
 
166
- return response.text.strip().strip('"')
234
+ # Create /tmp/secrets directory if it doesn't exist
235
+ secrets_dir = Path("/tmp/secrets")
236
+ secrets_dir.mkdir(parents=True, exist_ok=True)
237
+
238
+ # Get filename and content
239
+ filename = response_data.get('filename')
240
+ file_content = response_data.get('value')
241
+
242
+ if not filename or not file_content:
243
+ print(f"[Dataflow.secret] Missing filename or content for secret '{secret_name}'")
244
+ return None
245
+
246
+ file_path = os.path.join(secrets_dir, filename)
247
+
248
+ # Detect if content is Base64 encoded binary or text
249
+ try:
250
+ # Try to decode as Base64
251
+ decoded_content = base64.b64decode(file_content, validate=True)
252
+ # Check if it contains non-printable characters (likely binary)
253
+ is_binary = not all(32 <= byte <= 126 or byte in (9, 10, 13) for byte in decoded_content[:100])
254
+
255
+ if is_binary:
256
+ # Write as binary
257
+ with open(file_path, 'wb') as f:
258
+ f.write(decoded_content)
259
+ else:
260
+ # Decode and write as text
261
+ with open(file_path, 'w', encoding='utf-8') as f:
262
+ f.write(decoded_content.decode('utf-8'))
263
+ except Exception:
264
+ # Not Base64 or decode failed, treat as text
265
+ with open(file_path, 'w', encoding='utf-8') as f:
266
+ f.write(file_content)
267
+ return str(file_path)
268
+ else:
269
+ # For non-runtime mode, return the value as-is
270
+ return response_data.get('value')
271
+ else:
272
+ print(f"[Dataflow.secret] No file found for secret '{secret_name}'! If it is a non-file secret, please use the 'secret' method.")
273
+ return None
167
274
 
168
275
  except requests.exceptions.RequestException as e:
169
276
  raise RuntimeError(f"[Dataflow.secret] Failed to fetch secret '{secret_name}'") from e
@@ -186,31 +293,29 @@ class Dataflow:
186
293
  host_name = os.environ["HOSTNAME"]
187
294
  runtime = os.environ.get("RUNTIME")
188
295
  slug = os.environ.get("SLUG")
189
-
296
+ org_id = os.environ.get("ORGANIZATION")
297
+
190
298
  dataflow_config = ConfigurationManager('/dataflow/app/auth_config/dataflow_auth.cfg')
191
- if runtime:
192
- connection_api = dataflow_config.get_config_value("auth", "connection_ui_api")
193
- elif host_name:
194
- connection_api = dataflow_config.get_config_value("auth", "connection_manager_api")
195
- else:
196
- raise Exception("Cannot run dataflow methods here! HOSTNAME or RUNTIME env variable not set.")
197
-
198
299
  query_params = {
199
300
  "conn_id": conn_id
200
301
  }
201
302
 
202
303
  if runtime:
203
304
  query_params["runtime"] = runtime
204
- if slug:
305
+ query_params["org_id"] = org_id
205
306
  query_params["slug"] = slug
307
+ connection_api = dataflow_config.get_config_value("auth", "connection_ui_api")
308
+ elif host_name:
309
+ connection_api = dataflow_config.get_config_value("auth", "connection_manager_api")
310
+ else:
311
+ raise Exception("Cannot run dataflow methods here! HOSTNAME or RUNTIME env variable not set.")
206
312
 
207
313
  response = requests.get(connection_api, params=query_params)
208
314
 
209
- # Handle different HTTP status codes gracefully
210
315
  if response.status_code == 404:
211
316
  raise RuntimeError(f"[Dataflow.connection] Connection '{conn_id}' not found!")
212
317
  elif response.status_code >= 500:
213
- response.raise_for_status() # Let server errors propagate
318
+ response.raise_for_status()
214
319
  elif response.status_code >= 400:
215
320
  raise RuntimeError(f"[Dataflow.connection] Client error {response.status_code} for connection '{conn_id}'")
216
321
  elif response.status_code != 200:
@@ -222,9 +327,6 @@ class Dataflow:
222
327
  raise RuntimeError(f"[Dataflow.connection] Connection '{conn_id}' not found!")
223
328
 
224
329
  if mode == "dict":
225
- with open('/home/jovyan/log.txt', 'w') as log_file:
226
- log_file.write(f"Connection details for {conn_id}: {connection_details}\n")
227
- print(f"connection_details: {connection_details}")
228
330
  return dict(connection_details)
229
331
 
230
332
  conn_type = connection_details['conn_type'].lower()
@@ -281,20 +383,20 @@ class Dataflow:
281
383
  host_name = os.environ.get("HOSTNAME", "")
282
384
  runtime = os.environ.get("RUNTIME")
283
385
  slug = os.environ.get("SLUG")
386
+ org_id = os.environ.get("ORGANIZATION")
284
387
 
285
388
  dataflow_config = ConfigurationManager('/dataflow/app/auth_config/dataflow_auth.cfg')
286
- if runtime and slug:
287
- variableorsecret_api = dataflow_config.get_config_value("auth", "variableorsecret_ui_api")
288
- query_params = {
289
- "key": key,
290
- "runtime": runtime,
291
- "slug": slug
389
+ query_params = {
390
+ "key": key
292
391
  }
392
+
393
+ if runtime:
394
+ variableorsecret_api = dataflow_config.get_config_value("auth", "variableorsecret_ui_api")
395
+ query_params["runtime"] = runtime
396
+ query_params["slug"] = slug
397
+ query_params["org_id"] = org_id
293
398
  elif host_name:
294
399
  variableorsecret_api = dataflow_config.get_config_value("auth", "variableorsecret_manager_api")
295
- query_params = {
296
- "key": key
297
- }
298
400
  else:
299
401
  raise Exception("Cannot run dataflow methods here!")
300
402
 
@@ -304,9 +406,8 @@ class Dataflow:
304
406
 
305
407
  response = requests.get(variableorsecret_api, params=query_params)
306
408
 
307
- # Handle different HTTP status codes gracefully
308
409
  if response.status_code == 404:
309
- return None # Variable/secret not found
410
+ return None
310
411
  elif response.status_code >= 500:
311
412
  response.raise_for_status() # Let server errors propagate
312
413
  elif response.status_code >= 400:
@@ -316,8 +417,7 @@ class Dataflow:
316
417
  print(f"[Dataflow.variable_or_secret] Unexpected status {response.status_code} for key '{key}'")
317
418
  return None
318
419
 
319
- response_text = response.text.strip().strip('"')
320
- return response_text
420
+ return self._parse_response_data(response)
321
421
 
322
422
  except requests.exceptions.RequestException as e:
323
423
  raise RuntimeError(f"[Dataflow.variable_or_secret] Failed to fetch '{key}'") from e