Countdown des 2FA-Codes: Die Anzeige zaehlte blind im Sekundentakt herunter und lief dadurch aus dem Takt. Am Ende eines Zeitfensters wurde zwar ein neuer Code angefordert, der Zaehler aber nicht angehalten - bei langsamer oder fehlschlagender Antwort lief er ins Negative und stiess jede Sekunde eine weitere Anfrage an (inkl. Audit-Eintrag je Abruf), waehrend die Anzeige auf dem alten Wert stehenblieb. Die Restlaufzeit wird jetzt aus einem festen Ablaufzeitpunkt berechnet, sodass gedrosselte oder ausgefallene Ticks sie nicht verschieben. Pro Ablauf wird genau einmal nachgeladen; liefert der Server keinen Code mehr (gesperrt/offline), stoppt der Timer und die Anzeige wird geleert. Dieselbe Rechnung gilt fuer die Benachrichtigung im Seiteninhalt; die Periode steckt dort wie im Popup in einer Konstanten statt als 30 im Ausdruck. Manueller API-Token: Eingabefeld, "Token speichern" und "Verbindung testen" sind aus den Optionen entfernt; die Server-URL laesst sich weiterhin separat speichern. getAccessToken() kennt nur noch den SSO-Weg, und ein aus einer frueheren Version uebernommener Token wird bei onInstalled aus chrome.storage.local geloescht. Doku und Store-Listing entsprechend angepasst. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G12DRMpe4UjYDwuRU1sjy1
288 lines
No EOL
12 KiB
HTML
288 lines
No EOL
12 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>OpenNIT Vault – Einstellungen</title>
|
||
<style>
|
||
/* ── Reset & Base ──────────────────────────────────────────────────────── */
|
||
*, *::before, *::after { box-sizing: border-box; }
|
||
body {
|
||
font-family: "Source Sans Pro", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||
font-size: 14px;
|
||
color: #212529;
|
||
background: #f4f6f9;
|
||
margin: 0; padding: 0;
|
||
min-height: 100vh;
|
||
}
|
||
|
||
/* ── Main navbar (AdminLTE style) ─────────────────────────────────────── */
|
||
.main-header {
|
||
background: #343a40;
|
||
border-bottom: 3px solid #007bff;
|
||
padding: 0 1.25rem;
|
||
height: 56px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 1rem;
|
||
position: sticky;
|
||
top: 0;
|
||
z-index: 100;
|
||
box-shadow: 0 2px 6px rgba(0,0,0,.25);
|
||
}
|
||
.brand-logo {
|
||
width: 36px; height: 36px;
|
||
border-radius: 6px;
|
||
background: none;
|
||
display: flex; align-items: center; justify-content: center;
|
||
overflow: hidden; flex-shrink: 0;
|
||
}
|
||
.brand-logo img { width: 26px; height: 26px; object-fit: contain; }
|
||
.brand-text {
|
||
color: #fff;
|
||
font-size: 1rem;
|
||
font-weight: 700;
|
||
letter-spacing: .01em;
|
||
white-space: nowrap;
|
||
}
|
||
.brand-sep { color: rgba(255,255,255,.35); margin: 0 2px; font-weight: 300; }
|
||
.brand-sub { color: #adb5bd; font-weight: 400; font-size: .95rem; }
|
||
.header-user {
|
||
margin-left: auto;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: .5rem;
|
||
color: #dee2e6;
|
||
font-size: .82rem;
|
||
}
|
||
.user-badge {
|
||
background: rgba(255,255,255,.1);
|
||
border: 1px solid rgba(255,255,255,.15);
|
||
border-radius: 20px;
|
||
padding: 3px 10px 3px 6px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 5px;
|
||
font-size: .78rem;
|
||
}
|
||
.user-dot { width: 7px; height: 7px; background: #28a745; border-radius: 50%; }
|
||
|
||
/* ── Content wrapper ──────────────────────────────────────────────────── */
|
||
.content-wrapper { max-width: 680px; margin: 0 auto; padding: 1.5rem 1.25rem; }
|
||
|
||
/* ── Card (AdminLTE .card) ───────────────────────────────────────────── */
|
||
.card {
|
||
background: #fff;
|
||
border: none;
|
||
border-radius: .35rem;
|
||
box-shadow: 0 1px 3px rgba(0,0,0,.08), 0 1px 2px rgba(0,0,0,.06);
|
||
margin-bottom: 1.25rem;
|
||
}
|
||
.card-header {
|
||
background: #f8f9fa;
|
||
border-bottom: 1px solid #dee2e6;
|
||
padding: .75rem 1.25rem;
|
||
border-radius: .35rem .35rem 0 0;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: .5rem;
|
||
}
|
||
.card-header h3 {
|
||
margin: 0;
|
||
font-size: .9rem;
|
||
font-weight: 600;
|
||
color: #343a40;
|
||
line-height: 1;
|
||
}
|
||
.card-header .card-tools { margin-left: auto; }
|
||
.card-body { padding: 1.25rem; }
|
||
|
||
/* ── Form controls ───────────────────────────────────────────────────── */
|
||
.form-group { margin-bottom: 1rem; }
|
||
.form-group:last-child { margin-bottom: 0; }
|
||
label.control-label {
|
||
display: block;
|
||
font-size: .78rem;
|
||
font-weight: 700;
|
||
color: #6c757d;
|
||
text-transform: uppercase;
|
||
letter-spacing: .04em;
|
||
margin-bottom: .35rem;
|
||
}
|
||
.form-control {
|
||
display: block;
|
||
width: 100%;
|
||
padding: .45rem .75rem;
|
||
font-size: .9rem;
|
||
color: #495057;
|
||
background: #fff;
|
||
border: 1px solid #ced4da;
|
||
border-radius: .25rem;
|
||
transition: border-color .15s, box-shadow .15s;
|
||
}
|
||
.form-control:focus {
|
||
outline: none;
|
||
border-color: #80bdff;
|
||
box-shadow: 0 0 0 .2rem rgba(0,123,255,.25);
|
||
}
|
||
.form-text { font-size: .78rem; color: #6c757d; margin-top: .25rem; line-height: 1.5; }
|
||
|
||
/* ── Buttons ─────────────────────────────────────────────────────────── */
|
||
.btn {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: .35rem;
|
||
padding: .4rem .85rem;
|
||
font-size: .875rem;
|
||
font-weight: 600;
|
||
border: 1px solid transparent;
|
||
border-radius: .25rem;
|
||
cursor: pointer;
|
||
transition: background .15s, border-color .15s, color .15s;
|
||
text-decoration: none;
|
||
}
|
||
.btn-primary { background: #007bff; border-color: #007bff; color: #fff; }
|
||
.btn-primary:hover { background: #0069d9; border-color: #0062cc; }
|
||
.btn-default { background: #fff; border-color: #ced4da; color: #212529; }
|
||
.btn-default:hover { background: #f8f9fa; border-color: #adb5bd; }
|
||
.btn-sm { padding: .28rem .65rem; font-size: .8rem; }
|
||
.btn-group { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; margin-top: .75rem; }
|
||
|
||
/* ── Alerts ──────────────────────────────────────────────────────────── */
|
||
.alert { padding: .65rem 1rem; border-radius: .25rem; font-size: .85rem; margin-top: .75rem; border: 1px solid transparent; }
|
||
.alert-success { background: #d4edda; color: #155724; border-color: #c3e6cb; }
|
||
.alert-danger { background: #f8d7da; color: #721c24; border-color: #f5c6cb; }
|
||
.alert-info { background: #d1ecf1; color: #0c5460; border-color: #bee5eb; }
|
||
.save-ok { color: #28a745; font-size: .82rem; font-weight: 600; display: inline-flex; align-items: center; gap: 4px; }
|
||
|
||
/* ── Spinner ─────────────────────────────────────────────────────────── */
|
||
.fa-spin-sm {
|
||
display: inline-block;
|
||
width: 12px; height: 12px;
|
||
border: 2px solid #dee2e6;
|
||
border-top-color: #007bff;
|
||
border-radius: 50%;
|
||
animation: sp .7s linear infinite;
|
||
vertical-align: middle;
|
||
}
|
||
@keyframes sp { to { transform: rotate(360deg); } }
|
||
|
||
/* ── Steps list ──────────────────────────────────────────────────────── */
|
||
.steps-list { list-style: none; margin: 0; padding: 0; }
|
||
.steps-list li {
|
||
display: flex;
|
||
gap: .875rem;
|
||
align-items: flex-start;
|
||
padding: .5rem 0;
|
||
border-bottom: 1px solid #f1f3f5;
|
||
font-size: .875rem;
|
||
color: #495057;
|
||
line-height: 1.55;
|
||
}
|
||
.steps-list li:last-child { border-bottom: none; padding-bottom: 0; }
|
||
.step-badge {
|
||
min-width: 22px; height: 22px;
|
||
background: #007bff;
|
||
color: #fff;
|
||
border-radius: 50%;
|
||
display: flex; align-items: center; justify-content: center;
|
||
font-size: .7rem;
|
||
font-weight: 700;
|
||
flex-shrink: 0;
|
||
margin-top: .15rem;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<!-- Main Header -->
|
||
<nav class="main-header">
|
||
<div class="brand-logo"><img src="icon128.png" alt=""></div>
|
||
<span class="brand-text" id="optTitle">OpenNIT Vault</span>
|
||
<div class="header-user" id="headerStatus" style="display:none;">
|
||
<span class="user-badge">
|
||
<span class="user-dot"></span>
|
||
<span id="headerUser"></span>
|
||
</span>
|
||
</div>
|
||
</nav>
|
||
|
||
<div class="content-wrapper">
|
||
|
||
<!-- Connection card -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<h3>Server-Verbindung</h3>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="form-group">
|
||
<label class="control-label" for="serverUrl">Server-URL</label>
|
||
<input type="url" class="form-control" id="serverUrl"
|
||
placeholder="https://ihre-opennit.firma.de"
|
||
value="">
|
||
<span class="form-text">Adresse Ihrer OpenNIT-Instanz – ohne abschließenden Slash.</span>
|
||
</div>
|
||
|
||
<!-- SSO-Anmeldung (empfohlen) -->
|
||
<div class="btn-group">
|
||
<button class="btn btn-primary" id="btnSso">
|
||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right:2px;"><path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4"/><polyline points="10 17 15 12 10 7"/><line x1="15" y1="12" x2="3" y2="12"/></svg>
|
||
Mit OpenNIT anmelden
|
||
</button>
|
||
<button class="btn btn-default" id="btnSave">URL speichern</button>
|
||
<button class="btn btn-default" id="btnLogout" style="display:none;">Abmelden</button>
|
||
<span id="ssoMsg"></span>
|
||
<span id="savedMsg"></span>
|
||
</div>
|
||
<span class="form-text">Die Anmeldung erfolgt wie an OpenNIT (lokal + 2FA / Microsoft 365 / Keycloak). Der Zugang wird automatisch erneuert und lässt sich jederzeit im Vault widerrufen.</span>
|
||
<div id="statusMsg"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Security card -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<h3>Sicherheit</h3>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="form-group">
|
||
<label class="control-label" for="lockDuration">PIN-Sperre: erneut fragen</label>
|
||
<select class="form-control" id="lockDuration">
|
||
<option value="5">Nach 5 Minuten</option>
|
||
<option value="15">Nach 15 Minuten</option>
|
||
<option value="60">Nach 1 Stunde</option>
|
||
<option value="session">Bis der Browser geschlossen wird</option>
|
||
</select>
|
||
<span class="form-text">Ist im Web-Tresor ein <strong>Tresor-PIN</strong> aktiv, verlangt die Erweiterung diesen PIN – <strong>serverseitig erzwungen</strong>, damit ein gestohlener Token allein nichts nützt. Diese Einstellung legt nur fest, <em>wie lange</em> eine Entsperrung gilt. Ohne gesetzten Tresor-PIN entfällt die Sperre.</span>
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="control-label"><input type="checkbox" id="clipClear" checked> Zwischenablage nach 30 Sekunden leeren</label>
|
||
<span class="form-text">Kopierte Passwörter und 2FA-Codes werden nach kurzer Zeit automatisch aus der Zwischenablage entfernt.</span>
|
||
</div>
|
||
<div class="btn-group">
|
||
<button class="btn btn-primary btn-sm" id="btnSaveSec">Speichern</button>
|
||
<span id="savedSecMsg"></span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Setup card -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<h3>Einrichtungsschritte</h3>
|
||
</div>
|
||
<div class="card-body">
|
||
<ul class="steps-list">
|
||
<li><span class="step-badge">1</span><span>Tragen Sie oben die <strong>Server-URL</strong> Ihrer OpenNIT-Instanz ein (nur <strong>https://</strong>).</span></li>
|
||
<li><span class="step-badge">2</span><span>Klicken Sie auf <strong>„Mit OpenNIT anmelden“</strong> und melden Sie sich wie gewohnt an.</span></li>
|
||
<li><span class="step-badge">3</span><span>Bestätigen Sie den Zugriff auf den <strong>Passwort-Vault</strong> – die Erweiterung ist damit verbunden.</span></li>
|
||
<li><span class="step-badge">4</span><span>Fokussieren Sie ein Login-Feld auf einer Website – Vault-Vorschläge erscheinen automatisch.</span></li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
|
||
</div><!-- /.content-wrapper -->
|
||
|
||
<script src="options.js"></script>
|
||
</body>
|
||
</html> |