Skip to content

Commit 294181e

Browse files
Merge pull request #12134 from nextcloud/feat/user-migration
feat: support user migration
2 parents 9371db3 + 09b925e commit 294181e

File tree

14 files changed

+578
-25
lines changed

14 files changed

+578
-25
lines changed

lib/AppInfo/Application.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
use OCA\Mail\Service\UserPreferenceService;
6969
use OCA\Mail\SetupChecks\MailConnectionPerformance;
7070
use OCA\Mail\SetupChecks\MailTransport;
71+
use OCA\Mail\UserMigration\MailAccountMigrator;
7172
use OCA\Mail\Vendor\Favicon\Favicon;
7273
use OCP\AppFramework\App;
7374
use OCP\AppFramework\Bootstrap\IBootContext;
@@ -87,6 +88,9 @@
8788

8889
include_once __DIR__ . '/../../vendor/autoload.php';
8990

91+
/**
92+
* @codeCoverageIgnore
93+
*/
9094
final class Application extends App implements IBootstrap {
9195
public const APP_ID = 'mail';
9296

@@ -164,6 +168,8 @@ public function register(IRegistrationContext $context): void {
164168
$context->registerSetupCheck(MailTransport::class);
165169
$context->registerSetupCheck(MailConnectionPerformance::class);
166170

171+
$context->registerUserMigrator(MailAccountMigrator::class);
172+
167173
// bypass Horde Translation system
168174
Horde_Translation::setHandler('Horde_Imap_Client', new HordeTranslationHandler());
169175
Horde_Translation::setHandler('Horde_Mime', new HordeTranslationHandler());

lib/Controller/AccountsController.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
use OCP\AppFramework\Http;
3232
use OCP\AppFramework\Http\Attribute\OpenAPI;
3333
use OCP\AppFramework\Http\JSONResponse;
34+
use OCP\AppFramework\Utility\ITimeFactory;
3435
use OCP\IConfig;
3536
use OCP\IL10N;
3637
use OCP\IRequest;
@@ -52,7 +53,8 @@ class AccountsController extends Controller {
5253
private IRemoteHostValidator $hostValidator;
5354
private MailboxSync $mailboxSync;
5455

55-
public function __construct(string $appName,
56+
public function __construct(
57+
string $appName,
5658
IRequest $request,
5759
AccountService $accountService,
5860
$UserId,
@@ -66,6 +68,7 @@ public function __construct(string $appName,
6668
IConfig $config,
6769
IRemoteHostValidator $hostValidator,
6870
MailboxSync $mailboxSync,
71+
private ITimeFactory $timeFactory,
6972
) {
7073
parent::__construct($appName, $request);
7174
$this->accountService = $accountService;
@@ -386,6 +389,13 @@ public function create(string $accountName,
386389
}
387390
try {
388391
$account = $this->setup->createNewAccount($accountName, $emailAddress, $imapHost, $imapPort, $imapSslMode, $imapUser, $imapPassword, $smtpHost, $smtpPort, $smtpSslMode, $smtpUser, $smtpPassword, $this->currentUserId, $authMethod, null, $classificationEnabled);
392+
// Set initial heartbeat
393+
$this->config->setUserValue(
394+
$account->getUserId(),
395+
Application::APP_ID,
396+
'ui-heartbeat',
397+
(string)$this->timeFactory->getTime(),
398+
);
389399
} catch (CouldNotConnectException $e) {
390400
$data = [
391401
'error' => $e->getReason(),

lib/Db/MailAccount.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,12 +83,12 @@
8383
* @method void setAuthMethod(string $method)
8484
* @method int getSignatureMode()
8585
* @method void setSignatureMode(int $signatureMode)
86-
* @method string getOauthAccessToken()
86+
* @method string|null getOauthAccessToken()
8787
* @method void setOauthAccessToken(string $token)
88-
* @method string getOauthRefreshToken()
88+
* @method string|null getOauthRefreshToken()
8989
* @method void setOauthRefreshToken(string $token)
9090
* @method int|null getOauthTokenTtl()
91-
* @method void setOauthTokenTtl(int $ttl)
91+
* @method void setOauthTokenTtl(int|null $ttl)
9292
* @method int|null getSmimeCertificateId()
9393
* @method void setSmimeCertificateId(int|null $smimeCertificateId)
9494
* @method int|null getQuotaPercentage()
@@ -302,6 +302,7 @@ public function toJson() {
302302
'name' => $this->getName(),
303303
'order' => $this->getOrder(),
304304
'emailAddress' => $this->getEmail(),
305+
'authMethod' => $this->getAuthMethod() ?? 'password',
305306
'imapHost' => $this->getInboundHost(),
306307
'imapPort' => $this->getInboundPort(),
307308
'imapUser' => $this->getInboundUser(),

lib/IMAP/IMAPClientFactory.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,13 @@ public function getClient(Account $account, bool $useCache = true): Horde_Imap_C
101101
];
102102
if ($account->getMailAccount()->getAuthMethod() === 'xoauth2') {
103103
try {
104-
$decryptedAccessToken = $this->crypto->decrypt($account->getMailAccount()->getOauthAccessToken());
104+
$oauthAccessToken = $account->getMailAccount()->getOauthAccessToken();
105+
if ($oauthAccessToken === null) {
106+
throw new ServiceException('Missing access token for xoauth2 account');
107+
}
108+
$decryptedAccessToken = $this->crypto->decrypt($oauthAccessToken);
105109
} catch (Exception $e) {
106-
throw new ServiceException('Could not decrypt account password: ' . $e->getMessage(), 0, $e);
110+
throw new ServiceException('Could not decrypt account access token: ' . $e->getMessage(), 0, $e);
107111
}
108112

109113
$params['password'] = $decryptedAccessToken; // Not used, but Horde wants this

lib/Integration/GoogleIntegration.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,8 @@ public function finishConnect(Account $account,
119119
}
120120

121121
public function refresh(Account $account): Account {
122-
if ($account->getMailAccount()->getOauthTokenTtl() === null || $account->getMailAccount()->getOauthRefreshToken() === null) {
122+
$oauthRefreshToken = $account->getMailAccount()->getOauthRefreshToken();
123+
if ($account->getMailAccount()->getOauthTokenTtl() === null || $oauthRefreshToken === null) {
123124
// Account is not authorized yet
124125
return $account;
125126
}
@@ -137,7 +138,7 @@ public function refresh(Account $account): Account {
137138
return $account;
138139
}
139140

140-
$refreshToken = $this->crypto->decrypt($account->getMailAccount()->getOauthRefreshToken());
141+
$refreshToken = $this->crypto->decrypt($oauthRefreshToken);
141142
$clientSecret = $this->crypto->decrypt($encryptedClientSecret);
142143
$httpClient = $this->clientService->newClient();
143144
try {

lib/Integration/MicrosoftIntegration.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,8 @@ public function finishConnect(Account $account,
133133
}
134134

135135
public function refresh(Account $account): Account {
136-
if ($account->getMailAccount()->getOauthTokenTtl() === null || $account->getMailAccount()->getOauthRefreshToken() === null) {
136+
$oauthRefreshToken = $account->getMailAccount()->getOauthRefreshToken();
137+
if ($account->getMailAccount()->getOauthTokenTtl() === null || $oauthRefreshToken === null) {
137138
// Account is not authorized yet
138139
return $account;
139140
}
@@ -152,7 +153,7 @@ public function refresh(Account $account): Account {
152153
return $account;
153154
}
154155

155-
$refreshToken = $this->crypto->decrypt($account->getMailAccount()->getOauthRefreshToken());
156+
$refreshToken = $this->crypto->decrypt($oauthRefreshToken);
156157
$clientSecret = $this->crypto->decrypt($encryptedClientSecret);
157158
$httpClient = $this->clientService->newClient();
158159
try {

lib/SMTP/SmtpClientFactory.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@
99

1010
namespace OCA\Mail\SMTP;
1111

12+
use Exception;
1213
use Horde_Mail_Transport;
1314
use Horde_Mail_Transport_Smtphorde;
1415
use Horde_Smtp_Password_Xoauth2;
1516
use OCA\Mail\Account;
17+
use OCA\Mail\Exception\ServiceException;
1618
use OCA\Mail\Support\HostNameFactory;
1719
use OCP\IConfig;
1820
use OCP\Security\ICrypto;
@@ -64,7 +66,15 @@ public function create(Account $account): Horde_Mail_Transport {
6466
],
6567
];
6668
if ($account->getMailAccount()->getAuthMethod() === 'xoauth2') {
67-
$decryptedAccessToken = $this->crypto->decrypt($account->getMailAccount()->getOauthAccessToken());
69+
try {
70+
$oauthAccessToken = $account->getMailAccount()->getOauthAccessToken();
71+
if ($oauthAccessToken === null) {
72+
throw new ServiceException('Missing access token for xoauth2 account');
73+
}
74+
$decryptedAccessToken = $this->crypto->decrypt($oauthAccessToken);
75+
} catch (Exception $e) {
76+
throw new ServiceException('Could not decrypt account access token: ' . $e->getMessage(), 0, $e);
77+
}
6878

6979
$params['password'] = $decryptedAccessToken; // Not used, but Horde wants this
7080
$params['xoauth2_token'] = new Horde_Smtp_Password_Xoauth2(

lib/Service/AccountService.php

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
namespace OCA\Mail\Service;
1212

1313
use OCA\Mail\Account;
14-
use OCA\Mail\AppInfo\Application;
1514
use OCA\Mail\BackgroundJob\PreviewEnhancementProcessingJob;
1615
use OCA\Mail\BackgroundJob\QuotaJob;
1716
use OCA\Mail\BackgroundJob\RepairSyncJob;
@@ -176,14 +175,6 @@ public function save(MailAccount $newAccount): MailAccount {
176175
// Insert background jobs for this account
177176
$this->scheduleBackgroundJobs($newAccount->getId());
178177

179-
// Set initial heartbeat
180-
$this->config->setUserValue(
181-
$newAccount->getUserId(),
182-
Application::APP_ID,
183-
'ui-heartbeat',
184-
(string)$this->timeFactory->getTime(),
185-
);
186-
187178
return $newAccount;
188179
}
189180

0 commit comments

Comments
 (0)