meerschaum 2.9.5__py3-none-any.whl → 3.0.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/__init__.py +5 -2
- meerschaum/_internal/__init__.py +1 -0
- meerschaum/_internal/arguments/_parse_arguments.py +4 -4
- meerschaum/_internal/arguments/_parser.py +19 -2
- meerschaum/_internal/docs/index.py +49 -2
- meerschaum/_internal/entry.py +6 -6
- meerschaum/_internal/shell/Shell.py +1 -1
- meerschaum/_internal/static.py +356 -0
- meerschaum/actions/api.py +12 -2
- meerschaum/actions/bootstrap.py +7 -7
- meerschaum/actions/edit.py +142 -18
- meerschaum/actions/register.py +137 -6
- meerschaum/actions/show.py +117 -29
- meerschaum/actions/stop.py +4 -1
- meerschaum/actions/sync.py +1 -1
- meerschaum/actions/tag.py +9 -8
- meerschaum/actions/verify.py +5 -8
- meerschaum/api/__init__.py +11 -3
- meerschaum/api/_events.py +39 -2
- meerschaum/api/_oauth2.py +118 -8
- meerschaum/api/_tokens.py +102 -0
- meerschaum/api/dash/__init__.py +0 -3
- meerschaum/api/dash/callbacks/custom.py +2 -2
- meerschaum/api/dash/callbacks/dashboard.py +103 -19
- meerschaum/api/dash/callbacks/plugins.py +0 -1
- meerschaum/api/dash/callbacks/register.py +1 -1
- meerschaum/api/dash/callbacks/settings/__init__.py +1 -0
- meerschaum/api/dash/callbacks/settings/password_reset.py +2 -2
- meerschaum/api/dash/callbacks/settings/tokens.py +388 -0
- meerschaum/api/dash/components.py +30 -8
- meerschaum/api/dash/keys.py +19 -93
- meerschaum/api/dash/pages/dashboard.py +1 -20
- meerschaum/api/dash/pages/settings/__init__.py +1 -0
- meerschaum/api/dash/pages/settings/password_reset.py +1 -1
- meerschaum/api/dash/pages/settings/tokens.py +55 -0
- meerschaum/api/dash/pipes.py +94 -59
- meerschaum/api/dash/sessions.py +12 -0
- meerschaum/api/dash/tokens.py +606 -0
- meerschaum/api/dash/websockets.py +1 -1
- meerschaum/api/dash/webterm.py +4 -0
- meerschaum/api/models/__init__.py +23 -3
- meerschaum/api/models/_actions.py +22 -0
- meerschaum/api/models/_pipes.py +85 -7
- meerschaum/api/models/_tokens.py +81 -0
- meerschaum/api/resources/templates/termpage.html +12 -0
- meerschaum/api/routes/__init__.py +1 -0
- meerschaum/api/routes/_actions.py +3 -4
- meerschaum/api/routes/_connectors.py +3 -7
- meerschaum/api/routes/_jobs.py +14 -35
- meerschaum/api/routes/_login.py +49 -12
- meerschaum/api/routes/_misc.py +5 -10
- meerschaum/api/routes/_pipes.py +173 -140
- meerschaum/api/routes/_plugins.py +38 -28
- meerschaum/api/routes/_tokens.py +236 -0
- meerschaum/api/routes/_users.py +47 -35
- meerschaum/api/routes/_version.py +3 -3
- meerschaum/config/__init__.py +43 -20
- meerschaum/config/_default.py +43 -6
- meerschaum/config/_edit.py +28 -24
- meerschaum/config/_environment.py +1 -1
- meerschaum/config/_patch.py +6 -6
- meerschaum/config/_paths.py +5 -1
- meerschaum/config/_read_config.py +65 -34
- meerschaum/config/_sync.py +6 -3
- meerschaum/config/_version.py +1 -1
- meerschaum/config/stack/__init__.py +31 -11
- meerschaum/config/static.py +18 -0
- meerschaum/connectors/_Connector.py +10 -4
- meerschaum/connectors/__init__.py +4 -20
- meerschaum/connectors/api/_APIConnector.py +34 -6
- meerschaum/connectors/api/_actions.py +2 -2
- meerschaum/connectors/api/_jobs.py +1 -1
- meerschaum/connectors/api/_login.py +33 -7
- meerschaum/connectors/api/_misc.py +2 -2
- meerschaum/connectors/api/_pipes.py +16 -31
- meerschaum/connectors/api/_plugins.py +2 -2
- meerschaum/connectors/api/_request.py +1 -1
- meerschaum/connectors/api/_tokens.py +146 -0
- meerschaum/connectors/api/_users.py +70 -58
- meerschaum/connectors/instance/_InstanceConnector.py +83 -0
- meerschaum/connectors/instance/__init__.py +10 -0
- meerschaum/connectors/instance/_pipes.py +442 -0
- meerschaum/connectors/instance/_plugins.py +151 -0
- meerschaum/connectors/instance/_tokens.py +296 -0
- meerschaum/connectors/instance/_users.py +181 -0
- meerschaum/connectors/parse.py +4 -1
- meerschaum/connectors/sql/_SQLConnector.py +8 -5
- meerschaum/connectors/sql/_cli.py +12 -11
- meerschaum/connectors/sql/_create_engine.py +9 -168
- meerschaum/connectors/sql/_fetch.py +2 -18
- meerschaum/connectors/sql/_pipes.py +156 -190
- meerschaum/connectors/sql/_plugins.py +29 -0
- meerschaum/connectors/sql/_sql.py +46 -21
- meerschaum/connectors/sql/_users.py +29 -2
- meerschaum/connectors/sql/tables/__init__.py +1 -1
- meerschaum/connectors/valkey/_ValkeyConnector.py +2 -4
- meerschaum/connectors/valkey/_pipes.py +53 -26
- meerschaum/connectors/valkey/_plugins.py +2 -26
- meerschaum/core/Pipe/__init__.py +59 -19
- meerschaum/core/Pipe/_attributes.py +412 -90
- meerschaum/core/Pipe/_bootstrap.py +54 -24
- meerschaum/core/Pipe/_data.py +96 -18
- meerschaum/core/Pipe/_dtypes.py +48 -18
- meerschaum/core/Pipe/_edit.py +14 -4
- meerschaum/core/Pipe/_fetch.py +1 -1
- meerschaum/core/Pipe/_show.py +5 -5
- meerschaum/core/Pipe/_sync.py +118 -193
- meerschaum/core/Pipe/_verify.py +4 -4
- meerschaum/{plugins → core/Plugin}/_Plugin.py +9 -11
- meerschaum/core/Plugin/__init__.py +1 -1
- meerschaum/core/Token/_Token.py +220 -0
- meerschaum/core/Token/__init__.py +12 -0
- meerschaum/core/User/_User.py +34 -8
- meerschaum/core/User/__init__.py +9 -1
- meerschaum/core/__init__.py +1 -0
- meerschaum/jobs/_Job.py +3 -2
- meerschaum/jobs/__init__.py +3 -2
- meerschaum/jobs/systemd.py +1 -1
- meerschaum/models/__init__.py +35 -0
- meerschaum/models/pipes.py +247 -0
- meerschaum/models/tokens.py +38 -0
- meerschaum/models/users.py +26 -0
- meerschaum/plugins/__init__.py +22 -7
- meerschaum/plugins/bootstrap.py +2 -1
- meerschaum/utils/_get_pipes.py +68 -27
- meerschaum/utils/daemon/Daemon.py +2 -1
- meerschaum/utils/daemon/__init__.py +30 -2
- meerschaum/utils/dataframe.py +473 -81
- meerschaum/utils/debug.py +15 -15
- meerschaum/utils/dtypes/__init__.py +473 -34
- meerschaum/utils/dtypes/sql.py +368 -28
- meerschaum/utils/formatting/__init__.py +1 -1
- meerschaum/utils/formatting/_pipes.py +5 -4
- meerschaum/utils/formatting/_shell.py +11 -9
- meerschaum/utils/misc.py +246 -148
- meerschaum/utils/packages/__init__.py +10 -27
- meerschaum/utils/packages/_packages.py +41 -34
- meerschaum/utils/pipes.py +181 -0
- meerschaum/utils/process.py +1 -1
- meerschaum/utils/prompt.py +3 -1
- meerschaum/utils/schedule.py +2 -1
- meerschaum/utils/sql.py +121 -44
- meerschaum/utils/typing.py +1 -4
- meerschaum/utils/venv/_Venv.py +2 -2
- meerschaum/utils/venv/__init__.py +5 -7
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc2.dist-info}/METADATA +92 -96
- meerschaum-3.0.0rc2.dist-info/RECORD +283 -0
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc2.dist-info}/WHEEL +1 -1
- meerschaum-3.0.0rc2.dist-info/licenses/NOTICE +2 -0
- meerschaum/api/models/_interfaces.py +0 -15
- meerschaum/api/models/_locations.py +0 -15
- meerschaum/api/models/_metrics.py +0 -15
- meerschaum/config/static/__init__.py +0 -186
- meerschaum-2.9.5.dist-info/RECORD +0 -263
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc2.dist-info}/entry_points.txt +0 -0
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc2.dist-info}/licenses/LICENSE +0 -0
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc2.dist-info}/top_level.txt +0 -0
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc2.dist-info}/zip-safe +0 -0
meerschaum/actions/api.py
CHANGED
@@ -96,6 +96,7 @@ def _api_start(
|
|
96
96
|
workers: Optional[int] = None,
|
97
97
|
mrsm_instance: Optional[str] = None,
|
98
98
|
no_dash: bool = False,
|
99
|
+
no_webterm: bool = False,
|
99
100
|
no_auth: bool = False,
|
100
101
|
private: bool = False,
|
101
102
|
secure: bool = False,
|
@@ -166,7 +167,7 @@ def _api_start(
|
|
166
167
|
)
|
167
168
|
from meerschaum.config._patch import apply_patch_to_config
|
168
169
|
from meerschaum.config._environment import get_env_vars
|
169
|
-
from meerschaum.
|
170
|
+
from meerschaum._internal.static import STATIC_CONFIG, SERVER_ID
|
170
171
|
from meerschaum.connectors.parse import parse_instance_keys
|
171
172
|
from meerschaum.utils.pool import get_pool
|
172
173
|
|
@@ -244,6 +245,7 @@ def _api_start(
|
|
244
245
|
'env_file': str(uvicorn_env_path.as_posix()),
|
245
246
|
'mrsm_instance': mrsm_instance,
|
246
247
|
'no_dash': no_dash,
|
248
|
+
'no_webterm': no_webterm or no_auth,
|
247
249
|
'no_auth': no_auth,
|
248
250
|
'private': private,
|
249
251
|
'production': production,
|
@@ -263,7 +265,15 @@ def _api_start(
|
|
263
265
|
if secure:
|
264
266
|
cf['system']['api']['permissions']['actions']['non_admin'] = False
|
265
267
|
|
266
|
-
custom_keys = [
|
268
|
+
custom_keys = [
|
269
|
+
'mrsm_instance',
|
270
|
+
'no_dash',
|
271
|
+
'no_webterm',
|
272
|
+
'no_auth',
|
273
|
+
'private',
|
274
|
+
'debug',
|
275
|
+
'production',
|
276
|
+
]
|
267
277
|
|
268
278
|
### write config to a temporary file to communicate with uvicorn threads
|
269
279
|
try:
|
meerschaum/actions/bootstrap.py
CHANGED
@@ -10,7 +10,7 @@ Functions for bootstrapping elements
|
|
10
10
|
from __future__ import annotations
|
11
11
|
|
12
12
|
import meerschaum as mrsm
|
13
|
-
from meerschaum.utils.typing import Union, Any,
|
13
|
+
from meerschaum.utils.typing import Union, Any, SuccessTuple, Optional, Tuple, List
|
14
14
|
|
15
15
|
|
16
16
|
def bootstrap(
|
@@ -249,7 +249,7 @@ def _bootstrap_connectors(
|
|
249
249
|
from meerschaum.config._edit import write_config
|
250
250
|
from meerschaum.utils.formatting import pprint
|
251
251
|
from meerschaum.utils.formatting._shell import clear_screen
|
252
|
-
from meerschaum.
|
252
|
+
from meerschaum.config._default import CONNECTOR_ATTRIBUTES
|
253
253
|
from meerschaum.utils.warnings import warn, info
|
254
254
|
from meerschaum.utils.misc import is_int
|
255
255
|
|
@@ -314,7 +314,7 @@ def _bootstrap_connectors(
|
|
314
314
|
cls_required_attrs = getattr(cls, 'REQUIRED_ATTRIBUTES', [])
|
315
315
|
cls_optional_attrs = getattr(cls, 'OPTIONAL_ATTRIBUTES', [])
|
316
316
|
cls_default_attrs = getattr(cls, 'DEFAULT_ATTRIBUTES', {})
|
317
|
-
type_attributes =
|
317
|
+
type_attributes = CONNECTOR_ATTRIBUTES.get(
|
318
318
|
_type,
|
319
319
|
{
|
320
320
|
'required': cls_required_attrs,
|
@@ -330,16 +330,16 @@ def _bootstrap_connectors(
|
|
330
330
|
f"Flavor for connector '{_type}:{_label}':",
|
331
331
|
sorted(list(type_attributes['flavors'])),
|
332
332
|
default = (
|
333
|
-
'
|
334
|
-
if '
|
333
|
+
'postgresql'
|
334
|
+
if 'postgresql' in type_attributes['flavors']
|
335
335
|
else None
|
336
336
|
)
|
337
337
|
)
|
338
338
|
except KeyboardInterrupt:
|
339
339
|
return abort_tuple
|
340
340
|
new_attributes['flavor'] = flavor
|
341
|
-
required = sorted(list(
|
342
|
-
optional = sorted(list(
|
341
|
+
required = sorted(list(CONNECTOR_ATTRIBUTES[_type]['flavors'][flavor]['requirements']))
|
342
|
+
optional = sorted(list(CONNECTOR_ATTRIBUTES[_type]['flavors'][flavor].get('optional', {})))
|
343
343
|
default = type_attributes['flavors'][flavor].get('defaults', {})
|
344
344
|
else:
|
345
345
|
required = sorted(list(type_attributes.get('required', {})))
|
meerschaum/actions/edit.py
CHANGED
@@ -27,6 +27,7 @@ def edit(
|
|
27
27
|
'users' : _edit_users,
|
28
28
|
'plugins' : _edit_plugins,
|
29
29
|
'jobs' : _edit_jobs,
|
30
|
+
'tokens' : _edit_tokens,
|
30
31
|
}
|
31
32
|
return choose_subaction(action, options, **kw)
|
32
33
|
|
@@ -66,7 +67,7 @@ def _complete_edit(
|
|
66
67
|
return default_action_completer(action=(['edit'] + action), **kw)
|
67
68
|
|
68
69
|
|
69
|
-
def _edit_config(action: Optional[List[str]] = None, **kw
|
70
|
+
def _edit_config(action: Optional[List[str]] = None, **kw: Any) -> SuccessTuple:
|
70
71
|
"""
|
71
72
|
Edit Meerschaum configuration files.
|
72
73
|
|
@@ -104,14 +105,14 @@ def _complete_edit_config(action: Optional[List[str]] = None, **kw: Any) -> List
|
|
104
105
|
return possibilities
|
105
106
|
|
106
107
|
def _edit_pipes(
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
108
|
+
action: Optional[List[str]] = None,
|
109
|
+
params: Optional[Dict[str, Any]] = None,
|
110
|
+
yes: bool = False,
|
111
|
+
force: bool = False,
|
112
|
+
noask: bool = False,
|
113
|
+
debug: bool = False,
|
114
|
+
**kw: Any
|
115
|
+
) -> SuccessTuple:
|
115
116
|
"""
|
116
117
|
Open and edit pipes' configuration files.
|
117
118
|
|
@@ -218,7 +219,7 @@ def _edit_users(
|
|
218
219
|
from meerschaum.utils.prompt import prompt, yes_no, get_password, get_email
|
219
220
|
from meerschaum.utils.misc import edit_file
|
220
221
|
from meerschaum.config._paths import USERS_CACHE_RESOURCES_PATH
|
221
|
-
from meerschaum.
|
222
|
+
from meerschaum._internal.static import STATIC_CONFIG
|
222
223
|
from meerschaum.utils.yaml import yaml
|
223
224
|
import os, pathlib
|
224
225
|
instance_connector = parse_instance_keys(mrsm_instance)
|
@@ -235,7 +236,7 @@ def _edit_users(
|
|
235
236
|
minimum_length = STATIC_CONFIG['users']['min_password_length'],
|
236
237
|
)
|
237
238
|
|
238
|
-
|
239
|
+
### Make an admin
|
239
240
|
_type = ''
|
240
241
|
if yes_no(f"Change the user type for user '{username}'?", default='n', yes=yes, noask=noask):
|
241
242
|
is_admin = yes_no(
|
@@ -248,15 +249,34 @@ def _edit_users(
|
|
248
249
|
if yes_no(f"Change the email for user '{username}'?", default='n', yes=yes, noask=noask):
|
249
250
|
email = get_email(username)
|
250
251
|
|
252
|
+
attributes = {}
|
253
|
+
### Change the scopes.
|
254
|
+
attributes['scopes'] = prompt(
|
255
|
+
f"Scopes for user '{username}' (`*` to grant all scopes):",
|
256
|
+
default_editable=' '.join(User(
|
257
|
+
username,
|
258
|
+
instance=instance_connector,
|
259
|
+
).get_scopes(refresh=True, debug=debug)),
|
260
|
+
).split()
|
261
|
+
if attributes['scopes'] == ['*']:
|
262
|
+
attributes['scopes'] = list(STATIC_CONFIG['tokens']['scopes'])
|
263
|
+
|
251
264
|
### Change the attributes
|
252
|
-
attributes = None
|
253
265
|
if yes_no(
|
254
|
-
|
255
|
-
|
266
|
+
f"Edit the attributes YAML file for user '{username}'?",
|
267
|
+
default='n',
|
268
|
+
yes=yes,
|
269
|
+
noask=noask,
|
256
270
|
):
|
257
271
|
attr_path = pathlib.Path(os.path.join(USERS_CACHE_RESOURCES_PATH, f'{username}.yaml'))
|
258
272
|
try:
|
259
|
-
existing_attrs = instance_connector.get_user_attributes(
|
273
|
+
existing_attrs = instance_connector.get_user_attributes(
|
274
|
+
User(
|
275
|
+
username,
|
276
|
+
instance=instance_connector,
|
277
|
+
),
|
278
|
+
debug=debug,
|
279
|
+
)
|
260
280
|
with open(attr_path, 'w+') as f:
|
261
281
|
yaml.dump(existing_attrs, stream=f, sort_keys=False)
|
262
282
|
edit_file(attr_path)
|
@@ -271,7 +291,14 @@ def _edit_users(
|
|
271
291
|
attributes = None
|
272
292
|
|
273
293
|
### Submit changes
|
274
|
-
return User(
|
294
|
+
return User(
|
295
|
+
username,
|
296
|
+
password,
|
297
|
+
email=email,
|
298
|
+
type=_type,
|
299
|
+
attributes=attributes,
|
300
|
+
instance=instance_connector,
|
301
|
+
)
|
275
302
|
|
276
303
|
if not action:
|
277
304
|
return False, "No users to edit."
|
@@ -301,14 +328,14 @@ def _edit_users(
|
|
301
328
|
f" ({succeeded} succeeded, {failed} failed)."
|
302
329
|
)
|
303
330
|
info(msg)
|
304
|
-
return
|
331
|
+
return succeeded > 0, msg
|
305
332
|
|
306
333
|
|
307
334
|
def _edit_plugins(
|
308
335
|
action: Optional[List[str]] = None,
|
309
336
|
debug: bool = False,
|
310
337
|
**kwargs: Any
|
311
|
-
):
|
338
|
+
) -> mrsm.SuccessTuple:
|
312
339
|
"""
|
313
340
|
Edit a plugin's source code.
|
314
341
|
"""
|
@@ -507,6 +534,103 @@ def _edit_jobs(
|
|
507
534
|
return True, msg
|
508
535
|
|
509
536
|
|
537
|
+
def _edit_tokens(
|
538
|
+
action: Optional[List[str]] = None,
|
539
|
+
mrsm_instance: Optional[str] = None,
|
540
|
+
debug: bool = False,
|
541
|
+
) -> mrsm.SuccessTuple:
|
542
|
+
"""
|
543
|
+
Edit tokens registered to an instance.
|
544
|
+
"""
|
545
|
+
import uuid
|
546
|
+
from meerschaum.utils.warnings import warn
|
547
|
+
from meerschaum.utils.misc import is_uuid
|
548
|
+
from meerschaum.core import Token
|
549
|
+
from meerschaum.connectors.parse import parse_instance_keys
|
550
|
+
from meerschaum.utils.prompt import prompt, yes_no
|
551
|
+
from meerschaum._internal.static import STATIC_CONFIG
|
552
|
+
dateutil_parser = mrsm.attempt_import('dateutil.parser')
|
553
|
+
|
554
|
+
if not action:
|
555
|
+
return False, "Provide token labels or IDs for the tokens to edit."
|
556
|
+
|
557
|
+
conn = parse_instance_keys(mrsm_instance)
|
558
|
+
|
559
|
+
labels = [
|
560
|
+
label
|
561
|
+
for label in (action or [])
|
562
|
+
if not is_uuid(label)
|
563
|
+
]
|
564
|
+
potential_token_ids = [
|
565
|
+
uuid.UUID(potential_id)
|
566
|
+
for potential_id in (action or [])
|
567
|
+
if is_uuid(potential_id)
|
568
|
+
]
|
569
|
+
|
570
|
+
tokens = conn.get_tokens(
|
571
|
+
labels=(labels or None),
|
572
|
+
ids=(potential_token_ids or None),
|
573
|
+
debug=debug,
|
574
|
+
)
|
575
|
+
|
576
|
+
num_edited = 0
|
577
|
+
for token in tokens:
|
578
|
+
token_model = token.to_model(refresh=True)
|
579
|
+
if token_model is None:
|
580
|
+
warn(f"Token '{token.id}' does not exist.", stack=False)
|
581
|
+
continue
|
582
|
+
|
583
|
+
new_attrs = {}
|
584
|
+
|
585
|
+
new_attrs['label'] = prompt("Label:", default_editable=token_model.label)
|
586
|
+
new_expiration_str = prompt(
|
587
|
+
"Expiration (empty for no expiration):",
|
588
|
+
default_editable=('' if token.expiration is None else str(token_model.expiration)),
|
589
|
+
)
|
590
|
+
new_attrs['expiration'] = (
|
591
|
+
dateutil_parser.parse(new_expiration_str)
|
592
|
+
if new_expiration_str
|
593
|
+
else None
|
594
|
+
)
|
595
|
+
new_scopes_str = prompt(
|
596
|
+
"Scope (`*` to grant all permissions):",
|
597
|
+
default_editable=' '.join(token_model.scopes),
|
598
|
+
)
|
599
|
+
new_attrs['scopes'] = (
|
600
|
+
new_scopes_str.split(' ')
|
601
|
+
if new_scopes_str != '*'
|
602
|
+
else list(STATIC_CONFIG['tokens']['scopes'])
|
603
|
+
)
|
604
|
+
invalidate = (
|
605
|
+
yes_no("Do you want to invalidate this token?", default='n')
|
606
|
+
if token_model.is_valid
|
607
|
+
else True
|
608
|
+
)
|
609
|
+
new_attrs['is_valid'] = token_model.is_valid and not invalidate
|
610
|
+
|
611
|
+
new_token = Token(**{**dict(token_model), **new_attrs})
|
612
|
+
edit_success, edit_msg = new_token.edit(debug=debug)
|
613
|
+
if not edit_success:
|
614
|
+
return False, edit_msg
|
615
|
+
|
616
|
+
if invalidate:
|
617
|
+
invalidate_success, invalidate_msg = new_token.invalidate(debug=debug)
|
618
|
+
if not invalidate_success:
|
619
|
+
return False, invalidate_msg
|
620
|
+
|
621
|
+
num_edited += 1
|
622
|
+
|
623
|
+
msg = (
|
624
|
+
f"Successfully edited {num_edited} token"
|
625
|
+
+ ('s' if num_edited != 1 else '')
|
626
|
+
+ '.'
|
627
|
+
)
|
628
|
+
|
629
|
+
return True, msg
|
630
|
+
|
631
|
+
|
632
|
+
|
633
|
+
|
510
634
|
### NOTE: This must be the final statement of the module.
|
511
635
|
### Any subactions added below these lines will not
|
512
636
|
### be added to the `help` docstring.
|
meerschaum/actions/register.py
CHANGED
@@ -7,6 +7,9 @@ Register new Pipes. Requires the API to be running.
|
|
7
7
|
"""
|
8
8
|
|
9
9
|
from __future__ import annotations
|
10
|
+
|
11
|
+
from datetime import datetime, timedelta, timezone
|
12
|
+
|
10
13
|
import meerschaum as mrsm
|
11
14
|
from meerschaum.utils.typing import SuccessTuple, Any, List, Optional, Dict
|
12
15
|
|
@@ -26,6 +29,7 @@ def register(
|
|
26
29
|
'plugins' : _register_plugins,
|
27
30
|
'users' : _register_users,
|
28
31
|
'connectors': _register_connectors,
|
32
|
+
'tokens' : _register_tokens,
|
29
33
|
}
|
30
34
|
return choose_subaction(action, options, **kw)
|
31
35
|
|
@@ -175,11 +179,11 @@ def _register_plugins(
|
|
175
179
|
reload_plugins(debug=debug)
|
176
180
|
|
177
181
|
repo_connector = parse_repo_keys(repository)
|
178
|
-
if repo_connector.
|
182
|
+
if repo_connector.type != 'api':
|
179
183
|
return False, (
|
180
184
|
"Can only upload plugins to the Meerschaum API." +
|
181
|
-
f"Connector '{repo_connector}' is
|
182
|
-
f"'{
|
185
|
+
f"Connector '{repo_connector}' is " +
|
186
|
+
f"'{type(repo_connector)}'."
|
183
187
|
)
|
184
188
|
|
185
189
|
if len(action) == 0 or action == ['']:
|
@@ -272,6 +276,7 @@ def _complete_register_plugins(*args, **kw):
|
|
272
276
|
def _register_users(
|
273
277
|
action: Optional[List[str]] = None,
|
274
278
|
mrsm_instance: Optional[str] = None,
|
279
|
+
scopes: Optional[List[str]] = None,
|
275
280
|
shell: bool = False,
|
276
281
|
debug: bool = False,
|
277
282
|
**kw: Any
|
@@ -280,7 +285,7 @@ def _register_users(
|
|
280
285
|
Register a new user to a Meerschaum instance.
|
281
286
|
"""
|
282
287
|
from meerschaum.config import get_config
|
283
|
-
from meerschaum.
|
288
|
+
from meerschaum._internal.static import STATIC_CONFIG
|
284
289
|
from meerschaum.connectors.parse import parse_instance_keys
|
285
290
|
from meerschaum.utils.warnings import warn, info
|
286
291
|
from meerschaum.core import User
|
@@ -333,9 +338,17 @@ def _register_users(
|
|
333
338
|
)
|
334
339
|
if len(email) == 0:
|
335
340
|
email = None
|
336
|
-
user = User(
|
341
|
+
user = User(
|
342
|
+
username,
|
343
|
+
password,
|
344
|
+
email=email,
|
345
|
+
attributes={
|
346
|
+
'scopes': scopes or list(STATIC_CONFIG['tokens']['scopes']),
|
347
|
+
},
|
348
|
+
instance=instance_connector,
|
349
|
+
)
|
337
350
|
info(f"Registering user '{user}' to Meerschaum instance '{instance_connector}'...")
|
338
|
-
result_tuple =
|
351
|
+
result_tuple = user.register(debug=debug)
|
339
352
|
print_tuple(result_tuple)
|
340
353
|
success[username] = result_tuple[0]
|
341
354
|
if success[username]:
|
@@ -443,6 +456,124 @@ def _complete_register_connectors(
|
|
443
456
|
return _complete_show_connectors(action)
|
444
457
|
|
445
458
|
|
459
|
+
def _register_tokens(
|
460
|
+
mrsm_instance: Optional[str] = None,
|
461
|
+
name: Optional[str] = None,
|
462
|
+
ttl_days: Optional[int] = None,
|
463
|
+
scopes: Optional[List[str]] = None,
|
464
|
+
end: Optional[datetime] = None,
|
465
|
+
force: bool = False,
|
466
|
+
yes: bool = False,
|
467
|
+
noask: bool = False,
|
468
|
+
nopretty: bool = False,
|
469
|
+
debug: bool = False,
|
470
|
+
**kwargs: Any
|
471
|
+
) -> mrsm.SuccessTuple:
|
472
|
+
"""
|
473
|
+
Register a new long-lived access token for API access.
|
474
|
+
Note that omitting and end time or TTL will generate token which does not expire.
|
475
|
+
|
476
|
+
Examples:
|
477
|
+
|
478
|
+
mrsm register token --end 2032-01-01
|
479
|
+
|
480
|
+
mrsm register token --ttl-days 1000
|
481
|
+
|
482
|
+
mrsm register token --name weather-sensor
|
483
|
+
|
484
|
+
"""
|
485
|
+
import json
|
486
|
+
from meerschaum.utils.schedule import parse_start_time
|
487
|
+
from meerschaum.core import User
|
488
|
+
from meerschaum.core.Token._Token import Token, _PLACEHOLDER_EXPIRATION
|
489
|
+
from meerschaum.connectors.parse import parse_instance_keys
|
490
|
+
from meerschaum.utils.prompt import yes_no, choose
|
491
|
+
from meerschaum.utils.formatting import make_header, print_tuple
|
492
|
+
from meerschaum.utils.dtypes import value_is_null
|
493
|
+
|
494
|
+
expiration = (
|
495
|
+
end
|
496
|
+
if end is not None
|
497
|
+
else (
|
498
|
+
parse_start_time(f"starting in {ttl_days} days")
|
499
|
+
if ttl_days
|
500
|
+
else _PLACEHOLDER_EXPIRATION
|
501
|
+
)
|
502
|
+
)
|
503
|
+
|
504
|
+
instance_connector = parse_instance_keys(mrsm_instance)
|
505
|
+
user = None
|
506
|
+
if instance_connector.type != 'api':
|
507
|
+
usernames = instance_connector.get_users(debug=debug)
|
508
|
+
if not usernames:
|
509
|
+
return False, f"No users are registered to '{instance_connector}'."
|
510
|
+
|
511
|
+
username = choose(
|
512
|
+
"To which user should this token be registered? Enter the number.",
|
513
|
+
usernames,
|
514
|
+
)
|
515
|
+
user_id = instance_connector.get_user_id(
|
516
|
+
User(username, instance=mrsm_instance),
|
517
|
+
debug=debug,
|
518
|
+
)
|
519
|
+
if user_id is None:
|
520
|
+
return False, f"Cannot load ID for user '{username}'."
|
521
|
+
|
522
|
+
user = User(username, user_id=user_id, instance=mrsm_instance)
|
523
|
+
|
524
|
+
token = Token(
|
525
|
+
label=name,
|
526
|
+
expiration=expiration,
|
527
|
+
scopes=scopes,
|
528
|
+
user=user,
|
529
|
+
instance=mrsm_instance,
|
530
|
+
)
|
531
|
+
|
532
|
+
register_success, register_msg = token.register(debug=debug)
|
533
|
+
token_id = token.id
|
534
|
+
token_secret = token.secret
|
535
|
+
if not register_success:
|
536
|
+
return False, f"Failed to register token '{token.label}':\n{register_msg}"
|
537
|
+
|
538
|
+
token_model = token.to_model(refresh=True)
|
539
|
+
token_kwargs = dict(token_model)
|
540
|
+
token_kwargs['id'] = token_id
|
541
|
+
token_kwargs['secret'] = token_secret
|
542
|
+
token = Token(**token_kwargs)
|
543
|
+
|
544
|
+
if not nopretty:
|
545
|
+
print_tuple(
|
546
|
+
(
|
547
|
+
True,
|
548
|
+
(
|
549
|
+
f"Registered token '{token}'.\n "
|
550
|
+
"Write down the client secret, because it won't be shown again."
|
551
|
+
)
|
552
|
+
),
|
553
|
+
calm=True
|
554
|
+
)
|
555
|
+
|
556
|
+
msg_to_print = (
|
557
|
+
(
|
558
|
+
make_header(f"Attributes for token '{token}':") + "\n"
|
559
|
+
+ f" - Client ID: {token_model.id}\n"
|
560
|
+
+ f" - Client Secret: {token_secret}\n"
|
561
|
+
+ " - Expiration: "
|
562
|
+
+ (
|
563
|
+
token.expiration.isoformat()
|
564
|
+
if not value_is_null(token.expiration)
|
565
|
+
else 'Does not expire.'
|
566
|
+
)
|
567
|
+
+ "\n"
|
568
|
+
+ f" - API Key: {token.get_api_key()}\n"
|
569
|
+
)
|
570
|
+
if not nopretty
|
571
|
+
else json.dumps({'secret': token_secret, **dict(token_model)})
|
572
|
+
)
|
573
|
+
print(msg_to_print)
|
574
|
+
return True, f"Successfully registered token '{token.label}'."
|
575
|
+
|
576
|
+
|
446
577
|
### NOTE: This must be the final statement of the module.
|
447
578
|
### Any subactions added below these lines will not
|
448
579
|
### be added to the `help` docstring.
|