Fix various issues

This commit is contained in:
2025-05-16 17:36:45 +02:00
parent 96ce402f9f
commit 6daceef913
13 changed files with 608 additions and 519 deletions

View File

@ -5,33 +5,33 @@
<td
class="p-4 text-sm font-normal text-gray-500 whitespace-nowrap dark:text-gray-400"
>
{{ client.name }}
{{-client.name -}}
</td>
<td
class="p-4 text-base font-medium text-gray-900 whitespace-nowrap dark:text-white"
>
{{ client.id }}
{{- client.id -}}
</td>
<td
class="max-w-sm p-4 overflow-hidden text-base font-normal text-gray-500 truncate xl:max-w-xs dark:text-gray-400"
>
{{ client.description }}
{{- client.description -}}
</td>
<td
class="max-w-sm p-4 text-base font-normal text-gray-500 truncate xl:max-w-xs dark:text-gray-400"
>
{{ client.secrets|length }}
{{- client.secrets|length -}}
</td>
<td
class="max-w-sm p-4 text-base font-normal text-gray-500 truncate xl:max-w-xs dark:text-gray-400"
>
{{ client.policies|join(', ') }}
{{- client.policies|join(', ') -}}
</td>
<td class="p-4 space-x-2 whitespace-nowrap">
<button
type="button"
id="updateClientButton"
id="updateClientButton-{{ client.id }}"
data-drawer-target="drawer-update-client-{{ client.id }}"
data-drawer-show="drawer-update-client-{{ client.id }}"
aria-controls="drawer-update-client-{{ client.id }}"
@ -57,7 +57,7 @@
</button>
<button
type="button"
id="deleteClientButton"
id="deleteClientButton-{{ client.id }}"
data-drawer-target="drawer-delete-client-{{ client.id }}"
data-drawer-show="drawer-delete-client-{{ client.id }}"
aria-controls="drawer-delete-client-{{ client.id }}"

View File

@ -33,113 +33,6 @@
<span class="sr-only">Close menu</span>
</button>
<form hx-post="/clients/" hx-target="#clientContent">
<div class="space-y-4">
<div>
<label
for="name"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
>Name</label
>
<input
type="text"
name="name"
id="name"
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"
placeholder="Client name"
required=""
/>
</div>
<div>
<label
for="description"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
>Description</label
>
<input
type="text"
name="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"
placeholder="Client description"
/>
</div>
<div>
<label
for="sources"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
>Allowed subnets or IPs</label
>
<p
id="helper-text-explanation"
class="mt-2 text-sm text-gray-500 dark:text-gray-400"
>
Separate multiple entries with comma.
</p>
<input
type="text"
name="sources"
id="sources"
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"
placeholder="0.0.0.0/0"
value="0.0.0.0/0"
hx-post="/clients/validate/source"
hx-target="#clientSourceValidation"
/>
<span id="clientSourceValidation"></span>
</div>
<div>
<label
for="public_key"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
>Public Key</label
>
<textarea
id="public_key"
name="public_key"
rows="4"
class="block p-2.5 w-full text-sm text-gray-900 bg-gray-50 rounded-lg border border-gray-300 focus:ring-primary-500 focus:border-primary-500 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"
placeholder="Enter RSA SSH Public Key here"
hx-post="/clients/validate/public_key"
hx-target="#clientPublicKeyValidation"
></textarea>
<span id="clientPublicKeyValidation"></span>
</div>
<div
class="bottom-0 left-0 flex justify-center w-full pb-4 space-x-4 md:px-4 md:absolute"
>
<button
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"
>
Add Client
</button>
<button
type="button"
data-drawer-dismiss="drawer-create-client-default"
aria-controls="drawer-create-client-default"
class="inline-flex w-full justify-center text-gray-500 items-center bg-white hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-primary-300 rounded-lg border border-gray-200 text-sm font-medium px-5 py-2.5 hover:text-gray-900 focus:z-10 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-500 dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-gray-600"
>
<svg
aria-hidden="true"
class="w-5 h-5 -ml-1 sm:mr-1"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
></path>
</svg>
Cancel
</button>
</div>
</div>
{% include '/clients/drawer_client_create_inner.html.j2' %}
</form>
</div>

