meerschaum 2.9.4__py3-none-any.whl → 3.0.0rc1__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 +17 -1
- meerschaum/_internal/entry.py +6 -6
- meerschaum/_internal/shell/Shell.py +1 -1
- meerschaum/_internal/static.py +372 -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/api/__init__.py +9 -2
- meerschaum/api/_events.py +39 -2
- meerschaum/api/_oauth2.py +118 -8
- meerschaum/api/_tokens.py +102 -0
- meerschaum/api/dash/__init__.py +0 -1
- meerschaum/api/dash/callbacks/custom.py +2 -2
- meerschaum/api/dash/callbacks/dashboard.py +133 -18
- 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 +156 -58
- 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/static/css/dash.css +16 -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 +134 -111
- 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 +32 -5
- 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 +24 -5
- 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 +15 -14
- 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 +6 -154
- meerschaum/connectors/sql/_fetch.py +2 -18
- meerschaum/connectors/sql/_pipes.py +42 -31
- meerschaum/connectors/sql/_plugins.py +29 -0
- meerschaum/connectors/sql/_sql.py +9 -2
- 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 +9 -10
- meerschaum/connectors/valkey/_plugins.py +2 -26
- meerschaum/core/Pipe/__init__.py +31 -14
- meerschaum/core/Pipe/_attributes.py +156 -58
- meerschaum/core/Pipe/_bootstrap.py +54 -24
- meerschaum/core/Pipe/_data.py +41 -1
- meerschaum/core/Pipe/_dtypes.py +29 -14
- meerschaum/core/Pipe/_edit.py +12 -4
- meerschaum/core/Pipe/_show.py +5 -5
- meerschaum/core/Pipe/_sync.py +48 -53
- meerschaum/core/Pipe/_verify.py +1 -1
- meerschaum/{plugins → core/Plugin}/_Plugin.py +9 -11
- meerschaum/core/Plugin/__init__.py +1 -1
- meerschaum/core/Token/_Token.py +221 -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 +96 -15
- meerschaum/utils/dtypes/__init__.py +93 -21
- meerschaum/utils/dtypes/sql.py +44 -0
- 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 +237 -80
- meerschaum/utils/packages/__init__.py +3 -6
- meerschaum/utils/packages/_packages.py +34 -32
- meerschaum/utils/pipes.py +181 -0
- meerschaum/utils/process.py +1 -1
- meerschaum/utils/prompt.py +3 -1
- meerschaum/utils/schedule.py +1 -0
- meerschaum/utils/sql.py +115 -39
- meerschaum/utils/typing.py +1 -4
- meerschaum/utils/venv/_Venv.py +2 -2
- meerschaum/utils/venv/__init__.py +5 -7
- {meerschaum-2.9.4.dist-info → meerschaum-3.0.0rc1.dist-info}/METADATA +88 -80
- meerschaum-3.0.0rc1.dist-info/RECORD +282 -0
- {meerschaum-2.9.4.dist-info → meerschaum-3.0.0rc1.dist-info}/WHEEL +1 -1
- 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.4.dist-info/RECORD +0 -263
- {meerschaum-2.9.4.dist-info → meerschaum-3.0.0rc1.dist-info}/entry_points.txt +0 -0
- {meerschaum-2.9.4.dist-info → meerschaum-3.0.0rc1.dist-info}/licenses/LICENSE +0 -0
- {meerschaum-2.9.4.dist-info → meerschaum-3.0.0rc1.dist-info}/top_level.txt +0 -0
- {meerschaum-2.9.4.dist-info → meerschaum-3.0.0rc1.dist-info}/zip-safe +0 -0
meerschaum/api/__init__.py
CHANGED
@@ -14,7 +14,7 @@ from fnmatch import fnmatch
|
|
14
14
|
import meerschaum as mrsm
|
15
15
|
from meerschaum.utils.typing import Dict, Any, Optional, PipesDict
|
16
16
|
from meerschaum.config import get_config
|
17
|
-
from meerschaum.
|
17
|
+
from meerschaum._internal.static import STATIC_CONFIG, SERVER_ID
|
18
18
|
from meerschaum.utils.packages import attempt_import
|
19
19
|
from meerschaum.utils import get_pipes as _get_pipes
|
20
20
|
from meerschaum.config._paths import API_UVICORN_CONFIG_PATH, API_UVICORN_RESOURCES_PATH
|
@@ -95,10 +95,12 @@ def get_uvicorn_config() -> Dict[str, Any]:
|
|
95
95
|
|
96
96
|
debug = get_uvicorn_config().get('debug', False)
|
97
97
|
no_dash = get_uvicorn_config().get('no_dash', False)
|
98
|
+
no_webterm = get_uvicorn_config().get('no_webterm', False)
|
98
99
|
no_auth = get_uvicorn_config().get('no_auth', False)
|
99
100
|
private = get_uvicorn_config().get('private', False)
|
100
101
|
production = get_uvicorn_config().get('production', False)
|
101
102
|
_include_dash = (not no_dash)
|
103
|
+
_include_webterm = (not no_webterm) and _include_dash
|
102
104
|
docs_enabled = not production or sys_config.get('endpoints', {}).get('docs_in_production', True)
|
103
105
|
|
104
106
|
default_instance_keys = None
|
@@ -210,6 +212,11 @@ def get_pipe(
|
|
210
212
|
if location_key in ('[None]', 'None', 'null'):
|
211
213
|
location_key = None
|
212
214
|
instance_keys = str(get_api_connector(instance_keys))
|
215
|
+
if connector_keys == 'mrsm':
|
216
|
+
raise fastapi.HTTPException(
|
217
|
+
status_code=403,
|
218
|
+
detail="Unable to serve any pipes with connector keys `mrsm` over the API.",
|
219
|
+
)
|
213
220
|
pipe = mrsm.Pipe(connector_keys, metric_key, location_key, mrsm_instance=instance_keys)
|
214
221
|
if is_pipe_registered(pipe, pipes(instance_keys)):
|
215
222
|
return pipes(instance_keys, refresh=refresh)[connector_keys][metric_key][location_key]
|
@@ -291,7 +298,7 @@ def __getattr__(name: str):
|
|
291
298
|
raise AttributeError(f"Could not import '{name}'.")
|
292
299
|
|
293
300
|
### Import everything else within the API.
|
294
|
-
from meerschaum.api._oauth2 import manager
|
301
|
+
from meerschaum.api._oauth2 import manager, ScopedAuth
|
295
302
|
import meerschaum.api.routes as routes
|
296
303
|
import meerschaum.api._events
|
297
304
|
import meerschaum.api._websockets
|
meerschaum/api/_events.py
CHANGED
@@ -16,6 +16,8 @@ from meerschaum.api import (
|
|
16
16
|
get_uvicorn_config,
|
17
17
|
debug,
|
18
18
|
no_dash,
|
19
|
+
_include_dash,
|
20
|
+
_include_webterm,
|
19
21
|
)
|
20
22
|
from meerschaum.utils.debug import dprint
|
21
23
|
from meerschaum.connectors.poll import retry_connect
|
@@ -25,7 +27,7 @@ from meerschaum.jobs import (
|
|
25
27
|
start_check_jobs_thread,
|
26
28
|
stop_check_jobs_thread,
|
27
29
|
)
|
28
|
-
from meerschaum.
|
30
|
+
from meerschaum._internal.static import STATIC_CONFIG
|
29
31
|
|
30
32
|
TEMP_PREFIX: str = STATIC_CONFIG['api']['jobs']['temp_prefix']
|
31
33
|
|
@@ -35,8 +37,43 @@ async def startup():
|
|
35
37
|
"""
|
36
38
|
Connect to the instance database and begin monitoring jobs.
|
37
39
|
"""
|
40
|
+
app.openapi_schema = app.openapi()
|
41
|
+
|
42
|
+
### Remove the implicitly added HTTPBearer scheme if it exists.
|
43
|
+
if 'BearerAuth' in app.openapi_schema['components']['securitySchemes']:
|
44
|
+
del app.openapi_schema['components']['securitySchemes']['BearerAuth']
|
45
|
+
elif 'HTTPBearer' in app.openapi_schema['components']['securitySchemes']:
|
46
|
+
del app.openapi_schema['components']['securitySchemes']['HTTPBearer']
|
47
|
+
if 'LoginManager' in app.openapi_schema['components']['securitySchemes']:
|
48
|
+
del app.openapi_schema['components']['securitySchemes']['LoginManager']
|
49
|
+
|
50
|
+
scopes = STATIC_CONFIG['tokens']['scopes']
|
51
|
+
app.openapi_schema['components']['securitySchemes']['OAuth2PasswordBearer'] = {
|
52
|
+
'type': 'oauth2',
|
53
|
+
'flows': {
|
54
|
+
'password': {
|
55
|
+
'tokenUrl': STATIC_CONFIG['api']['endpoints']['login'],
|
56
|
+
'scopes': scopes,
|
57
|
+
},
|
58
|
+
},
|
59
|
+
}
|
60
|
+
app.openapi_schema['components']['securitySchemes']['APIKey'] = {
|
61
|
+
'type': 'http',
|
62
|
+
'scheme': 'bearer',
|
63
|
+
'bearerFormat': 'mrsm-key:{client_id}:{client_secret}',
|
64
|
+
'description': 'Authentication using a Meerschaum API Key.',
|
65
|
+
}
|
66
|
+
app.openapi_schema['security'] = [
|
67
|
+
{
|
68
|
+
'OAuth2PasswordBearer': [],
|
69
|
+
},
|
70
|
+
{
|
71
|
+
'APIKey': [],
|
72
|
+
}
|
73
|
+
]
|
74
|
+
|
38
75
|
try:
|
39
|
-
if
|
76
|
+
if _include_webterm:
|
40
77
|
from meerschaum.api.dash.webterm import start_webterm
|
41
78
|
start_webterm()
|
42
79
|
|
meerschaum/api/_oauth2.py
CHANGED
@@ -7,21 +7,34 @@ Define JWT authorization here.
|
|
7
7
|
"""
|
8
8
|
|
9
9
|
import os
|
10
|
-
|
10
|
+
import base64
|
11
|
+
import functools
|
12
|
+
import inspect
|
13
|
+
from typing import List, Optional, Union
|
14
|
+
|
15
|
+
from meerschaum.api import endpoints, CHECK_UPDATE, no_auth, debug
|
16
|
+
from meerschaum.api._tokens import optional_token, get_token_from_authorization
|
17
|
+
from meerschaum._internal.static import STATIC_CONFIG
|
11
18
|
from meerschaum.utils.packages import attempt_import
|
12
|
-
|
19
|
+
from meerschaum.core import User, Token
|
20
|
+
|
21
|
+
fastapi, starlette = attempt_import('fastapi', 'starlette', lazy=False, check_update=CHECK_UPDATE)
|
13
22
|
fastapi_responses = attempt_import('fastapi.responses', lazy=False, check_update=CHECK_UPDATE)
|
14
23
|
fastapi_login = attempt_import('fastapi_login', check_update=CHECK_UPDATE)
|
24
|
+
from fastapi import Depends, HTTPException, Request
|
25
|
+
from starlette import status
|
26
|
+
|
15
27
|
|
16
28
|
class CustomOAuth2PasswordRequestForm:
|
17
29
|
def __init__(
|
18
30
|
self,
|
19
31
|
grant_type: str = fastapi.Form(None, regex="password|client_credentials"),
|
20
|
-
username: str = fastapi.Form(
|
21
|
-
password: str = fastapi.Form(
|
22
|
-
scope: str = fastapi.Form(""),
|
23
|
-
client_id: str = fastapi.Form(None),
|
24
|
-
client_secret: str = fastapi.Form(None),
|
32
|
+
username: Optional[str] = fastapi.Form(None),
|
33
|
+
password: Optional[str] = fastapi.Form(None),
|
34
|
+
scope: str = fastapi.Form(" ".join(STATIC_CONFIG['tokens']['scopes'])),
|
35
|
+
client_id: Optional[str] = fastapi.Form(None),
|
36
|
+
client_secret: Optional[str] = fastapi.Form(None),
|
37
|
+
authorization: Optional[str] = fastapi.Header(None),
|
25
38
|
):
|
26
39
|
self.grant_type = grant_type
|
27
40
|
self.username = username
|
@@ -30,9 +43,106 @@ class CustomOAuth2PasswordRequestForm:
|
|
30
43
|
self.client_id = client_id
|
31
44
|
self.client_secret = client_secret
|
32
45
|
|
46
|
+
if (
|
47
|
+
not username
|
48
|
+
and not password
|
49
|
+
and not self.client_id
|
50
|
+
and not self.client_secret
|
51
|
+
and authorization
|
52
|
+
):
|
53
|
+
try:
|
54
|
+
scheme, credentials = authorization.split()
|
55
|
+
if credentials.startswith('mrsm-key:'):
|
56
|
+
credentials = credentials[len('mrsm-key:'):]
|
57
|
+
if scheme.lower() in ('basic', 'bearer'):
|
58
|
+
decoded_credentials = base64.b64decode(credentials).decode('utf-8')
|
59
|
+
_client_id, _client_secret = decoded_credentials.split(':', 1)
|
60
|
+
self.client_id = _client_id
|
61
|
+
self.client_secret = _client_secret
|
62
|
+
self.grant_type = 'client_credentials'
|
63
|
+
except ValueError:
|
64
|
+
pass
|
65
|
+
|
66
|
+
|
67
|
+
async def optional_user(request: Request) -> Optional[User]:
|
68
|
+
"""
|
69
|
+
FastAPI dependency that returns a User if logged in, otherwise None.
|
70
|
+
"""
|
71
|
+
if no_auth:
|
72
|
+
return None
|
73
|
+
return await manager(request)
|
74
|
+
try:
|
75
|
+
return await manager(request)
|
76
|
+
except HTTPException:
|
77
|
+
return None
|
78
|
+
|
79
|
+
|
80
|
+
async def load_user_or_token(
|
81
|
+
request: Request,
|
82
|
+
users: bool = True,
|
83
|
+
tokens: bool = True,
|
84
|
+
) -> Union[User, Token, None]:
|
85
|
+
"""
|
86
|
+
Load the current user or token.
|
87
|
+
"""
|
88
|
+
authorization = request.headers.get('authorization', request.headers.get('Authorization', None))
|
89
|
+
if not authorization:
|
90
|
+
raise HTTPException(
|
91
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
92
|
+
detail="Not authenticated.",
|
93
|
+
)
|
94
|
+
authorization = authorization.replace('Basic ', '').replace('Bearer ', '')
|
95
|
+
if not authorization.startswith('mrsm-key:'):
|
96
|
+
if not users:
|
97
|
+
raise HTTPException(
|
98
|
+
status=status.HTTP_401_UNAUTHORIZED,
|
99
|
+
detail="Users not authenticated for this endpoint.",
|
100
|
+
)
|
101
|
+
return await manager(request)
|
102
|
+
if not tokens:
|
103
|
+
raise HTTPException(
|
104
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
105
|
+
detail="Tokens not authenticated for this endpoint.",
|
106
|
+
)
|
107
|
+
return get_token_from_authorization(authorization)
|
108
|
+
|
109
|
+
|
110
|
+
def ScopedAuth(scopes: List[str]):
|
111
|
+
"""
|
112
|
+
Dependency factory for authenticating with either a user session or a scoped token.
|
113
|
+
"""
|
114
|
+
async def _authenticate(
|
115
|
+
user_or_token: Union[User, Token, None] = Depends(
|
116
|
+
load_user_or_token,
|
117
|
+
),
|
118
|
+
) -> Union[User, Token, None]:
|
119
|
+
if no_auth:
|
120
|
+
return None
|
121
|
+
|
122
|
+
if not user_or_token:
|
123
|
+
raise HTTPException(
|
124
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
125
|
+
detail="Not authenticated.",
|
126
|
+
headers={"WWW-Authenticate": "Basic"},
|
127
|
+
)
|
128
|
+
|
129
|
+
fresh_scopes = user_or_token.get_scopes(refresh=True, debug=debug)
|
130
|
+
if '*' in fresh_scopes:
|
131
|
+
return user_or_token
|
132
|
+
|
133
|
+
for scope in scopes:
|
134
|
+
if scope not in fresh_scopes:
|
135
|
+
raise HTTPException(
|
136
|
+
status_code=status.HTTP_403_FORBIDDEN,
|
137
|
+
detail=f"Missing required scope: '{scope}'",
|
138
|
+
)
|
139
|
+
|
140
|
+
return user_or_token
|
141
|
+
return _authenticate
|
142
|
+
|
33
143
|
|
34
144
|
LoginManager = fastapi_login.LoginManager
|
35
|
-
def generate_secret_key() ->
|
145
|
+
def generate_secret_key() -> bytes:
|
36
146
|
"""
|
37
147
|
Read or generate the secret keyfile.
|
38
148
|
"""
|
@@ -0,0 +1,102 @@
|
|
1
|
+
#! /usr/bin/env python3
|
2
|
+
# vim:fenc=utf-8
|
3
|
+
|
4
|
+
"""
|
5
|
+
Define the authentication logic for Meerschaum Tokens.
|
6
|
+
"""
|
7
|
+
|
8
|
+
import base64
|
9
|
+
import uuid
|
10
|
+
from typing import Optional, Union, List
|
11
|
+
from datetime import datetime, timezone
|
12
|
+
|
13
|
+
from fastapi import Depends, HTTPException, Request
|
14
|
+
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
15
|
+
from starlette import status
|
16
|
+
|
17
|
+
import meerschaum as mrsm
|
18
|
+
from meerschaum.api import (
|
19
|
+
get_api_connector,
|
20
|
+
debug,
|
21
|
+
)
|
22
|
+
from meerschaum.core import Token, User
|
23
|
+
from meerschaum.core.User import verify_password
|
24
|
+
|
25
|
+
|
26
|
+
http_bearer = HTTPBearer(auto_error=False, scheme_name="APIKey")
|
27
|
+
|
28
|
+
|
29
|
+
def get_token_from_authorization(authorization: str) -> Token:
|
30
|
+
"""
|
31
|
+
Helper function to decode and verify a token from credentials.
|
32
|
+
Raises HTTPException on failure.
|
33
|
+
"""
|
34
|
+
if authorization.startswith('mrsm-key:'):
|
35
|
+
authorization = authorization[len('mrsm-key:'):]
|
36
|
+
try:
|
37
|
+
credential_string = base64.b64decode(authorization).decode('utf-8')
|
38
|
+
token_id_str, secret = credential_string.split(':', 1)
|
39
|
+
token_id = uuid.UUID(token_id_str)
|
40
|
+
except Exception:
|
41
|
+
raise HTTPException(
|
42
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
43
|
+
detail="Invalid token format. Expected Base64-encoded 'token_id:secret'.",
|
44
|
+
headers={"WWW-Authenticate": "Bearer"},
|
45
|
+
)
|
46
|
+
|
47
|
+
conn = get_api_connector()
|
48
|
+
token = conn.get_token(token_id)
|
49
|
+
|
50
|
+
if not token or not token.is_valid:
|
51
|
+
raise HTTPException(
|
52
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
53
|
+
detail="Invalid or revoked token.",
|
54
|
+
headers={"WWW-Authenticate": "Bearer"},
|
55
|
+
)
|
56
|
+
|
57
|
+
if token.get_expiration_status(debug=debug):
|
58
|
+
raise HTTPException(
|
59
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
60
|
+
detail="Token has expired.",
|
61
|
+
headers={"WWW-Authenticate": "Bearer"},
|
62
|
+
)
|
63
|
+
|
64
|
+
if not verify_password(secret, token.secret_hash):
|
65
|
+
raise HTTPException(
|
66
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
67
|
+
detail="Invalid secret.",
|
68
|
+
headers={"WWW-Authenticate": "Bearer"},
|
69
|
+
)
|
70
|
+
|
71
|
+
return token
|
72
|
+
|
73
|
+
|
74
|
+
def get_current_token(
|
75
|
+
auth_creds: Optional[HTTPAuthorizationCredentials] = Depends(http_bearer),
|
76
|
+
) -> Token:
|
77
|
+
"""
|
78
|
+
FastAPI dependency to authenticate a request with a Meerschaum Token.
|
79
|
+
This dependency will fail if no token is provided.
|
80
|
+
"""
|
81
|
+
if auth_creds is None:
|
82
|
+
raise HTTPException(
|
83
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
84
|
+
detail="Not authenticated.",
|
85
|
+
headers={"WWW-Authenticate": "Bearer"},
|
86
|
+
)
|
87
|
+
return get_token_from_authorization(auth_creds.credentials)
|
88
|
+
|
89
|
+
|
90
|
+
async def optional_token(
|
91
|
+
auth_creds: Optional[HTTPAuthorizationCredentials] = Depends(http_bearer),
|
92
|
+
) -> Optional[Token]:
|
93
|
+
"""
|
94
|
+
FastAPI dependency that returns a Token if provided, otherwise None.
|
95
|
+
"""
|
96
|
+
if not auth_creds:
|
97
|
+
return None
|
98
|
+
|
99
|
+
try:
|
100
|
+
return get_token_from_authorization(auth_creds.credentials)
|
101
|
+
except HTTPException as e:
|
102
|
+
return None
|
meerschaum/api/dash/__init__.py
CHANGED
@@ -19,7 +19,6 @@ _monkey_patch_get_distribution('flask-compress', flask_compress.__version__)
|
|
19
19
|
dash = attempt_import('dash', lazy=False)
|
20
20
|
|
21
21
|
from meerschaum.utils.typing import List, Optional
|
22
|
-
from meerschaum.config.static import _static_config
|
23
22
|
from meerschaum.api import (
|
24
23
|
app as fastapi_app,
|
25
24
|
debug,
|
@@ -36,9 +36,9 @@ def add_plugin_pages(debug: bool = False):
|
|
36
36
|
"""
|
37
37
|
Allow users to add pages via the `@web_page` decorator.
|
38
38
|
"""
|
39
|
-
for
|
39
|
+
for page_group, pages_dicts in _plugin_endpoints_to_pages.items():
|
40
40
|
if debug:
|
41
|
-
dprint(f"Adding pages
|
41
|
+
dprint(f"Adding pages for group '{page_group}'...")
|
42
42
|
for _endpoint, _page_dict in pages_dicts.items():
|
43
43
|
page_layout = _page_dict['function']()
|
44
44
|
if not _page_dict['skip_navbar']:
|