diff --git a/packages/sshecret-admin/src/sshecret_admin/frontend/templates/dashboard.html b/packages/sshecret-admin/src/sshecret_admin/frontend/templates/dashboard.html index 55bcf75..800e0c5 100644 --- a/packages/sshecret-admin/src/sshecret_admin/frontend/templates/dashboard.html +++ b/packages/sshecret-admin/src/sshecret_admin/frontend/templates/dashboard.html @@ -1,242 +1,488 @@ {% extends "/dashboard/_base.html" %} {% block content %}
-
- -

Welcome to Sshecret

+
+

+ Welcome to Sshecret +

+
+
+
+
+

Stats

+
+
+
+ Clients +
+
+ {{ stats.clients }} +
+
+
+
+ Secrets +
+
+ {{ stats.secrets }} +
+
+
+
+ Audit Events +
+
+ {{ stats.audit_events }} +
+
+
+
+
+
+

+ Last Login Events +

+ {% if last_login_events.total > 0 %} + + + + + + + + + + + {% for entry in last_login_events.results | list %} + + + + {% endfor %} + +
+ Timestamp + + Subsystem + + Client/Username + + Origin +
+

+ {{ entry.timestamp }} +

-
-
-
-

Stats

-
+ -
-
-
-

Last Login Events

- {% if last_login_events.total > 0 %} - - - - - - - - - - - {% for entry in last_login_events.results | list %} - - - -
-
Timestamp
-
{{ entry.timestamp }}
-
-
-
Operation
-
{{ entry.operation }}
-
-
-
Client ID
-
{{ entry.client_id }}
-
-
-
Client Name
-
{{ entry.client_name }}
-
-
-
Secret ID
-
{{ entry.secret_id }}
-
-
-
Secret Name
-
{{ entry.secret_name }}
-
-
-
Message
-
{{ entry.message }}
-
-
-
Origin
-
{{ entry.origin }}
-
- {% if entry.data %} - {% for key, value in entry.data.items() %} -
-
{{ key | capitalize }}
-
{{ value }}
-
- {% endfor %} - {% endif %} + - - - - - - - - - - - - - - {% endfor %} - -
TimestampSubsystemClient/UsernameOrigin
-

{{ entry.timestamp }} -

+
+
+ Operation +
+
+ {{ entry.operation }} +
+
+
+
+ Client ID +
+
+ {{ entry.client_id }} +
+
+
+
+ Client Name +
+
+ {{ entry.client_name }} +
+
+
+
+ Secret ID +
+
+ {{ entry.secret_id }} +
+
+
+
+ Secret Name +
+
+ {{ entry.secret_name }} +
+
+
+
+ Message +
+
{{ entry.message }}
+
+
+
+ Origin +
+
{{ entry.origin }}
+
+ {% if entry.data %} {% for key, value in entry.data.items() + %} +
+
+ {{ key | capitalize }} +
+
{{ value }}
+
+ {% endfor %} {% endif %} + + +
+ {{ entry.subsystem }} + + {% if entry.client_name %} {{ entry.client_name }} {% elif + entry.data.username %} {{ entry.data.username }} {% endif %} + - {{ entry.subsystem }} - - {% if entry.client_name %} - {{ entry.client_name }} - {% elif entry.data.username %} - {{ entry.data.username }} - {% endif %} - - - {{ entry.origin }} -
- {% else %} -

No entries

- {% endif %} -
-
-
-
-

Last Audit Events

- {% if last_audit_events.total > 0 %} - - - - - - - - - - - {% for entry in last_audit_events.results | list %} - - - - - - - - - - {% endfor %} - -
TimestampSubsystemMessageOrigin
-

{{ entry.timestamp }}

- - - - - - -
- {{ entry.subsystem }} - - {{ entry.message }} - - {{ entry.origin }} -
- {% else %} -

No entries

- {% endif %} -
-
+
+ {{ entry.origin }} +
+ {% else %} +

No entries

+ {% endif %} +
- {% include '/dashboard/drawer_client_create_dashboard.html.j2' %} - {% include '/dashboard/drawer_secret_create_dashboard.html.j2' %} +
+
+

+ Last Audit Events +

+ {% if last_audit_events.total > 0 %} + + + + + + + + + + + {% for entry in last_audit_events.results | list %} + + + + + + + + + + {% endfor %} + +
+ Timestamp + + Subsystem + + Message + + Origin +
+

+ {{ entry.timestamp }} +

+ + +
+ {{ entry.subsystem }} + + {{ entry.message }} + + {{ entry.origin }} +
+ {% else %} +

No entries

