Merge pull request #1946 from nextcloud/federated-sharing-persona-settings
Add more personal information fields to the settings page for enhanced federated sharing
This commit is contained in:
commit
94004cf46b
51 changed files with 2527 additions and 427 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -22,6 +22,7 @@
|
|||
!/apps/files_sharing
|
||||
!/apps/files_trashbin
|
||||
!/apps/files_versions
|
||||
!/apps/lookup_server_connector
|
||||
!/apps/user_ldap
|
||||
!/apps/provisioning_api
|
||||
!/apps/systemtags
|
||||
|
|
|
@ -101,6 +101,12 @@ class Application extends App {
|
|||
}
|
||||
});
|
||||
|
||||
$dispatcher->addListener('OC\AccountManager::userUpdated', function(GenericEvent $event) {
|
||||
$user = $event->getSubject();
|
||||
$syncService = $this->getContainer()->query(SyncService::class);
|
||||
$syncService->updateUser($user);
|
||||
});
|
||||
|
||||
$dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::createCalendar', function(GenericEvent $event) {
|
||||
/** @var Backend $backend */
|
||||
$backend = $this->getContainer()->query(Backend::class);
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
namespace OCA\DAV\CardDAV;
|
||||
|
||||
use OC\Accounts\AccountManager;
|
||||
use OCP\IImage;
|
||||
use OCP\IUser;
|
||||
use Sabre\VObject\Component\VCard;
|
||||
|
@ -29,109 +30,76 @@ use Sabre\VObject\Property\Text;
|
|||
|
||||
class Converter {
|
||||
|
||||
/** @var AccountManager */
|
||||
private $accountManager;
|
||||
|
||||
/**
|
||||
* Converter constructor.
|
||||
*
|
||||
* @param AccountManager $accountManager
|
||||
*/
|
||||
public function __construct(AccountManager $accountManager) {
|
||||
$this->accountManager = $accountManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param IUser $user
|
||||
* @return VCard
|
||||
* @return VCard|null
|
||||
*/
|
||||
public function createCardFromUser(IUser $user) {
|
||||
|
||||
$userData = $this->accountManager->getUser($user);
|
||||
|
||||
$uid = $user->getUID();
|
||||
$displayName = $user->getDisplayName();
|
||||
$displayName = empty($displayName ) ? $uid : $displayName;
|
||||
$emailAddress = $user->getEMailAddress();
|
||||
$cloudId = $user->getCloudId();
|
||||
$image = $this->getAvatarImage($user);
|
||||
|
||||
$vCard = new VCard();
|
||||
$vCard->VERSION = '3.0';
|
||||
$vCard->UID = $uid;
|
||||
if (!empty($displayName)) {
|
||||
$vCard->FN = $displayName;
|
||||
$vCard->N = $this->splitFullName($displayName);
|
||||
}
|
||||
if (!empty($emailAddress)) {
|
||||
$vCard->add(new Text($vCard, 'EMAIL', $emailAddress, ['TYPE' => 'OTHER']));
|
||||
}
|
||||
if (!empty($cloudId)) {
|
||||
$vCard->CLOUD = $cloudId;
|
||||
}
|
||||
if ($image) {
|
||||
$vCard->add('PHOTO', $image->data(), ['ENCODING' => 'b', 'TYPE' => $image->mimeType()]);
|
||||
}
|
||||
$vCard->validate();
|
||||
$vCard->add(new Text($vCard, 'UID', $uid));
|
||||
|
||||
return $vCard;
|
||||
}
|
||||
$publish = false;
|
||||
|
||||
/**
|
||||
* @param VCard $vCard
|
||||
* @param IUser $user
|
||||
* @return bool
|
||||
*/
|
||||
public function updateCard(VCard $vCard, IUser $user) {
|
||||
$uid = $user->getUID();
|
||||
$displayName = $user->getDisplayName();
|
||||
$displayName = empty($displayName ) ? $uid : $displayName;
|
||||
$emailAddress = $user->getEMailAddress();
|
||||
$cloudId = $user->getCloudId();
|
||||
$image = $this->getAvatarImage($user);
|
||||
|
||||
$updated = false;
|
||||
if($this->propertyNeedsUpdate($vCard, 'FN', $displayName)) {
|
||||
$vCard->FN = new Text($vCard, 'FN', $displayName);
|
||||
unset($vCard->N);
|
||||
$vCard->add(new Text($vCard, 'N', $this->splitFullName($displayName)));
|
||||
$updated = true;
|
||||
}
|
||||
if($this->propertyNeedsUpdate($vCard, 'EMAIL', $emailAddress)) {
|
||||
$vCard->EMAIL = new Text($vCard, 'EMAIL', $emailAddress);
|
||||
$updated = true;
|
||||
}
|
||||
if($this->propertyNeedsUpdate($vCard, 'CLOUD', $cloudId)) {
|
||||
$vCard->CLOUD = new Text($vCard, 'CLOUD', $cloudId);
|
||||
$updated = true;
|
||||
foreach ($userData as $property => $value) {
|
||||
if ($value['scope'] === AccountManager::VISIBILITY_CONTACTS_ONLY ||
|
||||
$value['scope'] === AccountManager::VISIBILITY_PUBLIC
|
||||
) {
|
||||
$publish = true;
|
||||
switch ($property) {
|
||||
case AccountManager::PROPERTY_DISPLAYNAME:
|
||||
$vCard->add(new Text($vCard, 'FN', $value['value']));
|
||||
$vCard->add(new Text($vCard, 'N', $this->splitFullName($value['value'])));
|
||||
break;
|
||||
case AccountManager::PROPERTY_AVATAR:
|
||||
if ($image !== null) {
|
||||
$vCard->add('PHOTO', $image->data(), ['ENCODING' => 'b', 'TYPE' => $image->mimeType()]);
|
||||
}
|
||||
break;
|
||||
case AccountManager::PROPERTY_EMAIL:
|
||||
$vCard->add(new Text($vCard, 'EMAIL', $value['value'], ['TYPE' => 'OTHER']));
|
||||
break;
|
||||
case AccountManager::PROPERTY_WEBSITE:
|
||||
$vCard->add(new Text($vCard, 'URL', $value['value']));
|
||||
break;
|
||||
case AccountManager::PROPERTY_PHONE:
|
||||
$vCard->add(new Text($vCard, 'TEL', $value['value'], ['TYPE' => 'OTHER']));
|
||||
break;
|
||||
case AccountManager::PROPERTY_ADDRESS:
|
||||
$vCard->add(new Text($vCard, 'ADR', $value['value'], ['TYPE' => 'OTHER']));
|
||||
break;
|
||||
case AccountManager::PROPERTY_TWITTER:
|
||||
$vCard->add(new Text($vCard, 'X-SOCIALPROFILE', $value['value'], ['TYPE' => 'TWITTER']));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($this->propertyNeedsUpdate($vCard, 'PHOTO', $image)) {
|
||||
unset($vCard->PHOTO);
|
||||
$vCard->add('PHOTO', $image->data(), ['ENCODING' => 'b', 'TYPE' => $image->mimeType()]);
|
||||
$updated = true;
|
||||
if ($publish && !empty($cloudId)) {
|
||||
$vCard->add(new Text($vCard, 'CLOUD', $cloudId));
|
||||
$vCard->validate();
|
||||
return $vCard;
|
||||
}
|
||||
|
||||
if (empty($emailAddress) && !is_null($vCard->EMAIL)) {
|
||||
unset($vCard->EMAIL);
|
||||
$updated = true;
|
||||
}
|
||||
if (empty($cloudId) && !is_null($vCard->CLOUD)) {
|
||||
unset($vCard->CLOUD);
|
||||
$updated = true;
|
||||
}
|
||||
if (empty($image) && !is_null($vCard->PHOTO)) {
|
||||
unset($vCard->PHOTO);
|
||||
$updated = true;
|
||||
}
|
||||
|
||||
return $updated;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param VCard $vCard
|
||||
* @param string $name
|
||||
* @param string|IImage $newValue
|
||||
* @return bool
|
||||
*/
|
||||
private function propertyNeedsUpdate(VCard $vCard, $name, $newValue) {
|
||||
if (is_null($newValue)) {
|
||||
return false;
|
||||
}
|
||||
$value = $vCard->__get($name);
|
||||
if (!is_null($value)) {
|
||||
$value = $value->getValue();
|
||||
$newValue = $newValue instanceof IImage ? $newValue->data() : $newValue;
|
||||
|
||||
return $value !== $newValue;
|
||||
}
|
||||
return true;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
namespace OCA\DAV\CardDAV;
|
||||
|
||||
use OC\Accounts\AccountManager;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\ILogger;
|
||||
use OCP\IUser;
|
||||
|
@ -48,10 +49,22 @@ class SyncService {
|
|||
/** @var array */
|
||||
private $localSystemAddressBook;
|
||||
|
||||
public function __construct(CardDavBackend $backend, IUserManager $userManager, ILogger $logger) {
|
||||
/** @var AccountManager */
|
||||
private $accountManager;
|
||||
|
||||
/**
|
||||
* SyncService constructor.
|
||||
*
|
||||
* @param CardDavBackend $backend
|
||||
* @param IUserManager $userManager
|
||||
* @param ILogger $logger
|
||||
* @param AccountManager $accountManager
|
||||
*/
|
||||
public function __construct(CardDavBackend $backend, IUserManager $userManager, ILogger $logger, AccountManager $accountManager) {
|
||||
$this->backend = $backend;
|
||||
$this->userManager = $userManager;
|
||||
$this->logger = $logger;
|
||||
$this->accountManager = $accountManager;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -215,7 +228,7 @@ class SyncService {
|
|||
public function updateUser($user) {
|
||||
$systemAddressBook = $this->getLocalSystemAddressBook();
|
||||
$addressBookId = $systemAddressBook['id'];
|
||||
$converter = new Converter();
|
||||
$converter = new Converter($this->accountManager);
|
||||
$name = $user->getBackendClassName();
|
||||
$userId = $user->getUID();
|
||||
|
||||
|
@ -223,10 +236,14 @@ class SyncService {
|
|||
$card = $this->backend->getCard($addressBookId, $cardId);
|
||||
if ($card === false) {
|
||||
$vCard = $converter->createCardFromUser($user);
|
||||
$this->backend->createCard($addressBookId, $cardId, $vCard->serialize());
|
||||
if ($vCard !== null) {
|
||||
$this->backend->createCard($addressBookId, $cardId, $vCard->serialize());
|
||||
}
|
||||
} else {
|
||||
$vCard = Reader::read($card['carddata']);
|
||||
if ($converter->updateCard($vCard, $user)) {
|
||||
$vCard = $converter->createCardFromUser($user);
|
||||
if (is_null($vCard)) {
|
||||
$this->backend->deleteCard($addressBookId, $cardId);
|
||||
} else {
|
||||
$this->backend->updateCard($addressBookId, $cardId, $vCard->serialize());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,8 @@ use OCA\DAV\CardDAV\SyncService;
|
|||
use OCP\IUser;
|
||||
use OCP\IUserManager;
|
||||
use OCP\Util;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Symfony\Component\EventDispatcher\GenericEvent;
|
||||
|
||||
class HookManager {
|
||||
|
||||
|
@ -51,14 +53,19 @@ class HookManager {
|
|||
/** @var array */
|
||||
private $addressBooksToDelete;
|
||||
|
||||
/** @var EventDispatcher */
|
||||
private $eventDispatcher;
|
||||
|
||||
public function __construct(IUserManager $userManager,
|
||||
SyncService $syncService,
|
||||
CalDavBackend $calDav,
|
||||
CardDavBackend $cardDav) {
|
||||
CardDavBackend $cardDav,
|
||||
EventDispatcher $eventDispatcher) {
|
||||
$this->userManager = $userManager;
|
||||
$this->syncService = $syncService;
|
||||
$this->calDav = $calDav;
|
||||
$this->cardDav = $cardDav;
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
}
|
||||
|
||||
public function setup() {
|
||||
|
|
|
@ -24,79 +24,121 @@
|
|||
|
||||
namespace OCA\DAV\Tests\unit\CardDAV;
|
||||
|
||||
use OC\Accounts\AccountManager;
|
||||
use OCA\DAV\CardDAV\Converter;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\IImage;
|
||||
use OCP\IUser;
|
||||
use OpenCloud\ObjectStore\Resource\Account;
|
||||
use PHPUnit_Framework_MockObject_MockObject;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Test\TestCase;
|
||||
|
||||
class ConverterTest extends TestCase {
|
||||
|
||||
/** @var AccountManager | PHPUnit_Framework_MockObject_MockObject */
|
||||
private $accountManager;
|
||||
|
||||
/** @var EventDispatcher | PHPUnit_Framework_MockObject_MockObject */
|
||||
private $eventDispatcher;
|
||||
|
||||
/** @var IDBConnection | PHPUnit_Framework_MockObject_MockObject */
|
||||
private $databaseConnection;
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
$this->databaseConnection = $this->getMockBuilder('OCP\IDBConnection')->getMock();
|
||||
$this->eventDispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcher')
|
||||
->disableOriginalConstructor()->getMock();
|
||||
$this->accountManager = $this->getMockBuilder('OC\Accounts\AccountManager')
|
||||
->disableOriginalConstructor()->getMock();
|
||||
}
|
||||
|
||||
public function getAccountManager(IUser $user) {
|
||||
$accountManager = $this->getMockBuilder('OC\Accounts\AccountManager')
|
||||
->disableOriginalConstructor()->getMock();
|
||||
$accountManager->expects($this->any())->method('getUser')->willReturn(
|
||||
[
|
||||
AccountManager::PROPERTY_DISPLAYNAME =>
|
||||
[
|
||||
'value' => $user->getDisplayName(),
|
||||
'scope' => AccountManager::VISIBILITY_CONTACTS_ONLY,
|
||||
],
|
||||
AccountManager::PROPERTY_ADDRESS =>
|
||||
[
|
||||
'value' => '',
|
||||
'scope' => AccountManager::VISIBILITY_PRIVATE,
|
||||
],
|
||||
AccountManager::PROPERTY_WEBSITE =>
|
||||
[
|
||||
'value' => '',
|
||||
'scope' => AccountManager::VISIBILITY_PRIVATE,
|
||||
],
|
||||
AccountManager::PROPERTY_EMAIL =>
|
||||
[
|
||||
'value' => $user->getEMailAddress(),
|
||||
'scope' => AccountManager::VISIBILITY_CONTACTS_ONLY,
|
||||
],
|
||||
AccountManager::PROPERTY_AVATAR =>
|
||||
[
|
||||
'scope' => AccountManager::VISIBILITY_CONTACTS_ONLY
|
||||
],
|
||||
AccountManager::PROPERTY_PHONE =>
|
||||
[
|
||||
'value' => '',
|
||||
'scope' => AccountManager::VISIBILITY_PRIVATE,
|
||||
],
|
||||
AccountManager::PROPERTY_TWITTER =>
|
||||
[
|
||||
'value' => '',
|
||||
'scope' => AccountManager::VISIBILITY_PRIVATE,
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
return $accountManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providesNewUsers
|
||||
*/
|
||||
public function testCreation($expectedVCard, $displayName = null, $eMailAddress = null, $cloudId = null) {
|
||||
$user = $this->getUserMock($displayName, $eMailAddress, $cloudId);
|
||||
$accountManager = $this->getAccountManager($user);
|
||||
|
||||
$converter = new Converter();
|
||||
$converter = new Converter($accountManager);
|
||||
$vCard = $converter->createCardFromUser($user);
|
||||
$cardData = $vCard->serialize();
|
||||
if ($expectedVCard !== null) {
|
||||
$this->assertInstanceOf('Sabre\VObject\Component\VCard', $vCard);
|
||||
$cardData = $vCard->jsonSerialize();
|
||||
$this->compareData($expectedVCard, $cardData);
|
||||
|
||||
$this->assertEquals($expectedVCard, $cardData);
|
||||
} else {
|
||||
$this->assertSame($expectedVCard, $vCard);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected function compareData($expected, $data) {
|
||||
foreach ($expected as $key => $value) {
|
||||
$found = false;
|
||||
foreach ($data[1] as $d) {
|
||||
if($d[0] === $key && $d[3] === $value) {
|
||||
$found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$found) $this->assertTrue(false, 'Expected data: ' . $key . ' not found.');
|
||||
}
|
||||
}
|
||||
|
||||
public function providesNewUsers() {
|
||||
return [
|
||||
["BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nPHOTO;ENCODING=b;TYPE=JPEG:MTIzNDU2Nzg5\r\nEND:VCARD\r\n"],
|
||||
["BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:Dr. Foo Bar\r\nN:Bar;Dr.;Foo;;\r\nPHOTO;ENCODING=b;TYPE=JPEG:MTIzNDU2Nzg5\r\nEND:VCARD\r\n", "Dr. Foo Bar"],
|
||||
["BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:Dr. Foo Bar\r\nN:Bar;Dr.;Foo;;\r\nEMAIL;TYPE=OTHER:foo@bar.net\r\nPHOTO;ENCODING=b;TYPE=JPEG:MTIzNDU2Nzg5\r\nEND:VCARD\r\n", "Dr. Foo Bar", "foo@bar.net"],
|
||||
["BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:Dr. Foo Bar\r\nN:Bar;Dr.;Foo;;\r\nCLOUD:foo@bar.net\r\nPHOTO;ENCODING=b;TYPE=JPEG:MTIzNDU2Nzg5\r\nEND:VCARD\r\n", "Dr. Foo Bar", null, "foo@bar.net"],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providesNewUsers
|
||||
*/
|
||||
public function testUpdateOfUnchangedUser($expectedVCard, $displayName = null, $eMailAddress = null, $cloudId = null) {
|
||||
$user = $this->getUserMock($displayName, $eMailAddress, $cloudId);
|
||||
|
||||
$converter = new Converter();
|
||||
$vCard = $converter->createCardFromUser($user);
|
||||
$updated = $converter->updateCard($vCard, $user);
|
||||
$this->assertFalse($updated);
|
||||
$cardData = $vCard->serialize();
|
||||
|
||||
$this->assertEquals($expectedVCard, $cardData);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providesUsersForUpdateOfRemovedElement
|
||||
*/
|
||||
public function testUpdateOfRemovedElement($expectedVCard, $displayName = null, $eMailAddress = null, $cloudId = null) {
|
||||
$user = $this->getUserMock($displayName, $eMailAddress, $cloudId);
|
||||
|
||||
$converter = new Converter();
|
||||
$vCard = $converter->createCardFromUser($user);
|
||||
|
||||
$user1 = $this->getMockBuilder('OCP\IUser')->disableOriginalConstructor()->getMock();
|
||||
$user1->method('getUID')->willReturn('12345');
|
||||
$user1->method('getDisplayName')->willReturn(null);
|
||||
$user1->method('getEMailAddress')->willReturn(null);
|
||||
$user1->method('getCloudId')->willReturn(null);
|
||||
$user1->method('getAvatarImage')->willReturn(null);
|
||||
|
||||
$updated = $converter->updateCard($vCard, $user1);
|
||||
$this->assertTrue($updated);
|
||||
$cardData = $vCard->serialize();
|
||||
|
||||
$this->assertEquals($expectedVCard, $cardData);
|
||||
}
|
||||
|
||||
public function providesUsersForUpdateOfRemovedElement() {
|
||||
return [
|
||||
["BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nEND:VCARD\r\n", "Dr. Foo Bar"],
|
||||
["BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nEND:VCARD\r\n", "Dr. Foo Bar", "foo@bar.net"],
|
||||
["BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nEND:VCARD\r\n", "Dr. Foo Bar", null, "foo@bar.net"],
|
||||
[null],
|
||||
[null, null, 'foo@bar.net'],
|
||||
[['cloud' => 'foo@cloud.net', 'email' => 'foo@bar.net'], null, 'foo@bar.net', 'foo@cloud.net'],
|
||||
[['cloud' => 'foo@cloud.net', 'email' => 'foo@bar.net', 'fn' => 'Dr. Foo Bar'], "Dr. Foo Bar", "foo@bar.net", 'foo@cloud.net'],
|
||||
[['cloud' => 'foo@cloud.net', 'fn' => 'Dr. Foo Bar'], "Dr. Foo Bar", null, "foo@cloud.net"],
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -107,7 +149,7 @@ class ConverterTest extends TestCase {
|
|||
*/
|
||||
public function testNameSplitter($expected, $fullName) {
|
||||
|
||||
$converter = new Converter();
|
||||
$converter = new Converter($this->accountManager);
|
||||
$r = $converter->splitFullName($fullName);
|
||||
$r = implode(';', $r);
|
||||
$this->assertEquals($expected, $r);
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
namespace OCA\DAV\Tests\unit\CardDAV;
|
||||
|
||||
use OC\Accounts\AccountManager;
|
||||
use OCA\DAV\CardDAV\CardDavBackend;
|
||||
use OCA\DAV\CardDAV\SyncService;
|
||||
use OCP\IUser;
|
||||
|
@ -76,7 +77,8 @@ class SyncServiceTest extends TestCase {
|
|||
/** @var IUserManager $userManager */
|
||||
$userManager = $this->getMockBuilder('OCP\IUserManager')->disableOriginalConstructor()->getMock();
|
||||
$logger = $this->getMockBuilder('OCP\ILogger')->disableOriginalConstructor()->getMock();
|
||||
$ss = new SyncService($backend, $userManager, $logger);
|
||||
$accountManager = $this->getMockBuilder('OC\Accounts\AccountManager')->disableOriginalConstructor()->getMock();
|
||||
$ss = new SyncService($backend, $userManager, $logger, $accountManager);
|
||||
$book = $ss->ensureSystemAddressBookExists('principals/users/adam', 'contacts', []);
|
||||
}
|
||||
|
||||
|
@ -100,8 +102,47 @@ class SyncServiceTest extends TestCase {
|
|||
$user = $this->getMockBuilder('OCP\IUser')->disableOriginalConstructor()->getMock();
|
||||
$user->method('getBackendClassName')->willReturn('unittest');
|
||||
$user->method('getUID')->willReturn('test-user');
|
||||
$user->method('getCloudId')->willReturn('cloudId');
|
||||
$accountManager = $this->getMockBuilder('OC\Accounts\AccountManager')->disableOriginalConstructor()->getMock();
|
||||
$accountManager->expects($this->any())->method('getUser')
|
||||
->willReturn([
|
||||
AccountManager::PROPERTY_DISPLAYNAME =>
|
||||
[
|
||||
'value' => $user->getDisplayName(),
|
||||
'scope' => AccountManager::VISIBILITY_CONTACTS_ONLY,
|
||||
],
|
||||
AccountManager::PROPERTY_ADDRESS =>
|
||||
[
|
||||
'value' => '',
|
||||
'scope' => AccountManager::VISIBILITY_PRIVATE,
|
||||
],
|
||||
AccountManager::PROPERTY_WEBSITE =>
|
||||
[
|
||||
'value' => '',
|
||||
'scope' => AccountManager::VISIBILITY_PRIVATE,
|
||||
],
|
||||
AccountManager::PROPERTY_EMAIL =>
|
||||
[
|
||||
'value' => $user->getEMailAddress(),
|
||||
'scope' => AccountManager::VISIBILITY_CONTACTS_ONLY,
|
||||
],
|
||||
AccountManager::PROPERTY_AVATAR =>
|
||||
[
|
||||
'scope' => AccountManager::VISIBILITY_CONTACTS_ONLY
|
||||
],
|
||||
AccountManager::PROPERTY_PHONE =>
|
||||
[
|
||||
'value' => '',
|
||||
'scope' => AccountManager::VISIBILITY_PRIVATE,
|
||||
],
|
||||
AccountManager::PROPERTY_TWITTER =>
|
||||
[
|
||||
'value' => '',
|
||||
'scope' => AccountManager::VISIBILITY_PRIVATE,
|
||||
],
|
||||
]);
|
||||
|
||||
$ss = new SyncService($backend, $userManager, $logger);
|
||||
$ss = new SyncService($backend, $userManager, $logger, $accountManager);
|
||||
$ss->updateUser($user);
|
||||
|
||||
$user->method('getDisplayName')->willReturn('A test user for unit testing');
|
||||
|
@ -135,10 +176,11 @@ class SyncServiceTest extends TestCase {
|
|||
private function getSyncServiceMock($backend, $response) {
|
||||
$userManager = $this->getMockBuilder('OCP\IUserManager')->disableOriginalConstructor()->getMock();
|
||||
$logger = $this->getMockBuilder('OCP\ILogger')->disableOriginalConstructor()->getMock();
|
||||
$accountManager = $this->getMockBuilder('OC\Accounts\AccountManager')->disableOriginalConstructor()->getMock();
|
||||
/** @var SyncService | \PHPUnit_Framework_MockObject_MockObject $ss */
|
||||
$ss = $this->getMockBuilder(SyncService::class)
|
||||
->setMethods(['ensureSystemAddressBookExists', 'requestSyncReport', 'download'])
|
||||
->setConstructorArgs([$backend, $userManager, $logger])
|
||||
->setConstructorArgs([$backend, $userManager, $logger, $accountManager])
|
||||
->getMock();
|
||||
$ss->method('requestSyncReport')->withAnyParameters()->willReturn(['response' => $response, 'token' => 'sync-token-1']);
|
||||
$ss->method('ensureSystemAddressBookExists')->willReturn(['id' => 1]);
|
||||
|
|
|
@ -31,14 +31,19 @@ use OCA\DAV\HookManager;
|
|||
use OCP\IL10N;
|
||||
use OCP\IUser;
|
||||
use OCP\IUserManager;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Test\TestCase;
|
||||
|
||||
class HookManagerTest extends TestCase {
|
||||
/** @var IL10N */
|
||||
private $l10n;
|
||||
|
||||
/** @var EventDispatcher | \PHPUnit_Framework_MockObject_MockObject */
|
||||
private $eventDispatcher;
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
$this->eventDispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcher')->disableOriginalConstructor()->getMock();
|
||||
$this->l10n = $this->createMock(IL10N::class);
|
||||
$this->l10n
|
||||
->expects($this->any())
|
||||
|
@ -82,7 +87,7 @@ class HookManagerTest extends TestCase {
|
|||
'principals/users/newUser',
|
||||
'contacts', ['{DAV:}displayname' => 'Contacts']);
|
||||
|
||||
$hm = new HookManager($userManager, $syncService, $cal, $card);
|
||||
$hm = new HookManager($userManager, $syncService, $cal, $card, $this->eventDispatcher);
|
||||
$hm->firstLogin($user);
|
||||
}
|
||||
|
||||
|
@ -116,7 +121,7 @@ class HookManagerTest extends TestCase {
|
|||
$card->expects($this->once())->method('getAddressBooksForUserCount')->willReturn(1);
|
||||
$card->expects($this->never())->method('createAddressBook');
|
||||
|
||||
$hm = new HookManager($userManager, $syncService, $cal, $card);
|
||||
$hm = new HookManager($userManager, $syncService, $cal, $card, $this->eventDispatcher);
|
||||
$hm->firstLogin($user);
|
||||
}
|
||||
|
||||
|
@ -154,7 +159,7 @@ class HookManagerTest extends TestCase {
|
|||
'principals/users/newUser',
|
||||
'contacts', ['{DAV:}displayname' => 'Contacts']);
|
||||
|
||||
$hm = new HookManager($userManager, $syncService, $cal, $card);
|
||||
$hm = new HookManager($userManager, $syncService, $cal, $card, $this->eventDispatcher);
|
||||
$hm->firstLogin($user);
|
||||
}
|
||||
|
||||
|
@ -195,7 +200,7 @@ class HookManagerTest extends TestCase {
|
|||
]);
|
||||
$card->expects($this->once())->method('deleteAddressBook');
|
||||
|
||||
$hm = new HookManager($userManager, $syncService, $cal, $card, $this->l10n);
|
||||
$hm = new HookManager($userManager, $syncService, $cal, $card, $this->eventDispatcher);
|
||||
$hm->preDeleteUser(['uid' => 'newUser']);
|
||||
$hm->postDeleteUser(['uid' => 'newUser']);
|
||||
}
|
||||
|
|
|
@ -943,4 +943,14 @@ class FederatedShareProvider implements IShareProvider {
|
|||
$result = $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes');
|
||||
return ($result === 'yes') ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if querying sharees on the lookup server is enabled
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isLookupServerQueriesEnabled() {
|
||||
$result = $this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'no');
|
||||
return ($result === 'yes') ? true : false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ class Admin implements ISettings {
|
|||
$parameters = [
|
||||
'outgoingServer2serverShareEnabled' => $this->fedShareProvider->isOutgoingServer2serverShareEnabled(),
|
||||
'incomingServer2serverShareEnabled' => $this->fedShareProvider->isIncomingServer2serverShareEnabled(),
|
||||
'lookupServerEnabled' => $this->fedShareProvider->isLookupServerQueriesEnabled(),
|
||||
];
|
||||
|
||||
return new TemplateResponse('federatedfilesharing', 'settings-admin', $parameters, '');
|
||||
|
|
|
@ -25,4 +25,11 @@ script('federatedfilesharing', 'settings-admin');
|
|||
<?php p($l->t('Allow users on this server to receive shares from other servers'));?>
|
||||
</label><br/>
|
||||
</p>
|
||||
<p>
|
||||
<input type="checkbox" name="lookupServerEnabled" id="lookupServerEnabled" class="checkbox"
|
||||
value="1" <?php if ($_['lookupServerEnabled']) print_unescaped('checked="checked"'); ?> />
|
||||
<label for="lookupServerEnabled">
|
||||
<?php p($l->t('Enable lookups on lookup server'));?>
|
||||
</label><br/>
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
@ -65,10 +65,15 @@ class AdminTest extends TestCase {
|
|||
->expects($this->once())
|
||||
->method('isIncomingServer2serverShareEnabled')
|
||||
->willReturn($state);
|
||||
$this->federatedShareProvider
|
||||
->expects($this->once())
|
||||
->method('isLookupServerQueriesEnabled')
|
||||
->willReturn($state);
|
||||
|
||||
$params = [
|
||||
'outgoingServer2serverShareEnabled' => $state,
|
||||
'incomingServer2serverShareEnabled' => $state,
|
||||
'lookupServerEnabled' => $state,
|
||||
];
|
||||
$expected = new TemplateResponse('federatedfilesharing', 'settings-admin', $params, '');
|
||||
$this->assertEquals($expected, $this->admin->getForm());
|
||||
|
|
|
@ -550,7 +550,7 @@ html.ie8 #fileList tr.selected td.filename>.selectCheckBox {
|
|||
}
|
||||
.bubble:after,
|
||||
#app-navigation .app-navigation-entry-menu:after {
|
||||
right: 6px;
|
||||
right: 12px;
|
||||
}
|
||||
.bubble:before,
|
||||
#app-navigation .app-navigation-entry-menu:before {
|
||||
|
@ -625,13 +625,6 @@ html.ie8 .column-mtime .selectedActions {
|
|||
padding-right: 14px;
|
||||
}
|
||||
|
||||
#fileList .popovermenu {
|
||||
margin-right: 6px;
|
||||
}
|
||||
.ie8 #fileList .popovermenu {
|
||||
margin-top: -10px;
|
||||
}
|
||||
|
||||
.ie8 #fileList a.action img,
|
||||
#fileList tr:hover a.action,
|
||||
#fileList a.action.permanent,
|
||||
|
@ -649,7 +642,6 @@ html.ie8 .column-mtime .selectedActions {
|
|||
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=30)";
|
||||
filter: alpha(opacity=30);
|
||||
opacity: .3;
|
||||
display:inline;
|
||||
}
|
||||
.ie8 #fileList a.action:hover img,
|
||||
#fileList tr a.action.disabled.action-download,
|
||||
|
@ -673,6 +665,7 @@ html.ie8 .column-mtime .selectedActions {
|
|||
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=70)" !important;
|
||||
filter: alpha(opacity=70) !important;
|
||||
opacity: .7 !important;
|
||||
display:inline;
|
||||
}
|
||||
/* always show actions on mobile, not only on hover */
|
||||
#fileList a.action.action-menu.permanent {
|
||||
|
@ -775,11 +768,6 @@ table.dragshadow td.size {
|
|||
padding: initial;
|
||||
}
|
||||
|
||||
#fileList .popovermenu a.action {
|
||||
padding: 10px;
|
||||
margin: -10px;
|
||||
}
|
||||
|
||||
html.ie8 #controls .button.new {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
@ -826,11 +814,7 @@ html.ie8 #controls .button.new {
|
|||
}
|
||||
|
||||
#fileList .popovermenu .action {
|
||||
display: block;
|
||||
line-height: 30px;
|
||||
padding-left: 5px;
|
||||
color: #000;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#filestable .filename .action .icon,
|
||||
|
|
|
@ -28,6 +28,7 @@ use OCP\AppFramework\Http;
|
|||
use OCP\AppFramework\OCS\OCSBadRequestException;
|
||||
use OCP\AppFramework\OCSController;
|
||||
use OCP\Contacts\IManager;
|
||||
use OCP\Http\Client\IClientService;
|
||||
use OCP\IGroup;
|
||||
use OCP\IGroupManager;
|
||||
use OCP\ILogger;
|
||||
|
@ -65,6 +66,9 @@ class ShareesAPIController extends OCSController {
|
|||
/** @var \OCP\Share\IManager */
|
||||
protected $shareManager;
|
||||
|
||||
/** @var IClientService */
|
||||
protected $clientService;
|
||||
|
||||
/** @var bool */
|
||||
protected $shareWithGroupOnly = false;
|
||||
|
||||
|
@ -89,6 +93,7 @@ class ShareesAPIController extends OCSController {
|
|||
'groups' => [],
|
||||
'remotes' => [],
|
||||
'emails' => [],
|
||||
'lookup' => [],
|
||||
];
|
||||
|
||||
protected $reachedEndFor = [];
|
||||
|
@ -104,6 +109,7 @@ class ShareesAPIController extends OCSController {
|
|||
* @param IURLGenerator $urlGenerator
|
||||
* @param ILogger $logger
|
||||
* @param \OCP\Share\IManager $shareManager
|
||||
* @param IClientService $clientService
|
||||
*/
|
||||
public function __construct($appName,
|
||||
IRequest $request,
|
||||
|
@ -114,7 +120,8 @@ class ShareesAPIController extends OCSController {
|
|||
IUserSession $userSession,
|
||||
IURLGenerator $urlGenerator,
|
||||
ILogger $logger,
|
||||
\OCP\Share\IManager $shareManager) {
|
||||
\OCP\Share\IManager $shareManager,
|
||||
IClientService $clientService) {
|
||||
parent::__construct($appName, $request);
|
||||
|
||||
$this->groupManager = $groupManager;
|
||||
|
@ -125,6 +132,7 @@ class ShareesAPIController extends OCSController {
|
|||
$this->urlGenerator = $urlGenerator;
|
||||
$this->logger = $logger;
|
||||
$this->shareManager = $shareManager;
|
||||
$this->clientService = $clientService;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -414,10 +422,11 @@ class ShareesAPIController extends OCSController {
|
|||
* @param int $page
|
||||
* @param int $perPage
|
||||
* @param int|int[] $shareType
|
||||
* @param bool $lookup
|
||||
* @return Http\DataResponse
|
||||
* @throws OCSBadRequestException
|
||||
*/
|
||||
public function search($search = '', $itemType = null, $page = 1, $perPage = 200, $shareType = null) {
|
||||
public function search($search = '', $itemType = null, $page = 1, $perPage = 200, $shareType = null, $lookup = true) {
|
||||
if ($perPage <= 0) {
|
||||
throw new OCSBadRequestException('Invalid perPage argument');
|
||||
}
|
||||
|
@ -459,7 +468,7 @@ class ShareesAPIController extends OCSController {
|
|||
$this->limit = (int) $perPage;
|
||||
$this->offset = $perPage * ($page - 1);
|
||||
|
||||
return $this->searchSharees($search, $itemType, $shareTypes, $page, $perPage);
|
||||
return $this->searchSharees($search, $itemType, $shareTypes, $page, $perPage, $lookup);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -485,10 +494,11 @@ class ShareesAPIController extends OCSController {
|
|||
* @param array $shareTypes
|
||||
* @param int $page
|
||||
* @param int $perPage
|
||||
* @param bool $lookup
|
||||
* @return Http\DataResponse
|
||||
* @throws OCSBadRequestException
|
||||
*/
|
||||
protected function searchSharees($search, $itemType, array $shareTypes, $page, $perPage) {
|
||||
protected function searchSharees($search, $itemType, array $shareTypes, $page, $perPage, $lookup) {
|
||||
// Verify arguments
|
||||
if ($itemType === null) {
|
||||
throw new OCSBadRequestException('Missing itemType');
|
||||
|
@ -510,11 +520,17 @@ class ShareesAPIController extends OCSController {
|
|||
$remoteResults = $this->getRemote($search);
|
||||
}
|
||||
|
||||
// Get emails
|
||||
$mailResults = ['results' => [], 'exact' => [], 'exactIdMatch' => false];
|
||||
if (in_array(Share::SHARE_TYPE_EMAIL, $shareTypes)) {
|
||||
$mailResults = $this->getEmail($search);
|
||||
}
|
||||
|
||||
// Get from lookup server
|
||||
if ($lookup) {
|
||||
$this->getLookup($search);
|
||||
}
|
||||
|
||||
// if we have a exact match, either for the federated cloud id or for the
|
||||
// email address we only return the exact match. It is highly unlikely
|
||||
// that the exact same email address and federated cloud id exists
|
||||
|
@ -609,6 +625,40 @@ class ShareesAPIController extends OCSController {
|
|||
return $result;
|
||||
}
|
||||
|
||||
protected function getLookup($search) {
|
||||
$isEnabled = $this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'no');
|
||||
$result = [];
|
||||
|
||||
if($isEnabled === 'yes') {
|
||||
try {
|
||||
$client = $this->clientService->newClient();
|
||||
$response = $client->get(
|
||||
'https://lookup.nextcloud.com/users?search=' . urlencode($search),
|
||||
[
|
||||
'timeout' => 10,
|
||||
'connect_timeout' => 3,
|
||||
]
|
||||
);
|
||||
|
||||
$body = json_decode($response->getBody(), true);
|
||||
|
||||
$result = [];
|
||||
foreach ($body as $lookup) {
|
||||
$result[] = [
|
||||
'label' => $lookup['federationId'],
|
||||
'value' => [
|
||||
'shareType' => Share::SHARE_TYPE_REMOTE,
|
||||
'shareWith' => $lookup['federationId'],
|
||||
],
|
||||
'extra' => $lookup,
|
||||
];
|
||||
}
|
||||
} catch (\Exception $e) {}
|
||||
}
|
||||
|
||||
$this->result['lookup'] = $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a bunch of pagination links for the current page
|
||||
*
|
||||
|
|
|
@ -29,6 +29,7 @@ use OCA\Files_Sharing\Controller\ShareesAPIController;
|
|||
use OCA\Files_Sharing\Tests\TestCase;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\OCS\OCSBadRequestException;
|
||||
use OCP\Http\Client\IClientService;
|
||||
use OCP\Share;
|
||||
|
||||
/**
|
||||
|
@ -60,6 +61,9 @@ class ShareesAPIControllerTest extends TestCase {
|
|||
/** @var \OCP\Share\IManager|\PHPUnit_Framework_MockObject_MockObject */
|
||||
protected $shareManager;
|
||||
|
||||
/** @var IClientService|\PHPUnit_Framework_MockObject_MockObject */
|
||||
private $clientService;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
|
@ -87,6 +91,8 @@ class ShareesAPIControllerTest extends TestCase {
|
|||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$this->clientService = $this->createMock(IClientService::class);
|
||||
|
||||
$this->sharees = new ShareesAPIController(
|
||||
'files_sharing',
|
||||
$this->request,
|
||||
|
@ -97,7 +103,8 @@ class ShareesAPIControllerTest extends TestCase {
|
|||
$this->session,
|
||||
$this->getMockBuilder('OCP\IURLGenerator')->disableOriginalConstructor()->getMock(),
|
||||
$this->getMockBuilder('OCP\ILogger')->disableOriginalConstructor()->getMock(),
|
||||
$this->shareManager
|
||||
$this->shareManager,
|
||||
$this->clientService
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1386,7 +1393,8 @@ class ShareesAPIControllerTest extends TestCase {
|
|||
$this->session,
|
||||
$this->getMockBuilder('OCP\IURLGenerator')->disableOriginalConstructor()->getMock(),
|
||||
$this->getMockBuilder('OCP\ILogger')->disableOriginalConstructor()->getMock(),
|
||||
$this->shareManager
|
||||
$this->shareManager,
|
||||
$this->clientService
|
||||
])
|
||||
->setMethods(array('searchSharees', 'isRemoteSharingAllowed', 'shareProviderExists'))
|
||||
->getMock();
|
||||
|
@ -1477,7 +1485,8 @@ class ShareesAPIControllerTest extends TestCase {
|
|||
$this->session,
|
||||
$this->getMockBuilder('OCP\IURLGenerator')->disableOriginalConstructor()->getMock(),
|
||||
$this->getMockBuilder('OCP\ILogger')->disableOriginalConstructor()->getMock(),
|
||||
$this->shareManager
|
||||
$this->shareManager,
|
||||
$this->clientService
|
||||
])
|
||||
->setMethods(array('searchSharees', 'isRemoteSharingAllowed'))
|
||||
->getMock();
|
||||
|
@ -1522,6 +1531,7 @@ class ShareesAPIControllerTest extends TestCase {
|
|||
'groups' => [],
|
||||
'remotes' => [],
|
||||
'emails' => [],
|
||||
'lookup' => [],
|
||||
], false],
|
||||
['test', 'folder', [Share::SHARE_TYPE_USER, Share::SHARE_TYPE_GROUP, Share::SHARE_TYPE_REMOTE], 1, 2, false, [], [], ['results' => [], 'exact' => [], 'exactIdMatch' => false],
|
||||
[
|
||||
|
@ -1530,6 +1540,7 @@ class ShareesAPIControllerTest extends TestCase {
|
|||
'groups' => [],
|
||||
'remotes' => [],
|
||||
'emails' => [],
|
||||
'lookup' => [],
|
||||
], false],
|
||||
[
|
||||
'test', 'folder', [Share::SHARE_TYPE_USER, Share::SHARE_TYPE_GROUP, Share::SHARE_TYPE_REMOTE], 1, 2, false, [
|
||||
|
@ -1551,6 +1562,7 @@ class ShareesAPIControllerTest extends TestCase {
|
|||
['label' => 'testz@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'testz@remote']],
|
||||
],
|
||||
'emails' => [],
|
||||
'lookup' => [],
|
||||
], true,
|
||||
],
|
||||
// No groups requested
|
||||
|
@ -1570,6 +1582,7 @@ class ShareesAPIControllerTest extends TestCase {
|
|||
['label' => 'testz@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'testz@remote']],
|
||||
],
|
||||
'emails' => [],
|
||||
'lookup' => [],
|
||||
], false,
|
||||
],
|
||||
// Share type restricted to user - Only one user
|
||||
|
@ -1585,6 +1598,7 @@ class ShareesAPIControllerTest extends TestCase {
|
|||
'groups' => [],
|
||||
'remotes' => [],
|
||||
'emails' => [],
|
||||
'lookup' => [],
|
||||
], false,
|
||||
],
|
||||
// Share type restricted to user - Multipage result
|
||||
|
@ -1602,6 +1616,7 @@ class ShareesAPIControllerTest extends TestCase {
|
|||
'groups' => [],
|
||||
'remotes' => [],
|
||||
'emails' => [],
|
||||
'lookup' => [],
|
||||
], true,
|
||||
],
|
||||
];
|
||||
|
@ -1636,7 +1651,8 @@ class ShareesAPIControllerTest extends TestCase {
|
|||
$this->session,
|
||||
$this->getMockBuilder('OCP\IURLGenerator')->disableOriginalConstructor()->getMock(),
|
||||
$this->getMockBuilder('OCP\ILogger')->disableOriginalConstructor()->getMock(),
|
||||
$this->shareManager
|
||||
$this->shareManager,
|
||||
$this->clientService
|
||||
])
|
||||
->setMethods(array('getShareesForShareIds', 'getUsers', 'getGroups', 'getRemote'))
|
||||
->getMock();
|
||||
|
|
46
apps/lookup_server_connector/appinfo/app.php
Normal file
46
apps/lookup_server_connector/appinfo/app.php
Normal file
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2016 Bjoern Schiessle <bjoern@schiessle.org>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* 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
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
$dispatcher = \OC::$server->getEventDispatcher();
|
||||
|
||||
$dispatcher->addListener('OC\AccountManager::userUpdated', function(\Symfony\Component\EventDispatcher\GenericEvent $event) {
|
||||
$user = $event->getSubject();
|
||||
|
||||
$keyManager = new \OC\Security\IdentityProof\Manager(
|
||||
\OC::$server->getAppDataDir('identityproof'),
|
||||
\OC::$server->getCrypto()
|
||||
);
|
||||
$updateLookupServer = new \OCA\LookupServerConnector\UpdateLookupServer(
|
||||
new \OC\Accounts\AccountManager(\OC::$server->getDatabaseConnection(), \OC::$server->getEventDispatcher()),
|
||||
\OC::$server->getConfig(),
|
||||
\OC::$server->getSecureRandom(),
|
||||
\OC::$server->getHTTPClientService(),
|
||||
$keyManager,
|
||||
new \OC\Security\IdentityProof\Signer(
|
||||
$keyManager,
|
||||
new \OC\AppFramework\Utility\TimeFactory(),
|
||||
\OC::$server->getURLGenerator(),
|
||||
\OC::$server->getUserManager()
|
||||
),
|
||||
\OC::$server->getJobList()
|
||||
);
|
||||
$updateLookupServer->userUpdated($user);
|
||||
});
|
18
apps/lookup_server_connector/appinfo/info.xml
Normal file
18
apps/lookup_server_connector/appinfo/info.xml
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0"?>
|
||||
<info>
|
||||
<id>lookup_server_connector</id>
|
||||
<name>Lookup Server Connector</name>
|
||||
<description>Sync public user information with the lookup server</description>
|
||||
<licence>AGPL</licence>
|
||||
<author>Bjoern Schiessle</author>
|
||||
<namespace>LookupServerConnector</namespace>
|
||||
<version>1.0.0</version>
|
||||
<category>other</category>
|
||||
<dependencies>
|
||||
<owncloud min-version="11.0" max-version="11.0" />
|
||||
</dependencies>
|
||||
<default_enable/>
|
||||
<types>
|
||||
<authentication/>
|
||||
</types>
|
||||
</info>
|
81
apps/lookup_server_connector/lib/BackgroundJobs/RetryJob.php
Normal file
81
apps/lookup_server_connector/lib/BackgroundJobs/RetryJob.php
Normal file
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2016 Bjoern Schiessle <bjoern@schiessle.org>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* 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
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
namespace OCA\LookupServerConnector\BackgroundJobs;
|
||||
|
||||
|
||||
use OC\BackgroundJob\Job;
|
||||
use OCP\BackgroundJob\IJobList;
|
||||
use OCP\Http\Client\IClientService;
|
||||
|
||||
class RetryJob extends Job {
|
||||
/** @var IClientService */
|
||||
private $clientService;
|
||||
/** @var IJobList */
|
||||
private $jobList;
|
||||
/** @var string */
|
||||
private $lookupServer = 'https://lookup.nextcloud.com/users';
|
||||
|
||||
/**
|
||||
* @param IClientService|null $clientService
|
||||
* @param IJobList|null $jobList
|
||||
*/
|
||||
public function __construct(IClientService $clientService = null,
|
||||
IJobList $jobList = null) {
|
||||
if($clientService !== null) {
|
||||
$this->clientService = $clientService;
|
||||
} else {
|
||||
$this->clientService = \OC::$server->getHTTPClientService();
|
||||
}
|
||||
if($jobList !== null) {
|
||||
$this->jobList = $jobList;
|
||||
} else {
|
||||
$this->jobList = \OC::$server->getJobList();
|
||||
}
|
||||
}
|
||||
|
||||
protected function run($argument) {
|
||||
if($argument['retryNo'] === 5) {
|
||||
return;
|
||||
}
|
||||
|
||||
$client = $this->clientService->newClient();
|
||||
|
||||
try {
|
||||
$client->post($this->lookupServer,
|
||||
[
|
||||
'body' => json_encode($argument['dataArray']),
|
||||
'timeout' => 10,
|
||||
'connect_timeout' => 3,
|
||||
]
|
||||
);
|
||||
} catch (\Exception $e) {
|
||||
$this->jobList->add(RetryJob::class,
|
||||
[
|
||||
'dataArray' => $argument['dataArray'],
|
||||
'retryNo' => $argument['retryNo'] + 1,
|
||||
]
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
136
apps/lookup_server_connector/lib/UpdateLookupServer.php
Normal file
136
apps/lookup_server_connector/lib/UpdateLookupServer.php
Normal file
|
@ -0,0 +1,136 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2016 Bjoern Schiessle <bjoern@schiessle.org>
|
||||
* @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* 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
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\LookupServerConnector;
|
||||
|
||||
use OC\Accounts\AccountManager;
|
||||
use OC\Security\IdentityProof\Manager;
|
||||
use OC\Security\IdentityProof\Signer;
|
||||
use OCA\LookupServerConnector\BackgroundJobs\RetryJob;
|
||||
use OCP\BackgroundJob\IJobList;
|
||||
use OCP\Http\Client\IClientService;
|
||||
use OCP\IConfig;
|
||||
use OCP\IUser;
|
||||
use OCP\Security\ISecureRandom;
|
||||
|
||||
/**
|
||||
* Class UpdateLookupServer
|
||||
*
|
||||
* @package OCA\LookupServerConnector
|
||||
*/
|
||||
class UpdateLookupServer {
|
||||
/** @var AccountManager */
|
||||
private $accountManager;
|
||||
/** @var IConfig */
|
||||
private $config;
|
||||
/** @var ISecureRandom */
|
||||
private $secureRandom;
|
||||
/** @var IClientService */
|
||||
private $clientService;
|
||||
/** @var Manager */
|
||||
private $keyManager;
|
||||
/** @var Signer */
|
||||
private $signer;
|
||||
/** @var IJobList */
|
||||
private $jobList;
|
||||
/** @var string URL point to lookup server */
|
||||
private $lookupServer = 'https://lookup.nextcloud.com/users';
|
||||
|
||||
/**
|
||||
* @param AccountManager $accountManager
|
||||
* @param IConfig $config
|
||||
* @param ISecureRandom $secureRandom
|
||||
* @param IClientService $clientService
|
||||
* @param Manager $manager
|
||||
* @param Signer $signer
|
||||
* @param IJobList $jobList
|
||||
*/
|
||||
public function __construct(AccountManager $accountManager,
|
||||
IConfig $config,
|
||||
ISecureRandom $secureRandom,
|
||||
IClientService $clientService,
|
||||
Manager $manager,
|
||||
Signer $signer,
|
||||
IJobList $jobList) {
|
||||
$this->accountManager = $accountManager;
|
||||
$this->config = $config;
|
||||
$this->secureRandom = $secureRandom;
|
||||
$this->clientService = $clientService;
|
||||
$this->keyManager = $manager;
|
||||
$this->signer = $signer;
|
||||
$this->jobList = $jobList;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param IUser $user
|
||||
*/
|
||||
public function userUpdated(IUser $user) {
|
||||
$userData = $this->accountManager->getUser($user);
|
||||
$publicData = [];
|
||||
|
||||
foreach ($userData as $key => $data) {
|
||||
if ($data['scope'] === AccountManager::VISIBILITY_PUBLIC) {
|
||||
$publicData[$key] = $data;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($publicData)) {
|
||||
$this->sendToLookupServer($user, $publicData);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* send public user data to the lookup server
|
||||
*
|
||||
* @param IUser $user
|
||||
* @param array $publicData
|
||||
*/
|
||||
protected function sendToLookupServer(IUser $user, array $publicData) {
|
||||
$dataArray = [
|
||||
'federationId' => $user->getCloudId(),
|
||||
'name' => isset($publicData[AccountManager::PROPERTY_DISPLAYNAME]) ? $publicData[AccountManager::PROPERTY_DISPLAYNAME]['value'] : '',
|
||||
'email' => isset($publicData[AccountManager::PROPERTY_EMAIL]) ? $publicData[AccountManager::PROPERTY_EMAIL]['value'] : '',
|
||||
'address' => isset($publicData[AccountManager::PROPERTY_ADDRESS]) ? $publicData[AccountManager::PROPERTY_ADDRESS]['value'] : '',
|
||||
'website' => isset($publicData[AccountManager::PROPERTY_WEBSITE]) ? $publicData[AccountManager::PROPERTY_WEBSITE]['value'] : '',
|
||||
'twitter' => isset($publicData[AccountManager::PROPERTY_TWITTER]) ? $publicData[AccountManager::PROPERTY_TWITTER]['value'] : '',
|
||||
'phone' => isset($publicData[AccountManager::PROPERTY_PHONE]) ? $publicData[AccountManager::PROPERTY_PHONE]['value'] : '',
|
||||
];
|
||||
$dataArray = $this->signer->sign('lookupserver', $dataArray, $user);
|
||||
$httpClient = $this->clientService->newClient();
|
||||
try {
|
||||
$httpClient->post($this->lookupServer,
|
||||
[
|
||||
'body' => json_encode($dataArray),
|
||||
'timeout' => 10,
|
||||
'connect_timeout' => 3,
|
||||
]
|
||||
);
|
||||
} catch (\Exception $e) {
|
||||
$this->jobList->add(RetryJob::class,
|
||||
[
|
||||
'dataArray' => $dataArray,
|
||||
'retryNo' => 0,
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -290,6 +290,7 @@ Feature: provisioning
|
|||
| files_sharing |
|
||||
| files_trashbin |
|
||||
| files_versions |
|
||||
| lookup_server_connector |
|
||||
| provisioning_api |
|
||||
| sharebymail |
|
||||
| systemtags |
|
||||
|
|
|
@ -30,7 +30,10 @@
|
|||
|
||||
namespace OC\Core;
|
||||
|
||||
use OC\AppFramework\Utility\SimpleContainer;
|
||||
use OC\Security\IdentityProof\Manager;
|
||||
use OCP\AppFramework\App;
|
||||
use OCP\Files\IAppData;
|
||||
use OCP\Util;
|
||||
|
||||
/**
|
||||
|
@ -45,8 +48,14 @@ class Application extends App {
|
|||
|
||||
$container = $this->getContainer();
|
||||
|
||||
$container->registerService('defaultMailAddress', function() {
|
||||
$container->registerService('defaultMailAddress', function () {
|
||||
return Util::getDefaultEmailAddress('lostpassword-noreply');
|
||||
});
|
||||
$container->registerService(Manager::class, function () {
|
||||
return new Manager(
|
||||
\OC::$server->getAppDataDir('identityproof'),
|
||||
\OC::$server->getCrypto()
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ namespace OC\Core\Controller;
|
|||
|
||||
use OC\CapabilitiesManager;
|
||||
use OC\Security\Bruteforce\Throttler;
|
||||
use OC\Security\IdentityProof\Manager;
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
use OCP\IRequest;
|
||||
use OCP\IUserManager;
|
||||
|
@ -32,13 +33,12 @@ class OCSController extends \OCP\AppFramework\OCSController {
|
|||
|
||||
/** @var CapabilitiesManager */
|
||||
private $capabilitiesManager;
|
||||
|
||||
/** @var IUserSession */
|
||||
private $userSession;
|
||||
|
||||
/** @var IUserManager */
|
||||
private $userManager;
|
||||
|
||||
/** @var Manager */
|
||||
private $keyManager;
|
||||
/** @var Throttler */
|
||||
private $throttler;
|
||||
|
||||
|
@ -51,19 +51,21 @@ class OCSController extends \OCP\AppFramework\OCSController {
|
|||
* @param IUserSession $userSession
|
||||
* @param IUserManager $userManager
|
||||
* @param Throttler $throttler
|
||||
* @param Manager $keyManager
|
||||
*/
|
||||
public function __construct($appName,
|
||||
IRequest $request,
|
||||
CapabilitiesManager $capabilitiesManager,
|
||||
IUserSession $userSession,
|
||||
IUserManager $userManager,
|
||||
Throttler $throttler) {
|
||||
Throttler $throttler,
|
||||
Manager $keyManager) {
|
||||
parent::__construct($appName, $request);
|
||||
|
||||
$this->capabilitiesManager = $capabilitiesManager;
|
||||
$this->userSession = $userSession;
|
||||
$this->userManager = $userManager;
|
||||
$this->throttler = $throttler;
|
||||
$this->keyManager = $keyManager;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -139,4 +141,24 @@ class OCSController extends \OCP\AppFramework\OCSController {
|
|||
}
|
||||
return new DataResponse(null, 101);
|
||||
}
|
||||
|
||||
/**
|
||||
* @PublicPage
|
||||
*
|
||||
* @param string $cloudId
|
||||
* @return DataResponse
|
||||
*/
|
||||
public function getIdentityProof($cloudId) {
|
||||
$userObject = $this->userManager->get($cloudId);
|
||||
|
||||
if($userObject !== null) {
|
||||
$key = $this->keyManager->getKey($userObject);
|
||||
$data = [
|
||||
'public' => $key->getPublic(),
|
||||
];
|
||||
return new DataResponse($data);
|
||||
}
|
||||
|
||||
return new DataResponse('User not found', 404);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -289,7 +289,7 @@
|
|||
border-radius: 3px;
|
||||
border-top-right-radius: 0;
|
||||
z-index: 110;
|
||||
margin: -5px 14px 5px 10px;
|
||||
margin-top: -5px;
|
||||
right: 0;
|
||||
-webkit-filter: drop-shadow(0 0 5px rgba(150, 150, 150, 0.75));
|
||||
-moz-filter: drop-shadow(0 0 5px rgba(150, 150, 150, 0.75));
|
||||
|
@ -333,7 +333,8 @@
|
|||
opacity: .5 !important;
|
||||
}
|
||||
.bubble .action:hover,
|
||||
.bubble .action:focus {
|
||||
.bubble .action:focus,
|
||||
.bubble .action.active {
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)" !important;
|
||||
filter: alpha(opacity=100) !important;
|
||||
opacity: 1 !important;
|
||||
|
@ -646,20 +647,13 @@ em {
|
|||
}
|
||||
|
||||
.popovermenu .menuitem:hover,
|
||||
.popovermenu .menuitem:focus {
|
||||
.popovermenu .menuitem:focus,
|
||||
.popovermenu .menuitem.active {
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";
|
||||
filter: alpha(opacity=100);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.popovermenu {
|
||||
padding: 4px 12px;
|
||||
}
|
||||
|
||||
.popovermenu li {
|
||||
padding: 5px 0;
|
||||
}
|
||||
|
||||
.popovermenu .menuitem img {
|
||||
padding: initial;
|
||||
}
|
||||
|
@ -667,8 +661,8 @@ em {
|
|||
.popovermenu a.menuitem,
|
||||
.popovermenu label.menuitem,
|
||||
.popovermenu .menuitem {
|
||||
padding: 10px;
|
||||
margin: -10px;
|
||||
padding: 10px !important;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.popovermenu.hidden {
|
||||
|
@ -676,11 +670,10 @@ em {
|
|||
}
|
||||
|
||||
.popovermenu .menuitem {
|
||||
display: block;
|
||||
display: flex !important;
|
||||
line-height: 30px;
|
||||
padding-left: 5px;
|
||||
color: #000;
|
||||
padding: 0;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.popovermenu .menuitem .icon,
|
||||
|
|
|
@ -227,8 +227,8 @@
|
|||
#apps-management a:hover span,
|
||||
#apps-management a:focus span,
|
||||
#apps-management a.active span {
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=75)";
|
||||
opacity: .75;
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#navigation .app-icon {
|
||||
|
@ -372,8 +372,8 @@
|
|||
#expanddiv a:focus,
|
||||
#expanddiv a:active,
|
||||
#expanddiv a.active {
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=75)";
|
||||
opacity: .75;
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* do not show display name when profile picture is present */
|
||||
|
|
|
@ -149,6 +149,7 @@
|
|||
var users = result.ocs.data.exact.users.concat(result.ocs.data.users);
|
||||
var groups = result.ocs.data.exact.groups.concat(result.ocs.data.groups);
|
||||
var remotes = result.ocs.data.exact.remotes.concat(result.ocs.data.remotes);
|
||||
var lookup = result.ocs.data.lookup;
|
||||
if (typeof(result.ocs.data.emails) !== 'undefined') {
|
||||
var emails = result.ocs.data.exact.emails.concat(result.ocs.data.emails);
|
||||
} else {
|
||||
|
@ -159,6 +160,7 @@
|
|||
var groupsLength;
|
||||
var remotesLength;
|
||||
var emailsLength;
|
||||
var lookupLength;
|
||||
|
||||
var i, j;
|
||||
|
||||
|
@ -224,7 +226,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
var suggestions = users.concat(groups).concat(remotes).concat(emails);
|
||||
var suggestions = users.concat(groups).concat(remotes).concat(emails).concat(lookup);
|
||||
|
||||
if (suggestions.length > 0) {
|
||||
$('.shareWithField').removeClass('error')
|
||||
|
|
|
@ -529,7 +529,8 @@ describe('OC.Share.ShareDialogView', function() {
|
|||
},
|
||||
'users' : [{'label': 'bob', 'value': {'shareType': 0, 'shareWith': 'test'}}],
|
||||
'groups' : [],
|
||||
'remotes': []
|
||||
'remotes': [],
|
||||
'lookup': []
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -577,7 +578,8 @@ describe('OC.Share.ShareDialogView', function() {
|
|||
}
|
||||
],
|
||||
'groups': [],
|
||||
'remotes': []
|
||||
'remotes': [],
|
||||
'lookup': []
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -635,7 +637,8 @@ describe('OC.Share.ShareDialogView', function() {
|
|||
}
|
||||
],
|
||||
'groups': [],
|
||||
'remotes': []
|
||||
'remotes': [],
|
||||
'lookup': []
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -715,7 +718,8 @@ describe('OC.Share.ShareDialogView', function() {
|
|||
}
|
||||
],
|
||||
'groups': [],
|
||||
'remotes': []
|
||||
'remotes': [],
|
||||
'lookup': []
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -765,7 +769,8 @@ describe('OC.Share.ShareDialogView', function() {
|
|||
}
|
||||
}
|
||||
],
|
||||
'remotes': []
|
||||
'remotes': [],
|
||||
'lookup': []
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -815,7 +820,8 @@ describe('OC.Share.ShareDialogView', function() {
|
|||
'shareWith': 'foo2@bar.com/baz'
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
'lookup': []
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -61,6 +61,7 @@ $application->registerRoutes($this, [
|
|||
['root' => '/cloud', 'name' => 'OCS#getCurrentUser', 'url' => '/user', 'verb' => 'GET'],
|
||||
['root' => '', 'name' => 'OCS#getConfig', 'url' => '/config', 'verb' => 'GET'],
|
||||
['root' => '/person', 'name' => 'OCS#personCheck', 'url' => '/check', 'verb' => 'POST'],
|
||||
['root' => '/identityproof', 'name' => 'OCS#getIdentityProof', 'url' => '/key/{cloudId}', 'verb' => 'GET'],
|
||||
],
|
||||
]);
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
"firstrunwizard",
|
||||
"gallery",
|
||||
"logreader",
|
||||
"lookup_server_connector",
|
||||
"notifications",
|
||||
"password_policy",
|
||||
"provisioning_api",
|
||||
|
@ -42,6 +43,7 @@
|
|||
"files",
|
||||
"dav",
|
||||
"federatedfilesharing",
|
||||
"lookup_server_connector",
|
||||
"provisioning_api",
|
||||
"twofactor_backupcodes",
|
||||
"workflowengine"
|
||||
|
|
|
@ -2113,4 +2113,36 @@
|
|||
</declaration>
|
||||
</table>
|
||||
|
||||
<table>
|
||||
|
||||
<name>*dbprefix*accounts</name>
|
||||
|
||||
<declaration>
|
||||
<field>
|
||||
<name>uid</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
<length>64</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>data</name>
|
||||
<type>clob</type>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
</field>
|
||||
|
||||
<index>
|
||||
<name>uid_index</name>
|
||||
<primary>true</primary>
|
||||
<unique>true</unique>
|
||||
<field>
|
||||
<name>uid</name>
|
||||
<sorting>ascending</sorting>
|
||||
</field>
|
||||
</index>
|
||||
|
||||
</declaration>
|
||||
</table>
|
||||
|
||||
</database>
|
||||
|
|
201
lib/private/Accounts/AccountManager.php
Normal file
201
lib/private/Accounts/AccountManager.php
Normal file
|
@ -0,0 +1,201 @@
|
|||
<?php
|
||||
/**
|
||||
* @author Björn Schießle <bjoern@schiessle.org>
|
||||
*
|
||||
* @copyright Copyright (c) 2016, ownCloud, Inc.
|
||||
* @copyright Copyright (c) 2016, Björn Schießle
|
||||
* @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 OC\Accounts;
|
||||
|
||||
use OCP\IDBConnection;
|
||||
use OCP\IUser;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\GenericEvent;
|
||||
|
||||
/**
|
||||
* Class AccountManager
|
||||
*
|
||||
* Manage system accounts table
|
||||
*
|
||||
* @group DB
|
||||
* @package OC\Accounts
|
||||
*/
|
||||
class AccountManager {
|
||||
|
||||
/** nobody can see my account details */
|
||||
const VISIBILITY_PRIVATE = 'private';
|
||||
/** only contacts, especially trusted servers can see my contact details */
|
||||
const VISIBILITY_CONTACTS_ONLY = 'contacts';
|
||||
/** every body ca see my contact detail, will be published to the lookup server */
|
||||
const VISIBILITY_PUBLIC = 'public';
|
||||
|
||||
const PROPERTY_AVATAR = 'avatar';
|
||||
const PROPERTY_DISPLAYNAME = 'displayname';
|
||||
const PROPERTY_PHONE = 'phone';
|
||||
const PROPERTY_EMAIL = 'email';
|
||||
const PROPERTY_WEBSITE = 'website';
|
||||
const PROPERTY_ADDRESS = 'address';
|
||||
const PROPERTY_TWITTER = 'twitter';
|
||||
|
||||
/** @var IDBConnection database connection */
|
||||
private $connection;
|
||||
|
||||
/** @var string table name */
|
||||
private $table = 'accounts';
|
||||
|
||||
/** @var EventDispatcherInterface */
|
||||
private $eventDispatcher;
|
||||
|
||||
/**
|
||||
* AccountManager constructor.
|
||||
*
|
||||
* @param IDBConnection $connection
|
||||
* @param EventDispatcherInterface $eventDispatcher
|
||||
*/
|
||||
public function __construct(IDBConnection $connection, EventDispatcherInterface $eventDispatcher) {
|
||||
$this->connection = $connection;
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* update user record
|
||||
*
|
||||
* @param IUser $user
|
||||
* @param $data
|
||||
*/
|
||||
public function updateUser(IUser $user, $data) {
|
||||
$userData = $this->getUser($user);
|
||||
if (empty($userData)) {
|
||||
$this->insertNewUser($user, $data);
|
||||
} else {
|
||||
$this->updateExistingUser($user, $data);
|
||||
}
|
||||
|
||||
$this->eventDispatcher->dispatch(
|
||||
'OC\AccountManager::userUpdated',
|
||||
new GenericEvent($user)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* get stored data from a given user
|
||||
*
|
||||
* @param IUser $user
|
||||
* @return array
|
||||
*/
|
||||
public function getUser(IUser $user) {
|
||||
$uid = $user->getUID();
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
$query->select('data')->from($this->table)
|
||||
->where($query->expr()->eq('uid', $query->createParameter('uid')))
|
||||
->setParameter('uid', $uid);
|
||||
$query->execute();
|
||||
$result = $query->execute()->fetchAll();
|
||||
|
||||
if (empty($result)) {
|
||||
$userData = $this->buildDefaultUserRecord($user);
|
||||
$this->insertNewUser($user, $userData);
|
||||
return $userData;
|
||||
}
|
||||
|
||||
return json_decode($result[0]['data'], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* add new user to accounts table
|
||||
*
|
||||
* @param IUser $user
|
||||
* @param array $data
|
||||
*/
|
||||
protected function insertNewUser(IUser $user, $data) {
|
||||
$uid = $user->getUID();
|
||||
$jsonEncodedData = json_encode($data);
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
$query->insert($this->table)
|
||||
->values(
|
||||
[
|
||||
'uid' => $query->createNamedParameter($uid),
|
||||
'data' => $query->createNamedParameter($jsonEncodedData),
|
||||
]
|
||||
)
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* update existing user in accounts table
|
||||
*
|
||||
* @param IUser $user
|
||||
* @param array $data
|
||||
*/
|
||||
protected function updateExistingUser(IUser $user, $data) {
|
||||
$uid = $user->getUID();
|
||||
$jsonEncodedData = json_encode($data);
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
$query->update($this->table)
|
||||
->set('data', $query->createNamedParameter($jsonEncodedData))
|
||||
->where($query->expr()->eq('uid', $query->createNamedParameter($uid)))
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* build default user record in case not data set exists yet
|
||||
*
|
||||
* @param IUser $user
|
||||
* @return array
|
||||
*/
|
||||
protected function buildDefaultUserRecord(IUser $user) {
|
||||
return [
|
||||
self::PROPERTY_DISPLAYNAME =>
|
||||
[
|
||||
'value' => $user->getDisplayName(),
|
||||
'scope' => self::VISIBILITY_CONTACTS_ONLY,
|
||||
],
|
||||
self::PROPERTY_ADDRESS =>
|
||||
[
|
||||
'value' => '',
|
||||
'scope' => self::VISIBILITY_PRIVATE,
|
||||
],
|
||||
self::PROPERTY_WEBSITE =>
|
||||
[
|
||||
'value' => '',
|
||||
'scope' => self::VISIBILITY_PRIVATE,
|
||||
],
|
||||
self::PROPERTY_EMAIL =>
|
||||
[
|
||||
'value' => $user->getEMailAddress(),
|
||||
'scope' => self::VISIBILITY_CONTACTS_ONLY,
|
||||
],
|
||||
self::PROPERTY_AVATAR =>
|
||||
[
|
||||
'scope' => self::VISIBILITY_CONTACTS_ONLY
|
||||
],
|
||||
self::PROPERTY_PHONE =>
|
||||
[
|
||||
'value' => '',
|
||||
'scope' => self::VISIBILITY_PRIVATE,
|
||||
],
|
||||
self::PROPERTY_TWITTER =>
|
||||
[
|
||||
'value' => '',
|
||||
'scope' => self::VISIBILITY_PRIVATE,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
46
lib/private/Security/IdentityProof/Key.php
Normal file
46
lib/private/Security/IdentityProof/Key.php
Normal file
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* 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
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OC\Security\IdentityProof;
|
||||
|
||||
class Key {
|
||||
/** @var string */
|
||||
private $publicKey;
|
||||
/** @var string */
|
||||
private $privateKey;
|
||||
|
||||
/**
|
||||
* @param string $publicKey
|
||||
* @param string $privateKey
|
||||
*/
|
||||
public function __construct($publicKey, $privateKey) {
|
||||
$this->publicKey = $publicKey;
|
||||
$this->privateKey = $privateKey;
|
||||
}
|
||||
|
||||
public function getPrivate() {
|
||||
return $this->privateKey;
|
||||
}
|
||||
|
||||
public function getPublic() {
|
||||
return $this->publicKey;
|
||||
}
|
||||
}
|
108
lib/private/Security/IdentityProof/Manager.php
Normal file
108
lib/private/Security/IdentityProof/Manager.php
Normal file
|
@ -0,0 +1,108 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* 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
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OC\Security\IdentityProof;
|
||||
|
||||
use OCP\Files\IAppData;
|
||||
use OCP\IUser;
|
||||
use OCP\Security\ICrypto;
|
||||
|
||||
class Manager {
|
||||
/** @var IAppData */
|
||||
private $appData;
|
||||
/** @var ICrypto */
|
||||
private $crypto;
|
||||
|
||||
/**
|
||||
* @param IAppData $appData
|
||||
* @param ICrypto $crypto
|
||||
*/
|
||||
public function __construct(IAppData $appData,
|
||||
ICrypto $crypto) {
|
||||
$this->appData = $appData;
|
||||
$this->crypto = $crypto;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the openssl functions to generate a public and private key.
|
||||
* In a separate function for unit testing purposes.
|
||||
*
|
||||
* @return array [$publicKey, $privateKey]
|
||||
*/
|
||||
protected function generateKeyPair() {
|
||||
$config = [
|
||||
'digest_alg' => 'sha512',
|
||||
'private_key_bits' => 2048,
|
||||
];
|
||||
|
||||
// Generate new key
|
||||
$res = openssl_pkey_new($config);
|
||||
openssl_pkey_export($res, $privateKey);
|
||||
|
||||
// Extract the public key from $res to $pubKey
|
||||
$publicKey = openssl_pkey_get_details($res);
|
||||
$publicKey = $publicKey['key'];
|
||||
|
||||
return [$publicKey, $privateKey];
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a key for $user
|
||||
* Note: If a key already exists it will be overwritten
|
||||
*
|
||||
* @param IUser $user
|
||||
* @return Key
|
||||
*/
|
||||
protected function generateKey(IUser $user) {
|
||||
list($publicKey, $privateKey) = $this->generateKeyPair();
|
||||
|
||||
// Write the private and public key to the disk
|
||||
try {
|
||||
$this->appData->newFolder($user->getUID());
|
||||
} catch (\Exception $e) {}
|
||||
$folder = $this->appData->getFolder($user->getUID());
|
||||
$folder->newFile('private')
|
||||
->putContent($this->crypto->encrypt($privateKey));
|
||||
$folder->newFile('public')
|
||||
->putContent($publicKey);
|
||||
|
||||
return new Key($publicKey, $privateKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get public and private key for $user
|
||||
*
|
||||
* @param IUser $user
|
||||
* @return Key
|
||||
*/
|
||||
public function getKey(IUser $user) {
|
||||
try {
|
||||
$folder = $this->appData->getFolder($user->getUID());
|
||||
$privateKey = $this->crypto->decrypt(
|
||||
$folder->getFile('private')->getContent()
|
||||
);
|
||||
$publicKey = $folder->getFile('public')->getContent();
|
||||
return new Key($publicKey, $privateKey);
|
||||
} catch (\Exception $e) {
|
||||
return $this->generateKey($user);
|
||||
}
|
||||
}
|
||||
}
|
120
lib/private/Security/IdentityProof/Signer.php
Normal file
120
lib/private/Security/IdentityProof/Signer.php
Normal file
|
@ -0,0 +1,120 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* 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
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OC\Security\IdentityProof;
|
||||
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\IUser;
|
||||
use OCP\IUserManager;
|
||||
|
||||
class Signer {
|
||||
/** @var Manager */
|
||||
private $keyManager;
|
||||
/** @var ITimeFactory */
|
||||
private $timeFactory;
|
||||
/** @var IURLGenerator */
|
||||
private $urlGenerator;
|
||||
/** @var IUserManager */
|
||||
private $userManager;
|
||||
|
||||
/**
|
||||
* @param Manager $keyManager
|
||||
* @param ITimeFactory $timeFactory
|
||||
* @param IURLGenerator $urlGenerator
|
||||
* @param IUserManager $userManager
|
||||
*/
|
||||
public function __construct(Manager $keyManager,
|
||||
ITimeFactory $timeFactory,
|
||||
IURLGenerator $urlGenerator,
|
||||
IUserManager $userManager) {
|
||||
$this->keyManager = $keyManager;
|
||||
$this->timeFactory = $timeFactory;
|
||||
$this->userManager = $userManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a signed blob for $data
|
||||
*
|
||||
* @param string $type
|
||||
* @param array $data
|
||||
* @param IUser $user
|
||||
* @return array ['message', 'signature']
|
||||
*/
|
||||
public function sign($type, array $data, IUser $user) {
|
||||
$privateKey = $this->keyManager->getKey($user)->getPrivate();
|
||||
$data = [
|
||||
'data' => $data,
|
||||
'type' => $type,
|
||||
'signer' => $user->getCloudId(),
|
||||
'timestamp' => $this->timeFactory->getTime(),
|
||||
];
|
||||
openssl_sign(json_encode($data), $signature, $privateKey, OPENSSL_ALGO_SHA512);
|
||||
|
||||
return [
|
||||
'message' => $data,
|
||||
'signature' => base64_encode($signature),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @return string
|
||||
*/
|
||||
private function removeProtocolFromUrl($url) {
|
||||
if (strpos($url, 'https://') === 0) {
|
||||
return substr($url, strlen('https://'));
|
||||
} else if (strpos($url, 'http://') === 0) {
|
||||
return substr($url, strlen('http://'));
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the data is signed properly
|
||||
*
|
||||
* @param array $data
|
||||
* @return bool
|
||||
*/
|
||||
public function verify(array $data) {
|
||||
if(isset($data['message'])
|
||||
&& isset($data['signature'])
|
||||
&& isset($data['message']['signer'])
|
||||
) {
|
||||
$server = $this->urlGenerator->getAbsoluteURL('/');
|
||||
$postfix = strlen('@' . rtrim($this->removeProtocolFromUrl($server), '/'));
|
||||
$userId = substr($data['message']['signer'], -$postfix);
|
||||
|
||||
$user = $this->userManager->get($userId);
|
||||
if($user !== null) {
|
||||
$key = $this->keyManager->getKey($user);
|
||||
return (bool)openssl_verify(
|
||||
json_encode($data['message']),
|
||||
base64_decode($data['signature']),
|
||||
$key->getPublic()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -53,7 +53,7 @@ class Updater extends BasicEmitter {
|
|||
|
||||
/** @var ILogger $log */
|
||||
private $log;
|
||||
|
||||
|
||||
/** @var IConfig */
|
||||
private $config;
|
||||
|
||||
|
|
|
@ -34,9 +34,21 @@ use OC\App\AppStore\Fetcher\AppFetcher;
|
|||
use OC\App\AppStore\Fetcher\CategoryFetcher;
|
||||
use OC\AppFramework\Utility\TimeFactory;
|
||||
use OC\Authentication\Token\IProvider;
|
||||
use OC\Files\View;
|
||||
use OC\Server;
|
||||
use OC\Settings\Controller\AppSettingsController;
|
||||
use OC\Settings\Controller\AuthSettingsController;
|
||||
use OC\Settings\Controller\CertificateController;
|
||||
use OC\Settings\Controller\CheckSetupController;
|
||||
use OC\Settings\Controller\EncryptionController;
|
||||
use OC\Settings\Controller\GroupsController;
|
||||
use OC\Settings\Controller\LogSettingsController;
|
||||
use OC\Settings\Controller\MailSettingsController;
|
||||
use OC\Settings\Controller\SecuritySettingsController;
|
||||
use OC\Settings\Controller\UsersController;
|
||||
use OC\Settings\Middleware\SubadminMiddleware;
|
||||
use OCP\AppFramework\App;
|
||||
use OCP\AppFramework\IAppContainer;
|
||||
use OCP\IContainer;
|
||||
use OCP\Settings\IManager;
|
||||
use OCP\Util;
|
||||
|
@ -57,7 +69,129 @@ class Application extends App {
|
|||
|
||||
// Register Middleware
|
||||
$container->registerAlias('SubadminMiddleware', SubadminMiddleware::class);
|
||||
$container->registerMiddleWare('SubadminMiddleware');
|
||||
/**
|
||||
* Controllers
|
||||
*/
|
||||
$container->registerService('MailSettingsController', function(IContainer $c) {
|
||||
return new MailSettingsController(
|
||||
$c->query('AppName'),
|
||||
$c->query('Request'),
|
||||
$c->query('L10N'),
|
||||
$c->query('Config'),
|
||||
$c->query('UserSession'),
|
||||
$c->query('Defaults'),
|
||||
$c->query('Mailer'),
|
||||
$c->query('DefaultMailAddress')
|
||||
);
|
||||
});
|
||||
$container->registerService('EncryptionController', function(IContainer $c) {
|
||||
return new EncryptionController(
|
||||
$c->query('AppName'),
|
||||
$c->query('Request'),
|
||||
$c->query('L10N'),
|
||||
$c->query('Config'),
|
||||
$c->query('DatabaseConnection'),
|
||||
$c->query('UserManager'),
|
||||
new View(),
|
||||
$c->query('Logger')
|
||||
);
|
||||
});
|
||||
|
||||
$container->registerService('AppSettingsController', function(IContainer $c) {
|
||||
return new AppSettingsController(
|
||||
$c->query('AppName'),
|
||||
$c->query('Request'),
|
||||
$c->query('L10N'),
|
||||
$c->query('Config'),
|
||||
$c->query('INavigationManager'),
|
||||
$c->query('IAppManager'),
|
||||
$c->query('CategoryFetcher'),
|
||||
$c->query('AppFetcher'),
|
||||
\OC::$server->getL10NFactory()
|
||||
);
|
||||
});
|
||||
$container->registerService('AuthSettingsController', function(IContainer $c) {
|
||||
return new AuthSettingsController(
|
||||
$c->query('AppName'),
|
||||
$c->query('Request'),
|
||||
$c->query('ServerContainer')->query('OC\Authentication\Token\IProvider'),
|
||||
$c->query('UserManager'),
|
||||
$c->query('ServerContainer')->getSession(),
|
||||
$c->query('ServerContainer')->getSecureRandom(),
|
||||
$c->query('UserId')
|
||||
);
|
||||
});
|
||||
$container->registerService('SecuritySettingsController', function(IContainer $c) {
|
||||
return new SecuritySettingsController(
|
||||
$c->query('AppName'),
|
||||
$c->query('Request'),
|
||||
$c->query('Config')
|
||||
);
|
||||
});
|
||||
$container->registerService('AccountManager', function(IAppContainer $c) {
|
||||
return new AccountManager($c->getServer()->getDatabaseConnection());
|
||||
});
|
||||
$container->registerService('CertificateController', function(IContainer $c) {
|
||||
return new CertificateController(
|
||||
$c->query('AppName'),
|
||||
$c->query('Request'),
|
||||
$c->query('CertificateManager'),
|
||||
$c->query('SystemCertificateManager'),
|
||||
$c->query('L10N'),
|
||||
$c->query('IAppManager')
|
||||
);
|
||||
});
|
||||
$container->registerService('GroupsController', function(IContainer $c) {
|
||||
return new GroupsController(
|
||||
$c->query('AppName'),
|
||||
$c->query('Request'),
|
||||
$c->query('GroupManager'),
|
||||
$c->query('UserSession'),
|
||||
$c->query('IsAdmin'),
|
||||
$c->query('L10N')
|
||||
);
|
||||
});
|
||||
$container->registerService('UsersController', function(IContainer $c) {
|
||||
return new UsersController(
|
||||
$c->query('AppName'),
|
||||
$c->query('Request'),
|
||||
$c->query('UserManager'),
|
||||
$c->query('GroupManager'),
|
||||
$c->query('UserSession'),
|
||||
$c->query('Config'),
|
||||
$c->query('IsAdmin'),
|
||||
$c->query('L10N'),
|
||||
$c->query('Logger'),
|
||||
$c->query('Defaults'),
|
||||
$c->query('Mailer'),
|
||||
$c->query('DefaultMailAddress'),
|
||||
$c->query('URLGenerator'),
|
||||
$c->query('OCP\\App\\IAppManager'),
|
||||
$c->query('OCP\\IAvatarManager'),
|
||||
$c->query('AccountManager')
|
||||
);
|
||||
});
|
||||
$container->registerService('LogSettingsController', function(IContainer $c) {
|
||||
return new LogSettingsController(
|
||||
$c->query('AppName'),
|
||||
$c->query('Request'),
|
||||
$c->query('Config'),
|
||||
$c->query('L10N')
|
||||
);
|
||||
});
|
||||
$container->registerService('CheckSetupController', function(IContainer $c) {
|
||||
return new CheckSetupController(
|
||||
$c->query('AppName'),
|
||||
$c->query('Request'),
|
||||
$c->query('Config'),
|
||||
$c->query('ClientService'),
|
||||
$c->query('URLGenerator'),
|
||||
$c->query('Util'),
|
||||
$c->query('L10N'),
|
||||
$c->query('Checker')
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Core class wrappers
|
||||
|
|
|
@ -30,7 +30,9 @@
|
|||
|
||||
namespace OC\Settings\Controller;
|
||||
|
||||
use OC\Accounts\AccountManager;
|
||||
use OC\AppFramework\Http;
|
||||
use OC\ForbiddenException;
|
||||
use OC\User\User;
|
||||
use OCP\App\IAppManager;
|
||||
use OCP\AppFramework\Controller;
|
||||
|
@ -47,6 +49,7 @@ use OCP\IUserManager;
|
|||
use OCP\IUserSession;
|
||||
use OCP\Mail\IMailer;
|
||||
use OCP\IAvatarManager;
|
||||
use Punic\Exception;
|
||||
|
||||
/**
|
||||
* @package OC\Settings\Controller
|
||||
|
@ -80,6 +83,8 @@ class UsersController extends Controller {
|
|||
private $isRestoreEnabled = false;
|
||||
/** @var IAvatarManager */
|
||||
private $avatarManager;
|
||||
/** @var AccountManager */
|
||||
private $accountManager;
|
||||
|
||||
/**
|
||||
* @param string $appName
|
||||
|
@ -97,6 +102,7 @@ class UsersController extends Controller {
|
|||
* @param IURLGenerator $urlGenerator
|
||||
* @param IAppManager $appManager
|
||||
* @param IAvatarManager $avatarManager
|
||||
* @param AccountManager $accountManager
|
||||
*/
|
||||
public function __construct($appName,
|
||||
IRequest $request,
|
||||
|
@ -112,7 +118,9 @@ class UsersController extends Controller {
|
|||
$fromMailAddress,
|
||||
IURLGenerator $urlGenerator,
|
||||
IAppManager $appManager,
|
||||
IAvatarManager $avatarManager) {
|
||||
IAvatarManager $avatarManager,
|
||||
AccountManager $accountManager
|
||||
) {
|
||||
parent::__construct($appName, $request);
|
||||
$this->userManager = $userManager;
|
||||
$this->groupManager = $groupManager;
|
||||
|
@ -126,6 +134,7 @@ class UsersController extends Controller {
|
|||
$this->fromMailAddress = $fromMailAddress;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
$this->avatarManager = $avatarManager;
|
||||
$this->accountManager = $accountManager;
|
||||
|
||||
// check for encryption state - TODO see formatUserForIndex
|
||||
$this->isEncryptionAppEnabled = $appManager->isEnabledForUser('encryption');
|
||||
|
@ -493,35 +502,42 @@ class UsersController extends Controller {
|
|||
}
|
||||
|
||||
/**
|
||||
* Set the mail address of a user
|
||||
*
|
||||
* @NoAdminRequired
|
||||
* @NoSubadminRequired
|
||||
* @PasswordConfirmationRequired
|
||||
*
|
||||
* @param string $id
|
||||
* @param string $mailAddress
|
||||
* @param string $avatarScope
|
||||
* @param string $displayname
|
||||
* @param string $displaynameScope
|
||||
* @param string $phone
|
||||
* @param string $phoneScope
|
||||
* @param string $email
|
||||
* @param string $emailScope
|
||||
* @param string $website
|
||||
* @param string $websiteScope
|
||||
* @param string $address
|
||||
* @param string $addressScope
|
||||
* @param string $twitter
|
||||
* @param string $twitterScope
|
||||
* @return DataResponse
|
||||
*/
|
||||
public function setMailAddress($id, $mailAddress) {
|
||||
$userId = $this->userSession->getUser()->getUID();
|
||||
$user = $this->userManager->get($id);
|
||||
public function setUserSettings($avatarScope,
|
||||
$displayname,
|
||||
$displaynameScope,
|
||||
$phone,
|
||||
$phoneScope,
|
||||
$email,
|
||||
$emailScope,
|
||||
$website,
|
||||
$websiteScope,
|
||||
$address,
|
||||
$addressScope,
|
||||
$twitter,
|
||||
$twitterScope
|
||||
) {
|
||||
|
||||
if($userId !== $id
|
||||
&& !$this->isAdmin
|
||||
&& !$this->groupManager->getSubAdmin()->isUserAccessible($this->userSession->getUser(), $user)) {
|
||||
return new DataResponse(
|
||||
array(
|
||||
'status' => 'error',
|
||||
'data' => array(
|
||||
'message' => (string)$this->l10n->t('Forbidden')
|
||||
)
|
||||
),
|
||||
Http::STATUS_FORBIDDEN
|
||||
);
|
||||
}
|
||||
|
||||
if($mailAddress !== '' && !$this->mailer->validateMailAddress($mailAddress)) {
|
||||
if(!empty($email) && !$this->mailer->validateMailAddress($email)) {
|
||||
return new DataResponse(
|
||||
array(
|
||||
'status' => 'error',
|
||||
|
@ -533,46 +549,80 @@ class UsersController extends Controller {
|
|||
);
|
||||
}
|
||||
|
||||
if(!$user){
|
||||
$user = $this->userSession->getUser();
|
||||
|
||||
$data = [
|
||||
AccountManager::PROPERTY_AVATAR => ['scope' => $avatarScope],
|
||||
AccountManager::PROPERTY_DISPLAYNAME => ['value' => $displayname, 'scope' => $displaynameScope],
|
||||
AccountManager::PROPERTY_EMAIL=> ['value' => $email, 'scope' => $emailScope],
|
||||
AccountManager::PROPERTY_WEBSITE => ['value' => $website, 'scope' => $websiteScope],
|
||||
AccountManager::PROPERTY_ADDRESS => ['value' => $address, 'scope' => $addressScope],
|
||||
AccountManager::PROPERTY_PHONE => ['value' => $phone, 'scope' => $phoneScope],
|
||||
AccountManager::PROPERTY_TWITTER => ['value' => $twitter, 'scope' => $twitterScope]
|
||||
];
|
||||
|
||||
$this->accountManager->updateUser($user, $data);
|
||||
|
||||
try {
|
||||
$this->saveUserSettings($user, $data);
|
||||
return new DataResponse(
|
||||
array(
|
||||
'status' => 'error',
|
||||
'status' => 'success',
|
||||
'data' => array(
|
||||
'message' => (string)$this->l10n->t('Invalid user')
|
||||
'userId' => $user->getUID(),
|
||||
'avatarScope' => $avatarScope,
|
||||
'displayname' => $displayname,
|
||||
'displaynameScope' => $displaynameScope,
|
||||
'email' => $email,
|
||||
'emailScope' => $emailScope,
|
||||
'website' => $website,
|
||||
'websiteScope' => $websiteScope,
|
||||
'address' => $address,
|
||||
'addressScope' => $addressScope,
|
||||
'message' => (string)$this->l10n->t('Settings saved')
|
||||
)
|
||||
),
|
||||
Http::STATUS_UNPROCESSABLE_ENTITY
|
||||
Http::STATUS_OK
|
||||
);
|
||||
} catch (ForbiddenException $e) {
|
||||
return new DataResponse([
|
||||
'status' => 'error',
|
||||
'data' => [
|
||||
'message' => $e->getMessage()
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
// this is the only permission a backend provides and is also used
|
||||
// for the permission of setting a email address
|
||||
if(!$user->canChangeDisplayName()){
|
||||
return new DataResponse(
|
||||
array(
|
||||
'status' => 'error',
|
||||
'data' => array(
|
||||
'message' => (string)$this->l10n->t('Unable to change mail address')
|
||||
)
|
||||
),
|
||||
Http::STATUS_FORBIDDEN
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* update account manager with new user data
|
||||
*
|
||||
* @param IUser $user
|
||||
* @param array $data
|
||||
* @throws ForbiddenException
|
||||
*/
|
||||
private function saveUserSettings(IUser $user, $data) {
|
||||
|
||||
// keep the user back-end up-to-date with the latest display name and email
|
||||
// address
|
||||
$oldDisplayName = $user->getDisplayName();
|
||||
if (isset($data[AccountManager::PROPERTY_DISPLAYNAME]['value']) && $oldDisplayName !== $data[AccountManager::PROPERTY_DISPLAYNAME]['value']) {
|
||||
$result = $user->setDisplayName($data[AccountManager::PROPERTY_DISPLAYNAME]['value']);
|
||||
if ($result === false) {
|
||||
throw new ForbiddenException($this->l10n->t('Unable to change full name'));
|
||||
}
|
||||
}
|
||||
|
||||
// delete user value if email address is empty
|
||||
$user->setEMailAddress($mailAddress);
|
||||
if (isset($data['email'][0]['value']) && $user->getEMailAddress() !== $data['email'][0]['value']) {
|
||||
$result = $user->setEMailAddress($data['email'][0]['value']);
|
||||
if ($result === false) {
|
||||
throw new ForbiddenException($this->l10n->t('Unable to change mail address'));
|
||||
}
|
||||
}
|
||||
|
||||
return new DataResponse(
|
||||
array(
|
||||
'status' => 'success',
|
||||
'data' => array(
|
||||
'username' => $id,
|
||||
'mailAddress' => $mailAddress,
|
||||
'message' => (string)$this->l10n->t('Email saved')
|
||||
)
|
||||
),
|
||||
Http::STATUS_OK
|
||||
);
|
||||
$this->accountManager->updateUser($user, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -619,6 +669,7 @@ class UsersController extends Controller {
|
|||
* @NoAdminRequired
|
||||
* @NoSubadminRequired
|
||||
* @PasswordConfirmationRequired
|
||||
* @todo merge into saveUserSettings
|
||||
*
|
||||
* @param string $username
|
||||
* @param string $displayName
|
||||
|
@ -626,11 +677,6 @@ class UsersController extends Controller {
|
|||
*/
|
||||
public function setDisplayName($username, $displayName) {
|
||||
$currentUser = $this->userSession->getUser();
|
||||
|
||||
if ($username === null) {
|
||||
$username = $currentUser->getUID();
|
||||
}
|
||||
|
||||
$user = $this->userManager->get($username);
|
||||
|
||||
if ($user === null ||
|
||||
|
@ -638,8 +684,10 @@ class UsersController extends Controller {
|
|||
(
|
||||
!$this->groupManager->isAdmin($currentUser->getUID()) &&
|
||||
!$this->groupManager->getSubAdmin()->isUserAccessible($currentUser, $user) &&
|
||||
$currentUser !== $user)
|
||||
) {
|
||||
$currentUser->getUID() !== $username
|
||||
|
||||
)
|
||||
) {
|
||||
return new DataResponse([
|
||||
'status' => 'error',
|
||||
'data' => [
|
||||
|
@ -648,7 +696,12 @@ class UsersController extends Controller {
|
|||
]);
|
||||
}
|
||||
|
||||
if ($user->setDisplayName($displayName)) {
|
||||
$userData = $this->accountManager->getUser($user);
|
||||
$userData[AccountManager::PROPERTY_DISPLAYNAME]['value'] = $displayName;
|
||||
|
||||
|
||||
try {
|
||||
$this->saveUserSettings($user, $userData);
|
||||
return new DataResponse([
|
||||
'status' => 'success',
|
||||
'data' => [
|
||||
|
@ -657,11 +710,11 @@ class UsersController extends Controller {
|
|||
'displayName' => $displayName,
|
||||
],
|
||||
]);
|
||||
} else {
|
||||
} catch (ForbiddenException $e) {
|
||||
return new DataResponse([
|
||||
'status' => 'error',
|
||||
'data' => [
|
||||
'message' => $this->l10n->t('Unable to change full name'),
|
||||
'message' => $e->getMessage(),
|
||||
'displayName' => $user->getDisplayName(),
|
||||
],
|
||||
]);
|
||||
|
|
|
@ -7,16 +7,14 @@ input#openid, input#webdav { width:20em; }
|
|||
|
||||
/* PERSONAL */
|
||||
|
||||
#avatar {
|
||||
display: inline-block;
|
||||
float: left;
|
||||
#avatarform {
|
||||
width: 160px;
|
||||
padding-right: 0;
|
||||
}
|
||||
#avatar .avatardiv {
|
||||
#avatarform .avatardiv {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
#avatar .warning {
|
||||
#avatarform .warning {
|
||||
width: 100%;
|
||||
}
|
||||
#uploadavatarbutton,
|
||||
|
@ -28,7 +26,7 @@ input#openid, input#webdav { width:20em; }
|
|||
.jcrop-holder {
|
||||
z-index: 500;
|
||||
}
|
||||
#avatar #cropper {
|
||||
#cropper {
|
||||
float: left;
|
||||
z-index: 500;
|
||||
/* float cropper above settings page to prevent unexpected flowing from dynamically sized element */
|
||||
|
@ -40,7 +38,7 @@ input#openid, input#webdav { width:20em; }
|
|||
width: 100%;
|
||||
height: calc(100% - 45px);
|
||||
}
|
||||
#avatar #cropper .inner-container {
|
||||
#cropper .inner-container {
|
||||
z-index: 2001; /* above the top bar if needed */
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
|
@ -53,17 +51,80 @@ input#openid, input#webdav { width:20em; }
|
|||
padding: 15px;
|
||||
}
|
||||
|
||||
#avatar #cropper .inner-container .jcrop-holder {
|
||||
#cropper .inner-container .jcrop-holder {
|
||||
box-shadow: 0 0 7px #888;
|
||||
}
|
||||
#avatar #cropper .inner-container .button {
|
||||
#cropper .inner-container .button {
|
||||
margin-top: 15px;
|
||||
}
|
||||
#avatar #cropper .inner-container .primary {
|
||||
#cropper .inner-container .primary {
|
||||
float: right;
|
||||
}
|
||||
|
||||
#displaynameform,
|
||||
#personal-settings-avatar-container {
|
||||
float: left;
|
||||
}
|
||||
#personal-settings-container {
|
||||
position: relative;
|
||||
float: left;
|
||||
min-width: 280px;
|
||||
max-width: 700px;
|
||||
width: calc(100% - 200px);
|
||||
}
|
||||
#personal-settings-container:after {
|
||||
clear: both;
|
||||
}
|
||||
#personal-settings-container > div {
|
||||
float: left;
|
||||
height: 100px;
|
||||
min-width: 300px;
|
||||
}
|
||||
#personal-settings-container.no-edit > div {
|
||||
height: 20px;
|
||||
min-width: 200px;
|
||||
}
|
||||
#avatarform > h2,
|
||||
#personal-settings-container > div h2 {
|
||||
position: relative;
|
||||
}
|
||||
#personal-settings-container > div h2 span[class^="icon-"],
|
||||
#personal-settings-avatar-container h2 span[class^="icon-"] {
|
||||
display: inline-block;
|
||||
padding: 8px;
|
||||
margin-left: -8px;
|
||||
margin-bottom: -10px;
|
||||
background-size: 16px;
|
||||
opacity: .3;
|
||||
cursor: pointer;
|
||||
}
|
||||
.personal-settings-setting-box input[type="text"],
|
||||
.personal-settings-setting-box input[type="email"],
|
||||
.personal-settings-setting-box input[type="tel"] {
|
||||
width: 17em;
|
||||
}
|
||||
#personal-settings-container > div > form span[class^="icon-checkmark"] {
|
||||
position: absolute;
|
||||
left: 239px;
|
||||
top: 82px;
|
||||
pointer-events: none;
|
||||
}
|
||||
.federationScopeMenu {
|
||||
top: 44px;
|
||||
}
|
||||
.federationScopeMenu.bubble::after {
|
||||
right: 50%;
|
||||
transform: translate(50%, 0);
|
||||
}
|
||||
.federationScopeMenu.popovermenu a.menuitem,
|
||||
.federationScopeMenu.popovermenu label.menuitem,
|
||||
.federationScopeMenu.popovermenu .menuitem {
|
||||
font-size: 12.8px;
|
||||
line-height: 1.6em;
|
||||
}
|
||||
.federationScopeMenu.popovermenu .menuitem .menuitem-text-detail {
|
||||
opacity: .75;
|
||||
}
|
||||
|
||||
#lostpassword,
|
||||
#groups,
|
||||
#passwordform {
|
||||
|
@ -73,7 +134,7 @@ input#openid, input#webdav { width:20em; }
|
|||
padding-right: 0;
|
||||
min-width: 60%;
|
||||
}
|
||||
#avatar,
|
||||
#avatarform,
|
||||
#passwordform {
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
|
@ -81,6 +142,8 @@ input#openid, input#webdav { width:20em; }
|
|||
#groups {
|
||||
overflow-wrap: break-word;
|
||||
max-width: 75%;
|
||||
display: block;;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.clientsbox img {
|
||||
|
@ -104,10 +167,6 @@ input#openid, input#webdav { width:20em; }
|
|||
input#identity {
|
||||
width: 20em;
|
||||
}
|
||||
#displayName,
|
||||
#email {
|
||||
width: 17em;
|
||||
}
|
||||
|
||||
#showWizard {
|
||||
display: inline-block;
|
||||
|
|
156
settings/js/federationscopemenu.js
Normal file
156
settings/js/federationscopemenu.js
Normal file
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* Copyright (c) 2016
|
||||
*
|
||||
* This file is licensed under the Affero General Public License version 3
|
||||
* or later.
|
||||
*
|
||||
* See the COPYING-README file.
|
||||
*
|
||||
*/
|
||||
|
||||
/* global OC, Handlebars */
|
||||
(function() {
|
||||
|
||||
var TEMPLATE_MENU =
|
||||
'<ul>' +
|
||||
'{{#each items}}' +
|
||||
'<li>' +
|
||||
'<a href="#" class="menuitem action action-{{name}} permanent {{#if active}}active{{/if}}" data-action="{{name}}">' +
|
||||
'{{#if icon}}<img class="icon" src="{{icon}}"/>' +
|
||||
'{{else}}'+
|
||||
'{{#if iconClass}}' +
|
||||
'<span class="icon {{iconClass}}"></span>' +
|
||||
'{{else}}' +
|
||||
'<span class="no-icon"></span>' +
|
||||
'{{/if}}' +
|
||||
'{{/if}}' +
|
||||
'<p><strong class="menuitem-text">{{displayName}}</strong><br>' +
|
||||
'<span class="menuitem-text-detail">{{tooltip}}</span></p></a>' +
|
||||
'</li>' +
|
||||
'{{/each}}' +
|
||||
'</ul>';
|
||||
|
||||
/**
|
||||
* Construct a new FederationScopeMenu instance
|
||||
* @constructs FederationScopeMenu
|
||||
* @memberof OC.Settings
|
||||
*/
|
||||
var FederationScopeMenu = OC.Backbone.View.extend({
|
||||
tagName: 'div',
|
||||
className: 'federationScopeMenu popovermenu bubble hidden open menu',
|
||||
field: undefined,
|
||||
_scopes: undefined,
|
||||
|
||||
initialize: function(options) {
|
||||
this.field = options.field;
|
||||
this._scopes = [
|
||||
{
|
||||
name: 'private',
|
||||
displayName: (this.field === 'avatar' || this.field === 'displayname') ? t('core', 'Local') : t('core', 'Private'),
|
||||
tooltip: (this.field === 'avatar' || this.field === 'displayname') ? t('core', 'Only visible to local users') : t('core', 'Only visible to you'),
|
||||
icon: OC.imagePath('core', 'actions/password'),
|
||||
active: false
|
||||
},
|
||||
{
|
||||
name: 'contacts',
|
||||
displayName: t('core', 'Contacts'),
|
||||
tooltip: t('core', 'Visible to local users and to trusted servers'),
|
||||
icon: OC.imagePath('core', 'places/contacts-dark'),
|
||||
active: false
|
||||
},
|
||||
{
|
||||
name: 'public',
|
||||
displayName: t('core', 'Public'),
|
||||
tooltip: t('core', 'Will be synced to a global and public address book'),
|
||||
icon: OC.imagePath('core', 'places/link'),
|
||||
active: false
|
||||
}
|
||||
];
|
||||
},
|
||||
|
||||
/**
|
||||
* Current context
|
||||
*
|
||||
* @type OCA.Files.FileActionContext
|
||||
*/
|
||||
_context: null,
|
||||
|
||||
events: {
|
||||
'click a.action': '_onClickAction'
|
||||
},
|
||||
|
||||
template: Handlebars.compile(TEMPLATE_MENU),
|
||||
|
||||
/**
|
||||
* Event handler whenever an action has been clicked within the menu
|
||||
*
|
||||
* @param {Object} event event object
|
||||
*/
|
||||
_onClickAction: function(event) {
|
||||
var $target = $(event.currentTarget);
|
||||
if (!$target.hasClass('menuitem')) {
|
||||
$target = $target.closest('.menuitem');
|
||||
}
|
||||
|
||||
this.trigger('select:scope', $target.data('action'));
|
||||
|
||||
OC.hideMenus();
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders the menu with the currently set items
|
||||
*/
|
||||
render: function() {
|
||||
this.$el.html(this.template({
|
||||
items: this._scopes
|
||||
}));
|
||||
},
|
||||
|
||||
/**
|
||||
* Displays the menu
|
||||
*/
|
||||
show: function(context) {
|
||||
this._context = context;
|
||||
var currentlyActiveValue = $('#'+context.target.closest('form').id).find('.icon-checkmark > input')[0].value;
|
||||
|
||||
for(var i = 0 in this._scopes) {
|
||||
this._scopes[i].active = false;
|
||||
}
|
||||
|
||||
switch (currentlyActiveValue) {
|
||||
case "private":
|
||||
this._scopes[0].active = true;
|
||||
break;
|
||||
case "contacts":
|
||||
this._scopes[1].active = true;
|
||||
break;
|
||||
case "public":
|
||||
this._scopes[2].active = true;
|
||||
break;
|
||||
}
|
||||
|
||||
var $el = $(context.target);
|
||||
var offsetIcon = $el.offset();
|
||||
var offsetHeading = $el.closest('h2').offset();
|
||||
|
||||
this.render();
|
||||
this.$el.removeClass('hidden');
|
||||
|
||||
OC.showMenu(null, this.$el);
|
||||
|
||||
//Set the menuwidth
|
||||
var menuWidth = this.$el.width();
|
||||
this.$el.css('width', menuWidth);
|
||||
|
||||
//Calculate menu position
|
||||
var l = offsetIcon.left - offsetHeading.left;
|
||||
l = l - (menuWidth / 2) + ($el.outerWidth()/2);
|
||||
this.$el.css('left', l);
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
OC.Settings = OC.Settings || {};
|
||||
OC.Settings.FederationScopeMenu = FederationScopeMenu;
|
||||
|
||||
})();
|
176
settings/js/federationsettingsview.js
Normal file
176
settings/js/federationsettingsview.js
Normal file
|
@ -0,0 +1,176 @@
|
|||
/* global OC, result, _ */
|
||||
|
||||
/**
|
||||
* Copyright (c) 2016, Christoph Wurst <christoph@owncloud.com>
|
||||
*
|
||||
* This file is licensed under the Affero General Public License version 3 or later.
|
||||
* See the COPYING-README file.
|
||||
*/
|
||||
|
||||
(function(_, $, OC) {
|
||||
'use strict';
|
||||
|
||||
var FederationSettingsView = OC.Backbone.View.extend({
|
||||
_inputFields: undefined,
|
||||
|
||||
/** @var Backbone.Model */
|
||||
_config: undefined,
|
||||
|
||||
initialize: function(options) {
|
||||
options = options || {};
|
||||
|
||||
if (options.config) {
|
||||
this._config = options.config;
|
||||
} else {
|
||||
this._config = new OC.Settings.UserSettings();
|
||||
}
|
||||
|
||||
this._inputFields = [
|
||||
'displayname',
|
||||
'phone',
|
||||
'email',
|
||||
'website',
|
||||
'twitter',
|
||||
'address',
|
||||
'avatar'
|
||||
];
|
||||
|
||||
var self = this;
|
||||
_.each(this._inputFields, function(field) {
|
||||
var scopeOnly = field === 'avatar';
|
||||
|
||||
// Initialize config model
|
||||
if (!scopeOnly) {
|
||||
self._config.set(field, $('#' + field).val());
|
||||
}
|
||||
self._config.set(field + 'Scope', $('#' + field + 'scope').val());
|
||||
|
||||
// Set inputs whenever model values change
|
||||
if (!scopeOnly) {
|
||||
self.listenTo(self._config, 'change:' + field, function() {
|
||||
self.$('#' + field).val(self._config.get(field));
|
||||
});
|
||||
}
|
||||
self.listenTo(self._config, 'change:' + field + 'Scope', function() {
|
||||
self._setFieldScopeIcon(field, self._config.get(field + 'Scope'));
|
||||
});
|
||||
});
|
||||
|
||||
this._registerEvents();
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var self = this;
|
||||
_.each(this._inputFields, function(field) {
|
||||
var $heading = self.$('#' + field + 'form h2');
|
||||
var $icon = self.$('#' + field + 'form h2 > span');
|
||||
var scopeMenu = new OC.Settings.FederationScopeMenu({field: field});
|
||||
|
||||
self.listenTo(scopeMenu, 'select:scope', function(scope) {
|
||||
self._onScopeChanged(field, scope);
|
||||
});
|
||||
$heading.append(scopeMenu.$el);
|
||||
$icon.on('click', _.bind(scopeMenu.show, scopeMenu));
|
||||
|
||||
// Restore initial state
|
||||
self._setFieldScopeIcon(field, self._config.get(field + 'Scope'));
|
||||
});
|
||||
},
|
||||
|
||||
_registerEvents: function() {
|
||||
var self = this;
|
||||
_.each(this._inputFields, function(field) {
|
||||
if (field === 'avatar') {
|
||||
return;
|
||||
}
|
||||
self.$('#' + field).keyUpDelayedOrEnter(_.bind(self._onInputChanged, self));
|
||||
});
|
||||
},
|
||||
|
||||
_onInputChanged: function(e) {
|
||||
var self = this;
|
||||
|
||||
var $dialog = $('.oc-dialog:visible');
|
||||
if (OC.PasswordConfirmation.requiresPasswordConfirmation()) {
|
||||
if($dialog.length === 0) {
|
||||
OC.PasswordConfirmation.requirePasswordConfirmation(_.bind(this._onInputChanged, this, e));
|
||||
}
|
||||
return;
|
||||
}
|
||||
var $target = $(e.target);
|
||||
var value = $target.val();
|
||||
var field = $target.attr('id');
|
||||
this._config.set(field, value);
|
||||
|
||||
var savingData = this._config.save({
|
||||
error: function(jqXHR) {
|
||||
OC.msg.finishedSaving('#personal-settings-container .msg', jqXHR);
|
||||
}
|
||||
});
|
||||
|
||||
$.when(savingData).done(function() {
|
||||
//OC.msg.finishedSaving('#personal-settings-container .msg', result)
|
||||
self._showInputChangeSuccess(field);
|
||||
if (field === 'displayname') {
|
||||
self._updateDisplayName(value);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_updateDisplayName: function(displayName) {
|
||||
// update displayName on the top right expand button
|
||||
$('#expandDisplayName').text(displayName);
|
||||
// update avatar if avatar is available
|
||||
if (!$('#removeavatar').hasClass('hidden')) {
|
||||
updateAvatar();
|
||||
}
|
||||
},
|
||||
|
||||
_onScopeChanged: function(field, scope) {
|
||||
var $dialog = $('.oc-dialog:visible');
|
||||
if (OC.PasswordConfirmation.requiresPasswordConfirmation()) {
|
||||
if($dialog.length === 0) {
|
||||
OC.PasswordConfirmation.requirePasswordConfirmation(_.bind(this._onScopeChanged, this, field, scope));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this._config.set(field + 'Scope', scope);
|
||||
|
||||
$('#' + field).parent().find('span > input').val(scope);
|
||||
|
||||
// TODO: user loading/success feedback
|
||||
this._config.save();
|
||||
this._setFieldScopeIcon(field, scope);
|
||||
},
|
||||
|
||||
_showInputChangeSuccess: function(field) {
|
||||
var $icon = this.$('#' + field + 'form > span');
|
||||
$icon.fadeIn(200);
|
||||
setTimeout(function() {
|
||||
$icon.fadeOut(300);
|
||||
}, 2000);
|
||||
},
|
||||
|
||||
_setFieldScopeIcon: function(field, scope) {
|
||||
var $icon = this.$('#' + field + 'form > h2 > span');
|
||||
$icon.removeClass('icon-password');
|
||||
$icon.removeClass('icon-contacts-dark');
|
||||
$icon.removeClass('icon-link');
|
||||
switch (scope) {
|
||||
case 'private':
|
||||
$icon.addClass('icon-password');
|
||||
break;
|
||||
case 'contacts':
|
||||
$icon.addClass('icon-contacts-dark');
|
||||
break;
|
||||
case 'public':
|
||||
$icon.addClass('icon-link');
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
OC.Settings = OC.Settings || {};
|
||||
OC.Settings.FederationSettingsView = FederationSettingsView;
|
||||
})(_, $, OC);
|
|
@ -1,10 +1,15 @@
|
|||
/* global OC */
|
||||
|
||||
/**
|
||||
* Copyright (c) 2011, Robin Appelman <icewind1991@gmail.com>
|
||||
* 2013, Morris Jobke <morris.jobke@gmail.com>
|
||||
* 2016, Christoph Wurst <christoph@owncloud.com>
|
||||
* This file is licensed under the Affero General Public License version 3 or later.
|
||||
* See the COPYING-README file.
|
||||
*/
|
||||
|
||||
OC.Settings = OC.Settings || {};
|
||||
|
||||
/**
|
||||
* The callback will be fired as soon as enter is pressed by the
|
||||
* user or 1 second after the last data entry
|
||||
|
@ -21,21 +26,21 @@ jQuery.fn.keyUpDelayedOrEnter = function (callback, allowEmptyValue) {
|
|||
return;
|
||||
}
|
||||
if (allowEmptyValue || that.val() !== '') {
|
||||
cb();
|
||||
cb(event);
|
||||
}
|
||||
}, 1000));
|
||||
|
||||
this.keypress(function (event) {
|
||||
if (event.keyCode === 13 && (allowEmptyValue || that.val() !== '')) {
|
||||
event.preventDefault();
|
||||
cb();
|
||||
cb(event);
|
||||
}
|
||||
});
|
||||
|
||||
this.bind('paste', null, function (e) {
|
||||
if(!e.keyCode){
|
||||
this.bind('paste', null, function (event) {
|
||||
if(!event.keyCode){
|
||||
if (allowEmptyValue || that.val() !== '') {
|
||||
cb();
|
||||
cb(event);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -187,7 +192,7 @@ function avatarResponseHandler (data) {
|
|||
if (typeof data === 'string') {
|
||||
data = JSON.parse(data);
|
||||
}
|
||||
var $warning = $('#avatar .warning');
|
||||
var $warning = $('#avatarform .warning');
|
||||
$warning.hide();
|
||||
if (data.status === "success") {
|
||||
updateAvatar();
|
||||
|
@ -265,8 +270,10 @@ $(document).ready(function () {
|
|||
}
|
||||
});
|
||||
|
||||
$('#displayName').keyUpDelayedOrEnter(changeDisplayName);
|
||||
$('#email').keyUpDelayedOrEnter(changeEmailAddress, true);
|
||||
var federationSettingsView = new OC.Settings.FederationSettingsView({
|
||||
el: '#personal-settings'
|
||||
});
|
||||
federationSettingsView.render();
|
||||
|
||||
$("#languageinput").change(function () {
|
||||
// Serialize the data
|
||||
|
@ -405,7 +412,7 @@ $(document).ready(function () {
|
|||
|
||||
// Load the big avatar
|
||||
if (oc_config.enable_avatars) {
|
||||
$('#avatar .avatardiv').avatar(OC.currentUser, 145);
|
||||
$('#avatarform .avatardiv').avatar(OC.currentUser, 145);
|
||||
}
|
||||
|
||||
// Show token views
|
||||
|
@ -452,3 +459,5 @@ OC.Encryption.msg = {
|
|||
}
|
||||
}
|
||||
};
|
||||
|
||||
OC.Settings.updateAvatar = updateAvatar;
|
||||
|
|
50
settings/js/usersettings.js
Normal file
50
settings/js/usersettings.js
Normal file
|
@ -0,0 +1,50 @@
|
|||
/* global OC */
|
||||
|
||||
/**
|
||||
* Copyright (c) 2016, Christoph Wurst <christoph@owncloud.com>
|
||||
*
|
||||
* This file is licensed under the Affero General Public License version 3 or later.
|
||||
* See the COPYING-README file.
|
||||
*/
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Model for storing and saving user settings
|
||||
*
|
||||
* @class UserSettings
|
||||
*/
|
||||
var UserSettings = OC.Backbone.Model.extend({
|
||||
url: OC.generateUrl('/settings/users/{id}/settings', {id: OC.currentUser}),
|
||||
isNew: function() {
|
||||
return false; // Force PUT on .save()
|
||||
},
|
||||
parse: function(data) {
|
||||
if (_.isUndefined(data)) {
|
||||
return null;
|
||||
}
|
||||
if (_.isUndefined(data.data)) {
|
||||
return null;
|
||||
}
|
||||
data = data.data;
|
||||
|
||||
var ignored = [
|
||||
'userId',
|
||||
'message'
|
||||
];
|
||||
|
||||
_.each(ignored, function(ign) {
|
||||
if (!_.isUndefined(data[ign])) {
|
||||
delete data[ign];
|
||||
}
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
});
|
||||
|
||||
OC.Settings = OC.Settings || {};
|
||||
|
||||
OC.Settings.UserSettings = UserSettings;
|
||||
})();
|
|
@ -40,6 +40,7 @@ OC_Util::checkLoggedIn();
|
|||
|
||||
$defaults = \OC::$server->getThemingDefaults();
|
||||
$certificateManager = \OC::$server->getCertificateManager();
|
||||
$accountManager = new \OC\Accounts\AccountManager(\OC::$server->getDatabaseConnection(), \OC::$server->getEventDispatcher());
|
||||
$config = \OC::$server->getConfig();
|
||||
$urlGenerator = \OC::$server->getURLGenerator();
|
||||
|
||||
|
@ -47,7 +48,10 @@ $urlGenerator = \OC::$server->getURLGenerator();
|
|||
OC_Util::addScript('settings', 'authtoken');
|
||||
OC_Util::addScript('settings', 'authtoken_collection');
|
||||
OC_Util::addScript('settings', 'authtoken_view');
|
||||
OC_Util::addScript( 'settings', 'personal' );
|
||||
OC_Util::addScript('settings', 'usersettings');
|
||||
OC_Util::addScript('settings', 'federationsettingsview');
|
||||
OC_Util::addScript('settings', 'federationscopemenu');
|
||||
OC_Util::addScript('settings', 'personal');
|
||||
OC_Util::addScript('settings', 'certificates');
|
||||
OC_Util::addStyle( 'settings', 'settings' );
|
||||
\OC_Util::addVendorScript('strengthify/jquery.strengthify');
|
||||
|
@ -66,7 +70,6 @@ OC::$server->getNavigationManager()->setActiveEntry('personal');
|
|||
$storageInfo=OC_Helper::getStorageInfo('/');
|
||||
|
||||
$user = OC::$server->getUserManager()->get(OC_User::getUser());
|
||||
$email = $user->getEMailAddress();
|
||||
|
||||
$userLang=$config->getUserValue( OC_User::getUser(), 'core', 'lang', \OC::$server->getL10NFactory()->findLanguage() );
|
||||
$languageCodes = \OC::$server->getL10NFactory()->findAvailableLanguages();
|
||||
|
@ -152,16 +155,34 @@ if ($storageInfo['quota'] === \OCP\Files\FileInfo::SPACE_UNLIMITED) {
|
|||
} else {
|
||||
$totalSpace = OC_Helper::humanFileSize($storageInfo['total']);
|
||||
}
|
||||
|
||||
$uid = $user->getUID();
|
||||
$userData = $accountManager->getUser($user);
|
||||
|
||||
$tmpl->assign('total_space', $totalSpace);
|
||||
$tmpl->assign('usage_relative', $storageInfo['relative']);
|
||||
$tmpl->assign('clients', $clients);
|
||||
$tmpl->assign('email', $email);
|
||||
$tmpl->assign('email', $userData[\OC\Accounts\AccountManager::PROPERTY_EMAIL]['value']);
|
||||
$tmpl->assign('languages', $languages);
|
||||
$tmpl->assign('commonlanguages', $commonLanguages);
|
||||
$tmpl->assign('activelanguage', $userLang);
|
||||
$tmpl->assign('passwordChangeSupported', OC_User::canUserChangePassword(OC_User::getUser()));
|
||||
$tmpl->assign('displayNameChangeSupported', OC_User::canUserChangeDisplayName(OC_User::getUser()));
|
||||
$tmpl->assign('displayName', OC_User::getDisplayName());
|
||||
$tmpl->assign('displayName', $userData[\OC\Accounts\AccountManager::PROPERTY_DISPLAYNAME]['value']);
|
||||
|
||||
$tmpl->assign('phone', $userData[\OC\Accounts\AccountManager::PROPERTY_PHONE]['value']);
|
||||
$tmpl->assign('website', $userData[\OC\Accounts\AccountManager::PROPERTY_WEBSITE]['value']);
|
||||
$tmpl->assign('twitter', $userData[\OC\Accounts\AccountManager::PROPERTY_TWITTER]['value']);
|
||||
$tmpl->assign('address', $userData[\OC\Accounts\AccountManager::PROPERTY_ADDRESS]['value']);
|
||||
|
||||
$tmpl->assign('avatarScope', $userData[\OC\Accounts\AccountManager::PROPERTY_AVATAR]['scope']);
|
||||
$tmpl->assign('displayNameScope', $userData[\OC\Accounts\AccountManager::PROPERTY_DISPLAYNAME]['scope']);
|
||||
$tmpl->assign('phoneScope', $userData[\OC\Accounts\AccountManager::PROPERTY_PHONE]['scope']);
|
||||
$tmpl->assign('emailScope', $userData[\OC\Accounts\AccountManager::PROPERTY_EMAIL]['scope']);
|
||||
$tmpl->assign('websiteScope', $userData[\OC\Accounts\AccountManager::PROPERTY_WEBSITE]['scope']);
|
||||
$tmpl->assign('twitterScope', $userData[\OC\Accounts\AccountManager::PROPERTY_TWITTER]['scope']);
|
||||
$tmpl->assign('addressScope', $userData[\OC\Accounts\AccountManager::PROPERTY_ADDRESS]['scope']);
|
||||
|
||||
$tmpl->assign('enableAvatars', $config->getSystemValue('enable_avatars', true) === true);
|
||||
$tmpl->assign('avatarChangeSupported', OC_User::canUserChangeAvatar(OC_User::getUser()));
|
||||
$tmpl->assign('certs', $certificateManager->listCertificates());
|
||||
|
|
|
@ -51,7 +51,7 @@ $application->registerRoutes($this, [
|
|||
['name' => 'AppSettings#listApps', 'url' => '/settings/apps/list', 'verb' => 'GET'],
|
||||
['name' => 'SecuritySettings#trustedDomains', 'url' => '/settings/admin/security/trustedDomains', 'verb' => 'POST'],
|
||||
['name' => 'Users#setDisplayName', 'url' => '/settings/users/{username}/displayName', 'verb' => 'POST'],
|
||||
['name' => 'Users#setMailAddress', 'url' => '/settings/users/{id}/mailAddress', 'verb' => 'PUT'],
|
||||
['name' => 'Users#setUserSettings', 'url' => '/settings/users/{username}/settings', 'verb' => 'PUT'],
|
||||
['name' => 'Users#stats', 'url' => '/settings/users/stats', 'verb' => 'GET'],
|
||||
['name' => 'LogSettings#setLogLevel', 'url' => '/settings/admin/log/level', 'verb' => 'POST'],
|
||||
['name' => 'LogSettings#getEntries', 'url' => '/settings/admin/log/entries', 'verb' => 'GET'],
|
||||
|
|
|
@ -32,45 +32,128 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div id="personal-settings">
|
||||
<?php if ($_['enableAvatars']): ?>
|
||||
<form id="avatar" class="section" method="post" action="<?php p(\OC::$server->getURLGenerator()->linkToRoute('core.avatar.postAvatar')); ?>">
|
||||
<h2><?php p($l->t('Profile picture')); ?></h2>
|
||||
<div id="displayavatar">
|
||||
<div class="avatardiv"></div>
|
||||
<div class="warning hidden"></div>
|
||||
<?php if ($_['avatarChangeSupported']): ?>
|
||||
<label for="uploadavatar" class="inlineblock button icon-upload" id="uploadavatarbutton" title="<?php p($l->t('Upload new')); ?>"></label>
|
||||
<div class="inlineblock button icon-folder" id="selectavatar" title="<?php p($l->t('Select from Files')); ?>"></div>
|
||||
<div class="hidden button icon-delete" id="removeavatar" title="<?php p($l->t('Remove image')); ?>"></div>
|
||||
<input type="file" name="files[]" id="uploadavatar" class="hiddenuploadfield">
|
||||
<p><em><?php p($l->t('png or jpg, max. 20 MB')); ?></em></p>
|
||||
<?php else: ?>
|
||||
<?php p($l->t('Picture provided by original account')); ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div id="cropper" class="hidden">
|
||||
<div class="inner-container">
|
||||
<div class="inlineblock button" id="abortcropperbutton"><?php p($l->t('Cancel')); ?></div>
|
||||
<div class="inlineblock button primary" id="sendcropperbutton"><?php p($l->t('Choose as profile picture')); ?></div>
|
||||
<div id="personal-settings-avatar-container">
|
||||
<form id="avatarform" class="section" method="post" action="<?php p(\OC::$server->getURLGenerator()->linkToRoute('core.avatar.postAvatar')); ?>">
|
||||
<h2>
|
||||
<label><?php p($l->t('Profile picture')); ?></label>
|
||||
<span class="icon-password"/>
|
||||
</h2>
|
||||
<div id="displayavatar">
|
||||
<div class="avatardiv"></div>
|
||||
<div class="warning hidden"></div>
|
||||
<?php if ($_['avatarChangeSupported']): ?>
|
||||
<label for="uploadavatar" class="inlineblock button icon-upload svg" id="uploadavatarbutton" title="<?php p($l->t('Upload new')); ?>"></label>
|
||||
<div class="inlineblock button icon-folder svg" id="selectavatar" title="<?php p($l->t('Select from Files')); ?>"></div>
|
||||
<div class="hidden button icon-delete svg" id="removeavatar" title="<?php p($l->t('Remove image')); ?>"></div>
|
||||
<input type="file" name="files[]" id="uploadavatar" class="hiddenuploadfield">
|
||||
<p><em><?php p($l->t('png or jpg, max. 20 MB')); ?></em></p>
|
||||
<?php else: ?>
|
||||
<?php p($l->t('Picture provided by original account')); ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div id="cropper" class="hidden">
|
||||
<div class="inner-container">
|
||||
<div class="inlineblock button" id="abortcropperbutton"><?php p($l->t('Cancel')); ?></div>
|
||||
<div class="inlineblock button primary" id="sendcropperbutton"><?php p($l->t('Choose as profile picture')); ?></div>
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" id="avatarscope" value="<?php p($_['avatarScope']) ?>">
|
||||
</form>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php
|
||||
if($_['displayNameChangeSupported']) {
|
||||
?>
|
||||
<form id="displaynameform" class="section">
|
||||
<h2>
|
||||
<label for="displayName"><?php echo $l->t('Full name');?></label>
|
||||
</h2>
|
||||
<input type="text" id="displayName" name="displayName" class="password-confirm-required"
|
||||
value="<?php p($_['displayName'])?>"
|
||||
autocomplete="on" autocapitalize="off" autocorrect="off" />
|
||||
<span class="msg"></span>
|
||||
<input type="hidden" id="oldDisplayName" name="oldDisplayName" value="<?php p($_['displayName'])?>" />
|
||||
</form>
|
||||
<div id="personal-settings-container">
|
||||
<div class="personal-settings-setting-box">
|
||||
<form id="displaynameform" class="section">
|
||||
<h2>
|
||||
<label for="displayname"><?php p($l->t('Full name')); ?></label>
|
||||
<span class="icon-password"/>
|
||||
</h2>
|
||||
<input type="text" id="displayname" name="displayname"
|
||||
value="<?php p($_['displayName']) ?>"
|
||||
autocomplete="on" autocapitalize="off" autocorrect="off" />
|
||||
<span class="icon-checkmark hidden"/>
|
||||
<input type="hidden" id="displaynamescope" value="<?php p($_['displayNameScope']) ?>">
|
||||
</form>
|
||||
</div>
|
||||
<div class="personal-settings-setting-box">
|
||||
<form id="emailform" class="section">
|
||||
<h2>
|
||||
<label for="email"><?php p($l->t('Email')); ?></label>
|
||||
<span class="icon-password"/>
|
||||
</h2>
|
||||
<input type="email" name="email" id="email" value="<?php p($_['email']); ?>"
|
||||
placeholder="<?php p($l->t('Your email address')); ?>"
|
||||
autocomplete="on" autocapitalize="off" autocorrect="off" />
|
||||
<br />
|
||||
<em><?php p($l->t('For password recovery and notifications')); ?></em>
|
||||
<span class="icon-checkmark hidden"/>
|
||||
<input type="hidden" id="emailscope" value="<?php p($_['emailScope']) ?>">
|
||||
</form>
|
||||
</div>
|
||||
<div class="personal-settings-setting-box">
|
||||
<form id="phoneform" class="section">
|
||||
<h2>
|
||||
<label for="phone"><?php p($l->t('Phone number')); ?></label>
|
||||
<span class="icon-password"/>
|
||||
</h2>
|
||||
<input type="tel" id="phone" name="phone"
|
||||
value="<?php p($_['phone']) ?>"
|
||||
placeholder="<?php p($l->t('Your phone number')); ?>"
|
||||
autocomplete="on" autocapitalize="off" autocorrect="off" />
|
||||
<span class="icon-checkmark hidden"/>
|
||||
<input type="hidden" id="phonescope" value="<?php p($_['phoneScope']) ?>">
|
||||
</form>
|
||||
</div>
|
||||
<div class="personal-settings-setting-box">
|
||||
<form id="addressform" class="section">
|
||||
<h2>
|
||||
<label for="address"><?php p($l->t('Address')); ?></label>
|
||||
<span class="icon-password"/>
|
||||
</h2>
|
||||
<input type="text" id="address" name="address"
|
||||
placeholder="<?php p($l->t('Your postal address')); ?>"
|
||||
value="<?php p($_['address']) ?>"
|
||||
autocomplete="on" autocapitalize="off" autocorrect="off" />
|
||||
<span class="icon-checkmark hidden"/>
|
||||
<input type="hidden" id="addressscope" value="<?php p($_['addressScope']) ?>">
|
||||
</form>
|
||||
</div>
|
||||
<div class="personal-settings-setting-box">
|
||||
<form id="websiteform" class="section">
|
||||
<h2>
|
||||
<label for="website"><?php p($l->t('Website')); ?></label>
|
||||
<span class="icon-password"/>
|
||||
</h2>
|
||||
<input type="text" name="website" id="website" value="<?php p($_['website']); ?>"
|
||||
placeholder="<?php p($l->t('Your website')); ?>"
|
||||
autocomplete="on" autocapitalize="off" autocorrect="off" />
|
||||
<span class="icon-checkmark hidden"/>
|
||||
<input type="hidden" id="websitescope" value="<?php p($_['websiteScope']) ?>">
|
||||
</form>
|
||||
</div>
|
||||
<div class="personal-settings-setting-box">
|
||||
<form id="twitterform" class="section">
|
||||
<h2>
|
||||
<label for="twitter"><?php p($l->t('Twitter')); ?></label>
|
||||
<span class="icon-password"/>
|
||||
</h2>
|
||||
<input type="text" name="twitter" id="twitter" value="<?php p($_['twitter']); ?>"
|
||||
placeholder="<?php p($l->t('Your Twitter handle')); ?>"
|
||||
autocomplete="on" autocapitalize="off" autocorrect="off" />
|
||||
<span class="icon-checkmark hidden"/>
|
||||
<input type="hidden" id="twitterscope" value="<?php p($_['twitterScope']) ?>">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<span class="msg"></span>
|
||||
</div>
|
||||
<?php
|
||||
} else {
|
||||
?>
|
||||
|
@ -102,10 +185,37 @@ if($_['displayNameChangeSupported']) {
|
|||
<div id="lostpassword" class="section">
|
||||
<h2><?php echo $l->t('Email'); ?></h2>
|
||||
<span><?php if(isset($_['email'][0])) { p($_['email']); } else { p($l->t('No email address set')); }?></span>
|
||||
<div id="personal-settings-container" class="no-edit">
|
||||
<div id="displaynameform" class="section">
|
||||
<h2><?php p($l->t('Full name'));?></h2>
|
||||
<span><?php if(isset($_['displayName'][0])) { p($_['displayName']); } else { p($l->t('No display name set')); } ?></span>
|
||||
</div>
|
||||
<div id="emailform" class="section">
|
||||
<h2><?php p($l->t('Email')); ?></h2>
|
||||
<span><?php if(isset($_['email'][0])) { p($_['email']); } else { p($l->t('No email address set')); }?></span>
|
||||
</div>
|
||||
<div id="phoneform" class="section">
|
||||
<h2><?php p($l->t('Phone')); ?></h2>
|
||||
<span><?php if(isset($_['phone'][0])) { p($_['phone']); } else { p($l->t('No phone number set')); }?></span>
|
||||
</div>
|
||||
<div id="addressform" class="section">
|
||||
<h2><?php p($l->t('Address')); ?></h2>
|
||||
<span><?php if(isset($_['address'][0])) { p($_['address']); } else { p($l->t('No address set')); }?></span>
|
||||
</div>
|
||||
<div id="websiteform" class="section">
|
||||
<h2><?php p($l->t('Website')); ?></h2>
|
||||
<span><?php if(isset($_['website'][0])) { p($_['website']); } else { p($l->t('No website set')); }?></span>
|
||||
</div>
|
||||
<div id="twitterform" class="section">
|
||||
<h2><?php p($l->t('Twitter')); ?></h2>
|
||||
<span><?php if(isset($_['twitter'][0])) { p($_['twitter']); } else { p($l->t('No twitter handle set')); }?></span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
|
||||
<div id="groups" class="section">
|
||||
<h2><?php p($l->t('Groups')); ?></h2>
|
||||
|
@ -123,17 +233,17 @@ if($_['passwordChangeSupported']) {
|
|||
<h2 class="inlineblock"><?php p($l->t('Password'));?></h2>
|
||||
<div id="password-error-msg" class="msg success inlineblock" style="display: none;">Saved</div>
|
||||
<br>
|
||||
<label for="pass1" class="hidden-visually"><?php echo $l->t('Current password');?>: </label>
|
||||
<label for="pass1" class="hidden-visually"><?php p($l->t('Current password')); ?>: </label>
|
||||
<input type="password" id="pass1" name="oldpassword"
|
||||
placeholder="<?php echo $l->t('Current password');?>"
|
||||
placeholder="<?php p($l->t('Current password'));?>"
|
||||
autocomplete="off" autocapitalize="off" autocorrect="off" />
|
||||
<label for="pass2" class="hidden-visually"><?php echo $l->t('New password');?>: </label>
|
||||
<label for="pass2" class="hidden-visually"><?php p($l->t('New password'));?>: </label>
|
||||
<input type="password" id="pass2" name="newpassword"
|
||||
placeholder="<?php echo $l->t('New password');?>"
|
||||
placeholder="<?php p($l->t('New password')); ?>"
|
||||
data-typetoggle="#personal-show"
|
||||
autocomplete="off" autocapitalize="off" autocorrect="off" />
|
||||
<input type="checkbox" id="personal-show" name="show" /><label for="personal-show" class="personal-show-label"></label>
|
||||
<input id="passwordbutton" type="submit" value="<?php echo $l->t('Change password');?>" />
|
||||
<input id="passwordbutton" type="submit" value="<?php p($l->t('Change password')); ?>" />
|
||||
<br/>
|
||||
</form>
|
||||
<?php
|
||||
|
|
|
@ -24,6 +24,8 @@ namespace OC\Core\Controller;
|
|||
|
||||
use OC\CapabilitiesManager;
|
||||
use OC\Security\Bruteforce\Throttler;
|
||||
use OC\Security\IdentityProof\Key;
|
||||
use OC\Security\IdentityProof\Manager;
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
use OCP\IRequest;
|
||||
use OCP\IUser;
|
||||
|
@ -32,22 +34,18 @@ use OCP\IUserSession;
|
|||
use Test\TestCase;
|
||||
|
||||
class OCSControllerTest extends TestCase {
|
||||
|
||||
/** @var IRequest|\PHPUnit_Framework_MockObject_MockObject */
|
||||
private $request;
|
||||
|
||||
/** @var CapabilitiesManager|\PHPUnit_Framework_MockObject_MockObject */
|
||||
private $capabilitiesManager;
|
||||
|
||||
/** @var IUserSession|\PHPUnit_Framework_MockObject_MockObject */
|
||||
private $userSession;
|
||||
|
||||
/** @var IUserManager|\PHPUnit_Framework_MockObject_MockObject */
|
||||
private $userManager;
|
||||
|
||||
/** @var Throttler|\PHPUnit_Framework_MockObject_MockObject */
|
||||
private $throttler;
|
||||
|
||||
/** @var Manager|\PHPUnit_Framework_MockObject_MockObject */
|
||||
private $keyManager;
|
||||
/** @var OCSController */
|
||||
private $controller;
|
||||
|
||||
|
@ -59,6 +57,7 @@ class OCSControllerTest extends TestCase {
|
|||
$this->userSession = $this->createMock(IUserSession::class);
|
||||
$this->userManager = $this->createMock(IUserManager::class);
|
||||
$this->throttler = $this->createMock(Throttler::class);
|
||||
$this->keyManager = $this->createMock(Manager::class);
|
||||
|
||||
$this->controller = new OCSController(
|
||||
'core',
|
||||
|
@ -66,7 +65,8 @@ class OCSControllerTest extends TestCase {
|
|||
$this->capabilitiesManager,
|
||||
$this->userSession,
|
||||
$this->userManager,
|
||||
$this->throttler
|
||||
$this->throttler,
|
||||
$this->keyManager
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -206,4 +206,39 @@ class OCSControllerTest extends TestCase {
|
|||
|
||||
$this->assertEquals($expected, $this->controller->personCheck('', ''));
|
||||
}
|
||||
|
||||
public function testGetIdentityProofWithNotExistingUser() {
|
||||
$this->userManager
|
||||
->expects($this->once())
|
||||
->method('get')
|
||||
->with('NotExistingUser')
|
||||
->willReturn(null);
|
||||
|
||||
$expected = new DataResponse('User not found', 404);
|
||||
$this->assertEquals($expected, $this->controller->getIdentityProof('NotExistingUser'));
|
||||
}
|
||||
|
||||
public function testGetIdentityProof() {
|
||||
$user = $this->createMock(IUser::class);
|
||||
$key = $this->createMock(Key::class);
|
||||
$this->userManager
|
||||
->expects($this->once())
|
||||
->method('get')
|
||||
->with('ExistingUser')
|
||||
->willReturn($user);
|
||||
$this->keyManager
|
||||
->expects($this->once())
|
||||
->method('getKey')
|
||||
->with($user)
|
||||
->willReturn($key);
|
||||
$key
|
||||
->expects($this->once())
|
||||
->method('getPublic')
|
||||
->willReturn('Existing Users public key');
|
||||
|
||||
$expected = new DataResponse([
|
||||
'public' => 'Existing Users public key',
|
||||
]);
|
||||
$this->assertEquals($expected, $this->controller->getIdentityProof('ExistingUser'));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
namespace Tests\Settings\Controller;
|
||||
|
||||
use OC\Accounts\AccountManager;
|
||||
use OC\Group\Manager;
|
||||
use OC\Settings\Controller\UsersController;
|
||||
use OCP\App\IAppManager;
|
||||
|
@ -57,6 +58,8 @@ class UsersControllerTest extends \Test\TestCase {
|
|||
private $avatarManager;
|
||||
/** @var IL10N|\PHPUnit_Framework_MockObject_MockObject */
|
||||
private $l;
|
||||
/** @var AccountManager | \PHPUnit_Framework_MockObject_MockObject */
|
||||
private $accountManager;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
@ -71,6 +74,7 @@ class UsersControllerTest extends \Test\TestCase {
|
|||
$this->urlGenerator = $this->createMock(IURLGenerator::class);
|
||||
$this->appManager = $this->createMock(IAppManager::class);
|
||||
$this->avatarManager = $this->createMock(IAvatarManager::class);
|
||||
$this->accountManager = $this->createMock(AccountManager::class);
|
||||
$this->l = $this->createMock(IL10N::class);
|
||||
$this->l->method('t')
|
||||
->will($this->returnCallback(function ($text, $parameters = []) {
|
||||
|
@ -117,7 +121,8 @@ class UsersControllerTest extends \Test\TestCase {
|
|||
'no-reply@owncloud.com',
|
||||
$this->urlGenerator,
|
||||
$this->appManager,
|
||||
$this->avatarManager
|
||||
$this->avatarManager,
|
||||
$this->accountManager
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1760,74 +1765,6 @@ class UsersControllerTest extends \Test\TestCase {
|
|||
$this->assertEquals($expectedResult, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function setEmailAddressData() {
|
||||
return [
|
||||
/* mailAddress, isValid, expectsUpdate, canChangeDisplayName, responseCode */
|
||||
[ '', true, true, true, Http::STATUS_OK ],
|
||||
[ 'foo@local', true, true, true, Http::STATUS_OK],
|
||||
[ 'foo@bar@local', false, false, true, Http::STATUS_UNPROCESSABLE_ENTITY],
|
||||
[ 'foo@local', true, false, false, Http::STATUS_FORBIDDEN],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider setEmailAddressData
|
||||
*
|
||||
* @param string $mailAddress
|
||||
* @param bool $isValid
|
||||
* @param bool $expectsUpdate
|
||||
* @param bool $expectsDelete
|
||||
*/
|
||||
public function testSetEmailAddress($mailAddress, $isValid, $expectsUpdate, $canChangeDisplayName, $responseCode) {
|
||||
$controller = $this->getController(true);
|
||||
|
||||
$user = $this->getMockBuilder('\OC\User\User')
|
||||
->disableOriginalConstructor()->getMock();
|
||||
$user
|
||||
->expects($this->any())
|
||||
->method('getUID')
|
||||
->will($this->returnValue('foo'));
|
||||
$user
|
||||
->expects($this->any())
|
||||
->method('canChangeDisplayName')
|
||||
->will($this->returnValue($canChangeDisplayName));
|
||||
$user
|
||||
->expects($expectsUpdate ? $this->once() : $this->never())
|
||||
->method('setEMailAddress')
|
||||
->with(
|
||||
$this->equalTo($mailAddress)
|
||||
);
|
||||
|
||||
$this->userSession
|
||||
->expects($this->atLeastOnce())
|
||||
->method('getUser')
|
||||
->will($this->returnValue($user));
|
||||
$this->mailer
|
||||
->expects($this->any())
|
||||
->method('validateMailAddress')
|
||||
->with($mailAddress)
|
||||
->willReturn($isValid);
|
||||
|
||||
if ($isValid) {
|
||||
$user->expects($this->atLeastOnce())
|
||||
->method('canChangeDisplayName')
|
||||
->willReturn(true);
|
||||
|
||||
$this->userManager
|
||||
->expects($this->atLeastOnce())
|
||||
->method('get')
|
||||
->with('foo')
|
||||
->will($this->returnValue($user));
|
||||
}
|
||||
|
||||
$response = $controller->setMailAddress($user->getUID(), $mailAddress);
|
||||
|
||||
$this->assertSame($responseCode, $response->getStatus());
|
||||
}
|
||||
|
||||
public function testStatsAdmin() {
|
||||
$controller = $this->getController(true);
|
||||
|
||||
|
@ -1976,6 +1913,7 @@ class UsersControllerTest extends \Test\TestCase {
|
|||
->method('get')
|
||||
->with($editUser->getUID())
|
||||
->willReturn($editUser);
|
||||
$this->accountManager->expects($this->any())->method('getUser')->willReturn([]);
|
||||
|
||||
$subadmin = $this->getMockBuilder('\OC\SubAdmin')
|
||||
->disableOriginalConstructor()
|
||||
|
@ -1994,10 +1932,6 @@ class UsersControllerTest extends \Test\TestCase {
|
|||
->willReturn($isAdmin);
|
||||
|
||||
if ($valid === true) {
|
||||
$editUser->expects($this->once())
|
||||
->method('setDisplayName')
|
||||
->with('newDisplayName')
|
||||
->willReturn(true);
|
||||
$expectedResponse = new DataResponse(
|
||||
[
|
||||
'status' => 'success',
|
||||
|
@ -2009,7 +1943,6 @@ class UsersControllerTest extends \Test\TestCase {
|
|||
]
|
||||
);
|
||||
} else {
|
||||
$editUser->expects($this->never())->method('setDisplayName');
|
||||
$expectedResponse = new DataResponse(
|
||||
[
|
||||
'status' => 'error',
|
||||
|
@ -2040,6 +1973,7 @@ class UsersControllerTest extends \Test\TestCase {
|
|||
->expects($this->once())
|
||||
->method('getUser')
|
||||
->willReturn($user);
|
||||
|
||||
$this->userManager
|
||||
->expects($this->once())
|
||||
->method('get')
|
||||
|
|
202
tests/lib/Accounts/AccountsManagerTest.php
Normal file
202
tests/lib/Accounts/AccountsManagerTest.php
Normal file
|
@ -0,0 +1,202 @@
|
|||
<?php
|
||||
/**
|
||||
* @author Björn Schießle <schiessle@owncloud.com>
|
||||
*
|
||||
* @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 Test\Accounts;
|
||||
|
||||
|
||||
use OC\Accounts\AccountManager;
|
||||
use OC\Mail\Mailer;
|
||||
use OCP\IUser;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Test\TestCase;
|
||||
|
||||
/**
|
||||
* Class AccountsManagerTest
|
||||
*
|
||||
* @group DB
|
||||
* @package Test\Accounts
|
||||
*/
|
||||
class AccountsManagerTest extends TestCase {
|
||||
|
||||
/** @var \OCP\IDBConnection */
|
||||
private $connection;
|
||||
|
||||
/** @var EventDispatcherInterface | \PHPUnit_Framework_MockObject_MockObject */
|
||||
private $eventDispatcher;
|
||||
|
||||
/** @var string accounts table name */
|
||||
private $table = 'accounts';
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
$this->eventDispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')
|
||||
->disableOriginalConstructor()->getMock();
|
||||
$this->connection = \OC::$server->getDatabaseConnection();
|
||||
}
|
||||
|
||||
public function tearDown() {
|
||||
parent::tearDown();
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
$query->delete($this->table)->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* get a instance of the accountManager
|
||||
*
|
||||
* @param array $mockedMethods list of methods which should be mocked
|
||||
* @return \PHPUnit_Framework_MockObject_MockObject | AccountManager
|
||||
*/
|
||||
public function getInstance($mockedMethods = null) {
|
||||
return $this->getMockBuilder('OC\Accounts\AccountManager')
|
||||
->setConstructorArgs([$this->connection, $this->eventDispatcher])
|
||||
->setMethods($mockedMethods)
|
||||
->getMock();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataTrueFalse
|
||||
*
|
||||
* @param bool $userAlreadyExists
|
||||
*/
|
||||
public function testUpdateUser($userAlreadyExists) {
|
||||
$accountManager = $this->getInstance(['getUser', 'insertNewUser', 'updateExistingUser']);
|
||||
$user = $this->getMockBuilder('OCP\IUser')->getMock();
|
||||
|
||||
$accountManager->expects($this->once())->method('getUser')->with($user)->willReturn($userAlreadyExists);
|
||||
|
||||
if ($userAlreadyExists) {
|
||||
$accountManager->expects($this->once())->method('updateExistingUser')
|
||||
->with($user, 'data');
|
||||
$accountManager->expects($this->never())->method('insertNewUser');
|
||||
} else {
|
||||
$accountManager->expects($this->once())->method('insertNewUser')
|
||||
->with($user, 'data');
|
||||
$accountManager->expects($this->never())->method('updateExistingUser');
|
||||
}
|
||||
|
||||
$this->eventDispatcher->expects($this->once())->method('dispatch')
|
||||
->willReturnCallback(function($eventName, $event) use ($user) {
|
||||
$this->assertSame('OC\AccountManager::userUpdated', $eventName);
|
||||
$this->assertInstanceOf('Symfony\Component\EventDispatcher\GenericEvent', $event);
|
||||
}
|
||||
);
|
||||
|
||||
$accountManager->updateUser($user, 'data');
|
||||
}
|
||||
|
||||
public function dataTrueFalse() {
|
||||
return [
|
||||
[true],
|
||||
[false]
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @dataProvider dataTestGetUser
|
||||
*
|
||||
* @param string $setUser
|
||||
* @param array $setData
|
||||
* @param IUser $askUser
|
||||
* @param array $expectedData
|
||||
* @param book $userAlreadyExists
|
||||
*/
|
||||
public function testGetUser($setUser, $setData, $askUser, $expectedData, $userAlreadyExists) {
|
||||
$accountManager = $this->getInstance(['buildDefaultUserRecord', 'insertNewUser']);
|
||||
if (!$userAlreadyExists) {
|
||||
$accountManager->expects($this->once())->method('buildDefaultUserRecord')
|
||||
->with($askUser)->willReturn($expectedData);
|
||||
$accountManager->expects($this->once())->method('insertNewUser')
|
||||
->with($askUser, $expectedData);
|
||||
}
|
||||
$this->addDummyValuesToTable($setUser, $setData);
|
||||
$this->assertEquals($expectedData,
|
||||
$accountManager->getUser($askUser)
|
||||
);
|
||||
}
|
||||
|
||||
public function dataTestGetUser() {
|
||||
$user1 = $this->getMockBuilder('OCP\IUser')->getMock();
|
||||
$user1->expects($this->any())->method('getUID')->willReturn('user1');
|
||||
$user2 = $this->getMockBuilder('OCP\IUser')->getMock();
|
||||
$user2->expects($this->any())->method('getUID')->willReturn('user2');
|
||||
return [
|
||||
['user1', ['key' => 'value'], $user1, ['key' => 'value'], true],
|
||||
['user1', ['key' => 'value'], $user2, [], false],
|
||||
];
|
||||
}
|
||||
|
||||
public function testUpdateExistingUser() {
|
||||
$user = $this->getMockBuilder('OCP\IUser')->getMock();
|
||||
$user->expects($this->once())->method('getUID')->willReturn('uid');
|
||||
$oldData = ['key' => 'value'];
|
||||
$newData = ['newKey' => 'newValue'];
|
||||
|
||||
$accountManager = $this->getInstance();
|
||||
$this->addDummyValuesToTable('uid', $oldData);
|
||||
$this->invokePrivate($accountManager, 'updateExistingUser', [$user, $newData]);
|
||||
$newDataFromTable = $this->getDataFromTable('uid');
|
||||
$this->assertEquals($newData, $newDataFromTable);
|
||||
}
|
||||
|
||||
public function testInsertNewUser() {
|
||||
$user = $this->getMockBuilder('OCP\IUser')->getMock();
|
||||
$uid = 'uid';
|
||||
$data = ['key' => 'value'];
|
||||
|
||||
$accountManager = $this->getInstance();
|
||||
$user->expects($this->once())->method('getUID')->willReturn($uid);
|
||||
$this->assertNull($this->getDataFromTable($uid));
|
||||
$this->invokePrivate($accountManager, 'insertNewUser', [$user, $data]);
|
||||
|
||||
$dataFromDb = $this->getDataFromTable($uid);
|
||||
$this->assertEquals($data, $dataFromDb);
|
||||
}
|
||||
|
||||
private function addDummyValuesToTable($uid, $data) {
|
||||
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
$query->insert($this->table)
|
||||
->values(
|
||||
[
|
||||
'uid' => $query->createNamedParameter($uid),
|
||||
'data' => $query->createNamedParameter(json_encode($data)),
|
||||
]
|
||||
)
|
||||
->execute();
|
||||
}
|
||||
|
||||
private function getDataFromTable($uid) {
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
$query->select('data')->from($this->table)
|
||||
->where($query->expr()->eq('uid', $query->createParameter('uid')))
|
||||
->setParameter('uid', $uid);
|
||||
$query->execute();
|
||||
$result = $query->execute()->fetchAll();
|
||||
|
||||
if (!empty($result)) {
|
||||
return json_decode($result[0]['data'], true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -320,6 +320,7 @@ class ManagerTest extends TestCase {
|
|||
'dav',
|
||||
'federatedfilesharing',
|
||||
'files',
|
||||
'lookup_server_connector',
|
||||
'provisioning_api',
|
||||
'test1',
|
||||
'test3',
|
||||
|
@ -344,6 +345,7 @@ class ManagerTest extends TestCase {
|
|||
'dav',
|
||||
'federatedfilesharing',
|
||||
'files',
|
||||
'lookup_server_connector',
|
||||
'provisioning_api',
|
||||
'test1',
|
||||
'test3',
|
||||
|
@ -364,6 +366,7 @@ class ManagerTest extends TestCase {
|
|||
'files' => ['id' => 'files'],
|
||||
'federatedfilesharing' => ['id' => 'federatedfilesharing'],
|
||||
'provisioning_api' => ['id' => 'provisioning_api'],
|
||||
'lookup_server_connector' => ['id' => 'lookup_server_connector'],
|
||||
'test1' => ['id' => 'test1', 'version' => '1.0.1', 'requiremax' => '9.0.0'],
|
||||
'test2' => ['id' => 'test2', 'version' => '1.0.0', 'requiremin' => '8.2.0'],
|
||||
'test3' => ['id' => 'test3', 'version' => '1.2.4', 'requiremin' => '9.0.0'],
|
||||
|
@ -408,6 +411,7 @@ class ManagerTest extends TestCase {
|
|||
'files' => ['id' => 'files'],
|
||||
'federatedfilesharing' => ['id' => 'federatedfilesharing'],
|
||||
'provisioning_api' => ['id' => 'provisioning_api'],
|
||||
'lookup_server_connector' => ['id' => 'lookup_server_connector'],
|
||||
'test1' => ['id' => 'test1', 'version' => '1.0.1', 'requiremax' => '8.0.0'],
|
||||
'test2' => ['id' => 'test2', 'version' => '1.0.0', 'requiremin' => '8.2.0'],
|
||||
'test3' => ['id' => 'test3', 'version' => '1.2.4', 'requiremin' => '9.0.0'],
|
||||
|
|
|
@ -351,6 +351,7 @@ class AppTest extends \Test\TestCase {
|
|||
'appforgroup12',
|
||||
'dav',
|
||||
'federatedfilesharing',
|
||||
'lookup_server_connector',
|
||||
'provisioning_api',
|
||||
'twofactor_backupcodes',
|
||||
'workflowengine',
|
||||
|
@ -368,6 +369,7 @@ class AppTest extends \Test\TestCase {
|
|||
'appforgroup2',
|
||||
'dav',
|
||||
'federatedfilesharing',
|
||||
'lookup_server_connector',
|
||||
'provisioning_api',
|
||||
'twofactor_backupcodes',
|
||||
'workflowengine',
|
||||
|
@ -386,6 +388,7 @@ class AppTest extends \Test\TestCase {
|
|||
'appforgroup2',
|
||||
'dav',
|
||||
'federatedfilesharing',
|
||||
'lookup_server_connector',
|
||||
'provisioning_api',
|
||||
'twofactor_backupcodes',
|
||||
'workflowengine',
|
||||
|
@ -404,6 +407,7 @@ class AppTest extends \Test\TestCase {
|
|||
'appforgroup2',
|
||||
'dav',
|
||||
'federatedfilesharing',
|
||||
'lookup_server_connector',
|
||||
'provisioning_api',
|
||||
'twofactor_backupcodes',
|
||||
'workflowengine',
|
||||
|
@ -422,6 +426,7 @@ class AppTest extends \Test\TestCase {
|
|||
'appforgroup2',
|
||||
'dav',
|
||||
'federatedfilesharing',
|
||||
'lookup_server_connector',
|
||||
'provisioning_api',
|
||||
'twofactor_backupcodes',
|
||||
'workflowengine',
|
||||
|
@ -502,11 +507,11 @@ class AppTest extends \Test\TestCase {
|
|||
);
|
||||
|
||||
$apps = \OC_App::getEnabledApps();
|
||||
$this->assertEquals(array('files', 'app3', 'dav', 'federatedfilesharing', 'provisioning_api', 'twofactor_backupcodes', 'workflowengine'), $apps);
|
||||
$this->assertEquals(array('files', 'app3', 'dav', 'federatedfilesharing', 'lookup_server_connector', 'provisioning_api', 'twofactor_backupcodes', 'workflowengine'), $apps);
|
||||
|
||||
// mock should not be called again here
|
||||
$apps = \OC_App::getEnabledApps();
|
||||
$this->assertEquals(array('files', 'app3', 'dav', 'federatedfilesharing', 'provisioning_api', 'twofactor_backupcodes', 'workflowengine'), $apps);
|
||||
$this->assertEquals(array('files', 'app3', 'dav', 'federatedfilesharing', 'lookup_server_connector', 'provisioning_api', 'twofactor_backupcodes', 'workflowengine'), $apps);
|
||||
|
||||
$this->restoreAppConfig();
|
||||
\OC_User::setUserId(null);
|
||||
|
|
166
tests/lib/Security/IdentityProof/ManagerTest.php
Normal file
166
tests/lib/Security/IdentityProof/ManagerTest.php
Normal file
|
@ -0,0 +1,166 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* 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
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Test\Security;
|
||||
|
||||
use OC\Security\IdentityProof\Key;
|
||||
use OC\Security\IdentityProof\Manager;
|
||||
use OCP\Files\IAppData;
|
||||
use OCP\Files\SimpleFS\ISimpleFile;
|
||||
use OCP\Files\SimpleFS\ISimpleFolder;
|
||||
use OCP\IUser;
|
||||
use OCP\Security\ICrypto;
|
||||
use Test\TestCase;
|
||||
|
||||
class ManagerTest extends TestCase {
|
||||
/** @var IAppData|\PHPUnit_Framework_MockObject_MockObject */
|
||||
private $appData;
|
||||
/** @var ICrypto|\PHPUnit_Framework_MockObject_MockObject */
|
||||
private $crypto;
|
||||
/** @var Manager|\PHPUnit_Framework_MockObject_MockObject */
|
||||
private $manager;
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
$this->appData = $this->createMock(IAppData::class);
|
||||
$this->crypto = $this->createMock(ICrypto::class);
|
||||
$this->manager = $this->getMockBuilder(Manager::class)
|
||||
->setConstructorArgs([
|
||||
$this->appData,
|
||||
$this->crypto
|
||||
])
|
||||
->setMethods(['generateKeyPair'])
|
||||
->getMock();
|
||||
}
|
||||
|
||||
public function testGetKeyWithExistingKey() {
|
||||
$user = $this->createMock(IUser::class);
|
||||
$user
|
||||
->expects($this->once())
|
||||
->method('getUID')
|
||||
->willReturn('MyUid');
|
||||
$folder = $this->createMock(ISimpleFolder::class);
|
||||
$privateFile = $this->createMock(ISimpleFile::class);
|
||||
$privateFile
|
||||
->expects($this->once())
|
||||
->method('getContent')
|
||||
->willReturn('EncryptedPrivateKey');
|
||||
$publicFile = $this->createMock(ISimpleFile::class);
|
||||
$publicFile
|
||||
->expects($this->once())
|
||||
->method('getContent')
|
||||
->willReturn('MyPublicKey');
|
||||
$this->crypto
|
||||
->expects($this->once())
|
||||
->method('decrypt')
|
||||
->with('EncryptedPrivateKey')
|
||||
->willReturn('MyPrivateKey');
|
||||
$folder
|
||||
->expects($this->at(0))
|
||||
->method('getFile')
|
||||
->with('private')
|
||||
->willReturn($privateFile);
|
||||
$folder
|
||||
->expects($this->at(1))
|
||||
->method('getFile')
|
||||
->with('public')
|
||||
->willReturn($publicFile);
|
||||
$this->appData
|
||||
->expects($this->once())
|
||||
->method('getFolder')
|
||||
->with('MyUid')
|
||||
->willReturn($folder);
|
||||
|
||||
$expected = new Key('MyPublicKey', 'MyPrivateKey');
|
||||
$this->assertEquals($expected, $this->manager->getKey($user));
|
||||
}
|
||||
|
||||
public function testGetKeyWithNotExistingKey() {
|
||||
$user = $this->createMock(IUser::class);
|
||||
$user
|
||||
->expects($this->exactly(3))
|
||||
->method('getUID')
|
||||
->willReturn('MyUid');
|
||||
$this->appData
|
||||
->expects($this->at(0))
|
||||
->method('getFolder')
|
||||
->with('MyUid')
|
||||
->willThrowException(new \Exception());
|
||||
$this->manager
|
||||
->expects($this->once())
|
||||
->method('generateKeyPair')
|
||||
->willReturn(['MyNewPublicKey', 'MyNewPrivateKey']);
|
||||
$this->appData
|
||||
->expects($this->at(1))
|
||||
->method('newFolder')
|
||||
->with('MyUid');
|
||||
$folder = $this->createMock(ISimpleFolder::class);
|
||||
$this->crypto
|
||||
->expects($this->once())
|
||||
->method('encrypt')
|
||||
->with('MyNewPrivateKey')
|
||||
->willReturn('MyNewEncryptedPrivateKey');
|
||||
$privateFile = $this->createMock(ISimpleFile::class);
|
||||
$privateFile
|
||||
->expects($this->once())
|
||||
->method('putContent')
|
||||
->with('MyNewEncryptedPrivateKey');
|
||||
$publicFile = $this->createMock(ISimpleFile::class);
|
||||
$publicFile
|
||||
->expects($this->once())
|
||||
->method('putContent')
|
||||
->with('MyNewPublicKey');
|
||||
$folder
|
||||
->expects($this->at(0))
|
||||
->method('newFile')
|
||||
->with('private')
|
||||
->willReturn($privateFile);
|
||||
$folder
|
||||
->expects($this->at(1))
|
||||
->method('newFile')
|
||||
->with('public')
|
||||
->willReturn($publicFile);
|
||||
$this->appData
|
||||
->expects($this->at(2))
|
||||
->method('getFolder')
|
||||
->with('MyUid')
|
||||
->willReturn($folder);
|
||||
|
||||
|
||||
$expected = new Key('MyNewPublicKey', 'MyNewPrivateKey');
|
||||
$this->assertEquals($expected, $this->manager->getKey($user));
|
||||
}
|
||||
|
||||
public function testGenerateKeyPair() {
|
||||
$manager = new Manager(
|
||||
$this->appData,
|
||||
$this->crypto
|
||||
);
|
||||
$data = 'MyTestData';
|
||||
|
||||
list($resultPublicKey, $resultPrivateKey) = $this->invokePrivate($manager, 'generateKeyPair');
|
||||
openssl_sign($data, $signature, $resultPrivateKey);
|
||||
$details = openssl_pkey_get_details(openssl_pkey_get_public($resultPublicKey));
|
||||
|
||||
$this->assertSame(1, openssl_verify($data, $signature, $resultPublicKey));
|
||||
$this->assertSame(2048, $details['bits']);
|
||||
}
|
||||
}
|
|
@ -25,7 +25,8 @@
|
|||
// We only can count up. The 4. digit is only for the internal patchlevel to trigger DB upgrades
|
||||
// between betas, final and RCs. This is _not_ the public version number. Reset minor/patchlevel
|
||||
// when updating major/minor version number.
|
||||
$OC_Version = array(11, 0, 0, 1);
|
||||
|
||||
$OC_Version = array(11, 0, 0, 2);
|
||||
|
||||
// The human readable string
|
||||
$OC_VersionString = '11.0 alpha';
|
||||
|
|
Loading…
Reference in a new issue