Skip to content

Commit bb584c2

Browse files
committed
Add check-group command
Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
1 parent 27eda60 commit bb584c2

3 files changed

Lines changed: 194 additions & 7 deletions

File tree

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* @copyright Copyright (c) 2016, ownCloud, Inc.
7+
*
8+
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
9+
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
10+
* @author Côme Chilliet <come.chilliet@nextcloud.com>
11+
* @author Joas Schilling <coding@schilljs.com>
12+
* @author Morris Jobke <hey@morrisjobke.de>
13+
* @author Roeland Jago Douma <roeland@famdouma.nl>
14+
*
15+
* @license AGPL-3.0
16+
*
17+
* This code is free software: you can redistribute it and/or modify
18+
* it under the terms of the GNU Affero General Public License, version 3,
19+
* as published by the Free Software Foundation.
20+
*
21+
* This program is distributed in the hope that it will be useful,
22+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
23+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24+
* GNU Affero General Public License for more details.
25+
*
26+
* You should have received a copy of the GNU Affero General Public License, version 3,
27+
* along with this program. If not, see <http://www.gnu.org/licenses/>
28+
*
29+
*/
30+
31+
namespace OCA\User_LDAP\Command;
32+
33+
use OCA\User_LDAP\Helper;
34+
use OCA\User_LDAP\Mapping\GroupMapping;
35+
use OCA\User_LDAP\Group_Proxy;
36+
use Symfony\Component\Console\Command\Command;
37+
use Symfony\Component\Console\Input\InputArgument;
38+
use Symfony\Component\Console\Input\InputInterface;
39+
use Symfony\Component\Console\Input\InputOption;
40+
use Symfony\Component\Console\Output\OutputInterface;
41+
42+
class CheckGroup extends Command {
43+
public function __construct(
44+
protected Group_Proxy $backend,
45+
protected Helper $helper,
46+
protected GroupMapping $mapping,
47+
) {
48+
parent::__construct();
49+
}
50+
51+
protected function configure(): void {
52+
$this
53+
->setName('ldap:check-group')
54+
->setDescription('checks whether a group exists on LDAP.')
55+
->addArgument(
56+
'ocName',
57+
InputArgument::REQUIRED,
58+
'the group name as used in Nextcloud, or the LDAP DN'
59+
)
60+
->addOption(
61+
'force',
62+
null,
63+
InputOption::VALUE_NONE,
64+
'ignores disabled LDAP configuration'
65+
)
66+
->addOption(
67+
'update',
68+
null,
69+
InputOption::VALUE_NONE,
70+
'syncs values from LDAP'
71+
)
72+
;
73+
}
74+
75+
protected function execute(InputInterface $input, OutputInterface $output): int {
76+
try {
77+
$this->assertAllowed($input->getOption('force'));
78+
$gid = $input->getArgument('ocName');
79+
if ($this->backend->getLDAPAccess($gid)->stringResemblesDN($gid)) {
80+
$groupname = $this->backend->dn2GroupName($gid);
81+
if ($groupname !== false) {
82+
$gid = $groupname;
83+
}
84+
}
85+
$wasMapped = $this->groupWasMapped($gid);
86+
$exists = $this->backend->groupExistsOnLDAP($gid, true);
87+
if ($exists === true) {
88+
$output->writeln('The group is still available on LDAP.');
89+
if ($input->getOption('update')) {
90+
$this->updateGroup($gid, $output);
91+
}
92+
return 0;
93+
} elseif ($wasMapped) {
94+
$output->writeln('The group does not exists on LDAP anymore.');
95+
return 0;
96+
} else {
97+
throw new \Exception('The given group is not a recognized LDAP group.');
98+
}
99+
} catch (\Exception $e) {
100+
$output->writeln('<error>' . $e->getMessage(). '</error>');
101+
return 1;
102+
}
103+
}
104+
105+
/**
106+
* checks whether a group is actually mapped
107+
* @param string $ocName the groupname as used in Nextcloud
108+
*/
109+
protected function groupWasMapped(string $ocName): bool {
110+
$dn = $this->mapping->getDNByName($ocName);
111+
return $dn !== false;
112+
}
113+
114+
/**
115+
* checks whether the setup allows reliable checking of LDAP group existence
116+
* @throws \Exception
117+
*/
118+
protected function assertAllowed(bool $force): void {
119+
if ($this->helper->haveDisabledConfigurations() && !$force) {
120+
throw new \Exception('Cannot check group existence, because '
121+
. 'disabled LDAP configurations are present.');
122+
}
123+
124+
// we don't check ldapUserCleanupInterval from config.php because this
125+
// action is triggered manually, while the setting only controls the
126+
// background job.
127+
}
128+
129+
private function updateGroup(string $gid, OutputInterface $output): void {
130+
try {
131+
$access = $this->backend->getLDAPAccess($gid);
132+
$attrs = $access->userManager->getAttributes();
133+
$user = $access->userManager->get($gid);
134+
$avatarAttributes = $access->getConnection()->resolveRule('avatar');
135+
$result = $access->search('objectclass=*', $user->getDN(), $attrs, 1, 0);
136+
foreach ($result[0] as $attribute => $valueSet) {
137+
$output->writeln(' ' . $attribute . ': ');
138+
foreach ($valueSet as $value) {
139+
if (in_array($attribute, $avatarAttributes)) {
140+
$value = '{ImageData}';
141+
}
142+
$output->writeln(' ' . $value);
143+
}
144+
}
145+
$access->batchApplyUserAttributes($result);
146+
} catch (\Exception $e) {
147+
$output->writeln('<error>Error while trying to lookup and update attributes from LDAP</error>');
148+
}
149+
}
150+
}

