diff options
author | Christoph Wurst <christoph@winzerhof-wurst.at> | 2022-07-01 16:10:21 +0300 |
---|---|---|
committer | Christoph Wurst <christoph@winzerhof-wurst.at> | 2022-07-01 17:09:14 +0300 |
commit | 78704879b437a93cc3028418cfd65cbee886ee81 (patch) | |
tree | 4b1016d6d4122d5a78ef48b56721973d37c6ede8 | |
parent | ff28c48255c1247228c777b52a786667f5b4c0b3 (diff) |
Add support for XOAUTH2
Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
-rw-r--r-- | appinfo/info.xml | 2 | ||||
-rw-r--r-- | lib/Command/CreateAccount.php | 5 | ||||
-rw-r--r-- | lib/Db/MailAccount.php | 3 | ||||
-rw-r--r-- | lib/IMAP/IMAPClientFactory.php | 12 | ||||
-rw-r--r-- | lib/Migration/Version1140Date20220701103556.php | 59 | ||||
-rw-r--r-- | tests/Unit/Command/CreateAccountTest.php | 13 |
6 files changed, 87 insertions, 7 deletions
diff --git a/appinfo/info.xml b/appinfo/info.xml index 8ada4faff..fbd1a3c66 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -12,7 +12,7 @@ - **🙈 We’re not reinventing the wheel!** Based on the great [Horde](https://horde.org) libraries. - **📬 Want to host your own mail server?** We do not have to reimplement this as you could set up [Mail-in-a-Box](https://mailinabox.email)! ]]></description> - <version>1.14.0-alpha.3</version> + <version>1.14.0-alpha.4</version> <licence>agpl</licence> <author>Greta Doçi</author> <author homepage="https://github.com/nextcloud/groupware">Nextcloud Groupware Team</author> diff --git a/lib/Command/CreateAccount.php b/lib/Command/CreateAccount.php index a1b95a283..fd8a428a3 100644 --- a/lib/Command/CreateAccount.php +++ b/lib/Command/CreateAccount.php @@ -36,6 +36,7 @@ class CreateAccount extends Command { public const ARGUMENT_USER_ID = 'user-id'; public const ARGUMENT_NAME = 'name'; public const ARGUMENT_EMAIL = 'email'; + public const ARGUMENT_AUTH_METHOD = 'auth-method'; public const ARGUMENT_IMAP_HOST = 'imap-host'; public const ARGUMENT_IMAP_PORT = 'imap-port'; public const ARGUMENT_IMAP_SSL_MODE = 'imap-ssl-mode'; @@ -87,12 +88,15 @@ class CreateAccount extends Command { $this->addArgument(self::ARGUMENT_SMTP_SSL_MODE, InputArgument::REQUIRED); $this->addArgument(self::ARGUMENT_SMTP_USER, InputArgument::REQUIRED); $this->addArgument(self::ARGUMENT_SMTP_PASSWORD, InputArgument::REQUIRED); + + $this->addArgument(self::ARGUMENT_AUTH_METHOD, InputArgument::OPTIONAL, 'password or xoauth2', 'password'); } protected function execute(InputInterface $input, OutputInterface $output): int { $userId = $input->getArgument(self::ARGUMENT_USER_ID); $name = $input->getArgument(self::ARGUMENT_NAME); $email = $input->getArgument(self::ARGUMENT_EMAIL); + $authMethod = $input->getArgument(self::ARGUMENT_AUTH_METHOD); $imapHost = $input->getArgument(self::ARGUMENT_IMAP_HOST); $imapPort = $input->getArgument(self::ARGUMENT_IMAP_PORT); @@ -115,6 +119,7 @@ class CreateAccount extends Command { $account->setUserId($userId); $account->setName($name); $account->setEmail($email); + $account->setAuthMethod($authMethod); $account->setInboundHost($imapHost); $account->setInboundPort((int) $imapPort); diff --git a/lib/Db/MailAccount.php b/lib/Db/MailAccount.php index 7703165b8..b24ca0729 100644 --- a/lib/Db/MailAccount.php +++ b/lib/Db/MailAccount.php @@ -91,6 +91,8 @@ use OCP\AppFramework\Db\Entity; * @method void setSievePassword(?string $sievePassword) * @method bool|null isSignatureAboveQuote() * @method void setSignatureAboveQuote(bool $signatureAboveQuote) + * @method string getAuthMethod() + * @method void setAuthMethod(string $method) */ class MailAccount extends Entity { protected $userId; @@ -112,6 +114,7 @@ class MailAccount extends Entity { protected $order; protected $showSubscribedOnly; protected $personalNamespace; + protected $authMethod; /** @var int|null */ protected $draftsMailboxId; diff --git a/lib/IMAP/IMAPClientFactory.php b/lib/IMAP/IMAPClientFactory.php index 236e2a329..b7bf596f0 100644 --- a/lib/IMAP/IMAPClientFactory.php +++ b/lib/IMAP/IMAPClientFactory.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace OCA\Mail\IMAP; +use Horde_Imap_Client_Password_Xoauth2; use Horde_Imap_Client_Socket; use OCA\Mail\Account; use OCA\Mail\Cache\Cache; @@ -66,8 +67,7 @@ class IMAPClientFactory { public function getClient(Account $account, bool $useCache = true): Horde_Imap_Client_Socket { $host = $account->getMailAccount()->getInboundHost(); $user = $account->getMailAccount()->getInboundUser(); - $password = $account->getMailAccount()->getInboundPassword(); - $password = $this->crypto->decrypt($password); + $decryptedPassword = $this->crypto->decrypt($account->getMailAccount()->getInboundPassword()); $port = $account->getMailAccount()->getInboundPort(); $sslMode = $account->getMailAccount()->getInboundSslMode(); if ($sslMode === 'none') { @@ -76,7 +76,7 @@ class IMAPClientFactory { $params = [ 'username' => $user, - 'password' => $password, + 'password' => $decryptedPassword, 'hostspec' => $host, 'port' => $port, 'secure' => $sslMode, @@ -88,6 +88,12 @@ class IMAPClientFactory { ], ], ]; + if ($account->getMailAccount()->getAuthMethod() === 'xoauth2') { + $params['xoauth2_token'] = new Horde_Imap_Client_Password_Xoauth2( + $account->getEmail(), + $decryptedPassword, + ); + } if ($useCache && $this->cacheFactory->isAvailable()) { $params['cache'] = [ 'backend' => new Cache([ diff --git a/lib/Migration/Version1140Date20220701103556.php b/lib/Migration/Version1140Date20220701103556.php new file mode 100644 index 000000000..6d21fea34 --- /dev/null +++ b/lib/Migration/Version1140Date20220701103556.php @@ -0,0 +1,59 @@ +<?php + +declare(strict_types=1); + +/** + * @author Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\Mail\Migration; + +use Closure; +use OCP\DB\ISchemaWrapper; +use OCP\DB\Types; +use OCP\Migration\IOutput; +use OCP\Migration\SimpleMigrationStep; + +class Version1140Date20220701103556 extends SimpleMigrationStep { + + /** + * @param IOutput $output + * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` + * @param array $options + * @return null|ISchemaWrapper + */ + public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { + /** @var ISchemaWrapper $schema */ + $schema = $schemaClosure(); + + $accountsTable = $schema->getTable('mail_accounts'); + if (!$accountsTable->hasColumn('auth_method')) { + $accountsTable->addColumn( + 'auth_method', + Types::STRING, + [ + 'notnull' => true, + 'default' => 'password', + ], + ); + } + + return $schema; + } +} diff --git a/tests/Unit/Command/CreateAccountTest.php b/tests/Unit/Command/CreateAccountTest.php index a86fe7f0c..1102ee6c2 100644 --- a/tests/Unit/Command/CreateAccountTest.php +++ b/tests/Unit/Command/CreateAccountTest.php @@ -1,5 +1,7 @@ <?php +declare(strict_types=1); + /** * @author Christoph Wurst <christoph@winzerhof-wurst.at> * @@ -46,6 +48,7 @@ class CreateAccountTest extends TestCase { 'smtp-ssl-mode', 'smtp-user', 'smtp-password', + 'auth-method', ]; protected function setUp(): void { @@ -72,8 +75,12 @@ class CreateAccountTest extends TestCase { $actual = $this->command->getDefinition()->getArguments(); foreach ($actual as $actArg) { - $this->assertTrue($actArg->isRequired()); - $this->assertTrue(in_array($actArg->getName(), $this->args)); + if ($actArg->getName() === 'auth-method') { + self::assertFalse($actArg->isRequired()); + } else { + self::assertTrue($actArg->isRequired()); + } + self::assertTrue(in_array($actArg->getName(), $this->args)); } } @@ -98,7 +105,7 @@ class CreateAccountTest extends TestCase { $input = $this->createMock(InputInterface::class); $input->method('getArgument') ->willReturnCallback(function ($arg) use ($data) { - return $data[$arg]; + return $data[$arg] ?? null; }); $output = $this->createMock(OutputInterface::class); $output->expects($this->once()) |