Page MenuHomePhorge

D3989.1775436151.diff
No OneTemporary

Authored By
Unknown
Size
28 KB
Referenced Files
None
Subscribers
None

D3989.1775436151.diff

diff --git a/docker/proxy/rootfs/etc/nginx/nginx.conf b/docker/proxy/rootfs/etc/nginx/nginx.conf
--- a/docker/proxy/rootfs/etc/nginx/nginx.conf
+++ b/docker/proxy/rootfs/etc/nginx/nginx.conf
@@ -140,7 +140,9 @@
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
- rewrite ^/\\.well-known/(caldav|carddav) https://\$server_name/iRony/ redirect;
+ location ~ ^/\\.well-known/(caldav|carddav)(.*)$ {
+ return 301 /iRony/$2;
+ }
location /iRony {
auth_request /auth;
diff --git a/src/app/Backends/DAV.php b/src/app/Backends/DAV.php
--- a/src/app/Backends/DAV.php
+++ b/src/app/Backends/DAV.php
@@ -26,7 +26,7 @@
*/
public function __construct($user, $password)
{
- $this->url = \config('dav.uri');
+ $this->url = \config('services.dav.uri');
$this->user = $user;
$this->password = $password;
}
diff --git a/src/config/dav.php b/src/config/dav.php
deleted file mode 100644
--- a/src/config/dav.php
+++ /dev/null
@@ -1,5 +0,0 @@
-<?php
-
-return [
- 'uri' => env('DAV_URI', 'http://kolab/dav'),
-];
diff --git a/src/config/services.php b/src/config/services.php
--- a/src/config/services.php
+++ b/src/config/services.php
@@ -54,6 +54,21 @@
'openexchangerates' => [
'api_key' => env('OPENEXCHANGERATES_API_KEY', null),
- ]
+ ],
+ 'dav' => [
+ 'uri' => env('DAV_URI', 'https://proxy/'),
+ ],
+
+ 'activesync' => [
+ 'uri' => env('ACTIVESYNC_URI', 'https://proxy/Microsoft-Server-ActiveSync'),
+ ],
+
+ 'wopi' => [
+ 'uri' => env('WOPI_URI', 'http://roundcube/chwala/'),
+ ],
+
+ 'webmail' => [
+ 'uri' => env('WEBMAIL_URI', 'http://roundcube/roundcubemail/'),
+ ]
];
diff --git a/src/tests/Infrastructure/ActivesyncTest.php b/src/tests/Infrastructure/ActivesyncTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Infrastructure/ActivesyncTest.php
@@ -0,0 +1,184 @@
+<?php
+
+namespace Tests\Infrastructure;
+
+use Tests\TestCase;
+use Illuminate\Support\Str;
+
+class ActivesyncTest extends TestCase
+{
+ private static ?\GuzzleHttp\Client $client = null;
+ private static ?\App\User $user = null;
+ private static ?string $deviceId = null;
+
+ private static function toWbxml($xml)
+ {
+ $outputStream = fopen("php://temp", 'r+');
+ $encoder = new \Syncroton_Wbxml_Encoder($outputStream, 'UTF-8', 3);
+ $dom = new \DOMDocument();
+ $dom->loadXML($xml);
+ $encoder->encode($dom);
+ rewind($outputStream);
+ return stream_get_contents($outputStream);
+ }
+
+ private static function fromWbxml($binary)
+ {
+ $stream = fopen('php://memory', 'r+');
+ fwrite($stream, $binary);
+ rewind($stream);
+ $decoder = new \Syncroton_Wbxml_Decoder($stream);
+ return $decoder->decode();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function setUp(): void
+ {
+ parent::setUp();
+
+ if (!self::$user) {
+ self::$user = $this->getTestUser('activesynctest@kolab.org', ['password' => 'simple123'], true);
+ }
+ if (!self::$deviceId) {
+ // By always creating a new device we force syncroton to initialize.
+ // Otherwise we work against uninitialized metadata (subscription states),
+ // because the account has been removed, but syncroton doesn't reinitalize the metadata for known devices.
+ self::$deviceId = (string) Str::uuid();
+ }
+ if (!self::$client) {
+ self::$client = new \GuzzleHttp\Client([
+ 'http_errors' => false, // No exceptions
+ 'base_uri' => \config("services.activesync.uri"),
+ 'verify' => false,
+ 'auth' => [self::$user->email, 'simple123'],
+ 'connect_timeout' => 10,
+ 'timeout' => 10,
+ 'headers' => [
+ "Content-Type" => "application/xml; charset=utf-8",
+ "Depth" => "1",
+ ]
+ ]);
+ }
+ }
+
+ public function testOptions()
+ {
+ $response = self::$client->request('OPTIONS', '');
+ $this->assertEquals(200, $response->getStatusCode());
+ $this->assertStringContainsString('14', $response->getHeader('MS-Server-ActiveSync')[0]);
+ $this->assertStringContainsString('14.1', $response->getHeader('MS-ASProtocolVersions')[0]);
+ $this->assertStringContainsString('FolderSync', $response->getHeader('MS-ASProtocolCommands')[0]);
+ }
+
+ public function testList()
+ {
+ $user = self::$user;
+ $deviceId = self::$deviceId;
+ $request = <<<EOF
+ <?xml version="1.0" encoding="utf-8"?>
+ <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/">
+ <FolderSync xmlns="uri:FolderHierarchy">
+ <SyncKey>0</SyncKey>
+ </FolderSync>
+ EOF;
+ $body = self::toWbxml($request);
+ $response = self::$client->request(
+ 'POST',
+ "?Cmd=FolderSync&User={$user->email}&DeviceId={$deviceId}&DeviceType=iphone",
+ [
+ 'headers' => [
+ "Content-Type" => "application/vnd.ms-sync.wbxml",
+ 'MS-ASProtocolVersion' => "14.0"
+ ],
+ 'body' => $body
+ ]
+ );
+ $this->assertEquals(200, $response->getStatusCode());
+ $dom = self::fromWbxml($response->getBody());
+ $xml = $dom->saveXML();
+
+ $this->assertStringContainsString('INBOX', $xml);
+ // The hash is based on the name, so it's always the same
+ $inboxId = '38b950ebd62cd9a66929c89615d0fc04';
+ $this->assertStringContainsString($inboxId, $xml);
+ //TODO for this to work we need to create the default folders in IMAP::createUser
+ // $this->assertStringContainsString('Drafts', $result);
+ // $this->assertStringContainsString('Sent', $result);
+ // $this->assertStringContainsString('Trash', $result);
+ // $this->assertStringContainsString('Calendar', $result);
+ // $this->assertStringContainsString('Contacts', $result);
+
+ // Find the inbox for the next step
+ // $collectionIds = $dom->getElementsByTagName('ServerId');
+ // $inboxId = $collectionIds[0]->nodeValue;
+
+ return $inboxId;
+ }
+
+ /**
+ * @depends testList
+ */
+ public function testInitialSync($inboxId)
+ {
+ $user = self::$user;
+ $deviceId = self::$deviceId;
+ $request = <<<EOF
+ <?xml version="1.0" encoding="utf-8"?>
+ <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/">
+ <Sync xmlns="uri:AirSync" xmlns:AirSyncBase="uri:AirSyncBase">
+ <Collections>
+ <Collection>
+ <SyncKey>0</SyncKey>
+ <CollectionId>{$inboxId}</CollectionId>
+ <DeletesAsMoves>0</DeletesAsMoves>
+ <GetChanges>0</GetChanges>
+ <WindowSize>512</WindowSize>
+ <Options>
+ <FilterType>0</FilterType>
+ <BodyPreference xmlns="uri:AirSyncBase">
+ <Type>1</Type>
+ <AllOrNone>1</AllOrNone>
+ </BodyPreference>
+ </Options>
+ </Collection>
+ </Collections>
+ <WindowSize>16</WindowSize>
+ </Sync>
+ EOF;
+ $body = self::toWbxml($request);
+ $response = self::$client->request(
+ 'POST',
+ "?Cmd=Sync&User={$user->email}&DeviceId={$deviceId}&DeviceType=iphone",
+ [
+ 'headers' => [
+ "Content-Type" => "application/vnd.ms-sync.wbxml",
+ 'MS-ASProtocolVersion' => "14.0"
+ ],
+ 'body' => $body
+ ]
+ );
+ $this->assertEquals(200, $response->getStatusCode());
+ $dom = self::fromWbxml($response->getBody());
+ $status = $dom->getElementsByTagName('Status');
+ $this->assertEquals("1", $status[0]->nodeValue);
+ $collections = $dom->getElementsByTagName('Collection');
+ $this->assertEquals(1, $collections->length);
+ $collection = $collections->item(0);
+ $this->assertEquals("Class", $collection->childNodes->item(0)->nodeName);
+ $this->assertEquals("Email", $collection->childNodes->item(0)->nodeValue);
+ $this->assertEquals("SyncKey", $collection->childNodes->item(1)->nodeName);
+ $this->assertEquals("1", $collection->childNodes->item(1)->nodeValue);
+ $this->assertEquals("Status", $collection->childNodes->item(3)->nodeName);
+ $this->assertEquals("1", $collection->childNodes->item(3)->nodeValue);
+ }
+
+ /**
+ * @doesNotPerformAssertions
+ */
+ public function testCleanup(): void
+ {
+ $this->deleteTestUser(self::$user->email);
+ }
+}
diff --git a/src/tests/Infrastructure/AutodiscoverTest.php b/src/tests/Infrastructure/AutodiscoverTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Infrastructure/AutodiscoverTest.php
@@ -0,0 +1,92 @@
+<?php
+
+namespace Tests\Infrastructure;
+
+use Tests\TestCase;
+
+class AutodiscoverTest extends TestCase
+{
+ private static ?\GuzzleHttp\Client $client = null;
+
+ /**
+ * {@inheritDoc}
+ */
+ public function setUp(): void
+ {
+ parent::setUp();
+ if (!self::$client) {
+ self::$client = new \GuzzleHttp\Client([
+ 'http_errors' => false, // No exceptions
+ 'base_uri' => "http://roundcube/",
+ 'verify' => false,
+ 'connect_timeout' => 10,
+ 'timeout' => 10,
+ ]);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function tearDown(): void
+ {
+ parent::tearDown();
+ }
+
+ public function testWellKnownOutlook()
+ {
+ $body = <<<EOF
+ <Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006">
+ <Request>
+ <EMailAddress>admin@example.local</EMailAddress>
+ <AcceptableResponseSchema>
+ http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a
+ </AcceptableResponseSchema>
+ </Request>
+ </Autodiscover>
+ EOF;
+ $response = self::$client->request('POST', 'autodiscover/autodiscover.xml', [
+ 'headers' => [
+ "Content-Type" => "text/xml; charset=utf-8"
+ ],
+ 'body' => $body
+ ]);
+ $this->assertEquals($response->getStatusCode(), 200);
+ $data = $response->getBody();
+ $this->assertTrue(str_contains($data, '<Server>example.local</Server>'));
+ $this->assertTrue(str_contains($data, 'admin@example.local'));
+ }
+
+ public function testWellKnownActivesync()
+ {
+ $body = <<<EOF
+ <Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/mobilesync/requestschema/2006">
+ <Request>
+ <EMailAddress>admin@example.local</EMailAddress>
+ <AcceptableResponseSchema>
+ http://schemas.microsoft.com/exchange/autodiscover/mobilesync/responseschema/2006
+ </AcceptableResponseSchema>
+ </Request>
+ </Autodiscover>
+ EOF;
+ $response = self::$client->request('POST', 'autodiscover/autodiscover.xml', [
+ 'headers' => [
+ "Content-Type" => "text/xml; charset=utf-8"
+ ],
+ 'body' => $body
+ ]);
+ $this->assertEquals($response->getStatusCode(), 200);
+ $data = $response->getBody();
+ $this->assertTrue(str_contains($data, '<Url>https://example.local/Microsoft-Server-ActiveSync</Url>'));
+ $this->assertTrue(str_contains($data, 'admin@example.local'));
+ }
+
+ public function testWellKnownMail()
+ {
+ $response = self::$client->request(
+ 'GET',
+ '.well-known/autoconfig/mail/config-v1.1.xml?emailaddress=fred@example.com'
+ );
+ $this->assertEquals($response->getStatusCode(), 200);
+ }
+}
diff --git a/src/tests/Infrastructure/DavTest.php b/src/tests/Infrastructure/DavTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Infrastructure/DavTest.php
@@ -0,0 +1,293 @@
+<?php
+
+namespace Tests\Infrastructure;
+
+use Tests\TestCase;
+
+class DavTest extends TestCase
+{
+ private static ?\GuzzleHttp\Client $client = null;
+ private static ?\App\User $user = null;
+
+ /**
+ * {@inheritDoc}
+ */
+ public function setUp(): void
+ {
+ parent::setUp();
+
+ if (!self::$user) {
+ self::$user = $this->getTestUser('davtest@kolab.org', ['password' => 'simple123'], true);
+ }
+
+ if (!self::$client) {
+ self::$client = new \GuzzleHttp\Client([
+ 'http_errors' => false, // No exceptions
+ 'base_uri' => \config("services.dav.uri"),
+ 'verify' => false,
+ 'auth' => [self::$user->email, 'simple123'],
+ 'connect_timeout' => 10,
+ 'timeout' => 10,
+ 'headers' => [
+ "Content-Type" => "application/xml; charset=utf-8",
+ "Depth" => "1",
+ ]
+ ]);
+ }
+ }
+
+ public function testDiscoverPrincipal()
+ {
+ $user = self::$user;
+ $body = "<d:propfind xmlns:d='DAV:'><d:prop><d:current-user-principal/></d:prop></d:propfind>";
+ $response = self::$client->request('PROPFIND', '/iRony/', ['body' => $body]);
+ $this->assertEquals(207, $response->getStatusCode());
+ $data = $response->getBody();
+ $this->assertStringContainsString("<d:href>/iRony/principals/{$user->email}/</d:href>", $data);
+ $this->assertStringContainsString('<d:href>/iRony/calendars/</d:href>', $data);
+ $this->assertStringContainsString('<d:href>/iRony/addressbooks/</d:href>', $data);
+ }
+
+ /**
+ * This codepath is triggerd by MacOS CalDAV when it tries to login.
+ * Verify we don't crash and end up with a 500 status code.
+ */
+ public function testFailingLogin()
+ {
+ $body = "<d:propfind xmlns:d='DAV:'><d:prop><d:current-user-principal/></d:prop></d:propfind>";
+ $headers = [
+ "Content-Type" => "application/xml; charset=utf-8",
+ "Depth" => "1",
+ 'body' => $body,
+ 'auth' => ['invaliduser@kolab.org', 'invalid']
+ ];
+
+ $response = self::$client->request('PROPFIND', '/iRony/', $headers);
+ $this->assertEquals(403, $response->getStatusCode());
+ }
+
+ /**
+ * This codepath is triggerd by MacOS CardDAV when it tries to login.
+ * NOTE: This depends on the username_domain roundcube config option.
+ */
+ public function testShortlogin()
+ {
+ $this->markTestSkipped(
+ 'Shortlogins dont work with the nginx proxy.'
+ );
+ $body = "<d:propfind xmlns:d='DAV:'><d:prop><d:current-user-principal/></d:prop></d:propfind>";
+ $response = self::$client->request('PROPFIND', '/iRony/', [
+ 'body' => $body,
+ 'auth' => ['davtest', 'simple123']
+ ]);
+ $this->assertEquals(207, $response->getStatusCode());
+ }
+
+ public function testDiscoverCalendarHomeset()
+ {
+ $user = self::$user;
+ $body = <<<EOF
+ <d:propfind xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:caldav">
+ <d:prop>
+ <c:calendar-home-set />
+ </d:prop>
+ </d:propfind>
+ EOF;
+
+ $response = self::$client->request('PROPFIND', '/iRony/', ['body' => $body]);
+ $this->assertEquals(207, $response->getStatusCode());
+ $data = $response->getBody();
+ $this->assertStringContainsString("<d:href>/iRony/calendars/{$user->email}/</d:href>", $data);
+ }
+
+ public function testDiscoverCalendars()
+ {
+ $user = self::$user;
+ $body = <<<EOF
+ <d:propfind xmlns:d="DAV:" xmlns:cs="http://calendarserver.org/ns/" xmlns:c="urn:ietf:params:xml:ns:caldav">
+ <d:prop>
+ <d:resourcetype />
+ <d:displayname />
+ <cs:getctag />
+ <c:supported-calendar-component-set />
+ </d:prop>
+ </d:propfind>
+ EOF;
+
+ $response = self::$client->request('PROPFIND', "/iRony/calendars/{$user->email}", [
+ 'headers' => [
+ "Depth" => "infinity",
+ ],
+ 'body' => $body
+ ]);
+ $this->assertEquals(207, $response->getStatusCode());
+ $data = $response->getBody();
+ $this->assertStringContainsString("<d:href>/iRony/calendars/{$user->email}/</d:href>", $data);
+
+ $doc = new \DOMDocument('1.0', 'UTF-8');
+ $doc->loadXML($data);
+ $response = $doc->getElementsByTagName('response')->item(1);
+ $doc->getElementsByTagName('href')->item(0);
+
+ $this->assertEquals("d:href", $response->childNodes->item(0)->nodeName);
+ $href = $response->childNodes->item(0)->nodeValue;
+ return $href;
+ }
+
+ /**
+ * @depends testDiscoverCalendars
+ */
+ public function testPropfindCalendar($href)
+ {
+ $body = <<<EOF
+ <d:propfind xmlns:d="DAV:" xmlns:cs="http://calendarserver.org/ns/" xmlns:c="urn:ietf:params:xml:ns:caldav">
+ <d:prop>
+ <d:resourcetype />
+ <d:owner/>
+ <d:current-user-principal/>
+ <d:current-user-privilege-set/>
+ <d:supported-report-set/>
+ <cs:getctag />
+ <c:supported-calendar-component-set />
+ </d:prop>
+ </d:propfind>
+ EOF;
+
+ $response = self::$client->request('PROPFIND', $href, [
+ 'headers' => [
+ "Depth" => "0",
+ ],
+ 'body' => $body,
+ ]);
+ $this->assertEquals(207, $response->getStatusCode());
+ $data = $response->getBody();
+ $this->assertStringContainsString("<d:href>$href</d:href>", $data);
+ }
+
+ /**
+ * Thunderbird does this and relies on the WWW-Authenticate header response to
+ * start sending authenticated requests.
+ *
+ * @depends testDiscoverCalendars
+ */
+ public function testPropfindCalendarWithoutAuth($href)
+ {
+ $body = <<<EOF
+ <d:propfind xmlns:d="DAV:" xmlns:cs="http://calendarserver.org/ns/" xmlns:c="urn:ietf:params:xml:ns:caldav">
+ <d:prop>
+ <d:resourcetype />
+ <d:owner/>
+ <d:current-user-principal/>
+ <d:current-user-privilege-set/>
+ <d:supported-report-set/>
+ <cs:getctag />
+ <c:supported-calendar-component-set />
+ </d:prop>
+ </d:propfind>
+ EOF;
+
+ $response = self::$client->request('PROPFIND', $href, [
+ 'headers' => [
+ "Depth" => "0",
+ ],
+ 'body' => $body,
+ 'auth' => []
+ ]);
+ $this->assertEquals(401, $response->getStatusCode());
+ $this->assertStringContainsString('Basic realm=', $response->getHeader('WWW-Authenticate')[0]);
+ $data = $response->getBody();
+ $this->assertStringContainsString("<s:exception>Sabre\DAV\Exception\NotAuthenticated</s:exception>", $data);
+ }
+
+ /**
+ * Required for MacOS autoconfig
+ */
+ public function testOptions()
+ {
+ $user = self::$user;
+ $body = <<<EOF
+ <d:propfind xmlns:d="DAV:" xmlns:cs="http://calendarserver.org/ns/" xmlns:c="urn:ietf:params:xml:ns:caldav">
+ <d:prop>
+ <d:resourcetype />
+ <d:displayname />
+ <cs:getctag />
+ <c:supported-calendar-component-set />
+ </d:prop>
+ </d:propfind>
+ EOF;
+
+ $response = self::$client->request('OPTIONS', "/iRony/principals/{$user->email}/", ['body' => $body]);
+ $this->assertEquals(200, $response->getStatusCode());
+ $this->assertStringContainsString('PROPFIND', $response->getHeader('Allow')[0]);
+ }
+
+ public function testWellKnown()
+ {
+ $user = self::$user;
+ $body = <<<EOF
+ <d:propfind xmlns:d="DAV:" xmlns:cs="http://calendarserver.org/ns/" xmlns:c="urn:ietf:params:xml:ns:caldav">
+ <d:prop>
+ <d:resourcetype />
+ <d:displayname />
+ <cs:getctag />
+ <c:supported-calendar-component-set />
+ </d:prop>
+ </d:propfind>
+ EOF;
+
+ // The base URL needs to work as a redirect
+ $response = self::$client->request('PROPFIND', '/.well-known/caldav', [
+ 'headers' => [
+ "Depth" => "infinity",
+ ],
+ 'body' => $body,
+ 'allow_redirects' => false
+ ]);
+ $this->assertEquals(301, $response->getStatusCode());
+ $redirectTarget = $response->getHeader('location')[0];
+ $this->assertEquals(\config('services.dav.uri') . "iRony/", $redirectTarget);
+
+ // Follow the redirect
+ $response = self::$client->request('PROPFIND', $redirectTarget, [
+ 'headers' => [
+ "Depth" => "infinity",
+ ],
+ 'body' => $body,
+ 'allow_redirects' => false
+ ]);
+ $this->assertEquals(207, $response->getStatusCode());
+
+ // Any URL should result in a redirect to the same path
+ $response = self::$client->request('PROPFIND', "/.well-known/caldav/calendars/{$user->email}", [
+ 'headers' => [
+ "Depth" => "infinity",
+ ],
+ 'body' => $body,
+ 'allow_redirects' => false
+ ]);
+ $this->assertEquals(301, $response->getStatusCode());
+ $redirectTarget = $response->getHeader('location')[0];
+ //FIXME we have an extra slash that we don't technically want here
+ $this->assertEquals(\config('services.dav.uri') . "iRony//calendars/{$user->email}", $redirectTarget);
+
+ // Follow the redirect
+ $response = self::$client->request('PROPFIND', $redirectTarget, [
+ 'headers' => [
+ "Depth" => "infinity",
+ ],
+ 'body' => $body,
+ 'allow_redirects' => false
+ ]);
+ $this->assertEquals(207, $response->getStatusCode());
+ $data = $response->getBody();
+ $this->assertStringContainsString("<d:href>/iRony/calendars/{$user->email}/</d:href>", $data);
+ }
+
+ /**
+ * @doesNotPerformAssertions
+ */
+ public function testCleanup(): void
+ {
+ $this->deleteTestUser(self::$user->email);
+ }
+}
diff --git a/src/tests/Infrastructure/RoundcubeTest.php b/src/tests/Infrastructure/RoundcubeTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Infrastructure/RoundcubeTest.php
@@ -0,0 +1,81 @@
+<?php
+
+namespace Tests\Infrastructure;
+
+use Tests\Browser;
+use Tests\TestCaseDusk;
+
+class RoundcubeTest extends TestCaseDusk
+{
+ private static ?\App\User $user = null;
+
+ /**
+ * {@inheritDoc}
+ */
+ public function setUp(): void
+ {
+ parent::setUp();
+ Browser::$baseUrl = \config("services.webmail.uri");
+
+ if (!self::$user) {
+ self::$user = $this->getTestUser('roundcubetesttest@kolab.org', ['password' => 'simple123'], true);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function tearDown(): void
+ {
+ parent::tearDown();
+ }
+
+ public function testLogin()
+ {
+ $this->browse(function (Browser $browser) {
+ $browser->visit('/')
+ ->type('#rcmloginuser', self::$user->email)
+ ->type('#rcmloginpwd', "simple123")
+ ->press('#rcmloginsubmit')
+ ->waitFor('#logo')
+ ->waitUntil('!rcmail.busy')
+ ->assertSee('Inbox');
+
+ $browser->press('.contacts')
+ ->waitUntil('!rcmail.busy')
+ ->assertVisible('#directorylist')
+ ->assertVisible('.addressbook.personal')
+ ->assertSee('Contacts');
+
+ $browser->press('.button-calendar')
+ ->waitUntil('!rcmail.busy')
+ ->assertSee('Calendar');
+
+ //TODO requires the default folders to be created
+ // $browser->press('.button-files')
+ // ->waitUntil('!rcmail.busy')
+ // ->assertSeeIn('#files-folder-list', 'Files');
+
+ $browser->press('.button-notes')
+ ->waitUntil('!rcmail.busy')
+ ->assertSeeIn('#notebooks-content', 'Notes');
+
+ $browser->press('.button-tasklist')
+ ->waitUntil('!rcmail.busy')
+ ->assertSee('Tasks');
+
+ $browser->press('.settings')
+ ->waitUntil('!rcmail.busy')
+ ->assertSee('Activesync');
+ // print($browser->dump());
+ });
+ }
+
+ /**
+ * @doesNotPerformAssertions
+ */
+ public function testCleanup(): void
+ {
+ $this->deleteTestUser(self::$user->email);
+ }
+}
diff --git a/src/tests/Infrastructure/WOPITest.php b/src/tests/Infrastructure/WOPITest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Infrastructure/WOPITest.php
@@ -0,0 +1,65 @@
+<?php
+
+namespace Tests\Infrastructure;
+
+use Tests\TestCase;
+
+class WOPITest extends TestCase
+{
+ private static ?\GuzzleHttp\Client $client = null;
+ private static ?\App\User $user = null;
+
+ /**
+ * {@inheritDoc}
+ */
+ public function setUp(): void
+ {
+ parent::setUp();
+
+ if (!self::$user) {
+ self::$user = $this->getTestUser('wopitest@kolab.org', ['password' => 'simple123'], true);
+ }
+
+ if (!self::$client) {
+ self::$client = new \GuzzleHttp\Client([
+ 'base_uri' => \config('services.wopi.uri'),
+ 'verify' => false,
+ 'auth' => [self::$user->email, 'simple123'],
+ 'connect_timeout' => 10,
+ 'timeout' => 10
+ ]);
+ }
+ }
+
+ public function testAccess()
+ {
+ $response = self::$client->request('GET', 'api/?method=authenticate&version=4');
+ $this->assertEquals($response->getStatusCode(), 200);
+ $json = json_decode($response->getBody(), true);
+
+ $this->assertEquals('OK', $json['status']);
+ $token = $json['result']['token'];
+ $this->assertTrue(!empty($token));
+
+ //FIXME the session token doesn't seem to be required here?
+ $response = self::$client->request('GET', 'api/?method=mimetypes', [
+ 'headers' => [
+ 'X-Session_token' => $token
+ ]
+ ]);
+ $this->assertEquals($response->getStatusCode(), 200);
+ $json = json_decode($response->getBody(), true);
+ $this->assertEquals('OK', $json['status']);
+ $this->assertEquals('OK', $json['status']);
+ $this->assertContains('image/png', $json['result']['view']);
+ $this->assertArrayHasKey('text/plain', $json['result']['edit']);
+ }
+
+ /**
+ * @doesNotPerformAssertions
+ */
+ public function testCleanup(): void
+ {
+ $this->deleteTestUser(self::$user->email);
+ }
+}
diff --git a/src/tests/TestCaseTrait.php b/src/tests/TestCaseTrait.php
--- a/src/tests/TestCaseTrait.php
+++ b/src/tests/TestCaseTrait.php
@@ -2,6 +2,7 @@
namespace Tests;
+use App\Backends\IMAP;
use App\Backends\LDAP;
use App\CompanionApp;
use App\Domain;
@@ -403,7 +404,12 @@
return;
}
- LDAP::deleteUser($user);
+ if (\config('app.with_imap')) {
+ IMAP::deleteUser($user);
+ }
+ if (\config('app.with_ldap')) {
+ LDAP::deleteUser($user);
+ }
$user->forceDelete();
}
@@ -553,10 +559,12 @@
*
* @coversNothing
*/
- protected function getTestUser($email, $attrib = [])
+ protected function getTestUser($email, $attrib = [], $createInBackends = false)
{
// Disable jobs (i.e. skip LDAP oprations)
- Queue::fake();
+ if (!$createInBackends) {
+ Queue::fake();
+ }
$user = User::firstOrCreate(['email' => $email], $attrib);
if ($user->trashed()) {

File Metadata

Mime Type
text/plain
Expires
Mon, Apr 6, 12:42 AM (3 h, 13 m ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18834051
Default Alt Text
D3989.1775436151.diff (28 KB)

Event Timeline