PageRenderTime 166ms CodeModel.GetById 31ms RepoModel.GetById 6ms app.codeStats 0ms

/plugins/auth/ldap/LDAPAuthPlugin.inc.php

https://github.com/mcrider/ocs
PHP | 389 lines | 226 code | 33 blank | 130 comment | 49 complexity | 5148939cdd2c543f08595bf494f95002 MD5 | raw file
Possible License(s): GPL-2.0
  1. <?php
  2. /**
  3. * @file LDAPAuthPlugin.inc.php
  4. *
  5. * Copyright (c) 2000-2010 John Willinsky
  6. * Distributed under the GNU GPL v2. For full terms see the file docs/COPYING.
  7. *
  8. * @class LDAPAuthPlugin
  9. * @ingroup plugins_auth_ldap
  10. *
  11. * @brief LDAP authentication plugin.
  12. */
  13. // $Id$
  14. import('classes.plugins.AuthPlugin');
  15. class LDAPAuthPlugin extends AuthPlugin {
  16. /**
  17. * Called as a plugin is registered to the registry
  18. * @param $category String Name of category plugin was registered to
  19. * @return boolean True iff plugin initialized successfully; if false,
  20. * the plugin will not be registered.
  21. */
  22. function register($category, $path) {
  23. $success = parent::register($category, $path);
  24. $this->addLocaleData();
  25. return $success;
  26. }
  27. // LDAP-specific configuration settings:
  28. // - hostname
  29. // - port
  30. // - basedn
  31. // - managerdn
  32. // - managerpwd
  33. // - pwhash
  34. // - SASL: sasl, saslmech, saslrealm, saslauthzid, saslprop
  35. /** @var $conn resource the LDAP connection */
  36. var $conn;
  37. /**
  38. * Return the name of this plugin.
  39. * @return string
  40. */
  41. function getName() {
  42. return 'ldap';
  43. }
  44. /**
  45. * Return the localized name of this plugin.
  46. * @return string
  47. */
  48. function getDisplayName() {
  49. return Locale::translate('plugins.auth.ldap.displayName');
  50. }
  51. /**
  52. * Return the localized description of this plugin.
  53. * @return string
  54. */
  55. function getDescription() {
  56. return Locale::translate('plugins.auth.ldap.description');
  57. }
  58. //
  59. // Core Plugin Functions
  60. // (Must be implemented by every authentication plugin)
  61. //
  62. /**
  63. * Returns an instance of the authentication plugin
  64. * @param $settings array settings specific to this instance.
  65. * @param $authId int identifier for this instance
  66. * @return LDAPuthPlugin
  67. */
  68. function &getInstance($settings, $authId) {
  69. $returner = new LDAPAuthPlugin($settings, $authId);
  70. return $returner;
  71. }
  72. /**
  73. * Authenticate a username and password.
  74. * @param $username string
  75. * @param $password string
  76. * @return boolean true if authentication is successful
  77. */
  78. function authenticate($username, $password) {
  79. $valid = false;
  80. if ($this->open()) {
  81. if ($entry = $this->getUserEntry($username)) {
  82. $userdn = ldap_get_dn($this->conn, $entry);
  83. if ($this->bind($userdn, $password)) {
  84. $valid = true;
  85. }
  86. }
  87. $this->close();
  88. }
  89. return $valid;
  90. }
  91. //
  92. // Optional Plugin Functions
  93. //
  94. /**
  95. * Check if a username exists.
  96. * @param $username string
  97. * @return boolean
  98. */
  99. function userExists($username) {
  100. $exists = true;
  101. if ($this->open()) {
  102. if ($this->bind()) {
  103. $result = ldap_search($this->conn, $this->settings['basedn'], $this->settings['uid'] . '=' . $username);
  104. $exists = (ldap_count_entries($this->conn, $result) != 0);
  105. }
  106. $this->close();
  107. }
  108. return $exists;
  109. }
  110. /**
  111. * Retrieve user profile information from the LDAP server.
  112. * @param $user User to update
  113. * @return boolean true if successful
  114. */
  115. function getUserInfo(&$user) {
  116. $valid = false;
  117. if ($this->open()) {
  118. if ($entry = $this->getUserEntry($user->getUsername())) {
  119. $valid = true;
  120. $attr = ldap_get_attributes($this->conn, $entry);
  121. $this->userFromAttr($user, $attr);
  122. }
  123. $this->close();
  124. }
  125. return $valid;
  126. }
  127. /**
  128. * Store user profile information on the LDAP server.
  129. * @param $user User to store
  130. * @return boolean true if successful
  131. */
  132. function setUserInfo(&$user) {
  133. $valid = false;
  134. if ($this->open()) {
  135. if ($entry = $this->getUserEntry($user->getUsername())) {
  136. $userdn = ldap_get_dn($this->conn, $entry);
  137. if ($this->bind($this->settings['managerdn'], $this->settings['managerpwd'])) {
  138. $attr = array();
  139. $this->userToAttr($user, $attr);
  140. $valid = ldap_modify($this->conn, $userdn, $attr);
  141. }
  142. }
  143. $this->close();
  144. }
  145. return $valid;
  146. }
  147. /**
  148. * Change a user's password on the LDAP server.
  149. * @param $username string user to update
  150. * @param $password string the new password
  151. * @return boolean true if successful
  152. */
  153. function setUserPassword($username, $password) {
  154. if ($this->open()) {
  155. if ($entry = $this->getUserEntry($username)) {
  156. $userdn = ldap_get_dn($this->conn, $entry);
  157. if ($this->bind($this->settings['managerdn'], $this->settings['managerpwd'])) {
  158. $attr = array('userPassword' => $this->encodePassword($password));
  159. $valid = ldap_modify($this->conn, $userdn, $attr);
  160. }
  161. }
  162. $this->close();
  163. }
  164. }
  165. /**
  166. * Create a user on the LDAP server.
  167. * @param $user User to create
  168. * @return boolean true if successful
  169. */
  170. function createUser(&$user) {
  171. $valid = false;
  172. if ($this->open()) {
  173. if (!($entry = $this->getUserEntry($user->getUsername()))) {
  174. if ($this->bind($this->settings['managerdn'], $this->settings['managerpwd'])) {
  175. $userdn = $this->settings['uid'] . '=' . $user->getUsername() . ',' . $this->settings['basedn'];
  176. $attr = array(
  177. 'objectclass' => array('top', 'person', 'organizationalPerson', 'inetorgperson'),
  178. $this->settings['uid'] => $user->getUsername(),
  179. 'userPassword' => $this->encodePassword($user->getPassword())
  180. );
  181. $this->userToAttr($user, $attr);
  182. $valid = ldap_add($this->conn, $userdn, $attr);
  183. }
  184. }
  185. $this->close();
  186. }
  187. return $valid;
  188. }
  189. /**
  190. * Delete a user from the LDAP server.
  191. * @param $username string user to delete
  192. * @return boolean true if successful
  193. */
  194. function deleteUser($username) {
  195. $valid = false;
  196. if ($this->open()) {
  197. if ($entry = $this->getUserEntry($username)) {
  198. $userdn = ldap_get_dn($this->conn, $entry);
  199. if ($this->bind($this->settings['managerdn'], $this->settings['managerpwd'])) {
  200. $valid = ldap_delete($this->conn, $userdn);
  201. }
  202. }
  203. $this->close();
  204. }
  205. return $valid;
  206. }
  207. //
  208. // LDAP Helper Functions
  209. //
  210. /**
  211. * Open connection to the server.
  212. */
  213. function open() {
  214. $this->conn = ldap_connect($this->settings['hostname'], (int)$this->settings['port']);
  215. ldap_set_option($this->conn, LDAP_OPT_PROTOCOL_VERSION, 3);
  216. return $this->conn;
  217. }
  218. /**
  219. * Close connection.
  220. */
  221. function close() {
  222. ldap_close($this->conn);
  223. $this->conn = null;
  224. }
  225. /**
  226. * Bind to a directory.
  227. * $binddn string directory to bind (optional)
  228. * $password string (optional)
  229. */
  230. function bind($binddn = null, $password = null) {
  231. if (isset($this->settings['sasl'])) {
  232. // FIXME ldap_sasl_bind requires PHP5, haven't tested this
  233. return @ldap_sasl_bind($this->conn, $binddn, $password, $this->settings['saslmech'], $this->settings['saslrealm'], $this->settings['saslauthzid'], $this->settings['saslprop']);
  234. }
  235. return @ldap_bind($this->conn, $binddn, $password);
  236. }
  237. /**
  238. * Lookup a user entry in the directory.
  239. * @param $username string
  240. */
  241. function getUserEntry($username) {
  242. $entry = false;
  243. if ($this->bind($this->settings['managerdn'], $this->settings['managerpwd'])) {
  244. $result = ldap_search($this->conn, $this->settings['basedn'], $this->settings['uid'] . '=' . $username);
  245. if (ldap_count_entries($this->conn, $result) == 1) {
  246. $entry = ldap_first_entry($this->conn, $result);
  247. }
  248. }
  249. return $entry;
  250. }
  251. /**
  252. * Update User object from entry attributes.
  253. * TODO Abstract this to allow arbitrary LDAP <-> OJS schema mappings.
  254. * For now must be subclassed for other schemas.
  255. * TODO How to deal with deleted fields.
  256. * @param $user User
  257. * @param $uattr array
  258. */
  259. function userFromAttr(&$user, &$uattr) {
  260. $attr = array_change_key_case($uattr, CASE_LOWER); // Note: array_change_key_case requires PHP >= 4.2.0
  261. $firstName = @$attr['givenname'][0];
  262. $middleName = null;
  263. $initials = null;
  264. $lastName = @$attr['sn'][0];
  265. if (!isset($lastName))
  266. $lastName = @$attr['surname'][0];
  267. $affiliation = @$attr['o'][0];
  268. if (!isset($affiliation))
  269. $affiliation = @$attr['organizationname'][0];
  270. $email = @$attr['mail'][0];
  271. if (!isset($email))
  272. $email = @$attr['email'][0];
  273. $phone = @$attr['telephonenumber'][0];
  274. $fax = @$attr['facsimiletelephonenumber'][0];
  275. if (!isset($fax))
  276. $fax = @$attr['fax'][0];
  277. $mailingAddress = @$attr['postaladdress'][0];
  278. if (!isset($mailingAddress))
  279. $mailingAddress = @$attr['registeredAddress'][0];
  280. $biography = null;
  281. $interests = null;
  282. // Only update fields that exist
  283. if (isset($firstName))
  284. $user->setFirstName($firstName);
  285. if (isset($middleName))
  286. $user->setMiddleName($middleName);
  287. if (isset($initials))
  288. $user->setInitials($initials);
  289. if (isset($lastName))
  290. $user->setLastName($lastName);
  291. if (isset($affiliation))
  292. $user->setAffiliation($affiliation);
  293. if (isset($email))
  294. $user->setEmail($email);
  295. if (isset($phone))
  296. $user->setPhone($phone);
  297. if (isset($fax))
  298. $user->setFax($fax);
  299. if (isset($mailingAddress))
  300. $user->setMailingAddress($mailingAddress);
  301. if (isset($biography))
  302. $user->setBiography($biography, Locale::getLocale());
  303. if (isset($interests))
  304. $user->setInterests($interests, Locale::getLocale());
  305. }
  306. /**
  307. * Update entry attributes from User object.
  308. * TODO How to deal with deleted fields.
  309. * @param $user User
  310. * @param $attr array
  311. */
  312. function userToAttr(&$user, &$attr) {
  313. // FIXME empty strings for unset fields?
  314. if ($user->getFullName())
  315. $attr['cn'] = $user->getFullName();
  316. if ($user->getFirstName())
  317. $attr['givenName'] = $user->getFirstName();
  318. if ($user->getLastName())
  319. $attr['sn'] = $user->getLastName();
  320. if ($user->getAffiliation())
  321. $attr['organizationName'] = $user->getAffiliation();
  322. if ($user->getEmail())
  323. $attr['mail'] = $user->getEmail();
  324. if ($user->getPhone())
  325. $attr['telephoneNumber'] = $user->getPhone();
  326. if ($user->getFax())
  327. $attr['facsimileTelephoneNumber'] = $user->getFax();
  328. if ($user->getMailingAddress())
  329. $attr['postalAddress'] = $user->getMailingAddress();
  330. }
  331. /**
  332. * Encode password for the 'userPassword' field using the specified hash.
  333. * @param $password string
  334. * @return string hashed string (with prefix).
  335. */
  336. function encodePassword($password) {
  337. switch ($this->settings['pwhash']) {
  338. case 'md5':
  339. return '{MD5}' . base64_encode(pack('H*', md5($password)));
  340. case 'smd5':
  341. $salt = pack('C*', mt_rand(), mt_rand(), mt_rand(), mt_rand(), mt_rand(), mt_rand());
  342. return '{SMD5}' . base64_encode(pack('H*', md5($password . $salt)) . $salt);
  343. case 'sha':
  344. return '{SHA}' . base64_encode(pack('H*', sha1($password))); // Note: sha1 requres PHP >= 4.3.0
  345. case 'ssha':
  346. $salt = pack('C*', mt_rand(), mt_rand(), mt_rand(), mt_rand(), mt_rand(), mt_rand());
  347. return '{SSHA}' . base64_encode(pack('H*', sha1($password . $salt)) . $salt);
  348. case 'crypt':
  349. return '{CRYPT}' . crypt($password);
  350. default:
  351. //return '{CLEARTEXT}'. $password;
  352. return $password;
  353. }
  354. }
  355. }
  356. ?>