Finalize secret tree page
This commit is contained in:
@ -27,6 +27,11 @@
|
|||||||
{% if group.group_name in group_path_nodes %}
|
{% if group.group_name in group_path_nodes %}
|
||||||
expanded=""
|
expanded=""
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if selected_group | default(None) %}
|
||||||
|
{% if group.path == selected_group %}
|
||||||
|
selected=""
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
>
|
>
|
||||||
@ -77,7 +82,13 @@
|
|||||||
id="secret-group-root-item"
|
id="secret-group-root-item"
|
||||||
data-type="root"
|
data-type="root"
|
||||||
data-name="root"
|
data-name="root"
|
||||||
expanded=""
|
|
||||||
|
{% if "/" in group_path_nodes %}
|
||||||
|
expanded=""
|
||||||
|
{% endif %}
|
||||||
|
{% if selected_group == "/"%}
|
||||||
|
selected=""
|
||||||
|
{% endif %}
|
||||||
>
|
>
|
||||||
<sl-icon name="folder"> </sl-icon>
|
<sl-icon name="folder"> </sl-icon>
|
||||||
<span class="px-2">Ungrouped</span>
|
<span class="px-2">Ungrouped</span>
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<h3 class="text-xl font-semibold dark:text-white">Group {{name}}</h3>
|
<h3 class="text-xl font-semibold dark:text-white">Group {{group.group_name}}</h3>
|
||||||
{% if description %}
|
{% if description %}
|
||||||
<span class="text-sm text-gray-500 dark:text-gray-400">{{ description }}</span>
|
<span class="text-sm text-gray-500 dark:text-gray-400">{{ group.description }}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<sl-details summary="Create secret">
|
<sl-details summary="Create secret">
|
||||||
<form
|
<form
|
||||||
hx-post="/secrets/create/group/{{ name }}"
|
hx-post="/secrets/create/group/{{ group.group_name }}"
|
||||||
hx-target="#secretdetails"
|
hx-target="#secretdetails"
|
||||||
hx-swap="OuterHTML"
|
hx-swap="OuterHTML"
|
||||||
>
|
>
|
||||||
@ -48,7 +48,7 @@
|
|||||||
placeholder="Description"
|
placeholder="Description"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<input type="hidden" name="parent_group" value="{{ name }}" />
|
<input type="hidden" name="parent_group" value="{{ group.group_name }}" />
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="text-white w-full justify-center bg-primary-700 hover:bg-primary-800 focus:ring-4 focus:outline-none focus:ring-primary-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800"
|
class="text-white w-full justify-center bg-primary-700 hover:bg-primary-800 focus:ring-4 focus:outline-none focus:ring-primary-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800"
|
||||||
@ -59,7 +59,7 @@
|
|||||||
</sl-details>
|
</sl-details>
|
||||||
<sl-details summary="Edit group">
|
<sl-details summary="Edit group">
|
||||||
<form
|
<form
|
||||||
hx-put="/secrets/partial/group/{{name}}/description"
|
hx-put="/secrets/partial/group/{{group.group_name}}/description"
|
||||||
hx-target="#secretdetails"
|
hx-target="#secretdetails"
|
||||||
hx-swap="OuterHTML"
|
hx-swap="OuterHTML"
|
||||||
>
|
>
|
||||||
@ -77,7 +77,7 @@
|
|||||||
name="description"
|
name="description"
|
||||||
id="description"
|
id="description"
|
||||||
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500"
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500"
|
||||||
value="{{ description }}"
|
value="{{ group.description }}"
|
||||||
required=""
|
required=""
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -91,7 +91,7 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="inline-flex items-center px-3 py-2 text-sm font-medium text-center text-white bg-red-700 rounded-lg hover:bg-red-800 focus:ring-4 focus:ring-red-300 dark:focus:ring-red-900"
|
class="inline-flex items-center px-3 py-2 text-sm font-medium text-center text-white bg-red-700 rounded-lg hover:bg-red-800 focus:ring-4 focus:ring-red-300 dark:focus:ring-red-900"
|
||||||
hx-delete="/secrets/group/{{ name }}"
|
hx-delete="/secrets/group/{{ group.group_name }}"
|
||||||
hx-target="#secretdetails"
|
hx-target="#secretdetails"
|
||||||
hx-swap="OuterHTML"
|
hx-swap="OuterHTML"
|
||||||
hx-confirm="Deleting a group will move all its secrets to the Ungrouped category. Continue?"
|
hx-confirm="Deleting a group will move all its secrets to the Ungrouped category. Continue?"
|
||||||
|
|||||||
@ -0,0 +1,7 @@
|
|||||||
|
<div class="w-full" id="secretdetails">
|
||||||
|
<a
|
||||||
|
href="{{ destination }}"
|
||||||
|
class="font-medium text-blue-600 dark:text-blue-500 hover:underline">
|
||||||
|
Redirecting...
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
"""Secrets views."""
|
||||||
|
|
||||||
|
|
||||||
# pyright: reportUnusedFunction=false
|
# pyright: reportUnusedFunction=false
|
||||||
|
import os
|
||||||
import logging
|
import logging
|
||||||
import secrets as pysecrets
|
import secrets as pysecrets
|
||||||
from typing import Annotated, Any
|
from typing import Annotated, Any
|
||||||
@ -64,57 +64,17 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
|
|||||||
current_user: Annotated[LocalUserInfo, Depends(dependencies.get_user_info)],
|
current_user: Annotated[LocalUserInfo, Depends(dependencies.get_user_info)],
|
||||||
):
|
):
|
||||||
groups = await admin.get_secret_groups()
|
groups = await admin.get_secret_groups()
|
||||||
LOG.info("Groups: %s", groups.model_dump_json(indent=2))
|
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
request,
|
request,
|
||||||
"secrets/index.html.j2",
|
"secrets/index.html.j2",
|
||||||
{
|
{
|
||||||
"groups": groups,
|
"groups": groups,
|
||||||
"user": current_user,
|
"user": current_user,
|
||||||
|
"selected_group": None,
|
||||||
|
"group_path_nodes": ["/"],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
# @app.get("/secrets/partial/root_group")
|
|
||||||
# async def get_root_group(
|
|
||||||
# request: Request,
|
|
||||||
# admin: Annotated[AdminBackend, Depends(dependencies.get_admin_backend)],
|
|
||||||
# ):
|
|
||||||
# """Get root group."""
|
|
||||||
# clients = await admin.get_clients()
|
|
||||||
# return templates.TemplateResponse(
|
|
||||||
# request,
|
|
||||||
# "secrets/partials/edit_root.html.j2",
|
|
||||||
# {
|
|
||||||
# "group_path_nodes": [],
|
|
||||||
# "clients": clients,
|
|
||||||
# },
|
|
||||||
# )
|
|
||||||
|
|
||||||
# @app.get("/secrets/partial/secret/{name}")
|
|
||||||
# async def get_secret_tree_detail_partial(
|
|
||||||
# request: Request,
|
|
||||||
# name: str,
|
|
||||||
# admin: Annotated[AdminBackend, Depends(dependencies.get_admin_backend)],
|
|
||||||
# ):
|
|
||||||
# """Get partial secret detail."""
|
|
||||||
# secret = await admin.get_secret(name)
|
|
||||||
# groups = await admin.get_secret_groups(flat=True)
|
|
||||||
# events = await admin.get_audit_log_detailed(limit=10, secret_name=name)
|
|
||||||
|
|
||||||
# if not secret:
|
|
||||||
# raise HTTPException(
|
|
||||||
# status_code=status.HTTP_404_NOT_FOUND, detail="Secret not found"
|
|
||||||
# )
|
|
||||||
# return templates.TemplateResponse(
|
|
||||||
# request,
|
|
||||||
# "secrets/partials/tree_detail.html.j2",
|
|
||||||
# {
|
|
||||||
# "secret": secret,
|
|
||||||
# "groups": groups,
|
|
||||||
# "events": events,
|
|
||||||
# },
|
|
||||||
# )
|
|
||||||
|
|
||||||
@app.get("/secrets/group/")
|
@app.get("/secrets/group/")
|
||||||
async def show_root_group(
|
async def show_root_group(
|
||||||
request: Request,
|
request: Request,
|
||||||
@ -138,6 +98,7 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
|
|||||||
context["user"] = current_user
|
context["user"] = current_user
|
||||||
context["groups"] = groups
|
context["groups"] = groups
|
||||||
context["group_path_nodes"] = ["/"]
|
context["group_path_nodes"] = ["/"]
|
||||||
|
context["selected_group"] = "/"
|
||||||
|
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
request, template_name, context, headers=headers
|
request, template_name, context, headers=headers
|
||||||
@ -161,8 +122,7 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
|
|||||||
headers: dict[str, str] = {}
|
headers: dict[str, str] = {}
|
||||||
context: dict[str, Any] = {
|
context: dict[str, Any] = {
|
||||||
"group_page": True,
|
"group_page": True,
|
||||||
"name": group.group_name,
|
"group": group,
|
||||||
"description": group.description,
|
|
||||||
"clients": clients,
|
"clients": clients,
|
||||||
}
|
}
|
||||||
if request.headers.get("HX-Request"):
|
if request.headers.get("HX-Request"):
|
||||||
@ -176,6 +136,7 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
|
|||||||
context["user"] = current_user
|
context["user"] = current_user
|
||||||
context["groups"] = groups
|
context["groups"] = groups
|
||||||
context["group_path_nodes"] = group.path.split("/")
|
context["group_path_nodes"] = group.path.split("/")
|
||||||
|
context["selected_group"] = group.path
|
||||||
|
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
request, template_name, context, headers=headers
|
request, template_name, context, headers=headers
|
||||||
@ -190,7 +151,7 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
|
|||||||
):
|
):
|
||||||
"""Get secret detail."""
|
"""Get secret detail."""
|
||||||
secret = await admin.get_secret(name)
|
secret = await admin.get_secret(name)
|
||||||
groups = await admin.get_secret_groups(flat=True)
|
groups = await admin.get_secret_groups()
|
||||||
events = await admin.get_audit_log_detailed(limit=10, secret_name=name)
|
events = await admin.get_audit_log_detailed(limit=10, secret_name=name)
|
||||||
|
|
||||||
if not secret:
|
if not secret:
|
||||||
@ -222,35 +183,12 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
|
|||||||
context["user"] = current_user
|
context["user"] = current_user
|
||||||
context["groups"] = groups
|
context["groups"] = groups
|
||||||
context["group_path_nodes"] = group_path
|
context["group_path_nodes"] = group_path
|
||||||
|
context["selected_group"] = None
|
||||||
|
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
request, template_name, context, headers=headers
|
request, template_name, context, headers=headers
|
||||||
)
|
)
|
||||||
|
|
||||||
@app.get("/secrets/partial/group/{name}")
|
|
||||||
async def get_group_details(
|
|
||||||
request: Request,
|
|
||||||
name: str,
|
|
||||||
admin: Annotated[AdminBackend, Depends(dependencies.get_admin_backend)],
|
|
||||||
):
|
|
||||||
"""Get group details partial."""
|
|
||||||
group = await admin.get_secret_group(name)
|
|
||||||
if not group:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=status.HTTP_404_NOT_FOUND, detail="Group not found"
|
|
||||||
)
|
|
||||||
|
|
||||||
clients = await admin.get_clients()
|
|
||||||
return templates.TemplateResponse(
|
|
||||||
request,
|
|
||||||
"secrets/partials/group_detail.html.j2",
|
|
||||||
{
|
|
||||||
"name": group.group_name,
|
|
||||||
"description": group.description,
|
|
||||||
"clients": clients,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
@app.delete("/secrets/group/{name}")
|
@app.delete("/secrets/group/{name}")
|
||||||
async def delete_secret_group(
|
async def delete_secret_group(
|
||||||
request: Request,
|
request: Request,
|
||||||
@ -266,11 +204,15 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
|
|||||||
|
|
||||||
await admin.delete_secret_group(name)
|
await admin.delete_secret_group(name)
|
||||||
|
|
||||||
headers = {"Hx-Refresh": "true"}
|
new_path = "/secrets/group/"
|
||||||
|
if group.parent_group:
|
||||||
|
new_path = os.path.join(new_path, group.parent_group.path)
|
||||||
|
headers = {"Hx-Redirect": new_path}
|
||||||
|
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
request,
|
request,
|
||||||
"secrets/partials/default_detail.html.j2",
|
"secrets/partials/redirect.html.j2",
|
||||||
|
{"destination": new_path},
|
||||||
headers=headers,
|
headers=headers,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -288,6 +230,7 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
|
|||||||
description=group.description,
|
description=group.description,
|
||||||
parent_group=group.parent_group,
|
parent_group=group.parent_group,
|
||||||
)
|
)
|
||||||
|
|
||||||
headers = {"Hx-Refresh": "true"}
|
headers = {"Hx-Refresh": "true"}
|
||||||
|
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
@ -360,8 +303,7 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
|
|||||||
request,
|
request,
|
||||||
"secrets/partials/group_detail.html.j2",
|
"secrets/partials/group_detail.html.j2",
|
||||||
{
|
{
|
||||||
"name": group.group_name,
|
"group": group,
|
||||||
"description": group.description,
|
|
||||||
"clients": clients,
|
"clients": clients,
|
||||||
},
|
},
|
||||||
headers=headers,
|
headers=headers,
|
||||||
@ -449,7 +391,6 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
|
|||||||
secret: Annotated[CreateSecret, Form()],
|
secret: Annotated[CreateSecret, Form()],
|
||||||
):
|
):
|
||||||
"""Create secret in group."""
|
"""Create secret in group."""
|
||||||
LOG.info("secret: %s", secret.model_dump_json(indent=2))
|
|
||||||
if secret.value:
|
if secret.value:
|
||||||
value = secret.value
|
value = secret.value
|
||||||
else:
|
else:
|
||||||
@ -457,24 +398,14 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
|
|||||||
|
|
||||||
await admin.add_secret(secret.name, value, secret.clients, group=name)
|
await admin.add_secret(secret.name, value, secret.clients, group=name)
|
||||||
|
|
||||||
headers = {"Hx-Refresh": "true"}
|
new_path = f"/secrets/secret/{secret.name}"
|
||||||
new_secret = await admin.get_secret(secret.name)
|
|
||||||
groups = await admin.get_secret_groups()
|
|
||||||
events = await admin.get_audit_log_detailed(limit=10, secret_name=secret.name)
|
|
||||||
|
|
||||||
if not new_secret:
|
headers = {"Hx-Redirect": new_path}
|
||||||
raise HTTPException(
|
|
||||||
status_code=status.HTTP_404_NOT_FOUND, detail="Secret not found"
|
|
||||||
)
|
|
||||||
|
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
request,
|
request,
|
||||||
"secrets/partials/tree_detail.html.j2",
|
"secrets/partials/redirect.html.j2",
|
||||||
{
|
{"destination": new_path},
|
||||||
"secret": new_secret,
|
|
||||||
"groups": groups,
|
|
||||||
"events": events,
|
|
||||||
},
|
|
||||||
headers=headers,
|
headers=headers,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -493,23 +424,15 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
|
|||||||
|
|
||||||
await admin.add_secret(secret.name, value, secret.clients, group=None)
|
await admin.add_secret(secret.name, value, secret.clients, group=None)
|
||||||
|
|
||||||
headers = {"Hx-Refresh": "true"}
|
new_path = f"/secrets/secret/{secret.name}"
|
||||||
new_secret = await admin.get_secret(secret.name)
|
|
||||||
groups = await admin.get_secret_groups()
|
|
||||||
events = await admin.get_audit_log_detailed(limit=10, secret_name=secret.name)
|
|
||||||
|
|
||||||
if not new_secret:
|
headers = {"Hx-Redirect": new_path}
|
||||||
raise HTTPException(
|
|
||||||
status_code=status.HTTP_404_NOT_FOUND, detail="Secret not found"
|
|
||||||
)
|
|
||||||
|
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
request,
|
request,
|
||||||
"secrets/partials/tree_detail.html.j2",
|
"secrets/partials/redirect.html.j2",
|
||||||
{
|
{
|
||||||
"secret": new_secret,
|
"destination": new_path,
|
||||||
"groups": groups,
|
|
||||||
"events": events,
|
|
||||||
},
|
},
|
||||||
headers=headers,
|
headers=headers,
|
||||||
)
|
)
|
||||||
@ -598,12 +521,23 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
|
|||||||
admin: Annotated[AdminBackend, Depends(dependencies.get_admin_backend)],
|
admin: Annotated[AdminBackend, Depends(dependencies.get_admin_backend)],
|
||||||
):
|
):
|
||||||
"""Delete a secret."""
|
"""Delete a secret."""
|
||||||
|
secret = await admin.get_secret(name)
|
||||||
|
if not secret:
|
||||||
|
raise HTTPException(status_code=404, detail="Secret not found")
|
||||||
|
new_path = "/secrets/group/"
|
||||||
|
if secret.group:
|
||||||
|
secret_group = await admin.get_secret_group(secret.group)
|
||||||
|
if secret_group:
|
||||||
|
new_path = os.path.join("/secrets/group", secret_group.path)
|
||||||
|
|
||||||
await admin.delete_secret(name)
|
await admin.delete_secret(name)
|
||||||
headers = {"Hx-Refresh": "true"}
|
headers = {"Hx-Redirect": new_path}
|
||||||
|
# headers["HX-Push-Url"] = request.url.path
|
||||||
|
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
request,
|
request,
|
||||||
"secrets/partials/default_detail.html.j2",
|
"secrets/partials/redirect.html.j2",
|
||||||
|
{"destination": new_path},
|
||||||
headers=headers,
|
headers=headers,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user