Browse Source

Merge pull request #31661 from nextcloud/enh/user_ldap-add-command-to-unmap-groups

Add ldap:reset-group command to unmap groups from LDAP
pull/31847/head
blizzz 4 years ago
committed by GitHub
parent
commit
835e28d0b2
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      apps/user_ldap/appinfo/info.xml
  2. 1
      apps/user_ldap/composer/composer/autoload_classmap.php
  3. 1
      apps/user_ldap/composer/composer/autoload_static.php
  4. 111
      apps/user_ldap/lib/Command/ResetGroup.php
  5. 28
      apps/user_ldap/lib/GroupPluginManager.php
  6. 27
      apps/user_ldap/lib/Group_LDAP.php
  7. 10
      apps/user_ldap/lib/Group_Proxy.php
  8. 7
      apps/user_ldap/lib/UserPluginManager.php
  9. 16
      apps/user_ldap/tests/GroupLDAPPluginTest.php
  10. 4
      apps/user_ldap/tests/Group_LDAPTest.php
  11. 2
      core/Command/Group/Delete.php

1
apps/user_ldap/appinfo/info.xml

@ -50,6 +50,7 @@ A user logs into Nextcloud with their LDAP or AD credentials, and is granted acc
<command>OCA\User_LDAP\Command\CheckUser</command>
<command>OCA\User_LDAP\Command\CreateEmptyConfig</command>
<command>OCA\User_LDAP\Command\DeleteConfig</command>
<command>OCA\User_LDAP\Command\ResetGroup</command>
<command>OCA\User_LDAP\Command\ResetUser</command>
<command>OCA\User_LDAP\Command\Search</command>
<command>OCA\User_LDAP\Command\SetConfig</command>

1
apps/user_ldap/composer/composer/autoload_classmap.php

