auth: gate SPA boot on /api/auth/me, add user badge and logout
This commit is contained in:
parent
939bf38b66
commit
2d8d58a9ef
@ -1,3 +1,47 @@
|
|||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Auth gate: runs before the main app IIFE bootstraps. If the user is not
|
||||||
|
// authenticated, we redirect to /login.html (or /setup.html when the backend
|
||||||
|
// indicates the initial setup is still required) and abort further init.
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
(async function authGate() {
|
||||||
|
try {
|
||||||
|
const r = await fetch('/api/auth/me', { credentials: 'same-origin' });
|
||||||
|
if (r.status === 401) {
|
||||||
|
const setup = await fetch('/api/auth/setup-required').then(function (x) { return x.json(); }).catch(function () { return { setup_required: false }; });
|
||||||
|
window.location.replace(setup.setup_required ? '/setup.html' : '/login.html');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!r.ok) return;
|
||||||
|
const me = await r.json();
|
||||||
|
window.__clearviewUser = me;
|
||||||
|
renderUserBadge(me);
|
||||||
|
if (me.role !== 'admin') {
|
||||||
|
const usersLink = document.querySelector('[data-route="users"]');
|
||||||
|
if (usersLink) usersLink.style.display = 'none';
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
window.location.replace('/login.html');
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
function renderUserBadge(me) {
|
||||||
|
const slot = document.getElementById('userBadge');
|
||||||
|
if (!slot) return;
|
||||||
|
slot.innerHTML = '';
|
||||||
|
const wrap = document.createElement('span');
|
||||||
|
wrap.className = 'user-badge';
|
||||||
|
wrap.append(document.createTextNode(me.username + ' (' + me.role + ')'));
|
||||||
|
const btn = document.createElement('button');
|
||||||
|
btn.type = 'button';
|
||||||
|
btn.textContent = 'Sign out';
|
||||||
|
btn.addEventListener('click', async function () {
|
||||||
|
await fetch('/api/auth/logout', { method: 'POST', credentials: 'same-origin' });
|
||||||
|
window.location.replace('/login.html');
|
||||||
|
});
|
||||||
|
wrap.append(btn);
|
||||||
|
slot.append(wrap);
|
||||||
|
}
|
||||||
|
|
||||||
(function () {
|
(function () {
|
||||||
const state = {
|
const state = {
|
||||||
selectedJobId: null,
|
selectedJobId: null,
|
||||||
@ -113,7 +157,11 @@
|
|||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
async function requestJson(url, options) {
|
async function requestJson(url, options) {
|
||||||
const response = await fetch(url, options);
|
const response = await fetch(url, Object.assign({ credentials: 'same-origin' }, options || {}));
|
||||||
|
if (response.status === 401) {
|
||||||
|
window.location.replace('/login.html');
|
||||||
|
throw new Error('unauthenticated');
|
||||||
|
}
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
let detail = response.statusText;
|
let detail = response.statusText;
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -32,6 +32,7 @@
|
|||||||
<div class="nav-section">Entra</div>
|
<div class="nav-section">Entra</div>
|
||||||
<a href="#/scan/entra" class="nav-link" data-route="scan-entra">New Entra Scan</a>
|
<a href="#/scan/entra" class="nav-link" data-route="scan-entra">New Entra Scan</a>
|
||||||
|
|
||||||
|
<a href="#/users" class="nav-link" data-route="users">Users</a>
|
||||||
<div class="nav-spacer"></div>
|
<div class="nav-spacer"></div>
|
||||||
<a href="#/tenants" class="nav-link" data-route="tenants">Tenants</a>
|
<a href="#/tenants" class="nav-link" data-route="tenants">Tenants</a>
|
||||||
<a href="#/settings" class="nav-link" data-route="settings">Settings</a>
|
<a href="#/settings" class="nav-link" data-route="settings">Settings</a>
|
||||||
@ -46,6 +47,7 @@
|
|||||||
<div class="content-title" id="contentTitle">Dashboard</div>
|
<div class="content-title" id="contentTitle">Dashboard</div>
|
||||||
<div class="content-actions">
|
<div class="content-actions">
|
||||||
<button id="refreshJobsBtn" class="btn btn-outline" type="button">Refresh</button>
|
<button id="refreshJobsBtn" class="btn btn-outline" type="button">Refresh</button>
|
||||||
|
<div class="header-user" id="userBadge"></div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user