View File

@ -0,0 +1,108 @@
<div class="space-y-4">
<div>
<label
for="name"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
>Name</label
>
<input
type="text"
name="name"
id="name"
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"
placeholder="Client name"
required=""
/>
</div>
<div>
<label
for="description"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
>Description</label
>
<input
type="text"
name="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"
placeholder="Client description"
/>
</div>
<div>
<label
for="sources"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
>Allowed subnets or IPs</label
>
<p
id="helper-text-explanation"
class="mt-2 text-sm text-gray-500 dark:text-gray-400"
>
Separate multiple entries with comma.
</p>
<input
type="text"
name="sources"
id="sources"
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"
placeholder="0.0.0.0/0"
value="0.0.0.0/0"
hx-post="/clients/validate/source"
hx-target="#clientSourceValidation"
/>
<span id="clientSourceValidation"></span>
</div>
<div>
<label
for="public_key"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
>Public Key</label
>
<textarea
id="public_key"
name="public_key"
rows="4"
class="block p-2.5 w-full text-sm text-gray-900 bg-gray-50 rounded-lg border border-gray-300 focus:ring-primary-500 focus:border-primary-500 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"
placeholder="Enter RSA SSH Public Key here"
hx-post="/clients/validate/public_key"
hx-target="#clientPublicKeyValidation"
></textarea>
<span id="clientPublicKeyValidation"></span>
</div>
<div
class="bottom-0 left-0 flex justify-center w-full pb-4 space-x-4 md:px-4 md:absolute"
>
<button
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"
>
Add Client
</button>
<button
type="button"
data-drawer-dismiss="drawer-create-client-default"
aria-controls="drawer-create-client-default"
class="inline-flex w-full justify-center text-gray-500 items-center bg-white hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-primary-300 rounded-lg border border-gray-200 text-sm font-medium px-5 py-2.5 hover:text-gray-900 focus:z-10 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-500 dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-gray-600"
>
<svg
aria-hidden="true"
class="w-5 h-5 -ml-1 sm:mr-1"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
></path>
</svg>
Cancel
</button>
</div>
</div>

View File

@ -150,6 +150,7 @@
hx-delete="/clients/{{ client.id }}"
hx-confirm="Are you sure?"
hx-target="#clientContent"
id="delete-button-{{ client.id }}"
>
<svg

View File

@ -4,7 +4,7 @@
<div class="overflow-x-auto">
<div class="inline-block min-w-full align-middle">
<div class="overflow-hidden shadow">
<table class="min-w-full divide-y divide-gray-200 table-fixed dark:divide-gray-600">
<table class="min-w-full divide-y divide-gray-200 table-fixed dark:divide-gray-600" id="clientListTable">
<thead class="bg-gray-100 dark:bg-gray-700">
<tr>

View File

