Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/mail.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristoph Wurst <ChristophWurst@users.noreply.github.com>2019-12-04 13:17:39 +0300
committerGitHub <noreply@github.com>2019-12-04 13:17:39 +0300
commitfd9bd184f8154938ea7df85c7ee2193f8fb598cd (patch)
treee4f2c3c193747b84c4e8e6145531b204a55c3ff5
parent26c13bc035177ae100054e21ba97f61f6d8d5c50 (diff)
parentad29c8a46cc0bc6e783e82cc81ea5c701c66fb98 (diff)
Merge pull request #2259 from nextcloud/enhancement/persist-provisioned-account
Persist provisioned accounts
-rw-r--r--appinfo/info.xml7
-rw-r--r--appinfo/routes.php10
-rw-r--r--doc/admin.md46
-rw-r--r--lib/AppInfo/BootstrapSingleton.php6
-rw-r--r--lib/Controller/SettingsController.php73
-rw-r--r--lib/Db/MailAccount.php10
-rw-r--r--lib/Db/MailAccountMapper.php38
-rw-r--r--lib/Http/Middleware/ProvisioningMiddleware.php79
-rw-r--r--lib/Migration/MigrateProvisioningConfig.php66
-rw-r--r--lib/Migration/ProvisionAccounts.php56
-rw-r--r--lib/Migration/Version0100Date20180825194217.php1
-rw-r--r--lib/Migration/Version0190Date20191118160843.php60
-rw-r--r--lib/Service/AccountService.php30
-rw-r--r--lib/Service/DefaultAccount/Manager.php127
-rw-r--r--lib/Service/Provisioning/Config.php (renamed from lib/Service/DefaultAccount/Config.php)27
-rw-r--r--lib/Service/Provisioning/ConfigMapper.php62
-rw-r--r--lib/Service/Provisioning/Manager.php183
-rw-r--r--lib/Settings/AdminSettings.php78
-rw-r--r--src/components/NavigationAccount.vue2
-rw-r--r--src/components/settings/AdminSettings.vue53
-rw-r--r--src/components/settings/ProvisionPreview.vue99
-rw-r--r--src/components/settings/ProvisioningSettings.vue428
-rw-r--r--src/main-settings.js40
-rw-r--r--src/main.js15
-rw-r--r--src/mixins/Nextcloud.js29
-rw-r--r--src/service/SettingsService.js35
-rw-r--r--src/views/AccountSettings.vue9
-rw-r--r--templates/settings-admin.php28
-rw-r--r--tests/Integration/Service/MailTransmissionIntegrationTest.php3
-rw-r--r--tests/Unit/Controller/SettingsControllerTest.php86
-rw-r--r--tests/Unit/Db/MailAccountTest.php3
-rw-r--r--tests/Unit/Http/Middleware/ProvisioningMiddlewareTest.php147
-rw-r--r--tests/Unit/Migration/MigrateProvisioningConfigTest.php106
-rw-r--r--tests/Unit/Service/AccountServiceTest.php24
-rw-r--r--tests/Unit/Service/DefaultAccount/ManagerTest.php154
-rw-r--r--tests/Unit/Service/Provisioning/ConfigMapperTest.php85
-rw-r--r--tests/Unit/Service/Provisioning/ConfigTest.php (renamed from tests/Unit/Service/DefaultAccount/ConfigTest.php)4
-rw-r--r--tests/Unit/Service/Provisioning/ManagerTest.php184
-rw-r--r--tests/Unit/Service/Provisioning/TestConfig.php44
-rw-r--r--tests/Unit/Settings/AdminSettingsTest.php74
-rw-r--r--webpack.common.js3
41 files changed, 2216 insertions, 398 deletions
diff --git a/appinfo/info.xml b/appinfo/info.xml
index d51bdf020..1872e1c30 100644
--- a/appinfo/info.xml
+++ b/appinfo/info.xml
@@ -12,7 +12,7 @@
- **🙈 We’re not reinventing the wheel!** Based on the great [Horde](http://horde.org) libraries.
- **📬 Want to host your own mail server?** We don’t have to reimplement this as you could set up [Mail-in-a-Box](https://mailinabox.email)!
]]></description>
- <version>0.19.1</version>
+ <version>0.20.0</version>
<licence>agpl</licence>
<author>Christoph Wurst</author>
<author>Jan-Christoph Borchardt</author>
@@ -34,10 +34,15 @@
<repair-steps>
<post-migration>
<step>OCA\Mail\Migration\FixCollectedAddresses</step>
+ <step>OCA\Mail\Migration\MigrateProvisioningConfig</step>
+ <step>OCA\Mail\Migration\ProvisionAccounts</step>
</post-migration>
</repair-steps>
<commands>
<command>OCA\Mail\Command\CreateAccount</command>
<command>OCA\Mail\Command\ExportAccount</command>
</commands>
+ <settings>
+ <admin>OCA\Mail\Settings\AdminSettings</admin>
+ </settings>
</info>
diff --git a/appinfo/routes.php b/appinfo/routes.php
index 5812cb9d7..8ce7275c8 100644
--- a/appinfo/routes.php
+++ b/appinfo/routes.php
@@ -113,6 +113,16 @@ return [
'url' => '/proxy',
'verb' => 'GET'
],
+ [
+ 'name' => 'settings#provisioning',
+ 'url' => '/api/settings/provisioning',
+ 'verb' => 'POST'
+ ],
+ [
+ 'name' => 'settings#deprovision',
+ 'url' => '/api/settings/provisioning',
+ 'verb' => 'DELETE'
+ ],
],
'resources' => [
'accounts' => ['url' => '/api/accounts'],
diff --git a/doc/admin.md b/doc/admin.md
index acebe76bf..9496b43ee 100644
--- a/doc/admin.md
+++ b/doc/admin.md
@@ -9,52 +9,6 @@ Then open the Mail app from the app menu. Put in your mail account credentials a
Certain advanced or experimental features need to be specifically enabled in your `config.php`:
-### Automatic account creation
-
-In cases where an external user back-end is used for both your Nextcloud and your mail server you may want to have imap accounts set up automatically for your users.
-
-### Available patterns
-
-Two patterns are available to automatically construct credentials:
-* `%USERID%`, e.g. `jan`
-* `%EMAIL%`, e.g. `jan@domain.tld`
-
-### Minimal configuration
-
-The following minimal configuration will add such an account as soon as the user logs in. The login password is used for the IMAP and SMTP authentication.
-
-Note: Valid values for SSL are `'none'`, `'ssl'` and `'tls'`.
-
-```
- 'app.mail.accounts.default' => [
- 'email' => '%USERID%@domain.tld',
- 'imapHost' => 'imap.domain.tld',
- 'imapPort' => 993,
- 'imapSslMode' => 'ssl',
- 'smtpHost' => 'smtp.domain.tld',
- 'smtpPort' => 486,
- 'smtpSslMode' => 'tls',
- ],
-```
-
-### Advanced configuration
-
-In case you have to tweak IMAP and SMTP username, you can do that too.
-
-```
- 'app.mail.accounts.default' => [
- 'email' => '%USERID%@domain.tld',
- 'imapHost' => 'imap.domain.tld',
- 'imapPort' => 993,
- 'imapUser' => '%USERID%@domain.tld',
- 'imapSslMode' => 'ssl',
- 'smtpHost' => 'smtp.domain.tld',
- 'smtpPort' => 486,
- 'smtpUser' => '%USERID%@domain.tld',
- 'smtpSslMode' => 'tls',
- ],
-```
-
### Timeouts
Depending on your mail host, it may be necessary to increase your IMAP and/or SMTP timeout threshold. Currently IMAP defaults to 20 seconds and SMTP defaults to 2 seconds. They can be changed as follows:
diff --git a/lib/AppInfo/BootstrapSingleton.php b/lib/AppInfo/BootstrapSingleton.php
index 448a2fc68..114d0f897 100644
--- a/lib/AppInfo/BootstrapSingleton.php
+++ b/lib/AppInfo/BootstrapSingleton.php
@@ -23,6 +23,7 @@
namespace OCA\Mail\AppInfo;
+use OC\Hooks\PublicEmitter;
use OCA\Mail\Contracts\IAttachmentService;
use OCA\Mail\Contracts\IAvatarService;
use OCA\Mail\Contracts\IMailManager;
@@ -34,6 +35,7 @@ use OCA\Mail\Events\DraftSavedEvent;
use OCA\Mail\Events\MessageSentEvent;
use OCA\Mail\Events\SaveDraftEvent;
use OCA\Mail\Http\Middleware\ErrorMiddleware;
+use OCA\Mail\Http\Middleware\ProvisioningMiddleware;
use OCA\Mail\Listener\AddressCollectionListener;
use OCA\Mail\Listener\DeleteDraftListener;
use OCA\Mail\Listener\DraftMailboxCreatorListener;
@@ -50,7 +52,8 @@ use OCA\Mail\Service\MailTransmission;
use OCA\Mail\Service\UserPreferenceSevice;
use OCP\AppFramework\IAppContainer;
use OCP\EventDispatcher\IEventDispatcher;
-use OCP\IContainer;
+use OCP\IUser;
+use OCP\IUserManager;
use OCP\Util;
class BootstrapSingleton {
@@ -108,6 +111,7 @@ class BootstrapSingleton {
$container->registerAlias('ErrorMiddleware', ErrorMiddleware::class);
$container->registerMiddleWare('ErrorMiddleware');
+ $container->registerMiddleWare(ProvisioningMiddleware::class);
$container->registerAlias(IGroupService::class, NextcloudGroupService::class);
}
diff --git a/lib/Controller/SettingsController.php b/lib/Controller/SettingsController.php
new file mode 100644
index 000000000..ccd80549d
--- /dev/null
+++ b/lib/Controller/SettingsController.php
@@ -0,0 +1,73 @@
+<?php declare(strict_types=1);
+
+/**
+ * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2019 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\Controller;
+
+use OCA\Mail\AppInfo\Application;
+use OCA\Mail\Service\Provisioning\Manager as ProvisioningManager;
+use OCP\AppFramework\Controller;
+use OCP\AppFramework\Http\JSONResponse;
+use OCP\IRequest;
+
+class SettingsController extends Controller {
+
+ /** @var ProvisioningManager */
+ private $provisioningManager;
+
+ public function __construct(IRequest $request,
+ ProvisioningManager $provisioningManager) {
+ parent::__construct(Application::APP_ID, $request);
+ $this->provisioningManager = $provisioningManager;
+ }
+
+ public function provisioning(string $emailTemplate,
+ string $imapUser,
+ string $imapHost,
+ int $imapPort,
+ string $imapSslMode,
+ string $smtpUser,
+ string $smtpHost,
+ int $smtpPort,
+ string $smtpSslMode): JSONResponse {
+ $this->provisioningManager->newProvisioning(
+ $emailTemplate,
+ $imapUser,
+ $imapHost,
+ $imapPort,
+ $imapSslMode,
+ $smtpUser,
+ $smtpHost,
+ $smtpPort,
+ $smtpSslMode
+ );
+
+ return new JSONResponse(null);
+ }
+
+ public function deprovision(): JSONResponse {
+ $this->provisioningManager->deprovision();
+
+ return new JSONResponse(null);
+ }
+
+}
diff --git a/lib/Db/MailAccount.php b/lib/Db/MailAccount.php
index 1de82e356..ec09c09aa 100644
--- a/lib/Db/MailAccount.php
+++ b/lib/Db/MailAccount.php
@@ -45,7 +45,7 @@ use OCP\AppFramework\Db\Entity;
* @method void setInboundSslMode(string $inboundSslMode)
* @method string getInboundUser()
* @method void setInboundUser(string $inboundUser)
- * @method string getInboundPassword()
+ * @method string|null getInboundPassword()
* @method void setInboundPassword(string $inboundPassword)
* @method string getOutboundHost()
* @method void setOutboundHost(string $outboundHost)
@@ -55,7 +55,7 @@ use OCP\AppFramework\Db\Entity;
* @method void setOutboundSslMode(string $outboundSslMode)
* @method string getOutboundUser()
* @method void setOutboundUser(string $outboundUser)
- * @method string getOutboundPassword()
+ * @method string|null getOutboundPassword()
* @method void setOutboundPassword(string $outboundPassword)
* @method string|null getSignature()
* @method void setSignature(string|null $signature)
@@ -63,6 +63,8 @@ use OCP\AppFramework\Db\Entity;
* @method void setLastMailboxSync(int $time)
* @method string getEditorMode()
* @method void setEditorMode(string $editorMode)
+ * @method bool getProvisioned()
+ * @method void setProvisioned(bool $provisioned)
*/
class MailAccount extends Entity {
@@ -82,12 +84,12 @@ class MailAccount extends Entity {
protected $signature;
protected $lastMailboxSync;
protected $editorMode;
+ protected $provisioned;
/**
* @param array $params
*/
public function __construct(array $params=[]) {
-
if (isset($params['accountId'])) {
$this->setId($params['accountId']);
}
@@ -131,6 +133,7 @@ class MailAccount extends Entity {
}
$this->addType('lastMailboxSync', 'integer');
+ $this->addType('provisioned', 'bool');
}
/**
@@ -147,6 +150,7 @@ class MailAccount extends Entity {
'imapSslMode' => $this->getInboundSslMode(),
'signature' => $this->getSignature(),
'editorMode' => $this->getEditorMode(),
+ 'provisioned' => $this->getProvisioned(),
];
if (!is_null($this->getOutboundHost())) {
diff --git a/lib/Db/MailAccountMapper.php b/lib/Db/MailAccountMapper.php
index 409aa135d..bd10a408f 100644
--- a/lib/Db/MailAccountMapper.php
+++ b/lib/Db/MailAccountMapper.php
@@ -25,8 +25,12 @@ declare(strict_types=1);
namespace OCA\Mail\Db;
+use OCP\AppFramework\Db\DoesNotExistException;
+use OCP\AppFramework\Db\MultipleObjectsReturnedException;
use OCP\AppFramework\Db\QBMapper;
+use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
+use OCP\IUser;
class MailAccountMapper extends QBMapper {
@@ -73,6 +77,24 @@ class MailAccountMapper extends QBMapper {
}
/**
+ * @throws DoesNotExistException
+ * @throws MultipleObjectsReturnedException
+ */
+ public function findProvisionedAccount(IUser $user): MailAccount {
+ $qb = $this->db->getQueryBuilder();
+
+ $query = $qb
+ ->select('*')
+ ->from($this->getTableName())
+ ->where(
+ $qb->expr()->eq('user_id', $qb->createNamedParameter($user->getUID())),
+ $qb->expr()->eq('provisioned', $qb->createNamedParameter(true, IQueryBuilder::PARAM_BOOL))
+ );
+
+ return $this->findEntity($query);
+ }
+
+ /**
* Saves an User Account into the database
*
* @param MailAccount $account
@@ -80,12 +102,20 @@ class MailAccountMapper extends QBMapper {
* @return MailAccount
*/
public function save(MailAccount $account): MailAccount {
- if (is_null($account->getId())) {
+ if ($account->getId() === null) {
return $this->insert($account);
- } else {
- $this->update($account);
- return $account;
}
+
+ return $this->update($account);
+ }
+
+ public function deleteProvisionedAccounts(): void {
+ $qb = $this->db->getQueryBuilder();
+
+ $delete = $qb->delete($this->getTableName())
+ ->where($qb->expr()->eq('provisioned', $qb->createNamedParameter(true, IQueryBuilder::PARAM_BOOL)));
+
+ $delete->execute();
}
}
diff --git a/lib/Http/Middleware/ProvisioningMiddleware.php b/lib/Http/Middleware/ProvisioningMiddleware.php
new file mode 100644
index 000000000..73d976042
--- /dev/null
+++ b/lib/Http/Middleware/ProvisioningMiddleware.php
@@ -0,0 +1,79 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2019 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\Http\Middleware;
+
+use OCA\Mail\Service\Provisioning\Manager as ProvisioningManager;
+use OCP\AppFramework\Middleware;
+use OCP\Authentication\Exceptions\CredentialsUnavailableException;
+use OCP\Authentication\Exceptions\PasswordUnavailableException;
+use OCP\Authentication\LoginCredentials\ICredentials;
+use OCP\Authentication\LoginCredentials\IStore as ICredentialStore;
+use OCP\ILogger;
+use OCP\IUserSession;
+
+class ProvisioningMiddleware extends Middleware {
+
+ /** @var IUserSession */
+ private $userSession;
+
+ /** @var ICredentialStore */
+ private $credentialStore;
+
+ /** @var ProvisioningManager */
+ private $provisioningManager;
+
+ /** @var ILogger */
+ private $logger;
+
+ public function __construct(IUserSession $userSession,
+ ICredentialStore $credentialStore,
+ ProvisioningManager $provisioningManager,
+ ILogger $logger) {
+ $this->userSession = $userSession;
+ $this->credentialStore = $credentialStore;
+ $this->provisioningManager = $provisioningManager;
+ $this->logger = $logger;
+ }
+
+ public function beforeController($controller, $methodName) {
+ $user = $this->userSession->getUser();
+ if ($user === null) {
+ // Nothing to update
+ return;
+ }
+ try {
+ $this->provisioningManager->updatePassword(
+ $user,
+ $this->credentialStore->getLoginCredentials()->getPassword()
+ );
+ } catch (CredentialsUnavailableException|PasswordUnavailableException $e) {
+ // Nothing to update
+ return;
+ }
+ }
+
+}
diff --git a/lib/Migration/MigrateProvisioningConfig.php b/lib/Migration/MigrateProvisioningConfig.php
new file mode 100644
index 000000000..7f8bbe29d
--- /dev/null
+++ b/lib/Migration/MigrateProvisioningConfig.php
@@ -0,0 +1,66 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @author Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * Mail
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\Mail\Migration;
+
+use OCA\Mail\Service\Provisioning\Manager as ProvisioningManager;
+use OCP\IConfig;
+use OCP\Migration\IOutput;
+use OCP\Migration\IRepairStep;
+
+class MigrateProvisioningConfig implements IRepairStep {
+
+ /** @var ProvisioningManager */
+ private $provisioningManager;
+
+ /** @var IConfig */
+ private $config;
+
+ public function __construct(ProvisioningManager $provisioningManager,
+ IConfig $config) {
+ $this->provisioningManager = $provisioningManager;
+ $this->config = $config;
+ }
+
+ public function getName(): string {
+ return 'Migrate Mail provisioning config from config.php to the database';
+ }
+
+ public function run(IOutput $output) {
+ $fromConfigRaw = $this->config->getSystemValue('app.mail.accounts.default');
+ if ($fromConfigRaw === '') {
+ $output->info("No old config found");
+ return;
+ }
+
+ if ($this->provisioningManager->getConfig() !== null) {
+ $output->info("Mail provisioning config already set, ignoring old config");
+ return;
+ }
+
+ $this->provisioningManager->importConfig($fromConfigRaw);
+ $this->config->deleteSystemValue('app.mail.accounts.default');
+ $output->info("Config migrated. Accounts not updated yet");
+ }
+
+}
diff --git a/lib/Migration/ProvisionAccounts.php b/lib/Migration/ProvisionAccounts.php
new file mode 100644
index 000000000..11e14eb1d
--- /dev/null
+++ b/lib/Migration/ProvisionAccounts.php
@@ -0,0 +1,56 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2019 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 OCA\Mail\Service\Provisioning\Manager as ProvisioningManager;
+use OCP\Migration\IOutput;
+use OCP\Migration\IRepairStep;
+
+class ProvisionAccounts implements IRepairStep {
+
+ /** @var ProvisioningManager */
+ private $provisioningManager;
+
+ public function __construct(ProvisioningManager $provisioningManager) {
+ $this->provisioningManager = $provisioningManager;
+ }
+
+ public function getName(): string {
+ return 'Create or update provisioned Mail accounts';
+ }
+
+ public function run(IOutput $output) {
+ $config = $this->provisioningManager->getConfig();
+ if ($config === null) {
+ $output->info("No Mail provisioning config set");
+ return;
+ }
+
+ $cnt = $this->provisioningManager->provision($config);
+ $output->info("$cnt accounts provisioned");
+ }
+
+}
diff --git a/lib/Migration/Version0100Date20180825194217.php b/lib/Migration/Version0100Date20180825194217.php
index e5ff6a1a3..86a86cc8f 100644
--- a/lib/Migration/Version0100Date20180825194217.php
+++ b/lib/Migration/Version0100Date20180825194217.php
@@ -54,7 +54,6 @@ class Version0100Date20180825194217 extends SimpleMigrationStep {
$table->addColumn('user_id', 'string', [
'notnull' => true,
'length' => 64,
- 'default' => '',
]);
$table->addColumn('name', 'string', [
'notnull' => false,
diff --git a/lib/Migration/Version0190Date20191118160843.php b/lib/Migration/Version0190Date20191118160843.php
new file mode 100644
index 000000000..e66d42d3d
--- /dev/null
+++ b/lib/Migration/Version0190Date20191118160843.php
@@ -0,0 +1,60 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2019 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\IDBConnection;
+use OCP\Migration\SimpleMigrationStep;
+use OCP\Migration\IOutput;
+
+class Version0190Date20191118160843 extends SimpleMigrationStep {
+
+ /**
+ * @param IOutput $output
+ * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
+ * @param array $options
+ *
+ * @return ISchemaWrapper
+ */
+ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) {
+ /** @var ISchemaWrapper $schema */
+ $schema = $schemaClosure();
+
+ $accountsTable = $schema->getTable('mail_accounts');
+ $accountsTable->addColumn('provisioned', 'boolean', [
+ 'notnull' => true,
+ 'default' => false,
+ ]);
+ $accountsTable->changeColumn('inbound_password', [
+ 'notnull' => false,
+ 'default' => null,
+ ]);
+
+ return $schema;
+ }
+
+}
diff --git a/lib/Service/AccountService.php b/lib/Service/AccountService.php
index 2a869dee2..6b7d5dcf6 100644
--- a/lib/Service/AccountService.php
+++ b/lib/Service/AccountService.php
@@ -24,13 +24,12 @@ declare(strict_types=1);
namespace OCA\Mail\Service;
-use Exception;
use OCA\Mail\Account;
use OCA\Mail\Db\MailAccount;
use OCA\Mail\Db\MailAccountMapper;
use OCA\Mail\Exception\ServiceException;
-use OCA\Mail\Service\DefaultAccount\Manager;
use OCP\AppFramework\Db\DoesNotExistException;
+use function array_map;
class AccountService {
@@ -44,17 +43,12 @@ class AccountService {
*/
private $accounts;
- /** @var Manager */
- private $defaultAccountManager;
-
/** @var AliasesService */
private $aliasesService;
public function __construct(MailAccountMapper $mapper,
- Manager $defaultAccountManager,
AliasesService $aliasesService) {
$this->mapper = $mapper;
- $this->defaultAccountManager = $defaultAccountManager;
$this->aliasesService = $aliasesService;
}
@@ -64,16 +58,9 @@ class AccountService {
*/
public function findByUserId(string $currentUserId): array {
if ($this->accounts === null) {
- $accounts = array_map(function ($a) {
+ return $this->accounts = array_map(function ($a) {
return new Account($a);
- }, $this->mapper->findByUserId($currentUserId));
-
- $defaultAccount = $this->defaultAccountManager->getDefaultAccount();
- if (!is_null($defaultAccount)) {
- $accounts[] = new Account($defaultAccount);
- }
-
- $this->accounts = $accounts;
+ }, $this->mapper->findByUserId($currentUserId));;
}
return $this->accounts;
@@ -96,13 +83,6 @@ class AccountService {
throw new DoesNotExistException("Invalid account id <$accountId>");
}
- if ($accountId === Manager::ACCOUNT_ID) {
- $defaultAccount = $this->defaultAccountManager->getDefaultAccount();
- if (is_null($defaultAccount)) {
- throw new DoesNotExistException('Default account config missing');
- }
- return new Account($defaultAccount);
- }
return new Account($this->mapper->find($uid, $accountId));
}
@@ -110,10 +90,6 @@ class AccountService {
* @param int $accountId
*/
public function delete(string $currentUserId, int $accountId): void {
- if ($accountId === Manager::ACCOUNT_ID) {
- return;
- }
-
$mailAccount = $this->mapper->find($currentUserId, $accountId);
$this->aliasesService->deleteAll($accountId);
$this->mapper->delete($mailAccount);
diff --git a/lib/Service/DefaultAccount/Manager.php b/lib/Service/DefaultAccount/Manager.php
deleted file mode 100644
index a188e0fdb..000000000
--- a/lib/Service/DefaultAccount/Manager.php
+++ /dev/null
@@ -1,127 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-/**
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- *
- * Mail
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
- */
-
-namespace OCA\Mail\Service\DefaultAccount;
-
-use OCA\Mail\Db\MailAccount;
-use OCP\Authentication\Exceptions\CredentialsUnavailableException;
-use OCP\Authentication\LoginCredentials\IStore;
-use OCP\IConfig;
-use OCP\ILogger;
-use OCP\IUserSession;
-use OCP\Security\ICrypto;
-
-class Manager {
-
- const ACCOUNT_ID = -2;
-
- /** @var IConfig */
- private $config;
-
- /** @var IStore */
- private $credentialStore;
-
- /** @var ILogger */
- private $logger;
-
- /** @var IUserSession */
- private $userSession;
-
- /** @var ICrypto */
- private $crypto;
-
- /**
- * @param IConfig $config
- * @param IStore $credentialStore
- * @param ILogger $logger
- * @param IUserSession $userSession
- * @param ICrypto $crypto
- */
- public function __construct(IConfig $config,
- IStore $credentialStore,
- ILogger $logger,
- IUserSession $userSession,
- ICrypto $crypto) {
- $this->config = $config;
- $this->logger = $logger;
- $this->userSession = $userSession;
- $this->crypto = $crypto;
- $this->credentialStore = $credentialStore;
- }
-
- /**
- * @return Config|null
- */
- private function getConfig() {
- $config = $this->config->getSystemValue('app.mail.accounts.default', null);
- if (is_null($config)) {
- $this->logger->debug('no default config found');
- return null;
- } else {
- $this->logger->debug('default config to create a default account found');
- // TODO: check if config is complete
- return new Config($config);
- }
- }
-
- /**
- * @return MailAccount|null
- */
- public function getDefaultAccount() {
- $config = $this->getConfig();
- if (is_null($config)) {
- return null;
- }
- try {
- $credentials = $this->credentialStore->getLoginCredentials();
- } catch (CredentialsUnavailableException $ex) {
- $this->logger->debug('login credentials not available for default account');
- return null;
- }
-
- $user = $this->userSession->getUser();
- $password = $this->crypto->encrypt($credentials->getPassword());
- $this->logger->info('building default account for user ' . $user->getUID());
-
- $account = new MailAccount();
- $account->setId(self::ACCOUNT_ID);
- $account->setUserId($user->getUID());
- $account->setEmail($config->buildEmail($user));
- $account->setName($user->getDisplayName());
-
- $account->setInboundUser($config->buildImapUser($user));
- $account->setInboundHost($config->getImapHost());
- $account->setInboundPort($config->getImapPort());
- $account->setInboundSslMode($config->getImapSslMode());
- $account->setInboundPassword($password);
-
- $account->setOutboundUser($config->buildSmtpUser($user));
- $account->setOutboundHost($config->getSmtpHost());
- $account->setOutboundPort($config->getSmtpPort());
- $account->setOutboundSslMode($config->getSmtpSslMode());
- $account->setOutboundPassword($password);
-
- return $account;
- }
-
-}
diff --git a/lib/Service/DefaultAccount/Config.php b/lib/Service/Provisioning/Config.php
index 4a95929a2..b6a67191e 100644
--- a/lib/Service/DefaultAccount/Config.php
+++ b/lib/Service/Provisioning/Config.php
@@ -21,12 +21,16 @@ declare(strict_types=1);
*
*/
-namespace OCA\Mail\Service\DefaultAccount;
+namespace OCA\Mail\Service\Provisioning;
+use JsonSerializable;
use OCP\IUser;
-class Config {
+class Config implements JsonSerializable {
+ private const VERSION = 1;
+
+ /** @var string[] */
private $data;
/**
@@ -115,13 +119,28 @@ class Config {
* @return string
*/
private function buildUserEmail(string $original, IUser $user) {
- if (!is_null($user->getUID())) {
+ if ($user->getUID() !== null) {
$original = str_replace('%USERID%', $user->getUID(), $original);
}
- if (!is_null($user->getEMailAddress())) {
+ if ($user->getEMailAddress() !== null) {
$original = str_replace('%EMAIL%', $user->getEMailAddress(), $original);
}
return $original;
}
+ public function setActive(bool $state): self {
+ $this->data['active'] = $state;
+ return $this;
+ }
+
+ public function jsonSerialize() {
+ return array_merge(
+ [
+ 'active' => false,
+ 'version' => self::VERSION,
+ ],
+ $this->data
+ );
+ }
+
}
diff --git a/lib/Service/Provisioning/ConfigMapper.php b/lib/Service/Provisioning/ConfigMapper.php
new file mode 100644
index 000000000..5aef20f8f
--- /dev/null
+++ b/lib/Service/Provisioning/ConfigMapper.php
@@ -0,0 +1,62 @@
+<?php declare(strict_types=1);
+
+/**
+ * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2019 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\Service\Provisioning;
+
+use OCA\Mail\AppInfo\Application;
+use OCP\IConfig;
+
+class ConfigMapper {
+
+ private const CONFIG_KEY = 'provisioning_settings';
+
+ /** @var IConfig */
+ private $config;
+
+ public function __construct(IConfig $config) {
+ $this->config = $config;
+ }
+
+ public function load(): ?Config {
+ $raw = $this->config->getAppValue(
+ Application::APP_ID,
+ self::CONFIG_KEY
+ );
+ if ($raw === '') {
+ // Not config set yet
+ return null;
+ }
+ return new Config(json_decode($raw, true));
+ }
+
+ public function save(Config $config): Config {
+ $this->config->setAppValue(
+ Application::APP_ID,
+ self::CONFIG_KEY,
+ json_encode($config)
+ );
+
+ return $config;
+ }
+
+}
diff --git a/lib/Service/Provisioning/Manager.php b/lib/Service/Provisioning/Manager.php
new file mode 100644
index 000000000..60fafc332
--- /dev/null
+++ b/lib/Service/Provisioning/Manager.php
@@ -0,0 +1,183 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @author Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * Mail
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\Mail\Service\Provisioning;
+
+use OCA\Mail\Db\MailAccount;
+use OCA\Mail\Db\MailAccountMapper;
+use OCP\AppFramework\Db\DoesNotExistException;
+use OCP\AppFramework\Db\MultipleObjectsReturnedException;
+use OCP\IDBConnection;
+use OCP\ILogger;
+use OCP\IUser;
+use OCP\IUserManager;
+use OCP\Security\ICrypto;
+
+class Manager {
+
+ /** @var IUserManager */
+ private $userManager;
+
+ /** @var ConfigMapper */
+ private $configMapper;
+
+ /** @var MailAccountMapper */
+ private $mailAccountMapper;
+
+ /** @var ICrypto */
+ private $crypto;
+
+ /** @var ILogger */
+ private $logger;
+
+ public function __construct(IUserManager $userManager,
+ ConfigMapper $configMapper,
+ MailAccountMapper $mailAccountMapper,
+ ICrypto $crypto,
+ ILogger $logger) {
+ $this->userManager = $userManager;
+ $this->configMapper = $configMapper;
+ $this->mailAccountMapper = $mailAccountMapper;
+ $this->crypto = $crypto;
+ $this->logger = $logger;
+ }
+
+ public function getConfig(): ?Config {
+ return $this->configMapper->load();
+ }
+
+ public function provision(Config $config): int {
+ $cnt = 0;
+ $this->userManager->callForAllUsers(function (IUser $user) use ($config, &$cnt) {
+ $this->provisionSingleUser($config, $user);
+ $cnt++;
+ });
+ return $cnt;
+ }
+
+ public function provisionSingleUser(Config $config, IUser $user): void {
+ try {
+ $existing = $this->mailAccountMapper->findProvisionedAccount($user);
+
+ $this->mailAccountMapper->update(
+ $this->updateAccount($user, $existing, $config)
+ );
+ } catch (DoesNotExistException $e) {
+ // Fine, then we create a new one
+ $new = new MailAccount();
+ $new->setUserId($user->getUID());
+ if ($user->getDisplayName() !== $user->getUID()) {
+ // Only set if it's something meaningful
+ $new->setName($user->getDisplayName());
+ }
+ $new->setProvisioned(true);
+
+ $this->mailAccountMapper->insert(
+ $this->updateAccount($user, $new, $config)
+ );
+ }
+ }
+
+ public function newProvisioning(string $email,
+ string $imapUser,
+ string $imapHost,
+ int $imapPort,
+ string $imapSslMode,
+ string $smtpUser,
+ string $smtpHost,
+ int $smtpPort,
+ string $smtpSslMode): void {
+ $config = $this->configMapper->save(new Config([
+ 'active' => true,
+ 'email' => $email,
+ 'imapUser' => $imapUser,
+ 'imapHost' => $imapHost,
+ 'imapPort' => $imapPort,
+ 'imapSslMode' => $imapSslMode,
+ 'smtpUser' => $smtpUser,
+ 'smtpHost' => $smtpHost,
+ 'smtpPort' => $smtpPort,
+ 'smtpSslMode' => $smtpSslMode,
+ ]));
+
+ $this->provision($config);
+ }
+
+ private function updateAccount(IUser $user, MailAccount $account, Config $config): MailAccount {
+ $account->setEmail($config->buildEmail($user));
+ $account->setInboundUser($config->buildImapUser($user));
+ $account->setInboundHost($config->getImapHost());
+ $account->setInboundPort($config->getImapPort());
+ $account->setInboundSslMode($config->getImapSslMode());
+ $account->setOutboundUser($config->buildSmtpUser($user));
+ $account->setOutboundHost($config->getSmtpHost());
+ $account->setOutboundPort($config->getSmtpPort());
+ $account->setOutboundSslMode($config->getSmtpSslMode());
+
+ return $account;
+ }
+
+ public function deprovision(): void {
+ $this->mailAccountMapper->deleteProvisionedAccounts();
+
+ $config = $this->configMapper->load();
+ if ($config !== null) {
+ $config->setActive(false);
+ $this->configMapper->save($config);
+ }
+ }
+
+ public function importConfig(array $data): Config {
+ if (!isset($data['imapUser'])) {
+ $data['imapUser'] = $data['email'];
+ }
+ if (!isset($data['smtpUser'])) {
+ $data['smtpUser'] = $data['email'];
+ }
+
+ return $this->configMapper->save(new Config($data));
+ }
+
+ public function updatePassword(IUser $user, string $password): void {
+ try {
+ $account = $this->mailAccountMapper->findProvisionedAccount($user);
+
+ if ($account->getInboundPassword() !== null
+ && $this->crypto->decrypt($account->getInboundPassword()) === $password
+ && $account->getOutboundPassword() !== null
+ && $this->crypto->decrypt($account->getOutboundPassword()) === $password) {
+ $this->logger->debug('Password of provisioned account is up to date');
+ return;
+ }
+
+ $account->setInboundPassword($this->crypto->encrypt($password));
+ $account->setOutboundPassword($this->crypto->encrypt($password));
+ $this->mailAccountMapper->update($account);
+
+ $this->logger->debug('Provisioned account password udpated');
+ } catch (DoesNotExistException $e) {
+ // Nothing to update
+ }
+ }
+
+}
diff --git a/lib/Settings/AdminSettings.php b/lib/Settings/AdminSettings.php
new file mode 100644
index 000000000..582532471
--- /dev/null
+++ b/lib/Settings/AdminSettings.php
@@ -0,0 +1,78 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2019 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\Settings;
+
+use OCA\Mail\AppInfo\Application;
+use OCA\Mail\Service\Provisioning\Config;
+use OCA\Mail\Service\Provisioning\Manager as ProvisioningManager;
+use OCP\AppFramework\Http\TemplateResponse;
+use OCP\IInitialStateService;
+use OCP\Settings\ISettings;
+
+class AdminSettings implements ISettings {
+
+ /** @var IInitialStateService */
+ private $initialStateService;
+
+ /** @var ProvisioningManager */
+ private $provisioningManager;
+
+ public function __construct(IInitialStateService $initialStateService,
+ ProvisioningManager $provisioningManager) {
+ $this->initialStateService = $initialStateService;
+ $this->provisioningManager = $provisioningManager;
+ }
+
+ public function getForm() {
+ $this->initialStateService->provideInitialState(
+ Application::APP_ID,
+ 'provisioning_settings',
+ $this->provisioningManager->getConfig() ?? new Config([
+ 'active' => false,
+ 'email' => '%USERID%@domain.com',
+ 'imapUser' => '%USERID%@domain.com',
+ 'imapHost' => 'imap.domain.com',
+ 'imapPort' => 993,
+ 'imapSslMode' => 'ssl',
+ 'smtpUser' => '%USERID%@domain.com',
+ 'smtpHost' => 'smtp.domain.com',
+ 'smtpPort' => 587,
+ 'smtpSslMode' => 'tls',
+ ])
+ );
+
+ return new TemplateResponse(Application::APP_ID, 'settings-admin');
+ }
+
+ public function getSection() {
+ return 'groupware';
+ }
+
+ public function getPriority() {
+ return 90;
+ }
+
+}
diff --git a/src/components/NavigationAccount.vue b/src/components/NavigationAccount.vue
index 023ce9447..3c9a2fe81 100644
--- a/src/components/NavigationAccount.vue
+++ b/src/components/NavigationAccount.vue
@@ -37,7 +37,7 @@
<ActionRouter :to="settingsRoute" icon="icon-settings">
{{ t('mail', 'Edit account') }}
</ActionRouter>
- <ActionButton icon="icon-delete" @click="deleteAccount">
+ <ActionButton v-if="!account.provisioned" icon="icon-delete" @click="deleteAccount">
{{ t('mail', 'Delete account') }}
</ActionButton>
<ActionInput icon="icon-add" @submit="createFolder">
diff --git a/src/components/settings/AdminSettings.vue b/src/components/settings/AdminSettings.vue
new file mode 100644
index 000000000..e93acd457
--- /dev/null
+++ b/src/components/settings/AdminSettings.vue
@@ -0,0 +1,53 @@
+<!--
+ - @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
+ -
+ - @author 2019 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/>.
+ -->
+
+<template>
+ <div id="mail-admin-settings" class="section">
+ <h2>{{ t('mail', 'Mail app') }}</h2>
+ <h3>{{ t('mail', 'The mail app allows users to read mails on their IMAP accounts.') }}</h3>
+ <h3>
+ {{
+ t(
+ 'mail',
+ 'Here you can find instance-wide settings. User specific settings are found in the app itself (bottom-left corner).'
+ )
+ }}
+ </h3>
+ <ProvisioningSettings :settings="provisioningSettings" />
+ </div>
+</template>
+
+<script>
+import ProvisioningSettings from './ProvisioningSettings'
+
+export default {
+ name: 'AdminSettings',
+ components: {
+ ProvisioningSettings,
+ },
+ props: {
+ provisioningSettings: {
+ type: Object,
+ required: true,
+ },
+ },
+}
+</script>
diff --git a/src/components/settings/ProvisionPreview.vue b/src/components/settings/ProvisionPreview.vue
new file mode 100644
index 000000000..bf7cf391a
--- /dev/null
+++ b/src/components/settings/ProvisionPreview.vue
@@ -0,0 +1,99 @@
+<!--
+ - @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
+ -
+ - @author 2019 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/>.
+ -->
+
+<template>
+ <div class="provision-preview">
+ <b>
+ <span v-if="data.uid">uid={{ data.uid }}</span>
+ <span v-if="data.email">email={{ email }}</span>
+ </b>
+ <br />
+ {{ t('mail', 'Email: {email}', {email}) }}<br />
+ {{
+ t('mail', 'IMAP: {user} on {host}:{port} ({ssl} encryption)', {
+ user: imapUser,
+ host: imapHost,
+ port: imapPort,
+ ssl: imapSslMode,
+ })
+ }}<br />
+ {{
+ t('mail', 'SMTP: {user} on {host}:{port} ({ssl} encryption)', {
+ user: smtpUser,
+ host: smtpHost,
+ port: smtpPort,
+ ssl: smtpSslMode,
+ })
+ }}<br />
+ </div>
+</template>
+
+<script>
+export default {
+ name: 'ProvisionPreview',
+ props: {
+ data: {
+ type: Object,
+ required: true,
+ },
+ templates: {
+ type: Object,
+ required: true,
+ },
+ },
+ computed: {
+ email() {
+ return this.templates.email.replace('%USERID%', this.data.uid).replace('%EMAIL%', this.data.email)
+ },
+ imapHost() {
+ return this.templates.imapHost
+ },
+ imapPort() {
+ return this.templates.imapPort
+ },
+ imapSslMode() {
+ return this.templates.imapSslMode
+ },
+ imapUser() {
+ return this.templates.imapUser.replace('%USERID%', this.data.uid).replace('%EMAIL%', this.data.email)
+ },
+ smtpHost() {
+ return this.templates.smtpHost
+ },
+ smtpPort() {
+ return this.templates.smtpPort
+ },
+ smtpSslMode() {
+ return this.templates.smtpSslMode
+ },
+ smtpUser() {
+ return this.templates.smtpUser.replace('%USERID%', this.data.uid).replace('%EMAIL%', this.data.email)
+ },
+ },
+}
+</script>
+
+<style lang="scss" scoped>
+.provision-preview {
+ border: 1px solid var(--color-border-dark);
+ border-radius: var(--border-radius);
+}
+</style>
diff --git a/src/components/settings/ProvisioningSettings.vue b/src/components/settings/ProvisioningSettings.vue
new file mode 100644
index 000000000..07027b861
--- /dev/null
+++ b/src/components/settings/ProvisioningSettings.vue
@@ -0,0 +1,428 @@
+<!--
+ - @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
+ -
+ - @author 2019 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/>.
+ -->
+
+<template>
+ <div>
+ <h3>Account provisioning</h3>
+ <p>
+ {{
+ t(
+ 'mail',
+ 'You can configure a template for account settings, from which all users will get an account provisioned from.'
+ )
+ }}
+ {{
+ t(
+ 'mail',
+ "This setting only makes most sense if you use the same user back-end for your organization's Nextcloud and mail server."
+ )
+ }}
+ </p>
+ <div>
+ <input id="mail-provision-toggle" v-model="active" type="checkbox" class="checkbox" />
+ <label for="mail-provision-toggle">
+ {{ t('mail', 'Provision an account for every user') }}
+ </label>
+ </div>
+ <div v-if="active" class="form-preview-row">
+ <form @submit.prevent="submit">
+ <div class="settings-group">
+ <div class="group-title">{{ t('mail', 'General') }}</div>
+ <div class="group-inputs">
+ <label for="mail-provision-email"> {{ t('mail', 'Email address') }}* </label>
+ <br />
+ <input
+ id="mail-provision-email"
+ v-model="emailTemplate"
+ :disabled="loading"
+ name="email"
+ type="text"
+ />
+ </div>
+ </div>
+ <div class="settings-group">
+ <div class="group-title">{{ t('mail', 'IMAP') }}</div>
+ <div class="group-inputs">
+ <label for="mail-provision-imap-user">
+ {{ t('mail', 'User') }}*
+ <br />
+ <input
+ id="mail-provision-imap-user"
+ v-model="imapUser"
+ :disabled="loading"
+ name="email"
+ type="text"
+ />
+ </label>
+ <div class="flex-row">
+ <label for="mail-provision-imap-host">
+ {{ t('mail', 'Host') }}
+ <br />
+ <input
+ id="mail-provision-imap-host"
+ v-model="imapHost"
+ :disabled="loading"
+ name="email"
+ type="text"
+ />
+ </label>
+ <label for="mail-provision-imap-port">
+ {{ t('mail', 'Port') }}
+ <br />
+ <input
+ id="mail-provision-imap-port"
+ v-model="imapPort"
+ :disabled="loading"
+ name="email"
+ type="number"
+ />
+ </label>
+ </div>
+ <div class="flex-row">
+ <input
+ id="mail-provision-imap-user-none"
+ v-model="imapSslMode"
+ type="radio"
+ name="man-imap-sec"
+ :disabled="loading"
+ value="none"
+ />
+ <label
+ class="button"
+ for="mail-provision-imap-user-none"
+ :class="{primary: imapSslMode === 'none'}"
+ >{{ t('mail', 'None') }}</label
+ >
+ <input
+ id="mail-provision-imap-user-ssl"
+ v-model="imapSslMode"
+ type="radio"
+ name="man-imap-sec"
+ :disabled="loading"
+ value="ssl"
+ />
+ <label
+ class="button"
+ for="mail-provision-imap-user-ssl"
+ :class="{primary: imapSslMode === 'ssl'}"
+ >{{ t('mail', 'SSL/TLS') }}</label
+ >
+ <input
+ id="mail-provision-imap-user-tls"
+ v-model="imapSslMode"
+ type="radio"
+ name="man-imap-sec"
+ :disabled="loading"
+ value="tls"
+ />
+ <label
+ class="button"
+ for="mail-provision-imap-user-tls"
+ :class="{primary: imapSslMode === 'tls'}"
+ >{{ t('mail', 'STARTTLS') }}</label
+ >
+ </div>
+ </div>
+ </div>
+ <div class="settings-group">
+ <div class="group-title">{{ t('mail', 'SMTP') }}</div>
+ <div class="group-inputs">
+ <label for="mail-provision-smtp-user">
+ {{ t('mail', 'User') }}*
+ <br />
+ <input
+ id="mail-provision-smtp-user"
+ v-model="smtpUser"
+ :disabled="loading"
+ name="email"
+ type="text"
+ />
+ </label>
+ <div class="flex-row">
+ <label for="mail-provision-imap-host">
+ {{ t('mail', 'Host') }}
+ <br />
+ <input
+ id="mail-provision-smtp-host"
+ v-model="smtpHost"
+ :disabled="loading"
+ name="email"
+ type="text"
+ />
+ </label>
+ <label for="mail-provision-smtp-port">
+ {{ t('mail', 'Port') }}
+ <br />
+ <input
+ id="mail-provision-smtp-port"
+ v-model="smtpPort"
+ :disabled="loading"
+ name="email"
+ type="number"
+ />
+ </label>
+ </div>
+ <div class="flex-row">
+ <input
+ id="mail-provision-smtp-user-none"
+ v-model="smtpSslMode"
+ type="radio"
+ name="man-smtp-sec"
+ :disabled="loading"
+ value="none"
+ />
+ <label
+ class="button"
+ for="mail-provision-smtp-user-none"
+ :class="{primary: smtpSslMode === 'none'}"
+ >{{ t('mail', 'None') }}</label
+ >
+ <input
+ id="mail-provision-smtp-user-ssl"
+ v-model="smtpSslMode"
+ type="radio"
+ name="man-smtp-sec"
+ :disabled="loading"
+ value="ssl"
+ />
+ <label
+ class="button"
+ for="mail-provision-smtp-user-ssl"
+ :class="{primary: smtpSslMode === 'ssl'}"
+ >{{ t('mail', 'SSL/TLS') }}</label
+ >
+ <input
+ id="mail-provision-smtp-user-tls"
+ v-model="smtpSslMode"
+ type="radio"
+ name="man-smtp-sec"
+ :disabled="loading"
+ value="tls"
+ />
+ <label
+ class="button"
+ for="mail-provision-smtp-user-tls"
+ :class="{primary: smtpSslMode === 'tls'}"
+ >{{ t('mail', 'STARTTLS') }}</label
+ >
+ </div>
+ </div>
+ </div>
+ <div class="settings-group">
+ <div class="group-title"></div>
+ <div class="group-inputs">
+ <input
+ type="submit"
+ class="primary"
+ :disabled="loading"
+ :value="t('mail', 'Apply and create/update for all users')"
+ />
+ <input
+ type="button"
+ :disabled="loading"
+ :value="t('mail', 'Disable and un-provision existing accounts')"
+ @click="disable"
+ />
+ <br />
+ <small>{{
+ t('mail', "* %USERID% and %EMAIL% will be replaced with the user's UID and email")
+ }}</small>
+ </div>
+ </div>
+ </form>
+ <div>
+ <h4>Preview</h4>
+ <p>
+ {{
+ t('mail', 'With the settings above, the app will create account settings in the following way:')
+ }}
+ </p>
+ <div class="previews">
+ <ProvisionPreview class="preview-item" :templates="previewTemplates" :data="previewData1" />
+ <ProvisionPreview class="preview-item" :templates="previewTemplates" :data="previewData2" />
+ </div>
+ </div>
+ </div>
+ </div>
+</template>
+
+<script>
+import logger from '../../logger'
+import ProvisionPreview from './ProvisionPreview'
+import {disableProvisioning, saveProvisioningSettings} from '../../service/SettingsService'
+
+export default {
+ name: 'ProvisioningSettings',
+ components: {ProvisionPreview},
+ props: {
+ settings: {
+ type: Object,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ active: !!this.settings.active,
+ emailTemplate: this.settings.email || '',
+ imapHost: this.settings.imapHost || 'mx.domain.com',
+ imapPort: this.settings.imapPort || 993,
+ imapUser: this.settings.imapUser || '%USERID%domain.com',
+ imapSslMode: this.settings.imapSslMode || 'ssl',
+ smtpHost: this.settings.smtpHost || 'mx.domain.com',
+ smtpPort: this.settings.smtpPort || 587,
+ smtpUser: this.settings.smtpUser || '%USERID%domain.com',
+ smtpSslMode: this.settings.smtpSslMode || 'tls',
+ previewData1: {
+ uid: 'user123',
+ email: '',
+ },
+ previewData2: {
+ uid: 'user321',
+ email: 'user@domain.com',
+ },
+ loading: false,
+ }
+ },
+ computed: {
+ previewTemplates() {
+ return {
+ email: this.emailTemplate,
+ imapUser: this.imapUser,
+ imapHost: this.imapHost,
+ imapPort: this.imapPort,
+ imapSslMode: this.imapSslMode,
+ smtpUser: this.smtpUser,
+ smtpHost: this.smtpHost,
+ smtpPort: this.smtpPort,
+ smtpSslMode: this.smtpSslMode,
+ }
+ },
+ },
+ beforeMount() {
+ logger.debug('provisioning settings loaded', {settings: this.settings})
+ },
+ methods: {
+ submit() {
+ this.loading = true
+
+ return saveProvisioningSettings({
+ emailTemplate: this.emailTemplate,
+ imapUser: this.imapUser,
+ imapHost: this.imapHost,
+ imapPort: this.imapPort,
+ imapSslMode: this.imapSslMode,
+ smtpUser: this.smtpUser,
+ smtpHost: this.smtpHost,
+ smtpPort: this.smtpPort,
+ smtpSslMode: this.smtpSslMode,
+ })
+ .then(() => {
+ logger.info('provisioning settings updated')
+ })
+ .catch(error => {
+ // TODO: show user feedback
+ logger.error('Could not save provisioning settings', {error})
+ })
+ .then(() => {
+ this.loading = false
+ })
+ },
+ disable() {
+ this.loading = true
+
+ return disableProvisioning()
+ .then(() => {
+ logger.info('deprovisioned successfully')
+ })
+ .catch(error => {
+ logger.error('could not deprovision accounts', {error})
+ })
+ .then(() => {
+ this.active = false
+ this.loading = false
+ })
+ },
+ },
+}
+</script>
+
+<style lang="scss" scoped>
+.form-preview-row {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+
+ div:last-child {
+ margin-top: 10px;
+ }
+}
+
+.settings-group {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: nowrap;
+
+ .group-title {
+ min-width: 100px;
+ text-align: right;
+ margin: 10px;
+ font-weight: bold;
+ }
+ .group-inputs {
+ margin: 10px;
+ flex-grow: 1;
+
+ input[type='text'] {
+ min-width: 200px;
+ }
+ }
+}
+
+h4 {
+ font-weight: bold;
+}
+
+.previews {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ margin: 0 -10px;
+
+ .preview-item {
+ flex-grow: 1;
+ margin: 10px;
+ padding: 25px;
+ }
+}
+input[type='radio'] {
+ display: none;
+}
+
+.flex-row {
+ display: flex;
+}
+
+form {
+ label {
+ color: var(--color-text-maxcontrast);
+ }
+}
+</style>
diff --git a/src/main-settings.js b/src/main-settings.js
new file mode 100644
index 000000000..17c222193
--- /dev/null
+++ b/src/main-settings.js
@@ -0,0 +1,40 @@
+/*
+ * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2019 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/>.
+ */
+
+import {generateFilePath} from '@nextcloud/router'
+import {getRequestToken} from '@nextcloud/auth'
+import {loadState} from '@nextcloud/initial-state'
+import Vue from 'vue'
+
+import AdminSettings from './components/settings/AdminSettings'
+import Nextcloud from './mixins/Nextcloud'
+
+__webpack_nonce__ = btoa(getRequestToken())
+__webpack_public_path__ = generateFilePath('mail', '', 'js/')
+
+Vue.mixin(Nextcloud)
+
+const View = Vue.extend(AdminSettings)
+new View({
+ propsData: {
+ provisioningSettings: loadState('mail', 'provisioning_settings') || {},
+ },
+}).$mount('#mail-admin-settings')
diff --git a/src/main.js b/src/main.js
index b80824a8b..064e04a35 100644
--- a/src/main.js
+++ b/src/main.js
@@ -21,16 +21,16 @@
*/
import Vue from 'vue'
-import App from './App'
import {getRequestToken} from '@nextcloud/auth'
-import router from './router'
-import store from './store'
import {sync} from 'vuex-router-sync'
-import {translate, translatePlural} from '@nextcloud/l10n'
import {generateFilePath} from '@nextcloud/router'
import VueShortKey from 'vue-shortkey'
import VTooltip from 'v-tooltip'
+import App from './App'
+import Nextcloud from './mixins/Nextcloud'
+import router from './router'
+import store from './store'
import {fixAccountId} from './service/AccountService'
__webpack_nonce__ = btoa(getRequestToken())
@@ -38,12 +38,7 @@ __webpack_public_path__ = generateFilePath('mail', '', 'js/')
sync(store, router)
-Vue.mixin({
- methods: {
- t: translate,
- n: translatePlural,
- },
-})
+Vue.mixin(Nextcloud)
Vue.use(VueShortKey, {prevent: ['input', 'div']})
Vue.use(VTooltip)
diff --git a/src/mixins/Nextcloud.js b/src/mixins/Nextcloud.js
new file mode 100644
index 000000000..2b676c866
--- /dev/null
+++ b/src/mixins/Nextcloud.js
@@ -0,0 +1,29 @@
+/*
+ * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2019 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/>.
+ */
+
+import {translate as t, translatePlural as n} from '@nextcloud/l10n'
+
+export default {
+ methods: {
+ t,
+ n,
+ },
+}
diff --git a/src/service/SettingsService.js b/src/service/SettingsService.js
new file mode 100644
index 000000000..e83cc3bf2
--- /dev/null
+++ b/src/service/SettingsService.js
@@ -0,0 +1,35 @@
+/*
+ * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2019 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/>.
+ */
+
+import axios from '@nextcloud/axios'
+import {generateUrl} from '@nextcloud/router'
+
+export const saveProvisioningSettings = config => {
+ const url = generateUrl('/apps/mail/api/settings/provisioning')
+
+ return axios.post(url, config).then(resp => resp.data)
+}
+
+export const disableProvisioning = () => {
+ const url = generateUrl('/apps/mail/api/settings/provisioning')
+
+ return axios.delete(url).then(resp => resp.data)
+}
diff --git a/src/views/AccountSettings.vue b/src/views/AccountSettings.vue
index 4a3266c0c..9ddd15c88 100644
--- a/src/views/AccountSettings.vue
+++ b/src/views/AccountSettings.vue
@@ -6,12 +6,17 @@
<h2>{{ t('mail', 'Account settings') }}</h2>
<p>
<strong>{{ displayName }}</strong> &lt;{{ email }}&gt;
- <a class="button icon-rename" href="#account-form" :title="t('mail', 'Change name')"></a>
+ <a
+ v-if="!account.provisioned"
+ class="button icon-rename"
+ href="#account-form"
+ :title="t('mail', 'Change name')"
+ ></a>
</p>
</div>
<SignatureSettings :account="account" />
<EditorSettings :account="account" />
- <div class="section">
+ <div v-if="!account.provisioned" class="section">
<h2>{{ t('mail', 'Mail server') }}</h2>
<div id="mail-settings">
<AccountForm
diff --git a/templates/settings-admin.php b/templates/settings-admin.php
new file mode 100644
index 000000000..ca9b2feb1
--- /dev/null
+++ b/templates/settings-admin.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2019 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/>.
+ */
+
+script(\OCA\Mail\AppInfo\Application::APP_ID, 'settings');
+
+?>
+<div id="mail-admin-settings">
+</div>
diff --git a/tests/Integration/Service/MailTransmissionIntegrationTest.php b/tests/Integration/Service/MailTransmissionIntegrationTest.php
index 8c7cd1837..3261ef44e 100644
--- a/tests/Integration/Service/MailTransmissionIntegrationTest.php
+++ b/tests/Integration/Service/MailTransmissionIntegrationTest.php
@@ -64,12 +64,14 @@ class MailTransmissionIntegrationTest extends TestCase {
parent::setUp();
$this->resetImapAccount();
+ $this->user = $this->createTestUser();
/** @var ICrypto $crypo */
$crypo = OC::$server->getCrypto();
/** @var MailAccountMapper $mapper */
$mapper = OC::$server->query(MailAccountMapper::class);
$mailAccount = MailAccount::fromParams([
+ 'userId' => $this->user->getUID(),
'email' => 'user@domain.tld',
'inboundHost' => 'localhost',
'inboundPort' => '993',
@@ -86,7 +88,6 @@ class MailTransmissionIntegrationTest extends TestCase {
$this->account = new Account($mailAccount);
$this->attachmentService = OC::$server->query(IAttachmentService::class);
- $this->user = $this->createTestUser();
$userFolder = OC::$server->getUserFolder($this->user->getUID());
$this->transmission = new MailTransmission(
$userFolder,
diff --git a/tests/Unit/Controller/SettingsControllerTest.php b/tests/Unit/Controller/SettingsControllerTest.php
new file mode 100644
index 000000000..4e3280c11
--- /dev/null
+++ b/tests/Unit/Controller/SettingsControllerTest.php
@@ -0,0 +1,86 @@
+<?php declare(strict_types=1);
+
+/**
+ * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2019 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\Tests\Unit\Controller;
+
+use ChristophWurst\Nextcloud\Testing\ServiceMockObject;
+use OCA\Mail\Controller\SettingsController;
+use OCA\Mail\Tests\Integration\TestCase;
+use OCP\AppFramework\Http\JSONResponse;
+
+class SettingsControllerTest extends TestCase {
+
+ /** @var ServiceMockObject */
+ private $mock;
+
+ /** @var SettingsController */
+ private $controller;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->mock = $this->createServiceMock(SettingsController::class);
+ $this->controller = $this->mock->getService();
+ }
+
+ public function testProvisioning() {
+ $this->mock->getParameter('provisioningManager')
+ ->expects($this->once())
+ ->method('newProvisioning')
+ ->with(
+ '%USERID%@domain.com',
+ '%USERID%@domain.com',
+ 'mx.domain.com',
+ 993,
+ 'ssl',
+ '%USERID%@domain.com',
+ 'mx.domain.com',
+ 567,
+ 'tls'
+ );
+
+ $response = $this->controller->provisioning(
+ '%USERID%@domain.com',
+ '%USERID%@domain.com',
+ 'mx.domain.com',
+ 993,
+ 'ssl',
+ '%USERID%@domain.com',
+ 'mx.domain.com',
+ 567,
+ 'tls'
+ );
+
+ $this->assertInstanceOf(JSONResponse::class, $response);
+ }
+
+ public function testDeprovision() {
+ $this->mock->getParameter('provisioningManager')
+ ->expects($this->once())
+ ->method('deprovision');
+
+ $response = $this->controller->deprovision();
+
+ $this->assertInstanceOf(JSONResponse::class, $response);
+ }
+}
diff --git a/tests/Unit/Db/MailAccountTest.php b/tests/Unit/Db/MailAccountTest.php
index 3c6d01558..a4d1d7fcc 100644
--- a/tests/Unit/Db/MailAccountTest.php
+++ b/tests/Unit/Db/MailAccountTest.php
@@ -42,6 +42,7 @@ class MailAccountTest extends TestCase {
$a->setOutboundPassword('xxxx');
$a->setOutboundSslMode('ssl');
$a->setEditorMode('html');
+ $a->setProvisioned(false);
$this->assertEquals(array(
'accountId' => 12345,
@@ -57,6 +58,7 @@ class MailAccountTest extends TestCase {
'smtpSslMode' => 'ssl',
'signature' => null,
'editorMode' => 'html',
+ 'provisioned' => false,
), $a->toJson());
}
@@ -75,6 +77,7 @@ class MailAccountTest extends TestCase {
'smtpSslMode' => 'ssl',
'signature' => null,
'editorMode' => null,
+ 'provisioned' => false,
];
$a = new MailAccount($expected);
// TODO: fix inconsistency
diff --git a/tests/Unit/Http/Middleware/ProvisioningMiddlewareTest.php b/tests/Unit/Http/Middleware/ProvisioningMiddlewareTest.php
new file mode 100644
index 000000000..1fac60021
--- /dev/null
+++ b/tests/Unit/Http/Middleware/ProvisioningMiddlewareTest.php
@@ -0,0 +1,147 @@
+<?php declare(strict_types=1);
+
+/**
+ * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2019 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\Tests\Unit\Http\Middleware;
+
+use ChristophWurst\Nextcloud\Testing\TestCase;
+use OCA\Mail\Controller\PageController;
+use OCA\Mail\Http\Middleware\ProvisioningMiddleware;
+use OCA\Mail\Service\Provisioning\Manager;
+use OCP\Authentication\Exceptions\CredentialsUnavailableException;
+use OCP\Authentication\Exceptions\PasswordUnavailableException;
+use OCP\Authentication\LoginCredentials\ICredentials;
+use OCP\Authentication\LoginCredentials\IStore;
+use OCP\ILogger;
+use OCP\IUser;
+use OCP\IUserSession;
+use PHPUnit\Framework\MockObject\MockObject;
+
+class ProvisioningMiddlewareTest extends TestCase {
+
+ /** @var IUserSession|MockObject */
+ private $userSession;
+
+ /** @var IStore|MockObject */
+ private $credentialStore;
+
+ /** @var Manager|MockObject */
+ private $provisioningManager;
+
+ /** @var ILogger|MockObject */
+ private $logger;
+
+ /** @var ProvisioningMiddleware */
+ private $middleware;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->userSession = $this->createMock(IUserSession::class);
+ $this->credentialStore = $this->createMock(IStore::class);
+ $this->provisioningManager = $this->createMock(Manager::class);
+ $this->logger = $this->createMock(ILogger::class);
+
+ $this->middleware = new ProvisioningMiddleware(
+ $this->userSession,
+ $this->credentialStore,
+ $this->provisioningManager,
+ $this->logger
+ );
+ }
+
+ public function testBeforeControllerNotLoggedIn() {
+ $this->credentialStore->expects($this->never())
+ ->method('getLoginCredentials');
+ $this->provisioningManager->expects($this->never())
+ ->method('updatePassword');
+
+ $this->middleware->beforeController(
+ $this->createMock(PageController::class),
+ 'index'
+ );
+ }
+
+ public function testBeforeControllerNoCredentialsAvailable() {
+ $user = $this->createMock(IUser::class);
+ $this->userSession->expects($this->once())
+ ->method('getUser')
+ ->willReturn($user);
+ $this->credentialStore->expects($this->once())
+ ->method('getLoginCredentials')
+ ->willThrowException($this->createMock(CredentialsUnavailableException::class));
+ $this->provisioningManager->expects($this->never())
+ ->method('updatePassword');
+
+ $this->middleware->beforeController(
+ $this->createMock(PageController::class),
+ 'index'
+ );
+ }
+
+ public function testBeforeControllerNoPasswordAvailable() {
+ $user = $this->createMock(IUser::class);
+ $this->userSession->expects($this->once())
+ ->method('getUser')
+ ->willReturn($user);
+ $credentials = $this->createMock(ICredentials::class);
+ $this->credentialStore->expects($this->once())
+ ->method('getLoginCredentials')
+ ->willReturn($credentials);
+ $credentials->expects($this->once())
+ ->method('getPassword')
+ ->willThrowException($this->createMock(PasswordUnavailableException::class));
+ $this->provisioningManager->expects($this->never())
+ ->method('updatePassword');
+
+ $this->middleware->beforeController(
+ $this->createMock(PageController::class),
+ 'index'
+ );
+ }
+
+ public function testBeforeController() {
+ $user = $this->createMock(IUser::class);
+ $this->userSession->expects($this->once())
+ ->method('getUser')
+ ->willReturn($user);
+ $credentials = $this->createMock(ICredentials::class);
+ $this->credentialStore->expects($this->once())
+ ->method('getLoginCredentials')
+ ->willReturn($credentials);
+ $credentials->expects($this->once())
+ ->method('getPassword')
+ ->willReturn('123456');
+ $this->provisioningManager->expects($this->once())
+ ->method('updatePassword')
+ ->with(
+ $user,
+ '123456'
+ );
+
+ $this->middleware->beforeController(
+ $this->createMock(PageController::class),
+ 'index'
+ );
+ }
+
+}
diff --git a/tests/Unit/Migration/MigrateProvisioningConfigTest.php b/tests/Unit/Migration/MigrateProvisioningConfigTest.php
new file mode 100644
index 000000000..b75d1bc06
--- /dev/null
+++ b/tests/Unit/Migration/MigrateProvisioningConfigTest.php
@@ -0,0 +1,106 @@
+<?php declare(strict_types=1);
+
+/**
+ * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2019 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\Tests\Unit\Migration;
+
+use ChristophWurst\Nextcloud\Testing\ServiceMockObject;
+use ChristophWurst\Nextcloud\Testing\TestCase;
+use OCA\Mail\Migration\MigrateProvisioningConfig;
+use OCA\Mail\Service\Provisioning\Config;
+use OCP\Migration\IOutput;
+use PHPUnit\Framework\MockObject\MockObject;
+
+class MigrateProvisioningConfigTest extends TestCase {
+
+ /** @var ServiceMockObject */
+ private $mock;
+
+ /** @var MigrateProvisioningConfig */
+ private $repairStep;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->mock = $this->createServiceMock(MigrateProvisioningConfig::class);
+ $this->repairStep = $this->mock->getService();
+ }
+
+
+ public function testRunNoConfigToMigrate() {
+ /** @var IOutput|MockObject $output */
+ $output = $this->createMock(IOutput::class);
+ $this->mock->getParameter('config')
+ ->expects($this->once())
+ ->method('getSystemValue')
+ ->with('app.mail.accounts.default')
+ ->willReturn('');
+
+ $this->repairStep->run($output);
+ }
+
+ public function testRunAlreadyMigrated() {
+ /** @var IOutput|MockObject $output */
+ $output = $this->createMock(IOutput::class);
+ $this->mock->getParameter('config')
+ ->expects($this->once())
+ ->method('getSystemValue')
+ ->with('app.mail.accounts.default')
+ ->willReturn([]);
+ $this->mock->getParameter('provisioningManager')
+ ->expects($this->once())
+ ->method('getConfig')
+ ->willReturn($this->createMock(Config::class));
+
+ $this->repairStep->run($output);
+ }
+
+ public function testRun() {
+ /** @var IOutput|MockObject $output */
+ $output = $this->createMock(IOutput::class);
+ $this->mock->getParameter('config')
+ ->expects($this->once())
+ ->method('getSystemValue')
+ ->with('app.mail.accounts.default')
+ ->willReturn([]);
+ $this->mock->getParameter('provisioningManager')
+ ->expects($this->once())
+ ->method('getConfig')
+ ->willReturn(null);
+ $this->mock->getParameter('provisioningManager')
+ ->expects($this->once())
+ ->method('importConfig');
+ $this->mock->getParameter('config')
+ ->expects($this->once())
+ ->method('deleteSystemValue')
+ ->with('app.mail.accounts.default');
+
+ $this->repairStep->run($output);
+ }
+
+ public function testGetName() {
+ $name = $this->repairStep->getName();
+
+ $this->assertEquals('Migrate Mail provisioning config from config.php to the database', $name);
+ }
+
+}
diff --git a/tests/Unit/Service/AccountServiceTest.php b/tests/Unit/Service/AccountServiceTest.php
index 887659b21..a07be412a 100644
--- a/tests/Unit/Service/AccountServiceTest.php
+++ b/tests/Unit/Service/AccountServiceTest.php
@@ -27,44 +27,42 @@ use OCA\Mail\Db\MailAccount;
use OCA\Mail\Db\MailAccountMapper;
use OCA\Mail\Service\AccountService;
use OCA\Mail\Service\AliasesService;
-use OCA\Mail\Service\DefaultAccount\Manager;
use OCP\IL10N;
-use PHPUnit_Framework_MockObject_MockObject;
+use PHPUnit\Framework\MockObject\MockObject;
class AccountServiceTest extends TestCase {
/** @var string */
private $user = 'herbert';
- /** @var MailAccountMapper|PHPUnit_Framework_MockObject_MockObject */
+ /** @var MailAccountMapper|MockObject */
private $mapper;
- /** @var IL10N|PHPUnit_Framework_MockObject_MockObject */
+ /** @var IL10N|MockObject */
private $l10n;
- /** @var AccountService|PHPUnit_Framework_MockObject_MockObject */
+ /** @var AccountService|MockObject */
private $accountService;
- /** @var AliasesService|PHPUnit_Framework_MockObject_MockObject */
+ /** @var AliasesService|MockObject */
private $aliasesService;
- /** @var MailAccount|PHPUnit_Framework_MockObject_MockObject */
+ /** @var MailAccount|MockObject */
private $account1;
- /** @var MailAccount|PHPUnit_Framework_MockObject_MockObject */
+ /** @var MailAccount|MockObject */
private $account2;
- /** @var Manager|PHPUnit_Framework_MockObject_MockObject */
- private $defaultAccountManager;
-
protected function setUp(): void {
parent::setUp();
$this->mapper = $this->createMock(MailAccountMapper::class);
$this->l10n = $this->createMock(IL10N::class);
- $this->defaultAccountManager = $this->createMock(Manager::class);
$this->aliasesService = $this->createMock(AliasesService::class);
- $this->accountService = new AccountService($this->mapper, $this->defaultAccountManager, $this->aliasesService);
+ $this->accountService = new AccountService(
+ $this->mapper,
+ $this->aliasesService
+ );
$this->account1 = $this->createMock(MailAccount::class);
$this->account2 = $this->createMock(MailAccount::class);
diff --git a/tests/Unit/Service/DefaultAccount/ManagerTest.php b/tests/Unit/Service/DefaultAccount/ManagerTest.php
deleted file mode 100644
index 4e2ab7bbb..000000000
--- a/tests/Unit/Service/DefaultAccount/ManagerTest.php
+++ /dev/null
@@ -1,154 +0,0 @@
-<?php
-
-/**
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- *
- * Mail
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
- */
-
-namespace OCA\Mail\Tests\Unit\Service\DefaultAccount;
-
-use ChristophWurst\Nextcloud\Testing\TestCase;
-use OCA\Mail\Db\MailAccount;
-use OCA\Mail\Service\DefaultAccount\Manager;
-use OCP\Authentication\Exceptions\CredentialsUnavailableException;
-use OCP\Authentication\LoginCredentials\ICredentials;
-use OCP\Authentication\LoginCredentials\IStore;
-use OCP\IConfig;
-use OCP\ILogger;
-use OCP\IUser;
-use OCP\IUserSession;
-use OCP\Security\ICrypto;
-use PHPUnit_Framework_MockObject_MockObject;
-
-class ManagerTest extends TestCase {
-
- /** @var IConfig|PHPUnit_Framework_MockObject_MockObject */
- private $config;
-
- /** @var IStore|PHPUnit_Framework_MockObject_MockObject */
- private $credentialStore;
-
- /** @var ILogger|PHPUnit_Framework_MockObject_MockObject */
- private $logger;
-
- /** @var IUserSession|PHPUnit_Framework_MockObject_MockObject */
- private $userSession;
-
- /** @var ICrypto|PHPUnit_Framework_MockObject_MockObject */
- private $crypto;
-
- /** @var Manager|PHPUnit_Framework_MockObject_MockObject */
- private $manager;
-
- protected function setUp(): void {
- parent::setUp();
-
- $this->config = $this->createMock(IConfig::class);
- $this->credentialStore = $this->createMock(IStore::class);
- $this->logger = $this->createMock(ILogger::class);
- $this->userSession = $this->createMock(IUserSession::class);
- $this->crypto = $this->createMock(ICrypto::class);
-
- $this->manager = new Manager($this->config, $this->credentialStore, $this->logger, $this->userSession, $this->crypto);
- }
-
- public function testGetDefaultAccountWithoutConfigAvailble() {
- $this->config->expects($this->once())
- ->method('getSystemValue')
- ->with($this->equalTo('app.mail.accounts.default'), $this->equalTo(null))
- ->willReturn(null);
-
- $account = $this->manager->getDefaultAccount();
-
- $this->assertSame(null, $account);
- }
-
- public function testGetDefaultAccountWithCredentialsUnavailable() {
- $this->config->expects($this->once())
- ->method('getSystemValue')
- ->with($this->equalTo('app.mail.accounts.default'), $this->equalTo(null))
- ->willReturn([
- 'email' => '%EMAIL%',
- ]);
- $this->credentialStore->expects($this->once())
- ->method('getLoginCredentials')
- ->willThrowException(new CredentialsUnavailableException());
-
- $account = $this->manager->getDefaultAccount();
-
- $this->assertSame(null, $account);
- }
-
- public function testGetDefaultAccount() {
- $this->config->expects($this->once())
- ->method('getSystemValue')
- ->with($this->equalTo('app.mail.accounts.default'), $this->equalTo(null))
- ->willReturn([
- 'email' => '%EMAIL%',
- 'imapHost' => 'imap.domain.tld',
- 'imapPort' => 993,
- 'imapSslMode' => 'ssl',
- 'smtpHost' => 'smtp.domain.tld',
- 'smtpPort' => 465,
- 'smtpSslMode' => 'tls',
- ]);
- $credentials = $this->createMock(ICredentials::class);
- $user = $this->createMock(IUser::class);
- $this->userSession->expects($this->once())
- ->method('getUser')
- ->willReturn($user);
- $this->credentialStore->expects($this->once())
- ->method('getLoginCredentials')
- ->willReturn($credentials);
- $credentials->expects($this->once())
- ->method('getPassword')
- ->willReturn('123456');
- $this->crypto->expects($this->once())
- ->method('encrypt')
- ->with($this->equalTo('123456'))
- ->willReturn('encrypted');
- $expected = new MailAccount();
- $expected->setId(Manager::ACCOUNT_ID);
- $user->expects($this->any())
- ->method('getUID')
- ->willReturn('user123');
- $user->expects($this->any())
- ->method('getEMailAddress')
- ->willReturn('user@domain.tld');
- $user->expects($this->once())
- ->method('getDisplayName')
- ->willReturn('Test User');
- $expected->setUserId('user123');
- $expected->setEmail('user@domain.tld');
- $expected->setName('Test User');
- $expected->setInboundUser('user@domain.tld');
- $expected->setInboundHost('imap.domain.tld');
- $expected->setInboundPort(993);
- $expected->setInboundSslMode('ssl');
- $expected->setInboundPassword('encrypted');
- $expected->setOutboundUser('user@domain.tld');
- $expected->setOutboundHost('smtp.domain.tld');
- $expected->setOutboundPort(465);
- $expected->setOutboundSslMode('tls');
- $expected->setOutboundPassword('encrypted');
-
- $account = $this->manager->getDefaultAccount();
-
- $this->assertEquals($expected, $account);
- }
-
-}
diff --git a/tests/Unit/Service/Provisioning/ConfigMapperTest.php b/tests/Unit/Service/Provisioning/ConfigMapperTest.php
new file mode 100644
index 000000000..45e9090cd
--- /dev/null
+++ b/tests/Unit/Service/Provisioning/ConfigMapperTest.php
@@ -0,0 +1,85 @@
+<?php declare(strict_types=1);
+
+/**
+ * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2019 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\Tests\Unit\Service\Provisioning;
+
+use ChristophWurst\Nextcloud\Testing\ServiceMockObject;
+use ChristophWurst\Nextcloud\Testing\TestCase;
+use OCA\Mail\Service\Provisioning\Config;
+use OCA\Mail\Service\Provisioning\ConfigMapper;
+use PHPUnit\Framework\MockObject\MockObject;
+
+class ConfigMapperTest extends TestCase {
+
+ /** @var ServiceMockObject */
+ private $mock;
+
+ /** @var ConfigMapper */
+ private $mapper;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->mock = $this->createServiceMock(ConfigMapper::class);
+ $this->mapper = $this->mock->getService();
+ }
+
+ public function testSave() {
+ /** @var Config|MockObject $config */
+ $config = $this->createMock(Config::class);
+ $config->expects($this->once())
+ ->method('jsonSerialize')
+ ->willReturn([]);
+ $this->mock->getParameter('config')
+ ->expects($this->once())
+ ->method('setAppValue')
+ ->with('mail', 'provisioning_settings', '[]');
+
+ $this->mapper->save($config);
+ }
+
+ public function testLoadNoConfig() {
+ $this->mock->getParameter('config')
+ ->expects($this->once())
+ ->method('getAppValue')
+ ->with('mail', 'provisioning_settings')
+ ->willReturn('');
+
+ $config = $this->mapper->load();
+
+ $this->assertNull($config);
+ }
+
+ public function testLoad() {
+ $this->mock->getParameter('config')
+ ->expects($this->once())
+ ->method('getAppValue')
+ ->with('mail', 'provisioning_settings')
+ ->willReturn('[]');
+
+ $config = $this->mapper->load();
+
+ $this->assertInstanceOf(Config::class, $config);
+ }
+
+}
diff --git a/tests/Unit/Service/DefaultAccount/ConfigTest.php b/tests/Unit/Service/Provisioning/ConfigTest.php
index a05a4d26b..064843780 100644
--- a/tests/Unit/Service/DefaultAccount/ConfigTest.php
+++ b/tests/Unit/Service/Provisioning/ConfigTest.php
@@ -19,10 +19,10 @@
*
*/
-namespace OCA\Mail\Tests\Unit\Service\DefaultAccount;
+namespace OCA\Mail\Tests\Unit\Service\Provisioning;
use ChristophWurst\Nextcloud\Testing\TestCase;
-use OCA\Mail\Service\DefaultAccount\Config;
+use OCA\Mail\Service\Provisioning\Config;
use OCP\IUser;
class ConfigTest extends TestCase {
diff --git a/tests/Unit/Service/Provisioning/ManagerTest.php b/tests/Unit/Service/Provisioning/ManagerTest.php
new file mode 100644
index 000000000..66b0accfc
--- /dev/null
+++ b/tests/Unit/Service/Provisioning/ManagerTest.php
@@ -0,0 +1,184 @@
+<?php
+/**
+ * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2019 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\Tests\Unit\Service\Provisioning;
+
+use ChristophWurst\Nextcloud\Testing\ServiceMockObject;
+use ChristophWurst\Nextcloud\Testing\TestCase;
+use OCA\Mail\Db\MailAccount;
+use OCA\Mail\Service\Provisioning\Config;
+use OCA\Mail\Service\Provisioning\Manager;
+use OCP\AppFramework\Db\DoesNotExistException;
+use OCP\IUser;
+use PHPUnit\Framework\MockObject\MockObject;
+
+class ManagerTest extends TestCase {
+
+ /** @var ServiceMockObject */
+ private $mock;
+
+ /** @var Manager */
+ private $manager;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->mock = $this->createServiceMock(Manager::class);
+ $this->manager = $this->mock->getService();
+ }
+
+ public function testProvision() {
+ $config = new TestConfig();
+ $this->mock->getParameter('userManager')
+ ->expects($this->once())
+ ->method('callForAllUsers');
+
+ $cnt = $this->manager->provision($config);
+
+ $this->assertEquals(0, $cnt);
+ }
+
+ public function testUpdateProvisionSingleUser() {
+ /** @var IUser|MockObject $user */
+ $user = $this->createMock(IUser::class);
+ $config = new TestConfig();
+ $account = $this->createMock(MailAccount::class);
+ $this->mock->getParameter('mailAccountMapper')
+ ->expects($this->once())
+ ->method('findProvisionedAccount')
+ ->willReturn($account);
+ $this->mock->getParameter('mailAccountMapper')
+ ->expects($this->once())
+ ->method('update')
+ ->with($account);
+
+ $this->manager->provisionSingleUser($config, $user);
+ }
+
+ public function testProvisionSingleUser() {
+ /** @var IUser|MockObject $user */
+ $user = $this->createMock(IUser::class);
+ $config = new TestConfig();
+ $this->mock->getParameter('mailAccountMapper')
+ ->expects($this->once())
+ ->method('findProvisionedAccount')
+ ->willThrowException($this->createMock(DoesNotExistException::class));
+ $this->mock->getParameter('mailAccountMapper')
+ ->expects($this->once())
+ ->method('insert');
+
+ $this->manager->provisionSingleUser($config, $user);
+ }
+
+ public function testGetNoConfig() {
+ $config = $this->manager->getConfig();
+
+ $this->assertNull($config);
+ }
+
+ public function testGetConfig() {
+ $config = $this->createMock(Config::class);
+ $this->mock->getParameter('configMapper')
+ ->expects($this->once())
+ ->method('load')
+ ->willReturn($config);
+
+ $cfg = $this->manager->getConfig();
+
+ $this->assertSame($config, $cfg);
+ }
+
+ public function testDeprovision() {
+ $config = new TestConfig();
+ $this->mock->getParameter('mailAccountMapper')
+ ->expects($this->once())
+ ->method('deleteProvisionedAccounts');
+ $this->mock->getParameter('configMapper')
+ ->expects($this->once())
+ ->method('load')
+ ->willReturn($config);
+ $this->mock->getParameter('configMapper')
+ ->expects($this->once())
+ ->method('save')
+ ->willReturn($config);
+
+ $this->manager->deprovision();
+
+ $this->assertEquals(false, $config->jsonSerialize()['active']);
+ }
+
+ public function testImportConfig() {
+ $this->mock->getParameter('configMapper')
+ ->expects($this->once())
+ ->method('save');
+
+ $this->manager->importConfig([
+ 'email' => '%USERID%@domain.com',
+ ]);
+ }
+
+ public function testUpdatePasswordNotProvisioned() {
+ /** @var IUser|MockObject $user */
+ $user = $this->createMock(IUser::class);
+ $this->mock->getParameter('mailAccountMapper')
+ ->expects($this->once())
+ ->method('findProvisionedAccount')
+ ->with($user)
+ ->willThrowException($this->createMock(DoesNotExistException::class));
+
+ $this->manager->updatePassword($user, '123456');
+ }
+
+ public function testUpdatePassword() {
+ /** @var IUser|MockObject $user */
+ $user = $this->createMock(IUser::class);
+ $account = $this->createMock(MailAccount::class);
+ $this->mock->getParameter('mailAccountMapper')
+ ->expects($this->once())
+ ->method('findProvisionedAccount')
+ ->willReturn($account);
+ $this->mock->getParameter('mailAccountMapper')
+ ->expects($this->once())
+ ->method('update')
+ ->with($account);
+
+ $this->manager->updatePassword($user, '123456');
+ }
+
+ public function testNewProvisioning() {
+ $this->mock->getParameter('configMapper')
+ ->expects($this->once())
+ ->method('save');
+
+ $this->manager->newProvisioning(
+ '%USERID%@domain.com',
+ '%USERID%@domain.com',
+ 'mx.domain.com',
+ 993,
+ 'ssl',
+ '%USERID%@domain.com',
+ 'mx.domain.com',
+ 567,
+ 'tls'
+ );
+ }
+}
diff --git a/tests/Unit/Service/Provisioning/TestConfig.php b/tests/Unit/Service/Provisioning/TestConfig.php
new file mode 100644
index 000000000..66357a724
--- /dev/null
+++ b/tests/Unit/Service/Provisioning/TestConfig.php
@@ -0,0 +1,44 @@
+<?php declare(strict_types=1);
+
+/**
+ * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2019 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\tests\Unit\Service\Provisioning;
+
+use OCA\Mail\Service\Provisioning\Config;
+
+class TestConfig extends Config {
+
+ public function __construct() {
+ parent::__construct([
+ 'email' => '%USERID%@domain.com',
+ 'imapUser' => '%USERID%@domain.com',
+ 'imapHost' => 'mx.domain.com',
+ 'imapPort' => 993,
+ 'imapSslMode' => 'ssl',
+ 'smtpUser' => '%USERID%@domain.com',
+ 'smtpHost' => 'mx.domain.com',
+ 'smtpPort' => 567,
+ 'smtpSslMode' => 'tls',
+ ]);
+ }
+
+}
diff --git a/tests/Unit/Settings/AdminSettingsTest.php b/tests/Unit/Settings/AdminSettingsTest.php
new file mode 100644
index 000000000..c2e038e6c
--- /dev/null
+++ b/tests/Unit/Settings/AdminSettingsTest.php
@@ -0,0 +1,74 @@
+<?php declare(strict_types=1);
+
+/**
+ * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2019 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\Tests\Unit\Settings;
+
+use ChristophWurst\Nextcloud\Testing\ServiceMockObject;
+use ChristophWurst\Nextcloud\Testing\TestCase;
+use OCA\Mail\AppInfo\Application;
+use OCA\Mail\Settings\AdminSettings;
+use OCP\AppFramework\Http\TemplateResponse;
+
+class AdminSettingsTest extends TestCase {
+
+ /** @var ServiceMockObject */
+ private $serviceMock;
+
+ /** @var AdminSettings */
+ private $settings;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->serviceMock = $this->createServiceMock(AdminSettings::class);
+
+ $this->settings = $this->serviceMock->getService();
+ }
+
+ public function testGetSection() {
+ $section = $this->settings->getSection();
+
+ $this->assertSame('groupware', $section);
+ }
+
+ public function testGetForm() {
+ $this->serviceMock->getParameter('initialStateService')->expects($this->once())
+ ->method('provideInitialState')
+ ->with(
+ Application::APP_ID,
+ 'provisioning_settings',
+ $this->anything()
+ );
+ $expected = new TemplateResponse(Application::APP_ID, 'settings-admin');
+
+ $form = $this->settings->getForm();
+
+ $this->assertEquals($expected, $form);
+ }
+
+ public function testGetPriority() {
+ $priority = $this->settings->getPriority();
+
+ $this->assertIsInt($priority);
+ }
+}
diff --git a/webpack.common.js b/webpack.common.js
index 28b61b7cd..42a0777c0 100644
--- a/webpack.common.js
+++ b/webpack.common.js
@@ -20,7 +20,8 @@ if (process.env.BUNDLE_ANALYZER_TOKEN) {
module.exports = {
entry: {
autoredirect: path.join(__dirname, 'src/autoredirect.js'),
- mail: path.join(__dirname, 'src/main.js')
+ mail: path.join(__dirname, 'src/main.js'),
+ settings: path.join(__dirname, 'src/main-settings')
},
output: {
path: path.resolve(__dirname, 'js'),