meerschaum 2.4.0.dev1__py3-none-any.whl → 2.4.0rc2__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.
- meerschaum/_internal/arguments/_parse_arguments.py +2 -5
- meerschaum/_internal/docs/index.py +3 -2
- meerschaum/_internal/entry.py +13 -7
- meerschaum/_internal/shell/Shell.py +38 -44
- meerschaum/_internal/term/TermPageHandler.py +2 -3
- meerschaum/_internal/term/__init__.py +13 -11
- meerschaum/actions/api.py +10 -7
- meerschaum/actions/bootstrap.py +2 -1
- meerschaum/actions/delete.py +4 -1
- meerschaum/actions/register.py +1 -3
- meerschaum/actions/stack.py +24 -19
- meerschaum/actions/start.py +25 -26
- meerschaum/actions/sync.py +53 -52
- meerschaum/api/__init__.py +48 -14
- meerschaum/api/_events.py +15 -10
- meerschaum/api/_oauth2.py +2 -2
- meerschaum/api/_websockets.py +5 -4
- meerschaum/api/dash/__init__.py +1 -11
- meerschaum/api/dash/callbacks/dashboard.py +47 -55
- meerschaum/api/dash/callbacks/jobs.py +15 -16
- meerschaum/api/dash/callbacks/login.py +16 -10
- meerschaum/api/dash/callbacks/pipes.py +3 -4
- meerschaum/api/dash/callbacks/plugins.py +1 -1
- meerschaum/api/dash/callbacks/register.py +15 -11
- meerschaum/api/dash/components.py +54 -59
- meerschaum/api/dash/jobs.py +5 -9
- meerschaum/api/dash/pages/pipes.py +4 -1
- meerschaum/api/dash/pipes.py +13 -17
- meerschaum/api/dash/plugins.py +6 -4
- meerschaum/api/dash/sessions.py +176 -0
- meerschaum/api/dash/users.py +2 -53
- meerschaum/api/dash/webterm.py +12 -17
- meerschaum/api/resources/static/js/terminado.js +1 -1
- meerschaum/api/routes/_actions.py +4 -20
- meerschaum/api/routes/_jobs.py +8 -7
- meerschaum/api/routes/_webterm.py +5 -6
- meerschaum/config/_default.py +6 -1
- meerschaum/config/_version.py +1 -1
- meerschaum/config/stack/__init__.py +9 -7
- meerschaum/config/static/__init__.py +4 -0
- meerschaum/connectors/__init__.py +15 -9
- meerschaum/connectors/api/{APIConnector.py → _APIConnector.py} +3 -1
- meerschaum/connectors/api/__init__.py +2 -1
- meerschaum/connectors/parse.py +18 -16
- meerschaum/connectors/sql/__init__.py +3 -1
- meerschaum/connectors/sql/_pipes.py +39 -39
- meerschaum/connectors/valkey/{ValkeyConnector.py → _ValkeyConnector.py} +5 -5
- meerschaum/connectors/valkey/__init__.py +3 -1
- meerschaum/connectors/valkey/_pipes.py +13 -8
- meerschaum/core/Pipe/_data.py +155 -100
- meerschaum/jobs/_Job.py +1 -6
- meerschaum/jobs/__init__.py +7 -2
- meerschaum/utils/dataframe.py +4 -1
- meerschaum/utils/formatting/_shell.py +5 -6
- meerschaum/utils/packages/__init__.py +14 -9
- {meerschaum-2.4.0.dev1.dist-info → meerschaum-2.4.0rc2.dist-info}/METADATA +1 -1
- {meerschaum-2.4.0.dev1.dist-info → meerschaum-2.4.0rc2.dist-info}/RECORD +65 -65
- {meerschaum-2.4.0.dev1.dist-info → meerschaum-2.4.0rc2.dist-info}/WHEEL +1 -1
- meerschaum/api/dash/actions.py +0 -255
- /meerschaum/connectors/{Connector.py → _Connector.py} +0 -0
- /meerschaum/connectors/sql/{SQLConnector.py → _SQLConnector.py} +0 -0
- {meerschaum-2.4.0.dev1.dist-info → meerschaum-2.4.0rc2.dist-info}/LICENSE +0 -0
- {meerschaum-2.4.0.dev1.dist-info → meerschaum-2.4.0rc2.dist-info}/NOTICE +0 -0
- {meerschaum-2.4.0.dev1.dist-info → meerschaum-2.4.0rc2.dist-info}/entry_points.txt +0 -0
- {meerschaum-2.4.0.dev1.dist-info → meerschaum-2.4.0rc2.dist-info}/top_level.txt +0 -0
- {meerschaum-2.4.0.dev1.dist-info → meerschaum-2.4.0rc2.dist-info}/zip-safe +0 -0
@@ -7,27 +7,24 @@ Custom components are defined here.
|
|
7
7
|
"""
|
8
8
|
|
9
9
|
from __future__ import annotations
|
10
|
-
|
10
|
+
|
11
11
|
from meerschaum.utils.packages import attempt_import, import_dcc, import_html
|
12
12
|
from meerschaum.utils.typing import SuccessTuple, List
|
13
13
|
from meerschaum.config.static import STATIC_CONFIG
|
14
14
|
from meerschaum.utils.misc import remove_ansi
|
15
15
|
from meerschaum._internal.shell.Shell import get_shell_intro
|
16
16
|
from meerschaum.api import endpoints, CHECK_UPDATE
|
17
|
-
from meerschaum.connectors import instance_types
|
17
|
+
from meerschaum.connectors import instance_types, _load_builtin_custom_connectors
|
18
18
|
from meerschaum.utils.misc import get_connector_labels
|
19
19
|
from meerschaum.config import __doc__ as doc
|
20
20
|
dbc = attempt_import('dash_bootstrap_components', lazy=False, check_update=CHECK_UPDATE)
|
21
21
|
html, dcc = import_html(check_update=CHECK_UPDATE), import_dcc(check_update=CHECK_UPDATE)
|
22
22
|
dex = attempt_import('dash_extensions', lazy=False, check_update=CHECK_UPDATE)
|
23
23
|
dash_ace = attempt_import('dash_ace', lazy=False, check_update=CHECK_UPDATE)
|
24
|
-
|
25
|
-
component_ids = {
|
26
|
-
|
27
|
-
}
|
24
|
+
_load_builtin_custom_connectors()
|
28
25
|
|
29
26
|
go_button = dbc.Button('Execute', id='go-button', color='primary', style={'width': '100%'})
|
30
|
-
test_button = dbc.Button('Test', id='test-button', color='danger', style={'display'
|
27
|
+
test_button = dbc.Button('Test', id='test-button', color='danger', style={'display': 'none'})
|
31
28
|
get_items_menu = dbc.DropdownMenu(
|
32
29
|
label='More', id='get-items-menu', children=[
|
33
30
|
dbc.DropdownMenuItem("Plugins", id='get-plugins-button'),
|
@@ -42,21 +39,21 @@ get_items_menu = dbc.DropdownMenu(
|
|
42
39
|
)
|
43
40
|
show_pipes_button = dbc.Button(
|
44
41
|
'Pipes',
|
45
|
-
id
|
46
|
-
color
|
47
|
-
style
|
42
|
+
id='get-pipes-button',
|
43
|
+
color='info',
|
44
|
+
style={'width': '100%'},
|
48
45
|
)
|
49
46
|
show_jobs_button = dbc.Button(
|
50
47
|
'Jobs',
|
51
|
-
id
|
52
|
-
color
|
53
|
-
style
|
48
|
+
id='get-jobs-button',
|
49
|
+
color='success',
|
50
|
+
style={'width': '100%'},
|
54
51
|
)
|
55
52
|
cancel_button = dbc.Button(
|
56
53
|
'Terminal',
|
57
|
-
id
|
58
|
-
color
|
59
|
-
style
|
54
|
+
id='cancel-button',
|
55
|
+
color='dark',
|
56
|
+
style={'width': '100%', 'background-color': 'black', 'display': 'none'},
|
60
57
|
)
|
61
58
|
bottom_buttons_content = dbc.Card(
|
62
59
|
dbc.CardBody(
|
@@ -70,28 +67,26 @@ bottom_buttons_content = dbc.Card(
|
|
70
67
|
)
|
71
68
|
)
|
72
69
|
console_div = html.Div(
|
73
|
-
id
|
74
|
-
children
|
70
|
+
id='console-div',
|
71
|
+
children=[html.Pre(get_shell_intro(), id='console-pre')],
|
75
72
|
)
|
76
73
|
|
77
|
-
location = dcc.Location(id='location', refresh=False)
|
74
|
+
location = dcc.Location(id='mrsm-location', refresh=False)
|
78
75
|
|
79
76
|
search_parameters_editor = dash_ace.DashAceEditor(
|
80
|
-
id
|
81
|
-
theme
|
82
|
-
mode
|
83
|
-
tabSize
|
84
|
-
placeholder
|
77
|
+
id='search-parameters-editor',
|
78
|
+
theme='monokai',
|
79
|
+
mode='json',
|
80
|
+
tabSize=2,
|
81
|
+
placeholder=(
|
85
82
|
'Additional search parameters. ' +
|
86
83
|
'Simple dictionary format or JSON accepted.'
|
87
84
|
),
|
88
|
-
style
|
85
|
+
style={'height': 100},
|
89
86
|
)
|
90
87
|
|
91
88
|
sidebar = dbc.Offcanvas(
|
92
|
-
children=[
|
93
|
-
|
94
|
-
],
|
89
|
+
children=[],
|
95
90
|
title='Pages',
|
96
91
|
)
|
97
92
|
|
@@ -99,13 +94,13 @@ download_dataframe = dcc.Download(id='download-dataframe-csv')
|
|
99
94
|
download_logs = dcc.Download(id='download-logs')
|
100
95
|
|
101
96
|
instance_select = dbc.Select(
|
102
|
-
id
|
103
|
-
size
|
104
|
-
options
|
97
|
+
id='instance-select',
|
98
|
+
size='sm',
|
99
|
+
options=[
|
105
100
|
{'label': i, 'value': i}
|
106
101
|
for i in get_connector_labels(*instance_types)
|
107
102
|
],
|
108
|
-
class_name
|
103
|
+
class_name='dbc_dark custom-select custom-select-sm',
|
109
104
|
)
|
110
105
|
|
111
106
|
|
@@ -116,17 +111,17 @@ navbar = dbc.Navbar(
|
|
116
111
|
dbc.Row(
|
117
112
|
[
|
118
113
|
dbc.Col(
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
114
|
+
html.Img(
|
115
|
+
src=endpoints['dash'] + "/assets/logo_48x48.png",
|
116
|
+
title=doc,
|
117
|
+
),
|
123
118
|
),
|
124
119
|
],
|
125
|
-
align
|
126
|
-
className
|
120
|
+
align='center',
|
121
|
+
className='g-0 navbar-logo-row',
|
127
122
|
),
|
128
|
-
href
|
129
|
-
style
|
123
|
+
href='/docs',
|
124
|
+
style={"textDecoration": "none"},
|
130
125
|
),
|
131
126
|
dbc.NavbarToggler(id="navbar-toggler", n_clicks=0),
|
132
127
|
dbc.Collapse(
|
@@ -136,30 +131,30 @@ navbar = dbc.Navbar(
|
|
136
131
|
dbc.Col(
|
137
132
|
dbc.Button(
|
138
133
|
"Sign out",
|
139
|
-
color
|
140
|
-
style
|
141
|
-
id
|
134
|
+
color='link',
|
135
|
+
style={'margin-left': '30px'},
|
136
|
+
id='sign-out-button',
|
142
137
|
),
|
143
138
|
),
|
144
139
|
],
|
145
|
-
className
|
140
|
+
className="g-0 ms-auto flex-nowrap mt-3 mt-md-0",
|
146
141
|
),
|
147
|
-
id
|
148
|
-
is_open
|
149
|
-
navbar
|
142
|
+
id='navbar-collapse',
|
143
|
+
is_open=False,
|
144
|
+
navbar=True,
|
150
145
|
),
|
151
146
|
],
|
152
|
-
style
|
147
|
+
style={'max-width': '96%'},
|
153
148
|
),
|
154
|
-
color
|
155
|
-
style
|
149
|
+
color='dark', dark=True,
|
150
|
+
style={'width': '100% !important'},
|
156
151
|
)
|
157
152
|
|
158
153
|
refresh_jobs_interval = dcc.Interval(
|
159
|
-
id
|
160
|
-
interval
|
161
|
-
n_intervals
|
162
|
-
disabled
|
154
|
+
id='refresh-jobs-interval',
|
155
|
+
interval=(1 * 1000),
|
156
|
+
n_intervals=0,
|
157
|
+
disabled=False,
|
163
158
|
)
|
164
159
|
|
165
160
|
def alert_from_success_tuple(success: SuccessTuple) -> dbc.Alert:
|
@@ -169,11 +164,11 @@ def alert_from_success_tuple(success: SuccessTuple) -> dbc.Alert:
|
|
169
164
|
return dbc.Alert('', is_open=False) if not isinstance(success, tuple) else (
|
170
165
|
dbc.Alert(
|
171
166
|
remove_ansi(success[1]),
|
172
|
-
id
|
173
|
-
dismissable
|
174
|
-
fade
|
175
|
-
is_open
|
176
|
-
color
|
167
|
+
id='success-alert',
|
168
|
+
dismissable=True,
|
169
|
+
fade=True,
|
170
|
+
is_open=(success[1] not in STATIC_CONFIG['system']['success']['ignore']),
|
171
|
+
color='success' if success[0] else 'danger',
|
177
172
|
)
|
178
173
|
)
|
179
174
|
|
meerschaum/api/dash/jobs.py
CHANGED
@@ -7,24 +7,20 @@ Functions for interacting with jobs via the web interface.
|
|
7
7
|
"""
|
8
8
|
|
9
9
|
from __future__ import annotations
|
10
|
-
from
|
11
|
-
from meerschaum.utils.typing import List, Optional, Dict, Any, Tuple, Union, WebState
|
10
|
+
from meerschaum.utils.typing import List, Dict, WebState
|
12
11
|
from meerschaum.utils.packages import attempt_import, import_html, import_dcc
|
13
|
-
from meerschaum.api.dash.
|
14
|
-
from meerschaum.api.dash.users import is_session_authenticated
|
12
|
+
from meerschaum.api.dash.sessions import is_session_authenticated
|
15
13
|
from meerschaum.api import CHECK_UPDATE
|
16
|
-
dbc = attempt_import('dash_bootstrap_components', lazy=False, check_update=CHECK_UPDATE)
|
17
|
-
html, dcc = import_html(), import_dcc()
|
18
14
|
from meerschaum.jobs import (
|
19
15
|
get_jobs,
|
20
|
-
get_running_jobs,
|
21
|
-
get_paused_jobs,
|
22
|
-
get_stopped_jobs,
|
23
16
|
get_executor_keys_from_context,
|
24
17
|
Job,
|
25
18
|
)
|
26
19
|
from meerschaum.config import get_config
|
27
20
|
from meerschaum.utils.misc import sorted_dict
|
21
|
+
from dash.dependencies import Input, Output, State
|
22
|
+
dbc = attempt_import('dash_bootstrap_components', lazy=False, check_update=CHECK_UPDATE)
|
23
|
+
html, dcc = import_html(), import_dcc()
|
28
24
|
|
29
25
|
STATUS_EMOJI: Dict[str, str] = {
|
30
26
|
'running': get_config('formatting', 'emoji', 'running'),
|
@@ -5,12 +5,15 @@
|
|
5
5
|
Display pipes via a shareable URL.
|
6
6
|
"""
|
7
7
|
|
8
|
-
from meerschaum.api import
|
8
|
+
from meerschaum.api import CHECK_UPDATE
|
9
9
|
from meerschaum.utils.packages import import_html, import_dcc
|
10
|
+
from meerschaum.api.dash.components import download_dataframe
|
11
|
+
|
10
12
|
html, dcc = import_html(check_update=CHECK_UPDATE), import_dcc(check_update=CHECK_UPDATE)
|
11
13
|
import dash_bootstrap_components as dbc
|
12
14
|
|
13
15
|
layout = dbc.Container([
|
14
16
|
dcc.Location('pipes-location'),
|
17
|
+
download_dataframe,
|
15
18
|
html.Div(id='pipe-output-div'),
|
16
19
|
])
|
meerschaum/api/dash/pipes.py
CHANGED
@@ -12,18 +12,16 @@ import shlex
|
|
12
12
|
from textwrap import dedent
|
13
13
|
from dash.dependencies import Input, Output, State
|
14
14
|
from meerschaum.utils.typing import List, Optional, Dict, Any, Tuple, Union
|
15
|
-
from meerschaum.utils.misc import string_to_dict
|
15
|
+
from meerschaum.utils.misc import string_to_dict
|
16
16
|
from meerschaum.utils.packages import attempt_import, import_dcc, import_html, import_pandas
|
17
17
|
from meerschaum.utils.sql import get_pd_type
|
18
18
|
from meerschaum.utils.yaml import yaml
|
19
19
|
from meerschaum.connectors.sql._fetch import get_pipe_query
|
20
|
-
from meerschaum.api import
|
21
|
-
from meerschaum.api.dash import
|
22
|
-
dash_app, debug, _get_pipes
|
23
|
-
)
|
20
|
+
from meerschaum.api import CHECK_UPDATE
|
21
|
+
from meerschaum.api.dash import debug, _get_pipes
|
24
22
|
from meerschaum.api.dash.connectors import get_web_connector
|
25
23
|
from meerschaum.api.dash.components import alert_from_success_tuple, build_cards_grid
|
26
|
-
from meerschaum.api.dash.
|
24
|
+
from meerschaum.api.dash.sessions import is_session_authenticated
|
27
25
|
from meerschaum.config import get_config
|
28
26
|
import meerschaum as mrsm
|
29
27
|
dbc = attempt_import('dash_bootstrap_components', lazy=False, check_update=CHECK_UPDATE)
|
@@ -235,12 +233,10 @@ def build_pipe_card(
|
|
235
233
|
),
|
236
234
|
dbc.Col(
|
237
235
|
dbc.Button(
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
],
|
243
|
-
# href=pipe_url,
|
236
|
+
"🔗",
|
237
|
+
href=pipe_url,
|
238
|
+
external_link=True,
|
239
|
+
target="_blank",
|
244
240
|
style={'float': 'right'},
|
245
241
|
outline=True,
|
246
242
|
color='link',
|
@@ -553,11 +549,11 @@ def accordion_items_from_pipe(
|
|
553
549
|
backtrack_df = pipe.get_backtrack_data(debug=debug, limit=1)
|
554
550
|
try:
|
555
551
|
json_text = backtrack_df.fillna(pd.NA).to_json(
|
556
|
-
orient
|
557
|
-
date_format
|
558
|
-
force_ascii
|
559
|
-
indent
|
560
|
-
date_unit
|
552
|
+
orient='records',
|
553
|
+
date_format='iso',
|
554
|
+
force_ascii=False,
|
555
|
+
indent=4,
|
556
|
+
date_unit='ns',
|
561
557
|
) if backtrack_df is not None else '[]'
|
562
558
|
except Exception as e:
|
563
559
|
warn(e)
|
meerschaum/api/dash/plugins.py
CHANGED
@@ -13,7 +13,8 @@ from meerschaum.api import get_api_connector, endpoints, CHECK_UPDATE
|
|
13
13
|
html, dcc = import_html(check_update=CHECK_UPDATE), import_dcc(check_update=CHECK_UPDATE)
|
14
14
|
import dash_bootstrap_components as dbc
|
15
15
|
from meerschaum.core import Plugin
|
16
|
-
from meerschaum.api.dash import dash_app, debug
|
16
|
+
from meerschaum.api.dash import dash_app, debug
|
17
|
+
from meerschaum.api.dash.sessions import get_username_from_session
|
17
18
|
|
18
19
|
|
19
20
|
def get_plugins_cards(
|
@@ -88,9 +89,10 @@ def is_plugin_owner(plugin_name: str, session_data: Dict['str', Any]) -> bool:
|
|
88
89
|
Check whether the currently logged in user is the owner of a plugin.
|
89
90
|
"""
|
90
91
|
plugin = Plugin(plugin_name)
|
91
|
-
|
92
|
-
|
93
|
-
|
92
|
+
session_id = session_data.get('session-id', None)
|
93
|
+
if session_id is None:
|
94
|
+
return False
|
95
|
+
_username = get_username_from_session(session_id)
|
94
96
|
_plugin_username = get_api_connector().get_plugin_username(plugin, debug=debug)
|
95
97
|
return (
|
96
98
|
_username is not None
|
@@ -0,0 +1,176 @@
|
|
1
|
+
#! /usr/bin/env python3
|
2
|
+
# vim:fenc=utf-8
|
3
|
+
|
4
|
+
"""
|
5
|
+
Define caching functions for session management.
|
6
|
+
"""
|
7
|
+
|
8
|
+
import json
|
9
|
+
|
10
|
+
from meerschaum.utils.typing import Union, Dict, Any, Optional
|
11
|
+
from meerschaum.api import debug, no_auth
|
12
|
+
from meerschaum.api import get_cache_connector, get_api_connector
|
13
|
+
from meerschaum.core import User
|
14
|
+
from meerschaum.config import get_config
|
15
|
+
from meerschaum.utils.warnings import dprint
|
16
|
+
|
17
|
+
SESSION_KEY_TEMPLATE: str = 'mrsm_session_id:{session_id}'
|
18
|
+
EXPIRES_SECONDS: int = get_config('system', 'api', 'cache', 'session_expires_minutes') * 60
|
19
|
+
_active_sessions: Dict[str, Dict[str, Any]] = {}
|
20
|
+
|
21
|
+
|
22
|
+
def get_session_key(session_id: str) -> str:
|
23
|
+
"""
|
24
|
+
Return the session key for the cache connector.
|
25
|
+
"""
|
26
|
+
return SESSION_KEY_TEMPLATE.format(session_id=session_id)
|
27
|
+
|
28
|
+
|
29
|
+
def set_session(session_id: str, session_data: Dict[str, Any]):
|
30
|
+
"""
|
31
|
+
Set a `session_id` to a dictionary.
|
32
|
+
"""
|
33
|
+
conn = get_cache_connector()
|
34
|
+
if conn is None:
|
35
|
+
_active_sessions[session_id] = session_data
|
36
|
+
if debug:
|
37
|
+
dprint(f"Setting in-memory data for {session_id}:\n{session_data}")
|
38
|
+
return
|
39
|
+
|
40
|
+
session_key = get_session_key(session_id)
|
41
|
+
session_data_str = json.dumps(session_data, separators=(',', ':'))
|
42
|
+
if debug:
|
43
|
+
dprint(f"Setting production data for {session_id=}:\n{session_data_str}")
|
44
|
+
|
45
|
+
conn.set(session_key, session_data_str, ex=EXPIRES_SECONDS)
|
46
|
+
|
47
|
+
|
48
|
+
def update_session(session_id: Optional[str], session_data: Dict[str, Any]):
|
49
|
+
"""
|
50
|
+
Update the session's data dictionary.
|
51
|
+
"""
|
52
|
+
existing_session_data = get_session_data(session_id) or {}
|
53
|
+
existing_session_data.update(session_data)
|
54
|
+
set_session(str(session_id), existing_session_data)
|
55
|
+
|
56
|
+
|
57
|
+
def get_session_data(session_id: Optional[str]) -> Union[Dict[str, Any], None]:
|
58
|
+
"""
|
59
|
+
Return the session data dictionary.
|
60
|
+
"""
|
61
|
+
if debug:
|
62
|
+
dprint(f"Getting session data for {session_id=}")
|
63
|
+
conn = get_cache_connector()
|
64
|
+
if conn is None:
|
65
|
+
return _active_sessions.get(str(session_id), None)
|
66
|
+
|
67
|
+
session_key = get_session_key(str(session_id))
|
68
|
+
session_data_str = conn.get(session_key)
|
69
|
+
if not session_data_str:
|
70
|
+
return None
|
71
|
+
|
72
|
+
return json.loads(session_data_str)
|
73
|
+
|
74
|
+
|
75
|
+
def get_username_from_session(session_id: Optional[str]) -> Union[str, None]:
|
76
|
+
"""
|
77
|
+
If a `session_id` has been set, return the username.
|
78
|
+
Otherwise return `None`.
|
79
|
+
"""
|
80
|
+
if debug:
|
81
|
+
dprint(f"Getting username for {session_id=}")
|
82
|
+
session_data = get_session_data(session_id)
|
83
|
+
if session_data is None:
|
84
|
+
return None
|
85
|
+
|
86
|
+
return session_data.get('username', None)
|
87
|
+
|
88
|
+
|
89
|
+
def is_session_active(session_id: Union[str, None]) -> bool:
|
90
|
+
"""
|
91
|
+
Return whether a given `session_id` has been set.
|
92
|
+
"""
|
93
|
+
return get_username_from_session(str(session_id)) is not None
|
94
|
+
|
95
|
+
|
96
|
+
def delete_session(session_id: str):
|
97
|
+
"""
|
98
|
+
Delete a session if it's been set.
|
99
|
+
"""
|
100
|
+
if debug:
|
101
|
+
dprint(f"Deleting {session_id=}")
|
102
|
+
conn = get_cache_connector()
|
103
|
+
if conn is None:
|
104
|
+
_ = _active_sessions.pop(session_id, None)
|
105
|
+
return
|
106
|
+
|
107
|
+
session_key = get_session_key(session_id)
|
108
|
+
conn.client.delete(session_key)
|
109
|
+
|
110
|
+
|
111
|
+
def is_session_authenticated(session_id: Optional[str]) -> bool:
|
112
|
+
"""
|
113
|
+
Check is a session ID is active.
|
114
|
+
If running in secure mode, check whether a session ID corresponds to an admin.
|
115
|
+
|
116
|
+
Parameters
|
117
|
+
----------
|
118
|
+
session_id: str
|
119
|
+
The session UUID.
|
120
|
+
|
121
|
+
Returns
|
122
|
+
-------
|
123
|
+
A bool whether the session is authenticated to perform actions.
|
124
|
+
"""
|
125
|
+
if debug:
|
126
|
+
dprint(f"Checking authentication for {session_id=}")
|
127
|
+
|
128
|
+
if no_auth:
|
129
|
+
return True
|
130
|
+
|
131
|
+
if session_id is None:
|
132
|
+
return False
|
133
|
+
|
134
|
+
session_data = get_session_data(session_id)
|
135
|
+
if session_data is None:
|
136
|
+
return False
|
137
|
+
username = session_data.get('username', None)
|
138
|
+
if username is None:
|
139
|
+
return False
|
140
|
+
|
141
|
+
cached_auth = session_data.get('authenticated', None)
|
142
|
+
if cached_auth is not None:
|
143
|
+
return cached_auth
|
144
|
+
|
145
|
+
permissions = get_config('system', 'api', 'permissions')
|
146
|
+
allow_non_admin = permissions.get('actions', {}).get('non_admin', False)
|
147
|
+
|
148
|
+
is_auth = True if allow_non_admin else session_is_admin(session_id)
|
149
|
+
update_session(session_id, {'authenticated': is_auth})
|
150
|
+
return is_auth
|
151
|
+
|
152
|
+
|
153
|
+
def session_is_admin(session_id: str) -> bool:
|
154
|
+
"""
|
155
|
+
Check whether a session ID corresponds to an admin user.
|
156
|
+
"""
|
157
|
+
if debug:
|
158
|
+
dprint(f"Check admin for {session_id=}")
|
159
|
+
session_data = get_session_data(session_id)
|
160
|
+
if session_data is None:
|
161
|
+
return False
|
162
|
+
|
163
|
+
username = session_data.get('username', None)
|
164
|
+
if username is None:
|
165
|
+
return False
|
166
|
+
|
167
|
+
cached_admin = session_data.get('admin', None)
|
168
|
+
if cached_admin is not None:
|
169
|
+
return cached_admin
|
170
|
+
|
171
|
+
conn = get_api_connector()
|
172
|
+
user = User(username, instance=conn)
|
173
|
+
user_type = conn.get_user_type(user, debug=debug)
|
174
|
+
is_admin = user_type == 'admin'
|
175
|
+
update_session(session_id, {'admin': is_admin})
|
176
|
+
return is_admin
|
meerschaum/api/dash/users.py
CHANGED
@@ -8,13 +8,10 @@ Functions for interacting with users.
|
|
8
8
|
|
9
9
|
from __future__ import annotations
|
10
10
|
|
11
|
-
from meerschaum.api import debug, CHECK_UPDATE
|
12
|
-
from meerschaum.api.dash import active_sessions, authenticated_sessions, unauthenticated_sessions
|
11
|
+
from meerschaum.api import debug, CHECK_UPDATE
|
13
12
|
from meerschaum.api.dash.connectors import get_web_connector
|
14
|
-
from meerschaum.utils.typing import WebState, SuccessTuple, List, Tuple
|
13
|
+
from meerschaum.utils.typing import WebState, SuccessTuple, List, Tuple
|
15
14
|
from meerschaum.utils.packages import attempt_import, import_html, import_dcc
|
16
|
-
from meerschaum.config import get_config
|
17
|
-
from meerschaum.core import User
|
18
15
|
dcc, html = import_dcc(check_update=CHECK_UPDATE), import_html(check_update=CHECK_UPDATE)
|
19
16
|
dbc = attempt_import('dash_bootstrap_components', lazy=False, check_update=CHECK_UPDATE)
|
20
17
|
|
@@ -35,51 +32,3 @@ def get_users_cards(state: WebState) -> Tuple[List[dbc.Card], List[SuccessTuple]
|
|
35
32
|
)
|
36
33
|
|
37
34
|
return cards, alerts
|
38
|
-
|
39
|
-
|
40
|
-
def is_session_authenticated(session_id: str) -> bool:
|
41
|
-
"""
|
42
|
-
Check is a session ID is active.
|
43
|
-
If running in secure mode, check whether a session ID corresponds to an admin.
|
44
|
-
|
45
|
-
Parameters
|
46
|
-
----------
|
47
|
-
session_id: str
|
48
|
-
The session UUID.
|
49
|
-
|
50
|
-
Returns
|
51
|
-
-------
|
52
|
-
A bool whether the session is authenticated to perform actions.
|
53
|
-
"""
|
54
|
-
if no_auth:
|
55
|
-
return True
|
56
|
-
if session_id not in active_sessions:
|
57
|
-
return False
|
58
|
-
if session_id in unauthenticated_sessions:
|
59
|
-
return False
|
60
|
-
if session_id in authenticated_sessions:
|
61
|
-
return True
|
62
|
-
|
63
|
-
permissions = get_config('system', 'api', 'permissions')
|
64
|
-
allow_non_admin = permissions.get('actions', {}).get('non_admin', False)
|
65
|
-
|
66
|
-
is_auth = True if allow_non_admin else session_is_admin(session_id)
|
67
|
-
username = active_sessions.get(session_id, {}).get('username', None)
|
68
|
-
|
69
|
-
if is_auth:
|
70
|
-
authenticated_sessions[session_id] = username
|
71
|
-
else:
|
72
|
-
unauthenticated_sessions[session_id] = username
|
73
|
-
|
74
|
-
return is_auth
|
75
|
-
|
76
|
-
|
77
|
-
def session_is_admin(session_id: str) -> bool:
|
78
|
-
"""
|
79
|
-
Check whether a session ID corresponds to an admin user.
|
80
|
-
"""
|
81
|
-
conn = get_api_connector()
|
82
|
-
username = active_sessions.get(session_id, {}).get('username', None)
|
83
|
-
user = User(username, instance=conn)
|
84
|
-
user_type = conn.get_user_type(user, debug=debug)
|
85
|
-
return user_type == 'admin'
|
meerschaum/api/dash/webterm.py
CHANGED
@@ -7,16 +7,12 @@ Functions for interacting with the Webterm via the dashboard.
|
|
7
7
|
"""
|
8
8
|
|
9
9
|
import time
|
10
|
-
from
|
11
|
-
from meerschaum.
|
12
|
-
from meerschaum.api import debug, CHECK_UPDATE, get_api_connector, no_auth
|
13
|
-
from meerschaum.api.dash import active_sessions
|
14
|
-
from meerschaum.api.dash.users import is_session_authenticated
|
10
|
+
from meerschaum.api import CHECK_UPDATE, get_api_connector
|
11
|
+
from meerschaum.api.dash.sessions import is_session_authenticated, get_username_from_session
|
15
12
|
from meerschaum.api.dash.components import alert_from_success_tuple, console_div
|
16
|
-
from meerschaum.utils.typing import WebState,
|
17
|
-
from meerschaum.utils.packages import attempt_import, import_html, import_dcc
|
13
|
+
from meerschaum.utils.typing import WebState, Tuple, Any
|
14
|
+
from meerschaum.utils.packages import attempt_import, import_html, import_dcc
|
18
15
|
from meerschaum._internal.term.tools import is_webterm_running
|
19
|
-
from meerschaum.config.static import STATIC_CONFIG
|
20
16
|
from meerschaum.utils.threading import Thread, RLock
|
21
17
|
dcc, html = import_dcc(check_update=CHECK_UPDATE), import_html(check_update=CHECK_UPDATE)
|
22
18
|
dbc = attempt_import('dash_bootstrap_components', lazy=False, check_update=CHECK_UPDATE)
|
@@ -30,7 +26,7 @@ def get_webterm(state: WebState) -> Tuple[Any, Any]:
|
|
30
26
|
Start the webterm and return its iframe.
|
31
27
|
"""
|
32
28
|
session_id = state['session-store.data'].get('session-id', None)
|
33
|
-
username =
|
29
|
+
username = get_username_from_session(session_id)
|
34
30
|
if not is_session_authenticated(session_id):
|
35
31
|
msg = f"User '{username}' is not authorized to access the webterm."
|
36
32
|
return (
|
@@ -57,24 +53,23 @@ def get_webterm(state: WebState) -> Tuple[Any, Any]:
|
|
57
53
|
return console_div, [alert_from_success_tuple((False, "Could not start the webterm server."))]
|
58
54
|
|
59
55
|
|
60
|
-
|
61
56
|
webterm_procs = {}
|
62
57
|
def start_webterm() -> None:
|
63
58
|
"""
|
64
59
|
Start the webterm thread.
|
65
60
|
"""
|
66
|
-
from meerschaum._internal.entry import entry
|
67
61
|
from meerschaum.utils.packages import run_python_package
|
68
62
|
|
69
63
|
def run():
|
64
|
+
conn = get_api_connector()
|
70
65
|
_ = run_python_package(
|
71
66
|
'meerschaum',
|
72
|
-
['start', 'webterm'],
|
73
|
-
capture_output
|
74
|
-
as_proc
|
75
|
-
store_proc_dict
|
76
|
-
store_proc_key
|
77
|
-
venv
|
67
|
+
['start', 'webterm', '-i', str(conn)],
|
68
|
+
capture_output=True,
|
69
|
+
as_proc=True,
|
70
|
+
store_proc_dict=webterm_procs,
|
71
|
+
store_proc_key='process',
|
72
|
+
venv=None,
|
78
73
|
)
|
79
74
|
|
80
75
|
with _locks['webterm_thread']:
|
@@ -15,7 +15,7 @@ function make_terminal(element, size, ws_url) {
|
|
15
15
|
term.attachCustomKeyEventHandler(copyPasteKeyEventHandler);
|
16
16
|
term.open(element);
|
17
17
|
|
18
|
-
sessionStore =
|
18
|
+
sessionStore = localStorage.getItem("session-store");
|
19
19
|
ws.onopen = function (event) {
|
20
20
|
ws.send(sessionStore);
|
21
21
|
ws.send(
|