dataflow-core 2.0.10__py3-none-any.whl → 2.0.11__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of dataflow-core might be problematic. Click here for more details.
- authenticator/dataflowsupersetauthenticator.py +33 -15
- dataflow/dataflow.py +87 -14
- dataflow/environment.py +0 -1
- dataflow/utils/aws_secrets_manager.py +16 -20
- dataflow/utils/json_handler.py +33 -0
- {dataflow_core-2.0.10.dist-info → dataflow_core-2.0.11.dist-info}/METADATA +1 -1
- {dataflow_core-2.0.10.dist-info → dataflow_core-2.0.11.dist-info}/RECORD +10 -9
- {dataflow_core-2.0.10.dist-info → dataflow_core-2.0.11.dist-info}/WHEEL +0 -0
- {dataflow_core-2.0.10.dist-info → dataflow_core-2.0.11.dist-info}/entry_points.txt +0 -0
- {dataflow_core-2.0.10.dist-info → dataflow_core-2.0.11.dist-info}/top_level.txt +0 -0
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
from flask import redirect, request
|
|
1
|
+
from flask import redirect, request, jsonify
|
|
2
2
|
from flask_appbuilder.security.views import AuthDBView
|
|
3
|
-
from superset.security import SupersetSecurityManager
|
|
4
3
|
from flask_appbuilder.security.views import expose
|
|
5
4
|
from flask_login import login_user
|
|
6
|
-
from
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
from flask_jwt_extended import (
|
|
6
|
+
create_access_token
|
|
7
|
+
)
|
|
8
|
+
from flask_appbuilder.const import (
|
|
9
|
+
API_SECURITY_ACCESS_TOKEN_KEY
|
|
10
|
+
)
|
|
11
|
+
from superset.security import SupersetSecurityManager
|
|
12
12
|
from dataflow.dataflow import Dataflow
|
|
13
13
|
|
|
14
|
-
class
|
|
14
|
+
class DataflowAuthDBView(AuthDBView):
|
|
15
15
|
def __init__(self):
|
|
16
16
|
self.dataflow = Dataflow()
|
|
17
17
|
|
|
18
18
|
@expose('/login/', methods=['GET'])
|
|
19
|
-
def
|
|
19
|
+
def login_redirect(self):
|
|
20
20
|
try:
|
|
21
21
|
session_id = request.cookies.get('dataflow_session')
|
|
22
22
|
|
|
@@ -30,8 +30,7 @@ class CustomAuthDBView(AuthDBView):
|
|
|
30
30
|
first_name=user_details.get("first_name", ""),
|
|
31
31
|
last_name=user_details.get("last_name", ""),
|
|
32
32
|
email=user_details.get("email", ""),
|
|
33
|
-
role=self.appbuilder.sm.find_role('Admin')
|
|
34
|
-
password=""
|
|
33
|
+
role=self.appbuilder.sm.find_role('Admin')
|
|
35
34
|
)
|
|
36
35
|
if user:
|
|
37
36
|
login_user(user, remember=False)
|
|
@@ -41,8 +40,27 @@ class CustomAuthDBView(AuthDBView):
|
|
|
41
40
|
except Exception as e:
|
|
42
41
|
return super().login()
|
|
43
42
|
|
|
43
|
+
@expose("/login", methods=["POST"])
|
|
44
|
+
def login_token(self):
|
|
45
|
+
login_payload = request.get_json()
|
|
46
|
+
user = self.appbuilder.sm.find_user(username=login_payload["user_name"])
|
|
44
47
|
|
|
45
|
-
|
|
46
|
-
|
|
48
|
+
if not user:
|
|
49
|
+
user = self.appbuilder.sm.add_user(
|
|
50
|
+
username=login_payload["user_name"],
|
|
51
|
+
first_name=login_payload.get("first_name", ""),
|
|
52
|
+
last_name=login_payload.get("last_name", ""),
|
|
53
|
+
email=login_payload.get("email", ""),
|
|
54
|
+
role=self.appbuilder.sm.find_role('Admin')
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
resp = dict()
|
|
58
|
+
resp[API_SECURITY_ACCESS_TOKEN_KEY] = create_access_token(
|
|
59
|
+
identity=str(user.id), fresh=True
|
|
60
|
+
)
|
|
61
|
+
return jsonify(resp)
|
|
62
|
+
|
|
63
|
+
class DataflowSecurityManager(SupersetSecurityManager):
|
|
64
|
+
authdbview = DataflowAuthDBView
|
|
47
65
|
def __init__(self, appbuilder):
|
|
48
|
-
super(
|
|
66
|
+
super(DataflowSecurityManager, self).__init__(appbuilder)
|
dataflow/dataflow.py
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import os, requests
|
|
1
|
+
import os, requests
|
|
2
2
|
from .models.database import DatabaseManager
|
|
3
|
-
from .models.environment import JobLogs
|
|
4
|
-
from sqlalchemy.inspection import inspect
|
|
5
3
|
from .utils.aws_secrets_manager import SecretsManagerClient
|
|
6
|
-
import json
|
|
4
|
+
import json
|
|
7
5
|
from authenticator.package.configuration import ConfigurationManager
|
|
8
6
|
|
|
9
7
|
|
|
@@ -38,26 +36,101 @@ class Dataflow:
|
|
|
38
36
|
return e
|
|
39
37
|
|
|
40
38
|
def variable(self, variable_name: str):
|
|
41
|
-
"""
|
|
39
|
+
"""
|
|
40
|
+
Get variable value from UI API.
|
|
41
|
+
"""
|
|
42
42
|
try:
|
|
43
|
-
host_name = os.environ
|
|
44
|
-
user_name = host_name.replace("jupyter-","")
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
43
|
+
host_name = os.environ.get("HOSTNAME", "")
|
|
44
|
+
user_name = host_name.replace("jupyter-", "") if host_name.startswith("jupyter-") else host_name
|
|
45
|
+
runtime = os.environ.get("RUNTIME")
|
|
46
|
+
slug = os.environ.get("SLUG")
|
|
47
|
+
|
|
48
|
+
dataflow_config = ConfigurationManager('/dataflow/app/auth_config/dataflow_auth.cfg')
|
|
49
|
+
variable_api = dataflow_config.get_config_value("auth", "db_get_variables")
|
|
50
|
+
if not variable_api:
|
|
51
|
+
print("[Dataflow.variable] Variable Unreachable")
|
|
52
|
+
return None
|
|
53
|
+
|
|
54
|
+
if runtime:
|
|
55
|
+
query_params = {
|
|
56
|
+
"variable_key": variable_name,
|
|
57
|
+
"runtime": runtime,
|
|
58
|
+
"slug": slug
|
|
59
|
+
}
|
|
60
|
+
response = requests.get(variable_api, params=query_params)
|
|
61
|
+
if response.status_code == 200:
|
|
62
|
+
response_text = response.text.strip().strip('"')
|
|
63
|
+
return response_text
|
|
64
|
+
|
|
65
|
+
query_params["slug"] = "global"
|
|
66
|
+
response = requests.get(variable_api, params=query_params)
|
|
67
|
+
if response.status_code == 200:
|
|
68
|
+
response_text = response.text.strip().strip('"')
|
|
69
|
+
return response_text
|
|
70
|
+
else:
|
|
71
|
+
return None
|
|
72
|
+
|
|
73
|
+
query_params = {
|
|
74
|
+
"variable_key": variable_name,
|
|
75
|
+
"runtime": None,
|
|
76
|
+
"slug": None,
|
|
77
|
+
"created_by": user_name
|
|
78
|
+
}
|
|
79
|
+
response = requests.get(variable_api, params=query_params)
|
|
80
|
+
if response.status_code == 200:
|
|
81
|
+
response_text = response.text.strip().strip('"')
|
|
82
|
+
return response_text
|
|
83
|
+
else:
|
|
84
|
+
return None
|
|
50
85
|
except Exception as e:
|
|
86
|
+
print(f"[Dataflow.variable] Exception occurred: {e}")
|
|
51
87
|
return None
|
|
52
88
|
|
|
89
|
+
def secret(self, secret_name: str):
|
|
90
|
+
"""Get secret value from secrets manager for Studio or Runtime."""
|
|
91
|
+
try:
|
|
92
|
+
host_name = os.environ.get("HOSTNAME", "")
|
|
93
|
+
user_name = host_name.replace("jupyter-", "") if host_name.startswith("jupyter-") else host_name
|
|
94
|
+
runtime = os.environ.get("RUNTIME")
|
|
95
|
+
slug = os.environ.get("SLUG")
|
|
96
|
+
|
|
97
|
+
dataflow_config = ConfigurationManager('/dataflow/app/auth_config/dataflow_auth.cfg')
|
|
98
|
+
secret_api = dataflow_config.get_config_value("auth", "db_get_secrets")
|
|
99
|
+
if not secret_api:
|
|
100
|
+
print("[Dataflow.secret] Secret API Unreachable")
|
|
101
|
+
return None
|
|
102
|
+
|
|
103
|
+
query_params = {
|
|
104
|
+
"secret_key": secret_name,
|
|
105
|
+
"created_by": user_name
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if runtime:
|
|
109
|
+
query_params["runtime"] = runtime
|
|
110
|
+
if slug:
|
|
111
|
+
query_params["slug"] = slug
|
|
112
|
+
|
|
113
|
+
response = requests.get(secret_api, params=query_params)
|
|
114
|
+
|
|
115
|
+
if response.status_code == 200:
|
|
116
|
+
response_text = response.text.strip().strip('"')
|
|
117
|
+
return response_text
|
|
118
|
+
else:
|
|
119
|
+
return None
|
|
120
|
+
except Exception as e:
|
|
121
|
+
print(f"[Dataflow.secret] Exception occurred: {e}")
|
|
122
|
+
return None
|
|
123
|
+
|
|
53
124
|
def connection(self, conn_id: str):
|
|
54
125
|
"""Get connection details from secrets manager."""
|
|
55
126
|
try:
|
|
56
127
|
host_name = os.environ["HOSTNAME"]
|
|
57
128
|
user_name=host_name.replace("jupyter-","")
|
|
129
|
+
runtime = os.environ.get("RUNTIME")
|
|
130
|
+
slug = os.environ.get("SLUG")
|
|
58
131
|
|
|
59
132
|
vault_path = "connections"
|
|
60
|
-
secret = self.secrets_manager.get_secret_by_key(vault_path, user_name, conn_id)
|
|
133
|
+
secret = self.secrets_manager.get_secret_by_key(vault_path, user_name, conn_id, runtime, slug)
|
|
61
134
|
|
|
62
135
|
conn_type = secret['conn_type'].lower()
|
|
63
136
|
username = secret['login']
|
|
@@ -71,7 +144,7 @@ class Dataflow:
|
|
|
71
144
|
|
|
72
145
|
connection_string = f"{conn_type}://{user_info}{host}:{port}{db_info}"
|
|
73
146
|
|
|
74
|
-
extra = secret.get('extra', '')
|
|
147
|
+
extra = secret.get('extra', '')
|
|
75
148
|
if extra:
|
|
76
149
|
try:
|
|
77
150
|
extra_params = json.loads(extra)
|
dataflow/environment.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import boto3
|
|
2
2
|
from botocore.exceptions import ClientError, EndpointConnectionError, NoCredentialsError
|
|
3
3
|
import json
|
|
4
|
+
from .json_handler import JsonHandler
|
|
4
5
|
|
|
5
6
|
class SecretsManagerClient:
|
|
6
7
|
"""
|
|
@@ -11,6 +12,7 @@ class SecretsManagerClient:
|
|
|
11
12
|
json_handler: An instance of JsonHandler for handling JSON operations.
|
|
12
13
|
"""
|
|
13
14
|
def __init__(self):
|
|
15
|
+
self.json_handler = JsonHandler()
|
|
14
16
|
try:
|
|
15
17
|
self.client = boto3.client('secretsmanager')
|
|
16
18
|
except EndpointConnectionError as e:
|
|
@@ -21,14 +23,16 @@ class SecretsManagerClient:
|
|
|
21
23
|
raise Exception(f"Failed to initialize SecretsManagerClient: No AWS credentials found. {e}")
|
|
22
24
|
|
|
23
25
|
|
|
24
|
-
def get_secret_by_key(self, vault_path, user_name,
|
|
26
|
+
def get_secret_by_key(self, vault_path, user_name, conn_id: str, runtime, slug):
|
|
25
27
|
"""
|
|
26
|
-
Get information about a specific secret.
|
|
28
|
+
Get information about a specific secret using a dynamically constructed vault path.
|
|
27
29
|
|
|
28
30
|
Args:
|
|
29
|
-
vault_path (str): The vault path.
|
|
31
|
+
vault_path (str): The base vault path.
|
|
30
32
|
user_name (str): The user name.
|
|
31
|
-
|
|
33
|
+
conn_id (str): The key of the secret to retrieve.
|
|
34
|
+
runtime (str, optional): The runtime environment. Defaults to None.
|
|
35
|
+
slug (str, optional): The slug identifier. Defaults to None.
|
|
32
36
|
|
|
33
37
|
Returns:
|
|
34
38
|
str: Information about the secret in JSON format.
|
|
@@ -37,25 +41,17 @@ class SecretsManagerClient:
|
|
|
37
41
|
Exception: If the operation fails.
|
|
38
42
|
"""
|
|
39
43
|
try:
|
|
40
|
-
if
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
+
if runtime and slug:
|
|
45
|
+
secret_name = f"{runtime}/{slug}/{vault_path}/{conn_id}"
|
|
46
|
+
else:
|
|
47
|
+
secret_name = f"{user_name}/{vault_path}/{conn_id}"
|
|
48
|
+
|
|
44
49
|
response = self.client.get_secret_value(SecretId=secret_name)
|
|
45
|
-
secret_metadata = self.client.describe_secret(SecretId=secret_name)
|
|
46
50
|
secret_data = json.loads(response.get('SecretString'))
|
|
47
51
|
|
|
48
|
-
|
|
49
|
-
secret_info={
|
|
50
|
-
"Name": secret_key,
|
|
51
|
-
"Description": secret_metadata.get('Description')
|
|
52
|
-
}
|
|
53
|
-
secret_info.update(secret_data)
|
|
54
|
-
return secret_info
|
|
55
|
-
else:
|
|
56
|
-
raise Exception(f"Secret named '{secret_key}' is not active")
|
|
52
|
+
return secret_data
|
|
57
53
|
except ClientError as e:
|
|
58
54
|
if e.response['Error']['Code'] == 'ResourceNotFoundException':
|
|
59
|
-
raise Exception(f"Secret named '{
|
|
55
|
+
raise Exception(f"Secret named '{conn_id}' not found with path '{secret_name}'")
|
|
60
56
|
else:
|
|
61
|
-
raise Exception(f"Failed to get secret '{
|
|
57
|
+
raise Exception(f"Failed to get secret '{conn_id}': {e}")
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
class JsonHandler:
|
|
4
|
+
"""
|
|
5
|
+
Helper class for handling JSON serialization and deserialization.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
def __init__(self):
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
def dict_to_json(self, data_dict):
|
|
12
|
+
"""
|
|
13
|
+
Serialize a dictionary to JSON string.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
data_dict (dict): The dictionary to serialize.
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
str: The JSON string representation of the dictionary.
|
|
20
|
+
"""
|
|
21
|
+
return json.dumps(data_dict)
|
|
22
|
+
|
|
23
|
+
def json_to_dict(self, json_string):
|
|
24
|
+
"""
|
|
25
|
+
Deserialize a JSON string to dictionary.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
json_string (str): The JSON string to deserialize.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
dict: The dictionary representation of the JSON string.
|
|
32
|
+
"""
|
|
33
|
+
return json.loads(json_string)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
authenticator/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
2
|
authenticator/dataflowairflowauthenticator.py,sha256=PcGlL2cq5EA9RdtClyrOupvNXxk6h54UPQcQ-g4VQtA,3117
|
|
3
3
|
authenticator/dataflowhubauthenticator.py,sha256=m7ef84WfAjQ0IM8bg-1JhPs75rMNwKq3B0ZB5XF-07w,3403
|
|
4
|
-
authenticator/dataflowsupersetauthenticator.py,sha256=
|
|
4
|
+
authenticator/dataflowsupersetauthenticator.py,sha256=Qx8hUGSOfUTgffRCaVf88vmE9bFGNBvd9mEMHcpD488,2463
|
|
5
5
|
authenticator/package/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
6
|
authenticator/package/configuration.py,sha256=7To6XwH1eESiYp39eqPcswXWwrdBUdPF6xN6WnazOF0,663
|
|
7
7
|
authenticator/package/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -10,17 +10,18 @@ authenticator/package/models/session.py,sha256=j6PhbrTMJxEkeDT4Vf5SqGtM_LI_vZy9O
|
|
|
10
10
|
authenticator/package/models/user.py,sha256=fxaQeDssWLXqh4XeKdbr3LPG_cbkjP2LjJOnsM6ZCOE,721
|
|
11
11
|
dataflow/__init__.py,sha256=WTRg8HMpMWSgxYJ9ZGVldx4k07fAbta3mBmZ1hG9mWE,30
|
|
12
12
|
dataflow/configuration.py,sha256=7To6XwH1eESiYp39eqPcswXWwrdBUdPF6xN6WnazOF0,663
|
|
13
|
-
dataflow/dataflow.py,sha256=
|
|
14
|
-
dataflow/environment.py,sha256
|
|
13
|
+
dataflow/dataflow.py,sha256=_5f4nObBiXf8GfgIAyTTR05UTNZ65S7jNQAJkB7FN2E,6392
|
|
14
|
+
dataflow/environment.py,sha256=-wnkgjpX5CaYH44JxDt8fgkNv2JZVu7qM-pW_xf7pUY,19603
|
|
15
15
|
dataflow/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
16
|
dataflow/models/database.py,sha256=y09pqnglsBBtDZlyhvqDAlpUSFovwAzBAi6jOYl_XNk,896
|
|
17
17
|
dataflow/models/environment.py,sha256=kJ4yRAX0ulh7YZsIVMWHkGLrptHbKOMVYtaWtsejVds,2157
|
|
18
18
|
dataflow/scripts/clone_environment.sh,sha256=PJfMWPgiWSkvY-x98WmjeAkRREjC9824JzTazHe2iQQ,390
|
|
19
19
|
dataflow/scripts/create_environment.sh,sha256=ams50MD1r53cHRYDfpAvmEAMsCaCFvlL0cmnRVXhFgY,496
|
|
20
20
|
dataflow/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
21
|
-
dataflow/utils/aws_secrets_manager.py,sha256=
|
|
22
|
-
|
|
23
|
-
dataflow_core-2.0.
|
|
24
|
-
dataflow_core-2.0.
|
|
25
|
-
dataflow_core-2.0.
|
|
26
|
-
dataflow_core-2.0.
|
|
21
|
+
dataflow/utils/aws_secrets_manager.py,sha256=A_fNs9VNah9dDdl9NhqizJamYU7xr2v_GXlw9InEDFk,2380
|
|
22
|
+
dataflow/utils/json_handler.py,sha256=5_7WdypegRBDe2HSqBXyrJAdd92wsha8qRcmQvCj1TA,782
|
|
23
|
+
dataflow_core-2.0.11.dist-info/METADATA,sha256=MeQcg0yFee-f1Vdo0MOC_5u14E_G1aOIw_4O_LiTpFo,302
|
|
24
|
+
dataflow_core-2.0.11.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
25
|
+
dataflow_core-2.0.11.dist-info/entry_points.txt,sha256=ppj_EIbYrJJwCPg1kfdsZk5q1N-Ejfis1neYrnjhO8o,117
|
|
26
|
+
dataflow_core-2.0.11.dist-info/top_level.txt,sha256=SZsUOpSCK9ntUy-3Tusxzf5A2e8ebwD8vouPb1dPt_8,23
|
|
27
|
+
dataflow_core-2.0.11.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|