xref: /dokuwiki/lib/plugins/authad/adLDAP/adLDAP.php (revision 76ce1169a0c8cbb18423b1581800b9aa1050ccd5)
1*76ce1169SAndreas Gohr<?php
2*76ce1169SAndreas Gohr/**
3*76ce1169SAndreas Gohr * PHP LDAP CLASS FOR MANIPULATING ACTIVE DIRECTORY
4*76ce1169SAndreas Gohr * Version 4.0.4
5*76ce1169SAndreas Gohr *
6*76ce1169SAndreas Gohr * PHP Version 5 with SSL and LDAP support
7*76ce1169SAndreas Gohr *
8*76ce1169SAndreas Gohr * Written by Scott Barnett, Richard Hyland
9*76ce1169SAndreas Gohr *   email: scott@wiggumworld.com, adldap@richardhyland.com
10*76ce1169SAndreas Gohr *   http://adldap.sourceforge.net/
11*76ce1169SAndreas Gohr *
12*76ce1169SAndreas Gohr * Copyright (c) 2006-2012 Scott Barnett, Richard Hyland
13*76ce1169SAndreas Gohr *
14*76ce1169SAndreas Gohr * We'd appreciate any improvements or additions to be submitted back
15*76ce1169SAndreas Gohr * to benefit the entire community :)
16*76ce1169SAndreas Gohr *
17*76ce1169SAndreas Gohr * This library is free software; you can redistribute it and/or
18*76ce1169SAndreas Gohr * modify it under the terms of the GNU Lesser General Public
19*76ce1169SAndreas Gohr * License as published by the Free Software Foundation; either
20*76ce1169SAndreas Gohr * version 2.1 of the License.
21*76ce1169SAndreas Gohr *
22*76ce1169SAndreas Gohr * This library is distributed in the hope that it will be useful,
23*76ce1169SAndreas Gohr * but WITHOUT ANY WARRANTY; without even the implied warranty of
24*76ce1169SAndreas Gohr * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
25*76ce1169SAndreas Gohr * Lesser General Public License for more details.
26*76ce1169SAndreas Gohr *
27*76ce1169SAndreas Gohr * @category ToolsAndUtilities
28*76ce1169SAndreas Gohr * @package adLDAP
29*76ce1169SAndreas Gohr * @author Scott Barnett, Richard Hyland
30*76ce1169SAndreas Gohr * @copyright (c) 2006-2012 Scott Barnett, Richard Hyland
31*76ce1169SAndreas Gohr * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html LGPLv2.1
32*76ce1169SAndreas Gohr * @revision $Revision: 169 $
33*76ce1169SAndreas Gohr * @version 4.0.4
34*76ce1169SAndreas Gohr * @link http://adldap.sourceforge.net/
35*76ce1169SAndreas Gohr */
36*76ce1169SAndreas Gohr
37*76ce1169SAndreas Gohr/**
38*76ce1169SAndreas Gohr* Main adLDAP class
39*76ce1169SAndreas Gohr*
40*76ce1169SAndreas Gohr* Can be initialised using $adldap = new adLDAP();
41*76ce1169SAndreas Gohr*
42*76ce1169SAndreas Gohr* Something to keep in mind is that Active Directory is a permissions
43*76ce1169SAndreas Gohr* based directory. If you bind as a domain user, you can't fetch as
44*76ce1169SAndreas Gohr* much information on other users as you could as a domain admin.
45*76ce1169SAndreas Gohr*
46*76ce1169SAndreas Gohr* Before asking questions, please read the Documentation at
47*76ce1169SAndreas Gohr* http://adldap.sourceforge.net/wiki/doku.php?id=api
48*76ce1169SAndreas Gohr*/
49*76ce1169SAndreas Gohrrequire_once(dirname(__FILE__) . '/collections/adLDAPCollection.php');
50*76ce1169SAndreas Gohrrequire_once(dirname(__FILE__) . '/classes/adLDAPGroups.php');
51*76ce1169SAndreas Gohrrequire_once(dirname(__FILE__) . '/classes/adLDAPUsers.php');
52*76ce1169SAndreas Gohrrequire_once(dirname(__FILE__) . '/classes/adLDAPFolders.php');
53*76ce1169SAndreas Gohrrequire_once(dirname(__FILE__) . '/classes/adLDAPUtils.php');
54*76ce1169SAndreas Gohrrequire_once(dirname(__FILE__) . '/classes/adLDAPContacts.php');
55*76ce1169SAndreas Gohrrequire_once(dirname(__FILE__) . '/classes/adLDAPExchange.php');
56*76ce1169SAndreas Gohrrequire_once(dirname(__FILE__) . '/classes/adLDAPComputers.php');
57*76ce1169SAndreas Gohr
58*76ce1169SAndreas Gohrclass adLDAP {
59*76ce1169SAndreas Gohr
60*76ce1169SAndreas Gohr    /**
61*76ce1169SAndreas Gohr     * Define the different types of account in AD
62*76ce1169SAndreas Gohr     */
63*76ce1169SAndreas Gohr    const ADLDAP_NORMAL_ACCOUNT = 805306368;
64*76ce1169SAndreas Gohr    const ADLDAP_WORKSTATION_TRUST = 805306369;
65*76ce1169SAndreas Gohr    const ADLDAP_INTERDOMAIN_TRUST = 805306370;
66*76ce1169SAndreas Gohr    const ADLDAP_SECURITY_GLOBAL_GROUP = 268435456;
67*76ce1169SAndreas Gohr    const ADLDAP_DISTRIBUTION_GROUP = 268435457;
68*76ce1169SAndreas Gohr    const ADLDAP_SECURITY_LOCAL_GROUP = 536870912;
69*76ce1169SAndreas Gohr    const ADLDAP_DISTRIBUTION_LOCAL_GROUP = 536870913;
70*76ce1169SAndreas Gohr    const ADLDAP_FOLDER = 'OU';
71*76ce1169SAndreas Gohr    const ADLDAP_CONTAINER = 'CN';
72*76ce1169SAndreas Gohr
73*76ce1169SAndreas Gohr    /**
74*76ce1169SAndreas Gohr    * The default port for LDAP non-SSL connections
75*76ce1169SAndreas Gohr    */
76*76ce1169SAndreas Gohr    const ADLDAP_LDAP_PORT = '389';
77*76ce1169SAndreas Gohr    /**
78*76ce1169SAndreas Gohr    * The default port for LDAPS SSL connections
79*76ce1169SAndreas Gohr    */
80*76ce1169SAndreas Gohr    const ADLDAP_LDAPS_PORT = '636';
81*76ce1169SAndreas Gohr
82*76ce1169SAndreas Gohr    /**
83*76ce1169SAndreas Gohr    * The account suffix for your domain, can be set when the class is invoked
84*76ce1169SAndreas Gohr    *
85*76ce1169SAndreas Gohr    * @var string
86*76ce1169SAndreas Gohr    */
87*76ce1169SAndreas Gohr        protected $accountSuffix = "@mydomain.local";
88*76ce1169SAndreas Gohr
89*76ce1169SAndreas Gohr    /**
90*76ce1169SAndreas Gohr    * The base dn for your domain
91*76ce1169SAndreas Gohr    *
92*76ce1169SAndreas Gohr    * If this is set to null then adLDAP will attempt to obtain this automatically from the rootDSE
93*76ce1169SAndreas Gohr    *
94*76ce1169SAndreas Gohr    * @var string
95*76ce1169SAndreas Gohr    */
96*76ce1169SAndreas Gohr        protected $baseDn = "DC=mydomain,DC=local";
97*76ce1169SAndreas Gohr
98*76ce1169SAndreas Gohr    /**
99*76ce1169SAndreas Gohr    * Port used to talk to the domain controllers.
100*76ce1169SAndreas Gohr    *
101*76ce1169SAndreas Gohr    * @var int
102*76ce1169SAndreas Gohr    */
103*76ce1169SAndreas Gohr    protected $adPort = self::ADLDAP_LDAP_PORT;
104*76ce1169SAndreas Gohr
105*76ce1169SAndreas Gohr    /**
106*76ce1169SAndreas Gohr    * Array of domain controllers. Specifiy multiple controllers if you
107*76ce1169SAndreas Gohr    * would like the class to balance the LDAP queries amongst multiple servers
108*76ce1169SAndreas Gohr    *
109*76ce1169SAndreas Gohr    * @var array
110*76ce1169SAndreas Gohr    */
111*76ce1169SAndreas Gohr    protected $domainControllers = array("dc01.mydomain.local");
112*76ce1169SAndreas Gohr
113*76ce1169SAndreas Gohr    /**
114*76ce1169SAndreas Gohr    * Optional account with higher privileges for searching
115*76ce1169SAndreas Gohr    * This should be set to a domain admin account
116*76ce1169SAndreas Gohr    *
117*76ce1169SAndreas Gohr    * @var string
118*76ce1169SAndreas Gohr    * @var string
119*76ce1169SAndreas Gohr    */
120*76ce1169SAndreas Gohr        protected $adminUsername = NULL;
121*76ce1169SAndreas Gohr    protected $adminPassword = NULL;
122*76ce1169SAndreas Gohr
123*76ce1169SAndreas Gohr    /**
124*76ce1169SAndreas Gohr    * AD does not return the primary group. http://support.microsoft.com/?kbid=321360
125*76ce1169SAndreas Gohr    * This tweak will resolve the real primary group.
126*76ce1169SAndreas Gohr    * Setting to false will fudge "Domain Users" and is much faster. Keep in mind though that if
127*76ce1169SAndreas Gohr    * someone's primary group is NOT domain users, this is obviously going to mess up the results
128*76ce1169SAndreas Gohr    *
129*76ce1169SAndreas Gohr    * @var bool
130*76ce1169SAndreas Gohr    */
131*76ce1169SAndreas Gohr        protected $realPrimaryGroup = true;
132*76ce1169SAndreas Gohr
133*76ce1169SAndreas Gohr    /**
134*76ce1169SAndreas Gohr    * Use SSL (LDAPS), your server needs to be setup, please see
135*76ce1169SAndreas Gohr    * http://adldap.sourceforge.net/wiki/doku.php?id=ldap_over_ssl
136*76ce1169SAndreas Gohr    *
137*76ce1169SAndreas Gohr    * @var bool
138*76ce1169SAndreas Gohr    */
139*76ce1169SAndreas Gohr        protected $useSSL = false;
140*76ce1169SAndreas Gohr
141*76ce1169SAndreas Gohr    /**
142*76ce1169SAndreas Gohr    * Use TLS
143*76ce1169SAndreas Gohr    * If you wish to use TLS you should ensure that $useSSL is set to false and vice-versa
144*76ce1169SAndreas Gohr    *
145*76ce1169SAndreas Gohr    * @var bool
146*76ce1169SAndreas Gohr    */
147*76ce1169SAndreas Gohr    protected $useTLS = false;
148*76ce1169SAndreas Gohr
149*76ce1169SAndreas Gohr    /**
150*76ce1169SAndreas Gohr    * Use SSO
151*76ce1169SAndreas Gohr    * To indicate to adLDAP to reuse password set by the brower through NTLM or Kerberos
152*76ce1169SAndreas Gohr    *
153*76ce1169SAndreas Gohr    * @var bool
154*76ce1169SAndreas Gohr    */
155*76ce1169SAndreas Gohr    protected $useSSO = false;
156*76ce1169SAndreas Gohr
157*76ce1169SAndreas Gohr    /**
158*76ce1169SAndreas Gohr    * When querying group memberships, do it recursively
159*76ce1169SAndreas Gohr    * eg. User Fred is a member of Group A, which is a member of Group B, which is a member of Group C
160*76ce1169SAndreas Gohr    * user_ingroup("Fred","C") will returns true with this option turned on, false if turned off
161*76ce1169SAndreas Gohr    *
162*76ce1169SAndreas Gohr    * @var bool
163*76ce1169SAndreas Gohr    */
164*76ce1169SAndreas Gohr        protected $recursiveGroups = true;
165*76ce1169SAndreas Gohr
166*76ce1169SAndreas Gohr        // You should not need to edit anything below this line
167*76ce1169SAndreas Gohr        //******************************************************************************************
168*76ce1169SAndreas Gohr
169*76ce1169SAndreas Gohr        /**
170*76ce1169SAndreas Gohr    * Connection and bind default variables
171*76ce1169SAndreas Gohr    *
172*76ce1169SAndreas Gohr    * @var mixed
173*76ce1169SAndreas Gohr    * @var mixed
174*76ce1169SAndreas Gohr    */
175*76ce1169SAndreas Gohr        protected $ldapConnection;
176*76ce1169SAndreas Gohr        protected $ldapBind;
177*76ce1169SAndreas Gohr
178*76ce1169SAndreas Gohr    /**
179*76ce1169SAndreas Gohr    * Get the active LDAP Connection
180*76ce1169SAndreas Gohr    *
181*76ce1169SAndreas Gohr    * @return resource
182*76ce1169SAndreas Gohr    */
183*76ce1169SAndreas Gohr    public function getLdapConnection() {
184*76ce1169SAndreas Gohr        if ($this->ldapConnection) {
185*76ce1169SAndreas Gohr            return $this->ldapConnection;
186*76ce1169SAndreas Gohr        }
187*76ce1169SAndreas Gohr        return false;
188*76ce1169SAndreas Gohr    }
189*76ce1169SAndreas Gohr
190*76ce1169SAndreas Gohr    /**
191*76ce1169SAndreas Gohr    * Get the bind status
192*76ce1169SAndreas Gohr    *
193*76ce1169SAndreas Gohr    * @return bool
194*76ce1169SAndreas Gohr    */
195*76ce1169SAndreas Gohr    public function getLdapBind() {
196*76ce1169SAndreas Gohr        return $this->ldapBind;
197*76ce1169SAndreas Gohr    }
198*76ce1169SAndreas Gohr
199*76ce1169SAndreas Gohr    /**
200*76ce1169SAndreas Gohr    * Get the current base DN
201*76ce1169SAndreas Gohr    *
202*76ce1169SAndreas Gohr    * @return string
203*76ce1169SAndreas Gohr    */
204*76ce1169SAndreas Gohr    public function getBaseDn() {
205*76ce1169SAndreas Gohr        return $this->baseDn;
206*76ce1169SAndreas Gohr    }
207*76ce1169SAndreas Gohr
208*76ce1169SAndreas Gohr    /**
209*76ce1169SAndreas Gohr    * The group class
210*76ce1169SAndreas Gohr    *
211*76ce1169SAndreas Gohr    * @var adLDAPGroups
212*76ce1169SAndreas Gohr    */
213*76ce1169SAndreas Gohr    protected $groupClass;
214*76ce1169SAndreas Gohr
215*76ce1169SAndreas Gohr    /**
216*76ce1169SAndreas Gohr    * Get the group class interface
217*76ce1169SAndreas Gohr    *
218*76ce1169SAndreas Gohr    * @return adLDAPGroups
219*76ce1169SAndreas Gohr    */
220*76ce1169SAndreas Gohr    public function group() {
221*76ce1169SAndreas Gohr        if (!$this->groupClass) {
222*76ce1169SAndreas Gohr            $this->groupClass = new adLDAPGroups($this);
223*76ce1169SAndreas Gohr        }
224*76ce1169SAndreas Gohr        return $this->groupClass;
225*76ce1169SAndreas Gohr    }
226*76ce1169SAndreas Gohr
227*76ce1169SAndreas Gohr    /**
228*76ce1169SAndreas Gohr    * The user class
229*76ce1169SAndreas Gohr    *
230*76ce1169SAndreas Gohr    * @var adLDAPUsers
231*76ce1169SAndreas Gohr    */
232*76ce1169SAndreas Gohr    protected $userClass;
233*76ce1169SAndreas Gohr
234*76ce1169SAndreas Gohr    /**
235*76ce1169SAndreas Gohr    * Get the userclass interface
236*76ce1169SAndreas Gohr    *
237*76ce1169SAndreas Gohr    * @return adLDAPUsers
238*76ce1169SAndreas Gohr    */
239*76ce1169SAndreas Gohr    public function user() {
240*76ce1169SAndreas Gohr        if (!$this->userClass) {
241*76ce1169SAndreas Gohr            $this->userClass = new adLDAPUsers($this);
242*76ce1169SAndreas Gohr        }
243*76ce1169SAndreas Gohr        return $this->userClass;
244*76ce1169SAndreas Gohr    }
245*76ce1169SAndreas Gohr
246*76ce1169SAndreas Gohr    /**
247*76ce1169SAndreas Gohr    * The folders class
248*76ce1169SAndreas Gohr    *
249*76ce1169SAndreas Gohr    * @var adLDAPFolders
250*76ce1169SAndreas Gohr    */
251*76ce1169SAndreas Gohr    protected $folderClass;
252*76ce1169SAndreas Gohr
253*76ce1169SAndreas Gohr    /**
254*76ce1169SAndreas Gohr    * Get the folder class interface
255*76ce1169SAndreas Gohr    *
256*76ce1169SAndreas Gohr    * @return adLDAPFolders
257*76ce1169SAndreas Gohr    */
258*76ce1169SAndreas Gohr    public function folder() {
259*76ce1169SAndreas Gohr        if (!$this->folderClass) {
260*76ce1169SAndreas Gohr            $this->folderClass = new adLDAPFolders($this);
261*76ce1169SAndreas Gohr        }
262*76ce1169SAndreas Gohr        return $this->folderClass;
263*76ce1169SAndreas Gohr    }
264*76ce1169SAndreas Gohr
265*76ce1169SAndreas Gohr    /**
266*76ce1169SAndreas Gohr    * The utils class
267*76ce1169SAndreas Gohr    *
268*76ce1169SAndreas Gohr    * @var adLDAPUtils
269*76ce1169SAndreas Gohr    */
270*76ce1169SAndreas Gohr    protected $utilClass;
271*76ce1169SAndreas Gohr
272*76ce1169SAndreas Gohr    /**
273*76ce1169SAndreas Gohr    * Get the utils class interface
274*76ce1169SAndreas Gohr    *
275*76ce1169SAndreas Gohr    * @return adLDAPUtils
276*76ce1169SAndreas Gohr    */
277*76ce1169SAndreas Gohr    public function utilities() {
278*76ce1169SAndreas Gohr        if (!$this->utilClass) {
279*76ce1169SAndreas Gohr            $this->utilClass = new adLDAPUtils($this);
280*76ce1169SAndreas Gohr        }
281*76ce1169SAndreas Gohr        return $this->utilClass;
282*76ce1169SAndreas Gohr    }
283*76ce1169SAndreas Gohr
284*76ce1169SAndreas Gohr    /**
285*76ce1169SAndreas Gohr    * The contacts class
286*76ce1169SAndreas Gohr    *
287*76ce1169SAndreas Gohr    * @var adLDAPContacts
288*76ce1169SAndreas Gohr    */
289*76ce1169SAndreas Gohr    protected $contactClass;
290*76ce1169SAndreas Gohr
291*76ce1169SAndreas Gohr    /**
292*76ce1169SAndreas Gohr    * Get the contacts class interface
293*76ce1169SAndreas Gohr    *
294*76ce1169SAndreas Gohr    * @return adLDAPContacts
295*76ce1169SAndreas Gohr    */
296*76ce1169SAndreas Gohr    public function contact() {
297*76ce1169SAndreas Gohr        if (!$this->contactClass) {
298*76ce1169SAndreas Gohr            $this->contactClass = new adLDAPContacts($this);
299*76ce1169SAndreas Gohr        }
300*76ce1169SAndreas Gohr        return $this->contactClass;
301*76ce1169SAndreas Gohr    }
302*76ce1169SAndreas Gohr
303*76ce1169SAndreas Gohr    /**
304*76ce1169SAndreas Gohr    * The exchange class
305*76ce1169SAndreas Gohr    *
306*76ce1169SAndreas Gohr    * @var adLDAPExchange
307*76ce1169SAndreas Gohr    */
308*76ce1169SAndreas Gohr    protected $exchangeClass;
309*76ce1169SAndreas Gohr
310*76ce1169SAndreas Gohr    /**
311*76ce1169SAndreas Gohr    * Get the exchange class interface
312*76ce1169SAndreas Gohr    *
313*76ce1169SAndreas Gohr    * @return adLDAPExchange
314*76ce1169SAndreas Gohr    */
315*76ce1169SAndreas Gohr    public function exchange() {
316*76ce1169SAndreas Gohr        if (!$this->exchangeClass) {
317*76ce1169SAndreas Gohr            $this->exchangeClass = new adLDAPExchange($this);
318*76ce1169SAndreas Gohr        }
319*76ce1169SAndreas Gohr        return $this->exchangeClass;
320*76ce1169SAndreas Gohr    }
321*76ce1169SAndreas Gohr
322*76ce1169SAndreas Gohr    /**
323*76ce1169SAndreas Gohr    * The computers class
324*76ce1169SAndreas Gohr    *
325*76ce1169SAndreas Gohr    * @var adLDAPComputers
326*76ce1169SAndreas Gohr    */
327*76ce1169SAndreas Gohr    protected $computersClass;
328*76ce1169SAndreas Gohr
329*76ce1169SAndreas Gohr    /**
330*76ce1169SAndreas Gohr    * Get the computers class interface
331*76ce1169SAndreas Gohr    *
332*76ce1169SAndreas Gohr    * @return adLDAPComputers
333*76ce1169SAndreas Gohr    */
334*76ce1169SAndreas Gohr    public function computer() {
335*76ce1169SAndreas Gohr        if (!$this->computerClass) {
336*76ce1169SAndreas Gohr            $this->computerClass = new adLDAPComputers($this);
337*76ce1169SAndreas Gohr        }
338*76ce1169SAndreas Gohr        return $this->computerClass;
339*76ce1169SAndreas Gohr    }
340*76ce1169SAndreas Gohr
341*76ce1169SAndreas Gohr    /**
342*76ce1169SAndreas Gohr    * Getters and Setters
343*76ce1169SAndreas Gohr    */
344*76ce1169SAndreas Gohr
345*76ce1169SAndreas Gohr    /**
346*76ce1169SAndreas Gohr    * Set the account suffix
347*76ce1169SAndreas Gohr    *
348*76ce1169SAndreas Gohr    * @param string $accountSuffix
349*76ce1169SAndreas Gohr    * @return void
350*76ce1169SAndreas Gohr    */
351*76ce1169SAndreas Gohr    public function setAccountSuffix($accountSuffix)
352*76ce1169SAndreas Gohr    {
353*76ce1169SAndreas Gohr          $this->accountSuffix = $accountSuffix;
354*76ce1169SAndreas Gohr    }
355*76ce1169SAndreas Gohr
356*76ce1169SAndreas Gohr    /**
357*76ce1169SAndreas Gohr    * Get the account suffix
358*76ce1169SAndreas Gohr    *
359*76ce1169SAndreas Gohr    * @return string
360*76ce1169SAndreas Gohr    */
361*76ce1169SAndreas Gohr    public function getAccountSuffix()
362*76ce1169SAndreas Gohr    {
363*76ce1169SAndreas Gohr          return $this->accountSuffix;
364*76ce1169SAndreas Gohr    }
365*76ce1169SAndreas Gohr
366*76ce1169SAndreas Gohr    /**
367*76ce1169SAndreas Gohr    * Set the domain controllers array
368*76ce1169SAndreas Gohr    *
369*76ce1169SAndreas Gohr    * @param array $domainControllers
370*76ce1169SAndreas Gohr    * @return void
371*76ce1169SAndreas Gohr    */
372*76ce1169SAndreas Gohr    public function setDomainControllers(array $domainControllers)
373*76ce1169SAndreas Gohr    {
374*76ce1169SAndreas Gohr          $this->domainControllers = $domainControllers;
375*76ce1169SAndreas Gohr    }
376*76ce1169SAndreas Gohr
377*76ce1169SAndreas Gohr    /**
378*76ce1169SAndreas Gohr    * Get the list of domain controllers
379*76ce1169SAndreas Gohr    *
380*76ce1169SAndreas Gohr    * @return void
381*76ce1169SAndreas Gohr    */
382*76ce1169SAndreas Gohr    public function getDomainControllers()
383*76ce1169SAndreas Gohr    {
384*76ce1169SAndreas Gohr          return $this->domainControllers;
385*76ce1169SAndreas Gohr    }
386*76ce1169SAndreas Gohr
387*76ce1169SAndreas Gohr    /**
388*76ce1169SAndreas Gohr    * Sets the port number your domain controller communicates over
389*76ce1169SAndreas Gohr    *
390*76ce1169SAndreas Gohr    * @param int $adPort
391*76ce1169SAndreas Gohr    */
392*76ce1169SAndreas Gohr    public function setPort($adPort)
393*76ce1169SAndreas Gohr    {
394*76ce1169SAndreas Gohr        $this->adPort = $adPort;
395*76ce1169SAndreas Gohr    }
396*76ce1169SAndreas Gohr
397*76ce1169SAndreas Gohr    /**
398*76ce1169SAndreas Gohr    * Gets the port number your domain controller communicates over
399*76ce1169SAndreas Gohr    *
400*76ce1169SAndreas Gohr    * @return int
401*76ce1169SAndreas Gohr    */
402*76ce1169SAndreas Gohr    public function getPort()
403*76ce1169SAndreas Gohr    {
404*76ce1169SAndreas Gohr        return $this->adPort;
405*76ce1169SAndreas Gohr    }
406*76ce1169SAndreas Gohr
407*76ce1169SAndreas Gohr    /**
408*76ce1169SAndreas Gohr    * Set the username of an account with higher priviledges
409*76ce1169SAndreas Gohr    *
410*76ce1169SAndreas Gohr    * @param string $adminUsername
411*76ce1169SAndreas Gohr    * @return void
412*76ce1169SAndreas Gohr    */
413*76ce1169SAndreas Gohr    public function setAdminUsername($adminUsername)
414*76ce1169SAndreas Gohr    {
415*76ce1169SAndreas Gohr          $this->adminUsername = $adminUsername;
416*76ce1169SAndreas Gohr    }
417*76ce1169SAndreas Gohr
418*76ce1169SAndreas Gohr    /**
419*76ce1169SAndreas Gohr    * Get the username of the account with higher priviledges
420*76ce1169SAndreas Gohr    *
421*76ce1169SAndreas Gohr    * This will throw an exception for security reasons
422*76ce1169SAndreas Gohr    */
423*76ce1169SAndreas Gohr    public function getAdminUsername()
424*76ce1169SAndreas Gohr    {
425*76ce1169SAndreas Gohr          throw new adLDAPException('For security reasons you cannot access the domain administrator account details');
426*76ce1169SAndreas Gohr    }
427*76ce1169SAndreas Gohr
428*76ce1169SAndreas Gohr    /**
429*76ce1169SAndreas Gohr    * Set the password of an account with higher priviledges
430*76ce1169SAndreas Gohr    *
431*76ce1169SAndreas Gohr    * @param string $adminPassword
432*76ce1169SAndreas Gohr    * @return void
433*76ce1169SAndreas Gohr    */
434*76ce1169SAndreas Gohr    public function setAdminPassword($adminPassword)
435*76ce1169SAndreas Gohr    {
436*76ce1169SAndreas Gohr          $this->adminPassword = $adminPassword;
437*76ce1169SAndreas Gohr    }
438*76ce1169SAndreas Gohr
439*76ce1169SAndreas Gohr    /**
440*76ce1169SAndreas Gohr    * Get the password of the account with higher priviledges
441*76ce1169SAndreas Gohr    *
442*76ce1169SAndreas Gohr    * This will throw an exception for security reasons
443*76ce1169SAndreas Gohr    */
444*76ce1169SAndreas Gohr    public function getAdminPassword()
445*76ce1169SAndreas Gohr    {
446*76ce1169SAndreas Gohr          throw new adLDAPException('For security reasons you cannot access the domain administrator account details');
447*76ce1169SAndreas Gohr    }
448*76ce1169SAndreas Gohr
449*76ce1169SAndreas Gohr    /**
450*76ce1169SAndreas Gohr    * Set whether to detect the true primary group
451*76ce1169SAndreas Gohr    *
452*76ce1169SAndreas Gohr    * @param bool $realPrimaryGroup
453*76ce1169SAndreas Gohr    * @return void
454*76ce1169SAndreas Gohr    */
455*76ce1169SAndreas Gohr    public function setRealPrimaryGroup($realPrimaryGroup)
456*76ce1169SAndreas Gohr    {
457*76ce1169SAndreas Gohr          $this->realPrimaryGroup = $realPrimaryGroup;
458*76ce1169SAndreas Gohr    }
459*76ce1169SAndreas Gohr
460*76ce1169SAndreas Gohr    /**
461*76ce1169SAndreas Gohr    * Get the real primary group setting
462*76ce1169SAndreas Gohr    *
463*76ce1169SAndreas Gohr    * @return bool
464*76ce1169SAndreas Gohr    */
465*76ce1169SAndreas Gohr    public function getRealPrimaryGroup()
466*76ce1169SAndreas Gohr    {
467*76ce1169SAndreas Gohr          return $this->realPrimaryGroup;
468*76ce1169SAndreas Gohr    }
469*76ce1169SAndreas Gohr
470*76ce1169SAndreas Gohr    /**
471*76ce1169SAndreas Gohr    * Set whether to use SSL
472*76ce1169SAndreas Gohr    *
473*76ce1169SAndreas Gohr    * @param bool $useSSL
474*76ce1169SAndreas Gohr    * @return void
475*76ce1169SAndreas Gohr    */
476*76ce1169SAndreas Gohr    public function setUseSSL($useSSL)
477*76ce1169SAndreas Gohr    {
478*76ce1169SAndreas Gohr          $this->useSSL = $useSSL;
479*76ce1169SAndreas Gohr          // Set the default port correctly
480*76ce1169SAndreas Gohr          if($this->useSSL) {
481*76ce1169SAndreas Gohr            $this->setPort(self::ADLDAP_LDAPS_PORT);
482*76ce1169SAndreas Gohr          }
483*76ce1169SAndreas Gohr          else {
484*76ce1169SAndreas Gohr            $this->setPort(self::ADLDAP_LDAP_PORT);
485*76ce1169SAndreas Gohr          }
486*76ce1169SAndreas Gohr    }
487*76ce1169SAndreas Gohr
488*76ce1169SAndreas Gohr    /**
489*76ce1169SAndreas Gohr    * Get the SSL setting
490*76ce1169SAndreas Gohr    *
491*76ce1169SAndreas Gohr    * @return bool
492*76ce1169SAndreas Gohr    */
493*76ce1169SAndreas Gohr    public function getUseSSL()
494*76ce1169SAndreas Gohr    {
495*76ce1169SAndreas Gohr          return $this->useSSL;
496*76ce1169SAndreas Gohr    }
497*76ce1169SAndreas Gohr
498*76ce1169SAndreas Gohr    /**
499*76ce1169SAndreas Gohr    * Set whether to use TLS
500*76ce1169SAndreas Gohr    *
501*76ce1169SAndreas Gohr    * @param bool $useTLS
502*76ce1169SAndreas Gohr    * @return void
503*76ce1169SAndreas Gohr    */
504*76ce1169SAndreas Gohr    public function setUseTLS($useTLS)
505*76ce1169SAndreas Gohr    {
506*76ce1169SAndreas Gohr          $this->useTLS = $useTLS;
507*76ce1169SAndreas Gohr    }
508*76ce1169SAndreas Gohr
509*76ce1169SAndreas Gohr    /**
510*76ce1169SAndreas Gohr    * Get the TLS setting
511*76ce1169SAndreas Gohr    *
512*76ce1169SAndreas Gohr    * @return bool
513*76ce1169SAndreas Gohr    */
514*76ce1169SAndreas Gohr    public function getUseTLS()
515*76ce1169SAndreas Gohr    {
516*76ce1169SAndreas Gohr          return $this->useTLS;
517*76ce1169SAndreas Gohr    }
518*76ce1169SAndreas Gohr
519*76ce1169SAndreas Gohr    /**
520*76ce1169SAndreas Gohr    * Set whether to use SSO
521*76ce1169SAndreas Gohr    * Requires ldap_sasl_bind support. Be sure --with-ldap-sasl is used when configuring PHP otherwise this function will be undefined.
522*76ce1169SAndreas Gohr    *
523*76ce1169SAndreas Gohr    * @param bool $useSSO
524*76ce1169SAndreas Gohr    * @return void
525*76ce1169SAndreas Gohr    */
526*76ce1169SAndreas Gohr    public function setUseSSO($useSSO)
527*76ce1169SAndreas Gohr    {
528*76ce1169SAndreas Gohr          if ($useSSO === true && !$this->ldapSaslSupported()) {
529*76ce1169SAndreas Gohr              throw new adLDAPException('No LDAP SASL support for PHP.  See: http://www.php.net/ldap_sasl_bind');
530*76ce1169SAndreas Gohr          }
531*76ce1169SAndreas Gohr          $this->useSSO = $useSSO;
532*76ce1169SAndreas Gohr    }
533*76ce1169SAndreas Gohr
534*76ce1169SAndreas Gohr    /**
535*76ce1169SAndreas Gohr    * Get the SSO setting
536*76ce1169SAndreas Gohr    *
537*76ce1169SAndreas Gohr    * @return bool
538*76ce1169SAndreas Gohr    */
539*76ce1169SAndreas Gohr    public function getUseSSO()
540*76ce1169SAndreas Gohr    {
541*76ce1169SAndreas Gohr          return $this->useSSO;
542*76ce1169SAndreas Gohr    }
543*76ce1169SAndreas Gohr
544*76ce1169SAndreas Gohr    /**
545*76ce1169SAndreas Gohr    * Set whether to lookup recursive groups
546*76ce1169SAndreas Gohr    *
547*76ce1169SAndreas Gohr    * @param bool $recursiveGroups
548*76ce1169SAndreas Gohr    * @return void
549*76ce1169SAndreas Gohr    */
550*76ce1169SAndreas Gohr    public function setRecursiveGroups($recursiveGroups)
551*76ce1169SAndreas Gohr    {
552*76ce1169SAndreas Gohr          $this->recursiveGroups = $recursiveGroups;
553*76ce1169SAndreas Gohr    }
554*76ce1169SAndreas Gohr
555*76ce1169SAndreas Gohr    /**
556*76ce1169SAndreas Gohr    * Get the recursive groups setting
557*76ce1169SAndreas Gohr    *
558*76ce1169SAndreas Gohr    * @return bool
559*76ce1169SAndreas Gohr    */
560*76ce1169SAndreas Gohr    public function getRecursiveGroups()
561*76ce1169SAndreas Gohr    {
562*76ce1169SAndreas Gohr          return $this->recursiveGroups;
563*76ce1169SAndreas Gohr    }
564*76ce1169SAndreas Gohr
565*76ce1169SAndreas Gohr    /**
566*76ce1169SAndreas Gohr    * Default Constructor
567*76ce1169SAndreas Gohr    *
568*76ce1169SAndreas Gohr    * Tries to bind to the AD domain over LDAP or LDAPs
569*76ce1169SAndreas Gohr    *
570*76ce1169SAndreas Gohr    * @param array $options Array of options to pass to the constructor
571*76ce1169SAndreas Gohr    * @throws Exception - if unable to bind to Domain Controller
572*76ce1169SAndreas Gohr    * @return bool
573*76ce1169SAndreas Gohr    */
574*76ce1169SAndreas Gohr    function __construct($options = array()) {
575*76ce1169SAndreas Gohr        // You can specifically overide any of the default configuration options setup above
576*76ce1169SAndreas Gohr        if (count($options) > 0) {
577*76ce1169SAndreas Gohr            if (array_key_exists("account_suffix",$options)){ $this->accountSuffix = $options["account_suffix"]; }
578*76ce1169SAndreas Gohr            if (array_key_exists("base_dn",$options)){ $this->baseDn = $options["base_dn"]; }
579*76ce1169SAndreas Gohr            if (array_key_exists("domain_controllers",$options)){
580*76ce1169SAndreas Gohr                if (!is_array($options["domain_controllers"])) {
581*76ce1169SAndreas Gohr                    throw new adLDAPException('[domain_controllers] option must be an array');
582*76ce1169SAndreas Gohr                }
583*76ce1169SAndreas Gohr                $this->domainControllers = $options["domain_controllers"];
584*76ce1169SAndreas Gohr            }
585*76ce1169SAndreas Gohr            if (array_key_exists("admin_username",$options)){ $this->adminUsername = $options["admin_username"]; }
586*76ce1169SAndreas Gohr            if (array_key_exists("admin_password",$options)){ $this->adminPassword = $options["admin_password"]; }
587*76ce1169SAndreas Gohr            if (array_key_exists("real_primarygroup",$options)){ $this->realPrimaryGroup = $options["real_primarygroup"]; }
588*76ce1169SAndreas Gohr            if (array_key_exists("use_ssl",$options)){ $this->setUseSSL($options["use_ssl"]); }
589*76ce1169SAndreas Gohr            if (array_key_exists("use_tls",$options)){ $this->useTLS = $options["use_tls"]; }
590*76ce1169SAndreas Gohr            if (array_key_exists("recursive_groups",$options)){ $this->recursiveGroups = $options["recursive_groups"]; }
591*76ce1169SAndreas Gohr            if (array_key_exists("ad_port",$options)){ $this->setPort($options["ad_port"]); }
592*76ce1169SAndreas Gohr            if (array_key_exists("sso",$options)) {
593*76ce1169SAndreas Gohr                $this->setUseSSO($options["sso"]);
594*76ce1169SAndreas Gohr                if (!$this->ldapSaslSupported()) {
595*76ce1169SAndreas Gohr                    $this->setUseSSO(false);
596*76ce1169SAndreas Gohr                }
597*76ce1169SAndreas Gohr            }
598*76ce1169SAndreas Gohr        }
599*76ce1169SAndreas Gohr
600*76ce1169SAndreas Gohr        if ($this->ldapSupported() === false) {
601*76ce1169SAndreas Gohr            throw new adLDAPException('No LDAP support for PHP.  See: http://www.php.net/ldap');
602*76ce1169SAndreas Gohr        }
603*76ce1169SAndreas Gohr
604*76ce1169SAndreas Gohr        return $this->connect();
605*76ce1169SAndreas Gohr    }
606*76ce1169SAndreas Gohr
607*76ce1169SAndreas Gohr    /**
608*76ce1169SAndreas Gohr    * Default Destructor
609*76ce1169SAndreas Gohr    *
610*76ce1169SAndreas Gohr    * Closes the LDAP connection
611*76ce1169SAndreas Gohr    *
612*76ce1169SAndreas Gohr    * @return void
613*76ce1169SAndreas Gohr    */
614*76ce1169SAndreas Gohr    function __destruct() {
615*76ce1169SAndreas Gohr        $this->close();
616*76ce1169SAndreas Gohr    }
617*76ce1169SAndreas Gohr
618*76ce1169SAndreas Gohr    /**
619*76ce1169SAndreas Gohr    * Connects and Binds to the Domain Controller
620*76ce1169SAndreas Gohr    *
621*76ce1169SAndreas Gohr    * @return bool
622*76ce1169SAndreas Gohr    */
623*76ce1169SAndreas Gohr    public function connect()
624*76ce1169SAndreas Gohr    {
625*76ce1169SAndreas Gohr        // Connect to the AD/LDAP server as the username/password
626*76ce1169SAndreas Gohr        $domainController = $this->randomController();
627*76ce1169SAndreas Gohr        if ($this->useSSL) {
628*76ce1169SAndreas Gohr            $this->ldapConnection = ldap_connect("ldaps://" . $domainController, $this->adPort);
629*76ce1169SAndreas Gohr        } else {
630*76ce1169SAndreas Gohr            $this->ldapConnection = ldap_connect($domainController, $this->adPort);
631*76ce1169SAndreas Gohr        }
632*76ce1169SAndreas Gohr
633*76ce1169SAndreas Gohr        // Set some ldap options for talking to AD
634*76ce1169SAndreas Gohr        ldap_set_option($this->ldapConnection, LDAP_OPT_PROTOCOL_VERSION, 3);
635*76ce1169SAndreas Gohr        ldap_set_option($this->ldapConnection, LDAP_OPT_REFERRALS, 0);
636*76ce1169SAndreas Gohr
637*76ce1169SAndreas Gohr        if ($this->useTLS) {
638*76ce1169SAndreas Gohr            ldap_start_tls($this->ldapConnection);
639*76ce1169SAndreas Gohr        }
640*76ce1169SAndreas Gohr
641*76ce1169SAndreas Gohr        // Bind as a domain admin if they've set it up
642*76ce1169SAndreas Gohr        if ($this->adminUsername !== NULL && $this->adminPassword !== NULL) {
643*76ce1169SAndreas Gohr            $this->ldapBind = @ldap_bind($this->ldapConnection, $this->adminUsername . $this->accountSuffix, $this->adminPassword);
644*76ce1169SAndreas Gohr            if (!$this->ldapBind) {
645*76ce1169SAndreas Gohr                if ($this->useSSL && !$this->useTLS) {
646*76ce1169SAndreas Gohr                    // If you have problems troubleshooting, remove the @ character from the ldapldapBind command above to get the actual error message
647*76ce1169SAndreas Gohr                    throw new adLDAPException('Bind to Active Directory failed. Either the LDAPs connection failed or the login credentials are incorrect. AD said: ' . $this->getLastError());
648*76ce1169SAndreas Gohr                }
649*76ce1169SAndreas Gohr                else {
650*76ce1169SAndreas Gohr                    throw new adLDAPException('Bind to Active Directory failed. Check the login credentials and/or server details. AD said: ' . $this->getLastError());
651*76ce1169SAndreas Gohr                }
652*76ce1169SAndreas Gohr            }
653*76ce1169SAndreas Gohr        }
654*76ce1169SAndreas Gohr        if ($this->useSSO && $_SERVER['REMOTE_USER'] && $this->adminUsername === null && $_SERVER['KRB5CCNAME']) {
655*76ce1169SAndreas Gohr            putenv("KRB5CCNAME=" . $_SERVER['KRB5CCNAME']);
656*76ce1169SAndreas Gohr            $this->ldapBind = @ldap_sasl_bind($this->ldapConnection, NULL, NULL, "GSSAPI");
657*76ce1169SAndreas Gohr            if (!$this->ldapBind){
658*76ce1169SAndreas Gohr                throw new adLDAPException('Rebind to Active Directory failed. AD said: ' . $this->getLastError());
659*76ce1169SAndreas Gohr            }
660*76ce1169SAndreas Gohr            else {
661*76ce1169SAndreas Gohr                return true;
662*76ce1169SAndreas Gohr            }
663*76ce1169SAndreas Gohr        }
664*76ce1169SAndreas Gohr
665*76ce1169SAndreas Gohr
666*76ce1169SAndreas Gohr        if ($this->baseDn == NULL) {
667*76ce1169SAndreas Gohr            $this->baseDn = $this->findBaseDn();
668*76ce1169SAndreas Gohr        }
669*76ce1169SAndreas Gohr
670*76ce1169SAndreas Gohr        return true;
671*76ce1169SAndreas Gohr    }
672*76ce1169SAndreas Gohr
673*76ce1169SAndreas Gohr    /**
674*76ce1169SAndreas Gohr    * Closes the LDAP connection
675*76ce1169SAndreas Gohr    *
676*76ce1169SAndreas Gohr    * @return void
677*76ce1169SAndreas Gohr    */
678*76ce1169SAndreas Gohr    public function close() {
679*76ce1169SAndreas Gohr        if ($this->ldapConnection) {
680*76ce1169SAndreas Gohr            @ldap_close($this->ldapConnection);
681*76ce1169SAndreas Gohr        }
682*76ce1169SAndreas Gohr    }
683*76ce1169SAndreas Gohr
684*76ce1169SAndreas Gohr    /**
685*76ce1169SAndreas Gohr    * Validate a user's login credentials
686*76ce1169SAndreas Gohr    *
687*76ce1169SAndreas Gohr    * @param string $username A user's AD username
688*76ce1169SAndreas Gohr    * @param string $password A user's AD password
689*76ce1169SAndreas Gohr    * @param bool optional $preventRebind
690*76ce1169SAndreas Gohr    * @return bool
691*76ce1169SAndreas Gohr    */
692*76ce1169SAndreas Gohr    public function authenticate($username, $password, $preventRebind = false) {
693*76ce1169SAndreas Gohr        // Prevent null binding
694*76ce1169SAndreas Gohr        if ($username === NULL || $password === NULL) { return false; }
695*76ce1169SAndreas Gohr        if (empty($username) || empty($password)) { return false; }
696*76ce1169SAndreas Gohr
697*76ce1169SAndreas Gohr        // Allow binding over SSO for Kerberos
698*76ce1169SAndreas Gohr        if ($this->useSSO && $_SERVER['REMOTE_USER'] && $_SERVER['REMOTE_USER'] == $username && $this->adminUsername === NULL && $_SERVER['KRB5CCNAME']) {
699*76ce1169SAndreas Gohr            putenv("KRB5CCNAME=" . $_SERVER['KRB5CCNAME']);
700*76ce1169SAndreas Gohr            $this->ldapBind = @ldap_sasl_bind($this->ldapConnection, NULL, NULL, "GSSAPI");
701*76ce1169SAndreas Gohr            if (!$this->ldapBind) {
702*76ce1169SAndreas Gohr                throw new adLDAPException('Rebind to Active Directory failed. AD said: ' . $this->getLastError());
703*76ce1169SAndreas Gohr            }
704*76ce1169SAndreas Gohr            else {
705*76ce1169SAndreas Gohr                return true;
706*76ce1169SAndreas Gohr            }
707*76ce1169SAndreas Gohr        }
708*76ce1169SAndreas Gohr
709*76ce1169SAndreas Gohr        // Bind as the user
710*76ce1169SAndreas Gohr        $ret = true;
711*76ce1169SAndreas Gohr        $this->ldapBind = @ldap_bind($this->ldapConnection, $username . $this->accountSuffix, $password);
712*76ce1169SAndreas Gohr        if (!$this->ldapBind){
713*76ce1169SAndreas Gohr            $ret = false;
714*76ce1169SAndreas Gohr        }
715*76ce1169SAndreas Gohr
716*76ce1169SAndreas Gohr        // Cnce we've checked their details, kick back into admin mode if we have it
717*76ce1169SAndreas Gohr        if ($this->adminUsername !== NULL && !$preventRebind) {
718*76ce1169SAndreas Gohr            $this->ldapBind = @ldap_bind($this->ldapConnection, $this->adminUsername . $this->accountSuffix , $this->adminPassword);
719*76ce1169SAndreas Gohr            if (!$this->ldapBind){
720*76ce1169SAndreas Gohr                // This should never happen in theory
721*76ce1169SAndreas Gohr                throw new adLDAPException('Rebind to Active Directory failed. AD said: ' . $this->getLastError());
722*76ce1169SAndreas Gohr            }
723*76ce1169SAndreas Gohr        }
724*76ce1169SAndreas Gohr
725*76ce1169SAndreas Gohr        return $ret;
726*76ce1169SAndreas Gohr    }
727*76ce1169SAndreas Gohr
728*76ce1169SAndreas Gohr    /**
729*76ce1169SAndreas Gohr    * Find the Base DN of your domain controller
730*76ce1169SAndreas Gohr    *
731*76ce1169SAndreas Gohr    * @return string
732*76ce1169SAndreas Gohr    */
733*76ce1169SAndreas Gohr    public function findBaseDn()
734*76ce1169SAndreas Gohr    {
735*76ce1169SAndreas Gohr        $namingContext = $this->getRootDse(array('defaultnamingcontext'));
736*76ce1169SAndreas Gohr        return $namingContext[0]['defaultnamingcontext'][0];
737*76ce1169SAndreas Gohr    }
738*76ce1169SAndreas Gohr
739*76ce1169SAndreas Gohr    /**
740*76ce1169SAndreas Gohr    * Get the RootDSE properties from a domain controller
741*76ce1169SAndreas Gohr    *
742*76ce1169SAndreas Gohr    * @param array $attributes The attributes you wish to query e.g. defaultnamingcontext
743*76ce1169SAndreas Gohr    * @return array
744*76ce1169SAndreas Gohr    */
745*76ce1169SAndreas Gohr    public function getRootDse($attributes = array("*", "+")) {
746*76ce1169SAndreas Gohr        if (!$this->ldapBind){ return (false); }
747*76ce1169SAndreas Gohr
748*76ce1169SAndreas Gohr        $sr = @ldap_read($this->ldapConnection, NULL, 'objectClass=*', $attributes);
749*76ce1169SAndreas Gohr        $entries = @ldap_get_entries($this->ldapConnection, $sr);
750*76ce1169SAndreas Gohr        return $entries;
751*76ce1169SAndreas Gohr    }
752*76ce1169SAndreas Gohr
753*76ce1169SAndreas Gohr    /**
754*76ce1169SAndreas Gohr    * Get last error from Active Directory
755*76ce1169SAndreas Gohr    *
756*76ce1169SAndreas Gohr    * This function gets the last message from Active Directory
757*76ce1169SAndreas Gohr    * This may indeed be a 'Success' message but if you get an unknown error
758*76ce1169SAndreas Gohr    * it might be worth calling this function to see what errors were raised
759*76ce1169SAndreas Gohr    *
760*76ce1169SAndreas Gohr    * return string
761*76ce1169SAndreas Gohr    */
762*76ce1169SAndreas Gohr    public function getLastError() {
763*76ce1169SAndreas Gohr        return @ldap_error($this->ldapConnection);
764*76ce1169SAndreas Gohr    }
765*76ce1169SAndreas Gohr
766*76ce1169SAndreas Gohr    /**
767*76ce1169SAndreas Gohr    * Detect LDAP support in php
768*76ce1169SAndreas Gohr    *
769*76ce1169SAndreas Gohr    * @return bool
770*76ce1169SAndreas Gohr    */
771*76ce1169SAndreas Gohr    protected function ldapSupported()
772*76ce1169SAndreas Gohr    {
773*76ce1169SAndreas Gohr        if (!function_exists('ldap_connect')) {
774*76ce1169SAndreas Gohr            return false;
775*76ce1169SAndreas Gohr        }
776*76ce1169SAndreas Gohr        return true;
777*76ce1169SAndreas Gohr    }
778*76ce1169SAndreas Gohr
779*76ce1169SAndreas Gohr    /**
780*76ce1169SAndreas Gohr    * Detect ldap_sasl_bind support in PHP
781*76ce1169SAndreas Gohr    *
782*76ce1169SAndreas Gohr    * @return bool
783*76ce1169SAndreas Gohr    */
784*76ce1169SAndreas Gohr    protected function ldapSaslSupported()
785*76ce1169SAndreas Gohr    {
786*76ce1169SAndreas Gohr        if (!function_exists('ldap_sasl_bind')) {
787*76ce1169SAndreas Gohr            return false;
788*76ce1169SAndreas Gohr        }
789*76ce1169SAndreas Gohr        return true;
790*76ce1169SAndreas Gohr    }
791*76ce1169SAndreas Gohr
792*76ce1169SAndreas Gohr    /**
793*76ce1169SAndreas Gohr    * Schema
794*76ce1169SAndreas Gohr    *
795*76ce1169SAndreas Gohr    * @param array $attributes Attributes to be queried
796*76ce1169SAndreas Gohr    * @return array
797*76ce1169SAndreas Gohr    */
798*76ce1169SAndreas Gohr    public function adldap_schema($attributes){
799*76ce1169SAndreas Gohr
800*76ce1169SAndreas Gohr        // LDAP doesn't like NULL attributes, only set them if they have values
801*76ce1169SAndreas Gohr        // If you wish to remove an attribute you should set it to a space
802*76ce1169SAndreas Gohr        // TO DO: Adapt user_modify to use ldap_mod_delete to remove a NULL attribute
803*76ce1169SAndreas Gohr        $mod=array();
804*76ce1169SAndreas Gohr
805*76ce1169SAndreas Gohr        // Check every attribute to see if it contains 8bit characters and then UTF8 encode them
806*76ce1169SAndreas Gohr        array_walk($attributes, array($this, 'encode8bit'));
807*76ce1169SAndreas Gohr
808*76ce1169SAndreas Gohr        if ($attributes["address_city"]){ $mod["l"][0]=$attributes["address_city"]; }
809*76ce1169SAndreas Gohr        if ($attributes["address_code"]){ $mod["postalCode"][0]=$attributes["address_code"]; }
810*76ce1169SAndreas Gohr        //if ($attributes["address_country"]){ $mod["countryCode"][0]=$attributes["address_country"]; } // use country codes?
811*76ce1169SAndreas Gohr        if ($attributes["address_country"]){ $mod["c"][0]=$attributes["address_country"]; }
812*76ce1169SAndreas Gohr        if ($attributes["address_pobox"]){ $mod["postOfficeBox"][0]=$attributes["address_pobox"]; }
813*76ce1169SAndreas Gohr        if ($attributes["address_state"]){ $mod["st"][0]=$attributes["address_state"]; }
814*76ce1169SAndreas Gohr        if ($attributes["address_street"]){ $mod["streetAddress"][0]=$attributes["address_street"]; }
815*76ce1169SAndreas Gohr        if ($attributes["company"]){ $mod["company"][0]=$attributes["company"]; }
816*76ce1169SAndreas Gohr        if ($attributes["change_password"]){ $mod["pwdLastSet"][0]=0; }
817*76ce1169SAndreas Gohr        if ($attributes["department"]){ $mod["department"][0]=$attributes["department"]; }
818*76ce1169SAndreas Gohr        if ($attributes["description"]){ $mod["description"][0]=$attributes["description"]; }
819*76ce1169SAndreas Gohr        if ($attributes["display_name"]){ $mod["displayName"][0]=$attributes["display_name"]; }
820*76ce1169SAndreas Gohr        if ($attributes["email"]){ $mod["mail"][0]=$attributes["email"]; }
821*76ce1169SAndreas Gohr        if ($attributes["expires"]){ $mod["accountExpires"][0]=$attributes["expires"]; } //unix epoch format?
822*76ce1169SAndreas Gohr        if ($attributes["firstname"]){ $mod["givenName"][0]=$attributes["firstname"]; }
823*76ce1169SAndreas Gohr        if ($attributes["home_directory"]){ $mod["homeDirectory"][0]=$attributes["home_directory"]; }
824*76ce1169SAndreas Gohr        if ($attributes["home_drive"]){ $mod["homeDrive"][0]=$attributes["home_drive"]; }
825*76ce1169SAndreas Gohr        if ($attributes["initials"]){ $mod["initials"][0]=$attributes["initials"]; }
826*76ce1169SAndreas Gohr        if ($attributes["logon_name"]){ $mod["userPrincipalName"][0]=$attributes["logon_name"]; }
827*76ce1169SAndreas Gohr        if ($attributes["manager"]){ $mod["manager"][0]=$attributes["manager"]; }  //UNTESTED ***Use DistinguishedName***
828*76ce1169SAndreas Gohr        if ($attributes["office"]){ $mod["physicalDeliveryOfficeName"][0]=$attributes["office"]; }
829*76ce1169SAndreas Gohr        if ($attributes["password"]){ $mod["unicodePwd"][0]=$this->user()->encodePassword($attributes["password"]); }
830*76ce1169SAndreas Gohr        if ($attributes["profile_path"]){ $mod["profilepath"][0]=$attributes["profile_path"]; }
831*76ce1169SAndreas Gohr        if ($attributes["script_path"]){ $mod["scriptPath"][0]=$attributes["script_path"]; }
832*76ce1169SAndreas Gohr        if ($attributes["surname"]){ $mod["sn"][0]=$attributes["surname"]; }
833*76ce1169SAndreas Gohr        if ($attributes["title"]){ $mod["title"][0]=$attributes["title"]; }
834*76ce1169SAndreas Gohr        if ($attributes["telephone"]){ $mod["telephoneNumber"][0]=$attributes["telephone"]; }
835*76ce1169SAndreas Gohr        if ($attributes["mobile"]){ $mod["mobile"][0]=$attributes["mobile"]; }
836*76ce1169SAndreas Gohr        if ($attributes["pager"]){ $mod["pager"][0]=$attributes["pager"]; }
837*76ce1169SAndreas Gohr        if ($attributes["ipphone"]){ $mod["ipphone"][0]=$attributes["ipphone"]; }
838*76ce1169SAndreas Gohr        if ($attributes["web_page"]){ $mod["wWWHomePage"][0]=$attributes["web_page"]; }
839*76ce1169SAndreas Gohr        if ($attributes["fax"]){ $mod["facsimileTelephoneNumber"][0]=$attributes["fax"]; }
840*76ce1169SAndreas Gohr        if ($attributes["enabled"]){ $mod["userAccountControl"][0]=$attributes["enabled"]; }
841*76ce1169SAndreas Gohr        if ($attributes["homephone"]){ $mod["homephone"][0]=$attributes["homephone"]; }
842*76ce1169SAndreas Gohr
843*76ce1169SAndreas Gohr        // Distribution List specific schema
844*76ce1169SAndreas Gohr        if ($attributes["group_sendpermission"]){ $mod["dlMemSubmitPerms"][0]=$attributes["group_sendpermission"]; }
845*76ce1169SAndreas Gohr        if ($attributes["group_rejectpermission"]){ $mod["dlMemRejectPerms"][0]=$attributes["group_rejectpermission"]; }
846*76ce1169SAndreas Gohr
847*76ce1169SAndreas Gohr        // Exchange Schema
848*76ce1169SAndreas Gohr        if ($attributes["exchange_homemdb"]){ $mod["homeMDB"][0]=$attributes["exchange_homemdb"]; }
849*76ce1169SAndreas Gohr        if ($attributes["exchange_mailnickname"]){ $mod["mailNickname"][0]=$attributes["exchange_mailnickname"]; }
850*76ce1169SAndreas Gohr        if ($attributes["exchange_proxyaddress"]){ $mod["proxyAddresses"][0]=$attributes["exchange_proxyaddress"]; }
851*76ce1169SAndreas Gohr        if ($attributes["exchange_usedefaults"]){ $mod["mDBUseDefaults"][0]=$attributes["exchange_usedefaults"]; }
852*76ce1169SAndreas Gohr        if ($attributes["exchange_policyexclude"]){ $mod["msExchPoliciesExcluded"][0]=$attributes["exchange_policyexclude"]; }
853*76ce1169SAndreas Gohr        if ($attributes["exchange_policyinclude"]){ $mod["msExchPoliciesIncluded"][0]=$attributes["exchange_policyinclude"]; }
854*76ce1169SAndreas Gohr        if ($attributes["exchange_addressbook"]){ $mod["showInAddressBook"][0]=$attributes["exchange_addressbook"]; }
855*76ce1169SAndreas Gohr        if ($attributes["exchange_altrecipient"]){ $mod["altRecipient"][0]=$attributes["exchange_altrecipient"]; }
856*76ce1169SAndreas Gohr        if ($attributes["exchange_deliverandredirect"]){ $mod["deliverAndRedirect"][0]=$attributes["exchange_deliverandredirect"]; }
857*76ce1169SAndreas Gohr
858*76ce1169SAndreas Gohr        // This schema is designed for contacts
859*76ce1169SAndreas Gohr        if ($attributes["exchange_hidefromlists"]){ $mod["msExchHideFromAddressLists"][0]=$attributes["exchange_hidefromlists"]; }
860*76ce1169SAndreas Gohr        if ($attributes["contact_email"]){ $mod["targetAddress"][0]=$attributes["contact_email"]; }
861*76ce1169SAndreas Gohr
862*76ce1169SAndreas Gohr        //echo ("<pre>"); print_r($mod);
863*76ce1169SAndreas Gohr        /*
864*76ce1169SAndreas Gohr        // modifying a name is a bit fiddly
865*76ce1169SAndreas Gohr        if ($attributes["firstname"] && $attributes["surname"]){
866*76ce1169SAndreas Gohr            $mod["cn"][0]=$attributes["firstname"]." ".$attributes["surname"];
867*76ce1169SAndreas Gohr            $mod["displayname"][0]=$attributes["firstname"]." ".$attributes["surname"];
868*76ce1169SAndreas Gohr            $mod["name"][0]=$attributes["firstname"]." ".$attributes["surname"];
869*76ce1169SAndreas Gohr        }
870*76ce1169SAndreas Gohr        */
871*76ce1169SAndreas Gohr
872*76ce1169SAndreas Gohr        if (count($mod)==0){ return (false); }
873*76ce1169SAndreas Gohr        return ($mod);
874*76ce1169SAndreas Gohr    }
875*76ce1169SAndreas Gohr
876*76ce1169SAndreas Gohr    /**
877*76ce1169SAndreas Gohr    * Convert 8bit characters e.g. accented characters to UTF8 encoded characters
878*76ce1169SAndreas Gohr    */
879*76ce1169SAndreas Gohr    protected function encode8Bit(&$item, $key) {
880*76ce1169SAndreas Gohr        $encode = false;
881*76ce1169SAndreas Gohr        if (is_string($item)) {
882*76ce1169SAndreas Gohr            for ($i=0; $i<strlen($item); $i++) {
883*76ce1169SAndreas Gohr                if (ord($item[$i]) >> 7) {
884*76ce1169SAndreas Gohr                    $encode = true;
885*76ce1169SAndreas Gohr                }
886*76ce1169SAndreas Gohr            }
887*76ce1169SAndreas Gohr        }
888*76ce1169SAndreas Gohr        if ($encode === true && $key != 'password') {
889*76ce1169SAndreas Gohr            $item = utf8_encode($item);
890*76ce1169SAndreas Gohr        }
891*76ce1169SAndreas Gohr    }
892*76ce1169SAndreas Gohr
893*76ce1169SAndreas Gohr    /**
894*76ce1169SAndreas Gohr    * Select a random domain controller from your domain controller array
895*76ce1169SAndreas Gohr    *
896*76ce1169SAndreas Gohr    * @return string
897*76ce1169SAndreas Gohr    */
898*76ce1169SAndreas Gohr    protected function randomController()
899*76ce1169SAndreas Gohr    {
900*76ce1169SAndreas Gohr        mt_srand(doubleval(microtime()) * 100000000); // For older PHP versions
901*76ce1169SAndreas Gohr        /*if (sizeof($this->domainControllers) > 1) {
902*76ce1169SAndreas Gohr            $adController = $this->domainControllers[array_rand($this->domainControllers)];
903*76ce1169SAndreas Gohr            // Test if the controller is responding to pings
904*76ce1169SAndreas Gohr            $ping = $this->pingController($adController);
905*76ce1169SAndreas Gohr            if ($ping === false) {
906*76ce1169SAndreas Gohr                // Find the current key in the domain controllers array
907*76ce1169SAndreas Gohr                $key = array_search($adController, $this->domainControllers);
908*76ce1169SAndreas Gohr                // Remove it so that we don't end up in a recursive loop
909*76ce1169SAndreas Gohr                unset($this->domainControllers[$key]);
910*76ce1169SAndreas Gohr                // Select a new controller
911*76ce1169SAndreas Gohr                return $this->randomController();
912*76ce1169SAndreas Gohr            }
913*76ce1169SAndreas Gohr            else {
914*76ce1169SAndreas Gohr                return ($adController);
915*76ce1169SAndreas Gohr            }
916*76ce1169SAndreas Gohr        } */
917*76ce1169SAndreas Gohr        return $this->domainControllers[array_rand($this->domainControllers)];
918*76ce1169SAndreas Gohr    }
919*76ce1169SAndreas Gohr
920*76ce1169SAndreas Gohr    /**
921*76ce1169SAndreas Gohr    * Test basic connectivity to controller
922*76ce1169SAndreas Gohr    *
923*76ce1169SAndreas Gohr    * @return bool
924*76ce1169SAndreas Gohr    */
925*76ce1169SAndreas Gohr    protected function pingController($host) {
926*76ce1169SAndreas Gohr        $port = $this->adPort;
927*76ce1169SAndreas Gohr        fsockopen($host, $port, $errno, $errstr, 10);
928*76ce1169SAndreas Gohr        if ($errno > 0) {
929*76ce1169SAndreas Gohr            return false;
930*76ce1169SAndreas Gohr        }
931*76ce1169SAndreas Gohr        return true;
932*76ce1169SAndreas Gohr    }
933*76ce1169SAndreas Gohr
934*76ce1169SAndreas Gohr}
935*76ce1169SAndreas Gohr
936*76ce1169SAndreas Gohr/**
937*76ce1169SAndreas Gohr* adLDAP Exception Handler
938*76ce1169SAndreas Gohr*
939*76ce1169SAndreas Gohr* Exceptions of this type are thrown on bind failure or when SSL is required but not configured
940*76ce1169SAndreas Gohr* Example:
941*76ce1169SAndreas Gohr* try {
942*76ce1169SAndreas Gohr*   $adldap = new adLDAP();
943*76ce1169SAndreas Gohr* }
944*76ce1169SAndreas Gohr* catch (adLDAPException $e) {
945*76ce1169SAndreas Gohr*   echo $e;
946*76ce1169SAndreas Gohr*   exit();
947*76ce1169SAndreas Gohr* }
948*76ce1169SAndreas Gohr*/
949*76ce1169SAndreas Gohrclass adLDAPException extends Exception {}
950*76ce1169SAndreas Gohr
951*76ce1169SAndreas Gohr?>