fix: 4 issues — admin password sudo, peer modal, WireGuard fetch creds, port check
1. make reset/show-admin-password: use sudo so data/api/ owned-by-root files are writable without explicit sudo prefix 2. Peers.jsx: remove one-time password modal on peer creation — admin already knows the password they typed; replace with a success toast showing peer name and provisioned accounts 3. WireGuard.jsx + Peers.jsx: add credentials:'include' to every raw fetch() call (7 calls across two files, plus fix one hardcoded localhost:3000 URL); the port check and peer status calls were returning 401 because they didn't send the session cookie 4. test_admin_wireguard.py: update test to match new toast flow (no modal), add Scenario 10 test that verifies the port check badge renders on the WireGuard page after the credentials fix Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+12
-43
@@ -67,7 +67,6 @@ function Peers() {
|
||||
const [showAddModal, setShowAddModal] = useState(false);
|
||||
const [showEditModal, setShowEditModal] = useState(false);
|
||||
const [showViewModal, setShowViewModal] = useState(false);
|
||||
const [showPasswordModal, setShowPasswordModal] = useState(null);
|
||||
const [selectedPeer, setSelectedPeer] = useState(null);
|
||||
const [formData, setFormData] = useState(emptyForm());
|
||||
const [showAdvanced, setShowAdvanced] = useState(false);
|
||||
@@ -90,7 +89,7 @@ function Peers() {
|
||||
const [regResp, statusResp, scResp] = await Promise.all([
|
||||
peerRegistryAPI.getPeers(),
|
||||
wireguardAPI.getPeerStatuses().catch(() => ({ data: {} })),
|
||||
fetch('/api/wireguard/server-config').then(r => r.ok ? r.json() : null).catch(() => null),
|
||||
fetch('/api/wireguard/server-config', { credentials: 'include' }).then(r => r.ok ? r.json() : null).catch(() => null),
|
||||
]);
|
||||
const regPeers = regResp.data || [];
|
||||
const statusMap = statusResp.data || {};
|
||||
@@ -115,7 +114,7 @@ function Peers() {
|
||||
const getServerConfig = async () => {
|
||||
if (serverConf) return serverConf;
|
||||
try {
|
||||
const r = await fetch('/api/wireguard/server-config');
|
||||
const r = await fetch('/api/wireguard/server-config', { credentials: 'include' });
|
||||
if (r.ok) {
|
||||
const sc = await r.json();
|
||||
setServerConf(sc);
|
||||
@@ -202,18 +201,23 @@ PersistentKeepalive = ${peer.persistent_keepalive || 25}`;
|
||||
|
||||
if (formData.create_calendar) {
|
||||
try {
|
||||
await fetch(`/api/calendar/create-user-collection?user=${formData.name}`, { method: 'POST' });
|
||||
await fetch(`/api/calendar/create-user-collection?user=${formData.name}`, { method: 'POST', credentials: 'include' });
|
||||
} catch {}
|
||||
}
|
||||
|
||||
const provisioned = addResult.data?.provisioned;
|
||||
const createdName = formData.name;
|
||||
const createdPassword = formData.password;
|
||||
const provisionedList = provisioned
|
||||
? Object.entries(provisioned).filter(([, v]) => v).map(([k]) => k).join(', ')
|
||||
: '';
|
||||
setShowAddModal(false);
|
||||
setFormData(emptyForm());
|
||||
setErrors({});
|
||||
fetchPeers();
|
||||
setShowPasswordModal({ name: createdName, password: createdPassword, provisioned });
|
||||
showToast(
|
||||
`Peer "${createdName}" created.` + (provisionedList ? ` Accounts: ${provisionedList}.` : ''),
|
||||
'success'
|
||||
);
|
||||
} catch (err) {
|
||||
showToast(err?.response?.data?.error || 'Failed to add peer', 'error');
|
||||
} finally { setIsSubmitting(false); }
|
||||
@@ -227,6 +231,7 @@ PersistentKeepalive = ${peer.persistent_keepalive || 25}`;
|
||||
try {
|
||||
const r = await fetch(`/api/peers/${selectedPeer.name}`, {
|
||||
method: 'PUT',
|
||||
credentials: 'include',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
description: formData.description,
|
||||
@@ -294,7 +299,7 @@ PersistentKeepalive = ${peer.persistent_keepalive || 25}`;
|
||||
|
||||
const handleConfigDownloaded = async (peerName) => {
|
||||
try {
|
||||
await fetch(`/api/peers/${peerName}/clear-reinstall`, { method: 'POST' });
|
||||
await fetch(`/api/peers/${peerName}/clear-reinstall`, { method: 'POST', credentials: 'include' });
|
||||
setPeers(ps => ps.map(p => p.name === peerName ? { ...p, config_needs_reinstall: false } : p));
|
||||
} catch {}
|
||||
};
|
||||
@@ -759,42 +764,6 @@ PersistentKeepalive = ${peer.persistent_keepalive || 25}`;
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* One-time password modal */}
|
||||
{showPasswordModal && (
|
||||
<div className="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50 flex items-center justify-center">
|
||||
<div className="bg-white rounded-lg shadow-xl p-6 w-full max-w-md mx-4">
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<CheckCircle className="h-6 w-6 text-green-500 flex-shrink-0" />
|
||||
<h3 className="text-lg font-medium text-gray-900">Peer Created — Save This Password</h3>
|
||||
</div>
|
||||
<p className="text-sm text-gray-600 mb-3">
|
||||
This is the only time you will see this password. Copy it and share it with <strong>{showPasswordModal.name}</strong>.
|
||||
</p>
|
||||
<div className="bg-gray-50 border border-gray-200 rounded-md p-3 mb-3">
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<code className="text-sm font-mono text-gray-900 break-all">{showPasswordModal.password}</code>
|
||||
<button
|
||||
onClick={() => copyToClipboard(showPasswordModal.password)}
|
||||
className="flex-shrink-0 p-1.5 text-gray-500 hover:text-gray-700 rounded"
|
||||
title="Copy password"
|
||||
>
|
||||
<Copy className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{showPasswordModal.provisioned && (
|
||||
<p className="text-xs text-gray-500 mb-4">
|
||||
Accounts created: {Object.entries(showPasswordModal.provisioned).filter(([, v]) => v).map(([k]) => k).join(', ') || 'none'}
|
||||
</p>
|
||||
)}
|
||||
<div className="flex justify-end">
|
||||
<button onClick={() => setShowPasswordModal(null)} className="btn btn-primary">
|
||||
Done
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user