From 88297d5543accb0251e601e67a224f919c51b036 Mon Sep 17 00:00:00 2001 From: Tatjana Kaschperko Lindt Date: Mon, 16 Mar 2026 10:57:16 +0100 Subject: [PATCH 1/5] feat(files_external): convert to delegated settings Signed-off-by: Tatjana Kaschperko Lindt --- apps/files_external/lib/Settings/Admin.php | 15 ++++++++++++--- apps/files_external/tests/Settings/AdminTest.php | 9 ++++++++- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/apps/files_external/lib/Settings/Admin.php b/apps/files_external/lib/Settings/Admin.php index 9af0f3c61c16b..ce7631a7acc0a 100644 --- a/apps/files_external/lib/Settings/Admin.php +++ b/apps/files_external/lib/Settings/Admin.php @@ -12,15 +12,16 @@ use OCA\Files_External\Service\GlobalStoragesService; use OCP\AppFramework\Http\TemplateResponse; use OCP\Encryption\IManager; -use OCP\Settings\ISettings; - -class Admin implements ISettings { +use OCP\IL10N; +use OCP\Settings\IDelegatedSettings; +class Admin implements IDelegatedSettings { public function __construct( private IManager $encryptionManager, private GlobalStoragesService $globalStoragesService, private BackendService $backendService, private GlobalAuth $globalAuth, + private IL10N $l10n, ) { } @@ -60,4 +61,12 @@ public function getSection() { public function getPriority() { return 40; } + + public function getName(): string { + return $this->l10n->t('External storage'); + } + + public function getAuthorizedAppConfig(): array { + return []; + } } diff --git a/apps/files_external/tests/Settings/AdminTest.php b/apps/files_external/tests/Settings/AdminTest.php index fd4a1949760c0..92eb9b89f6b6b 100644 --- a/apps/files_external/tests/Settings/AdminTest.php +++ b/apps/files_external/tests/Settings/AdminTest.php @@ -14,6 +14,7 @@ use OCA\Files_External\Settings\Admin; use OCP\AppFramework\Http\TemplateResponse; use OCP\Encryption\IManager; +use OCP\IL10N; use PHPUnit\Framework\MockObject\MockObject; use Test\TestCase; @@ -22,6 +23,7 @@ class AdminTest extends TestCase { private GlobalStoragesService&MockObject $globalStoragesService; private BackendService&MockObject $backendService; private GlobalAuth&MockObject $globalAuth; + private IL10N&MockObject $l10n; private Admin $admin; protected function setUp(): void { @@ -30,12 +32,17 @@ protected function setUp(): void { $this->globalStoragesService = $this->createMock(GlobalStoragesService::class); $this->backendService = $this->createMock(BackendService::class); $this->globalAuth = $this->createMock(GlobalAuth::class); + $this->l10n = $this->createMock(IL10N::class); + $this->l10n->method('t')->willReturnCallback(function ($text) { + return $text; + }); $this->admin = new Admin( $this->encryptionManager, $this->globalStoragesService, $this->backendService, - $this->globalAuth + $this->globalAuth, + $this->l10n ); } From 295ad2bd6fa7f8270407857fa740a52288ba2b9b Mon Sep 17 00:00:00 2001 From: Tatjana Kaschperko Lindt Date: Mon, 30 Mar 2026 12:25:54 +0200 Subject: [PATCH 2/5] feat(files_external): add #[AuthorizedAdminSetting] to GlobalStoragesController Signed-off-by: Tatjana Kaschperko Lindt --- .../Controller/GlobalStoragesController.php | 20 +++++++++++++++++++ .../tests/Settings/AdminTest.php | 18 +++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/apps/files_external/lib/Controller/GlobalStoragesController.php b/apps/files_external/lib/Controller/GlobalStoragesController.php index e7274c9cfb64c..ebfc4e1d40933 100644 --- a/apps/files_external/lib/Controller/GlobalStoragesController.php +++ b/apps/files_external/lib/Controller/GlobalStoragesController.php @@ -9,7 +9,9 @@ use OCA\Files_External\NotFoundException; use OCA\Files_External\Service\GlobalStoragesService; +use OCA\Files_External\Settings\Admin; use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\AuthorizedAdminSetting; use OCP\AppFramework\Http\Attribute\PasswordConfirmationRequired; use OCP\AppFramework\Http\DataResponse; use OCP\IConfig; @@ -71,6 +73,7 @@ public function __construct( * * @return DataResponse */ + #[AuthorizedAdminSetting(settings: Admin::class)] #[PasswordConfirmationRequired(strict: true)] public function create( $mountPoint, @@ -136,6 +139,7 @@ public function create( * * @return DataResponse */ + #[AuthorizedAdminSetting(settings: Admin::class)] #[PasswordConfirmationRequired(strict: true)] public function update( $id, @@ -186,4 +190,20 @@ public function update( Http::STATUS_OK ); } + + #[AuthorizedAdminSetting(settings: Admin::class)] + public function index() { + return parent::index(); + } + + #[AuthorizedAdminSetting(settings: Admin::class)] + public function show(int $id, $testOnly = true) { + return parent::show($id, $testOnly); + } + + #[AuthorizedAdminSetting(settings: Admin::class)] + #[PasswordConfirmationRequired(strict: true)] + public function destroy(int $id) { + return parent::destroy($id); + } } diff --git a/apps/files_external/tests/Settings/AdminTest.php b/apps/files_external/tests/Settings/AdminTest.php index 92eb9b89f6b6b..7abbb33d23d4a 100644 --- a/apps/files_external/tests/Settings/AdminTest.php +++ b/apps/files_external/tests/Settings/AdminTest.php @@ -98,4 +98,22 @@ public function testGetSection(): void { public function testGetPriority(): void { $this->assertSame(40, $this->admin->getPriority()); } + + public function testGetName(): void { + $this->l10n->expects($this->once()) + ->method('t') + ->with('External storage') + ->willReturn('External storage'); + + $this->assertSame('External storage', $this->admin->getName()); + } + + public function testGetAuthorizedAppConfig(): void { + $this->assertSame([], $this->admin->getAuthorizedAppConfig()); + } + + public function testImplementsIDelegatedSettings(): void { + $this->assertInstanceOf(\OCP\Settings\IDelegatedSettings::class, $this->admin); + $this->assertInstanceOf(\OCP\Settings\ISettings::class, $this->admin); + } } From 23f0d0440ab29c9a42add9bae2ed6fdeea66d873 Mon Sep 17 00:00:00 2001 From: Tatjana Kaschperko Lindt Date: Mon, 30 Mar 2026 12:54:06 +0200 Subject: [PATCH 3/5] feat(files_external): allow delegated admins to save global credentials Signed-off-by: Tatjana Kaschperko Lindt --- .../lib/Controller/AjaxController.php | 6 +- .../tests/Controller/AjaxControllerTest.php | 72 +++++++++++++++++++ 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/apps/files_external/lib/Controller/AjaxController.php b/apps/files_external/lib/Controller/AjaxController.php index 5cee642253010..154c30b13653f 100644 --- a/apps/files_external/lib/Controller/AjaxController.php +++ b/apps/files_external/lib/Controller/AjaxController.php @@ -7,8 +7,10 @@ */ namespace OCA\Files_External\Controller; +use OC\Settings\AuthorizedGroupMapper; use OCA\Files_External\Lib\Auth\Password\GlobalAuth; use OCA\Files_External\Lib\Auth\PublicKey\RSA; +use OCA\Files_External\Settings\Admin; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; use OCP\AppFramework\Http\Attribute\NoAdminRequired; @@ -36,6 +38,7 @@ public function __construct( private IUserSession $userSession, private IGroupManager $groupManager, private IL10N $l10n, + private AuthorizedGroupMapper $authorizedGroupMapper, ) { parent::__construct($appName, $request); } @@ -87,9 +90,10 @@ public function saveGlobalCredentials($uid, $user, $password): JSONResponse { } // Non-admins can only edit their own credentials - // Admin can edit global credentials + // Admin or delegated admin can edit global credentials $allowedToEdit = $uid === '' ? $this->groupManager->isAdmin($currentUser->getUID()) + || in_array(Admin::class, $this->authorizedGroupMapper->findAllClassesForUser($currentUser), true) : $currentUser->getUID() === $uid; if ($allowedToEdit) { diff --git a/apps/files_external/tests/Controller/AjaxControllerTest.php b/apps/files_external/tests/Controller/AjaxControllerTest.php index b1ea7a2b1b1b6..6c3a48740c884 100644 --- a/apps/files_external/tests/Controller/AjaxControllerTest.php +++ b/apps/files_external/tests/Controller/AjaxControllerTest.php @@ -7,9 +7,11 @@ */ namespace OCA\Files_External\Tests\Controller; +use OC\Settings\AuthorizedGroupMapper; use OCA\Files_External\Controller\AjaxController; use OCA\Files_External\Lib\Auth\Password\GlobalAuth; use OCA\Files_External\Lib\Auth\PublicKey\RSA; +use OCA\Files_External\Settings\Admin; use OCP\AppFramework\Http\JSONResponse; use OCP\IGroupManager; use OCP\IL10N; @@ -26,6 +28,7 @@ class AjaxControllerTest extends TestCase { private IUserSession&MockObject $userSession; private IGroupManager&MockObject $groupManager; private IL10N&MockObject $l10n; + private AuthorizedGroupMapper&MockObject $authorizedGroupMapper; private AjaxController $ajaxController; protected function setUp(): void { @@ -35,6 +38,7 @@ protected function setUp(): void { $this->userSession = $this->createMock(IUserSession::class); $this->groupManager = $this->createMock(IGroupManager::class); $this->l10n = $this->createMock(IL10N::class); + $this->authorizedGroupMapper = $this->createMock(AuthorizedGroupMapper::class); $this->ajaxController = new AjaxController( 'files_external', @@ -44,6 +48,7 @@ protected function setUp(): void { $this->userSession, $this->groupManager, $this->l10n, + $this->authorizedGroupMapper, ); $this->l10n->expects($this->any()) @@ -149,4 +154,71 @@ public function testSaveGlobalCredentialsAsNormalUserForAnotherUser(): void { $this->assertSame($response->getStatus(), 403); $this->assertSame('Permission denied', $response->getData()['message']); } + + public function testSaveGlobalCredentialsAsAdminForGlobal(): void { + $user = $this->createMock(IUser::class); + $user->method('getUID')->willReturn('MyAdminUid'); + $this->userSession->method('getUser')->willReturn($user); + $this->groupManager + ->expects($this->once()) + ->method('isAdmin') + ->with('MyAdminUid') + ->willReturn(true); + $this->authorizedGroupMapper + ->expects($this->never()) + ->method('findAllClassesForUser'); + $this->globalAuth + ->expects($this->once()) + ->method('saveAuth') + ->with('', 'test', 'password'); + + $response = $this->ajaxController->saveGlobalCredentials('', 'test', 'password'); + $this->assertSame(200, $response->getStatus()); + } + + public function testSaveGlobalCredentialsAsDelegatedAdminForGlobal(): void { + $user = $this->createMock(IUser::class); + $user->method('getUID')->willReturn('DelegatedUid'); + $this->userSession->method('getUser')->willReturn($user); + $this->groupManager + ->expects($this->once()) + ->method('isAdmin') + ->with('DelegatedUid') + ->willReturn(false); + $this->authorizedGroupMapper + ->expects($this->once()) + ->method('findAllClassesForUser') + ->with($user) + ->willReturn([Admin::class]); + $this->globalAuth + ->expects($this->once()) + ->method('saveAuth') + ->with('', 'test', 'password'); + + $response = $this->ajaxController->saveGlobalCredentials('', 'test', 'password'); + $this->assertSame(200, $response->getStatus()); + } + + public function testSaveGlobalCredentialsAsNormalUserForGlobal(): void { + $user = $this->createMock(IUser::class); + $user->method('getUID')->willReturn('NormalUid'); + $this->userSession->method('getUser')->willReturn($user); + $this->groupManager + ->expects($this->once()) + ->method('isAdmin') + ->with('NormalUid') + ->willReturn(false); + $this->authorizedGroupMapper + ->expects($this->once()) + ->method('findAllClassesForUser') + ->with($user) + ->willReturn([]); + $this->globalAuth + ->expects($this->never()) + ->method('saveAuth'); + + $response = $this->ajaxController->saveGlobalCredentials('', 'test', 'password'); + $this->assertSame(403, $response->getStatus()); + $this->assertSame('Permission denied', $response->getData()['message']); + } } From 33bad598c9dc166b62fbe2047b8e119c01cc3e16 Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Sun, 28 Sep 2025 12:05:00 +0200 Subject: [PATCH 4/5] refactor(files_external): migrate Ajax with `OC_JSON` to proper controller Signed-off-by: Ferdinand Thiessen (cherry picked from commit 31571476d3f4b95049e5af4aa2a6dda1b4ffdb9f) --- apps/files_external/ajax/applicable.php | 42 ------------------- apps/files_external/ajax/oauth2.php | 13 ------ apps/files_external/appinfo/routes.php | 18 ++++---- .../lib/Controller/AjaxController.php | 35 ++++++++++++++++ apps/files_external/src/settings.js | 33 +++++++-------- .../tests/Controller/AjaxControllerTest.php | 4 ++ build/psalm-baseline.xml | 21 ---------- tests/lib/UrlGeneratorTest.php | 8 ++-- 8 files changed, 67 insertions(+), 107 deletions(-) delete mode 100644 apps/files_external/ajax/applicable.php delete mode 100644 apps/files_external/ajax/oauth2.php diff --git a/apps/files_external/ajax/applicable.php b/apps/files_external/ajax/applicable.php deleted file mode 100644 index ece913ffc068a..0000000000000 --- a/apps/files_external/ajax/applicable.php +++ /dev/null @@ -1,42 +0,0 @@ -search($pattern, $limit, $offset) as $group) { - $groups[$group->getGID()] = $group->getDisplayName(); -} - -$users = []; -foreach (Server::get(IUserManager::class)->searchDisplayName($pattern, $limit, $offset) as $user) { - $users[$user->getUID()] = $user->getDisplayName(); -} - -$results = ['groups' => $groups, 'users' => $users]; - -\OC_JSON::success($results); diff --git a/apps/files_external/ajax/oauth2.php b/apps/files_external/ajax/oauth2.php deleted file mode 100644 index d961d41ea6bcc..0000000000000 --- a/apps/files_external/ajax/oauth2.php +++ /dev/null @@ -1,13 +0,0 @@ -getL10N('files_external'); - -// TODO: implement redirect to which storage backend requested this diff --git a/apps/files_external/appinfo/routes.php b/apps/files_external/appinfo/routes.php index 5602e1c0d11ea..fb695eafefead 100644 --- a/apps/files_external/appinfo/routes.php +++ b/apps/files_external/appinfo/routes.php @@ -6,13 +6,6 @@ * SPDX-License-Identifier: AGPL-3.0-only */ - -$this->create('files_external_oauth2', 'apps/files_external/ajax/oauth2.php') - ->actionInclude('files_external/ajax/oauth2.php'); - -$this->create('files_external_list_applicable', '/apps/files_external/applicable') - ->actionInclude('files_external/ajax/applicable.php'); - return [ 'resources' => [ 'global_storages' => ['url' => '/globalstorages'], @@ -20,11 +13,20 @@ 'user_global_storages' => ['url' => '/userglobalstorages'], ], 'routes' => [ + [ + 'name' => 'Ajax#getApplicableEntities', + 'url' => '/ajax/applicable', + 'verb' => 'GET', + ], + [ + 'name' => 'Ajax#oauth2Callback', + 'url' => '/ajax/oauth2.php', + 'verb' => 'GET', + ], [ 'name' => 'Ajax#getSshKeys', 'url' => '/ajax/public_key.php', 'verb' => 'POST', - 'requirements' => [], ], [ 'name' => 'Ajax#saveGlobalCredentials', diff --git a/apps/files_external/lib/Controller/AjaxController.php b/apps/files_external/lib/Controller/AjaxController.php index 154c30b13653f..8f0015b0cc6ed 100644 --- a/apps/files_external/lib/Controller/AjaxController.php +++ b/apps/files_external/lib/Controller/AjaxController.php @@ -19,6 +19,7 @@ use OCP\IGroupManager; use OCP\IL10N; use OCP\IRequest; +use OCP\IUserManager; use OCP\IUserSession; class AjaxController extends Controller { @@ -37,12 +38,46 @@ public function __construct( private GlobalAuth $globalAuth, private IUserSession $userSession, private IGroupManager $groupManager, + private IUserManager $userManager, private IL10N $l10n, private AuthorizedGroupMapper $authorizedGroupMapper, ) { parent::__construct($appName, $request); } + + /** + * Legacy endpoint for oauth2 callback + */ + #[NoAdminRequired()] + public function oauth2Callback(): JSONResponse { + return new JSONResponse(['status' => 'success']); + } + + /** + * Returns a list of users and groups that match the given pattern. + * Used for user and group picker in the admin settings. + * + * @param string $pattern The search pattern + * @param int|null $limit The maximum number of results to return + * @param int|null $offset The offset from which to start returning results + * @return JSONResponse + */ + public function getApplicableEntities(string $pattern = '', ?int $limit = null, ?int $offset = null): JSONResponse { + $groups = []; + foreach ($this->groupManager->search($pattern, $limit, $offset) as $group) { + $groups[$group->getGID()] = $group->getDisplayName(); + } + + $users = []; + foreach ($this->userManager->searchDisplayName($pattern, $limit, $offset) as $user) { + $users[$user->getUID()] = $user->getDisplayName(); + } + + $results = ['groups' => $groups, 'users' => $users]; + return new JSONResponse($results); + } + /** * @param int $keyLength * @return array diff --git a/apps/files_external/src/settings.js b/apps/files_external/src/settings.js index 6b86245b9c9d2..223237da38b8e 100644 --- a/apps/files_external/src/settings.js +++ b/apps/files_external/src/settings.js @@ -120,7 +120,7 @@ function initApplicableUsersMultiselect($elements, userListLimit) { dropdownCssClass: 'files-external-select2', // minimumInputLength: 1, ajax: { - url: OC.generateUrl('apps/files_external/applicable'), + url: OC.generateUrl('apps/files_external/ajax/applicable'), dataType: 'json', quietMillis: 100, data(term, page) { // page is the one-based page number tracked by Select2 @@ -131,26 +131,21 @@ function initApplicableUsersMultiselect($elements, userListLimit) { } }, results(data) { - if (data.status === 'success') { - - const results = [] - let userCount = 0 // users is an object + const results = [] + let userCount = 0 // users is an object - // add groups - $.each(data.groups, function(gid, group) { - results.push({ name: gid + '(group)', displayname: group, type: 'group' }) - }) - // add users - $.each(data.users, function(id, user) { - userCount++ - results.push({ name: id, displayname: user, type: 'user' }) - }) + // add groups + $.each(data.groups, function(gid, group) { + results.push({ name: gid + '(group)', displayname: group, type: 'group' }) + }) + // add users + $.each(data.users, function(id, user) { + userCount++ + results.push({ name: id, displayname: user, type: 'user' }) + }) - const more = (userCount >= userListLimit) || (data.groups.length >= userListLimit) - return { results, more } - } else { - // FIXME add error handling - } + const more = (userCount >= userListLimit) || (data.groups.length >= userListLimit) + return { results, more } }, }, initSelection(element, callback) { diff --git a/apps/files_external/tests/Controller/AjaxControllerTest.php b/apps/files_external/tests/Controller/AjaxControllerTest.php index 6c3a48740c884..036e88788c203 100644 --- a/apps/files_external/tests/Controller/AjaxControllerTest.php +++ b/apps/files_external/tests/Controller/AjaxControllerTest.php @@ -17,6 +17,7 @@ use OCP\IL10N; use OCP\IRequest; use OCP\IUser; +use OCP\IUserManager; use OCP\IUserSession; use PHPUnit\Framework\MockObject\MockObject; use Test\TestCase; @@ -27,6 +28,7 @@ class AjaxControllerTest extends TestCase { private GlobalAuth&MockObject $globalAuth; private IUserSession&MockObject $userSession; private IGroupManager&MockObject $groupManager; + private IUserManager&MockObject $userManager; private IL10N&MockObject $l10n; private AuthorizedGroupMapper&MockObject $authorizedGroupMapper; private AjaxController $ajaxController; @@ -37,6 +39,7 @@ protected function setUp(): void { $this->globalAuth = $this->createMock(GlobalAuth::class); $this->userSession = $this->createMock(IUserSession::class); $this->groupManager = $this->createMock(IGroupManager::class); + $this->userManager = $this->createMock(IUserManager::class); $this->l10n = $this->createMock(IL10N::class); $this->authorizedGroupMapper = $this->createMock(AuthorizedGroupMapper::class); @@ -47,6 +50,7 @@ protected function setUp(): void { $this->globalAuth, $this->userSession, $this->groupManager, + $this->userManager, $this->l10n, $this->authorizedGroupMapper, ); diff --git a/build/psalm-baseline.xml b/build/psalm-baseline.xml index 24b8f823832f0..b5f2271284608 100644 --- a/build/psalm-baseline.xml +++ b/build/psalm-baseline.xml @@ -1404,27 +1404,6 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/lib/UrlGeneratorTest.php b/tests/lib/UrlGeneratorTest.php index 4320efc419083..e8f88d8f35b80 100644 --- a/tests/lib/UrlGeneratorTest.php +++ b/tests/lib/UrlGeneratorTest.php @@ -116,16 +116,16 @@ public static function provideRoutes(): array { public static function provideDocRootAppUrlParts(): array { return [ - ['files_external', 'ajax/oauth2.php', [], '/index.php/apps/files_external/ajax/oauth2.php'], - ['files_external', 'ajax/oauth2.php', ['trut' => 'trat', 'dut' => 'dat'], '/index.php/apps/files_external/ajax/oauth2.php?trut=trat&dut=dat'], + ['user_ldap', 'ajax/wizard.php', [], '/index.php/apps/user_ldap/ajax/wizard.php'], + ['user_ldap', 'ajax/wizard.php', ['trut' => 'trat', 'dut' => 'dat'], '/index.php/apps/user_ldap/ajax/wizard.php?trut=trat&dut=dat'], ['', 'index.php', ['trut' => 'trat', 'dut' => 'dat'], '/index.php?trut=trat&dut=dat'], ]; } public static function provideSubDirAppUrlParts(): array { return [ - ['files_external', 'ajax/oauth2.php', [], '/nextcloud/index.php/apps/files_external/ajax/oauth2.php'], - ['files_external', 'ajax/oauth2.php', ['trut' => 'trat', 'dut' => 'dat'], '/nextcloud/index.php/apps/files_external/ajax/oauth2.php?trut=trat&dut=dat'], + ['user_ldap', 'ajax/wizard.php', [], '/nextcloud/index.php/apps/user_ldap/ajax/wizard.php'], + ['user_ldap', 'ajax/wizard.php', ['trut' => 'trat', 'dut' => 'dat'], '/nextcloud/index.php/apps/user_ldap/ajax/wizard.php?trut=trat&dut=dat'], ['', 'index.php', ['trut' => 'trat', 'dut' => 'dat'], '/nextcloud/index.php?trut=trat&dut=dat'], ]; } From 886009ae1f2e9bb46c06d247f22fa4322c240aad Mon Sep 17 00:00:00 2001 From: Tatjana Kaschperko Lindt Date: Wed, 1 Apr 2026 12:08:30 +0200 Subject: [PATCH 5/5] feat(files_external): allow delegated admins to search applicable users/groups Signed-off-by: Tatjana Kaschperko Lindt --- .../lib/Controller/AjaxController.php | 2 + .../tests/Controller/AjaxControllerTest.php | 45 +++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/apps/files_external/lib/Controller/AjaxController.php b/apps/files_external/lib/Controller/AjaxController.php index 8f0015b0cc6ed..b97bd7f55b53b 100644 --- a/apps/files_external/lib/Controller/AjaxController.php +++ b/apps/files_external/lib/Controller/AjaxController.php @@ -13,6 +13,7 @@ use OCA\Files_External\Settings\Admin; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\AuthorizedAdminSetting; use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\AppFramework\Http\Attribute\PasswordConfirmationRequired; use OCP\AppFramework\Http\JSONResponse; @@ -63,6 +64,7 @@ public function oauth2Callback(): JSONResponse { * @param int|null $offset The offset from which to start returning results * @return JSONResponse */ + #[AuthorizedAdminSetting(settings: Admin::class)] public function getApplicableEntities(string $pattern = '', ?int $limit = null, ?int $offset = null): JSONResponse { $groups = []; foreach ($this->groupManager->search($pattern, $limit, $offset) as $group) { diff --git a/apps/files_external/tests/Controller/AjaxControllerTest.php b/apps/files_external/tests/Controller/AjaxControllerTest.php index 036e88788c203..f4bb34994bbf5 100644 --- a/apps/files_external/tests/Controller/AjaxControllerTest.php +++ b/apps/files_external/tests/Controller/AjaxControllerTest.php @@ -13,6 +13,7 @@ use OCA\Files_External\Lib\Auth\PublicKey\RSA; use OCA\Files_External\Settings\Admin; use OCP\AppFramework\Http\JSONResponse; +use OCP\IGroup; use OCP\IGroupManager; use OCP\IL10N; use OCP\IRequest; @@ -67,6 +68,50 @@ protected function setUp(): void { parent::setUp(); } + public function testGetApplicableEntitiesReturnsGroupsAndUsers(): void { + $group = $this->createMock(IGroup::class); + $group->method('getGID')->willReturn('group1'); + $group->method('getDisplayName')->willReturn('Group One'); + + $user = $this->createMock(IUser::class); + $user->method('getUID')->willReturn('user1'); + $user->method('getDisplayName')->willReturn('User One'); + + $this->groupManager + ->expects($this->once()) + ->method('search') + ->with('test', 10, 0) + ->willReturn([$group]); + $this->userManager + ->expects($this->once()) + ->method('searchDisplayName') + ->with('test', 10, 0) + ->willReturn([$user]); + + $response = $this->ajaxController->getApplicableEntities('test', 10, 0); + $this->assertSame(200, $response->getStatus()); + $this->assertSame(['group1' => 'Group One'], $response->getData()['groups']); + $this->assertSame(['user1' => 'User One'], $response->getData()['users']); + } + + public function testGetApplicableEntitiesWithNoResults(): void { + $this->groupManager + ->expects($this->once()) + ->method('search') + ->with('', null, null) + ->willReturn([]); + $this->userManager + ->expects($this->once()) + ->method('searchDisplayName') + ->with('', null, null) + ->willReturn([]); + + $response = $this->ajaxController->getApplicableEntities(); + $this->assertSame(200, $response->getStatus()); + $this->assertSame([], $response->getData()['groups']); + $this->assertSame([], $response->getData()['users']); + } + public function testGetSshKeys(): void { $this->rsa ->expects($this->once())