PageRenderTime 39ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

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

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