Skip to content

Commit

Permalink
Merge pull request #9891 from nextcloud/feat/trusted-senders
Browse files Browse the repository at this point in the history
feat: add internal addresses
  • Loading branch information
hamza221 authored Aug 13, 2024
2 parents 9b43440 + aab2db9 commit 649f29e
Show file tree
Hide file tree
Showing 20 changed files with 925 additions and 5 deletions.
15 changes: 15 additions & 0 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,21 @@
'url' => '/api/trustedsenders',
'verb' => 'GET'
],
[
'name' => 'internal_address#setAddress',
'url' => '/api/internalAddress/{address}',
'verb' => 'PUT'
],
[
'name' => 'internal_address#removeAddress',
'url' => '/api/internalAddress/{address}',
'verb' => 'DELETE'
],
[
'name' => 'internal_address#list',
'url' => '/api/internalAddress',
'verb' => 'GET'
],
[
'name' => 'sieve#updateAccount',
'url' => '/api/sieve/account/{id}',
Expand Down
24 changes: 24 additions & 0 deletions lib/Contracts/IInternalAddressService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\Mail\Contracts;

use OCA\Mail\Db\InternalAddress;

interface IInternalAddressService {
public function isInternal(string $uid, string $address): bool;

public function add(string $uid, string $address, string $type, ?bool $trust = true);

/**
* @param string $uid
* @return InternalAddress[]
*/
public function getInternalAddresses(string $uid): array;
}
90 changes: 90 additions & 0 deletions lib/Controller/InternalAddressController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\Mail\Controller;

use OCA\Mail\AppInfo\Application;
use OCA\Mail\Http\JsonResponse;
use OCA\Mail\Http\TrapError;
use OCA\Mail\Service\InternalAddressService;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\IRequest;

class InternalAddressController extends Controller {
private ?string $uid;

public function __construct(IRequest $request,
?string $userId,
private InternalAddressService $internalAddressService) {
parent::__construct(Application::APP_ID, $request);

$this->internalAddressService = $internalAddressService;
$this->uid = $userId;
}

/**
* @NoAdminRequired
*
* @param string $address
* @param string $type
* @return JsonResponse
*/
#[TrapError]
public function setAddress(string $address, string $type): JsonResponse {
$address = $this->internalAddressService->add(
$this->uid,
$address,
$type
)->jsonSerialize();

return JsonResponse::success($address, Http::STATUS_CREATED);
}

/**
* @NoAdminRequired
*
* @param string $address
* @param string $type
* @return JsonResponse
*/
#[TrapError]
public function removeAddress(string $address, string $type): JsonResponse {
if($this->uid === null) {
return JsonResponse::error('User not found', Http::STATUS_UNAUTHORIZED);
}

$this->internalAddressService->add(
$this->uid,
$address,
$type,
false
);

return JsonResponse::success();
}

/**
* @NoAdminRequired
*
* @return JsonResponse
*/
#[TrapError]
public function list(): JsonResponse {
if($this->uid === null) {
return JsonResponse::error('User not found', Http::STATUS_UNAUTHORIZED);
}
$list = $this->internalAddressService->getInternalAddresses(
$this->uid
);

return JsonResponse::success($list);
}
}
16 changes: 15 additions & 1 deletion lib/Controller/PageController.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use OCA\Mail\Service\AiIntegrations\AiIntegrationsService;
use OCA\Mail\Service\AliasesService;
use OCA\Mail\Service\Classification\ClassificationSettingsService;
use OCA\Mail\Service\InternalAddressService;
use OCA\Mail\Service\OutboxService;
use OCA\Mail\Service\SmimeService;
use OCA\Viewer\Event\LoadViewer;
Expand Down Expand Up @@ -70,6 +71,7 @@ class PageController extends Controller {
private IUserManager $userManager;
private ?IAvailabilityCoordinator $availabilityCoordinator;
private ClassificationSettingsService $classificationSettingsService;
private InternalAddressService $internalAddressService;

public function __construct(string $appName,
IRequest $request,
Expand All @@ -91,7 +93,8 @@ public function __construct(string $appName,
AiIntegrationsService $aiIntegrationsService,
IUserManager $userManager,
ContainerInterface $container,
ClassificationSettingsService $classificationSettingsService) {
ClassificationSettingsService $classificationSettingsService,
InternalAddressService $internalAddressService, ) {
parent::__construct($appName, $request);

$this->urlGenerator = $urlGenerator;
Expand All @@ -112,6 +115,7 @@ public function __construct(string $appName,
$this->aiIntegrationsService = $aiIntegrationsService;
$this->userManager = $userManager;
$this->classificationSettingsService = $classificationSettingsService;
$this->internalAddressService = $internalAddressService;

// TODO: inject directly if support for nextcloud < 28 is dropped
try {
Expand Down Expand Up @@ -173,6 +177,16 @@ public function index(): TemplateResponse {
$this->tagMapper->getAllTagsForUser($this->currentUserId)
);

$this->initialStateService->provideInitialState(
'internal-addresses-list',
$this->internalAddressService->getInternalAddresses($this->currentUserId)
);

$this->initialStateService->provideInitialState(
'internal-addresses',
$this->preferences->getPreference($this->currentUserId, 'internal-addresses', false)
);

$this->initialStateService->provideInitialState(
'sort-order',
$this->preferences->getPreference($this->currentUserId, 'sort-order', 'newest')
Expand Down
39 changes: 39 additions & 0 deletions lib/Db/InternalAddress.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\Mail\Db;

use JsonSerializable;
use OCP\AppFramework\Db\Entity;
use ReturnTypeWillChange;

/**
* @method setAddress(string $address): void
* @method getAddress(): string
* @method setUserId(string $userId): void
* @method getUserId(): string
* @method setType(string $type): void
* @method getType(): string
*/
class InternalAddress extends Entity implements JsonSerializable {

protected $address ;
protected $userId;
protected $type;

#[ReturnTypeWillChange]
public function jsonSerialize() {
return [
'id' => $this->id,
'address' => $this->address,
'uid' => $this->userId,
'type' => $this->type,
];
}
}
103 changes: 103 additions & 0 deletions lib/Db/InternalAddressMapper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\Mail\Db;

use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Db\QBMapper;
use OCP\IDBConnection;

/**
* @template-extends QBMapper<InternalAddress>
*/
class InternalAddressMapper extends QBMapper {
public function __construct(IDBConnection $db) {
parent::__construct($db, 'mail_internal_address');
}

public function exists(string $uid, string $address): bool {

$emailObject = new \Horde_Mail_Rfc822_Address($address);
$host = $emailObject->host;
$qb = $this->db->getQueryBuilder();

$select = $qb->select('*')
->from($this->getTableName())
->where(
$qb->expr()->orX(
$qb->expr()->andX(
$qb->expr()->eq('address', $qb->createNamedParameter($address)),
$qb->expr()->eq('type', $qb->createNamedParameter('individual'))
),
$qb->expr()->andX(
$qb->expr()->eq('address', $qb->createNamedParameter($host)),
$qb->expr()->eq('type', $qb->createNamedParameter('domain'))
)
),
$qb->expr()->eq('user_id', $qb->createNamedParameter($uid))
);

$rows = $this->findEntities($select);

return $rows !== [];
}

public function create(string $uid, string $address, string $type): int {

$address = InternalAddress::fromParams([
'userId' => $uid,
'address' => $address,
'type' => $type,
]);

$result = $this->insert($address);

return $result->getId();
}

public function remove(string $uid, string $address, string $type): void {
$qb = $this->db->getQueryBuilder();

$delete = $qb->delete($this->getTableName())
->where(
$qb->expr()->eq('user_id', $qb->createNamedParameter($uid)),
$qb->expr()->eq('address', $qb->createNamedParameter($address)),
$qb->expr()->eq('type', $qb->createNamedParameter($type))
);

$delete->executeStatement();
}

/**
* @param string $uid
* @return InternalAddress[]
*/
public function findAll(string $uid): array {
$qb = $this->db->getQueryBuilder();
$select = $qb->select('*')
->from($this->getTableName())
->where($qb->expr()->eq('user_id', $qb->createNamedParameter($uid)));
return $this->findEntities($select);
}

public function find(string $uid, string $address): ?InternalAddress {
$qb = $this->db->getQueryBuilder();
$select = $qb->select('*')
->from($this->getTableName())
->where(
$qb->expr()->eq('user_id', $qb->createNamedParameter($uid)),
$qb->expr()->eq('address', $qb->createNamedParameter($address))
);
try {
return $this->findEntity($select);
} catch (DoesNotExistException $e) {
return null;
}
}
}
54 changes: 54 additions & 0 deletions lib/Migration/Version4000Date20240716172702.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\Mail\Migration;

use Closure;
use OCP\DB\ISchemaWrapper;
use OCP\DB\Types;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;

class Version4000Date20240716172702 extends SimpleMigrationStep {
/**
* @param IOutput $output
* @param Closure(): ISchemaWrapper $schemaClosure
* @param array $options
* @return ISchemaWrapper
*/
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();
$table = $schema->createTable('mail_internal_address');

$table->addColumn('id', Types::INTEGER, [
'autoincrement' => true,
'notnull' => true,
'length' => 4,
]);
$table->addColumn('address', Types::STRING, [
'notnull' => true,
'length' => 255,
]);
$table->addColumn('type', Types::STRING, [
'notnull' => true,
'length' => 64,
]);
$table->addColumn('user_id', Types::STRING, [
'notnull' => true,
'length' => 64,
]);
$table->setPrimaryKey(['id']);
$table->addUniqueIndex(['address', 'user_id'], 'mail_internal_address_uniq');

return $schema;
}


}
Loading

0 comments on commit 649f29e

Please sign in to comment.