7 * @license http://www.gnu.org/licenses/agpl.html AGPL3
8 * @copyright Copyright (c) 2008-2014 Metaways Infosystems GmbH (http://www.metaways.de)
9 * @author Lars Kneschke <l.kneschke@metaways.de>
13 * LDAP base class for tine 2.0
17 class Tinebase_Ldap extends Zend_Ldap
22 * @param array $_options
23 * @return @see Zend_Ldap
25 public function __construct(array $_options)
27 if (Tinebase_Config::getInstance()->get(Tinebase_Config::LDAP_DISABLE_TLSREQCERT)) {
28 if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__
29 . ' Disable TLS certificate check');
30 putenv('LDAPTLS_REQCERT=never');
33 // strip non Zend_Ldap options
34 $options = array_intersect_key($_options, array(
40 'bindRequiresDn' => null,
42 'accountCanonicalForm' => null,
43 'accountDomainName' => null,
44 'accountDomainNameShort' => null,
45 'accountFilterFormat' => null,
46 'allowEmptyPassword' => null,
47 'useStartTls' => null,
48 'optReferrals' => null,
49 'tryUsernameSplit' => null
52 $returnValue = parent::__construct($options);
54 if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
55 . ' LDAP options: ' . print_r($options, true));
61 * Delete an LDAP entry
63 * @param string|Zend_Ldap_Dn $dn
65 * @return Zend_Ldap *Provides a fluid interface*
66 * @throws Zend_Ldap_Exception
68 public function deleteProperty($dn, array $data)
70 if ($dn instanceof Zend_Ldap_Dn) {
71 $dn = $dn->toString();
74 $isDeleted = @ldap_mod_del($this->getResource(), $dn, $data);
75 if($isDeleted === false) {
77 * @see Zend_Ldap_Exception
79 require_once 'Zend/Ldap/Exception.php';
80 throw new Zend_Ldap_Exception($this, 'deleting: ' . $dn);
86 * read binary attribute from one entry from the ldap directory
88 * @todo still needed???
90 * @param string $_dn the dn to read
91 * @param string $_filter search filter
92 * @param array $_attribute which field to return
93 * @return blob binary data of given field
94 * @throws Exception with ldap error
96 public function fetchBinaryAttribute($_dn, $_filter, $_attribute)
98 $searchResult = @ldap_search($this->getResource(), $_dn, $_filter, $_attribute, $this->_attrsOnly, $this->_sizeLimit, $this->_timeLimit);
100 if($searchResult === FALSE) {
101 throw new Exception(ldap_error($this->getResource()));
104 $searchCount = ldap_count_entries($this->getResource(), $searchResult);
105 if($searchCount === 0) {
106 throw new Exception('Nothing found for filter: ' . $_filter);
107 } elseif ($searchCount > 1) {
108 throw new Exception('More than one entry found for filter: ' . $_filter);
111 $entry = ldap_first_entry($this->getResource(), $searchResult);
113 return ldap_get_values_len($this->getResource(), $entry, $_attribute);
117 * Add new information to the LDAP repository
119 * @param string|Zend_Ldap_Dn $dn
120 * @param array $entry
121 * @return Zend_Ldap *Provides a fluid interface*
122 * @throws Zend_Ldap_Exception
124 public function addProperty($dn, array $entry)
126 if (!($dn instanceof Zend_Ldap_Dn)) {
127 $dn = Zend_Ldap_Dn::factory($dn, null);
129 self::prepareLdapEntryArray($entry);
130 foreach ($entry as $key => $value) {
131 if (is_array($value) && count($value) === 0) {
136 $rdnParts = $dn->getRdn(Zend_Ldap_Dn::ATTR_CASEFOLD_LOWER);
137 $adAttributes = array('distinguishedname', 'instancetype', 'name', 'objectcategory',
138 'objectguid', 'usnchanged', 'usncreated', 'whenchanged', 'whencreated');
139 $stripAttributes = array_merge(array_keys($rdnParts), $adAttributes);
140 foreach ($stripAttributes as $attr) {
141 if ((isset($entry[$attr]) || array_key_exists($attr, $entry))) {
142 unset($entry[$attr]);
146 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' $dn: ' . $dn->toString());
147 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' $data: ' . print_r($entry, true));
149 $isAdded = @ldap_mod_add($this->getResource(), $dn->toString(), $entry);
150 if($isAdded === false) {
152 * @see Zend_Ldap_Exception
154 require_once 'Zend/Ldap/Exception.php';
155 throw new Zend_Ldap_Exception($this, 'adding: ' . $dn->toString());
161 * Update LDAP registry
163 * @param string|Zend_Ldap_Dn $dn
164 * @param array $entry
165 * @return Zend_Ldap *Provides a fluid interface*
166 * @throws Zend_Ldap_Exception
168 public function updateProperty($dn, array $entry)
170 if (!($dn instanceof Zend_Ldap_Dn)) {
171 $dn = Zend_Ldap_Dn::factory($dn, null);
173 self::prepareLdapEntryArray($entry);
175 $rdnParts = $dn->getRdn(Zend_Ldap_Dn::ATTR_CASEFOLD_LOWER);
176 $adAttributes = array('distinguishedname', 'instancetype', 'name', 'objectcategory',
177 'objectguid', 'usnchanged', 'usncreated', 'whenchanged', 'whencreated');
178 $stripAttributes = array_merge(array_keys($rdnParts), $adAttributes);
179 foreach ($stripAttributes as $attr) {
180 if ((isset($entry[$attr]) || array_key_exists($attr, $entry))) {
181 unset($entry[$attr]);
185 if (count($entry) > 0) {
186 $isModified = @ldap_mod_replace($this->getResource(), $dn->toString(), $entry);
187 if($isModified === false) {
189 * @see Zend_Ldap_Exception
191 require_once 'Zend/Ldap/Exception.php';
192 throw new Zend_Ldap_Exception($this, 'updating: ' . $dn->toString());
199 * return first namingContext from LDAP root DSE
203 public function getFirstNamingContext()
205 return Tinebase_Helper::array_value(0, $this->getRootDse()->getNamingContexts());
209 * convert binary objectGUID to to plain ASCII string
210 * example guid: c8ab4322-3a4b-4af9-a100-9ed746049c91
212 * @param string $binaryGuid
215 public static function decodeGuid($binaryGuid)
217 $hexGuid = unpack("H*hex", $binaryGuid);
218 $hex = $hexGuid["hex"];
220 $hex1 = substr($hex, -26, 2) . substr($hex, -28, 2) . substr($hex, -30, 2) . substr($hex, -32, 2);
221 $hex2 = substr($hex, -22, 2) . substr($hex, -24, 2);
222 $hex3 = substr($hex, -18, 2) . substr($hex, -20, 2);
223 $hex4 = substr($hex, -16, 4);
224 $hex5 = substr($hex, -12, 12);
226 $guid = $hex1 . "-" . $hex2 . "-" . $hex3 . "-" . $hex4 . "-" . $hex5;
232 * convert plain ASCII objectGUID to binary string
233 * example guid: c8ab4322-3a4b-4af9-a100-9ed746049c91
235 * @param string $guid
238 public static function encodeGuid($guid)
240 $hex = substr($guid, -30, 2) . substr($guid, -32, 2) . substr($guid, -34, 2) . substr($guid, -36, 2);
241 $hex .= substr($guid, -25, 2) . substr($guid, -27, 2);
242 $hex .= substr($guid, -20, 2) . substr($guid, -22, 2);
243 $hex .= substr($guid, -17, 4);
244 $hex .= substr($guid, -12, 12);
246 $binaryGuid = pack('H*', $hex);
252 * decode ActiveDirectory SID
254 * @see https://msdn.microsoft.com/en-us/library/ff632068.aspx
256 * @param string $binarySid the binary encoded SID
259 * TODO should be moved to AD trait/abstract
261 public static function decodeSid($binarySid)
263 if (preg_match('/^S\-1/', $binarySid)) {
270 $unpacked = unpack("crev/cdashes/nc/Nd/V*e", $binarySid);
274 unset($unpacked["dashes"]); // unused
275 $unpacked["c"] = $n232 * $unpacked["c"] + $unpacked["d"];
276 unset($unpacked["d"]);
280 foreach ($unpacked as $v) {