1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
16: class Horde_Prefs_Storage_Ldap extends Horde_Prefs_Storage_Base
17: {
18: 19: 20: 21: 22:
23: protected $_connection;
24:
25: 26: 27: 28: 29:
30: protected $_connected = false;
31:
32: 33: 34: 35: 36:
37: protected $_dn = '';
38:
39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64:
65: public function __construct($user, array $params = array())
66: {
67: throw new Horde_Prefs_Exception('This driver needs to be refactored to use Horde_Ldap.');
68:
69: if (!isset($params['port']) || !is_integer($params['port'])) {
70: $params['port'] = 389;
71: }
72:
73: parent::__construct($user, $params);
74: }
75:
76: 77:
78: public function get($scope_ob)
79: {
80: $this->_connect();
81:
82:
83:
84: $search = @ldap_search(
85: $this->_connection,
86: $this->_params['basedn'],
87: $this->_params['uid'] . '=' . $this->params['user'],
88: array($scope_ob->scope . 'Prefs'));
89: if ($search === false) {
90: throw new Horde_Prefs_Exception(sprintf('Error while searching for the user\'s prefs: [%d]: %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)));
91: }
92:
93: $result = @ldap_get_entries($this->_connection, $search);
94: if ($result === false) {
95: throw new Horde_Prefs_Exception(sprintf('Error while retrieving LDAP search results for the user\'s prefs: [%d]: %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)));
96: }
97:
98:
99:
100:
101:
102:
103: $field = Horde_String::lower($scope_ob->scope . 'prefs');
104: $prefs = isset($result[0][$field])
105: ? $result[0][$field]
106: : array();
107:
108: foreach ($prefs as $prefstr) {
109:
110: if (strpos($prefstr, ':') !== false) {
111:
112: list($name, $val) = explode(':', $prefstr, 2);
113: $scope_ob->set($name, base64_decode($val));
114: }
115: }
116:
117: return $scope_ob;
118: }
119:
120: 121:
122: public function store($scope_ob)
123: {
124: $this->_connect();
125:
126:
127:
128:
129:
130:
131: $new_vals = array();
132:
133:
134: foreach ($scope_ob->getDirty() as $name) {
135: $new_vals[$scope_ob->scope . 'Prefs'][] = $name . ':' . base64_encode($scope_ob->get($name));
136: }
137:
138:
139:
140:
141: $search = @ldap_search(
142: $this->_connection,
143: $this->_params['basedn'],
144: $this->_params['uid'] . '=' . $this->prefs['user'],
145: array('objectclass')
146: );
147: if ($search === false) {
148: throw new Horde_Prefs_Exception(sprintf('Error searching the directory for required objectClasses: [%d] %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)));
149: }
150:
151: $result = @ldap_get_entries($this->_connection, $search);
152: if ($result === false) {
153: throw new Horde_Prefs_Exception(sprintf('Error retrieving results while checking for required objectClasses: [%d] %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)));
154: }
155:
156: if ($result['count'] > 0) {
157: $hordeperson = $top = false;
158:
159: for ($i = 0; $i < $result[0]['objectclass']['count']; ++$i) {
160: if ($result[0]['objectclass'][$i] == 'top') {
161: $top = true;
162: } elseif ($result[0]['objectclass'][$i] == 'hordePerson') {
163: $hordeperson = true;
164: }
165: }
166:
167:
168: if (!$top) {
169: @ldap_mod_add($this->_connection, $this->_dn, array('objectclass' => 'top'));
170: }
171:
172: if (!$hordeperson) {
173: @ldap_mod_add($this->_connection, $this->_dn, array('objectclass' => 'hordePerson'));
174: }
175: }
176:
177:
178: $result = @ldap_mod_replace($this->_connection, $this->_dn, $new_vals);
179: if ($result === false) {
180: throw new Horde_Prefs_Exception(sprintf('Unable to modify user\'s objectClass for preferences: [%d] %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)));
181: }
182: }
183:
184: 185:
186: public function remove($scope = null, $pref = null)
187: {
188:
189: if (!is_null($scope) || !is_null($pref)) {
190: throw new Horde_Prefs_Exception('Removal not supported.');
191: }
192:
193: $this->_connect();
194:
195:
196: $attrs = $GLOBALS['registry']->listApps(array('inactive', 'active', 'hidden', 'notoolbar', 'admin'));
197: foreach ($attrs as $key => $val) {
198: $attrs[$key] = $val . 'Prefs';
199: }
200:
201: $search = @ldap_read($this->_connection, $this->_dn,
202: 'objectClass=hordePerson', $attrs, 1);
203: if ($search === false) {
204: throw new Horde_Prefs_Exception(sprintf('Error while getting preferenes from LDAP: [%d] %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)));
205: }
206:
207: $result = @ldap_get_entries($this->_connection, $search);
208: if ($result === false) {
209: throw new Horde_Prefs_Exception(sprintf('Error while retrieving results from LDAP: [%d] %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)));
210: }
211:
212: $attrs = array();
213: for ($i = 0; $i < $result[0]['count']; $i++) {
214: $attrs[$result[0][$i]] = array();
215: }
216: $result = @ldap_mod_del($this->_connection, $this->_dn, $attrs);
217: if ($result === false) {
218: throw new Horde_Prefs_Exception(sprintf('Unable to clear user\'s preferences: [%d] %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)));
219: }
220: }
221:
222: 223:
224:
225: 226: 227: 228: 229:
230: protected function _connect()
231: {
232: if ($this->_connected) {
233: return;
234: }
235:
236: if (!Horde_Util::extensionExists('ldap')) {
237: throw new Horde_Prefs_Exception('Required LDAP extension not found.');
238: }
239:
240: Horde::assertDriverConfig($this->_params, 'prefs',
241: array('hostspec', 'basedn', 'uid', 'writeas'),
242: 'preferences LDAP');
243:
244:
245: $conn = ldap_connect($this->_params['hostspec'], $this->_params['port']);
246: if (!$conn) {
247: throw new Horde_Prefs_Exception(sprintf('Failed to open an LDAP connection to %s.', $this->_params['hostspec']));
248: }
249:
250:
251: if (isset($this->_params['version'])) {
252: $result = @ldap_set_option($conn, LDAP_OPT_PROTOCOL_VERSION,
253: $this->_params['version']);
254: if ($result === false) {
255: throw new Horde_Prefs_Exception(sprintf('Set LDAP protocol version to %d failed: [%d] %s', $this->_params['version'], @ldap_errno($conn), @ldap_error($conn)));
256: }
257: }
258:
259:
260: if (!empty($this->_params['tls'])) {
261: @ldap_start_tls($conn);
262: }
263:
264: 265:
266: if (!empty($this->_params['searchdn'])) {
267: $bind = @ldap_bind($conn, $this->_params['searchdn'],
268: $this->_params['searchpw']);
269: if ($bind === false) {
270: throw new Horde_Prefs_Exception(sprintf('Bind to server %s:%d with DN %s failed: [%d] %s', $this->_params['hostspec'], $this->_params['port'], $this->_params['searchdn'], @ldap_errno($conn), @ldap_error($conn)));
271: }
272: }
273:
274:
275: if (function_exists('ldap_set_rebind_proc')) {
276: $result = @ldap_set_rebind_proc($conn, array($this, 'rebindProc'));
277: if ($result === false) {
278: throw new Horde_Prefs_Exception(sprintf('Setting referral callback failed: [%d] %s', @ldap_errno($conn), @ldap_error($conn)));
279: }
280: }
281:
282:
283: $this->_connection = $conn;
284:
285:
286: $search = @ldap_search($this->_connection, $this->_params['basedn'],
287: $this->_params['uid'] . '=' . $this->params['user'], array('dn'));
288: if ($search === false) {
289: throw new Horde_Prefs_Exception(sprintf('Error while searching the directory for the user\'s DN: [%d]: %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)));
290: }
291:
292: $result = @ldap_get_entries($this->_connection, $search);
293: if ($result === false) {
294: throw new Horde_Prefs_Exception(sprintf('Error while retrieving LDAP search results for the user\'s DN: [%d]: %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)));
295: }
296:
297: if ($result['count'] != 1) {
298: throw new Horde_Prefs_Exception('Zero or more than one DN returned from search; unable to determine user\'s correct DN.');
299: }
300: $this->_dn = $result[0]['dn'];
301:
302:
303:
304: switch($this->_params['writeas']) {
305: case 'user':
306: $result = @ldap_bind($this->_connection,
307: $this->_dn, $this->_opts['password']);
308: break;
309:
310: case 'admin':
311: $result = @ldap_bind($this->_connection,
312: $this->_params['binddn'],
313: $this->_params['bindpw']);
314: break;
315:
316: case 'search':
317:
318:
319: $result = true;
320: break;
321: }
322:
323: if ($result === false) {
324: throw new Horde_Prefs_Exception(sprintf('Error rebinding for prefs writing: [%d]: %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)));
325: }
326:
327:
328: $this->_connected = true;
329: }
330:
331: 332: 333: 334: 335: 336:
337: public function rebindProc($conn, $who)
338: {
339:
340: $who = preg_replace(array('|^.*://|', '|:\d*$|'), '', $who);
341:
342: 343:
344: if (strpos($this->_params['hostspec'], $who) === false) {
345: if ($this->_opts['logger']) {
346: $this->_opts['logger']->log(sprintf('Referral target %s for DN %s is not in the authorized server list.', $who, $bind_dn), 'ERR');
347: }
348: return 1;
349: }
350:
351:
352: switch($this->_params['writeas']) {
353: case 'user':
354: $bind_dn = $this->_dn;
355: $bind_pw = $this->_opts['password'];
356: break;
357:
358: case 'admin':
359: $bind_dn = $this->_params['binddn'];
360: $bind_pw = $this->_params['bindpw'];
361: break;
362:
363: case 'search':
364: $bind_dn = $this->_params['searchdn'];
365: $bind_dn = $this->_params['searchpw'];
366: break;
367: }
368:
369:
370: $bind = @ldap_bind($conn, $bind_dn, $bind_pw);
371: if (($bind === false) && $this->_opts['logger']) {
372: $this->_opts['logger']->log(sprintf('Rebind to server %s:%d with DN %s failed: [%d] %s', $this->_params['hostspec'], $this->_params['port'], $bind_dn, @ldap_errno($this->_connection), @ldap_error($this->_connection)), 'ERR');
373: }
374:
375: return 0;
376: }
377:
378:
379: }
380: