Skip to content

Commit 2a25ddd

Browse files
committed
feat: add delegation backend
Signed-off-by: Hamza <hamzamahjoubi221@gmail.com>
1 parent 1e74c52 commit 2a25ddd

23 files changed

Lines changed: 889 additions & 151 deletions

appinfo/info.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ The rating depends on the installed text processing backend. See [the rating ove
3434
3535
Learn more about the Nextcloud Ethical AI Rating [in our blog](https://nextcloud.com/blog/nextcloud-ethical-ai-rating/).
3636
]]></description>
37-
<version>5.8.0-dev.2</version>
37+
<version>5.8.0-dev.3</version>
3838
<licence>agpl</licence>
3939
<author homepage="https://github.com/ChristophWurst">Christoph Wurst</author>
4040
<author homepage="https://github.com/GretaD">GretaD</author>

appinfo/routes.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,26 @@
505505
'url' => '/api/follow-up/check-message-ids',
506506
'verb' => 'POST',
507507
],
508+
[
509+
'name' => 'delegation#getDelegatedAccounts',
510+
'url' => '/api/delegations',
511+
'verb' => 'GET',
512+
],
513+
[
514+
'name' => 'delegation#getDelegatedUsers',
515+
'url' => '/api/delegations/{accountId}',
516+
'verb' => 'GET',
517+
],
518+
[
519+
'name' => 'delegation#delegate',
520+
'url' => '/api/delegations/{accountId}',
521+
'verb' => 'POST',
522+
],
523+
[
524+
'name' => 'delegation#unDelegate',
525+
'url' => '/api/delegations/{accountId}/{userId}',
526+
'verb' => 'DELETE',
527+
],
508528
[
509529
'name' => 'textBlockShares#getTextBlockShares',
510530
'url' => '/api/textBlocks/{id}/shares',

lib/Controller/AccountApiController.php

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use OCA\Mail\ResponseDefinitions;
1515
use OCA\Mail\Service\AccountService;
1616
use OCA\Mail\Service\AliasesService;
17+
use OCA\Mail\Service\DelegationService;
1718
use OCP\AppFramework\Http;
1819
use OCP\AppFramework\Http\Attribute\ApiRoute;
1920
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
@@ -32,6 +33,7 @@ public function __construct(
3233
private readonly ?string $userId,
3334
private readonly AccountService $accountService,
3435
private readonly AliasesService $aliasesService,
36+
private readonly DelegationService $delegationService,
3537
) {
3638
parent::__construct($appName, $request);
3739
}
@@ -54,17 +56,37 @@ public function list(): DataResponse {
5456
}
5557

5658
$accounts = $this->accountService->findByUserId($userId);
57-
return new DataResponse(array_map(function (Account $account) use ($userId) {
59+
$result = array_map(function (Account $account) use ($userId) {
5860
$aliases = $this->aliasesService->findAll($account->getId(), $userId);
5961
return [
6062
'id' => $account->getId(),
6163
'email' => $account->getEmail(),
64+
'isDelegated' => false,
6265
'aliases' => array_map(static fn (Alias $alias) => [
6366
'id' => $alias->getId(),
6467
'email' => $alias->getAlias(),
6568
'name' => $alias->getName(),
6669
], $aliases),
6770
];
68-
}, $accounts));
71+
}, $accounts);
72+
73+
$delegatedAccounts = $this->accountService->findDelegatedAccounts($userId);
74+
foreach ($delegatedAccounts as $mailAccount) {
75+
$account = new Account($mailAccount);
76+
$ownerUserId = $mailAccount->getUserId();
77+
$aliases = $this->aliasesService->findAll($account->getId(), $ownerUserId);
78+
$result[] = [
79+
'id' => $account->getId(),
80+
'email' => $account->getEmail(),
81+
'isDelegated' => true,
82+
'aliases' => array_map(static fn (Alias $alias) => [
83+
'id' => $alias->getId(),
84+
'email' => $alias->getAlias(),
85+
'name' => $alias->getName(),
86+
], $aliases),
87+
];
88+
}
89+
90+
return new DataResponse($result);
6991
}
7092
}