@ -5,34 +5,24 @@
<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 class="flex flex-col space-y-4 sm:flex-row sm:justify-center sm:space-y-0">
<button id="createClientButton" class="text-white bg-primary-700 hover:bg-primary-800 focus:ring-4 focus:ring-primary-300 font-medium rounded-lg text-sm px-3 py-2.5 dark:bg-primary-600 dark:hover:bg-primary-700 focus:outline-none dark:focus:ring-primary-800" type="button" data-drawer-target="drawer-create-client-default" data-drawer-show="drawer-create-client-default" aria-controls="drawer-create-client-default" data-drawer-placement="right">
Add new client
</button>
<button id="createSecretButton" class="text-white bg-primary-700 hover:bg-primary-800 focus:ring-4 focus:ring-primary-300 font-medium rounded-lg text-sm px-3 py-2.5 ms-2 dark:bg-primary-600 dark:hover:bg-primary-700 focus:outline-none dark:focus:ring-primary-800" type="button" data-drawer-target="drawer-create-secret-default" data-drawer-show="drawer-create-secret-default" aria-controls="drawer-create-secret-default" data-drawer-placement="right">
Add new secret
</button>
</div>
</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">
<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">
<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">{{ stats.clients }}</dd>
<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">{{ stats.secrets }}</dd>
<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">{{ stats.audit_events }}</dd>
<dd class="text-lg font-semibold" id="stats-audit-count">{{ stats.audit_events }}</dd>
</div>
</dl>
</div>
@ -41,7 +31,7 @@
<div class="w-full">
<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">
<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>
@ -52,15 +42,14 @@
</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 }}"
>
<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"><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>{{ 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">
<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>
@ -148,7 +137,7 @@
<div class="w-full">
<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">
<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>
@ -159,12 +148,11 @@
</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 }}"
>
<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>
<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">
@ -247,9 +235,8 @@
</div>
</div>
</div>
{% include '/clients/drawer_client_create.html.j2' %}
{% include '/secrets/drawer_secret_create.html.j2' %}
{% include '/dashboard/drawer_client_create_dashboard.html.j2' %}
{% include '/dashboard/drawer_secret_create_dashboard.html.j2' %}
</div>
{% endblock %}

View File

@ -1,31 +1,90 @@
<nav class="fixed z-30 w-full bg-white border-b border-gray-200 dark:bg-gray-800 dark:border-gray-700">
<nav
class="fixed z-30 w-full bg-white border-b border-gray-200 dark:bg-gray-800 dark:border-gray-700"
>
<div class="px-3 py-3 lg:px-5 lg:pl-3">
<div class="flex items-center justify-between">
<div class="flex items-center justify-start">
<button id="toggleSidebarMobile" aria-expanded="true" aria-controls="sidebar" class="p-2 text-gray-600 rounded cursor-pointer lg:hidden hover:text-gray-900 hover:bg-gray-100 focus:bg-gray-100 dark:focus:bg-gray-700 focus:ring-2 focus:ring-gray-100 dark:focus:ring-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white">
<svg id="toggleSidebarMobileHamburger" class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h6a1 1 0 110 2H4a1 1 0 01-1-1zM3 15a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z" clip-rule="evenodd"></path></svg>
<svg id="toggleSidebarMobileClose" class="hidden w-6 h-6" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg>
<button
id="toggleSidebarMobile"
aria-expanded="true"
aria-controls="sidebar"
class="p-2 text-gray-600 rounded cursor-pointer lg:hidden hover:text-gray-900 hover:bg-gray-100 focus:bg-gray-100 dark:focus:bg-gray-700 focus:ring-2 focus:ring-gray-100 dark:focus:ring-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white"
>
<svg
id="toggleSidebarMobileHamburger"
class="w-6 h-6"
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h6a1 1 0 110 2H4a1 1 0 01-1-1zM3 15a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z"
clip-rule="evenodd"
></path>
</svg>
<svg
id="toggleSidebarMobileClose"
class="hidden w-6 h-6"
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
clip-rule="evenodd"
></path>
</svg>
</button>
<a href="/" class="flex ml-2 md:mr-24">
<img src="{{ url_for('static', path='logo.svg') }}" class="h-11 mr-3" alt="Sshecret Logo" />
<span class="self-center text-xl font-semibold sm:text-2xl whitespace-nowrap dark:text-white">Sshecret</span>
<img
src="{{ url_for('static', path='logo.svg') }}"
class="h-11 mr-3"
alt="Sshecret Logo"
/>
<span
class="self-center text-xl font-semibold sm:text-2xl whitespace-nowrap dark:text-white"
>Sshecret</span
>
</a>
</div>
<div class="flex items-center">
<div class="flex items-center ml-3">
<div>
<button type="button" class="flex text-sm bg-gray-800 rounded-full focus:ring-4 focus:ring-gray-300 dark:focus:ring-gray-600" id="user-menu-button-2" aria-expanded="false" data-dropdown-toggle="dropdown-2">
<button
type="button"
class="flex text-sm bg-gray-800 rounded-full focus:ring-4 focus:ring-gray-300 dark:focus:ring-gray-600"
id="user-menu-button-2"
aria-expanded="false"
data-dropdown-toggle="dropdown-2"
>
<span class="sr-only">Open user menu</span>
<svg class="w-6 h-6 text-gray-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18Zm0 0a8.949 8.949 0 0 0 4.951-1.488A3.987 3.987 0 0 0 13 16h-2a3.987 3.987 0 0 0-3.951 3.512A8.948 8.948 0 0 0 12 21Zm3-11a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"/>
<svg
class="w-6 h-6 text-gray-800 dark:text-white"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
fill="none"
viewBox="0 0 24 24"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18Zm0 0a8.949 8.949 0 0 0 4.951-1.488A3.987 3.987 0 0 0 13 16h-2a3.987 3.987 0 0 0-3.951 3.512A8.948 8.948 0 0 0 12 21Zm3-11a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"
/>
</svg>
</button>
</div>
<!-- Dropdown menu -->
<div class="z-50 hidden my-4 text-base list-none bg-white divide-y divide-gray-100 rounded shadow dark:bg-gray-700 dark:divide-gray-600" id="dropdown-2">
<div
class="z-50 hidden my-4 text-base list-none bg-white divide-y divide-gray-100 rounded shadow dark:bg-gray-700 dark:divide-gray-600"
id="dropdown-2"
>
<div class="px-4 py-3" role="none">
<p class="text-sm text-gray-900 dark:text-white" role="none">
{{ user }}
@ -33,10 +92,20 @@
</div>
<ul class="py-1" role="none">
<li>
<a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-600 dark:hover:text-white" role="menuitem">Change Password</a>
<a
href="#"
class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-600 dark:hover:text-white"
role="menuitem"
>Change Password</a
>
</li>
<li>
<a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-600 dark:hover:text-white" role="menuitem">Logout</a>
<a
href="/logout"
class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-600 dark:hover:text-white"
role="menuitem"
>Logout</a
>
</li>
</ul>
</div>

