feat(files_external): allow delegated admins to save global credentials

Signed-off-by: Tatjana Kaschperko Lindt <kaschperko-lindt@strato.de>
This commit is contained in:
Tatjana Kaschperko Lindt 2026-03-30 12:54:06 +02:00 committed by backportbot[bot]
parent f6f4bce01e
commit 281f0e7367
2 changed files with 101 additions and 2 deletions

View file

@ -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;
@ -38,6 +40,7 @@ class AjaxController extends Controller {
private IGroupManager $groupManager,
private IUserManager $userManager,
private IL10N $l10n,
private AuthorizedGroupMapper $authorizedGroupMapper,
) {
parent::__construct($appName, $request);
}
@ -112,10 +115,14 @@ class AjaxController extends Controller {
], Http::STATUS_UNAUTHORIZED);
}
// Non-admins can only edit their own credentials
// Admin can edit global credentials
// Non-admins can only edit their own credentials.
// Admin or delegated admin can edit global credentials (uid === '').
// Cannot use #[AuthorizedAdminSetting] here because this endpoint is
// #[NoAdminRequired] and must also allow users to edit their own (uid !== '')
// credentials — the two paths share one method.
$allowedToEdit = $uid === ''
? $this->groupManager->isAdmin($currentUser->getUID())
|| in_array(Admin::class, $this->authorizedGroupMapper->findAllClassesForUser($currentUser), true)
: $currentUser->getUID() === $uid;
if ($allowedToEdit) {

View file

@ -7,9 +7,11 @@ declare(strict_types=1);
*/
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;
@ -28,6 +30,7 @@ class AjaxControllerTest extends TestCase {
private IGroupManager&MockObject $groupManager;
private IUserManager&MockObject $userManager;
private IL10N&MockObject $l10n;
private AuthorizedGroupMapper&MockObject $authorizedGroupMapper;
private AjaxController $ajaxController;
protected function setUp(): void {
@ -38,6 +41,7 @@ class AjaxControllerTest extends TestCase {
$this->groupManager = $this->createMock(IGroupManager::class);
$this->userManager = $this->createMock(IUserManager::class);
$this->l10n = $this->createMock(IL10N::class);
$this->authorizedGroupMapper = $this->createMock(AuthorizedGroupMapper::class);
$this->ajaxController = new AjaxController(
'files_external',
@ -48,6 +52,7 @@ class AjaxControllerTest extends TestCase {
$this->groupManager,
$this->userManager,
$this->l10n,
$this->authorizedGroupMapper,
);
$this->l10n->expects($this->any())
@ -153,4 +158,91 @@ class AjaxControllerTest extends TestCase {
$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 testSaveGlobalCredentialsAsDelegatedAdminForAnotherUser(): void {
// Delegated admins may only set global (uid='') credentials, not impersonate other users.
$user = $this->createMock(IUser::class);
$user->method('getUID')->willReturn('DelegatedUid');
$this->userSession->method('getUser')->willReturn($user);
$this->groupManager
->expects($this->never())
->method('isAdmin');
$this->authorizedGroupMapper
->expects($this->never())
->method('findAllClassesForUser');
$this->globalAuth
->expects($this->never())
->method('saveAuth');
$response = $this->ajaxController->saveGlobalCredentials('OtherUserUid', 'test', 'password');
$this->assertSame(403, $response->getStatus());
$this->assertSame('Permission denied', $response->getData()['message']);
}
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']);
}
}