Issues (2553)

apps/user_ldap/lib/Connection.php (4 issues)

1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Arthur Schiwon <[email protected]>
6
 * @author Bart Visscher <[email protected]>
7
 * @author Christoph Wurst <[email protected]>
8
 * @author Jarkko Lehtoranta <[email protected]>
9
 * @author Joas Schilling <[email protected]>
10
 * @author Jörn Friedrich Dreyer <[email protected]>
11
 * @author Julius Härtl <[email protected]>
12
 * @author Lukas Reschke <[email protected]>
13
 * @author Morris Jobke <[email protected]>
14
 * @author Robin Appelman <[email protected]>
15
 * @author Robin McCorkell <[email protected]>
16
 * @author Roeland Jago Douma <[email protected]>
17
 * @author Roger Szabo <[email protected]>
18
 * @author root <[email protected]>
19
 * @author Victor Dubiniuk <[email protected]>
20
 * @author Xuanwo <[email protected]>
21
 * @author Vincent Van Houtte <[email protected]>
22
 *
23
 * @license AGPL-3.0
24
 *
25
 * This code is free software: you can redistribute it and/or modify
26
 * it under the terms of the GNU Affero General Public License, version 3,
27
 * as published by the Free Software Foundation.
28
 *
29
 * This program is distributed in the hope that it will be useful,
30
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
31
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32
 * GNU Affero General Public License for more details.
33
 *
34
 * You should have received a copy of the GNU Affero General Public License, version 3,
35
 * along with this program. If not, see <http://www.gnu.org/licenses/>
36
 *
37
 */
38
namespace OCA\User_LDAP;
39
40
use OC\ServerNotAvailableException;
41
use Psr\Log\LoggerInterface;
42
43
/**
44
 * magic properties (incomplete)
45
 * responsible for LDAP connections in context with the provided configuration
46
 *
47
 * @property string ldapHost
48
 * @property string ldapPort holds the port number
49
 * @property string ldapUserFilter
50
 * @property string ldapUserDisplayName
51
 * @property string ldapUserDisplayName2
52
 * @property string ldapUserAvatarRule
53
 * @property boolean turnOnPasswordChange
54
 * @property string[] ldapBaseUsers
55
 * @property int|null ldapPagingSize holds an integer
56
 * @property bool|mixed|void ldapGroupMemberAssocAttr
57
 * @property string ldapUuidUserAttribute
58
 * @property string ldapUuidGroupAttribute
59
 * @property string ldapExpertUUIDUserAttr
60
 * @property string ldapExpertUUIDGroupAttr
61
 * @property string ldapQuotaAttribute
62
 * @property string ldapQuotaDefault
63
 * @property string ldapEmailAttribute
64
 * @property string ldapExtStorageHomeAttribute
65
 * @property string homeFolderNamingRule
66
 * @property bool|string ldapNestedGroups
67
 * @property string[] ldapBaseGroups
68
 * @property string ldapGroupFilter
69
 * @property string ldapGroupDisplayName
70
 * @property string ldapLoginFilter
71
 * @property string ldapDynamicGroupMemberURL
72
 * @property string ldapGidNumber
73
 * @property int hasMemberOfFilterSupport
74
 * @property int useMemberOfToDetectMembership
75
 * @property string ldapMatchingRuleInChainState
76
 * @property string ldapAttributePhone
77
 * @property string ldapAttributeWebsite
78
 * @property string ldapAttributeAddress
79
 * @property string ldapAttributeTwitter
80
 * @property string ldapAttributeFediverse
81
 * @property string ldapAttributeOrganisation
82
 * @property string ldapAttributeRole
83
 * @property string ldapAttributeHeadline
84
 * @property string ldapAttributeBiography
85
 */