apps/user_ldap/lib/Group_LDAP.php

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,31 +1105,43 @@ public function getGroups($search = '', $limit = -1, $offset = 0) {
11051105
* @throws ServerNotAvailableException
11061106
*/
11071107
public function groupExists($gid) {
1108-
$groupExists = $this->access->connection->getFromCache('groupExists' . $gid);
1109-
if (!is_null($groupExists)) {
1110-
return (bool)$groupExists;
1108+
return $this->groupExistsOnLDAP($gid, false);
1109+
}
1110+
1111+
/**
1112+
* Check if a group exists
1113+
*
1114+
* @throws ServerNotAvailableException
1115+
*/
1116+
public function groupExistsOnLDAP(string $gid, bool $ignoreCache = false): bool {
1117+
$cacheKey = 'groupExists' . $gid;
1118+
if (!$ignoreCache) {
1119+
$groupExists = $this->access->connection->getFromCache($cacheKey);
1120+
if (!is_null($groupExists)) {
1121+
return (bool)$groupExists;
1122+
}
11111123
}
11121124

11131125
//getting dn, if false the group does not exist. If dn, it may be mapped
11141126
//only, requires more checking.
11151127
$dn = $this->access->groupname2dn($gid);
11161128
if (!$dn) {
1117-
$this->access->connection->writeToCache('groupExists' . $gid, false);
1129+
$this->access->connection->writeToCache($cacheKey, false);
11181130
return false;
11191131
}
11201132

11211133
if (!$this->access->isDNPartOfBase($dn, $this->access->connection->ldapBaseGroups)) {
1122-
$this->access->connection->writeToCache('groupExists' . $gid, false);
1134+
$this->access->connection->writeToCache($cacheKey, false);
11231135
return false;
11241136
}
11251137

11261138
//if group really still exists, we will be able to read its objectClass
11271139
if (!is_array($this->access->readAttribute($dn, '', $this->access->connection->ldapGroupFilter))) {
1128-
$this->access->connection->writeToCache('groupExists' . $gid, false);
1140+
$this->access->connection->writeToCache($cacheKey, false);
11291141
return false;
11301142
}
11311143

1132-
$this->access->connection->writeToCache('groupExists' . $gid, true);
1144+
$this->access->connection->writeToCache($cacheKey, true);
11331145
return true;
11341146
}
11351147

@@ -1336,4 +1348,11 @@ public function getDisplayName(string $gid): string {
13361348
$this->access->connection->writeToCache($cacheKey, $displayName);
13371349
return $displayName;
13381350
}
1351+
1352+
/**
1353+
* returns the groupname for the given LDAP DN, if available
1354+
*/
1355+
public function dn2GroupName(string $dn): string|false {
1356+
return $this->access->dn2groupname($dn);
1357+
}
13391358
}

apps/user_ldap/lib/Group_Proxy.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
*/
2929
namespace OCA\User_LDAP;
3030

31+
use OC\ServerNotAvailableException;
3132
use OCP\Group\Backend\IDeleteGroupBackend;
3233
use OCP\Group\Backend\IGetDisplayNameBackend;
3334
use OCP\Group\Backend\INamedBackend;
@@ -286,6 +287,23 @@ public function groupExists($gid) {
286287
return $this->handleRequest($gid, 'groupExists', [$gid]);
287288
}
288289

290+
/**
291+
* Check if a group exists
292+
*
293+
* @throws ServerNotAvailableException
294+
*/
295+
public function groupExistsOnLDAP(string $gid, bool $ignoreCache = false): bool {
296+
return $this->handleRequest($gid, 'groupExistsOnLDAP', [$gid, $ignoreCache]);
297+
}
298+
299+
/**
300+
* returns the groupname for the given LDAP DN, if available
301+
*/
302+
public function dn2GroupName(string $dn): string|false {
303+
$id = 'DN,' . $dn;
304+
return $this->handleRequest($id, 'dn2GroupName', [$dn]);
305+
}
306+
289307
/**
290308
* Check if backend implements actions
291309
*

0 commit comments

Comments
 (0)