Commit ae0000d5168ebbdcf4e44e23f8ec43d7aa11a619

Authored by Michael Joseph
1 parent 557ab992

moved ldap class to lib/authentication


git-svn-id: https://kt-dms.svn.sourceforge.net/svnroot/kt-dms/trunk@255 c91229c3-7414-0410-bfa2-8a42b809f60b
lib/authentication/class.AuthLdap.php 0 → 100644
  1 +<?php
  2 +/* class.AuthLdap.php , version 0.1
  3 +** Mark Round, December 2002 - http://www.markround.com/unix
  4 +** Provides LDAP authentication and user functions.
  5 +**
  6 +** Not intended as a full-blown LDAP access class - but it does provide
  7 +** several useful functions for dealing with users.
  8 +** Note - this works out of the box on Sun's iPlanet Directory Server - but
  9 +** an ACL has to be defined giving all users the ability to change their
  10 +** password (userPassword attribute).
  11 +** See the README file for more information and examples.
  12 +**
  13 +** This program is free software; you can redistribute it and/or modify
  14 +** it under the terms of the GNU General Public License as published by
  15 +** the Free Software Foundation; either version 2 of the License, or
  16 +** (at your option) any later version.
  17 +**
  18 +** This program is distributed in the hope that it will be useful,
  19 +** but WITHOUT ANY WARRANTY; without even the implied warranty of
  20 +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  21 +** GNU General Public License for more details.
  22 +**
  23 +** You should have received a copy of the GNU General Public License
  24 +** along with this program; if not, write to the Free Software
  25 +** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  26 +*/
  27 +
  28 +class AuthLdap {
  29 +
  30 + // 1.1 Public properties -----------------------------------------------------
  31 +
  32 + var $server; // Array of server IP address or hostnames
  33 + var $dn; // The base DN (e.g. "dc=foo,dc=com")
  34 + var $people; // Where the user records are kept
  35 + var $groups; // Where the group definitions are kept
  36 + var $ldapErrorCode; // The last error code returned by the LDAP server
  37 + var $ldapErrorText; // Text of the error message
  38 +
  39 + // 1.2 Private properties ----------------------------------------------------
  40 +
  41 + var $connection; // The internal LDAP connection handle
  42 + var $result; // Result of any connections etc.
  43 +
  44 + // 2.1 Connection handling methods -------------------------------------------
  45 +
  46 + function connect() {
  47 + /* 2.1.1 : Connects to the server. Just creates a connection which is used
  48 + ** in all later access to the LDAP server. If it can't connect and bind
  49 + ** anonymously, it creates an error code of -1. Returns true if connected,
  50 + ** false if failed. Takes an array of possible servers - if one doesn't work,
  51 + ** it tries the next and so on.
  52 + */
  53 +
  54 + foreach ($this->server as $key => $host) {
  55 + echo "key=$key; host=$host<br>";
  56 + $this->connection = ldap_connect( $host);
  57 + if ( $this->connection) {
  58 + echo "connected<br>";
  59 + // Connected, now try binding....
  60 + if ( $this->result=@ldap_bind( $this->connection)) {
  61 + // Bound OK!
  62 + $this->bound = $host;
  63 + return true;
  64 + }
  65 + } else { echo "not connected<br>";}
  66 + }
  67 +
  68 + $this->ldapErrorCode = -1;
  69 + $this->ldapErrorText = "Unable to connect to any server";
  70 + return false;
  71 +
  72 + }
  73 +
  74 +
  75 +
  76 + function close() {
  77 + /* 2.1.2 : Simply closes the connection set up earlier.
  78 + ** Returns true if OK, false if there was an error.
  79 + */
  80 +
  81 + if ( !@ldap_close( $this->connection)) {
  82 + $this->ldapErrorCode = ldap_errno( $this->connection);
  83 + $this->ldapErrorText = ldap_error( $this->connection);
  84 + return false;
  85 + } else
  86 + return true;
  87 + }
  88 +
  89 +
  90 +
  91 + function bind() {
  92 + /* 2.1.3 : Anonymously binds to the connection. After this is done,
  93 + ** queries and searches can be done - but read-only.
  94 + */
  95 +
  96 + if ( !$this->result=@ldap_bind( $this->connection)) {
  97 + $this->ldapErrorCode = ldap_errno( $this->connection);
  98 + $this->ldapErrorText = ldap_error( $this->connection);
  99 + return false;
  100 + } else
  101 + return true;
  102 + }
  103 +
  104 +
  105 +
  106 + function authBind( $bindDn,$pass) {
  107 + /* 2.1.4 : Binds as an authenticated user, which usually allows for write
  108 + ** access. The FULL dn must be passed. For a directory manager, this is
  109 + ** "cn=Directory Manager" under iPlanet. For a user, it will be something
  110 + ** like "uid=jbloggs,ou=People,dc=foo,dc=com".
  111 + */
  112 + if ( !$this->result = @ldap_bind( $this->connection,$bindDn,$pass)) {
  113 + $this->ldapErrorCode = ldap_errno( $this->connection);
  114 + $this->ldapErrorText = ldap_error( $this->connection);
  115 + return false;
  116 + } else
  117 + return true;
  118 + }
  119 +
  120 + // 2.2 Password methods ------------------------------------------------------
  121 +
  122 + function checkPass( $uname,$pass) {
  123 + /* 2.2.1 : Checks a username and password - does this by logging on to the
  124 + ** server as a user - specified in the DN. There are several reasons why
  125 + ** this login could fail - these are listed below.
  126 + */
  127 +
  128 + /* Construct the full DN, eg:-
  129 + ** "uid=username, ou=People, dc=orgname,dc=com"
  130 + */
  131 + $checkDn = "uid=" .$uname. ", ou=" .$this->people. ", " .$this->dn;
  132 + // Try and connect...
  133 + $this->result = @ldap_bind( $this->connection,$checkDn,$pass);
  134 + if ( $this->result) {
  135 + // Connected OK - login credentials are fine!
  136 + return true;
  137 + } else {
  138 + /* Login failed. Return false, together with the error code and text from
  139 + ** the LDAP server. The common error codes and reasons are listed below :
  140 + ** (for iPlanet, other servers may differ)
  141 + ** 19 - Account locked out (too many invalid login attempts)
  142 + ** 32 - User does not exist
  143 + ** 49 - Wrong password
  144 + ** 53 - Account inactive (manually locked out by administrator)
  145 + */
  146 + $this->ldapErrorCode = ldap_errno( $this->connection);
  147 + $this->ldapErrorText = ldap_error( $this->connection);
  148 + return false;
  149 + }
  150 + }
  151 +
  152 +
  153 +
  154 + function changePass( $uname,$oldPass,$newPass) {
  155 + /* 2.2.2 : Allows a password to be changed. Note that on most LDAP servers,
  156 + ** a new ACL must be defined giving users the ability to modify their
  157 + ** password attribute (userPassword). Otherwise this will fail.
  158 + */
  159 +
  160 + $checkDn = "uid=" .$uname. ", ou=" .$this->people. ", " .$this->dn;
  161 + $this->result = @ldap_bind( $this->connection,$checkDn,$oldPass);
  162 +
  163 + if ( $this->result) {
  164 + // Connected OK - Now modify the password...
  165 + $info["userPassword"] = $newPass;
  166 + $this->result = @ldap_modify( $this->connection, $checkDn, $info);
  167 + if ( $this->result) {
  168 + // Change went OK
  169 + return true;
  170 + } else {
  171 + // Couldn't change password...
  172 + $this->ldapErrorCode = ldap_errno( $this->connection);
  173 + $this->ldapErrorText = ldap_error( $this->connection);
  174 + return false;
  175 + }
  176 + } else {
  177 + // Login failed - see checkPass method for common error codes
  178 + $this->ldapErrorCode = ldap_errno( $this->connection);
  179 + $this->ldapErrorText = ldap_error( $this->connection);
  180 + return false;
  181 + }
  182 + }
  183 +
  184 +
  185 +
  186 + function checkPassAge ( $uname) {
  187 + /* 2.2.3 : Returns days until the password will expire.
  188 + ** We have to explicitly state this is what we want returned from the
  189 + ** LDAP server - by default, it will only send back the "basic"
  190 + ** attributes.
  191 + */
  192 + $results[0] = "passwordexpirationtime";
  193 + $this->result = @ldap_search( $this->connection,$this->dn,"uid=".$uname,$results);
  194 +
  195 + if ( !$info=@ldap_get_entries( $this->connection, $this->result)) {
  196 + $this->ldapErrorCode = ldap_errno( $this->connection);
  197 + $this->ldapErrorText = ldap_error( $this->connection);
  198 + return false;
  199 + } else {
  200 + /* Now work out how many days remaining....
  201 + ** Yes, it's very verbose code but I left it like this so it can easily
  202 + ** be modified for your needs.
  203 + */
  204 + $date = $info[0]["passwordexpirationtime"][0];
  205 + $year = substr( $date,0,4);
  206 + $month = substr( $date,4,2);
  207 + $day = substr( $date,6,2);
  208 + $hour = substr( $date,8,2);
  209 + $min = substr( $date,10,2);
  210 + $sec = substr( $date,12,2);
  211 +
  212 + $timestamp = mktime( $hour,$min,$sec,$month,$day,$year);
  213 + $today = mktime();
  214 + $diff = $timestamp-$today;
  215 + return round( ( ( ( $diff/60)/60)/24));
  216 + }
  217 + }
  218 +
  219 + // 2.3 Group methods ---------------------------------------------------------
  220 +
  221 + function checkGroup ( $uname,$group) {
  222 + /* 2.3.1 : Checks to see if a user is in a given group. If so, it returns
  223 + ** true, and returns false if the user isn't in the group, or any other
  224 + ** error occurs (eg:- no such user, no group by that name etc.)
  225 + */
  226 +
  227 + $checkDn = "ou=" .$this->groups. ", " .$this->dn;
  228 +
  229 + // We need to search for the group in order to get it's entry.
  230 + $this->result = @ldap_search( $this->connection, $checkDn, "cn=" .$group);
  231 + $info = @ldap_get_entries( $this->connection, $this->result);
  232 +
  233 + // Only one entry should be returned(no groups will have the same name)
  234 + $entry = ldap_first_entry( $this->connection,$this->result);
  235 +
  236 + if ( !$entry) {
  237 + $this->ldapErrorCode = ldap_errno( $this->connection);
  238 + $this->ldapErrorText = ldap_error( $this->connection);
  239 + return false; // Couldn't find the group...
  240 + }
  241 + // Get all the member DNs
  242 + if ( !$values = @ldap_get_values( $this->connection, $entry, "uniqueMember")) {
  243 + $this->ldapErrorCode = ldap_errno( $this->connection);
  244 + $this->ldapErrorText = ldap_error( $this->connection);
  245 + return false; // No users in the group
  246 + }
  247 + foreach ( $values as $key => $value) {
  248 + /* Loop through all members - see if the uname is there...
  249 + ** Also check for sub-groups - this allows us to define a group as
  250 + ** having membership of another group.
  251 + ** FIXME:- This is pretty ugly code and unoptimised. It takes ages
  252 + ** to search if you have sub-groups.
  253 + */
  254 + list( $cn,$ou) = explode( ",",$value);
  255 + list( $ou_l,$ou_r) = explode( "=",$ou);
  256 +
  257 + if ( $this->groups==$ou_r) {
  258 + list( $cn_l,$cn_r) = explode( "=",$cn);
  259 + // OK, So we now check the sub-group...
  260 + if ( $this->checkGroup ( $uname,$cn_r)) {
  261 + return true;
  262 + }
  263 + }
  264 +
  265 + if ( preg_match( "/$uname/i",$value)) {
  266 + return true;
  267 + }
  268 + }
  269 + }
  270 +
  271 + // 2.4 Attribute methods -----------------------------------------------------
  272 +
  273 + function getAttribute ( $uname,$attribute) {
  274 + /* 2.4.1 : Returns an array containing a set of attribute values.
  275 + ** For most searches, this will just be one row, but sometimes multiple
  276 + ** results are returned (eg:- multiple email addresses)
  277 + */
  278 +
  279 + $checkDn = "ou=" .$this->people. ", " .$this->dn;
  280 + $results[0] = $attribute;
  281 +
  282 + // We need to search for this user in order to get their entry.
  283 + $this->result = ldap_search( $this->connection, $checkDn, "uid=" .$uname, $results);
  284 + $info = ldap_get_entries( $this->connection, $this->result);
  285 +
  286 + // Only one entry should ever be returned (no user will have the same uid)
  287 + $entry = ldap_first_entry( $this->connection, $this->result);
  288 +
  289 + if ( !$entry) {
  290 + $this->ldapErrorCode = -1;
  291 + $this->ldapErrorText = "Couldn't find user";
  292 + return false; // Couldn't find the user...
  293 + }
  294 +
  295 + // Get all the member DNs
  296 + if ( !$values = @ldap_get_values( $this->connection, $entry, $attribute)) {
  297 + $this->ldapErrorCode = ldap_errno( $this->connection);
  298 + $this->ldapErrorText = ldap_error( $this->connection);
  299 + return false; // No matching attributes
  300 + }
  301 +
  302 + // Return an array containing the attributes.
  303 + return $values;
  304 + }
  305 +
  306 +
  307 +
  308 + function setAttribute( $uname, $attribute, $value) {
  309 + /* 2.4.2 : Allows an attribute value to be set.
  310 + ** This can only usually be done after an authenticated bind as a
  311 + ** directory manager - otherwise, read/write access will not be granted.
  312 + */
  313 +
  314 + // Construct a full DN...
  315 + $attrib_dn = "uid=" .$uname. ",ou=" .$this->people. "," .$this->dn;
  316 + $info[$attribute] = $value;
  317 + // Change attribute
  318 + $this->result = ldap_modify( $this->connection, $attrib_dn, $info);
  319 + if ( $this->result) {
  320 + // Change went OK
  321 + return true;
  322 + } else {
  323 + // Couldn't change password...
  324 + $this->ldapErrorCode = ldap_errno( $this->connection);
  325 + $this->ldapErrorText = ldap_error( $this->connection);
  326 + return false;
  327 + }
  328 + }
  329 +
  330 + /**
  331 + * Sets and returns the appropriate dn, based on whether there
  332 + * are values in $this->people and $this->groups.
  333 + *
  334 + * @param $peopleOrGroups
  335 + * this boolean specifies whether to build a groups
  336 + * dn or a people dn ie. if the boolean is true:
  337 + * ou=$this->people,$this->dn else:
  338 + * ou=$this->groups,$this->dn
  339 + * @return
  340 + * the dn to use
  341 + */
  342 + function setDn( $peopleOrGroups) {
  343 +
  344 + if ( $peopleOrGroups) {
  345 + if ( isset( $this->people) && ( strlen( $this->people) > 0)) {
  346 + $checkDn = "ou=" .$this->people. ", " .$this->dn;
  347 + }
  348 + } else {
  349 + if ( isset( $this->groups) && ( strlen( $this->groups) > 0)) {
  350 + $checkDn = "ou=" .$this->groups. ", " .$this->dn;
  351 + }
  352 + }
  353 +
  354 + if ( !isset( $checkDn)) {
  355 + $checkDn = $this->dn;
  356 + }
  357 + return $checkDn;
  358 + }
  359 + // 2.5 User methods ----------------------------------------------------------
  360 +
  361 + function getUsers( $search, $attributeArray) {
  362 + /* 2.5.1 : Returns an array containing a details of users, sorted by
  363 + ** username. The search criteria is a standard LDAP query - * returns all
  364 + ** users. The $attributeArray variable contains the required user detail field names
  365 + */
  366 +
  367 + // builds the appropriate dn, based on whether $this->people and/or $this->group is set
  368 + $checkDn = $this->setDn( true);
  369 +
  370 + // Perform the search and get the entry handles
  371 + $this->result = ldap_search( $this->connection, $checkDn, "uid=" .$search);
  372 + $info = ldap_get_entries( $this->connection, $this->result);
  373 + for( $i = 0; $i < $info["count"]; $i++){
  374 + // Get the username, and create an array indexed by it...
  375 + // Modify these as you see fit.
  376 + $uname = $info[$i]["uid"][0];
  377 + // add to the array for each attribute in my list
  378 + for ( $j = 0; $j < count( $attributeArray); $j++) {
  379 + if (strtolower($attributeArray[$j]) == "dn") {
  380 + $userslist["$uname"]["$attributeArray[$j]"] = $info[$i][strtolower($attributeArray[$j])];
  381 + } else {
  382 + $userslist["$uname"]["$attributeArray[$j]"] = $info[$i][strtolower($attributeArray[$j])][0];
  383 + }
  384 + }
  385 + }
  386 +
  387 + if ( !@asort( $userslist)) {
  388 + /* Sort into alphabetical order. If this fails, it's because there
  389 + ** were no results returned (array is empty) - so just return false.
  390 + */
  391 + $this->ldapErrorCode = -1;
  392 + $this->ldapErrorText = "No users found matching search criteria ".$search;
  393 + return false;
  394 + }
  395 + return $userslist;
  396 + }
  397 +
  398 +} // End of class
  399 +
  400 +
  401 +?>
... ...