View File

@ -1,15 +1,25 @@
{% extends "/shared/_base.html" %} {% block content %}
{% if login_error %}
{% extends "/shared/_base.html" %} {% block content %} {% if login_error %}
<div class="flex bg-gray-100">
<div class="flex w-full items-center p-4 mb-4 text-sm text-red-800 border border-red-300 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400 dark:border-red-800" role="alert">
<svg class="shrink-0 inline w-4 h-4 me-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
<path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z"/>
<div
class="flex w-full items-center p-4 mb-4 text-sm text-red-800 border border-red-300 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400 dark:border-red-800"
role="alert"
>
<svg
class="shrink-0 inline w-4 h-4 me-3"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z"
/>
</svg>
<span class="sr-only">Info</span>
<div>
<span class="font-medium">{{ login_error.title }}</span> {{login_error.message}}
<span class="font-medium">{{ login_error.title }}</span>
{{login_error.message}}
</div>
</div>
</div>
@ -28,6 +38,8 @@
name="username"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none transition-all"
placeholder="Username"
autocomplete="username"
required=""
/>
</div>
@ -40,11 +52,14 @@
name="password"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none transition-all"
placeholder="••••••••"
autocomplete="current-password"
required=""
/>
</div>
<button
class="w-full bg-indigo-600 hover:bg-indigo-700 text-white font-medium py-2.5 rounded-lg transition-colors"
type="submit"
>
Sign In
</button>

View File

