Passed
Push — master ( 8568c1...95eefd )
by Blizzz
17:42 queued 13s
created
apps/user_ldap/lib/LDAP.php 2 patches
Indentation   +372 added lines, -372 removed lines patch added patch discarded remove patch
@@ -40,376 +40,376 @@
 block discarded – undo
40 40
 use Psr\Log\LoggerInterface;
41 41
 
42 42
 class LDAP implements ILDAPWrapper {
43
-	protected string $logFile = '';
44
-	protected array $curArgs = [];
45
-	protected LoggerInterface $logger;
46
-
47
-	private ?LdapDataCollector $dataCollector = null;
48
-
49
-	public function __construct(string $logFile = '') {
50
-		$this->logFile = $logFile;
51
-
52
-		/** @var IProfiler $profiler */
53
-		$profiler = \OC::$server->get(IProfiler::class);
54
-		if ($profiler->isEnabled()) {
55
-			$this->dataCollector = new LdapDataCollector();
56
-			$profiler->add($this->dataCollector);
57
-		}
58
-
59
-		$this->logger = \OCP\Server::get(LoggerInterface::class);
60
-	}
61
-
62
-	/**
63
-	 * {@inheritDoc}
64
-	 */
65
-	public function bind($link, $dn, $password) {
66
-		return $this->invokeLDAPMethod('bind', $link, $dn, $password);
67
-	}
68
-
69
-	/**
70
-	 * {@inheritDoc}
71
-	 */
72
-	public function connect($host, $port) {
73
-		$pos = strpos($host, '://');
74
-		if ($pos === false) {
75
-			$host = 'ldap://' . $host;
76
-			$pos = 4;
77
-		}
78
-		if (strpos($host, ':', $pos + 1) === false && !empty($port)) {
79
-			//ldap_connect ignores port parameter when URLs are passed
80
-			$host .= ':' . $port;
81
-		}
82
-		return $this->invokeLDAPMethod('connect', $host);
83
-	}
84
-
85
-	/**
86
-	 * {@inheritDoc}
87
-	 */
88
-	public function controlPagedResultResponse($link, $result, &$cookie): bool {
89
-		$errorCode = 0;
90
-		$errorMsg = '';
91
-		$controls = [];
92
-		$matchedDn = null;
93
-		$referrals = [];
94
-
95
-		/** Cannot use invokeLDAPMethod because arguments are passed by reference */
96
-		$this->preFunctionCall('ldap_parse_result', [$link, $result]);
97
-		$success = ldap_parse_result($link, $result,
98
-			$errorCode,
99
-			$matchedDn,
100
-			$errorMsg,
101
-			$referrals,
102
-			$controls);
103
-		if ($errorCode !== 0) {
104
-			$this->processLDAPError($link, 'ldap_parse_result', $errorCode, $errorMsg);
105
-		}
106
-		if ($this->dataCollector !== null) {
107
-			$this->dataCollector->stopLastLdapRequest();
108
-		}
109
-
110
-		$cookie = $controls[LDAP_CONTROL_PAGEDRESULTS]['value']['cookie'] ?? '';
111
-
112
-		return $success;
113
-	}
114
-
115
-	/**
116
-	 * {@inheritDoc}
117
-	 */
118
-	public function countEntries($link, $result) {
119
-		return $this->invokeLDAPMethod('count_entries', $link, $result);
120
-	}
121
-
122
-	/**
123
-	 * {@inheritDoc}
124
-	 */
125
-	public function errno($link) {
126
-		return $this->invokeLDAPMethod('errno', $link);
127
-	}
128
-
129
-	/**
130
-	 * {@inheritDoc}
131
-	 */
132
-	public function error($link) {
133
-		return $this->invokeLDAPMethod('error', $link);
134
-	}
135
-
136
-	/**
137
-	 * Splits DN into its component parts
138
-	 * @param string $dn
139
-	 * @param int $withAttrib
140
-	 * @return array|false
141
-	 * @link https://www.php.net/manual/en/function.ldap-explode-dn.php
142
-	 */
143
-	public function explodeDN($dn, $withAttrib) {
144
-		return $this->invokeLDAPMethod('explode_dn', $dn, $withAttrib);
145
-	}
146
-
147
-	/**
148
-	 * {@inheritDoc}
149
-	 */
150
-	public function firstEntry($link, $result) {
151
-		return $this->invokeLDAPMethod('first_entry', $link, $result);
152
-	}
153
-
154
-	/**
155
-	 * {@inheritDoc}
156
-	 */
157
-	public function getAttributes($link, $result) {
158
-		return $this->invokeLDAPMethod('get_attributes', $link, $result);
159
-	}
160
-
161
-	/**
162
-	 * {@inheritDoc}
163
-	 */
164
-	public function getDN($link, $result) {
165
-		return $this->invokeLDAPMethod('get_dn', $link, $result);
166
-	}
167
-
168
-	/**
169
-	 * {@inheritDoc}
170
-	 */
171
-	public function getEntries($link, $result) {
172
-		return $this->invokeLDAPMethod('get_entries', $link, $result);
173
-	}
174
-
175
-	/**
176
-	 * {@inheritDoc}
177
-	 */
178
-	public function nextEntry($link, $result) {
179
-		return $this->invokeLDAPMethod('next_entry', $link, $result);
180
-	}
181
-
182
-	/**
183
-	 * {@inheritDoc}
184
-	 */
185
-	public function read($link, $baseDN, $filter, $attr) {
186
-		return $this->invokeLDAPMethod('read', $link, $baseDN, $filter, $attr, 0, -1);
187
-	}
188
-
189
-	/**
190
-	 * {@inheritDoc}
191
-	 */
192
-	public function search($link, $baseDN, $filter, $attr, $attrsOnly = 0, $limit = 0, int $pageSize = 0, string $cookie = '') {
193
-		if ($pageSize > 0 || $cookie !== '') {
194
-			$serverControls = [[
195
-				'oid' => LDAP_CONTROL_PAGEDRESULTS,
196
-				'value' => [
197
-					'size' => $pageSize,
198
-					'cookie' => $cookie,
199
-				],
200
-				'iscritical' => false,
201
-			]];
202
-		} else {
203
-			$serverControls = [];
204
-		}
205
-
206
-		$oldHandler = set_error_handler(function ($no, $message, $file, $line) use (&$oldHandler) {
207
-			if (strpos($message, 'Partial search results returned: Sizelimit exceeded') !== false) {
208
-				return true;
209
-			}
210
-			$oldHandler($no, $message, $file, $line);
211
-			return true;
212
-		});
213
-		try {
214
-			$result = $this->invokeLDAPMethod('search', $link, $baseDN, $filter, $attr, $attrsOnly, $limit, -1, LDAP_DEREF_NEVER, $serverControls);
215
-
216
-			restore_error_handler();
217
-			return $result;
218
-		} catch (\Exception $e) {
219
-			restore_error_handler();
220
-			throw $e;
221
-		}
222
-	}
223
-
224
-	/**
225
-	 * {@inheritDoc}
226
-	 */
227
-	public function modReplace($link, $userDN, $password) {
228
-		return $this->invokeLDAPMethod('mod_replace', $link, $userDN, ['userPassword' => $password]);
229
-	}
230
-
231
-	/**
232
-	 * {@inheritDoc}
233
-	 */
234
-	public function exopPasswd($link, string $userDN, string $oldPassword, string $password) {
235
-		return $this->invokeLDAPMethod('exop_passwd', $link, $userDN, $oldPassword, $password);
236
-	}
237
-
238
-	/**
239
-	 * {@inheritDoc}
240
-	 */
241
-	public function setOption($link, $option, $value) {
242
-		return $this->invokeLDAPMethod('set_option', $link, $option, $value);
243
-	}
244
-
245
-	/**
246
-	 * {@inheritDoc}
247
-	 */
248
-	public function startTls($link) {
249
-		return $this->invokeLDAPMethod('start_tls', $link);
250
-	}
251
-
252
-	/**
253
-	 * {@inheritDoc}
254
-	 */
255
-	public function unbind($link) {
256
-		return $this->invokeLDAPMethod('unbind', $link);
257
-	}
258
-
259
-	/**
260
-	 * Checks whether the server supports LDAP
261
-	 * @return boolean if it the case, false otherwise
262
-	 * */
263
-	public function areLDAPFunctionsAvailable() {
264
-		return function_exists('ldap_connect');
265
-	}
266
-
267
-	/**
268
-	 * {@inheritDoc}
269
-	 */
270
-	public function isResource($resource) {
271
-		return is_resource($resource) || is_object($resource);
272
-	}
273
-
274
-	/**
275
-	 * Checks whether the return value from LDAP is wrong or not.
276
-	 *
277
-	 * When using ldap_search we provide an array, in case multiple bases are
278
-	 * configured. Thus, we need to check the array elements.
279
-	 *
280
-	 * @param mixed $result
281
-	 */
282
-	protected function isResultFalse(string $functionName, $result): bool {
283
-		if ($result === false) {
284
-			return true;
285
-		}
286
-
287
-		if ($functionName === 'ldap_search' && is_array($result)) {
288
-			foreach ($result as $singleResult) {
289
-				if ($singleResult === false) {
290
-					return true;
291
-				}
292
-			}
293
-		}
294
-
295
-		return false;
296
-	}
297
-
298
-	/**
299
-	 * @param array $arguments
300
-	 * @return mixed
301
-	 */
302
-	protected function invokeLDAPMethod(string $func, ...$arguments) {
303
-		$func = 'ldap_' . $func;
304
-		if (function_exists($func)) {
305
-			$this->preFunctionCall($func, $arguments);
306
-			$result = call_user_func_array($func, $arguments);
307
-			if ($this->isResultFalse($func, $result)) {
308
-				$this->postFunctionCall($func);
309
-			}
310
-			if ($this->dataCollector !== null) {
311
-				$this->dataCollector->stopLastLdapRequest();
312
-			}
313
-			return $result;
314
-		}
315
-		return null;
316
-	}
317
-
318
-	private function preFunctionCall(string $functionName, array $args): void {
319
-		$this->curArgs = $args;
320
-		$this->logger->debug('Calling LDAP function {func} with parameters {args}', [
321
-			'app' => 'user_ldap',
322
-			'func' => $functionName,
323
-			'args' => json_encode($args),
324
-		]);
325
-
326
-		if ($this->dataCollector !== null) {
327
-			$args = array_map(function ($item) {
328
-				if ($this->isResource($item)) {
329
-					return '(resource)';
330
-				}
331
-				if (isset($item[0]['value']['cookie']) && $item[0]['value']['cookie'] !== "") {
332
-					$item[0]['value']['cookie'] = "*opaque cookie*";
333
-				}
334
-				return $item;
335
-			}, $this->curArgs);
336
-
337
-			$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
338
-			$this->dataCollector->startLdapRequest($functionName, $args, $backtrace);
339
-		}
340
-
341
-		if ($this->logFile !== '' && is_writable(dirname($this->logFile)) && (!file_exists($this->logFile) || is_writable($this->logFile))) {
342
-			$args = array_map(fn ($item) => (!$this->isResource($item) ? $item : '(resource)'), $this->curArgs);
343
-			file_put_contents(
344
-				$this->logFile,
345
-				$functionName . '::' . json_encode($args) . "\n",
346
-				FILE_APPEND
347
-			);
348
-		}
349
-	}
350
-
351
-	/**
352
-	 * Analyzes the returned LDAP error and acts accordingly if not 0
353
-	 *
354
-	 * @param resource|\LDAP\Connection $resource the LDAP Connection resource
355
-	 * @throws ConstraintViolationException
356
-	 * @throws ServerNotAvailableException
357
-	 * @throws \Exception
358
-	 */
359
-	private function processLDAPError($resource, string $functionName, int $errorCode, string $errorMsg): void {
360
-		$this->logger->debug('LDAP error {message} ({code}) after calling {func}', [
361
-			'app' => 'user_ldap',
362
-			'message' => $errorMsg,
363
-			'code' => $errorCode,
364
-			'func' => $functionName,
365
-		]);
366
-		if ($functionName === 'ldap_get_entries'
367
-			&& $errorCode === -4) {
368
-		} elseif ($errorCode === 32) {
369
-			//for now
370
-		} elseif ($errorCode === 10) {
371
-			//referrals, we switch them off, but then there is AD :)
372
-		} elseif ($errorCode === -1) {
373
-			throw new ServerNotAvailableException('Lost connection to LDAP server.');
374
-		} elseif ($errorCode === 52) {
375
-			throw new ServerNotAvailableException('LDAP server is shutting down.');
376
-		} elseif ($errorCode === 48) {
377
-			throw new \Exception('LDAP authentication method rejected', $errorCode);
378
-		} elseif ($errorCode === 1) {
379
-			throw new \Exception('LDAP Operations error', $errorCode);
380
-		} elseif ($errorCode === 19) {
381
-			ldap_get_option($resource, LDAP_OPT_ERROR_STRING, $extended_error);
382
-			throw new ConstraintViolationException(!empty($extended_error) ? $extended_error : $errorMsg, $errorCode);
383
-		}
384
-	}
385
-
386
-	/**
387
-	 * Called after an ldap method is run to act on LDAP error if necessary
388
-	 * @throw \Exception
389
-	 */
390
-	private function postFunctionCall(string $functionName): void {
391
-		if ($this->isResource($this->curArgs[0])) {
392
-			$resource = $this->curArgs[0];
393
-		} elseif (
394
-			$functionName === 'ldap_search'
395
-			&& is_array($this->curArgs[0])
396
-			&& $this->isResource($this->curArgs[0][0])
397
-		) {
398
-			// we use always the same LDAP connection resource, is enough to
399
-			// take the first one.
400
-			$resource = $this->curArgs[0][0];
401
-		} else {
402
-			return;
403
-		}
404
-
405
-		$errorCode = ldap_errno($resource);
406
-		if ($errorCode === 0) {
407
-			return;
408
-		}
409
-		$errorMsg = ldap_error($resource);
410
-
411
-		$this->processLDAPError($resource, $functionName, $errorCode, $errorMsg);
412
-
413
-		$this->curArgs = [];
414
-	}
43
+    protected string $logFile = '';
44
+    protected array $curArgs = [];
45
+    protected LoggerInterface $logger;
46
+
47
+    private ?LdapDataCollector $dataCollector = null;
48
+
49
+    public function __construct(string $logFile = '') {
50
+        $this->logFile = $logFile;
51
+
52
+        /** @var IProfiler $profiler */
53
+        $profiler = \OC::$server->get(IProfiler::class);
54
+        if ($profiler->isEnabled()) {
55
+            $this->dataCollector = new LdapDataCollector();
56
+            $profiler->add($this->dataCollector);
57
+        }
58
+
59
+        $this->logger = \OCP\Server::get(LoggerInterface::class);
60
+    }
61
+
62
+    /**
63
+     * {@inheritDoc}
64
+     */
65
+    public function bind($link, $dn, $password) {
66
+        return $this->invokeLDAPMethod('bind', $link, $dn, $password);
67
+    }
68
+
69
+    /**
70
+     * {@inheritDoc}
71
+     */
72
+    public function connect($host, $port) {
73
+        $pos = strpos($host, '://');
74
+        if ($pos === false) {
75
+            $host = 'ldap://' . $host;
76
+            $pos = 4;
77
+        }
78
+        if (strpos($host, ':', $pos + 1) === false && !empty($port)) {
79
+            //ldap_connect ignores port parameter when URLs are passed
80
+            $host .= ':' . $port;
81
+        }
82
+        return $this->invokeLDAPMethod('connect', $host);
83
+    }
84
+
85
+    /**
86
+     * {@inheritDoc}
87
+     */
88
+    public function controlPagedResultResponse($link, $result, &$cookie): bool {
89
+        $errorCode = 0;
90
+        $errorMsg = '';
91
+        $controls = [];
92
+        $matchedDn = null;
93
+        $referrals = [];
94
+
95
+        /** Cannot use invokeLDAPMethod because arguments are passed by reference */
96
+        $this->preFunctionCall('ldap_parse_result', [$link, $result]);
97
+        $success = ldap_parse_result($link, $result,
98
+            $errorCode,
99
+            $matchedDn,
100
+            $errorMsg,
101
+            $referrals,
102
+            $controls);
103
+        if ($errorCode !== 0) {
104
+            $this->processLDAPError($link, 'ldap_parse_result', $errorCode, $errorMsg);
105
+        }
106
+        if ($this->dataCollector !== null) {
107
+            $this->dataCollector->stopLastLdapRequest();
108
+        }
109
+
110
+        $cookie = $controls[LDAP_CONTROL_PAGEDRESULTS]['value']['cookie'] ?? '';
111
+
112
+        return $success;
113
+    }
114
+
115
+    /**
116
+     * {@inheritDoc}
117
+     */
118
+    public function countEntries($link, $result) {
119
+        return $this->invokeLDAPMethod('count_entries', $link, $result);
120
+    }
121
+
122
+    /**
123
+     * {@inheritDoc}
124
+     */
125
+    public function errno($link) {
126
+        return $this->invokeLDAPMethod('errno', $link);
127
+    }
128
+
129
+    /**
130
+     * {@inheritDoc}
131
+     */
132
+    public function error($link) {
133
+        return $this->invokeLDAPMethod('error', $link);
134
+    }
135
+
136
+    /**
137
+     * Splits DN into its component parts
138
+     * @param string $dn
139
+     * @param int $withAttrib
140
+     * @return array|false
141
+     * @link https://www.php.net/manual/en/function.ldap-explode-dn.php
142
+     */
143
+    public function explodeDN($dn, $withAttrib) {
144
+        return $this->invokeLDAPMethod('explode_dn', $dn, $withAttrib);
145
+    }
146
+
147
+    /**
148
+     * {@inheritDoc}
149
+     */
150
+    public function firstEntry($link, $result) {
151
+        return $this->invokeLDAPMethod('first_entry', $link, $result);
152
+    }
153
+
154
+    /**
155
+     * {@inheritDoc}
156
+     */
157
+    public function getAttributes($link, $result) {
158
+        return $this->invokeLDAPMethod('get_attributes', $link, $result);
159
+    }
160
+
161
+    /**
162
+     * {@inheritDoc}
163
+     */
164
+    public function getDN($link, $result) {
165
+        return $this->invokeLDAPMethod('get_dn', $link, $result);
166
+    }
167
+
168
+    /**
169
+     * {@inheritDoc}
170
+     */
171
+    public function getEntries($link, $result) {
172
+        return $this->invokeLDAPMethod('get_entries', $link, $result);
173
+    }
174
+
175
+    /**
176
+     * {@inheritDoc}
177
+     */
178
+    public function nextEntry($link, $result) {
179
+        return $this->invokeLDAPMethod('next_entry', $link, $result);
180
+    }
181
+
182
+    /**
183
+     * {@inheritDoc}
184
+     */
185
+    public function read($link, $baseDN, $filter, $attr) {
186
+        return $this->invokeLDAPMethod('read', $link, $baseDN, $filter, $attr, 0, -1);
187
+    }
188
+
189
+    /**
190
+     * {@inheritDoc}
191
+     */
192
+    public function search($link, $baseDN, $filter, $attr, $attrsOnly = 0, $limit = 0, int $pageSize = 0, string $cookie = '') {
193
+        if ($pageSize > 0 || $cookie !== '') {
194
+            $serverControls = [[
195
+                'oid' => LDAP_CONTROL_PAGEDRESULTS,
196
+                'value' => [
197
+                    'size' => $pageSize,
198
+                    'cookie' => $cookie,
199
+                ],
200
+                'iscritical' => false,
201
+            ]];
202
+        } else {
203
+            $serverControls = [];
204
+        }
205
+
206
+        $oldHandler = set_error_handler(function ($no, $message, $file, $line) use (&$oldHandler) {
207
+            if (strpos($message, 'Partial search results returned: Sizelimit exceeded') !== false) {
208
+                return true;
209
+            }
210
+            $oldHandler($no, $message, $file, $line);
211
+            return true;
212
+        });
213
+        try {
214
+            $result = $this->invokeLDAPMethod('search', $link, $baseDN, $filter, $attr, $attrsOnly, $limit, -1, LDAP_DEREF_NEVER, $serverControls);
215
+
216
+            restore_error_handler();
217
+            return $result;
218
+        } catch (\Exception $e) {
219
+            restore_error_handler();
220
+            throw $e;
221
+        }
222
+    }
223
+
224
+    /**
225
+     * {@inheritDoc}
226
+     */
227
+    public function modReplace($link, $userDN, $password) {
228
+        return $this->invokeLDAPMethod('mod_replace', $link, $userDN, ['userPassword' => $password]);
229
+    }
230
+
231
+    /**
232
+     * {@inheritDoc}
233
+     */
234
+    public function exopPasswd($link, string $userDN, string $oldPassword, string $password) {
235
+        return $this->invokeLDAPMethod('exop_passwd', $link, $userDN, $oldPassword, $password);
236
+    }
237
+
238
+    /**
239
+     * {@inheritDoc}
240
+     */
241
+    public function setOption($link, $option, $value) {
242
+        return $this->invokeLDAPMethod('set_option', $link, $option, $value);
243
+    }
244
+
245
+    /**
246
+     * {@inheritDoc}
247
+     */
248
+    public function startTls($link) {
249
+        return $this->invokeLDAPMethod('start_tls', $link);
250
+    }
251
+
252
+    /**
253
+     * {@inheritDoc}
254
+     */
255
+    public function unbind($link) {
256
+        return $this->invokeLDAPMethod('unbind', $link);
257
+    }
258
+
259
+    /**
260
+     * Checks whether the server supports LDAP
261
+     * @return boolean if it the case, false otherwise
262
+     * */
263
+    public function areLDAPFunctionsAvailable() {
264
+        return function_exists('ldap_connect');
265
+    }
266
+
267
+    /**
268
+     * {@inheritDoc}
269
+     */
270
+    public function isResource($resource) {
271
+        return is_resource($resource) || is_object($resource);
272
+    }
273
+
274
+    /**
275
+     * Checks whether the return value from LDAP is wrong or not.
276
+     *
277
+     * When using ldap_search we provide an array, in case multiple bases are
278
+     * configured. Thus, we need to check the array elements.
279
+     *
280
+     * @param mixed $result
281
+     */
282
+    protected function isResultFalse(string $functionName, $result): bool {
283
+        if ($result === false) {
284
+            return true;
285
+        }
286
+
287
+        if ($functionName === 'ldap_search' && is_array($result)) {
288
+            foreach ($result as $singleResult) {
289
+                if ($singleResult === false) {
290
+                    return true;
291
+                }
292
+            }
293
+        }
294
+
295
+        return false;
296
+    }
297
+
298
+    /**
299
+     * @param array $arguments
300
+     * @return mixed
301
+     */
302
+    protected function invokeLDAPMethod(string $func, ...$arguments) {
303
+        $func = 'ldap_' . $func;
304
+        if (function_exists($func)) {
305
+            $this->preFunctionCall($func, $arguments);
306
+            $result = call_user_func_array($func, $arguments);
307
+            if ($this->isResultFalse($func, $result)) {
308
+                $this->postFunctionCall($func);
309
+            }
310
+            if ($this->dataCollector !== null) {
311
+                $this->dataCollector->stopLastLdapRequest();
312
+            }
313
+            return $result;
314
+        }
315
+        return null;
316
+    }
317
+
318
+    private function preFunctionCall(string $functionName, array $args): void {
319
+        $this->curArgs = $args;
320
+        $this->logger->debug('Calling LDAP function {func} with parameters {args}', [
321
+            'app' => 'user_ldap',
322
+            'func' => $functionName,
323
+            'args' => json_encode($args),
324
+        ]);
325
+
326
+        if ($this->dataCollector !== null) {
327
+            $args = array_map(function ($item) {
328
+                if ($this->isResource($item)) {
329
+                    return '(resource)';
330
+                }
331
+                if (isset($item[0]['value']['cookie']) && $item[0]['value']['cookie'] !== "") {
332
+                    $item[0]['value']['cookie'] = "*opaque cookie*";
333
+                }
334
+                return $item;
335
+            }, $this->curArgs);
336
+
337
+            $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
338
+            $this->dataCollector->startLdapRequest($functionName, $args, $backtrace);
339
+        }
340
+
341
+        if ($this->logFile !== '' && is_writable(dirname($this->logFile)) && (!file_exists($this->logFile) || is_writable($this->logFile))) {
342
+            $args = array_map(fn ($item) => (!$this->isResource($item) ? $item : '(resource)'), $this->curArgs);
343
+            file_put_contents(
344
+                $this->logFile,
345
+                $functionName . '::' . json_encode($args) . "\n",
346
+                FILE_APPEND
347
+            );
348
+        }
349
+    }
350
+
351
+    /**
352
+     * Analyzes the returned LDAP error and acts accordingly if not 0
353
+     *
354
+     * @param resource|\LDAP\Connection $resource the LDAP Connection resource
355
+     * @throws ConstraintViolationException
356
+     * @throws ServerNotAvailableException
357
+     * @throws \Exception
358
+     */
359
+    private function processLDAPError($resource, string $functionName, int $errorCode, string $errorMsg): void {
360
+        $this->logger->debug('LDAP error {message} ({code}) after calling {func}', [
361
+            'app' => 'user_ldap',
362
+            'message' => $errorMsg,
363
+            'code' => $errorCode,
364
+            'func' => $functionName,
365
+        ]);
366
+        if ($functionName === 'ldap_get_entries'
367
+            && $errorCode === -4) {
368
+        } elseif ($errorCode === 32) {
369
+            //for now
370
+        } elseif ($errorCode === 10) {
371
+            //referrals, we switch them off, but then there is AD :)
372
+        } elseif ($errorCode === -1) {
373
+            throw new ServerNotAvailableException('Lost connection to LDAP server.');
374
+        } elseif ($errorCode === 52) {
375
+            throw new ServerNotAvailableException('LDAP server is shutting down.');
376
+        } elseif ($errorCode === 48) {
377
+            throw new \Exception('LDAP authentication method rejected', $errorCode);
378
+        } elseif ($errorCode === 1) {
379
+            throw new \Exception('LDAP Operations error', $errorCode);
380
+        } elseif ($errorCode === 19) {
381
+            ldap_get_option($resource, LDAP_OPT_ERROR_STRING, $extended_error);
382
+            throw new ConstraintViolationException(!empty($extended_error) ? $extended_error : $errorMsg, $errorCode);
383
+        }
384
+    }
385
+
386
+    /**
387
+     * Called after an ldap method is run to act on LDAP error if necessary
388
+     * @throw \Exception
389
+     */
390
+    private function postFunctionCall(string $functionName): void {
391
+        if ($this->isResource($this->curArgs[0])) {
392
+            $resource = $this->curArgs[0];
393
+        } elseif (
394
+            $functionName === 'ldap_search'
395
+            && is_array($this->curArgs[0])
396
+            && $this->isResource($this->curArgs[0][0])
397
+        ) {
398
+            // we use always the same LDAP connection resource, is enough to
399
+            // take the first one.
400
+            $resource = $this->curArgs[0][0];
401
+        } else {
402
+            return;
403
+        }
404
+
405
+        $errorCode = ldap_errno($resource);
406
+        if ($errorCode === 0) {
407
+            return;
408
+        }
409
+        $errorMsg = ldap_error($resource);
410
+
411
+        $this->processLDAPError($resource, $functionName, $errorCode, $errorMsg);
412
+
413
+        $this->curArgs = [];
414
+    }
415 415
 }
