meerschaum 2.3.5.dev0__py3-none-any.whl → 2.4.0__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/__init__.py +2 -1
- meerschaum/_internal/arguments/_parse_arguments.py +88 -12
- meerschaum/_internal/docs/index.py +3 -2
- meerschaum/_internal/entry.py +42 -20
- 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 +26 -23
- meerschaum/actions/bootstrap.py +38 -11
- meerschaum/actions/copy.py +3 -3
- meerschaum/actions/delete.py +4 -1
- meerschaum/actions/register.py +1 -3
- meerschaum/actions/stack.py +24 -19
- meerschaum/actions/start.py +41 -41
- meerschaum/actions/sync.py +53 -52
- meerschaum/api/__init__.py +48 -14
- meerschaum/api/_events.py +26 -17
- meerschaum/api/_oauth2.py +2 -2
- meerschaum/api/_websockets.py +5 -4
- meerschaum/api/dash/__init__.py +7 -16
- meerschaum/api/dash/callbacks/__init__.py +1 -0
- meerschaum/api/dash/callbacks/dashboard.py +52 -58
- meerschaum/api/dash/callbacks/jobs.py +15 -16
- meerschaum/api/dash/callbacks/login.py +16 -10
- meerschaum/api/dash/callbacks/pipes.py +41 -0
- 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/__init__.py +1 -0
- meerschaum/api/dash/pages/pipes.py +19 -0
- meerschaum/api/dash/pipes.py +86 -58
- meerschaum/api/dash/plugins.py +6 -4
- meerschaum/api/dash/sessions.py +176 -0
- meerschaum/api/dash/users.py +3 -41
- meerschaum/api/dash/webterm.py +12 -17
- meerschaum/api/resources/static/js/terminado.js +1 -1
- meerschaum/api/routes/_actions.py +4 -118
- meerschaum/api/routes/_jobs.py +45 -24
- meerschaum/api/routes/_login.py +4 -4
- meerschaum/api/routes/_pipes.py +3 -3
- meerschaum/api/routes/_webterm.py +5 -6
- meerschaum/config/_default.py +15 -3
- meerschaum/config/_version.py +1 -1
- meerschaum/config/stack/__init__.py +64 -21
- meerschaum/config/static/__init__.py +6 -0
- meerschaum/connectors/{Connector.py → _Connector.py} +19 -13
- meerschaum/connectors/__init__.py +24 -14
- meerschaum/connectors/api/{APIConnector.py → _APIConnector.py} +3 -1
- meerschaum/connectors/api/__init__.py +2 -1
- meerschaum/connectors/api/_actions.py +22 -36
- meerschaum/connectors/api/_jobs.py +1 -0
- meerschaum/connectors/parse.py +18 -16
- meerschaum/connectors/poll.py +30 -24
- meerschaum/connectors/sql/__init__.py +3 -1
- meerschaum/connectors/sql/_pipes.py +172 -197
- meerschaum/connectors/sql/_plugins.py +45 -43
- meerschaum/connectors/sql/_users.py +46 -38
- meerschaum/connectors/valkey/_ValkeyConnector.py +535 -0
- meerschaum/connectors/valkey/__init__.py +10 -0
- meerschaum/connectors/valkey/_fetch.py +75 -0
- meerschaum/connectors/valkey/_pipes.py +844 -0
- meerschaum/connectors/valkey/_plugins.py +265 -0
- meerschaum/connectors/valkey/_users.py +305 -0
- meerschaum/core/Pipe/__init__.py +3 -0
- meerschaum/core/Pipe/_attributes.py +1 -2
- meerschaum/core/Pipe/_clear.py +16 -13
- meerschaum/core/Pipe/_copy.py +106 -0
- meerschaum/core/Pipe/_data.py +165 -101
- meerschaum/core/Pipe/_drop.py +4 -4
- meerschaum/core/Pipe/_dtypes.py +14 -14
- meerschaum/core/Pipe/_edit.py +15 -14
- meerschaum/core/Pipe/_sync.py +134 -53
- meerschaum/core/Pipe/_verify.py +11 -11
- meerschaum/core/User/_User.py +14 -12
- meerschaum/jobs/_Job.py +27 -14
- meerschaum/jobs/__init__.py +7 -2
- meerschaum/jobs/systemd.py +20 -8
- meerschaum/plugins/_Plugin.py +17 -13
- meerschaum/utils/_get_pipes.py +14 -20
- meerschaum/utils/dataframe.py +291 -101
- meerschaum/utils/dtypes/__init__.py +31 -6
- meerschaum/utils/dtypes/sql.py +4 -4
- meerschaum/utils/formatting/_shell.py +5 -6
- meerschaum/utils/misc.py +3 -3
- meerschaum/utils/packages/__init__.py +14 -9
- meerschaum/utils/packages/_packages.py +2 -0
- meerschaum/utils/prompt.py +1 -1
- meerschaum/utils/schedule.py +1 -0
- {meerschaum-2.3.5.dev0.dist-info → meerschaum-2.4.0.dist-info}/METADATA +7 -1
- {meerschaum-2.3.5.dev0.dist-info → meerschaum-2.4.0.dist-info}/RECORD +98 -89
- {meerschaum-2.3.5.dev0.dist-info → meerschaum-2.4.0.dist-info}/WHEEL +1 -1
- meerschaum/api/dash/actions.py +0 -255
- /meerschaum/connectors/sql/{SQLConnector.py → _SQLConnector.py} +0 -0
- {meerschaum-2.3.5.dev0.dist-info → meerschaum-2.4.0.dist-info}/LICENSE +0 -0
- {meerschaum-2.3.5.dev0.dist-info → meerschaum-2.4.0.dist-info}/NOTICE +0 -0
- {meerschaum-2.3.5.dev0.dist-info → meerschaum-2.4.0.dist-info}/entry_points.txt +0 -0
- {meerschaum-2.3.5.dev0.dist-info → meerschaum-2.4.0.dist-info}/top_level.txt +0 -0
- {meerschaum-2.3.5.dev0.dist-info → meerschaum-2.4.0.dist-info}/zip-safe +0 -0
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)
|
@@ -48,13 +46,14 @@ def pipe_from_ctx(ctx, trigger_property: str = 'n_clicks') -> Union[mrsm.Pipe, N
|
|
48
46
|
return None
|
49
47
|
return mrsm.Pipe(**meta)
|
50
48
|
|
49
|
+
|
51
50
|
def keys_from_state(
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
51
|
+
state: Dict[str, Any],
|
52
|
+
with_params: bool = False
|
53
|
+
) -> Union[
|
54
|
+
Tuple[List[str], List[str], List[str]],
|
55
|
+
Tuple[List[str], List[str], List[str], str],
|
56
|
+
]:
|
58
57
|
"""
|
59
58
|
Read the current state and return the selected keys lists.
|
60
59
|
"""
|
@@ -85,10 +84,11 @@ def keys_from_state(
|
|
85
84
|
keys.append(params)
|
86
85
|
return tuple(keys)
|
87
86
|
|
87
|
+
|
88
88
|
def pipes_from_state(
|
89
|
-
|
90
|
-
|
91
|
-
|
89
|
+
state: Dict[str, Any],
|
90
|
+
**kw
|
91
|
+
):
|
92
92
|
_ck, _mk, _lk, _params = keys_from_state(state, with_params=True)
|
93
93
|
try:
|
94
94
|
_pipes = _get_pipes(
|
@@ -103,10 +103,10 @@ def pipes_from_state(
|
|
103
103
|
|
104
104
|
|
105
105
|
def build_pipe_card(
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
106
|
+
pipe: mrsm.Pipe,
|
107
|
+
authenticated: bool = False,
|
108
|
+
_build_children_num: int = 10,
|
109
|
+
) -> 'dbc.Card':
|
110
110
|
"""
|
111
111
|
Return a card for the given pipe.
|
112
112
|
|
@@ -128,11 +128,11 @@ def build_pipe_card(
|
|
128
128
|
dbc.Col(
|
129
129
|
(
|
130
130
|
dbc.DropdownMenu(
|
131
|
-
label
|
132
|
-
children
|
131
|
+
label="Manage",
|
132
|
+
children=[
|
133
133
|
dbc.DropdownMenuItem(
|
134
134
|
'Open in Python',
|
135
|
-
id
|
135
|
+
id={
|
136
136
|
'type': 'manage-pipe-button',
|
137
137
|
'index': meta_str,
|
138
138
|
'action': 'python',
|
@@ -140,7 +140,7 @@ def build_pipe_card(
|
|
140
140
|
),
|
141
141
|
dbc.DropdownMenuItem(
|
142
142
|
'Delete',
|
143
|
-
id
|
143
|
+
id={
|
144
144
|
'type': 'manage-pipe-button',
|
145
145
|
'index': meta_str,
|
146
146
|
'action': 'delete',
|
@@ -148,7 +148,7 @@ def build_pipe_card(
|
|
148
148
|
),
|
149
149
|
dbc.DropdownMenuItem(
|
150
150
|
'Drop',
|
151
|
-
id
|
151
|
+
id={
|
152
152
|
'type': 'manage-pipe-button',
|
153
153
|
'index': meta_str,
|
154
154
|
'action': 'drop',
|
@@ -156,7 +156,7 @@ def build_pipe_card(
|
|
156
156
|
),
|
157
157
|
dbc.DropdownMenuItem(
|
158
158
|
'Clear',
|
159
|
-
id
|
159
|
+
id={
|
160
160
|
'type': 'manage-pipe-button',
|
161
161
|
'index': meta_str,
|
162
162
|
'action': 'clear',
|
@@ -164,7 +164,7 @@ def build_pipe_card(
|
|
164
164
|
),
|
165
165
|
dbc.DropdownMenuItem(
|
166
166
|
'Verify',
|
167
|
-
id
|
167
|
+
id={
|
168
168
|
'type': 'manage-pipe-button',
|
169
169
|
'index': meta_str,
|
170
170
|
'action': 'verify',
|
@@ -172,56 +172,84 @@ def build_pipe_card(
|
|
172
172
|
),
|
173
173
|
dbc.DropdownMenuItem(
|
174
174
|
'Sync',
|
175
|
-
id
|
175
|
+
id={
|
176
176
|
'type': 'manage-pipe-button',
|
177
177
|
'index': meta_str,
|
178
178
|
'action': 'sync',
|
179
179
|
},
|
180
180
|
),
|
181
181
|
],
|
182
|
-
direction
|
183
|
-
menu_variant
|
184
|
-
size
|
185
|
-
color
|
182
|
+
direction="up",
|
183
|
+
menu_variant="dark",
|
184
|
+
size='sm',
|
185
|
+
color='secondary',
|
186
186
|
)
|
187
187
|
) if authenticated else [],
|
188
|
-
width
|
188
|
+
width=2,
|
189
189
|
),
|
190
190
|
dbc.Col(width=6),
|
191
191
|
dbc.Col(
|
192
192
|
dbc.Button(
|
193
193
|
'Download CSV',
|
194
|
-
size
|
195
|
-
color
|
196
|
-
style
|
197
|
-
id
|
194
|
+
size='sm',
|
195
|
+
color='link',
|
196
|
+
style={'float': 'right'},
|
197
|
+
id={'type': 'pipe-download-csv-button', 'index': meta_str},
|
198
198
|
),
|
199
|
-
width
|
199
|
+
width=4,
|
200
200
|
),
|
201
201
|
],
|
202
|
-
justify
|
202
|
+
justify='start',
|
203
203
|
)
|
204
204
|
card_body_children = [
|
205
|
-
html.H5(
|
206
|
-
html.B(str(pipe)),
|
207
|
-
className = 'card-title',
|
208
|
-
style = {'font-family': ['monospace']}
|
209
|
-
),
|
210
205
|
html.Div(
|
211
206
|
dbc.Accordion(
|
212
207
|
accordion_items_from_pipe(
|
213
208
|
pipe,
|
214
|
-
authenticated
|
215
|
-
_build_children_num
|
209
|
+
authenticated=authenticated,
|
210
|
+
_build_children_num=_build_children_num,
|
216
211
|
),
|
217
|
-
flush
|
218
|
-
start_collapsed
|
219
|
-
id
|
212
|
+
flush=True,
|
213
|
+
start_collapsed=True,
|
214
|
+
id={'type': 'pipe-accordion', 'index': meta_str},
|
220
215
|
)
|
221
216
|
)
|
222
217
|
|
223
218
|
]
|
219
|
+
|
220
|
+
pipe_url = (
|
221
|
+
f"/dash/pipes/{pipe.connector_keys}/{pipe.metric_key}/{pipe.location_key}"
|
222
|
+
)
|
223
|
+
|
224
|
+
card_header_children = dbc.Row(
|
225
|
+
[
|
226
|
+
dbc.Col(
|
227
|
+
html.H5(
|
228
|
+
html.B(str(pipe)),
|
229
|
+
className='card-title',
|
230
|
+
style={'font-family': ['monospace']}
|
231
|
+
),
|
232
|
+
width=11,
|
233
|
+
),
|
234
|
+
dbc.Col(
|
235
|
+
dbc.Button(
|
236
|
+
"🔗",
|
237
|
+
href=pipe_url,
|
238
|
+
external_link=True,
|
239
|
+
target="_blank",
|
240
|
+
style={'float': 'right'},
|
241
|
+
outline=True,
|
242
|
+
color='link',
|
243
|
+
id={'type': 'share-pipe-button', 'index': meta_str},
|
244
|
+
),
|
245
|
+
width=1,
|
246
|
+
),
|
247
|
+
],
|
248
|
+
justify='start',
|
249
|
+
)
|
250
|
+
|
224
251
|
return dbc.Card([
|
252
|
+
dbc.CardHeader(children=card_header_children),
|
225
253
|
dbc.CardBody(children=card_body_children),
|
226
254
|
dbc.CardFooter(children=footer_children),
|
227
255
|
])
|
@@ -270,11 +298,11 @@ def get_pipes_cards(*keys, session_data: Optional[Dict[str, Any]] = None):
|
|
270
298
|
|
271
299
|
|
272
300
|
def accordion_items_from_pipe(
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
301
|
+
pipe: mrsm.Pipe,
|
302
|
+
active_items: Optional[List[str]] = None,
|
303
|
+
authenticated: bool = False,
|
304
|
+
_build_children_num: int = 10,
|
305
|
+
) -> 'List[dbc.AccordionItem]':
|
278
306
|
"""
|
279
307
|
Build the accordion items for a given pipe.
|
280
308
|
"""
|
@@ -521,11 +549,11 @@ def accordion_items_from_pipe(
|
|
521
549
|
backtrack_df = pipe.get_backtrack_data(debug=debug, limit=1)
|
522
550
|
try:
|
523
551
|
json_text = backtrack_df.fillna(pd.NA).to_json(
|
524
|
-
orient
|
525
|
-
date_format
|
526
|
-
force_ascii
|
527
|
-
indent
|
528
|
-
date_unit
|
552
|
+
orient='records',
|
553
|
+
date_format='iso',
|
554
|
+
force_ascii=False,
|
555
|
+
indent=4,
|
556
|
+
date_unit='ns',
|
529
557
|
) if backtrack_df is not None else '[]'
|
530
558
|
except Exception as e:
|
531
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,16 +8,14 @@ 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
|
|
18
|
+
|
21
19
|
def get_users_cards(state: WebState) -> Tuple[List[dbc.Card], List[SuccessTuple]]:
|
22
20
|
"""
|
23
21
|
Return the cards and alerts for users.
|
@@ -34,39 +32,3 @@ def get_users_cards(state: WebState) -> Tuple[List[dbc.Card], List[SuccessTuple]
|
|
34
32
|
)
|
35
33
|
|
36
34
|
return cards, alerts
|
37
|
-
|
38
|
-
|
39
|
-
def is_session_authenticated(session_id: str) -> bool:
|
40
|
-
"""
|
41
|
-
Check is a session ID is active.
|
42
|
-
If running in secure mode, check whether a session ID corresponds to an admin.
|
43
|
-
|
44
|
-
Parameters
|
45
|
-
----------
|
46
|
-
session_id: str
|
47
|
-
The session UUID.
|
48
|
-
|
49
|
-
Returns
|
50
|
-
-------
|
51
|
-
A bool whether the session is authenticated to perform actions.
|
52
|
-
"""
|
53
|
-
if no_auth:
|
54
|
-
return True
|
55
|
-
if session_id in unauthenticated_sessions:
|
56
|
-
return False
|
57
|
-
if session_id in authenticated_sessions:
|
58
|
-
return True
|
59
|
-
permissions = get_config('system', 'api', 'permissions')
|
60
|
-
allow_non_admin = permissions.get('actions', {}).get('non_admin', False)
|
61
|
-
if allow_non_admin:
|
62
|
-
return True
|
63
|
-
conn = get_api_connector()
|
64
|
-
username = active_sessions.get(session_id, {}).get('username', None)
|
65
|
-
user = User(username, instance=conn)
|
66
|
-
user_type = conn.get_user_type(user, debug=debug)
|
67
|
-
is_auth = user_type == 'admin'
|
68
|
-
if is_auth:
|
69
|
-
authenticated_sessions[session_id] = username
|
70
|
-
else:
|
71
|
-
unauthenticated_sessions[session_id] = username
|
72
|
-
return is_auth
|
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(
|