@ -33,123 +33,6 @@
<span class="sr-only">Close menu</span>
</button>
<form hx-post="/secrets/" hx-target="#secretsContent">
<div class="space-y-4">
<div>
<label
for="name"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
>Name</label
>
<input
type="text"
name="name"
id="name"
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"
placeholder="Secret name"
required=""
/>
</div>
<div>
<label
for="value"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
>Secret Value</label
>
<p
id="helper-text-explanation"
class="mt-2 text-sm text-gray-500 dark:text-gray-400"
>
Enter the secret string here.
</p>
<input
type="text"
name="value"
id="secretValueInput"
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"
placeholder="Your secret string here"
/>
</div>
<div>
<label
for="auto_generate"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
>
<label class="inline-flex items-center cursor-pointer">
<input
type="checkbox"
name="auto_generate"
id="autoGenerateCheckbox"
class="sr-only peer"
hx-on:change="document.getElementById('secretValueInput').disabled = this.checked;
if (this.checked) { document.getElementById('secretValueInput').value = '' }"
/>
<div
class="relative w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600 dark:peer-checked:bg-blue-600"
></div>
<span
class="ms-3 text-sm font-medium text-gray-900 dark:text-gray-300"
>Auto-generate secret</span
>
</label>
</label>
</div>
<div>
<label
for="clients"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
>Clients</label
>
<select
multiple="multiple"
id="clients"
name="clients"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-500 focus:border-primary-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-primary-500 dark:focus:border-primary-500"
>
<option selected="selected">Select clients to assign the secret to</option>
{% for client in clients %}
<option value="{{ client.id }}">{{ client.name }}</option>
{% endfor %}
</select>
</div>
<div
class="bottom-0 left-0 flex justify-center w-full pb-4 space-x-4 md:px-4 md:absolute"
>
<button
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"
>
Add Secret
</button>
<button
type="button"
data-drawer-dismiss="drawer-create-secret-default"
aria-controls="drawer-create-secret-default"
class="inline-flex w-full justify-center text-gray-500 items-center bg-white hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-primary-300 rounded-lg border border-gray-200 text-sm font-medium px-5 py-2.5 hover:text-gray-900 focus:z-10 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-500 dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-gray-600"
>
<svg
aria-hidden="true"
class="w-5 h-5 -ml-1 sm:mr-1"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
></path>
</svg>
Cancel
</button>
</div>
</div>
{% include '/secrets/drawer_secret_create_inner.html.j2' %}
</form>
</div>

View File

@ -0,0 +1,118 @@
<div class="space-y-4">
<div>
<label
for="name"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
>Name</label
>
<input
type="text"
name="name"
id="name"
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"
placeholder="Secret name"
required=""
/>
</div>
<div>
<label
for="value"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
>Secret Value</label
>
<p
id="helper-text-explanation"
class="mt-2 text-sm text-gray-500 dark:text-gray-400"
>
Enter the secret string here.
</p>
<input
type="text"
name="value"
id="secretValueInput"
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"
placeholder="Your secret string here"
/>
</div>
<div>
<label
for="auto_generate"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
>
<label class="inline-flex items-center cursor-pointer" id="autoGenerateCheckboxLabel">
<input
type="checkbox"
name="auto_generate"
id="autoGenerateCheckbox"
class="sr-only peer"
hx-on:change="document.getElementById('secretValueInput').disabled = this.checked;
if (this.checked) { document.getElementById('secretValueInput').value = '' }"
/>
<div
class="relative w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600 dark:peer-checked:bg-blue-600"
></div>
<span
class="ms-3 text-sm font-medium text-gray-900 dark:text-gray-300"
>Auto-generate secret</span
>
</label>
</label>
</div>
<div>
<label
for="clients"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
>Clients</label
>
<select
multiple="multiple"
id="clients"
name="clients"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-500 focus:border-primary-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-primary-500 dark:focus:border-primary-500"
>
<option selected="selected">Select clients to assign the secret to</option>
{% for client in clients %}
<option value="{{ client.id }}">{{ client.name }}</option>
{% endfor %}
</select>
</div>
<div
class="bottom-0 left-0 flex justify-center w-full pb-4 space-x-4 md:px-4 md:absolute"
>
<button
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"
>
Add Secret
</button>
<button
type="button"
data-drawer-dismiss="drawer-create-secret-default"
aria-controls="drawer-create-secret-default"
class="inline-flex w-full justify-center text-gray-500 items-center bg-white hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-primary-300 rounded-lg border border-gray-200 text-sm font-medium px-5 py-2.5 hover:text-gray-900 focus:z-10 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-500 dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-gray-600"
>
<svg
aria-hidden="true"
class="w-5 h-5 -ml-1 sm:mr-1"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
></path>
</svg>
Cancel
</button>
</div>
</div>

