Files
wevads-platform/scripts/brain-consent.php
2026-02-26 04:53:11 +01:00

138 lines
9.4 KiB
PHP
Executable File

<?php
$pdo = new PDO("pgsql:host=localhost;dbname=adx_system","admin","admin123");
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
if (($_GET['action'] ?? '') === 'verify') {
header('Content-Type: application/json');
$results = [];
$tenants = $pdo->query("SELECT id, tenant_domain, tenant_id, client_id, client_secret FROM admin.graph_tenants WHERE status='active'")->fetchAll(PDO::FETCH_ASSOC);
foreach ($tenants as $t) {
$ch = curl_init("https://login.microsoftonline.com/{$t['tenant_id']}/oauth2/v2.0/token");
curl_setopt_array($ch,[CURLOPT_POST=>true,CURLOPT_RETURNTRANSFER=>true,CURLOPT_TIMEOUT=>10,
CURLOPT_POSTFIELDS=>http_build_query(['grant_type'=>'client_credentials','client_id'=>$t['client_id'],'client_secret'=>$t['client_secret'],'scope'=>'https://graph.microsoft.com/.default'])]);
$resp = json_decode(curl_exec($ch),true); curl_close($ch);
if (!isset($resp['access_token'])) { $results[] = ['domain'=>$t['tenant_domain'],'send'=>false,'read'=>false,'error'=>'no token']; continue; }
$parts = explode('.', $resp['access_token']);
$payload = json_decode(base64_decode($parts[1]), true);
$roles = $payload['roles'] ?? [];
$hasSend = in_array('Mail.Send', $roles);
$hasRead = in_array('Mail.ReadWrite', $roles) || in_array('Mail.Read', $roles);
if ($hasRead) {
$pdo->prepare("UPDATE admin.graph_tenants SET permissions='Mail.Send,Mail.ReadWrite' WHERE id=?")->execute([$t['id']]);
$ch2 = curl_init("https://graph.microsoft.com/v1.0/users/\$count");
curl_setopt_array($ch2,[CURLOPT_RETURNTRANSFER=>true,CURLOPT_TIMEOUT=>10,
CURLOPT_HTTPHEADER=>["Authorization: Bearer ".$resp['access_token'],"ConsistencyLevel: eventual"]]);
$uc = (int)curl_exec($ch2); curl_close($ch2);
} else { $uc = 0; }
$results[] = ['domain'=>$t['tenant_domain'],'send'=>$hasSend,'read'=>$hasRead,'users'=>$uc,'roles'=>count($roles)];
}
echo json_encode(['tenants'=>$results]); exit;
}
$tenants = $pdo->query("SELECT id, tenant_domain, tenant_id, client_id, permissions FROM admin.graph_tenants WHERE status='active' ORDER BY id")->fetchAll(PDO::FETCH_ASSOC);
$totalTenants = count($tenants);
$readyCount = 0;
foreach($tenants as $t) { if(strpos($t['permissions']??'','ReadWrite')!==false) $readyCount++; }
?><!DOCTYPE html><html lang="fr"><head>
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>Brain Graph Consent</title>
<link rel="stylesheet" href="wevads-global.css?v1770771306">
<style>
:root{--bg:#060a14;--s:#0c1220;--s2:#111827;--b:#1e293b;--t:#e2e8f0;--d:#64748b;--cy:#22d3ee;--gn:#34d399;--rd:#f87171;--am:#fbbf24;--pu:#a78bfa;--bl:#60a5fa}
*{margin:0;padding:0;box-sizing:border-box}
body{background:var(--bg)!important;color:var(--t)!important;font-family:'DM Sans',system-ui,sans-serif;min-height:100vh;padding:20px}
.container{max-width:960px;margin:0 auto;padding-top:50px}
.hdr{text-align:center;margin-bottom:32px}
.hdr h1{font-size:1.8em;font-weight:700;color:var(--t);margin-bottom:8px}
.hdr h1 span{color:var(--cy)}
.hdr p{color:var(--d);font-size:13px;line-height:1.6}
.hdr code{background:rgba(34,211,238,.15);color:var(--cy);padding:2px 8px;border-radius:4px;font-size:12px}
.stats{display:flex;gap:12px;justify-content:center;margin:16px 0 24px}
.stat{background:var(--s);border:1px solid var(--b);border-radius:10px;padding:10px 20px;text-align:center;min-width:120px}
.stat .v{font-size:1.4em;font-weight:700;color:var(--cy)}.stat .l{font-size:10px;color:var(--d);text-transform:uppercase;letter-spacing:1px}
.tenant{background:var(--s);border:1px solid var(--b);border-radius:10px;padding:14px 20px;margin-bottom:10px;display:flex;align-items:center;gap:16px;transition:all .25s ease;position:relative;overflow:hidden}
.tenant:hover{transform:translateY(-2px);box-shadow:0 6px 20px rgba(0,0,0,.3);border-color:var(--cy)}
.tenant::after{content:'';position:absolute;bottom:0;left:0;right:0;height:2px;background:var(--cy);opacity:0;transition:opacity .25s}.tenant:hover::after{opacity:.6}
.tenant .num{font-size:11px;color:var(--d);font-weight:700;min-width:24px}
.tenant .dom{font-weight:600;font-size:14px;color:var(--t);min-width:240px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.badges{display:flex;gap:6px;flex:1}
.badge-ok{background:rgba(52,211,153,.15);color:var(--gn);padding:4px 10px;border-radius:6px;font-size:11px;font-weight:600;display:inline-flex;align-items:center;gap:4px}
.badge-fail{background:rgba(248,113,113,.15);color:var(--rd);padding:4px 10px;border-radius:6px;font-size:11px;font-weight:600;display:inline-flex;align-items:center;gap:4px}
.btn-consent{background:linear-gradient(135deg,var(--pu),var(--bl));color:#fff;border:none;padding:6px 16px;border-radius:8px;font-size:12px;font-weight:600;cursor:pointer;text-decoration:none;display:inline-flex;align-items:center;gap:5px;transition:all .2s}
.btn-consent:hover{transform:scale(1.05);box-shadow:0 4px 12px rgba(96,165,250,.3);color:#fff}
.btn-verify{background:linear-gradient(135deg,var(--cy),var(--bl));color:#0c1220;border:none;padding:10px 28px;border-radius:10px;font-size:14px;font-weight:700;cursor:pointer;display:inline-flex;align-items:center;gap:8px;transition:all .2s;margin-top:20px}
.btn-verify:hover{transform:translateY(-2px);box-shadow:0 6px 20px rgba(34,211,238,.3)}
.btn-verify:disabled{opacity:.5;cursor:wait}
.results{margin-top:20px}
.results table{width:100%;border-collapse:collapse;font-size:13px}
.results th{background:var(--s2);color:var(--d);padding:8px 12px;text-align:left;font-size:11px;text-transform:uppercase;letter-spacing:1px;border-bottom:1px solid var(--b)}
.results td{padding:8px 12px;border-bottom:1px solid var(--b);color:var(--t)}
.results tr:hover{background:rgba(34,211,238,.05)}
.alert-ok{background:rgba(52,211,153,.1);border:1px solid rgba(52,211,153,.3);color:var(--gn);padding:12px 16px;border-radius:8px;margin-top:12px;font-size:13px}
.alert-warn{background:rgba(251,191,36,.1);border:1px solid rgba(251,191,36,.3);color:var(--am);padding:12px 16px;border-radius:8px;margin-top:12px;font-size:13px}
.ok-check{color:var(--gn);font-weight:700}
@keyframes fadeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}
.tenant{animation:fadeIn .4s ease both}
</style></head><body>
<div class="container">
<div class="hdr">
<h1>🧠 Brain <span>Graph API</span> — Admin Consent</h1>
<p>Autorise <code>Mail.ReadWrite</code> sur chaque tenant pour que le Brain puisse vérifier inbox/spam</p>
<p style="color:var(--d);font-size:11px;margin-top:4px">Clique chaque bouton → accepte chez Microsoft → reviens vérifier</p>
</div>
<div class="stats">
<div class="stat"><div class="v"><?=$totalTenants?></div><div class="l">Tenants</div></div>
<div class="stat"><div class="v" style="color:var(--gn)"><?=$readyCount?></div><div class="l">Mail.Read ✅</div></div>
<div class="stat"><div class="v" style="color:<?=($totalTenants-$readyCount)>0?'var(--rd)':'var(--gn)'?>"><?=$totalTenants-$readyCount?></div><div class="l">À consentir</div></div>
</div>
<?php foreach ($tenants as $i => $t):
$url = "https://login.microsoftonline.com/{$t['tenant_id']}/adminconsent?client_id={$t['client_id']}&redirect_uri=".urlencode("https://login.microsoftonline.com/common/oauth2/nativeclient");
$hasRead = strpos($t['permissions'] ?? '', 'ReadWrite') !== false;
?>
<div class="tenant" style="animation-delay:<?=($i*0.05)?>s">
<span class="num"><?=($i+1)?></span>
<span class="dom"><?=$t['tenant_domain']?></span>
<div class="badges">
<span class="badge-ok">Mail.Send ✅</span>
<span class="<?=$hasRead?'badge-ok':'badge-fail'?>" id="read-<?=$t['id']?>"><?=$hasRead?'Mail.Read ✅':'Mail.Read ❌'?></span>
</div>
<div>
<?php if (!$hasRead): ?>
<a href="<?=$url?>" target="_blank" class="btn-consent">🔐 Consent</a>
<?php else: ?>
<span class="ok-check">✅ OK</span>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>
<div style="text-align:center">
<button onclick="verify()" class="btn-verify" id="verifyBtn">🔄 Vérifier toutes les permissions</button>
</div>
<div id="results" class="results" style="display:none"></div>
</div>
<script src="arsenal-common.js?v1770771306"></script>
<script>
async function verify(){
const btn=document.getElementById('verifyBtn');
btn.innerHTML='⏳ Vérification...';btn.disabled=true;
const r=await fetch('?action=verify');const d=await r.json();
let ok=0,total=d.tenants.length;
let h='<table><thead><tr><th>Tenant</th><th>Send</th><th>Read</th><th>Users</th><th>Roles</th></tr></thead><tbody>';
d.tenants.forEach(t=>{
if(t.read)ok++;
h+=`<tr><td>${t.domain}</td><td>${t.send?'✅':'❌'}</td><td>${t.read?'✅':'❌'}</td><td>${t.users||'-'}</td><td>${t.roles||'-'}</td></tr>`;
});
h+='</tbody></table>';
h+=ok===total
?`<div class="alert-ok">🎉 <strong>${ok}/${total} tenants complets!</strong> Brain peut envoyer ET vérifier inbox.</div>`
:`<div class="alert-warn">⚠️ <strong>${ok}/${total}</strong> tenants avec Mail.Read. Consent les ${total-ok} restants.</div>`;
document.getElementById('results').style.display='block';
document.getElementById('results').innerHTML=h;
btn.innerHTML='🔄 Revérifier';btn.disabled=false;
setTimeout(()=>location.reload(),3000);
}
</script>
</body></html>