1 <?php 2 /** 3 * simple class for LDAP authentification 4 * 5 Copyright (C) 2013 Petr Palas 6 7 This program is free software; you can redistribute it and/or 8 modify it under the terms of the GNU General Public License 9 as published by the Free Software Foundation; either version 2 10 of the License, or (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program; if not, write to the Free Software 19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 20 * inspired by http://samjlevy.com/2010/09/php-login-script-using-ldap-verify-group-membership/ 21 */ 22 23 namespace LDAP; 24 25 use Exception; 26 27 class auth { 28 /** 29 * url or ip of ldap server 30 * @var type string 31 */ 32 protected $ldap_host; 33 /** 34 * active directory DN 35 * @var type string 36 */ 37 protected $ldap_dn; 38 /** 39 * target user group 40 * @var type string 41 */ 42 protected $ldap_user_group; 43 /** 44 * manager group (shud contain users with management access) 45 * @var type string 46 */ 47 protected $ldap_manager_group; 48 /** 49 * contains email domain like "@somedomain.com" 50 * @var type string 51 */ 52 protected $ldap_usr_dom; 53 54 /** 55 * countains connection resource 56 * @var type resource 57 */ 58 protected $ldap; 59 60 /** 61 * contains status text 62 * if exeption is thrown msg contains this string 63 * @var type string 64 */ 65 public $status; 66 /** 67 * contains result array if ldap_search is succesfull 68 * @var type array 69 */ 70 public $result; 71 /** 72 * contains auth state 0=unathrized 1=authorized 73 * @var type int 74 */ 75 public $auth=0; 76 /** 77 * contains access level 0=none or unathorized 1=user 2=managment acc 78 * @var type int 79 */ 80 public $access=0; 81 82 /** 83 * contains username after user init 84 * @var type string 85 */ 86 public $user; 87 88 /** 89 * contain user password after user init 90 * @var type string 91 */ 92 protected $password; 93 94 /** 95 * Exeptions code constants 96 */ 97 const ERROR_WRONG_USER_GROUP=2; 98 const ERROR_CANT_AUTH=1; 99 const ERROR_CANT_SEARCH=3; 100 const ERROR_IMG_DECODE=4; 101 const ERROR_CANT_CONNECT=5; 102 103 /** 104 * loads passed configuration in case of the ldap_usr_dom it makes sure that this strings begins with '@' 105 * @param type $ldap_host 106 * @param type $ldap_dn 107 * @param type $ldap_user_group 108 * @param type $ldap_manager_group 109 * @param type $ldap_usr_dom 110 */ 111 function __construct($ldap_host,$ldap_dn,$ldap_user_group,$ldap_manager_group,$ldap_usr_dom) { 112 $this->ldap_host=$ldap_host; 113 $this->ldap_dn=$ldap_dn; 114 $this->ldap_user_group=$ldap_user_group; 115 $this->ldap_manager_group=$ldap_manager_group; 116 $this->ldap_usr_dom= '@'.trim($ldap_usr_dom,'@'); 117 } 118 119 /** 120 * well destructor :P 121 * just in case there is opened connection to LDAP while destructing this class 122 */ 123 public function __destruct() { 124 @ldap_unbind($this->ldap); 125 } 126 127 /** 128 * dumps result array for debug enclosed in pre tag 129 * Wont terminate script! 130 */ 131 public function dump_resut() { 132 echo '<pre>'; 133 print_r($this->result,FALSE); 134 echo '</pre>'; 135 } 136 137 /** 138 * Inits connection to LDAP server throws exeption on failure 139 * @return boolean 140 * @throws Exception 141 */ 142 protected function init_connection(){ 143 $this->ldap=ldap_connect($this->ldap_host,3268); 144 if($this->ldap){ 145 $this->status='connected :)'; 146 ldap_set_option($this->ldap, LDAP_OPT_PROTOCOL_VERSION,3); 147 ldap_set_option($this->ldap, LDAP_OPT_REFERRALS,0); 148 } 149 else { 150 //TODO: PHP actualy dont check if there is LDAP present on the other end nor it will fail if target host is unreachable. So I need some work around that :( 151 $this->status='Cant connect to LDAP'; 152 throw new Exception($this->status, self::ERROR_CANT_CONNECT); 153 } 154 return TRUE; 155 } 156 157 public function userInit($user,$password) { 158 $this->user=$user; 159 $this->password=$password; 160 161 return TRUE; 162 } 163 164 /** 165 * Converts Binary string (like thumbnail from LDAP to base64 datastring for display 166 * @param type $file 167 * @param type $mime 168 * @return type base64 datastring 169 */ 170 protected function data_uri($file, $mime) { 171 $base64 = base64_encode($file); 172 return ('data:' . $mime . ';base64,' . $base64); 173 } 174 175 /** 176 * Gets LDAP thumbnail img 177 * @param type $user 178 * @param type $password 179 * @param type $raw if TRUE method will return raw binary string instead of base64 encoded with mime 180 * @return type base64 datatring of the thumbnail 181 * @throws Exception 182 */ 183 public function getLDAPimg($user=null,$password=null,$raw=FALSE) { 184 $this->refreshCredentials($user, $password); 185 //since conection is one off we need to get it 186 $this->init_connection(); 187 188 $bind = @ldap_bind($this->ldap, $user . $this->ldap_usr_dom, $password);//ldap_bind($this->ldap, $this->ldap_dn, $password); 189 190 if($bind){ 191 $filter = "(sAMAccountName=" . $user . ")"; 192 $attr = array("thumbnailphoto"); 193 $result = @ldap_search($this->ldap, $this->ldap_dn, $filter, $attr); 194 if($result==FALSE){ 195 throw new Exception("Unable to search LDAP server. Reason: ". ldap_error($this->ldap), self::ERROR_CANT_SEARCH); 196 } 197 $entry= ldap_first_entry($this->ldap, $result); 198 199 if ($entry) { 200 $info = @ldap_get_values_len($this->ldap, $entry, "thumbnailphoto"); 201 if(!$info){ 202 throw new Exception("Unable to decode thumbnail. Error: ". ldap_error($this->ldap), self::ERROR_IMG_DECODE); 203 } 204 //echo '<img src="'.$this->data_uri($info[0], 'image/png').'">'; 205 } 206 207 208 if(!$raw){ 209 return $this->data_uri($info[0], 'image/png'); 210 } 211 else{ 212 return $info[0]; 213 } 214 } 215 else { 216 // invalid name or password 217 $this->status='Cant authenticate for search on LDAP'; 218 throw new Exception($this->status.' '. ldap_error($this->ldap), self::ERROR_CANT_AUTH); 219 } 220 ldap_unbind($this->ldap); 221 } 222 223 /** 224 * Tries to authenticate suplied user with suplied pass 225 * @param type $user 226 * @param type $password 227 * @return boolean 228 * @throws Exception 229 */ 230 public function authenticate($user=null, $password=null) { 231 $this->refreshCredentials($user, $password); 232 //since conection is one off we need to get it 233 $this->init_connection(); 234 235 // verify user and password 236 $bind = @ldap_bind($this->ldap, $user . $this->ldap_usr_dom, $password); 237 238 239 if($bind) { 240 // valid 241 // check presence in groups 242 $filter = "(sAMAccountName=" . $user . ")"; 243 $attr = array("memberof"); 244 $result = @ldap_search($this->ldap, $this->ldap_dn, $filter, $attr); 245 if($result==FALSE){ 246 throw new Exception("Unable to search LDAP server. Reason: ". ldap_error($this->ldap), self::ERROR_CANT_SEARCH); 247 } 248 $entries = ldap_get_entries($this->ldap, $result); 249 250 //save result for future use 251 $this->result=$entries; 252 253 254 255 $access = 0; 256 257 // check groups 258 foreach($entries[0]['memberof'] as $grps) { 259 // is manager, break loop 260 if (strpos($grps, $this->ldap_manager_group)) { $access = 2; break; } 261 262 // is user 263 if (strpos($grps, $this->ldap_user_group)) $access = 1; 264 } 265 266 if ($access != 0) { 267 // establish result vars 268 269 $this->status='Authenticated'; 270 $this->access=$access; 271 $this->user= $user; 272 $this->auth=1; 273 return true; 274 } else { 275 // user has no rights 276 $this->access=$access; 277 $this->user= $user; 278 $this->auth=1; 279 $this->status='User exists but not part of the target group'; 280 throw new Exception($this->status.' '. ldap_error($this->ldap), self::ERROR_WRONG_USER_GROUP); 281 } 282 283 } else { 284 // invalid name or password 285 $this->status='Cant authenticate for search on LDAP'; 286 throw new Exception($this->status.' '. ldap_error($this->ldap), self::ERROR_CANT_AUTH); 287 } 288 ldap_unbind($this->ldap); 289 } 290 291 /** 292 * Saves new credentials if we got new or sets the old ones into referenced vars 293 * @param type $user Reference to var that shuld contain username or null 294 * @param type $password Reference to var that shuld contain password or null 295 */ 296 private function refreshCredentials(&$user,&$password) { 297 $newCredentials=TRUE; 298 //since we cant set those in param def 299 if($password===null){$password= $this->password;$newCredentials=FALSE;} 300 if($user===null){$user= $this->user;$newCredentials=FALSE;} 301 //store user pass and name for future use 302 if($newCredentials){$this->userInit($user, $password);} 303 } 304 305 }