2
0

Node.php 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Ldap
  17. * @subpackage Node
  18. * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://framework.zend.com/license/new-bsd New BSD License
  20. * @version $Id$
  21. */
  22. /**
  23. * @see Zend_Ldap
  24. */
  25. require_once 'Zend/Ldap.php';
  26. /**
  27. * @see Zend_Ldap_Node_Abstract
  28. */
  29. require_once 'Zend/Ldap/Node/Abstract.php';
  30. /**
  31. * Zend_Ldap_Node provides an object oriented view into a LDAP node.
  32. *
  33. * @category Zend
  34. * @package Zend_Ldap
  35. * @subpackage Node
  36. * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
  37. * @license http://framework.zend.com/license/new-bsd New BSD License
  38. */
  39. class Zend_Ldap_Node extends Zend_Ldap_Node_Abstract implements Iterator, RecursiveIterator
  40. {
  41. /**
  42. * Holds the node's new DN if node is renamed.
  43. *
  44. * @var Zend_Ldap_Dn
  45. */
  46. protected $_newDn;
  47. /**
  48. * Holds the node's orginal attributes (as loaded).
  49. *
  50. * @var array
  51. */
  52. protected $_originalData;
  53. /**
  54. * This node will be added
  55. *
  56. * @var boolean
  57. */
  58. protected $_new;
  59. /**
  60. * This node will be deleted
  61. *
  62. * @var boolean
  63. */
  64. protected $_delete;
  65. /**
  66. * Holds the connection to the LDAP server if in connected mode.
  67. *
  68. * @var Zend_Ldap
  69. */
  70. protected $_ldap;
  71. /**
  72. * Holds an array of the current node's children.
  73. *
  74. * @var array
  75. */
  76. protected $_children;
  77. /**
  78. * Controls iteration status
  79. *
  80. * @var boolean
  81. */
  82. private $_iteratorRewind = false;
  83. /**
  84. * Constructor.
  85. *
  86. * Constructor is protected to enforce the use of factory methods.
  87. *
  88. * @param Zend_Ldap_Dn $dn
  89. * @param array $data
  90. * @param boolean $fromDataSource
  91. * @param Zend_Ldap $ldap
  92. * @throws Zend_Ldap_Exception
  93. */
  94. protected function __construct(Zend_Ldap_Dn $dn, array $data, $fromDataSource, Zend_Ldap $ldap = null)
  95. {
  96. parent::__construct($dn, $data, $fromDataSource);
  97. if ($ldap !== null) $this->attachLdap($ldap);
  98. else $this->detachLdap();
  99. }
  100. /**
  101. * Serialization callback
  102. *
  103. * Only DN and attributes will be serialized.
  104. *
  105. * @return array
  106. */
  107. public function __sleep()
  108. {
  109. return array('_dn', '_currentData', '_newDn', '_originalData',
  110. '_new', '_delete', '_children');
  111. }
  112. /**
  113. * Deserialization callback
  114. *
  115. * Enforces a detached node.
  116. *
  117. * @return null
  118. */
  119. public function __wakeup()
  120. {
  121. $this->detachLdap();
  122. }
  123. /**
  124. * Gets the current LDAP connection.
  125. *
  126. * @return Zend_Ldap
  127. * @throws Zend_Ldap_Exception
  128. */
  129. public function getLdap()
  130. {
  131. if ($this->_ldap === null) {
  132. /**
  133. * @see Zend_Ldap_Exception
  134. */
  135. require_once 'Zend/Ldap/Exception.php';
  136. throw new Zend_Ldap_Exception(null, 'No LDAP connection specified.', Zend_Ldap_Exception::LDAP_OTHER);
  137. }
  138. else return $this->_ldap;
  139. }
  140. /**
  141. * Attach node to an LDAP connection
  142. *
  143. * This is an offline method.
  144. *
  145. * @uses Zend_Ldap_Dn::isChildOf()
  146. * @param Zend_Ldap $ldap
  147. * @return Zend_Ldap_Node Provides a fluent interface
  148. * @throws Zend_Ldap_Exception
  149. */
  150. public function attachLdap(Zend_Ldap $ldap)
  151. {
  152. if (!Zend_Ldap_Dn::isChildOf($this->_getDn(), $ldap->getBaseDn())) {
  153. /**
  154. * @see Zend_Ldap_Exception
  155. */
  156. require_once 'Zend/Ldap/Exception.php';
  157. throw new Zend_Ldap_Exception(null, 'LDAP connection is not responsible for given node.',
  158. Zend_Ldap_Exception::LDAP_OTHER);
  159. }
  160. if ($ldap !== $this->_ldap) {
  161. $this->_ldap = $ldap;
  162. if (is_array($this->_children)) {
  163. foreach ($this->_children as $child) {
  164. /* @var Zend_Ldap_Node $child */
  165. $child->attachLdap($ldap);
  166. }
  167. }
  168. }
  169. return $this;
  170. }
  171. /**
  172. * Detach node from LDAP connection
  173. *
  174. * This is an offline method.
  175. *
  176. * @return Zend_Ldap_Node Provides a fluent interface
  177. */
  178. public function detachLdap()
  179. {
  180. $this->_ldap = null;
  181. if (is_array($this->_children)) {
  182. foreach ($this->_children as $child) {
  183. /* @var Zend_Ldap_Node $child */
  184. $child->detachLdap();
  185. }
  186. }
  187. return $this;
  188. }
  189. /**
  190. * Checks if the current node is attached to a LDAP server.
  191. *
  192. * This is an offline method.
  193. *
  194. * @return boolean
  195. */
  196. public function isAttached()
  197. {
  198. return ($this->_ldap !== null);
  199. }
  200. /**
  201. * @param array $data
  202. * @param boolean $fromDataSource
  203. * @throws Zend_Ldap_Exception
  204. */
  205. protected function _loadData(array $data, $fromDataSource)
  206. {
  207. parent::_loadData($data, $fromDataSource);
  208. if ($fromDataSource === true) {
  209. $this->_originalData = $data;
  210. } else {
  211. $this->_originalData = array();
  212. }
  213. $this->_children = null;
  214. $this->_markAsNew(($fromDataSource === true) ? false : true);
  215. $this->_markAsToBeDeleted(false);
  216. }
  217. /**
  218. * Factory method to create a new detached Zend_Ldap_Node for a given DN.
  219. *
  220. * @param string|array|Zend_Ldap_Dn $dn
  221. * @param array $objectClass
  222. * @return Zend_Ldap_Node
  223. * @throws Zend_Ldap_Exception
  224. */
  225. public static function create($dn, array $objectClass = array())
  226. {
  227. if (is_string($dn) || is_array($dn)) {
  228. $dn = Zend_Ldap_Dn::factory($dn);
  229. } else if ($dn instanceof Zend_Ldap_Dn) {
  230. $dn = clone $dn;
  231. } else {
  232. /**
  233. * @see Zend_Ldap_Exception
  234. */
  235. require_once 'Zend/Ldap/Exception.php';
  236. throw new Zend_Ldap_Exception(null, '$dn is of a wrong data type.');
  237. }
  238. $new = new self($dn, array(), false, null);
  239. $new->_ensureRdnAttributeValues();
  240. $new->setAttribute('objectClass', $objectClass);
  241. return $new;
  242. }
  243. /**
  244. * Factory method to create an attached Zend_Ldap_Node for a given DN.
  245. *
  246. * @param string|array|Zend_Ldap_Dn $dn
  247. * @param Zend_Ldap $ldap
  248. * @return Zend_Ldap_Node|null
  249. * @throws Zend_Ldap_Exception
  250. */
  251. public static function fromLdap($dn, Zend_Ldap $ldap)
  252. {
  253. if (is_string($dn) || is_array($dn)) {
  254. $dn = Zend_Ldap_Dn::factory($dn);
  255. } else if ($dn instanceof Zend_Ldap_Dn) {
  256. $dn = clone $dn;
  257. } else {
  258. /**
  259. * @see Zend_Ldap_Exception
  260. */
  261. require_once 'Zend/Ldap/Exception.php';
  262. throw new Zend_Ldap_Exception(null, '$dn is of a wrong data type.');
  263. }
  264. $data = $ldap->getEntry($dn, array('*', '+'), true);
  265. if ($data === null) {
  266. return null;
  267. }
  268. $entry = new self($dn, $data, true, $ldap);
  269. return $entry;
  270. }
  271. /**
  272. * Factory method to create a detached Zend_Ldap_Node from array data.
  273. *
  274. * @param array $data
  275. * @param boolean $fromDataSource
  276. * @return Zend_Ldap_Node
  277. * @throws Zend_Ldap_Exception
  278. */
  279. public static function fromArray(array $data, $fromDataSource = false)
  280. {
  281. if (!array_key_exists('dn', $data)) {
  282. /**
  283. * @see Zend_Ldap_Exception
  284. */
  285. require_once 'Zend/Ldap/Exception.php';
  286. throw new Zend_Ldap_Exception(null, '\'dn\' key is missing in array.');
  287. }
  288. if (is_string($data['dn']) || is_array($data['dn'])) {
  289. $dn = Zend_Ldap_Dn::factory($data['dn']);
  290. } else if ($data['dn'] instanceof Zend_Ldap_Dn) {
  291. $dn = clone $data['dn'];
  292. } else {
  293. /**
  294. * @see Zend_Ldap_Exception
  295. */
  296. require_once 'Zend/Ldap/Exception.php';
  297. throw new Zend_Ldap_Exception(null, '\'dn\' key is of a wrong data type.');
  298. }
  299. $fromDataSource = ($fromDataSource === true) ? true : false;
  300. $new = new self($dn, $data, $fromDataSource, null);
  301. $new->_ensureRdnAttributeValues();
  302. return $new;
  303. }
  304. /**
  305. * Ensures that teh RDN attributes are correctly set.
  306. *
  307. * @param boolean $overwrite True to overwrite the RDN attributes
  308. * @return void
  309. */
  310. protected function _ensureRdnAttributeValues($overwrite = false)
  311. {
  312. foreach ($this->getRdnArray() as $key => $value) {
  313. if (!array_key_exists($key, $this->_currentData) || $overwrite) {
  314. Zend_Ldap_Attribute::setAttribute($this->_currentData, $key, $value, false);
  315. } else if (!in_array($value, $this->_currentData[$key])) {
  316. Zend_Ldap_Attribute::setAttribute($this->_currentData, $key, $value, true);
  317. }
  318. }
  319. }
  320. /**
  321. * Marks this node as new.
  322. *
  323. * Node will be added (instead of updated) on calling update() if $new is true.
  324. *
  325. * @param boolean $new
  326. */
  327. protected function _markAsNew($new)
  328. {
  329. $this->_new = ($new === false) ? false : true;
  330. }
  331. /**
  332. * Tells if the node is consiedered as new (not present on the server)
  333. *
  334. * Please note, that this doesn't tell you if the node is present on the server.
  335. * Use {@link exits()} to see if a node is already there.
  336. *
  337. * @return boolean
  338. */
  339. public function isNew()
  340. {
  341. return $this->_new;
  342. }
  343. /**
  344. * Marks this node as to be deleted.
  345. *
  346. * Node will be deleted on calling update() if $delete is true.
  347. *
  348. * @param boolean $delete
  349. */
  350. protected function _markAsToBeDeleted($delete)
  351. {
  352. $this->_delete = ($delete === true) ? true : false;
  353. }
  354. /**
  355. * Is this node going to be deleted once update() is called?
  356. *
  357. * @return boolean
  358. */
  359. public function willBeDeleted()
  360. {
  361. return $this->_delete;
  362. }
  363. /**
  364. * Marks this node as to be deleted
  365. *
  366. * Node will be deleted on calling update() if $delete is true.
  367. *
  368. * @return Zend_Ldap_Node Provides a fluent interface
  369. */
  370. public function delete()
  371. {
  372. $this->_markAsToBeDeleted(true);
  373. return $this;
  374. }
  375. /**
  376. * Is this node going to be moved once update() is called?
  377. *
  378. * @return boolean
  379. */
  380. public function willBeMoved()
  381. {
  382. if ($this->isNew() || $this->willBeDeleted()) {
  383. return false;
  384. } else if ($this->_newDn !== null) {
  385. return ($this->_dn != $this->_newDn);
  386. } else {
  387. return false;
  388. }
  389. }
  390. /**
  391. * Sends all pending changes to the LDAP server
  392. *
  393. * @param Zend_Ldap $ldap
  394. * @return Zend_Ldap_Node Provides a fluent interface
  395. * @throws Zend_Ldap_Exception
  396. */
  397. public function update(Zend_Ldap $ldap = null)
  398. {
  399. if ($ldap !== null) {
  400. $this->attachLdap($ldap);
  401. }
  402. $ldap = $this->getLdap();
  403. if (!($ldap instanceof Zend_Ldap)) {
  404. /**
  405. * @see Zend_Ldap_Exception
  406. */
  407. require_once 'Zend/Ldap/Exception.php';
  408. throw new Zend_Ldap_Exception(null, 'No LDAP connection available');
  409. }
  410. if ($this->willBeDeleted()) {
  411. if ($ldap->exists($this->_dn)) {
  412. $this->_preDelete();
  413. $ldap->delete($this->_dn);
  414. $this->_postDelete();
  415. }
  416. return $this;
  417. }
  418. if ($this->isNew()) {
  419. $this->_preAdd();
  420. $data = $this->getData();
  421. $ldap->add($this->_getDn(), $data);
  422. $this->_loadData($data, true);
  423. $this->_postAdd();
  424. return $this;
  425. }
  426. $changedData = $this->getChangedData();
  427. if ($this->willBeMoved()) {
  428. $this->_preRename();
  429. $recursive = $this->hasChildren();
  430. $ldap->rename($this->_dn, $this->_newDn, $recursive, false);
  431. foreach ($this->_newDn->getRdn() as $key => $value) {
  432. if (array_key_exists($key, $changedData)) {
  433. unset($changedData[$key]);
  434. }
  435. }
  436. $this->_dn = $this->_newDn;
  437. $this->_newDn = null;
  438. $this->_postRename();
  439. }
  440. if (count($changedData) > 0) {
  441. $this->_preUpdate();
  442. $ldap->update($this->_getDn(), $changedData);
  443. $this->_postUpdate();
  444. }
  445. $this->_originalData = $this->_currentData;
  446. return $this;
  447. }
  448. /**
  449. * Gets the DN of the current node as a Zend_Ldap_Dn.
  450. *
  451. * This is an offline method.
  452. *
  453. * @return Zend_Ldap_Dn
  454. */
  455. protected function _getDn()
  456. {
  457. return ($this->_newDn === null) ? parent::_getDn() : $this->_newDn;
  458. }
  459. /**
  460. * Gets the current DN of the current node as a Zend_Ldap_Dn.
  461. * The method returns a clone of the node's DN to prohibit modification.
  462. *
  463. * This is an offline method.
  464. *
  465. * @return Zend_Ldap_Dn
  466. */
  467. public function getCurrentDn()
  468. {
  469. $dn = clone parent::_getDn();
  470. return $dn;
  471. }
  472. /**
  473. * Sets the new DN for this node
  474. *
  475. * This is an offline method.
  476. *
  477. * @param Zend_Ldap_Dn|string|array $newDn
  478. * @throws Zend_Ldap_Exception
  479. * @return Zend_Ldap_Node Provides a fluent interface
  480. */
  481. public function setDn($newDn)
  482. {
  483. if ($newDn instanceof Zend_Ldap_Dn) {
  484. $this->_newDn = clone $newDn;
  485. } else {
  486. $this->_newDn = Zend_Ldap_Dn::factory($newDn);
  487. }
  488. $this->_ensureRdnAttributeValues(true);
  489. return $this;
  490. }
  491. /**
  492. * {@see setDn()}
  493. *
  494. * This is an offline method.
  495. *
  496. * @param Zend_Ldap_Dn|string|array $newDn
  497. * @throws Zend_Ldap_Exception
  498. * @return Zend_Ldap_Node Provides a fluent interface
  499. */
  500. public function move($newDn)
  501. {
  502. return $this->setDn($newDn);
  503. }
  504. /**
  505. * {@see setDn()}
  506. *
  507. * This is an offline method.
  508. *
  509. * @param Zend_Ldap_Dn|string|array $newDn
  510. * @throws Zend_Ldap_Exception
  511. * @return Zend_Ldap_Node Provides a fluent interface
  512. */
  513. public function rename($newDn)
  514. {
  515. return $this->setDn($newDn);
  516. }
  517. /**
  518. * Sets the objectClass.
  519. *
  520. * This is an offline method.
  521. *
  522. * @param array|string $value
  523. * @return Zend_Ldap_Node Provides a fluent interface
  524. * @throws Zend_Ldap_Exception
  525. */
  526. public function setObjectClass($value)
  527. {
  528. $this->setAttribute('objectClass', $value);
  529. return $this;
  530. }
  531. /**
  532. * Appends to the objectClass.
  533. *
  534. * This is an offline method.
  535. *
  536. * @param array|string $value
  537. * @return Zend_Ldap_Node Provides a fluent interface
  538. * @throws Zend_Ldap_Exception
  539. */
  540. public function appendObjectClass($value)
  541. {
  542. $this->appendToAttribute('objectClass', $value);
  543. return $this;
  544. }
  545. /**
  546. * Returns a LDIF representation of the current node
  547. *
  548. * @param array $options Additional options used during encoding
  549. * @return string
  550. */
  551. public function toLdif(array $options = array())
  552. {
  553. $attributes = array_merge(array('dn' => $this->getDnString()), $this->getData(false));
  554. /**
  555. * Zend_Ldap_Ldif_Encoder
  556. */
  557. require_once 'Zend/Ldap/Ldif/Encoder.php';
  558. return Zend_Ldap_Ldif_Encoder::encode($attributes, $options);
  559. }
  560. /**
  561. * Gets changed node data.
  562. *
  563. * The array contains all changed attributes.
  564. * This format can be used in {@link Zend_Ldap::add()} and {@link Zend_Ldap::update()}.
  565. *
  566. * This is an offline method.
  567. *
  568. * @return array
  569. */
  570. public function getChangedData()
  571. {
  572. $changed = array();
  573. foreach ($this->_currentData as $key => $value) {
  574. if (!array_key_exists($key, $this->_originalData) && !empty($value)) {
  575. $changed[$key] = $value;
  576. } else if ($this->_originalData[$key] !== $this->_currentData[$key]) {
  577. $changed[$key] = $value;
  578. }
  579. }
  580. return $changed;
  581. }
  582. /**
  583. * Returns all changes made.
  584. *
  585. * This is an offline method.
  586. *
  587. * @return array
  588. */
  589. public function getChanges()
  590. {
  591. $changes = array(
  592. 'add' => array(),
  593. 'delete' => array(),
  594. 'replace' => array());
  595. foreach ($this->_currentData as $key => $value) {
  596. if (!array_key_exists($key, $this->_originalData) && !empty($value)) {
  597. $changes['add'][$key] = $value;
  598. } else if (count($this->_originalData[$key]) === 0 && !empty($value)) {
  599. $changes['add'][$key] = $value;
  600. } else if ($this->_originalData[$key] !== $this->_currentData[$key]) {
  601. if (empty($value)) {
  602. $changes['delete'][$key] = $value;
  603. } else {
  604. $changes['replace'][$key] = $value;
  605. }
  606. }
  607. }
  608. return $changes;
  609. }
  610. /**
  611. * Sets a LDAP attribute.
  612. *
  613. * This is an offline method.
  614. *
  615. * @param string $name
  616. * @param mixed $value
  617. * @return Zend_Ldap_Node Provides a fluent interface
  618. * @throws Zend_Ldap_Exception
  619. */
  620. public function setAttribute($name, $value)
  621. {
  622. $this->_setAttribute($name, $value, false);
  623. return $this;
  624. }
  625. /**
  626. * Appends to a LDAP attribute.
  627. *
  628. * This is an offline method.
  629. *
  630. * @param string $name
  631. * @param mixed $value
  632. * @return Zend_Ldap_Node Provides a fluent interface
  633. * @throws Zend_Ldap_Exception
  634. */
  635. public function appendToAttribute($name, $value)
  636. {
  637. $this->_setAttribute($name, $value, true);
  638. return $this;
  639. }
  640. /**
  641. * Checks if the attribute can be set and sets it accordingly.
  642. *
  643. * @param string $name
  644. * @param mixed $value
  645. * @param boolean $append
  646. * @throws Zend_Ldap_Exception
  647. */
  648. protected function _setAttribute($name, $value, $append)
  649. {
  650. $this->_assertChangeableAttribute($name);
  651. Zend_Ldap_Attribute::setAttribute($this->_currentData, $name, $value, $append);
  652. }
  653. /**
  654. * Sets a LDAP date/time attribute.
  655. *
  656. * This is an offline method.
  657. *
  658. * @param string $name
  659. * @param integer|array $value
  660. * @param boolean $utc
  661. * @return Zend_Ldap_Node Provides a fluent interface
  662. * @throws Zend_Ldap_Exception
  663. */
  664. public function setDateTimeAttribute($name, $value, $utc = false)
  665. {
  666. $this->_setDateTimeAttribute($name, $value, $utc, false);
  667. return $this;
  668. }
  669. /**
  670. * Appends to a LDAP date/time attribute.
  671. *
  672. * This is an offline method.
  673. *
  674. * @param string $name
  675. * @param integer|array $value
  676. * @param boolean $utc
  677. * @return Zend_Ldap_Node Provides a fluent interface
  678. * @throws Zend_Ldap_Exception
  679. */
  680. public function appendToDateTimeAttribute($name, $value, $utc = false)
  681. {
  682. $this->_setDateTimeAttribute($name, $value, $utc, true);
  683. return $this;
  684. }
  685. /**
  686. * Checks if the attribute can be set and sets it accordingly.
  687. *
  688. * @param string $name
  689. * @param integer|array $value
  690. * @param boolean $utc
  691. * @param boolean $append
  692. * @throws Zend_Ldap_Exception
  693. */
  694. protected function _setDateTimeAttribute($name, $value, $utc, $append)
  695. {
  696. $this->_assertChangeableAttribute($name);
  697. Zend_Ldap_Attribute::setDateTimeAttribute($this->_currentData, $name, $value, $utc, $append);
  698. }
  699. /**
  700. * Sets a LDAP password.
  701. *
  702. * @param string $password
  703. * @param string $hashType
  704. * @param string $attribName
  705. * @return Zend_Ldap_Node Provides a fluent interface
  706. * @throws Zend_Ldap_Exception
  707. */
  708. public function setPasswordAttribute($password, $hashType = Zend_Ldap_Attribute::PASSWORD_HASH_MD5,
  709. $attribName = 'userPassword')
  710. {
  711. $this->_assertChangeableAttribute($attribName);
  712. Zend_Ldap_Attribute::setPassword($this->_currentData, $password, $hashType, $attribName);
  713. return $this;
  714. }
  715. /**
  716. * Deletes a LDAP attribute.
  717. *
  718. * This method deletes the attribute.
  719. *
  720. * This is an offline method.
  721. *
  722. * @param string $name
  723. * @return Zend_Ldap_Node Provides a fluent interface
  724. * @throws Zend_Ldap_Exception
  725. */
  726. public function deleteAttribute($name)
  727. {
  728. if ($this->existsAttribute($name, true)) {
  729. $this->_setAttribute($name, null, false);
  730. }
  731. return $this;
  732. }
  733. /**
  734. * Removes duplicate values from a LDAP attribute
  735. *
  736. * @param string $attribName
  737. * @return void
  738. */
  739. public function removeDuplicatesFromAttribute($attribName)
  740. {
  741. Zend_Ldap_Attribute::removeDuplicatesFromAttribute($this->_currentData, $attribName);
  742. }
  743. /**
  744. * Remove given values from a LDAP attribute
  745. *
  746. * @param string $attribName
  747. * @param mixed|array $value
  748. * @return void
  749. */
  750. public function removeFromAttribute($attribName, $value)
  751. {
  752. Zend_Ldap_Attribute::removeFromAttribute($this->_currentData, $attribName, $value);
  753. }
  754. /**
  755. * @param string $name
  756. * @return boolean
  757. * @throws Zend_Ldap_Exception
  758. */
  759. protected function _assertChangeableAttribute($name)
  760. {
  761. $name = strtolower($name);
  762. $rdn = $this->getRdnArray(Zend_Ldap_Dn::ATTR_CASEFOLD_LOWER);
  763. if ($name == 'dn') {
  764. /**
  765. * @see Zend_Ldap_Exception
  766. */
  767. require_once 'Zend/Ldap/Exception.php';
  768. throw new Zend_Ldap_Exception(null, 'DN cannot be changed.');
  769. }
  770. else if (array_key_exists($name, $rdn)) {
  771. /**
  772. * @see Zend_Ldap_Exception
  773. */
  774. require_once 'Zend/Ldap/Exception.php';
  775. throw new Zend_Ldap_Exception(null, 'Cannot change attribute because it\'s part of the RDN');
  776. } else if (in_array($name, self::$_systemAttributes)) {
  777. /**
  778. * @see Zend_Ldap_Exception
  779. */
  780. require_once 'Zend/Ldap/Exception.php';
  781. throw new Zend_Ldap_Exception(null, 'Cannot change attribute because it\'s read-only');
  782. }
  783. else return true;
  784. }
  785. /**
  786. * Sets a LDAP attribute.
  787. *
  788. * This is an offline method.
  789. *
  790. * @param string $name
  791. * @param mixed $value
  792. * @return null
  793. * @throws Zend_Ldap_Exception
  794. */
  795. public function __set($name, $value)
  796. {
  797. $this->setAttribute($name, $value);
  798. }
  799. /**
  800. * Deletes a LDAP attribute.
  801. *
  802. * This method deletes the attribute.
  803. *
  804. * This is an offline method.
  805. *
  806. * @param string $name
  807. * @return null
  808. * @throws Zend_Ldap_Exception
  809. */
  810. public function __unset($name)
  811. {
  812. $this->deleteAttribute($name);
  813. }
  814. /**
  815. * Sets a LDAP attribute.
  816. * Implements ArrayAccess.
  817. *
  818. * This is an offline method.
  819. *
  820. * @param string $name
  821. * @param mixed $value
  822. * @return null
  823. * @throws Zend_Ldap_Exception
  824. */
  825. public function offsetSet($name, $value)
  826. {
  827. $this->setAttribute($name, $value);
  828. }
  829. /**
  830. * Deletes a LDAP attribute.
  831. * Implements ArrayAccess.
  832. *
  833. * This method deletes the attribute.
  834. *
  835. * This is an offline method.
  836. *
  837. * @param string $name
  838. * @return null
  839. * @throws Zend_Ldap_Exception
  840. */
  841. public function offsetUnset($name)
  842. {
  843. $this->deleteAttribute($name);
  844. }
  845. /**
  846. * Check if node exists on LDAP.
  847. *
  848. * This is an online method.
  849. *
  850. * @param Zend_Ldap $ldap
  851. * @return boolean
  852. * @throws Zend_Ldap_Exception
  853. */
  854. public function exists(Zend_Ldap $ldap = null)
  855. {
  856. if ($ldap !== null) {
  857. $this->attachLdap($ldap);
  858. }
  859. $ldap = $this->getLdap();
  860. return $ldap->exists($this->_getDn());
  861. }
  862. /**
  863. * Reload node attributes from LDAP.
  864. *
  865. * This is an online method.
  866. *
  867. * @param Zend_Ldap $ldap
  868. * @return Zend_Ldap_Node Provides a fluent interface
  869. * @throws Zend_Ldap_Exception
  870. */
  871. public function reload(Zend_Ldap $ldap = null)
  872. {
  873. if ($ldap !== null) {
  874. $this->attachLdap($ldap);
  875. }
  876. $ldap = $this->getLdap();
  877. parent::reload($ldap);
  878. return $this;
  879. }
  880. /**
  881. * Search current subtree with given options.
  882. *
  883. * This is an online method.
  884. *
  885. * @param string|Zend_Ldap_Filter_Abstract $filter
  886. * @param integer $scope
  887. * @param string $sort
  888. * @return Zend_Ldap_Node_Collection
  889. * @throws Zend_Ldap_Exception
  890. */
  891. public function searchSubtree($filter, $scope = Zend_Ldap::SEARCH_SCOPE_SUB, $sort = null)
  892. {
  893. /**
  894. * @see Zend_Ldap_Node_Collection
  895. */
  896. require_once 'Zend/Ldap/Node/Collection.php';
  897. return $this->getLdap()->search($filter, $this->_getDn(), $scope, array('*', '+'), $sort,
  898. 'Zend_Ldap_Node_Collection');
  899. }
  900. /**
  901. * Count items in current subtree found by given filter.
  902. *
  903. * This is an online method.
  904. *
  905. * @param string|Zend_Ldap_Filter_Abstract $filter
  906. * @param integer $scope
  907. * @return integer
  908. * @throws Zend_Ldap_Exception
  909. */
  910. public function countSubtree($filter, $scope = Zend_Ldap::SEARCH_SCOPE_SUB)
  911. {
  912. return $this->getLdap()->count($filter, $this->_getDn(), $scope);
  913. }
  914. /**
  915. * Count children of current node.
  916. *
  917. * This is an online method.
  918. *
  919. * @return integer
  920. * @throws Zend_Ldap_Exception
  921. */
  922. public function countChildren()
  923. {
  924. return $this->countSubtree('(objectClass=*)', Zend_Ldap::SEARCH_SCOPE_ONE);
  925. }
  926. /**
  927. * Gets children of current node.
  928. *
  929. * This is an online method.
  930. *
  931. * @param string|Zend_Ldap_Filter_Abstract $filter
  932. * @param string $sort
  933. * @return Zend_Ldap_Node_Collection
  934. * @throws Zend_Ldap_Exception
  935. */
  936. public function searchChildren($filter, $sort = null)
  937. {
  938. return $this->searchSubtree($filter, Zend_Ldap::SEARCH_SCOPE_ONE, $sort);
  939. }
  940. /**
  941. * Checks if current node has children.
  942. * Returns whether the current element has children.
  943. *
  944. * Can be used offline but returns false if children have not been retrieved yet.
  945. *
  946. * @return boolean
  947. * @throws Zend_Ldap_Exception
  948. */
  949. public function hasChildren()
  950. {
  951. if (!is_array($this->_children)) {
  952. if ($this->isAttached()) {
  953. return ($this->countChildren() > 0);
  954. } else {
  955. return false;
  956. }
  957. } else {
  958. return (count($this->_children) > 0);
  959. }
  960. }
  961. /**
  962. * Returns the children for the current node.
  963. *
  964. * Can be used offline but returns an empty array if children have not been retrieved yet.
  965. *
  966. * @return Zend_Ldap_Node_ChildrenIterator
  967. * @throws Zend_Ldap_Exception
  968. */
  969. public function getChildren()
  970. {
  971. if (!is_array($this->_children)) {
  972. $this->_children = array();
  973. if ($this->isAttached()) {
  974. $children = $this->searchChildren('(objectClass=*)', null);
  975. foreach ($children as $child) {
  976. /* @var Zend_Ldap_Node $child */
  977. $this->_children[$child->getRdnString(Zend_Ldap_Dn::ATTR_CASEFOLD_LOWER)] = $child;
  978. }
  979. }
  980. }
  981. /**
  982. * @see Zend_Ldap_Node_ChildrenIterator
  983. */
  984. require_once 'Zend/Ldap/Node/ChildrenIterator.php';
  985. return new Zend_Ldap_Node_ChildrenIterator($this->_children);
  986. }
  987. /**
  988. * Returns the parent of the current node.
  989. *
  990. * @param Zend_Ldap $ldap
  991. * @return Zend_Ldap_Node
  992. * @throws Zend_Ldap_Exception
  993. */
  994. public function getParent(Zend_Ldap $ldap = null)
  995. {
  996. if ($ldap !== null) {
  997. $this->attachLdap($ldap);
  998. }
  999. $ldap = $this->getLdap();
  1000. $parentDn = $this->_getDn()->getParentDn(1);
  1001. return self::fromLdap($parentDn, $ldap);
  1002. }
  1003. /**
  1004. * Return the current attribute.
  1005. * Implements Iterator
  1006. *
  1007. * @return array
  1008. */
  1009. public function current()
  1010. {
  1011. return $this;
  1012. }
  1013. /**
  1014. * Return the attribute name.
  1015. * Implements Iterator
  1016. *
  1017. * @return string
  1018. */
  1019. public function key()
  1020. {
  1021. return $this->getRdnString();
  1022. }
  1023. /**
  1024. * Move forward to next attribute.
  1025. * Implements Iterator
  1026. */
  1027. public function next()
  1028. {
  1029. $this->_iteratorRewind = false;
  1030. }
  1031. /**
  1032. * Rewind the Iterator to the first attribute.
  1033. * Implements Iterator
  1034. */
  1035. public function rewind()
  1036. {
  1037. $this->_iteratorRewind = true;
  1038. }
  1039. /**
  1040. * Check if there is a current attribute
  1041. * after calls to rewind() or next().
  1042. * Implements Iterator
  1043. *
  1044. * @return boolean
  1045. */
  1046. public function valid()
  1047. {
  1048. return $this->_iteratorRewind;
  1049. }
  1050. ####################################################
  1051. # Empty method bodies for overriding in subclasses #
  1052. ####################################################
  1053. /**
  1054. * Allows pre-delete logic to be applied to node.
  1055. * Subclasses may override this method.
  1056. *
  1057. * @return void
  1058. */
  1059. protected function _preDelete() { }
  1060. /**
  1061. * Allows post-delete logic to be applied to node.
  1062. * Subclasses may override this method.
  1063. *
  1064. * @return void
  1065. */
  1066. protected function _postDelete() { }
  1067. /**
  1068. * Allows pre-add logic to be applied to node.
  1069. * Subclasses may override this method.
  1070. *
  1071. * @return void
  1072. */
  1073. protected function _preAdd() { }
  1074. /**
  1075. * Allows post-add logic to be applied to node.
  1076. * Subclasses may override this method.
  1077. *
  1078. * @return void
  1079. */
  1080. protected function _postAdd() { }
  1081. /**
  1082. * Allows pre-rename logic to be applied to node.
  1083. * Subclasses may override this method.
  1084. *
  1085. * @return void
  1086. */
  1087. protected function _preRename() { }
  1088. /**
  1089. * Allows post-rename logic to be applied to node.
  1090. * Subclasses may override this method.
  1091. *
  1092. * @return void
  1093. */
  1094. protected function _postRename() { }
  1095. /**
  1096. * Allows pre-update logic to be applied to node.
  1097. * Subclasses may override this method.
  1098. *
  1099. * @return void
  1100. */
  1101. protected function _preUpdate() { }
  1102. /**
  1103. * Allows post-update logic to be applied to node.
  1104. * Subclasses may override this method.
  1105. *
  1106. * @return void
  1107. */
  1108. protected function _postUpdate() { }
  1109. }