xref: /dokuwiki/lib/plugins/authad/adLDAP/adLDAP.php (revision 59752844d1903a528e765e8b80dafa2a1ca140ad)
176ce1169SAndreas Gohr<?php
276ce1169SAndreas Gohr/**
376ce1169SAndreas Gohr * PHP LDAP CLASS FOR MANIPULATING ACTIVE DIRECTORY
476ce1169SAndreas Gohr * Version 4.0.4
576ce1169SAndreas Gohr *
676ce1169SAndreas Gohr * PHP Version 5 with SSL and LDAP support
776ce1169SAndreas Gohr *
876ce1169SAndreas Gohr * Written by Scott Barnett, Richard Hyland
976ce1169SAndreas Gohr *   email: scott@wiggumworld.com, adldap@richardhyland.com
1076ce1169SAndreas Gohr *   http://adldap.sourceforge.net/
1176ce1169SAndreas Gohr *
1276ce1169SAndreas Gohr * Copyright (c) 2006-2012 Scott Barnett, Richard Hyland
1376ce1169SAndreas Gohr *
1476ce1169SAndreas Gohr * We'd appreciate any improvements or additions to be submitted back
1576ce1169SAndreas Gohr * to benefit the entire community :)
1676ce1169SAndreas Gohr *
1776ce1169SAndreas Gohr * This library is free software; you can redistribute it and/or
1876ce1169SAndreas Gohr * modify it under the terms of the GNU Lesser General Public
1976ce1169SAndreas Gohr * License as published by the Free Software Foundation; either
2076ce1169SAndreas Gohr * version 2.1 of the License.
2176ce1169SAndreas Gohr *
2276ce1169SAndreas Gohr * This library is distributed in the hope that it will be useful,
2376ce1169SAndreas Gohr * but WITHOUT ANY WARRANTY; without even the implied warranty of
2476ce1169SAndreas Gohr * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
2576ce1169SAndreas Gohr * Lesser General Public License for more details.
2676ce1169SAndreas Gohr *
2776ce1169SAndreas Gohr * @category ToolsAndUtilities
2876ce1169SAndreas Gohr * @package adLDAP
2976ce1169SAndreas Gohr * @author Scott Barnett, Richard Hyland
3076ce1169SAndreas Gohr * @copyright (c) 2006-2012 Scott Barnett, Richard Hyland
3176ce1169SAndreas Gohr * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html LGPLv2.1
3276ce1169SAndreas Gohr * @revision $Revision: 169 $
3376ce1169SAndreas Gohr * @version 4.0.4
3476ce1169SAndreas Gohr * @link http://adldap.sourceforge.net/
3576ce1169SAndreas Gohr */
3676ce1169SAndreas Gohr
3776ce1169SAndreas Gohr/**
3876ce1169SAndreas Gohr* Main adLDAP class
3976ce1169SAndreas Gohr*
4076ce1169SAndreas Gohr* Can be initialised using $adldap = new adLDAP();
4176ce1169SAndreas Gohr*
4276ce1169SAndreas Gohr* Something to keep in mind is that Active Directory is a permissions
4376ce1169SAndreas Gohr* based directory. If you bind as a domain user, you can't fetch as
4476ce1169SAndreas Gohr* much information on other users as you could as a domain admin.
4576ce1169SAndreas Gohr*
4676ce1169SAndreas Gohr* Before asking questions, please read the Documentation at
4776ce1169SAndreas Gohr* http://adldap.sourceforge.net/wiki/doku.php?id=api
4876ce1169SAndreas Gohr*/
4976ce1169SAndreas Gohrrequire_once(dirname(__FILE__) . '/collections/adLDAPCollection.php');
5076ce1169SAndreas Gohrrequire_once(dirname(__FILE__) . '/classes/adLDAPGroups.php');
5176ce1169SAndreas Gohrrequire_once(dirname(__FILE__) . '/classes/adLDAPUsers.php');
5276ce1169SAndreas Gohrrequire_once(dirname(__FILE__) . '/classes/adLDAPFolders.php');
5376ce1169SAndreas Gohrrequire_once(dirname(__FILE__) . '/classes/adLDAPUtils.php');
5476ce1169SAndreas Gohrrequire_once(dirname(__FILE__) . '/classes/adLDAPContacts.php');
5576ce1169SAndreas Gohrrequire_once(dirname(__FILE__) . '/classes/adLDAPExchange.php');
5676ce1169SAndreas Gohrrequire_once(dirname(__FILE__) . '/classes/adLDAPComputers.php');
5776ce1169SAndreas Gohr
5876ce1169SAndreas Gohrclass adLDAP {
5976ce1169SAndreas Gohr
6076ce1169SAndreas Gohr    /**
6176ce1169SAndreas Gohr     * Define the different types of account in AD
6276ce1169SAndreas Gohr     */
6376ce1169SAndreas Gohr    const ADLDAP_NORMAL_ACCOUNT = 805306368;
6476ce1169SAndreas Gohr    const ADLDAP_WORKSTATION_TRUST = 805306369;
6576ce1169SAndreas Gohr    const ADLDAP_INTERDOMAIN_TRUST = 805306370;
6676ce1169SAndreas Gohr    const ADLDAP_SECURITY_GLOBAL_GROUP = 268435456;
6776ce1169SAndreas Gohr    const ADLDAP_DISTRIBUTION_GROUP = 268435457;
6876ce1169SAndreas Gohr    const ADLDAP_SECURITY_LOCAL_GROUP = 536870912;
6976ce1169SAndreas Gohr    const ADLDAP_DISTRIBUTION_LOCAL_GROUP = 536870913;
7076ce1169SAndreas Gohr    const ADLDAP_FOLDER = 'OU';
7176ce1169SAndreas Gohr    const ADLDAP_CONTAINER = 'CN';
7276ce1169SAndreas Gohr
7376ce1169SAndreas Gohr    /**
7476ce1169SAndreas Gohr    * The default port for LDAP non-SSL connections
7576ce1169SAndreas Gohr    */
7676ce1169SAndreas Gohr    const ADLDAP_LDAP_PORT = '389';
7776ce1169SAndreas Gohr    /**
7876ce1169SAndreas Gohr    * The default port for LDAPS SSL connections
7976ce1169SAndreas Gohr    */
8076ce1169SAndreas Gohr    const ADLDAP_LDAPS_PORT = '636';
8176ce1169SAndreas Gohr
8276ce1169SAndreas Gohr    /**
8376ce1169SAndreas Gohr    * The account suffix for your domain, can be set when the class is invoked
8476ce1169SAndreas Gohr    *
8576ce1169SAndreas Gohr    * @var string
8676ce1169SAndreas Gohr    */
8776ce1169SAndreas Gohr        protected $accountSuffix = "@mydomain.local";
8876ce1169SAndreas Gohr
8976ce1169SAndreas Gohr    /**
9076ce1169SAndreas Gohr    * The base dn for your domain
9176ce1169SAndreas Gohr    *
9276ce1169SAndreas Gohr    * If this is set to null then adLDAP will attempt to obtain this automatically from the rootDSE
9376ce1169SAndreas Gohr    *
9476ce1169SAndreas Gohr    * @var string
9576ce1169SAndreas Gohr    */
9676ce1169SAndreas Gohr        protected $baseDn = "DC=mydomain,DC=local";
9776ce1169SAndreas Gohr
9876ce1169SAndreas Gohr    /**
9976ce1169SAndreas Gohr    * Port used to talk to the domain controllers.
10076ce1169SAndreas Gohr    *
10176ce1169SAndreas Gohr    * @var int
10276ce1169SAndreas Gohr    */
10376ce1169SAndreas Gohr    protected $adPort = self::ADLDAP_LDAP_PORT;
10476ce1169SAndreas Gohr
10576ce1169SAndreas Gohr    /**
10676ce1169SAndreas Gohr    * Array of domain controllers. Specifiy multiple controllers if you
10776ce1169SAndreas Gohr    * would like the class to balance the LDAP queries amongst multiple servers
10876ce1169SAndreas Gohr    *
10976ce1169SAndreas Gohr    * @var array
11076ce1169SAndreas Gohr    */
11176ce1169SAndreas Gohr    protected $domainControllers = array("dc01.mydomain.local");
11276ce1169SAndreas Gohr
11376ce1169SAndreas Gohr    /**
11476ce1169SAndreas Gohr    * Optional account with higher privileges for searching
11576ce1169SAndreas Gohr    * This should be set to a domain admin account
11676ce1169SAndreas Gohr    *
11776ce1169SAndreas Gohr    * @var string
11876ce1169SAndreas Gohr    * @var string
11976ce1169SAndreas Gohr    */
12076ce1169SAndreas Gohr        protected $adminUsername = NULL;
12176ce1169SAndreas Gohr    protected $adminPassword = NULL;
12276ce1169SAndreas Gohr
12376ce1169SAndreas Gohr    /**
12476ce1169SAndreas Gohr    * AD does not return the primary group. http://support.microsoft.com/?kbid=321360
12576ce1169SAndreas Gohr    * This tweak will resolve the real primary group.
12676ce1169SAndreas Gohr    * Setting to false will fudge "Domain Users" and is much faster. Keep in mind though that if
12776ce1169SAndreas Gohr    * someone's primary group is NOT domain users, this is obviously going to mess up the results
12876ce1169SAndreas Gohr    *
12976ce1169SAndreas Gohr    * @var bool
13076ce1169SAndreas Gohr    */
13176ce1169SAndreas Gohr        protected $realPrimaryGroup = true;
13276ce1169SAndreas Gohr
13376ce1169SAndreas Gohr    /**
13476ce1169SAndreas Gohr    * Use SSL (LDAPS), your server needs to be setup, please see
13576ce1169SAndreas Gohr    * http://adldap.sourceforge.net/wiki/doku.php?id=ldap_over_ssl
13676ce1169SAndreas Gohr    *
13776ce1169SAndreas Gohr    * @var bool
13876ce1169SAndreas Gohr    */
13976ce1169SAndreas Gohr        protected $useSSL = false;
14076ce1169SAndreas Gohr
14176ce1169SAndreas Gohr    /**
14276ce1169SAndreas Gohr    * Use TLS
14376ce1169SAndreas Gohr    * If you wish to use TLS you should ensure that $useSSL is set to false and vice-versa
14476ce1169SAndreas Gohr    *
14576ce1169SAndreas Gohr    * @var bool
14676ce1169SAndreas Gohr    */
14776ce1169SAndreas Gohr    protected $useTLS = false;
14876ce1169SAndreas Gohr
14976ce1169SAndreas Gohr    /**
15076ce1169SAndreas Gohr    * Use SSO
15176ce1169SAndreas Gohr    * To indicate to adLDAP to reuse password set by the brower through NTLM or Kerberos
15276ce1169SAndreas Gohr    *
15376ce1169SAndreas Gohr    * @var bool
15476ce1169SAndreas Gohr    */
15576ce1169SAndreas Gohr    protected $useSSO = false;
15676ce1169SAndreas Gohr
15776ce1169SAndreas Gohr    /**
15876ce1169SAndreas Gohr    * When querying group memberships, do it recursively
15976ce1169SAndreas Gohr    * eg. User Fred is a member of Group A, which is a member of Group B, which is a member of Group C
16076ce1169SAndreas Gohr    * user_ingroup("Fred","C") will returns true with this option turned on, false if turned off
16176ce1169SAndreas Gohr    *
16276ce1169SAndreas Gohr    * @var bool
16376ce1169SAndreas Gohr    */
16476ce1169SAndreas Gohr        protected $recursiveGroups = true;
16576ce1169SAndreas Gohr
16676ce1169SAndreas Gohr        // You should not need to edit anything below this line
16776ce1169SAndreas Gohr        //******************************************************************************************
16876ce1169SAndreas Gohr
16976ce1169SAndreas Gohr        /**
17076ce1169SAndreas Gohr    * Connection and bind default variables
17176ce1169SAndreas Gohr    *
17276ce1169SAndreas Gohr    * @var mixed
17376ce1169SAndreas Gohr    * @var mixed
17476ce1169SAndreas Gohr    */
17576ce1169SAndreas Gohr        protected $ldapConnection;
17676ce1169SAndreas Gohr        protected $ldapBind;
17776ce1169SAndreas Gohr
17876ce1169SAndreas Gohr    /**
17976ce1169SAndreas Gohr    * Get the active LDAP Connection
18076ce1169SAndreas Gohr    *
18176ce1169SAndreas Gohr    * @return resource
18276ce1169SAndreas Gohr    */
18376ce1169SAndreas Gohr    public function getLdapConnection() {
18476ce1169SAndreas Gohr        if ($this->ldapConnection) {
18576ce1169SAndreas Gohr            return $this->ldapConnection;
18676ce1169SAndreas Gohr        }
18776ce1169SAndreas Gohr        return false;
18876ce1169SAndreas Gohr    }
18976ce1169SAndreas Gohr
19076ce1169SAndreas Gohr    /**
19176ce1169SAndreas Gohr    * Get the bind status
19276ce1169SAndreas Gohr    *
19376ce1169SAndreas Gohr    * @return bool
19476ce1169SAndreas Gohr    */
19576ce1169SAndreas Gohr    public function getLdapBind() {
19676ce1169SAndreas Gohr        return $this->ldapBind;
19776ce1169SAndreas Gohr    }
19876ce1169SAndreas Gohr
19976ce1169SAndreas Gohr    /**
20076ce1169SAndreas Gohr    * Get the current base DN
20176ce1169SAndreas Gohr    *
20276ce1169SAndreas Gohr    * @return string
20376ce1169SAndreas Gohr    */
20476ce1169SAndreas Gohr    public function getBaseDn() {
20576ce1169SAndreas Gohr        return $this->baseDn;
20676ce1169SAndreas Gohr    }
20776ce1169SAndreas Gohr
20876ce1169SAndreas Gohr    /**
20976ce1169SAndreas Gohr    * The group class
21076ce1169SAndreas Gohr    *
21176ce1169SAndreas Gohr    * @var adLDAPGroups
21276ce1169SAndreas Gohr    */
21376ce1169SAndreas Gohr    protected $groupClass;
21476ce1169SAndreas Gohr
21576ce1169SAndreas Gohr    /**
21676ce1169SAndreas Gohr    * Get the group class interface
21776ce1169SAndreas Gohr    *
21876ce1169SAndreas Gohr    * @return adLDAPGroups
21976ce1169SAndreas Gohr    */
22076ce1169SAndreas Gohr    public function group() {
22176ce1169SAndreas Gohr        if (!$this->groupClass) {
22276ce1169SAndreas Gohr            $this->groupClass = new adLDAPGroups($this);
22376ce1169SAndreas Gohr        }
22476ce1169SAndreas Gohr        return $this->groupClass;
22576ce1169SAndreas Gohr    }
22676ce1169SAndreas Gohr
22776ce1169SAndreas Gohr    /**
22876ce1169SAndreas Gohr    * The user class
22976ce1169SAndreas Gohr    *
23076ce1169SAndreas Gohr    * @var adLDAPUsers
23176ce1169SAndreas Gohr    */
23276ce1169SAndreas Gohr    protected $userClass;
23376ce1169SAndreas Gohr
23476ce1169SAndreas Gohr    /**
23576ce1169SAndreas Gohr    * Get the userclass interface
23676ce1169SAndreas Gohr    *
23776ce1169SAndreas Gohr    * @return adLDAPUsers
23876ce1169SAndreas Gohr    */
23976ce1169SAndreas Gohr    public function user() {
24076ce1169SAndreas Gohr        if (!$this->userClass) {
24176ce1169SAndreas Gohr            $this->userClass = new adLDAPUsers($this);
24276ce1169SAndreas Gohr        }
24376ce1169SAndreas Gohr        return $this->userClass;
24476ce1169SAndreas Gohr    }
24576ce1169SAndreas Gohr
24676ce1169SAndreas Gohr    /**
24776ce1169SAndreas Gohr    * The folders class
24876ce1169SAndreas Gohr    *
24976ce1169SAndreas Gohr    * @var adLDAPFolders
25076ce1169SAndreas Gohr    */
25176ce1169SAndreas Gohr    protected $folderClass;
25276ce1169SAndreas Gohr
25376ce1169SAndreas Gohr    /**
25476ce1169SAndreas Gohr    * Get the folder class interface
25576ce1169SAndreas Gohr    *
25676ce1169SAndreas Gohr    * @return adLDAPFolders
25776ce1169SAndreas Gohr    */
25876ce1169SAndreas Gohr    public function folder() {
25976ce1169SAndreas Gohr        if (!$this->folderClass) {
26076ce1169SAndreas Gohr            $this->folderClass = new adLDAPFolders($this);
26176ce1169SAndreas Gohr        }
26276ce1169SAndreas Gohr        return $this->folderClass;
26376ce1169SAndreas Gohr    }
26476ce1169SAndreas Gohr
26576ce1169SAndreas Gohr    /**
26676ce1169SAndreas Gohr    * The utils class
26776ce1169SAndreas Gohr    *
26876ce1169SAndreas Gohr    * @var adLDAPUtils
26976ce1169SAndreas Gohr    */
27076ce1169SAndreas Gohr    protected $utilClass;
27176ce1169SAndreas Gohr
27276ce1169SAndreas Gohr    /**
27376ce1169SAndreas Gohr    * Get the utils class interface
27476ce1169SAndreas Gohr    *
27576ce1169SAndreas Gohr    * @return adLDAPUtils
27676ce1169SAndreas Gohr    */
27776ce1169SAndreas Gohr    public function utilities() {
27876ce1169SAndreas Gohr        if (!$this->utilClass) {
27976ce1169SAndreas Gohr            $this->utilClass = new adLDAPUtils($this);
28076ce1169SAndreas Gohr        }
28176ce1169SAndreas Gohr        return $this->utilClass;
28276ce1169SAndreas Gohr    }
28376ce1169SAndreas Gohr
28476ce1169SAndreas Gohr    /**
28576ce1169SAndreas Gohr    * The contacts class
28676ce1169SAndreas Gohr    *
28776ce1169SAndreas Gohr    * @var adLDAPContacts
28876ce1169SAndreas Gohr    */
28976ce1169SAndreas Gohr    protected $contactClass;
29076ce1169SAndreas Gohr
29176ce1169SAndreas Gohr    /**
29276ce1169SAndreas Gohr    * Get the contacts class interface
29376ce1169SAndreas Gohr    *
29476ce1169SAndreas Gohr    * @return adLDAPContacts
29576ce1169SAndreas Gohr    */
29676ce1169SAndreas Gohr    public function contact() {
29776ce1169SAndreas Gohr        if (!$this->contactClass) {
29876ce1169SAndreas Gohr            $this->contactClass = new adLDAPContacts($this);
29976ce1169SAndreas Gohr        }
30076ce1169SAndreas Gohr        return $this->contactClass;
30176ce1169SAndreas Gohr    }
30276ce1169SAndreas Gohr
30376ce1169SAndreas Gohr    /**
30476ce1169SAndreas Gohr    * The exchange class
30576ce1169SAndreas Gohr    *
30676ce1169SAndreas Gohr    * @var adLDAPExchange
30776ce1169SAndreas Gohr    */
30876ce1169SAndreas Gohr    protected $exchangeClass;
30976ce1169SAndreas Gohr
31076ce1169SAndreas Gohr    /**
31176ce1169SAndreas Gohr    * Get the exchange class interface
31276ce1169SAndreas Gohr    *
31376ce1169SAndreas Gohr    * @return adLDAPExchange
31476ce1169SAndreas Gohr    */
31576ce1169SAndreas Gohr    public function exchange() {
31676ce1169SAndreas Gohr        if (!$this->exchangeClass) {
31776ce1169SAndreas Gohr            $this->exchangeClass = new adLDAPExchange($this);
31876ce1169SAndreas Gohr        }
31976ce1169SAndreas Gohr        return $this->exchangeClass;
32076ce1169SAndreas Gohr    }
32176ce1169SAndreas Gohr
32276ce1169SAndreas Gohr    /**
32376ce1169SAndreas Gohr    * The computers class
32476ce1169SAndreas Gohr    *
32576ce1169SAndreas Gohr    * @var adLDAPComputers
32676ce1169SAndreas Gohr    */
32776ce1169SAndreas Gohr    protected $computersClass;
32876ce1169SAndreas Gohr
32976ce1169SAndreas Gohr    /**
33076ce1169SAndreas Gohr    * Get the computers class interface
33176ce1169SAndreas Gohr    *
33276ce1169SAndreas Gohr    * @return adLDAPComputers
33376ce1169SAndreas Gohr    */
33476ce1169SAndreas Gohr    public function computer() {
33576ce1169SAndreas Gohr        if (!$this->computerClass) {
33676ce1169SAndreas Gohr            $this->computerClass = new adLDAPComputers($this);
33776ce1169SAndreas Gohr        }
33876ce1169SAndreas Gohr        return $this->computerClass;
33976ce1169SAndreas Gohr    }
34076ce1169SAndreas Gohr
34176ce1169SAndreas Gohr    /**
34276ce1169SAndreas Gohr    * Getters and Setters
34376ce1169SAndreas Gohr    */
34476ce1169SAndreas Gohr
34576ce1169SAndreas Gohr    /**
34676ce1169SAndreas Gohr    * Set the account suffix
34776ce1169SAndreas Gohr    *
34876ce1169SAndreas Gohr    * @param string $accountSuffix
34976ce1169SAndreas Gohr    * @return void
35076ce1169SAndreas Gohr    */
35176ce1169SAndreas Gohr    public function setAccountSuffix($accountSuffix)
35276ce1169SAndreas Gohr    {
35376ce1169SAndreas Gohr          $this->accountSuffix = $accountSuffix;
35476ce1169SAndreas Gohr    }
35576ce1169SAndreas Gohr
35676ce1169SAndreas Gohr    /**
35776ce1169SAndreas Gohr    * Get the account suffix
35876ce1169SAndreas Gohr    *
35976ce1169SAndreas Gohr    * @return string
36076ce1169SAndreas Gohr    */
36176ce1169SAndreas Gohr    public function getAccountSuffix()
36276ce1169SAndreas Gohr    {
36376ce1169SAndreas Gohr          return $this->accountSuffix;
36476ce1169SAndreas Gohr    }
36576ce1169SAndreas Gohr
36676ce1169SAndreas Gohr    /**
36776ce1169SAndreas Gohr    * Set the domain controllers array
36876ce1169SAndreas Gohr    *
36976ce1169SAndreas Gohr    * @param array $domainControllers
37076ce1169SAndreas Gohr    * @return void
37176ce1169SAndreas Gohr    */
37276ce1169SAndreas Gohr    public function setDomainControllers(array $domainControllers)
37376ce1169SAndreas Gohr    {
37476ce1169SAndreas Gohr          $this->domainControllers = $domainControllers;
37576ce1169SAndreas Gohr    }
37676ce1169SAndreas Gohr
37776ce1169SAndreas Gohr    /**
37876ce1169SAndreas Gohr    * Get the list of domain controllers
37976ce1169SAndreas Gohr    *
38076ce1169SAndreas Gohr    * @return void
38176ce1169SAndreas Gohr    */
38276ce1169SAndreas Gohr    public function getDomainControllers()
38376ce1169SAndreas Gohr    {
38476ce1169SAndreas Gohr          return $this->domainControllers;
38576ce1169SAndreas Gohr    }
38676ce1169SAndreas Gohr
38776ce1169SAndreas Gohr    /**
38876ce1169SAndreas Gohr    * Sets the port number your domain controller communicates over
38976ce1169SAndreas Gohr    *
39076ce1169SAndreas Gohr    * @param int $adPort
39176ce1169SAndreas Gohr    */
39276ce1169SAndreas Gohr    public function setPort($adPort)
39376ce1169SAndreas Gohr    {
39476ce1169SAndreas Gohr        $this->adPort = $adPort;
39576ce1169SAndreas Gohr    }
39676ce1169SAndreas Gohr
39776ce1169SAndreas Gohr    /**
39876ce1169SAndreas Gohr    * Gets the port number your domain controller communicates over
39976ce1169SAndreas Gohr    *
40076ce1169SAndreas Gohr    * @return int
40176ce1169SAndreas Gohr    */
40276ce1169SAndreas Gohr    public function getPort()
40376ce1169SAndreas Gohr    {
40476ce1169SAndreas Gohr        return $this->adPort;
40576ce1169SAndreas Gohr    }
40676ce1169SAndreas Gohr
40776ce1169SAndreas Gohr    /**
40876ce1169SAndreas Gohr    * Set the username of an account with higher priviledges
40976ce1169SAndreas Gohr    *
41076ce1169SAndreas Gohr    * @param string $adminUsername
41176ce1169SAndreas Gohr    * @return void
41276ce1169SAndreas Gohr    */
41376ce1169SAndreas Gohr    public function setAdminUsername($adminUsername)
41476ce1169SAndreas Gohr    {
41576ce1169SAndreas Gohr          $this->adminUsername = $adminUsername;
41676ce1169SAndreas Gohr    }
41776ce1169SAndreas Gohr
41876ce1169SAndreas Gohr    /**
41976ce1169SAndreas Gohr    * Get the username of the account with higher priviledges
42076ce1169SAndreas Gohr    *
42176ce1169SAndreas Gohr    * This will throw an exception for security reasons
42276ce1169SAndreas Gohr    */
42376ce1169SAndreas Gohr    public function getAdminUsername()
42476ce1169SAndreas Gohr    {
42576ce1169SAndreas Gohr          throw new adLDAPException('For security reasons you cannot access the domain administrator account details');
42676ce1169SAndreas Gohr    }
42776ce1169SAndreas Gohr
42876ce1169SAndreas Gohr    /**
42976ce1169SAndreas Gohr    * Set the password of an account with higher priviledges
43076ce1169SAndreas Gohr    *
43176ce1169SAndreas Gohr    * @param string $adminPassword
43276ce1169SAndreas Gohr    * @return void
43376ce1169SAndreas Gohr    */
43476ce1169SAndreas Gohr    public function setAdminPassword($adminPassword)
43576ce1169SAndreas Gohr    {
43676ce1169SAndreas Gohr          $this->adminPassword = $adminPassword;
43776ce1169SAndreas Gohr    }
43876ce1169SAndreas Gohr
43976ce1169SAndreas Gohr    /**
44076ce1169SAndreas Gohr    * Get the password of the account with higher priviledges
44176ce1169SAndreas Gohr    *
44276ce1169SAndreas Gohr    * This will throw an exception for security reasons
44376ce1169SAndreas Gohr    */
44476ce1169SAndreas Gohr    public function getAdminPassword()
44576ce1169SAndreas Gohr    {
44676ce1169SAndreas Gohr          throw new adLDAPException('For security reasons you cannot access the domain administrator account details');
44776ce1169SAndreas Gohr    }
44876ce1169SAndreas Gohr
44976ce1169SAndreas Gohr    /**
45076ce1169SAndreas Gohr    * Set whether to detect the true primary group
45176ce1169SAndreas Gohr    *
45276ce1169SAndreas Gohr    * @param bool $realPrimaryGroup
45376ce1169SAndreas Gohr    * @return void
45476ce1169SAndreas Gohr    */
45576ce1169SAndreas Gohr    public function setRealPrimaryGroup($realPrimaryGroup)
45676ce1169SAndreas Gohr    {
45776ce1169SAndreas Gohr          $this->realPrimaryGroup = $realPrimaryGroup;
45876ce1169SAndreas Gohr    }
45976ce1169SAndreas Gohr
46076ce1169SAndreas Gohr    /**
46176ce1169SAndreas Gohr    * Get the real primary group setting
46276ce1169SAndreas Gohr    *
46376ce1169SAndreas Gohr    * @return bool
46476ce1169SAndreas Gohr    */
46576ce1169SAndreas Gohr    public function getRealPrimaryGroup()
46676ce1169SAndreas Gohr    {
46776ce1169SAndreas Gohr          return $this->realPrimaryGroup;
46876ce1169SAndreas Gohr    }
46976ce1169SAndreas Gohr
47076ce1169SAndreas Gohr    /**
47176ce1169SAndreas Gohr    * Set whether to use SSL
47276ce1169SAndreas Gohr    *
47376ce1169SAndreas Gohr    * @param bool $useSSL
47476ce1169SAndreas Gohr    * @return void
47576ce1169SAndreas Gohr    */
47676ce1169SAndreas Gohr    public function setUseSSL($useSSL)
47776ce1169SAndreas Gohr    {
47876ce1169SAndreas Gohr          $this->useSSL = $useSSL;
47976ce1169SAndreas Gohr          // Set the default port correctly
48076ce1169SAndreas Gohr          if($this->useSSL) {
48176ce1169SAndreas Gohr            $this->setPort(self::ADLDAP_LDAPS_PORT);
48276ce1169SAndreas Gohr          }
48376ce1169SAndreas Gohr          else {
48476ce1169SAndreas Gohr            $this->setPort(self::ADLDAP_LDAP_PORT);
48576ce1169SAndreas Gohr          }
48676ce1169SAndreas Gohr    }
48776ce1169SAndreas Gohr
48876ce1169SAndreas Gohr    /**
48976ce1169SAndreas Gohr    * Get the SSL setting
49076ce1169SAndreas Gohr    *
49176ce1169SAndreas Gohr    * @return bool
49276ce1169SAndreas Gohr    */
49376ce1169SAndreas Gohr    public function getUseSSL()
49476ce1169SAndreas Gohr    {
49576ce1169SAndreas Gohr          return $this->useSSL;
49676ce1169SAndreas Gohr    }
49776ce1169SAndreas Gohr
49876ce1169SAndreas Gohr    /**
49976ce1169SAndreas Gohr    * Set whether to use TLS
50076ce1169SAndreas Gohr    *
50176ce1169SAndreas Gohr    * @param bool $useTLS
50276ce1169SAndreas Gohr    * @return void
50376ce1169SAndreas Gohr    */
50476ce1169SAndreas Gohr    public function setUseTLS($useTLS)
50576ce1169SAndreas Gohr    {
50676ce1169SAndreas Gohr          $this->useTLS = $useTLS;
50776ce1169SAndreas Gohr    }
50876ce1169SAndreas Gohr
50976ce1169SAndreas Gohr    /**
51076ce1169SAndreas Gohr    * Get the TLS setting
51176ce1169SAndreas Gohr    *
51276ce1169SAndreas Gohr    * @return bool
51376ce1169SAndreas Gohr    */
51476ce1169SAndreas Gohr    public function getUseTLS()
51576ce1169SAndreas Gohr    {
51676ce1169SAndreas Gohr          return $this->useTLS;
51776ce1169SAndreas Gohr    }
51876ce1169SAndreas Gohr
51976ce1169SAndreas Gohr    /**
52076ce1169SAndreas Gohr    * Set whether to use SSO
52176ce1169SAndreas Gohr    * Requires ldap_sasl_bind support. Be sure --with-ldap-sasl is used when configuring PHP otherwise this function will be undefined.
52276ce1169SAndreas Gohr    *
52376ce1169SAndreas Gohr    * @param bool $useSSO
52476ce1169SAndreas Gohr    * @return void
52576ce1169SAndreas Gohr    */
52676ce1169SAndreas Gohr    public function setUseSSO($useSSO)
52776ce1169SAndreas Gohr    {
52876ce1169SAndreas Gohr          if ($useSSO === true && !$this->ldapSaslSupported()) {
529*59752844SAnders Sandblad              throw new adLDAPException('No LDAP SASL support for PHP.  See: http://php.net/ldap_sasl_bind');
53076ce1169SAndreas Gohr          }
53176ce1169SAndreas Gohr          $this->useSSO = $useSSO;
53276ce1169SAndreas Gohr    }
53376ce1169SAndreas Gohr
53476ce1169SAndreas Gohr    /**
53576ce1169SAndreas Gohr    * Get the SSO setting
53676ce1169SAndreas Gohr    *
53776ce1169SAndreas Gohr    * @return bool
53876ce1169SAndreas Gohr    */
53976ce1169SAndreas Gohr    public function getUseSSO()
54076ce1169SAndreas Gohr    {
54176ce1169SAndreas Gohr          return $this->useSSO;
54276ce1169SAndreas Gohr    }
54376ce1169SAndreas Gohr
54476ce1169SAndreas Gohr    /**
54576ce1169SAndreas Gohr    * Set whether to lookup recursive groups
54676ce1169SAndreas Gohr    *
54776ce1169SAndreas Gohr    * @param bool $recursiveGroups
54876ce1169SAndreas Gohr    * @return void
54976ce1169SAndreas Gohr    */
55076ce1169SAndreas Gohr    public function setRecursiveGroups($recursiveGroups)
55176ce1169SAndreas Gohr    {
55276ce1169SAndreas Gohr          $this->recursiveGroups = $recursiveGroups;
55376ce1169SAndreas Gohr    }
55476ce1169SAndreas Gohr
55576ce1169SAndreas Gohr    /**
55676ce1169SAndreas Gohr    * Get the recursive groups setting
55776ce1169SAndreas Gohr    *
55876ce1169SAndreas Gohr    * @return bool
55976ce1169SAndreas Gohr    */
56076ce1169SAndreas Gohr    public function getRecursiveGroups()
56176ce1169SAndreas Gohr    {
56276ce1169SAndreas Gohr          return $this->recursiveGroups;
56376ce1169SAndreas Gohr    }
56476ce1169SAndreas Gohr
56576ce1169SAndreas Gohr    /**
56676ce1169SAndreas Gohr    * Default Constructor
56776ce1169SAndreas Gohr    *
56876ce1169SAndreas Gohr    * Tries to bind to the AD domain over LDAP or LDAPs
56976ce1169SAndreas Gohr    *
57076ce1169SAndreas Gohr    * @param array $options Array of options to pass to the constructor
57176ce1169SAndreas Gohr    * @throws Exception - if unable to bind to Domain Controller
57276ce1169SAndreas Gohr    * @return bool
57376ce1169SAndreas Gohr    */
57476ce1169SAndreas Gohr    function __construct($options = array()) {
57576ce1169SAndreas Gohr        // You can specifically overide any of the default configuration options setup above
57676ce1169SAndreas Gohr        if (count($options) > 0) {
57776ce1169SAndreas Gohr            if (array_key_exists("account_suffix",$options)){ $this->accountSuffix = $options["account_suffix"]; }
57876ce1169SAndreas Gohr            if (array_key_exists("base_dn",$options)){ $this->baseDn = $options["base_dn"]; }
57976ce1169SAndreas Gohr            if (array_key_exists("domain_controllers",$options)){
58076ce1169SAndreas Gohr                if (!is_array($options["domain_controllers"])) {
58176ce1169SAndreas Gohr                    throw new adLDAPException('[domain_controllers] option must be an array');
58276ce1169SAndreas Gohr                }
58376ce1169SAndreas Gohr                $this->domainControllers = $options["domain_controllers"];
58476ce1169SAndreas Gohr            }
58576ce1169SAndreas Gohr            if (array_key_exists("admin_username",$options)){ $this->adminUsername = $options["admin_username"]; }
58676ce1169SAndreas Gohr            if (array_key_exists("admin_password",$options)){ $this->adminPassword = $options["admin_password"]; }
58776ce1169SAndreas Gohr            if (array_key_exists("real_primarygroup",$options)){ $this->realPrimaryGroup = $options["real_primarygroup"]; }
58876ce1169SAndreas Gohr            if (array_key_exists("use_ssl",$options)){ $this->setUseSSL($options["use_ssl"]); }
58976ce1169SAndreas Gohr            if (array_key_exists("use_tls",$options)){ $this->useTLS = $options["use_tls"]; }
59076ce1169SAndreas Gohr            if (array_key_exists("recursive_groups",$options)){ $this->recursiveGroups = $options["recursive_groups"]; }
59176ce1169SAndreas Gohr            if (array_key_exists("ad_port",$options)){ $this->setPort($options["ad_port"]); }
59276ce1169SAndreas Gohr            if (array_key_exists("sso",$options)) {
59376ce1169SAndreas Gohr                $this->setUseSSO($options["sso"]);
59476ce1169SAndreas Gohr                if (!$this->ldapSaslSupported()) {
59576ce1169SAndreas Gohr                    $this->setUseSSO(false);
59676ce1169SAndreas Gohr                }
59776ce1169SAndreas Gohr            }
59876ce1169SAndreas Gohr        }
59976ce1169SAndreas Gohr
60076ce1169SAndreas Gohr        if ($this->ldapSupported() === false) {
601*59752844SAnders Sandblad            throw new adLDAPException('No LDAP support for PHP.  See: http://php.net/ldap');
60276ce1169SAndreas Gohr        }
60376ce1169SAndreas Gohr
60476ce1169SAndreas Gohr        return $this->connect();
60576ce1169SAndreas Gohr    }
60676ce1169SAndreas Gohr
60776ce1169SAndreas Gohr    /**
60876ce1169SAndreas Gohr    * Default Destructor
60976ce1169SAndreas Gohr    *
61076ce1169SAndreas Gohr    * Closes the LDAP connection
61176ce1169SAndreas Gohr    *
61276ce1169SAndreas Gohr    * @return void
61376ce1169SAndreas Gohr    */
61476ce1169SAndreas Gohr    function __destruct() {
61576ce1169SAndreas Gohr        $this->close();
61676ce1169SAndreas Gohr    }
61776ce1169SAndreas Gohr
61876ce1169SAndreas Gohr    /**
61976ce1169SAndreas Gohr    * Connects and Binds to the Domain Controller
62076ce1169SAndreas Gohr    *
62176ce1169SAndreas Gohr    * @return bool
62276ce1169SAndreas Gohr    */
62376ce1169SAndreas Gohr    public function connect()
62476ce1169SAndreas Gohr    {
62576ce1169SAndreas Gohr        // Connect to the AD/LDAP server as the username/password
62676ce1169SAndreas Gohr        $domainController = $this->randomController();
62776ce1169SAndreas Gohr        if ($this->useSSL) {
62876ce1169SAndreas Gohr            $this->ldapConnection = ldap_connect("ldaps://" . $domainController, $this->adPort);
62976ce1169SAndreas Gohr        } else {
63076ce1169SAndreas Gohr            $this->ldapConnection = ldap_connect($domainController, $this->adPort);
63176ce1169SAndreas Gohr        }
63276ce1169SAndreas Gohr
63376ce1169SAndreas Gohr        // Set some ldap options for talking to AD
63476ce1169SAndreas Gohr        ldap_set_option($this->ldapConnection, LDAP_OPT_PROTOCOL_VERSION, 3);
63576ce1169SAndreas Gohr        ldap_set_option($this->ldapConnection, LDAP_OPT_REFERRALS, 0);
63676ce1169SAndreas Gohr
63776ce1169SAndreas Gohr        if ($this->useTLS) {
63876ce1169SAndreas Gohr            ldap_start_tls($this->ldapConnection);
63976ce1169SAndreas Gohr        }
64076ce1169SAndreas Gohr
64176ce1169SAndreas Gohr        // Bind as a domain admin if they've set it up
64276ce1169SAndreas Gohr        if ($this->adminUsername !== NULL && $this->adminPassword !== NULL) {
64376ce1169SAndreas Gohr            $this->ldapBind = @ldap_bind($this->ldapConnection, $this->adminUsername . $this->accountSuffix, $this->adminPassword);
64476ce1169SAndreas Gohr            if (!$this->ldapBind) {
64576ce1169SAndreas Gohr                if ($this->useSSL && !$this->useTLS) {
64676ce1169SAndreas Gohr                    // If you have problems troubleshooting, remove the @ character from the ldapldapBind command above to get the actual error message
64776ce1169SAndreas Gohr                    throw new adLDAPException('Bind to Active Directory failed. Either the LDAPs connection failed or the login credentials are incorrect. AD said: ' . $this->getLastError());
64876ce1169SAndreas Gohr                }
64976ce1169SAndreas Gohr                else {
65076ce1169SAndreas Gohr                    throw new adLDAPException('Bind to Active Directory failed. Check the login credentials and/or server details. AD said: ' . $this->getLastError());
65176ce1169SAndreas Gohr                }
65276ce1169SAndreas Gohr            }
65376ce1169SAndreas Gohr        }
65476ce1169SAndreas Gohr        if ($this->useSSO && $_SERVER['REMOTE_USER'] && $this->adminUsername === null && $_SERVER['KRB5CCNAME']) {
65576ce1169SAndreas Gohr            putenv("KRB5CCNAME=" . $_SERVER['KRB5CCNAME']);
65676ce1169SAndreas Gohr            $this->ldapBind = @ldap_sasl_bind($this->ldapConnection, NULL, NULL, "GSSAPI");
65776ce1169SAndreas Gohr            if (!$this->ldapBind){
65876ce1169SAndreas Gohr                throw new adLDAPException('Rebind to Active Directory failed. AD said: ' . $this->getLastError());
65976ce1169SAndreas Gohr            }
66076ce1169SAndreas Gohr            else {
66176ce1169SAndreas Gohr                return true;
66276ce1169SAndreas Gohr            }
66376ce1169SAndreas Gohr        }
66476ce1169SAndreas Gohr
66576ce1169SAndreas Gohr
66676ce1169SAndreas Gohr        if ($this->baseDn == NULL) {
66776ce1169SAndreas Gohr            $this->baseDn = $this->findBaseDn();
66876ce1169SAndreas Gohr        }
66976ce1169SAndreas Gohr
67076ce1169SAndreas Gohr        return true;
67176ce1169SAndreas Gohr    }
67276ce1169SAndreas Gohr
67376ce1169SAndreas Gohr    /**
67476ce1169SAndreas Gohr    * Closes the LDAP connection
67576ce1169SAndreas Gohr    *
67676ce1169SAndreas Gohr    * @return void
67776ce1169SAndreas Gohr    */
67876ce1169SAndreas Gohr    public function close() {
67976ce1169SAndreas Gohr        if ($this->ldapConnection) {
68076ce1169SAndreas Gohr            @ldap_close($this->ldapConnection);
68176ce1169SAndreas Gohr        }
68276ce1169SAndreas Gohr    }
68376ce1169SAndreas Gohr
68476ce1169SAndreas Gohr    /**
68576ce1169SAndreas Gohr    * Validate a user's login credentials
68676ce1169SAndreas Gohr    *
68776ce1169SAndreas Gohr    * @param string $username A user's AD username
68876ce1169SAndreas Gohr    * @param string $password A user's AD password
68976ce1169SAndreas Gohr    * @param bool optional $preventRebind
69076ce1169SAndreas Gohr    * @return bool
69176ce1169SAndreas Gohr    */
69276ce1169SAndreas Gohr    public function authenticate($username, $password, $preventRebind = false) {
69376ce1169SAndreas Gohr        // Prevent null binding
69476ce1169SAndreas Gohr        if ($username === NULL || $password === NULL) { return false; }
69576ce1169SAndreas Gohr        if (empty($username) || empty($password)) { return false; }
69676ce1169SAndreas Gohr
69776ce1169SAndreas Gohr        // Allow binding over SSO for Kerberos
69876ce1169SAndreas Gohr        if ($this->useSSO && $_SERVER['REMOTE_USER'] && $_SERVER['REMOTE_USER'] == $username && $this->adminUsername === NULL && $_SERVER['KRB5CCNAME']) {
69976ce1169SAndreas Gohr            putenv("KRB5CCNAME=" . $_SERVER['KRB5CCNAME']);
70076ce1169SAndreas Gohr            $this->ldapBind = @ldap_sasl_bind($this->ldapConnection, NULL, NULL, "GSSAPI");
70176ce1169SAndreas Gohr            if (!$this->ldapBind) {
70276ce1169SAndreas Gohr                throw new adLDAPException('Rebind to Active Directory failed. AD said: ' . $this->getLastError());
70376ce1169SAndreas Gohr            }
70476ce1169SAndreas Gohr            else {
70576ce1169SAndreas Gohr                return true;
70676ce1169SAndreas Gohr            }
70776ce1169SAndreas Gohr        }
70876ce1169SAndreas Gohr
70976ce1169SAndreas Gohr        // Bind as the user
71076ce1169SAndreas Gohr        $ret = true;
71176ce1169SAndreas Gohr        $this->ldapBind = @ldap_bind($this->ldapConnection, $username . $this->accountSuffix, $password);
71276ce1169SAndreas Gohr        if (!$this->ldapBind){
71376ce1169SAndreas Gohr            $ret = false;
71476ce1169SAndreas Gohr        }
71576ce1169SAndreas Gohr
71676ce1169SAndreas Gohr        // Cnce we've checked their details, kick back into admin mode if we have it
71776ce1169SAndreas Gohr        if ($this->adminUsername !== NULL && !$preventRebind) {
71876ce1169SAndreas Gohr            $this->ldapBind = @ldap_bind($this->ldapConnection, $this->adminUsername . $this->accountSuffix , $this->adminPassword);
71976ce1169SAndreas Gohr            if (!$this->ldapBind){
72076ce1169SAndreas Gohr                // This should never happen in theory
72176ce1169SAndreas Gohr                throw new adLDAPException('Rebind to Active Directory failed. AD said: ' . $this->getLastError());
72276ce1169SAndreas Gohr            }
72376ce1169SAndreas Gohr        }
72476ce1169SAndreas Gohr
72576ce1169SAndreas Gohr        return $ret;
72676ce1169SAndreas Gohr    }
72776ce1169SAndreas Gohr
72876ce1169SAndreas Gohr    /**
72976ce1169SAndreas Gohr    * Find the Base DN of your domain controller
73076ce1169SAndreas Gohr    *
73176ce1169SAndreas Gohr    * @return string
73276ce1169SAndreas Gohr    */
73376ce1169SAndreas Gohr    public function findBaseDn()
73476ce1169SAndreas Gohr    {
73576ce1169SAndreas Gohr        $namingContext = $this->getRootDse(array('defaultnamingcontext'));
73676ce1169SAndreas Gohr        return $namingContext[0]['defaultnamingcontext'][0];
73776ce1169SAndreas Gohr    }
73876ce1169SAndreas Gohr
73976ce1169SAndreas Gohr    /**
74076ce1169SAndreas Gohr    * Get the RootDSE properties from a domain controller
74176ce1169SAndreas Gohr    *
74276ce1169SAndreas Gohr    * @param array $attributes The attributes you wish to query e.g. defaultnamingcontext
74376ce1169SAndreas Gohr    * @return array
74476ce1169SAndreas Gohr    */
74576ce1169SAndreas Gohr    public function getRootDse($attributes = array("*", "+")) {
74676ce1169SAndreas Gohr        if (!$this->ldapBind){ return (false); }
74776ce1169SAndreas Gohr
74876ce1169SAndreas Gohr        $sr = @ldap_read($this->ldapConnection, NULL, 'objectClass=*', $attributes);
74976ce1169SAndreas Gohr        $entries = @ldap_get_entries($this->ldapConnection, $sr);
75076ce1169SAndreas Gohr        return $entries;
75176ce1169SAndreas Gohr    }
75276ce1169SAndreas Gohr
75376ce1169SAndreas Gohr    /**
75476ce1169SAndreas Gohr    * Get last error from Active Directory
75576ce1169SAndreas Gohr    *
75676ce1169SAndreas Gohr    * This function gets the last message from Active Directory
75776ce1169SAndreas Gohr    * This may indeed be a 'Success' message but if you get an unknown error
75876ce1169SAndreas Gohr    * it might be worth calling this function to see what errors were raised
75976ce1169SAndreas Gohr    *
76076ce1169SAndreas Gohr    * return string
76176ce1169SAndreas Gohr    */
76276ce1169SAndreas Gohr    public function getLastError() {
76376ce1169SAndreas Gohr        return @ldap_error($this->ldapConnection);
76476ce1169SAndreas Gohr    }
76576ce1169SAndreas Gohr
76676ce1169SAndreas Gohr    /**
76776ce1169SAndreas Gohr    * Detect LDAP support in php
76876ce1169SAndreas Gohr    *
76976ce1169SAndreas Gohr    * @return bool
77076ce1169SAndreas Gohr    */
77176ce1169SAndreas Gohr    protected function ldapSupported()
77276ce1169SAndreas Gohr    {
77376ce1169SAndreas Gohr        if (!function_exists('ldap_connect')) {
77476ce1169SAndreas Gohr            return false;
77576ce1169SAndreas Gohr        }
77676ce1169SAndreas Gohr        return true;
77776ce1169SAndreas Gohr    }
77876ce1169SAndreas Gohr
77976ce1169SAndreas Gohr    /**
78076ce1169SAndreas Gohr    * Detect ldap_sasl_bind support in PHP
78176ce1169SAndreas Gohr    *
78276ce1169SAndreas Gohr    * @return bool
78376ce1169SAndreas Gohr    */
78476ce1169SAndreas Gohr    protected function ldapSaslSupported()
78576ce1169SAndreas Gohr    {
78676ce1169SAndreas Gohr        if (!function_exists('ldap_sasl_bind')) {
78776ce1169SAndreas Gohr            return false;
78876ce1169SAndreas Gohr        }
78976ce1169SAndreas Gohr        return true;
79076ce1169SAndreas Gohr    }
79176ce1169SAndreas Gohr
79276ce1169SAndreas Gohr    /**
79376ce1169SAndreas Gohr    * Schema
79476ce1169SAndreas Gohr    *
79576ce1169SAndreas Gohr    * @param array $attributes Attributes to be queried
79676ce1169SAndreas Gohr    * @return array
79776ce1169SAndreas Gohr    */
79876ce1169SAndreas Gohr    public function adldap_schema($attributes){
79976ce1169SAndreas Gohr
80076ce1169SAndreas Gohr        // LDAP doesn't like NULL attributes, only set them if they have values
80176ce1169SAndreas Gohr        // If you wish to remove an attribute you should set it to a space
80276ce1169SAndreas Gohr        // TO DO: Adapt user_modify to use ldap_mod_delete to remove a NULL attribute
80376ce1169SAndreas Gohr        $mod=array();
80476ce1169SAndreas Gohr
80576ce1169SAndreas Gohr        // Check every attribute to see if it contains 8bit characters and then UTF8 encode them
80676ce1169SAndreas Gohr        array_walk($attributes, array($this, 'encode8bit'));
80776ce1169SAndreas Gohr
80876ce1169SAndreas Gohr        if ($attributes["address_city"]){ $mod["l"][0]=$attributes["address_city"]; }
80976ce1169SAndreas Gohr        if ($attributes["address_code"]){ $mod["postalCode"][0]=$attributes["address_code"]; }
81076ce1169SAndreas Gohr        //if ($attributes["address_country"]){ $mod["countryCode"][0]=$attributes["address_country"]; } // use country codes?
81176ce1169SAndreas Gohr        if ($attributes["address_country"]){ $mod["c"][0]=$attributes["address_country"]; }
81276ce1169SAndreas Gohr        if ($attributes["address_pobox"]){ $mod["postOfficeBox"][0]=$attributes["address_pobox"]; }
81376ce1169SAndreas Gohr        if ($attributes["address_state"]){ $mod["st"][0]=$attributes["address_state"]; }
81476ce1169SAndreas Gohr        if ($attributes["address_street"]){ $mod["streetAddress"][0]=$attributes["address_street"]; }
81576ce1169SAndreas Gohr        if ($attributes["company"]){ $mod["company"][0]=$attributes["company"]; }
81676ce1169SAndreas Gohr        if ($attributes["change_password"]){ $mod["pwdLastSet"][0]=0; }
81776ce1169SAndreas Gohr        if ($attributes["department"]){ $mod["department"][0]=$attributes["department"]; }
81876ce1169SAndreas Gohr        if ($attributes["description"]){ $mod["description"][0]=$attributes["description"]; }
81976ce1169SAndreas Gohr        if ($attributes["display_name"]){ $mod["displayName"][0]=$attributes["display_name"]; }
82076ce1169SAndreas Gohr        if ($attributes["email"]){ $mod["mail"][0]=$attributes["email"]; }
82176ce1169SAndreas Gohr        if ($attributes["expires"]){ $mod["accountExpires"][0]=$attributes["expires"]; } //unix epoch format?
82276ce1169SAndreas Gohr        if ($attributes["firstname"]){ $mod["givenName"][0]=$attributes["firstname"]; }
82376ce1169SAndreas Gohr        if ($attributes["home_directory"]){ $mod["homeDirectory"][0]=$attributes["home_directory"]; }
82476ce1169SAndreas Gohr        if ($attributes["home_drive"]){ $mod["homeDrive"][0]=$attributes["home_drive"]; }
82576ce1169SAndreas Gohr        if ($attributes["initials"]){ $mod["initials"][0]=$attributes["initials"]; }
82676ce1169SAndreas Gohr        if ($attributes["logon_name"]){ $mod["userPrincipalName"][0]=$attributes["logon_name"]; }
82776ce1169SAndreas Gohr        if ($attributes["manager"]){ $mod["manager"][0]=$attributes["manager"]; }  //UNTESTED ***Use DistinguishedName***
82876ce1169SAndreas Gohr        if ($attributes["office"]){ $mod["physicalDeliveryOfficeName"][0]=$attributes["office"]; }
82976ce1169SAndreas Gohr        if ($attributes["password"]){ $mod["unicodePwd"][0]=$this->user()->encodePassword($attributes["password"]); }
83076ce1169SAndreas Gohr        if ($attributes["profile_path"]){ $mod["profilepath"][0]=$attributes["profile_path"]; }
83176ce1169SAndreas Gohr        if ($attributes["script_path"]){ $mod["scriptPath"][0]=$attributes["script_path"]; }
83276ce1169SAndreas Gohr        if ($attributes["surname"]){ $mod["sn"][0]=$attributes["surname"]; }
83376ce1169SAndreas Gohr        if ($attributes["title"]){ $mod["title"][0]=$attributes["title"]; }
83476ce1169SAndreas Gohr        if ($attributes["telephone"]){ $mod["telephoneNumber"][0]=$attributes["telephone"]; }
83576ce1169SAndreas Gohr        if ($attributes["mobile"]){ $mod["mobile"][0]=$attributes["mobile"]; }
83676ce1169SAndreas Gohr        if ($attributes["pager"]){ $mod["pager"][0]=$attributes["pager"]; }
83776ce1169SAndreas Gohr        if ($attributes["ipphone"]){ $mod["ipphone"][0]=$attributes["ipphone"]; }
83876ce1169SAndreas Gohr        if ($attributes["web_page"]){ $mod["wWWHomePage"][0]=$attributes["web_page"]; }
83976ce1169SAndreas Gohr        if ($attributes["fax"]){ $mod["facsimileTelephoneNumber"][0]=$attributes["fax"]; }
84076ce1169SAndreas Gohr        if ($attributes["enabled"]){ $mod["userAccountControl"][0]=$attributes["enabled"]; }
84176ce1169SAndreas Gohr        if ($attributes["homephone"]){ $mod["homephone"][0]=$attributes["homephone"]; }
84276ce1169SAndreas Gohr
84376ce1169SAndreas Gohr        // Distribution List specific schema
84476ce1169SAndreas Gohr        if ($attributes["group_sendpermission"]){ $mod["dlMemSubmitPerms"][0]=$attributes["group_sendpermission"]; }
84576ce1169SAndreas Gohr        if ($attributes["group_rejectpermission"]){ $mod["dlMemRejectPerms"][0]=$attributes["group_rejectpermission"]; }
84676ce1169SAndreas Gohr
84776ce1169SAndreas Gohr        // Exchange Schema
84876ce1169SAndreas Gohr        if ($attributes["exchange_homemdb"]){ $mod["homeMDB"][0]=$attributes["exchange_homemdb"]; }
84976ce1169SAndreas Gohr        if ($attributes["exchange_mailnickname"]){ $mod["mailNickname"][0]=$attributes["exchange_mailnickname"]; }
85076ce1169SAndreas Gohr        if ($attributes["exchange_proxyaddress"]){ $mod["proxyAddresses"][0]=$attributes["exchange_proxyaddress"]; }
85176ce1169SAndreas Gohr        if ($attributes["exchange_usedefaults"]){ $mod["mDBUseDefaults"][0]=$attributes["exchange_usedefaults"]; }
85276ce1169SAndreas Gohr        if ($attributes["exchange_policyexclude"]){ $mod["msExchPoliciesExcluded"][0]=$attributes["exchange_policyexclude"]; }
85376ce1169SAndreas Gohr        if ($attributes["exchange_policyinclude"]){ $mod["msExchPoliciesIncluded"][0]=$attributes["exchange_policyinclude"]; }
85476ce1169SAndreas Gohr        if ($attributes["exchange_addressbook"]){ $mod["showInAddressBook"][0]=$attributes["exchange_addressbook"]; }
85576ce1169SAndreas Gohr        if ($attributes["exchange_altrecipient"]){ $mod["altRecipient"][0]=$attributes["exchange_altrecipient"]; }
85676ce1169SAndreas Gohr        if ($attributes["exchange_deliverandredirect"]){ $mod["deliverAndRedirect"][0]=$attributes["exchange_deliverandredirect"]; }
85776ce1169SAndreas Gohr
85876ce1169SAndreas Gohr        // This schema is designed for contacts
85976ce1169SAndreas Gohr        if ($attributes["exchange_hidefromlists"]){ $mod["msExchHideFromAddressLists"][0]=$attributes["exchange_hidefromlists"]; }
86076ce1169SAndreas Gohr        if ($attributes["contact_email"]){ $mod["targetAddress"][0]=$attributes["contact_email"]; }
86176ce1169SAndreas Gohr
86276ce1169SAndreas Gohr        //echo ("<pre>"); print_r($mod);
86376ce1169SAndreas Gohr        /*
86476ce1169SAndreas Gohr        // modifying a name is a bit fiddly
86576ce1169SAndreas Gohr        if ($attributes["firstname"] && $attributes["surname"]){
86676ce1169SAndreas Gohr            $mod["cn"][0]=$attributes["firstname"]." ".$attributes["surname"];
86776ce1169SAndreas Gohr            $mod["displayname"][0]=$attributes["firstname"]." ".$attributes["surname"];
86876ce1169SAndreas Gohr            $mod["name"][0]=$attributes["firstname"]." ".$attributes["surname"];
86976ce1169SAndreas Gohr        }
87076ce1169SAndreas Gohr        */
87176ce1169SAndreas Gohr
87276ce1169SAndreas Gohr        if (count($mod)==0){ return (false); }
87376ce1169SAndreas Gohr        return ($mod);
87476ce1169SAndreas Gohr    }
87576ce1169SAndreas Gohr
87676ce1169SAndreas Gohr    /**
87776ce1169SAndreas Gohr    * Convert 8bit characters e.g. accented characters to UTF8 encoded characters
87876ce1169SAndreas Gohr    */
87976ce1169SAndreas Gohr    protected function encode8Bit(&$item, $key) {
88076ce1169SAndreas Gohr        $encode = false;
88176ce1169SAndreas Gohr        if (is_string($item)) {
88276ce1169SAndreas Gohr            for ($i=0; $i<strlen($item); $i++) {
88376ce1169SAndreas Gohr                if (ord($item[$i]) >> 7) {
88476ce1169SAndreas Gohr                    $encode = true;
88576ce1169SAndreas Gohr                }
88676ce1169SAndreas Gohr            }
88776ce1169SAndreas Gohr        }
88876ce1169SAndreas Gohr        if ($encode === true && $key != 'password') {
88976ce1169SAndreas Gohr            $item = utf8_encode($item);
89076ce1169SAndreas Gohr        }
89176ce1169SAndreas Gohr    }
89276ce1169SAndreas Gohr
89376ce1169SAndreas Gohr    /**
89476ce1169SAndreas Gohr    * Select a random domain controller from your domain controller array
89576ce1169SAndreas Gohr    *
89676ce1169SAndreas Gohr    * @return string
89776ce1169SAndreas Gohr    */
89876ce1169SAndreas Gohr    protected function randomController()
89976ce1169SAndreas Gohr    {
90076ce1169SAndreas Gohr        mt_srand(doubleval(microtime()) * 100000000); // For older PHP versions
90176ce1169SAndreas Gohr        /*if (sizeof($this->domainControllers) > 1) {
90276ce1169SAndreas Gohr            $adController = $this->domainControllers[array_rand($this->domainControllers)];
90376ce1169SAndreas Gohr            // Test if the controller is responding to pings
90476ce1169SAndreas Gohr            $ping = $this->pingController($adController);
90576ce1169SAndreas Gohr            if ($ping === false) {
90676ce1169SAndreas Gohr                // Find the current key in the domain controllers array
90776ce1169SAndreas Gohr                $key = array_search($adController, $this->domainControllers);
90876ce1169SAndreas Gohr                // Remove it so that we don't end up in a recursive loop
90976ce1169SAndreas Gohr                unset($this->domainControllers[$key]);
91076ce1169SAndreas Gohr                // Select a new controller
91176ce1169SAndreas Gohr                return $this->randomController();
91276ce1169SAndreas Gohr            }
91376ce1169SAndreas Gohr            else {
91476ce1169SAndreas Gohr                return ($adController);
91576ce1169SAndreas Gohr            }
91676ce1169SAndreas Gohr        } */
91776ce1169SAndreas Gohr        return $this->domainControllers[array_rand($this->domainControllers)];
91876ce1169SAndreas Gohr    }
91976ce1169SAndreas Gohr
92076ce1169SAndreas Gohr    /**
92176ce1169SAndreas Gohr    * Test basic connectivity to controller
92276ce1169SAndreas Gohr    *
92376ce1169SAndreas Gohr    * @return bool
92476ce1169SAndreas Gohr    */
92576ce1169SAndreas Gohr    protected function pingController($host) {
92676ce1169SAndreas Gohr        $port = $this->adPort;
92776ce1169SAndreas Gohr        fsockopen($host, $port, $errno, $errstr, 10);
92876ce1169SAndreas Gohr        if ($errno > 0) {
92976ce1169SAndreas Gohr            return false;
93076ce1169SAndreas Gohr        }
93176ce1169SAndreas Gohr        return true;
93276ce1169SAndreas Gohr    }
93376ce1169SAndreas Gohr
93476ce1169SAndreas Gohr}
93576ce1169SAndreas Gohr
93676ce1169SAndreas Gohr/**
93776ce1169SAndreas Gohr* adLDAP Exception Handler
93876ce1169SAndreas Gohr*
93976ce1169SAndreas Gohr* Exceptions of this type are thrown on bind failure or when SSL is required but not configured
94076ce1169SAndreas Gohr* Example:
94176ce1169SAndreas Gohr* try {
94276ce1169SAndreas Gohr*   $adldap = new adLDAP();
94376ce1169SAndreas Gohr* }
94476ce1169SAndreas Gohr* catch (adLDAPException $e) {
94576ce1169SAndreas Gohr*   echo $e;
94676ce1169SAndreas Gohr*   exit();
94776ce1169SAndreas Gohr* }
94876ce1169SAndreas Gohr*/
94976ce1169SAndreas Gohrclass adLDAPException extends Exception {}
950