Merge pull request #26031 from nextcloud/feature/noid/allow-autocomplete-based-on-phone-sync
Allow autocomplete based on phone sync
This commit is contained in:
commit
56b08c04c7
25
.drone.yml
25
.drone.yml
|
@ -754,6 +754,31 @@ trigger:
|
|||
- pull_request
|
||||
- push
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
name: integration-collaboration_features
|
||||
|
||||
steps:
|
||||
- name: submodules
|
||||
image: docker:git
|
||||
commands:
|
||||
- git submodule update --init
|
||||
- name: integration-collaboration_features
|
||||
image: nextcloudci/integration-php7.3:integration-php7.3-2
|
||||
commands:
|
||||
- bash tests/drone-run-integration-tests.sh || exit 0
|
||||
- ./occ maintenance:install --admin-pass=admin --data-dir=/dev/shm/nc_int
|
||||
- cd build/integration
|
||||
- ./run.sh collaboration_features/
|
||||
|
||||
trigger:
|
||||
branch:
|
||||
- master
|
||||
- stable*
|
||||
event:
|
||||
- pull_request
|
||||
- push
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
name: integration-federation_features
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
*/
|
||||
|
||||
// Backends
|
||||
use OC\KnownUser\KnownUserService;
|
||||
use OCA\DAV\CalDAV\CalDavBackend;
|
||||
use OCA\DAV\Connector\LegacyDAVACL;
|
||||
use OCA\DAV\CalDAV\CalendarRoot;
|
||||
|
@ -50,6 +51,7 @@ $principalBackend = new Principal(
|
|||
\OC::$server->getUserSession(),
|
||||
\OC::$server->getAppManager(),
|
||||
\OC::$server->query(\OCA\DAV\CalDAV\Proxy\ProxyMapper::class),
|
||||
\OC::$server->get(KnownUserService::class),
|
||||
\OC::$server->getConfig(),
|
||||
'principals/'
|
||||
);
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
*/
|
||||
|
||||
// Backends
|
||||
use OC\KnownUser\KnownUserService;
|
||||
use OCA\DAV\AppInfo\PluginManager;
|
||||
use OCA\DAV\CardDAV\AddressBookRoot;
|
||||
use OCA\DAV\CardDAV\CardDavBackend;
|
||||
|
@ -53,6 +54,7 @@ $principalBackend = new Principal(
|
|||
\OC::$server->getUserSession(),
|
||||
\OC::$server->getAppManager(),
|
||||
\OC::$server->query(\OCA\DAV\CalDAV\Proxy\ProxyMapper::class),
|
||||
\OC::$server->get(KnownUserService::class),
|
||||
\OC::$server->getConfig(),
|
||||
'principals/'
|
||||
);
|
||||
|
|
|
@ -43,8 +43,9 @@ class SystemAddressbook extends AddressBook {
|
|||
|
||||
public function getChildren() {
|
||||
$shareEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
|
||||
$restrictShareEnumeration = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes';
|
||||
if (!$shareEnumeration || ($shareEnumeration && $restrictShareEnumeration)) {
|
||||
$shareEnumerationGroup = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes';
|
||||
$shareEnumerationPhone = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes';
|
||||
if (!$shareEnumeration || $shareEnumerationGroup || $shareEnumerationPhone) {
|
||||
return [];
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
namespace OCA\DAV\Command;
|
||||
|
||||
use OC\KnownUser\KnownUserService;
|
||||
use OCA\DAV\CalDAV\CalDavBackend;
|
||||
use OCA\DAV\CalDAV\Proxy\ProxyMapper;
|
||||
use OCA\DAV\Connector\Sabre\Principal;
|
||||
|
@ -86,6 +87,7 @@ class CreateCalendar extends Command {
|
|||
\OC::$server->getUserSession(),
|
||||
\OC::$server->getAppManager(),
|
||||
\OC::$server->query(ProxyMapper::class),
|
||||
\OC::$server->get(KnownUserService::class),
|
||||
\OC::$server->getConfig()
|
||||
);
|
||||
$random = \OC::$server->getSecureRandom();
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
|
||||
namespace OCA\DAV\Connector\Sabre;
|
||||
|
||||
use OC\KnownUser\KnownUserService;
|
||||
use OCA\Circles\Exceptions\CircleDoesNotExistException;
|
||||
use OCA\DAV\CalDAV\Proxy\ProxyMapper;
|
||||
use OCA\DAV\Traits\PrincipalProxyTrait;
|
||||
|
@ -82,27 +83,19 @@ class Principal implements BackendInterface {
|
|||
/** @var ProxyMapper */
|
||||
private $proxyMapper;
|
||||
|
||||
/** @var KnownUserService */
|
||||
private $knownUserService;
|
||||
|
||||
/** @var IConfig */
|
||||
private $config;
|
||||
|
||||
/**
|
||||
* Principal constructor.
|
||||
*
|
||||
* @param IUserManager $userManager
|
||||
* @param IGroupManager $groupManager
|
||||
* @param IShareManager $shareManager
|
||||
* @param IUserSession $userSession
|
||||
* @param IAppManager $appManager
|
||||
* @param ProxyMapper $proxyMapper
|
||||
* @param IConfig $config
|
||||
* @param string $principalPrefix
|
||||
*/
|
||||
public function __construct(IUserManager $userManager,
|
||||
IGroupManager $groupManager,
|
||||
IShareManager $shareManager,
|
||||
IUserSession $userSession,
|
||||
IAppManager $appManager,
|
||||
ProxyMapper $proxyMapper,
|
||||
KnownUserService $knownUserService,
|
||||
IConfig $config,
|
||||
string $principalPrefix = 'principals/users/') {
|
||||
$this->userManager = $userManager;
|
||||
|
@ -113,6 +106,7 @@ class Principal implements BackendInterface {
|
|||
$this->principalPrefix = trim($principalPrefix, '/');
|
||||
$this->hasGroups = $this->hasCircles = ($principalPrefix === 'principals/users/');
|
||||
$this->proxyMapper = $proxyMapper;
|
||||
$this->knownUserService = $knownUserService;
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
|
@ -267,24 +261,25 @@ class Principal implements BackendInterface {
|
|||
}
|
||||
|
||||
$allowEnumeration = $this->shareManager->allowEnumeration();
|
||||
$limitEnumeration = $this->shareManager->limitEnumerationToGroups();
|
||||
$limitEnumerationGroup = $this->shareManager->limitEnumerationToGroups();
|
||||
$limitEnumerationPhone = $this->shareManager->limitEnumerationToPhone();
|
||||
$allowEnumerationFullMatch = $this->shareManager->allowEnumerationFullMatch();
|
||||
|
||||
// If sharing is restricted to group members only,
|
||||
// return only members that have groups in common
|
||||
$restrictGroups = false;
|
||||
$currentUser = $this->userSession->getUser();
|
||||
if ($this->shareManager->shareWithGroupMembersOnly()) {
|
||||
$user = $this->userSession->getUser();
|
||||
if (!$user) {
|
||||
if (!$currentUser instanceof IUser) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$restrictGroups = $this->groupManager->getUserGroupIds($user);
|
||||
$restrictGroups = $this->groupManager->getUserGroupIds($currentUser);
|
||||
}
|
||||
|
||||
$currentUserGroups = [];
|
||||
if ($limitEnumeration) {
|
||||
$currentUser = $this->userSession->getUser();
|
||||
if ($currentUser) {
|
||||
if ($limitEnumerationGroup) {
|
||||
if ($currentUser instanceof IUser) {
|
||||
$currentUserGroups = $this->groupManager->getUserGroupIds($currentUser);
|
||||
}
|
||||
}
|
||||
|
@ -296,20 +291,38 @@ class Principal implements BackendInterface {
|
|||
foreach ($searchProperties as $prop => $value) {
|
||||
switch ($prop) {
|
||||
case '{http://sabredav.org/ns}email-address':
|
||||
$users = $this->userManager->getByEmail($value);
|
||||
|
||||
if (!$allowEnumeration) {
|
||||
$users = \array_filter($users, static function (IUser $user) use ($value) {
|
||||
return $user->getEMailAddress() === $value;
|
||||
});
|
||||
}
|
||||
if ($allowEnumerationFullMatch) {
|
||||
$users = $this->userManager->getByEmail($value);
|
||||
$users = \array_filter($users, static function (IUser $user) use ($value) {
|
||||
return $user->getEMailAddress() === $value;
|
||||
});
|
||||
} else {
|
||||
$users = [];
|
||||
}
|
||||
} else {
|
||||
$users = $this->userManager->getByEmail($value);
|
||||
$users = \array_filter($users, function (IUser $user) use ($currentUser, $value, $limitEnumerationPhone, $limitEnumerationGroup, $allowEnumerationFullMatch, $currentUserGroups) {
|
||||
if ($allowEnumerationFullMatch && $user->getEMailAddress() === $value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($limitEnumeration) {
|
||||
$users = \array_filter($users, function (IUser $user) use ($currentUserGroups, $value) {
|
||||
return !empty(array_intersect(
|
||||
$this->groupManager->getUserGroupIds($user),
|
||||
$currentUserGroups
|
||||
)) || $user->getEMailAddress() === $value;
|
||||
if ($limitEnumerationPhone
|
||||
&& $currentUser instanceof IUser
|
||||
&& $this->knownUserService->isKnownToUser($currentUser->getUID(), $user->getUID())) {
|
||||
// Synced phonebook match
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!$limitEnumerationGroup) {
|
||||
// No limitation on enumeration, all allowed
|
||||
return true;
|
||||
}
|
||||
|
||||
return !empty($currentUserGroups) && !empty(array_intersect(
|
||||
$this->groupManager->getUserGroupIds($user),
|
||||
$currentUserGroups
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -328,20 +341,39 @@ class Principal implements BackendInterface {
|
|||
break;
|
||||
|
||||
case '{DAV:}displayname':
|
||||
$users = $this->userManager->searchDisplayName($value, $searchLimit);
|
||||
|
||||
if (!$allowEnumeration) {
|
||||
$users = \array_filter($users, static function (IUser $user) use ($value) {
|
||||
return $user->getDisplayName() === $value;
|
||||
});
|
||||
}
|
||||
if ($allowEnumerationFullMatch) {
|
||||
$users = $this->userManager->searchDisplayName($value, $searchLimit);
|
||||
$users = \array_filter($users, static function (IUser $user) use ($value) {
|
||||
return $user->getDisplayName() === $value;
|
||||
});
|
||||
} else {
|
||||
$users = [];
|
||||
}
|
||||
} else {
|
||||
$users = $this->userManager->searchDisplayName($value, $searchLimit);
|
||||
$users = \array_filter($users, function (IUser $user) use ($currentUser, $value, $limitEnumerationPhone, $limitEnumerationGroup, $allowEnumerationFullMatch, $currentUserGroups) {
|
||||
if ($allowEnumerationFullMatch && $user->getDisplayName() === $value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($limitEnumeration) {
|
||||
$users = \array_filter($users, function (IUser $user) use ($currentUserGroups, $value) {
|
||||
return !empty(array_intersect(
|
||||
$this->groupManager->getUserGroupIds($user),
|
||||
$currentUserGroups
|
||||
)) || $user->getDisplayName() === $value;
|
||||
if ($limitEnumerationPhone
|
||||
&& $currentUser instanceof IUser
|
||||
&& $this->knownUserService->isKnownToUser($currentUser->getUID(), $user->getUID())) {
|
||||
// Synced phonebook match
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!$limitEnumerationGroup) {
|
||||
// No limitation on enumeration, all allowed
|
||||
return true;
|
||||
}
|
||||
|
||||
return !empty($currentUserGroups) && !empty(array_intersect(
|
||||
$this->groupManager->getUserGroupIds($user),
|
||||
$currentUserGroups
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
namespace OCA\DAV;
|
||||
|
||||
use OC\KnownUser\KnownUserService;
|
||||
use OCA\DAV\AppInfo\PluginManager;
|
||||
use OCA\DAV\CalDAV\CalDavBackend;
|
||||
use OCA\DAV\CalDAV\CalendarRoot;
|
||||
|
@ -70,6 +71,7 @@ class RootCollection extends SimpleCollection {
|
|||
\OC::$server->getUserSession(),
|
||||
\OC::$server->getAppManager(),
|
||||
$proxyMapper,
|
||||
\OC::$server->get(KnownUserService::class),
|
||||
\OC::$server->getConfig()
|
||||
);
|
||||
$groupPrincipalBackend = new GroupPrincipalBackend($groupManager, $userSession, $shareManager, $config);
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
namespace OCA\DAV\Tests\unit\CalDAV;
|
||||
|
||||
use OC\KnownUser\KnownUserService;
|
||||
use OCA\DAV\CalDAV\CalDavBackend;
|
||||
use OCA\DAV\CalDAV\Proxy\ProxyMapper;
|
||||
use OCA\DAV\Connector\Sabre\Principal;
|
||||
|
@ -92,6 +93,7 @@ abstract class AbstractCalDavBackend extends TestCase {
|
|||
$this->createMock(IUserSession::class),
|
||||
$this->createMock(IAppManager::class),
|
||||
$this->createMock(ProxyMapper::class),
|
||||
$this->createMock(KnownUserService::class),
|
||||
$this->createMock(IConfig::class),
|
||||
])
|
||||
->setMethods(['getPrincipalByPath', 'getGroupMembership'])
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
|
||||
namespace OCA\DAV\Tests\unit\CardDAV;
|
||||
|
||||
use OC\KnownUser\KnownUserService;
|
||||
use OCA\DAV\CalDAV\Proxy\ProxyMapper;
|
||||
use OCA\DAV\CardDAV\AddressBook;
|
||||
use OCA\DAV\CardDAV\CardDavBackend;
|
||||
|
@ -139,6 +140,7 @@ class CardDavBackendTest extends TestCase {
|
|||
$this->createMock(IUserSession::class),
|
||||
$this->createMock(IAppManager::class),
|
||||
$this->createMock(ProxyMapper::class),
|
||||
$this->createMock(KnownUserService::class),
|
||||
$this->createMock(IConfig::class),
|
||||
])
|
||||
->setMethods(['getPrincipalByPath', 'getGroupMembership'])
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
namespace OCA\DAV\Tests\unit\Connector\Sabre;
|
||||
|
||||
use OC\KnownUser\KnownUserService;
|
||||
use OC\User\User;
|
||||
use OCA\DAV\CalDAV\Proxy\Proxy;
|
||||
use OCA\DAV\CalDAV\Proxy\ProxyMapper;
|
||||
|
@ -41,6 +42,7 @@ use OCP\IUser;
|
|||
use OCP\IUserManager;
|
||||
use OCP\IUserSession;
|
||||
use OCP\Share\IManager;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Sabre\DAV\PropPatch;
|
||||
use Test\TestCase;
|
||||
|
||||
|
@ -67,6 +69,8 @@ class PrincipalTest extends TestCase {
|
|||
/** @var ProxyMapper | \PHPUnit\Framework\MockObject\MockObject */
|
||||
private $proxyMapper;
|
||||
|
||||
/** @var KnownUserService|MockObject */
|
||||
private $knownUserService;
|
||||
/** @var IConfig | \PHPUnit\Framework\MockObject\MockObject */
|
||||
private $config;
|
||||
|
||||
|
@ -77,6 +81,7 @@ class PrincipalTest extends TestCase {
|
|||
$this->userSession = $this->createMock(IUserSession::class);
|
||||
$this->appManager = $this->createMock(IAppManager::class);
|
||||
$this->proxyMapper = $this->createMock(ProxyMapper::class);
|
||||
$this->knownUserService = $this->createMock(KnownUserService::class);
|
||||
$this->config = $this->createMock(IConfig::class);
|
||||
|
||||
$this->connector = new \OCA\DAV\Connector\Sabre\Principal(
|
||||
|
@ -86,6 +91,7 @@ class PrincipalTest extends TestCase {
|
|||
$this->userSession,
|
||||
$this->appManager,
|
||||
$this->proxyMapper,
|
||||
$this->knownUserService,
|
||||
$this->config
|
||||
);
|
||||
parent::setUp();
|
||||
|
@ -442,7 +448,7 @@ class PrincipalTest extends TestCase {
|
|||
|
||||
if ($groupsOnly) {
|
||||
$user = $this->createMock(IUser::class);
|
||||
$this->userSession->expects($this->once())
|
||||
$this->userSession->expects($this->atLeastOnce())
|
||||
->method('getUser')
|
||||
->willReturn($user);
|
||||
|
||||
|
@ -564,6 +570,10 @@ class PrincipalTest extends TestCase {
|
|||
->method('shareWithGroupMembersOnly')
|
||||
->willReturn(false);
|
||||
|
||||
$this->shareManager->expects($this->once())
|
||||
->method('allowEnumerationFullMatch')
|
||||
->willReturn(true);
|
||||
|
||||
$user2 = $this->createMock(IUser::class);
|
||||
$user2->method('getUID')->willReturn('user2');
|
||||
$user2->method('getDisplayName')->willReturn('User 2');
|
||||
|
@ -586,6 +596,27 @@ class PrincipalTest extends TestCase {
|
|||
['{DAV:}displayname' => 'User 2']));
|
||||
}
|
||||
|
||||
public function testSearchPrincipalWithEnumerationDisabledDisplaynameOnFullMatch() {
|
||||
$this->shareManager->expects($this->once())
|
||||
->method('shareAPIEnabled')
|
||||
->willReturn(true);
|
||||
|
||||
$this->shareManager->expects($this->once())
|
||||
->method('allowEnumeration')
|
||||
->willReturn(false);
|
||||
|
||||
$this->shareManager->expects($this->once())
|
||||
->method('shareWithGroupMembersOnly')
|
||||
->willReturn(false);
|
||||
|
||||
$this->shareManager->expects($this->once())
|
||||
->method('allowEnumerationFullMatch')
|
||||
->willReturn(false);
|
||||
|
||||
$this->assertEquals([], $this->connector->searchPrincipals('principals/users',
|
||||
['{DAV:}displayname' => 'User 2']));
|
||||
}
|
||||
|
||||
public function testSearchPrincipalWithEnumerationDisabledEmail() {
|
||||
$this->shareManager->expects($this->once())
|
||||
->method('shareAPIEnabled')
|
||||
|
@ -599,6 +630,10 @@ class PrincipalTest extends TestCase {
|
|||
->method('shareWithGroupMembersOnly')
|
||||
->willReturn(false);
|
||||
|
||||
$this->shareManager->expects($this->once())
|
||||
->method('allowEnumerationFullMatch')
|
||||
->willReturn(true);
|
||||
|
||||
$user2 = $this->createMock(IUser::class);
|
||||
$user2->method('getUID')->willReturn('user2');
|
||||
$user2->method('getDisplayName')->willReturn('User 2');
|
||||
|
@ -621,6 +656,28 @@ class PrincipalTest extends TestCase {
|
|||
['{http://sabredav.org/ns}email-address' => 'user2@foo.bar']));
|
||||
}
|
||||
|
||||
public function testSearchPrincipalWithEnumerationDisabledEmailOnFullMatch() {
|
||||
$this->shareManager->expects($this->once())
|
||||
->method('shareAPIEnabled')
|
||||
->willReturn(true);
|
||||
|
||||
$this->shareManager->expects($this->once())
|
||||
->method('allowEnumeration')
|
||||
->willReturn(false);
|
||||
|
||||
$this->shareManager->expects($this->once())
|
||||
->method('shareWithGroupMembersOnly')
|
||||
->willReturn(false);
|
||||
|
||||
$this->shareManager->expects($this->once())
|
||||
->method('allowEnumerationFullMatch')
|
||||
->willReturn(false);
|
||||
|
||||
|
||||
$this->assertEquals([], $this->connector->searchPrincipals('principals/users',
|
||||
['{http://sabredav.org/ns}email-address' => 'user2@foo.bar']));
|
||||
}
|
||||
|
||||
public function testSearchPrincipalWithEnumerationLimitedDisplayname() {
|
||||
$this->shareManager->expects($this->at(0))
|
||||
->method('shareAPIEnabled')
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
namespace OCA\Files_Versions\AppInfo;
|
||||
|
||||
use OC\KnownUser\KnownUserService;
|
||||
use OCA\DAV\CalDAV\Proxy\ProxyMapper;
|
||||
use OCA\DAV\Connector\Sabre\Principal;
|
||||
use OCA\Files\Event\LoadAdditionalScriptsEvent;
|
||||
|
@ -72,6 +73,7 @@ class Application extends App implements IBootstrap {
|
|||
$server->getUserSession(),
|
||||
$server->getAppManager(),
|
||||
$server->get(ProxyMapper::class),
|
||||
$server->get(KnownUserService::class),
|
||||
$server->getConfig()
|
||||
);
|
||||
});
|
||||
|
|
|
@ -14,6 +14,7 @@ return array(
|
|||
'OCA\\Provisioning_API\\Controller\\GroupsController' => $baseDir . '/../lib/Controller/GroupsController.php',
|
||||
'OCA\\Provisioning_API\\Controller\\UsersController' => $baseDir . '/../lib/Controller/UsersController.php',
|
||||
'OCA\\Provisioning_API\\FederatedShareProviderFactory' => $baseDir . '/../lib/FederatedShareProviderFactory.php',
|
||||
'OCA\\Provisioning_API\\Listener\\UserDeletedListener' => $baseDir . '/../lib/Listener/UserDeletedListener.php',
|
||||
'OCA\\Provisioning_API\\Middleware\\Exceptions\\NotSubAdminException' => $baseDir . '/../lib/Middleware/Exceptions/NotSubAdminException.php',
|
||||
'OCA\\Provisioning_API\\Middleware\\ProvisioningApiMiddleware' => $baseDir . '/../lib/Middleware/ProvisioningApiMiddleware.php',
|
||||
);
|
||||
|
|
|
@ -29,6 +29,7 @@ class ComposerStaticInitProvisioning_API
|
|||
'OCA\\Provisioning_API\\Controller\\GroupsController' => __DIR__ . '/..' . '/../lib/Controller/GroupsController.php',
|
||||
'OCA\\Provisioning_API\\Controller\\UsersController' => __DIR__ . '/..' . '/../lib/Controller/UsersController.php',
|
||||
'OCA\\Provisioning_API\\FederatedShareProviderFactory' => __DIR__ . '/..' . '/../lib/FederatedShareProviderFactory.php',
|
||||
'OCA\\Provisioning_API\\Listener\\UserDeletedListener' => __DIR__ . '/..' . '/../lib/Listener/UserDeletedListener.php',
|
||||
'OCA\\Provisioning_API\\Middleware\\Exceptions\\NotSubAdminException' => __DIR__ . '/..' . '/../lib/Middleware/Exceptions/NotSubAdminException.php',
|
||||
'OCA\\Provisioning_API\\Middleware\\ProvisioningApiMiddleware' => __DIR__ . '/..' . '/../lib/Middleware/ProvisioningApiMiddleware.php',
|
||||
);
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
namespace OCA\Provisioning_API\AppInfo;
|
||||
|
||||
use OC\Group\Manager as GroupManager;
|
||||
use OCA\Provisioning_API\Listener\UserDeletedListener;
|
||||
use OCA\Provisioning_API\Middleware\ProvisioningApiMiddleware;
|
||||
use OCA\Settings\Mailer\NewUserMailHelper;
|
||||
use OCP\AppFramework\App;
|
||||
|
@ -47,6 +48,7 @@ use OCP\L10N\IFactory;
|
|||
use OCP\Mail\IMailer;
|
||||
use OCP\Security\ICrypto;
|
||||
use OCP\Security\ISecureRandom;
|
||||
use OCP\User\Events\UserDeletedEvent;
|
||||
use OCP\Util;
|
||||
use Psr\Container\ContainerInterface;
|
||||
|
||||
|
@ -56,6 +58,8 @@ class Application extends App implements IBootstrap {
|
|||
}
|
||||
|
||||
public function register(IRegistrationContext $context): void {
|
||||
$context->registerEventListener(UserDeletedEvent::class, UserDeletedListener::class);
|
||||
|
||||
$context->registerService(NewUserMailHelper::class, function (ContainerInterface $c) {
|
||||
return new NewUserMailHelper(
|
||||
$c->get(Defaults::class),
|
||||
|
|
|
@ -49,6 +49,7 @@ use libphonenumber\PhoneNumberUtil;
|
|||
use OC\Accounts\AccountManager;
|
||||
use OC\Authentication\Token\RemoteWipe;
|
||||
use OC\HintException;
|
||||
use OC\KnownUser\KnownUserService;
|
||||
use OCA\Provisioning_API\FederatedShareProviderFactory;
|
||||
use OCA\Settings\Mailer\NewUserMailHelper;
|
||||
use OCP\Accounts\IAccountManager;
|
||||
|
@ -90,6 +91,8 @@ class UsersController extends AUserData {
|
|||
private $secureRandom;
|
||||
/** @var RemoteWipe */
|
||||
private $remoteWipe;
|
||||
/** @var KnownUserService */
|
||||
private $knownUserService;
|
||||
/** @var IEventDispatcher */
|
||||
private $eventDispatcher;
|
||||
|
||||
|
@ -108,6 +111,7 @@ class UsersController extends AUserData {
|
|||
FederatedShareProviderFactory $federatedShareProviderFactory,
|
||||
ISecureRandom $secureRandom,
|
||||
RemoteWipe $remoteWipe,
|
||||
KnownUserService $knownUserService,
|
||||
IEventDispatcher $eventDispatcher) {
|
||||
parent::__construct($appName,
|
||||
$request,
|
||||
|
@ -126,6 +130,7 @@ class UsersController extends AUserData {
|
|||
$this->federatedShareProviderFactory = $federatedShareProviderFactory;
|
||||
$this->secureRandom = $secureRandom;
|
||||
$this->remoteWipe = $remoteWipe;
|
||||
$this->knownUserService = $knownUserService;
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
}
|
||||
|
||||
|
@ -231,6 +236,13 @@ class UsersController extends AUserData {
|
|||
return new DataResponse([], Http::STATUS_BAD_REQUEST);
|
||||
}
|
||||
|
||||
/** @var IUser $user */
|
||||
$user = $this->userSession->getUser();
|
||||
$knownTo = $user->getUID();
|
||||
|
||||
// Cleanup all previous entries and only allow new matches
|
||||
$this->knownUserService->deleteKnownTo($knownTo);
|
||||
|
||||
$normalizedNumberToKey = [];
|
||||
foreach ($search as $key => $phoneNumbers) {
|
||||
foreach ($phoneNumbers as $phone) {
|
||||
|
@ -268,6 +280,7 @@ class UsersController extends AUserData {
|
|||
foreach ($userMatches as $phone => $userId) {
|
||||
// Not using the ICloudIdManager as that would run a search for each contact to find the display name in the address book
|
||||
$matches[$normalizedNumberToKey[$phone]] = $userId . '@' . $cloudUrl;
|
||||
$this->knownUserService->storeIsKnownToUser($knownTo, $userId);
|
||||
}
|
||||
|
||||
return new DataResponse($matches);
|
||||
|
@ -677,6 +690,10 @@ class UsersController extends AUserData {
|
|||
$userAccount[$key]['value'] = $value;
|
||||
try {
|
||||
$this->accountManager->updateUser($targetUser, $userAccount, true);
|
||||
|
||||
if ($key === IAccountManager::PROPERTY_PHONE) {
|
||||
$this->knownUserService->deleteByContactUserId($targetUser->getUID());
|
||||
}
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
throw new OCSException('Invalid ' . $e->getMessage(), 102);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2020 Joas Schilling <coding@schilljs.com>
|
||||
*
|
||||
* @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\Provisioning_API\Listener;
|
||||
|
||||
use OC\KnownUser\KnownUserService;
|
||||
use OCP\EventDispatcher\Event;
|
||||
use OCP\EventDispatcher\IEventListener;
|
||||
use OCP\User\Events\UserDeletedEvent;
|
||||
|
||||
class UserDeletedListener implements IEventListener {
|
||||
|
||||
/** @var KnownUserService */
|
||||
private $service;
|
||||
|
||||
public function __construct(KnownUserService $service) {
|
||||
$this->service = $service;
|
||||
}
|
||||
|
||||
public function handle(Event $event): void {
|
||||
if (!($event instanceof UserDeletedEvent)) {
|
||||
// Unrelated
|
||||
return;
|
||||
}
|
||||
|
||||
$user = $event->getUser();
|
||||
|
||||
// Delete all entries of this user
|
||||
$this->service->deleteKnownTo($user->getUID());
|
||||
|
||||
// Delete all entries that other users know this user
|
||||
$this->service->deleteByContactUserId($user->getUID());
|
||||
}
|
||||
}
|
|
@ -44,6 +44,7 @@ use Exception;
|
|||
use OC\Accounts\AccountManager;
|
||||
use OC\Authentication\Token\RemoteWipe;
|
||||
use OC\Group\Manager;
|
||||
use OC\KnownUser\KnownUserService;
|
||||
use OC\SubAdmin;
|
||||
use OCA\FederatedFileSharing\FederatedShareProvider;
|
||||
use OCA\Provisioning_API\Controller\UsersController;
|
||||
|
@ -102,6 +103,8 @@ class UsersControllerTest extends TestCase {
|
|||
private $secureRandom;
|
||||
/** @var RemoteWipe|MockObject */
|
||||
private $remoteWipe;
|
||||
/** @var KnownUserService|MockObject */
|
||||
private $knownUserService;
|
||||
/** @var IEventDispatcher */
|
||||
private $eventDispatcher;
|
||||
|
||||
|
@ -122,6 +125,7 @@ class UsersControllerTest extends TestCase {
|
|||
$this->federatedShareProviderFactory = $this->createMock(FederatedShareProviderFactory::class);
|
||||
$this->secureRandom = $this->createMock(ISecureRandom::class);
|
||||
$this->remoteWipe = $this->createMock(RemoteWipe::class);
|
||||
$this->knownUserService = $this->createMock(KnownUserService::class);
|
||||
$this->eventDispatcher = $this->createMock(IEventDispatcher::class);
|
||||
|
||||
$this->api = $this->getMockBuilder(UsersController::class)
|
||||
|
@ -141,6 +145,7 @@ class UsersControllerTest extends TestCase {
|
|||
$this->federatedShareProviderFactory,
|
||||
$this->secureRandom,
|
||||
$this->remoteWipe,
|
||||
$this->knownUserService,
|
||||
$this->eventDispatcher,
|
||||
])
|
||||
->setMethods(['fillStorageInfo'])
|
||||
|
@ -405,6 +410,7 @@ class UsersControllerTest extends TestCase {
|
|||
$this->federatedShareProviderFactory,
|
||||
$this->secureRandom,
|
||||
$this->remoteWipe,
|
||||
$this->knownUserService,
|
||||
$this->eventDispatcher,
|
||||
])
|
||||
->setMethods(['editUser'])
|
||||
|
@ -1400,6 +1406,13 @@ class UsersControllerTest extends TestCase {
|
|||
* @param array $expected
|
||||
*/
|
||||
public function testSearchByPhoneNumbers(string $location, array $search, int $status, ?array $searchUsers, ?array $userMatches, array $expected) {
|
||||
$knownTo = 'knownTo';
|
||||
$user = $this->createMock(IUser::class);
|
||||
$user->method('getUID')
|
||||
->willReturn($knownTo);
|
||||
$this->userSession->method('getUser')
|
||||
->willReturn($user);
|
||||
|
||||
if ($searchUsers === null) {
|
||||
$this->accountManager->expects($this->never())
|
||||
->method('searchUsers');
|
||||
|
@ -1408,6 +1421,14 @@ class UsersControllerTest extends TestCase {
|
|||
->method('searchUsers')
|
||||
->with(IAccountManager::PROPERTY_PHONE, $searchUsers)
|
||||
->willReturn($userMatches);
|
||||
|
||||
$this->knownUserService->expects($this->once())
|
||||
->method('deleteKnownTo')
|
||||
->with($knownTo);
|
||||
|
||||
$this->knownUserService->expects($this->exactly(count($expected)))
|
||||
->method('storeIsKnownToUser')
|
||||
->with($knownTo, $this->anything());
|
||||
}
|
||||
|
||||
$this->urlGenerator->method('getAbsoluteURL')
|
||||
|
@ -3229,6 +3250,7 @@ class UsersControllerTest extends TestCase {
|
|||
$this->federatedShareProviderFactory,
|
||||
$this->secureRandom,
|
||||
$this->remoteWipe,
|
||||
$this->knownUserService,
|
||||
$this->eventDispatcher,
|
||||
])
|
||||
->setMethods(['getUserData'])
|
||||
|
@ -3295,6 +3317,7 @@ class UsersControllerTest extends TestCase {
|
|||
$this->federatedShareProviderFactory,
|
||||
$this->secureRandom,
|
||||
$this->remoteWipe,
|
||||
$this->knownUserService,
|
||||
$this->eventDispatcher,
|
||||
])
|
||||
->setMethods(['getUserData'])
|
||||
|
|
|
@ -144,6 +144,8 @@ window.addEventListener('DOMContentLoaded', function(){
|
|||
|
||||
$('#shareapi_allow_share_dialog_user_enumeration').on('change', function() {
|
||||
$('#shareapi_restrict_user_enumeration_to_group_setting').toggleClass('hidden', !this.checked);
|
||||
$('#shareapi_restrict_user_enumeration_to_phone_setting').toggleClass('hidden', !this.checked);
|
||||
$('#shareapi_restrict_user_enumeration_combinewarning_setting').toggleClass('hidden', !this.checked);
|
||||
})
|
||||
|
||||
$('#allowLinks').change(function() {
|
||||
|
|
|
@ -42,6 +42,7 @@ use OC\AppFramework\Http;
|
|||
use OC\Encryption\Exceptions\ModuleDoesNotExistsException;
|
||||
use OC\ForbiddenException;
|
||||
use OC\Group\Manager as GroupManager;
|
||||
use OC\KnownUser\KnownUserService;
|
||||
use OC\L10N\Factory;
|
||||
use OC\Security\IdentityProof\Manager;
|
||||
use OC\User\Manager as UserManager;
|
||||
|
@ -96,6 +97,8 @@ class UsersController extends Controller {
|
|||
private $jobList;
|
||||
/** @var IManager */
|
||||
private $encryptionManager;
|
||||
/** @var KnownUserService */
|
||||
private $knownUserService;
|
||||
/** @var IEventDispatcher */
|
||||
private $dispatcher;
|
||||
|
||||
|
@ -116,6 +119,7 @@ class UsersController extends Controller {
|
|||
Manager $keyManager,
|
||||
IJobList $jobList,
|
||||
IManager $encryptionManager,
|
||||
KnownUserService $knownUserService,
|
||||
IEventDispatcher $dispatcher
|
||||
) {
|
||||
parent::__construct($appName, $request);
|
||||
|
@ -132,6 +136,7 @@ class UsersController extends Controller {
|
|||
$this->keyManager = $keyManager;
|
||||
$this->jobList = $jobList;
|
||||
$this->encryptionManager = $encryptionManager;
|
||||
$this->knownUserService = $knownUserService;
|
||||
$this->dispatcher = $dispatcher;
|
||||
}
|
||||
|
||||
|
@ -363,6 +368,19 @@ class UsersController extends Controller {
|
|||
?string $twitter = null,
|
||||
?string $twitterScope = null
|
||||
) {
|
||||
$user = $this->userSession->getUser();
|
||||
if (!$user instanceof IUser) {
|
||||
return new DataResponse(
|
||||
[
|
||||
'status' => 'error',
|
||||
'data' => [
|
||||
'message' => $this->l10n->t('Invalid user')
|
||||
]
|
||||
],
|
||||
Http::STATUS_UNAUTHORIZED
|
||||
);
|
||||
}
|
||||
|
||||
$email = strtolower($email);
|
||||
if (!empty($email) && !$this->mailer->validateMailAddress($email)) {
|
||||
return new DataResponse(
|
||||
|
@ -375,8 +393,9 @@ class UsersController extends Controller {
|
|||
Http::STATUS_UNPROCESSABLE_ENTITY
|
||||
);
|
||||
}
|
||||
$user = $this->userSession->getUser();
|
||||
|
||||
$data = $this->accountManager->getUser($user);
|
||||
$beforeData = $data;
|
||||
$data[IAccountManager::PROPERTY_AVATAR] = ['scope' => $avatarScope];
|
||||
if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
|
||||
$data[IAccountManager::PROPERTY_DISPLAYNAME] = ['value' => $displayname, 'scope' => $displaynameScope];
|
||||
|
@ -393,6 +412,9 @@ class UsersController extends Controller {
|
|||
}
|
||||
try {
|
||||
$data = $this->saveUserSettings($user, $data);
|
||||
if ($beforeData[IAccountManager::PROPERTY_PHONE]['value'] !== $data[IAccountManager::PROPERTY_PHONE]['value']) {
|
||||
$this->knownUserService->deleteByContactUserId($user->getUID());
|
||||
}
|
||||
return new DataResponse(
|
||||
[
|
||||
'status' => 'success',
|
||||
|
|
|
@ -73,6 +73,8 @@ class Sharing implements ISettings {
|
|||
'allowResharing' => $this->config->getAppValue('core', 'shareapi_allow_resharing', 'yes'),
|
||||
'allowShareDialogUserEnumeration' => $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes'),
|
||||
'restrictUserEnumerationToGroup' => $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no'),
|
||||
'restrictUserEnumerationToPhone' => $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no'),
|
||||
'restrictUserEnumerationFullMatch' => $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match', 'yes'),
|
||||
'enforceLinkPassword' => Util::isPublicLinkPasswordRequired(),
|
||||
'onlyShareWithGroupMembers' => $this->shareManager->shareWithGroupMembersOnly(),
|
||||
'shareAPIEnabled' => $this->config->getAppValue('core', 'shareapi_enabled', 'yes'),
|
||||
|
|
|
@ -163,7 +163,7 @@
|
|||
<?php if ($_['allowShareDialogUserEnumeration'] === 'yes') {
|
||||
print_unescaped('checked="checked"');
|
||||
} ?> />
|
||||
<label for="shareapi_allow_share_dialog_user_enumeration"><?php p($l->t('Allow username autocompletion in share dialog (if this is disabled the full username or email address needs to be entered)'));?></label><br />
|
||||
<label for="shareapi_allow_share_dialog_user_enumeration"><?php p($l->t('Allow username autocompletion in share dialog'));?></label><br />
|
||||
</p>
|
||||
|
||||
<p id="shareapi_restrict_user_enumeration_to_group_setting" class="indent <?php if ($_['shareAPIEnabled'] === 'no' || $_['allowShareDialogUserEnumeration'] === 'no') {
|
||||
|
@ -173,7 +173,31 @@
|
|||
<?php if ($_['restrictUserEnumerationToGroup'] === 'yes') {
|
||||
print_unescaped('checked="checked"');
|
||||
} ?> />
|
||||
<label for="shareapi_restrict_user_enumeration_to_group"><?php p($l->t('Restrict username autocompletion to users within the same groups'));?></label><br />
|
||||
<label for="shareapi_restrict_user_enumeration_to_group"><?php p($l->t('Allow username autocompletion to users within the same groups'));?></label><br />
|
||||
</p>
|
||||
|
||||
<p id="shareapi_restrict_user_enumeration_to_phone_setting" class="indent <?php if ($_['shareAPIEnabled'] === 'no' || $_['allowShareDialogUserEnumeration'] === 'no') {
|
||||
p('hidden');
|
||||
}?>">
|
||||
<input type="checkbox" name="shareapi_restrict_user_enumeration_to_phone" value="1" id="shareapi_restrict_user_enumeration_to_phone" class="checkbox"
|
||||
<?php if ($_['restrictUserEnumerationToPhone'] === 'yes') {
|
||||
print_unescaped('checked="checked"');
|
||||
} ?> />
|
||||
<label for="shareapi_restrict_user_enumeration_to_phone"><?php p($l->t('Allow username autocompletion to users based on phonebook matches'));?></label><br />
|
||||
</p>
|
||||
<p id="shareapi_restrict_user_enumeration_combinewarning_setting" class="indent <?php if ($_['shareAPIEnabled'] === 'no' || $_['allowShareDialogUserEnumeration'] === 'no') {
|
||||
p('hidden');
|
||||
}?>">
|
||||
<em><?php p($l->t('If autocompletion "same group" and "phonebook matches" are enabled a match in either is enough to show the user.'));?></em><br />
|
||||
</p>
|
||||
<p id="shareapi_restrict_user_enumeration_full_match_setting" class="indent <?php if ($_['shareAPIEnabled'] === 'no') {
|
||||
p('hidden');
|
||||
}?>">
|
||||
<input type="checkbox" name="shareapi_restrict_user_enumeration_full_match" value="1" id="shareapi_restrict_user_enumeration_full_match" class="checkbox"
|
||||
<?php if ($_['restrictUserEnumerationFullMatch'] === 'yes') {
|
||||
print_unescaped('checked="checked"');
|
||||
} ?> />
|
||||
<label for="shareapi_restrict_user_enumeration_full_match"><?php p($l->t('Allow username autocompletion when entering the full name or email address (ignoring missing phonebook match and being in the same group)'));?></label><br />
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
|
|
@ -32,6 +32,7 @@ namespace OCA\Settings\Tests\Controller;
|
|||
use OC\Accounts\AccountManager;
|
||||
use OC\Encryption\Exceptions\ModuleDoesNotExistsException;
|
||||
use OC\Group\Manager;
|
||||
use OC\KnownUser\KnownUserService;
|
||||
use OCA\Settings\Controller\UsersController;
|
||||
use OCP\Accounts\IAccountManager;
|
||||
use OCP\App\IAppManager;
|
||||
|
@ -91,6 +92,8 @@ class UsersControllerTest extends \Test\TestCase {
|
|||
private $securityManager;
|
||||
/** @var IManager | \PHPUnit\Framework\MockObject\MockObject */
|
||||
private $encryptionManager;
|
||||
/** @var KnownUserService|\PHPUnit\Framework\MockObject\MockObject */
|
||||
private $knownUserService;
|
||||
/** @var IEncryptionModule | \PHPUnit\Framework\MockObject\MockObject */
|
||||
private $encryptionModule;
|
||||
/** @var IEventDispatcher|\PHPUnit\Framework\MockObject\MockObject */
|
||||
|
@ -111,6 +114,7 @@ class UsersControllerTest extends \Test\TestCase {
|
|||
$this->securityManager = $this->getMockBuilder(\OC\Security\IdentityProof\Manager::class)->disableOriginalConstructor()->getMock();
|
||||
$this->jobList = $this->createMock(IJobList::class);
|
||||
$this->encryptionManager = $this->createMock(IManager::class);
|
||||
$this->knownUserService = $this->createMock(KnownUserService::class);
|
||||
$this->dispatcher = $this->createMock(IEventDispatcher::class);
|
||||
|
||||
$this->l->method('t')
|
||||
|
@ -147,6 +151,7 @@ class UsersControllerTest extends \Test\TestCase {
|
|||
$this->securityManager,
|
||||
$this->jobList,
|
||||
$this->encryptionManager,
|
||||
$this->knownUserService,
|
||||
$this->dispatcher
|
||||
);
|
||||
} else {
|
||||
|
@ -168,6 +173,7 @@ class UsersControllerTest extends \Test\TestCase {
|
|||
$this->securityManager,
|
||||
$this->jobList,
|
||||
$this->encryptionManager,
|
||||
$this->knownUserService,
|
||||
$this->dispatcher
|
||||
]
|
||||
)->setMethods($mockedMethods)->getMock();
|
||||
|
|
|
@ -64,95 +64,29 @@ class SharingTest extends TestCase {
|
|||
|
||||
public function testGetFormWithoutExcludedGroups() {
|
||||
$this->config
|
||||
->expects($this->at(0))
|
||||
->method('getAppValue')
|
||||
->with('core', 'shareapi_exclude_groups_list', '')
|
||||
->willReturn('');
|
||||
$this->config
|
||||
->expects($this->at(1))
|
||||
->method('getAppValue')
|
||||
->with('core', 'shareapi_allow_group_sharing', 'yes')
|
||||
->willReturn('yes');
|
||||
$this->config
|
||||
->expects($this->at(2))
|
||||
->method('getAppValue')
|
||||
->with('core', 'shareapi_allow_links', 'yes')
|
||||
->willReturn('yes');
|
||||
$this->config
|
||||
->expects($this->at(3))
|
||||
->method('getAppValue')
|
||||
->with('core', 'shareapi_allow_public_upload', 'yes')
|
||||
->willReturn('yes');
|
||||
$this->config
|
||||
->expects($this->at(4))
|
||||
->method('getAppValue')
|
||||
->with('core', 'shareapi_allow_resharing', 'yes')
|
||||
->willReturn('yes');
|
||||
$this->config
|
||||
->expects($this->at(5))
|
||||
->method('getAppValue')
|
||||
->with('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes')
|
||||
->willReturn('yes');
|
||||
$this->config
|
||||
->expects($this->at(6))
|
||||
->method('getAppValue')
|
||||
->with('core', 'shareapi_restrict_user_enumeration_to_group', 'no')
|
||||
->willReturn('no');
|
||||
$this->config
|
||||
->expects($this->at(7))
|
||||
->method('getAppValue')
|
||||
->with('core', 'shareapi_enabled', 'yes')
|
||||
->willReturn('yes');
|
||||
$this->config
|
||||
->expects($this->at(8))
|
||||
->method('getAppValue')
|
||||
->with('core', 'shareapi_default_expire_date', 'no')
|
||||
->willReturn('no');
|
||||
$this->config
|
||||
->expects($this->at(9))
|
||||
->method('getAppValue')
|
||||
->with('core', 'shareapi_expire_after_n_days', '7')
|
||||
->willReturn('7');
|
||||
$this->config
|
||||
->expects($this->at(10))
|
||||
->method('getAppValue')
|
||||
->with('core', 'shareapi_enforce_expire_date', 'no')
|
||||
->willReturn('no');
|
||||
$this->config
|
||||
->expects($this->at(11))
|
||||
->method('getAppValue')
|
||||
->with('core', 'shareapi_exclude_groups', 'no')
|
||||
->willReturn('no');
|
||||
$this->config
|
||||
->expects($this->at(12))
|
||||
->method('getAppValue')
|
||||
->with('core', 'shareapi_public_link_disclaimertext', null)
|
||||
->willReturn('Lorem ipsum');
|
||||
$this->config
|
||||
->expects($this->at(13))
|
||||
->method('getAppValue')
|
||||
->with('core', 'shareapi_enable_link_password_by_default', 'no')
|
||||
->willReturn('yes');
|
||||
$this->config
|
||||
->expects($this->at(14))
|
||||
->method('getAppValue')
|
||||
->with('core', 'shareapi_default_permissions', Constants::PERMISSION_ALL)
|
||||
->willReturn(Constants::PERMISSION_ALL);
|
||||
$this->config
|
||||
->expects($this->at(15))
|
||||
->method('getAppValue')
|
||||
->with('core', 'shareapi_default_internal_expire_date', 'no')
|
||||
->willReturn('no');
|
||||
$this->config
|
||||
->expects($this->at(16))
|
||||
->method('getAppValue')
|
||||
->with('core', 'shareapi_internal_expire_after_n_days', '7')
|
||||
->willReturn('7');
|
||||
$this->config
|
||||
->expects($this->at(17))
|
||||
->method('getAppValue')
|
||||
->with('core', 'shareapi_enforce_internal_expire_date', 'no')
|
||||
->willReturn('no');
|
||||
->willReturnMap([
|
||||
['core', 'shareapi_exclude_groups_list', '', ''],
|
||||
['core', 'shareapi_allow_group_sharing', 'yes', 'yes'],
|
||||
['core', 'shareapi_allow_links', 'yes', 'yes'],
|
||||
['core', 'shareapi_allow_public_upload', 'yes', 'yes'],
|
||||
['core', 'shareapi_allow_resharing', 'yes', 'yes'],
|
||||
['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
|
||||
['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no'],
|
||||
['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'no'],
|
||||
['core', 'shareapi_restrict_user_enumeration_full_match', 'yes', 'yes'],
|
||||
['core', 'shareapi_enabled', 'yes', 'yes'],
|
||||
['core', 'shareapi_default_expire_date', 'no', 'no'],
|
||||
['core', 'shareapi_expire_after_n_days', '7', '7'],
|
||||
['core', 'shareapi_enforce_expire_date', 'no', 'no'],
|
||||
['core', 'shareapi_exclude_groups', 'no', 'no'],
|
||||
['core', 'shareapi_public_link_disclaimertext', null, 'Lorem ipsum'],
|
||||
['core', 'shareapi_enable_link_password_by_default', 'no', 'yes'],
|
||||
['core', 'shareapi_default_permissions', Constants::PERMISSION_ALL, Constants::PERMISSION_ALL],
|
||||
['core', 'shareapi_default_internal_expire_date', 'no', 'no'],
|
||||
['core', 'shareapi_internal_expire_after_n_days', '7', '7'],
|
||||
['core', 'shareapi_enforce_internal_expire_date', 'no', 'no'],
|
||||
]);
|
||||
|
||||
$expected = new TemplateResponse(
|
||||
'settings',
|
||||
|
@ -164,6 +98,8 @@ class SharingTest extends TestCase {
|
|||
'allowResharing' => 'yes',
|
||||
'allowShareDialogUserEnumeration' => 'yes',
|
||||
'restrictUserEnumerationToGroup' => 'no',
|
||||
'restrictUserEnumerationToPhone' => 'no',
|
||||
'restrictUserEnumerationFullMatch' => 'yes',
|
||||
'enforceLinkPassword' => false,
|
||||
'onlyShareWithGroupMembers' => false,
|
||||
'shareAPIEnabled' => 'yes',
|
||||
|
@ -188,96 +124,29 @@ class SharingTest extends TestCase {
|
|||
|
||||
public function testGetFormWithExcludedGroups() {
|
||||
$this->config
|
||||
->expects($this->at(0))
|
||||
->method('getAppValue')
|
||||
->with('core', 'shareapi_exclude_groups_list', '')
|
||||
->willReturn('["NoSharers","OtherNoSharers"]');
|
||||
$this->config
|
||||
->expects($this->at(1))
|
||||
->method('getAppValue')
|
||||
->with('core', 'shareapi_allow_group_sharing', 'yes')
|
||||
->willReturn('yes');
|
||||
$this->config
|
||||
->expects($this->at(2))
|
||||
->method('getAppValue')
|
||||
->with('core', 'shareapi_allow_links', 'yes')
|
||||
->willReturn('yes');
|
||||
$this->config
|
||||
->expects($this->at(3))
|
||||
->method('getAppValue')
|
||||
->with('core', 'shareapi_allow_public_upload', 'yes')
|
||||
->willReturn('yes');
|
||||
$this->config
|
||||
->expects($this->at(4))
|
||||
->method('getAppValue')
|
||||
->with('core', 'shareapi_allow_resharing', 'yes')
|
||||
->willReturn('yes');
|
||||
$this->config
|
||||
->expects($this->at(5))
|
||||
->method('getAppValue')
|
||||
->with('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes')
|
||||
->willReturn('yes');
|
||||
$this->config
|
||||
->expects($this->at(6))
|
||||
->method('getAppValue')
|
||||
->with('core', 'shareapi_restrict_user_enumeration_to_group', 'no')
|
||||
->willReturn('no');
|
||||
$this->config
|
||||
->expects($this->at(7))
|
||||
->method('getAppValue')
|
||||
->with('core', 'shareapi_enabled', 'yes')
|
||||
->willReturn('yes');
|
||||
$this->config
|
||||
->expects($this->at(8))
|
||||
->method('getAppValue')
|
||||
->with('core', 'shareapi_default_expire_date', 'no')
|
||||
->willReturn('no');
|
||||
$this->config
|
||||
->expects($this->at(9))
|
||||
->method('getAppValue')
|
||||
->with('core', 'shareapi_expire_after_n_days', '7')
|
||||
->willReturn('7');
|
||||
$this->config
|
||||
->expects($this->at(10))
|
||||
->method('getAppValue')
|
||||
->with('core', 'shareapi_enforce_expire_date', 'no')
|
||||
->willReturn('no');
|
||||
$this->config
|
||||
->expects($this->at(11))
|
||||
->method('getAppValue')
|
||||
->with('core', 'shareapi_exclude_groups', 'no')
|
||||
->willReturn('yes');
|
||||
$this->config
|
||||
->expects($this->at(12))
|
||||
->method('getAppValue')
|
||||
->with('core', 'shareapi_public_link_disclaimertext', null)
|
||||
->willReturn('Lorem ipsum');
|
||||
$this->config
|
||||
->expects($this->at(13))
|
||||
->method('getAppValue')
|
||||
->with('core', 'shareapi_enable_link_password_by_default', 'no')
|
||||
->willReturn('yes');
|
||||
$this->config
|
||||
->expects($this->at(14))
|
||||
->method('getAppValue')
|
||||
->with('core', 'shareapi_default_permissions', Constants::PERMISSION_ALL)
|
||||
->willReturn(Constants::PERMISSION_ALL);
|
||||
$this->config
|
||||
->expects($this->at(15))
|
||||
->method('getAppValue')
|
||||
->with('core', 'shareapi_default_internal_expire_date', 'no')
|
||||
->willReturn('no');
|
||||
$this->config
|
||||
->expects($this->at(16))
|
||||
->method('getAppValue')
|
||||
->with('core', 'shareapi_internal_expire_after_n_days', '7')
|
||||
->willReturn('7');
|
||||
$this->config
|
||||
->expects($this->at(17))
|
||||
->method('getAppValue')
|
||||
->with('core', 'shareapi_enforce_internal_expire_date', 'no')
|
||||
->willReturn('no');
|
||||
|
||||
->willReturnMap([
|
||||
['core', 'shareapi_exclude_groups_list', '', '["NoSharers","OtherNoSharers"]'],
|
||||
['core', 'shareapi_allow_group_sharing', 'yes', 'yes'],
|
||||
['core', 'shareapi_allow_links', 'yes', 'yes'],
|
||||
['core', 'shareapi_allow_public_upload', 'yes', 'yes'],
|
||||
['core', 'shareapi_allow_resharing', 'yes', 'yes'],
|
||||
['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
|
||||
['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no'],
|
||||
['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'no'],
|
||||
['core', 'shareapi_restrict_user_enumeration_full_match', 'yes', 'yes'],
|
||||
['core', 'shareapi_enabled', 'yes', 'yes'],
|
||||
['core', 'shareapi_default_expire_date', 'no', 'no'],
|
||||
['core', 'shareapi_expire_after_n_days', '7', '7'],
|
||||
['core', 'shareapi_enforce_expire_date', 'no', 'no'],
|
||||
['core', 'shareapi_exclude_groups', 'no', 'yes'],
|
||||
['core', 'shareapi_public_link_disclaimertext', null, 'Lorem ipsum'],
|
||||
['core', 'shareapi_enable_link_password_by_default', 'no', 'yes'],
|
||||
['core', 'shareapi_default_permissions', Constants::PERMISSION_ALL, Constants::PERMISSION_ALL],
|
||||
['core', 'shareapi_default_internal_expire_date', 'no', 'no'],
|
||||
['core', 'shareapi_internal_expire_after_n_days', '7', '7'],
|
||||
['core', 'shareapi_enforce_internal_expire_date', 'no', 'no'],
|
||||
]);
|
||||
|
||||
$expected = new TemplateResponse(
|
||||
'settings',
|
||||
|
@ -289,6 +158,8 @@ class SharingTest extends TestCase {
|
|||
'allowResharing' => 'yes',
|
||||
'allowShareDialogUserEnumeration' => 'yes',
|
||||
'restrictUserEnumerationToGroup' => 'no',
|
||||
'restrictUserEnumerationToPhone' => 'no',
|
||||
'restrictUserEnumerationFullMatch' => 'yes',
|
||||
'enforceLinkPassword' => false,
|
||||
'onlyShareWithGroupMembers' => false,
|
||||
'shareAPIEnabled' => 'yes',
|
||||
|
|
|
@ -0,0 +1,218 @@
|
|||
Feature: autocomplete
|
||||
Background:
|
||||
Given using api version "2"
|
||||
And group "commongroup" exists
|
||||
And user "admin" belongs to group "commongroup"
|
||||
And user "auto" exists
|
||||
And user "autocomplete" exists
|
||||
And user "autocomplete2" exists
|
||||
And user "autocomplete2" belongs to group "commongroup"
|
||||
|
||||
Scenario: getting autocomplete
|
||||
Given As an "admin"
|
||||
When get autocomplete for "auto"
|
||||
| id | source |
|
||||
| auto | users |
|
||||
| autocomplete | users |
|
||||
| autocomplete2 | users |
|
||||
When parameter "shareapi_restrict_user_enumeration_full_match" of app "core" is set to "no"
|
||||
Then get autocomplete for "auto"
|
||||
| id | source |
|
||||
| auto | users |
|
||||
| autocomplete | users |
|
||||
| autocomplete2 | users |
|
||||
|
||||
|
||||
Scenario: getting autocomplete without enumeration
|
||||
Given As an "admin"
|
||||
When parameter "shareapi_allow_share_dialog_user_enumeration" of app "core" is set to "no"
|
||||
Then get autocomplete for "auto"
|
||||
| id | source |
|
||||
| auto | users |
|
||||
Then get autocomplete for "autocomplete"
|
||||
| id | source |
|
||||
| autocomplete | users |
|
||||
When parameter "shareapi_restrict_user_enumeration_full_match" of app "core" is set to "no"
|
||||
Then get autocomplete for "auto"
|
||||
| id | source |
|
||||
Then get autocomplete for "autocomplete"
|
||||
| id | source |
|
||||
|
||||
|
||||
Scenario: getting autocomplete with limited enumeration by group
|
||||
Given As an "admin"
|
||||
When parameter "shareapi_restrict_user_enumeration_to_group" of app "core" is set to "yes"
|
||||
Then get autocomplete for "auto"
|
||||
| id | source |
|
||||
| auto | users |
|
||||
| autocomplete2 | users |
|
||||
Then get autocomplete for "autocomplete"
|
||||
| id | source |
|
||||
| autocomplete | users |
|
||||
| autocomplete2 | users |
|
||||
Then get autocomplete for "autocomplete2"
|
||||
| id | source |
|
||||
| autocomplete2 | users |
|
||||
When parameter "shareapi_restrict_user_enumeration_full_match" of app "core" is set to "no"
|
||||
Then get autocomplete for "autocomplete"
|
||||
| id | source |
|
||||
| autocomplete2 | users |
|
||||
Then get autocomplete for "autocomplete2"
|
||||
| id | source |
|
||||
| autocomplete2 | users |
|
||||
|
||||
|
||||
Scenario: getting autocomplete with limited enumeration by phone
|
||||
Given As an "admin"
|
||||
When parameter "shareapi_restrict_user_enumeration_to_phone" of app "core" is set to "yes"
|
||||
Then get autocomplete for "auto"
|
||||
| id | source |
|
||||
| auto | users |
|
||||
|
||||
# autocomplete stores their phone number
|
||||
Given As an "autocomplete"
|
||||
And sending "PUT" to "/cloud/users/autocomplete" with
|
||||
| key | phone |
|
||||
| value | +49 711 / 25 24 28-90 |
|
||||
And the HTTP status code should be "200"
|
||||
And the OCS status code should be "200"
|
||||
|
||||
Given As an "admin"
|
||||
Then get autocomplete for "auto"
|
||||
| id | source |
|
||||
| auto | users |
|
||||
|
||||
# admin populates they have the phone number
|
||||
When search users by phone for region "DE" with
|
||||
| random-string1 | 0711 / 252 428-90 |
|
||||
Then get autocomplete for "auto"
|
||||
| id | source |
|
||||
| auto | users |
|
||||
| autocomplete | users |
|
||||
|
||||
When parameter "shareapi_restrict_user_enumeration_full_match" of app "core" is set to "no"
|
||||
Then get autocomplete for "auto"
|
||||
| id | source |
|
||||
| autocomplete | users |
|
||||
|
||||
|
||||
Scenario: getting autocomplete with limited enumeration by group or phone
|
||||
Given As an "admin"
|
||||
When parameter "shareapi_restrict_user_enumeration_to_group" of app "core" is set to "yes"
|
||||
And parameter "shareapi_restrict_user_enumeration_to_phone" of app "core" is set to "yes"
|
||||
|
||||
# autocomplete stores their phone number
|
||||
Given As an "autocomplete"
|
||||
And sending "PUT" to "/cloud/users/autocomplete" with
|
||||
| key | phone |
|
||||
| value | +49 711 / 25 24 28-90 |
|
||||
And the HTTP status code should be "200"
|
||||
And the OCS status code should be "200"
|
||||
# admin populates they have the phone number
|
||||
Given As an "admin"
|
||||
When search users by phone for region "DE" with
|
||||
| random-string1 | 0711 / 252 428-90 |
|
||||
|
||||
Then get autocomplete for "auto"
|
||||
| id | source |
|
||||
| auto | users |
|
||||
| autocomplete | users |
|
||||
| autocomplete2 | users |
|
||||
|
||||
When parameter "shareapi_restrict_user_enumeration_full_match" of app "core" is set to "no"
|
||||
Then get autocomplete for "auto"
|
||||
| id | source |
|
||||
| autocomplete | users |
|
||||
| autocomplete2 | users |
|
||||
|
||||
|
||||
Scenario: getting autocomplete with limited enumeration but sharing is group restricted
|
||||
Given As an "admin"
|
||||
When parameter "shareapi_restrict_user_enumeration_to_group" of app "core" is set to "yes"
|
||||
And parameter "shareapi_restrict_user_enumeration_to_phone" of app "core" is set to "yes"
|
||||
|
||||
# autocomplete stores their phone number
|
||||
Given As an "autocomplete"
|
||||
And sending "PUT" to "/cloud/users/autocomplete" with
|
||||
| key | phone |
|
||||
| value | +49 711 / 25 24 28-90 |
|
||||
And the HTTP status code should be "200"
|
||||
And the OCS status code should be "200"
|
||||
# admin populates they have the phone number
|
||||
Given As an "admin"
|
||||
When search users by phone for region "DE" with
|
||||
| random-string1 | 0711 / 252 428-90 |
|
||||
|
||||
Then get autocomplete for "auto"
|
||||
| id | source |
|
||||
| auto | users |
|
||||
| autocomplete | users |
|
||||
| autocomplete2 | users |
|
||||
When parameter "shareapi_only_share_with_group_members" of app "core" is set to "yes"
|
||||
Then get autocomplete for "auto"
|
||||
| id | source |
|
||||
| autocomplete2 | users |
|
||||
|
||||
|
||||
Scenario: getting autocomplete with limited enumeration by phone but user changes it
|
||||
Given As an "admin"
|
||||
When parameter "shareapi_restrict_user_enumeration_to_phone" of app "core" is set to "yes"
|
||||
Then get autocomplete for "auto"
|
||||
| id | source |
|
||||
| auto | users |
|
||||
|
||||
# autocomplete stores their phone number
|
||||
Given As an "autocomplete"
|
||||
And sending "PUT" to "/cloud/users/autocomplete" with
|
||||
| key | phone |
|
||||
| value | +49 711 / 25 24 28-90 |
|
||||
And the HTTP status code should be "200"
|
||||
And the OCS status code should be "200"
|
||||
|
||||
Given As an "admin"
|
||||
Then get autocomplete for "auto"
|
||||
| id | source |
|
||||
| auto | users |
|
||||
|
||||
# admin populates they have the phone number
|
||||
When search users by phone for region "DE" with
|
||||
| random-string1 | 0711 / 252 428-90 |
|
||||
Then get autocomplete for "auto"
|
||||
| id | source |
|
||||
| auto | users |
|
||||
| autocomplete | users |
|
||||
|
||||
# autocomplete changes their phone number
|
||||
Given As an "autocomplete"
|
||||
And sending "PUT" to "/cloud/users/autocomplete" with
|
||||
| key | phone |
|
||||
| value | +49 711 / 25 24 28-91 |
|
||||
And the HTTP status code should be "200"
|
||||
And the OCS status code should be "200"
|
||||
|
||||
Given As an "admin"
|
||||
Then get autocomplete for "auto"
|
||||
| id | source |
|
||||
| auto | users |
|
||||
|
||||
# admin populates they have the new phone number
|
||||
When search users by phone for region "DE" with
|
||||
| random-string1 | 0711 / 252 428-91 |
|
||||
Then get autocomplete for "auto"
|
||||
| id | source |
|
||||
| auto | users |
|
||||
| autocomplete | users |
|
||||
|
||||
|
||||
Scenario: getting autocomplete without enumeration and sharing is group restricted
|
||||
Given As an "admin"
|
||||
When parameter "shareapi_allow_share_dialog_user_enumeration" of app "core" is set to "no"
|
||||
And parameter "shareapi_only_share_with_group_members" of app "core" is set to "yes"
|
||||
|
||||
Then get autocomplete for "auto"
|
||||
| id | source |
|
||||
Then get autocomplete for "autocomplete"
|
||||
| id | source |
|
||||
Then get autocomplete for "autocomplete2"
|
||||
| id | source |
|
||||
| autocomplete2 | users |
|
|
@ -45,6 +45,16 @@ default:
|
|||
- admin
|
||||
- admin
|
||||
regular_user_password: 123456
|
||||
collaboration:
|
||||
paths:
|
||||
- "%paths.base%/../collaboration_features"
|
||||
contexts:
|
||||
- CollaborationContext:
|
||||
baseUrl: http://localhost:8080/ocs/
|
||||
admin:
|
||||
- admin
|
||||
- admin
|
||||
regular_user_password: 123456
|
||||
sharees:
|
||||
paths:
|
||||
- "%paths.base%/../sharees_features"
|
||||
|
|
|
@ -202,6 +202,40 @@ trait BasicStructure {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $verb
|
||||
* @param string $url
|
||||
* @param TableNode|array|null $body
|
||||
* @param array $headers
|
||||
*/
|
||||
protected function sendRequestForJSON(string $verb, string $url, $body = null, array $headers = []): void {
|
||||
$fullUrl = $this->baseUrl . "v{$this->apiVersion}.php" . $url;
|
||||
$client = new Client();
|
||||
$options = [];
|
||||
if ($this->currentUser === 'admin') {
|
||||
$options['auth'] = ['admin', 'admin'];
|
||||
} elseif (strpos($this->currentUser, 'guest') !== 0) {
|
||||
$options['auth'] = [$this->currentUser, self::TEST_PASSWORD];
|
||||
}
|
||||
if ($body instanceof TableNode) {
|
||||
$fd = $body->getRowsHash();
|
||||
$options['form_params'] = $fd;
|
||||
} elseif (is_array($body)) {
|
||||
$options['form_params'] = $body;
|
||||
}
|
||||
|
||||
$options['headers'] = array_merge($headers, [
|
||||
'OCS-ApiRequest' => 'true',
|
||||
'Accept' => 'application/json',
|
||||
]);
|
||||
|
||||
try {
|
||||
$this->response = $client->{$verb}($fullUrl, $options);
|
||||
} catch (ClientException $ex) {
|
||||
$this->response = $ex->getResponse();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^sending "([^"]*)" with exact url to "([^"]*)"$/
|
||||
* @param string $verb
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2021, Joas Schilling <coding@schilljs.com>
|
||||
*
|
||||
* @author Joas Schilling <coding@schilljs.com>
|
||||
*
|
||||
* @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/>
|
||||
*
|
||||
*/
|
||||
|
||||
use Behat\Behat\Context\Context;
|
||||
use Behat\Gherkin\Node\TableNode;
|
||||
use PHPUnit\Framework\Assert;
|
||||
|
||||
require __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
class CollaborationContext implements Context {
|
||||
use Provisioning;
|
||||
use AppConfiguration;
|
||||
|
||||
/**
|
||||
* @Then /^get autocomplete for "([^"]*)"$/
|
||||
* @param TableNode|null $formData
|
||||
*/
|
||||
public function getAutocomplete(string $search, TableNode $formData): void {
|
||||
$query = $search === 'null' ? null : $search;
|
||||
|
||||
$this->sendRequestForJSON('GET', '/core/autocomplete/get?itemType=files&itemId=123&search=' . $query, [
|
||||
'itemType' => 'files',
|
||||
'itemId' => '123',
|
||||
'search' => $query,
|
||||
]);
|
||||
$this->theHTTPStatusCodeShouldBe(200);
|
||||
|
||||
$data = json_decode($this->response->getBody()->getContents(), true);
|
||||
$suggestions = $data['ocs']['data'];
|
||||
|
||||
Assert::assertCount(count($formData->getHash()), $suggestions, 'Suggestion count does not match');
|
||||
Assert::assertEquals($formData->getHash(), array_map(static function ($suggestion, $expected) {
|
||||
$data = [];
|
||||
if (isset($expected['id'])) {
|
||||
$data['id'] = $suggestion['id'];
|
||||
}
|
||||
if (isset($expected['source'])) {
|
||||
$data['source'] = $suggestion['source'];
|
||||
}
|
||||
return $data;
|
||||
}, $suggestions, $formData->getHash()));
|
||||
}
|
||||
|
||||
protected function resetAppConfigs(): void {
|
||||
$this->deleteServerConfig('core', 'shareapi_allow_share_dialog_user_enumeration');
|
||||
$this->deleteServerConfig('core', 'shareapi_restrict_user_enumeration_to_group');
|
||||
$this->deleteServerConfig('core', 'shareapi_restrict_user_enumeration_to_phone');
|
||||
$this->deleteServerConfig('core', 'shareapi_restrict_user_enumeration_full_match');
|
||||
$this->deleteServerConfig('core', 'shareapi_only_share_with_group_members');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2021 Joas Schilling <coding@schilljs.com>
|
||||
*
|
||||
* @author Joas Schilling <coding@schilljs.com>
|
||||
*
|
||||
* @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\Core\Migrations;
|
||||
|
||||
use Closure;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use OCP\DB\ISchemaWrapper;
|
||||
use OCP\Migration\IOutput;
|
||||
use OCP\Migration\SimpleMigrationStep;
|
||||
|
||||
class Version21000Date20210309185126 extends SimpleMigrationStep {
|
||||
/**
|
||||
* @param IOutput $output
|
||||
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
|
||||
* @param array $options
|
||||
* @return null|ISchemaWrapper
|
||||
*/
|
||||
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
|
||||
/** @var ISchemaWrapper $schema */
|
||||
$schema = $schemaClosure();
|
||||
|
||||
if (!$schema->hasTable('known_users')) {
|
||||
$table = $schema->createTable('known_users');
|
||||
|
||||
// Auto increment id
|
||||
$table->addColumn('id', Types::BIGINT, [
|
||||
'autoincrement' => true,
|
||||
'notnull' => true,
|
||||
]);
|
||||
|
||||
$table->addColumn('known_to', Types::STRING, [
|
||||
'notnull' => true,
|
||||
'length' => 255,
|
||||
]);
|
||||
$table->addColumn('known_user', Types::STRING, [
|
||||
'notnull' => true,
|
||||
'length' => 255,
|
||||
]);
|
||||
|
||||
$table->setPrimaryKey(['id']);
|
||||
$table->addIndex(['known_to'], 'ku_known_to');
|
||||
return $schema;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -948,6 +948,7 @@ return array(
|
|||
'OC\\Core\\Migrations\\Version21000Date20201120141228' => $baseDir . '/core/Migrations/Version21000Date20201120141228.php',
|
||||
'OC\\Core\\Migrations\\Version21000Date20201202095923' => $baseDir . '/core/Migrations/Version21000Date20201202095923.php',
|
||||
'OC\\Core\\Migrations\\Version21000Date20210119195004' => $baseDir . '/core/Migrations/Version21000Date20210119195004.php',
|
||||
'OC\\Core\\Migrations\\Version21000Date20210309185126' => $baseDir . '/core/Migrations/Version21000Date20210309185126.php',
|
||||
'OC\\Core\\Migrations\\Version22000Date20210216080825' => $baseDir . '/core/Migrations/Version22000Date20210216080825.php',
|
||||
'OC\\Core\\Notification\\CoreNotifier' => $baseDir . '/core/Notification/CoreNotifier.php',
|
||||
'OC\\Core\\Service\\LoginFlowV2Service' => $baseDir . '/core/Service/LoginFlowV2Service.php',
|
||||
|
@ -1165,6 +1166,9 @@ return array(
|
|||
'OC\\IntegrityCheck\\Helpers\\FileAccessHelper' => $baseDir . '/lib/private/IntegrityCheck/Helpers/FileAccessHelper.php',
|
||||
'OC\\IntegrityCheck\\Iterator\\ExcludeFileByNameFilterIterator' => $baseDir . '/lib/private/IntegrityCheck/Iterator/ExcludeFileByNameFilterIterator.php',
|
||||
'OC\\IntegrityCheck\\Iterator\\ExcludeFoldersByPathFilterIterator' => $baseDir . '/lib/private/IntegrityCheck/Iterator/ExcludeFoldersByPathFilterIterator.php',
|
||||
'OC\\KnownUser\\KnownUser' => $baseDir . '/lib/private/KnownUser/KnownUser.php',
|
||||
'OC\\KnownUser\\KnownUserMapper' => $baseDir . '/lib/private/KnownUser/KnownUserMapper.php',
|
||||
'OC\\KnownUser\\KnownUserService' => $baseDir . '/lib/private/KnownUser/KnownUserService.php',
|
||||
'OC\\L10N\\Factory' => $baseDir . '/lib/private/L10N/Factory.php',
|
||||
'OC\\L10N\\L10N' => $baseDir . '/lib/private/L10N/L10N.php',
|
||||
'OC\\L10N\\L10NString' => $baseDir . '/lib/private/L10N/L10NString.php',
|
||||
|
|
|
@ -977,6 +977,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
|
|||
'OC\\Core\\Migrations\\Version21000Date20201120141228' => __DIR__ . '/../../..' . '/core/Migrations/Version21000Date20201120141228.php',
|
||||
'OC\\Core\\Migrations\\Version21000Date20201202095923' => __DIR__ . '/../../..' . '/core/Migrations/Version21000Date20201202095923.php',
|
||||
'OC\\Core\\Migrations\\Version21000Date20210119195004' => __DIR__ . '/../../..' . '/core/Migrations/Version21000Date20210119195004.php',
|
||||
'OC\\Core\\Migrations\\Version21000Date20210309185126' => __DIR__ . '/../../..' . '/core/Migrations/Version21000Date20210309185126.php',
|
||||
'OC\\Core\\Migrations\\Version22000Date20210216080825' => __DIR__ . '/../../..' . '/core/Migrations/Version22000Date20210216080825.php',
|
||||
'OC\\Core\\Notification\\CoreNotifier' => __DIR__ . '/../../..' . '/core/Notification/CoreNotifier.php',
|
||||
'OC\\Core\\Service\\LoginFlowV2Service' => __DIR__ . '/../../..' . '/core/Service/LoginFlowV2Service.php',
|
||||
|
@ -1194,6 +1195,9 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
|
|||
'OC\\IntegrityCheck\\Helpers\\FileAccessHelper' => __DIR__ . '/../../..' . '/lib/private/IntegrityCheck/Helpers/FileAccessHelper.php',
|
||||
'OC\\IntegrityCheck\\Iterator\\ExcludeFileByNameFilterIterator' => __DIR__ . '/../../..' . '/lib/private/IntegrityCheck/Iterator/ExcludeFileByNameFilterIterator.php',
|
||||
'OC\\IntegrityCheck\\Iterator\\ExcludeFoldersByPathFilterIterator' => __DIR__ . '/../../..' . '/lib/private/IntegrityCheck/Iterator/ExcludeFoldersByPathFilterIterator.php',
|
||||
'OC\\KnownUser\\KnownUser' => __DIR__ . '/../../..' . '/lib/private/KnownUser/KnownUser.php',
|
||||
'OC\\KnownUser\\KnownUserMapper' => __DIR__ . '/../../..' . '/lib/private/KnownUser/KnownUserMapper.php',
|
||||
'OC\\KnownUser\\KnownUserService' => __DIR__ . '/../../..' . '/lib/private/KnownUser/KnownUserService.php',
|
||||
'OC\\L10N\\Factory' => __DIR__ . '/../../..' . '/lib/private/L10N/Factory.php',
|
||||
'OC\\L10N\\L10N' => __DIR__ . '/../../..' . '/lib/private/L10N/L10N.php',
|
||||
'OC\\L10N\\L10NString' => __DIR__ . '/../../..' . '/lib/private/L10N/L10NString.php',
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
namespace OC\Collaboration\Collaborators;
|
||||
|
||||
use OC\KnownUser\KnownUserService;
|
||||
use OCP\Collaboration\Collaborators\ISearchPlugin;
|
||||
use OCP\Collaboration\Collaborators\ISearchResult;
|
||||
use OCP\Collaboration\Collaborators\SearchResultType;
|
||||
|
@ -40,8 +41,16 @@ use OCP\IUserSession;
|
|||
use OCP\Share\IShare;
|
||||
|
||||
class MailPlugin implements ISearchPlugin {
|
||||
protected $shareeEnumeration;
|
||||
/* @var bool */
|
||||
protected $shareWithGroupOnly;
|
||||
/* @var bool */
|
||||
protected $shareeEnumeration;
|
||||
/* @var bool */
|
||||
protected $shareeEnumerationInGroupOnly;
|
||||
/* @var bool */
|
||||
protected $shareeEnumerationPhone;
|
||||
/* @var bool */
|
||||
protected $shareeEnumerationFullMatch;
|
||||
|
||||
/** @var IManager */
|
||||
private $contactsManager;
|
||||
|
@ -52,20 +61,29 @@ class MailPlugin implements ISearchPlugin {
|
|||
|
||||
/** @var IGroupManager */
|
||||
private $groupManager;
|
||||
|
||||
/** @var KnownUserService */
|
||||
private $knownUserService;
|
||||
/** @var IUserSession */
|
||||
private $userSession;
|
||||
|
||||
public function __construct(IManager $contactsManager, ICloudIdManager $cloudIdManager, IConfig $config, IGroupManager $groupManager, IUserSession $userSession) {
|
||||
public function __construct(IManager $contactsManager,
|
||||
ICloudIdManager $cloudIdManager,
|
||||
IConfig $config,
|
||||
IGroupManager $groupManager,
|
||||
KnownUserService $knownUserService,
|
||||
IUserSession $userSession) {
|
||||
$this->contactsManager = $contactsManager;
|
||||
$this->cloudIdManager = $cloudIdManager;
|
||||
$this->config = $config;
|
||||
$this->groupManager = $groupManager;
|
||||
$this->knownUserService = $knownUserService;
|
||||
$this->userSession = $userSession;
|
||||
|
||||
$this->shareeEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
|
||||
$this->shareWithGroupOnly = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
|
||||
$this->shareeEnumerationInGroupOnly = $this->shareeEnumeration && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes';
|
||||
$this->shareeEnumerationPhone = $this->shareeEnumeration && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes';
|
||||
$this->shareeEnumerationFullMatch = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match', 'yes') === 'yes';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -77,6 +95,8 @@ class MailPlugin implements ISearchPlugin {
|
|||
* @since 13.0.0
|
||||
*/
|
||||
public function search($search, $limit, $offset, ISearchResult $searchResult) {
|
||||
$currentUserId = $this->userSession->getUser()->getUID();
|
||||
|
||||
$result = $userResults = ['wide' => [], 'exact' => []];
|
||||
$userType = new SearchResultType('users');
|
||||
$emailType = new SearchResultType('emails');
|
||||
|
@ -120,7 +140,7 @@ class MailPlugin implements ISearchPlugin {
|
|||
continue;
|
||||
}
|
||||
}
|
||||
if ($exactEmailMatch) {
|
||||
if ($exactEmailMatch && $this->shareeEnumerationFullMatch) {
|
||||
try {
|
||||
$cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0]);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
|
@ -152,8 +172,12 @@ class MailPlugin implements ISearchPlugin {
|
|||
continue;
|
||||
}
|
||||
|
||||
$addToWide = !$this->shareeEnumerationInGroupOnly;
|
||||
if ($this->shareeEnumerationInGroupOnly) {
|
||||
$addToWide = !($this->shareeEnumerationInGroupOnly || $this->shareeEnumerationPhone);
|
||||
if (!$addToWide && $this->shareeEnumerationPhone && $this->knownUserService->isKnownToUser($currentUserId, $contact['UID'])) {
|
||||
$addToWide = true;
|
||||
}
|
||||
|
||||
if (!$addToWide && $this->shareeEnumerationInGroupOnly) {
|
||||
$addToWide = false;
|
||||
$userGroups = $this->groupManager->getUserGroupIds($this->userSession->getUser());
|
||||
foreach ($userGroups as $userGroup) {
|
||||
|
@ -181,7 +205,7 @@ class MailPlugin implements ISearchPlugin {
|
|||
}
|
||||
|
||||
if ($exactEmailMatch
|
||||
|| isset($contact['FN']) && strtolower($contact['FN']) === $lowerSearch) {
|
||||
|| (isset($contact['FN']) && strtolower($contact['FN']) === $lowerSearch)) {
|
||||
if ($exactEmailMatch) {
|
||||
$searchResult->markExactIdMatch($emailType);
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
|
||||
namespace OC\Collaboration\Collaborators;
|
||||
|
||||
use OC\KnownUser\KnownUserService;
|
||||
use OCP\Collaboration\Collaborators\ISearchPlugin;
|
||||
use OCP\Collaboration\Collaborators\ISearchResult;
|
||||
use OCP\Collaboration\Collaborators\SearchResultType;
|
||||
|
@ -46,8 +47,14 @@ use OCP\UserStatus\IManager as IUserStatusManager;
|
|||
class UserPlugin implements ISearchPlugin {
|
||||
/* @var bool */
|
||||
protected $shareWithGroupOnly;
|
||||
/* @var bool */
|
||||
protected $shareeEnumeration;
|
||||
/* @var bool */
|
||||
protected $shareeEnumerationInGroupOnly;
|
||||
/* @var bool */
|
||||
protected $shareeEnumerationPhone;
|
||||
/* @var bool */
|
||||
protected $shareeEnumerationFullMatch;
|
||||
|
||||
/** @var IConfig */
|
||||
private $config;
|
||||
|
@ -57,33 +64,30 @@ class UserPlugin implements ISearchPlugin {
|
|||
private $userSession;
|
||||
/** @var IUserManager */
|
||||
private $userManager;
|
||||
/** @var KnownUserService */
|
||||
private $knownUserService;
|
||||
/** @var IUserStatusManager */
|
||||
private $userStatusManager;
|
||||
|
||||
/**
|
||||
* UserPlugin constructor.
|
||||
*
|
||||
* @param IConfig $config
|
||||
* @param IUserManager $userManager
|
||||
* @param IGroupManager $groupManager
|
||||
* @param IUserSession $userSession
|
||||
* @param IUserStatusManager $userStatusManager
|
||||
*/
|
||||
public function __construct(IConfig $config,
|
||||
IUserManager $userManager,
|
||||
IGroupManager $groupManager,
|
||||
IUserSession $userSession,
|
||||
KnownUserService $knownUserService,
|
||||
IUserStatusManager $userStatusManager) {
|
||||
$this->config = $config;
|
||||
|
||||
$this->groupManager = $groupManager;
|
||||
$this->userSession = $userSession;
|
||||
$this->userManager = $userManager;
|
||||
$this->knownUserService = $knownUserService;
|
||||
$this->userStatusManager = $userStatusManager;
|
||||
|
||||
$this->shareWithGroupOnly = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
|
||||
$this->shareeEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
|
||||
$this->shareeEnumerationInGroupOnly = $this->shareeEnumeration && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes';
|
||||
$this->shareeEnumerationPhone = $this->shareeEnumeration && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes';
|
||||
$this->shareeEnumerationFullMatch = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match', 'yes') === 'yes';
|
||||
}
|
||||
|
||||
public function search($search, $limit, $offset, ISearchResult $searchResult) {
|
||||
|
@ -91,6 +95,7 @@ class UserPlugin implements ISearchPlugin {
|
|||
$users = [];
|
||||
$hasMoreResults = false;
|
||||
|
||||
$currentUserId = $this->userSession->getUser()->getUID();
|
||||
$currentUserGroups = $this->groupManager->getUserGroupIds($this->userSession->getUser());
|
||||
if ($this->shareWithGroupOnly) {
|
||||
// Search in all the groups this user is part of
|
||||
|
@ -148,6 +153,7 @@ class UserPlugin implements ISearchPlugin {
|
|||
|
||||
|
||||
if (
|
||||
$this->shareeEnumerationFullMatch &&
|
||||
$lowerSearch !== '' && (strtolower($uid) === $lowerSearch ||
|
||||
strtolower($userDisplayName) === $lowerSearch ||
|
||||
strtolower($userEmail) === $lowerSearch)
|
||||
|
@ -168,11 +174,16 @@ class UserPlugin implements ISearchPlugin {
|
|||
];
|
||||
} else {
|
||||
$addToWideResults = false;
|
||||
if ($this->shareeEnumeration && !$this->shareeEnumerationInGroupOnly) {
|
||||
if ($this->shareeEnumeration &&
|
||||
!($this->shareeEnumerationInGroupOnly || $this->shareeEnumerationPhone)) {
|
||||
$addToWideResults = true;
|
||||
}
|
||||
|
||||
if ($this->shareeEnumerationInGroupOnly) {
|
||||
if ($this->shareeEnumerationPhone && $this->knownUserService->isKnownToUser($currentUserId, $user->getUID())) {
|
||||
$addToWideResults = true;
|
||||
}
|
||||
|
||||
if (!$addToWideResults && $this->shareeEnumerationInGroupOnly) {
|
||||
$commonGroups = array_intersect($currentUserGroups, $this->groupManager->getUserGroupIds($user));
|
||||
if (!empty($commonGroups)) {
|
||||
$addToWideResults = true;
|
||||
|
@ -195,7 +206,7 @@ class UserPlugin implements ISearchPlugin {
|
|||
}
|
||||
}
|
||||
|
||||
if ($offset === 0 && !$foundUserById) {
|
||||
if ($this->shareeEnumerationFullMatch && $offset === 0 && !$foundUserById) {
|
||||
// On page one we try if the search result has a direct hit on the
|
||||
// user id and if so, we add that to the exact match list
|
||||
$user = $this->userManager->get($search);
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
|
||||
namespace OC\Contacts\ContactsMenu;
|
||||
|
||||
use OC\KnownUser\KnownUserService;
|
||||
use OCP\Contacts\ContactsMenu\IContactsStore;
|
||||
use OCP\Contacts\ContactsMenu\IEntry;
|
||||
use OCP\Contacts\IManager;
|
||||
|
@ -53,20 +54,19 @@ class ContactsStore implements IContactsStore {
|
|||
/** @var IGroupManager */
|
||||
private $groupManager;
|
||||
|
||||
/**
|
||||
* @param IManager $contactsManager
|
||||
* @param IConfig $config
|
||||
* @param IUserManager $userManager
|
||||
* @param IGroupManager $groupManager
|
||||
*/
|
||||
/** @var KnownUserService */
|
||||
private $knownUserService;
|
||||
|
||||
public function __construct(IManager $contactsManager,
|
||||
IConfig $config,
|
||||
IUserManager $userManager,
|
||||
IGroupManager $groupManager) {
|
||||
IGroupManager $groupManager,
|
||||
KnownUserService $knownUserService) {
|
||||
$this->contactsManager = $contactsManager;
|
||||
$this->config = $config;
|
||||
$this->userManager = $userManager;
|
||||
$this->groupManager = $groupManager;
|
||||
$this->knownUserService = $knownUserService;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -103,7 +103,7 @@ class ContactsStore implements IContactsStore {
|
|||
}
|
||||
|
||||
/**
|
||||
* Filters the contacts. Applies 3 filters:
|
||||
* Filters the contacts. Applied filters:
|
||||
* 1. filter the current user
|
||||
* 2. if the `shareapi_allow_share_dialog_user_enumeration` config option is
|
||||
* enabled it will filter all local users
|
||||
|
@ -122,20 +122,22 @@ class ContactsStore implements IContactsStore {
|
|||
array $entries,
|
||||
$filter) {
|
||||
$disallowEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') !== 'yes';
|
||||
$restrictEnumeration = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes';
|
||||
$restrictEnumerationGroup = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes';
|
||||
$restrictEnumerationPhone = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes';
|
||||
$allowEnumerationFullMatch = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match', 'yes') === 'yes';
|
||||
$excludedGroups = $this->config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes';
|
||||
|
||||
// whether to filter out local users
|
||||
$skipLocal = false;
|
||||
// whether to filter out all users which doesn't have the same group as the current user
|
||||
$ownGroupsOnly = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes' || $restrictEnumeration;
|
||||
// whether to filter out all users which don't have a common group as the current user
|
||||
$ownGroupsOnly = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
|
||||
|
||||
$selfGroups = $this->groupManager->getUserGroupIds($self);
|
||||
|
||||
if ($excludedGroups) {
|
||||
$excludedGroups = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', '');
|
||||
$decodedExcludeGroups = json_decode($excludedGroups, true);
|
||||
$excludeGroupsList = ($decodedExcludeGroups !== null) ? $decodedExcludeGroups : [];
|
||||
$excludeGroupsList = $decodedExcludeGroups ?? [];
|
||||
|
||||
if (count(array_intersect($excludeGroupsList, $selfGroups)) !== 0) {
|
||||
// a group of the current user is excluded -> filter all local users
|
||||
|
@ -145,47 +147,80 @@ class ContactsStore implements IContactsStore {
|
|||
|
||||
$selfUID = $self->getUID();
|
||||
|
||||
return array_values(array_filter($entries, function (IEntry $entry) use ($self, $skipLocal, $ownGroupsOnly, $selfGroups, $selfUID, $disallowEnumeration, $filter) {
|
||||
if ($skipLocal && $entry->getProperty('isLocalSystemBook') === true) {
|
||||
return array_values(array_filter($entries, function (IEntry $entry) use ($skipLocal, $ownGroupsOnly, $selfGroups, $selfUID, $disallowEnumeration, $restrictEnumerationGroup, $restrictEnumerationPhone, $allowEnumerationFullMatch, $filter) {
|
||||
if ($entry->getProperty('UID') === $selfUID) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prevent enumerating local users
|
||||
if ($disallowEnumeration && $entry->getProperty('isLocalSystemBook')) {
|
||||
$filterUser = true;
|
||||
if ($entry->getProperty('isLocalSystemBook')) {
|
||||
if ($skipLocal) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$mailAddresses = $entry->getEMailAddresses();
|
||||
foreach ($mailAddresses as $mailAddress) {
|
||||
if ($mailAddress === $filter) {
|
||||
$filterUser = false;
|
||||
break;
|
||||
$checkedCommonGroupAlready = false;
|
||||
|
||||
// Prevent enumerating local users
|
||||
if ($disallowEnumeration) {
|
||||
if (!$allowEnumerationFullMatch) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$filterOutUser = true;
|
||||
|
||||
$mailAddresses = $entry->getEMailAddresses();
|
||||
foreach ($mailAddresses as $mailAddress) {
|
||||
if ($mailAddress === $filter) {
|
||||
$filterOutUser = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($entry->getProperty('UID') && $entry->getProperty('UID') === $filter) {
|
||||
$filterOutUser = false;
|
||||
}
|
||||
|
||||
if ($filterOutUser) {
|
||||
return false;
|
||||
}
|
||||
} elseif ($restrictEnumerationPhone || $restrictEnumerationGroup) {
|
||||
$canEnumerate = false;
|
||||
if ($restrictEnumerationPhone) {
|
||||
$canEnumerate = $this->knownUserService->isKnownToUser($selfUID, $entry->getProperty('UID'));
|
||||
}
|
||||
|
||||
if (!$canEnumerate && $restrictEnumerationGroup) {
|
||||
$user = $this->userManager->get($entry->getProperty('UID'));
|
||||
|
||||
if ($user === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$contactGroups = $this->groupManager->getUserGroupIds($user);
|
||||
$canEnumerate = !empty(array_intersect($contactGroups, $selfGroups));
|
||||
$checkedCommonGroupAlready = true;
|
||||
}
|
||||
|
||||
if (!$canEnumerate) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($entry->getProperty('UID') && $entry->getProperty('UID') === $filter) {
|
||||
$filterUser = false;
|
||||
}
|
||||
if ($ownGroupsOnly && !$checkedCommonGroupAlready) {
|
||||
$user = $this->userManager->get($entry->getProperty('UID'));
|
||||
|
||||
if ($filterUser) {
|
||||
return false;
|
||||
if (!$user instanceof IUser) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$contactGroups = $this->groupManager->getUserGroupIds($user);
|
||||
if (empty(array_intersect($contactGroups, $selfGroups))) {
|
||||
// no groups in common, so shouldn't see the contact
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($ownGroupsOnly && $entry->getProperty('isLocalSystemBook') === true) {
|
||||
$uid = $this->userManager->get($entry->getProperty('UID'));
|
||||
|
||||
if ($uid === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$contactGroups = $this->groupManager->getUserGroupIds($uid);
|
||||
if (count(array_intersect($contactGroups, $selfGroups)) === 0) {
|
||||
// no groups in common, so shouldn't see the contact
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return $entry->getProperty('UID') !== $selfUID;
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2021 Joas Schilling <coding@schilljs.com>
|
||||
*
|
||||
* @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\KnownUser;
|
||||
|
||||
use OCP\AppFramework\Db\Entity;
|
||||
|
||||
/**
|
||||
* @method void setKnownTo(string $knownTo)
|
||||
* @method string getKnownTo()
|
||||
* @method void setKnownUser(string $knownUser)
|
||||
* @method string getKnownUser()
|
||||
*/
|
||||
class KnownUser extends Entity {
|
||||
|
||||
/** @var string */
|
||||
protected $knownTo;
|
||||
|
||||
/** @var string */
|
||||
protected $knownUser;
|
||||
|
||||
public function __construct() {
|
||||
$this->addType('knownTo', 'string');
|
||||
$this->addType('knownUser', 'string');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2021 Joas Schilling <coding@schilljs.com>
|
||||
*
|
||||
* @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\KnownUser;
|
||||
|
||||
use OCP\AppFramework\Db\QBMapper;
|
||||
use OCP\IDBConnection;
|
||||
|
||||
/**
|
||||
* @method KnownUser mapRowToEntity(array $row)
|
||||
*/
|
||||
class KnownUserMapper extends QBMapper {
|
||||
|
||||
/**
|
||||
* @param IDBConnection $db
|
||||
*/
|
||||
public function __construct(IDBConnection $db) {
|
||||
parent::__construct($db, 'known_users', KnownUser::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $knownTo
|
||||
* @return int Number of deleted entities
|
||||
*/
|
||||
public function deleteKnownTo(string $knownTo): int {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->delete($this->getTableName())
|
||||
->where($query->expr()->eq('known_to', $query->createNamedParameter($knownTo)));
|
||||
|
||||
return (int) $query->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $knownUser
|
||||
* @return int Number of deleted entities
|
||||
*/
|
||||
public function deleteKnownUser(string $knownUser): int {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->delete($this->getTableName())
|
||||
->where($query->expr()->eq('known_user', $query->createNamedParameter($knownUser)));
|
||||
|
||||
return (int) $query->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all "known users" for the given "known to" user
|
||||
*
|
||||
* @param string $knownTo
|
||||
* @return KnownUser[]
|
||||
*/
|
||||
public function getKnownUsers(string $knownTo): array {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->eq('known_to', $query->createNamedParameter($knownTo)));
|
||||
|
||||
return $this->findEntities($query);
|
||||
}
|
||||
|
||||
public function createKnownUserFromRow(array $row): KnownUser {
|
||||
return $this->mapRowToEntity([
|
||||
'id' => $row['s_id'],
|
||||
'known_to' => $row['known_to'],
|
||||
'known_user' => $row['known_user'],
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2021 Joas Schilling <coding@schilljs.com>
|
||||
*
|
||||
* @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\KnownUser;
|
||||
|
||||
class KnownUserService {
|
||||
/** @var KnownUserMapper */
|
||||
protected $mapper;
|
||||
/** @var array */
|
||||
protected $knownUsers = [];
|
||||
|
||||
public function __construct(KnownUserMapper $mapper) {
|
||||
$this->mapper = $mapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all matches where the given user is the owner of the phonebook
|
||||
*
|
||||
* @param string $knownTo
|
||||
* @return int Number of deleted matches
|
||||
*/
|
||||
public function deleteKnownTo(string $knownTo): int {
|
||||
return $this->mapper->deleteKnownTo($knownTo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all matches where the given user is the one in the phonebook
|
||||
*
|
||||
* @param string $contactUserId
|
||||
* @return int Number of deleted matches
|
||||
*/
|
||||
public function deleteByContactUserId(string $contactUserId): int {
|
||||
return $this->mapper->deleteKnownUser($contactUserId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a match because $knownTo has $contactUserId in his phonebook
|
||||
*
|
||||
* @param string $knownTo User id of the owner of the phonebook
|
||||
* @param string $contactUserId User id of the contact in the phonebook
|
||||
*/
|
||||
public function storeIsKnownToUser(string $knownTo, string $contactUserId): void {
|
||||
$entity = new KnownUser();
|
||||
$entity->setKnownTo($knownTo);
|
||||
$entity->setKnownUser($contactUserId);
|
||||
$this->mapper->insert($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if $contactUserId is in the phonebook of $knownTo
|
||||
*
|
||||
* @param string $knownTo User id of the owner of the phonebook
|
||||
* @param string $contactUserId User id of the contact in the phonebook
|
||||
* @return bool
|
||||
*/
|
||||
public function isKnownToUser(string $knownTo, string $contactUserId): bool {
|
||||
if (!isset($this->knownUsers[$knownTo])) {
|
||||
$entities = $this->mapper->getKnownUsers($knownTo);
|
||||
$this->knownUsers[$knownTo] = [];
|
||||
foreach ($entities as $entity) {
|
||||
$this->knownUsers[$knownTo][$entity->getKnownUser()] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return isset($this->knownUsers[$knownTo][$contactUserId]);
|
||||
}
|
||||
}
|
|
@ -1829,6 +1829,15 @@ class Manager implements IManager {
|
|||
$this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes';
|
||||
}
|
||||
|
||||
public function limitEnumerationToPhone(): bool {
|
||||
return $this->allowEnumeration() &&
|
||||
$this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes';
|
||||
}
|
||||
|
||||
public function allowEnumerationFullMatch(): bool {
|
||||
return $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match', 'yes') === 'yes';
|
||||
}
|
||||
|
||||
/**
|
||||
* Copied from \OC_Util::isSharingDisabledForUser
|
||||
*
|
||||
|
|
|
@ -384,6 +384,22 @@ interface IManager {
|
|||
*/
|
||||
public function limitEnumerationToGroups(): bool;
|
||||
|
||||
/**
|
||||
* Check if user enumeration is limited to the phonebook matches
|
||||
*
|
||||
* @return bool
|
||||
* @since 21.0.1
|
||||
*/
|
||||
public function limitEnumerationToPhone(): bool;
|
||||
|
||||
/**
|
||||
* Check if user enumeration is allowed to return on full match
|
||||
*
|
||||
* @return bool
|
||||
* @since 21.0.1
|
||||
*/
|
||||
public function allowEnumerationFullMatch(): bool;
|
||||
|
||||
/**
|
||||
* Check if sharing is disabled for the given user
|
||||
*
|
||||
|
|
|
@ -71,16 +71,16 @@ class SettingsContext implements Context, ActorAwareInterface {
|
|||
// forThe()->checkbox("Restrict username...") can not be used here; that
|
||||
// would return the checkbox itself, but the element that the user
|
||||
// interacts with is the label.
|
||||
return Locator::forThe()->xpath("//label[normalize-space() = 'Restrict username autocompletion to users within the same groups']")->
|
||||
describedAs("Restrict username autocompletion to groups checkbox in Sharing section in Administration Sharing Settings");
|
||||
return Locator::forThe()->xpath("//label[normalize-space() = 'Allow username autocompletion to users within the same groups']")->
|
||||
describedAs("Allow username autocompletion to users within the same groups checkbox in Sharing section in Administration Sharing Settings");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Locator
|
||||
*/
|
||||
public static function restrictUsernameAutocompletionToGroupsCheckboxInput() {
|
||||
return Locator::forThe()->checkbox("Restrict username autocompletion to users within the same groups")->
|
||||
describedAs("Restrict username autocompletion to groups checkbox input in Sharing section in Administration Sharing Settings");
|
||||
return Locator::forThe()->checkbox("Allow username autocompletion to users within the same groups")->
|
||||
describedAs("Allow username autocompletion to users within the same groups checkbox input in Sharing section in Administration Sharing Settings");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -26,6 +26,7 @@ namespace Test\Collaboration\Collaborators;
|
|||
use OC\Collaboration\Collaborators\MailPlugin;
|
||||
use OC\Collaboration\Collaborators\SearchResult;
|
||||
use OC\Federation\CloudIdManager;
|
||||
use OC\KnownUser\KnownUserService;
|
||||
use OCP\Collaboration\Collaborators\SearchResultType;
|
||||
use OCP\Contacts\IManager;
|
||||
use OCP\Federation\ICloudIdManager;
|
||||
|
@ -55,6 +56,9 @@ class MailPluginTest extends TestCase {
|
|||
/** @var IGroupManager|\PHPUnit\Framework\MockObject\MockObject */
|
||||
protected $groupManager;
|
||||
|
||||
/** @var KnownUserService|\PHPUnit\Framework\MockObject\MockObject */
|
||||
protected $knownUserService;
|
||||
|
||||
/** @var IUserSession|\PHPUnit\Framework\MockObject\MockObject */
|
||||
protected $userSession;
|
||||
|
||||
|
@ -64,6 +68,7 @@ class MailPluginTest extends TestCase {
|
|||
$this->config = $this->createMock(IConfig::class);
|
||||
$this->contactsManager = $this->createMock(IManager::class);
|
||||
$this->groupManager = $this->createMock(IGroupManager::class);
|
||||
$this->knownUserService = $this->createMock(KnownUserService::class);
|
||||
$this->userSession = $this->createMock(IUserSession::class);
|
||||
$this->cloudIdManager = new CloudIdManager($this->contactsManager);
|
||||
|
||||
|
@ -71,7 +76,14 @@ class MailPluginTest extends TestCase {
|
|||
}
|
||||
|
||||
public function instantiatePlugin() {
|
||||
$this->plugin = new MailPlugin($this->contactsManager, $this->cloudIdManager, $this->config, $this->groupManager, $this->userSession);
|
||||
$this->plugin = new MailPlugin(
|
||||
$this->contactsManager,
|
||||
$this->cloudIdManager,
|
||||
$this->config,
|
||||
$this->groupManager,
|
||||
$this->knownUserService,
|
||||
$this->userSession
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -25,6 +25,7 @@ namespace Test\Collaboration\Collaborators;
|
|||
|
||||
use OC\Collaboration\Collaborators\SearchResult;
|
||||
use OC\Collaboration\Collaborators\UserPlugin;
|
||||
use OC\KnownUser\KnownUserService;
|
||||
use OCP\Collaboration\Collaborators\ISearchResult;
|
||||
use OCP\IConfig;
|
||||
use OCP\IGroup;
|
||||
|
@ -49,6 +50,9 @@ class UserPluginTest extends TestCase {
|
|||
/** @var IUserSession|\PHPUnit\Framework\MockObject\MockObject */
|
||||
protected $session;
|
||||
|
||||
/** @var KnownUserService|\PHPUnit\Framework\MockObject\MockObject */
|
||||
protected $knownUserService;
|
||||
|
||||
/** @var IUserStatusManager|\PHPUnit\Framework\MockObject\MockObject */
|
||||
protected $userStatusManager;
|
||||
|
||||
|
@ -78,6 +82,8 @@ class UserPluginTest extends TestCase {
|
|||
|
||||
$this->session = $this->createMock(IUserSession::class);
|
||||
|
||||
$this->knownUserService = $this->createMock(KnownUserService::class);
|
||||
|
||||
$this->userStatusManager = $this->createMock(IUserStatusManager::class);
|
||||
|
||||
$this->searchResult = new SearchResult();
|
||||
|
@ -93,6 +99,7 @@ class UserPluginTest extends TestCase {
|
|||
$this->userManager,
|
||||
$this->groupManager,
|
||||
$this->session,
|
||||
$this->knownUserService,
|
||||
$this->userStatusManager
|
||||
);
|
||||
}
|
||||
|
|
|
@ -26,11 +26,13 @@
|
|||
namespace Tests\Contacts\ContactsMenu;
|
||||
|
||||
use OC\Contacts\ContactsMenu\ContactsStore;
|
||||
use OC\KnownUser\KnownUserService;
|
||||
use OCP\Contacts\IManager;
|
||||
use OCP\IConfig;
|
||||
use OCP\IGroupManager;
|
||||
use OCP\IUser;
|
||||
use OCP\IUserManager;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Test\TestCase;
|
||||
|
||||
class ContactsStoreTest extends TestCase {
|
||||
|
@ -44,6 +46,8 @@ class ContactsStoreTest extends TestCase {
|
|||
private $groupManager;
|
||||
/** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */
|
||||
private $config;
|
||||
/** @var KnownUserService|MockObject */
|
||||
private $knownUserService;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
@ -52,7 +56,14 @@ class ContactsStoreTest extends TestCase {
|
|||
$this->userManager = $this->createMock(IUserManager::class);
|
||||
$this->groupManager = $this->createMock(IGroupManager::class);
|
||||
$this->config = $this->createMock(IConfig::class);
|
||||
$this->contactsStore = new ContactsStore($this->contactsManager, $this->config, $this->userManager, $this->groupManager);
|
||||
$this->knownUserService = $this->createMock(KnownUserService::class);
|
||||
$this->contactsStore = new ContactsStore(
|
||||
$this->contactsManager,
|
||||
$this->config,
|
||||
$this->userManager,
|
||||
$this->groupManager,
|
||||
$this->knownUserService
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetContactsWithoutFilter() {
|
||||
|
@ -171,29 +182,16 @@ class ContactsStoreTest extends TestCase {
|
|||
}
|
||||
|
||||
public function testGetContactsWhenUserIsInExcludeGroups() {
|
||||
$this->config->expects($this->at(0))->method('getAppValue')
|
||||
->with($this->equalTo('core'), $this->equalTo('shareapi_allow_share_dialog_user_enumeration'), $this->equalTo('yes'))
|
||||
->willReturn('yes');
|
||||
|
||||
$this->config->expects($this->at(1))
|
||||
$this->config
|
||||
->method('getAppValue')
|
||||
->with($this->equalTo('core'), $this->equalTo('shareapi_restrict_user_enumeration_to_group'), $this->equalTo('no'))
|
||||
->willReturn('no');
|
||||
|
||||
$this->config->expects($this->at(2))
|
||||
->method('getAppValue')
|
||||
->with($this->equalTo('core'), $this->equalTo('shareapi_exclude_groups'), $this->equalTo('no'))
|
||||
->willReturn('yes');
|
||||
|
||||
$this->config->expects($this->at(3))
|
||||
->method('getAppValue')
|
||||
->with($this->equalTo('core'), $this->equalTo('shareapi_only_share_with_group_members'), $this->equalTo('no'))
|
||||
->willReturn('yes');
|
||||
|
||||
$this->config->expects($this->at(4))
|
||||
->method('getAppValue')
|
||||
->with($this->equalTo('core'), $this->equalTo('shareapi_exclude_groups_list'), $this->equalTo(''))
|
||||
->willReturn('["group1", "group5", "group6"]');
|
||||
->willReturnMap([
|
||||
['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
|
||||
['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no'],
|
||||
['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'no'],
|
||||
['core', 'shareapi_exclude_groups', 'no', 'yes'],
|
||||
['core', 'shareapi_only_share_with_group_members', 'no', 'yes'],
|
||||
['core', 'shareapi_exclude_groups_list', '', '["group1", "group5", "group6"]'],
|
||||
]);
|
||||
|
||||
/** @var IUser|\PHPUnit\Framework\MockObject\MockObject $currentUser */
|
||||
$currentUser = $this->createMock(IUser::class);
|
||||
|
@ -228,22 +226,15 @@ class ContactsStoreTest extends TestCase {
|
|||
}
|
||||
|
||||
public function testGetContactsOnlyShareIfInTheSameGroup() {
|
||||
$this->config->expects($this->at(0))->method('getAppValue')
|
||||
->with($this->equalTo('core'), $this->equalTo('shareapi_allow_share_dialog_user_enumeration'), $this->equalTo('yes'))
|
||||
->willReturn('yes');
|
||||
|
||||
$this->config->expects($this->at(1)) ->method('getAppValue')
|
||||
->with($this->equalTo('core'), $this->equalTo('shareapi_restrict_user_enumeration_to_group'), $this->equalTo('no'))
|
||||
->willReturn('no');
|
||||
|
||||
$this->config->expects($this->at(2)) ->method('getAppValue')
|
||||
->with($this->equalTo('core'), $this->equalTo('shareapi_exclude_groups'), $this->equalTo('no'))
|
||||
->willReturn('no');
|
||||
|
||||
$this->config->expects($this->at(3))
|
||||
$this->config
|
||||
->method('getAppValue')
|
||||
->with($this->equalTo('core'), $this->equalTo('shareapi_only_share_with_group_members'), $this->equalTo('no'))
|
||||
->willReturn('yes');
|
||||
->willReturnMap([
|
||||
['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
|
||||
['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no'],
|
||||
['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'no'],
|
||||
['core', 'shareapi_exclude_groups', 'no', 'no'],
|
||||
['core', 'shareapi_only_share_with_group_members', 'no', 'yes'],
|
||||
]);
|
||||
|
||||
/** @var IUser|\PHPUnit\Framework\MockObject\MockObject $currentUser */
|
||||
$currentUser = $this->createMock(IUser::class);
|
||||
|
@ -314,22 +305,15 @@ class ContactsStoreTest extends TestCase {
|
|||
}
|
||||
|
||||
public function testGetContactsOnlyEnumerateIfInTheSameGroup() {
|
||||
$this->config->expects($this->at(0))->method('getAppValue')
|
||||
->with($this->equalTo('core'), $this->equalTo('shareapi_allow_share_dialog_user_enumeration'), $this->equalTo('yes'))
|
||||
->willReturn('yes');
|
||||
|
||||
$this->config->expects($this->at(1)) ->method('getAppValue')
|
||||
->with($this->equalTo('core'), $this->equalTo('shareapi_restrict_user_enumeration_to_group'), $this->equalTo('no'))
|
||||
->willReturn('yes');
|
||||
|
||||
$this->config->expects($this->at(2)) ->method('getAppValue')
|
||||
->with($this->equalTo('core'), $this->equalTo('shareapi_exclude_groups'), $this->equalTo('no'))
|
||||
->willReturn('no');
|
||||
|
||||
$this->config->expects($this->at(3))
|
||||
$this->config
|
||||
->method('getAppValue')
|
||||
->with($this->equalTo('core'), $this->equalTo('shareapi_only_share_with_group_members'), $this->equalTo('no'))
|
||||
->willReturn('no');
|
||||
->willReturnMap([
|
||||
['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
|
||||
['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'yes'],
|
||||
['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'no'],
|
||||
['core', 'shareapi_exclude_groups', 'no', 'no'],
|
||||
['core', 'shareapi_only_share_with_group_members', 'no', 'yes'],
|
||||
]);
|
||||
|
||||
/** @var IUser|\PHPUnit\Framework\MockObject\MockObject $currentUser */
|
||||
$currentUser = $this->createMock(IUser::class);
|
||||
|
@ -399,10 +383,312 @@ class ContactsStoreTest extends TestCase {
|
|||
$this->assertEquals('contact', $entries[2]->getProperty('UID'));
|
||||
}
|
||||
|
||||
public function testGetContactsOnlyEnumerateIfPhoneBookMatch() {
|
||||
$this->config
|
||||
->method('getAppValue')
|
||||
->willReturnMap([
|
||||
['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
|
||||
['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no'],
|
||||
['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'yes'],
|
||||
['core', 'shareapi_exclude_groups', 'no', 'no'],
|
||||
['core', 'shareapi_only_share_with_group_members', 'no', 'no'],
|
||||
]);
|
||||
|
||||
/** @var IUser|\PHPUnit\Framework\MockObject\MockObject $currentUser */
|
||||
$currentUser = $this->createMock(IUser::class);
|
||||
$currentUser->expects($this->once())
|
||||
->method('getUID')
|
||||
->willReturn('user001');
|
||||
|
||||
$this->groupManager->expects($this->at(0))
|
||||
->method('getUserGroupIds')
|
||||
->with($this->equalTo($currentUser))
|
||||
->willReturn(['group1', 'group2', 'group3']);
|
||||
|
||||
$this->knownUserService->method('isKnownToUser')
|
||||
->willReturnMap([
|
||||
['user001', 'user1', true],
|
||||
['user001', 'user2', true],
|
||||
['user001', 'user3', false],
|
||||
]);
|
||||
|
||||
$this->contactsManager->expects($this->once())
|
||||
->method('search')
|
||||
->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
|
||||
->willReturn([
|
||||
[
|
||||
'UID' => 'user1',
|
||||
'isLocalSystemBook' => true
|
||||
],
|
||||
[
|
||||
'UID' => 'user2',
|
||||
'isLocalSystemBook' => true
|
||||
],
|
||||
[
|
||||
'UID' => 'user3',
|
||||
'isLocalSystemBook' => true
|
||||
],
|
||||
[
|
||||
'UID' => 'contact',
|
||||
],
|
||||
]);
|
||||
|
||||
$entries = $this->contactsStore->getContacts($currentUser, '');
|
||||
|
||||
$this->assertCount(3, $entries);
|
||||
$this->assertEquals('user1', $entries[0]->getProperty('UID'));
|
||||
$this->assertEquals('user2', $entries[1]->getProperty('UID'));
|
||||
$this->assertEquals('contact', $entries[2]->getProperty('UID'));
|
||||
}
|
||||
|
||||
public function testGetContactsOnlyEnumerateIfPhoneBookMatchWithOwnGroupsOnly() {
|
||||
$this->config
|
||||
->method('getAppValue')
|
||||
->willReturnMap([
|
||||
['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
|
||||
['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no'],
|
||||
['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'yes'],
|
||||
['core', 'shareapi_exclude_groups', 'no', 'no'],
|
||||
['core', 'shareapi_only_share_with_group_members', 'no', 'yes'],
|
||||
]);
|
||||
|
||||
/** @var IUser|\PHPUnit\Framework\MockObject\MockObject $currentUser */
|
||||
$currentUser = $this->createMock(IUser::class);
|
||||
$currentUser->expects($this->once())
|
||||
->method('getUID')
|
||||
->willReturn('user001');
|
||||
|
||||
$this->groupManager->expects($this->at(0))
|
||||
->method('getUserGroupIds')
|
||||
->with($this->equalTo($currentUser))
|
||||
->willReturn(['group1', 'group2', 'group3']);
|
||||
|
||||
$user1 = $this->createMock(IUser::class);
|
||||
$this->userManager->expects($this->at(0))
|
||||
->method('get')
|
||||
->with('user1')
|
||||
->willReturn($user1);
|
||||
$this->groupManager->expects($this->at(1))
|
||||
->method('getUserGroupIds')
|
||||
->with($this->equalTo($user1))
|
||||
->willReturn(['group1']);
|
||||
$user2 = $this->createMock(IUser::class);
|
||||
$this->userManager->expects($this->at(1))
|
||||
->method('get')
|
||||
->with('user2')
|
||||
->willReturn($user2);
|
||||
$this->groupManager->expects($this->at(2))
|
||||
->method('getUserGroupIds')
|
||||
->with($this->equalTo($user2))
|
||||
->willReturn(['group2', 'group3']);
|
||||
$user3 = $this->createMock(IUser::class);
|
||||
$this->userManager->expects($this->at(2))
|
||||
->method('get')
|
||||
->with('user3')
|
||||
->willReturn($user3);
|
||||
$this->groupManager->expects($this->at(3))
|
||||
->method('getUserGroupIds')
|
||||
->with($this->equalTo($user3))
|
||||
->willReturn(['group8', 'group9']);
|
||||
|
||||
$this->knownUserService->method('isKnownToUser')
|
||||
->willReturnMap([
|
||||
['user001', 'user1', true],
|
||||
['user001', 'user2', true],
|
||||
['user001', 'user3', true],
|
||||
]);
|
||||
|
||||
$this->contactsManager->expects($this->once())
|
||||
->method('search')
|
||||
->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
|
||||
->willReturn([
|
||||
[
|
||||
'UID' => 'user1',
|
||||
'isLocalSystemBook' => true
|
||||
],
|
||||
[
|
||||
'UID' => 'user2',
|
||||
'isLocalSystemBook' => true
|
||||
],
|
||||
[
|
||||
'UID' => 'user3',
|
||||
'isLocalSystemBook' => true
|
||||
],
|
||||
[
|
||||
'UID' => 'contact',
|
||||
],
|
||||
]);
|
||||
|
||||
$entries = $this->contactsStore->getContacts($currentUser, '');
|
||||
|
||||
$this->assertCount(3, $entries);
|
||||
$this->assertEquals('user1', $entries[0]->getProperty('UID'));
|
||||
$this->assertEquals('user2', $entries[1]->getProperty('UID'));
|
||||
$this->assertEquals('contact', $entries[2]->getProperty('UID'));
|
||||
}
|
||||
|
||||
public function testGetContactsOnlyEnumerateIfPhoneBookOrSameGroup() {
|
||||
$this->config
|
||||
->method('getAppValue')
|
||||
->willReturnMap([
|
||||
['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
|
||||
['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'yes'],
|
||||
['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'yes'],
|
||||
['core', 'shareapi_exclude_groups', 'no', 'no'],
|
||||
['core', 'shareapi_only_share_with_group_members', 'no', 'no'],
|
||||
]);
|
||||
|
||||
/** @var IUser|\PHPUnit\Framework\MockObject\MockObject $currentUser */
|
||||
$currentUser = $this->createMock(IUser::class);
|
||||
$currentUser->expects($this->once())
|
||||
->method('getUID')
|
||||
->willReturn('user001');
|
||||
|
||||
$this->groupManager->expects($this->at(0))
|
||||
->method('getUserGroupIds')
|
||||
->with($this->equalTo($currentUser))
|
||||
->willReturn(['group1', 'group2', 'group3']);
|
||||
|
||||
$user1 = $this->createMock(IUser::class);
|
||||
$this->userManager->expects($this->at(0))
|
||||
->method('get')
|
||||
->with('user1')
|
||||
->willReturn($user1);
|
||||
$this->groupManager->expects($this->at(1))
|
||||
->method('getUserGroupIds')
|
||||
->with($this->equalTo($user1))
|
||||
->willReturn(['group1']);
|
||||
|
||||
$this->knownUserService->method('isKnownToUser')
|
||||
->willReturnMap([
|
||||
['user001', 'user1', false],
|
||||
['user001', 'user2', true],
|
||||
['user001', 'user3', true],
|
||||
]);
|
||||
|
||||
$this->contactsManager->expects($this->once())
|
||||
->method('search')
|
||||
->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
|
||||
->willReturn([
|
||||
[
|
||||
'UID' => 'user1',
|
||||
'isLocalSystemBook' => true
|
||||
],
|
||||
[
|
||||
'UID' => 'user2',
|
||||
'isLocalSystemBook' => true
|
||||
],
|
||||
[
|
||||
'UID' => 'user3',
|
||||
'isLocalSystemBook' => true
|
||||
],
|
||||
[
|
||||
'UID' => 'contact',
|
||||
],
|
||||
]);
|
||||
|
||||
$entries = $this->contactsStore->getContacts($currentUser, '');
|
||||
|
||||
$this->assertCount(4, $entries);
|
||||
$this->assertEquals('user1', $entries[0]->getProperty('UID'));
|
||||
$this->assertEquals('user2', $entries[1]->getProperty('UID'));
|
||||
$this->assertEquals('user3', $entries[2]->getProperty('UID'));
|
||||
$this->assertEquals('contact', $entries[3]->getProperty('UID'));
|
||||
}
|
||||
|
||||
public function testGetContactsOnlyEnumerateIfPhoneBookOrSameGroupInOwnGroupsOnly() {
|
||||
$this->config
|
||||
->method('getAppValue')
|
||||
->willReturnMap([
|
||||
['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
|
||||
['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'yes'],
|
||||
['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'yes'],
|
||||
['core', 'shareapi_exclude_groups', 'no', 'no'],
|
||||
['core', 'shareapi_only_share_with_group_members', 'no', 'yes'],
|
||||
]);
|
||||
|
||||
/** @var IUser|\PHPUnit\Framework\MockObject\MockObject $currentUser */
|
||||
$currentUser = $this->createMock(IUser::class);
|
||||
$currentUser->expects($this->once())
|
||||
->method('getUID')
|
||||
->willReturn('user001');
|
||||
|
||||
$this->groupManager->expects($this->at(0))
|
||||
->method('getUserGroupIds')
|
||||
->with($this->equalTo($currentUser))
|
||||
->willReturn(['group1', 'group2', 'group3']);
|
||||
|
||||
$user1 = $this->createMock(IUser::class);
|
||||
$this->userManager->expects($this->at(0))
|
||||
->method('get')
|
||||
->with('user1')
|
||||
->willReturn($user1);
|
||||
$this->groupManager->expects($this->at(1))
|
||||
->method('getUserGroupIds')
|
||||
->with($this->equalTo($user1))
|
||||
->willReturn(['group1']);
|
||||
$user2 = $this->createMock(IUser::class);
|
||||
$this->userManager->expects($this->at(1))
|
||||
->method('get')
|
||||
->with('user2')
|
||||
->willReturn($user2);
|
||||
$this->groupManager->expects($this->at(2))
|
||||
->method('getUserGroupIds')
|
||||
->with($this->equalTo($user2))
|
||||
->willReturn(['group2', 'group3']);
|
||||
$user3 = $this->createMock(IUser::class);
|
||||
$this->userManager->expects($this->at(2))
|
||||
->method('get')
|
||||
->with('user3')
|
||||
->willReturn($user3);
|
||||
$this->groupManager->expects($this->at(3))
|
||||
->method('getUserGroupIds')
|
||||
->with($this->equalTo($user3))
|
||||
->willReturn(['group8', 'group9']);
|
||||
|
||||
$this->knownUserService->method('isKnownToUser')
|
||||
->willReturnMap([
|
||||
['user001', 'user1', false],
|
||||
['user001', 'user2', true],
|
||||
['user001', 'user3', true],
|
||||
]);
|
||||
|
||||
$this->contactsManager->expects($this->once())
|
||||
->method('search')
|
||||
->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
|
||||
->willReturn([
|
||||
[
|
||||
'UID' => 'user1',
|
||||
'isLocalSystemBook' => true
|
||||
],
|
||||
[
|
||||
'UID' => 'user2',
|
||||
'isLocalSystemBook' => true
|
||||
],
|
||||
[
|
||||
'UID' => 'user3',
|
||||
'isLocalSystemBook' => true
|
||||
],
|
||||
[
|
||||
'UID' => 'contact',
|
||||
],
|
||||
]);
|
||||
|
||||
$entries = $this->contactsStore->getContacts($currentUser, '');
|
||||
|
||||
$this->assertCount(3, $entries);
|
||||
$this->assertEquals('user1', $entries[0]->getProperty('UID'));
|
||||
$this->assertEquals('user2', $entries[1]->getProperty('UID'));
|
||||
$this->assertEquals('contact', $entries[2]->getProperty('UID'));
|
||||
}
|
||||
|
||||
public function testGetContactsWithFilter() {
|
||||
$this->config->expects($this->at(0))->method('getAppValue')
|
||||
->with($this->equalTo('core'), $this->equalTo('shareapi_allow_share_dialog_user_enumeration'), $this->equalTo('yes'))
|
||||
->willReturn('no');
|
||||
$this->config
|
||||
->method('getAppValue')
|
||||
->willReturnMap([
|
||||
['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'no'],
|
||||
['core', 'shareapi_restrict_user_enumeration_full_match', 'yes', 'yes'],
|
||||
]);
|
||||
|
||||
/** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */
|
||||
$user = $this->createMock(IUser::class);
|
||||
|
@ -483,6 +769,90 @@ class ContactsStoreTest extends TestCase {
|
|||
], $entry[0]->getEMailAddresses());
|
||||
}
|
||||
|
||||
public function testGetContactsWithFilterWithoutFullMatch() {
|
||||
$this->config
|
||||
->method('getAppValue')
|
||||
->willReturnMap([
|
||||
['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'no'],
|
||||
['core', 'shareapi_restrict_user_enumeration_full_match', 'yes', 'no'],
|
||||
]);
|
||||
|
||||
/** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */
|
||||
$user = $this->createMock(IUser::class);
|
||||
$this->contactsManager->expects($this->any())
|
||||
->method('search')
|
||||
->willReturn([
|
||||
[
|
||||
'UID' => 'a567',
|
||||
'FN' => 'Darren Roner',
|
||||
'EMAIL' => [
|
||||
'darren@roner.au',
|
||||
],
|
||||
'isLocalSystemBook' => true,
|
||||
],
|
||||
[
|
||||
'UID' => 'john',
|
||||
'FN' => 'John Doe',
|
||||
'EMAIL' => [
|
||||
'john@example.com',
|
||||
],
|
||||
'isLocalSystemBook' => true,
|
||||
],
|
||||
[
|
||||
'FN' => 'Anne D',
|
||||
'EMAIL' => [
|
||||
'anne@example.com',
|
||||
],
|
||||
'isLocalSystemBook' => false,
|
||||
],
|
||||
]);
|
||||
$user->expects($this->any())
|
||||
->method('getUID')
|
||||
->willReturn('user123');
|
||||
|
||||
// Complete match on UID should not match
|
||||
$entry = $this->contactsStore->getContacts($user, 'a567');
|
||||
$this->assertSame(1, count($entry));
|
||||
$this->assertEquals([
|
||||
'anne@example.com'
|
||||
], $entry[0]->getEMailAddresses());
|
||||
|
||||
// Partial match on UID should not match
|
||||
$entry = $this->contactsStore->getContacts($user, 'a56');
|
||||
$this->assertSame(1, count($entry));
|
||||
$this->assertEquals([
|
||||
'anne@example.com'
|
||||
], $entry[0]->getEMailAddresses());
|
||||
|
||||
// Complete match on email should not match
|
||||
$entry = $this->contactsStore->getContacts($user, 'john@example.com');
|
||||
$this->assertSame(1, count($entry));
|
||||
$this->assertEquals([
|
||||
'anne@example.com'
|
||||
], $entry[0]->getEMailAddresses());
|
||||
|
||||
// Partial match on email should not match
|
||||
$entry = $this->contactsStore->getContacts($user, 'john@example.co');
|
||||
$this->assertSame(1, count($entry));
|
||||
$this->assertEquals([
|
||||
'anne@example.com'
|
||||
], $entry[0]->getEMailAddresses());
|
||||
|
||||
// Match on FN should not match
|
||||
$entry = $this->contactsStore->getContacts($user, 'Darren Roner');
|
||||
$this->assertSame(1, count($entry));
|
||||
$this->assertEquals([
|
||||
'anne@example.com'
|
||||
], $entry[0]->getEMailAddresses());
|
||||
|
||||
// Don't filter users in local addressbook
|
||||
$entry = $this->contactsStore->getContacts($user, 'Anne D');
|
||||
$this->assertSame(1, count($entry));
|
||||
$this->assertEquals([
|
||||
'anne@example.com'
|
||||
], $entry[0]->getEMailAddresses());
|
||||
}
|
||||
|
||||
public function testFindOneUser() {
|
||||
$this->config->expects($this->at(0))->method('getAppValue')
|
||||
->with($this->equalTo('core'), $this->equalTo('shareapi_allow_share_dialog_user_enumeration'), $this->equalTo('yes'))
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
// between betas, final and RCs. This is _not_ the public version number. Reset minor/patchlevel
|
||||
// when updating major/minor version number.
|
||||
|
||||
$OC_Version = [22, 0, 0, 1];
|
||||
$OC_Version = [22, 0, 0, 2];
|
||||
|
||||
// The human readable string
|
||||
$OC_VersionString = '22.0.0 alpha';
|
||||
|
|
Loading…
Reference in New Issue