# Issue #207: Authorization Middleware Uses instanceof Chains

## Problem

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

```php
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:

```php
#[Attribute]
class RequiresAdmin implements \Attribute {}

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

Then scan for attributes in middleware:

```php
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