Files
Mitgliederverwaltung/.plans/open/issue-207-authorization-middleware-instanceof-chains.md
shahondin1624 b29a268b1d Restructure .plans/ into done/ and open/ subdirectories
- Move completed plan files to .plans/done/
- Move 18 open plan files to .plans/open/
- Update .gitignore to exclude .verified_plans temp file
- Verified all 18 open plans still describe unimplemented issues
2026-04-28 20:30:55 +02:00

2.0 KiB

Issue #207: Authorization Middleware Uses instanceof Chains

Problem

The AuthorizationMiddleware::getAdminMethods() method uses a chain of instanceof checks to determine admin-only methods:

private function getAdminMethods($controller): ?array {
    if ($controller instanceof FeeController) {
        return self::ADMIN_METHODS_FEE;
    }
    if ($controller instanceof StufeController) {
        return self::ADMIN_METHODS_STUFE;
    }
    if ($controller instanceof ExportController) {
        return self::ADMIN_METHODS_EXPORT;
    }
    if ($controller instanceof MemberController) {
        return self::ADMIN_METHODS_MEMBER;
    }
    // ... more controllers
    return null;
}

This pattern violates Open/Closed Principle — adding a new controller requires modifying the middleware.

Impact

  • Hard to maintain as controller count grows
  • Easy to forget to add a new controller to the chain
  • Mixing of concerns (controller identity vs. permissions)

Solution

Use PHP attributes to declare permissions on controller methods:

#[Attribute]
class RequiresAdmin implements \Attribute {}

#[RequiresAdmin]
public function createRule(): JSONResponse { ... }

Then scan for attributes in middleware:

public function beforeController($controller, $methodName): void {
    $method = new \ReflectionMethod($controller, $methodName);
    if ($method->getAttributes(RequiresAdmin::class)) {
        if (!$this->permissionService->isAdmin($userId)) {
            throw new \RuntimeException('Authorization: admin required');
        }
    }
    // ... rest of checks
}

Tasks

  • Create RequiresRead and RequiresWrite attributes
  • Create RequiresAdmin attribute
  • Add attributes to all controller methods
  • Refactor AuthorizationMiddleware to use reflection + attributes
  • Remove ADMIN_METHODS_* constants and getAdminMethods() method
  • Test all permission levels still work correctly

Labels

  • refactoring
  • backend
  • priority:medium
  • architecture