summaryrefslogtreecommitdiffstats
path: root/apps/user_ldap/lib
diff options
context:
space:
mode:
Diffstat (limited to 'apps/user_ldap/lib')
-rw-r--r--apps/user_ldap/lib/Access.php (renamed from apps/user_ldap/lib/access.php)20
-rw-r--r--apps/user_ldap/lib/BackendUtility.php (renamed from apps/user_ldap/lib/backendutility.php)2
-rw-r--r--apps/user_ldap/lib/Command/CheckUser.php134
-rw-r--r--apps/user_ldap/lib/Command/CreateEmptyConfig.php73
-rw-r--r--apps/user_ldap/lib/Command/DeleteConfig.php68
-rw-r--r--apps/user_ldap/lib/Command/Search.php127
-rw-r--r--apps/user_ldap/lib/Command/SetConfig.php84
-rw-r--r--apps/user_ldap/lib/Command/ShowConfig.php108
-rw-r--r--apps/user_ldap/lib/Command/ShowRemnants.php92
-rw-r--r--apps/user_ldap/lib/Command/TestConfig.php90
-rw-r--r--apps/user_ldap/lib/Configuration.php (renamed from apps/user_ldap/lib/configuration.php)2
-rw-r--r--apps/user_ldap/lib/Connection.php (renamed from apps/user_ldap/lib/connection.php)2
-rw-r--r--apps/user_ldap/lib/FilesystemHelper.php (renamed from apps/user_ldap/lib/filesystemhelper.php)2
-rw-r--r--apps/user_ldap/lib/Group_LDAP.php899
-rw-r--r--apps/user_ldap/lib/Group_Proxy.php197
-rw-r--r--apps/user_ldap/lib/Helper.php (renamed from apps/user_ldap/lib/helper.php)4
-rw-r--r--apps/user_ldap/lib/ILDAPWrapper.php (renamed from apps/user_ldap/lib/ildapwrapper.php)2
-rw-r--r--apps/user_ldap/lib/Jobs/CleanUp.php (renamed from apps/user_ldap/lib/jobs/cleanup.php)14
-rw-r--r--apps/user_ldap/lib/Jobs/UpdateGroups.php (renamed from apps/user_ldap/lib/jobs.php)23
-rw-r--r--apps/user_ldap/lib/LDAP.php (renamed from apps/user_ldap/lib/ldap.php)4
-rw-r--r--apps/user_ldap/lib/LDAPUtility.php (renamed from apps/user_ldap/lib/ldaputility.php)2
-rw-r--r--apps/user_ldap/lib/LogWrapper.php (renamed from apps/user_ldap/lib/logwrapper.php)2
-rw-r--r--apps/user_ldap/lib/Mapping/AbstractMapping.php (renamed from apps/user_ldap/lib/mapping/abstractmapping.php)0
-rw-r--r--apps/user_ldap/lib/Mapping/GroupMapping.php (renamed from apps/user_ldap/lib/mapping/groupmapping.php)0
-rw-r--r--apps/user_ldap/lib/Mapping/UserMapping.php (renamed from apps/user_ldap/lib/mapping/usermapping.php)0
-rw-r--r--apps/user_ldap/lib/Proxy.php (renamed from apps/user_ldap/lib/proxy.php)3
-rw-r--r--apps/user_ldap/lib/User/DeletedUsersIndex.php (renamed from apps/user_ldap/lib/user/deletedusersindex.php)6
-rw-r--r--apps/user_ldap/lib/User/IUserTools.php (renamed from apps/user_ldap/lib/user/iusertools.php)2
-rw-r--r--apps/user_ldap/lib/User/Manager.php (renamed from apps/user_ldap/lib/user/manager.php)25
-rw-r--r--apps/user_ldap/lib/User/OfflineUser.php (renamed from apps/user_ldap/lib/user/offlineuser.php)2
-rw-r--r--apps/user_ldap/lib/User/User.php (renamed from apps/user_ldap/lib/user/user.php)17
-rw-r--r--apps/user_ldap/lib/User_LDAP.php459
-rw-r--r--apps/user_ldap/lib/User_Proxy.php274
-rw-r--r--apps/user_ldap/lib/Wizard.php (renamed from apps/user_ldap/lib/wizard.php)8
-rw-r--r--apps/user_ldap/lib/WizardResult.php (renamed from apps/user_ldap/lib/wizardresult.php)2
35 files changed, 2678 insertions, 71 deletions
diff --git a/apps/user_ldap/lib/access.php b/apps/user_ldap/lib/Access.php
index f92ded64797..6ec90cfff8f 100644
--- a/apps/user_ldap/lib/access.php
+++ b/apps/user_ldap/lib/Access.php
@@ -35,18 +35,20 @@
*
*/
-namespace OCA\user_ldap\lib;
+namespace OCA\User_LDAP;
-use OCA\user_ldap\lib\user\OfflineUser;
+use OCA\User_LDAP\User\IUserTools;
+use OCA\User_LDAP\User\Manager;
+use OCA\User_LDAP\User\OfflineUser;
use OCA\User_LDAP\Mapping\AbstractMapping;
/**
* Class Access
- * @package OCA\user_ldap\lib
+ * @package OCA\User_LDAP
*/
-class Access extends LDAPUtility implements user\IUserTools {
+class Access extends LDAPUtility implements IUserTools {
/**
- * @var \OCA\user_ldap\lib\Connection
+ * @var \OCA\User_LDAP\Connection
*/
public $connection;
public $userManager;
@@ -75,7 +77,7 @@ class Access extends LDAPUtility implements user\IUserTools {
protected $groupMapper;
public function __construct(Connection $connection, ILDAPWrapper $ldap,
- user\Manager $userManager) {
+ Manager $userManager) {
parent::__construct($ldap);
$this->connection = $connection;
$this->userManager = $userManager;
@@ -131,7 +133,7 @@ class Access extends LDAPUtility implements user\IUserTools {
/**
* returns the Connection instance
- * @return \OCA\user_ldap\lib\Connection
+ * @return \OCA\User_LDAP\Connection
*/
public function getConnection() {
return $this->connection;
@@ -314,7 +316,7 @@ class Access extends LDAPUtility implements user\IUserTools {
* @return string|false LDAP DN on success, otherwise false
*/
public function groupname2dn($name) {
- return $this->groupMapper->getDNbyName($name);
+ return $this->groupMapper->getDNByName($name);
}
/**
@@ -323,7 +325,7 @@ class Access extends LDAPUtility implements user\IUserTools {
* @return string|false with the LDAP DN on success, otherwise false
*/
public function username2dn($name) {
- $fdn = $this->userMapper->getDNbyName($name);
+ $fdn = $this->userMapper->getDNByName($name);
//Check whether the DN belongs to the Base, to avoid issues on multi-
//server setups
diff --git a/apps/user_ldap/lib/backendutility.php b/apps/user_ldap/lib/BackendUtility.php
index 87c1649cada..fc4efe28924 100644
--- a/apps/user_ldap/lib/backendutility.php
+++ b/apps/user_ldap/lib/BackendUtility.php
@@ -21,7 +21,7 @@
*
*/
-namespace OCA\user_ldap\lib;
+namespace OCA\User_LDAP;
abstract class BackendUtility {
diff --git a/apps/user_ldap/lib/Command/CheckUser.php b/apps/user_ldap/lib/Command/CheckUser.php
new file mode 100644
index 00000000000..43de421de6a
--- /dev/null
+++ b/apps/user_ldap/lib/Command/CheckUser.php
@@ -0,0 +1,134 @@
+<?php
+/**
+ * @author Arthur Schiwon <blizzz@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\User_LDAP\Command;
+
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+use OCA\User_LDAP\User\DeletedUsersIndex;
+use OCA\User_LDAP\Mapping\UserMapping;
+use OCA\User_LDAP\Helper as LDAPHelper;
+use OCA\User_LDAP\User_Proxy;
+
+class CheckUser extends Command {
+ /** @var \OCA\User_LDAP\User_Proxy */
+ protected $backend;
+
+ /** @var \OCA\User_LDAP\Helper */
+ protected $helper;
+
+ /** @var \OCA\User_LDAP\User\DeletedUsersIndex */
+ protected $dui;
+
+ /** @var \OCA\User_LDAP\Mapping\UserMapping */
+ protected $mapping;
+
+ /**
+ * @param User_Proxy $uBackend
+ * @param LDAPHelper $helper
+ * @param DeletedUsersIndex $dui
+ * @param UserMapping $mapping
+ */
+ public function __construct(User_Proxy $uBackend, LDAPHelper $helper, DeletedUsersIndex $dui, UserMapping $mapping) {
+ $this->backend = $uBackend;
+ $this->helper = $helper;
+ $this->dui = $dui;
+ $this->mapping = $mapping;
+ parent::__construct();
+ }
+
+ protected function configure() {
+ $this
+ ->setName('ldap:check-user')
+ ->setDescription('checks whether a user exists on LDAP.')
+ ->addArgument(
+ 'ocName',
+ InputArgument::REQUIRED,
+ 'the user name as used in ownCloud'
+ )
+ ->addOption(
+ 'force',
+ null,
+ InputOption::VALUE_NONE,
+ 'ignores disabled LDAP configuration'
+ )
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ try {
+ $uid = $input->getArgument('ocName');
+ $this->isAllowed($input->getOption('force'));
+ $this->confirmUserIsMapped($uid);
+ $exists = $this->backend->userExistsOnLDAP($uid);
+ if($exists === true) {
+ $output->writeln('The user is still available on LDAP.');
+ return;
+ }
+
+ $this->dui->markUser($uid);
+ $output->writeln('The user does not exists on LDAP anymore.');
+ $output->writeln('Clean up the user\'s remnants by: ./occ user:delete "'
+ . $uid . '"');
+ } catch (\Exception $e) {
+ $output->writeln('<error>' . $e->getMessage(). '</error>');
+ }
+ }
+
+ /**
+ * checks whether a user is actually mapped
+ * @param string $ocName the username as used in ownCloud
+ * @throws \Exception
+ * @return true
+ */
+ protected function confirmUserIsMapped($ocName) {
+ $dn = $this->mapping->getDNByName($ocName);
+ if ($dn === false) {
+ throw new \Exception('The given user is not a recognized LDAP user.');
+ }
+
+ return true;
+ }
+
+ /**
+ * checks whether the setup allows reliable checking of LDAP user existence
+ * @throws \Exception
+ * @return true
+ */
+ protected function isAllowed($force) {
+ if($this->helper->haveDisabledConfigurations() && !$force) {
+ throw new \Exception('Cannot check user existence, because '
+ . 'disabled LDAP configurations are present.');
+ }
+
+ // we don't check ldapUserCleanupInterval from config.php because this
+ // action is triggered manually, while the setting only controls the
+ // background job.
+
+ return true;
+ }
+
+}
diff --git a/apps/user_ldap/lib/Command/CreateEmptyConfig.php b/apps/user_ldap/lib/Command/CreateEmptyConfig.php
new file mode 100644
index 00000000000..c735e51f491
--- /dev/null
+++ b/apps/user_ldap/lib/Command/CreateEmptyConfig.php
@@ -0,0 +1,73 @@
+<?php
+/**
+ * @author Arthur Schiwon <blizzz@owncloud.com>
+ * @author Martin Konrad <konrad@frib.msu.edu>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\User_LDAP\Command;
+
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use \OCA\User_LDAP\Helper;
+use \OCA\User_LDAP\Configuration;
+
+class CreateEmptyConfig extends Command {
+ /** @var \OCA\User_LDAP\Helper */
+ protected $helper;
+
+ /**
+ * @param Helper $helper
+ */
+ public function __construct(Helper $helper) {
+ $this->helper = $helper;
+ parent::__construct();
+ }
+
+ protected function configure() {
+ $this
+ ->setName('ldap:create-empty-config')
+ ->setDescription('creates an empty LDAP configuration')
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $configPrefix = $this->getNewConfigurationPrefix();
+ $output->writeln("Created new configuration with configID '{$configPrefix}'");
+
+ $configHolder = new Configuration($configPrefix);
+ $configHolder->saveConfiguration();
+ }
+
+ protected function getNewConfigurationPrefix() {
+ $serverConnections = $this->helper->getServerConfigurationPrefixes();
+
+ // first connection uses no prefix
+ if(sizeof($serverConnections) == 0) {
+ return '';
+ }
+
+ sort($serverConnections);
+ $lastKey = array_pop($serverConnections);
+ $lastNumber = intval(str_replace('s', '', $lastKey));
+ $nextPrefix = 's' . str_pad($lastNumber + 1, 2, '0', STR_PAD_LEFT);
+ return $nextPrefix;
+ }
+}
diff --git a/apps/user_ldap/lib/Command/DeleteConfig.php b/apps/user_ldap/lib/Command/DeleteConfig.php
new file mode 100644
index 00000000000..a56753daddc
--- /dev/null
+++ b/apps/user_ldap/lib/Command/DeleteConfig.php
@@ -0,0 +1,68 @@
+<?php
+/**
+ * @author Arthur Schiwon <blizzz@owncloud.com>
+ * @author Martin Konrad <info@martin-konrad.net>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\User_LDAP\Command;
+
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use \OCA\User_LDAP\Helper;
+
+class DeleteConfig extends Command {
+ /** @var \OCA\User_LDAP\Helper */
+ protected $helper;
+
+ /**
+ * @param Helper $helper
+ */
+ public function __construct(Helper $helper) {
+ $this->helper = $helper;
+ parent::__construct();
+ }
+
+ protected function configure() {
+ $this
+ ->setName('ldap:delete-config')
+ ->setDescription('deletes an existing LDAP configuration')
+ ->addArgument(
+ 'configID',
+ InputArgument::REQUIRED,
+ 'the configuration ID'
+ )
+ ;
+ }
+
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $configPrefix = $input->getArgument('configID');
+
+ $success = $this->helper->deleteServerConfiguration($configPrefix);
+
+ if($success) {
+ $output->writeln("Deleted configuration with configID '{$configPrefix}'");
+ } else {
+ $output->writeln("Cannot delete configuration with configID '{$configPrefix}'");
+ }
+ }
+}
diff --git a/apps/user_ldap/lib/Command/Search.php b/apps/user_ldap/lib/Command/Search.php
new file mode 100644
index 00000000000..795530e2044
--- /dev/null
+++ b/apps/user_ldap/lib/Command/Search.php
@@ -0,0 +1,127 @@
+<?php
+/**
+ * @author Arthur Schiwon <blizzz@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\User_LDAP\Command;
+
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+use OCA\User_LDAP\User_Proxy;
+use OCA\User_LDAP\Group_Proxy;
+use OCA\User_LDAP\Helper;
+use OCA\User_LDAP\LDAP;
+use OCP\IConfig;
+
+class Search extends Command {
+ /** @var \OCP\IConfig */
+ protected $ocConfig;
+
+ /**
+ * @param \OCP\IConfig $ocConfig
+ */
+ public function __construct(IConfig $ocConfig) {
+ $this->ocConfig = $ocConfig;
+ parent::__construct();
+ }
+
+ protected function configure() {
+ $this
+ ->setName('ldap:search')
+ ->setDescription('executes a user or group search')
+ ->addArgument(
+ 'search',
+ InputArgument::REQUIRED,
+ 'the search string (can be empty)'
+ )
+ ->addOption(
+ 'group',
+ null,
+ InputOption::VALUE_NONE,
+ 'searches groups instead of users'
+ )
+ ->addOption(
+ 'offset',
+ null,
+ InputOption::VALUE_REQUIRED,
+ 'The offset of the result set. Needs to be a multiple of limit. defaults to 0.',
+ 0
+ )
+ ->addOption(
+ 'limit',
+ null,
+ InputOption::VALUE_REQUIRED,
+ 'limit the results. 0 means no limit, defaults to 15',
+ 15
+ )
+ ;
+ }
+
+ /**
+ * Tests whether the offset and limit options are valid
+ * @param int $offset
+ * @param int $limit
+ * @throws \InvalidArgumentException
+ */
+ protected function validateOffsetAndLimit($offset, $limit) {
+ if($limit < 0) {
+ throw new \InvalidArgumentException('limit must be 0 or greater');
+ }
+ if($offset < 0) {
+ throw new \InvalidArgumentException('offset must be 0 or greater');
+ }
+ if($limit === 0 && $offset !== 0) {
+ throw new \InvalidArgumentException('offset must be 0 if limit is also set to 0');
+ }
+ if($offset > 0 && ($offset % $limit !== 0)) {
+ throw new \InvalidArgumentException('offset must be a multiple of limit');
+ }
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $helper = new Helper();
+ $configPrefixes = $helper->getServerConfigurationPrefixes(true);
+ $ldapWrapper = new LDAP();
+
+ $offset = intval($input->getOption('offset'));
+ $limit = intval($input->getOption('limit'));
+ $this->validateOffsetAndLimit($offset, $limit);
+
+ if($input->getOption('group')) {
+ $proxy = new Group_Proxy($configPrefixes, $ldapWrapper);
+ $getMethod = 'getGroups';
+ $printID = false;
+ } else {
+ $proxy = new User_Proxy($configPrefixes, $ldapWrapper, $this->ocConfig);
+ $getMethod = 'getDisplayNames';
+ $printID = true;
+ }
+
+ $result = $proxy->$getMethod($input->getArgument('search'), $limit, $offset);
+ foreach($result as $id => $name) {
+ $line = $name . ($printID ? ' ('.$id.')' : '');
+ $output->writeln($line);
+ }
+ }
+}
diff --git a/apps/user_ldap/lib/Command/SetConfig.php b/apps/user_ldap/lib/Command/SetConfig.php
new file mode 100644
index 00000000000..d466e1eb769
--- /dev/null
+++ b/apps/user_ldap/lib/Command/SetConfig.php
@@ -0,0 +1,84 @@
+<?php
+/**
+ * @author Arthur Schiwon <blizzz@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\User_LDAP\Command;
+
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use OCA\User_LDAP\Helper;
+use OCA\User_LDAP\Configuration;
+
+class SetConfig extends Command {
+
+ protected function configure() {
+ $this
+ ->setName('ldap:set-config')
+ ->setDescription('modifies an LDAP configuration')
+ ->addArgument(
+ 'configID',
+ InputArgument::REQUIRED,
+ 'the configuration ID'
+ )
+ ->addArgument(
+ 'configKey',
+ InputArgument::REQUIRED,
+ 'the configuration key'
+ )
+ ->addArgument(
+ 'configValue',
+ InputArgument::REQUIRED,
+ 'the new configuration value'
+ )
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $helper = new Helper();
+ $availableConfigs = $helper->getServerConfigurationPrefixes();
+ $configID = $input->getArgument('configID');
+ if(!in_array($configID, $availableConfigs)) {
+ $output->writeln("Invalid configID");
+ return;
+ }
+
+ $this->setValue(
+ $configID,
+ $input->getArgument('configKey'),
+ $input->getArgument('configValue')
+ );
+ }
+
+ /**
+ * save the configuration value as provided
+ * @param string $configID
+ * @param string $configKey
+ * @param string $configValue
+ */
+ protected function setValue($configID, $key, $value) {
+ $configHolder = new Configuration($configID);
+ $configHolder->$key = $value;
+ $configHolder->saveConfiguration();
+ }
+}
diff --git a/apps/user_ldap/lib/Command/ShowConfig.php b/apps/user_ldap/lib/Command/ShowConfig.php
new file mode 100644
index 00000000000..dbd18216f81
--- /dev/null
+++ b/apps/user_ldap/lib/Command/ShowConfig.php
@@ -0,0 +1,108 @@
+<?php
+/**
+ * @author Arthur Schiwon <blizzz@owncloud.com>
+ * @author Laurens Post <Crote@users.noreply.github.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\User_LDAP\Command;
+
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+use OCA\User_LDAP\Helper;
+use OCA\User_LDAP\Configuration;
+
+class ShowConfig extends Command {
+ /** @var \OCA\User_LDAP\Helper */
+ protected $helper;
+
+ /**
+ * @param Helper $helper
+ */
+ public function __construct(Helper $helper) {
+ $this->helper = $helper;
+ parent::__construct();
+ }
+
+ protected function configure() {
+ $this
+ ->setName('ldap:show-config')
+ ->setDescription('shows the LDAP configuration')
+ ->addArgument(
+ 'configID',
+ InputArgument::OPTIONAL,
+ 'will show the configuration of the specified id'
+ )
+ ->addOption(
+ 'show-password',
+ null,
+ InputOption::VALUE_NONE,
+ 'show ldap bind password'
+ )
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $availableConfigs = $this->helper->getServerConfigurationPrefixes();
+ $configID = $input->getArgument('configID');
+ if(!is_null($configID)) {
+ $configIDs[] = $configID;
+ if(!in_array($configIDs[0], $availableConfigs)) {
+ $output->writeln("Invalid configID");
+ return;
+ }
+ } else {
+ $configIDs = $availableConfigs;
+ }
+
+ $this->renderConfigs($configIDs, $output, $input->getOption('show-password'));
+ }
+
+ /**
+ * prints the LDAP configuration(s)
+ * @param string[] configID(s)
+ * @param OutputInterface $output
+ * @param bool $withPassword Set to TRUE to show plaintext passwords in output
+ */
+ protected function renderConfigs($configIDs, $output, $withPassword) {
+ foreach($configIDs as $id) {
+ $configHolder = new Configuration($id);
+ $configuration = $configHolder->getConfiguration();
+ ksort($configuration);
+
+ $table = $this->getHelperSet()->get('table');
+ $table->setHeaders(array('Configuration', $id));
+ $rows = array();
+ foreach($configuration as $key => $value) {
+ if($key === 'ldapAgentPassword' && !$withPassword) {
+ $value = '***';
+ }
+ if(is_array($value)) {
+ $value = implode(';', $value);
+ }
+ $rows[] = array($key, $value);
+ }
+ $table->setRows($rows);
+ $table->render($output);
+ }
+ }
+}
diff --git a/apps/user_ldap/lib/Command/ShowRemnants.php b/apps/user_ldap/lib/Command/ShowRemnants.php
new file mode 100644
index 00000000000..59da6e5522d
--- /dev/null
+++ b/apps/user_ldap/lib/Command/ShowRemnants.php
@@ -0,0 +1,92 @@
+<?php
+/**
+ * @author Arthur Schiwon <blizzz@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author scolebrook <scolebrook@mac.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\User_LDAP\Command;
+
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+use OCA\User_LDAP\User\DeletedUsersIndex;
+use OCP\IDateTimeFormatter;
+
+class ShowRemnants extends Command {
+ /** @var \OCA\User_LDAP\User\DeletedUsersIndex */
+ protected $dui;
+
+ /** @var \OCP\IDateTimeFormatter */
+ protected $dateFormatter;
+
+ /**
+ * @param DeletedUsersIndex $dui
+ * @param IDateTimeFormatter $dateFormatter
+ */
+ public function __construct(DeletedUsersIndex $dui, IDateTimeFormatter $dateFormatter) {
+ $this->dui = $dui;
+ $this->dateFormatter = $dateFormatter;
+ parent::__construct();
+ }
+
+ protected function configure() {
+ $this
+ ->setName('ldap:show-remnants')
+ ->setDescription('shows which users are not available on LDAP anymore, but have remnants in ownCloud.')
+ ->addOption('json', null, InputOption::VALUE_NONE, 'return JSON array instead of pretty table.');
+ }
+
+ /**
+ * executes the command, i.e. creeates and outputs a table of LDAP users marked as deleted
+ *
+ * {@inheritdoc}
+ */
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ /** @var \Symfony\Component\Console\Helper\Table $table */
+ $table = $this->getHelperSet()->get('table');
+ $table->setHeaders(array(
+ 'ownCloud name', 'Display Name', 'LDAP UID', 'LDAP DN', 'Last Login',
+ 'Dir', 'Sharer'));
+ $rows = array();
+ $resultSet = $this->dui->getUsers();
+ foreach($resultSet as $user) {
+ $hAS = $user->getHasActiveShares() ? 'Y' : 'N';
+ $lastLogin = ($user->getLastLogin() > 0) ?
+ $this->dateFormatter->formatDate($user->getLastLogin()) : '-';
+ $rows[] = array('ocName' => $user->getOCName(),
+ 'displayName' => $user->getDisplayName(),
+ 'uid' => $user->getUID(),
+ 'dn' => $user->getDN(),
+ 'lastLogin' => $lastLogin,
+ 'homePath' => $user->getHomePath(),
+ 'sharer' => $hAS
+ );
+ }
+
+ if ($input->getOption('json')) {
+ $output->writeln(json_encode($rows));
+ } else {
+ $table->setRows($rows);
+ $table->render($output);
+ }
+ }
+}
diff --git a/apps/user_ldap/lib/Command/TestConfig.php b/apps/user_ldap/lib/Command/TestConfig.php
new file mode 100644
index 00000000000..93a9f644714
--- /dev/null
+++ b/apps/user_ldap/lib/Command/TestConfig.php
@@ -0,0 +1,90 @@
+<?php
+/**
+ * @author Arthur Schiwon <blizzz@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\User_LDAP\Command;
+
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use \OCA\User_LDAP\Helper;
+use \OCA\User_LDAP\Connection;
+
+class TestConfig extends Command {
+
+ protected function configure() {
+ $this
+ ->setName('ldap:test-config')
+ ->setDescription('tests an LDAP configuration')
+ ->addArgument(
+ 'configID',
+ InputArgument::REQUIRED,
+ 'the configuration ID'
+ )
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $helper = new Helper();
+ $availableConfigs = $helper->getServerConfigurationPrefixes();
+ $configID = $input->getArgument('configID');
+ if(!in_array($configID, $availableConfigs)) {
+ $output->writeln("Invalid configID");
+ return;
+ }
+
+ $result = $this->testConfig($configID);
+ if($result === 0) {
+ $output->writeln('The configuration is valid and the connection could be established!');
+ } else if($result === 1) {
+ $output->writeln('The configuration is invalid. Please have a look at the logs for further details.');
+ } else if($result === 2) {
+ $output->writeln('The configuration is valid, but the Bind failed. Please check the server settings and credentials.');
+ } else {
+ $output->writeln('Your LDAP server was kidnapped by aliens.');
+ }
+ }
+
+ /**
+ * tests the specified connection
+ * @param string $configID
+ * @return int
+ */
+ protected function testConfig($configID) {
+ $lw = new \OCA\User_LDAP\LDAP();
+ $connection = new Connection($lw, $configID);
+
+ //ensure validation is run before we attempt the bind
+ $connection->getConfiguration();
+
+ if(!$connection->setConfiguration(array(
+ 'ldap_configuration_active' => 1,
+ ))) {
+ return 1;
+ }
+ if($connection->bind()) {
+ return 0;
+ }
+ return 2;
+ }
+}
diff --git a/apps/user_ldap/lib/configuration.php b/apps/user_ldap/lib/Configuration.php
index 418a2bfc015..476483ecc2f 100644
--- a/apps/user_ldap/lib/configuration.php
+++ b/apps/user_ldap/lib/Configuration.php
@@ -26,7 +26,7 @@
*
*/
-namespace OCA\user_ldap\lib;
+namespace OCA\User_LDAP;
/**
* @property int ldapPagingSize holds an integer
diff --git a/apps/user_ldap/lib/connection.php b/apps/user_ldap/lib/Connection.php
index 93e7e4bf974..1cf02a0a898 100644
--- a/apps/user_ldap/lib/connection.php
+++ b/apps/user_ldap/lib/Connection.php
@@ -26,7 +26,7 @@
*
*/
-namespace OCA\user_ldap\lib;
+namespace OCA\User_LDAP;
use OC\ServerNotAvailableException;
diff --git a/apps/user_ldap/lib/filesystemhelper.php b/apps/user_ldap/lib/FilesystemHelper.php
index 03f4c4274f4..439e7fb1d0b 100644
--- a/apps/user_ldap/lib/filesystemhelper.php
+++ b/apps/user_ldap/lib/FilesystemHelper.php
@@ -21,7 +21,7 @@
*
*/
-namespace OCA\user_ldap\lib;
+namespace OCA\User_LDAP;
/**
* @brief wraps around static ownCloud core methods
diff --git a/apps/user_ldap/lib/Group_LDAP.php b/apps/user_ldap/lib/Group_LDAP.php
new file mode 100644
index 00000000000..39b2e9ff446
--- /dev/null
+++ b/apps/user_ldap/lib/Group_LDAP.php
@@ -0,0 +1,899 @@
+<?php
+/**
+ * @author Alex Weirig <alex.weirig@technolink.lu>
+ * @author Alexander Bergolth <leo@strike.wu.ac.at>
+ * @author alexweirig <alex.weirig@technolink.lu>
+ * @author Andreas Fischer <bantu@owncloud.com>
+ * @author Arthur Schiwon <blizzz@owncloud.com>
+ * @author Bart Visscher <bartv@thisnet.nl>
+ * @author Christopher Schäpers <kondou@ts.unde.re>
+ * @author Frédéric Fortier <frederic.fortier@oronospolytechnique.com>
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Nicolas Grekas <nicolas.grekas@gmail.com>
+ * @author Robin McCorkell <robin@mccorkell.me.uk>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\User_LDAP;
+
+
+class Group_LDAP extends BackendUtility implements \OCP\GroupInterface {
+ protected $enabled = false;
+
+ /**
+ * @var string[] $cachedGroupMembers array of users with gid as key
+ */
+ protected $cachedGroupMembers = array();
+
+ /**
+ * @var string[] $cachedGroupsByMember array of groups with uid as key
+ */
+ protected $cachedGroupsByMember = array();
+
+ public function __construct(Access $access) {
+ parent::__construct($access);
+ $filter = $this->access->connection->ldapGroupFilter;
+ $gassoc = $this->access->connection->ldapGroupMemberAssocAttr;
+ if(!empty($filter) && !empty($gassoc)) {
+ $this->enabled = true;
+ }
+ }
+
+ /**
+ * is user in group?
+ * @param string $uid uid of the user
+ * @param string $gid gid of the group
+ * @return bool
+ *
+ * Checks whether the user is member of a group or not.
+ */
+ public function inGroup($uid, $gid) {
+ if(!$this->enabled) {
+ return false;
+ }
+ $cacheKey = 'inGroup'.$uid.':'.$gid;
+ $inGroup = $this->access->connection->getFromCache($cacheKey);
+ if(!is_null($inGroup)) {
+ return (bool)$inGroup;
+ }
+
+ $userDN = $this->access->username2dn($uid);
+
+ if(isset($this->cachedGroupMembers[$gid])) {
+ $isInGroup = in_array($userDN, $this->cachedGroupMembers[$gid]);
+ return $isInGroup;
+ }
+
+ $cacheKeyMembers = 'inGroup-members:'.$gid;
+ $members = $this->access->connection->getFromCache($cacheKeyMembers);
+ if(!is_null($members)) {
+ $this->cachedGroupMembers[$gid] = $members;
+ $isInGroup = in_array($userDN, $members);
+ $this->access->connection->writeToCache($cacheKey, $isInGroup);
+ return $isInGroup;
+ }
+
+ $groupDN = $this->access->groupname2dn($gid);
+ // just in case
+ if(!$groupDN || !$userDN) {
+ $this->access->connection->writeToCache($cacheKey, false);
+ return false;
+ }
+
+ //check primary group first
+ if($gid === $this->getUserPrimaryGroup($userDN)) {
+ $this->access->connection->writeToCache($cacheKey, true);
+ return true;
+ }
+
+ //usually, LDAP attributes are said to be case insensitive. But there are exceptions of course.
+ $members = $this->_groupMembers($groupDN);
+ $members = array_keys($members); // uids are returned as keys
+ if(!is_array($members) || count($members) === 0) {
+ $this->access->connection->writeToCache($cacheKey, false);
+ return false;
+ }
+
+ //extra work if we don't get back user DNs
+ if(strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'memberuid') {
+ $dns = array();
+ $filterParts = array();
+ $bytes = 0;
+ foreach($members as $mid) {
+ $filter = str_replace('%uid', $mid, $this->access->connection->ldapLoginFilter);
+ $filterParts[] = $filter;
+ $bytes += strlen($filter);
+ if($bytes >= 9000000) {
+ // AD has a default input buffer of 10 MB, we do not want
+ // to take even the chance to exceed it
+ $filter = $this->access->combineFilterWithOr($filterParts);
+ $bytes = 0;
+ $filterParts = array();
+ $users = $this->access->fetchListOfUsers($filter, 'dn', count($filterParts));
+ $dns = array_merge($dns, $users);
+ }
+ }
+ if(count($filterParts) > 0) {
+ $filter = $this->access->combineFilterWithOr($filterParts);
+ $users = $this->access->fetchListOfUsers($filter, 'dn', count($filterParts));
+ $dns = array_merge($dns, $users);
+ }
+ $members = $dns;
+ }
+
+ $isInGroup = in_array($userDN, $members);
+ $this->access->connection->writeToCache($cacheKey, $isInGroup);
+ $this->access->connection->writeToCache($cacheKeyMembers, $members);
+ $this->cachedGroupMembers[$gid] = $members;
+
+ return $isInGroup;
+ }
+
+ /**
+ * @param string $dnGroup
+ * @return array
+ *
+ * For a group that has user membership defined by an LDAP search url attribute returns the users
+ * that match the search url otherwise returns an empty array.
+ */
+ public function getDynamicGroupMembers($dnGroup) {
+ $dynamicGroupMemberURL = strtolower($this->access->connection->ldapDynamicGroupMemberURL);
+
+ if (empty($dynamicGroupMemberURL)) {
+ return array();
+ }
+
+ $dynamicMembers = array();
+ $memberURLs = $this->access->readAttribute(
+ $dnGroup,
+ $dynamicGroupMemberURL,
+ $this->access->connection->ldapGroupFilter
+ );
+ if ($memberURLs !== false) {
+ // this group has the 'memberURL' attribute so this is a dynamic group
+ // example 1: ldap:///cn=users,cn=accounts,dc=dcsubbase,dc=dcbase??one?(o=HeadOffice)
+ // example 2: ldap:///cn=users,cn=accounts,dc=dcsubbase,dc=dcbase??one?(&(o=HeadOffice)(uidNumber>=500))
+ $pos = strpos($memberURLs[0], '(');
+ if ($pos !== false) {
+ $memberUrlFilter = substr($memberURLs[0], $pos);
+ $foundMembers = $this->access->searchUsers($memberUrlFilter,'dn');
+ $dynamicMembers = array();
+ foreach($foundMembers as $value) {
+ $dynamicMembers[$value['dn'][0]] = 1;
+ }
+ } else {
+ \OCP\Util::writeLog('user_ldap', 'No search filter found on member url '.
+ 'of group ' . $dnGroup, \OCP\Util::DEBUG);
+ }
+ }
+ return $dynamicMembers;
+ }
+
+ /**
+ * @param string $dnGroup
+ * @param array|null &$seen
+ * @return array|mixed|null
+ */
+ private function _groupMembers($dnGroup, &$seen = null) {
+ if ($seen === null) {
+ $seen = array();
+ }
+ $allMembers = array();
+ if (array_key_exists($dnGroup, $seen)) {
+ // avoid loops
+ return array();
+ }
+ // used extensively in cron job, caching makes sense for nested groups
+ $cacheKey = '_groupMembers'.$dnGroup;
+ $groupMembers = $this->access->connection->getFromCache($cacheKey);
+ if(!is_null($groupMembers)) {
+ return $groupMembers;
+ }
+ $seen[$dnGroup] = 1;
+ $members = $this->access->readAttribute($dnGroup, $this->access->connection->ldapGroupMemberAssocAttr,
+ $this->access->connection->ldapGroupFilter);
+ if (is_array($members)) {
+ foreach ($members as $memberDN) {
+ $allMembers[$memberDN] = 1;
+ $nestedGroups = $this->access->connection->ldapNestedGroups;
+ if (!empty($nestedGroups)) {
+ $subMembers = $this->_groupMembers($memberDN, $seen);
+ if ($subMembers) {
+ $allMembers = array_merge($allMembers, $subMembers);
+ }
+ }
+ }
+ }
+
+ $allMembers = array_merge($allMembers, $this->getDynamicGroupMembers($dnGroup));
+
+ $this->access->connection->writeToCache($cacheKey, $allMembers);
+ return $allMembers;
+ }
+
+ /**
+ * @param string $DN
+ * @param array|null &$seen
+ * @return array
+ */
+ private function _getGroupDNsFromMemberOf($DN, &$seen = null) {
+ if ($seen === null) {
+ $seen = array();
+ }
+ if (array_key_exists($DN, $seen)) {
+ // avoid loops
+ return array();
+ }
+ $seen[$DN] = 1;
+ $groups = $this->access->readAttribute($DN, 'memberOf');
+ if (!is_array($groups)) {
+ return array();
+ }
+ $groups = $this->access->groupsMatchFilter($groups);
+ $allGroups = $groups;
+ $nestedGroups = $this->access->connection->ldapNestedGroups;
+ if (intval($nestedGroups) === 1) {
+ foreach ($groups as $group) {
+ $subGroups = $this->_getGroupDNsFromMemberOf($group, $seen);
+ $allGroups = array_merge($allGroups, $subGroups);
+ }
+ }
+ return $allGroups;
+ }
+
+ /**
+ * translates a primary group ID into an ownCloud internal name
+ * @param string $gid as given by primaryGroupID on AD
+ * @param string $dn a DN that belongs to the same domain as the group
+ * @return string|bool
+ */
+ public function primaryGroupID2Name($gid, $dn) {
+ $cacheKey = 'primaryGroupIDtoName';
+ $groupNames = $this->access->connection->getFromCache($cacheKey);
+ if(!is_null($groupNames) && isset($groupNames[$gid])) {
+ return $groupNames[$gid];
+ }
+
+ $domainObjectSid = $this->access->getSID($dn);
+ if($domainObjectSid === false) {
+ return false;
+ }
+
+ //we need to get the DN from LDAP
+ $filter = $this->access->combineFilterWithAnd(array(
+ $this->access->connection->ldapGroupFilter,
+ 'objectsid=' . $domainObjectSid . '-' . $gid
+ ));
+ $result = $this->access->searchGroups($filter, array('dn'), 1);
+ if(empty($result)) {
+ return false;
+ }
+ $dn = $result[0]['dn'][0];
+
+ //and now the group name
+ //NOTE once we have separate ownCloud group IDs and group names we can
+ //directly read the display name attribute instead of the DN
+ $name = $this->access->dn2groupname($dn);
+
+ $this->access->connection->writeToCache($cacheKey, $name);
+
+ return $name;
+ }
+
+ /**
+ * returns the entry's primary group ID
+ * @param string $dn
+ * @param string $attribute
+ * @return string|bool
+ */
+ private function getEntryGroupID($dn, $attribute) {
+ $value = $this->access->readAttribute($dn, $attribute);
+ if(is_array($value) && !empty($value)) {
+ return $value[0];
+ }
+ return false;
+ }
+
+ /**
+ * returns the group's primary ID
+ * @param string $dn
+ * @return string|bool
+ */
+ public function getGroupPrimaryGroupID($dn) {
+ return $this->getEntryGroupID($dn, 'primaryGroupToken');
+ }
+
+ /**
+ * returns the user's primary group ID
+ * @param string $dn
+ * @return string|bool
+ */
+ public function getUserPrimaryGroupIDs($dn) {
+ $primaryGroupID = false;
+ if($this->access->connection->hasPrimaryGroups) {
+ $primaryGroupID = $this->getEntryGroupID($dn, 'primaryGroupID');
+ if($primaryGroupID === false) {
+ $this->access->connection->hasPrimaryGroups = false;
+ }
+ }
+ return $primaryGroupID;
+ }
+
+ /**
+ * returns a filter for a "users in primary group" search or count operation
+ *
+ * @param string $groupDN
+ * @param string $search
+ * @return string
+ * @throws \Exception
+ */
+ private function prepareFilterForUsersInPrimaryGroup($groupDN, $search = '') {
+ $groupID = $this->getGroupPrimaryGroupID($groupDN);
+ if($groupID === false) {
+ throw new \Exception('Not a valid group');
+ }
+
+ $filterParts = [];
+ $filterParts[] = $this->access->getFilterForUserCount();
+ if(!empty($search)) {
+ $filterParts[] = $this->access->getFilterPartForUserSearch($search);
+ }
+ $filterParts[] = 'primaryGroupID=' . $groupID;
+
+ $filter = $this->access->combineFilterWithAnd($filterParts);
+
+ return $filter;
+ }
+
+ /**
+ * returns a list of users that have the given group as primary group
+ *
+ * @param string $groupDN
+ * @param string $search
+ * @param int $limit
+ * @param int $offset
+ * @return string[]
+ */
+ public function getUsersInPrimaryGroup($groupDN, $search = '', $limit = -1, $offset = 0) {
+ try {
+ $filter = $this->prepareFilterForUsersInPrimaryGroup($groupDN, $search);
+ $users = $this->access->fetchListOfUsers(
+ $filter,
+ array($this->access->connection->ldapUserDisplayName, 'dn'),
+ $limit,
+ $offset
+ );
+ return $this->access->ownCloudUserNames($users);
+ } catch (\Exception $e) {
+ return array();
+ }
+ }
+
+ /**
+ * returns the number of users that have the given group as primary group
+ *
+ * @param string $groupDN
+ * @param string $search
+ * @param int $limit
+ * @param int $offset
+ * @return int
+ */
+ public function countUsersInPrimaryGroup($groupDN, $search = '', $limit = -1, $offset = 0) {
+ try {
+ $filter = $this->prepareFilterForUsersInPrimaryGroup($groupDN, $search);
+ $users = $this->access->countUsers($filter, array('dn'), $limit, $offset);
+ return (int)$users;
+ } catch (\Exception $e) {
+ return 0;
+ }
+ }
+
+ /**
+ * gets the primary group of a user
+ * @param string $dn
+ * @return string
+ */
+ public function getUserPrimaryGroup($dn) {
+ $groupID = $this->getUserPrimaryGroupIDs($dn);
+ if($groupID !== false) {
+ $groupName = $this->primaryGroupID2Name($groupID, $dn);
+ if($groupName !== false) {
+ return $groupName;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Get all groups a user belongs to
+ * @param string $uid Name of the user
+ * @return array with group names
+ *
+ * This function fetches all groups a user belongs to. It does not check
+ * if the user exists at all.
+ *
+ * This function includes groups based on dynamic group membership.
+ */
+ public function getUserGroups($uid) {
+ if(!$this->enabled) {
+ return array();
+ }
+ $cacheKey = 'getUserGroups'.$uid;
+ $userGroups = $this->access->connection->getFromCache($cacheKey);
+ if(!is_null($userGroups)) {
+ return $userGroups;
+ }
+ $userDN = $this->access->username2dn($uid);
+ if(!$userDN) {
+ $this->access->connection->writeToCache($cacheKey, array());
+ return array();
+ }
+
+ $groups = [];
+ $primaryGroup = $this->getUserPrimaryGroup($userDN);
+
+ $dynamicGroupMemberURL = strtolower($this->access->connection->ldapDynamicGroupMemberURL);
+
+ if (!empty($dynamicGroupMemberURL)) {
+ // look through dynamic groups to add them to the result array if needed
+ $groupsToMatch = $this->access->fetchListOfGroups(
+ $this->access->connection->ldapGroupFilter,array('dn',$dynamicGroupMemberURL));
+ foreach($groupsToMatch as $dynamicGroup) {
+ if (!array_key_exists($dynamicGroupMemberURL, $dynamicGroup)) {
+ continue;
+ }
+ $pos = strpos($dynamicGroup[$dynamicGroupMemberURL][0], '(');
+ if ($pos !== false) {
+ $memberUrlFilter = substr($dynamicGroup[$dynamicGroupMemberURL][0],$pos);
+ // apply filter via ldap search to see if this user is in this
+ // dynamic group
+ $userMatch = $this->access->readAttribute(
+ $userDN,
+ $this->access->connection->ldapUserDisplayName,
+ $memberUrlFilter
+ );
+ if ($userMatch !== false) {
+ // match found so this user is in this group
+ $groupName = $this->access->dn2groupname($dynamicGroup['dn'][0]);
+ if(is_string($groupName)) {
+ // be sure to never return false if the dn could not be
+ // resolved to a name, for whatever reason.
+ $groups[] = $groupName;
+ }
+ }
+ } else {
+ \OCP\Util::writeLog('user_ldap', 'No search filter found on member url '.
+ 'of group ' . print_r($dynamicGroup, true), \OCP\Util::DEBUG);
+ }
+ }
+ }
+
+ // if possible, read out membership via memberOf. It's far faster than
+ // performing a search, which still is a fallback later.
+ if(intval($this->access->connection->hasMemberOfFilterSupport) === 1
+ && intval($this->access->connection->useMemberOfToDetectMembership) === 1
+ ) {
+ $groupDNs = $this->_getGroupDNsFromMemberOf($userDN);
+ if (is_array($groupDNs)) {
+ foreach ($groupDNs as $dn) {
+ $groupName = $this->access->dn2groupname($dn);
+ if(is_string($groupName)) {
+ // be sure to never return false if the dn could not be
+ // resolved to a name, for whatever reason.
+ $groups[] = $groupName;
+ }
+ }
+ }
+
+ if($primaryGroup !== false) {
+ $groups[] = $primaryGroup;
+ }
+ $this->access->connection->writeToCache($cacheKey, $groups);
+ return $groups;
+ }
+
+ //uniqueMember takes DN, memberuid the uid, so we need to distinguish
+ if((strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'uniquemember')
+ || (strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'member')
+ ) {
+ $uid = $userDN;
+ } else if(strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'memberuid') {
+ $result = $this->access->readAttribute($userDN, 'uid');
+ if ($result === false) {
+ \OCP\Util::writeLog('user_ldap', 'No uid attribute found for DN ' . $userDN . ' on '.
+ $this->access->connection->ldapHost, \OCP\Util::DEBUG);
+ }
+ $uid = $result[0];
+ } else {
+ // just in case
+ $uid = $userDN;
+ }
+
+ if(isset($this->cachedGroupsByMember[$uid])) {
+ $groups[] = $this->cachedGroupsByMember[$uid];
+ } else {
+ $groupsByMember = array_values($this->getGroupsByMember($uid));
+ $groupsByMember = $this->access->ownCloudGroupNames($groupsByMember);
+ $this->cachedGroupsByMember[$uid] = $groupsByMember;
+ $groups = array_merge($groups, $groupsByMember);
+ }
+
+ if($primaryGroup !== false) {
+ $groups[] = $primaryGroup;
+ }
+
+ $groups = array_unique($groups, SORT_LOCALE_STRING);
+ $this->access->connection->writeToCache($cacheKey, $groups);
+
+ return $groups;
+ }
+
+ /**
+ * @param string $dn
+ * @param array|null &$seen
+ * @return array
+ */
+ private function getGroupsByMember($dn, &$seen = null) {
+ if ($seen === null) {
+ $seen = array();
+ }
+ $allGroups = array();
+ if (array_key_exists($dn, $seen)) {
+ // avoid loops
+ return array();
+ }
+ $seen[$dn] = true;
+ $filter = $this->access->combineFilterWithAnd(array(
+ $this->access->connection->ldapGroupFilter,
+ $this->access->connection->ldapGroupMemberAssocAttr.'='.$dn
+ ));
+ $groups = $this->access->fetchListOfGroups($filter,
+ array($this->access->connection->ldapGroupDisplayName, 'dn'));
+ if (is_array($groups)) {
+ foreach ($groups as $groupobj) {
+ $groupDN = $groupobj['dn'][0];
+ $allGroups[$groupDN] = $groupobj;
+ $nestedGroups = $this->access->connection->ldapNestedGroups;
+ if (!empty($nestedGroups)) {
+ $supergroups = $this->getGroupsByMember($groupDN, $seen);
+ if (is_array($supergroups) && (count($supergroups)>0)) {
+ $allGroups = array_merge($allGroups, $supergroups);
+ }
+ }
+ }
+ }
+ return $allGroups;
+ }
+
+ /**
+ * get a list of all users in a group
+ *
+ * @param string $gid
+ * @param string $search
+ * @param int $limit
+ * @param int $offset
+ * @return array with user ids
+ */
+ public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) {
+ if(!$this->enabled) {
+ return array();
+ }
+ if(!$this->groupExists($gid)) {
+ return array();
+ }
+ $search = $this->access->escapeFilterPart($search, true);
+ $cacheKey = 'usersInGroup-'.$gid.'-'.$search.'-'.$limit.'-'.$offset;
+ // check for cache of the exact query
+ $groupUsers = $this->access->connection->getFromCache($cacheKey);
+ if(!is_null($groupUsers)) {
+ return $groupUsers;
+ }
+
+ // check for cache of the query without limit and offset
+ $groupUsers = $this->access->connection->getFromCache('usersInGroup-'.$gid.'-'.$search);
+ if(!is_null($groupUsers)) {
+ $groupUsers = array_slice($groupUsers, $offset, $limit);
+ $this->access->connection->writeToCache($cacheKey, $groupUsers);
+ return $groupUsers;
+ }
+
+ if($limit === -1) {
+ $limit = null;
+ }
+ $groupDN = $this->access->groupname2dn($gid);
+ if(!$groupDN) {
+ // group couldn't be found, return empty resultset
+ $this->access->connection->writeToCache($cacheKey, array());
+ return array();
+ }
+
+ $primaryUsers = $this->getUsersInPrimaryGroup($groupDN, $search, $limit, $offset);
+ $members = array_keys($this->_groupMembers($groupDN));
+ if(!$members && empty($primaryUsers)) {
+ //in case users could not be retrieved, return empty result set
+ $this->access->connection->writeToCache($cacheKey, array());
+ return array();
+ }
+
+ $groupUsers = array();
+ $isMemberUid = (strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'memberuid');
+ $attrs = $this->access->userManager->getAttributes(true);
+ foreach($members as $member) {
+ if($isMemberUid) {
+ //we got uids, need to get their DNs to 'translate' them to user names
+ $filter = $this->access->combineFilterWithAnd(array(
+ str_replace('%uid', $member, $this->access->connection->ldapLoginFilter),
+ $this->access->getFilterPartForUserSearch($search)
+ ));
+ $ldap_users = $this->access->fetchListOfUsers($filter, $attrs, 1);
+ if(count($ldap_users) < 1) {
+ continue;
+ }
+ $groupUsers[] = $this->access->dn2username($ldap_users[0]['dn'][0]);
+ } else {
+ //we got DNs, check if we need to filter by search or we can give back all of them
+ if(!empty($search)) {
+ if(!$this->access->readAttribute($member,
+ $this->access->connection->ldapUserDisplayName,
+ $this->access->getFilterPartForUserSearch($search))) {
+ continue;
+ }
+ }
+ // dn2username will also check if the users belong to the allowed base
+ if($ocname = $this->access->dn2username($member)) {
+ $groupUsers[] = $ocname;
+ }
+ }
+ }
+
+ $groupUsers = array_unique(array_merge($groupUsers, $primaryUsers));
+ natsort($groupUsers);
+ $this->access->connection->writeToCache('usersInGroup-'.$gid.'-'.$search, $groupUsers);
+ $groupUsers = array_slice($groupUsers, $offset, $limit);
+
+
+ $this->access->connection->writeToCache($cacheKey, $groupUsers);
+
+ return $groupUsers;
+ }
+
+ /**
+ * returns the number of users in a group, who match the search term
+ * @param string $gid the internal group name
+ * @param string $search optional, a search string
+ * @return int|bool
+ */
+ public function countUsersInGroup($gid, $search = '') {
+ $cacheKey = 'countUsersInGroup-'.$gid.'-'.$search;
+ if(!$this->enabled || !$this->groupExists($gid)) {
+ return false;
+ }
+ $groupUsers = $this->access->connection->getFromCache($cacheKey);
+ if(!is_null($groupUsers)) {
+ return $groupUsers;
+ }
+
+ $groupDN = $this->access->groupname2dn($gid);
+ if(!$groupDN) {
+ // group couldn't be found, return empty result set
+ $this->access->connection->writeToCache($cacheKey, false);
+ return false;
+ }
+
+ $members = array_keys($this->_groupMembers($groupDN));
+ $primaryUserCount = $this->countUsersInPrimaryGroup($groupDN, '');
+ if(!$members && $primaryUserCount === 0) {
+ //in case users could not be retrieved, return empty result set
+ $this->access->connection->writeToCache($cacheKey, false);
+ return false;
+ }
+
+ if(empty($search)) {
+ $groupUsers = count($members) + $primaryUserCount;
+ $this->access->connection->writeToCache($cacheKey, $groupUsers);
+ return $groupUsers;
+ }
+ $search = $this->access->escapeFilterPart($search, true);
+ $isMemberUid =
+ (strtolower($this->access->connection->ldapGroupMemberAssocAttr)
+ === 'memberuid');
+
+ //we need to apply the search filter
+ //alternatives that need to be checked:
+ //a) get all users by search filter and array_intersect them
+ //b) a, but only when less than 1k 10k ?k users like it is
+ //c) put all DNs|uids in a LDAP filter, combine with the search string
+ // and let it count.
+ //For now this is not important, because the only use of this method
+ //does not supply a search string
+ $groupUsers = array();
+ foreach($members as $member) {
+ if($isMemberUid) {
+ //we got uids, need to get their DNs to 'translate' them to user names
+ $filter = $this->access->combineFilterWithAnd(array(
+ str_replace('%uid', $member, $this->access->connection->ldapLoginFilter),
+ $this->access->getFilterPartForUserSearch($search)
+ ));
+ $ldap_users = $this->access->fetchListOfUsers($filter, 'dn', 1);
+ if(count($ldap_users) < 1) {
+ continue;
+ }
+ $groupUsers[] = $this->access->dn2username($ldap_users[0]);
+ } else {
+ //we need to apply the search filter now
+ if(!$this->access->readAttribute($member,
+ $this->access->connection->ldapUserDisplayName,
+ $this->access->getFilterPartForUserSearch($search))) {
+ continue;
+ }
+ // dn2username will also check if the users belong to the allowed base
+ if($ocname = $this->access->dn2username($member)) {
+ $groupUsers[] = $ocname;
+ }
+ }
+ }
+
+ //and get users that have the group as primary
+ $primaryUsers = $this->countUsersInPrimaryGroup($groupDN, $search);
+
+ return count($groupUsers) + $primaryUsers;
+ }
+
+ /**
+ * get a list of all groups
+ *
+ * @param string $search
+ * @param $limit
+ * @param int $offset
+ * @return array with group names
+ *
+ * Returns a list with all groups (used by getGroups)
+ */
+ protected function getGroupsChunk($search = '', $limit = -1, $offset = 0) {
+ if(!$this->enabled) {
+ return array();
+ }
+ $cacheKey = 'getGroups-'.$search.'-'.$limit.'-'.$offset;
+
+ //Check cache before driving unnecessary searches
+ \OCP\Util::writeLog('user_ldap', 'getGroups '.$cacheKey, \OCP\Util::DEBUG);
+ $ldap_groups = $this->access->connection->getFromCache($cacheKey);
+ if(!is_null($ldap_groups)) {
+ return $ldap_groups;
+ }
+
+ // if we'd pass -1 to LDAP search, we'd end up in a Protocol
+ // error. With a limit of 0, we get 0 results. So we pass null.
+ if($limit <= 0) {
+ $limit = null;
+ }
+ $filter = $this->access->combineFilterWithAnd(array(
+ $this->access->connection->ldapGroupFilter,
+ $this->access->getFilterPartForGroupSearch($search)
+ ));
+ \OCP\Util::writeLog('user_ldap', 'getGroups Filter '.$filter, \OCP\Util::DEBUG);
+ $ldap_groups = $this->access->fetchListOfGroups($filter,
+ array($this->access->connection->ldapGroupDisplayName, 'dn'),
+ $limit,
+ $offset);
+ $ldap_groups = $this->access->ownCloudGroupNames($ldap_groups);
+
+ $this->access->connection->writeToCache($cacheKey, $ldap_groups);
+ return $ldap_groups;
+ }
+
+ /**
+ * get a list of all groups using a paged search
+ *
+ * @param string $search
+ * @param int $limit
+ * @param int $offset
+ * @return array with group names
+ *
+ * Returns a list with all groups
+ * Uses a paged search if available to override a
+ * server side search limit.
+ * (active directory has a limit of 1000 by default)
+ */
+ public function getGroups($search = '', $limit = -1, $offset = 0) {
+ if(!$this->enabled) {
+ return array();
+ }
+ $search = $this->access->escapeFilterPart($search, true);
+ $pagingSize = $this->access->connection->ldapPagingSize;
+ if ((! $this->access->connection->hasPagedResultSupport)
+ || empty($pagingSize)) {
+ return $this->getGroupsChunk($search, $limit, $offset);
+ }
+ $maxGroups = 100000; // limit max results (just for safety reasons)
+ if ($limit > -1) {
+ $overallLimit = min($limit + $offset, $maxGroups);
+ } else {
+ $overallLimit = $maxGroups;
+ }
+ $chunkOffset = $offset;
+ $allGroups = array();
+ while ($chunkOffset < $overallLimit) {
+ $chunkLimit = min($pagingSize, $overallLimit - $chunkOffset);
+ $ldapGroups = $this->getGroupsChunk($search, $chunkLimit, $chunkOffset);
+ $nread = count($ldapGroups);
+ \OCP\Util::writeLog('user_ldap', 'getGroups('.$search.'): read '.$nread.' at offset '.$chunkOffset.' (limit: '.$chunkLimit.')', \OCP\Util::DEBUG);
+ if ($nread) {
+ $allGroups = array_merge($allGroups, $ldapGroups);
+ $chunkOffset += $nread;
+ }
+ if ($nread < $chunkLimit) {
+ break;
+ }
+ }
+ return $allGroups;
+ }
+
+ /**
+ * @param string $group
+ * @return bool
+ */
+ public function groupMatchesFilter($group) {
+ return (strripos($group, $this->groupSearch) !== false);
+ }
+
+ /**
+ * check if a group exists
+ * @param string $gid
+ * @return bool
+ */
+ public function groupExists($gid) {
+ $groupExists = $this->access->connection->getFromCache('groupExists'.$gid);
+ if(!is_null($groupExists)) {
+ return (bool)$groupExists;
+ }
+
+ //getting dn, if false the group does not exist. If dn, it may be mapped
+ //only, requires more checking.
+ $dn = $this->access->groupname2dn($gid);
+ if(!$dn) {
+ $this->access->connection->writeToCache('groupExists'.$gid, false);
+ return false;
+ }
+
+ //if group really still exists, we will be able to read its objectclass
+ if(!is_array($this->access->readAttribute($dn, ''))) {
+ $this->access->connection->writeToCache('groupExists'.$gid, false);
+ return false;
+ }
+
+ $this->access->connection->writeToCache('groupExists'.$gid, true);
+ return true;
+ }
+
+ /**
+ * Check if backend implements actions
+ * @param int $actions bitwise-or'ed actions
+ * @return boolean
+ *
+ * Returns the supported actions as int to be
+ * compared with OC_USER_BACKEND_CREATE_USER etc.
+ */
+ public function implementsActions($actions) {
+ return (bool)(\OC\Group\Backend::COUNT_USERS & $actions);
+ }
+}
diff --git a/apps/user_ldap/lib/Group_Proxy.php b/apps/user_ldap/lib/Group_Proxy.php
new file mode 100644
index 00000000000..333a74bab22
--- /dev/null
+++ b/apps/user_ldap/lib/Group_Proxy.php
@@ -0,0 +1,197 @@
+<?php
+/**
+ * @author Arthur Schiwon <blizzz@owncloud.com>
+ * @author Christopher Schäpers <kondou@ts.unde.re>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Robin McCorkell <robin@mccorkell.me.uk>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\User_LDAP;
+
+class Group_Proxy extends Proxy implements \OCP\GroupInterface {
+ private $backends = array();
+ private $refBackend = null;
+
+ /**
+ * Constructor
+ * @param string[] $serverConfigPrefixes array containing the config Prefixes
+ */
+ public function __construct($serverConfigPrefixes, ILDAPWrapper $ldap) {
+ parent::__construct($ldap);
+ foreach($serverConfigPrefixes as $configPrefix) {
+ $this->backends[$configPrefix] =
+ new \OCA\User_LDAP\Group_LDAP($this->getAccess($configPrefix));
+ if(is_null($this->refBackend)) {
+ $this->refBackend = &$this->backends[$configPrefix];
+ }
+ }
+ }
+
+ /**
+ * Tries the backends one after the other until a positive result is returned from the specified method
+ * @param string $gid the gid connected to the request
+ * @param string $method the method of the group backend that shall be called
+ * @param array $parameters an array of parameters to be passed
+ * @return mixed, the result of the method or false
+ */
+ protected function walkBackends($gid, $method, $parameters) {
+ $cacheKey = $this->getGroupCacheKey($gid);
+ foreach($this->backends as $configPrefix => $backend) {
+ if($result = call_user_func_array(array($backend, $method), $parameters)) {
+ $this->writeToCache($cacheKey, $configPrefix);
+ return $result;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Asks the backend connected to the server that supposely takes care of the gid from the request.
+ * @param string $gid the gid connected to the request
+ * @param string $method the method of the group backend that shall be called
+ * @param array $parameters an array of parameters to be passed
+ * @param mixed $passOnWhen the result matches this variable
+ * @return mixed, the result of the method or false
+ */
+ protected function callOnLastSeenOn($gid, $method, $parameters, $passOnWhen) {
+ $cacheKey = $this->getGroupCacheKey($gid);;
+ $prefix = $this->getFromCache($cacheKey);
+ //in case the uid has been found in the past, try this stored connection first
+ if(!is_null($prefix)) {
+ if(isset($this->backends[$prefix])) {
+ $result = call_user_func_array(array($this->backends[$prefix], $method), $parameters);
+ if($result === $passOnWhen) {
+ //not found here, reset cache to null if group vanished
+ //because sometimes methods return false with a reason
+ $groupExists = call_user_func_array(
+ array($this->backends[$prefix], 'groupExists'),
+ array($gid)
+ );
+ if(!$groupExists) {
+ $this->writeToCache($cacheKey, null);
+ }
+ }
+ return $result;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * is user in group?
+ * @param string $uid uid of the user
+ * @param string $gid gid of the group
+ * @return bool
+ *
+ * Checks whether the user is member of a group or not.
+ */
+ public function inGroup($uid, $gid) {
+ return $this->handleRequest($gid, 'inGroup', array($uid, $gid));
+ }
+
+ /**
+ * Get all groups a user belongs to
+ * @param string $uid Name of the user
+ * @return string[] with group names
+ *
+ * This function fetches all groups a user belongs to. It does not check
+ * if the user exists at all.
+ */
+ public function getUserGroups($uid) {
+ $groups = array();
+
+ foreach($this->backends as $backend) {
+ $backendGroups = $backend->getUserGroups($uid);
+ if (is_array($backendGroups)) {
+ $groups = array_merge($groups, $backendGroups);
+ }
+ }
+
+ return $groups;
+ }
+
+ /**
+ * get a list of all users in a group
+ * @return string[] with user ids
+ */
+ public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) {
+ $users = array();
+
+ foreach($this->backends as $backend) {
+ $backendUsers = $backend->usersInGroup($gid, $search, $limit, $offset);
+ if (is_array($backendUsers)) {
+ $users = array_merge($users, $backendUsers);
+ }
+ }
+
+ return $users;
+ }
+
+ /**
+ * returns the number of users in a group, who match the search term
+ * @param string $gid the internal group name
+ * @param string $search optional, a search string
+ * @return int|bool
+ */
+ public function countUsersInGroup($gid, $search = '') {
+ return $this->handleRequest(
+ $gid, 'countUsersInGroup', array($gid, $search));
+ }
+
+ /**
+ * get a list of all groups
+ * @return string[] with group names
+ *
+ * Returns a list with all groups
+ */
+ public function getGroups($search = '', $limit = -1, $offset = 0) {
+ $groups = array();
+
+ foreach($this->backends as $backend) {
+ $backendGroups = $backend->getGroups($search, $limit, $offset);
+ if (is_array($backendGroups)) {
+ $groups = array_merge($groups, $backendGroups);
+ }
+ }
+
+ return $groups;
+ }
+
+ /**
+ * check if a group exists
+ * @param string $gid
+ * @return bool
+ */
+ public function groupExists($gid) {
+ return $this->handleRequest($gid, 'groupExists', array($gid));
+ }
+
+ /**
+ * Check if backend implements actions
+ * @param int $actions bitwise-or'ed actions
+ * @return boolean
+ *
+ * Returns the supported actions as int to be
+ * compared with OC_USER_BACKEND_CREATE_USER etc.
+ */
+ public function implementsActions($actions) {
+ //it's the same across all our user backends obviously
+ return $this->refBackend->implementsActions($actions);
+ }
+}
diff --git a/apps/user_ldap/lib/helper.php b/apps/user_ldap/lib/Helper.php
index bfff6baf0d3..aa6d6aa1bce 100644
--- a/apps/user_ldap/lib/helper.php
+++ b/apps/user_ldap/lib/Helper.php
@@ -25,9 +25,7 @@
*
*/
-namespace OCA\user_ldap\lib;
-
-use OCA\user_ldap\User_Proxy;
+namespace OCA\User_LDAP;
class Helper {
diff --git a/apps/user_ldap/lib/ildapwrapper.php b/apps/user_ldap/lib/ILDAPWrapper.php
index d607a3fdb66..a95f3a615f2 100644
--- a/apps/user_ldap/lib/ildapwrapper.php
+++ b/apps/user_ldap/lib/ILDAPWrapper.php
@@ -23,7 +23,7 @@
*
*/
-namespace OCA\user_ldap\lib;
+namespace OCA\User_LDAP;
interface ILDAPWrapper {
diff --git a/apps/user_ldap/lib/jobs/cleanup.php b/apps/user_ldap/lib/Jobs/CleanUp.php
index c9f5f2021eb..48f80ed9fa0 100644
--- a/apps/user_ldap/lib/jobs/cleanup.php
+++ b/apps/user_ldap/lib/Jobs/CleanUp.php
@@ -23,11 +23,11 @@
namespace OCA\User_LDAP\Jobs;
use \OC\BackgroundJob\TimedJob;
-use \OCA\user_ldap\User_LDAP;
-use \OCA\user_ldap\User_Proxy;
-use \OCA\user_ldap\lib\Helper;
-use \OCA\user_ldap\lib\LDAP;
-use \OCA\user_ldap\lib\user\DeletedUsersIndex;
+use \OCA\User_LDAP\User_LDAP;
+use \OCA\User_LDAP\User_Proxy;
+use \OCA\User_LDAP\Helper;
+use \OCA\User_LDAP\LDAP;
+use \OCA\User_LDAP\User\DeletedUsersIndex;
use \OCA\User_LDAP\Mapping\UserMapping;
/**
@@ -35,7 +35,7 @@ use \OCA\User_LDAP\Mapping\UserMapping;
*
* a Background job to clean up deleted users
*
- * @package OCA\user_ldap\lib;
+ * @package OCA\User_LDAP\Jobs;
*/
class CleanUp extends TimedJob {
/** @var int $limit amount of users that should be checked per run */
@@ -59,7 +59,7 @@ class CleanUp extends TimedJob {
/** @var \OCA\User_LDAP\Mapping\UserMapping */
protected $mapping;
- /** @var \OCA\User_LDAP\lib\User\DeletedUsersIndex */
+ /** @var \OCA\User_LDAP\User\DeletedUsersIndex */
protected $dui;
public function __construct() {
diff --git a/apps/user_ldap/lib/jobs.php b/apps/user_ldap/lib/Jobs/UpdateGroups.php
index ecf2e6daa0f..3ea74959f5c 100644
--- a/apps/user_ldap/lib/jobs.php
+++ b/apps/user_ldap/lib/Jobs/UpdateGroups.php
@@ -26,12 +26,19 @@
*
*/
-namespace OCA\user_ldap\lib;
-
+namespace OCA\User_LDAP\Jobs;
+
+use OCA\User_LDAP\Access;
+use OCA\User_LDAP\Connection;
+use OCA\User_LDAP\FilesystemHelper;
+use OCA\User_LDAP\Helper;
+use OCA\User_LDAP\LDAP;
+use OCA\User_LDAP\LogWrapper;
use OCA\User_LDAP\Mapping\GroupMapping;
use OCA\User_LDAP\Mapping\UserMapping;
+use OCA\User_LDAP\User\Manager;
-class Jobs extends \OC\BackgroundJob\TimedJob {
+class UpdateGroups extends \OC\BackgroundJob\TimedJob {
static private $groupsFromDB;
static private $groupBE;
@@ -45,7 +52,7 @@ class Jobs extends \OC\BackgroundJob\TimedJob {
* @param mixed $argument
*/
public function run($argument){
- Jobs::updateGroups();
+ self::updateGroups();
}
static public function updateGroups() {
@@ -158,7 +165,7 @@ class Jobs extends \OC\BackgroundJob\TimedJob {
}
/**
- * @return \OCA\user_ldap\GROUP_LDAP|\OCA\user_ldap\Group_Proxy
+ * @return \OCA\User_LDAP\Group_LDAP|\OCA\User_LDAP\Group_Proxy
*/
static private function getGroupBE() {
if(!is_null(self::$groupBE)) {
@@ -170,7 +177,7 @@ class Jobs extends \OC\BackgroundJob\TimedJob {
if(count($configPrefixes) === 1) {
//avoid the proxy when there is only one LDAP server configured
$dbc = \OC::$server->getDatabaseConnection();
- $userManager = new user\Manager(
+ $userManager = new Manager(
\OC::$server->getConfig(),
new FilesystemHelper(),
new LogWrapper(),
@@ -184,9 +191,9 @@ class Jobs extends \OC\BackgroundJob\TimedJob {
$userMapper = new UserMapping($dbc);
$ldapAccess->setGroupMapper($groupMapper);
$ldapAccess->setUserMapper($userMapper);
- self::$groupBE = new \OCA\user_ldap\GROUP_LDAP($ldapAccess);
+ self::$groupBE = new \OCA\User_LDAP\Group_LDAP($ldapAccess);
} else {
- self::$groupBE = new \OCA\user_ldap\Group_Proxy($configPrefixes, $ldapWrapper);
+ self::$groupBE = new \OCA\User_LDAP\Group_Proxy($configPrefixes, $ldapWrapper);
}
return self::$groupBE;
diff --git a/apps/user_ldap/lib/ldap.php b/apps/user_ldap/lib/LDAP.php
index 383d71ca6eb..a7033d82254 100644
--- a/apps/user_ldap/lib/ldap.php
+++ b/apps/user_ldap/lib/LDAP.php
@@ -24,7 +24,7 @@
*
*/
-namespace OCA\user_ldap\lib;
+namespace OCA\User_LDAP;
use OC\ServerNotAvailableException;
@@ -160,7 +160,7 @@ class LDAP implements ILDAPWrapper {
/**
* @param LDAP $link
* @param resource $result
- * @return mixed|an
+ * @return mixed
*/
public function nextEntry($link, $result) {
return $this->invokeLDAPMethod('next_entry', $link, $result);
diff --git a/apps/user_ldap/lib/ldaputility.php b/apps/user_ldap/lib/LDAPUtility.php
index e80fc12e087..33b2685593f 100644
--- a/apps/user_ldap/lib/ldaputility.php
+++ b/apps/user_ldap/lib/LDAPUtility.php
@@ -21,7 +21,7 @@
*
*/
-namespace OCA\user_ldap\lib;
+namespace OCA\User_LDAP;
abstract class LDAPUtility {
protected $ldap;
diff --git a/apps/user_ldap/lib/logwrapper.php b/apps/user_ldap/lib/LogWrapper.php
index 41ae4fc3426..5368e82417b 100644
--- a/apps/user_ldap/lib/logwrapper.php
+++ b/apps/user_ldap/lib/LogWrapper.php
@@ -20,7 +20,7 @@
*
*/
-namespace OCA\user_ldap\lib;
+namespace OCA\User_LDAP;
/**
* @brief wraps around static ownCloud core methods
diff --git a/apps/user_ldap/lib/mapping/abstractmapping.php b/apps/user_ldap/lib/Mapping/AbstractMapping.php
index 1c896a9bbf4..1c896a9bbf4 100644
--- a/apps/user_ldap/lib/mapping/abstractmapping.php
+++ b/apps/user_ldap/lib/Mapping/AbstractMapping.php
diff --git a/apps/user_ldap/lib/mapping/groupmapping.php b/apps/user_ldap/lib/Mapping/GroupMapping.php
index 49bb41b8c76..49bb41b8c76 100644
--- a/apps/user_ldap/lib/mapping/groupmapping.php
+++ b/apps/user_ldap/lib/Mapping/GroupMapping.php
diff --git a/apps/user_ldap/lib/mapping/usermapping.php b/apps/user_ldap/lib/Mapping/UserMapping.php
index b39f738ea8c..b39f738ea8c 100644
--- a/apps/user_ldap/lib/mapping/usermapping.php
+++ b/apps/user_ldap/lib/Mapping/UserMapping.php
diff --git a/apps/user_ldap/lib/proxy.php b/apps/user_ldap/lib/Proxy.php
index 7002aaadaa5..ce1a67b29de 100644
--- a/apps/user_ldap/lib/proxy.php
+++ b/apps/user_ldap/lib/Proxy.php
@@ -26,9 +26,8 @@
*
*/
-namespace OCA\user_ldap\lib;
+namespace OCA\User_LDAP;
-use OCA\user_ldap\lib\Access;
use OCA\User_LDAP\Mapping\UserMapping;
use OCA\User_LDAP\Mapping\GroupMapping;
diff --git a/apps/user_ldap/lib/user/deletedusersindex.php b/apps/user_ldap/lib/User/DeletedUsersIndex.php
index 48daeb9b8bc..7a9823786dd 100644
--- a/apps/user_ldap/lib/user/deletedusersindex.php
+++ b/apps/user_ldap/lib/User/DeletedUsersIndex.php
@@ -21,7 +21,7 @@
*
*/
-namespace OCA\user_ldap\lib\user;
+namespace OCA\User_LDAP\User;
use OCA\User_LDAP\Mapping\UserMapping;
@@ -63,7 +63,7 @@ class DeletedUsersIndex {
/**
* reads LDAP users marked as deleted from the database
- * @return \OCA\user_ldap\lib\user\OfflineUser[]
+ * @return \OCA\User_LDAP\User\OfflineUser[]
*/
private function fetchDeletedUsers() {
$deletedUsers = $this->config->getUsersForUserValue(
@@ -80,7 +80,7 @@ class DeletedUsersIndex {
/**
* returns all LDAP users that are marked as deleted
- * @return \OCA\user_ldap\lib\user\OfflineUser[]
+ * @return \OCA\User_LDAP\User\OfflineUser[]
*/
public function getUsers() {
if(is_array($this->deletedUsers)) {
diff --git a/apps/user_ldap/lib/user/iusertools.php b/apps/user_ldap/lib/User/IUserTools.php
index b0eb9e1ffb3..747b46ec68a 100644
--- a/apps/user_ldap/lib/user/iusertools.php
+++ b/apps/user_ldap/lib/User/IUserTools.php
@@ -20,7 +20,7 @@
*
*/
-namespace OCA\user_ldap\lib\user;
+namespace OCA\User_LDAP\User;
/**
* IUserTools
diff --git a/apps/user_ldap/lib/user/manager.php b/apps/user_ldap/lib/User/Manager.php
index dc12ebd6e9d..d97112e43c7 100644
--- a/apps/user_ldap/lib/user/manager.php
+++ b/apps/user_ldap/lib/User/Manager.php
@@ -23,13 +23,10 @@
*
*/
-namespace OCA\user_ldap\lib\user;
+namespace OCA\User_LDAP\User;
-use OCA\user_ldap\lib\user\IUserTools;
-use OCA\user_ldap\lib\user\User;
-use OCA\user_ldap\lib\LogWrapper;
-use OCA\user_ldap\lib\FilesystemHelper;
-use OCA\user_ldap\lib\user\OfflineUser;
+use OCA\User_LDAP\LogWrapper;
+use OCA\User_LDAP\FilesystemHelper;
use OCP\IAvatarManager;
use OCP\IConfig;
use OCP\IDBConnection;
@@ -65,8 +62,8 @@ class Manager {
protected $avatarManager;
/**
- * array['byDN'] \OCA\user_ldap\lib\User[]
- * ['byUid'] \OCA\user_ldap\lib\User[]
+ * array['byDN'] \OCA\User_LDAP\User\User[]
+ * ['byUid'] \OCA\User_LDAP\User\User[]
* @var array $users
*/
protected $users = array(
@@ -76,9 +73,9 @@ class Manager {
/**
* @param IConfig $ocConfig
- * @param \OCA\user_ldap\lib\FilesystemHelper $ocFilesystem object that
+ * @param \OCA\User_LDAP\FilesystemHelper $ocFilesystem object that
* gives access to necessary functions from the OC filesystem
- * @param \OCA\user_ldap\lib\LogWrapper $ocLog
+ * @param \OCA\User_LDAP\LogWrapper $ocLog
* @param IAvatarManager $avatarManager
* @param Image $image an empty image instance
* @param IDBConnection $db
@@ -112,7 +109,7 @@ class Manager {
* property array
* @param string $dn the DN of the user
* @param string $uid the internal (owncloud) username
- * @return \OCA\user_ldap\lib\User\User
+ * @return \OCA\User_LDAP\User\User
*/
private function createAndCache($dn, $uid) {
$this->checkAccess();
@@ -187,7 +184,7 @@ class Manager {
/**
* creates and returns an instance of OfflineUser for the specified user
* @param string $id
- * @return \OCA\user_ldap\lib\user\OfflineUser
+ * @return \OCA\User_LDAP\User\OfflineUser
*/
public function getDeletedUser($id) {
return new OfflineUser(
@@ -200,7 +197,7 @@ class Manager {
/**
* @brief returns a User object by it's ownCloud username
* @param string $id the DN or username of the user
- * @return \OCA\user_ldap\lib\user\User|\OCA\user_ldap\lib\user\OfflineUser|null
+ * @return \OCA\User_LDAP\User\User|\OCA\User_LDAP\User\OfflineUser|null
*/
protected function createInstancyByUserName($id) {
//most likely a uid. Check whether it is a deleted user
@@ -217,7 +214,7 @@ class Manager {
/**
* @brief returns a User object by it's DN or ownCloud username
* @param string $id the DN or username of the user
- * @return \OCA\user_ldap\lib\user\User|\OCA\user_ldap\lib\user\OfflineUser|null
+ * @return \OCA\User_LDAP\User\User|\OCA\User_LDAP\User\OfflineUser|null
* @throws \Exception when connection could not be established
*/
public function get($id) {
diff --git a/apps/user_ldap/lib/user/offlineuser.php b/apps/user_ldap/lib/User/OfflineUser.php
index aee1a137a96..a3a9995490d 100644
--- a/apps/user_ldap/lib/user/offlineuser.php
+++ b/apps/user_ldap/lib/User/OfflineUser.php
@@ -21,7 +21,7 @@
*
*/
-namespace OCA\user_ldap\lib\user;
+namespace OCA\User_LDAP\User;
use OCA\User_LDAP\Mapping\UserMapping;
diff --git a/apps/user_ldap/lib/user/user.php b/apps/user_ldap/lib/User/User.php
index 4da8ae5f098..8f7666790c1 100644
--- a/apps/user_ldap/lib/user/user.php
+++ b/apps/user_ldap/lib/User/User.php
@@ -22,11 +22,11 @@
*
*/
-namespace OCA\user_ldap\lib\user;
+namespace OCA\User_LDAP\User;
-use OCA\user_ldap\lib\Connection;
-use OCA\user_ldap\lib\FilesystemHelper;
-use OCA\user_ldap\lib\LogWrapper;
+use OCA\User_LDAP\Connection;
+use OCA\User_LDAP\FilesystemHelper;
+use OCA\User_LDAP\LogWrapper;
use OCP\IAvatarManager;
use OCP\IConfig;
use OCP\Image;
@@ -464,7 +464,7 @@ class User {
}
}
if(!is_null($quota)) {
- $user = $this->userManager->get($this->uid)->setQuota($quota);
+ $this->userManager->get($this->uid)->setQuota($quota);
}
}
@@ -502,16 +502,13 @@ class User {
*/
private function setOwnCloudAvatar() {
if(!$this->image->valid()) {
- $this->log->log('user_ldap', 'jpegPhoto data invalid for '.$this->dn,
- \OCP\Util::ERROR);
+ $this->log->log('jpegPhoto data invalid for '.$this->dn, \OCP\Util::ERROR);
return;
}
//make sure it is a square and not bigger than 128x128
$size = min(array($this->image->width(), $this->image->height(), 128));
if(!$this->image->centerCrop($size)) {
- $this->log->log('user_ldap',
- 'croping image for avatar failed for '.$this->dn,
- \OCP\Util::ERROR);
+ $this->log->log('croping image for avatar failed for '.$this->dn, \OCP\Util::ERROR);
return;
}
diff --git a/apps/user_ldap/lib/User_LDAP.php b/apps/user_ldap/lib/User_LDAP.php
new file mode 100644
index 00000000000..87c603fc753
--- /dev/null
+++ b/apps/user_ldap/lib/User_LDAP.php
@@ -0,0 +1,459 @@
+<?php
+/**
+ * @author Arthur Schiwon <blizzz@owncloud.com>
+ * @author Bart Visscher <bartv@thisnet.nl>
+ * @author Dominik Schmidt <dev@dominik-schmidt.de>
+ * @author Jörn Friedrich Dreyer <jfd@butonic.de>
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Renaud Fortier <Renaud.Fortier@fsaa.ulaval.ca>
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Robin McCorkell <robin@mccorkell.me.uk>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ * @author Tom Needham <tom@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\User_LDAP;
+
+use OC\User\NoUserException;
+use OCA\User_LDAP\User\OfflineUser;
+use OCA\User_LDAP\User\User;
+use OCP\IConfig;
+
+class User_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserInterface {
+ /** @var string[] $homesToKill */
+ protected $homesToKill = array();
+
+ /** @var \OCP\IConfig */
+ protected $ocConfig;
+
+ /**
+ * @param Access $access
+ * @param \OCP\IConfig $ocConfig
+ */
+ public function __construct(Access $access, IConfig $ocConfig) {
+ parent::__construct($access);
+ $this->ocConfig = $ocConfig;
+ }
+
+ /**
+ * checks whether the user is allowed to change his avatar in ownCloud
+ * @param string $uid the ownCloud user name
+ * @return boolean either the user can or cannot
+ */
+ public function canChangeAvatar($uid) {
+ $user = $this->access->userManager->get($uid);
+ if(!$user instanceof User) {
+ return false;
+ }
+ if($user->getAvatarImage() === false) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * returns the username for the given login name, if available
+ *
+ * @param string $loginName
+ * @return string|false
+ */
+ public function loginName2UserName($loginName) {
+ try {
+ $ldapRecord = $this->getLDAPUserByLoginName($loginName);
+ $user = $this->access->userManager->get($ldapRecord['dn'][0]);
+ if($user instanceof OfflineUser) {
+ return false;
+ }
+ return $user->getUsername();
+ } catch (\Exception $e) {
+ return false;
+ }
+ }
+
+ /**
+ * returns an LDAP record based on a given login name
+ *
+ * @param string $loginName
+ * @return array
+ * @throws \Exception
+ */
+ public function getLDAPUserByLoginName($loginName) {
+ //find out dn of the user name
+ $attrs = $this->access->userManager->getAttributes();
+ $users = $this->access->fetchUsersByLoginName($loginName, $attrs);
+ if(count($users) < 1) {
+ throw new \Exception('No user available for the given login name on ' .
+ $this->access->connection->ldapHost . ':' . $this->access->connection->ldapPort);
+ }
+ return $users[0];
+ }
+
+ /**
+ * Check if the password is correct
+ * @param string $uid The username
+ * @param string $password The password
+ * @return false|string
+ *
+ * Check if the password is correct without logging in the user
+ */
+ public function checkPassword($uid, $password) {
+ try {
+ $ldapRecord = $this->getLDAPUserByLoginName($uid);
+ } catch(\Exception $e) {
+ \OC::$server->getLogger()->logException($e, ['app' => 'user_ldap']);
+ return false;
+ }
+ $dn = $ldapRecord['dn'][0];
+ $user = $this->access->userManager->get($dn);
+
+ if(!$user instanceof User) {
+ \OCP\Util::writeLog('user_ldap',
+ 'LDAP Login: Could not get user object for DN ' . $dn .
+ '. Maybe the LDAP entry has no set display name attribute?',
+ \OCP\Util::WARN);
+ return false;
+ }
+ if($user->getUsername() !== false) {
+ //are the credentials OK?
+ if(!$this->access->areCredentialsValid($dn, $password)) {
+ return false;
+ }
+
+ $this->access->cacheUserExists($user->getUsername());
+ $user->processAttributes($ldapRecord);
+ $user->markLogin();
+
+ return $user->getUsername();
+ }
+
+ return false;
+ }
+
+ /**
+ * Get a list of all users
+ *
+ * @param string $search
+ * @param integer $limit
+ * @param integer $offset
+ * @return string[] an array of all uids
+ */
+ public function getUsers($search = '', $limit = 10, $offset = 0) {
+ $search = $this->access->escapeFilterPart($search, true);
+ $cachekey = 'getUsers-'.$search.'-'.$limit.'-'.$offset;
+
+ //check if users are cached, if so return
+ $ldap_users = $this->access->connection->getFromCache($cachekey);
+ if(!is_null($ldap_users)) {
+ return $ldap_users;
+ }
+
+ // if we'd pass -1 to LDAP search, we'd end up in a Protocol
+ // error. With a limit of 0, we get 0 results. So we pass null.
+ if($limit <= 0) {
+ $limit = null;
+ }
+ $filter = $this->access->combineFilterWithAnd(array(
+ $this->access->connection->ldapUserFilter,
+ $this->access->connection->ldapUserDisplayName . '=*',
+ $this->access->getFilterPartForUserSearch($search)
+ ));
+ $attrs = array($this->access->connection->ldapUserDisplayName, 'dn');
+ $additionalAttribute = $this->access->connection->ldapUserDisplayName2;
+ if(!empty($additionalAttribute)) {
+ $attrs[] = $additionalAttribute;
+ }
+
+ \OCP\Util::writeLog('user_ldap',
+ 'getUsers: Options: search '.$search.' limit '.$limit.' offset '.$offset.' Filter: '.$filter,
+ \OCP\Util::DEBUG);
+ //do the search and translate results to owncloud names
+ $ldap_users = $this->access->fetchListOfUsers(
+ $filter,
+ $this->access->userManager->getAttributes(true),
+ $limit, $offset);
+ $ldap_users = $this->access->ownCloudUserNames($ldap_users);
+ \OCP\Util::writeLog('user_ldap', 'getUsers: '.count($ldap_users). ' Users found', \OCP\Util::DEBUG);
+
+ $this->access->connection->writeToCache($cachekey, $ldap_users);
+ return $ldap_users;
+ }
+
+ /**
+ * checks whether a user is still available on LDAP
+ *
+ * @param string|\OCA\User_LDAP\User\User $user either the ownCloud user
+ * name or an instance of that user
+ * @return bool
+ * @throws \Exception
+ * @throws \OC\ServerNotAvailableException
+ */
+ public function userExistsOnLDAP($user) {
+ if(is_string($user)) {
+ $user = $this->access->userManager->get($user);
+ }
+ if(is_null($user)) {
+ return false;
+ }
+
+ $dn = $user->getDN();
+ //check if user really still exists by reading its entry
+ if(!is_array($this->access->readAttribute($dn, '', $this->access->connection->ldapUserFilter))) {
+ $lcr = $this->access->connection->getConnectionResource();
+ if(is_null($lcr)) {
+ throw new \Exception('No LDAP Connection to server ' . $this->access->connection->ldapHost);
+ }
+
+ try {
+ $uuid = $this->access->getUserMapper()->getUUIDByDN($dn);
+ if(!$uuid) {
+ return false;
+ }
+ $newDn = $this->access->getUserDnByUuid($uuid);
+ $this->access->getUserMapper()->setDNbyUUID($newDn, $uuid);
+ return true;
+ } catch (\Exception $e) {
+ return false;
+ }
+ }
+
+ if($user instanceof OfflineUser) {
+ $user->unmark();
+ }
+
+ return true;
+ }
+
+ /**
+ * check if a user exists
+ * @param string $uid the username
+ * @return boolean
+ * @throws \Exception when connection could not be established
+ */
+ public function userExists($uid) {
+ $userExists = $this->access->connection->getFromCache('userExists'.$uid);
+ if(!is_null($userExists)) {
+ return (bool)$userExists;
+ }
+ //getting dn, if false the user does not exist. If dn, he may be mapped only, requires more checking.
+ $user = $this->access->userManager->get($uid);
+
+ if(is_null($user)) {
+ \OCP\Util::writeLog('user_ldap', 'No DN found for '.$uid.' on '.
+ $this->access->connection->ldapHost, \OCP\Util::DEBUG);
+ $this->access->connection->writeToCache('userExists'.$uid, false);
+ return false;
+ } else if($user instanceof OfflineUser) {
+ //express check for users marked as deleted. Returning true is
+ //necessary for cleanup
+ return true;
+ }
+
+ $result = $this->userExistsOnLDAP($user);
+ $this->access->connection->writeToCache('userExists'.$uid, $result);
+ if($result === true) {
+ $user->update();
+ }
+ return $result;
+ }
+
+ /**
+ * returns whether a user was deleted in LDAP
+ *
+ * @param string $uid The username of the user to delete
+ * @return bool
+ */
+ public function deleteUser($uid) {
+ $marked = $this->ocConfig->getUserValue($uid, 'user_ldap', 'isDeleted', 0);
+ if(intval($marked) === 0) {
+ \OC::$server->getLogger()->notice(
+ 'User '.$uid . ' is not marked as deleted, not cleaning up.',
+ array('app' => 'user_ldap'));
+ return false;
+ }
+ \OC::$server->getLogger()->info('Cleaning up after user ' . $uid,
+ array('app' => 'user_ldap'));
+
+ //Get Home Directory out of user preferences so we can return it later,
+ //necessary for removing directories as done by OC_User.
+ $home = $this->ocConfig->getUserValue($uid, 'user_ldap', 'homePath', '');
+ $this->homesToKill[$uid] = $home;
+ $this->access->getUserMapper()->unmap($uid);
+
+ return true;
+ }
+
+ /**
+ * get the user's home directory
+ *
+ * @param string $uid the username
+ * @return bool|string
+ * @throws NoUserException
+ * @throws \Exception
+ */
+ public function getHome($uid) {
+ if(isset($this->homesToKill[$uid]) && !empty($this->homesToKill[$uid])) {
+ //a deleted user who needs some clean up
+ return $this->homesToKill[$uid];
+ }
+
+ // user Exists check required as it is not done in user proxy!
+ if(!$this->userExists($uid)) {
+ return false;
+ }
+
+ $cacheKey = 'getHome'.$uid;
+ $path = $this->access->connection->getFromCache($cacheKey);
+ if(!is_null($path)) {
+ return $path;
+ }
+
+ $user = $this->access->userManager->get($uid);
+ if(is_null($user) || ($user instanceof OfflineUser && !$this->userExistsOnLDAP($user->getOCName()))) {
+ throw new NoUserException($uid . ' is not a valid user anymore');
+ }
+ if($user instanceof OfflineUser) {
+ // apparently this user survived the userExistsOnLDAP check,
+ // we request the user instance again in order to retrieve a User
+ // instance instead
+ $user = $this->access->userManager->get($uid);
+ }
+ $path = $user->getHomePath();
+ $this->access->cacheUserHome($uid, $path);
+
+ return $path;
+ }
+
+ /**
+ * get display name of the user
+ * @param string $uid user ID of the user
+ * @return string|false display name
+ */
+ public function getDisplayName($uid) {
+ if(!$this->userExists($uid)) {
+ return false;
+ }
+
+ $cacheKey = 'getDisplayName'.$uid;
+ if(!is_null($displayName = $this->access->connection->getFromCache($cacheKey))) {
+ return $displayName;
+ }
+
+ //Check whether the display name is configured to have a 2nd feature
+ $additionalAttribute = $this->access->connection->ldapUserDisplayName2;
+ $displayName2 = '';
+ if(!empty($additionalAttribute)) {
+ $displayName2 = $this->access->readAttribute(
+ $this->access->username2dn($uid),
+ $additionalAttribute);
+ }
+
+ $displayName = $this->access->readAttribute(
+ $this->access->username2dn($uid),
+ $this->access->connection->ldapUserDisplayName);
+
+ if($displayName && (count($displayName) > 0)) {
+ $displayName = $displayName[0];
+
+ if(is_array($displayName2) && (count($displayName2) > 0)) {
+ $displayName2 = $displayName2[0];
+ }
+
+ $user = $this->access->userManager->get($uid);
+ $displayName = $user->composeAndStoreDisplayName($displayName, $displayName2);
+ $this->access->connection->writeToCache($cacheKey, $displayName);
+ return $displayName;
+ }
+
+ return null;
+ }
+
+ /**
+ * Get a list of all display names
+ *
+ * @param string $search
+ * @param string|null $limit
+ * @param string|null $offset
+ * @return array an array of all displayNames (value) and the corresponding uids (key)
+ */
+ public function getDisplayNames($search = '', $limit = null, $offset = null) {
+ $cacheKey = 'getDisplayNames-'.$search.'-'.$limit.'-'.$offset;
+ if(!is_null($displayNames = $this->access->connection->getFromCache($cacheKey))) {
+ return $displayNames;
+ }
+
+ $displayNames = array();
+ $users = $this->getUsers($search, $limit, $offset);
+ foreach ($users as $user) {
+ $displayNames[$user] = $this->getDisplayName($user);
+ }
+ $this->access->connection->writeToCache($cacheKey, $displayNames);
+ return $displayNames;
+ }
+
+ /**
+ * Check if backend implements actions
+ * @param int $actions bitwise-or'ed actions
+ * @return boolean
+ *
+ * Returns the supported actions as int to be
+ * compared with OC_USER_BACKEND_CREATE_USER etc.
+ */
+ public function implementsActions($actions) {
+ return (bool)((\OC\User\Backend::CHECK_PASSWORD
+ | \OC\User\Backend::GET_HOME
+ | \OC\User\Backend::GET_DISPLAYNAME
+ | \OC\User\Backend::PROVIDE_AVATAR
+ | \OC\User\Backend::COUNT_USERS)
+ & $actions);
+ }
+
+ /**
+ * @return bool
+ */
+ public function hasUserListings() {
+ return true;
+ }
+
+ /**
+ * counts the users in LDAP
+ *
+ * @return int|bool
+ */
+ public function countUsers() {
+ $filter = $this->access->getFilterForUserCount();
+ $cacheKey = 'countUsers-'.$filter;
+ if(!is_null($entries = $this->access->connection->getFromCache($cacheKey))) {
+ return $entries;
+ }
+ $entries = $this->access->countUsers($filter);
+ $this->access->connection->writeToCache($cacheKey, $entries);
+ return $entries;
+ }
+
+ /**
+ * Backend name to be shown in user management
+ * @return string the name of the backend to be shown
+ */
+ public function getBackendName(){
+ return 'LDAP';
+ }
+
+}
diff --git a/apps/user_ldap/lib/User_Proxy.php b/apps/user_ldap/lib/User_Proxy.php
new file mode 100644
index 00000000000..800d1d36c16
--- /dev/null
+++ b/apps/user_ldap/lib/User_Proxy.php
@@ -0,0 +1,274 @@
+<?php
+/**
+ * @author Arthur Schiwon <blizzz@owncloud.com>
+ * @author Christopher Schäpers <kondou@ts.unde.re>
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Robin McCorkell <robin@mccorkell.me.uk>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\User_LDAP;
+
+use OCA\User_LDAP\User\User;
+use OCP\IConfig;
+
+class User_Proxy extends Proxy implements \OCP\IUserBackend, \OCP\UserInterface {
+ private $backends = array();
+ private $refBackend = null;
+
+ /**
+ * Constructor
+ * @param array $serverConfigPrefixes array containing the config Prefixes
+ */
+ public function __construct(array $serverConfigPrefixes, ILDAPWrapper $ldap, IConfig $ocConfig) {
+ parent::__construct($ldap);
+ foreach($serverConfigPrefixes as $configPrefix) {
+ $this->backends[$configPrefix] =
+ new User_LDAP($this->getAccess($configPrefix), $ocConfig);
+ if(is_null($this->refBackend)) {
+ $this->refBackend = &$this->backends[$configPrefix];
+ }
+ }
+ }
+
+ /**
+ * Tries the backends one after the other until a positive result is returned from the specified method
+ * @param string $uid the uid connected to the request
+ * @param string $method the method of the user backend that shall be called
+ * @param array $parameters an array of parameters to be passed
+ * @return mixed the result of the method or false
+ */
+ protected function walkBackends($uid, $method, $parameters) {
+ $cacheKey = $this->getUserCacheKey($uid);
+ foreach($this->backends as $configPrefix => $backend) {
+ $instance = $backend;
+ if(!method_exists($instance, $method)
+ && method_exists($this->getAccess($configPrefix), $method)) {
+ $instance = $this->getAccess($configPrefix);
+ }
+ if($result = call_user_func_array(array($instance, $method), $parameters)) {
+ $this->writeToCache($cacheKey, $configPrefix);
+ return $result;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Asks the backend connected to the server that supposely takes care of the uid from the request.
+ * @param string $uid the uid connected to the request
+ * @param string $method the method of the user backend that shall be called
+ * @param array $parameters an array of parameters to be passed
+ * @param mixed $passOnWhen the result matches this variable
+ * @return mixed the result of the method or false
+ */
+ protected function callOnLastSeenOn($uid, $method, $parameters, $passOnWhen) {
+ $cacheKey = $this->getUserCacheKey($uid);
+ $prefix = $this->getFromCache($cacheKey);
+ //in case the uid has been found in the past, try this stored connection first
+ if(!is_null($prefix)) {
+ if(isset($this->backends[$prefix])) {
+ $instance = $this->backends[$prefix];
+ if(!method_exists($instance, $method)
+ && method_exists($this->getAccess($prefix), $method)) {
+ $instance = $this->getAccess($prefix);
+ }
+ $result = call_user_func_array(array($instance, $method), $parameters);
+ if($result === $passOnWhen) {
+ //not found here, reset cache to null if user vanished
+ //because sometimes methods return false with a reason
+ $userExists = call_user_func_array(
+ array($this->backends[$prefix], 'userExists'),
+ array($uid)
+ );
+ if(!$userExists) {
+ $this->writeToCache($cacheKey, null);
+ }
+ }
+ return $result;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Check if backend implements actions
+ * @param int $actions bitwise-or'ed actions
+ * @return boolean
+ *
+ * Returns the supported actions as int to be
+ * compared with OC_USER_BACKEND_CREATE_USER etc.
+ */
+ public function implementsActions($actions) {
+ //it's the same across all our user backends obviously
+ return $this->refBackend->implementsActions($actions);
+ }
+
+ /**
+ * Backend name to be shown in user management
+ * @return string the name of the backend to be shown
+ */
+ public function getBackendName() {
+ return $this->refBackend->getBackendName();
+ }
+
+ /**
+ * Get a list of all users
+ *
+ * @param string $search
+ * @param null|int $limit
+ * @param null|int $offset
+ * @return string[] an array of all uids
+ */
+ public function getUsers($search = '', $limit = 10, $offset = 0) {
+ //we do it just as the /OC_User implementation: do not play around with limit and offset but ask all backends
+ $users = array();
+ foreach($this->backends as $backend) {
+ $backendUsers = $backend->getUsers($search, $limit, $offset);
+ if (is_array($backendUsers)) {
+ $users = array_merge($users, $backendUsers);
+ }
+ }
+ return $users;
+ }
+
+ /**
+ * check if a user exists
+ * @param string $uid the username
+ * @return boolean
+ */
+ public function userExists($uid) {
+ return $this->handleRequest($uid, 'userExists', array($uid));
+ }
+
+ /**
+ * check if a user exists on LDAP
+ * @param string|\OCA\User_LDAP\User\User $user either the ownCloud user
+ * name or an instance of that user
+ * @return boolean
+ */
+ public function userExistsOnLDAP($user) {
+ $id = ($user instanceof User) ? $user->getUsername() : $user;
+ return $this->handleRequest($id, 'userExistsOnLDAP', array($user));
+ }
+
+ /**
+ * Check if the password is correct
+ * @param string $uid The username
+ * @param string $password The password
+ * @return bool
+ *
+ * Check if the password is correct without logging in the user
+ */
+ public function checkPassword($uid, $password) {
+ return $this->handleRequest($uid, 'checkPassword', array($uid, $password));
+ }
+
+ /**
+ * returns the username for the given login name, if available
+ *
+ * @param string $loginName
+ * @return string|false
+ */
+ public function loginName2UserName($loginName) {
+ $id = 'LOGINNAME,' . $loginName;
+ return $this->handleRequest($id, 'loginName2UserName', array($loginName));
+ }
+
+ /**
+ * get the user's home directory
+ * @param string $uid the username
+ * @return boolean
+ */
+ public function getHome($uid) {
+ return $this->handleRequest($uid, 'getHome', array($uid));
+ }
+
+ /**
+ * get display name of the user
+ * @param string $uid user ID of the user
+ * @return string display name
+ */
+ public function getDisplayName($uid) {
+ return $this->handleRequest($uid, 'getDisplayName', array($uid));
+ }
+
+ /**
+ * checks whether the user is allowed to change his avatar in ownCloud
+ * @param string $uid the ownCloud user name
+ * @return boolean either the user can or cannot
+ */
+ public function canChangeAvatar($uid) {
+ return $this->handleRequest($uid, 'canChangeAvatar', array($uid), true);
+ }
+
+ /**
+ * Get a list of all display names and user ids.
+ * @param string $search
+ * @param string|null $limit
+ * @param string|null $offset
+ * @return array an array of all displayNames (value) and the corresponding uids (key)
+ */
+ public function getDisplayNames($search = '', $limit = null, $offset = null) {
+ //we do it just as the /OC_User implementation: do not play around with limit and offset but ask all backends
+ $users = array();
+ foreach($this->backends as $backend) {
+ $backendUsers = $backend->getDisplayNames($search, $limit, $offset);
+ if (is_array($backendUsers)) {
+ $users = $users + $backendUsers;
+ }
+ }
+ return $users;
+ }
+
+ /**
+ * delete a user
+ * @param string $uid The username of the user to delete
+ * @return bool
+ *
+ * Deletes a user
+ */
+ public function deleteUser($uid) {
+ return $this->handleRequest($uid, 'deleteUser', array($uid));
+ }
+
+ /**
+ * @return bool
+ */
+ public function hasUserListings() {
+ return $this->refBackend->hasUserListings();
+ }
+
+ /**
+ * Count the number of users
+ * @return int|bool
+ */
+ public function countUsers() {
+ $users = false;
+ foreach($this->backends as $backend) {
+ $backendUsers = $backend->countUsers();
+ if ($backendUsers !== false) {
+ $users += $backendUsers;
+ }
+ }
+ return $users;
+ }
+
+}
diff --git a/apps/user_ldap/lib/wizard.php b/apps/user_ldap/lib/Wizard.php
index 0b475ee7143..f71d5af2c49 100644
--- a/apps/user_ldap/lib/wizard.php
+++ b/apps/user_ldap/lib/Wizard.php
@@ -29,11 +29,12 @@
*
*/
-namespace OCA\user_ldap\lib;
+namespace OCA\User_LDAP;
use OC\ServerNotAvailableException;
class Wizard extends LDAPUtility {
+ /** @var \OCP\IL10N */
static protected $l;
protected $access;
protected $cr;
@@ -58,6 +59,7 @@ class Wizard extends LDAPUtility {
* Constructor
* @param Configuration $configuration an instance of Configuration
* @param ILDAPWrapper $ldap an instance of ILDAPWrapper
+ * @param Access $access
*/
public function __construct(Configuration $configuration, ILDAPWrapper $ldap, Access $access) {
parent::__construct($ldap);
@@ -1032,12 +1034,12 @@ class Wizard extends LDAPUtility {
$host = $this->configuration->ldapHost;
$hostInfo = parse_url($host);
if(!$hostInfo) {
- throw new \Exception($this->l->t('Invalid Host'));
+ throw new \Exception(self::$l->t('Invalid Host'));
}
\OCP\Util::writeLog('user_ldap', 'Wiz: Attempting to connect ', \OCP\Util::DEBUG);
$cr = $this->ldap->connect($host, $port);
if(!is_resource($cr)) {
- throw new \Exception($this->l->t('Invalid Host'));
+ throw new \Exception(self::$l->t('Invalid Host'));
}
\OCP\Util::writeLog('user_ldap', 'Wiz: Setting LDAP Options ', \OCP\Util::DEBUG);
diff --git a/apps/user_ldap/lib/wizardresult.php b/apps/user_ldap/lib/WizardResult.php
index 54f01cf59b8..1adce9a1389 100644
--- a/apps/user_ldap/lib/wizardresult.php
+++ b/apps/user_ldap/lib/WizardResult.php
@@ -23,7 +23,7 @@
*
*/
-namespace OCA\user_ldap\lib;
+namespace OCA\User_LDAP;
class WizardResult {
protected $changes = array();