feat: Basic Auth se moze ukljuciti/iskljuciti iz Web GUI-ja

Panel je dosad znao samo dodavati i brisati htpasswd korisnike, a sama
auth_basic direktiva se mogla dirati jedino rucno u nginx konfiguraciji.

Dodan prekidac po lokaciji — /guacamole/ i /guacadmin/ su odvojeni, pa
se zastita moze skinuti sa samo jedne. Stanje se cita iz konfiguracije,
ne pamti se zasebno, pa GUI uvijek pokazuje ono sto nginx stvarno radi.

Sigurnosne kocnice:
- Nakon izmjene ide nginx -t; ako padne, konfiguracija se vraca na staro
  prije nego se reload uopce dogodi.
- Ne moze se ukljuciti zastita ako nema nijednog htpasswd korisnika —
  to bi zakljucalo sve.
- Ne moze se obrisati zadnji korisnik dok je Basic Auth aktivan. Ta rupa
  je postojala i prije: brisanje zadnjeg korisnika ostavljalo je prazan
  htpasswd uz aktivan auth_basic, sto zakljuca i Guacamole i sam panel.

Parsiranje ide po location bloku s brojanjem viticastih zagrada, ne
regexom preko cijele datoteke, da se ne pogodi kriva lokacija.

Provjereno na zivoj instalaciji: iskljucenje na /guacamole/ daje 200 bez
prijave dok /guacadmin/ ostaje na 401; ponovno ukljucenje vraca 401 i
konfiguracija je bajt-identicna originalu.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-29 10:08:26 +02:00
parent d50b7215e3
commit 1a213f807f
2 changed files with 165 additions and 1 deletions
+49
View File
@@ -4,6 +4,40 @@
{% block content %}
<h4 class="mb-4"><i class="bi bi-person-lock me-2"></i>Basic Auth korisnici</h4>
<div class="card mb-4">
<div class="card-header fw-semibold">
<i class="bi bi-shield-lock me-1"></i> Basic Auth zaštita
</div>
<div class="card-body">
{% for loc, label in auth_locations.items() %}
<div class="d-flex justify-content-between align-items-center
{% if not loop.last %}mb-3 pb-3 border-bottom{% endif %}"
style="border-color:#30363d !important">
<div>
<div class="fw-semibold">{{ label }}</div>
<small class="text-muted"><code>{{ loc }}</code></small>
</div>
<div class="form-check form-switch mb-0">
{% if auth_state[loc] is none %}
<span class="badge bg-secondary">nije konfiguriran</span>
{% else %}
<input class="form-check-input" type="checkbox" role="switch"
id="sw-{{ loop.index }}" {% if auth_state[loc] %}checked{% endif %}
onchange="toggleAuth('{{ loc }}', this)">
{% endif %}
</div>
</div>
{% endfor %}
<div class="alert alert-warning mt-3 mb-0 py-2 small">
<i class="bi bi-exclamation-triangle me-1"></i>
Isključite li zaštitu, na toj lokaciji nestaje prvi sloj obrane i
<strong>fail2ban jail <code>nginx-basicauth</code> ostaje bez posla</strong>.
Za <code>/guacadmin/</code> to znači da je ovaj panel izložen samo
s vlastitom prijavom.
</div>
</div>
</div>
<div class="row g-4">
<!-- Lista korisnika -->
<div class="col-md-5">
@@ -112,6 +146,21 @@ async function updateUser() {
showToast(r.msg, r.ok);
}
async function toggleAuth(location, el) {
const enabled = el.checked;
if (!enabled && !confirm(
`Isključiti Basic Auth za ${location}?\n\n` +
`Nakon toga se toj lokaciji pristupa bez prompta.`)) {
el.checked = true;
return;
}
el.disabled = true;
const r = await api('/api/basicauth/toggle', {location, enabled});
showToast(r.msg, r.ok);
if (!r.ok) el.checked = !enabled; // odbijeno — vrati prekidac
el.disabled = false;
}
async function deleteUser(username) {
if (!confirm(`Ukloniti korisnika "${username}"?`)) return;
const r = await api('/api/basicauth/delete', {username});