zrb 1.7.4__py3-none-any.whl → 1.8.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.
- zrb/__init__.py +2 -2
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.amber.min.css +3 -3
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.blue.min.css +3 -3
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.cyan.min.css +3 -3
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.fuchsia.min.css +3 -3
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.green.min.css +3 -3
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.grey.min.css +3 -3
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.indigo.min.css +3 -3
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.jade.min.css +3 -3
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.lime.min.css +3 -3
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.min.css +3 -3
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.orange.min.css +3 -3
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.pink.min.css +3 -3
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.pumpkin.min.css +3 -3
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.purple.min.css +3 -3
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.red.min.css +3 -3
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.sand.min.css +3 -3
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.slate.min.css +3 -3
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.violet.min.css +3 -3
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.yellow.min.css +3 -3
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.zinc.min.css +3 -3
- zrb/config.py +30 -0
- zrb/llm_config.py +2 -2
- zrb/runner/cli.py +2 -3
- zrb/runner/web_app.py +17 -14
- zrb/runner/web_auth_config.py +189 -0
- zrb/runner/web_route/docs_route.py +4 -2
- zrb/runner/web_route/error_page/serve_default_404.py +3 -3
- zrb/runner/web_route/error_page/show_error_page.py +2 -1
- zrb/runner/web_route/error_page/view.html +22 -39
- zrb/runner/web_route/home_page/home_page_route.py +5 -4
- zrb/runner/web_route/login_api_route.py +4 -4
- zrb/runner/web_route/login_page/login_page_route.py +11 -6
- zrb/runner/web_route/logout_api_route.py +4 -4
- zrb/runner/web_route/logout_page/logout_page_route.py +11 -6
- zrb/runner/web_route/node_page/group/show_group_page.py +2 -1
- zrb/runner/web_route/node_page/node_page_route.py +3 -3
- zrb/runner/web_route/node_page/task/show_task_page.py +2 -1
- zrb/runner/web_route/refresh_token_api_route.py +5 -5
- zrb/runner/web_route/static/global_template.html +6 -3
- zrb/runner/web_route/static/resources/pico-css/pico.amber.min.css +4 -0
- zrb/runner/web_route/static/resources/pico-css/pico.blue.min.css +4 -0
- zrb/runner/web_route/static/resources/pico-css/pico.cyan.min.css +4 -0
- zrb/runner/web_route/static/resources/pico-css/pico.fuchsia.min.css +4 -0
- zrb/runner/web_route/static/resources/pico-css/pico.green.min.css +4 -0
- zrb/runner/web_route/static/resources/pico-css/pico.grey.min.css +4 -0
- zrb/runner/web_route/static/resources/pico-css/pico.indigo.min.css +4 -0
- zrb/runner/web_route/static/resources/pico-css/pico.jade.min.css +4 -0
- zrb/runner/web_route/static/resources/pico-css/pico.lime.min.css +4 -0
- zrb/runner/web_route/static/resources/pico-css/pico.min.css +4 -0
- zrb/runner/web_route/static/resources/pico-css/pico.orange.min.css +4 -0
- zrb/runner/web_route/static/resources/pico-css/pico.pink.min.css +4 -0
- zrb/runner/web_route/static/resources/pico-css/pico.pumpkin.min.css +4 -0
- zrb/runner/web_route/static/resources/pico-css/pico.purple.min.css +4 -0
- zrb/runner/web_route/static/resources/pico-css/pico.red.min.css +4 -0
- zrb/runner/web_route/static/resources/pico-css/pico.sand.min.css +4 -0
- zrb/runner/web_route/static/resources/pico-css/pico.slate.min.css +4 -0
- zrb/runner/web_route/static/resources/pico-css/pico.violet.min.css +4 -0
- zrb/runner/web_route/static/resources/pico-css/pico.yellow.min.css +4 -0
- zrb/runner/web_route/static/resources/pico-css/pico.zinc.min.css +4 -0
- zrb/runner/web_route/static/static_route.py +3 -3
- zrb/runner/web_route/task_input_api_route.py +3 -3
- zrb/runner/web_route/task_session_api_route.py +4 -4
- zrb/runner/web_util/cookie.py +6 -6
- zrb/runner/web_util/token.py +18 -18
- zrb/runner/web_util/user.py +17 -15
- zrb/util/string/format.py +11 -14
- {zrb-1.7.4.dist-info → zrb-1.8.0.dist-info}/METADATA +1 -1
- {zrb-1.7.4.dist-info → zrb-1.8.0.dist-info}/RECORD +71 -53
- zrb/runner/web_config/config.py +0 -91
- zrb/runner/web_config/config_factory.py +0 -15
- zrb/runner/web_route/static/resources/pico.min.css +0 -4
- {zrb-1.7.4.dist-info → zrb-1.8.0.dist-info}/WHEEL +0 -0
- {zrb-1.7.4.dist-info → zrb-1.8.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,189 @@
|
|
1
|
+
from typing import Callable
|
2
|
+
|
3
|
+
from zrb.config import CFG
|
4
|
+
from zrb.runner.web_schema.user import User
|
5
|
+
from zrb.task.any_task import AnyTask
|
6
|
+
|
7
|
+
|
8
|
+
class WebAuthConfig:
|
9
|
+
def __init__(
|
10
|
+
self,
|
11
|
+
port: int | None = None,
|
12
|
+
secret_key: str | None = None,
|
13
|
+
access_token_expire_minutes: int | None = None,
|
14
|
+
refresh_token_expire_minutes: int | None = None,
|
15
|
+
access_token_cookie_name: str | None = None,
|
16
|
+
refresh_token_cookie_name: str | None = None,
|
17
|
+
enable_auth: bool | None = None,
|
18
|
+
super_admin_username: str | None = None,
|
19
|
+
super_admin_password: str | None = None,
|
20
|
+
guest_username: str | None = None,
|
21
|
+
guest_accessible_tasks: list[AnyTask | str] = [],
|
22
|
+
find_user_by_username: Callable[[str], User | None] | None = None,
|
23
|
+
):
|
24
|
+
self._port = port
|
25
|
+
self._secret_key = secret_key
|
26
|
+
self._access_token_expire_minutes = access_token_expire_minutes
|
27
|
+
self._refresh_token_expire_minutes = refresh_token_expire_minutes
|
28
|
+
self._access_token_cookie_name = access_token_cookie_name
|
29
|
+
self._refresh_token_cookie_name = refresh_token_cookie_name
|
30
|
+
self._enable_auth = enable_auth
|
31
|
+
self._super_admin_username = super_admin_username
|
32
|
+
self._super_admin_password = super_admin_password
|
33
|
+
self._guest_username = guest_username
|
34
|
+
self._user_list = []
|
35
|
+
self._guest_accessible_tasks = guest_accessible_tasks
|
36
|
+
self._find_user_by_username = find_user_by_username
|
37
|
+
|
38
|
+
@property
|
39
|
+
def port(self) -> int:
|
40
|
+
if self._port is not None:
|
41
|
+
return self._port
|
42
|
+
return CFG.WEB_HTTP_PORT
|
43
|
+
|
44
|
+
@property
|
45
|
+
def secret_key(self) -> str:
|
46
|
+
if self._secret_key is not None:
|
47
|
+
return self._secret_key
|
48
|
+
return CFG.WEB_SECRET_KEY
|
49
|
+
|
50
|
+
@property
|
51
|
+
def access_token_expire_minutes(self) -> int:
|
52
|
+
if self._access_token_expire_minutes is not None:
|
53
|
+
return self._access_token_expire_minutes
|
54
|
+
return CFG.WEB_AUTH_ACCESS_TOKEN_EXPIRE_MINUTES
|
55
|
+
|
56
|
+
@property
|
57
|
+
def refresh_token_expire_minutes(self) -> int:
|
58
|
+
if self._refresh_token_expire_minutes is not None:
|
59
|
+
return self._refresh_token_expire_minutes
|
60
|
+
return CFG.WEB_AUTH_REFRESH_TOKEN_EXPIRE_MINUTES
|
61
|
+
|
62
|
+
@property
|
63
|
+
def access_token_cookie_name(self) -> str:
|
64
|
+
if self._access_token_cookie_name is not None:
|
65
|
+
return self._access_token_cookie_name
|
66
|
+
return CFG.WEB_ACCESS_TOKEN_COOKIE_NAME
|
67
|
+
|
68
|
+
@property
|
69
|
+
def refresh_token_cookie_name(self) -> str:
|
70
|
+
if self._refresh_token_cookie_name is not None:
|
71
|
+
return self._refresh_token_cookie_name
|
72
|
+
return CFG.WEB_REFRESH_TOKEN_COOKIE_NAME
|
73
|
+
|
74
|
+
@property
|
75
|
+
def enable_auth(self) -> bool:
|
76
|
+
if self._enable_auth is not None:
|
77
|
+
return self._enable_auth
|
78
|
+
return CFG.WEB_ENABLE_AUTH
|
79
|
+
|
80
|
+
@property
|
81
|
+
def super_admin_username(self) -> str:
|
82
|
+
if self._super_admin_username is not None:
|
83
|
+
return self._super_admin_username
|
84
|
+
return CFG.WEB_SUPER_ADMIN_USERNAME
|
85
|
+
|
86
|
+
@property
|
87
|
+
def super_admin_password(self) -> str:
|
88
|
+
if self._super_admin_password is not None:
|
89
|
+
return self._super_admin_password
|
90
|
+
return CFG.WEB_SUPER_ADMIN_PASSWORD
|
91
|
+
|
92
|
+
@property
|
93
|
+
def guest_username(self) -> str:
|
94
|
+
if self._guest_username is not None:
|
95
|
+
return self._guest_username
|
96
|
+
return CFG.WEB_GUEST_USERNAME
|
97
|
+
|
98
|
+
@property
|
99
|
+
def guest_accessible_tasks(self) -> list[AnyTask | str]:
|
100
|
+
return self._guest_accessible_tasks
|
101
|
+
|
102
|
+
@property
|
103
|
+
def default_user(self) -> User:
|
104
|
+
if self.enable_auth:
|
105
|
+
return User(
|
106
|
+
username=self.guest_username,
|
107
|
+
password="",
|
108
|
+
is_guest=True,
|
109
|
+
accessible_tasks=self.guest_accessible_tasks,
|
110
|
+
)
|
111
|
+
return User(
|
112
|
+
username=self.guest_username,
|
113
|
+
password="",
|
114
|
+
is_guest=True,
|
115
|
+
is_super_admin=True,
|
116
|
+
)
|
117
|
+
|
118
|
+
@property
|
119
|
+
def super_admin(self) -> User:
|
120
|
+
return User(
|
121
|
+
username=self.super_admin_username,
|
122
|
+
password=self.super_admin_password,
|
123
|
+
is_super_admin=True,
|
124
|
+
)
|
125
|
+
|
126
|
+
@property
|
127
|
+
def user_list(self) -> list[User]:
|
128
|
+
if not self.enable_auth:
|
129
|
+
return [self.default_user]
|
130
|
+
return self._user_list + [self.super_admin, self.default_user]
|
131
|
+
|
132
|
+
def set_port(self, port: int):
|
133
|
+
self._port = port
|
134
|
+
|
135
|
+
def set_secret_key(self, secret_key: str):
|
136
|
+
self._secret_key = secret_key
|
137
|
+
|
138
|
+
def set_access_token_expire_minutes(self, minutes: int):
|
139
|
+
self._access_token_expire_minutes = minutes
|
140
|
+
|
141
|
+
def set_refresh_token_expire_minutes(self, minutes: int):
|
142
|
+
self._refresh_token_expire_minutes = minutes
|
143
|
+
|
144
|
+
def set_access_token_cookie_name(self, name: str):
|
145
|
+
self._access_token_cookie_name = name
|
146
|
+
|
147
|
+
def set_refresh_token_cookie_name(self, name: str):
|
148
|
+
self._refresh_token_cookie_name = name
|
149
|
+
|
150
|
+
def set_enable_auth(self, enable: bool):
|
151
|
+
self._enable_auth = enable
|
152
|
+
|
153
|
+
def set_super_admin_username(self, username: str):
|
154
|
+
self._super_admin_username = username
|
155
|
+
|
156
|
+
def set_super_admin_password(self, password: str):
|
157
|
+
self._super_admin_password = password
|
158
|
+
|
159
|
+
def set_guest_username(self, username: str):
|
160
|
+
self._guest_username = username
|
161
|
+
|
162
|
+
def set_guest_accessible_tasks(self, tasks: list[AnyTask | str]):
|
163
|
+
self._guest_accessible_tasks = tasks
|
164
|
+
|
165
|
+
def set_find_user_by_username(
|
166
|
+
self, find_user_by_username: Callable[[str], User | None]
|
167
|
+
):
|
168
|
+
self._find_user_by_username = find_user_by_username
|
169
|
+
|
170
|
+
def append_user(self, user: User):
|
171
|
+
duplicates = [
|
172
|
+
existing_user
|
173
|
+
for existing_user in self.user_list
|
174
|
+
if existing_user.username == user.username
|
175
|
+
]
|
176
|
+
if len(duplicates) > 0:
|
177
|
+
raise ValueError(f"User already exists {user.username}")
|
178
|
+
self._user_list.append(user)
|
179
|
+
|
180
|
+
def find_user_by_username(self, username: str) -> User | None:
|
181
|
+
user = None
|
182
|
+
if self._find_user_by_username is not None:
|
183
|
+
user = self._find_user_by_username(username)
|
184
|
+
if user is None:
|
185
|
+
user = next((u for u in self.user_list if u.username == username), None)
|
186
|
+
return user
|
187
|
+
|
188
|
+
|
189
|
+
web_auth_config = WebAuthConfig()
|
@@ -1,5 +1,7 @@
|
|
1
1
|
from typing import TYPE_CHECKING
|
2
2
|
|
3
|
+
from zrb.config import CFG
|
4
|
+
|
3
5
|
if TYPE_CHECKING:
|
4
6
|
# We want fastapi to only be loaded when necessary to decrease footprint
|
5
7
|
from fastapi import FastAPI
|
@@ -12,6 +14,6 @@ def serve_docs(app: "FastAPI") -> None:
|
|
12
14
|
async def swagger_ui_html():
|
13
15
|
return get_swagger_ui_html(
|
14
16
|
openapi_url="/openapi.json",
|
15
|
-
title=
|
16
|
-
swagger_favicon_url=
|
17
|
+
title=CFG.WEB_TITLE,
|
18
|
+
swagger_favicon_url=CFG.WEB_FAVICON_PATH,
|
17
19
|
)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
from typing import TYPE_CHECKING
|
2
2
|
|
3
3
|
from zrb.group.any_group import AnyGroup
|
4
|
-
from zrb.runner.
|
4
|
+
from zrb.runner.web_auth_config import WebAuthConfig
|
5
5
|
from zrb.runner.web_route.error_page.show_error_page import show_error_page
|
6
6
|
from zrb.runner.web_util.user import get_user_from_request
|
7
7
|
|
@@ -13,7 +13,7 @@ if TYPE_CHECKING:
|
|
13
13
|
def serve_default_404(
|
14
14
|
app: "FastAPI",
|
15
15
|
root_group: AnyGroup,
|
16
|
-
|
16
|
+
web_auth_config: WebAuthConfig,
|
17
17
|
) -> None:
|
18
18
|
from fastapi import Request
|
19
19
|
from fastapi.exception_handlers import http_exception_handler
|
@@ -24,5 +24,5 @@ def serve_default_404(
|
|
24
24
|
if request.url.path.startswith("/api"):
|
25
25
|
# Re-raise the exception to let FastAPI handle it
|
26
26
|
return await http_exception_handler(request, exc)
|
27
|
-
user = await get_user_from_request(
|
27
|
+
user = await get_user_from_request(web_auth_config, request)
|
28
28
|
return show_error_page(user, root_group, 404, "Not found")
|
@@ -1,39 +1,22 @@
|
|
1
|
-
|
2
|
-
<
|
3
|
-
|
4
|
-
|
5
|
-
<
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
</ul>
|
24
|
-
<ul>
|
25
|
-
<li>{auth_link}</li>
|
26
|
-
</ul>
|
27
|
-
</nav>
|
28
|
-
</hgroup>
|
29
|
-
</header>
|
30
|
-
<main class="container">
|
31
|
-
<article>
|
32
|
-
<h2>{error_status_code}</h2>
|
33
|
-
<p>{error_message}</p>
|
34
|
-
</article>
|
35
|
-
</main>
|
36
|
-
</body>
|
37
|
-
<script src="/refresh-token.js"></script>
|
38
|
-
|
39
|
-
</html>
|
1
|
+
<header class="container">
|
2
|
+
<hgroup>
|
3
|
+
<h1>{name}</h1>
|
4
|
+
<p>{description}</p>
|
5
|
+
<nav>
|
6
|
+
<ul>
|
7
|
+
<li><a href="/">🏠 Home</a></li>
|
8
|
+
<li><a href="/docs">💻 API Documentation</a></li>
|
9
|
+
</ul>
|
10
|
+
<ul>
|
11
|
+
<li>{auth_link}</li>
|
12
|
+
</ul>
|
13
|
+
</nav>
|
14
|
+
</hgroup>
|
15
|
+
</header>
|
16
|
+
<main class="container">
|
17
|
+
<article>
|
18
|
+
<h2>{error_status_code}</h2>
|
19
|
+
<p>{error_message}</p>
|
20
|
+
</article>
|
21
|
+
</main>
|
22
|
+
<script src="/refresh-token.js"></script>
|
@@ -3,7 +3,7 @@ from typing import TYPE_CHECKING
|
|
3
3
|
|
4
4
|
from zrb.config import CFG
|
5
5
|
from zrb.group.any_group import AnyGroup
|
6
|
-
from zrb.runner.
|
6
|
+
from zrb.runner.web_auth_config import WebAuthConfig
|
7
7
|
from zrb.runner.web_util.html import (
|
8
8
|
get_html_auth_link,
|
9
9
|
get_html_subgroup_info,
|
@@ -21,7 +21,7 @@ if TYPE_CHECKING:
|
|
21
21
|
def serve_home_page(
|
22
22
|
app: "FastAPI",
|
23
23
|
root_group: AnyGroup,
|
24
|
-
|
24
|
+
web_auth_config: WebAuthConfig,
|
25
25
|
) -> None:
|
26
26
|
from fastapi import Request
|
27
27
|
from fastapi.responses import HTMLResponse
|
@@ -40,7 +40,7 @@ def serve_home_page(
|
|
40
40
|
web_jargon = (
|
41
41
|
CFG.WEB_JARGON if CFG.WEB_JARGON.strip() != "" else root_group.description
|
42
42
|
)
|
43
|
-
user = await get_user_from_request(
|
43
|
+
user = await get_user_from_request(web_auth_config, request)
|
44
44
|
group_info = get_html_subgroup_info(user, "/ui/", root_group)
|
45
45
|
task_info = get_html_subtask_info(user, "/ui/", root_group)
|
46
46
|
auth_link = get_html_auth_link(user)
|
@@ -48,7 +48,8 @@ def serve_home_page(
|
|
48
48
|
fstring_format(
|
49
49
|
_GLOBAL_TEMPLATE,
|
50
50
|
{
|
51
|
-
"
|
51
|
+
"CFG": CFG,
|
52
|
+
"root_group": root_group,
|
52
53
|
"content": fstring_format(
|
53
54
|
_VIEW_TEMPLATE,
|
54
55
|
{
|
@@ -1,6 +1,6 @@
|
|
1
1
|
from typing import TYPE_CHECKING, Annotated
|
2
2
|
|
3
|
-
from zrb.runner.
|
3
|
+
from zrb.runner.web_auth_config import WebAuthConfig
|
4
4
|
from zrb.runner.web_util.cookie import set_auth_cookie
|
5
5
|
from zrb.runner.web_util.token import generate_tokens_by_credentials
|
6
6
|
|
@@ -9,7 +9,7 @@ if TYPE_CHECKING:
|
|
9
9
|
from fastapi import FastAPI
|
10
10
|
|
11
11
|
|
12
|
-
def serve_login_api(app: "FastAPI",
|
12
|
+
def serve_login_api(app: "FastAPI", web_auth_config: WebAuthConfig) -> None:
|
13
13
|
from fastapi import Depends, Response
|
14
14
|
from fastapi.responses import JSONResponse
|
15
15
|
from fastapi.security import OAuth2PasswordRequestForm
|
@@ -19,7 +19,7 @@ def serve_login_api(app: "FastAPI", web_config: WebConfig) -> None:
|
|
19
19
|
response: Response, form_data: Annotated[OAuth2PasswordRequestForm, Depends()]
|
20
20
|
):
|
21
21
|
token = generate_tokens_by_credentials(
|
22
|
-
|
22
|
+
web_auth_config=web_auth_config,
|
23
23
|
username=form_data.username,
|
24
24
|
password=form_data.password,
|
25
25
|
)
|
@@ -27,5 +27,5 @@ def serve_login_api(app: "FastAPI", web_config: WebConfig) -> None:
|
|
27
27
|
return JSONResponse(
|
28
28
|
content={"detail": "Incorrect username or password"}, status_code=400
|
29
29
|
)
|
30
|
-
set_auth_cookie(
|
30
|
+
set_auth_cookie(web_auth_config, response, token)
|
31
31
|
return token
|
@@ -3,7 +3,7 @@ from typing import TYPE_CHECKING
|
|
3
3
|
|
4
4
|
from zrb.config import CFG
|
5
5
|
from zrb.group.any_group import AnyGroup
|
6
|
-
from zrb.runner.
|
6
|
+
from zrb.runner.web_auth_config import WebAuthConfig
|
7
7
|
from zrb.runner.web_util.html import get_html_auth_link
|
8
8
|
from zrb.runner.web_util.user import get_user_from_request
|
9
9
|
from zrb.util.file import read_file
|
@@ -17,7 +17,7 @@ if TYPE_CHECKING:
|
|
17
17
|
def serve_login_page(
|
18
18
|
app: "FastAPI",
|
19
19
|
root_group: AnyGroup,
|
20
|
-
|
20
|
+
web_auth_config: WebAuthConfig,
|
21
21
|
) -> None:
|
22
22
|
from fastapi import Request
|
23
23
|
from fastapi.responses import HTMLResponse
|
@@ -29,18 +29,23 @@ def serve_login_page(
|
|
29
29
|
os.path.join(os.path.dirname(_DIR), "static", "global_template.html")
|
30
30
|
)
|
31
31
|
_VIEW_TEMPLATE = read_file(os.path.join(_DIR, "view.html"))
|
32
|
-
user = await get_user_from_request(
|
32
|
+
user = await get_user_from_request(web_auth_config, request)
|
33
|
+
web_title = CFG.WEB_TITLE if CFG.WEB_TITLE.strip() != "" else root_group.name
|
34
|
+
web_jargon = (
|
35
|
+
CFG.WEB_JARGON if CFG.WEB_JARGON.strip() != "" else root_group.description
|
36
|
+
)
|
33
37
|
auth_link = get_html_auth_link(user)
|
34
38
|
return HTMLResponse(
|
35
39
|
fstring_format(
|
36
40
|
_GLOBAL_TEMPLATE,
|
37
41
|
{
|
38
|
-
"
|
42
|
+
"CFG": CFG,
|
43
|
+
"root_group": root_group,
|
39
44
|
"content": fstring_format(
|
40
45
|
_VIEW_TEMPLATE,
|
41
46
|
{
|
42
|
-
"name":
|
43
|
-
"description":
|
47
|
+
"name": web_title,
|
48
|
+
"description": web_jargon,
|
44
49
|
"auth_link": auth_link,
|
45
50
|
},
|
46
51
|
),
|
@@ -1,18 +1,18 @@
|
|
1
1
|
from typing import TYPE_CHECKING
|
2
2
|
|
3
|
-
from zrb.runner.
|
3
|
+
from zrb.runner.web_auth_config import WebAuthConfig
|
4
4
|
|
5
5
|
if TYPE_CHECKING:
|
6
6
|
# We want fastapi to only be loaded when necessary to decrease footprint
|
7
7
|
from fastapi import FastAPI
|
8
8
|
|
9
9
|
|
10
|
-
def serve_logout_api(app: "FastAPI",
|
10
|
+
def serve_logout_api(app: "FastAPI", web_auth_config: WebAuthConfig) -> None:
|
11
11
|
from fastapi import Response
|
12
12
|
|
13
13
|
@app.get("/api/v1/logout")
|
14
14
|
@app.post("/api/v1/logout")
|
15
15
|
async def logout_api(response: Response):
|
16
|
-
response.delete_cookie(
|
17
|
-
response.delete_cookie(
|
16
|
+
response.delete_cookie(web_auth_config.access_token_cookie_name)
|
17
|
+
response.delete_cookie(web_auth_config.refresh_token_cookie_name)
|
18
18
|
return {"message": "Logout successful"}
|
@@ -3,7 +3,7 @@ from typing import TYPE_CHECKING
|
|
3
3
|
|
4
4
|
from zrb.config import CFG
|
5
5
|
from zrb.group.any_group import AnyGroup
|
6
|
-
from zrb.runner.
|
6
|
+
from zrb.runner.web_auth_config import WebAuthConfig
|
7
7
|
from zrb.runner.web_util.html import get_html_auth_link
|
8
8
|
from zrb.runner.web_util.user import get_user_from_request
|
9
9
|
from zrb.util.file import read_file
|
@@ -17,7 +17,7 @@ if TYPE_CHECKING:
|
|
17
17
|
def serve_logout_page(
|
18
18
|
app: "FastAPI",
|
19
19
|
root_group: AnyGroup,
|
20
|
-
|
20
|
+
web_auth_config: WebAuthConfig,
|
21
21
|
) -> None:
|
22
22
|
from fastapi import Request
|
23
23
|
from fastapi.responses import HTMLResponse
|
@@ -29,18 +29,23 @@ def serve_logout_page(
|
|
29
29
|
os.path.join(os.path.dirname(_DIR), "static", "global_template.html")
|
30
30
|
)
|
31
31
|
_VIEW_TEMPLATE = read_file(os.path.join(_DIR, "view.html"))
|
32
|
-
user = await get_user_from_request(
|
32
|
+
user = await get_user_from_request(web_auth_config, request)
|
33
|
+
web_title = CFG.WEB_TITLE if CFG.WEB_TITLE.strip() != "" else root_group.name
|
34
|
+
web_jargon = (
|
35
|
+
CFG.WEB_JARGON if CFG.WEB_JARGON.strip() != "" else root_group.description
|
36
|
+
)
|
33
37
|
auth_link = get_html_auth_link(user)
|
34
38
|
return HTMLResponse(
|
35
39
|
fstring_format(
|
36
40
|
_GLOBAL_TEMPLATE,
|
37
41
|
{
|
38
|
-
"
|
42
|
+
"CFG": CFG,
|
43
|
+
"root_group": root_group,
|
39
44
|
"content": fstring_format(
|
40
45
|
_VIEW_TEMPLATE,
|
41
46
|
{
|
42
|
-
"name":
|
43
|
-
"description":
|
47
|
+
"name": web_title,
|
48
|
+
"description": web_jargon,
|
44
49
|
"auth_link": auth_link,
|
45
50
|
"user": user,
|
46
51
|
},
|
@@ -36,7 +36,8 @@ def show_group_page(user: User, root_group: AnyGroup, group: AnyGroup, url: str)
|
|
36
36
|
fstring_format(
|
37
37
|
_GLOBAL_TEMPLATE,
|
38
38
|
{
|
39
|
-
"
|
39
|
+
"CFG": CFG,
|
40
|
+
"root_group": root_group,
|
40
41
|
"content": fstring_format(
|
41
42
|
_VIEW_TEMPLATE,
|
42
43
|
{
|
@@ -3,7 +3,7 @@ from typing import TYPE_CHECKING
|
|
3
3
|
|
4
4
|
from zrb.context.shared_context import SharedContext
|
5
5
|
from zrb.group.any_group import AnyGroup
|
6
|
-
from zrb.runner.
|
6
|
+
from zrb.runner.web_auth_config import WebAuthConfig
|
7
7
|
from zrb.runner.web_route.error_page.show_error_page import show_error_page
|
8
8
|
from zrb.runner.web_route.node_page.group.show_group_page import show_group_page
|
9
9
|
from zrb.runner.web_route.node_page.task.show_task_page import show_task_page
|
@@ -20,14 +20,14 @@ if TYPE_CHECKING:
|
|
20
20
|
def serve_node_page(
|
21
21
|
app: "FastAPI",
|
22
22
|
root_group: AnyGroup,
|
23
|
-
|
23
|
+
web_auth_config: WebAuthConfig,
|
24
24
|
) -> None:
|
25
25
|
from fastapi import Request
|
26
26
|
from fastapi.responses import HTMLResponse
|
27
27
|
|
28
28
|
@app.get("/ui/{path:path}", response_class=HTMLResponse, include_in_schema=False)
|
29
29
|
async def ui_page(path: str, request: Request) -> HTMLResponse:
|
30
|
-
user = await get_user_from_request(
|
30
|
+
user = await get_user_from_request(web_auth_config, request)
|
31
31
|
# Avoid capturing '/ui' itself
|
32
32
|
if not path:
|
33
33
|
return show_error_page(user, root_group, 422, "Undefined path")
|
@@ -1,6 +1,6 @@
|
|
1
1
|
from typing import TYPE_CHECKING
|
2
2
|
|
3
|
-
from zrb.runner.
|
3
|
+
from zrb.runner.web_auth_config import WebAuthConfig
|
4
4
|
from zrb.runner.web_schema.token import RefreshTokenRequest
|
5
5
|
from zrb.runner.web_util.cookie import set_auth_cookie
|
6
6
|
from zrb.runner.web_util.token import regenerate_tokens
|
@@ -10,7 +10,7 @@ if TYPE_CHECKING:
|
|
10
10
|
from fastapi import FastAPI
|
11
11
|
|
12
12
|
|
13
|
-
def serve_refresh_token_api(app: "FastAPI",
|
13
|
+
def serve_refresh_token_api(app: "FastAPI", web_auth_config: WebAuthConfig) -> None:
|
14
14
|
from fastapi import Cookie, Response
|
15
15
|
from fastapi.responses import JSONResponse
|
16
16
|
|
@@ -19,7 +19,7 @@ def serve_refresh_token_api(app: "FastAPI", web_config: WebConfig) -> None:
|
|
19
19
|
response: Response,
|
20
20
|
body: RefreshTokenRequest = None,
|
21
21
|
refresh_token_cookie: str = Cookie(
|
22
|
-
None, alias=
|
22
|
+
None, alias=web_auth_config.refresh_token_cookie_name
|
23
23
|
),
|
24
24
|
):
|
25
25
|
# Try to get the refresh token from the request body first
|
@@ -33,6 +33,6 @@ def serve_refresh_token_api(app: "FastAPI", web_config: WebConfig) -> None:
|
|
33
33
|
content={"detail": "Refresh token not provided"}, status_code=401
|
34
34
|
)
|
35
35
|
# Get token
|
36
|
-
new_token = regenerate_tokens(
|
37
|
-
set_auth_cookie(
|
36
|
+
new_token = regenerate_tokens(web_auth_config, refresh_token)
|
37
|
+
set_auth_cookie(web_auth_config, response, new_token)
|
38
38
|
return new_token
|
@@ -4,10 +4,11 @@
|
|
4
4
|
<meta charset="utf-8">
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
6
6
|
<meta name="color-scheme" content="light dark">
|
7
|
-
<link rel="stylesheet" href="/static/pico.min.css">
|
8
|
-
<link rel="icon" href="
|
9
|
-
<title>{
|
7
|
+
<link rel="stylesheet" href="/static/pico-css/pico{'.' + CFG.WEB_COLOR if CFG.WEB_COLOR != '' else ''}.min.css">
|
8
|
+
<link rel="icon" href="{CFG.WEB_FAVICON_PATH}" sizes="32x32" type="image/png">
|
9
|
+
<title>{CFG.WEB_TITLE if CFG.WEB_TITLE.strip() != "" else root_group.name}</title>
|
10
10
|
<link rel="stylesheet" href="/static/common.css">
|
11
|
+
{'\n'.join(['<link rel="stylesheet" href="' + path + '">' for path in CFG.WEB_CSS_PATH])}
|
11
12
|
</head>
|
12
13
|
<body>
|
13
14
|
<div class="theme-switcher">
|
@@ -24,4 +25,6 @@
|
|
24
25
|
</body>
|
25
26
|
<script src="/refresh-token.js"></script>
|
26
27
|
<script src="/static/common.js"></script>
|
28
|
+
{'\n'.join(['<script src="' + path + '"></script>' for path in CFG.WEB_JS_PATH])}
|
29
|
+
|
27
30
|
</html>
|