86
class Connection extends LDAPUtility {
87
	/**
88
	 * @var resource|\LDAP\Connection|null
89
	 */
90
	private $ldapConnectionRes = null;
91
92
	/**
93
	 * @var string
94
	 */
95
	private $configPrefix;
96
97
	/**
98
	 * @var ?string
99
	 */
100
	private $configID;
101
102
	/**
103
	 * @var bool
104
	 */
105
	private $configured = false;
106
107
	/**
108
	 * @var bool whether connection should be kept on __destruct
109
	 */
110
	private $dontDestruct = false;
111
112
	/**
113
	 * @var bool runtime flag that indicates whether supported primary groups are available
114
	 */
115
	public $hasPrimaryGroups = true;
116
117
	/**
118
	 * @var bool runtime flag that indicates whether supported POSIX gidNumber are available
119
	 */
120
	public $hasGidNumber = true;
121
122
	/**
123
	 * @var \OCP\ICache|null
124
	 */
125
	protected $cache = null;
126
127
	/** @var Configuration settings handler **/
128
	protected $configuration;
129
130
	/**
131
	 * @var bool
132
	 */
133
	protected $doNotValidate = false;
134
135
	/**
136
	 * @var bool
137
	 */
138
	protected $ignoreValidation = false;
139
140
	/**
141
	 * @var array{sum?: string, result?: bool}
142
	 */
143
	protected $bindResult = [];
144
145
	/** @var LoggerInterface */
146
	protected $logger;
147
148
	/**
149
	 * Constructor
150
	 * @param string $configPrefix a string with the prefix for the configkey column (appconfig table)
151
	 * @param string|null $configID a string with the value for the appid column (appconfig table) or null for on-the-fly connections
152
	 */
153
	public function __construct(ILDAPWrapper $ldap, string $configPrefix = '', ?string $configID = 'user_ldap') {
154
		parent::__construct($ldap);
155
		$this->configPrefix = $configPrefix;
156
		$this->configID = $configID;
157
		$this->configuration = new Configuration($configPrefix, !is_null($configID));
158
		$memcache = \OC::$server->getMemCacheFactory();
159
		if ($memcache->isAvailable()) {
160
			$this->cache = $memcache->createDistributed();
161
		}
162
		$helper = new Helper(\OC::$server->getConfig(), \OC::$server->getDatabaseConnection());
163
		$this->doNotValidate = !in_array($this->configPrefix,
164
			$helper->getServerConfigurationPrefixes());
165
		$this->logger = \OC::$server->get(LoggerInterface::class);
166
	}
167
168
	public function __destruct() {
169
		if (!$this->dontDestruct && $this->ldap->isResource($this->ldapConnectionRes)) {
170
			@$this->ldap->unbind($this->ldapConnectionRes);
171
			$this->bindResult = [];
172
		}
173
	}
174
175
	/**
176
	 * defines behaviour when the instance is cloned
177
	 */
178
	public function __clone() {
179
		$this->configuration = new Configuration($this->configPrefix,
180
			!is_null($this->configID));
181
		if (count($this->bindResult) !== 0 && $this->bindResult['result'] === true) {
182
			$this->bindResult = [];
183
		}
184
		$this->ldapConnectionRes = null;
185
		$this->dontDestruct = true;
186
	}
187
188
	public function __get(string $name) {
189
		if (!$this->configured) {
190
			$this->readConfiguration();
191
		}
192
193
		return $this->configuration->$name;
194
	}
195
196
	/**
197
	 * @param string $name
198
	 * @param mixed $value
199
	 */
200
	public function __set($name, $value) {
201
		$this->doNotValidate = false;
202
		$before = $this->configuration->$name;
203
		$this->configuration->$name = $value;
204
		$after = $this->configuration->$name;
205
		if ($before !== $after) {
206
			if ($this->configID !== '' && $this->configID !== null) {
207
				$this->configuration->saveConfiguration();
208
			}
209
			$this->validateConfiguration();
210
		}
211
	}
212
213
	/**
214
	 * @param string $rule
215
	 * @return array
216
	 * @throws \RuntimeException
217
	 */
218
	public function resolveRule($rule) {
219
		return $this->configuration->resolveRule($rule);
220
	}
221
222
	/**
223
	 * sets whether the result of the configuration validation shall
224
	 * be ignored when establishing the connection. Used by the Wizard
225
	 * in early configuration state.
226
	 * @param bool $state
227
	 */
228
	public function setIgnoreValidation($state) {
229
		$this->ignoreValidation = (bool)$state;
230
	}
231
232
	/**
233
	 * initializes the LDAP backend
234
	 * @param bool $force read the config settings no matter what
235
	 */
236
	public function init($force = false) {
237
		$this->readConfiguration($force);
238
		$this->establishConnection();
239
	}
240
241
	/**
242
	 * @return resource|\LDAP\Connection The LDAP resource
243
	 */
244
	public function getConnectionResource() {
245
		if (!$this->ldapConnectionRes) {
246
			$this->init();
247
		} elseif (!$this->ldap->isResource($this->ldapConnectionRes)) {
248
			$this->ldapConnectionRes = null;
249
			$this->establishConnection();
250
		}
251
		if (is_null($this->ldapConnectionRes)) {
252
			$this->logger->error(
253
				'No LDAP Connection to server ' . $this->configuration->ldapHost,
254
				['app' => 'user_ldap']
255
			);
256
			throw new ServerNotAvailableException('Connection to LDAP server could not be established');
257
		}
258
		return $this->ldapConnectionRes;
259
	}
260
261
	/**
262
	 * resets the connection resource
263
	 */
264
	public function resetConnectionResource() {
265
		if (!is_null($this->ldapConnectionRes)) {
266
			@$this->ldap->unbind($this->ldapConnectionRes);
267
			$this->ldapConnectionRes = null;
268
			$this->bindResult = [];
269
		}
270
	}
271
272
	/**
273
	 * @param string|null $key
274
	 * @return string
275
	 */
276
	private function getCacheKey($key) {
277
		$prefix = 'LDAP-'.$this->configID.'-'.$this->configPrefix.'-';
278
		if (is_null($key)) {
279
			return $prefix;
280
		}
281
		return $prefix.hash('sha256', $key);
282
	}
283
284
	/**
285
	 * @param string $key
286
	 * @return mixed|null
287
	 */
288
	public function getFromCache($key) {
289
		if (!$this->configured) {
290
			$this->readConfiguration();
291
		}
292
		if (is_null($this->cache) || !$this->configuration->ldapCacheTTL) {
293
			return null;
294
		}
295
		$key = $this->getCacheKey($key);
296
297
		return json_decode(base64_decode($this->cache->get($key) ?? ''), true);
298
	}
299
300
	/**
301
	 * @param string $key
302
	 * @param mixed $value
303
	 */
304
	public function writeToCache($key, $value, int $ttlOverride = null): void {
305
		if (!$this->configured) {
306
			$this->readConfiguration();
307
		}
308
		if (is_null($this->cache)
309
			|| !$this->configuration->ldapCacheTTL
310
			|| !$this->configuration->ldapConfigurationActive) {
311
			return;
312
		}
313
		$key = $this->getCacheKey($key);
314
		$value = base64_encode(json_encode($value));
315
		$ttl = $ttlOverride ?? $this->configuration->ldapCacheTTL;
316
		$this->cache->set($key, $value, $ttl);
317
	}
318
319
	public function clearCache() {
320
		if (!is_null($this->cache)) {
321
			$this->cache->clear($this->getCacheKey(null));
322
		}
323
	}
324
325
	/**
326
	 * Caches the general LDAP configuration.
327
	 * @param bool $force optional. true, if the re-read should be forced. defaults
328
	 * to false.
329
	 * @return null
330
	 */
331
	private function readConfiguration($force = false) {
332
		if ((!$this->configured || $force) && !is_null($this->configID)) {
333
			$this->configuration->readConfiguration();
334
			$this->configured = $this->validateConfiguration();
335
		}
336
	}
337
338
	/**
339
	 * set LDAP configuration with values delivered by an array, not read from configuration
340
	 * @param array $config array that holds the config parameters in an associated array
341
	 * @param array &$setParameters optional; array where the set fields will be given to
342
	 * @return bool true if config validates, false otherwise. Check with $setParameters for detailed success on single parameters
343
	 */
344
	public function setConfiguration($config, &$setParameters = null): bool {
345
		if (is_null($setParameters)) {
346
			$setParameters = [];
347
		}
348
		$this->doNotValidate = false;
349
		$this->configuration->setConfiguration($config, $setParameters);
350
		if (count($setParameters) > 0) {
351
			$this->configured = $this->validateConfiguration();
352
		}
353
354
355
		return $this->configured;
356
	}
357
358
	/**
359
	 * saves the current Configuration in the database and empties the
360
	 * cache
361
	 * @return null
362
	 */
363
	public function saveConfiguration() {
364
		$this->configuration->saveConfiguration();
365
		$this->clearCache();
366
	}
367
368
	/**
369
	 * get the current LDAP configuration
370
	 * @return array
371
	 */
372
	public function getConfiguration() {
373
		$this->readConfiguration();
374
		$config = $this->configuration->getConfiguration();
375
		$cta = $this->configuration->getConfigTranslationArray();
376
		$result = [];
377
		foreach ($cta as $dbkey => $configkey) {
378
			switch ($configkey) {
379
				case 'homeFolderNamingRule':
380
					if (str_starts_with($config[$configkey], 'attr:')) {
381
						$result[$dbkey] = substr($config[$configkey], 5);
382
					} else {
383
						$result[$dbkey] = '';
384
					}
385
					break;
386
				case 'ldapBase':
387
				case 'ldapBaseUsers':
388
				case 'ldapBaseGroups':
389
				case 'ldapAttributesForUserSearch':
390
				case 'ldapAttributesForGroupSearch':
391
					if (is_array($config[$configkey])) {
392
						$result[$dbkey] = implode("\n", $config[$configkey]);
393
						break;
394
					} //else follows default
395
					// no break
396
				default:
397
					$result[$dbkey] = $config[$configkey];
398
			}
399
		}
400
		return $result;
401
	}
402
403
	private function doSoftValidation() {
404
		//if User or Group Base are not set, take over Base DN setting
405
		foreach (['ldapBaseUsers', 'ldapBaseGroups'] as $keyBase) {
406
			$val = $this->configuration->$keyBase;
407
			if (empty($val)) {
408
				$this->configuration->$keyBase = $this->configuration->ldapBase;
409
			}
410
		}
411
412
		foreach (['ldapExpertUUIDUserAttr' => 'ldapUuidUserAttribute',
413
			'ldapExpertUUIDGroupAttr' => 'ldapUuidGroupAttribute']
414
				as $expertSetting => $effectiveSetting) {
415
			$uuidOverride = $this->configuration->$expertSetting;
416
			if (!empty($uuidOverride)) {
417
				$this->configuration->$effectiveSetting = $uuidOverride;
418
			} else {
419
				$uuidAttributes = Access::UUID_ATTRIBUTES;
420
				array_unshift($uuidAttributes, 'auto');
421
				if (!in_array($this->configuration->$effectiveSetting, $uuidAttributes)
422
					&& !is_null($this->configID)) {
423
					$this->configuration->$effectiveSetting = 'auto';
424
					$this->configuration->saveConfiguration();
425
					$this->logger->info(
426
						'Illegal value for the '.$effectiveSetting.', reset to autodetect.',
427
						['app' => 'user_ldap']
428
					);
429
				}
430
			}
431
		}
432
433
		$backupPort = (int)$this->configuration->ldapBackupPort;
434
		if ($backupPort <= 0) {
435
			$this->configuration->backupPort = $this->configuration->ldapPort;
436
		}
437
438
		//make sure empty search attributes are saved as simple, empty array
439
		$saKeys = ['ldapAttributesForUserSearch',
440
			'ldapAttributesForGroupSearch'];
441
		foreach ($saKeys as $key) {
442
			$val = $this->configuration->$key;
443
			if (is_array($val) && count($val) === 1 && empty($val[0])) {
444
				$this->configuration->$key = [];
445
			}
446
		}
447
448
		if ((stripos((string)$this->configuration->ldapHost, 'ldaps://') === 0)
449
			&& $this->configuration->ldapTLS) {
450
			$this->configuration->ldapTLS = false;
451
			$this->logger->info(
452
				'LDAPS (already using secure connection) and TLS do not work together. Switched off TLS.',
453
				['app' => 'user_ldap']
454
			);
455
		}
456
	}
457
458
	/**
459
	 * @return bool
460
	 */
461
	private function doCriticalValidation() {
462
		$configurationOK = true;
463
		$errorStr = 'Configuration Error (prefix '.
464
			(string)$this->configPrefix .'): ';
465
466
		//options that shall not be empty
467
		$options = ['ldapHost', 'ldapUserDisplayName',
468
			'ldapGroupDisplayName', 'ldapLoginFilter'];
469
470
		//ldapPort should not be empty either unless ldapHost is pointing to a socket
471
		if (!$this->configuration->usesLdapi()) {
472
			$options[] = 'ldapPort';
473
		}
474
475
		foreach ($options as $key) {
476
			$val = $this->configuration->$key;
477
			if (empty($val)) {
478
				switch ($key) {
479
					case 'ldapHost':
480
						$subj = 'LDAP Host';
481
						break;
482
					case 'ldapPort':
483
						$subj = 'LDAP Port';
484
						break;
485
					case 'ldapUserDisplayName':
486
						$subj = 'LDAP User Display Name';
487
						break;
488
					case 'ldapGroupDisplayName':
489
						$subj = 'LDAP Group Display Name';
490
						break;
491
					case 'ldapLoginFilter':
492
						$subj = 'LDAP Login Filter';
493
						break;
494
					default:
495
						$subj = $key;
496
						break;
497
				}
498
				$configurationOK = false;
499
				$this->logger->warning(
500
					$errorStr.'No '.$subj.' given!',
501
					['app' => 'user_ldap']
502
				);
503
			}
504
		}
505
506
		//combinations
507
		$agent = $this->configuration->ldapAgentName;
508
		$pwd = $this->configuration->ldapAgentPassword;
509
		if (
510
			($agent === '' && $pwd !== '')
511
			|| ($agent !== '' && $pwd === '')
512
		) {
513
			$this->logger->warning(
514
				$errorStr.'either no password is given for the user ' .
515
					'agent or a password is given, but not an LDAP agent.',
516
				['app' => 'user_ldap']
517
			);
518
			$configurationOK = false;
519
		}
520
521
		$base = $this->configuration->ldapBase;
522
		$baseUsers = $this->configuration->ldapBaseUsers;
523
		$baseGroups = $this->configuration->ldapBaseGroups;
524
525
		if (empty($base) && empty($baseUsers) && empty($baseGroups)) {
526
			$this->logger->warning(
527
				$errorStr.'Not a single Base DN given.',
528
				['app' => 'user_ldap']
529
			);
530
			$configurationOK = false;
531
		}
532
533
		if (mb_strpos((string)$this->configuration->ldapLoginFilter, '%uid', 0, 'UTF-8')
534
		   === false) {
535
			$this->logger->warning(
536
				$errorStr.'login filter does not contain %uid place holder.',
537
				['app' => 'user_ldap']
538
			);
539
			$configurationOK = false;
540
		}
541
542
		return $configurationOK;
543
	}
544
545
	/**
546
	 * Validates the user specified configuration
547
	 * @return bool true if configuration seems OK, false otherwise
548
	 */
549
	private function validateConfiguration() {
550
		if ($this->doNotValidate) {
551
			//don't do a validation if it is a new configuration with pure
552
			//default values. Will be allowed on changes via __set or
553
			//setConfiguration
554
			return false;
555
		}
556
557
		// first step: "soft" checks: settings that are not really
558
		// necessary, but advisable. If left empty, give an info message
559
		$this->doSoftValidation();
560
561
		//second step: critical checks. If left empty or filled wrong, mark as
562
		//not configured and give a warning.
563
		return $this->doCriticalValidation();
564
	}
565
566
567
	/**
568
	 * Connects and Binds to LDAP
569
	 *
570
	 * @throws ServerNotAvailableException
571
	 */
572
	private function establishConnection() {
573
		if (!$this->configuration->ldapConfigurationActive) {
574
			return null;
575
		}
576
		static $phpLDAPinstalled = true;
577
		if (!$phpLDAPinstalled) {
578
			return false;
579
		}
580
		if (!$this->ignoreValidation && !$this->configured) {
581
			$this->logger->warning(
582
				'Configuration is invalid, cannot connect',
583
				['app' => 'user_ldap']
584
			);
585
			return false;
586
		}
587
		if (!$this->ldapConnectionRes) {
588
			if (!$this->ldap->areLDAPFunctionsAvailable()) {
589
				$phpLDAPinstalled = false;
590
				$this->logger->error(
591
					'function ldap_connect is not available. Make sure that the PHP ldap module is installed.',
592
					['app' => 'user_ldap']
593
				);
594
595
				return false;
596
			}
597
			if ($this->configuration->turnOffCertCheck) {
598
				if (putenv('LDAPTLS_REQCERT=never')) {
599
					$this->logger->debug(
600
						'Turned off SSL certificate validation successfully.',
601
						['app' => 'user_ldap']
602
					);
603
				} else {
604
					$this->logger->warning(
605
						'Could not turn off SSL certificate validation.',
606
						['app' => 'user_ldap']
607
					);
608
				}
609
			}
610
611
			$hasBackupHost = (trim($this->configuration->ldapBackupHost ?? '') !== '');
0 ignored issues
show
Bug Best Practice introduced by
The property ldapBackupHost does not exist on OCA\User_LDAP\Configuration. Since you implemented __get, consider adding a @property annotation.
Loading history...
612
			$hasBackgroundHost = (trim($this->configuration->ldapBackgroundHost ?? '') !== '');
613
			$useBackgroundHost = (\OC::$CLI && $hasBackgroundHost);
614
			$overrideCacheKey = ($useBackgroundHost ? 'overrideBackgroundServer' : 'overrideMainServer');
615
			$forceBackupHost = ($this->configuration->ldapOverrideMainServer || $this->getFromCache($overrideCacheKey));
0 ignored issues
show
Bug Best Practice introduced by
The property ldapOverrideMainServer does not exist on OCA\User_LDAP\Configuration. Since you implemented __get, consider adding a @property annotation.
Loading history...
616
			$bindStatus = false;
617
			if (!$forceBackupHost) {
618
				try {
619
					$host = $this->configuration->ldapHost ?? '';
620
					$port = $this->configuration->ldapPort ?? '';
621
					if ($useBackgroundHost) {
622
						$host = $this->configuration->ldapBackgroundHost ?? '';
623
						$port = $this->configuration->ldapBackgroundPort ?? '';
624
					}
625
					$this->doConnect($host, $port);
626
					return $this->bind();
627
				} catch (ServerNotAvailableException $e) {
628
					if (!$hasBackupHost) {
629
						throw $e;
630
					}
631
				}
632
				$this->logger->warning(
633
					'Main LDAP not reachable, connecting to backup',
634
					[
635
						'app' => 'user_ldap'
636
					]
637
				);
638
			}
639
640
			// if LDAP server is not reachable, try the Backup (Replica!) Server
641
			$this->doConnect($this->configuration->ldapBackupHost ?? '', $this->configuration->ldapBackupPort ?? '');
642
			$this->bindResult = [];
643
			$bindStatus = $this->bind();
644
			$error = $this->ldap->isResource($this->ldapConnectionRes) ?
645
				$this->ldap->errno($this->ldapConnectionRes) : -1;
646
			if ($bindStatus && $error === 0 && !$forceBackupHost) {
0 ignored issues
show
The condition $bindStatus is always false.
Loading history...
647
				//when bind to backup server succeeded and failed to main server,
648
				//skip contacting it for 15min
649
				$this->writeToCache($overrideCacheKey, true, 60 * 15);
650
			}
651
652
			return $bindStatus;
653
		}
654
		return null;
655
	}
656
657
	/**
658
	 * @param string $host
659
	 * @param string $port
660
	 * @return bool
661
	 * @throws \OC\ServerNotAvailableException
662
	 */
663
	private function doConnect($host, $port) {
664
		if ($host === '') {
665
			return false;
666
		}
667
668
		$this->ldapConnectionRes = $this->ldap->connect($host, $port);
669
670
		if (!$this->ldap->setOption($this->ldapConnectionRes, LDAP_OPT_PROTOCOL_VERSION, 3)) {
671
			throw new ServerNotAvailableException('Could not set required LDAP Protocol version.');
672
		}
673
674
		if (!$this->ldap->setOption($this->ldapConnectionRes, LDAP_OPT_REFERRALS, 0)) {
675
			throw new ServerNotAvailableException('Could not disable LDAP referrals.');
676
		}
677
678
		if (!$this->ldap->setOption($this->ldapConnectionRes, LDAP_OPT_NETWORK_TIMEOUT, $this->configuration->ldapConnectionTimeout)) {
679
			throw new ServerNotAvailableException('Could not set network timeout');
680
		}
681
682
		if ($this->configuration->ldapTLS) {
0 ignored issues
show
Bug Best Practice introduced by
The property ldapTLS does not exist on OCA\User_LDAP\Configuration. Since you implemented __get, consider adding a @property annotation.
Loading history...
683
			if (!$this->ldap->startTls($this->ldapConnectionRes)) {
684
				throw new ServerNotAvailableException('Start TLS failed, when connecting to LDAP host ' . $host . '.');
685
			}
686
		}
687
688
		return true;
689
	}
690
691
	/**
692
	 * Binds to LDAP
693
	 */
694
	public function bind() {
695
		if (!$this->configuration->ldapConfigurationActive) {
696
			return false;
697
		}
698
		$cr = $this->ldapConnectionRes;
699
		if (!$this->ldap->isResource($cr)) {
700
			$cr = $this->getConnectionResource();
701
		}
702
703
		if (
704
			count($this->bindResult) !== 0
705
			&& $this->bindResult['sum'] === md5($this->configuration->ldapAgentName . $this->configPrefix . $this->configuration->ldapAgentPassword)
706
		) {
707
			// don't attempt to bind again with the same data as before
708
			// bind might have been invoked via getConnectionResource(),
709
			// but we need results specifically for e.g. user login
710
			return $this->bindResult['result'];
711
		}
712
713
		$ldapLogin = @$this->ldap->bind($cr,
714
			$this->configuration->ldapAgentName,
715
			$this->configuration->ldapAgentPassword);
716
717
		$this->bindResult = [
718
			'sum' => md5($this->configuration->ldapAgentName . $this->configPrefix . $this->configuration->ldapAgentPassword),
719
			'result' => $ldapLogin,
720
		];
721
722
		if (!$ldapLogin) {
723
			$errno = $this->ldap->errno($cr);
724
725
			$this->logger->warning(
726
				'Bind failed: ' . $errno . ': ' . $this->ldap->error($cr),
727
				['app' => 'user_ldap']
728
			);
729
730
			// Set to failure mode, if LDAP error code is not one of
731
			// - LDAP_SUCCESS (0)
732
			// - LDAP_INVALID_CREDENTIALS (49)
733
			// - LDAP_INSUFFICIENT_ACCESS (50, spotted Apple Open Directory)
734
			// - LDAP_UNWILLING_TO_PERFORM (53, spotted eDirectory)
735
			if (!in_array($errno, [0, 49, 50, 53], true)) {
736
				$this->ldapConnectionRes = null;
737
			}
738
739
			return false;
740
		}
741
		return true;
742
	}
743
}
744