Skip to content

Commit f814754

Browse files
committed
refactor: Move hasAnnotationOrAttribute to MiddlewareUtils
Signed-off-by: Carl Schwan <carlschwan@kde.org>
1 parent 6408ed0 commit f814754

14 files changed

Lines changed: 194 additions & 333 deletions

File tree

build/psalm-baseline.xml

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3210,13 +3210,6 @@
32103210
</DeprecatedMethod>
32113211
</file>
32123212
<file src="core/Middleware/TwoFactorMiddleware.php">
3213-
<DeprecatedInterface>
3214-
<code><![CDATA[private]]></code>
3215-
</DeprecatedInterface>
3216-
<DeprecatedMethod>
3217-
<code><![CDATA[hasAnnotation]]></code>
3218-
<code><![CDATA[hasAnnotation]]></code>
3219-
</DeprecatedMethod>
32203213
<NoInterfaceProperties>
32213214
<code><![CDATA[$this->request->server]]></code>
32223215
</NoInterfaceProperties>

core/Middleware/TwoFactorMiddleware.php

Lines changed: 3 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
use Exception;
1313
use OC\AppFramework\Http\Attributes\TwoFactorSetUpDoneRequired;
14+
use OC\AppFramework\Middleware\MiddlewareUtils;
1415
use OC\Authentication\Exceptions\TwoFactorAuthRequiredException;
1516
use OC\Authentication\Exceptions\UserAlreadyLoggedInException;
1617
use OC\Authentication\TwoFactorAuth\Manager;
@@ -22,13 +23,11 @@
2223
use OCP\AppFramework\Http\Attribute\NoTwoFactorRequired;
2324
use OCP\AppFramework\Http\RedirectResponse;
2425
use OCP\AppFramework\Middleware;
25-
use OCP\AppFramework\Utility\IControllerMethodReflector;
2626
use OCP\Authentication\TwoFactorAuth\ALoginSetupController;
2727
use OCP\IRequest;
2828
use OCP\ISession;
2929
use OCP\IURLGenerator;
3030
use OCP\IUser;
31-
use Psr\Log\LoggerInterface;
3231
use ReflectionMethod;
3332

3433
class TwoFactorMiddleware extends Middleware {
@@ -37,9 +36,8 @@ public function __construct(
3736
private Session $userSession,
3837
private ISession $session,
3938
private IURLGenerator $urlGenerator,
40-
private IControllerMethodReflector $reflector,
39+
private MiddlewareUtils $middlewareUtils,
4140
private IRequest $request,
42-
private LoggerInterface $logger,
4341
) {
4442
}
4543

@@ -50,7 +48,7 @@ public function __construct(
5048
public function beforeController($controller, $methodName) {
5149
$reflectionMethod = new ReflectionMethod($controller, $methodName);
5250

53-
if ($this->hasAnnotationOrAttribute($reflectionMethod, 'NoTwoFactorRequired', NoTwoFactorRequired::class)) {
51+
if ($this->middlewareUtils->hasAnnotationOrAttribute($reflectionMethod, 'NoTwoFactorRequired', NoTwoFactorRequired::class)) {
5452
// Route handler explicitly marked to work without finished 2FA are
5553
// not blocked
5654
return;
@@ -137,26 +135,4 @@ public function afterException($controller, $methodName, Exception $exception) {
137135

138136
throw $exception;
139137
}
140-
141-
142-
/**
143-
* @template T
144-
*
145-
* @param ReflectionMethod $reflectionMethod
146-
* @param ?string $annotationName
147-
* @param class-string<T> $attributeClass
148-
* @return boolean
149-
*/
150-
protected function hasAnnotationOrAttribute(ReflectionMethod $reflectionMethod, ?string $annotationName, string $attributeClass): bool {
151-
if (!empty($reflectionMethod->getAttributes($attributeClass))) {
152-
return true;
153-
}
154-
155-
if ($annotationName && $this->reflector->hasAnnotation($annotationName)) {
156-
$this->logger->debug($reflectionMethod->getDeclaringClass()->getName() . '::' . $reflectionMethod->getName() . ' uses the @' . $annotationName . ' annotation and should use the #[' . $attributeClass . '] attribute instead');
157-
return true;
158-
}
159-
160-
return false;
161-
}
162138
}

lib/composer/composer/autoload_classmap.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,6 +1103,7 @@
11031103
'OC\\AppFramework\\Middleware\\CompressionMiddleware' => $baseDir . '/lib/private/AppFramework/Middleware/CompressionMiddleware.php',
11041104
'OC\\AppFramework\\Middleware\\FlowV2EphemeralSessionsMiddleware' => $baseDir . '/lib/private/AppFramework/Middleware/FlowV2EphemeralSessionsMiddleware.php',
11051105
'OC\\AppFramework\\Middleware\\MiddlewareDispatcher' => $baseDir . '/lib/private/AppFramework/Middleware/MiddlewareDispatcher.php',
1106+
'OC\\AppFramework\\Middleware\\MiddlewareUtils' => $baseDir . '/lib/private/AppFramework/Middleware/MiddlewareUtils.php',
11061107
'OC\\AppFramework\\Middleware\\NotModifiedMiddleware' => $baseDir . '/lib/private/AppFramework/Middleware/NotModifiedMiddleware.php',
11071108
'OC\\AppFramework\\Middleware\\OCSMiddleware' => $baseDir . '/lib/private/AppFramework/Middleware/OCSMiddleware.php',
11081109
'OC\\AppFramework\\Middleware\\PublicShare\\Exceptions\\NeedAuthenticationException' => $baseDir . '/lib/private/AppFramework/Middleware/PublicShare/Exceptions/NeedAuthenticationException.php',

lib/composer/composer/autoload_static.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1144,6 +1144,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
11441144
'OC\\AppFramework\\Middleware\\CompressionMiddleware' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/CompressionMiddleware.php',
11451145
'OC\\AppFramework\\Middleware\\FlowV2EphemeralSessionsMiddleware' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/FlowV2EphemeralSessionsMiddleware.php',
11461146
'OC\\AppFramework\\Middleware\\MiddlewareDispatcher' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/MiddlewareDispatcher.php',
1147+
'OC\\AppFramework\\Middleware\\MiddlewareUtils' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/MiddlewareUtils.php',
11471148
'OC\\AppFramework\\Middleware\\NotModifiedMiddleware' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/NotModifiedMiddleware.php',
11481149
'OC\\AppFramework\\Middleware\\OCSMiddleware' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/OCSMiddleware.php',
11491150
'OC\\AppFramework\\Middleware\\PublicShare\\Exceptions\\NeedAuthenticationException' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/PublicShare/Exceptions/NeedAuthenticationException.php',

lib/private/AppFramework/DependencyInjection/DIContainer.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use OC\AppFramework\Middleware\CompressionMiddleware;
1818
use OC\AppFramework\Middleware\FlowV2EphemeralSessionsMiddleware;
1919
use OC\AppFramework\Middleware\MiddlewareDispatcher;
20+
use OC\AppFramework\Middleware\MiddlewareUtils;
2021
use OC\AppFramework\Middleware\NotModifiedMiddleware;
2122
use OC\AppFramework\Middleware\OCSMiddleware;
2223
use OC\AppFramework\Middleware\PublicShare\PublicShareMiddleware;
@@ -31,6 +32,7 @@
3132
use OC\AppFramework\Middleware\Security\SecurityMiddleware;
3233
use OC\AppFramework\Middleware\SessionMiddleware;
3334
use OC\AppFramework\ScopedPsrLogger;
35+
use OC\AppFramework\Utility\ControllerMethodReflector;
3436
use OC\AppFramework\Utility\SimpleContainer;
3537
use OC\Core\Middleware\TwoFactorMiddleware;
3638
use OC\Diagnostics\EventLogger;
@@ -44,7 +46,6 @@
4446
use OCP\AppFramework\QueryException;
4547
use OCP\AppFramework\Services\IAppConfig;
4648
use OCP\AppFramework\Services\IInitialState;
47-
use OCP\AppFramework\Utility\IControllerMethodReflector;
4849
use OCP\Files\AppData\IAppDataFactory;
4950
use OCP\Files\Folder;
5051
use OCP\Files\IAppData;
@@ -155,7 +156,7 @@ public function __construct(string $appName, array $urlParams = [], ?ServerConta
155156
return new Dispatcher(
156157
$c->get(Http::class),
157158
$c->get(MiddlewareDispatcher::class),
158-
$c->get(IControllerMethodReflector::class),
159+
$c->get(ControllerMethodReflector::class),
159160
$c->get(IRequest::class),
160161
$c->get(IConfig::class),
161162
$c->get(IDBConnection::class),
@@ -193,7 +194,7 @@ public function __construct(string $appName, array $urlParams = [], ?ServerConta
193194

194195
$securityMiddleware = new SecurityMiddleware(
195196
$c->get(IRequest::class),
196-
$c->get(IControllerMethodReflector::class),
197+
$c->get(MiddlewareUtils::class),
197198
$c->get(INavigationManager::class),
198199
$c->get(IURLGenerator::class),
199200
$c->get(LoggerInterface::class),

lib/private/AppFramework/Http/Dispatcher.php

Lines changed: 24 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -26,64 +26,22 @@
2626
* Class to dispatch the request to the middleware dispatcher
2727
*/
2828
class Dispatcher {
29-
/** @var MiddlewareDispatcher */
30-
private $middlewareDispatcher;
31-
32-
/** @var Http */
33-
private $protocol;
34-
35-
/** @var ControllerMethodReflector */
36-
private $reflector;
37-
38-
/** @var IRequest */
39-
private $request;
40-
41-
/** @var IConfig */
42-
private $config;
43-
44-
/** @var ConnectionAdapter */
45-
private $connection;
46-
47-
/** @var LoggerInterface */
48-
private $logger;
49-
50-
/** @var IEventLogger */
51-
private $eventLogger;
52-
53-
private ContainerInterface $appContainer;
54-
5529
/**
5630
* @param Http $protocol the http protocol with contains all status headers
5731
* @param MiddlewareDispatcher $middlewareDispatcher the dispatcher which
5832
* runs the middleware
59-
* @param ControllerMethodReflector $reflector the reflector that is used to inject
60-
* the arguments for the controller
61-
* @param IRequest $request the incoming request
62-
* @param IConfig $config
63-
* @param ConnectionAdapter $connection
64-
* @param LoggerInterface $logger
65-
* @param IEventLogger $eventLogger
6633
*/
6734
public function __construct(
68-
Http $protocol,
69-
MiddlewareDispatcher $middlewareDispatcher,
70-
ControllerMethodReflector $reflector,
71-
IRequest $request,
72-
IConfig $config,
73-
ConnectionAdapter $connection,
74-
LoggerInterface $logger,
75-
IEventLogger $eventLogger,
76-
ContainerInterface $appContainer,
35+
private readonly Http $protocol,
36+
private readonly MiddlewareDispatcher $middlewareDispatcher,
37+
private readonly ControllerMethodReflector $reflector,
38+
private readonly IRequest $request,
39+
private readonly IConfig $config,
40+
private readonly ConnectionAdapter $connection,
41+
private readonly LoggerInterface $logger,
42+
private readonly IEventLogger $eventLogger,
43+
private readonly ContainerInterface $appContainer,
7744
) {
78-
$this->protocol = $protocol;
79-
$this->middlewareDispatcher = $middlewareDispatcher;
80-
$this->reflector = $reflector;
81-
$this->request = $request;
82-
$this->config = $config;
83-
$this->connection = $connection;
84-
$this->logger = $logger;
85-
$this->eventLogger = $eventLogger;
86-
$this->appContainer = $appContainer;
8745
}
8846

8947

@@ -92,16 +50,15 @@ public function __construct(
9250
* @param Controller $controller the controller which will be called
9351
* @param string $methodName the method name which will be called on
9452
* the controller
95-
* @return array $array[0] contains the http status header as a string,
96-
* $array[1] contains response headers as an array,
97-
* $array[2] contains response cookies as an array,
98-
* $array[3] contains the response output as a string,
99-
* $array[4] contains the response object
53+
* @return array{0: string, 1: array, 2: array, 3: string, 4: Response}
54+
* $array[0] contains the http status header as a string,
55+
* $array[1] contains response headers as an array,
56+
* $array[2] contains response cookies as an array,
57+
* $array[3] contains the response output as a string,
58+
* $array[4] contains the response object
10059
* @throws \Exception
10160
*/
10261
public function dispatch(Controller $controller, string $methodName): array {
103-
$out = [null, [], null];
104-
10562
try {
10663
// prefill reflector with everything that's needed for the
10764
// middlewares
@@ -156,15 +113,15 @@ public function dispatch(Controller $controller, string $methodName): array {
156113
$controller, $methodName, $response);
157114

158115
// depending on the cache object the headers need to be changed
159-
$out[0] = $this->protocol->getStatusHeader($response->getStatus());
160-
$out[1] = array_merge($response->getHeaders());
161-
$out[2] = $response->getCookies();
162-
$out[3] = $this->middlewareDispatcher->beforeOutput(
163-
$controller, $methodName, $response->render()
164-
);
165-
$out[4] = $response;
166-
167-
return $out;
116+
return [
117+
$this->protocol->getStatusHeader($response->getStatus()),
118+
array_merge($response->getHeaders()),
119+
$response->getCookies(),
120+
$this->middlewareDispatcher->beforeOutput(
121+
$controller, $methodName, $response->render()
122+
),
123+
$response,
124+
];
168125
}
169126

170127

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH
7+
* SPDX-FileContributor: Carl Schwan
8+
* SPDX-License-Identifier: AGPL-3.0-or-later
9+
*/
10+
11+
namespace OC\AppFramework\Middleware;
12+
13+
use OC\AppFramework\Utility\ControllerMethodReflector;
14+
use OCP\AppFramework\Http\Attribute\AuthorizedAdminSetting;
15+
use Psr\Log\LoggerInterface;
16+
use ReflectionMethod;
17+
18+
/**
19+
* Temporary helper to abstract IControllerMethodReflector and ReflectionMethod
20+
*/
21+
class MiddlewareUtils {
22+
public function __construct(
23+
private readonly ControllerMethodReflector $reflector,
24+
private readonly LoggerInterface $logger,
25+
) {
26+
}
27+
28+
/**
29+
* @template T
30+
*
31+
* @param ReflectionMethod $reflectionMethod
32+
* @param ?string $annotationName
33+
* @param class-string<T> $attributeClass
34+
* @return boolean
35+
*/
36+
public function hasAnnotationOrAttribute(ReflectionMethod $reflectionMethod, ?string $annotationName, string $attributeClass): bool {
37+
if (!empty($reflectionMethod->getAttributes($attributeClass))) {
38+
return true;
39+
}
40+
41+
if ($annotationName && $this->reflector->hasAnnotation($annotationName)) {
42+
$this->logger->debug($reflectionMethod->getDeclaringClass()->getName() . '::' . $reflectionMethod->getName() . ' uses the @' . $annotationName . ' annotation and should use the #[' . $attributeClass . '] attribute instead');
43+
return true;
44+
}
45+
46+
return false;
47+
}
48+
49+
/**
50+
* @param ReflectionMethod $reflectionMethod
51+
* @return string[]
52+
*/
53+
public function getAuthorizedAdminSettingClasses(ReflectionMethod $reflectionMethod): array {
54+
$classes = [];
55+
if ($this->reflector->hasAnnotation('AuthorizedAdminSetting')) {
56+
$classes = explode(';', $this->reflector->getAnnotationParameter('AuthorizedAdminSetting', 'settings'));
57+
}
58+
59+
$attributes = $reflectionMethod->getAttributes(AuthorizedAdminSetting::class);
60+
if (!empty($attributes)) {
61+
foreach ($attributes as $attribute) {
62+
/** @var AuthorizedAdminSetting $setting */
63+
$setting = $attribute->newInstance();
64+
$classes[] = $setting->getSettings();
65+
}
66+
}
67+
68+
return $classes;
69+
}
70+
}

0 commit comments

Comments
 (0)