|
7 | 7 | use Exception; |
8 | 8 | use Horde\Core\Config\State; |
9 | 9 | use Horde\Core\Horde; |
| 10 | +use Horde\Core\Service\PermissionService; |
10 | 11 | use Psr\Http\Message\ResponseFactoryInterface; |
11 | 12 | use Psr\Http\Message\ResponseInterface; |
12 | 13 | use Psr\Http\Message\ServerRequestInterface; |
|
17 | 18 | /** |
18 | 19 | * RedirectToLogin middleware |
19 | 20 | * |
20 | | - * Purpose: Redirect to login if not authenticated |
| 21 | + * Purpose: Redirect to login if not authenticated or if the user lacks |
| 22 | + * top level read permission on the target app. |
21 | 23 | * |
22 | 24 | * Reads attribute: |
23 | 25 | * - HORDE_AUTHENTICATED_USER the uid, if authenticated |
| 26 | + * - app the target application name |
24 | 27 | * |
25 | 28 | */ |
26 | 29 | class RedirectToLogin implements MiddlewareInterface |
27 | 30 | { |
28 | 31 | private State $conf; |
29 | 32 | private Horde_Registry $registry; |
30 | 33 | private ResponseFactoryInterface $responseFactory; |
31 | | - public function __construct(Horde_Registry $registry, ResponseFactoryInterface $responseFactory, State $conf) |
32 | | - { |
| 34 | + private ?PermissionService $permissionService; |
| 35 | + |
| 36 | + public function __construct( |
| 37 | + Horde_Registry $registry, |
| 38 | + ResponseFactoryInterface $responseFactory, |
| 39 | + State $conf, |
| 40 | + ?PermissionService $permissionService = null, |
| 41 | + ) { |
33 | 42 | $this->registry = $registry; |
34 | 43 | $this->responseFactory = $responseFactory; |
35 | 44 | $this->conf = $conf; |
| 45 | + $this->permissionService = $permissionService; |
36 | 46 | } |
| 47 | + |
37 | 48 | public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface |
38 | 49 | { |
39 | | - if ($request->getAttribute('HORDE_AUTHENTICATED_USER')) { |
| 50 | + $user = $request->getAttribute('HORDE_AUTHENTICATED_USER'); |
| 51 | + $app = $request->getAttribute('app'); |
| 52 | + |
| 53 | + // Check app-level read permission if PermissionService is available |
| 54 | + if ($this->permissionService !== null && $app) { |
| 55 | + if ($this->permissionService->exists($app)) { |
| 56 | + // Pass empty string for guests — backend returns guest permissions |
| 57 | + $checkUser = $user ?: ''; |
| 58 | + if (!$this->permissionService->hasPermission($app, $checkUser, ['read'])) { |
| 59 | + return $this->redirectToLogin($request); |
| 60 | + } |
| 61 | + // Permission granted — allow through even if not authenticated (guest read) |
| 62 | + return $handler->handle($request); |
| 63 | + } |
| 64 | + } |
| 65 | + |
| 66 | + // No permission defined or no service — fall back to authentication check |
| 67 | + if ($user) { |
40 | 68 | return $handler->handle($request); |
41 | 69 | } |
42 | 70 |
|
| 71 | + return $this->redirectToLogin($request); |
| 72 | + } |
| 73 | + |
| 74 | + private function redirectToLogin(ServerRequestInterface $request): ResponseInterface |
| 75 | + { |
43 | 76 | $requestUrl = (string) $request->getUri(); |
44 | 77 | $signedRequestUrl = Horde::signUrl($requestUrl); |
45 | 78 |
|
46 | | - // set baseurl: check if alternative login is set and use it as baseurl |
47 | 79 | $configArray = $this->conf->toArray(); |
48 | | - $configArray['auth']['alternate_login'] ?? null; |
| 80 | + $alternateLogin = $configArray['auth']['alternate_login'] ?? null; |
49 | 81 |
|
50 | 82 | if (!empty($alternateLogin)) { |
51 | 83 | $baseUrl = $alternateLogin; |
52 | 84 | } else { |
53 | | - // set baseurl: if no alternative login, use Horde login as baseurl |
54 | 85 | $baseUrl = $this->registry->getServiceLink('login'); |
55 | | - }; |
| 86 | + } |
56 | 87 |
|
57 | 88 | $redirectUrl = (string) Horde::url($baseUrl, true)->add('url', $signedRequestUrl); |
58 | 89 |
|
|
0 commit comments