diff options
-rw-r--r-- | apps/dav/appinfo/app.php | 12 | ||||
-rw-r--r-- | apps/dav/command/createaddressbook.php | 1 | ||||
-rw-r--r-- | apps/dav/command/syncsystemaddressbook.php | 1 | ||||
-rw-r--r-- | apps/dav/lib/carddav/carddavbackend.php | 7 | ||||
-rw-r--r-- | apps/dav/lib/carddav/syncservice.php | 184 | ||||
-rw-r--r-- | apps/dav/lib/rootcollection.php | 4 | ||||
-rw-r--r-- | apps/dav/lib/server.php | 10 | ||||
-rw-r--r-- | apps/dav/tests/unit/carddav/carddavbackendtest.php | 14 | ||||
-rw-r--r-- | apps/dav/tests/unit/carddav/syncservicetest.php | 92 | ||||
-rw-r--r-- | apps/federation/appinfo/application.php | 16 | ||||
-rw-r--r-- | apps/federation/appinfo/database.xml | 8 | ||||
-rw-r--r-- | apps/federation/appinfo/info.xml | 5 | ||||
-rw-r--r-- | apps/federation/appinfo/register_command.php | 8 | ||||
-rw-r--r-- | apps/federation/command/syncfederationaddressbooks.php | 72 | ||||
-rw-r--r-- | apps/federation/dav/fedauth.php | 54 | ||||
-rw-r--r-- | apps/federation/lib/dbhandler.php | 31 | ||||
-rw-r--r-- | apps/federation/tests/dav/fedauthtest.php | 52 | ||||
-rw-r--r-- | apps/federation/tests/lib/dbhandlertest.php | 26 | ||||
-rw-r--r-- | lib/public/sabrepluginevent.php | 15 |
19 files changed, 582 insertions, 30 deletions
diff --git a/apps/dav/appinfo/app.php b/apps/dav/appinfo/app.php index bc889176f7f..51689b965da 100644 --- a/apps/dav/appinfo/app.php +++ b/apps/dav/appinfo/app.php @@ -19,6 +19,18 @@ * */ +use OCA\DAV\CardDAV\CardDavBackend; +use OCA\DAV\CardDAV\SyncService; + +\OC::$server->registerService('CardDAVSyncService', function() { + + $app = new \OCA\Dav\AppInfo\Application(); + /** @var CardDavBackend */ + $backend = $app->getContainer()->query('CardDavBackend'); + + return new SyncService($backend); +}); + $cm = \OC::$server->getContactsManager(); $cm->register(function() use ($cm) { $userId = \OC::$server->getUserSession()->getUser()->getUID(); diff --git a/apps/dav/command/createaddressbook.php b/apps/dav/command/createaddressbook.php index 7b70cea7f80..4d72c12954f 100644 --- a/apps/dav/command/createaddressbook.php +++ b/apps/dav/command/createaddressbook.php @@ -63,7 +63,6 @@ class CreateAddressBook extends Command { throw new \InvalidArgumentException("User <$user> in unknown."); } $principalBackend = new Principal( - $this->config, $this->userManager ); diff --git a/apps/dav/command/syncsystemaddressbook.php b/apps/dav/command/syncsystemaddressbook.php index 162ab362892..8e04df46098 100644 --- a/apps/dav/command/syncsystemaddressbook.php +++ b/apps/dav/command/syncsystemaddressbook.php @@ -57,7 +57,6 @@ class SyncSystemAddressBook extends Command { */ protected function execute(InputInterface $input, OutputInterface $output) { $principalBackend = new Principal( - $this->config, $this->userManager ); diff --git a/apps/dav/lib/carddav/carddavbackend.php b/apps/dav/lib/carddav/carddavbackend.php index 95175b20d1b..cdb3481eaf5 100644 --- a/apps/dav/lib/carddav/carddavbackend.php +++ b/apps/dav/lib/carddav/carddavbackend.php @@ -37,9 +37,6 @@ class CardDavBackend implements BackendInterface, SyncSupport { /** @var Principal */ private $principalBackend; - /** @var ILogger */ - private $logger; - /** @var string */ private $dbCardsTable = 'cards'; @@ -59,12 +56,10 @@ class CardDavBackend implements BackendInterface, SyncSupport { * * @param IDBConnection $db * @param Principal $principalBackend - * @param ILogger $logger */ - public function __construct(IDBConnection $db, Principal $principalBackend, ILogger $logger) { + public function __construct(IDBConnection $db, Principal $principalBackend) { $this->db = $db; $this->principalBackend = $principalBackend; - $this->logger = $logger; } /** diff --git a/apps/dav/lib/carddav/syncservice.php b/apps/dav/lib/carddav/syncservice.php new file mode 100644 index 00000000000..148ba4b9081 --- /dev/null +++ b/apps/dav/lib/carddav/syncservice.php @@ -0,0 +1,184 @@ +<?php +/** + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OCA\DAV\CardDAV; + +use Sabre\DAV\Client; +use Sabre\DAV\Xml\Response\MultiStatus; +use Sabre\DAV\Xml\Service; +use Sabre\HTTP\ClientException; + +class SyncService { + + /** @var CardDavBackend */ + private $backend; + + public function __construct(CardDavBackend $backend) { + $this->backend = $backend; + } + + /** + * @param string $url + * @param string $userName + * @param string $sharedSecret + * @param string $syncToken + * @param int $targetBookId + * @param string $targetPrincipal + * @param array $targetProperties + * @return string + */ + public function syncRemoteAddressBook($url, $userName, $sharedSecret, $syncToken, $targetBookId, $targetPrincipal, $targetProperties) { + // 1. create addressbook + $book = $this->ensureSystemAddressBookExists($targetPrincipal, $targetBookId, $targetProperties); + $addressBookId = $book['id']; + + // 2. query changes + $response = $this->requestSyncReport($url, $userName, $sharedSecret, $syncToken); + + // 3. apply changes + // TODO: use multi-get for download + foreach ($response['response'] as $resource => $status) { + $cardUri = basename($resource); + if (isset($status[200])) { + $vCard = $this->download($url, $sharedSecret, $resource); + $existingCard = $this->backend->getCard($addressBookId, $cardUri); + if ($existingCard === false) { + $this->backend->createCard($addressBookId, $cardUri, $vCard['body']); + } else { + $this->backend->updateCard($addressBookId, $cardUri, $vCard['body']); + } + } else { + $this->backend->deleteCard($addressBookId, $cardUri); + } + } + + return $response['token']; + } + + /** + * @param string $principal + * @param string $id + * @param array $properties + * @return array|null + * @throws \Sabre\DAV\Exception\BadRequest + */ + protected function ensureSystemAddressBookExists($principal, $id, $properties) { + $book = $this->backend->getAddressBooksByUri($id); + if (!is_null($book)) { + return $book; + } + $this->backend->createAddressBook($principal, $id, $properties); + + return $this->backend->getAddressBooksByUri($id); + } + + /** + * @param string $url + * @param string $userName + * @param string $sharedSecret + * @param string $syncToken + * @return array + */ + protected function requestSyncReport($url, $userName, $sharedSecret, $syncToken) { + $settings = [ + 'baseUri' => $url . '/', + 'userName' => $userName, + 'password' => $sharedSecret, + ]; + $client = new Client($settings); + $client->setThrowExceptions(true); + + $addressBookUrl = "remote.php/dav/addressbooks/system/system/system"; + $body = $this->buildSyncCollectionRequestBody($syncToken); + + $response = $client->request('REPORT', $addressBookUrl, $body, [ + 'Content-Type' => 'application/xml' + ]); + + $result = $this->parseMultiStatus($response['body']); + + return $result; + } + + /** + * @param string $url + * @param string $sharedSecret + * @param string $resourcePath + * @return array + */ + private function download($url, $sharedSecret, $resourcePath) { + $settings = [ + 'baseUri' => $url, + 'userName' => 'system', + 'password' => $sharedSecret, + ]; + $client = new Client($settings); + $client->setThrowExceptions(true); + + $response = $client->request('GET', $resourcePath); + return $response; + } + + /** + * @param string|null $syncToken + * @return string + */ + private function buildSyncCollectionRequestBody($syncToken) { + + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->formatOutput = true; + $root = $dom->createElementNS('DAV:', 'd:sync-collection'); + $sync = $dom->createElement('d:sync-token', $syncToken); + $prop = $dom->createElement('d:prop'); + $cont = $dom->createElement('d:getcontenttype'); + $etag = $dom->createElement('d:getetag'); + + $prop->appendChild($cont); + $prop->appendChild($etag); + $root->appendChild($sync); + $root->appendChild($prop); + $dom->appendChild($root); + $body = $dom->saveXML(); + + return $body; + } + + /** + * @param string $body + * @return array + * @throws \Sabre\Xml\ParseException + */ + private function parseMultiStatus($body) { + $xml = new Service(); + + /** @var MultiStatus $multiStatus */ + $multiStatus = $xml->expect('{DAV:}multistatus', $body); + + $result = []; + foreach ($multiStatus->getResponses() as $response) { + $result[$response->getHref()] = $response->getResponseProperties(); + } + + return ['response' => $result, 'token' => $multiStatus->getSyncToken()]; + } + + +} diff --git a/apps/dav/lib/rootcollection.php b/apps/dav/lib/rootcollection.php index 2261712200a..053ff47f977 100644 --- a/apps/dav/lib/rootcollection.php +++ b/apps/dav/lib/rootcollection.php @@ -46,11 +46,11 @@ class RootCollection extends SimpleCollection { \OC::$server->getSystemTagObjectMapper() ); - $usersCardDavBackend = new CardDavBackend($db, $userPrincipalBackend, \OC::$server->getLogger()); + $usersCardDavBackend = new CardDavBackend($db, $userPrincipalBackend); $usersAddressBookRoot = new AddressBookRoot($userPrincipalBackend, $usersCardDavBackend, 'principals/users'); $usersAddressBookRoot->disableListing = $disableListing; - $systemCardDavBackend = new CardDavBackend($db, $userPrincipalBackend, \OC::$server->getLogger()); + $systemCardDavBackend = new CardDavBackend($db, $userPrincipalBackend); $systemAddressBookRoot = new AddressBookRoot(new SystemPrincipalBackend(), $systemCardDavBackend, 'principals/system'); $systemAddressBookRoot->disableListing = $disableListing; diff --git a/apps/dav/lib/server.php b/apps/dav/lib/server.php index a6ad878d29f..93e903e6bf1 100644 --- a/apps/dav/lib/server.php +++ b/apps/dav/lib/server.php @@ -3,10 +3,12 @@ namespace OCA\DAV; use OCA\DAV\CalDAV\Schedule\IMipPlugin; +use OCA\DAV\Connector\FedAuth; use OCA\DAV\Connector\Sabre\Auth; use OCA\DAV\Connector\Sabre\BlockLegacyClientPlugin; use OCA\DAV\Files\CustomPropertiesBackend; use OCP\IRequest; +use OCP\SabrePluginEvent; use Sabre\DAV\Auth\Plugin; class Server { @@ -35,7 +37,13 @@ class Server { $this->server->setBaseUri($this->baseUri); $this->server->addPlugin(new BlockLegacyClientPlugin(\OC::$server->getConfig())); - $this->server->addPlugin(new Plugin($authBackend, 'ownCloud')); + $authPlugin = new Plugin($authBackend, 'ownCloud'); + $this->server->addPlugin($authPlugin); + + // allow setup of additional auth backends + $event = new SabrePluginEvent($this->server); + $dispatcher->dispatch('OCA\DAV\Connector\Sabre::authInit', $event); + $this->server->addPlugin(new \OCA\DAV\Connector\Sabre\DummyGetResponsePlugin()); $this->server->addPlugin(new \OCA\DAV\Connector\Sabre\ExceptionLoggerPlugin('webdav', $logger)); $this->server->addPlugin(new \OCA\DAV\Connector\Sabre\LockPlugin()); diff --git a/apps/dav/tests/unit/carddav/carddavbackendtest.php b/apps/dav/tests/unit/carddav/carddavbackendtest.php index fe01aa65cca..2f5e9eeded6 100644 --- a/apps/dav/tests/unit/carddav/carddavbackendtest.php +++ b/apps/dav/tests/unit/carddav/carddavbackendtest.php @@ -45,9 +45,6 @@ class CardDavBackendTest extends TestCase { /** @var Principal | \PHPUnit_Framework_MockObject_MockObject */ private $principal; - /** @var ILogger | \PHPUnit_Framework_MockObject_MockObject */ - private $logger; - /** @var IDBConnection */ private $db; @@ -70,11 +67,10 @@ class CardDavBackendTest extends TestCase { ->willReturn([ 'uri' => 'principals/best-friend' ]); - $this->logger = $this->getMock('\OCP\ILogger'); $this->db = \OC::$server->getDatabaseConnection(); - $this->backend = new CardDavBackend($this->db, $this->principal, $this->logger); + $this->backend = new CardDavBackend($this->db, $this->principal); // start every test with a empty cards_properties and cards table $query = $this->db->getQueryBuilder(); @@ -129,7 +125,7 @@ class CardDavBackendTest extends TestCase { /** @var CardDavBackend | \PHPUnit_Framework_MockObject_MockObject $backend */ $backend = $this->getMockBuilder('OCA\DAV\CardDAV\CardDavBackend') - ->setConstructorArgs([$this->db, $this->principal, $this->logger]) + ->setConstructorArgs([$this->db, $this->principal]) ->setMethods(['updateProperties', 'purgeProperties'])->getMock(); // create a new address book @@ -175,7 +171,7 @@ class CardDavBackendTest extends TestCase { public function testMultiCard() { $this->backend = $this->getMockBuilder('OCA\DAV\CardDAV\CardDavBackend') - ->setConstructorArgs([$this->db, $this->principal, $this->logger]) + ->setConstructorArgs([$this->db, $this->principal]) ->setMethods(['updateProperties'])->getMock(); // create a new address book @@ -222,7 +218,7 @@ class CardDavBackendTest extends TestCase { public function testSyncSupport() { $this->backend = $this->getMockBuilder('OCA\DAV\CardDAV\CardDavBackend') - ->setConstructorArgs([$this->db, $this->principal, $this->logger]) + ->setConstructorArgs([$this->db, $this->principal]) ->setMethods(['updateProperties'])->getMock(); // create a new address book @@ -279,7 +275,7 @@ class CardDavBackendTest extends TestCase { $cardId = 2; $backend = $this->getMockBuilder('OCA\DAV\CardDAV\CardDavBackend') - ->setConstructorArgs([$this->db, $this->principal, $this->logger]) + ->setConstructorArgs([$this->db, $this->principal]) ->setMethods(['getCardId'])->getMock(); $backend->expects($this->any())->method('getCardId')->willReturn($cardId); diff --git a/apps/dav/tests/unit/carddav/syncservicetest.php b/apps/dav/tests/unit/carddav/syncservicetest.php new file mode 100644 index 00000000000..a6a773babc2 --- /dev/null +++ b/apps/dav/tests/unit/carddav/syncservicetest.php @@ -0,0 +1,92 @@ +<?php +/** + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ +namespace OCA\DAV\CardDAV; + +use Test\TestCase; + +class SyncServiceTest extends TestCase { + public function testEmptySync() { + $backend = $this->getBackendMock(0, 0, 0); + + $ss = $this->getSyncServiceMock($backend, []); + $return = $ss->syncRemoteAddressBook('', 'system', '1234567890', null, '1', 'principals/system/system', []); + $this->assertEquals('sync-token-1', $return); + } + + public function testSyncWithNewElement() { + $backend = $this->getBackendMock(1, 0, 0); + $backend->method('getCard')->willReturn(false); + + $ss = $this->getSyncServiceMock($backend, ['0' => [200 => '']]); + $return = $ss->syncRemoteAddressBook('', 'system', '1234567890', null, '1', 'principals/system/system', []); + $this->assertEquals('sync-token-1', $return); + } + + public function testSyncWithUpdatedElement() { + $backend = $this->getBackendMock(0, 1, 0); + $backend->method('getCard')->willReturn(true); + + $ss = $this->getSyncServiceMock($backend, ['0' => [200 => '']]); + $return = $ss->syncRemoteAddressBook('', 'system', '1234567890', null, '1', 'principals/system/system', []); + $this->assertEquals('sync-token-1', $return); + } + + public function testSyncWithDeletedElement() { + $backend = $this->getBackendMock(0, 0, 1); + + $ss = $this->getSyncServiceMock($backend, ['0' => [404 => '']]); + $return = $ss->syncRemoteAddressBook('', 'system', '1234567890', null, '1', 'principals/system/system', []); + $this->assertEquals('sync-token-1', $return); + } + + /** + * @param int $createCount + * @param int $updateCount + * @param int $deleteCount + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getBackendMock($createCount, $updateCount, $deleteCount) { + $backend = $this->getMockBuilder('OCA\DAV\CardDAV\CardDAVBackend')->disableOriginalConstructor()->getMock(); + $backend->expects($this->exactly($createCount))->method('createCard'); + $backend->expects($this->exactly($updateCount))->method('updateCard'); + $backend->expects($this->exactly($deleteCount))->method('deleteCard'); + return $backend; + } + + /** + * @param $backend + * @param $response + * @return SyncService|\PHPUnit_Framework_MockObject_MockObject + */ + private function getSyncServiceMock($backend, $response) { + /** @var SyncService | \PHPUnit_Framework_MockObject_MockObject $ss */ + $ss = $this->getMock('OCA\DAV\CardDAV\SyncService', ['ensureSystemAddressBookExists', 'requestSyncReport', 'download'], [$backend]); + $ss->method('requestSyncReport')->withAnyParameters()->willReturn(['response' => $response, 'token' => 'sync-token-1']); + $ss->method('ensureSystemAddressBookExists')->willReturn(['id' => 1]); + $ss->method('download')->willReturn([ + 'body' => '', + 'statusCode' => 200, + 'headers' => [] + ]); + return $ss; + } + +} diff --git a/apps/federation/appinfo/application.php b/apps/federation/appinfo/application.php index 45d88548b70..0214d73d900 100644 --- a/apps/federation/appinfo/application.php +++ b/apps/federation/appinfo/application.php @@ -23,6 +23,7 @@ namespace OCA\Federation\AppInfo; use OCA\Federation\API\OCSAuthAPI; use OCA\Federation\Controller\SettingsController; +use OCA\Federation\DAV\FedAuth; use OCA\Federation\DbHandler; use OCA\Federation\Hooks; use OCA\Federation\Middleware\AddServerMiddleware; @@ -30,7 +31,9 @@ use OCA\Federation\TrustedServers; use OCP\API; use OCP\App; use OCP\AppFramework\IAppContainer; +use OCP\SabrePluginEvent; use OCP\Util; +use Sabre\DAV\Auth\Plugin; class Application extends \OCP\AppFramework\App { @@ -144,6 +147,19 @@ class Application extends \OCP\AppFramework\App { $hooksManager, 'addServerHook' ); + + $dispatcher = $this->getContainer()->getServer()->getEventDispatcher(); + $dispatcher->addListener('OCA\DAV\Connector\Sabre::authInit', function($event) use($container) { + if ($event instanceof SabrePluginEvent) { + $authPlugin = $event->getServer()->getPlugin('auth'); + if ($authPlugin instanceof Plugin) { + $h = new DbHandler($container->getServer()->getDatabaseConnection(), + $container->getServer()->getL10N('federation') + ); + $authPlugin->addBackend(new FedAuth($h)); + } + } + }); } } diff --git a/apps/federation/appinfo/database.xml b/apps/federation/appinfo/database.xml index e0bb241918e..05b7fb12b49 100644 --- a/apps/federation/appinfo/database.xml +++ b/apps/federation/appinfo/database.xml @@ -34,7 +34,7 @@ <name>token</name> <type>text</type> <length>128</length> - <comments>toke used to exchange the shared secret</comments> + <comments>token used to exchange the shared secret</comments> </field> <field> <name>shared_secret</name> @@ -50,6 +50,12 @@ <default>2</default> <comments>current status of the connection</comments> </field> + <field> + <name>sync_token</name> + <type>text</type> + <length>512</length> + <comments>cardDav sync token</comments> + </field> <index> <name>url_hash</name> <unique>true</unique> diff --git a/apps/federation/appinfo/info.xml b/apps/federation/appinfo/info.xml index 92afc995366..54ea4831be0 100644 --- a/apps/federation/appinfo/info.xml +++ b/apps/federation/appinfo/info.xml @@ -5,10 +5,13 @@ <description>ownCloud Federation allows you to connect with other trusted ownClouds to exchange the user directory. For example this will be used to auto-complete external users for federated sharing.</description> <licence>AGPL</licence> <author>Bjoern Schiessle</author> - <version>0.0.1</version> + <version>0.0.2</version> <namespace>Federation</namespace> <category>other</category> <dependencies> <owncloud min-version="9.0" max-version="9.0" /> </dependencies> + <types> + <authentication/> + </types> </info> diff --git a/apps/federation/appinfo/register_command.php b/apps/federation/appinfo/register_command.php new file mode 100644 index 00000000000..541b01706d0 --- /dev/null +++ b/apps/federation/appinfo/register_command.php @@ -0,0 +1,8 @@ +<?php + +$dbConnection = \OC::$server->getDatabaseConnection(); +$l10n = \OC::$server->getL10N('federation'); +$dbHandler = new \OCA\Federation\DbHandler($dbConnection, $l10n); + +/** @var Symfony\Component\Console\Application $application */ +$application->add(new \OCA\Federation\Command\SyncFederationAddressBooks($dbHandler)); diff --git a/apps/federation/command/syncfederationaddressbooks.php b/apps/federation/command/syncfederationaddressbooks.php new file mode 100644 index 00000000000..516029d25ab --- /dev/null +++ b/apps/federation/command/syncfederationaddressbooks.php @@ -0,0 +1,72 @@ +<?php + +namespace OCA\Federation\Command; + +use OCA\DAV\CardDAV\SyncService; +use OCA\Federation\DbHandler; +use OCA\Federation\TrustedServers; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\ProgressBar; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class SyncFederationAddressBooks extends Command { + + /** @var DbHandler */ + protected $dbHandler; + + /** @var SyncService */ + private $syncService; + + /** + * @param DbHandler $dbHandler + */ + function __construct(DbHandler $dbHandler) { + parent::__construct(); + + $this->syncService = \OC::$server->query('CardDAVSyncService'); + $this->dbHandler = $dbHandler; + } + + protected function configure() { + $this + ->setName('federation:sync-addressbooks') + ->setDescription('Synchronizes addressbooks of all federated clouds'); + } + + /** + * @param InputInterface $input + * @param OutputInterface $output + */ + protected function execute(InputInterface $input, OutputInterface $output) { + + $progress = new ProgressBar($output); + $progress->start(); + $trustedServers = $this->dbHandler->getAllServer(); + foreach ($trustedServers as $trustedServer) { + $progress->advance(); + $url = $trustedServer['url']; + $sharedSecret = $trustedServer['shared_secret']; + $syncToken = $trustedServer['sync_token']; + + if (is_null($sharedSecret)) { + continue; + } + $targetBookId = sha1($url); + $targetPrincipal = "principals/system/system"; + $targetBookProperties = [ + '{DAV:}displayname' => $url + ]; + try { + $newToken = $this->syncService->syncRemoteAddressBook($url, 'system', $sharedSecret, $syncToken, $targetPrincipal, $targetBookId, $targetBookProperties); + if ($newToken !== $syncToken) { + $this->dbHandler->setServerStatus($url, TrustedServers::STATUS_OK, $newToken); + } + } catch (\Exception $ex) { + $output->writeln("Error while syncing $url : " . $ex->getMessage()); + } + } + $progress->finish(); + $output->writeln(''); + } +} diff --git a/apps/federation/dav/fedauth.php b/apps/federation/dav/fedauth.php new file mode 100644 index 00000000000..09d61a1f2c7 --- /dev/null +++ b/apps/federation/dav/fedauth.php @@ -0,0 +1,54 @@ +<?php +/** + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ +namespace OCA\Federation\DAV; + +use OCA\Federation\DbHandler; +use Sabre\DAV\Auth\Backend\AbstractBasic; + +class FedAuth extends AbstractBasic { + + /** @var DbHandler */ + private $db; + + /** + * FedAuth constructor. + * + * @param DbHandler $db + */ + public function __construct(DbHandler $db) { + $this->db = $db; + $this->principalPrefix = 'principals/system/'; + } + + /** + * Validates a username and password + * + * This method should return true or false depending on if login + * succeeded. + * + * @param string $username + * @param string $password + * @return bool + */ + protected function validateUserPass($username, $password) { + return $this->db->auth($username, $password); + } +} diff --git a/apps/federation/lib/dbhandler.php b/apps/federation/lib/dbhandler.php index 7606593f780..f50854fefb3 100644 --- a/apps/federation/lib/dbhandler.php +++ b/apps/federation/lib/dbhandler.php @@ -111,7 +111,7 @@ class DbHandler { */ public function getAllServer() { $query = $this->connection->getQueryBuilder(); - $query->select(['url', 'id', 'status'])->from($this->dbTable); + $query->select(['url', 'id', 'status', 'shared_secret', 'sync_token'])->from($this->dbTable); $result = $query->execute()->fetchAll(); return $result; } @@ -206,15 +206,17 @@ class DbHandler { * * @param string $url * @param int $status + * @param string|null $token */ - public function setServerStatus($url, $status) { + public function setServerStatus($url, $status, $token = null) { $hash = $this->hash($url); $query = $this->connection->getQueryBuilder(); $query->update($this->dbTable) - ->set('status', $query->createParameter('status')) - ->where($query->expr()->eq('url_hash', $query->createParameter('url_hash'))) - ->setParameter('url_hash', $hash) - ->setParameter('status', $status); + ->set('status', $query->createNamedParameter($status)) + ->where($query->expr()->eq('url_hash', $query->createNamedParameter($hash))); + if (!is_null($token)) { + $query->set('sync_token', $query->createNamedParameter($token)); + } $query->execute(); } @@ -267,4 +269,21 @@ class DbHandler { return $normalized; } + /** + * @param $username + * @param $password + * @return bool + */ + public function auth($username, $password) { + if ($username !== 'system') { + return false; + } + $query = $this->connection->getQueryBuilder(); + $query->select('url')->from($this->dbTable) + ->where($query->expr()->eq('shared_secret', $query->createNamedParameter($password))); + + $result = $query->execute()->fetch(); + return !empty($result); + } + } diff --git a/apps/federation/tests/dav/fedauthtest.php b/apps/federation/tests/dav/fedauthtest.php new file mode 100644 index 00000000000..845cfc622d2 --- /dev/null +++ b/apps/federation/tests/dav/fedauthtest.php @@ -0,0 +1,52 @@ +<?php +/** + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + + +namespace OCA\Federation\Tests\DAV; + +use OCA\Federation\DAV\FedAuth; +use OCA\Federation\DbHandler; +use Test\TestCase; + +class FedAuthTest extends TestCase { + + /** + * @dataProvider providesUser + * + * @param array $expected + * @param string $user + * @param string $password + */ + public function testFedAuth($expected, $user, $password) { + /** @var DbHandler | \PHPUnit_Framework_MockObject_MockObject $db */ + $db = $this->getMockBuilder('OCA\Federation\DbHandler')->disableOriginalConstructor()->getMock(); + $db->method('auth')->willReturn(true); + $auth = new FedAuth($db); + $result = $this->invokePrivate($auth, 'validateUserPass', [$user, $password]); + $this->assertEquals($expected, $result); + } + + public function providesUser() { + return [ + [true, 'system', '123456'] + ]; + } +} diff --git a/apps/federation/tests/lib/dbhandlertest.php b/apps/federation/tests/lib/dbhandlertest.php index 123eaaee450..65126e39f72 100644 --- a/apps/federation/tests/lib/dbhandlertest.php +++ b/apps/federation/tests/lib/dbhandlertest.php @@ -26,6 +26,7 @@ namespace OCA\Federation\Tests\lib; use OCA\Federation\DbHandler; use OCA\Federation\TrustedServers; use OCP\IDBConnection; +use OCP\IL10N; use Test\TestCase; /** @@ -36,7 +37,7 @@ class DbHandlerTest extends TestCase { /** @var DbHandler */ private $dbHandler; - /** @var \PHPUnit_Framework_MockObject_MockObject */ + /** @var IL10N | \PHPUnit_Framework_MockObject_MockObject */ private $il10n; /** @var IDBConnection */ @@ -209,6 +210,11 @@ class DbHandlerTest extends TestCase { $this->assertSame(TrustedServers::STATUS_OK, $this->dbHandler->getServerStatus('https://server1') ); + + // test sync token + $this->dbHandler->setServerStatus('http://server1', TrustedServers::STATUS_OK, 'token1234567890'); + $servers = $this->dbHandler->getAllServer(); + $this->assertSame('token1234567890', $servers[0]['sync_token']); } /** @@ -256,4 +262,22 @@ class DbHandlerTest extends TestCase { ]; } + /** + * @dataProvider providesAuth + */ + public function testAuth($expectedResult, $user, $password) { + if ($expectedResult) { + $this->dbHandler->addServer('url1'); + $this->dbHandler->addSharedSecret('url1', $password); + } + $result = $this->dbHandler->auth($user, $password); + $this->assertEquals($expectedResult, $result); + } + + public function providesAuth() { + return [ + [false, 'foo', ''], + [true, 'system', '123456789'], + ]; + } } diff --git a/lib/public/sabrepluginevent.php b/lib/public/sabrepluginevent.php index fed3237166d..1a64c8ac3ed 100644 --- a/lib/public/sabrepluginevent.php +++ b/lib/public/sabrepluginevent.php @@ -23,6 +23,7 @@ namespace OCP; use OCP\AppFramework\Http; +use Sabre\DAV\Server; use Symfony\Component\EventDispatcher\Event; /** @@ -36,12 +37,16 @@ class SabrePluginEvent extends Event { /** @var string */ protected $message; + /** @var Server */ + protected $server; + /** * @since 8.2.0 */ - public function __construct() { + public function __construct($server = null) { $this->message = ''; $this->statusCode = Http::STATUS_OK; + $this->server = $server; } /** @@ -79,4 +84,12 @@ class SabrePluginEvent extends Event { public function getMessage() { return $this->message; } + + /** + * @return null|Server + * @since 9.0.0 + */ + public function getServer() { + return $this->server; + } } |