+ {% endif %} +
+
+
{% endblock %} diff --git a/packages/sshecret-admin/src/sshecret_admin/frontend/templates/secrets/partials/tree_detail.html.j2 b/packages/sshecret-admin/src/sshecret_admin/frontend/templates/secrets/partials/tree_detail.html.j2 index e106f1a..f85d080 100644 --- a/packages/sshecret-admin/src/sshecret_admin/frontend/templates/secrets/partials/tree_detail.html.j2 +++ b/packages/sshecret-admin/src/sshecret_admin/frontend/templates/secrets/partials/tree_detail.html.j2 @@ -50,13 +50,18 @@ {% if groups.groups %} -
+
- {% for group in groups.groups %} - + {% endfor %}
diff --git a/packages/sshecret-admin/src/sshecret_admin/frontend/views/secrets.py b/packages/sshecret-admin/src/sshecret_admin/frontend/views/secrets.py index 3a52446..d3ee5b4 100644 --- a/packages/sshecret-admin/src/sshecret_admin/frontend/views/secrets.py +++ b/packages/sshecret-admin/src/sshecret_admin/frontend/views/secrets.py @@ -97,7 +97,7 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter: ): """Get partial secret detail.""" secret = await admin.get_secret(name) - groups = await admin.get_secret_groups() + groups = await admin.get_secret_groups(flat=True) events = await admin.get_audit_log_detailed(limit=10, secret_name=name) if not secret: @@ -183,6 +183,49 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter: headers=headers, ) + @app.put("/secrets/set-group/{name}") + async def set_secret_group( + request: Request, + name: str, + admin: Annotated[AdminBackend, Depends(dependencies.get_admin_backend)], + group_name: Annotated[str, Form()], + ): + """Move a secret to a group.""" + secret = await admin.get_secret(name) + if not secret: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Secret not found" + ) + + if group_name == "__ROOT": + await admin.set_secret_group(name, None) + + else: + group = await admin.get_secret_group(group_name) + if not group: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Group not found" + ) + await admin.set_secret_group(name, group_name) + + groups = await admin.get_secret_groups() + events = await admin.get_audit_log_detailed(limit=10, secret_name=secret.name) + + secret = await admin.get_secret(name) + + headers = {"Hx-Refresh": "true"} + + return templates.TemplateResponse( + request, + "secrets/partials/tree_detail.html.j2", + { + "secret": secret, + "groups": groups, + "events": events, + }, + headers=headers, + ) + @app.put("/secrets/partial/group/{name}/description") async def update_group_description( request: Request, diff --git a/packages/sshecret-admin/src/sshecret_admin/services/admin_backend.py b/packages/sshecret-admin/src/sshecret_admin/services/admin_backend.py index d1371ae..559f06f 100644 --- a/packages/sshecret-admin/src/sshecret_admin/services/admin_backend.py +++ b/packages/sshecret-admin/src/sshecret_admin/services/admin_backend.py @@ -359,16 +359,26 @@ class AdminBackend: self, group_filter: str | None = None, regex: bool = True, + flat: bool = False, ) -> ClientSecretGroupList: """Get secret groups. The starting group can be filtered with the group_name argument, which may be a regular expression. + + Groups are returned in a tree, unless flat is True. """ all_secrets = await self.backend.get_detailed_secrets() secrets_mapping = {secret.name: secret for secret in all_secrets} with self.password_manager() as password_manager: - all_groups = password_manager.get_secret_groups(group_filter, regex=regex) + if flat: + all_groups = password_manager.get_secret_group_list( + group_filter, regex=regex + ) + else: + all_groups = password_manager.get_secret_groups( + group_filter, regex=regex + ) ungrouped = password_manager.get_ungrouped_secrets() group_result: list[ClientSecretGroup] = [] diff --git a/packages/sshecret-admin/src/sshecret_admin/services/keepass.py b/packages/sshecret-admin/src/sshecret_admin/services/keepass.py index 845a523..3f6ede7 100644 --- a/packages/sshecret-admin/src/sshecret_admin/services/keepass.py +++ b/packages/sshecret-admin/src/sshecret_admin/services/keepass.py @@ -160,6 +160,16 @@ class PasswordContext: secret_groups = [_kp_group_to_secret_group(group) for group in groups] return secret_groups + def get_secret_group_list(self, pattern: str | None = None, regex: bool = True) -> list[SecretGroup]: + """Get a flat list of groups.""" + if pattern: + return self.get_secret_groups(pattern, regex) + + groups = [ group for group in self.keepass.groups if not group.is_root_group ] + secret_groups = [_kp_group_to_secret_group(group) for group in groups] + return secret_groups + + def get_ungrouped_secrets(self) -> list[str]: """Get secrets without groups.""" entries: list[str] = []