Fix Authentik permissions fallback and add debug endpoint
Deploy Standortplaner / deploy (push) Successful in 17s
Details
Deploy Standortplaner / deploy (push) Successful in 17s
Details
This commit is contained in:
parent
cf12184948
commit
b81389d180
77
server.js
77
server.js
|
|
@ -38,21 +38,22 @@ function log(msg) {
|
||||||
// Hilfsfunktion zur Ermittlung der erlaubten Projekte aus Authentik-Headern
|
// Hilfsfunktion zur Ermittlung der erlaubten Projekte aus Authentik-Headern
|
||||||
function getAllowedProjects(req) {
|
function getAllowedProjects(req) {
|
||||||
const isProd = process.env.NODE_ENV === 'production';
|
const isProd = process.env.NODE_ENV === 'production';
|
||||||
const username = req.headers['x-authentik-username'];
|
|
||||||
const projectsHeader = req.headers['x-authentik-meta-projekte'];
|
|
||||||
|
|
||||||
// Debug-Ausgabe der Authentik-Header
|
// Debug-Ausgabe der Authentik-Header
|
||||||
const authHeaders = {};
|
const authHeaders = {};
|
||||||
|
let hasAuthentikHeaders = false;
|
||||||
for (const key of Object.keys(req.headers)) {
|
for (const key of Object.keys(req.headers)) {
|
||||||
if (key.startsWith('x-authentik-')) {
|
if (key.startsWith('x-authentik-')) {
|
||||||
authHeaders[key] = req.headers[key];
|
authHeaders[key] = req.headers[key];
|
||||||
|
hasAuthentikHeaders = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Object.keys(authHeaders).length > 0) {
|
|
||||||
|
if (hasAuthentikHeaders) {
|
||||||
log(`Authentik Headers empfangen: ${JSON.stringify(authHeaders)}`);
|
log(`Authentik Headers empfangen: ${JSON.stringify(authHeaders)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!username) {
|
if (!hasAuthentikHeaders) {
|
||||||
// Lokale Entwicklung (Fallback)
|
// Lokale Entwicklung (Fallback)
|
||||||
if (!isProd) {
|
if (!isProd) {
|
||||||
return {
|
return {
|
||||||
|
|
@ -65,6 +66,9 @@ function getAllowedProjects(req) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const username = req.headers['x-authentik-username'] || req.headers['x-authentik-email'] || req.headers['x-authentik-name'] || 'authentik-user';
|
||||||
|
const projectsHeader = req.headers['x-authentik-meta-projekte'];
|
||||||
|
|
||||||
// Projekte aus Attribut-Header parsen (Komma-separiert)
|
// Projekte aus Attribut-Header parsen (Komma-separiert)
|
||||||
let allowedProjects = [];
|
let allowedProjects = [];
|
||||||
if (projectsHeader) {
|
if (projectsHeader) {
|
||||||
|
|
@ -74,7 +78,9 @@ function getAllowedProjects(req) {
|
||||||
// Gruppen aus Authentik-Header extrahieren und ebenfalls als Projekte behandeln
|
// Gruppen aus Authentik-Header extrahieren und ebenfalls als Projekte behandeln
|
||||||
const groupsHeader = req.headers['x-authentik-groups'];
|
const groupsHeader = req.headers['x-authentik-groups'];
|
||||||
if (groupsHeader) {
|
if (groupsHeader) {
|
||||||
const groups = groupsHeader.split('|').map(g => g.trim()).filter(Boolean);
|
// Authentik might use | or , depending on proxy settings. Replace commas with pipes to be safe.
|
||||||
|
const normalizedGroupsHeader = groupsHeader.replace(/,/g, '|');
|
||||||
|
const groups = normalizedGroupsHeader.split('|').map(g => g.trim()).filter(Boolean);
|
||||||
for (const group of groups) {
|
for (const group of groups) {
|
||||||
// Alle Gruppen einfach als erlaubte Projekte hinzufügen.
|
// Alle Gruppen einfach als erlaubte Projekte hinzufügen.
|
||||||
// Was kein Projekt ist (z.B. "portal-users"), wird später beim DB-Abgleich einfach ignoriert.
|
// Was kein Projekt ist (z.B. "portal-users"), wird später beim DB-Abgleich einfach ignoriert.
|
||||||
|
|
@ -105,9 +111,15 @@ async function checkProjectAccess(req, requestedProjectIdentifier, client) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hilfsfunktion zum Normalisieren (entfernt Bindestriche, Unterstriche und Leerzeichen)
|
||||||
|
const normalizeStr = (str) => {
|
||||||
|
if (!str) return '';
|
||||||
|
return str.toLowerCase().replace(/[-_ ]/g, '').trim();
|
||||||
|
};
|
||||||
|
|
||||||
// Zuerst prüfen, ob die angeforderte ID/Name direkt im Header steht
|
// Zuerst prüfen, ob die angeforderte ID/Name direkt im Header steht
|
||||||
const normalizedRequested = requestedProjectIdentifier.toLowerCase().trim();
|
const normalizedRequested = normalizeStr(requestedProjectIdentifier);
|
||||||
const hasDirectAccess = auth.projects.some(p => p.toLowerCase().trim() === normalizedRequested);
|
const hasDirectAccess = auth.projects.some(p => normalizeStr(p) === normalizedRequested);
|
||||||
if (hasDirectAccess) {
|
if (hasDirectAccess) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -126,10 +138,9 @@ async function checkProjectAccess(req, requestedProjectIdentifier, client) {
|
||||||
projectName = res.rows[0].name;
|
projectName = res.rows[0].name;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const normalized = requestedProjectIdentifier.toLowerCase().replace(/[-_]/g, '');
|
|
||||||
const res = await client.query(
|
const res = await client.query(
|
||||||
'SELECT id, name FROM geodaten.projekte WHERE LOWER(REPLACE(REPLACE(name, \'-\', \'\'), \'_\', \'\')) = $1',
|
'SELECT id, name FROM geodaten.projekte WHERE LOWER(REPLACE(REPLACE(REPLACE(name, \'-\', \'\'), \'_\', \'\'), \' \', \'\')) = $1',
|
||||||
[normalized]
|
[normalizedRequested]
|
||||||
);
|
);
|
||||||
if (res.rows.length > 0) {
|
if (res.rows.length > 0) {
|
||||||
projectId = res.rows[0].id;
|
projectId = res.rows[0].id;
|
||||||
|
|
@ -141,9 +152,9 @@ async function checkProjectAccess(req, requestedProjectIdentifier, client) {
|
||||||
|
|
||||||
// Prüfen, ob die aufgelöste ID oder der Projektname in der erlaubten Liste steht
|
// Prüfen, ob die aufgelöste ID oder der Projektname in der erlaubten Liste steht
|
||||||
const match = auth.projects.some(p => {
|
const match = auth.projects.some(p => {
|
||||||
const normalizedP = p.toLowerCase().trim();
|
const normalizedP = normalizeStr(p);
|
||||||
return normalizedP === projectId.toLowerCase() ||
|
return normalizedP === normalizeStr(projectId) ||
|
||||||
normalizedP === (projectName || '').toLowerCase().trim();
|
normalizedP === normalizeStr(projectName);
|
||||||
});
|
});
|
||||||
|
|
||||||
return match;
|
return match;
|
||||||
|
|
@ -172,11 +183,11 @@ app.get('/api/user/projekte', async (req, res) => {
|
||||||
projects = result.rows;
|
projects = result.rows;
|
||||||
} else if (auth.projects.length > 0) {
|
} else if (auth.projects.length > 0) {
|
||||||
const originalIds = auth.projects.filter(p => /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(p));
|
const originalIds = auth.projects.filter(p => /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(p));
|
||||||
const normalizedNames = auth.projects.map(p => p.toLowerCase().replace(/[-_]/g, '').trim());
|
const normalizedNames = auth.projects.map(p => p.toLowerCase().replace(/[-_ ]/g, '').trim());
|
||||||
const result = await client.query(
|
const result = await client.query(
|
||||||
`SELECT id, name FROM geodaten.projekte
|
`SELECT id, name FROM geodaten.projekte
|
||||||
WHERE id::text = ANY($1::text[])
|
WHERE id::text = ANY($1::text[])
|
||||||
OR LOWER(REPLACE(REPLACE(name, '-', ''), '_', '')) = ANY($2::text[])
|
OR LOWER(REPLACE(REPLACE(REPLACE(name, '-', ''), '_', ''), ' ', '')) = ANY($2::text[])
|
||||||
ORDER BY name`,
|
ORDER BY name`,
|
||||||
[originalIds, normalizedNames]
|
[originalIds, normalizedNames]
|
||||||
);
|
);
|
||||||
|
|
@ -186,7 +197,14 @@ app.get('/api/user/projekte', async (req, res) => {
|
||||||
res.json({
|
res.json({
|
||||||
username: auth.username,
|
username: auth.username,
|
||||||
projects: projects,
|
projects: projects,
|
||||||
isLocalDev: auth.isLocalDev
|
isLocalDev: auth.isLocalDev,
|
||||||
|
debug_raw_groups: req.headers['x-authentik-groups'] || null,
|
||||||
|
debug_raw_headers: Object.keys(req.headers)
|
||||||
|
.filter(k => k.startsWith('x-authentik-'))
|
||||||
|
.reduce((obj, key) => {
|
||||||
|
obj[key] = req.headers[key];
|
||||||
|
return obj;
|
||||||
|
}, {})
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log(`FEHLER beim Laden der erlaubten Projekte: ${err.message}`);
|
log(`FEHLER beim Laden der erlaubten Projekte: ${err.message}`);
|
||||||
|
|
@ -211,6 +229,19 @@ app.get('/api/status', async (req, res) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Debug-Endpunkt für HTTP Headers (hilfreich für Authentik-Fehlersuche)
|
||||||
|
app.get('/api/debug/headers', (req, res) => {
|
||||||
|
res.json({
|
||||||
|
all_headers: req.headers,
|
||||||
|
authentik_headers: Object.keys(req.headers)
|
||||||
|
.filter(k => k.startsWith('x-authentik-'))
|
||||||
|
.reduce((obj, key) => {
|
||||||
|
obj[key] = req.headers[key];
|
||||||
|
return obj;
|
||||||
|
}, {})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
app.get('/api/logs', (req, res) => {
|
app.get('/api/logs', (req, res) => {
|
||||||
res.type('text/plain').send(serverLogs.join('\n'));
|
res.type('text/plain').send(serverLogs.join('\n'));
|
||||||
});
|
});
|
||||||
|
|
@ -321,11 +352,11 @@ async function resolveProjectId(client, input) {
|
||||||
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
||||||
if (uuidRegex.test(input)) return input;
|
if (uuidRegex.test(input)) return input;
|
||||||
|
|
||||||
// Normalisierte Suche (Kleinschreibung, alle Bindestriche und Unterstriche entfernen)
|
// Normalisierte Suche (Kleinschreibung, alle Bindestriche, Unterstriche und Leerzeichen entfernen)
|
||||||
const normalized = input.toLowerCase().replace(/[-_]/g, '');
|
const normalized = input.toLowerCase().replace(/[-_ ]/g, '');
|
||||||
|
|
||||||
const res = await client.query(
|
const res = await client.query(
|
||||||
'SELECT id FROM geodaten.projekte WHERE LOWER(REPLACE(REPLACE(name, \'-\', \'\'), \'_\', \'\')) = $1',
|
'SELECT id FROM geodaten.projekte WHERE LOWER(REPLACE(REPLACE(REPLACE(name, \'-\', \'\'), \'_\', \'\'), \' \', \'\')) = $1',
|
||||||
[normalized]
|
[normalized]
|
||||||
);
|
);
|
||||||
return res.rows.length > 0 ? res.rows[0].id : null;
|
return res.rows.length > 0 ? res.rows[0].id : null;
|
||||||
|
|
@ -691,6 +722,12 @@ app.get('/api/layers/leitungsverlaeufe', async (req, res) => {
|
||||||
|
|
||||||
const client = await pool.connect();
|
const client = await pool.connect();
|
||||||
try {
|
try {
|
||||||
|
const hasAccess = await checkProjectAccess(req, projectParam, client);
|
||||||
|
if (!hasAccess) {
|
||||||
|
log(`Zugriff verweigert für Leitungsverläufe in Projekt: ${projectParam}`);
|
||||||
|
return res.status(403).json({ error: 'Keine Berechtigung für dieses Projekt.' });
|
||||||
|
}
|
||||||
|
|
||||||
const projId = await resolveProjectId(client, projectParam);
|
const projId = await resolveProjectId(client, projectParam);
|
||||||
if (!projId) {
|
if (!projId) {
|
||||||
return res.status(404).json({ error: 'Projekt nicht gefunden.' });
|
return res.status(404).json({ error: 'Projekt nicht gefunden.' });
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue