Skip to content

Commit 0fdd21f

Browse files
committed
feat(AppFramework): Add missing NoSameSiteCookieRequired attribute
Allow to replace the old annotation. Signed-off-by: Carl Schwan <[email protected]>
1 parent ae43824 commit 0fdd21f

File tree

11 files changed

+97
-49
lines changed

11 files changed

+97
-49
lines changed

apps/files_sharing/lib/Controller/PublicPreviewController.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
use OCP\AppFramework\Http;
1010
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
11+
use OCP\AppFramework\Http\Attribute\NoSameSiteCookieRequired;
1112
use OCP\AppFramework\Http\Attribute\OpenAPI;
1213
use OCP\AppFramework\Http\Attribute\PublicPage;
1314
use OCP\AppFramework\Http\DataResponse;
@@ -144,8 +145,6 @@ public function getPreview(
144145
}
145146

146147
/**
147-
* @NoSameSiteCookieRequired
148-
*
149148
* Get a direct link preview for a shared file
150149
*
151150
* @param string $token Token of the share
@@ -159,6 +158,7 @@ public function getPreview(
159158
#[PublicPage]
160159
#[NoCSRFRequired]
161160
#[OpenAPI(scope: OpenAPI::SCOPE_DEFAULT)]
161+
#[NoSameSiteCookieRequired]
162162
public function directLink(string $token) {
163163
// No token no image
164164
if ($token === '') {

apps/files_sharing/lib/Controller/ShareController.php

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@
1515
use OCP\Accounts\IAccountManager;
1616
use OCP\AppFramework\AuthPublicShareController;
1717
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
18+
use OCP\AppFramework\Http\Attribute\NoSameSiteCookieRequired;
1819
use OCP\AppFramework\Http\Attribute\OpenAPI;
1920
use OCP\AppFramework\Http\Attribute\PublicPage;
2021
use OCP\AppFramework\Http\DataResponse;
2122
use OCP\AppFramework\Http\NotFoundResponse;
2223
use OCP\AppFramework\Http\RedirectResponse;
23-
use OCP\AppFramework\Http\Response;
2424
use OCP\AppFramework\Http\TemplateResponse;
2525
use OCP\Constants;
2626
use OCP\Defaults;
@@ -338,18 +338,13 @@ public function showShare($path = ''): TemplateResponse {
338338
}
339339

340340
/**
341-
* @NoSameSiteCookieRequired
342-
*
343-
* @param string $token
344-
* @param string|null $files
345-
* @param string $path
346-
* @return void|Response
347341
* @throws NotFoundException
348342
* @deprecated 31.0.0 Users are encouraged to use the DAV endpoint
349343
*/
350344
#[PublicPage]
351345
#[NoCSRFRequired]
352-
public function downloadShare($token, $files = null, $path = '') {
346+
#[NoSameSiteCookieRequired]
347+
public function downloadShare(string $token, ?string $files = null, string $path = ''): NotFoundResponse|RedirectResponse|DataResponse {
353348
\OC_User::setIncognitoMode(true);
354349

355350
$share = $this->shareManager->getShareByToken($token);

apps/theming/lib/Controller/ThemingController.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use OCP\AppFramework\Http\Attribute\AuthorizedAdminSetting;
1818
use OCP\AppFramework\Http\Attribute\BruteForceProtection;
1919
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
20+
use OCP\AppFramework\Http\Attribute\NoSameSiteCookieRequired;
2021
use OCP\AppFramework\Http\Attribute\NoTwoFactorRequired;
2122
use OCP\AppFramework\Http\Attribute\OpenAPI;
2223
use OCP\AppFramework\Http\Attribute\PublicPage;
@@ -360,8 +361,6 @@ public function getImage(string $key, bool $useSvg = true) {
360361
}
361362

362363
/**
363-
* @NoSameSiteCookieRequired
364-
*
365364
* Get the CSS stylesheet for a theme
366365
*
367366
* @param string $themeId ID of the theme
@@ -376,6 +375,7 @@ public function getImage(string $key, bool $useSvg = true) {
376375
#[NoCSRFRequired]
377376
#[NoTwoFactorRequired]
378377
#[OpenAPI(scope: OpenAPI::SCOPE_DEFAULT)]
378+
#[NoSameSiteCookieRequired]
379379
public function getThemeStylesheet(string $themeId, bool $plain = false, bool $withCustomCss = false) {
380380
$themes = $this->themesService->getThemes();
381381
if (!in_array($themeId, array_keys($themes))) {

core/Controller/AvatarController.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
1414
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
1515
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
16+
use OCP\AppFramework\Http\Attribute\NoSameSiteCookieRequired;
1617
use OCP\AppFramework\Http\Attribute\OpenAPI;
1718
use OCP\AppFramework\Http\Attribute\PublicPage;
1819
use OCP\AppFramework\Http\FileDisplayResponse;
@@ -50,8 +51,6 @@ public function __construct(
5051
}
5152

5253
/**
53-
* @NoSameSiteCookieRequired
54-
*
5554
* Get the dark avatar
5655
*
5756
* @param string $userId ID of the user
@@ -67,6 +66,7 @@ public function __construct(
6766
#[PublicPage]
6867
#[FrontpageRoute(verb: 'GET', url: '/avatar/{userId}/{size}/dark')]
6968
#[OpenAPI(scope: OpenAPI::SCOPE_DEFAULT)]
69+
#[NoSameSiteCookieRequired]
7070
public function getAvatarDark(string $userId, int $size, bool $guestFallback = false) {
7171
if ($size <= 64) {
7272
if ($size !== 64) {
@@ -102,8 +102,6 @@ public function getAvatarDark(string $userId, int $size, bool $guestFallback = f
102102

103103

104104
/**
105-
* @NoSameSiteCookieRequired
106-
*
107105
* Get the avatar
108106
*
109107
* @param string $userId ID of the user
@@ -119,6 +117,7 @@ public function getAvatarDark(string $userId, int $size, bool $guestFallback = f
119117
#[PublicPage]
120118
#[FrontpageRoute(verb: 'GET', url: '/avatar/{userId}/{size}')]
121119
#[OpenAPI(scope: OpenAPI::SCOPE_DEFAULT)]
120+
#[NoSameSiteCookieRequired]
122121
public function getAvatar(string $userId, int $size, bool $guestFallback = false) {
123122
if ($size <= 64) {
124123
if ($size !== 64) {

core/Controller/ClientFlowLoginController.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
1818
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
1919
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
20+
use OCP\AppFramework\Http\Attribute\NoSameSiteCookieRequired;
2021
use OCP\AppFramework\Http\Attribute\OpenAPI;
2122
use OCP\AppFramework\Http\Attribute\PasswordConfirmationRequired;
2223
use OCP\AppFramework\Http\Attribute\PublicPage;
@@ -157,13 +158,11 @@ public function showAuthPickerPage(string $clientIdentifier = '', string $user =
157158
return $response;
158159
}
159160

160-
/**
161-
* @NoSameSiteCookieRequired
162-
*/
163161
#[NoAdminRequired]
164162
#[NoCSRFRequired]
165163
#[UseSession]
166164
#[FrontpageRoute(verb: 'GET', url: '/login/flow/grant')]
165+
#[NoSameSiteCookieRequired]
167166
public function grantPage(
168167
string $stateToken = '',
169168
string $clientIdentifier = '',

core/Controller/ClientFlowLoginV2Controller.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
1919
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
2020
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
21+
use OCP\AppFramework\Http\Attribute\NoSameSiteCookieRequired;
2122
use OCP\AppFramework\Http\Attribute\OpenAPI;
2223
use OCP\AppFramework\Http\Attribute\PasswordConfirmationRequired;
2324
use OCP\AppFramework\Http\Attribute\PublicPage;
@@ -137,14 +138,12 @@ public function showAuthPickerPage(string $user = '', int $direct = 0): Standalo
137138
);
138139
}
139140

140-
/**
141-
* @NoSameSiteCookieRequired
142-
*/
143141
#[NoAdminRequired]
144142
#[NoCSRFRequired]
145143
#[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)]
146144
#[UseSession]
147145
#[FrontpageRoute(verb: 'GET', url: '/login/v2/grant')]
146+
#[NoSameSiteCookieRequired]
148147
public function grantPage(?string $stateToken, int $direct = 0): StandaloneTemplateResponse {
149148
if ($stateToken === null) {
150149
return $this->stateTokenMissingResponse();

core/Controller/CssController.php

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@
1313
use OCP\AppFramework\Http;
1414
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
1515
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
16+
use OCP\AppFramework\Http\Attribute\NoSameSiteCookieRequired;
1617
use OCP\AppFramework\Http\Attribute\OpenAPI;
1718
use OCP\AppFramework\Http\Attribute\PublicPage;
1819
use OCP\AppFramework\Http\FileDisplayResponse;
1920
use OCP\AppFramework\Http\NotFoundResponse;
20-
use OCP\AppFramework\Http\Response;
2121
use OCP\AppFramework\Utility\ITimeFactory;
2222
use OCP\Files\IAppData;
2323
use OCP\Files\NotFoundException;
@@ -41,21 +41,19 @@ public function __construct(
4141
}
4242

4343
/**
44-
* @NoSameSiteCookieRequired
45-
*
4644
* @param string $fileName css filename with extension
4745
* @param string $appName css folder name
48-
* @return FileDisplayResponse|NotFoundResponse
4946
*/
5047
#[PublicPage]
5148
#[NoCSRFRequired]
5249
#[FrontpageRoute(verb: 'GET', url: '/css/{appName}/{fileName}')]
53-
public function getCss(string $fileName, string $appName): Response {
50+
#[NoSameSiteCookieRequired]
51+
public function getCss(string $fileName, string $appName): FileDisplayResponse|NotFoundResponse {
5452
try {
5553
$folder = $this->appData->getFolder($appName);
5654
$gzip = false;
5755
$file = $this->getFile($folder, $fileName, $gzip);
58-
} catch (NotFoundException $e) {
56+
} catch (NotFoundException) {
5957
return new NotFoundResponse();
6058
}
6159

core/Controller/JsController.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@
1313
use OCP\AppFramework\Http;
1414
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
1515
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
16+
use OCP\AppFramework\Http\Attribute\NoSameSiteCookieRequired;
1617
use OCP\AppFramework\Http\Attribute\NoTwoFactorRequired;
1718
use OCP\AppFramework\Http\Attribute\OpenAPI;
1819
use OCP\AppFramework\Http\Attribute\PublicPage;
1920
use OCP\AppFramework\Http\FileDisplayResponse;
2021
use OCP\AppFramework\Http\NotFoundResponse;
21-
use OCP\AppFramework\Http\Response;
2222
use OCP\AppFramework\Utility\ITimeFactory;
2323
use OCP\Files\IAppData;
2424
use OCP\Files\NotFoundException;
@@ -42,15 +42,14 @@ public function __construct(
4242
}
4343

4444
/**
45-
* @NoSameSiteCookieRequired
46-
*
4745
* @param string $fileName js filename with extension
4846
* @param string $appName js folder name
4947
*/
5048
#[PublicPage]
5149
#[NoCSRFRequired]
5250
#[FrontpageRoute(verb: 'GET', url: '/js/{appName}/{fileName}')]
5351
#[NoTwoFactorRequired]
52+
#[NoSameSiteCookieRequired]
5453
public function getJs(string $fileName, string $appName): FileDisplayResponse|NotFoundResponse {
5554
try {
5655
$folder = $this->appData->getFolder($appName);

lib/private/AppFramework/Middleware/Security/SameSiteCookieMiddleware.php

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<?php
22

3+
declare(strict_types=1);
4+
35
/**
46
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
57
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -10,13 +12,17 @@
1012
use OC\AppFramework\Middleware\Security\Exceptions\LaxSameSiteCookieFailedException;
1113
use OC\AppFramework\Utility\ControllerMethodReflector;
1214
use OCP\AppFramework\Http;
15+
use OCP\AppFramework\Http\Attribute\NoSameSiteCookieRequired;
1316
use OCP\AppFramework\Http\Response;
1417
use OCP\AppFramework\Middleware;
18+
use Psr\Log\LoggerInterface;
19+
use ReflectionMethod;
1520

1621
class SameSiteCookieMiddleware extends Middleware {
1722
public function __construct(
18-
private Request $request,
19-
private ControllerMethodReflector $reflector,
23+
private readonly Request $request,
24+
private readonly ControllerMethodReflector $reflector,
25+
private readonly LoggerInterface $logger,
2026
) {
2127
}
2228

@@ -29,7 +35,8 @@ public function beforeController($controller, $methodName) {
2935
return;
3036
}
3137

32-
$noSSC = $this->reflector->hasAnnotation('NoSameSiteCookieRequired');
38+
$reflectionMethod = new ReflectionMethod($controller, $methodName);
39+
$noSSC = $this->hasAnnotationOrAttribute($reflectionMethod, 'NoSameSiteCookieRequired', NoSameSiteCookieRequired::class);
3340
if ($noSSC) {
3441
return;
3542
}
@@ -80,4 +87,25 @@ protected function setSameSiteCookie(): void {
8087
);
8188
}
8289
}
90+
91+
/**
92+
* @template T
93+
*
94+
* @param ReflectionMethod $reflectionMethod
95+
* @param ?string $annotationName
96+
* @param class-string<T> $attributeClass
97+
* @return boolean
98+
*/
99+
protected function hasAnnotationOrAttribute(ReflectionMethod $reflectionMethod, ?string $annotationName, string $attributeClass): bool {
100+
if (!empty($reflectionMethod->getAttributes($attributeClass))) {
101+
return true;
102+
}
103+
104+
if ($annotationName && $this->reflector->hasAnnotation($annotationName)) {
105+
$this->logger->debug($reflectionMethod->getDeclaringClass()->getName() . '::' . $reflectionMethod->getName() . ' uses the @' . $annotationName . ' annotation and should use the #[' . $attributeClass . '] attribute instead');
106+
return true;
107+
}
108+
109+
return false;
110+
}
83111
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
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 OCP\AppFramework\Http\Attribute;
12+
13+
use Attribute;
14+
15+
#[Attribute]
16+
class NoSameSiteCookieRequired {
17+
}

0 commit comments

Comments
 (0)