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
|
||||
function getAllowedProjects(req) {
|
||||
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
|
||||
const authHeaders = {};
|
||||
let hasAuthentikHeaders = false;
|
||||
for (const key of Object.keys(req.headers)) {
|
||||
if (key.startsWith('x-authentik-')) {
|
||||
authHeaders[key] = req.headers[key];
|
||||
hasAuthentikHeaders = true;
|
||||
}
|
||||
}
|
||||
if (Object.keys(authHeaders).length > 0) {
|
||||
|
||||
if (hasAuthentikHeaders) {
|
||||
log(`Authentik Headers empfangen: ${JSON.stringify(authHeaders)}`);
|
||||
}
|
||||
|
||||
if (!username) {
|
||||
if (!hasAuthentikHeaders) {
|
||||
// Lokale Entwicklung (Fallback)
|
||||
if (!isProd) {
|
||||
return {
|
||||
|
|
@ -65,6 +66,9 @@ function getAllowedProjects(req) {
|
|||
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)
|
||||
let allowedProjects = [];
|
||||
if (projectsHeader) {
|
||||
|
|
@ -74,7 +78,9 @@ function getAllowedProjects(req) {
|
|||
// Gruppen aus Authentik-Header extrahieren und ebenfalls als Projekte behandeln
|
||||
const groupsHeader = req.headers['x-authentik-groups'];
|
||||
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) {
|
||||
// Alle Gruppen einfach als erlaubte Projekte hinzufügen.
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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
|
||||
const normalizedRequested = requestedProjectIdentifier.toLowerCase().trim();
|
||||
const hasDirectAccess = auth.projects.some(p => p.toLowerCase().trim() === normalizedRequested);
|
||||
const normalizedRequested = normalizeStr(requestedProjectIdentifier);
|
||||
const hasDirectAccess = auth.projects.some(p => normalizeStr(p) === normalizedRequested);
|
||||
if (hasDirectAccess) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -126,10 +138,9 @@ async function checkProjectAccess(req, requestedProjectIdentifier, client) {
|
|||
projectName = res.rows[0].name;
|
||||
}
|
||||
} else {
|
||||
const normalized = requestedProjectIdentifier.toLowerCase().replace(/[-_]/g, '');
|
||||
const res = await client.query(
|
||||
'SELECT id, name FROM geodaten.projekte WHERE LOWER(REPLACE(REPLACE(name, \'-\', \'\'), \'_\', \'\')) = $1',
|
||||
[normalized]
|
||||
'SELECT id, name FROM geodaten.projekte WHERE LOWER(REPLACE(REPLACE(REPLACE(name, \'-\', \'\'), \'_\', \'\'), \' \', \'\')) = $1',
|
||||
[normalizedRequested]
|
||||
);
|
||||
if (res.rows.length > 0) {
|
||||
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
|
||||
const match = auth.projects.some(p => {
|
||||
const normalizedP = p.toLowerCase().trim();
|
||||
return normalizedP === projectId.toLowerCase() ||
|
||||
normalizedP === (projectName || '').toLowerCase().trim();
|
||||
const normalizedP = normalizeStr(p);
|
||||
return normalizedP === normalizeStr(projectId) ||
|
||||
normalizedP === normalizeStr(projectName);
|
||||
});
|
||||
|
||||
return match;
|
||||
|
|
@ -172,11 +183,11 @@ app.get('/api/user/projekte', async (req, res) => {
|
|||
projects = result.rows;
|
||||
} 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 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(
|
||||
`SELECT id, name FROM geodaten.projekte
|
||||
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`,
|
||||
[originalIds, normalizedNames]
|
||||
);
|
||||
|
|
@ -186,7 +197,14 @@ app.get('/api/user/projekte', async (req, res) => {
|
|||
res.json({
|
||||
username: auth.username,
|
||||
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) {
|
||||
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) => {
|
||||
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;
|
||||
if (uuidRegex.test(input)) return input;
|
||||
|
||||
// Normalisierte Suche (Kleinschreibung, alle Bindestriche und Unterstriche entfernen)
|
||||
const normalized = input.toLowerCase().replace(/[-_]/g, '');
|
||||
// Normalisierte Suche (Kleinschreibung, alle Bindestriche, Unterstriche und Leerzeichen entfernen)
|
||||
const normalized = input.toLowerCase().replace(/[-_ ]/g, '');
|
||||
|
||||
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]
|
||||
);
|
||||
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();
|
||||
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);
|
||||
if (!projId) {
|
||||
return res.status(404).json({ error: 'Projekt nicht gefunden.' });
|
||||
|
|
|
|||
Loading…
Reference in New Issue