Allow group updates
This commit is contained in:
@ -2,126 +2,259 @@
|
||||
|
||||
<div class="px-4 pt-6">
|
||||
<div class="py-8 px-4 mt-4 mx-auto max-w-screen-xl text-center lg:py-16">
|
||||
|
||||
<h1 class="mb-4 text-4xl font-extrabold leading-none tracking-tight text-gray-900 md:text-5xl lg:text-6xl dark:text-white">Welcome to Sshecret</h1>
|
||||
|
||||
<h1
|
||||
class="mb-4 text-4xl font-extrabold leading-none tracking-tight text-gray-900 md:text-5xl lg:text-6xl dark:text-white"
|
||||
>
|
||||
Welcome to Sshecret
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div class="grid w-full grid-cols-1 gap-4 mt-4 xl:grid-cols-2 2xl:grid-cols-3">
|
||||
<div class="items-center justify-between p-4 bg-white border border-gray-200 rounded-lg shadow-sm sm:flex dark:border-gray-700 sm:p-6 dark:bg-gray-800" id="dashboard-stats-panel">
|
||||
<div
|
||||
class="grid w-full grid-cols-1 gap-4 mt-4 xl:grid-cols-2 2xl:grid-cols-3"
|
||||
>
|
||||
<div
|
||||
class="items-center justify-between p-4 bg-white border border-gray-200 rounded-lg shadow-sm sm:flex dark:border-gray-700 sm:p-6 dark:bg-gray-800"
|
||||
id="dashboard-stats-panel"
|
||||
>
|
||||
<div class="w-full">
|
||||
<h3 class="text-base text-gray-500 dark:text-gray-400">Stats</h3>
|
||||
<dl class="max-w-md text-gray-900 divide-y divide-gray-200 dark:text-white dark:divide-gray-700">
|
||||
<dl
|
||||
class="max-w-md text-gray-900 divide-y divide-gray-200 dark:text-white dark:divide-gray-700"
|
||||
>
|
||||
<div class="flex flex-col pb-3">
|
||||
<dt class="mb-1 text-gray-500 text-xs dark:text-gray-400">Clients</dt>
|
||||
<dd class="text-lg font-semibold" id="stats-client-count">{{ stats.clients }}</dd>
|
||||
<dt class="mb-1 text-gray-500 text-xs dark:text-gray-400">
|
||||
Clients
|
||||
</dt>
|
||||
<dd class="text-lg font-semibold" id="stats-client-count">
|
||||
{{ stats.clients }}
|
||||
</dd>
|
||||
</div>
|
||||
<div class="flex flex-col py-3">
|
||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Secrets</dt>
|
||||
<dd class="text-lg font-semibold" id="stats-secret-count">{{ stats.secrets }}</dd>
|
||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">
|
||||
Secrets
|
||||
</dt>
|
||||
<dd class="text-lg font-semibold" id="stats-secret-count">
|
||||
{{ stats.secrets }}
|
||||
</dd>
|
||||
</div>
|
||||
<div class="flex flex-col py-3">
|
||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Audit Events</dt>
|
||||
<dd class="text-lg font-semibold" id="stats-audit-count">{{ stats.audit_events }}</dd>
|
||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">
|
||||
Audit Events
|
||||
</dt>
|
||||
<dd class="text-lg font-semibold" id="stats-audit-count">
|
||||
{{ stats.audit_events }}
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
<div class="items-center 2xl: col-span-2 xl:col-span-2 justify-between p-4 bg-white border border-gray-200 rounded-lg shadow-sm sm:flex dark:border-gray-700 sm:p-6 dark:bg-gray-800">
|
||||
<div
|
||||
class="items-center 2xl: col-span-2 xl:col-span-2 justify-between p-4 bg-white border border-gray-200 rounded-lg shadow-sm sm:flex dark:border-gray-700 sm:p-6 dark:bg-gray-800"
|
||||
>
|
||||
<div class="w-full">
|
||||
<h3 class="text-base font-normal text-gray-500 dark:text-gray-400">Last Login Events</h3>
|
||||
<h3 class="text-base font-normal text-gray-500 dark:text-gray-400">
|
||||
Last Login Events
|
||||
</h3>
|
||||
{% if last_login_events.total > 0 %}
|
||||
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-600" id="last-login-events">
|
||||
<table
|
||||
class="min-w-full divide-y divide-gray-200 dark:divide-gray-600"
|
||||
id="last-login-events"
|
||||
>
|
||||
<thead class="bg-gray-50 dark:bg-gray-700">
|
||||
<tr>
|
||||
<th scope="col" class="p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white">Timestamp</th>
|
||||
<th scope="col" class="p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white">Subsystem</th>
|
||||
<th scope="col" class="p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white">Client/Username</th>
|
||||
<th scope="col" class="p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white">Origin</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white"
|
||||
>
|
||||
Timestamp
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white"
|
||||
>
|
||||
Subsystem
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white"
|
||||
>
|
||||
Client/Username
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white"
|
||||
>
|
||||
Origin
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white dark:bg-gray-800">
|
||||
{% for entry in last_login_events.results | list %}
|
||||
<tr class="{{ loop.cycle('', 'bg-gray-50 dark:bg-gray-700 ') }}hover:bg-gray-100 dark:hover:bg-gray-700" id="login-entry-{{ entry.id }}">
|
||||
<td class="p-4 text-sm font-normal text-gray-900 whitespace-nowrap dark:text-white">
|
||||
<p>{{ entry.timestamp }}<button data-popover-target="popover-login-entry-{{ entry.id }}" data-popover-placement="bottom-end" type="button" id="btn-popover-login-entry-{{ entry.id }}"><svg class="w-4 h-4 ms-2 text-gray-400 hover:text-gray-500" aria-hidden="true" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-3a1 1 0 00-.867.5 1 1 0 11-1.731-1A3 3 0 0113 8a3.001 3.001 0 01-2 2.83V11a1 1 0 11-2 0v-1a1 1 0 011-1 1 1 0 100-2zm0 8a1 1 0 100-2 1 1 0 000 2z" clip-rule="evenodd"></path>
|
||||
</svg><span class="sr-only">Show information</span></button>
|
||||
<tr
|
||||
class="{{ loop.cycle('', 'bg-gray-50 dark:bg-gray-700 ') }}hover:bg-gray-100 dark:hover:bg-gray-700"
|
||||
id="login-entry-{{ entry.id }}"
|
||||
>
|
||||
<td
|
||||
class="p-4 text-sm font-normal text-gray-900 whitespace-nowrap dark:text-white"
|
||||
>
|
||||
<p>
|
||||
{{ entry.timestamp }}<button
|
||||
data-popover-target="popover-login-entry-{{ entry.id }}"
|
||||
data-popover-placement="bottom-end"
|
||||
type="button"
|
||||
id="btn-popover-login-entry-{{ entry.id }}"
|
||||
>
|
||||
<svg
|
||||
class="w-4 h-4 ms-2 text-gray-400 hover:text-gray-500"
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-3a1 1 0 00-.867.5 1 1 0 11-1.731-1A3 3 0 0113 8a3.001 3.001 0 01-2 2.83V11a1 1 0 11-2 0v-1a1 1 0 011-1 1 1 0 100-2zm0 8a1 1 0 100-2 1 1 0 000 2z"
|
||||
clip-rule="evenodd"
|
||||
></path></svg
|
||||
><span class="sr-only">Show information</span>
|
||||
</button>
|
||||
</p>
|
||||
|
||||
<div data-popover id="popover-login-entry-{{entry.id}}" role="tooltip" class="absolute z-10 invisible inline-block text-sm text-gray-500 transition-opacity duration-300 bg-white border border-gray-200 rounded-lg shadow-xs opacity-0 w-80 dark:bg-gray-800 dark:border-gray-600 dark:text-gray-400 popover-login-entry">
|
||||
<dl class="max-w-md text-gray-900 divide-y divide-gray-200 dark:text-white dark:divide-gray-700 px-2 py-2">
|
||||
<div
|
||||
data-popover
|
||||
id="popover-login-entry-{{entry.id}}"
|
||||
role="tooltip"
|
||||
class="absolute z-10 invisible inline-block text-sm text-gray-500 transition-opacity duration-300 bg-white border border-gray-200 rounded-lg shadow-xs opacity-0 w-80 dark:bg-gray-800 dark:border-gray-600 dark:text-gray-400 popover-login-entry"
|
||||
>
|
||||
<dl
|
||||
class="max-w-md text-gray-900 divide-y divide-gray-200 dark:text-white dark:divide-gray-700 px-2 py-2"
|
||||
>
|
||||
<div class="flex flex-col pb-3">
|
||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">ID</dt>
|
||||
<dt
|
||||
class="mb-1 text-gray-500 md:text-xs dark:text-gray-400"
|
||||
>
|
||||
ID
|
||||
</dt>
|
||||
<dd class="text-xs font-semibold">{{ entry.id }}</dd>
|
||||
</div>
|
||||
<div class="flex flex-col pb-3">
|
||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Subsystem</dt>
|
||||
<dd class="text-xs font-semibold">{{ entry.subsystem }}</dd>
|
||||
<dt
|
||||
class="mb-1 text-gray-500 md:text-xs dark:text-gray-400"
|
||||
>
|
||||
Subsystem
|
||||
</dt>
|
||||
<dd class="text-xs font-semibold">
|
||||
{{ entry.subsystem }}
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col pb-3">
|
||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Timestamp</dt>
|
||||
<dd class="text-xs font-semibold">{{ entry.timestamp }}</dd>
|
||||
<dt
|
||||
class="mb-1 text-gray-500 md:text-xs dark:text-gray-400"
|
||||
>
|
||||
Timestamp
|
||||
</dt>
|
||||
<dd class="text-xs font-semibold">
|
||||
{{ entry.timestamp }}
|
||||
</dd>
|
||||
</div>
|
||||
<div class="flex flex-col pb-3">
|
||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Operation</dt>
|
||||
<dd class="text-xs font-semibold">{{ entry.operation }}</dd>
|
||||
<dt
|
||||
class="mb-1 text-gray-500 md:text-xs dark:text-gray-400"
|
||||
>
|
||||
Operation
|
||||
</dt>
|
||||
<dd class="text-xs font-semibold">
|
||||
{{ entry.operation }}
|
||||
</dd>
|
||||
</div>
|
||||
<div class="flex flex-col pb-3">
|
||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Client ID</dt>
|
||||
<dd class="text-xs font-semibold">{{ entry.client_id }}</dd>
|
||||
<dt
|
||||
class="mb-1 text-gray-500 md:text-xs dark:text-gray-400"
|
||||
>
|
||||
Client ID
|
||||
</dt>
|
||||
<dd class="text-xs font-semibold">
|
||||
{{ entry.client_id }}
|
||||
</dd>
|
||||
</div>
|
||||
<div class="flex flex-col pb-3">
|
||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Client Name</dt>
|
||||
<dd class="text-xs font-semibold">{{ entry.client_name }}</dd>
|
||||
<dt
|
||||
class="mb-1 text-gray-500 md:text-xs dark:text-gray-400"
|
||||
>
|
||||
Client Name
|
||||
</dt>
|
||||
<dd class="text-xs font-semibold">
|
||||
{{ entry.client_name }}
|
||||
</dd>
|
||||
</div>
|
||||
<div class="flex flex-col pb-3">
|
||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Secret ID</dt>
|
||||
<dd class="text-xs font-semibold">{{ entry.secret_id }}</dd>
|
||||
<dt
|
||||
class="mb-1 text-gray-500 md:text-xs dark:text-gray-400"
|
||||
>
|
||||
Secret ID
|
||||
</dt>
|
||||
<dd class="text-xs font-semibold">
|
||||
{{ entry.secret_id }}
|
||||
</dd>
|
||||
</div>
|
||||
<div class="flex flex-col pb-3">
|
||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Secret Name</dt>
|
||||
<dd class="text-xs font-semibold">{{ entry.secret_name }}</dd>
|
||||
<dt
|
||||
class="mb-1 text-gray-500 md:text-xs dark:text-gray-400"
|
||||
>
|
||||
Secret Name
|
||||
</dt>
|
||||
<dd class="text-xs font-semibold">
|
||||
{{ entry.secret_name }}
|
||||
</dd>
|
||||
</div>
|
||||
<div class="flex flex-col pb-3">
|
||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Message</dt>
|
||||
<dt
|
||||
class="mb-1 text-gray-500 md:text-xs dark:text-gray-400"
|
||||
>
|
||||
Message
|
||||
</dt>
|
||||
<dd class="text-xs font-semibold">{{ entry.message }}</dd>
|
||||
</div>
|
||||
<div class="flex flex-col pb-3">
|
||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Origin</dt>
|
||||
<dt
|
||||
class="mb-1 text-gray-500 md:text-xs dark:text-gray-400"
|
||||
>
|
||||
Origin
|
||||
</dt>
|
||||
<dd class="text-xs font-semibold">{{ entry.origin }}</dd>
|
||||
</div>
|
||||
{% if entry.data %}
|
||||
{% for key, value in entry.data.items() %}
|
||||
{% if entry.data %} {% for key, value in entry.data.items()
|
||||
%}
|
||||
<div class="flex flex-col pb-3">
|
||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">{{ key | capitalize }}</dt>
|
||||
<dt
|
||||
class="mb-1 text-gray-500 md:text-xs dark:text-gray-400"
|
||||
>
|
||||
{{ key | capitalize }}
|
||||
</dt>
|
||||
<dd class="text-xs font-semibold">{{ value }}</dd>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% endfor %} {% endif %}
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
|
||||
<td class="p-4 text-sm font-normal text-gray-900 whitespace-nowrap dark:text-white">
|
||||
<td
|
||||
class="p-4 text-sm font-normal text-gray-900 whitespace-nowrap dark:text-white"
|
||||
>
|
||||
{{ entry.subsystem }}
|
||||
</td>
|
||||
|
||||
<td class="p-4 text-sm font-normal text-gray-900 whitespace-nowrap dark:text-white">
|
||||
{% if entry.client_name %}
|
||||
{{ entry.client_name }}
|
||||
{% elif entry.data.username %}
|
||||
{{ entry.data.username }}
|
||||
{% endif %}
|
||||
|
||||
<td
|
||||
class="p-4 text-sm font-normal text-gray-900 whitespace-nowrap dark:text-white"
|
||||
>
|
||||
{% if entry.client_name %} {{ entry.client_name }} {% elif
|
||||
entry.data.username %} {{ entry.data.username }} {% endif %}
|
||||
</td>
|
||||
|
||||
<td class="p-4 text-sm font-normal text-gray-900 whitespace-nowrap dark:text-white">
|
||||
<td
|
||||
class="p-4 text-sm font-normal text-gray-900 whitespace-nowrap dark:text-white"
|
||||
>
|
||||
{{ entry.origin }}
|
||||
</td>
|
||||
</tr>
|
||||
@ -133,96 +266,211 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="items-center 2xl:col-span-3 xl:col-span-3 justify-between p-4 bg-white border border-gray-200 rounded-lg shadow-sm sm:flex dark:border-gray-700 sm:p-6 dark:bg-gray-800">
|
||||
<div
|
||||
class="items-center 2xl:col-span-3 xl:col-span-3 justify-between p-4 bg-white border border-gray-200 rounded-lg shadow-sm sm:flex dark:border-gray-700 sm:p-6 dark:bg-gray-800"
|
||||
>
|
||||
<div class="w-full">
|
||||
<h3 class="text-base font-normal text-gray-500 dark:text-gray-400">Last Audit Events</h3>
|
||||
<h3 class="text-base font-normal text-gray-500 dark:text-gray-400">
|
||||
Last Audit Events
|
||||
</h3>
|
||||
{% if last_audit_events.total > 0 %}
|
||||
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-600" id="last-audit-events">
|
||||
<table
|
||||
class="min-w-full divide-y divide-gray-200 dark:divide-gray-600"
|
||||
id="last-audit-events"
|
||||
>
|
||||
<thead class="bg-gray-50 dark:bg-gray-700">
|
||||
<tr>
|
||||
<th scope="col" class="p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white">Timestamp</th>
|
||||
<th scope="col" class="p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white">Subsystem</th>
|
||||
<th scope="col" class="p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white">Message</th>
|
||||
<th scope="col" class="p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white">Origin</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white"
|
||||
>
|
||||
Timestamp
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white"
|
||||
>
|
||||
Subsystem
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white"
|
||||
>
|
||||
Message
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white"
|
||||
>
|
||||
Origin
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white dark:bg-gray-800">
|
||||
{% for entry in last_audit_events.results | list %}
|
||||
<tr class="{{ loop.cycle('', 'bg-gray-50 dark:bg-gray-700 ') }}hover:bg-gray-100 dark:hover:bg-gray-700" id="login-entry-{{ entry.id }}">
|
||||
<td class="p-4 text-sm font-normal text-gray-900 whitespace-nowrap dark:text-white">
|
||||
<p>{{ entry.timestamp }}<button data-popover-target="popover-audit-entry-{{ entry.id }}" data-popover-placement="bottom-end" type="button"><svg class="w-4 h-4 ms-2 text-gray-400 hover:text-gray-500" aria-hidden="true" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-3a1 1 0 00-.867.5 1 1 0 11-1.731-1A3 3 0 0113 8a3.001 3.001 0 01-2 2.83V11a1 1 0 11-2 0v-1a1 1 0 011-1 1 1 0 100-2zm0 8a1 1 0 100-2 1 1 0 000 2z" clip-rule="evenodd"></path>
|
||||
</svg><span class="sr-only">Show information</span></button></p>
|
||||
<tr
|
||||
class="{{ loop.cycle('', 'bg-gray-50 dark:bg-gray-700 ') }}hover:bg-gray-100 dark:hover:bg-gray-700"
|
||||
id="login-entry-{{ entry.id }}"
|
||||
>
|
||||
<td
|
||||
class="p-4 text-sm font-normal text-gray-900 whitespace-nowrap dark:text-white"
|
||||
>
|
||||
<p>
|
||||
{{ entry.timestamp }}<button
|
||||
data-popover-target="popover-audit-entry-{{ entry.id }}"
|
||||
data-popover-placement="bottom-end"
|
||||
type="button"
|
||||
>
|
||||
<svg
|
||||
class="w-4 h-4 ms-2 text-gray-400 hover:text-gray-500"
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-3a1 1 0 00-.867.5 1 1 0 11-1.731-1A3 3 0 0113 8a3.001 3.001 0 01-2 2.83V11a1 1 0 11-2 0v-1a1 1 0 011-1 1 1 0 100-2zm0 8a1 1 0 100-2 1 1 0 000 2z"
|
||||
clip-rule="evenodd"
|
||||
></path></svg
|
||||
><span class="sr-only">Show information</span>
|
||||
</button>
|
||||
</p>
|
||||
|
||||
<div data-popover id="popover-audit-entry-{{entry.id}}" role="tooltip" class="absolute z-10 invisible inline-block text-sm text-gray-500 transition-opacity duration-300 bg-white border border-gray-200 rounded-lg shadow-xs opacity-0 w-80 dark:bg-gray-800 dark:border-gray-600 dark:text-gray-400">
|
||||
<dl class="max-w-md text-gray-900 divide-y divide-gray-200 dark:text-white dark:divide-gray-700 px-2 py-2">
|
||||
<div
|
||||
data-popover
|
||||
id="popover-audit-entry-{{entry.id}}"
|
||||
role="tooltip"
|
||||
class="absolute z-10 invisible inline-block text-sm text-gray-500 transition-opacity duration-300 bg-white border border-gray-200 rounded-lg shadow-xs opacity-0 w-80 dark:bg-gray-800 dark:border-gray-600 dark:text-gray-400"
|
||||
>
|
||||
<dl
|
||||
class="max-w-md text-gray-900 divide-y divide-gray-200 dark:text-white dark:divide-gray-700 px-2 py-2"
|
||||
>
|
||||
<div class="flex flex-col pb-3">
|
||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">ID</dt>
|
||||
<dt
|
||||
class="mb-1 text-gray-500 md:text-xs dark:text-gray-400"
|
||||
>
|
||||
ID
|
||||
</dt>
|
||||
<dd class="text-xs font-semibold">{{ entry.id }}</dd>
|
||||
</div>
|
||||
<div class="flex flex-col pb-3">
|
||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Subsystem</dt>
|
||||
<dd class="text-xs font-semibold">{{ entry.subsystem }}</dd>
|
||||
<dt
|
||||
class="mb-1 text-gray-500 md:text-xs dark:text-gray-400"
|
||||
>
|
||||
Subsystem
|
||||
</dt>
|
||||
<dd class="text-xs font-semibold">
|
||||
{{ entry.subsystem }}
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col pb-3">
|
||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Timestamp</dt>
|
||||
<dd class="text-xs font-semibold">{{ entry.timestamp }}</dd>
|
||||
<dt
|
||||
class="mb-1 text-gray-500 md:text-xs dark:text-gray-400"
|
||||
>
|
||||
Timestamp
|
||||
</dt>
|
||||
<dd class="text-xs font-semibold">
|
||||
{{ entry.timestamp }}
|
||||
</dd>
|
||||
</div>
|
||||
<div class="flex flex-col pb-3">
|
||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Operation</dt>
|
||||
<dd class="text-xs font-semibold">{{ entry.operation }}</dd>
|
||||
<dt
|
||||
class="mb-1 text-gray-500 md:text-xs dark:text-gray-400"
|
||||
>
|
||||
Operation
|
||||
</dt>
|
||||
<dd class="text-xs font-semibold">
|
||||
{{ entry.operation }}
|
||||
</dd>
|
||||
</div>
|
||||
<div class="flex flex-col pb-3">
|
||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Client ID</dt>
|
||||
<dd class="text-xs font-semibold">{{ entry.client_id }}</dd>
|
||||
<dt
|
||||
class="mb-1 text-gray-500 md:text-xs dark:text-gray-400"
|
||||
>
|
||||
Client ID
|
||||
</dt>
|
||||
<dd class="text-xs font-semibold">
|
||||
{{ entry.client_id }}
|
||||
</dd>
|
||||
</div>
|
||||
<div class="flex flex-col pb-3">
|
||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Client Name</dt>
|
||||
<dd class="text-xs font-semibold">{{ entry.client_name }}</dd>
|
||||
<dt
|
||||
class="mb-1 text-gray-500 md:text-xs dark:text-gray-400"
|
||||
>
|
||||
Client Name
|
||||
</dt>
|
||||
<dd class="text-xs font-semibold">
|
||||
{{ entry.client_name }}
|
||||
</dd>
|
||||
</div>
|
||||
<div class="flex flex-col pb-3">
|
||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Secret ID</dt>
|
||||
<dd class="text-xs font-semibold">{{ entry.secret_id }}</dd>
|
||||
<dt
|
||||
class="mb-1 text-gray-500 md:text-xs dark:text-gray-400"
|
||||
>
|
||||
Secret ID
|
||||
</dt>
|
||||
<dd class="text-xs font-semibold">
|
||||
{{ entry.secret_id }}
|
||||
</dd>
|
||||
</div>
|
||||
<div class="flex flex-col pb-3">
|
||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Secret Name</dt>
|
||||
<dd class="text-xs font-semibold">{{ entry.secret_name }}</dd>
|
||||
<dt
|
||||
class="mb-1 text-gray-500 md:text-xs dark:text-gray-400"
|
||||
>
|
||||
Secret Name
|
||||
</dt>
|
||||
<dd class="text-xs font-semibold">
|
||||
{{ entry.secret_name }}
|
||||
</dd>
|
||||
</div>
|
||||
<div class="flex flex-col pb-3">
|
||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Message</dt>
|
||||
<dt
|
||||
class="mb-1 text-gray-500 md:text-xs dark:text-gray-400"
|
||||
>
|
||||
Message
|
||||
</dt>
|
||||
<dd class="text-xs font-semibold">{{ entry.message }}</dd>
|
||||
</div>
|
||||
<div class="flex flex-col pb-3">
|
||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Origin</dt>
|
||||
<dt
|
||||
class="mb-1 text-gray-500 md:text-xs dark:text-gray-400"
|
||||
>
|
||||
Origin
|
||||
</dt>
|
||||
<dd class="text-xs font-semibold">{{ entry.origin }}</dd>
|
||||
</div>
|
||||
{% if entry.data %}
|
||||
{% for key, value in entry.data.items() %}
|
||||
{% if entry.data %} {% for key, value in entry.data.items()
|
||||
%}
|
||||
<div class="flex flex-col pb-3">
|
||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">{{ key | capitalize }}</dt>
|
||||
<dt
|
||||
class="mb-1 text-gray-500 md:text-xs dark:text-gray-400"
|
||||
>
|
||||
{{ key | capitalize }}
|
||||
</dt>
|
||||
<dd class="text-xs font-semibold">{{ value }}</dd>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% endfor %} {% endif %}
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
|
||||
<td class="p-4 text-sm font-normal text-gray-900 whitespace-nowrap dark:text-white">
|
||||
<td
|
||||
class="p-4 text-sm font-normal text-gray-900 whitespace-nowrap dark:text-white"
|
||||
>
|
||||
{{ entry.subsystem }}
|
||||
</td>
|
||||
|
||||
<td class="p-4 text-sm font-normal text-gray-900 whitespace-nowrap dark:text-white">
|
||||
<td
|
||||
class="p-4 text-sm font-normal text-gray-900 whitespace-nowrap dark:text-white"
|
||||
>
|
||||
{{ entry.message }}
|
||||
</td>
|
||||
|
||||
<td class="p-4 text-sm font-normal text-gray-900 whitespace-nowrap dark:text-white">
|
||||
<td
|
||||
class="p-4 text-sm font-normal text-gray-900 whitespace-nowrap dark:text-white"
|
||||
>
|
||||
{{ entry.origin }}
|
||||
</td>
|
||||
</tr>
|
||||
@ -235,8 +483,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% include '/dashboard/drawer_client_create_dashboard.html.j2' %}
|
||||
{% include '/dashboard/drawer_secret_create_dashboard.html.j2' %}
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@ -50,13 +50,18 @@
|
||||
</sl-details>
|
||||
{% if groups.groups %}
|
||||
<sl-details summary="Group">
|
||||
<form>
|
||||
<form
|
||||
hx-put="/secrets/set-group/{{ secret.name }}"
|
||||
hx-target="#secretdetails"
|
||||
hx-swap="OuterHTML"
|
||||
hx-indicator=".secret-spinner"
|
||||
>
|
||||
<div class="flex w-full">
|
||||
<div class="relative w-full">
|
||||
<select id="group" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
|
||||
<select id="group_name" name="group_name" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
|
||||
<option value="__ROOT">Ungrouped</option>
|
||||
{% for group in groups.groups %}
|
||||
<option value="{{ group.group_name }}" {% if group.name == secret.group -%}selected{% endif %}>{{ group.group_name }}</option>
|
||||
<option value="{{ group.group_name }}" {% if group.name == secret.group -%}selected{% endif %}>{{ group.path }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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] = []
|
||||
|
||||
@ -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] = []
|
||||
|
||||
Reference in New Issue
Block a user