@ -14,6 +14,7 @@ return array(
'OCA\\User_LDAP\\Command\\CheckUser' => $baseDir . '/../lib/Command/CheckUser.php',
'OCA\\User_LDAP\\Command\\CreateEmptyConfig' => $baseDir . '/../lib/Command/CreateEmptyConfig.php',
'OCA\\User_LDAP\\Command\\DeleteConfig' => $baseDir . '/../lib/Command/DeleteConfig.php',
'OCA\\User_LDAP\\Command\\ResetGroup' => $baseDir . '/../lib/Command/ResetGroup.php',
'OCA\\User_LDAP\\Command\\ResetUser' => $baseDir . '/../lib/Command/ResetUser.php',
'OCA\\User_LDAP\\Command\\Search' => $baseDir . '/../lib/Command/Search.php',
'OCA\\User_LDAP\\Command\\SetConfig' => $baseDir . '/../lib/Command/SetConfig.php',

1
apps/user_ldap/composer/composer/autoload_static.php

@ -29,6 +29,7 @@ class ComposerStaticInitUser_LDAP
'OCA\\User_LDAP\\Command\\CheckUser' => __DIR__ . '/..' . '/../lib/Command/CheckUser.php',
'OCA\\User_LDAP\\Command\\CreateEmptyConfig' => __DIR__ . '/..' . '/../lib/Command/CreateEmptyConfig.php',
'OCA\\User_LDAP\\Command\\DeleteConfig' => __DIR__ . '/..' . '/../lib/Command/DeleteConfig.php',
'OCA\\User_LDAP\\Command\\ResetGroup' => __DIR__ . '/..' . '/../lib/Command/ResetGroup.php',
'OCA\\User_LDAP\\Command\\ResetUser' => __DIR__ . '/..' . '/../lib/Command/ResetUser.php',
'OCA\\User_LDAP\\Command\\Search' => __DIR__ . '/..' . '/../lib/Command/Search.php',
'OCA\\User_LDAP\\Command\\SetConfig' => __DIR__ . '/..' . '/../lib/Command/SetConfig.php',

111
apps/user_ldap/lib/Command/ResetGroup.php

@ -0,0 +1,111 @@
<?php
/**
* @copyright Copyright (c) 2021 Arthur Schiwon <blizzz@arthur-schiwon.de>
*
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
* @author Côme Chilliet <come.chilliet@nextcloud.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\User_LDAP\Command;
use OCA\User_LDAP\Group_Proxy;
use OCA\User_LDAP\GroupPluginManager;
use OCP\IGroup;
use OCP\IGroupManager;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\QuestionHelper;
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 Symfony\Component\Console\Question\Question;
class ResetGroup extends Command {
private IGroupManager $groupManager;
private GroupPluginManager $pluginManager;
private Group_Proxy $backend;
public function __construct(
IGroupManager $groupManager,
GroupPluginManager $pluginManager,
Group_Proxy $backend
) {
$this->groupManager = $groupManager;
$this->pluginManager = $pluginManager;
$this->backend = $backend;
parent::__construct();
}
protected function configure(): void {
$this
->setName('ldap:reset-group')
->setDescription('deletes an LDAP group independent of the group state in the LDAP')
->addArgument(
'gid',
InputArgument::REQUIRED,
'the group name as used in Nextcloud'
)
->addOption(
'yes',
'y',
InputOption::VALUE_NONE,
'do not ask for confirmation'
);
}
protected function execute(InputInterface $input, OutputInterface $output): int {
try {
$gid = $input->getArgument('gid');
$group = $this->groupManager->get($gid);
if (!$group instanceof IGroup) {
throw new \Exception('Group not found');
}
$backends = $group->getBackendNames();
if (!in_array('LDAP', $backends)) {
throw new \Exception('The given group is not a recognized LDAP group.');
}
if ($input->getOption('yes') === false) {
/** @var QuestionHelper $helper */
$helper = $this->getHelper('question');
$q = new Question('Delete all local data of this group (y|N)? ');
$input->setOption('yes', $helper->ask($input, $output, $q) === 'y');
}
if ($input->getOption('yes') !== true) {
throw new \Exception('Reset cancelled by operator');
}
// Disable real deletion if a plugin supports it
$pluginManagerSuppressed = $this->pluginManager->setSuppressDeletion(true);
// Bypass groupExists test to force mapping deletion
$this->backend->getLDAPAccess($gid)->connection->writeToCache('groupExists' . $gid, false);
echo "calling delete $gid\n";
if ($group->delete()) {
$this->pluginManager->setSuppressDeletion($pluginManagerSuppressed);
return 0;
}
} catch (\Throwable $e) {
if (isset($pluginManagerSuppressed)) {
$this->pluginManager->setSuppressDeletion($pluginManagerSuppressed);
}
$output->writeln('<error>' . $e->getMessage() . '</error>');
return 1;
}
$output->writeln('<error>Error while resetting group</error>');
return 2;
}
}

28
apps/user_ldap/lib/GroupPluginManager.php

