Complete project rebrand for better positioning in the press freedom and digital security space. FieldWitness communicates both field deployment and evidence testimony — appropriate for the target audience of journalists, NGOs, and human rights organizations. Rename mapping: - soosef → fieldwitness (package, CLI, all imports) - soosef.stegasoo → fieldwitness.stego - soosef.verisoo → fieldwitness.attest - ~/.soosef/ → ~/.fwmetadata/ (innocuous data dir name) - SOOSEF_DATA_DIR → FIELDWITNESS_DATA_DIR - SoosefConfig → FieldWitnessConfig - SoosefError → FieldWitnessError Also includes: - License switch from MIT to GPL-3.0 - C2PA bridge module (Phase 0-2 MVP): cert.py, export.py, vendor_assertions.py - README repositioned to lead with provenance/federation, stego backgrounded - Threat model skeleton at docs/security/threat-model.md - Planning docs: docs/planning/c2pa-integration.md, docs/planning/gtm-feasibility.md Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
178 lines
7.1 KiB
HTML
178 lines
7.1 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Recovery Key Setup - Stego{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="row justify-content-center">
|
|
<div class="col-md-8 col-lg-6">
|
|
<div class="card">
|
|
<div class="card-header text-center">
|
|
<i class="bi bi-shield-lock fs-1 d-block mb-2"></i>
|
|
<h5 class="mb-0">Recovery Key Setup</h5>
|
|
<small class="text-muted">Step 2 of 2</small>
|
|
</div>
|
|
<div class="card-body">
|
|
<!-- Explanation -->
|
|
<div class="alert alert-info">
|
|
<i class="bi bi-info-circle me-2"></i>
|
|
<strong>What is a recovery key?</strong><br>
|
|
If you forget your admin password, this key is the ONLY way to reset it.
|
|
Save it somewhere safe - it will not be shown again.
|
|
</div>
|
|
|
|
<!-- Recovery Key Display -->
|
|
<div class="mb-4">
|
|
<label class="form-label">
|
|
<i class="bi bi-key-fill me-1"></i> Your Recovery Key
|
|
</label>
|
|
<div class="input-group">
|
|
<input type="text" class="form-control font-monospace text-center"
|
|
id="recoveryKey" value="{{ recovery_key }}" readonly
|
|
style="font-size: 1.1em; letter-spacing: 0.5px;">
|
|
<button class="btn btn-outline-secondary" type="button"
|
|
onclick="copyToClipboard()" title="Copy to clipboard">
|
|
<i class="bi bi-clipboard" id="copyIcon"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- QR Code (if available) -->
|
|
{% if qr_base64 %}
|
|
<div class="mb-4 text-center">
|
|
<label class="form-label d-block">
|
|
<i class="bi bi-qr-code me-1"></i> QR Code
|
|
</label>
|
|
<img src="data:image/png;base64,{{ qr_base64 }}"
|
|
alt="Recovery Key QR Code" class="img-fluid border rounded"
|
|
style="max-width: 200px;" id="qrImage">
|
|
<div class="mt-2">
|
|
<small class="text-muted">Scan with your phone's camera app</small>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Download Options -->
|
|
<div class="mb-4">
|
|
<label class="form-label">
|
|
<i class="bi bi-download me-1"></i> Download Options
|
|
</label>
|
|
<div class="d-flex gap-2 flex-wrap">
|
|
<button class="btn btn-outline-primary btn-sm" onclick="downloadTextFile()">
|
|
<i class="bi bi-file-text me-1"></i> Text File
|
|
</button>
|
|
{% if qr_base64 %}
|
|
<button class="btn btn-outline-primary btn-sm" onclick="downloadQRImage()">
|
|
<i class="bi bi-image me-1"></i> QR Image
|
|
</button>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<hr>
|
|
|
|
<!-- Confirmation Form -->
|
|
<form method="POST" id="recoveryForm">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
|
<input type="hidden" name="recovery_key" value="{{ recovery_key }}">
|
|
|
|
<!-- Confirm checkbox -->
|
|
<div class="form-check mb-3">
|
|
<input class="form-check-input" type="checkbox" id="confirmSaved"
|
|
onchange="updateButtons()">
|
|
<label class="form-check-label" for="confirmSaved">
|
|
I have saved my recovery key in a secure location
|
|
</label>
|
|
</div>
|
|
|
|
<div class="d-flex gap-2 justify-content-between">
|
|
<!-- Skip button (no recovery) -->
|
|
<button type="submit" name="action" value="skip"
|
|
class="btn btn-outline-secondary"
|
|
onclick="return confirm('Are you sure? Without a recovery key, there is NO way to reset your password if you forget it.')">
|
|
<i class="bi bi-skip-forward me-1"></i> Skip (No Recovery)
|
|
</button>
|
|
|
|
<!-- Save button (with key) -->
|
|
<button type="submit" name="action" value="save"
|
|
class="btn btn-primary" id="saveBtn" disabled>
|
|
<i class="bi bi-check-lg me-1"></i> Continue
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Security Notes -->
|
|
<div class="card mt-3">
|
|
<div class="card-header">
|
|
<i class="bi bi-shield-check me-2"></i>Security Notes
|
|
</div>
|
|
<div class="card-body small">
|
|
<ul class="mb-0">
|
|
<li>The recovery key is <strong>not stored</strong> - only a hash is saved</li>
|
|
<li>Keep it separate from your password (different location)</li>
|
|
<li>Anyone with this key can reset admin passwords</li>
|
|
<li>If you lose it and forget your password, you must recreate the database</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block scripts %}
|
|
<script>
|
|
// Copy recovery key to clipboard
|
|
function copyToClipboard() {
|
|
const keyInput = document.getElementById('recoveryKey');
|
|
navigator.clipboard.writeText(keyInput.value).then(() => {
|
|
const icon = document.getElementById('copyIcon');
|
|
icon.className = 'bi bi-clipboard-check';
|
|
setTimeout(() => { icon.className = 'bi bi-clipboard'; }, 2000);
|
|
});
|
|
}
|
|
|
|
// Download as text file
|
|
function downloadTextFile() {
|
|
const key = document.getElementById('recoveryKey').value;
|
|
const content = `Stego Recovery Key
|
|
=====================
|
|
|
|
${key}
|
|
|
|
IMPORTANT:
|
|
- Keep this file in a secure location
|
|
- Anyone with this key can reset admin passwords
|
|
- Do not store with your password
|
|
|
|
Generated: ${new Date().toISOString()}
|
|
`;
|
|
const blob = new Blob([content], { type: 'text/plain' });
|
|
const url = URL.createObjectURL(blob);
|
|
const a = document.createElement('a');
|
|
a.href = url;
|
|
a.download = 'stego-recovery-key.txt';
|
|
a.click();
|
|
URL.revokeObjectURL(url);
|
|
}
|
|
|
|
// Download QR as image
|
|
function downloadQRImage() {
|
|
const img = document.getElementById('qrImage');
|
|
if (!img) return;
|
|
|
|
const a = document.createElement('a');
|
|
a.href = img.src;
|
|
a.download = 'stego-recovery-qr.png';
|
|
a.click();
|
|
}
|
|
|
|
// Enable save button when checkbox is checked
|
|
function updateButtons() {
|
|
const checkbox = document.getElementById('confirmSaved');
|
|
const saveBtn = document.getElementById('saveBtn');
|
|
saveBtn.disabled = !checkbox.checked;
|
|
}
|
|
</script>
|
|
{% endblock %}
|