Source for file user-defs.php

Documentation is available at user-defs.php

  1. <?php
  2. /* ******************************************************************** */
  3. /* CATALYST PHP Source Code */
  4. /* -------------------------------------------------------------------- */
  5. /* This program is free software; you can redistribute it and/or modify */
  6. /* it under the terms of the GNU General Public License as published by */
  7. /* the Free Software Foundation; either version 2 of the License, or */
  8. /* (at your option) any later version. */
  9. /* */
  10. /* This program is distributed in the hope that it will be useful, */
  11. /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
  12. /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
  13. /* GNU General Public License for more details. */
  14. /* */
  15. /* You should have received a copy of the GNU General Public License */
  16. /* along with this program; if not, write to: */
  17. /* The Free Software Foundation, Inc., 59 Temple Place, Suite 330, */
  18. /* Boston, MA 02111-1307 USA */
  19. /* -------------------------------------------------------------------- */
  20. /* */
  21. /* Filename: user-defs.php */
  22. /* Author: Paul Waite */
  23. /* Description: Definitions for managing USERS */
  24. /* */
  25. /* ******************************************************************** */
  26. /** @package core */ * This is the default. */
  27. define("LOCAL_AUTH", 0);
  28. /** User data is maintained on a remote database */
  29. ("REMOTE_AUTH_REMOTEDB", 1);
  30. /** User data maintained on an LDAP server */
  31. ("REMOTE_AUTH_LDAP", 2);
  32. /** Used to indicate items do not have a remote mapping */
  33. ("NOT_MAPPED", "");
  34.  
  35. //-----------------------------------------------------------------------
  36. /**
  37. * The user class
  38. * This class managed users. It pre-supposes a particular database
  39. * structure based on three tables: uuser, ugroup, and uuser_group.
  40. * Please see the example schemas for Phplib for further details.
  41. * @package core
  42. */
  43. class user {
  44. /** Login user id, string */
  45.  
  46. var $userid = "";
  47. /** True if user record is valid */
  48.  
  49. var $valid = false;
  50. /** Optional authorisation hash code */
  51.  
  52. var $auth_code = "";
  53. /** Formatted full display name of the person */
  54.  
  55. var $name = "";
  56. /** Honorific prefix Eg. 'Mr.', 'Ms.', 'Mrs.' etc. */
  57.  
  58. var $honorific_prefix = "";
  59. /** First name of the person */
  60.  
  61. var $first_name = "";
  62. /** Middle names or initials of the person */
  63.  
  64. var $mid_names = "";
  65. /** Last name of the person */
  66.  
  67. var $last_name = "";
  68. /** Text password (encrypted or plain) */
  69.  
  70. var $password = "";
  71. /** User e-mail address */
  72.  
  73. var $email = "";
  74. /** User type: arbitrary textual type */
  75.  
  76. var $user_type = "";
  77. /** True of user is active/enabled */
  78.  
  79. var $enabled = false;
  80. /** Total logins so far */
  81.  
  82. var $total_logins = 0;
  83. /** Limit of logins allowed (0=unlimited) */
  84.  
  85. var $limit_logins = 0;
  86. /** True if user has a group membership */
  87.  
  88. var $hasgroups = false;
  89. /** Array of group membership names (strings) */
  90.  
  91. var $group_names = array();
  92. /** Group membership details in full, as associative array */
  93.  
  94. var $group_info;
  95. /** Group membership count */
  96.  
  97. var $user_groups_cnt = 0;
  98. /** Complete user record as an associative array */
  99.  
  100. var $user_record;
  101. /** List of IP addresses this user will be auto-logged-in from. */
  102.  
  103. var $IP;
  104. /** Flag, true if user has auto-login IP addresses */
  105.  
  106. var $hasIPlist = false;
  107. /** Flag, true if user password never expires */
  108. var $passwd_forever = false;
  109. /** Date-time that the password will expire at (Unix timestamp)*/
  110.  
  111. var $passwd_expiry_ts = 0;
  112. /** Array of previously used passwords */
  113.  
  114. var $passwd_history = array();
  115. /** Number of consequetive password failures we have had */
  116.  
  117. var $passwd_failures = 0;
  118. /** Flag, true if this user account is locked */
  119.  
  120. var $locked;
  121. /** Security profile: how passwords are encrypted:
  122. * 'none', 'md5', 'md5salted', 'custom' */
  123.  
  124. var $passwd_encryption = "md5";
  125. /** Security profile: no. of days a password lasts */
  126.  
  127. var $passwd_expiry_days = 90;
  128. /** Security profile: no. of consequetive password failures allowed */
  129.  
  130. var $passwd_max_attempts = 5;
  131. /** Security profile: delay in millisec after a password failure */
  132.  
  133. var $passwd_delay_ms = 0;
  134. /** Security profile: min characters in a new password */
  135.  
  136. var $passwd_min_chars = 6;
  137. /** Security profile: char uniqueness level - none, low, medium, or high */
  138.  
  139. var $passwd_char_uniqueness = "medium";
  140. /** Security profile: if true, passwords must be mixture of alpha & numeric */
  141.  
  142. var $passwd_alphanum_mixed = false;
  143. /** Security profile: if true, passwords must not match built-in stopword list */
  144.  
  145. var $passwd_apply_stopwords = false;
  146. /** Security profile: password history cycle - number of saved passwords */
  147.  
  148. var $passwd_history_cycle = 0;
  149. /** User authentication source */
  150.  
  151. var $remote_auth_source = LOCAL_AUTH;
  152. /** User authentication method */
  153.  
  154. var $authentication_method = "md5";
  155. /** User authentication remote database name */
  156.  
  157. var $remote_auth_dbname = NOT_MAPPED;
  158. /** User authentication remote user table */
  159.  
  160. var $remote_auth_tablename = NOT_MAPPED;
  161. /** User authentication remote table field mapping */
  162.  
  163. var $remote_auth_fields = array();
  164.  
  165. // .....................................................................
  166. /**
  167. * Constructor
  168. * Create a new user object.
  169. * @param string $userid User ID of the user
  170. */
  171. function user($userid="") {
  172. global $RESPONSE;
  173. $this->userid = $userid;
  174. // A special case provided for making a brand new
  175. // user object for a new user record..
  176. if ($userid == "#new#") {
  177. // A new user profile..
  178. $this->valid = true;
  179. $this->name = "--enter user name--";
  180. $this->user_type = "user";
  181. $this->group_info[2] = "User"; // User
  182. $this->group_names[] = "User"; // User
  183. $this->user_groups_cnt = 1;
  184. $this->hasgroups = true;
  185. $this->userid = "<enter user id>";
  186. }
  187. // A normal user..
  188. else {
  189. // If id supplied, get user details..
  190. if ($userid != "") {
  191. $this->get_user_by_id($userid);
  192. }
  193. }
  194. return $this->valid;
  195. } // user
  196. // .....................................................................
  197. /**
  198. * Set the user authentication method. This determines how we authenticate
  199. * the user. Normally we just authenticate via the local database, but this
  200. * method allows that to be varied for remotely maintained account details.
  201. * @param integer $auth_method Code for auth moethod 0=local, 1=remote db
  202. * @param string $auth_dbname Name of the remote database
  203. * @param string $auth_tablename Name of the remote database table
  204. * @param array $auth_mappings Array of field mappings for account info
  205. */
  206. function set_remote_authentication(
  207. $auth_source = 0,
  208. $auth_method = "md5",
  209. $auth_dbname = "",
  210. $auth_tablename = "",
  211. $auth_mappings = false
  212. ) {
  213. $this->remote_auth_source = $auth_source;
  214. $this->authentication_method = $auth_method;
  215. $this->remote_auth_dbname = $auth_dbname;
  216. $this->remote_auth_tablename = $auth_tablename;
  217. $this->remote_auth_fields = array();
  218. if (is_array($auth_mappings)) {
  219. $this->remote_auth_fields = $auth_mappings;
  220. foreach ($auth_mappings as $local_fieldname => $remote_fieldname) {
  221. if ($local_fieldname != "") {
  222. $this->remote_auth_fields[$local_fieldname] = $remote_fieldname;
  223. }
  224. }
  225. }
  226. } // set_remote_authentication
  227. // .....................................................................
  228. /**
  229. * Set the user security profile. This is a bunch of parameters which will
  230. * are applied to ALL users, including this one, when passwords are being
  231. * set, created or otherwise checked.
  232. * @param string $encryption Password encryption: 'none', 'md5', 'md5salted', 'custom'
  233. * @param integer $expiry_days No. of days passwords last before expiring
  234. * @param integer $max_attempts Max. no. of consequetive failed logins
  235. * @param boolean $history_cycle No. saved passwords before cycling the list
  236. * @param integer $delay_ms Delay in mS, for a failed login
  237. * @param integer $min_chars Minimum characters in a new password
  238. * @param boolean $char_uniqueness Char uniqueness level ('low', 'medium', 'high')
  239. * @param boolean $alphanum_mixed Whether a mix of alpha and numerics are required
  240. * @param boolean $apply_stopwords Whether to apply stopword list to passwords
  241. */
  242. function set_security_profile(
  243. $encryption = "md5",
  244. $expiry_days = 0,
  245. $max_attempts = 0,
  246. $history_cycle = 0,
  247. $delay_ms = 0,
  248. $min_chars = 0,
  249. $char_uniqueness = "low",
  250. $alphanum_mixed = false,
  251. $apply_stopwords = false
  252. ) {
  253. $this->passwd_encryption = $encryption;
  254. if ($encryption == "custom") {
  255. if (!function_exists("custom_generate_password")
  256. || !function_exists("custom_authenticate_password")) {
  257. $this->passwd_encryption = "md5";
  258. debugbr("Warning: set_security_profile: custom password "
  259. . "handlers are undefined - falling back to md5.",
  260. DBG_DEBUG
  261. );
  262. }
  263. }
  264. $this->passwd_expiry_days = $expiry_days;
  265. $this->passwd_max_attempts = $max_attempts;
  266. $this->passwd_history_cycle = $history_cycle;
  267. $this->passwd_delay_ms = $delay_ms;
  268. $this->passwd_min_chars = $min_chars;
  269. $this->passwd_char_uniqueness = $char_uniqueness;
  270. $this->passwd_alphanum_mixed = $alphanum_mixed;
  271. $this->passwd_apply_stopwords = $apply_stopwords;
  272. } // set_security_profile
  273. // .....................................................................
  274. /**
  275. * Set the user login password. Store it according to the encryption
  276. * mode. We assume a plain text password is being supplied.
  277. * NB: Axyl-encrypted passwords always have an 'axenc_' prefix.
  278. * @param string $password Plain text password to set for this user
  279. * $return string The password which is going to be stored
  280. */
  281. function set_password($plaintext_password) {
  282. // Generate the password string we will be storing..
  283. $new_password = $this->generate_password($plaintext_password);
  284.  
  285. // Push old password, if we have history..
  286. $this->push_password_history();
  287. // Reset the expiry too..
  288. $this->set_password_expiry();
  289. // Assign new password..
  290. $this->password = $new_password;
  291. $this->user_record["password"] = $this->password;
  292. return $this->password;
  293. } // set_password
  294. // .....................................................................
  295. /**
  296. * Authenticate a password according to the appropriate encryption regime.
  297. * The encryption method used depends on whether the user is a normal (local)
  298. * Axyl user, or one which is being maintained on a remote system.
  299. * @param string $submitted_passwd Password submitted for authentication.
  300. * @return boolean True if the password was authenticated.
  301. */
  302. function authenticate_password($submitted_passwd) {
  303. // Pessimism is good..
  304. $authenticated = false;
  305.  
  306. // Determine which encryption method to use: local or remote..
  307. $passwd_encryption = $this->passwd_encryption;
  308. if ($this->user_type == "remote") {
  309. $passwd_encryption = $this->authentication_method;
  310. }
  311. // First, always try to match plain text, case-sensitive..
  312. if ($this->password == $submitted_passwd) {
  313. $authenticated = true;
  314. debugbr("authenticated: plaintext password", DBG_DEBUG);
  315. if ($this->passwd_encryption != "none") {
  316. debugbr("Warning: password should be encrypted.", DBG_DEBUG);
  317. }
  318. }
  319. else {
  320. // Encrypted passwords..
  321. switch ($passwd_encryption) {
  322. // Standard Axyl md5 encoded password..
  323. case "md5":
  324. $password = "axenc_" . md5($submitted_passwd);
  325. if ($this->password == $password) {
  326. $authenticated = true;
  327. debugbr("authenticated: md5 encrypted password", DBG_DEBUG);
  328. }
  329. break;
  330. // Salted md5 formatted passwords (kudos to AWM)..
  331. case "md5salted":
  332. // Check for a '**whatever' administrator-brute-forced password..
  333. if (ereg("^\*\*.+$", $this->password)) {
  334. $authenticated = ("**$submitted_passwd" == $this->password);
  335. }
  336. if (!$authenticated) {
  337. if (ereg("^\*(.+)\*.+$", $this->password, $matched)) {
  338. // A salted md5 formatted as "*<salt>*<salted_md5>"
  339. $salt = $matched[1];
  340. $password = sprintf("*%s*%s", $salt, md5($salt . $submitted_passwd));
  341. $authenticated = ($this->password == $password);
  342. }
  343. }
  344. break;
  345. // Custom authentication algorithm defined in application.php..
  346. case "custom":
  347. $new_password = custom_generate_password($plaintext_password, $salt);
  348. break;
  349. // Last chance saloon - case-insensitive plaintext match..
  350. default:
  351. $authenticated = (strcasecmp($this->password, $plaintext_password) == 0 );
  352. } // switch
  353. }
  354. return $authenticated;
  355. } // authenticate_password
  356. // .....................................................................
  357. /**
  358. * Generate a new password. Although we take note of whether the user is
  359. * local or remote, in general we don't expect to be generating passwords
  360. * for remotely maintained users.
  361. * @param string $plaintext_password The plaintext password we will use
  362. * @param string $salt Optional salt for MD5 salted passwords
  363. */
  364. function generate_password($plaintext_password, $salt="") {
  365. // Which encryption method to use: local or remote..
  366. $passwd_encryption = $this->passwd_encryption;
  367. if ($this->user_type == "remote") {
  368. $passwd_encryption = $this->authentication_method;
  369. }
  370. switch ($passwd_encryption) {
  371. // Standard Axyl prefixed md5..
  372. case "md5":
  373. $new_password = "axenc_" . md5($plaintext_password);
  374. break;
  375. // A salted md5 string..
  376. case "md5salted":
  377. if ($salt == "") {
  378. $salt = substr(md5(rand(100000,999999)), 2, 8);
  379. }
  380. $new_password = sprintf("*%s*%s", $salt, md5($salt . $plaintext_password));
  381. break;
  382. // Custom password generation algorithm..
  383. case "custom":
  384. $new_password = custom_generate_password($plaintext_password, $salt);
  385. break;
  386. // Plain-text default..
  387. default:
  388. $new_password = $plaintext_password;
  389. } // switch
  390. // Return the resulting password..
  391. return $new_password;
  392. } // generate_password
  393. // .....................................................................
  394. /**
  395. * Save the password data as stored in this object, to the user record.
  396. * $return boolean True if the data was saved ok.
  397. */
  398. function save_password_data() {
  399. $result = true;
  400. if ($this->user_type != "remote") {
  401. $uup = new dbupdate("ax_user");
  402. $uup->set("password", $this->password);
  403. $uup->set("passwd_expiry", timestamp_to_datetime($this->passwd_expiry_ts));
  404. $uup->set("passwd_history", implode("^_^", $this->passwd_history));
  405. $uup->where("user_id='" . addslashes($this->userid) . "'");
  406. $result = $uup->execute();
  407. }
  408. return $result;
  409. } // save_password_data
  410. // .....................................................................
  411. /**
  412. * Check whether the password for this user has expired. Returns true
  413. * if it has, else false.
  414. * $return boolean True if this user has an expired password.
  415. */
  416. function password_expired() {
  417. $expired = false;
  418. if ($this->user_type != "remote") {
  419. if (!$this->passwd_forever) {
  420. if (time() >= $this->passwd_expiry_ts) {
  421. $expired = true;
  422. }
  423. }
  424. }
  425. return $expired;
  426. } // password_expired
  427. // .....................................................................
  428. /**
  429. * Set the password expiry timestamp afresh. We use the settings for
  430. * how long passwords should last, and add this to the time now to
  431. * get the expiry datetime.
  432. */
  433. function set_password_expiry() {
  434. $this->passwd_expiry_ts = time() + ($this->passwd_expiry_days * SECS_1_DAY);
  435. } // set_password_expiry
  436. // .....................................................................
  437. /**
  438. * Push the current password on the history stack. Trim the history
  439. * to the number we are supposed to retain in the cycle. This method
  440. * only does anything if 'passwd_cycle_history' is non-zero. It also
  441. * checks and makes sure that the password isn't already in the
  442. * history array, and if it is, does nothing.
  443. */
  444. function push_password_history() {
  445. if ($this->passwd_history_cycle > 0) {
  446. $found = false;
  447. foreach ($this->passwd_history as $used_password) {
  448. if ($this->password == $used_password) {
  449. $found = true;
  450. break;
  451. }
  452. }
  453. if (!$found) {
  454. array_push($this->passwd_history, $this->password);
  455. while (count($this->passwd_history) > $this->passwd_history_cycle) {
  456. array_shift($this->passwd_history);
  457. }
  458. }
  459. }
  460. } // push_password_history
  461. // .....................................................................
  462. /**
  463. * Validate password against all the rules for it. Returns true if the
  464. * password passed all the tests, else false. Also provides a resulting
  465. * error message which is either a nullstring "", or an explanation of
  466. * why the validation failed.
  467. * @param string $password Plain text password to validate
  468. * @param string An array of error message explaining failure.
  469. * @return boolean True if password validated ok, else false.
  470. */
  471. function valid_password($password, &$errmsgs) {
  472. // Initialise..
  473. $valid = true;
  474. $errmsgs = array();
  475. // Length of the password..
  476. $passwd_length = strlen($password);
  477. // Check password length..
  478. if ($passwd_length < $this->passwd_min_chars ) {
  479. $errmsgs[] = "Please provide a password longer than $this->passwd_min_chars characters.";
  480. $valid = false;
  481. }
  482.  
  483. // Trivial case, must be ok after length check..
  484. if ($passwd_length == 0) {
  485. return true;
  486. }
  487. // Check character uniqueness..
  488. if ($this->passwd_char_uniqueness != "none") {
  489. $required_uniqueness = 0;
  490. switch ($this->passwd_char_uniqueness) {
  491. case "low": $required_uniqueness = 0.20; break;
  492. case "medium": $required_uniqueness = 0.60; break;
  493. case "high": $required_uniqueness = 0.80; break;
  494. } // switch
  495. $unique = strlen(count_chars($password, 3));
  496. $uniqueness = $unique / $passwd_length;
  497. if ($uniqueness < $required_uniqueness) {
  498. $errmsgs[] = "Please provide more unique characters - too many repeated.";
  499. $valid = false;
  500. }
  501. }
  502. // Check password history..
  503. if ($this->passwd_history_cycle > 0 && count($this->passwd_history) > 0) {
  504. foreach ($this->passwd_history as $used_password) {
  505. $test_password = $this->generate_password($password);
  506. if (ereg("^\*(.+)\*.+$", $used_password, $matched)) {
  507. $salt = $matched[1];
  508. if ($salt != "") {
  509. $test_password = sprintf("*%s*%s", $salt, md5($salt . $password));
  510. }
  511. }
  512. if ($used_password == $test_password) {
  513. $errmsgs[] = "That password has been used in the past. Please invent another.";
  514. $valid = false;
  515. break;
  516. }
  517. }
  518. }
  519. // Check mixture of alpha & numerics..
  520. if ($this->passwd_alphanum_mixed) {
  521. if (!preg_match("/[A-z.;:!@#%^&-_+]+[0-9]+[A-z.;:!@#%^&-_+]+/", $password)) {
  522. $errmsgs[] = "Please provide a mixture of numbers and letters, but starting "
  523. . "and ending with letters.";
  524. $valid = false;
  525. }
  526. }
  527.  
  528. // Check for common stop-words..
  529. if ($this->passwd_apply_stopwords) {
  530. // Add localised custom words first..
  531. $badwords = "password passw0rd passwd pass secret safe qwerty asdf earth mars venus pluto sexy";
  532. $badwords .= "linux unix microsoft wizard guru gandalf";
  533. $badwords .= " " . $this->full_name;
  534. $badwords .= " " . $this->first_name;
  535. $badwords .= " " . $this->mid_names;
  536. $badwords .= " " . $this->last_name;
  537. $badwords .= " " . "axyl";
  538. $badwords .= " " . APP_NAME;
  539. $badwords .= " " . APP_PREFIX;
  540. $badwords .= " " . $this->http_host;
  541. // Now add the common passwords dictionary..
  542. $AXYL_CONF = "/etc/axyl/axyl.conf";
  543. if (file_exists($AXYL_CONF)) {
  544. $result = exec("grep \"AXYL_HOME=\" $AXYL_CONF");
  545. if ($result != "") {
  546. $bits = explode("=", $result);
  547. if (is_dir($bits[1])) {
  548. $AXYL_HOME = $bits[1];
  549. $PASSWD_DICT = "$AXYL_HOME/misc/common_passwords.txt";
  550. if (file_exists($PASSWD_DICT)) {
  551. $dict = new inputfile($PASSWD_DICT);
  552. if ($dict->opened) {
  553. $badwords .= " " . $dict->readall();
  554. $dict->closefile();
  555. }
  556. }
  557. }
  558. }
  559. }
  560. // Now test the password..
  561. $passwords = explode(" ", $password);
  562. $foundbad = array();
  563. foreach ($passwords as $word) {
  564. if (stristr($badwords, $word)) {
  565. $foundbad[$word] = true;
  566. }
  567. }
  568. if (count($foundbad) > 0) {
  569. $errmsgs[] = "The password is too weak - insecure words found: " . implode(", ", array_keys($foundbad));
  570. $valid = false;
  571. }
  572. }
  573. // Return result..
  574. return $valid;
  575.  
  576. } // valid_password
  577. // .....................................................................
  578. /**
  579. * Authenticate a user
  580. * Tries all types of authentication we know about using the parameters
  581. * passed to it.
  582. * @param string $authid Unique user ID, authorization code or IP
  583. * @param string $password Password for the user
  584. * @return integer Login type code
  585. */
  586. function authenticate($authid, $password="") {
  587. $login_type = authenticate_userid($authid, $password);
  588. if ($login_type == LOGIN_UNKNOWN) {
  589. $login_type = authenticate_ip($authid);
  590. if ($login_type == LOGIN_UNKNOWN) {
  591. $login_type = authenticate_authid($authid);
  592. }
  593. }
  594. if ($login_type == LOGIN_UNKNOWN) {
  595. $this->passwd_failures += 1;
  596. if ($this->passwd_failures > $this->passwd_max_attempts) {
  597. $this->locked = true;
  598. }
  599. }
  600. return $login_type;
  601. } // authenticate
  602. // .....................................................................
  603. /**
  604. * Authenticate a user by userid/password.
  605. * @param string $userid Unique user ID of the user
  606. * @param string $submitted_password Password for the user
  607. * @return integer Login type code
  608. */
  609. function authenticate_userid($userid, $submitted_password="") {
  610. $login_type = LOGIN_UNKNOWN;
  611. // Guest authentication..
  612. if (stristr($userid, "guest")) {
  613. if ($this->user("guest") && $this->enabled) {
  614. $login_type = LOGIN_BY_GUEST;
  615. }
  616. }
  617. // Authentication by userid and password..
  618. elseif ($this->get_user_by_id($userid)) {
  619. if ($this->enabled && !$this->locked) {
  620. if ($this->authenticate_password($submitted_password)) {
  621. $login_type = LOGIN_BY_PASSWD;
  622. }
  623. }
  624. }
  625. // Flag and return result..
  626. if ($login_type != LOGIN_UNKNOWN) {
  627. debugbr("user '$userid' was authenticated", DBG_DEBUG);
  628. // If we care about failures, zeroize previous misdemeanours..
  629. if ($this->passwd_max_attempts > 0 && $this->passwd_failures > 0) {
  630. $this->passwd_failures = 0;
  631. $lkup = new dbupdate("ax_user");
  632. $lkup->set("passwd_failures", $this->passwd_failures);
  633. $lkup->where("user_id='" . addslashes($this->userid) . "'");
  634. $lkup->execute();
  635. }
  636. }
  637. else {
  638. // If we care about failures, lock out suspicious login activity..
  639. if (!$this->locked && $this->passwd_max_attempts > 0 && $this->userid != "") {
  640. $this->passwd_failures += 1;
  641. if ($this->passwd_failures >= $this->passwd_max_attempts) {
  642. $this->locked = true;
  643. debugbr("password failures exceed limit ($this->passwd_max_attempts)", DBG_DEBUG);
  644. }
  645. $lkup = new dbupdate("ax_user");
  646. $lkup->set("locked", $this->locked);
  647. $lkup->set("passwd_failures", $this->passwd_failures);
  648. $lkup->where("user_id='" . addslashes($this->userid) . "'");
  649. $lkup->execute();
  650. }
  651. // Implement failed login delay tactic, if enabled..
  652. if ($this->passwd_delay_ms > 0) {
  653. usleep($this->passwd_delay_ms * 1000);
  654. }
  655. // Report on it..
  656. $fmsg = "user '$userid' failed authentication";
  657. if ($this->locked) {
  658. $fmsg .= " (account locked)";
  659. }
  660. debugbr($fmsg, DBG_DEBUG);
  661. $this->valid = false;
  662. }
  663. return $login_type;
  664. } // authenticate_userid
  665. // .....................................................................
  666. /**
  667. * Authenticate a user by IP address
  668. * @param string $ip IP address of remote host accessing this site
  669. * @return integer Login type code
  670. */
  671. function authenticate_ipaddress($ip) {
  672. $login_type = LOGIN_UNKNOWN;
  673. // Authentication by IP..
  674. if ($this->get_user_by_ip($ip)) {
  675. if ($this->enabled && !$this->locked) {
  676. $login_type = LOGIN_BY_IP;
  677. }
  678. }
  679. // Flag and return result..
  680. if ($login_type != LOGIN_UNKNOWN) debugbr("IP address '$ip' was authenticated", DBG_DEBUG);
  681. else {
  682. $this->valid = false;
  683. $fmsg = "IP address '$ip' failed authentication.";
  684. if ($this->locked) {
  685. $fmsg .= " (account locked)";
  686. }
  687. debugbr($fmsg, DBG_DEBUG);
  688. }
  689. return $login_type;
  690. } // authenticate_ipaddress
  691. // .....................................................................
  692. /**
  693. * Authenticate a user by authorisation ID
  694. * @param string $authid Authorisation code/id of the user
  695. * @return integer Login type code
  696. */
  697. function authenticate_authid($authid) {
  698. $login_type = LOGIN_UNKNOWN;
  699. // Authentication by unique authorsation code..
  700. if ($this->get_user_by_auth_code($authid)) {
  701. if ($this->enabled) {
  702. $login_type = LOGIN_BY_AUTHCODE;
  703. }
  704. }
  705. // Flag and return result..
  706. if ($login_type != LOGIN_UNKNOWN) debugbr("authid '$authid' was authenticated", DBG_DEBUG);
  707. else {
  708. $this->valid = false;
  709. $fmsg = "user '$authid' failed authentication.";
  710. if ($this->locked) {
  711. $fmsg .= " (account locked)";
  712. }
  713. debugbr($fmsg, DBG_DEBUG);
  714. }
  715. return $login_type;
  716. } // authenticate_authid
  717. // .....................................................................
  718. /**
  719. * Get user by ID
  720. * Internal function to return the user record from id.
  721. * @param string $userid Unique user ID
  722. * @return bool True if the user was found with the given user ID
  723. */
  724. function get_user_by_id($userid) {
  725. global $RESPONSE;
  726. debug_trace($this);
  727. $this->valid = false;
  728. $this->user_record = array();
  729. // Guests are always local..
  730. if ($userid == "guest") {
  731. $this->authentication_method = LOCAL_AUTH;
  732. }
  733. debugbr("get_user_by_id: auth source is $this->remote_auth_source");
  734. switch ($this->remote_auth_source) {
  735. case REMOTE_AUTH_REMOTEDB:
  736. debugbr("get_user_by_id: getting remote user '$userid'", DBG_DEBUG);
  737. if ($RESPONSE->select_database($this->remote_auth_dbname)) {
  738. $q = "SELECT * FROM $this->remote_auth_tablename";
  739. $q .= " WHERE " . $this->remote_auth_fields["user_id"] . "='" . addslashes($userid) . "'";
  740. $remqu = dbrecordset($q);
  741. // Revert to default database..
  742. $RESPONSE->select_database();
  743. if ($remqu->hasdata) {
  744. // Got remote user record, now check if we have our copy of it..
  745. $qu = dbrecordset("SELECT * FROM ax_user WHERE user_id='" . addslashes($userid) . "'");
  746. if (!$qu->hasdata) {
  747. // Create new remote user, locally..
  748. debugbr("get_user_by_id: creating local copy of new remote user '$userid'", DBG_DEBUG);
  749. start_transaction();
  750. $axins = new dbinsert("ax_user");
  751. foreach ($RESPONSE->remote_auth_fields as $axyl_fieldname => $remote_fieldname) {
  752. if ($remote_fieldname != NOT_MAPPED && $remqu->field_exists($remote_fieldname)) {
  753. $axins->set($axyl_fieldname, $remqu->field($remote_fieldname));
  754. $this->user_record["$axyl_fieldname"] = $remqu->field($remote_fieldname);
  755. }
  756. else {
  757. $this->user_record["$axyl_fieldname"] = "";
  758. }
  759. }
  760. // Assign the correct user type..
  761. $axins->set("user_type", "remote");
  762. $axins->execute();
  763. // And create the basic group membership too..
  764. $axng = new dbinsert("ax_user_group");
  765. $axng->set("user_id", $userid);
  766. $axng->set("group_id", 2); // normal user
  767. $axng->execute();
  768. commit();
  769. }
  770. else {
  771. // Initialise to Axyl record retrieved..
  772. $this->user_record = $qu->current_row;
  773. // Check data, refresh if anything has changed..
  774. $axupd = new dbupdate("ax_user");
  775. $axupd->where("user_id='" . addslashes($userid) . "'");
  776. $user_refresh = false;
  777. foreach ($RESPONSE->remote_auth_fields as $axyl_fieldname => $remote_fieldname) {
  778. if ($remote_fieldname != NOT_MAPPED && $remqu->field_exists($remote_fieldname)) {
  779. $axval = $qu->field($axyl_fieldname);
  780. $remval = $remqu->field($remote_fieldname);
  781. if ($axval != $remval) {
  782. $user_refresh = true;
  783. $axupd->set($axyl_fieldname, $remval);
  784. $this->user_record["$axyl_fieldname"] = $remval;
  785. }
  786. }
  787. }
  788. // Refresh if required.
  789. if ($user_refresh) {
  790. debugbr("get_user_by_id: refreshing local copy of remote user '$userid'", DBG_DEBUG);
  791. $axupd->execute();
  792. }
  793. }
  794. // Now process Axyl user as normal..
  795. $this->valid = true;
  796. $this->assign_vars();
  797. }
  798. else {
  799. debugbr("get_user_by_id: remote falling back to local '$userid'", DBG_DEBUG);
  800. $qu = dbrecordset("SELECT * FROM ax_user WHERE user_id='" . addslashes($userid) . "'");
  801. if ($qu->hasdata) {
  802. if ($qu->field("user_type") == "remote") {
  803. // Remote user has been deleted, so delete locally too..
  804. $axdel = new dbdelete("ax_user");
  805. $axdel->where("user_id='" . addslashes($userid) . "'");
  806. $axdel->execute();
  807. debugbr("get_user_by_id: remote user '$userid' not found - culling local copy.", DBG_DEBUG);
  808. }
  809. else {
  810. // This is the case of a purely local user..
  811. $this->authentication_method = LOCAL_AUTH;
  812. $this->user_record = $qu->current_row;
  813. $this->valid = true;
  814. $this->assign_vars();
  815. }
  816. }
  817. }
  818. }
  819. break;
  820. case LOCAL_AUTH:
  821. default:
  822. debugbr("get_user_by_id: getting local '$userid'", DBG_DEBUG);
  823. $qu = dbrecordset("SELECT * FROM ax_user WHERE user_id='" . addslashes($userid) . "'");
  824. if ($qu->hasdata) {
  825. $this->user_record = $qu->current_row;
  826. $this->valid = true;
  827. $this->assign_vars();
  828. }
  829. break;
  830. } // switch
  831. debug_trace();
  832. return $this->valid;
  833. } // get_user_by_id
  834. // .....................................................................
  835. /**
  836. * Get user by Authorisation Code
  837. * Internal function to return the user record from auth_code. The
  838. * authorisation code is usually a string containing a complex key
  839. * generated by something like MD5 or better.
  840. * @param string $auth_code Authorisation code to match for this user
  841. * @return bool True if the user was found with the given authorisation code
  842. */
  843. function get_user_by_auth_code($auth_code) {
  844. debug_trace($this);
  845. $this->valid = false;
  846. debugbr("get_user_by_auth_code: getting '$auth_code'", DBG_DEBUG);
  847. $qu = dbrecordset("SELECT * FROM ax_user WHERE auth_code='$auth_code'");
  848. if ($qu->hasdata) {
  849. $this->user_record = $qu->current_row;
  850. $this->valid = true;
  851. $this->assign_vars();
  852. }
  853. debug_trace();
  854. return $this->valid;
  855. } // get_user_by_auth_code
  856. // .....................................................................
  857. /**
  858. * Get user by IP
  859. * Internal function to return the user record which has IP address(es)
  860. * which coincide with the client IP address being used for this access.
  861. * @param string $ip Allowed IP host or network to allow logins from
  862. * @return bool True if a user was found with matching IP address
  863. */
  864. function get_user_by_ip($ip) {
  865. global $RESPONSE;
  866. debug_trace($this);
  867. debugbr("get_user_by_ip: getting '$ip'", DBG_DEBUG);
  868. $this->valid = false;
  869. if ($ip != "") {
  870. if (is_ipaddress($ip)) {
  871. switch ($RESPONSE->datasource->dbtype()) {
  872. case "postgres":
  873. // PostgreSQL support special inet datatype..
  874. $qu = dbrecordset("SELECT * FROM ax_user_ip WHERE ip >>= inet '$ip'");
  875. break;
  876. default:
  877. // All others use string comparison..
  878. $qu = dbrecordset("SELECT * FROM ax_user_ip WHERE ip='$ip'");
  879. }
  880. if ($qu->hasdata) {
  881. $userid = $qu->field("user_id");
  882. $ipcount = $qu->rowcount;
  883. $qu = dbrecordset("SELECT * FROM ax_user WHERE user_id='". addslashes($userid) . "'");
  884. if ($qu->hasdata) {
  885. $this->user_record = $qu->current_row;
  886. $this->valid = true;
  887. $this->assign_vars();
  888. debugbr("IP auth success. User=$this->userid", DBG_DEBUG);
  889. }
  890. $qip = dbrecordset("SELECT * FROM ax_user_ip WHERE user_id='". addslashes($userid) . "'");
  891. if ($qip->hasdata) {
  892. if (isset($this->IP)) unset($this->IP);
  893. $this->hasIPlist = true;
  894. do {
  895. $this->IP[] = $qip->field("ip");
  896. } while ($qip->get_next());
  897. }
  898. // Warning for badly defined IP login data..
  899. if ($ipcount > 1) {
  900. debugbr("WARNING: IP-based login overlap: $ipcount matches for Remote IP='$ip'", DBG_DEBUG);
  901. }
  902. }
  903. else {
  904. debugbr("get_user_by_ip: failed to authenticate '$ip'", DBG_DEBUG);
  905. }
  906. }
  907. }
  908. debug_trace();
  909. return $this->valid;
  910. } // get_user_by_ip
  911. // ....................................................................
  912. /**
  913. * Get user Authorisation Code
  914. * Return this user's unique authorisation code; generate
  915. * one if it isn't there yet, from userid and current time.
  916. * @return string The authorisation code for the current user
  917. */
  918. function get_auth_code() {
  919. if ($this->valid) {
  920. if ($this->auth_code == "") {
  921. debug_trace($this);
  922. $seed = $this->userid . $this->name . microtime();
  923. $this->auth_code = md5($seed);
  924. $this->user_record["auth_code"] = $this->auth_code;
  925. dbcommand("UPDATE ax_user SET auth_code='$this->auth_code' WHERE user_id='" . addslashes($this->userid) . "'");
  926. debug_trace();
  927. }
  928. return $this->auth_code;
  929. }
  930. else {
  931. return false;
  932. }
  933. } // get_auth_code
  934. // ....................................................................
  935. /**
  936. * Get user groups info
  937. * For this user, populate the group data for this object. We
  938. * read the uuser_group and ugroup tables and populate the
  939. * two variables @see $user_groups and @see $group_info
  940. * @return string The groups list for the user, delimited by pipe ("|")
  941. */
  942. function get_groups() {
  943. // Initialise..
  944. $ugroups = "";
  945. if (isset($this->group_info)) unset($this->group_info);
  946.  
  947. // User group data acquisition..
  948. $q = "SELECT *";
  949. $q .= " FROM ax_user_group ug, ax_group g";
  950. $q .= " WHERE ug.user_id='" . addslashes($this->userid) . "'";
  951. $q .= " AND g.group_id=ug.group_id";
  952. $group = dbrecordset($q);
  953. if ($group->hasdata) {
  954. $this->hasgroups = true;
  955. do {
  956. $groupid = $group->field("group_id");
  957. $groupname = $group->field("group_desc");
  958. $this->group_info[$groupid] = $groupname;
  959. if ($ugroups != "") $ugroups .= "|";
  960. $ugroups .= $groupname;
  961. } while ($group->get_next());
  962. }
  963. // Force guest users into Guest group..
  964. if (stristr($this->userid, "guest")) {
  965. $ugroups = "Guest";
  966. }
  967. // Assign the group list and count..
  968. $this->group_names = explode("|", $ugroups);
  969. $this->user_groups_cnt = count($this->group_names);
  970.  
  971. // A by-product..
  972. return $ugroups;
  973. } // get_groups
  974. // ....................................................................
  975. /**
  976. * Assign user variables
  977. * Internal function to assign variables from new record..
  978. * @access private
  979. */
  980. function assign_vars() {
  981. global $RESPONSE;
  982. debug_trace($this);
  983. if ($this->valid) {
  984. $this->userid = stripslashes($this->user_record["user_id"]);
  985. $this->name = stripslashes($this->user_record["full_name"]);
  986. $this->honorific_prefix = stripslashes($this->user_record["honorific_prefix"]);
  987. $this->first_name = stripslashes($this->user_record["first_name"]);
  988. $this->mid_names = stripslashes($this->user_record["mid_names"]);
  989. $this->last_name = stripslashes($this->user_record["last_name"]);
  990. $this->email = $this->user_record["email"];
  991. $this->password = $this->user_record["password"];
  992. if ($this->passwd_encryption == "none") {
  993. $this->password = stripslashes($this->password);
  994. }
  995. $this->auth_code = $this->user_record["auth_code"];
  996. $this->user_type = $this->user_record["user_type"];
  997. $this->total_logins = $this->user_record["total_logins"];
  998. $this->limit_logins = $this->user_record["limit_logins"];
  999. $this->passwd_forever = $RESPONSE->datasource->bool_from_db_value($this->user_record["passwd_forever"]);
  1000. $this->passwd_expiry_ts = datetime_to_timestamp($this->user_record["passwd_expiry"]);
  1001. if ($this->user_record["passwd_history"] != "") {
  1002. $this->passwd_history = explode("^_^", $this->user_record["passwd_history"]);
  1003. }
  1004. $this->passwd_failures = $this->user_record["passwd_failures"];
  1005. $this->locked = $RESPONSE->datasource->bool_from_db_value($this->user_record["locked"]);
  1006. $this->enabled = $RESPONSE->datasource->bool_from_db_value($this->user_record["enabled"]);
  1007. // Get groups info..
  1008. $this->get_groups();
  1009. }
  1010. else {
  1011. $this->name = "Error: User not found";
  1012. $this->user_type = "user";
  1013. }
  1014. debug_trace();
  1015. } // assign_vars
  1016. // ....................................................................
  1017. /**
  1018. * Is user a member of a named group. The argument passed in must be a
  1019. * single group name string (ie. not a numeric group id) which is defined
  1020. * in the database.
  1021. * Return true if the user is a member of the named group.
  1022. * @param string $groupname Name of the group we are checking user membership of
  1023. * @return bool True if the user is a member of the group, else false
  1024. */
  1025. function ismemberof_group($groupname) {
  1026. $found = false;
  1027. for ($i = 0; $i < $this->user_groups_cnt; $i++) {
  1028. if (!strcasecmp(trim($this->group_names[$i]), trim($groupname))) {
  1029. $found = true;
  1030. break;
  1031. }
  1032. } // for
  1033. return $found;
  1034. } // ismemberof_group
  1035. // ....................................................................
  1036. /**
  1037. * Is user a member of one group of many
  1038. * Check user against a list of groups, return true if member of at
  1039. * least one of them. The list in $groupnames can be either a comma-delimited
  1040. * string of group names, OR an array of group names.
  1041. * @param mixed $groupnames_list Comma-delimited list OR array of group names
  1042. * @return bool True if user is member of at least one of the groups, else false
  1043. */
  1044. function ismemberof_group_in($groupnames_list) {
  1045. if (is_string($groupnames_list)) {
  1046. if (trim($groupnames_list) == "") {
  1047. return true;
  1048. }
  1049. $groupnames = explode(",", $groupnames_list);
  1050. }
  1051. else {
  1052. $groupnames = $groupnames_list;
  1053. }
  1054. $found = false;
  1055. if (count($groupnames) == 0) {
  1056. return true;
  1057. }
  1058. foreach ($groupnames as $groupname) {
  1059. $found = $this->ismemberof_group($groupname);
  1060. if ($found) break;
  1061. }
  1062. return $found;
  1063. } // ismemberof_group_in
  1064. // ....................................................................
  1065. /**
  1066. * Is user a member of a group with ID
  1067. * Return true if the user is a member of the group with given ID.
  1068. * @param string $groupid ID of the group we are checking user membership of
  1069. * @return bool True if the user is a member of the group, else false
  1070. */
  1071. function ismemberof_group_with_id($groupid) {
  1072. if (!isset($this->group_info)) {
  1073. $this->get_groups();
  1074. }
  1075. return (isset($this->group_info[$groupid]));
  1076. } // ismemberwith_groupid
  1077. // ....................................................................
  1078. /**
  1079. * Return true if the current user is a valid one. This is false when the
  1080. * user has not been authorised, or the user ID wasn't found etc. It is
  1081. * an error condition for this to be false.
  1082. * @return bool True if the current user object is valid
  1083. */
  1084. function isvalid() {
  1085. return $this->valid;
  1086. } // isvalid
  1087. // ....................................................................
  1088. /**
  1089. * Get group IDs list
  1090. * Return a string with the comma-delimited list of group ids which this
  1091. * user belongs to in it. This is useful for using in an SQL statement like:
  1092. * WHERE group_id IN (group_ids_list())
  1093. * for example. Note we only access the database to populate $this->group_info
  1094. * when we need to, not every session.
  1095. * @param string $delim Delimiter character (defaults to comma)
  1096. * @return string List of group ID's comma-delimited
  1097. */
  1098. function group_ids_list($delim=",") {
  1099. if (!isset($this->group_info)) {
  1100. $this->get_groups();
  1101. }
  1102. $gplist = array();
  1103. if (isset($this->group_info)) {
  1104. foreach ($this->group_info as $gid => $gdesc) {
  1105. $gplist[] = $gid;
  1106. }
  1107. }
  1108. return implode($delim, $gplist);
  1109. } // group_ids_list
  1110. // ....................................................................
  1111. /**
  1112. * Get group names list
  1113. * Return a string with the comma-delimited list of group names which this
  1114. * user belongs to in it. Eg. "Editor,Author,Admin"
  1115. * @param string $delim Delimiter character (defaults to comma)
  1116. * @return string List of group name's comma-delimited
  1117. */
  1118. function group_names_list($delim=",") {
  1119. if (!isset($this->group_info)) {
  1120. $this->get_groups();
  1121. }
  1122. $gplist = array();
  1123. if (isset($this->group_info)) {
  1124. foreach ($this->group_info as $gid => $gdesc) {
  1125. $gplist[] = $gdesc;
  1126. }
  1127. }
  1128. return implode($delim, $gplist);
  1129. } // group_names_list
  1130. // ....................................................................
  1131. /**
  1132. * Get friendly name
  1133. * Make a 'friendly' name from a full one. Good for "Dear... ,"
  1134. * @return string Friendly name for the current user
  1135. */
  1136. function friendlyName() {
  1137. if ($this->valid) {
  1138. $splitname = explode(" ", $this->name);
  1139. $mate = trim($splitname[0]);
  1140. if ($mate == "") $mate = $this->name;
  1141. return $mate;
  1142. }
  1143. else return "Invalid User";
  1144. } // friendlyName
  1145.  
  1146.  
  1147.  
  1148. } // user class
  1149. // ----------------------------------------------------------------------
  1150.  
  1151. /**
  1152. * The Authorised User class
  1153. * This derived class just allows us a different way of defining
  1154. * a new user, when we know their authorisation code.
  1155. * @package core
  1156. */
  1157. class authorised_user extends user {
  1158. // .....................................................................
  1159. /**
  1160. * Constructor
  1161. * Create a new authorised user object.
  1162. * @param string $auth_code Authorisation code of the user
  1163. */
  1164. function authorised_user($auth_code="") {
  1165. $this->user();
  1166. if ($auth_code != "") {
  1167. $this->get_user_by_auth_code($auth_code);
  1168. }
  1169. } // authorised_user
  1170.  
  1171.  
  1172.  
  1173. } // authorised_user class
  1174. // ----------------------------------------------------------------------
  1175.  
  1176. /**
  1177. * The Permissions class. This generic class manages permissions for a
  1178. * set of "agents" which are identified by a supplied "id". The permissions
  1179. * are the standard Create, Read, Update, Delete or any combination by
  1180. * ORing these values together.
  1181. * @package core
  1182. */
  1183. // ACCESS MODES
  1184. /** Permission to create items */
  1185. ("PERM_CREATE", 0x01);
  1186. /** Permission to read/view items */
  1187. ("PERM_READ", 0x02);
  1188. /** Permission to update/modify items */
  1189. ("PERM_UPDATE", 0x04);
  1190. /** Permission to delete items */
  1191. ("PERM_DELETE", 0x08);
  1192.  
  1193. /** All permitted */
  1194. ("PERM_ALL", 0x0f);
  1195. /** Nothing permitted */
  1196. ("PERM_NONE", 0x00);
  1197.  
  1198. // PERMISSION RETURN CODES
  1199. /** Permission is given */
  1200. ("PERM_ALLOWED", 1);
  1201. /** Permission is refused */
  1202. ("PERM_DISALLOWED", 2);
  1203. /** Permission is undefined */
  1204. ("PERM_UNDEFINED", 3);
  1205.  
  1206. /** The default agent ID */
  1207. ("DEFAULT_AGENT", "__perm_default_agent__");
  1208.  
  1209. // ......................................................................
  1210. /**
  1211. * The permissions class. This class encpasulates a set of permissions
  1212. * which can be managed and tested by the associated methods.
  1213. * @package core
  1214. */
  1215. class permissions {
  1216. /** Array of permisssions. This is an associative array with the
  1217. key being the identifier of an agent which can be permitted or
  1218. disallowed from accessing things, and the value being a
  1219. permission code as defined above. */
  1220. var $perms = array();
  1221. // .....................................................................
  1222. /**
  1223. * Constructor
  1224. * Create a new permissions object with an optional permissions set.
  1225. * @param mixed $perms If provided, must be an array of permissions
  1226. */
  1227. function permissions($perms=false) {
  1228. // Always include default perm..
  1229. $this->permit(DEFAULT_AGENT, PERM_READ);
  1230. if ( is_array($perms) ) {
  1231. $this->perms = $perms;
  1232. }
  1233. } // permissions
  1234. // .....................................................................
  1235. /**
  1236. * Assign the default permission. This is the permission which is applied
  1237. * if the supplied agent is not recognised.
  1238. * @param integer $perm The default permission to apply for unrecognised agents
  1239. */
  1240. function setdefault($perm) {
  1241. $this->permit(DEFAULT_AGENT, $perm);
  1242. } // setdefault
  1243. // .....................................................................
  1244. /**
  1245. * Assign the given agent(s) the given access permission. The first paramter
  1246. * is a (comma) delimited list of agent IDs to assign the permission to.
  1247. * @param mixed $agentids Agents to assign the permission to (array or delimited string)
  1248. * @param integer $perm The permission of combination of perms to assign
  1249. * @param string $delim The delimiter string separating agent IDs (default comma)
  1250. */
  1251. function permit($agentids, $perm, $delim=",") {
  1252. if (is_array($agentids)) $agents = $agentids;
  1253. else $agents = explode($delim, $agentids);
  1254. foreach ($agents as $agentid) {
  1255. $this->perms[$agentid] = $perm;
  1256. }
  1257. } // permit
  1258. // .....................................................................
  1259. /**
  1260. * Un-assign the given agent(s) the given access permission. The first paramter
  1261. * is a (comma) delimited list of agent IDs to unassign the permission from.
  1262. * @param mixed $agentids Agents to unassign the permission from (array or delimited string)
  1263. * @param integer $perm The permission of combination of perms to unassign
  1264. * @param string $delim The delimiter string separating agent IDs (default comma)
  1265. */
  1266. function unpermit($agentids, $perm, $delim=",") {
  1267. if (is_array($agentids)) $agents = $agentids;
  1268. else $agents = explode($delim, $agentids);
  1269. foreach ($agents as $agentid) {
  1270. if (isset($this->perms[$agentid])) {
  1271. $unperm = $this->perms[$agentid] & $perm;
  1272. $newperm = $this->perms[$agentid] ^ $unperm;
  1273. $this->perms[$agentid] = $newperm;
  1274. }
  1275. }
  1276. } // unpermit
  1277. // .....................................................................
  1278. /**
  1279. * Low-level method for returning the permission for the given agent and
  1280. * perm. We return one of three states: agent is allowed, agent is disallowed,
  1281. * or agent permission status is undefined/unknown. The latter would occur
  1282. * if the agent ID is unrecognised in this class (ie. not in the $perms array).
  1283. * @param integer $agentid The unique agent ID to return the permission of
  1284. * @param integer $perm The permission of combination of perms to assign
  1285. * @return integer The permission status: allowed, disallowed or undefined
  1286. */
  1287. function permission($agentid, $perm) {
  1288. if ( isset($this->perms[$agentid]) ) {
  1289. return ($perm & $this->perms[$agentid] ? PERM_ALLOWED : PERM_DISALLOWED );
  1290. }
  1291. else {
  1292. return PERM_UNDEFINED;
  1293. }
  1294. } // permission
  1295. // .....................................................................
  1296. /**
  1297. * This is the main method for querying permission access rights for a given
  1298. * agent. Returns a boolean value, true if the agent is permitted to access
  1299. * in the given way, else false. If the agent ID is unrecognised, then the
  1300. * method uses the 'default agent' permissions.
  1301. * @param integer $agentid The agent to query the access permission of
  1302. * @param integer $perm The access permission
  1303. * @return boolean True if the agent is permitted access in given ways
  1304. */
  1305. function ispermitted($agentid, $perm) {
  1306. $permission = $this->permission($agentid, $perm);
  1307. if ($permission == PERM_UNDEFINED) {
  1308. $permission = $this->permission(DEFAULT_AGENT, $perm);
  1309. }
  1310. return ($permission == PERM_ALLOWED);
  1311. } // ispermitted
  1312. // .....................................................................
  1313. /**
  1314. * This is a variant permitted query method, which takes a comma-delimited
  1315. * list of agent IDs, and returns true if ANY one or more of these has the
  1316. * required permissions. This facilitates passing of a group membership
  1317. * list for a given user, for example.
  1318. * @param mixed $agentids Agents to query the permission of (array or delimited string)
  1319. * @param integer $perm The access permission
  1320. * @param string $delim Delimiter character used (default is a comma)
  1321. * @return boolean True if the agent is permitted access in given ways
  1322. */
  1323. function anypermitted($agentids, $perm, $delim=",") {
  1324. $permitted = false;
  1325. if (is_array($agentids)) $agents = $agentids;
  1326. else $agents = explode($delim, $agentids);
  1327. foreach ($agents as $agentid) {
  1328. if ($this->ispermitted($agentid, $perm)) {
  1329. $permitted = true;
  1330. break;
  1331. }
  1332. }
  1333. return $permitted;
  1334. } // anypermitted
  1335. // .....................................................................
  1336. /**
  1337. * Decode permission as a string of the form 'crud'
  1338. * @param integer $perm The access permission to decode
  1339. * @access private
  1340. */
  1341. function decode($perm) {
  1342. $s = "";
  1343. $s .= ($perm & PERM_CREATE ? "c" : "-");
  1344. $s .= ($perm & PERM_READ ? "r" : "-");
  1345. $s .= ($perm & PERM_UPDATE ? "u" : "-");
  1346. $s .= ($perm & PERM_DELETE ? "d" : "-");
  1347. return $s;
  1348. } // decode
  1349. // .....................................................................
  1350. /** Dump these permissions as text. Mainly a debugging aid.
  1351. * @access private
  1352. */
  1353. function dump() {
  1354. $s = "";
  1355. reset($this->perms);
  1356. while (list($agentid, $perm) = each($this->perms)) {
  1357. if ($agentid != DEFAULT_AGENT) {
  1358. $s .= $agentid . "&nbsp;" . "(" . $this->decode($perm) . ") ";
  1359. }
  1360. }
  1361. if ($s == "") $s = "no perms";
  1362. return $s;
  1363. } // dump
  1364.  
  1365.  
  1366.  
  1367. } // permissions class
  1368. // ----------------------------------------------------------------------
  1369.  
  1370. ?>

Documentation generated by phpDocumentor 1.3.0RC3