feat(auth): Add hasPermission method for role-based access control

feat(module-registry): Enhance module booting logic for development environment

feat(menu-item-registry): Allow loading of unlicensed plugins in development mode
This commit is contained in:
olli 2025-12-05 15:02:42 +01:00
parent a787019a3b
commit 6b5e82cd2e
3 changed files with 57 additions and 5 deletions

View File

@ -15,6 +15,26 @@ export const useAuthStore = defineStore('auth', () => {
return user.value.roles && user.value.roles.includes(role);
};
const hasPermission = (permission) => {
if (!user.value) return false;
// Admin hat immer alle Permissions
if (hasRole('ROLE_ADMIN')) return true;
// Permission format: "module.action" z.B. "billing.view"
const [module, action] = permission.split('.');
if (!module || !action) return false;
// Prüfe ob User die Module-Permission hat
// Dies wird serverseitig validiert, hier nur UI-Steuerung
if (!user.value.modulePermissions) return false;
const modulePerms = user.value.modulePermissions[module];
if (!modulePerms) return false;
return modulePerms.includes(action);
};
const isAdmin = computed(() => hasRole('ROLE_ADMIN'));
const initializeFromElement = () => {
@ -40,6 +60,7 @@ export const useAuthStore = defineStore('auth', () => {
isAuthenticated,
fullName,
hasRole,
hasPermission,
isAdmin,
initializeFromElement,
logout

View File

@ -61,17 +61,30 @@ class ModuleRegistry
*/
public function bootModules(): void
{
$isDev = ($_ENV['APP_ENV'] ?? 'prod') === 'dev';
foreach ($this->modules as $identifier => $module) {
try {
// Lizenzprüfung
if (!$module->isLicensed()) {
$licenseInfo = $module->getLicenseInfo();
$this->logger->warning(sprintf(
'Modul "%s" wird nicht gebootet: %s',
// In Production: Module ohne Lizenz nicht booten
if (!$isDev) {
$this->logger->warning(sprintf(
'Modul "%s" wird nicht gebootet: %s',
$identifier,
$licenseInfo['message'] ?? 'Keine gültige Lizenz'
));
continue;
}
// In Development: Warnung loggen, aber trotzdem booten
$this->logger->info(sprintf(
'Modul "%s" (Development): Boot ohne Lizenz - %s',
$identifier,
$licenseInfo['message'] ?? 'Keine gültige Lizenz'
));
continue;
}
// Modul booten
@ -111,6 +124,7 @@ class ModuleRegistry
/**
* Prüft, ob ein Modul registriert UND lizenziert ist
* (oder in Development ohne Lizenz läuft)
*/
public function isModuleActive(string $identifier): bool
{
@ -120,6 +134,13 @@ class ModuleRegistry
return false;
}
// In Development: Modul ist aktiv wenn kein Boot-Error
$isDev = ($_ENV['APP_ENV'] ?? 'prod') === 'dev';
if ($isDev) {
return !isset($this->bootErrors[$identifier]);
}
// In Production: Modul muss lizenziert sein
return $module->isLicensed() && !isset($this->bootErrors[$identifier]);
}

View File

@ -36,8 +36,11 @@ class MenuItemRegistry
$menuItems = [];
foreach ($this->moduleRegistry->getAllModules() as $plugin) {
// Nur lizenzierte Module berücksichtigen
if (!$plugin->isLicensed()) {
// In Production: Nur lizenzierte Module
// In Development: Alle Module (für lokale Entwicklung)
$isDev = ($_ENV['APP_ENV'] ?? 'prod') === 'dev';
if (!$isDev && !$plugin->isLicensed()) {
$this->logger->debug(sprintf(
'Plugin "%s" übersprungen: Nicht lizenziert',
$plugin->getIdentifier()
@ -45,6 +48,13 @@ class MenuItemRegistry
continue;
}
if ($isDev && !$plugin->isLicensed()) {
$this->logger->info(sprintf(
'Plugin "%s": Development-Modus - wird ohne Lizenz geladen',
$plugin->getIdentifier()
));
}
try {
$pluginMenuItems = $plugin->getMenuItems();