Merge branch '2013.10' into 2014.11
[tine20] / tine20 / Tinebase / Ldap.php
1 <?php
2 /**
3  * Tine 2.0
4  *
5  * @package     Tinebase
6  * @subpackage  Ldap
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>
10  */
11
12 /**
13  * LDAP base class for tine 2.0
14  * @package     Tinebase
15  * @subpackage  Ldap
16  */
17 class Tinebase_Ldap extends Zend_Ldap
18 {
19     /**
20      * Extend constructor
21      *
22      * @param array $_options
23      * @return @see Zend_Ldap
24      */
25     public function __construct(array $_options)
26     {
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');
31         }
32         
33         // strip non Zend_Ldap options
34         $options = array_intersect_key($_options, array(
35             'host'                      => null,
36             'port'                      => null,
37             'useSsl'                    => null,
38             'username'                  => null,
39             'password'                  => null,
40             'bindRequiresDn'            => null,
41             'baseDn'                    => 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
50         ));
51         
52         $returnValue = parent::__construct($options);
53
54         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
55             . ' LDAP options: ' . print_r($options, true));
56         
57         return $returnValue;
58     }
59     
60     /**
61      * Delete an LDAP entry
62      *
63      * @param  string|Zend_Ldap_Dn $dn
64      * @param  array $data
65      * @return Zend_Ldap *Provides a fluid interface*
66      * @throws Zend_Ldap_Exception
67      */
68     public function deleteProperty($dn, array $data)
69     {
70         if ($dn instanceof Zend_Ldap_Dn) {
71             $dn = $dn->toString();
72         }
73
74         $isDeleted = @ldap_mod_del($this->getResource(), $dn, $data);
75         if($isDeleted === false) {
76             /**
77              * @see Zend_Ldap_Exception
78              */
79             require_once 'Zend/Ldap/Exception.php';
80             throw new Zend_Ldap_Exception($this, 'deleting: ' . $dn);
81         }
82         return $this;
83     }
84     
85     /**
86      * read binary attribute from one entry from the ldap directory
87      *
88      * @todo still needed???
89      * 
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
95      */
96     public function fetchBinaryAttribute($_dn, $_filter, $_attribute)
97     {
98         $searchResult = @ldap_search($this->getResource(), $_dn, $_filter, $_attribute, $this->_attrsOnly, $this->_sizeLimit, $this->_timeLimit);
99         
100         if($searchResult === FALSE) {
101             throw new Exception(ldap_error($this->getResource()));
102         }
103         
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);
109         }
110         
111         $entry = ldap_first_entry($this->getResource(), $searchResult);
112         
113         return ldap_get_values_len($this->getResource(), $entry, $_attribute);
114     }
115     
116     /**
117      * Add new information to the LDAP repository
118      *
119      * @param string|Zend_Ldap_Dn $dn
120      * @param array $entry
121      * @return Zend_Ldap *Provides a fluid interface*
122      * @throws Zend_Ldap_Exception
123      */
124     public function addProperty($dn, array $entry)
125     {
126         if (!($dn instanceof Zend_Ldap_Dn)) {
127             $dn = Zend_Ldap_Dn::factory($dn, null);
128         }
129         self::prepareLdapEntryArray($entry);
130         foreach ($entry as $key => $value) {
131             if (is_array($value) && count($value) === 0) {
132                 unset($entry[$key]);
133             }
134         }
135
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]);
143             }
144         }
145         
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));
148         
149         $isAdded = @ldap_mod_add($this->getResource(), $dn->toString(), $entry);
150         if($isAdded === false) {
151             /**
152              * @see Zend_Ldap_Exception
153              */
154             require_once 'Zend/Ldap/Exception.php';
155             throw new Zend_Ldap_Exception($this, 'adding: ' . $dn->toString());
156         }
157         return $this;
158     }
159     
160     /**
161      * Update LDAP registry
162      *
163      * @param string|Zend_Ldap_Dn $dn
164      * @param array $entry
165      * @return Zend_Ldap *Provides a fluid interface*
166      * @throws Zend_Ldap_Exception
167      */
168     public function updateProperty($dn, array $entry)
169     {
170         if (!($dn instanceof Zend_Ldap_Dn)) {
171             $dn = Zend_Ldap_Dn::factory($dn, null);
172         }
173         self::prepareLdapEntryArray($entry);
174
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]);
182             }
183         }
184
185         if (count($entry) > 0) {
186             $isModified = @ldap_mod_replace($this->getResource(), $dn->toString(), $entry);
187             if($isModified === false) {
188                 /**
189                  * @see Zend_Ldap_Exception
190                  */
191                 require_once 'Zend/Ldap/Exception.php';
192                 throw new Zend_Ldap_Exception($this, 'updating: ' . $dn->toString());
193             }
194         }
195         return $this;
196     }
197     
198     /**
199      * return first namingContext from LDAP root DSE
200      * 
201      * @return string
202      */
203     public function getFirstNamingContext()
204     {
205         return Tinebase_Helper::array_value(0, $this->getRootDse()->getNamingContexts());
206     }
207     
208     /**
209      * convert binary objectGUID to to plain ASCII string
210      * example guid: c8ab4322-3a4b-4af9-a100-9ed746049c91
211      * 
212      * @param  string  $binaryGuid
213      * @return string
214      */
215     public static function decodeGuid($binaryGuid)
216     {
217         $hexGuid = unpack("H*hex", $binaryGuid); 
218         $hex = $hexGuid["hex"];
219         
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);
225         
226         $guid = $hex1 . "-" . $hex2 . "-" . $hex3 . "-" . $hex4 . "-" . $hex5;
227         
228         return $guid;
229     }
230     
231     /**
232      * convert plain ASCII objectGUID to binary string
233      * example guid: c8ab4322-3a4b-4af9-a100-9ed746049c91
234      * 
235      * @param  string $guid
236      * @return string
237      */
238     public static function encodeGuid($guid)
239     {
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);
245         
246         $binaryGuid = pack('H*', $hex);
247         
248         return $binaryGuid;
249     }
250     
251     /**
252      * decode ActiveDirectory SID
253      * 
254      * @param  string  $binarySid  the binary encoded SID
255      * @return string
256      */
257     public static function decodeSid($binarySid) 
258     {
259         if (strpos($binarySid, '-') !== false) {
260             return $binarySid;
261         }
262         
263         $sid = false; 
264         
265         $unpacked = unpack("crev/cdashes/nc/Nd/V*e", $binarySid); 
266         
267         if ($unpacked) { 
268             $n232 = pow(2,32); 
269             unset($unpacked["dashes"]); // unused 
270             $unpacked["c"] = $n232 * $unpacked["c"] + $unpacked["d"]; 
271             unset($unpacked["d"]); 
272             
273             $sid = "S";
274             
275             foreach ($unpacked as $v) { 
276                 if ($v < 0) {
277                     $v = $n232 + $v; 
278                 }
279                 $sid .= '-' . $v; 
280             } 
281         }
282          
283         return $sid; 
284     }
285 }