123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490 |
- <?php
- /**
- * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-only
- */
- namespace OCA\DAV\Tests\unit\CardDAV;
- use GuzzleHttp\Exception\ClientException;
- use GuzzleHttp\Psr7\Request as PsrRequest;
- use GuzzleHttp\Psr7\Response as PsrResponse;
- use OC\Http\Client\Response;
- use OCA\DAV\CardDAV\CardDavBackend;
- use OCA\DAV\CardDAV\Converter;
- use OCA\DAV\CardDAV\SyncService;
- use OCP\Http\Client\IClient;
- use OCP\Http\Client\IClientService;
- use OCP\IConfig;
- use OCP\IDBConnection;
- use OCP\IUser;
- use OCP\IUserManager;
- use Psr\Http\Client\ClientExceptionInterface;
- use Psr\Log\LoggerInterface;
- use Psr\Log\NullLogger;
- use Sabre\VObject\Component\VCard;
- use Test\TestCase;
- class SyncServiceTest extends TestCase {
- protected CardDavBackend $backend;
- protected IUserManager $userManager;
- protected IDBConnection $dbConnection;
- protected LoggerInterface $logger;
- protected Converter $converter;
- protected IClient $client;
- protected IConfig $config;
- protected SyncService $service;
- public function setUp(): void {
- $addressBook = [
- 'id' => 1,
- 'uri' => 'system',
- 'principaluri' => 'principals/system/system',
- '{DAV:}displayname' => 'system',
- // watch out, incomplete address book mock.
- ];
- $this->backend = $this->createMock(CardDavBackend::class);
- $this->backend->method('getAddressBooksByUri')
- ->with('principals/system/system', 1)
- ->willReturn($addressBook);
- $this->userManager = $this->createMock(IUserManager::class);
- $this->dbConnection = $this->createMock(IDBConnection::class);
- $this->logger = new NullLogger();
- $this->converter = $this->createMock(Converter::class);
- $this->client = $this->createMock(IClient::class);
- $this->config = $this->createMock(IConfig::class);
- $clientService = $this->createMock(IClientService::class);
- $clientService->method('newClient')
- ->willReturn($this->client);
- $this->service = new SyncService(
- $this->backend,
- $this->userManager,
- $this->dbConnection,
- $this->logger,
- $this->converter,
- $clientService,
- $this->config
- );
- }
- public function testEmptySync(): void {
- $this->backend->expects($this->exactly(0))
- ->method('createCard');
- $this->backend->expects($this->exactly(0))
- ->method('updateCard');
- $this->backend->expects($this->exactly(0))
- ->method('deleteCard');
- $body = '<?xml version="1.0"?>
- <d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:card="urn:ietf:params:xml:ns:carddav" xmlns:oc="http://owncloud.org/ns">
- <d:sync-token>http://sabre.io/ns/sync/1</d:sync-token>
- </d:multistatus>';
- $requestResponse = new Response(new PsrResponse(
- 207,
- ['Content-Type' => 'application/xml; charset=utf-8', 'Content-Length' => strlen($body)],
- $body
- ));
- $this->client
- ->method('request')
- ->willReturn($requestResponse);
- $token = $this->service->syncRemoteAddressBook(
- '',
- 'system',
- 'system',
- '1234567890',
- null,
- '1',
- 'principals/system/system',
- []
- );
- $this->assertEquals('http://sabre.io/ns/sync/1', $token);
- }
- public function testSyncWithNewElement(): void {
- $this->backend->expects($this->exactly(1))
- ->method('createCard');
- $this->backend->expects($this->exactly(0))
- ->method('updateCard');
- $this->backend->expects($this->exactly(0))
- ->method('deleteCard');
- $this->backend->method('getCard')
- ->willReturn(false);
- $body = '<?xml version="1.0"?>
- <d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:card="urn:ietf:params:xml:ns:carddav" xmlns:oc="http://owncloud.org/ns">
- <d:response>
- <d:href>/remote.php/dav/addressbooks/system/system/system/Database:alice.vcf</d:href>
- <d:propstat>
- <d:prop>
- <d:getcontenttype>text/vcard; charset=utf-8</d:getcontenttype>
- <d:getetag>"2df155fa5c2a24cd7f750353fc63f037"</d:getetag>
- </d:prop>
- <d:status>HTTP/1.1 200 OK</d:status>
- </d:propstat>
- </d:response>
- <d:sync-token>http://sabre.io/ns/sync/2</d:sync-token>
- </d:multistatus>';
- $reportResponse = new Response(new PsrResponse(
- 207,
- ['Content-Type' => 'application/xml; charset=utf-8', 'Content-Length' => strlen($body)],
- $body
- ));
- $this->client
- ->method('request')
- ->willReturn($reportResponse);
- $vCard = 'BEGIN:VCARD
- VERSION:3.0
- PRODID:-//Sabre//Sabre VObject 4.5.4//EN
- UID:alice
- FN;X-NC-SCOPE=v2-federated:alice
- N;X-NC-SCOPE=v2-federated:alice;;;;
- X-SOCIALPROFILE;TYPE=NEXTCLOUD;X-NC-SCOPE=v2-published:https://server2.internal/index.php/u/alice
- CLOUD:alice@server2.internal
- END:VCARD';
- $getResponse = new Response(new PsrResponse(
- 200,
- ['Content-Type' => 'text/vcard; charset=utf-8', 'Content-Length' => strlen($vCard)],
- $vCard,
- ));
- $this->client
- ->method('get')
- ->willReturn($getResponse);
- $token = $this->service->syncRemoteAddressBook(
- '',
- 'system',
- 'system',
- '1234567890',
- null,
- '1',
- 'principals/system/system',
- []
- );
- $this->assertEquals('http://sabre.io/ns/sync/2', $token);
- }
- public function testSyncWithUpdatedElement(): void {
- $this->backend->expects($this->exactly(0))
- ->method('createCard');
- $this->backend->expects($this->exactly(1))
- ->method('updateCard');
- $this->backend->expects($this->exactly(0))
- ->method('deleteCard');
- $this->backend->method('getCard')
- ->willReturn(true);
- $body = '<?xml version="1.0"?>
- <d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:card="urn:ietf:params:xml:ns:carddav" xmlns:oc="http://owncloud.org/ns">
- <d:response>
- <d:href>/remote.php/dav/addressbooks/system/system/system/Database:alice.vcf</d:href>
- <d:propstat>
- <d:prop>
- <d:getcontenttype>text/vcard; charset=utf-8</d:getcontenttype>
- <d:getetag>"2df155fa5c2a24cd7f750353fc63f037"</d:getetag>
- </d:prop>
- <d:status>HTTP/1.1 200 OK</d:status>
- </d:propstat>
- </d:response>
- <d:sync-token>http://sabre.io/ns/sync/3</d:sync-token>
- </d:multistatus>';
- $reportResponse = new Response(new PsrResponse(
- 207,
- ['Content-Type' => 'application/xml; charset=utf-8', 'Content-Length' => strlen($body)],
- $body
- ));
- $this->client
- ->method('request')
- ->willReturn($reportResponse);
- $vCard = 'BEGIN:VCARD
- VERSION:3.0
- PRODID:-//Sabre//Sabre VObject 4.5.4//EN
- UID:alice
- FN;X-NC-SCOPE=v2-federated:alice
- N;X-NC-SCOPE=v2-federated:alice;;;;
- X-SOCIALPROFILE;TYPE=NEXTCLOUD;X-NC-SCOPE=v2-published:https://server2.internal/index.php/u/alice
- CLOUD:alice@server2.internal
- END:VCARD';
- $getResponse = new Response(new PsrResponse(
- 200,
- ['Content-Type' => 'text/vcard; charset=utf-8', 'Content-Length' => strlen($vCard)],
- $vCard,
- ));
- $this->client
- ->method('get')
- ->willReturn($getResponse);
- $token = $this->service->syncRemoteAddressBook(
- '',
- 'system',
- 'system',
- '1234567890',
- null,
- '1',
- 'principals/system/system',
- []
- );
- $this->assertEquals('http://sabre.io/ns/sync/3', $token);
- }
- public function testSyncWithDeletedElement(): void {
- $this->backend->expects($this->exactly(0))
- ->method('createCard');
- $this->backend->expects($this->exactly(0))
- ->method('updateCard');
- $this->backend->expects($this->exactly(1))
- ->method('deleteCard');
- $body = '<?xml version="1.0"?>
- <d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:card="urn:ietf:params:xml:ns:carddav" xmlns:oc="http://owncloud.org/ns">
- <d:response>
- <d:href>/remote.php/dav/addressbooks/system/system/system/Database:alice.vcf</d:href>
- <d:status>HTTP/1.1 404 Not Found</d:status>
- </d:response>
- <d:sync-token>http://sabre.io/ns/sync/4</d:sync-token>
- </d:multistatus>';
- $reportResponse = new Response(new PsrResponse(
- 207,
- ['Content-Type' => 'application/xml; charset=utf-8', 'Content-Length' => strlen($body)],
- $body
- ));
- $this->client
- ->method('request')
- ->willReturn($reportResponse);
- $token = $this->service->syncRemoteAddressBook(
- '',
- 'system',
- 'system',
- '1234567890',
- null,
- '1',
- 'principals/system/system',
- []
- );
- $this->assertEquals('http://sabre.io/ns/sync/4', $token);
- }
- public function testEnsureSystemAddressBookExists(): void {
- /** @var CardDavBackend | \PHPUnit\Framework\MockObject\MockObject $backend */
- $backend = $this->getMockBuilder(CardDavBackend::class)->disableOriginalConstructor()->getMock();
- $backend->expects($this->exactly(1))->method('createAddressBook');
- $backend->expects($this->exactly(2))
- ->method('getAddressBooksByUri')
- ->willReturnOnConsecutiveCalls(
- null,
- [],
- );
- /** @var IUserManager $userManager */
- $userManager = $this->getMockBuilder(IUserManager::class)->disableOriginalConstructor()->getMock();
- $dbConnection = $this->createMock(IDBConnection::class);
- $logger = $this->getMockBuilder(LoggerInterface::class)->disableOriginalConstructor()->getMock();
- $converter = $this->createMock(Converter::class);
- $clientService = $this->createMock(IClientService::class);
- $config = $this->createMock(IConfig::class);
- $ss = new SyncService($backend, $userManager, $dbConnection, $logger, $converter, $clientService, $config);
- $ss->ensureSystemAddressBookExists('principals/users/adam', 'contacts', []);
- }
- public function dataActivatedUsers() {
- return [
- [true, 1, 1, 1],
- [false, 0, 0, 3],
- ];
- }
- /**
- * @dataProvider dataActivatedUsers
- *
- * @param boolean $activated
- * @param integer $createCalls
- * @param integer $updateCalls
- * @param integer $deleteCalls
- * @return void
- */
- public function testUpdateAndDeleteUser($activated, $createCalls, $updateCalls, $deleteCalls): void {
- /** @var CardDavBackend | \PHPUnit\Framework\MockObject\MockObject $backend */
- $backend = $this->getMockBuilder(CardDavBackend::class)->disableOriginalConstructor()->getMock();
- $logger = $this->getMockBuilder(LoggerInterface::class)->disableOriginalConstructor()->getMock();
- $backend->expects($this->exactly($createCalls))->method('createCard');
- $backend->expects($this->exactly($updateCalls))->method('updateCard');
- $backend->expects($this->exactly($deleteCalls))->method('deleteCard');
- $backend->method('getCard')->willReturnOnConsecutiveCalls(false, [
- 'carddata' => "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 3.4.8//EN\r\nUID:test-user\r\nFN:test-user\r\nN:test-user;;;;\r\nEND:VCARD\r\n\r\n"
- ]);
- $backend->method('getAddressBooksByUri')
- ->with('principals/system/system', 'system')
- ->willReturn(['id' => -1]);
- /** @var IUserManager | \PHPUnit\Framework\MockObject\MockObject $userManager */
- $userManager = $this->getMockBuilder(IUserManager::class)->disableOriginalConstructor()->getMock();
- $dbConnection = $this->createMock(IDBConnection::class);
- /** @var IUser | \PHPUnit\Framework\MockObject\MockObject $user */
- $user = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
- $user->method('getBackendClassName')->willReturn('unittest');
- $user->method('getUID')->willReturn('test-user');
- $user->method('getCloudId')->willReturn('cloudId');
- $user->method('getDisplayName')->willReturn('test-user');
- $user->method('isEnabled')->willReturn($activated);
- $converter = $this->createMock(Converter::class);
- $converter->expects($this->any())
- ->method('createCardFromUser')
- ->willReturn($this->createMock(VCard::class));
- $clientService = $this->createMock(IClientService::class);
- $config = $this->createMock(IConfig::class);
- $ss = new SyncService($backend, $userManager, $dbConnection, $logger, $converter, $clientService, $config);
- $ss->updateUser($user);
- $ss->updateUser($user);
- $ss->deleteUser($user);
- }
- public function testDeleteAddressbookWhenAccessRevoked(): void {
- $this->expectException(ClientExceptionInterface::class);
- $this->backend->expects($this->exactly(0))
- ->method('createCard');
- $this->backend->expects($this->exactly(0))
- ->method('updateCard');
- $this->backend->expects($this->exactly(0))
- ->method('deleteCard');
- $this->backend->expects($this->exactly(1))
- ->method('deleteAddressBook');
- $request = new PsrRequest(
- 'REPORT',
- 'https://server2.internal/remote.php/dav/addressbooks/system/system/system',
- ['Content-Type' => 'application/xml'],
- );
- $body = '<?xml version="1.0" encoding="utf-8"?>
- <d:error xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns">
- <s:exception>Sabre\DAV\Exception\NotAuthenticated</s:exception>
- <s:message>No public access to this resource., Username or password was incorrect, No \'Authorization: Bearer\' header found. Either the client didn\'t send one, or the server is mis-configured, Username or password was incorrect</s:message>
- </d:error>';
- $response = new PsrResponse(
- 401,
- ['Content-Type' => 'application/xml; charset=utf-8', 'Content-Length' => strlen($body)],
- $body
- );
- $message = 'Client error: `REPORT https://server2.internal/cloud/remote.php/dav/addressbooks/system/system/system` resulted in a `401 Unauthorized` response:
- <?xml version="1.0" encoding="utf-8"?>
- <d:error xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns">
- <s:exception>Sabre\DA (truncated...)
- ';
- $reportException = new ClientException(
- $message,
- $request,
- $response
- );
- $this->client
- ->method('request')
- ->willThrowException($reportException);
- $this->service->syncRemoteAddressBook(
- '',
- 'system',
- 'system',
- '1234567890',
- null,
- '1',
- 'principals/system/system',
- []
- );
- }
- /**
- * @dataProvider providerUseAbsoluteUriReport
- */
- public function testUseAbsoluteUriReport(string $host, string $expected): void {
- $body = '<?xml version="1.0"?>
- <d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:card="urn:ietf:params:xml:ns:carddav" xmlns:oc="http://owncloud.org/ns">
- <d:sync-token>http://sabre.io/ns/sync/1</d:sync-token>
- </d:multistatus>';
- $requestResponse = new Response(new PsrResponse(
- 207,
- ['Content-Type' => 'application/xml; charset=utf-8', 'Content-Length' => strlen($body)],
- $body
- ));
- $this->client
- ->method('request')
- ->with(
- 'REPORT',
- $this->callback(function ($uri) use ($expected) {
- $this->assertEquals($expected, $uri);
- return true;
- }),
- $this->callback(function ($options) {
- $this->assertIsArray($options);
- return true;
- }),
- )
- ->willReturn($requestResponse);
- $this->service->syncRemoteAddressBook(
- $host,
- 'system',
- 'remote.php/dav/addressbooks/system/system/system',
- '1234567890',
- null,
- '1',
- 'principals/system/system',
- []
- );
- }
- public function providerUseAbsoluteUriReport(): array {
- return [
- ['https://server.internal', 'https://server.internal/remote.php/dav/addressbooks/system/system/system'],
- ['https://server.internal/', 'https://server.internal/remote.php/dav/addressbooks/system/system/system'],
- ['https://server.internal/nextcloud', 'https://server.internal/nextcloud/remote.php/dav/addressbooks/system/system/system'],
- ['https://server.internal/nextcloud/', 'https://server.internal/nextcloud/remote.php/dav/addressbooks/system/system/system'],
- ['https://server.internal:8080', 'https://server.internal:8080/remote.php/dav/addressbooks/system/system/system'],
- ['https://server.internal:8080/', 'https://server.internal:8080/remote.php/dav/addressbooks/system/system/system'],
- ['https://server.internal:8080/nextcloud', 'https://server.internal:8080/nextcloud/remote.php/dav/addressbooks/system/system/system'],
- ['https://server.internal:8080/nextcloud/', 'https://server.internal:8080/nextcloud/remote.php/dav/addressbooks/system/system/system'],
- ];
- }
- }
|