]> source.dussan.org Git - nextcloud-server.git/commitdiff
Add check-group command
authorCôme Chilliet <come.chilliet@nextcloud.com>
Thu, 20 Jul 2023 14:44:25 +0000 (16:44 +0200)
committerCôme Chilliet <come.chilliet@nextcloud.com>
Thu, 10 Aug 2023 08:57:33 +0000 (10:57 +0200)
Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
apps/user_ldap/appinfo/info.xml
apps/user_ldap/lib/Command/CheckGroup.php [new file with mode: 0644]
apps/user_ldap/lib/Group_LDAP.php
apps/user_ldap/lib/Group_Proxy.php
apps/user_ldap/lib/Service/UpdateGroupsService.php

index 425fefe38e84d29f17b79f0e17e9f42c2ea28f12..e96d1b40a0213f122b54d2e498469f8a09c30cb7 100644 (file)
@@ -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 (file)
index 0000000..8617b06
--- /dev/null
@@ -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>');
+               }
+       }
+}
index 84267171d3749fba4a964cd6b96060575db3c9c5..3a17ba70bb1a896db29fed9a254312a7478583e1 100644 (file)
@@ -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);
+       }
 }
index 5f8d0562fd91853d214c5fd96581c7e0fdd1ea5b..9d996b45f4330cf3d4d22c5b361c7c78c58e0ae3 100644 (file)
@@ -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;
@@ -286,6 +287,23 @@ class Group_Proxy extends Proxy implements \OCP\GroupInterface, IGroupLDAP, IGet
                return $this->handleRequest($gid, 'groupExists', [$gid]);
        }
 
+       /**
+        * 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
         *
index aaa86435695f95b6b9add50e0e04c882e5ab0a44..ba564819d01a162ff5dacce8ab9bd20acee67c1c 100644 (file)
@@ -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.');
        }