Completed
Push — master ( 181b29...5b9f96 )
by Lukas
07:38
created

LDAP::modReplace()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 3
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Alexander Bergolth <[email protected]>
6
 * @author Arthur Schiwon <[email protected]>
7
 * @author Joas Schilling <[email protected]>
8
 * @author Jörn Friedrich Dreyer <[email protected]>
9
 * @author Lukas Reschke <[email protected]>
10
 * @author Morris Jobke <[email protected]>
11
 * @author Robin McCorkell <[email protected]>
12
 * @author Roger Szabo <[email protected]>
13
 *
14
 * @license AGPL-3.0
15
 *
16
 * This code is free software: you can redistribute it and/or modify
17
 * it under the terms of the GNU Affero General Public License, version 3,
18
 * as published by the Free Software Foundation.
19
 *
20
 * This program is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23
 * GNU Affero General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU Affero General Public License, version 3,
26
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
27
 *
28
 */
29
30
namespace OCA\User_LDAP;
31
32
use OC\ServerNotAvailableException;
33
use OCA\User_LDAP\Exceptions\ConstraintViolationException;
34
35
class LDAP implements ILDAPWrapper {
36
	protected $curFunc = '';
37
	protected $curArgs = array();
38
39
	/**
40
	 * @param resource $link
41
	 * @param string $dn
42
	 * @param string $password
43
	 * @return bool|mixed
44
	 */
45
	public function bind($link, $dn, $password) {
46
		return $this->invokeLDAPMethod('bind', $link, $dn, $password);
47
	}
48
49
	/**
50
	 * @param string $host
51
	 * @param string $port
52
	 * @return mixed
53
	 */
54
	public function connect($host, $port) {
55
		if(strpos($host, '://') === false) {
56
			$host = 'ldap://' . $host;
57
		}
58
		if(strpos($host, ':', strpos($host, '://') + 1) === false) {
59
			//ldap_connect ignores port parameter when URLs are passed
60
			$host .= ':' . $port;
61
		}
62
		return $this->invokeLDAPMethod('connect', $host);
63
	}
64
65
	/**
66
	 * @param LDAP $link
67
	 * @param LDAP $result
68
	 * @param string $cookie
69
	 * @return bool|LDAP
70
	 */
71
	public function controlPagedResultResponse($link, $result, &$cookie) {
72
		$this->preFunctionCall('ldap_control_paged_result_response',
73
			array($link, $result, $cookie));
74
		$result = ldap_control_paged_result_response($link, $result, $cookie);
75
		$this->postFunctionCall();
76
77
		return $result;
78
	}
79
80
	/**
81
	 * @param LDAP $link
82
	 * @param int $pageSize
83
	 * @param bool $isCritical
84
	 * @param string $cookie
85
	 * @return mixed|true
86
	 */
87
	public function controlPagedResult($link, $pageSize, $isCritical, $cookie) {
88
		return $this->invokeLDAPMethod('control_paged_result', $link, $pageSize,
89
										$isCritical, $cookie);
90
	}
91
92
	/**
93
	 * @param LDAP $link
94
	 * @param LDAP $result
95
	 * @return mixed
96
	 */
97
	public function countEntries($link, $result) {
98
		return $this->invokeLDAPMethod('count_entries', $link, $result);
99
	}
100
101
	/**
102
	 * @param LDAP $link
103
	 * @return mixed|string
104
	 */
105
	public function errno($link) {
106
		return $this->invokeLDAPMethod('errno', $link);
107
	}
108
109
	/**
110
	 * @param LDAP $link
111
	 * @return int|mixed
112
	 */
113
	public function error($link) {
114
		return $this->invokeLDAPMethod('error', $link);
115
	}
116
117
	/**
118
	 * Splits DN into its component parts
119
	 * @param string $dn
120
	 * @param int @withAttrib
121
	 * @return array|false
122
	 * @link http://www.php.net/manual/en/function.ldap-explode-dn.php
123
	 */
124
	public function explodeDN($dn, $withAttrib) {
125
		return $this->invokeLDAPMethod('explode_dn', $dn, $withAttrib);
126
	}
127
128
	/**
129
	 * @param LDAP $link
130
	 * @param LDAP $result
131
	 * @return mixed
132
	 */
133
	public function firstEntry($link, $result) {
134
		return $this->invokeLDAPMethod('first_entry', $link, $result);
135
	}
136
137
	/**
138
	 * @param LDAP $link
139
	 * @param LDAP $result
140
	 * @return array|mixed
141
	 */
142
	public function getAttributes($link, $result) {
143
		return $this->invokeLDAPMethod('get_attributes', $link, $result);
144
	}
145
146
	/**
147
	 * @param LDAP $link
148
	 * @param LDAP $result
149
	 * @return mixed|string
150
	 */
151
	public function getDN($link, $result) {
152
		return $this->invokeLDAPMethod('get_dn', $link, $result);
153
	}
154
155
	/**
156
	 * @param LDAP $link
157
	 * @param LDAP $result
158
	 * @return array|mixed
159
	 */
160
	public function getEntries($link, $result) {
161
		return $this->invokeLDAPMethod('get_entries', $link, $result);
162
	}
163
164
	/**
165
	 * @param LDAP $link
166
	 * @param resource $result
167
	 * @return mixed
168
	 */
169
	public function nextEntry($link, $result) {
170
		return $this->invokeLDAPMethod('next_entry', $link, $result);
171
	}
172
173
	/**
174
	 * @param LDAP $link
175
	 * @param string $baseDN
176
	 * @param string $filter
177
	 * @param array $attr
178
	 * @return mixed
179
	 */
180
	public function read($link, $baseDN, $filter, $attr) {
181
		return $this->invokeLDAPMethod('read', $link, $baseDN, $filter, $attr);
182
	}
183
184
	/**
185
	 * @param LDAP $link
186
	 * @param string $baseDN
187
	 * @param string $filter
188
	 * @param array $attr
189
	 * @param int $attrsOnly
190
	 * @param int $limit
191
	 * @return mixed
192
	 */
193
	public function search($link, $baseDN, $filter, $attr, $attrsOnly = 0, $limit = 0) {
194
		return $this->invokeLDAPMethod('search', $link, $baseDN, $filter, $attr, $attrsOnly, $limit);
195
	}
196
197
	/**
198
	 * @param LDAP $link
199
	 * @param string $userDN
200
	 * @param string $password
201
	 * @return bool
202
	 */
203
	public function modReplace($link, $userDN, $password) {
204
		return $this->invokeLDAPMethod('mod_replace', $link, $userDN, array('userPassword' => $password));
205
	}
206
207
	/**
208
	 * @param LDAP $link
209
	 * @param string $option
210
	 * @param int $value
211
	 * @return bool|mixed
212
	 */
213
	public function setOption($link, $option, $value) {
214
		return $this->invokeLDAPMethod('set_option', $link, $option, $value);
215
	}
216
217
	/**
218
	 * @param LDAP $link
219
	 * @return mixed|true
220
	 */
221
	public function startTls($link) {
222
		return $this->invokeLDAPMethod('start_tls', $link);
223
	}
224
225
	/**
226
	 * @param resource $link
227
	 * @return bool|mixed
228
	 */
229
	public function unbind($link) {
230
		return $this->invokeLDAPMethod('unbind', $link);
231
	}
232
233
	/**
234
	 * Checks whether the server supports LDAP
235
	 * @return boolean if it the case, false otherwise
236
	 * */
237
	public function areLDAPFunctionsAvailable() {
238
		return function_exists('ldap_connect');
239
	}
240
241
	/**
242
	 * Checks whether PHP supports LDAP Paged Results
243
	 * @return boolean if it the case, false otherwise
244
	 * */
245
	public function hasPagedResultSupport() {
246
		$hasSupport = function_exists('ldap_control_paged_result')
247
			&& function_exists('ldap_control_paged_result_response');
248
		return $hasSupport;
249
	}
250
251
	/**
252
	 * Checks whether the submitted parameter is a resource
253
	 * @param Resource $resource the resource variable to check
254
	 * @return bool true if it is a resource, false otherwise
255
	 */
256
	public function isResource($resource) {
257
		return is_resource($resource);
258
	}
259
260
	/**
261
	 * Checks whether the return value from LDAP is wrong or not.
262
	 *
263
	 * When using ldap_search we provide an array, in case multiple bases are
264
	 * configured. Thus, we need to check the array elements.
265
	 *
266
	 * @param $result
267
	 * @return bool
268
	 */
269
	protected function isResultFalse($result) {
270
		if($result === false) {
271
			return true;
272
		}
273
274
		if($this->curFunc === 'ldap_search' && is_array($result)) {
275
			foreach ($result as $singleResult) {
276
				if($singleResult === false) {
277
					return true;
278
				}
279
			}
280
		}
281
282
		return false;
283
	}
284
285
	/**
286
	 * @return mixed
287
	 */
288
	protected function invokeLDAPMethod() {
289
		$arguments = func_get_args();
290
		$func = 'ldap_' . array_shift($arguments);
291
		if(function_exists($func)) {
292
			$this->preFunctionCall($func, $arguments);
293
			$result = call_user_func_array($func, $arguments);
294
			if ($this->isResultFalse($result)) {
295
				$this->postFunctionCall();
296
			}
297
			return $result;
298
		}
299
		return null;
300
	}
301
302
	/**
303
	 * @param string $functionName
304
	 * @param array $args
305
	 */
306
	private function preFunctionCall($functionName, $args) {
307
		$this->curFunc = $functionName;
308
		$this->curArgs = $args;
309
	}
310
311
	/**
312
	 * Analyzes the returned LDAP error and acts accordingly if not 0
313
	 *
314
	 * @param resource $resource the LDAP Connection resource
315
	 * @throws ConstraintViolationException
316
	 * @throws ServerNotAvailableException
317
	 * @throws \Exception
318
	 */
319
	private function processLDAPError($resource) {
320
		$errorCode = ldap_errno($resource);
321
		if($errorCode === 0) {
322
			return;
323
		}
324
		$errorMsg  = ldap_error($resource);
325
326
		if($this->curFunc === 'ldap_get_entries'
327
			&& $errorCode === -4) {
328
		} else if ($errorCode === 32) {
329
			//for now
330
		} else if ($errorCode === 10) {
331
			//referrals, we switch them off, but then there is AD :)
332
		} else if ($errorCode === -1) {
333
			throw new ServerNotAvailableException('Lost connection to LDAP server.');
334
		} else if ($errorCode === 48) {
335
			throw new \Exception('LDAP authentication method rejected', $errorCode);
336
		} else if ($errorCode === 1) {
337
			throw new \Exception('LDAP Operations error', $errorCode);
338
		} else if ($errorCode === 19) {
339
			ldap_get_option($this->curArgs[0], LDAP_OPT_ERROR_STRING, $extended_error);
340
			throw new ConstraintViolationException(!empty($extended_error)?$extended_error:$errorMsg, $errorCode);
341
		} else {
342
			\OCP\Util::writeLog('user_ldap',
343
				'LDAP error '.$errorMsg.' (' .
344
				$errorCode.') after calling '.
345
				$this->curFunc,
346
				\OCP\Util::DEBUG);
347
		}
348
	}
349
350
	/**
351
	 * Called after an ldap method is run to act on LDAP error if necessary
352
	 */
353
	private function postFunctionCall() {
354
		if($this->isResource($this->curArgs[0])) {
355
			$resource = $this->curArgs[0];
356
		} else if(
357
			   $this->curFunc === 'ldap_search'
358
			&& is_array($this->curArgs[0])
359
			&& $this->isResource($this->curArgs[0][0])
360
		) {
361
			// we use always the same LDAP connection resource, is enough to
362
			// take the first one.
363
			$resource = $this->curArgs[0][0];
364
		} else {
365
			return;
366
		}
367
368
		$this->processLDAPError($resource);
369
370
		$this->curFunc = '';
371
		$this->curArgs = [];
372
	}
373
}
374