lib/Controller/AccountsController.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,18 @@ public function index(): JSONResponse {
9898
foreach ($mailAccounts as $mailAccount) {
9999
$conf = $mailAccount->jsonSerialize();
100100
$conf['aliases'] = $this->aliasesService->findAll($conf['accountId'], $this->currentUserId);
101+
$conf['isDelegated'] = false;
101102
$json[] = $conf;
102103
}
104+
105+
$delegatedAccounts = $this->accountService->findDelegatedAccounts($this->currentUserId);
106+
foreach ($delegatedAccounts as $delegatedAccount) {
107+
$conf = $delegatedAccount->jsonSerialize();
108+
$conf['isDelegated'] = true;
109+
$conf['aliases'] = $this->aliasesService->findAll($conf['accountId'], $delegatedAccount->getUserId());
110+
$json[] = $conf;
111+
}
112+
103113
return new JSONResponse($json);
104114
}
105115

lib/Controller/AliasesController.php

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use OCA\Mail\Exception\NotImplemented;
1313
use OCA\Mail\Http\TrapError;
1414
use OCA\Mail\Service\AliasesService;
15+
use OCA\Mail\Service\DelegationService;
1516
use OCP\AppFramework\Controller;
1617
use OCP\AppFramework\Db\DoesNotExistException;
1718
use OCP\AppFramework\Http;
@@ -23,14 +24,17 @@
2324
class AliasesController extends Controller {
2425
private AliasesService $aliasService;
2526
private string $currentUserId;
27+
private DelegationService $delegationService;
2628

2729
public function __construct(string $appName,
2830
IRequest $request,
2931
AliasesService $aliasesService,
30-
string $userId) {
32+
string $userId,
33+
DelegationService $delegationService) {
3134
parent::__construct($appName, $request);
3235
$this->aliasService = $aliasesService;
3336
$this->currentUserId = $userId;
37+
$this->delegationService = $delegationService;
3438
}
3539

3640
/**
@@ -42,7 +46,8 @@ public function __construct(string $appName,
4246
*/
4347
#[TrapError]
4448
public function index(int $accountId): JSONResponse {
45-
return new JSONResponse($this->aliasService->findAll($accountId, $this->currentUserId));
49+
$effectiveUserId = $this->delegationService->resolveAccountUserId($accountId, $this->currentUserId);
50+
return new JSONResponse($this->aliasService->findAll($accountId, $effectiveUserId));
4651
}
4752

4853
/**
@@ -64,9 +69,10 @@ public function update(int $id,
6469
string $alias,
6570
string $aliasName,
6671
?int $smimeCertificateId = null): JSONResponse {
72+
$effectiveUserId = $this->delegationService->resolveAliasUserId($id, $this->currentUserId);
6773
return new JSONResponse(
6874
$this->aliasService->update(
69-
$this->currentUserId,
75+
$effectiveUserId,
7076
$id,
7177
$alias,
7278
$aliasName,
@@ -83,7 +89,8 @@ public function update(int $id,
8389
*/
8490
#[TrapError]
8591
public function destroy(int $id): JSONResponse {
86-
return new JSONResponse($this->aliasService->delete($this->currentUserId, $id));
92+
$effectiveUserId = $this->delegationService->resolveAliasUserId($id, $this->currentUserId);
93+
return new JSONResponse($this->aliasService->delete($effectiveUserId, $id));
8794
}
8895

8996
/**
@@ -98,8 +105,9 @@ public function destroy(int $id): JSONResponse {
98105
*/
99106
#[TrapError]
100107
public function create(int $accountId, string $alias, string $aliasName): JSONResponse {
108+
$effectiveUserId = $this->delegationService->resolveAccountUserId($accountId, $this->currentUserId);
101109
return new JSONResponse(
102-
$this->aliasService->create($this->currentUserId, $accountId, $alias, $aliasName),
110+
$this->aliasService->create($effectiveUserId, $accountId, $alias, $aliasName),
103111
Http::STATUS_CREATED
104112
);
105113
}
@@ -115,6 +123,7 @@ public function create(int $accountId, string $alias, string $aliasName): JSONRe
115123
*/
116124
#[TrapError]
117125
public function updateSignature(int $id, ?string $signature = null): JSONResponse {
118-
return new JSONResponse($this->aliasService->updateSignature($this->currentUserId, $id, $signature));
126+
$effectiveUserId = $this->delegationService->resolveAliasUserId($id, $this->currentUserId);
127+
return new JSONResponse($this->aliasService->updateSignature($effectiveUserId, $id, $signature));
119128
}
120129
}
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
10+
namespace OCA\Mail\Controller;
11+
12+
use OCA\Mail\Exception\DelegationExistsException;
13+
use OCA\Mail\Http\TrapError;
14+
use OCA\Mail\Service\AccountService;
15+
use OCA\Mail\Service\DelegationService;
16+
use OCP\AppFramework\Controller;
17+
use OCP\AppFramework\Http;
18+
use OCP\AppFramework\Http\Attribute\OpenAPI;
19+
use OCP\AppFramework\Http\JSONResponse;
20+
use OCP\IRequest;
21+
use OCP\IUserManager;
22+
23+
#[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)]
24+
class DelegationController extends Controller {
25+
private ?string $currentUserId;
26+
27+
public function __construct(
28+
string $appName,
29+
IRequest $request,
30+
private DelegationService $delegationService,
31+
private AccountService $accountService,
32+
private IUserManager $userManager,
33+
?string $UserId,
34+
) {
35+
parent::__construct($appName, $request);
36+
$this->currentUserId = $UserId;
37+
}
38+
39+
/**
40+
* Get all accounts delegated to the current user
41+
*
42+
* @NoAdminRequired
43+
*
44+
* @return JSONResponse
45+
*/
46+
#[TrapError]
47+
public function getDelegatedAccounts(): JSONResponse {
48+
if ($this->currentUserId === null) {
49+
return new JSONResponse([], Http::STATUS_UNAUTHORIZED);
50+
}
51+
52+
return new JSONResponse(
53+
$this->delegationService->findDelegatedAccountForUser($this->currentUserId)
54+
);
55+
}
56+
57+
/**
58+
* Get all users that have delegation for a given account
59+
*
60+
* @NoAdminRequired
61+
*
62+
* @param int $accountId
63+
* @return JSONResponse
64+
*/
65+
#[TrapError]
66+
public function getDelegatedUsers(int $accountId): JSONResponse {
67+
if ($this->currentUserId === null) {
68+
return new JSONResponse([], Http::STATUS_UNAUTHORIZED);
69+
}
70+
71+
$account = $this->accountService->findById($accountId);
72+
if ($account->getUserId() !== $this->currentUserId) {
73+
return new JSONResponse([], Http::STATUS_NOT_FOUND);
74+
}
75+
76+
return new JSONResponse(
77+
$this->delegationService->findDelegatedToUsersForAccount($accountId)
78+
);
79+
}
80+
81+
/**
82+
* Delegate an account to a user
83+
*
84+
* @NoAdminRequired
85+
*
86+
* @param int $accountId
87+
* @param string $userId
88+
* @return JSONResponse
89+
*/
90+
#[TrapError]
91+
public function delegate(int $accountId, string $userId): JSONResponse {
92+
if ($this->currentUserId === null) {
93+
return new JSONResponse([], Http::STATUS_UNAUTHORIZED);
94+
}
95+
96+
$account = $this->accountService->findById($accountId);
97+
if ($account->getUserId() !== $this->currentUserId) {
98+
return new JSONResponse([], Http::STATUS_NOT_FOUND);
99+
}
100+
101+
if ($userId === $this->currentUserId) {
102+
return new JSONResponse(['message' => 'Cannot delegate to yourself'], Http::STATUS_BAD_REQUEST);
103+
}
104+
105+
if (!$this->userManager->userExists($userId)) {
106+
return new JSONResponse([], Http::STATUS_NOT_FOUND);
107+
}
108+
109+
try {
110+
$delegation = $this->delegationService->delegate($accountId, $userId);
111+
} catch (DelegationExistsException) {
112+
return new JSONResponse(['message' => 'Delegation already exists'], Http::STATUS_CONFLICT);
113+
}
114+
115+
return new JSONResponse($delegation, Http::STATUS_CREATED);
116+
}
117+
118+
/**
119+
* Revoke delegation of an account for a user
120+
*
121+
* @NoAdminRequired
122+
*
123+
* @param int $accountId
124+
* @param string $userId
125+
* @return JSONResponse
126+
*/
127+
#[TrapError]
128+
public function unDelegate(int $accountId, string $userId): JSONResponse {
129+
if ($this->currentUserId === null) {
130+
return new JSONResponse([], Http::STATUS_UNAUTHORIZED);
131+
}
132+
133+
$account = $this->accountService->findById($accountId);
134+
if ($account->getUserId() !== $this->currentUserId) {
135+
return new JSONResponse([], Http::STATUS_NOT_FOUND);
136+
}
137+
138+
$this->delegationService->unDelegate($accountId, $userId);
139+
return new JSONResponse([], Http::STATUS_OK);
140+
}
141+
}

