Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117794805
D5098.1775261397.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
10 KB
Referenced Files
None
Subscribers
None
D5098.1775261397.diff
View Options
diff --git a/src/app/DataMigrator/Driver/DAV.php b/src/app/DataMigrator/Driver/DAV.php
--- a/src/app/DataMigrator/Driver/DAV.php
+++ b/src/app/DataMigrator/Driver/DAV.php
@@ -39,6 +39,8 @@
*/
public function __construct(Account $account, Engine $engine)
{
+ // Note: This user impersonation method works only with Kolab v3 iRony when kolab_auth_proxy plugin is enabled.
+ // For Kolab v4 we need another approach - just use the kolab:// scheme for this.
$username = $account->username . ($account->loginas ? "**{$account->loginas}" : '');
$baseUri = preg_replace('|^dav|', 'http', $account->uri);
diff --git a/src/app/DataMigrator/Driver/IMAP.php b/src/app/DataMigrator/Driver/IMAP.php
--- a/src/app/DataMigrator/Driver/IMAP.php
+++ b/src/app/DataMigrator/Driver/IMAP.php
@@ -19,9 +19,12 @@
/** @const int Max number of items to migrate in one go */
protected const CHUNK_SIZE = 100;
- /** @var \rcube_imap_generic Imap backend */
+ /** @var ?\rcube_imap_generic Imap backend */
protected $imap;
+ /** @var array IMAP configuration */
+ protected $config;
+
/** @var Account Account to operate on */
protected $account;
@@ -36,10 +39,7 @@
{
$this->account = $account;
$this->engine = $engine;
-
- // TODO: Move this to self::authenticate()?
- $config = self::getConfig($account);
- $this->imap = static::initIMAP($config);
+ $this->config = self::getConfig($account);
}
/**
@@ -47,6 +47,10 @@
*/
public function __destruct()
{
+ if (!$this->imap) {
+ return;
+ }
+
try {
$this->imap->closeConnection();
} catch (\Throwable $e) {
@@ -60,6 +64,7 @@
*/
public function authenticate(): void
{
+ $this->initIMAP();
}
/**
@@ -71,6 +76,8 @@
*/
public function createFolder(Folder $folder): void
{
+ $this->initIMAP();
+
if ($folder->type != 'mail') {
throw new \Exception("IMAP does not support folder of type {$folder->type}");
}
@@ -122,6 +129,8 @@
*/
public function createItem(Item $item): void
{
+ $this->initIMAP();
+
$mailbox = self::toUTF7($item->folder->targetname);
if (strlen($item->content)) {
@@ -180,6 +189,8 @@
*/
public function fetchFolder(Folder $folder): void
{
+ $this->initIMAP();
+
$mailbox = self::toUTF7($folder->targetname);
// Get folder ACL
@@ -203,6 +214,8 @@
*/
public function fetchItem(Item $item): void
{
+ $this->initIMAP();
+
[$uid, $messageId] = explode(':', $item->id, 2);
$mailbox = self::toUTF7($item->folder->fullname);
@@ -269,6 +282,8 @@
*/
public function fetchItemList(Folder $folder, $callback, ImporterInterface $importer): void
{
+ $this->initIMAP();
+
// Get existing messages' headers from the destination mailbox
$existing = $importer->getItems($folder);
@@ -331,6 +346,8 @@
*/
public function getFolders($types = []): array
{
+ $this->initIMAP();
+
$folders = $this->imap->listMailboxes('', '');
if ($folders === false) {
@@ -367,6 +384,8 @@
*/
public function getItems(Folder $folder): array
{
+ $this->initIMAP();
+
$mailbox = self::toUTF7($folder->targetname ? $folder->targetname : $folder->fullname);
// TODO: We should probably first use SEARCH/SORT to skip messages marked as \Deleted
@@ -401,25 +420,37 @@
/**
* Initialize IMAP connection and authenticate the user
*/
- protected static function initIMAP(array $config): \rcube_imap_generic
+ protected function initIMAP(): void
{
- $imap = new \rcube_imap_generic();
+ if ($this->imap) {
+ return;
+ }
+
+ $this->imap = new \rcube_imap_generic();
if (\config('app.debug')) {
- $imap->setDebug(true, 'App\Backends\IMAP::logDebug');
+ $this->imap->setDebug(true, 'App\Backends\IMAP::logDebug');
}
- $imap->connect($config['host'], $config['user'], $config['password'], $config['options']);
+ $this->imap->connect(
+ $this->config['host'],
+ $this->config['user'],
+ $this->config['password'],
+ $this->config['options']
+ );
- if (!$imap->connected()) {
- $message = sprintf("Login failed for %s against %s. %s", $config['user'], $config['host'], $imap->error);
+ if (!$this->imap->connected()) {
+ $message = sprintf(
+ "Login failed for %s against %s. %s",
+ $this->config['user'],
+ $this->config['host'],
+ $this->imap->error
+ );
\Log::error($message);
throw new \Exception("Connection to IMAP failed");
}
-
- return $imap;
}
/**
diff --git a/src/app/DataMigrator/Driver/Kolab.php b/src/app/DataMigrator/Driver/Kolab.php
--- a/src/app/DataMigrator/Driver/Kolab.php
+++ b/src/app/DataMigrator/Driver/Kolab.php
@@ -8,6 +8,7 @@
use App\DataMigrator\Interface\ImporterInterface;
use App\DataMigrator\Interface\Item;
use App\DataMigrator\Interface\ItemSet;
+use App\User;
/**
* Data migration from/to a Kolab server
@@ -50,6 +51,18 @@
parent::__construct(new Account($uri), $engine);
+ // Support user impersonation in Kolab v4 DAV by issuing a token
+ // Note: This is not needed for Kolab v3 (iRony), but the kolab_auth_proxy plugin must be enabled there.
+ if ($account->loginas && $account->scheme != 'kolab3' && ($user = $account->getUser())) {
+ // Cyrus DAV does not support proxy authorization via DAV. Even though it has
+ // the Authorize-As header, it is used only for cummunication with Murder backends.
+ // We use a one-time token instead. It's valid for 6 hours, assume it's enough time
+ // to migrate an account.
+ $account->password = \App\Auth\Utils::tokenCreate((string) $user->id, 6 * 60 * 60);
+ $account->username = $account->loginas;
+ unset($account->loginas);
+ }
+
// Setup DAV connection
$uri = sprintf(
'davs://%s:%s@%s',
@@ -139,6 +152,7 @@
// Configuration (v3 tags)
if ($item->folder->type == Engine::TYPE_CONFIGURATION) {
+ $this->initIMAP();
Kolab\Tags::migrateKolab3Tag($this->imap, $item);
return;
}
@@ -186,12 +200,14 @@
// Files (IMAP)
if ($item->folder->type == Engine::TYPE_FILE) {
+ $this->initIMAP();
Kolab\Files::fetchKolab3File($this->imap, $item);
return;
}
// Configuration (v3 tags)
if ($item->folder->type == Engine::TYPE_CONFIGURATION) {
+ $this->initIMAP();
Kolab\Tags::fetchKolab3Tag($this->imap, $item);
return;
}
@@ -221,6 +237,8 @@
// Files
if ($folder->type == Engine::TYPE_FILE) {
+ $this->initIMAP();
+
// Get existing files from the destination account
$existing = $importer->getItems($folder);
@@ -234,6 +252,8 @@
// Configuration (v3 tags)
if ($folder->type == Engine::TYPE_CONFIGURATION) {
+ $this->initIMAP();
+
// Get existing tags from the destination account
$existing = $importer->getItems($folder);
@@ -251,6 +271,8 @@
*/
public function getFolders($types = []): array
{
+ $this->initIMAP();
+
// Note: No support for migration from Kolab4 yet.
// We use IMAP to get the list of all folders, but we'll get folder contents from IMAP and DAV.
// This will not work with Kolab4
@@ -387,6 +409,7 @@
// Configuration folder (tags)
if ($folder->type == Engine::TYPE_CONFIGURATION) {
+ $this->initIMAP();
return Kolab\Tags::getKolab4Tags($this->imap);
}
@@ -396,13 +419,11 @@
/**
* Initialize IMAP connection and authenticate the user
*/
- protected static function initIMAP(array $config): \rcube_imap_generic
+ protected function initIMAP(): void
{
- $imap = parent::initIMAP($config);
+ parent::initIMAP();
// Advertise itself as a Kolab client, in case Guam is in the way
- $imap->id(['name' => 'Cockpit/Kolab']);
-
- return $imap;
+ $this->imap->id(['name' => 'Cockpit/Kolab']);
}
}
diff --git a/src/tests/Feature/DataMigrator/KolabTest.php b/src/tests/Feature/DataMigrator/KolabTest.php
--- a/src/tests/Feature/DataMigrator/KolabTest.php
+++ b/src/tests/Feature/DataMigrator/KolabTest.php
@@ -4,8 +4,10 @@
use App\Backends\Storage;
use App\DataMigrator\Account;
+use App\DataMigrator\Driver\Kolab;
use App\DataMigrator\Driver\Kolab\Tags as KolabTags;
use App\DataMigrator\Engine;
+use App\DataMigrator\Interface\Folder;
use App\DataMigrator\Queue as MigratorQueue;
use App\Fs\Item as FsItem;
use Illuminate\Support\Facades\DB;
@@ -273,6 +275,41 @@
$this->assertSame('123', Storage::fileFetch($files['&kość.odt']));
}
+ /**
+ * Test user impersonation support
+ */
+ public function testUserImpersonation(): void
+ {
+ [$src, $src_imap, $src_dav, $dst, $dst_imap, $dst_dav] = $this->getTestAccounts();
+
+ $this->initAccount($dst_imap);
+ $this->initAccount($dst_dav);
+ $this->imapAppend($dst_imap, 'INBOX', 'mail/1.eml');
+ $this->davAppend($dst_dav, 'Calendar', ['event/3.ics'], Engine::TYPE_EVENT);
+
+ $adminUser = \config('services.imap.admin_login');
+ $adminPass = \config('services.imap.admin_password');
+ $dst_impersonated = str_replace('jack%40kolab.org:simple123', "{$adminUser}:{$adminPass}", (string) $dst)
+ . '&user=jack@kolab.org';
+
+ $migrator = new Engine();
+ $account = new Account($dst_impersonated);
+ $driver = new Kolab($account, $migrator);
+
+ // This involves IMAP and DAV authentication, so it should be enough,
+ // but we make some more assertions to make sure we're connected to the proper account.
+ $driver->authenticate();
+
+ $mail_items = $driver->getItems(Folder::fromArray(['fullname' => 'INBOX', 'type' => Engine::TYPE_MAIL]));
+ $this->assertCount(1, $mail_items);
+ $this->assertArrayHasKey('<sync1@kolab.org>', $mail_items);
+ $event_items = $driver->getItems(Folder::fromArray(['fullname' => 'Calendar', 'type' => Engine::TYPE_EVENT]));
+ $this->assertCount(1, $event_items);
+ $this->assertArrayHasKey('aaa-aaa', $event_items);
+
+ // TODO: We'll need a Kolab3 installation (iRony) to test if impersonation works there
+ }
+
/**
* Initialize accounts for tests
*/
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Apr 4, 12:09 AM (7 h, 13 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18827374
Default Alt Text
D5098.1775261397.diff (10 KB)
Attached To
Mode
D5098: DataMigrator: Support user impersonation on Kolab4 account
Attached
Detach File
Event Timeline