From 40ecb5ab4a45c1493b27c7f92dc00490e1dc3fa4 Mon Sep 17 00:00:00 2001 From: Peter Ringelmann Date: Thu, 22 Oct 2026 13:06:26 +0200 Subject: [PATCH 1/3] feat: let scoped app passwords read notifications and register for push Signed-off-by: Peter Ringelmann --- lib/Controller/EndpointController.php | 3 ++ lib/Controller/PushController.php | 3 ++ lib/TokenScopes.php | 17 ++++++++ tests/Unit/Controller/TokenScopeTest.php | 52 ++++++++++++++++++++++++ 4 files changed, 75 insertions(+) create mode 100644 lib/TokenScopes.php create mode 100644 tests/Unit/Controller/TokenScopeTest.php diff --git a/lib/Controller/EndpointController.php b/lib/Controller/EndpointController.php index d21fc08b0..f18c05bd0 100644 --- a/lib/Controller/EndpointController.php +++ b/lib/Controller/EndpointController.php @@ -10,11 +10,13 @@ namespace OCA\Notifications\Controller; +use NCU\AppFramework\Http\Attribute\TokenScope; use OCA\Notifications\Exceptions\NotificationNotFoundException; use OCA\Notifications\Handler; use OCA\Notifications\Push; use OCA\Notifications\ResponseDefinitions; use OCA\Notifications\Service\ClientService; +use OCA\Notifications\TokenScopes; use OCP\AppFramework\Http; use OCP\AppFramework\Http\Attribute\ApiRoute; use OCP\AppFramework\Http\Attribute\NoAdminRequired; @@ -38,6 +40,7 @@ * @psalm-import-type NotificationsNotification from ResponseDefinitions * @psalm-import-type NotificationsNotificationAction from ResponseDefinitions */ +#[TokenScope(scope: TokenScopes::NOTIFICATIONS_READ)] class EndpointController extends OCSController { public function __construct( string $appName, diff --git a/lib/Controller/PushController.php b/lib/Controller/PushController.php index 3fd9d829a..964f792b4 100644 --- a/lib/Controller/PushController.php +++ b/lib/Controller/PushController.php @@ -9,9 +9,11 @@ namespace OCA\Notifications\Controller; +use NCU\AppFramework\Http\Attribute\TokenScope; use OC\Authentication\Token\IProvider; use OC\Security\IdentityProof\Manager; use OCA\Notifications\ResponseDefinitions; +use OCA\Notifications\TokenScopes; use OCP\AppFramework\Http; use OCP\AppFramework\Http\Attribute\ApiRoute; use OCP\AppFramework\Http\Attribute\NoAdminRequired; @@ -32,6 +34,7 @@ * @psalm-import-type NotificationsPushDevice from ResponseDefinitions */ #[OpenAPI(scope: 'push')] +#[TokenScope(scope: TokenScopes::NOTIFICATIONS_READ)] class PushController extends OCSController { public function __construct( string $appName, diff --git a/lib/TokenScopes.php b/lib/TokenScopes.php new file mode 100644 index 000000000..aaf6aead1 --- /dev/null +++ b/lib/TokenScopes.php @@ -0,0 +1,17 @@ + [EndpointController::class], + 'push registration' => [PushController::class], + ]; + } + + #[DataProvider('openControllers')] + public function testOpenControllers(string $class): void { + $attribute = (new \ReflectionClass($class))->getAttributes(TokenScope::class)[0]; + $this->assertSame(TokenScopes::NOTIFICATIONS_READ, $attribute->getArguments()['scope']); + } + + public static function closedControllers(): array { + return [ + 'admin' => [APIController::class], + 'settings' => [SettingsController::class], + 'web push' => [WebPushController::class], + ]; + } + + #[DataProvider('closedControllers')] + public function testClosedControllers(string $class): void { + $class = new \ReflectionClass($class); + $this->assertSame([], $class->getAttributes(TokenScope::class)); + foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) { + $this->assertSame([], $method->getAttributes(TokenScope::class), $method->getName()); + } + } +} From 93621eed9b73e83840d1bd43b1bc5f8bb3f8de3c Mon Sep 17 00:00:00 2001 From: Peter Ringelmann Date: Thu, 22 Oct 2026 14:30:07 +0200 Subject: [PATCH 2/3] feat: let scoped app passwords use web push and personal notification settings Signed-off-by: Peter Ringelmann --- lib/Controller/SettingsController.php | 3 +++ lib/Controller/WebPushController.php | 3 +++ tests/Unit/Controller/TokenScopeTest.php | 10 ++++++++-- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/Controller/SettingsController.php b/lib/Controller/SettingsController.php index d3523612a..11790ef55 100644 --- a/lib/Controller/SettingsController.php +++ b/lib/Controller/SettingsController.php @@ -9,9 +9,11 @@ namespace OCA\Notifications\Controller; +use NCU\AppFramework\Http\Attribute\TokenScope; use OCA\Notifications\AppInfo\Application; use OCA\Notifications\Model\Settings; use OCA\Notifications\Model\SettingsMapper; +use OCA\Notifications\TokenScopes; use OCP\AppFramework\Http; use OCP\AppFramework\Http\Attribute\ApiRoute; use OCP\AppFramework\Http\Attribute\NoAdminRequired; @@ -48,6 +50,7 @@ public function __construct( #[NoAdminRequired] #[OpenAPI] #[ApiRoute(verb: 'POST', url: '/api/{apiVersion}/settings', requirements: ['apiVersion' => '(v2)'])] + #[TokenScope(scope: TokenScopes::NOTIFICATIONS_READ)] public function personal(int $batchSetting, string $soundNotification, string $soundTalk): DataResponse { $this->settingsMapper->setBatchSettingForUser($this->settingsMapper->getSettingsByUser($this->userId), $batchSetting); diff --git a/lib/Controller/WebPushController.php b/lib/Controller/WebPushController.php index 844bbf4b5..32217b161 100644 --- a/lib/Controller/WebPushController.php +++ b/lib/Controller/WebPushController.php @@ -9,8 +9,10 @@ namespace OCA\Notifications\Controller; +use NCU\AppFramework\Http\Attribute\TokenScope; use OC\Authentication\Token\IProvider; use OC\Security\IdentityProof\Manager; +use OCA\Notifications\TokenScopes; use OCA\Notifications\WebPush\ActivationSubStatus; use OCA\Notifications\WebPush\NewSubStatus; use OCA\Notifications\WebPushClient; @@ -35,6 +37,7 @@ use Symfony\Component\Uid\Uuid; #[OpenAPI(scope: 'webpush')] +#[TokenScope(scope: TokenScopes::NOTIFICATIONS_READ)] class WebPushController extends OCSController { public function __construct( string $appName, diff --git a/tests/Unit/Controller/TokenScopeTest.php b/tests/Unit/Controller/TokenScopeTest.php index dcaf41583..93ce13f52 100644 --- a/tests/Unit/Controller/TokenScopeTest.php +++ b/tests/Unit/Controller/TokenScopeTest.php @@ -24,6 +24,7 @@ public static function openControllers(): array { return [ 'notifications' => [EndpointController::class], 'push registration' => [PushController::class], + 'web push registration' => [WebPushController::class], ]; } @@ -33,11 +34,16 @@ public function testOpenControllers(string $class): void { $this->assertSame(TokenScopes::NOTIFICATIONS_READ, $attribute->getArguments()['scope']); } + public function testPersonalSettingsOnly(): void { + $personal = (new \ReflectionMethod(SettingsController::class, 'personal'))->getAttributes(TokenScope::class)[0]; + $this->assertSame(TokenScopes::NOTIFICATIONS_READ, $personal->getArguments()['scope']); + $this->assertSame([], (new \ReflectionMethod(SettingsController::class, 'admin'))->getAttributes(TokenScope::class)); + $this->assertSame([], (new \ReflectionClass(SettingsController::class))->getAttributes(TokenScope::class)); + } + public static function closedControllers(): array { return [ 'admin' => [APIController::class], - 'settings' => [SettingsController::class], - 'web push' => [WebPushController::class], ]; } From 3fb3e481a72d89b63ad176d823187ee03386db99 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Thu, 22 Oct 2026 17:22:17 +0200 Subject: [PATCH 3/3] TEMP: Test server branch for performance Signed-off-by: Joas Schilling --- .github/workflows/performance.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/performance.yml b/.github/workflows/performance.yml index 140d93c92..7a8211569 100644 --- a/.github/workflows/performance.yml +++ b/.github/workflows/performance.yml @@ -90,7 +90,7 @@ jobs: persist-credentials: false submodules: true repository: nextcloud/server - ref: ${{ matrix.server-versions }} + ref: feat/noid/app-password-route-gate - name: Checkout app uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1