lib/Controller/FilterController.php

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

1212
use OCA\Mail\AppInfo\Application;
1313
use OCA\Mail\Service\AccountService;
14+
use OCA\Mail\Service\DelegationService;
1415
use OCA\Mail\Service\FilterService;
1516
use OCP\AppFramework\Controller;
16-
use OCP\AppFramework\Http;
1717
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
1818
use OCP\AppFramework\Http\Attribute\Route;
1919
use OCP\AppFramework\Http\JSONResponse;
@@ -27,6 +27,7 @@ public function __construct(
2727
string $userId,
2828
private FilterService $mailFilterService,
2929
private AccountService $accountService,
30+
private DelegationService $delegationService,
3031
) {
3132
parent::__construct(Application::APP_ID, $request);
3233
$this->currentUserId = $userId;
@@ -39,11 +40,8 @@ public function __construct(
3940
#[Route(Route::TYPE_FRONTPAGE, verb: 'GET', url: '/api/filter/{accountId}', requirements: ['accountId' => '[\d]+'])]
4041
#[NoAdminRequired]
4142
public function getFilters(int $accountId): JSONResponse {
42-
$account = $this->accountService->findById($accountId);
43-
44-
if ($account->getUserId() !== $this->currentUserId) {
45-
return new JSONResponse([], Http::STATUS_NOT_FOUND);
46-
}
43+
$effectiveUserId = $this->delegationService->resolveAccountUserId($accountId, $this->currentUserId);
44+
$account = $this->accountService->find($effectiveUserId, $accountId);
4745

4846
$result = $this->mailFilterService->parse($account->getMailAccount());
4947

@@ -56,11 +54,8 @@ public function getFilters(int $accountId): JSONResponse {
5654
#[Route(Route::TYPE_FRONTPAGE, verb: 'PUT', url: '/api/filter/{accountId}', requirements: ['accountId' => '[\d]+'])]
5755
#[NoAdminRequired]
5856
public function updateFilters(int $accountId, array $filters): JSONResponse {
59-
$account = $this->accountService->findById($accountId);
60-
61-
if ($account->getUserId() !== $this->currentUserId) {
62-
return new JSONResponse([], Http::STATUS_NOT_FOUND);
63-
}
57+
$effectiveUserId = $this->delegationService->resolveAccountUserId($accountId, $this->currentUserId);
58+
$account = $this->accountService->find($effectiveUserId, $accountId);
6459

6560
$this->mailFilterService->update($account->getMailAccount(), $filters);
6661

0 commit comments

Comments
 (0)