View File

@ -45,7 +45,8 @@
{% for client in secret.clients %}
<span
class="inline-flex items-center px-2 py-1 me-2 text-sm font-medium text-red-800 bg-red-100 rounded-sm dark:bg-red-900 dark:text-red-300"
class="inline-flex items-center px-2 py-1 me-2 text-sm font-medium text-red-800 bg-red-100 rounded-sm dark:bg-red-900 dark:text-red-300 pill-client-secret"
id="client-secret-{{ secret.name }}-pill-{{ client.name }}"
>{{ client.name }}
<button
type="button"
@ -54,6 +55,7 @@
hx-delete="/secrets/{{ secret.name }}/clients/{{ client.id }}"
hx-target="#secretsContent"
hx-confirm="Remove client {{ client.name }} from secret {{secret.name}}?"
id="btn-remove-client-{{ client.name }}-secret-{{ secret.name }}"
>
<svg
class="w-2 h-2"
@ -93,6 +95,7 @@
<select
name="client"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-500 focus:border-primary-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-primary-500 dark:focus:border-primary-500"
id="sel-add-client-secret-{{ secret.name }}"
>
<option selected="selected">
Select clients to assign the secret to

View File

@ -4,11 +4,10 @@
>
<td
class="p-4 text-sm font-normal text-gray-500 whitespace-nowrap dark:text-gray-400"
>
{{ secret.name }}
</td>
>{{- secret.name -}}</td>
<td
class="max-w-sm p-4 overflow-hidden text-base font-normal text-gray-500 truncate xl:max-w-xs dark:text-gray-400"
class="max-w-sm p-4 overflow-hidden text-base font-normal text-gray-500 truncate xl:max-w-xs dark:text-gray-400 secret-client-list"
id="secret-client-list-{{ secret.name }}"
>
{% if secret.clients %}
{% for client in secret.clients %}
@ -16,7 +15,7 @@
<svg class="w-6 h-6 text-gray-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 24 24">
<path fill-rule="evenodd" d="M12 4a4 4 0 1 0 0 8 4 4 0 0 0 0-8Zm-2 9a4 4 0 0 0-4 4v1a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2v-1a4 4 0 0 0-4-4h-4Z" clip-rule="evenodd"/>
</svg>
{{ client.name }}
{{- client.name -}}
</span>
{% endfor %}
{% else %}
@ -28,6 +27,7 @@
type="button"
data-modal-target="client-secret-modal-{{secret.name}}" data-modal-toggle="client-secret-modal-{{ secret.name }}"
class="inline-flex items-center px-3 py-2 text-sm font-medium text-center text-white rounded-lg bg-primary-700 hover:bg-primary-800 focus:ring-4 focus:ring-primary-300 dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800"
id="manage-client-access-btn-{{ secret.name }}"
>
<svg
class="w-4 h-4 mr-2"
@ -53,6 +53,7 @@
hx-delete="/secrets/{{ secret.name }}"
hx-confirm="Are you sure you want to delete the secret {{ secret.name }}?"
hx-target="#secretsContent"
id="delete-secret-btn-{{ secret.name }}"
>
<svg
class="w-4 h-4 mr-2"
@ -69,3 +70,4 @@
Delete item
</button>
</td>
</tr>

View File

@ -169,4 +169,14 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
)
return response
@app.get("/logout")
async def logout(
response: Response,
):
"""Log out user."""
response = RedirectResponse(url="/login", status_code=status.HTTP_302_FOUND)
response.delete_cookie("refresh_token", httponly=True, secure=False, samesite="strict")
response.delete_cookie("access_token", httponly=True, secure=False, samesite="strict")
return response
return app