Skip to content

Commit 212138d

Browse files
authored
Merge pull request #19919 from nextcloud/enh/noid/ldpa_group_perf
LDAP Group Backend optimizations
2 parents 84a3536 + 4babdc0 commit 212138d

File tree

10 files changed

+428
-206
lines changed

10 files changed

+428
-206
lines changed

apps/user_ldap/lib/Access.php

Lines changed: 100 additions & 88 deletions
Large diffs are not rendered by default.

apps/user_ldap/lib/Group_LDAP.php

Lines changed: 78 additions & 36 deletions
Large diffs are not rendered by default.

apps/user_ldap/lib/Group_Proxy.php

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ class Group_Proxy extends Proxy implements \OCP\GroupInterface, IGroupLDAP, IGet
3636

3737
/**
3838
* Constructor
39+
*
3940
* @param string[] $serverConfigPrefixes array containing the config Prefixes
4041
*/
4142
public function __construct($serverConfigPrefixes, ILDAPWrapper $ldap, GroupPluginManager $groupPluginManager) {
@@ -51,6 +52,7 @@ public function __construct($serverConfigPrefixes, ILDAPWrapper $ldap, GroupPlug
5152

5253
/**
5354
* Tries the backends one after the other until a positive result is returned from the specified method
55+
*
5456
* @param string $gid the gid connected to the request
5557
* @param string $method the method of the group backend that shall be called
5658
* @param array $parameters an array of parameters to be passed
@@ -60,7 +62,9 @@ protected function walkBackends($gid, $method, $parameters) {
6062
$cacheKey = $this->getGroupCacheKey($gid);
6163
foreach ($this->backends as $configPrefix => $backend) {
6264
if ($result = call_user_func_array([$backend, $method], $parameters)) {
63-
$this->writeToCache($cacheKey, $configPrefix);
65+
if (!$this->isSingleBackend()) {
66+
$this->writeToCache($cacheKey, $configPrefix);
67+
}
6468
return $result;
6569
}
6670
}
@@ -69,6 +73,7 @@ protected function walkBackends($gid, $method, $parameters) {
6973

7074
/**
7175
* Asks the backend connected to the server that supposely takes care of the gid from the request.
76+
*
7277
* @param string $gid the gid connected to the request
7378
* @param string $method the method of the group backend that shall be called
7479
* @param array $parameters an array of parameters to be passed
@@ -99,8 +104,13 @@ protected function callOnLastSeenOn($gid, $method, $parameters, $passOnWhen) {
99104
return false;
100105
}
101106

107+
protected function activeBackends(): int {
108+
return count($this->backends);
109+
}
110+
102111
/**
103112
* is user in group?
113+
*
104114
* @param string $uid uid of the user
105115
* @param string $gid gid of the group
106116
* @return bool
@@ -113,6 +123,7 @@ public function inGroup($uid, $gid) {
113123

114124
/**
115125
* Get all groups a user belongs to
126+
*
116127
* @param string $uid Name of the user
117128
* @return string[] with group names
118129
*
@@ -134,6 +145,7 @@ public function getUserGroups($uid) {
134145

135146
/**
136147
* get a list of all users in a group
148+
*
137149
* @return string[] with user ids
138150
*/
139151
public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) {
@@ -160,6 +172,7 @@ public function createGroup($gid) {
160172

161173
/**
162174
* delete a group
175+
*
163176
* @param string $gid gid of the group to delete
164177
* @return bool
165178
*/
@@ -170,6 +183,7 @@ public function deleteGroup($gid) {
170183

171184
/**
172185
* Add a user to a group
186+
*
173187
* @param string $uid Name of the user to add to group
174188
* @param string $gid Name of the group in which add the user
175189
* @return bool
@@ -183,6 +197,7 @@ public function addToGroup($uid, $gid) {
183197

184198
/**
185199
* Removes a user from a group
200+
*
186201
* @param string $uid Name of the user to remove from group
187202
* @param string $gid Name of the group from which remove the user
188203
* @return bool
@@ -196,6 +211,7 @@ public function removeFromGroup($uid, $gid) {
196211

197212
/**
198213
* returns the number of users in a group, who match the search term
214+
*
199215
* @param string $gid the internal group name
200216
* @param string $search optional, a search string
201217
* @return int|bool
@@ -207,6 +223,7 @@ public function countUsersInGroup($gid, $search = '') {
207223

208224
/**
209225
* get an array with group details
226+
*
210227
* @param string $gid
211228
* @return array|false
212229
*/
@@ -217,6 +234,7 @@ public function getGroupDetails($gid) {
217234

218235
/**
219236
* get a list of all groups
237+
*
220238
* @return string[] with group names
221239
*
222240
* Returns a list with all groups
@@ -236,6 +254,7 @@ public function getGroups($search = '', $limit = -1, $offset = 0) {
236254

237255
/**
238256
* check if a group exists
257+
*
239258
* @param string $gid
240259
* @return bool
241260
*/
@@ -245,6 +264,7 @@ public function groupExists($gid) {
245264

246265
/**
247266
* Check if backend implements actions
267+
*
248268
* @param int $actions bitwise-or'ed actions
249269
* @return boolean
250270
*
@@ -258,6 +278,7 @@ public function implementsActions($actions) {
258278

259279
/**
260280
* Return access for LDAP interaction.
281+
*
261282
* @param string $gid
262283
* @return Access instance of Access for LDAP interaction
263284
*/
@@ -268,6 +289,7 @@ public function getLDAPAccess($gid) {
268289
/**
269290
* Return a new LDAP connection for the specified group.
270291
* The connection needs to be closed manually.
292+
*
271293
* @param string $gid
272294
* @return resource of the LDAP connection
273295
*/

apps/user_ldap/lib/Helper.php

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,24 +34,30 @@
3434

3535
namespace OCA\User_LDAP;
3636

37+
use OC\Cache\CappedMemoryCache;
3738
use OCP\IConfig;
3839

3940
class Helper {
4041

4142
/** @var IConfig */
4243
private $config;
4344

45+
/** @var CappedMemoryCache */
46+
protected $sanitizeDnCache;
47+
4448
/**
4549
* Helper constructor.
4650
*
4751
* @param IConfig $config
4852
*/
4953
public function __construct(IConfig $config) {
5054
$this->config = $config;
55+
$this->sanitizeDnCache = new CappedMemoryCache(10000);
5156
}
5257

5358
/**
5459
* returns prefixes for each saved LDAP/AD server configuration.
60+
*
5561
* @param bool $activeConfigurations optional, whether only active configuration shall be
5662
* retrieved, defaults to false
5763
* @return array with a list of the available prefixes
@@ -92,6 +98,7 @@ public function getServerConfigurationPrefixes($activeConfigurations = false) {
9298
/**
9399
*
94100
* determines the host for every configured connection
101+
*
95102
* @return array an array with configprefix as keys
96103
*
97104
*/
@@ -144,6 +151,7 @@ private function getServersConfig($value) {
144151

145152
/**
146153
* deletes a given saved LDAP/AD server configuration.
154+
*
147155
* @param string $prefix the configuration prefix of the config to delete
148156
* @return bool true on success, false otherwise
149157
*/
@@ -161,11 +169,11 @@ public function deleteServerConfiguration($prefix) {
161169
DELETE
162170
FROM `*PREFIX*appconfig`
163171
WHERE `configkey` LIKE ?
164-
'.$saveOtherConfigurations.'
172+
' . $saveOtherConfigurations . '
165173
AND `appid` = \'user_ldap\'
166174
AND `configkey` NOT IN (\'enabled\', \'installed_version\', \'types\', \'bgjUpdateGroupsLastRun\')
167175
');
168-
$delRows = $query->execute([$prefix.'%']);
176+
$delRows = $query->execute([$prefix . '%']);
169177

170178
if ($delRows === null) {
171179
return false;
@@ -180,8 +188,9 @@ public function deleteServerConfiguration($prefix) {
180188

181189
/**
182190
* checks whether there is one or more disabled LDAP configurations
183-
* @throws \Exception
191+
*
184192
* @return bool
193+
* @throws \Exception
185194
*/
186195
public function haveDisabledConfigurations() {
187196
$all = $this->getServerConfigurationPrefixes(false);
@@ -196,6 +205,7 @@ public function haveDisabledConfigurations() {
196205

197206
/**
198207
* extracts the domain from a given URL
208+
*
199209
* @param string $url the URL
200210
* @return string|false domain as string on success, false otherwise
201211
*/
@@ -229,6 +239,7 @@ public function setLDAPProvider() {
229239

230240
/**
231241
* sanitizes a DN received from the LDAP server
242+
*
232243
* @param array $dn the DN in question
233244
* @return array|string the sanitized DN
234245
*/
@@ -242,12 +253,20 @@ public function sanitizeDN($dn) {
242253
return $result;
243254
}
244255

256+
if (!is_string($dn)) {
257+
throw new \LogicException('String expected ' . \gettype($dn) . ' given');
258+
}
259+
260+
if (($sanitizedDn = $this->sanitizeDnCache->get($dn)) !== null) {
261+
return $sanitizedDn;
262+
}
263+
245264
//OID sometimes gives back DNs with whitespace after the comma
246265
// a la "uid=foo, cn=bar, dn=..." We need to tackle this!
247-
$dn = preg_replace('/([^\\\]),(\s+)/u', '\1,', $dn);
266+
$sanitizedDn = preg_replace('/([^\\\]),(\s+)/u', '\1,', $dn);
248267

249268
//make comparisons and everything work
250-
$dn = mb_strtolower($dn, 'UTF-8');
269+
$sanitizedDn = mb_strtolower($sanitizedDn, 'UTF-8');
251270

252271
//escape DN values according to RFC 2253 – this is already done by ldap_explode_dn
253272
//to use the DN in search filters, \ needs to be escaped to \5c additionally
@@ -261,17 +280,19 @@ public function sanitizeDN($dn) {
261280
'\;' => '\5c3B',
262281
'\"' => '\5c22',
263282
'\#' => '\5c23',
264-
'(' => '\28',
265-
')' => '\29',
266-
'*' => '\2A',
283+
'(' => '\28',
284+
')' => '\29',
285+
'*' => '\2A',
267286
];
268-
$dn = str_replace(array_keys($replacements), array_values($replacements), $dn);
287+
$sanitizedDn = str_replace(array_keys($replacements), array_values($replacements), $sanitizedDn);
288+
$this->sanitizeDnCache->set($dn, $sanitizedDn);
269289

270-
return $dn;
290+
return $sanitizedDn;
271291
}
272292

273293
/**
274294
* converts a stored DN so it can be used as base parameter for LDAP queries, internally we store them for usage in LDAP filters
295+
*
275296
* @param string $dn the DN
276297
* @return string
277298
*/
@@ -302,7 +323,7 @@ public static function loginName2UserName($param) {
302323
$userSession = \OC::$server->getUserSession();
303324
$userPluginManager = \OC::$server->query('LDAPUserPluginManager');
304325

305-
$userBackend = new User_Proxy(
326+
$userBackend = new User_Proxy(
306327
$configPrefixes, $ldapWrapper, $ocConfig, $notificationManager, $userSession, $userPluginManager
307328
);
308329
$uid = $userBackend->loginName2UserName($param['uid']);

0 commit comments

Comments
 (0)