Please login to merge, or discard this patch.
Spacing   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -72,12 +72,12 @@  discard block
 block discarded – undo
72 72
 	public function connect($host, $port) {
73 73
 		$pos = strpos($host, '://');
74 74
 		if ($pos === false) {
75
-			$host = 'ldap://' . $host;
75
+			$host = 'ldap://'.$host;
76 76
 			$pos = 4;
77 77
 		}
78 78
 		if (strpos($host, ':', $pos + 1) === false && !empty($port)) {
79 79
 			//ldap_connect ignores port parameter when URLs are passed
80
-			$host .= ':' . $port;
80
+			$host .= ':'.$port;
81 81
 		}
82 82
 		return $this->invokeLDAPMethod('connect', $host);
83 83
 	}
@@ -203,7 +203,7 @@  discard block
 block discarded – undo
203 203
 			$serverControls = [];
204 204
 		}
205 205
 
206
-		$oldHandler = set_error_handler(function ($no, $message, $file, $line) use (&$oldHandler) {
206
+		$oldHandler = set_error_handler(function($no, $message, $file, $line) use (&$oldHandler) {
207 207
 			if (strpos($message, 'Partial search results returned: Sizelimit exceeded') !== false) {
208 208
 				return true;
209 209
 			}
@@ -300,7 +300,7 @@  discard block
 block discarded – undo
300 300
 	 * @return mixed
301 301
 	 */
302 302
 	protected function invokeLDAPMethod(string $func, ...$arguments) {
303
-		$func = 'ldap_' . $func;
303
+		$func = 'ldap_'.$func;
304 304
 		if (function_exists($func)) {
305 305
 			$this->preFunctionCall($func, $arguments);
306 306
 			$result = call_user_func_array($func, $arguments);
@@ -324,7 +324,7 @@  discard block
 block discarded – undo
324 324
 		]);
325 325
 
326 326
 		if ($this->dataCollector !== null) {
327
-			$args = array_map(function ($item) {
327
+			$args = array_map(function($item) {
328 328
 				if ($this->isResource($item)) {
329 329
 					return '(resource)';
330 330
 				}
@@ -342,7 +342,7 @@  discard block
 block discarded – undo
342 342
 			$args = array_map(fn ($item) => (!$this->isResource($item) ? $item : '(resource)'), $this->curArgs);
343 343
 			file_put_contents(
344 344
 				$this->logFile,
345
-				$functionName . '::' . json_encode($args) . "\n",
345
+				$functionName.'::'.json_encode($args)."\n",
346 346
 				FILE_APPEND
347 347
 			);
348 348
 		}
Please login to merge, or discard this patch.