From 1da427f113e56b3c3c519aaf0ef8b224e35b5d24 Mon Sep 17 00:00:00 2001 From: Alexis Saettler Date: Mon, 11 Oct 2021 20:46:42 +0200 Subject: [PATCH] fix: fix distant contact etag handle (#5605) --- .../DAV/Backend/CalDAV/CalDAVBirthdays.php | 2 +- .../DAV/Backend/CalDAV/CalDAVTasks.php | 2 +- .../DAV/Backend/CardDAV/CardDAVBackend.php | 15 ++++-- app/Jobs/Dav/PushVCard.php | 21 +++++--- app/Jobs/Dav/UpdateVCard.php | 15 +++--- app/Providers/AppServiceProvider.php | 2 +- .../Utils/AddressBookContactsPush.php | 25 +++++++--- .../Utils/AddressBookContactsPushMissed.php | 10 +++- .../DavClient/Utils/Model/ContactDto.php | 6 +-- .../DavClient/Utils/Model/ContactPushDto.php | 10 +++- .../Utils/Model/ContactUpdateDto.php | 4 +- app/Services/VCard/GetEtag.php | 38 ++++++++++++++ app/Services/VCard/ImportVCard.php | 48 ++++++++++++------ .../2021_10_11_060512_add_distant_etag.php | 32 ++++++++++++ tests/Api/DAV/CardEtag.php | 2 +- tests/Feature/StorageControllerTest.php | 22 ++++----- tests/Unit/Jobs/Dav/PushVCardTest.php | 6 +-- .../AddressBookContactsPushMissedTest.php | 2 + .../Utils/AddressBookContactsPushTest.php | 8 ++- tests/Unit/Services/VCard/GetEtagTest.php | 49 +++++++++++++++++++ tests/Unit/Services/VCard/ImportVCardTest.php | 8 +-- 21 files changed, 255 insertions(+), 72 deletions(-) create mode 100644 app/Services/VCard/GetEtag.php create mode 100644 database/migrations/2021_10_11_060512_add_distant_etag.php create mode 100644 tests/Unit/Services/VCard/GetEtagTest.php diff --git a/app/Http/Controllers/DAV/Backend/CalDAV/CalDAVBirthdays.php b/app/Http/Controllers/DAV/Backend/CalDAV/CalDAVBirthdays.php index 43335f86b68..e4fff2f4ec2 100644 --- a/app/Http/Controllers/DAV/Backend/CalDAV/CalDAVBirthdays.php +++ b/app/Http/Controllers/DAV/Backend/CalDAV/CalDAVBirthdays.php @@ -62,7 +62,7 @@ public function prepareData($obj) 'id' => $obj->id, 'uri' => $this->encodeUri($obj), 'calendardata' => $calendardata, - 'etag' => '"'.md5($calendardata).'"', + 'etag' => '"'.sha1($calendardata).'"', 'lastmodified' => $obj->updated_at->timestamp, ]; } catch (\Exception $e) { diff --git a/app/Http/Controllers/DAV/Backend/CalDAV/CalDAVTasks.php b/app/Http/Controllers/DAV/Backend/CalDAV/CalDAVTasks.php index f2fedebc932..4004ffc568c 100644 --- a/app/Http/Controllers/DAV/Backend/CalDAV/CalDAVTasks.php +++ b/app/Http/Controllers/DAV/Backend/CalDAV/CalDAVTasks.php @@ -91,7 +91,7 @@ public function prepareData($obj) 'id' => $obj->id, 'uri' => $this->encodeUri($obj), 'calendardata' => $calendardata, - 'etag' => '"'.md5($calendardata).'"', + 'etag' => '"'.sha1($calendardata).'"', 'lastmodified' => $obj->updated_at->timestamp, ]; } catch (\Exception $e) { diff --git a/app/Http/Controllers/DAV/Backend/CardDAV/CardDAVBackend.php b/app/Http/Controllers/DAV/Backend/CardDAV/CardDAVBackend.php index 13f26d8b8b8..c6f4ffd2000 100644 --- a/app/Http/Controllers/DAV/Backend/CardDAV/CardDAVBackend.php +++ b/app/Http/Controllers/DAV/Backend/CardDAV/CardDAVBackend.php @@ -5,6 +5,7 @@ use Sabre\DAV; use App\Jobs\Dav\UpdateVCard; use App\Models\Contact\Contact; +use App\Services\VCard\GetEtag; use App\Models\Account\AddressBook; use App\Services\VCard\ExportVCard; use Illuminate\Support\Facades\Bus; @@ -189,11 +190,17 @@ public function prepareCard($contact): array $carddata = $this->refreshObject($contact); } + $etag = app(GetEtag::class)->execute([ + 'account_id' => $this->user->account_id, + 'contact_id' => $contact->id, + ]); + return [ - 'id' => $contact->hashID(), + 'contact_id' => $contact->id, 'uri' => $this->encodeUri($contact), 'carddata' => $carddata, - 'etag' => '"'.md5($carddata).'"', + 'etag' => $etag, + 'distant_etag' => $contact->distant_etag, 'lastmodified' => $contact->updated_at->timestamp, ]; } catch (\Exception $e) { @@ -364,9 +371,7 @@ public function createCard($addressBookId, $cardUri, $cardData) */ public function updateCard($addressBookId, $cardUri, $cardData): ?string { - $dto = new ContactUpdateDto($cardUri, '"'.md5($cardData).'"', $cardData); - - $job = new UpdateVCard($this->user, $addressBookId, $dto); + $job = new UpdateVCard($this->user, $addressBookId, new ContactUpdateDto($cardUri, null, $cardData)); Bus::batch([$job]) ->allowFailures() diff --git a/app/Jobs/Dav/PushVCard.php b/app/Jobs/Dav/PushVCard.php index f3daf6f2148..e249dbdc5dd 100644 --- a/app/Jobs/Dav/PushVCard.php +++ b/app/Jobs/Dav/PushVCard.php @@ -4,6 +4,7 @@ use Illuminate\Bus\Batchable; use Illuminate\Bus\Queueable; +use App\Models\Contact\Contact; use Illuminate\Support\Facades\Log; use Illuminate\Queue\SerializesModels; use Illuminate\Queue\InteractsWithQueue; @@ -53,17 +54,23 @@ public function handle(): void $headers = []; - if ($this->contact->mode === 1) { - $headers['If-Match'] = $this->contact->etag; - } elseif ($this->contact->mode === 2) { - $headers['If-Match'] = '*'; + switch ($this->contact->mode) { + case ContactPushDto::MODE_MATCH_ETAG: + $headers['If-Match'] = $this->contact->etag; + break; + case ContactPushDto::MODE_MATCH_ANY: + $headers['If-Match'] = '*'; + break; } $response = $this->subscription->getClient() ->request('PUT', $this->contact->uri, $this->contact->card, $headers); - if (! empty($etag = $response->header('Etag')) && $etag !== $this->contact->etag) { - Log::warning(__CLASS__.' wrong etag when updating contact. Expected '.$this->contact->etag.', get '.$etag); - } + $etag = $response->header('Etag'); + + $contact = Contact::where('account_id', $this->subscription->account_id) + ->findOrFail($this->contact->contactId); + $contact->distant_etag = empty($etag) ? null : $etag; + $contact->save(); } } diff --git a/app/Jobs/Dav/UpdateVCard.php b/app/Jobs/Dav/UpdateVCard.php index 747daf43e64..aecbc320c99 100644 --- a/app/Jobs/Dav/UpdateVCard.php +++ b/app/Jobs/Dav/UpdateVCard.php @@ -6,7 +6,7 @@ use Illuminate\Support\Arr; use Illuminate\Bus\Batchable; use Illuminate\Bus\Queueable; -use App\Models\Contact\Contact; +use App\Services\VCard\GetEtag; use App\Services\VCard\ImportVCard; use Illuminate\Support\Facades\Log; use Illuminate\Queue\SerializesModels; @@ -62,7 +62,7 @@ public function handle(): void $newtag = $this->updateCard($this->addressBookName, $this->contact->uri, $this->contact->card); - if ($newtag !== $this->contact->etag) { + if (! is_null($this->contact->etag) && $newtag !== $this->contact->etag) { Log::warning(__CLASS__.' wrong etag when updating contact. Expected '.$this->contact->etag.', get '.$newtag, [ 'contacturl' => $this->contact->uri, 'carddata' => $this->contact->card, @@ -98,18 +98,19 @@ private function updateCard($addressBookId, $cardUri, $cardData): ?string 'user_id' => $this->user->id, 'contact_id' => $contact_id, 'entry' => $cardData, + 'etag' => $this->contact->etag, 'behaviour' => ImportVCard::BEHAVIOUR_REPLACE, 'addressBookName' => $addressBookId === $backend->backendUri() ? null : $addressBookId, ]); if (! Arr::has($result, 'error')) { - $contact = Contact::where('account_id', $this->user->account_id) - ->find($result['contact_id']); - - return '"'.md5($contact->vcard).'"'; + return app(GetEtag::class)->execute([ + 'account_id' => $this->user->account_id, + 'contact_id' => $result['contact_id'], + ]); } } catch (\Exception $e) { - Log::debug(__CLASS__.' updateCard: '.(string) $e, [ + Log::debug(__CLASS__.' updateCard: '.$e->getMessage(), [ $e, 'contacturl' => $cardUri, 'carddata' => $cardData, diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index c99d3b66a54..38afaf86540 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -86,7 +86,7 @@ public function boot() $url = $request->getRequestUri(); return Cache::rememberForever('etag.'.$url, function () use ($url) { - return md5($url); + return sha1($url); }); }); } diff --git a/app/Services/DavClient/Utils/AddressBookContactsPush.php b/app/Services/DavClient/Utils/AddressBookContactsPush.php index 60c6f4821b4..b888c129bad 100644 --- a/app/Services/DavClient/Utils/AddressBookContactsPush.php +++ b/app/Services/DavClient/Utils/AddressBookContactsPush.php @@ -48,9 +48,15 @@ private function preparePushAddedContacts(array $contacts): Collection ->map(function (string $uri): ?PushVCard { $card = $this->backend()->getCard($this->sync->addressBookName(), $uri); - return $card !== false - ? new PushVCard($this->sync->subscription, new ContactPushDto($uri, $card['etag'], $card['carddata'])) - : null; + return $card === false ? null + : new PushVCard($this->sync->subscription, + new ContactPushDto( + $uri, + $card['distant_etag'], + $card['carddata'], + $card['contact_id'] + ) + ); }); } @@ -78,9 +84,16 @@ private function preparePushChangedContacts(Collection $changes, array $contacts })->map(function (string $uri) use ($backend): ?PushVCard { $card = $backend->getCard($this->sync->addressBookName(), $uri); - return $card !== false - ? new PushVCard($this->sync->subscription, new ContactPushDto($uri, $card['etag'], $card['carddata'], ContactPushDto::MODE_MATCH_ETAG)) - : null; + return $card === false ? null + : new PushVCard($this->sync->subscription, + new ContactPushDto( + $uri, + $card['distant_etag'], + $card['carddata'], + $card['contact_id'], + $card['distant_etag'] !== null ? ContactPushDto::MODE_MATCH_ETAG : ContactPushDto::MODE_MATCH_ANY + ) + ); }); } } diff --git a/app/Services/DavClient/Utils/AddressBookContactsPushMissed.php b/app/Services/DavClient/Utils/AddressBookContactsPushMissed.php index e4523d2839a..3c84a4694b8 100644 --- a/app/Services/DavClient/Utils/AddressBookContactsPushMissed.php +++ b/app/Services/DavClient/Utils/AddressBookContactsPushMissed.php @@ -64,7 +64,15 @@ private function preparePushMissedContacts(array $added, Collection $distContact })->map(function (Contact $contact) use ($backend): PushVCard { $card = $backend->prepareCard($contact); - return new PushVCard($this->sync->subscription, new ContactPushDto($card['uri'], $card['etag'], $card['carddata'], ContactPushDto::MODE_MATCH_ANY)); + return new PushVCard($this->sync->subscription, + new ContactPushDto( + $card['uri'], + $contact->distant_etag, + $card['carddata'], + $contact->id, + ContactPushDto::MODE_MATCH_ANY + ) + ); }); } } diff --git a/app/Services/DavClient/Utils/Model/ContactDto.php b/app/Services/DavClient/Utils/Model/ContactDto.php index 3a1063750a8..4c21df6c280 100644 --- a/app/Services/DavClient/Utils/Model/ContactDto.php +++ b/app/Services/DavClient/Utils/Model/ContactDto.php @@ -10,7 +10,7 @@ class ContactDto public $uri; /** - * @var string + * @var string|null */ public $etag; @@ -18,9 +18,9 @@ class ContactDto * Create a new ContactDto. * * @param string $uri - * @param string $etag + * @param string|null $etag */ - public function __construct(string $uri, string $etag) + public function __construct(string $uri, ?string $etag) { $this->uri = $uri; $this->etag = $etag; diff --git a/app/Services/DavClient/Utils/Model/ContactPushDto.php b/app/Services/DavClient/Utils/Model/ContactPushDto.php index 3f35ee730eb..7135b88ee36 100644 --- a/app/Services/DavClient/Utils/Model/ContactPushDto.php +++ b/app/Services/DavClient/Utils/Model/ContactPushDto.php @@ -15,17 +15,23 @@ class ContactPushDto extends ContactUpdateDto public const MODE_MATCH_ANY = 2; + /** + * @var int + */ + public $contactId; + /** * Create a new ContactPushDto. * * @param string $uri - * @param string $etag + * @param string|null $etag * @param string|resource $card * @param int $mode */ - public function __construct(string $uri, string $etag, $card, int $mode = self::MODE_MATCH_NONE) + public function __construct(string $uri, ?string $etag, $card, int $contact_id, int $mode = self::MODE_MATCH_NONE) { parent::__construct($uri, $etag, $card); $this->mode = $mode; + $this->contactId = $contact_id; } } diff --git a/app/Services/DavClient/Utils/Model/ContactUpdateDto.php b/app/Services/DavClient/Utils/Model/ContactUpdateDto.php index 184e8a3c43c..9484ef0d0a6 100644 --- a/app/Services/DavClient/Utils/Model/ContactUpdateDto.php +++ b/app/Services/DavClient/Utils/Model/ContactUpdateDto.php @@ -16,10 +16,10 @@ class ContactUpdateDto extends ContactDto * Create a new ContactUpdateDto. * * @param string $uri - * @param string $etag + * @param string|null $etag * @param string|resource $card */ - public function __construct(string $uri, string $etag, $card) + public function __construct(string $uri, ?string $etag, $card) { parent::__construct($uri, $etag); $this->card = self::transformCard($card); diff --git a/app/Services/VCard/GetEtag.php b/app/Services/VCard/GetEtag.php new file mode 100644 index 00000000000..776f795aaad --- /dev/null +++ b/app/Services/VCard/GetEtag.php @@ -0,0 +1,38 @@ + 'required|integer|exists:accounts,id', + 'contact_id' => 'required|integer|exists:contacts,id', + ]; + } + + /** + * Export etag of the VCard. + * + * @param array $data + * @return string + */ + public function execute(array $data): string + { + $this->validate($data); + + $contact = Contact::where('account_id', $data['account_id']) + ->findOrFail($data['contact_id']); + + return $contact->distant_etag ?? '"'.sha1($contact->vcard).'"'; + } +} diff --git a/app/Services/VCard/ImportVCard.php b/app/Services/VCard/ImportVCard.php index 925cadbc24b..85942eb6180 100644 --- a/app/Services/VCard/ImportVCard.php +++ b/app/Services/VCard/ImportVCard.php @@ -124,6 +124,7 @@ function ($attribute, $value, $fail) { Rule::in(self::$behaviourTypes), ], 'addressBookName' => 'nullable|string|exists:addressbooks,name', + 'etag' => 'nullable|string', ]; } @@ -192,9 +193,13 @@ private function process(array $data): array ])->first(); } - $entry = $this->getEntry($data); + /** + * @var VCard|null $entry + * @var string $vcard + */ + ['entry' => $entry, 'vcard' => $vcard] = $this->getEntry($data); - if (! $entry) { + if ($entry === null) { return [ 'error' => 'ERROR_PARSER', 'reason' => $this->errorResults['ERROR_PARSER'], @@ -202,7 +207,7 @@ private function process(array $data): array ]; } - return $this->processEntry($data, $entry); + return $this->processEntry($data, $entry, $vcard); } /** @@ -210,9 +215,10 @@ private function process(array $data): array * * @param array $data * @param VCard $entry + * @param string $vcard * @return array */ - private function processEntry(array $data, VCard $entry): array + private function processEntry(array $data, VCard $entry, string $vcard): array { if (! $this->canImportCurrentEntry($entry)) { return [ @@ -225,7 +231,7 @@ private function processEntry(array $data, VCard $entry): array $contactId = Arr::get($data, 'contact_id'); $contact = $this->getExistingContact($entry, $contactId); - return $this->processEntryContact($data, $entry, $contact); + return $this->processEntryContact($data, $entry, $vcard, $contact); } /** @@ -233,10 +239,11 @@ private function processEntry(array $data, VCard $entry): array * * @param array $data * @param VCard $entry + * @param string $vcard * @param Contact|null $contact * @return array */ - private function processEntryContact(array $data, VCard $entry, $contact): array + private function processEntryContact(array $data, VCard $entry, string $vcard, ?Contact $contact): array { $behaviour = $data['behaviour'] ?: self::BEHAVIOUR_ADD; if ($contact && $behaviour === self::BEHAVIOUR_ADD) { @@ -253,7 +260,7 @@ private function processEntryContact(array $data, VCard $entry, $contact): array $contact->timestamps = false; } - $contact = $this->importEntry($contact, $entry); + $contact = $this->importEntry($contact, $entry, $vcard, Arr::get($data, 'etag')); if (isset($timestamps)) { $contact->timestamps = $timestamps; @@ -267,25 +274,31 @@ private function processEntryContact(array $data, VCard $entry, $contact): array /** * @param array $data - * @return VCard|null + * @return array */ - private function getEntry($data): ?VCard + private function getEntry($data): array { - $entry = $data['entry']; + $entry = $vcard = $data['entry']; if (! $entry instanceof VCard) { try { $entry = Reader::read($entry, Reader::OPTION_FORGIVING + Reader::OPTION_IGNORE_INVALID_LINES); } catch (ParseException $e) { - return null; + return [ + 'entry' => null, + 'vcard' => $vcard, + ]; } } - if ($entry instanceof VCard) { - return $entry; + if ($vcard instanceof VCard) { + $vcard = $entry->serialize(); } - return null; + return [ + 'entry' => $entry, + 'vcard' => $vcard, + ]; } /** @@ -518,9 +531,11 @@ private function existingUuid(VCard $entry): ?Contact * * @param Contact|null $contact * @param VCard $entry + * @param string $vcard + * @param string|null $etag * @return Contact */ - private function importEntry(?Contact $contact, VCard $entry): Contact + private function importEntry(?Contact $contact, VCard $entry, string $vcard, ?string $etag): Contact { $contact = $this->importGeneralInformation($contact, $entry); @@ -535,7 +550,8 @@ private function importEntry(?Contact $contact, VCard $entry): Contact // Save vcard content if ($contact->address_book_id) { - $contact->vcard = $entry->serialize(); + $contact->vcard = $vcard; + $contact->distant_etag = $etag; } $contact->save(); diff --git a/database/migrations/2021_10_11_060512_add_distant_etag.php b/database/migrations/2021_10_11_060512_add_distant_etag.php new file mode 100644 index 00000000000..0e86ffaeb80 --- /dev/null +++ b/database/migrations/2021_10_11_060512_add_distant_etag.php @@ -0,0 +1,32 @@ +string('distant_etag', 256)->after('vcard')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('contacts', function (Blueprint $table) { + $table->dropColumn('distant_etag'); + }); + } +} diff --git a/tests/Api/DAV/CardEtag.php b/tests/Api/DAV/CardEtag.php index 14c453cc3ef..5803855a587 100644 --- a/tests/Api/DAV/CardEtag.php +++ b/tests/Api/DAV/CardEtag.php @@ -20,7 +20,7 @@ protected function getEtag($obj, bool $quotes = false) $data = $this->getVTodo($obj, true); } - $etag = md5($data); + $etag = sha1($data); if ($quotes) { $etag = '"'.$etag.'"'; } diff --git a/tests/Feature/StorageControllerTest.php b/tests/Feature/StorageControllerTest.php index 1c56dcbf54e..25fa6223e1a 100644 --- a/tests/Feature/StorageControllerTest.php +++ b/tests/Feature/StorageControllerTest.php @@ -46,7 +46,7 @@ public function it_get_photo_content() $response->assertStatus(200); $response->assertHeader('Last-Modified', 'Sat, 19 Jun 2021 07:00:00 GMT'); $response->assertHeader('Cache-Control', 'max-age=2628000, private'); - $response->assertHeader('etag', '"'.md5('/store/'.$file).'"'); + $response->assertHeader('etag', '"'.sha1('/store/'.$file).'"'); } /** @test */ @@ -63,7 +63,7 @@ public function it_get_avatar_content() $response->assertStatus(200); $response->assertHeader('Last-Modified', 'Sat, 19 Jun 2021 07:00:00 GMT'); $response->assertHeader('Cache-Control', 'max-age=2628000, private'); - $response->assertHeader('etag', '"'.md5('/store/'.$file).'"'); + $response->assertHeader('etag', '"'.sha1('/store/'.$file).'"'); } /** @test */ @@ -80,7 +80,7 @@ public function it_get_document_content() $response->assertStatus(200); $response->assertHeader('Last-Modified', 'Sat, 19 Jun 2021 07:00:00 GMT'); $response->assertHeader('Cache-Control', 'max-age=2628000, private'); - $response->assertHeader('etag', '"'.md5('/store/'.$file).'"'); + $response->assertHeader('etag', '"'.sha1('/store/'.$file).'"'); } /** @test */ @@ -123,7 +123,7 @@ public function it_returns_200_if_modified_after_IfModifiedSince() $response->assertStatus(200); $response->assertHeader('Last-Modified', 'Sat, 19 Jun 2021 07:00:00 GMT'); $response->assertHeader('Cache-Control', 'max-age=2628000, private'); - $response->assertHeader('etag', '"'.md5('/store/'.$file).'"'); + $response->assertHeader('etag', '"'.sha1('/store/'.$file).'"'); } /** @test */ @@ -142,7 +142,7 @@ public function it_returns_304_if_not_modified_since_IfModifiedSince() $response->assertNoContent(304); $response->assertHeaderMissing('Last-Modified'); $response->assertHeader('Cache-Control', 'max-age=2628000, private'); - $response->assertHeader('etag', '"'.md5('/store/'.$file).'"'); + $response->assertHeader('etag', '"'.sha1('/store/'.$file).'"'); } /** @test */ @@ -161,7 +161,7 @@ public function it_returns_200_if_not_modified_after_IfUnmodifiedSince() $response->assertStatus(200); $response->assertHeader('Last-Modified', 'Sat, 19 Jun 2021 07:00:00 GMT'); $response->assertHeader('Cache-Control', 'max-age=2628000, private'); - $response->assertHeader('etag', '"'.md5('/store/'.$file).'"'); + $response->assertHeader('etag', '"'.sha1('/store/'.$file).'"'); } /** @test */ @@ -242,13 +242,13 @@ public function it_returns_200_if_matching_IfMatch() $file = $this->storeImage($contact); $response = $this->get('/store/'.$file, [ - 'If-Match' => '"'.md5('/store/'.$file).'"', + 'If-Match' => '"'.sha1('/store/'.$file).'"', ]); $response->assertNoContent(200); $response->assertHeader('Last-Modified', 'Sat, 19 Jun 2021 07:00:00 GMT'); $response->assertHeader('Cache-Control', 'max-age=2628000, private'); - $response->assertHeader('etag', '"'.md5('/store/'.$file).'"'); + $response->assertHeader('etag', '"'.sha1('/store/'.$file).'"'); } /** @test */ @@ -267,7 +267,7 @@ public function it_returns_200_with_none_matching_IfNoneMatch() $response->assertNoContent(200); $response->assertHeader('Last-Modified', 'Sat, 19 Jun 2021 07:00:00 GMT'); $response->assertHeader('Cache-Control', 'max-age=2628000, private'); - $response->assertHeader('etag', '"'.md5('/store/'.$file).'"'); + $response->assertHeader('etag', '"'.sha1('/store/'.$file).'"'); } /** @test */ @@ -280,13 +280,13 @@ public function it_returns_304_if_matching_IfNoneMatch() $file = $this->storeImage($contact); $response = $this->get('/store/'.$file, [ - 'If-None-Match' => '"'.md5('/store/'.$file).'"', + 'If-None-Match' => '"'.sha1('/store/'.$file).'"', ]); $response->assertNoContent(304); $response->assertHeaderMissing('Last-Modified'); $response->assertHeader('Cache-Control', 'max-age=2628000, private'); - $response->assertHeader('etag', '"'.md5('/store/'.$file).'"'); + $response->assertHeader('etag', '"'.sha1('/store/'.$file).'"'); } public function storeImage(Contact $contact) diff --git a/tests/Unit/Jobs/Dav/PushVCardTest.php b/tests/Unit/Jobs/Dav/PushVCardTest.php index 9a97d029cfe..bd210164465 100644 --- a/tests/Unit/Jobs/Dav/PushVCardTest.php +++ b/tests/Unit/Jobs/Dav/PushVCardTest.php @@ -36,8 +36,8 @@ public function it_push_card($mode, $ifmatch) 'uri' => 'https://test/dav', ]); - $contact = new Contact(); - $contact->forceFill([ + $contact = factory(Contact::class)->create([ + 'account_id' => $user->account_id, 'first_name' => 'John', 'last_name' => 'Doe', 'uuid' => 'affacde9-b2fe-4371-9acb-6612aaee6971', @@ -59,7 +59,7 @@ public function it_push_card($mode, $ifmatch) }); $pendingBatch = $fake->batch([ - $job = new PushVCard($addressBookSubscription, new ContactPushDto('https://test/dav/uri', $etag, $card, $mode)), + $job = new PushVCard($addressBookSubscription, new ContactPushDto('https://test/dav/uri', $etag, $card, $contact->id, $mode)), ]); $batch = $pendingBatch->dispatch(); diff --git a/tests/Unit/Services/DavClient/Utils/AddressBookContactsPushMissedTest.php b/tests/Unit/Services/DavClient/Utils/AddressBookContactsPushMissedTest.php index 58b8af0153c..9f3a2f428aa 100644 --- a/tests/Unit/Services/DavClient/Utils/AddressBookContactsPushMissedTest.php +++ b/tests/Unit/Services/DavClient/Utils/AddressBookContactsPushMissedTest.php @@ -61,6 +61,8 @@ public function it_push_contacts_missed() return true; }) ->andReturn([ + 'account_id' => $contact->account_id, + 'contact_id' => $contact->id, 'carddata' => $card, 'uri' => 'uuid3', 'etag' => $etag, diff --git a/tests/Unit/Services/DavClient/Utils/AddressBookContactsPushTest.php b/tests/Unit/Services/DavClient/Utils/AddressBookContactsPushTest.php index 093c406db92..f41c3108ac6 100644 --- a/tests/Unit/Services/DavClient/Utils/AddressBookContactsPushTest.php +++ b/tests/Unit/Services/DavClient/Utils/AddressBookContactsPushTest.php @@ -44,7 +44,7 @@ public function it_push_contacts_added() $card = $this->getCard($contact); $etag = $this->getEtag($contact, true); - $this->mock(CardDAVBackend::class, function (MockInterface $mock) use ($card, $etag) { + $this->mock(CardDAVBackend::class, function (MockInterface $mock) use ($contact, $card, $etag) { $mock->shouldReceive('init')->andReturn($mock); $mock->shouldReceive('getCard') ->withArgs(function ($name, $uri) { @@ -53,8 +53,10 @@ public function it_push_contacts_added() return true; }) ->andReturn([ + 'contact_id' => $contact->id, 'carddata' => $card, 'etag' => $etag, + 'distant_etag' => $etag, ]); $mock->shouldReceive('getUuid') ->withArgs(function ($uri) { @@ -104,7 +106,7 @@ public function it_push_contacts_modified() $card = $this->getCard($contact); $etag = $this->getEtag($contact, true); - $this->mock(CardDAVBackend::class, function (MockInterface $mock) use ($card, $etag) { + $this->mock(CardDAVBackend::class, function (MockInterface $mock) use ($contact, $card, $etag) { $mock->shouldReceive('init')->andReturn($mock); $mock->shouldReceive('getUuid') ->withArgs(function ($uri) { @@ -122,8 +124,10 @@ public function it_push_contacts_modified() return true; }) ->andReturn([ + 'contact_id' => $contact->id, 'carddata' => $card, 'etag' => $etag, + 'distant_etag' => $etag, ]); }); diff --git a/tests/Unit/Services/VCard/GetEtagTest.php b/tests/Unit/Services/VCard/GetEtagTest.php new file mode 100644 index 00000000000..b67cf30d803 --- /dev/null +++ b/tests/Unit/Services/VCard/GetEtagTest.php @@ -0,0 +1,49 @@ +create(); + $contact = factory(Contact::class)->create([ + 'account_id' => $account->id, + 'vcard' => 'test', + ]); + + $etag = app(GetEtag::class)->execute([ + 'account_id' => $account->id, + 'contact_id' => $contact->id, + ]); + + $this->assertEquals('"a94a8fe5ccb19ba61c4c0873d391e987982fbbd3"', $etag); + } + + /** @test */ + public function it_get_etag_distant_contact() + { + $account = factory(Account::class)->create(); + $contact = factory(Contact::class)->create([ + 'account_id' => $account->id, + 'vcard' => 'test', + 'distant_etag' => '"test"', + ]); + + $etag = app(GetEtag::class)->execute([ + 'account_id' => $account->id, + 'contact_id' => $contact->id, + ]); + + $this->assertEquals('"test"', $etag); + } +} diff --git a/tests/Unit/Services/VCard/ImportVCardTest.php b/tests/Unit/Services/VCard/ImportVCardTest.php index b7ec1147db9..96f8f4922f4 100644 --- a/tests/Unit/Services/VCard/ImportVCardTest.php +++ b/tests/Unit/Services/VCard/ImportVCardTest.php @@ -262,7 +262,7 @@ public function it_creates_a_contact() 'type' => 'email', ]); - $contact = $this->invokePrivateMethod($importVCard, 'importEntry', [null, $vcard]); + $contact = $this->invokePrivateMethod($importVCard, 'importEntry', [null, $vcard, $vcard->serialize(), null]); $this->assertTrue($contact->exists); } @@ -292,7 +292,7 @@ public function it_creates_a_contact_in_address_book() 'type' => 'email', ]); - $contact = $this->invokePrivateMethod($importVCard, 'importEntry', [null, $vcard]); + $contact = $this->invokePrivateMethod($importVCard, 'importEntry', [null, $vcard, $vcard->serialize(), null]); $this->assertTrue($contact->exists); $this->assertEquals($addressBook->id, $contact->address_book_id); @@ -420,6 +420,7 @@ public function it_creates_a_contact_with_process() $result = $this->invokePrivateMethod($importVCard, 'processEntry', [ ['behaviour' => 'behaviour_add'], $vcard, + $vcard->serialize(), ]); $this->assertDatabaseHas('contacts', [ @@ -449,6 +450,7 @@ public function it_updates_a_contact_with_process() 'contact_id' => $contact->id, ], $vcard, + $vcard->serialize(), ]); $this->assertDatabaseHas('contacts', [ @@ -1332,7 +1334,7 @@ public function it_imports_uuid_contact() 'UID' => '31fdc242-c974-436e-98de-6b21624d6e34', ]); - $contact = $this->invokePrivateMethod($importVCard, 'importEntry', [null, $vcard]); + $contact = $this->invokePrivateMethod($importVCard, 'importEntry', [null, $vcard, $vcard->serialize(), null]); $this->assertDatabaseHas('contacts', [ 'account_id' => $user->account_id,