rights system
This commit is contained in:
@@ -154,6 +154,52 @@ class PermissionController {
|
||||
res.status(500).json({ success: false, message: 'Fehler beim Setzen des Wartungsmodus' });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/admin/permissions/config
|
||||
* Returns the dependency configuration (group hierarchy + permission deps).
|
||||
*/
|
||||
async getDependencyConfig(_req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const config = await permissionService.getDependencyConfig();
|
||||
res.json({ success: true, data: config });
|
||||
} catch (error) {
|
||||
logger.error('Failed to get dependency config', { error });
|
||||
res.status(500).json({ success: false, message: 'Fehler beim Laden der Konfiguration' });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* PUT /api/admin/permissions/config
|
||||
* Updates the dependency configuration.
|
||||
* Body: { groupHierarchy?: Record<string, string[]>, permissionDeps?: Record<string, string[]> }
|
||||
*/
|
||||
async setDependencyConfig(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { groupHierarchy, permissionDeps } = req.body;
|
||||
|
||||
if (groupHierarchy !== undefined) {
|
||||
if (typeof groupHierarchy !== 'object' || groupHierarchy === null) {
|
||||
res.status(400).json({ success: false, message: 'groupHierarchy must be an object' });
|
||||
return;
|
||||
}
|
||||
await permissionService.setGroupHierarchy(groupHierarchy, req.user!.id);
|
||||
}
|
||||
|
||||
if (permissionDeps !== undefined) {
|
||||
if (typeof permissionDeps !== 'object' || permissionDeps === null) {
|
||||
res.status(400).json({ success: false, message: 'permissionDeps must be an object' });
|
||||
return;
|
||||
}
|
||||
await permissionService.setPermissionDeps(permissionDeps, req.user!.id);
|
||||
}
|
||||
|
||||
res.json({ success: true, message: 'Konfiguration aktualisiert' });
|
||||
} catch (error) {
|
||||
logger.error('Failed to set dependency config', { error });
|
||||
res.status(500).json({ success: false, message: 'Fehler beim Speichern der Konfiguration' });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new PermissionController();
|
||||
|
||||
@@ -12,6 +12,8 @@ router.get('/me', authenticate, permissionController.getMyPermissions.bind(permi
|
||||
router.get('/admin/matrix', authenticate, requirePermission('admin:view'), permissionController.getMatrix.bind(permissionController));
|
||||
router.get('/admin/groups', authenticate, requirePermission('admin:view'), permissionController.getGroups.bind(permissionController));
|
||||
router.get('/admin/unknown-groups', authenticate, requirePermission('admin:view'), permissionController.getUnknownGroups.bind(permissionController));
|
||||
router.get('/admin/config', authenticate, requirePermission('admin:view'), permissionController.getDependencyConfig.bind(permissionController));
|
||||
router.put('/admin/config', authenticate, requirePermission('admin:write'), permissionController.setDependencyConfig.bind(permissionController));
|
||||
router.put('/admin/group/:groupName', authenticate, requirePermission('admin:write'), permissionController.setGroupPermissions.bind(permissionController));
|
||||
router.put('/admin/bulk', authenticate, requirePermission('admin:write'), permissionController.setBulkPermissions.bind(permissionController));
|
||||
router.put('/admin/maintenance/:featureGroupId', authenticate, requirePermission('admin:write'), permissionController.setMaintenanceFlag.bind(permissionController));
|
||||
|
||||
@@ -1,6 +1,55 @@
|
||||
import pool from '../config/database';
|
||||
import logger from '../utils/logger';
|
||||
|
||||
// Default configs — used when no DB config exists yet
|
||||
const DEFAULT_GROUP_HIERARCHY: Record<string, string[]> = {
|
||||
'dashboard_mitglied': ['dashboard_chargen', 'dashboard_atemschutz', 'dashboard_moderator', 'dashboard_zeugmeister', 'dashboard_fahrmeister', 'dashboard_gruppenfuehrer', 'dashboard_kommando'],
|
||||
'dashboard_chargen': ['dashboard_gruppenfuehrer', 'dashboard_kommando'],
|
||||
'dashboard_atemschutz': ['dashboard_kommando'],
|
||||
'dashboard_moderator': ['dashboard_kommando'],
|
||||
'dashboard_zeugmeister': ['dashboard_kommando'],
|
||||
'dashboard_fahrmeister': ['dashboard_kommando'],
|
||||
'dashboard_gruppenfuehrer': ['dashboard_kommando'],
|
||||
'dashboard_kommando': [],
|
||||
};
|
||||
|
||||
const DEFAULT_PERMISSION_DEPS: Record<string, string[]> = {
|
||||
'kalender:create': ['kalender:view'],
|
||||
'kalender:cancel': ['kalender:view'],
|
||||
'kalender:mark_attendance': ['kalender:view'],
|
||||
'kalender:create_bookings': ['kalender:view'],
|
||||
'kalender:edit_bookings': ['kalender:view', 'kalender:create_bookings'],
|
||||
'kalender:cancel_own_bookings': ['kalender:view'],
|
||||
'kalender:delete_bookings': ['kalender:view', 'kalender:edit_bookings'],
|
||||
'kalender:manage_categories': ['kalender:view'],
|
||||
'kalender:view_reports': ['kalender:view'],
|
||||
'kalender:widget_events': ['kalender:view'],
|
||||
'kalender:widget_bookings': ['kalender:view'],
|
||||
'kalender:widget_quick_add': ['kalender:view', 'kalender:create'],
|
||||
'fahrzeuge:create': ['fahrzeuge:view'],
|
||||
'fahrzeuge:change_status': ['fahrzeuge:view'],
|
||||
'fahrzeuge:manage_maintenance': ['fahrzeuge:view'],
|
||||
'fahrzeuge:delete': ['fahrzeuge:view', 'fahrzeuge:create'],
|
||||
'fahrzeuge:widget': ['fahrzeuge:view'],
|
||||
'einsaetze:view_reports': ['einsaetze:view'],
|
||||
'einsaetze:create': ['einsaetze:view'],
|
||||
'einsaetze:delete': ['einsaetze:view', 'einsaetze:create'],
|
||||
'einsaetze:manage_personnel': ['einsaetze:view'],
|
||||
'ausruestung:create': ['ausruestung:view'],
|
||||
'ausruestung:manage_maintenance': ['ausruestung:view'],
|
||||
'ausruestung:delete': ['ausruestung:view', 'ausruestung:create'],
|
||||
'ausruestung:widget': ['ausruestung:view'],
|
||||
'mitglieder:view_all': ['mitglieder:view_own'],
|
||||
'mitglieder:edit': ['mitglieder:view_own', 'mitglieder:view_all'],
|
||||
'mitglieder:create_profile': ['mitglieder:view_own', 'mitglieder:view_all', 'mitglieder:edit'],
|
||||
'atemschutz:create': ['atemschutz:view'],
|
||||
'atemschutz:delete': ['atemschutz:view', 'atemschutz:create'],
|
||||
'atemschutz:widget': ['atemschutz:view'],
|
||||
'wissen:widget_recent': ['wissen:view'],
|
||||
'wissen:widget_search': ['wissen:view'],
|
||||
'admin:write': ['admin:view'],
|
||||
};
|
||||
|
||||
export interface FeatureGroupRow {
|
||||
id: string;
|
||||
label: string;
|
||||
@@ -255,10 +304,76 @@ class PermissionService {
|
||||
'UPDATE feature_groups SET maintenance = $1 WHERE id = $2',
|
||||
[active, featureGroup]
|
||||
);
|
||||
// Reload cache
|
||||
await this.loadCache();
|
||||
logger.info('Maintenance flag updated', { featureGroup, active });
|
||||
}
|
||||
|
||||
// ── Dependency config (stored in app_settings) ──
|
||||
|
||||
async getGroupHierarchy(): Promise<Record<string, string[]>> {
|
||||
try {
|
||||
const result = await pool.query(
|
||||
"SELECT value FROM app_settings WHERE key = 'permission_group_hierarchy'"
|
||||
);
|
||||
if (result.rows.length > 0 && result.rows[0].value) {
|
||||
const val = typeof result.rows[0].value === 'string'
|
||||
? JSON.parse(result.rows[0].value)
|
||||
: result.rows[0].value;
|
||||
return val;
|
||||
}
|
||||
} catch (error) {
|
||||
logger.warn('Failed to load group hierarchy from DB, using defaults', { error });
|
||||
}
|
||||
return DEFAULT_GROUP_HIERARCHY;
|
||||
}
|
||||
|
||||
async setGroupHierarchy(hierarchy: Record<string, string[]>, userId: string): Promise<void> {
|
||||
await pool.query(
|
||||
`INSERT INTO app_settings (key, value, updated_by, updated_at)
|
||||
VALUES ('permission_group_hierarchy', $1, $2, NOW())
|
||||
ON CONFLICT (key) DO UPDATE SET value = $1, updated_by = $2, updated_at = NOW()`,
|
||||
[JSON.stringify(hierarchy), userId]
|
||||
);
|
||||
logger.info('Group hierarchy updated', { userId });
|
||||
}
|
||||
|
||||
async getPermissionDeps(): Promise<Record<string, string[]>> {
|
||||
try {
|
||||
const result = await pool.query(
|
||||
"SELECT value FROM app_settings WHERE key = 'permission_deps'"
|
||||
);
|
||||
if (result.rows.length > 0 && result.rows[0].value) {
|
||||
const val = typeof result.rows[0].value === 'string'
|
||||
? JSON.parse(result.rows[0].value)
|
||||
: result.rows[0].value;
|
||||
return val;
|
||||
}
|
||||
} catch (error) {
|
||||
logger.warn('Failed to load permission deps from DB, using defaults', { error });
|
||||
}
|
||||
return DEFAULT_PERMISSION_DEPS;
|
||||
}
|
||||
|
||||
async setPermissionDeps(deps: Record<string, string[]>, userId: string): Promise<void> {
|
||||
await pool.query(
|
||||
`INSERT INTO app_settings (key, value, updated_by, updated_at)
|
||||
VALUES ('permission_deps', $1, $2, NOW())
|
||||
ON CONFLICT (key) DO UPDATE SET value = $1, updated_by = $2, updated_at = NOW()`,
|
||||
[JSON.stringify(deps), userId]
|
||||
);
|
||||
logger.info('Permission deps updated', { userId });
|
||||
}
|
||||
|
||||
async getDependencyConfig(): Promise<{
|
||||
groupHierarchy: Record<string, string[]>;
|
||||
permissionDeps: Record<string, string[]>;
|
||||
}> {
|
||||
const [groupHierarchy, permissionDeps] = await Promise.all([
|
||||
this.getGroupHierarchy(),
|
||||
this.getPermissionDeps(),
|
||||
]);
|
||||
return { groupHierarchy, permissionDeps };
|
||||
}
|
||||
}
|
||||
|
||||
export const permissionService = new PermissionService();
|
||||
|
||||
Reference in New Issue
Block a user