Merge pull request #21700 from owncloud/update-system-addressbook-on-user-change

Updating system addressbook as soon as a user is added or removed
This commit is contained in:
Thomas Müller 2016-01-18 20:28:11 +01:00
commit 816df90aec
7 changed files with 248 additions and 78 deletions

View File

@ -22,18 +22,16 @@
use OCA\DAV\CardDAV\CardDavBackend;
use OCA\DAV\CardDAV\SyncService;
\OC::$server->registerService('CardDAVSyncService', function() {
$app = new \OCA\Dav\AppInfo\Application();
$app->registerHooks();
$app = new \OCA\Dav\AppInfo\Application();
/** @var CardDavBackend */
$backend = $app->getContainer()->query('CardDavBackend');
\OC::$server->registerService('CardDAVSyncService', function() use ($app) {
return new SyncService($backend);
return $app->getSyncService();
});
$cm = \OC::$server->getContactsManager();
$cm->register(function() use ($cm) {
$cm->register(function() use ($cm, $app) {
$userId = \OC::$server->getUserSession()->getUser()->getUID();
$app = new \OCA\Dav\AppInfo\Application();
$app->setupContactsProvider($cm, $userId);
});

View File

@ -20,7 +20,10 @@
*/
namespace OCA\Dav\AppInfo;
use OCA\DAV\CardDAV\CardDavBackend;
use OCA\DAV\CardDAV\ContactsManager;
use OCA\DAV\CardDAV\SyncService;
use OCA\DAV\HookManager;
use \OCP\AppFramework\App;
use OCP\AppFramework\IAppContainer;
use OCP\Contacts\IManager;
@ -43,6 +46,22 @@ class Application extends App {
);
});
$container->registerService('HookManager', function($c) {
/** @var IAppContainer $c */
return new HookManager(
$c->getServer()->getUserManager(),
$c->query('SyncService')
);
});
$container->registerService('SyncService', function($c) {
/** @var IAppContainer $c */
return new SyncService(
$c->query('CardDavBackend'),
$c->getServer()->getUserManager()
);
});
$container->registerService('CardDavBackend', function($c) {
/** @var IAppContainer $c */
$db = $c->getServer()->getDatabaseConnection();
@ -65,4 +84,14 @@ class Application extends App {
$cm->setupContactsProvider($contactsManager, $userID);
}
public function registerHooks() {
/** @var HookManager $hm */
$hm = $this->getContainer()->query('HookManager');
$hm->setup();
}
public function getSyncService() {
return $this->getContainer()->query('SyncService');
}
}

View File

@ -19,6 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
use OCA\Dav\AppInfo\Application;
use OCA\DAV\Command\CreateAddressBook;
use OCA\DAV\Command\CreateCalendar;
use OCA\DAV\Command\SyncSystemAddressBook;
@ -29,7 +30,9 @@ $userManager = OC::$server->getUserManager();
$config = \OC::$server->getConfig();
$logger = \OC::$server->getLogger();
$app = new Application();
/** @var Symfony\Component\Console\Application $application */
$application->add(new CreateAddressBook($userManager, $dbConnection, $config, $logger));
$application->add(new CreateCalendar($userManager, $dbConnection));
$application->add(new SyncSystemAddressBook($userManager, $dbConnection, $config));
$application->add(new SyncSystemAddressBook($app->getSyncService()));

View File

@ -22,6 +22,7 @@ namespace OCA\DAV\Command;
use OCA\DAV\CardDAV\CardDavBackend;
use OCA\DAV\CardDAV\Converter;
use OCA\DAV\CardDAV\SyncService;
use OCA\DAV\Connector\Sabre\Principal;
use OCP\IConfig;
use OCP\IDBConnection;
@ -39,28 +40,16 @@ use Symfony\Component\Console\Output\OutputInterface;
class SyncSystemAddressBook extends Command {
/** @var IUserManager */
protected $userManager;
/** @var \OCP\IDBConnection */
protected $dbConnection;
/** @var IConfig */
protected $config;
/** @var CardDavBackend */
private $backend;
/** @var SyncService */
private $syncService;
/**
* @param IUserManager $userManager
* @param IDBConnection $dbConnection
* @param IConfig $config
* @param SyncService $syncService
*/
function __construct(IUserManager $userManager, IDBConnection $dbConnection, IConfig $config) {
function __construct(SyncService $syncService) {
parent::__construct();
$this->userManager = $userManager;
$this->dbConnection = $dbConnection;
$this->config = $config;
$this->syncService = $syncService;
}
protected function configure() {
@ -74,63 +63,15 @@ class SyncSystemAddressBook extends Command {
* @param OutputInterface $output
*/
protected function execute(InputInterface $input, OutputInterface $output) {
$principalBackend = new Principal(
$this->userManager
);
$this->backend = new CardDavBackend($this->dbConnection, $principalBackend);
// ensure system addressbook exists
$systemAddressBook = $this->ensureSystemAddressBookExists();
$converter = new Converter();
$output->writeln('Syncing users ...');
$progress = new ProgressBar($output);
$progress->start();
$this->userManager->callForAllUsers(function($user) use ($systemAddressBook, $converter, $progress) {
/** @var IUser $user */
$name = $user->getBackendClassName();
$userId = $user->getUID();
$cardId = "$name:$userId.vcf";
$card = $this->backend->getCard($systemAddressBook['id'], $cardId);
if ($card === false) {
$vCard = $converter->createCardFromUser($user);
$this->backend->createCard($systemAddressBook['id'], $cardId, $vCard->serialize());
} else {
$vCard = Reader::read($card['carddata']);
if ($converter->updateCard($vCard, $user)) {
$this->backend->updateCard($systemAddressBook['id'], $cardId, $vCard->serialize());
}
}
$this->syncService->syncInstance(function() use ($progress) {
$progress->advance();
});
// remove no longer existing
$allCards = $this->backend->getCards($systemAddressBook['id']);
foreach($allCards as $card) {
$vCard = Reader::read($card['carddata']);
$uid = $vCard->UID->getValue();
// load backend and see if user exists
if (!$this->userManager->userExists($uid)) {
$this->backend->deleteCard($systemAddressBook['id'], $card['uri']);
}
}
$progress->finish();
$output->writeln('');
}
protected function ensureSystemAddressBookExists() {
$book = $this->backend->getAddressBooksByUri('system');
if (!is_null($book)) {
return $book;
}
$systemPrincipal = "principals/system/system";
$this->backend->createAddressBook($systemPrincipal, 'system', [
'{' . Plugin::NS_CARDDAV . '}addressbook-description' => 'System addressbook which holds all users of this instance'
]);
return $this->backend->getAddressBooksByUri('system');
}
}

View File

@ -21,18 +21,27 @@
namespace OCA\DAV\CardDAV;
use OCP\IUser;
use OCP\IUserManager;
use Sabre\DAV\Client;
use Sabre\DAV\Xml\Response\MultiStatus;
use Sabre\DAV\Xml\Service;
use Sabre\HTTP\ClientException;
use Sabre\VObject\Reader;
class SyncService {
/** @var CardDavBackend */
private $backend;
public function __construct(CardDavBackend $backend) {
/** @var IUserManager */
private $userManager;
/** @var array */
private $localSystemAddressBook;
public function __construct(CardDavBackend $backend, IUserManager $userManager) {
$this->backend = $backend;
$this->userManager = $userManager;
}
/**
@ -80,7 +89,7 @@ class SyncService {
* @return array|null
* @throws \Sabre\DAV\Exception\BadRequest
*/
protected function ensureSystemAddressBookExists($principal, $id, $properties) {
public function ensureSystemAddressBookExists($principal, $id, $properties) {
$book = $this->backend->getAddressBooksByUri($id);
if (!is_null($book)) {
return $book;
@ -180,5 +189,75 @@ class SyncService {
return ['response' => $result, 'token' => $multiStatus->getSyncToken()];
}
/**
* @param IUser $user
*/
public function updateUser($user) {
$systemAddressBook = $this->getLocalSystemAddressBook();
$addressBookId = $systemAddressBook['id'];
$converter = new Converter();
$name = $user->getBackendClassName();
$userId = $user->getUID();
$cardId = "$name:$userId.vcf";
$card = $this->backend->getCard($addressBookId, $cardId);
if ($card === false) {
$vCard = $converter->createCardFromUser($user);
$this->backend->createCard($addressBookId, $cardId, $vCard->serialize());
} else {
$vCard = Reader::read($card['carddata']);
if ($converter->updateCard($vCard, $user)) {
$this->backend->updateCard($addressBookId, $cardId, $vCard->serialize());
}
}
}
/**
* @param IUser|string $userOrCardId
*/
public function deleteUser($userOrCardId) {
$systemAddressBook = $this->getLocalSystemAddressBook();
if ($userOrCardId instanceof IUser){
$name = $userOrCardId->getBackendClassName();
$userId = $userOrCardId->getUID();
$userOrCardId = "$name:$userId.vcf";
}
$this->backend->deleteCard($systemAddressBook['id'], $userOrCardId);
}
/**
* @return array|null
*/
public function getLocalSystemAddressBook() {
if (is_null($this->localSystemAddressBook)) {
$systemPrincipal = "principals/system/system";
$this->localSystemAddressBook = $this->ensureSystemAddressBookExists($systemPrincipal, 'system', [
'{' . Plugin::NS_CARDDAV . '}addressbook-description' => 'System addressbook which holds all users of this instance'
]);
}
return $this->localSystemAddressBook;
}
public function syncInstance(\Closure $progressCallback) {
$systemAddressBook = $this->getLocalSystemAddressBook();
$this->userManager->callForAllUsers(function($user) use ($systemAddressBook, $progressCallback) {
$this->updateUser($user);
$progressCallback();
});
// remove no longer existing
$allCards = $this->backend->getCards($systemAddressBook['id']);
foreach($allCards as $card) {
$vCard = Reader::read($card['carddata']);
$uid = $vCard->UID->getValue();
// load backend and see if user exists
if (!$this->userManager->userExists($uid)) {
$this->deleteUser($card['uri']);
}
}
}
}

View File

@ -0,0 +1,74 @@
<?php
/**
* @author Thomas Müller <thomas.mueller@tmit.eu>
*
* @copyright Copyright (c) 2016, 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;
use OCA\DAV\CardDAV\SyncService;
use OCP\IUser;
use OCP\IUserManager;
use OCP\Util;
class HookManager {
/** @var IUserManager */
private $userManager;
/** @var SyncService */
private $syncService;
/** @var IUser[] */
private $usersToDelete;
public function __construct(IUserManager $userManager, SyncService $syncService) {
$this->userManager = $userManager;
$this->syncService = $syncService;
}
public function setup() {
Util::connectHook('OC_User',
'post_createUser',
$this,
'postCreateUser');
Util::connectHook('OC_User',
'pre_deleteUser',
$this,
'preDeleteUser');
Util::connectHook('OC_User',
'post_deleteUser',
$this,
'postDeleteUser');
}
public function postCreateUser($params) {
$user = $this->userManager->get($params['uid']);
$this->syncService->updateUser($user);
}
public function preDeleteUser($params) {
$this->usersToDelete[$params['uid']] = $this->userManager->get($params['uid']);
}
public function postDeleteUser($params) {
$uid = $params['uid'];
if (isset($this->usersToDelete[$uid])){
$this->syncService->deleteUser($this->usersToDelete[$uid]);
}
}
}

View File

@ -20,6 +20,8 @@
*/
namespace OCA\DAV\CardDAV;
use OCP\IUser;
use OCP\IUserManager;
use Test\TestCase;
class SyncServiceTest extends TestCase {
@ -57,6 +59,49 @@ class SyncServiceTest extends TestCase {
$this->assertEquals('sync-token-1', $return);
}
public function testEnsureSystemAddressBookExists() {
/** @var CardDavBackend | \PHPUnit_Framework_MockObject_MockObject $backend */
$backend = $this->getMockBuilder('OCA\DAV\CardDAV\CardDAVBackend')->disableOriginalConstructor()->getMock();
$backend->expects($this->exactly(1))->method('createAddressBook');
$backend->expects($this->at(0))->method('getAddressBooksByUri')->willReturn(null);
$backend->expects($this->at(1))->method('getAddressBooksByUri')->willReturn([]);
/** @var IUserManager $userManager */
$userManager = $this->getMockBuilder('OCP\IUserManager')->disableOriginalConstructor()->getMock();
$ss = new SyncService($backend, $userManager);
$book = $ss->ensureSystemAddressBookExists('principals/users/adam', 'contacts', []);
}
public function testUpdateAndDeleteUser() {
/** @var CardDavBackend | \PHPUnit_Framework_MockObject_MockObject $backend */
$backend = $this->getMockBuilder('OCA\DAV\CardDAV\CardDAVBackend')->disableOriginalConstructor()->getMock();
$backend->expects($this->once())->method('createCard');
$backend->expects($this->once())->method('updateCard');
$backend->expects($this->once())->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"
]);
/** @var IUserManager | \PHPUnit_Framework_MockObject_MockObject $userManager */
$userManager = $this->getMockBuilder('OCP\IUserManager')->disableOriginalConstructor()->getMock();
/** @var IUser | \PHPUnit_Framework_MockObject_MockObject $user */
$user = $this->getMockBuilder('OCP\IUser')->disableOriginalConstructor()->getMock();
$user->method('getBackendClassName')->willReturn('unittest');
$user->method('getUID')->willReturn('test-user');
$ss = new SyncService($backend, $userManager);
$ss->updateUser($user);
$user->method('getDisplayName')->willReturn('A test user for unit testing');
$ss->updateUser($user);
$ss->deleteUser($user);
}
/**
* @param int $createCount
* @param int $updateCount
@ -77,8 +122,9 @@ class SyncServiceTest extends TestCase {
* @return SyncService|\PHPUnit_Framework_MockObject_MockObject
*/
private function getSyncServiceMock($backend, $response) {
$userManager = $this->getMockBuilder('OCP\IUserManager')->disableOriginalConstructor()->getMock();
/** @var SyncService | \PHPUnit_Framework_MockObject_MockObject $ss */
$ss = $this->getMock('OCA\DAV\CardDAV\SyncService', ['ensureSystemAddressBookExists', 'requestSyncReport', 'download'], [$backend]);
$ss = $this->getMock('OCA\DAV\CardDAV\SyncService', ['ensureSystemAddressBookExists', 'requestSyncReport', 'download'], [$backend, $userManager]);
$ss->method('requestSyncReport')->withAnyParameters()->willReturn(['response' => $response, 'token' => 'sync-token-1']);
$ss->method('ensureSystemAddressBookExists')->willReturn(['id' => 1]);
$ss->method('download')->willReturn([