diff --git a/blocks/html/users/addUserForm.inc b/blocks/html/users/addUserForm.inc index cd21b56..b2a86c6 100644 --- a/blocks/html/users/addUserForm.inc +++ b/blocks/html/users/addUserForm.inc @@ -1,6 +1,6 @@ */ @@ -11,12 +11,14 @@ diff --git a/blocks/html/users/updateUserForm.inc b/blocks/html/users/updateUserForm.inc index 75e7769..97203c1 100644 --- a/blocks/html/users/updateUserForm.inc +++ b/blocks/html/users/updateUserForm.inc @@ -1,6 +1,6 @@ * @param User $this->user @@ -13,12 +13,14 @@
diff --git a/classes/User.php b/classes/User.php index f8c4b3f..4da3751 100644 --- a/classes/User.php +++ b/classes/User.php @@ -1,6 +1,6 @@ */ @@ -16,6 +16,20 @@ class User extends SystemUser private $roles = array(); private $newPassword; // the User's new password, unencrypted + /** + * Should provide the list of methods supported + * + * There should always be at least one method, called "local" + * Additional methods must match classes that implement External Identities + * See: ExternalIdentity.php + * + * @return array + */ + public static function getAuthenticationMethods() + { + return array('local','Employee'); + } + /** * @param int|string $id */ diff --git a/configuration.inc.default b/configuration.inc.default index e86ff46..38b52cd 100644 --- a/configuration.inc.default +++ b/configuration.inc.default @@ -4,7 +4,7 @@ * will include this copyright statement */ define('COPYRIGHT',"/** - * @copyright 2007-2009 City of Bloomington, Indiana + * @copyright 2007-2012 City of Bloomington, Indiana * @license http://www.gnu.org/licenses/agpl.txt GNU/AGPL, see LICENSE.txt * @author Cliff Ingham */"); @@ -86,17 +86,25 @@ define('DB_USER',APPLICATION_NAME); define('DB_PASS','password'); /** - * LDAP Configuration - * This is required in order to use the LDAP authentication - * If you do not want to use LDAP authentication, you can comment this out + * Directory Configuration + * + * This is required in order to use the LDAP or ADS authentication + * If you do not want to use external authentication, you can comment this out */ -define('LDAP_DOMAIN','ldap.domain.somewhere'); -define('LDAP_DN','ou=people,o='.LDAP_DOMAIN); -define('LDAP_USERNAME_ATTRIBUTE','uid'); -define('LDAP_ADMIN_USER','username'); -define('LDAP_ADMIN_PASS','password'); -define('LDAP_SERVER','ldap.somewhere.com'); -define('LDAP_PASSWORD_ATTRIBUTE','userpassword'); +// Example for ADS style authentication +define('DIRECTORY_SERVER','ldaps://example.org:636'); +define('DIRECTORY_BASE_DN','OU=Department,DC=example,DC=org'); +define('DIRECTORY_USERNAME_ATTRIBUTE', 'CN'); +define('DIRECTORY_USER_BINDING','{username}@bloomington.in.gov'); +define('DIRECTORY_ADMIN_BINDING', 'admin@bloomington.in.gov'); +define('DIRECTORY_ADMIN_PASS','password'); +// Example for LDAP style authentication +//define('DIRECTORY_SERVER','ldaps://example.org:636'); +//define('DIRECTORY_BASE_DN','ou=people,o=ldap.domain.somewhere'); +//define('DIRECTORY_USERNAME_ATTRIBUTE', 'uid'); +//define('DIRECTORY_USER_BINDING','uid={username},'.DIRECTORY_BASE_DN); +//define('DIRECTORY_ADMIN_BINDING', 'uid=admin,'.DIRECTORY_BASE_DN); +//define('DIRECTORY_ADMIN_PASS','password'); /** * Import global functions that we use for many applications we write diff --git a/html/users/addUser.php b/html/users/addUser.php index 9012b73..7031540 100644 --- a/html/users/addUser.php +++ b/html/users/addUser.php @@ -1,6 +1,6 @@ * @param GET person_id @@ -32,18 +32,19 @@ if (isset($_POST['user'])) { } else { // Load their information from LDAP - // Delete this statement if you're not using LDAP - if ($user->getAuthenticationMethod() == 'LDAP') { + if ($user->getAuthenticationMethod() != 'local') { try { - $ldap = new LDAPEntry($user->getUsername()); + $externalIdentity = $user->getAuthenticationMethod(); + $identity = new $externalIdentity($user->getUsername()); + try { - $person = new Person($ldap->getEmail()); + $person = new Person($identity->getEmail()); } catch (Exception $e) { $person = new Person(); - $person->setFirstname($ldap->getFirstname()); - $person->setLastname($ldap->getLastname()); - $person->setEmail($ldap->getEmail()); + $person->setFirstname($identity->getFirstname()); + $person->setLastname($identity->getLastname()); + $person->setEmail($identity->getEmail()); $person->save(); } $user->setPerson($person); diff --git a/libraries/framework/classes/Employee.php b/libraries/framework/classes/Employee.php new file mode 100644 index 0000000..ff7c869 --- /dev/null +++ b/libraries/framework/classes/Employee.php @@ -0,0 +1,112 @@ + + */ +class Employee implements ExternalIdentity +{ + private static $connection; + private $entry; + + /** + * @param string $username + * @param string $password + * @throws Exception + */ + public static function authenticate($username,$password) + { + $bindUser = sprintf(str_replace('{username}','%s',DIRECTORY_USER_BINDING),$username); + + $connection = ldap_connect(DIRECTORY_SERVER) or die("Couldn't connect to ADS"); + ldap_set_option($connection, LDAP_OPT_PROTOCOL_VERSION, 3); + if (ldap_bind($connection,$bindUser,$password)) { + return true; + } + } + + + /** + * Loads an entry from the LDAP server for the given user + * + * @param string $username + */ + public function __construct($username) + { + $this->openConnection(); + + $result = ldap_search( + self::$connection, + DIRECTORY_BASE_DN, + DIRECTORY_USERNAME_ATTRIBUTE."=$username" + ); + if (ldap_count_entries(self::$connection,$result)) { + $entries = ldap_get_entries(self::$connection, $result); + $this->entry = $entries[0]; + } + } + + /** + * @return string + */ + public function getUsername() + { + return $this->entry['uid']; + } + + /** + * @return string + */ + public function getFirstname() + { + return $this->entry['givenname'][0]; + } + + /** + * @return string + */ + public function getLastname() + { + return $this->entry['sn'][0]; + } + + /** + * @return string + */ + public function getEmail() + { + return $this->entry['mail'][0]; + } + + /** + * Creates the connection to the LDAP server + */ + private function openConnection() + { + if (!self::$connection) { + if (self::$connection = ldap_connect(DIRECTORY_SERVER)) { + ldap_set_option(self::$connection,LDAP_OPT_PROTOCOL_VERSION,3); + if (defined(DIRECTORY_ADMIN_BINDING) && DIRECTORY_ADMIN_BINDING) { + if (!ldap_bind(self::$connection,DIRECTORY_ADMIN_BINDING,DIRECTORY_ADMIN_PASS)) { + throw new Exception(ldap_error(self::$connection)); + } + } + else { + if (!ldap_bind(self::$connection)) { + throw new Exception(ldap_error(self::$connection)); + } + } + } + else { + throw new Exception(ldap_error(self::$connection)); + } + } + } +} diff --git a/libraries/framework/classes/ExternalAuthentication.php b/libraries/framework/classes/ExternalAuthentication.php deleted file mode 100644 index efe063e..0000000 --- a/libraries/framework/classes/ExternalAuthentication.php +++ /dev/null @@ -1,12 +0,0 @@ - - */ - -interface ExternalAuthentication -{ - public static function authenticate($username,$password); - public static function savePassword($username,$password); -} \ No newline at end of file diff --git a/libraries/framework/classes/ExternalIdentity.php b/libraries/framework/classes/ExternalIdentity.php new file mode 100644 index 0000000..a81e70c --- /dev/null +++ b/libraries/framework/classes/ExternalIdentity.php @@ -0,0 +1,39 @@ + + */ + +interface ExternalIdentity +{ + /** + * Should load user data from storage + */ + public function __construct($username); + + /** + * Return whether the username, password combo is valid + * + * @param string $username + * @param string $password The unencrypted password + * @return bool + */ + public static function authenticate($username,$password); + + /** + * @return string + */ + public function getFirstname(); + + /** + * @return string + */ + public function getLastname(); + + /** + * @return string + */ + public function getEmail(); + +} \ No newline at end of file diff --git a/libraries/framework/classes/LDAP.php b/libraries/framework/classes/LDAP.php deleted file mode 100644 index 8b11523..0000000 --- a/libraries/framework/classes/LDAP.php +++ /dev/null @@ -1,87 +0,0 @@ - - */ -class LDAP implements ExternalAuthentication -{ - /** - * @param string $username - * @param string $password - * @throws Exception - */ - public static function authenticate($username,$password) - { - $connection = ldap_connect(LDAP_SERVER) or die("Couldn't connect to LDAP"); - ldap_set_option($connection, LDAP_OPT_PROTOCOL_VERSION, 3); - ldap_bind($connection); - - $result = ldap_search($connection,LDAP_DN,LDAP_USERNAME_ATTRIBUTE."=$username"); - if (ldap_count_entries($connection,$result)) { - $entries = ldap_get_entries($connection, $result); - - if (preg_match("/^\{crypt\}(.+)/i",$entries[0][LDAP_PASSWORD_ATTRIBUTE][0],$matches)) { - $ldapPassword = $matches[1]; - $salt = substr($ldapPassword,0,2); - - $encryptedPassword = crypt($password,$salt); - if ($encryptedPassword === $ldapPassword) { - return true; - } - else { - throw new Exception('wrongPassword'); - } - } - else { - throw new Exception("passwordIsCorrupted"); - } - } - else { - throw new Exception("unknownUser"); - } - } - - /** - * Saves a user's password to the LDAP server - * - * @param string $username - * @param string $password - */ - public static function savePassword($username,$password) - { - $connection = ldap_connect(LDAP_SERVER); - ldap_set_option($connection,LDAP_OPT_PROTOCOL_VERSION,3); - ldap_bind($connection, - LDAP_USERNAME_ATTRIBUTE."=".LDAP_ADMIN_USER.",o=".LDAP_DOMAIN, - LDAP_ADMIN_PASS) or die(ldap_error($connection)); - - $result = ldap_search($connection,LDAP_DN,LDAP_USERNAME_ATTRIBUTE."=$username"); - $entries = ldap_get_entries($connection, $result); - - $dn = LDAP_USERNAME_ATTRIBUTE."=$username,ou=people,o=".LDAP_DOMAIN; - if ($this->getPassword()) { - $salt = substr(md5(time()),0,2); - $encryptedPassword = "{CRYPT}".crypt($password,$salt); - - $password = array(LDAP_PASSWORD_ATTRIBUTE=>$encryptedPassword); - - if (isset($entries[0][LDAP_PASSWORD_ATTRIBUTE])) { - // Modify - ldap_mod_replace($connection,$dn,$password) - or die(print_r($password).ldap_error($connection)); - } - else { - // Add - ldap_mod_add($connection,$dn,$password) - or die(print_r($password).ldap_error($connection)); - } - } - else { - // Delete - $password = array(); - ldap_mod_del($connection,$dn,$password) - or die(print_r($password).ldap_error($connection)); - } - } -} diff --git a/libraries/framework/classes/LDAPEntry.php b/libraries/framework/classes/LDAPEntry.php deleted file mode 100644 index a7477bf..0000000 --- a/libraries/framework/classes/LDAPEntry.php +++ /dev/null @@ -1,526 +0,0 @@ - - */ -class LDAPEntry -{ - private static $connection; - - private $ou; - - private $uid; - private $userPassword; - - private $givenName; - private $sn; - private $cn; - private $displayName; - - private $businessCategory; - private $departmentNumber; - private $physicalDeliveryOfficeName; - private $title; - - private $mail; - private $telephoneNumber; - private $preferredTelephoneNumber; - private $webTelephoneNumber; - private $facsimileTelephoneNumber; - private $homePhone; - private $mobile; - private $dialupAccess; - - private $jpegPhoto; - - private $sambaLMPassword; - private $sambaNTPassword; - private $sambaSID; - - private $objectClasses = array(); - - // Used to keep track of changes we make to this entry. This is because LDAP - // requires us to send seperate modify, add, and delete commands. - private $modifiedAttributes = array(); - private $addedAttributes = array(); - private $deletedAttributes = array(); - - - /** - * Loads an entry from the LDAP server for the given user - * @param string $username - */ - public function __construct($username=null) - { - $this->openConnection(); - - if ($username) { - $result = ldap_search(LDAPEntry::$connection,LDAP_DN, - LDAP_USERNAME_ATTRIBUTE."=$username"); - if (ldap_count_entries(LDAPEntry::$connection,$result)) { - $entries = ldap_get_entries(LDAPEntry::$connection, $result); - $this->uid = $username; - if (isset($entries[0]['ou'])) { - $this->ou = $entries[0]['ou'][0]; - } - if (isset($entries[0]['givenname'])) { - $this->givenName = $entries[0]['givenname'][0]; - } - if (isset($entries[0]['sn'])) { - $this->sn = $entries[0]['sn'][0]; - } - if (isset($entries[0]['cn'])) { - $this->cn = $entries[0]['cn'][0]; - } - if (isset($entries[0]['displayname'])) { - $this->displayName = $entries[0]['displayname'][0]; - } - if (isset($entries[0]['businesscategory'])) { - $this->businessCategory = $entries[0]['businesscategory'][0]; - } - if (isset($entries[0]['departmentnumber'])) { - $this->departmentNumber = $entries[0]['departmentnumber'][0]; - } - if (isset($entries[0]['physicaldeliveryofficename'])) { - $this->physicalDeliveryOfficeName = $entries[0]['physicaldeliveryofficename'][0]; - } - if (isset($entries[0]['title'])) { - $this->title = $entries[0]['title'][0]; - } - if (isset($entries[0]['mail'])) { - $this->mail = $entries[0]['mail'][0]; - } - if (isset($entries[0]['telephonenumber'])) { - $this->telephoneNumber = $entries[0]['telephonenumber'][0]; - } - if (isset($entries[0]['preferredtelephonenumber'])) { - $this->preferredTelephoneNumber = $entries[0]['preferredtelephonenumber'][0]; - } - if (isset($entries[0]['webtelephonenumber'])) { - $this->webTelephoneNumber = $entries[0]['webtelephonenumber'][0]; - } - if (isset($entries[0]['facsimiletelephonenumber'])) { - $this->facsimileTelephoneNumber = $entries[0]['facsimiletelephonenumber'][0]; - } - if (isset($entries[0]['homephone'])) { - $this->homePhone = $entries[0]['homephone'][0]; - } - if (isset($entries[0]['mobile'])) { - $this->mobile = $entries[0]['mobile'][0]; - } - if (isset($entries[0]['dialupaccess'])) { - $this->dialupAccess = $entries[0]['dialupaccess'][0]; - } - if (isset($entries[0]['objectclass'])) { - $this->objectClasses = $entries[0]['objectclass']; - } - if (isset($entries[0]['jpegphoto'])) { - $photo = ldap_get_values_len(LDAPEntry::$connection, - ldap_first_entry(LDAPEntry::$connection,$result), - 'jpegphoto'); - $this->jpegPhoto = $photo[0]; - } - } - else { - throw new Exception("ldap/unknownUser"); - } - } - } - - /** - * Creates the connection to the LDAP server - */ - private function openConnection() - { - if (!LDAPEntry::$connection) { - if (LDAPEntry::$connection = ldap_connect(LDAP_SERVER)) { - ldap_set_option(LDAPEntry::$connection,LDAP_OPT_PROTOCOL_VERSION,3); - if (LDAP_ADMIN_USER) { - if (!ldap_bind(LDAPEntry::$connection, - LDAP_USERNAME_ATTRIBUTE."=".LDAP_ADMIN_USER.",o=".LDAP_DOMAIN, - LDAP_ADMIN_PASS)) { - throw new Exception(ldap_error(LDAPEntry::$connection)); - } - } - else { - if (!ldap_bind(LDAPEntry::$connection)) { - throw new Exception(ldap_error(LDAPEntry::$connection)); - } - } - } - else { - throw new Exception(ldap_error(LDAPEntry::$connection)); - } - } - } - - /** - * Saves any changed information back to the LDAP server - */ - public function save() - { - $dn = "uid={$this->uid},ou=people,o=".LDAP_DOMAIN; - if (count($this->modifiedAttributes)) { - ldap_mod_replace(LDAPEntry::$connection,$dn,$this->modifiedAttributes) - or die(print_r($this->modifiedAttributes).ldap_error(LDAPEntry::$connection)); - } - if (count($this->addedAttributes)) { - ldap_mod_add(LDAPEntry::$connection,$dn,$this->addedAttributes) - or die(print_r($this->addedAttributes).ldap_error(LDAPEntry::$connection)); - } - if (count($this->deletedAttributes)) { - ldap_mod_del(LDAPEntry::$connection,$dn,$this->deletedAttributes) - or die(print_r($this->deletedAttributes).ldap_error(LDAPEntry::$connection)); - } - } - - /** - * Escapes any problematic characters - * @param string $str - */ - private function sanitize($str) - { - $tmp = trim($str); - $tmp = str_replace('\\', '\\\\', $tmp); - $tmp = str_replace('(', '\(', $tmp); - $tmp = str_replace(')', '\)', $tmp); - $tmp = str_replace('*', '\*', $tmp); - return $tmp; - } - - /** - * Keeps track of what properties have been changed - * - * All setters should call this function. Otherwise, we won't - * know what's been changed in order to do the appropriate calls in LDAP - * @param string $property - * @param string $value - */ - private function changeProperty($property,$value) - { - if ($value) { - if ($value != $this->{$property}) { - if ($this->{$property}) { - $this->modifiedAttributes[$property] = $value; - } - else { - $this->addedAttributes[$property] = $value; - } - $this->{$property} = $value; - } - } - else { - if ($this->{$property}) { - $this->{$property} = ''; - $this->deletedAttributes[$property] = array(); - } - } - } - - /** - * @return string - */ - public function getOU() - { - return $this->ou; - } - /** - * @return string - */ - public function getUID() - { - return $this->uid; - } - /** - * @return string - */ - public function getUsername() - { - return $this->uid; - } - /** - * @return string - */ - public function getFirstname() - { - return $this->givenName; - } - /** - * @return string - */ - public function getLastname() - { - return $this->sn; - } - /** - * @return string - */ - public function getCommonName() - { - return $this->cn; - } - /** - * @return string - */ - public function getDisplayName() - { - return $this->displayName; - } - /** - * @return string - */ - public function getBusinessCategory() - { - return $this->businessCategory; - } - /** - * @return string - */ - public function getDepartment() - { - return $this->departmentNumber; - } - /** - * @return string - */ - public function getOffice() - { - return $this->physicalDeliveryOfficeName; - } - /** - * @return string - */ - public function getTitle() - { - return $this->title; - } - /** - * @return string - */ - public function getEmail() - { - return $this->mail; - } - /** - * @return string - */ - public function getPhone() - { - return $this->telephoneNumber; - } - /** - * @return string - */ - public function getPreferredPhone() - { - return $this->preferredTelephoneNumber; - } - /** - * @return string - */ - public function getWebPhone() - { - return $this->webTelephoneNumber; - } - /** - * @return string - */ - public function getFax() - { - return $this->facsimileTelephoneNumber; - } - /** - * @return string - */ - public function getHomePhone() - { - return $this->homePhone; - } - /** - * @return string - */ - public function getCellPhone() - { - return $this->mobile; - } - /** - * @return string - */ - public function getDialup() - { - return $this->dialupAccess; - } - /** - * @return string - */ - public function getSambaLMPassword() - { - return $this->sambaLMPassword; - } - /** - * @return string - */ - public function getSambaNTPassword() - { - return $this->sambaNTPassword; - } - /** - * @return string - */ - public function getSambaSID() - { - return $this->sambaSID; - } - /** - * @return string - */ - public function getObjectClasses() - { - return $this->objectClasses; - } - /** - * @return raw - */ - public function getPhoto() - { - return $this->jpegPhoto; - } - - /** - * @param string $string - */ - public function setUsername($string) - { - $this->changeProperty("uid",$this->sanitize($string)); - } - /** - * @param string $string - */ - public function setFirstname($string) - { - $this->changeProperty("givenName",$this->sanitize($string)); - } - /** - * @param string $string - */ - public function setLastname($string) - { - $this->changeProperty("sn",$this->sanitize($string)); - } - /** - * @param string $string - */ - public function setCommonName($string) - { - $this->changeProperty("cn",$this->sanitize($string)); - } - /** - * @param string $string - */ - public function setDisplayName($string) - { - $this->changeProperty("displayName",$this->sanitize($string)); - } - /** - * @param string $string - */ - public function setBusinessCategory($string) - { - $this->changeProperty("businessCategory",$this->sanitize($string)); - } - /** - * @param string $string - */ - public function setDepartment($string) - { - $this->changeProperty("departmentNumber",$this->sanitize($string)); - } - /** - * @param string $string - */ - public function setOffice($string) - { - $this->changeProperty("physicalDeliveryOfficeName",$this->sanitize($string)); - } - /** - * @param string $string - */ - public function setTitle($string) - { - $this->changeProperty("title",$this->sanitize($string)); - } - /** - * @param string $string - */ - public function setEmail($string) - { - $this->changeProperty("mail",$this->sanitize($string)); - } - /** - * @param string $string - */ - public function setPhone($string) - { - $this->changeProperty("telephoneNumber",preg_replace('/[^0-9ext\-\s]/','',$string)); - } - /** - * @param string $string - */ - public function setPreferredPhone($string) - { - $this->changeProperty('preferredTelephoneNumber', - preg_replace('/[^0-9ext\-\s]/','',$string)); - } - /** - * @param string $string - */ - public function setWebPhone($string) - { - $this->changeProperty('webTelephoneNumber', - preg_replace('/[^0-9ext\-\s]/','',$string)); - } - /** - * @param string $string - */ - public function setFax($string) - { - $this->changeProperty('facsimileTelephoneNumber', - preg_replace('/[^0-9ext\-\s]/','',$string)); - } - /** - * @param string $string - */ - public function setHomePhone($string) - { - $this->changeProperty('homePhone',preg_replace('/[^0-9ext\-\s]/','',$string)); - } - /** - * @param string $string - */ - public function setCellPhone($string) - { - $this->changeProperty('mobile',preg_replace('/[^0-9ext\-\s]/','',$string)); - } - /** - * @param string $string - */ - public function setDialup($string) - { - $this->changeProperty('dialupAccess',preg_replace('/[^0-9ext\-\s]/','',$string)); - } - /** - * @param string $filePath - */ - public function setPhoto($filePath) - { - $this->changeProperty("jpegPhoto",file_get_contents($filePath)); - } -} diff --git a/libraries/framework/classes/SystemUser.php b/libraries/framework/classes/SystemUser.php index 925746c..4a48eb8 100644 --- a/libraries/framework/classes/SystemUser.php +++ b/libraries/framework/classes/SystemUser.php @@ -6,7 +6,7 @@ * a city employee will have the same username and password on all applications. * Applications should use these public functions for their own users. * - * @copyright 2006-2009 City of Bloomington, Indiana + * @copyright 2006-2012 City of Bloomington, Indiana * @license http://www.gnu.org/licenses/agpl.txt GNU/AGPL, see LICENSE.txt * @author Cliff Ingham */ @@ -57,7 +57,7 @@ abstract class SystemUser default: $type = $this->getAuthenticationMethod(); - return call_user_func(array($type,'authenticate'),$this->getUsername(),$password); + return $type::authenticate($this->getUsername(),$password); } } @@ -74,8 +74,10 @@ abstract class SystemUser } /** - * Determines which authentication method is being used, and sends the password to the - * appropriate method + * Used to save passwords to the database + * + * Only local passwords should be saved. External Identities should have + * their own methods for users to change passwords */ public function savePassword() { @@ -83,10 +85,6 @@ abstract class SystemUser case "local": $this->saveLocalPassword(); break; - - default: - $type = $this->getAuthenticationMethod(); - call_user_func(array($type,'savePassword'),$this->getUsername(),$password); } } } diff --git a/scripts/migration/r70-r71.sql b/scripts/migration/r70-r71.sql new file mode 100644 index 0000000..a249b2c --- /dev/null +++ b/scripts/migration/r70-r71.sql @@ -0,0 +1 @@ +update users set authenticationMethod='Employee' where authenticationMethod='LDAP';