@ -26,9 +26,10 @@ namespace OCA\User_LDAP;
use OCP\GroupInterface;
class GroupPluginManager {
private $respondToActions = 0;
private int $respondToActions = 0;
private $which = [
/** @var array<int, ?ILDAPGroupPlugin> */
private array $which = [
GroupInterface::CREATE_GROUP => null,
GroupInterface::DELETE_GROUP => null,
GroupInterface::ADD_TO_GROUP => null,
@ -37,6 +38,8 @@ class GroupPluginManager {
GroupInterface::GROUP_DETAILS => null
];
private bool $suppressDeletion = false;
/**
* @return int All implemented actions
*/
@ -84,16 +87,31 @@ class GroupPluginManager {
throw new \Exception('No plugin implements createGroup in this LDAP Backend.');
}
public function canDeleteGroup(): bool {
return !$this->suppressDeletion && $this->implementsActions(GroupInterface::DELETE_GROUP);
}
/**
* @return bool the value before the change
*/
public function setSuppressDeletion(bool $value): bool {
$old = $this->suppressDeletion;
$this->suppressDeletion = $value;
return $old;
}
/**
* Delete a group
* @param string $gid Group Id of the group to delete
* @return bool
*
* @throws \Exception
*/
public function deleteGroup($gid) {
public function deleteGroup(string $gid): bool {
$plugin = $this->which[GroupInterface::DELETE_GROUP];
if ($plugin) {
if ($this->suppressDeletion) {
return false;
}
return $plugin->deleteGroup($gid);
}
throw new \Exception('No plugin implements deleteGroup in this LDAP Backend.');

27
apps/user_ldap/lib/Group_LDAP.php

@ -48,10 +48,11 @@ use OC;
use OC\Cache\CappedMemoryCache;
use OC\ServerNotAvailableException;
use OCP\Group\Backend\IGetDisplayNameBackend;
use OCP\Group\Backend\IDeleteGroupBackend;
use OCP\GroupInterface;
use Psr\Log\LoggerInterface;
class Group_LDAP extends BackendUtility implements GroupInterface, IGroupLDAP, IGetDisplayNameBackend {
class Group_LDAP extends BackendUtility implements GroupInterface, IGroupLDAP, IGetDisplayNameBackend, IDeleteGroupBackend {
protected $enabled = false;
/** @var string[][] $cachedGroupMembers array of users with gid as key */
@ -1204,6 +1205,7 @@ class Group_LDAP extends BackendUtility implements GroupInterface, IGroupLDAP, I
*/
public function implementsActions($actions) {
return (bool)((GroupInterface::COUNT_USERS |
GroupInterface::DELETE_GROUP |
$this->groupPluginManager->getImplementedActions()) & $actions);
}
@ -1249,19 +1251,32 @@ class Group_LDAP extends BackendUtility implements GroupInterface, IGroupLDAP, I
* delete a group
*
* @param string $gid gid of the group to delete
* @return bool
* @throws Exception
*/
public function deleteGroup($gid) {
if ($this->groupPluginManager->implementsActions(GroupInterface::DELETE_GROUP)) {
public function deleteGroup(string $gid): bool {
if ($this->groupPluginManager->canDeleteGroup()) {
if ($ret = $this->groupPluginManager->deleteGroup($gid)) {
#delete group in nextcloud internal db
// Delete group in nextcloud internal db
$this->access->getGroupMapper()->unmap($gid);
$this->access->connection->writeToCache("groupExists" . $gid, false);
}
return $ret;
}
throw new Exception('Could not delete group in LDAP backend.');
// Getting dn, if false the group is not mapped
$dn = $this->access->groupname2dn($gid);
if (!$dn) {
throw new Exception('Could not delete unknown group '.$gid.' in LDAP backend.');
}
if (!$this->groupExists($gid)) {
// The group does not exist in the LDAP, remove the mapping
$this->access->getGroupMapper()->unmap($gid);
$this->access->connection->writeToCache("groupExists" . $gid, false);
return true;
}
throw new Exception('Could not delete existing group '.$gid.' in LDAP backend.');
}
/**

10
apps/user_ldap/lib/Group_Proxy.php

@ -28,10 +28,11 @@
*/
namespace OCA\User_LDAP;
use OCP\Group\Backend\INamedBackend;
use OCP\Group\Backend\IDeleteGroupBackend;
use OCP\Group\Backend\IGetDisplayNameBackend;
use OCP\Group\Backend\INamedBackend;
class Group_Proxy extends Proxy implements \OCP\GroupInterface, IGroupLDAP, IGetDisplayNameBackend, INamedBackend {
class Group_Proxy extends Proxy implements \OCP\GroupInterface, IGroupLDAP, IGetDisplayNameBackend, INamedBackend, IDeleteGroupBackend {
private $backends = [];
private $refBackend = null;
@ -171,11 +172,8 @@ class Group_Proxy extends Proxy implements \OCP\GroupInterface, IGroupLDAP, IGet
/**
* delete a group
*
* @param string $gid gid of the group to delete
* @return bool
*/
public function deleteGroup($gid) {
public function deleteGroup(string $gid): bool {
return $this->handleRequest(
$gid, 'deleteGroup', [$gid]);
}

7
apps/user_ldap/lib/UserPluginManager.php

@ -28,9 +28,9 @@ namespace OCA\User_LDAP;
use OC\User\Backend;
class UserPluginManager {
private $respondToActions = 0;
private int $respondToActions = 0;
private $which = [
private array $which = [
Backend::CREATE_USER => null,
Backend::SET_PASSWORD => null,
Backend::GET_HOME => null,
@ -41,8 +41,7 @@ class UserPluginManager {
'deleteUser' => null
];
/** @var bool */
private $suppressDeletion = false;
private bool $suppressDeletion = false;
/**
* @return int All implemented actions, except for 'deleteUser'

16
apps/user_ldap/tests/GroupLDAPPluginTest.php

@ -84,7 +84,7 @@ class GroupLDAPPluginTest extends \Test\TestCase {
$pluginManager->createGroup('group');
}
public function testCreateGroupNotRegistered() {
$this->expectException(\Exception::class);
$this->expectExceptionMessage('No plugin implements createGroup in this LDAP Backend.');
@ -108,13 +108,13 @@ class GroupLDAPPluginTest extends \Test\TestCase {
->method('deleteGroup')
->with(
$this->equalTo('group')
);
)->willReturn(true);
$pluginManager->register($plugin);
$pluginManager->deleteGroup('group');
$this->assertTrue($pluginManager->deleteGroup('group'));
}
public function testDeleteGroupNotRegistered() {
$this->expectException(\Exception::class);
$this->expectExceptionMessage('No plugin implements deleteGroup in this LDAP Backend.');
@ -145,7 +145,7 @@ class GroupLDAPPluginTest extends \Test\TestCase {
$pluginManager->addToGroup('uid', 'gid');
}
public function testAddToGroupNotRegistered() {
$this->expectException(\Exception::class);
$this->expectExceptionMessage('No plugin implements addToGroup in this LDAP Backend.');
@ -176,7 +176,7 @@ class GroupLDAPPluginTest extends \Test\TestCase {
$pluginManager->removeFromGroup('uid', 'gid');
}
public function testRemoveFromGroupNotRegistered() {
$this->expectException(\Exception::class);
$this->expectExceptionMessage('No plugin implements removeFromGroup in this LDAP Backend.');
@ -207,7 +207,7 @@ class GroupLDAPPluginTest extends \Test\TestCase {
$pluginManager->countUsersInGroup('gid', 'search');
}
public function testCountUsersInGroupNotRegistered() {
$this->expectException(\Exception::class);
$this->expectExceptionMessage('No plugin implements countUsersInGroup in this LDAP Backend.');
@ -237,7 +237,7 @@ class GroupLDAPPluginTest extends \Test\TestCase {
$pluginManager->getGroupDetails('gid');
}
public function testgetGroupDetailsNotRegistered() {
$this->expectException(\Exception::class);
$this->expectExceptionMessage('No plugin implements getGroupDetails in this LDAP Backend.');

4
apps/user_ldap/tests/Group_LDAPTest.php

@ -1092,7 +1092,7 @@ class Group_LDAPTest extends TestCase {
$pluginManager->expects($this->once())
->method('deleteGroup')
->with('gid')
->willReturn('result');
->willReturn(true);
$mapper = $this->getMockBuilder(GroupMapping::class)
->setMethods(['unmap'])
@ -1108,7 +1108,7 @@ class Group_LDAPTest extends TestCase {
$ldap = new GroupLDAP($access, $pluginManager);
$this->assertEquals($ldap->deleteGroup('gid'), 'result');
$this->assertTrue($ldap->deleteGroup('gid'));
}

2
core/Command/Group/Delete.php

@ -61,7 +61,7 @@ class Delete extends Base {
$output->writeln('<error>Group "' . $gid . '" could not be deleted.</error>');
return 1;
}
if (! $this->groupManager->groupExists($gid)) {
if (!$this->groupManager->groupExists($gid)) {
$output->writeln('<error>Group "' . $gid . '" does not exist.</error>');
return 1;
}

Loading…
Cancel
Save