diff options
-rw-r--r-- | apps/user_ldap/appinfo/info.xml | 1 | ||||
-rw-r--r-- | apps/user_ldap/lib/Command/CheckGroup.php | 142 | ||||
-rw-r--r-- | apps/user_ldap/lib/Group_LDAP.php | 33 | ||||
-rw-r--r-- | apps/user_ldap/lib/Group_Proxy.php | 18 | ||||
-rw-r--r-- | apps/user_ldap/lib/Service/UpdateGroupsService.php | 15 |
5 files changed, 199 insertions, 10 deletions
diff --git a/apps/user_ldap/appinfo/info.xml b/apps/user_ldap/appinfo/info.xml index 425fefe38e8..e96d1b40a02 100644 --- a/apps/user_ldap/appinfo/info.xml +++ b/apps/user_ldap/appinfo/info.xml @@ -48,6 +48,7 @@ A user logs into Nextcloud with their LDAP or AD credentials, and is granted acc <commands> <command>OCA\User_LDAP\Command\CheckUser</command> + <command>OCA\User_LDAP\Command\CheckGroup</command> <command>OCA\User_LDAP\Command\CreateEmptyConfig</command> <command>OCA\User_LDAP\Command\DeleteConfig</command> <command>OCA\User_LDAP\Command\ResetGroup</command> diff --git a/apps/user_ldap/lib/Command/CheckGroup.php b/apps/user_ldap/lib/Command/CheckGroup.php new file mode 100644 index 00000000000..8617b06c57f --- /dev/null +++ b/apps/user_ldap/lib/Command/CheckGroup.php @@ -0,0 +1,142 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2016, ownCloud, Inc. + * + * @author Arthur Schiwon <blizzz@arthur-schiwon.de> + * @author Christoph Wurst <christoph@winzerhof-wurst.at> + * @author Côme Chilliet <come.chilliet@nextcloud.com> + * @author Joas Schilling <coding@schilljs.com> + * @author Morris Jobke <hey@morrisjobke.de> + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @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 OCA\User_LDAP\Helper; +use OCA\User_LDAP\Mapping\GroupMapping; +use OCA\User_LDAP\Group_Proxy; +use OCA\User_LDAP\Service\UpdateGroupsService; +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; + +class CheckGroup extends Command { + public function __construct( + private UpdateGroupsService $service, + protected Group_Proxy $backend, + protected Helper $helper, + protected GroupMapping $mapping, + ) { + parent::__construct(); + } + + protected function configure(): void { + $this + ->setName('ldap:check-group') + ->setDescription('checks whether a group exists on LDAP.') + ->addArgument( + 'ocName', + InputArgument::REQUIRED, + 'the group name as used in Nextcloud, or the LDAP DN' + ) + ->addOption( + 'force', + null, + InputOption::VALUE_NONE, + 'ignores disabled LDAP configuration' + ) + ->addOption( + 'update', + null, + InputOption::VALUE_NONE, + 'syncs values from LDAP' + ) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output): int { + try { + $this->assertAllowed($input->getOption('force')); + $gid = $input->getArgument('ocName'); + if ($this->backend->getLDAPAccess($gid)->stringResemblesDN($gid)) { + $groupname = $this->backend->dn2GroupName($gid); + if ($groupname !== false) { + $gid = $groupname; + } + } + $wasMapped = $this->groupWasMapped($gid); + $exists = $this->backend->groupExistsOnLDAP($gid, true); + if ($exists === true) { + $output->writeln('The group is still available on LDAP.'); + if ($input->getOption('update')) { + $this->updateGroup($gid, $output, $wasMapped); + } + return 0; + } elseif ($wasMapped) { + $output->writeln('The group does not exists on LDAP anymore.'); + return 0; + } else { + throw new \Exception('The given group is not a recognized LDAP group.'); + } + } catch (\Exception $e) { + $output->writeln('<error>' . $e->getMessage(). '</error>'); + return 1; + } + } + + /** + * checks whether a group is actually mapped + * @param string $ocName the groupname as used in Nextcloud + */ + protected function groupWasMapped(string $ocName): bool { + $dn = $this->mapping->getDNByName($ocName); + return $dn !== false; + } + + /** + * checks whether the setup allows reliable checking of LDAP group existence + * @throws \Exception + */ + protected function assertAllowed(bool $force): void { + if ($this->helper->haveDisabledConfigurations() && !$force) { + throw new \Exception('Cannot check group 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. + } + + private function updateGroup(string $gid, OutputInterface $output, bool $wasMapped): void { + try { + if ($wasMapped) { + $this->service->handleKnownGroups([$gid]); + } else { + $this->service->handleCreatedGroups([$gid]); + } + } catch (\Exception $e) { + $output->writeln('<error>Error while trying to lookup and update attributes from LDAP</error>'); + } + } +} diff --git a/apps/user_ldap/lib/Group_LDAP.php b/apps/user_ldap/lib/Group_LDAP.php index 84267171d37..3a17ba70bb1 100644 --- a/apps/user_ldap/lib/Group_LDAP.php +++ b/apps/user_ldap/lib/Group_LDAP.php @@ -1105,31 +1105,43 @@ class Group_LDAP extends BackendUtility implements GroupInterface, IGroupLDAP, I * @throws ServerNotAvailableException */ public function groupExists($gid) { - $groupExists = $this->access->connection->getFromCache('groupExists' . $gid); - if (!is_null($groupExists)) { - return (bool)$groupExists; + return $this->groupExistsOnLDAP($gid, false); + } + + /** + * Check if a group exists + * + * @throws ServerNotAvailableException + */ + public function groupExistsOnLDAP(string $gid, bool $ignoreCache = false): bool { + $cacheKey = 'groupExists' . $gid; + if (!$ignoreCache) { + $groupExists = $this->access->connection->getFromCache($cacheKey); + 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); + $this->access->connection->writeToCache($cacheKey, false); return false; } if (!$this->access->isDNPartOfBase($dn, $this->access->connection->ldapBaseGroups)) { - $this->access->connection->writeToCache('groupExists' . $gid, false); + $this->access->connection->writeToCache($cacheKey, 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->ldapGroupFilter))) { - $this->access->connection->writeToCache('groupExists' . $gid, false); + $this->access->connection->writeToCache($cacheKey, false); return false; } - $this->access->connection->writeToCache('groupExists' . $gid, true); + $this->access->connection->writeToCache($cacheKey, true); return true; } @@ -1336,4 +1348,11 @@ class Group_LDAP extends BackendUtility implements GroupInterface, IGroupLDAP, I $this->access->connection->writeToCache($cacheKey, $displayName); return $displayName; } + + /** + * returns the groupname for the given LDAP DN, if available + */ + public function dn2GroupName(string $dn): string|false { + return $this->access->dn2groupname($dn); + } } diff --git a/apps/user_ldap/lib/Group_Proxy.php b/apps/user_ldap/lib/Group_Proxy.php index 5f8d0562fd9..9d996b45f43 100644 --- a/apps/user_ldap/lib/Group_Proxy.php +++ b/apps/user_ldap/lib/Group_Proxy.php @@ -28,6 +28,7 @@ */ namespace OCA\User_LDAP; +use OC\ServerNotAvailableException; use OCP\Group\Backend\IDeleteGroupBackend; use OCP\Group\Backend\IGetDisplayNameBackend; use OCP\Group\Backend\INamedBackend; @@ -287,6 +288,23 @@ class Group_Proxy extends Proxy implements \OCP\GroupInterface, IGroupLDAP, IGet } /** + * Check if a group exists + * + * @throws ServerNotAvailableException + */ + public function groupExistsOnLDAP(string $gid, bool $ignoreCache = false): bool { + return $this->handleRequest($gid, 'groupExistsOnLDAP', [$gid, $ignoreCache]); + } + + /** + * returns the groupname for the given LDAP DN, if available + */ + public function dn2GroupName(string $dn): string|false { + $id = 'DN,' . $dn; + return $this->handleRequest($id, 'dn2GroupName', [$dn]); + } + + /** * Check if backend implements actions * * @param int $actions bitwise-or'ed actions diff --git a/apps/user_ldap/lib/Service/UpdateGroupsService.php b/apps/user_ldap/lib/Service/UpdateGroupsService.php index aaa86435695..ba564819d01 100644 --- a/apps/user_ldap/lib/Service/UpdateGroupsService.php +++ b/apps/user_ldap/lib/Service/UpdateGroupsService.php @@ -38,8 +38,10 @@ use OCA\User_LDAP\Db\GroupMembershipMapper; use OCA\User_LDAP\Group_Proxy; use OCP\DB\Exception; use OCP\EventDispatcher\IEventDispatcher; +use OCP\Group\Events\GroupCreatedEvent; use OCP\Group\Events\UserAddedEvent; use OCP\Group\Events\UserRemovedEvent; +use OCP\IGroup; use OCP\IGroupManager; use OCP\IUser; use OCP\IUserManager; @@ -85,6 +87,7 @@ class UpdateGroupsService { $this->logger->debug('service "updateGroups" – Dealing with known Groups.'); foreach ($groups as $group) { + $this->logger->debug('service "updateGroups" – Dealing with {group}.', ['group' => $group]); $groupMemberships = $this->groupMembershipMapper->findGroupMemberships($group); $knownUsers = array_map( fn (GroupMembership $groupMembership): string => $groupMembership->getUserid(), @@ -147,13 +150,19 @@ class UpdateGroupsService { $this->logger->info('service "updateGroups" – new group "' . $createdGroup . '" found.'); $users = $this->groupBackend->usersInGroup($createdGroup); - foreach ($users as $user) { - $this->groupMembershipMapper->insert(GroupMembership::fromParams(['groupid' => $createdGroup,'userid' => $user])); - } $groupObject = $this->groupManager->get($group); if ($groupObject instanceof IGroup) { $this->dispatcher->dispatchTyped(new GroupCreatedEvent($groupObject)); } + foreach ($users as $user) { + $this->groupMembershipMapper->insert(GroupMembership::fromParams(['groupid' => $createdGroup,'userid' => $user])); + if ($groupObject instanceof IGroup) { + $userObject = $this->userManager->get($user); + if ($userObject instanceof IUser) { + $this->dispatcher->dispatchTyped(new UserAddedEvent($groupObject, $userObject)); + } + } + } } $this->logger->debug('service "updateGroups" – FINISHED dealing with created Groups.'); } |