Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion REUSE.toml
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ SPDX-FileCopyrightText = "2018-2024 Google LLC, 2016-2024 Nextcloud GmbH and Nex
SPDX-License-Identifier = "Apache-2.0"

[[annotations]]
path = ["img/mail.png", "img/mail.svg", "img/mail-dark.svg", "img/important.svg", "img/star.png", "img/star.svg", "img/mail-notification.png", "img/mail-notification.svg", "img/text_snippet.svg", "img/format-pilcrow-arrow-right.svg", "img/format-pilcrow-arrow-left.svg"]
path = ["img/mail.png", "img/mail.svg", "img/mail-dark.svg", "img/important.svg", "img/star.png", "img/star.svg", "img/mail-notification.png", "img/mail-notification.svg", "img/text_snippet.svg", "img/format-pilcrow-arrow-right.svg", "img/format-pilcrow-arrow-left.svg", "img/delegation.svg"]
precedence = "aggregate"
SPDX-FileCopyrightText = "2018-2026 Google LLC"
SPDX-License-Identifier = "Apache-2.0"
Expand Down
2 changes: 1 addition & 1 deletion appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ The rating depends on the installed text processing backend. See [the rating ove

Learn more about the Nextcloud Ethical AI Rating [in our blog](https://nextcloud.com/blog/nextcloud-ethical-ai-rating/).
]]></description>
<version>5.8.0-dev.2</version>
<version>5.8.0-dev.3</version>
<licence>agpl</licence>
<author homepage="https://github.com/ChristophWurst">Christoph Wurst</author>
<author homepage="https://github.com/GretaD">GretaD</author>
Expand Down
15 changes: 15 additions & 0 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,21 @@
'url' => '/api/follow-up/check-message-ids',
'verb' => 'POST',
],
[
'name' => 'delegation#getDelegatedUsers',
'url' => '/api/delegations/{accountId}',
'verb' => 'GET',
],
[
'name' => 'delegation#delegate',
'url' => '/api/delegations/{accountId}',
'verb' => 'POST',
],
[
'name' => 'delegation#unDelegate',
'url' => '/api/delegations/{accountId}/{userId}',
'verb' => 'DELETE',
],
[
'name' => 'textBlockShares#getTextBlockShares',
'url' => '/api/textBlocks/{id}/shares',
Expand Down
1 change: 1 addition & 0 deletions img/delegation.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 23 additions & 2 deletions lib/Controller/AccountApiController.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use OCA\Mail\ResponseDefinitions;
use OCA\Mail\Service\AccountService;
use OCA\Mail\Service\AliasesService;
use OCA\Mail\Service\DelegationService;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\ApiRoute;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
Expand All @@ -32,6 +33,7 @@ public function __construct(
private readonly ?string $userId,
private readonly AccountService $accountService,
private readonly AliasesService $aliasesService,
private readonly DelegationService $delegationService,
) {
parent::__construct($appName, $request);
}
Expand All @@ -54,17 +56,36 @@ public function list(): DataResponse {
}

$accounts = $this->accountService->findByUserId($userId);
return new DataResponse(array_map(function (Account $account) use ($userId) {
$result = array_map(function (Account $account) use ($userId) {
$aliases = $this->aliasesService->findAll($account->getId(), $userId);
return [
'id' => $account->getId(),
'email' => $account->getEmail(),
'isDelegated' => false,
'aliases' => array_map(static fn (Alias $alias) => [
'id' => $alias->getId(),
'email' => $alias->getAlias(),
'name' => $alias->getName(),
], $aliases),
];
}, $accounts));
}, $accounts);

$delegatedAccounts = $this->accountService->findDelegatedAccounts($userId);
foreach ($delegatedAccounts as $account) {
$ownerUserId = $account->getUserId();
$aliases = $this->aliasesService->findAll($account->getId(), $ownerUserId);
$result[] = [
'id' => $account->getId(),
'email' => $account->getEmail(),
'isDelegated' => true,
'aliases' => array_map(static fn (Alias $alias) => [
'id' => $alias->getId(),
'email' => $alias->getAlias(),
'name' => $alias->getName(),
], $aliases),
];
}
Comment on lines +73 to +87

return new DataResponse($result);
}
}
62 changes: 43 additions & 19 deletions lib/Controller/AccountsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
use OCA\Mail\Model\NewMessageData;
use OCA\Mail\Service\AccountService;
use OCA\Mail\Service\AliasesService;
use OCA\Mail\Service\DelegationService;
use OCA\Mail\Service\SetupService;
use OCA\Mail\Service\Sync\SyncService;
use OCP\AppFramework\Controller;
Expand Down Expand Up @@ -52,6 +53,7 @@ class AccountsController extends Controller {
private IConfig $config;
private IRemoteHostValidator $hostValidator;
private MailboxSync $mailboxSync;
private DelegationService $delegationService;

public function __construct(
string $appName,
Expand All @@ -69,6 +71,7 @@ public function __construct(
IRemoteHostValidator $hostValidator,
MailboxSync $mailboxSync,
private ITimeFactory $timeFactory,
DelegationService $delegationService,
) {
parent::__construct($appName, $request);
$this->accountService = $accountService;
Expand All @@ -83,6 +86,7 @@ public function __construct(
$this->config = $config;
$this->hostValidator = $hostValidator;
$this->mailboxSync = $mailboxSync;
$this->delegationService = $delegationService;
}

/**
Expand All @@ -98,6 +102,15 @@ public function index(): JSONResponse {
foreach ($mailAccounts as $mailAccount) {
$conf = $mailAccount->jsonSerialize();
$conf['aliases'] = $this->aliasesService->findAll($conf['accountId'], $this->currentUserId);
$conf['isDelegated'] = false;
$json[] = $conf;
}

$delegatedAccounts = $this->accountService->findDelegatedAccounts($this->currentUserId);
foreach ($delegatedAccounts as $delegatedAccount) {
$conf = $delegatedAccount->jsonSerialize();
$conf['isDelegated'] = true;
$conf['aliases'] = $this->aliasesService->findAll($conf['accountId'], $delegatedAccount->getUserId());
$json[] = $conf;
}
return new JSONResponse($json);
Expand All @@ -113,7 +126,8 @@ public function index(): JSONResponse {
*/
#[TrapError]
public function show(int $id): JSONResponse {
return new JSONResponse($this->accountService->find($this->currentUserId, $id));
$effectiveUserId = $this->delegationService->resolveAccountUserId($id, $this->currentUserId);
return new JSONResponse($this->accountService->find($effectiveUserId, $id));
}

/**
Expand All @@ -136,9 +150,10 @@ public function update(int $id,
?string $imapPassword = null,
?string $smtpPassword = null,
string $authMethod = 'password'): JSONResponse {
$effectiveUserId = $this->delegationService->resolveAccountUserId($id, $this->currentUserId);
try {
// Make sure the account actually exists
$this->accountService->find($this->currentUserId, $id);
$this->accountService->find($effectiveUserId, $id);
} catch (ClientException $e) {
return new JSONResponse([], Http::STATUS_BAD_REQUEST);
}
Expand All @@ -164,9 +179,10 @@ public function update(int $id,
}

try {
return MailJsonResponse::success(
$this->setup->createNewAccount($accountName, $emailAddress, $imapHost, $imapPort, $imapSslMode, $imapUser, $imapPassword, $smtpHost, $smtpPort, $smtpSslMode, $smtpUser, $smtpPassword, $this->currentUserId, $authMethod, $id)
$result = MailJsonResponse::success(
$this->setup->createNewAccount($accountName, $emailAddress, $imapHost, $imapPort, $imapSslMode, $imapUser, $imapPassword, $smtpHost, $smtpPort, $smtpSslMode, $smtpUser, $smtpPassword, $effectiveUserId, $authMethod, $id)
);
return $result;
} catch (CouldNotConnectException $e) {
$data = [
'error' => $e->getReason(),
Expand Down Expand Up @@ -221,28 +237,29 @@ public function patchAccount(int $id,
?bool $classificationEnabled = null,
?bool $imipCreate = null,
): JSONResponse {
$account = $this->accountService->find($this->currentUserId, $id);
$effectiveUserId = $this->delegationService->resolveAccountUserId($id, $this->currentUserId);
$account = $this->accountService->find($effectiveUserId, $id);

$dbAccount = $account->getMailAccount();

if ($draftsMailboxId !== null) {
$this->mailManager->getMailbox($this->currentUserId, $draftsMailboxId);
$this->mailManager->getMailbox($effectiveUserId, $draftsMailboxId);
$dbAccount->setDraftsMailboxId($draftsMailboxId);
}
if ($sentMailboxId !== null) {
$this->mailManager->getMailbox($this->currentUserId, $sentMailboxId);
$this->mailManager->getMailbox($effectiveUserId, $sentMailboxId);
$dbAccount->setSentMailboxId($sentMailboxId);
}
if ($trashMailboxId !== null) {
$this->mailManager->getMailbox($this->currentUserId, $trashMailboxId);
$this->mailManager->getMailbox($effectiveUserId, $trashMailboxId);
$dbAccount->setTrashMailboxId($trashMailboxId);
}
if ($archiveMailboxId !== null) {
$this->mailManager->getMailbox($this->currentUserId, $archiveMailboxId);
$this->mailManager->getMailbox($effectiveUserId, $archiveMailboxId);
$dbAccount->setarchiveMailboxId($archiveMailboxId);
}
if ($snoozeMailboxId !== null) {
$this->mailManager->getMailbox($this->currentUserId, $snoozeMailboxId);
$this->mailManager->getMailbox($effectiveUserId, $snoozeMailboxId);
$dbAccount->setSnoozeMailboxId($snoozeMailboxId);
}
if ($editorMode !== null) {
Expand All @@ -262,7 +279,7 @@ public function patchAccount(int $id,
$dbAccount->setTrashRetentionDays($trashRetentionDays <= 0 ? null : $trashRetentionDays);
}
if ($junkMailboxId !== null) {
$this->mailManager->getMailbox($this->currentUserId, $junkMailboxId);
$this->mailManager->getMailbox($effectiveUserId, $junkMailboxId);
$dbAccount->setJunkMailboxId($junkMailboxId);
}
if ($searchBody !== null) {
Expand All @@ -274,9 +291,10 @@ public function patchAccount(int $id,
if ($imipCreate !== null) {
$dbAccount->setImipCreate($imipCreate);
}
return new JSONResponse(
$result = new JSONResponse(
new Account($this->accountService->save($dbAccount))
);
return $result;
}

/**
Expand All @@ -292,7 +310,8 @@ public function patchAccount(int $id,
*/
#[TrapError]
public function updateSignature(int $id, ?string $signature = null): JSONResponse {
$this->accountService->updateSignature($id, $this->currentUserId, $signature);
$effectiveUserId = $this->delegationService->resolveAccountUserId($id, $this->currentUserId);
$this->accountService->updateSignature($id, $effectiveUserId, $signature);
return new JSONResponse();
}

Expand All @@ -307,7 +326,8 @@ public function updateSignature(int $id, ?string $signature = null): JSONRespons
*/
#[TrapError]
public function destroy(int $id): JSONResponse {
$this->accountService->delete($this->currentUserId, $id);
$effectiveUserId = $this->delegationService->resolveAccountUserId($id, $this->currentUserId);
$this->accountService->delete($effectiveUserId, $id);
Comment on lines +329 to +330
return new JSONResponse();
}

Expand Down Expand Up @@ -420,11 +440,12 @@ public function draft(int $id,
$this->logger->info("Updating draft <$draftId> in account <$id>");
}

$account = $this->accountService->find($this->currentUserId, $id);
$effectiveUserId = $this->delegationService->resolveAccountUserId($id, $this->currentUserId);
$account = $this->accountService->find($effectiveUserId, $id);
$previousDraft = null;
if ($draftId !== null) {
try {
$previousDraft = $this->mailManager->getMessage($this->currentUserId, $draftId);
$previousDraft = $this->mailManager->getMessage($effectiveUserId, $draftId);
} catch (ClientException $e) {
$this->logger->info("Draft {$draftId} could not be loaded: {$e->getMessage()}");
}
Expand Down Expand Up @@ -460,7 +481,8 @@ public function draft(int $id,
* @throws ClientException
*/
public function getQuota(int $id): JSONResponse {
$account = $this->accountService->find($this->currentUserId, $id);
$effectiveUserId = $this->delegationService->resolveAccountUserId($id, $this->currentUserId);
$account = $this->accountService->find($effectiveUserId, $id);

$quota = $this->mailManager->getQuota($account);
if ($quota === null) {
Expand All @@ -479,7 +501,8 @@ public function getQuota(int $id): JSONResponse {
* @throws ClientException
*/
public function updateSmimeCertificate(int $id, ?int $smimeCertificateId = null) {
$account = $this->accountService->find($this->currentUserId, $id)->getMailAccount();
$effectiveUserId = $this->delegationService->resolveAccountUserId($id, $this->currentUserId);
$account = $this->accountService->find($effectiveUserId, $id)->getMailAccount();
$account->setSmimeCertificateId($smimeCertificateId);
$this->accountService->update($account);
return MailJsonResponse::success();
Expand All @@ -494,8 +517,9 @@ public function updateSmimeCertificate(int $id, ?int $smimeCertificateId = null)
* @throws ClientException
*/
public function testAccountConnection(int $id) {
$effectiveUserId = $this->delegationService->resolveAccountUserId($id, $this->currentUserId);
return new JSONResponse([
'data' => $this->accountService->testAccountConnection($this->currentUserId, $id),
'data' => $this->accountService->testAccountConnection($effectiveUserId, $id),
]);
}

Expand Down
21 changes: 15 additions & 6 deletions lib/Controller/AliasesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use OCA\Mail\Exception\NotImplemented;
use OCA\Mail\Http\TrapError;
use OCA\Mail\Service\AliasesService;
use OCA\Mail\Service\DelegationService;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Http;
Expand All @@ -23,14 +24,17 @@
class AliasesController extends Controller {
private AliasesService $aliasService;
private string $currentUserId;
private DelegationService $delegationService;

public function __construct(string $appName,
IRequest $request,
AliasesService $aliasesService,
string $userId) {
string $userId,
DelegationService $delegationService) {
parent::__construct($appName, $request);
$this->aliasService = $aliasesService;
$this->currentUserId = $userId;
$this->delegationService = $delegationService;
}

/**
Expand All @@ -42,7 +46,8 @@ public function __construct(string $appName,
*/
#[TrapError]
public function index(int $accountId): JSONResponse {
return new JSONResponse($this->aliasService->findAll($accountId, $this->currentUserId));
$effectiveUserId = $this->delegationService->resolveAccountUserId($accountId, $this->currentUserId);
return new JSONResponse($this->aliasService->findAll($accountId, $effectiveUserId));
}

/**
Expand All @@ -64,9 +69,10 @@ public function update(int $id,
string $alias,
string $aliasName,
?int $smimeCertificateId = null): JSONResponse {
$effectiveUserId = $this->delegationService->resolveAliasUserId($id, $this->currentUserId);
return new JSONResponse(
$this->aliasService->update(
$this->currentUserId,
$effectiveUserId,
$id,
$alias,
$aliasName,
Expand All @@ -83,7 +89,8 @@ public function update(int $id,
*/
#[TrapError]
public function destroy(int $id): JSONResponse {
return new JSONResponse($this->aliasService->delete($this->currentUserId, $id));
$effectiveUserId = $this->delegationService->resolveAliasUserId($id, $this->currentUserId);
return new JSONResponse($this->aliasService->delete($effectiveUserId, $id));
}

/**
Expand All @@ -98,8 +105,9 @@ public function destroy(int $id): JSONResponse {
*/
#[TrapError]
public function create(int $accountId, string $alias, string $aliasName): JSONResponse {
$effectiveUserId = $this->delegationService->resolveAccountUserId($accountId, $this->currentUserId);
return new JSONResponse(
$this->aliasService->create($this->currentUserId, $accountId, $alias, $aliasName),
$this->aliasService->create($effectiveUserId, $accountId, $alias, $aliasName),
Http::STATUS_CREATED
);
}
Expand All @@ -115,6 +123,7 @@ public function create(int $accountId, string $alias, string $aliasName): JSONRe
*/
#[TrapError]
public function updateSignature(int $id, ?string $signature = null): JSONResponse {
return new JSONResponse($this->aliasService->updateSignature($this->currentUserId, $id, $signature));
$effectiveUserId = $this->delegationService->resolveAliasUserId($id, $this->currentUserId);
return new JSONResponse($this->aliasService->updateSignature($effectiveUserId, $id, $signature));
}
}
Loading