[FIX] Connettori per-azienda: aggiunti realmente i 4 metodi controller + UI card (commit 0dc2a11 era guscio vuoto, Edit fallite su ancore errate)
Verificato E2E in prod: list 200 (8 tipi), save m365 201, secret 'client_secret' STRIPPATO (assente da config DB), delete 200, openConnectors servito in companies.html. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
109aa57d04
commit
789f663419
@@ -432,6 +432,9 @@
|
||||
|
||||
<div class="company-card-footer">
|
||||
<span class="company-card-sector">${escapeHtml(sectorLabel)}</span>
|
||||
<button class="btn btn-ghost btn-sm" onclick="openConnectors(${orgId}, '${escapeHtml(name).replace(/'/g, "\\'")}')" title="Connettori Evidence Automation">
|
||||
Connettori
|
||||
</button>
|
||||
<button class="btn btn-primary btn-sm" onclick="manageCompany(${orgId})">
|
||||
Gestisci
|
||||
<svg width="14" height="14" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M10.293 3.293a1 1 0 011.414 0l6 6a1 1 0 010 1.414l-6 6a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-4.293-4.293a1 1 0 010-1.414z" clip-rule="evenodd"/></svg>
|
||||
@@ -440,6 +443,92 @@
|
||||
</div>`;
|
||||
}
|
||||
|
||||
// ── Connettori per-azienda (Evidence Automation) ─────────────
|
||||
const CONNECTOR_LABELS = {
|
||||
m365: 'Microsoft 365 / Entra ID', google: 'Google Workspace', aws: 'Amazon Web Services',
|
||||
azure: 'Microsoft Azure', idp: 'Identity Provider', edr: 'EDR / XDR',
|
||||
siem: 'SIEM / SOC', ticketing: 'Ticketing (Jira/ServiceNow)'
|
||||
};
|
||||
let _connOrgId = null;
|
||||
|
||||
async function openConnectors(orgId, orgName) {
|
||||
_connOrgId = orgId;
|
||||
try {
|
||||
const res = await fetch(`/api/organizations/${orgId}/connectors`, {
|
||||
headers: { 'Authorization': `Bearer ${api.token}`, 'X-Organization-Id': orgId }
|
||||
});
|
||||
const data = await res.json();
|
||||
if (!data.success) { showConnModal(orgName, `<p style="color:#ef4444;">${escapeHtml(data.message || 'Errore')}</p>`); return; }
|
||||
const existing = {};
|
||||
(data.data.connectors || []).forEach(c => existing[c.connector_type] = c);
|
||||
const rows = (data.data.available_types || []).map(t => {
|
||||
const c = existing[t] || {};
|
||||
const cfg = (c.config && typeof c.config === 'object') ? c.config : {};
|
||||
const ss = c.secret_status || 'not_set';
|
||||
const ssBadge = { not_set: ['Segreto non impostato', '#9ca3af'], pending: ['Segreto in attesa', '#eab308'], configured: ['Segreto configurato', '#22c55e'] }[ss];
|
||||
return `<div class="conn-row" data-type="${t}" style="border:1px solid var(--gray-200);border-radius:8px;padding:12px;margin-bottom:10px;">
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;">
|
||||
<strong>${escapeHtml(CONNECTOR_LABELS[t] || t)}</strong>
|
||||
<label style="font-size:.8rem;"><input type="checkbox" class="c-en" ${c.enabled ? 'checked' : ''}> Attivo</label>
|
||||
</div>
|
||||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:6px;margin-top:8px;">
|
||||
<input class="c-tenant" placeholder="tenant_id / account_id" value="${escapeHtml(cfg.tenant_id || cfg.account_id || '')}" style="padding:6px;border:1px solid var(--gray-300);border-radius:6px;">
|
||||
<input class="c-client" placeholder="client_id / app_id" value="${escapeHtml(cfg.client_id || cfg.app_id || '')}" style="padding:6px;border:1px solid var(--gray-300);border-radius:6px;">
|
||||
</div>
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;margin-top:8px;">
|
||||
<span style="font-size:.72rem;color:${ssBadge[1]};">● ${ssBadge[0]}</span>
|
||||
<button class="btn btn-sm btn-primary" onclick="saveConn('${t}', this)">Salva</button>
|
||||
</div>
|
||||
</div>`;
|
||||
}).join('');
|
||||
showConnModal(orgName, `
|
||||
<p style="font-size:.82rem;color:var(--gray-600);margin-bottom:12px;">
|
||||
Configura i connettori di <strong>Evidence Automation</strong>. Qui si salvano solo i parametri
|
||||
NON segreti (tenant/client id). Il <strong>client secret</strong> va caricato nel vault dall'amministratore
|
||||
(il comando viene mostrato dopo il salvataggio).</p>${rows}`);
|
||||
} catch (e) { showConnModal(orgName, '<p style="color:#ef4444;">Errore di connessione</p>'); }
|
||||
}
|
||||
|
||||
function showConnModal(orgName, body) {
|
||||
const old = document.getElementById('conn-modal'); if (old) old.remove();
|
||||
document.body.insertAdjacentHTML('beforeend', `
|
||||
<div id="conn-modal" onclick="if(event.target===this)closeConn()" style="position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:1000;display:flex;align-items:center;justify-content:center;padding:20px;">
|
||||
<div style="background:var(--card-bg,#fff);border-radius:12px;max-width:640px;width:100%;max-height:85vh;overflow-y:auto;padding:22px;">
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:14px;">
|
||||
<h3 style="margin:0;">Connettori — ${escapeHtml(orgName)}</h3>
|
||||
<button class="btn btn-ghost btn-sm" onclick="closeConn()">✕</button>
|
||||
</div>${body}
|
||||
</div></div>`);
|
||||
}
|
||||
function closeConn() { const m = document.getElementById('conn-modal'); if (m) m.remove(); }
|
||||
|
||||
async function saveConn(type, btn) {
|
||||
const row = btn.closest('.conn-row');
|
||||
const payload = {
|
||||
type,
|
||||
enabled: row.querySelector('.c-en').checked,
|
||||
config: { tenant_id: row.querySelector('.c-tenant').value.trim(), client_id: row.querySelector('.c-client').value.trim() },
|
||||
secret_status: 'pending'
|
||||
};
|
||||
btn.disabled = true; btn.textContent = '...';
|
||||
try {
|
||||
const res = await fetch(`/api/organizations/${_connOrgId}/connectors`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Authorization': `Bearer ${api.token}`, 'X-Organization-Id': _connOrgId, 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
const j = await res.json();
|
||||
btn.disabled = false; btn.textContent = 'Salva';
|
||||
if (j.success) {
|
||||
if (typeof showNotification === 'function') showNotification('Connettore salvato', 'success');
|
||||
if (j.data && j.data.cli_hint) alert('Connettore salvato.\n\nPer caricare il SEGRETO nel vault, l\'amministratore esegue:\n\n' + j.data.cli_hint);
|
||||
} else if (typeof showNotification === 'function') showNotification(j.message || 'Errore', 'error');
|
||||
} catch (e) {
|
||||
btn.disabled = false; btn.textContent = 'Salva';
|
||||
if (typeof showNotification === 'function') showNotification('Errore di connessione', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
function renderEntityBadge(type, voluntary) {
|
||||
if (voluntary) {
|
||||
return `<span class="entity-badge voluntary">Volontaria</span>`;
|
||||
|
||||
Reference in New Issue
Block a user