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_AUTH
  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. debugbr("set_password: password changed", DBG_AUTH);
  285.  
  286. // Push old password, if we have history..
  287. $this->push_password_history();
  288. // Reset the expiry too..
  289. $this->set_password_expiry();
  290. debugbr("set_password: new expiry set to: "
  291. . timestamp_to_displaydate(NICE_FULLDATETIME, $this->passwd_expiry_ts),
  292. DBG_AUTH
  293. );
  294. // Assign new password..
  295. $this->password = $new_password;
  296. $this->user_record["password"] = $this->password;
  297. return $this->password;
  298. } // set_password
  299. // .....................................................................
  300. /**
  301. * Authenticate a password according to the appropriate encryption regime.
  302. * The encryption method used depends on whether the user is a normal (local)
  303. * Axyl user, or one which is being maintained on a remote system.
  304. * @param string $submitted_passwd Password submitted for authentication.
  305. * @return boolean True if the password was authenticated.
  306. */
  307. function authenticate_password($submitted_passwd) {
  308. // Pessimism is good..
  309. $authenticated = false;
  310.  
  311. // Determine which encryption method to use: local or remote..
  312. $passwd_encryption = $this->passwd_encryption;
  313. if ($this->user_type == "remote") {
  314. $passwd_encryption = $this->authentication_method;
  315. }
  316. // First, always try to match plain text, case-sensitive..
  317. $authmsg = "";
  318. if ($this->password == $submitted_passwd) {
  319. $authenticated = true;
  320. $authtype = "plaintext";
  321. if ($this->passwd_encryption != "none") {
  322. debugbr("authenticate_password: plaintext password should be "
  323. . "encrypted.",
  324. DBG_AUTH
  325. );
  326. }
  327. }
  328. else {
  329. // Encrypted passwords..
  330. switch ($passwd_encryption) {
  331. // Standard Axyl md5 encoded password..
  332. case "md5":
  333. if ($this->password == "axenc_" . md5($submitted_passwd)) {
  334. $authenticated = true;
  335. $authtype = "md5-axyl";
  336. }
  337. break;
  338. // Salted md5 formatted passwords (kudos to AWM)..
  339. case "md5salted":
  340. // Check for a '**whatever' administrator-brute-forced password..
  341. if (ereg("^\*\*.+$", $this->password)) {
  342. if ("**$submitted_passwd" == $this->password) {
  343. $authenticated = true;
  344. $authtype = "**admin";
  345. }
  346. }
  347. if (!$authenticated) {
  348. $matches = array();
  349. if (ereg("^\*(.+)\*.+$", $this->password, $matches)) {
  350. // A salted md5 formatted as "*<salt>*<salted_md5>"
  351. $salt = $matches[1];
  352. $password = sprintf("*%s*%s", $salt, md5($salt . $submitted_passwd));
  353. if ($this->password == $password) {
  354. $authenticated = true;
  355. $authtype = "md5-salted";
  356. }
  357. }
  358. }
  359. break;
  360. // Custom authentication algorithm defined in application.php..
  361. case "custom":
  362. if (custom_authenticate_password($submitted_passwd, $this->password)) {
  363. $authenticated = true;
  364. $authtype = "custom";
  365. }
  366. break;
  367. // Last chance saloon - case-insensitive plaintext match..
  368. default:
  369. if (strcasecmp($this->password, $submitted_passwd) == 0 ) {
  370. $authenticated = true;
  371. $authtype = "plain";
  372. }
  373. } // switch
  374. }
  375. // For the record..
  376. if ($authenticated) {
  377. debugbr("authenticate_password: authenticated ok ($authtype)", DBG_AUTH);
  378. }
  379. else {
  380. debugbr("authenticate_password: failed.", DBG_AUTH);
  381. }
  382. return $authenticated;
  383. } // authenticate_password
  384. // .....................................................................
  385. /**
  386. * Generate a new password. Although we take note of whether the user is
  387. * local or remote, in general we don't expect to be generating passwords
  388. * for remotely maintained users.
  389. * @param string $plaintext_password The plaintext password we will use
  390. * @param string $salt Optional salt for MD5 salted passwords
  391. */
  392. function generate_password($plaintext_password, $salt="") {
  393. // Which encryption method to use: local or remote..
  394. $passwd_encryption = $this->passwd_encryption;
  395. if ($this->user_type == "remote") {
  396. $passwd_encryption = $this->authentication_method;
  397. }
  398. switch ($passwd_encryption) {
  399. // Standard Axyl prefixed md5..
  400. case "md5":
  401. $new_password = "axenc_" . md5($plaintext_password);
  402. break;
  403. // A salted md5 string..
  404. case "md5salted":
  405. if ($salt == "") {
  406. $salt = substr(md5(rand(100000,999999)), 2, 8);
  407. }
  408. $new_password = sprintf("*%s*%s", $salt, md5($salt . $plaintext_password));
  409. break;
  410. // Custom password generation algorithm..
  411. case "custom":
  412. $new_password = custom_generate_password($plaintext_password, $salt);
  413. break;
  414. // Plain-text default..
  415. default:
  416. $new_password = $plaintext_password;
  417. } // switch
  418. // Return the resulting password..
  419. return $new_password;
  420. } // generate_password
  421. // .....................................................................
  422. /**
  423. * Save the password data as stored in this object, to the user record.
  424. * $return boolean True if the data was saved ok.
  425. */
  426. function save_password_data() {
  427. $result = true;
  428. if ($this->user_type != "remote") {
  429. $uup = new dbupdate("ax_user");
  430. $uup->set("password", $this->password);
  431. $uup->set("passwd_expiry", timestamp_to_datetime($this->passwd_expiry_ts));
  432. $uup->set("passwd_history", implode("^_^", $this->passwd_history));
  433. $uup->where("user_id='" . escape_string($this->userid) . "'");
  434. $result = $uup->execute();
  435. }
  436. return $result;
  437. } // save_password_data
  438. // .....................................................................
  439. /**
  440. * Check whether the password for this user has expired. Returns true
  441. * if it has, else false.
  442. * $return boolean True if this user has an expired password.
  443. */
  444. function password_expired() {
  445. $expired = false;
  446. if ($this->user_type != "remote") {
  447. if (!$this->passwd_forever) {
  448. if (time() >= $this->passwd_expiry_ts) {
  449. debugbr("password_expired: ["
  450. . timestamp_to_displaydate(NICE_FULLDATETIME, $this->passwd_expiry_ts)
  451. . "]",
  452. DBG_AUTH
  453. );
  454. $expired = true;
  455. }
  456. }
  457. }
  458. return $expired;
  459. } // password_expired
  460. // .....................................................................
  461. /**
  462. * Set the password expiry timestamp afresh. We use the settings for
  463. * how long passwords should last, and add this to the time now to
  464. * get the expiry datetime.
  465. */
  466. function set_password_expiry() {
  467. $this->passwd_expiry_ts = time() + ($this->passwd_expiry_days * SECS_1_DAY);
  468. } // set_password_expiry
  469. // .....................................................................
  470. /**
  471. * Push the current password on the history stack. Trim the history
  472. * to the number we are supposed to retain in the cycle. This method
  473. * only does anything if 'passwd_cycle_history' is non-zero. It also
  474. * checks and makes sure that the password isn't already in the
  475. * history array, and if it is, does nothing.
  476. */
  477. function push_password_history() {
  478. if ($this->passwd_history_cycle > 0) {
  479. $found = false;
  480. foreach ($this->passwd_history as $used_password) {
  481. if ($this->password == $used_password) {
  482. $found = true;
  483. break;
  484. }
  485. }
  486. if (!$found) {
  487. array_push($this->passwd_history, $this->password);
  488. while (count($this->passwd_history) > $this->passwd_history_cycle) {
  489. array_shift($this->passwd_history);
  490. }
  491. }
  492. }
  493. } // push_password_history
  494. // .....................................................................
  495. /**
  496. * Validate password against all the rules for it. Returns true if the
  497. * password passed all the tests, else false. Also provides a resulting
  498. * error message which is either a nullstring "", or an explanation of
  499. * why the validation failed.
  500. * @param string $password Plain text password to validate
  501. * @param string An array of error message explaining failure.
  502. * @return boolean True if password validated ok, else false.
  503. */
  504. function valid_password($password, &$errmsgs) {
  505. // Initialise..
  506. $valid = true;
  507. $errmsgs = array();
  508. // Length of the password..
  509. $passwd_length = strlen($password);
  510. // Check password length..
  511. if ($passwd_length < $this->passwd_min_chars ) {
  512. $errmsgs[] = "Please provide a password longer than $this->passwd_min_chars characters.";
  513. $valid = false;
  514. }
  515.  
  516. // Trivial case, must be ok after length check..
  517. if ($passwd_length == 0) {
  518. return true;
  519. }
  520. // Check character uniqueness..
  521. if ($this->passwd_char_uniqueness != "none") {
  522. $required_uniqueness = 0;
  523. switch ($this->passwd_char_uniqueness) {
  524. case "low": $required_uniqueness = 0.20; break;
  525. case "medium": $required_uniqueness = 0.60; break;
  526. case "high": $required_uniqueness = 0.80; break;
  527. } // switch
  528. $unique = strlen(count_chars($password, 3));
  529. $uniqueness = $unique / $passwd_length;
  530. if ($uniqueness < $required_uniqueness) {
  531. $errmsgs[] = "Please provide more unique characters - too many repeated.";
  532. $valid = false;
  533. }
  534. }
  535. // Check password history..
  536. if ($this->passwd_history_cycle > 0 && count($this->passwd_history) > 0) {
  537. foreach ($this->passwd_history as $used_password) {
  538. $test_password = $this->generate_password($password);
  539. if (ereg("^\*(.+)\*.+$", $used_password, $matched)) {
  540. $salt = $matched[1];
  541. if ($salt != "") {
  542. $test_password = sprintf("*%s*%s", $salt, md5($salt . $password));
  543. }
  544. }
  545. if ($used_password == $test_password) {
  546. $errmsgs[] = "That password has been used in the past. Please invent another.";
  547. $valid = false;
  548. break;
  549. }
  550. }
  551. }
  552. // Check mixture of alpha & numerics..
  553. if ($this->passwd_alphanum_mixed) {
  554. if (!preg_match("/[A-z.;:!@#%^&-_+]+[0-9]+[A-z.;:!@#%^&-_+]+/", $password)) {
  555. $errmsgs[] = "Please provide a mixture of numbers and letters, but starting "
  556. . "and ending with letters.";
  557. $valid = false;
  558. }
  559. }
  560.  
  561. // Check for common stop-words..
  562. if ($this->passwd_apply_stopwords) {
  563. // Add localised custom words first..
  564. $badwords = "password passw0rd passwd pass secret safe qwerty asdf earth mars venus pluto sexy";
  565. $badwords .= "linux unix microsoft wizard guru gandalf";
  566. $badwords .= " " . $this->full_name;
  567. $badwords .= " " . $this->first_name;
  568. $badwords .= " " . $this->mid_names;
  569. $badwords .= " " . $this->last_name;
  570. $badwords .= " " . "axyl";
  571. $badwords .= " " . APP_NAME;
  572. $badwords .= " " . APP_PREFIX;
  573. $badwords .= " " . $this->http_host;
  574. // Now add the common passwords dictionary..
  575. $AXYL_CONF = "/etc/axyl/axyl.conf";
  576. if (file_exists($AXYL_CONF)) {
  577. $result = exec("grep \"AXYL_HOME=\" $AXYL_CONF");
  578. if ($result != "") {
  579. $bits = explode("=", $result);
  580. if (is_dir($bits[1])) {
  581. $AXYL_HOME = $bits[1];
  582. $PASSWD_DICT = "$AXYL_HOME/misc/common_passwords.txt";
  583. if (file_exists($PASSWD_DICT)) {
  584. $dict = new inputfile($PASSWD_DICT);
  585. if ($dict->opened) {
  586. $badwords .= " " . $dict->readall();
  587. $dict->closefile();
  588. }
  589. }
  590. }
  591. }
  592. }
  593. // Now test the password..
  594. $passwords = explode(" ", $password);
  595. $foundbad = array();
  596. foreach ($passwords as $word) {
  597. if (stristr($badwords, $word)) {
  598. $foundbad[$word] = true;
  599. }
  600. }
  601. if (count($foundbad) > 0) {
  602. $errmsgs[] = "The password is too weak - insecure words found: " . implode(", ", array_keys($foundbad));
  603. $valid = false;
  604. }
  605. }
  606. // Return result..
  607. return $valid;
  608.  
  609. } // valid_password
  610. // .....................................................................
  611. /**
  612. * Authenticate a user
  613. * Tries all types of authentication we know about using the parameters
  614. * passed to it.
  615. * @param string $authid Unique user ID, authorization code or IP
  616. * @param string $password Password for the user
  617. * @return integer Login type code
  618. */
  619. function authenticate($authid, $password="") {
  620. $login_type = authenticate_userid($authid, $password);
  621. if ($login_type == LOGIN_UNKNOWN) {
  622. $login_type = authenticate_ip($authid);
  623. if ($login_type == LOGIN_UNKNOWN) {
  624. $login_type = authenticate_authid($authid);
  625. }
  626. }
  627. if ($login_type == LOGIN_UNKNOWN) {
  628. $this->passwd_failures += 1;
  629. if ($this->passwd_failures > $this->passwd_max_attempts) {
  630. $this->locked = true;
  631. }
  632. }
  633. return $login_type;
  634. } // authenticate
  635. // .....................................................................
  636. /**
  637. * Authenticate a user by userid/password.
  638. * @param string $userid Unique user ID of the user
  639. * @param string $submitted_password Password for the user
  640. * @return integer Login type code
  641. */
  642. function authenticate_userid($userid, $submitted_password="") {
  643. $login_type = LOGIN_UNKNOWN;
  644. // Guest authentication..
  645. if (stristr($userid, "guest")) {
  646. if ($this->user("guest") && $this->enabled) {
  647. $login_type = LOGIN_BY_GUEST;
  648. }
  649. }
  650. // Authentication by userid and password..
  651. elseif ($this->get_user_by_id($userid)) {
  652. if ($this->enabled && !$this->locked) {
  653. if ($this->authenticate_password($submitted_password)) {
  654. $login_type = LOGIN_BY_PASSWD;
  655. }
  656. }
  657. }
  658. // Flag and return result..
  659. if ($login_type != LOGIN_UNKNOWN) {
  660. // If we care about failures, zeroize previous misdemeanours..
  661. if ($this->passwd_max_attempts > 0 && $this->passwd_failures > 0) {
  662. debugbr("authenticate_userid: zeroing password failures "
  663. . "(was $this->passwd_failures)",
  664. DBG_AUTH
  665. );
  666. $this->passwd_failures = 0;
  667. $lkup = new dbupdate("ax_user");
  668. $lkup->set("passwd_failures", $this->passwd_failures);
  669. $lkup->where("user_id='" . escape_string($this->userid) . "'");
  670. $lkup->execute();
  671. }
  672. }
  673. else {
  674. // If we care about failures, lock out suspicious login activity..
  675. if (!$this->locked && $this->passwd_max_attempts > 0 && $this->userid != "") {
  676. $this->passwd_failures += 1;
  677. if ($this->passwd_failures >= $this->passwd_max_attempts) {
  678. $this->locked = true;
  679. debugbr("authenticate_userid: password failures exceed limit "
  680. . "($this->passwd_max_attempts)",
  681. DBG_AUTH
  682. );
  683. }
  684. $lkup = new dbupdate("ax_user");
  685. $lkup->set("locked", $this->locked);
  686. $lkup->set("passwd_failures", $this->passwd_failures);
  687. $lkup->where("user_id='" . escape_string($this->userid) . "'");
  688. $lkup->execute();
  689. }
  690. // Implement failed login delay tactic, if enabled..
  691. if ($this->passwd_delay_ms > 0) {
  692. usleep($this->passwd_delay_ms * 1000);
  693. }
  694. // Report on it..
  695. $fmsg = "authenticate_userid: user [$userid] failed authentication.";
  696. if ($this->locked) {
  697. $fmsg .= " (account locked)";
  698. }
  699. debugbr($fmsg, DBG_AUTH);
  700. $this->valid = false;
  701. }
  702. return $login_type;
  703. } // authenticate_userid
  704. // .....................................................................
  705. /**
  706. * Authenticate a user by IP address
  707. * @param string $ip IP address of remote host accessing this site
  708. * @return integer Login type code
  709. */
  710. function authenticate_ipaddress($ip) {
  711. $login_type = LOGIN_UNKNOWN;
  712. // Authentication by IP..
  713. if ($this->get_user_by_ip($ip)) {
  714. if ($this->enabled && !$this->locked) {
  715. $login_type = LOGIN_BY_IP;
  716. }
  717. }
  718. // Flag and return result..
  719. if ($login_type != LOGIN_UNKNOWN) debugbr("IP address '$ip' was authenticated", DBG_DEBUG);
  720. else {
  721. $this->valid = false;
  722. $fmsg = "authenticate_ipaddress: IP address '$ip' failed authentication.";
  723. if ($this->locked) {
  724. $fmsg .= " (account locked)";
  725. }
  726. debugbr($fmsg, DBG_DEBUG);
  727. }
  728. return $login_type;
  729. } // authenticate_ipaddress
  730. // .....................................................................
  731. /**
  732. * Authenticate a user by authorisation ID
  733. * @param string $authid Authorisation code/id of the user
  734. * @return integer Login type code
  735. */
  736. function authenticate_authid($authid) {
  737. $login_type = LOGIN_UNKNOWN;
  738. // Authentication by unique authorsation code..
  739. if ($this->get_user_by_auth_code($authid)) {
  740. if ($this->enabled) {
  741. $login_type = LOGIN_BY_AUTHCODE;
  742. }
  743. }
  744. // Flag and return result..
  745. if ($login_type != LOGIN_UNKNOWN) debugbr("authid '$authid' was authenticated", DBG_DEBUG);
  746. else {
  747. $this->valid = false;
  748. $fmsg = "authenticate_authid: user '$authid' failed authentication.";
  749. if ($this->locked) {
  750. $fmsg .= " (account locked)";
  751. }
  752. debugbr($fmsg, DBG_DEBUG);
  753. }
  754. return $login_type;
  755. } // authenticate_authid
  756. // .....................................................................
  757. /**
  758. * Get user by ID
  759. * Internal function to return the user record from id.
  760. * @param string $userid Unique user ID
  761. * @return bool True if the user was found with the given user ID
  762. */
  763. function get_user_by_id($userid) {
  764. global $RESPONSE;
  765. debug_trace($this);
  766. $this->valid = false;
  767. $this->user_record = array();
  768. // Guests are always local..
  769. if ($userid == "guest") {
  770. $this->authentication_method = LOCAL_AUTH;
  771. }
  772. debugbr("get_user_by_id: auth source is $this->remote_auth_source", DBG_DEBUG);
  773. switch ($this->remote_auth_source) {
  774. case REMOTE_AUTH_REMOTEDB:
  775. debugbr("get_user_by_id: getting remote user [$userid]", DBG_AUTH);
  776. if ($RESPONSE->select_database($this->remote_auth_dbname)) {
  777. $q = "SELECT * FROM $this->remote_auth_tablename";
  778. $q .= " WHERE " . $this->remote_auth_fields["user_id"] . "='" . escape_string($userid) . "'";
  779. $remqu = dbrecordset($q);
  780. // Revert to default database..
  781. $RESPONSE->select_database();
  782. if ($remqu->hasdata) {
  783. // Got remote user record, now check if we have our copy of it..
  784. $qu = dbrecordset("SELECT * FROM ax_user WHERE user_id='" . escape_string($userid) . "'");
  785. if (!$qu->hasdata) {
  786. // Create new remote user, locally..
  787. debugbr("get_user_by_id: creating local copy of new remote user '$userid'", DBG_AUTH);
  788. start_transaction();
  789. $axins = new dbinsert("ax_user");
  790. foreach ($RESPONSE->remote_auth_fields as $axyl_fieldname => $remote_fieldname) {
  791. if ($remote_fieldname != NOT_MAPPED && $remqu->field_exists($remote_fieldname)) {
  792. $axins->set($axyl_fieldname, $remqu->field($remote_fieldname));
  793. $this->user_record["$axyl_fieldname"] = $remqu->field($remote_fieldname);
  794. }
  795. else {
  796. $this->user_record["$axyl_fieldname"] = "";
  797. }
  798. }
  799. // Assign the correct user type..
  800. $axins->set("user_type", "remote");
  801. $axins->execute();
  802. // And create the basic group membership too..
  803. $axng = new dbinsert("ax_user_group");
  804. $axng->set("user_id", $userid);
  805. $axng->set("group_id", 2); // normal user
  806. $axng->execute();
  807. commit();
  808. }
  809. else {
  810. // Initialise to Axyl record retrieved..
  811. $this->user_record = $qu->current_row;
  812. // Check data, refresh if anything has changed..
  813. $axupd = new dbupdate("ax_user");
  814. $axupd->where("user_id='" . escape_string($userid) . "'");
  815. $user_refresh = false;
  816. foreach ($RESPONSE->remote_auth_fields as $axyl_fieldname => $remote_fieldname) {
  817. if ($remote_fieldname != NOT_MAPPED && $remqu->field_exists($remote_fieldname)) {
  818. $axval = $qu->field($axyl_fieldname);
  819. $remval = $remqu->field($remote_fieldname);
  820. if ($axval != $remval) {
  821. $user_refresh = true;
  822. $axupd->set($axyl_fieldname, $remval);
  823. $this->user_record["$axyl_fieldname"] = $remval;
  824. }
  825. }
  826. }
  827. // Refresh if required.
  828. if ($user_refresh) {
  829. debugbr("get_user_by_id: refreshing local copy of remote user [$userid]", DBG_AUTH);
  830. $axupd->execute();
  831. }
  832. }
  833. // Now process Axyl user as normal..
  834. $this->valid = true;
  835. $this->assign_vars();
  836. }
  837. else {
  838. debugbr("get_user_by_id: remote falling back to local [$userid]", DBG_AUTH);
  839. $qu = dbrecordset("SELECT * FROM ax_user WHERE user_id='" . escape_string($userid) . "'");
  840. if ($qu->hasdata) {
  841. if ($qu->field("user_type") == "remote") {
  842. // Remote user has been deleted, so delete locally too..
  843. $axdel = new dbdelete("ax_user");
  844. $axdel->where("user_id='" . escape_string($userid) . "'");
  845. $axdel->execute();
  846. debugbr("get_user_by_id: remote user [$userid] not found - culling local copy.", DBG_AUTH);
  847. }
  848. else {
  849. // This is the case of a purely local user..
  850. $this->authentication_method = LOCAL_AUTH;
  851. $this->user_record = $qu->current_row;
  852. $this->valid = true;
  853. $this->assign_vars();
  854. }
  855. }
  856. }
  857. }
  858. break;
  859. case LOCAL_AUTH:
  860. default:
  861. debugbr("get_user_by_id: getting local [$userid]", DBG_AUTH);
  862. $qu = dbrecordset("SELECT * FROM ax_user WHERE user_id='" . escape_string($userid) . "'");
  863. if ($qu->hasdata) {
  864. $this->user_record = $qu->current_row;
  865. $this->valid = true;
  866. $this->assign_vars();
  867. }
  868. break;
  869. } // switch
  870. debug_trace();
  871. return $this->valid;
  872. } // get_user_by_id
  873. // .....................................................................
  874. /**
  875. * Get user by Authorisation Code
  876. * Internal function to return the user record from auth_code. The
  877. * authorisation code is usually a string containing a complex key
  878. * generated by something like MD5 or better.
  879. * @param string $auth_code Authorisation code to match for this user
  880. * @return bool True if the user was found with the given authorisation code
  881. */
  882. function get_user_by_auth_code($auth_code) {
  883. debug_trace($this);
  884. $this->valid = false;
  885. debugbr("get_user_by_auth_code: getting '$auth_code'", DBG_DEBUG);
  886. $qu = dbrecordset("SELECT * FROM ax_user WHERE auth_code='$auth_code'");
  887. if ($qu->hasdata) {
  888. $this->user_record = $qu->current_row;
  889. $this->valid = true;
  890. $this->assign_vars();
  891. }
  892. debug_trace();
  893. return $this->valid;
  894. } // get_user_by_auth_code
  895. // .....................................................................
  896. /**
  897. * Get user by IP
  898. * Internal function to return the user record which has IP address(es)
  899. * which coincide with the client IP address being used for this access.
  900. * @param string $ip Allowed IP host or network to allow logins from
  901. * @return bool True if a user was found with matching IP address
  902. */
  903. function get_user_by_ip($ip) {
  904. global $RESPONSE;
  905. debug_trace($this);
  906. debugbr("get_user_by_ip: getting '$ip'", DBG_DEBUG);
  907. $this->valid = false;
  908. if ($ip != "") {
  909. if (is_ipaddress($ip)) {
  910. switch ($RESPONSE->datasource->dbtype()) {
  911. case "postgres":
  912. // PostgreSQL support special inet datatype..
  913. $qu = dbrecordset("SELECT * FROM ax_user_ip WHERE ip >>= inet '$ip'");
  914. break;
  915. default:
  916. // All others use string comparison..
  917. $qu = dbrecordset("SELECT * FROM ax_user_ip WHERE ip='$ip'");
  918. }
  919. if ($qu->hasdata) {
  920. $userid = $qu->field("user_id");
  921. $ipcount = $qu->rowcount;
  922. $qu = dbrecordset("SELECT * FROM ax_user WHERE user_id='". escape_string($userid) . "'");
  923. if ($qu->hasdata) {
  924. $this->user_record = $qu->current_row;
  925. $this->valid = true;
  926. $this->assign_vars();
  927. debugbr("IP auth success. User=$this->userid", DBG_DEBUG);
  928. }
  929. $qip = dbrecordset("SELECT * FROM ax_user_ip WHERE user_id='". escape_string($userid) . "'");
  930. if ($qip->hasdata) {
  931. if (isset($this->IP)) unset($this->IP);
  932. $this->hasIPlist = true;
  933. do {
  934. $this->IP[] = $qip->field("ip");
  935. } while ($qip->get_next());
  936. }
  937. // Warning for badly defined IP login data..
  938. if ($ipcount > 1) {
  939. debugbr("WARNING: IP-based login overlap: $ipcount matches for Remote IP='$ip'", DBG_DEBUG);
  940. }
  941. }
  942. else {
  943. debugbr("get_user_by_ip: failed to authenticate '$ip'", DBG_DEBUG);
  944. }
  945. }
  946. }
  947. debug_trace();
  948. return $this->valid;
  949. } // get_user_by_ip
  950. // ....................................................................
  951. /**
  952. * Get user Authorisation Code
  953. * Return this user's unique authorisation code; generate
  954. * one if it isn't there yet, from userid and current time.
  955. * @return string The authorisation code for the current user
  956. */
  957. function get_auth_code() {
  958. if ($this->valid) {
  959. if ($this->auth_code == "") {
  960. debug_trace($this);
  961. $seed = $this->userid . $this->name . microtime();
  962. $this->auth_code = md5($seed);
  963. $this->user_record["auth_code"] = $this->auth_code;
  964. dbcommand("UPDATE ax_user SET auth_code='$this->auth_code' WHERE user_id='" . escape_string($this->userid) . "'");
  965. debug_trace();
  966. }
  967. return $this->auth_code;
  968. }
  969. else {
  970. return false;
  971. }
  972. } // get_auth_code
  973. // ....................................................................
  974. /**
  975. * Get user groups info
  976. * For this user, populate the group data for this object. We
  977. * read the uuser_group and ugroup tables and populate the
  978. * two variables @see $user_groups and @see $group_info
  979. * @return string The groups list for the user, delimited by pipe ("|")
  980. */
  981. function get_groups() {
  982. // Initialise..
  983. $ugroups = "";
  984. if (isset($this->group_info)) unset($this->group_info);
  985.  
  986. // User group data acquisition..
  987. $q = "SELECT *";
  988. $q .= " FROM ax_user_group ug, ax_group g";
  989. $q .= " WHERE ug.user_id='" . escape_string($this->userid) . "'";
  990. $q .= " AND g.group_id=ug.group_id";
  991. $group = dbrecordset($q);
  992. if ($group->hasdata) {
  993. $this->hasgroups = true;
  994. do {
  995. $groupid = $group->field("group_id");
  996. $groupname = $group->field("group_desc");
  997. $this->group_info[$groupid] = $groupname;
  998. if ($ugroups != "") $ugroups .= "|";
  999. $ugroups .= $groupname;
  1000. } while ($group->get_next());
  1001. }
  1002. // Force guest users into Guest group..
  1003. if (stristr($this->userid, "guest")) {
  1004. $ugroups = "Guest";
  1005. }
  1006. // Assign the group list and count..
  1007. $this->group_names = explode("|", $ugroups);
  1008. $this->user_groups_cnt = count($this->group_names);
  1009.  
  1010. // A by-product..
  1011. return $ugroups;
  1012. } // get_groups
  1013. // ....................................................................
  1014. /**
  1015. * Assign user variables
  1016. * Internal function to assign variables from new record..
  1017. * @access private
  1018. */
  1019. function assign_vars() {
  1020. global $RESPONSE;
  1021. debug_trace($this);
  1022. if ($this->valid) {
  1023. $this->userid = unescape_string($this->user_record["user_id"]);
  1024. $this->name = unescape_string($this->user_record["full_name"]);
  1025. $this->honorific_prefix = unescape_string($this->user_record["honorific_prefix"]);
  1026. $this->first_name = unescape_string($this->user_record["first_name"]);
  1027. $this->mid_names = unescape_string($this->user_record["mid_names"]);
  1028. $this->last_name = unescape_string($this->user_record["last_name"]);
  1029. $this->email = $this->user_record["email"];
  1030. $this->password = $this->user_record["password"];
  1031. if ($this->passwd_encryption == "none") {
  1032. $this->password = unescape_string($this->password);
  1033. }
  1034. $this->auth_code = $this->user_record["auth_code"];
  1035. $this->user_type = $this->user_record["user_type"];
  1036. $this->total_logins = $this->user_record["total_logins"];
  1037. $this->limit_logins = $this->user_record["limit_logins"];
  1038. $this->passwd_forever = $RESPONSE->datasource->bool_from_db_value($this->user_record["passwd_forever"]);
  1039. $this->passwd_expiry_ts = datetime_to_timestamp($this->user_record["passwd_expiry"]);
  1040. $this->last_login_ts = datetime_to_timestamp($this->user_record["last_login"]);
  1041. if ($this->user_record["passwd_history"] != "") {
  1042. $this->passwd_history = explode("^_^", $this->user_record["passwd_history"]);
  1043. }
  1044. $this->passwd_failures = $this->user_record["passwd_failures"];
  1045. $this->locked = $RESPONSE->datasource->bool_from_db_value($this->user_record["locked"]);
  1046. $this->enabled = $RESPONSE->datasource->bool_from_db_value($this->user_record["enabled"]);
  1047. // Get groups info..
  1048. $this->get_groups();
  1049. }
  1050. else {
  1051. $this->name = "Error: User not found";
  1052. $this->user_type = "user";
  1053. }
  1054. debug_trace();
  1055. } // assign_vars
  1056. // ....................................................................
  1057. /**
  1058. * Is user a member of a named group. The argument passed in must be a
  1059. * single group name string (ie. not a numeric group id) which is defined
  1060. * in the database.
  1061. * Return true if the user is a member of the named group.
  1062. * @param string $groupname Name of the group we are checking user membership of
  1063. * @return bool True if the user is a member of the group, else false
  1064. */
  1065. function ismemberof_group($groupname) {
  1066. $found = false;
  1067. for ($i = 0; $i < $this->user_groups_cnt; $i++) {
  1068. if (!strcasecmp(trim($this->group_names[$i]), trim($groupname))) {
  1069. $found = true;
  1070. break;
  1071. }
  1072. } // for
  1073. return $found;
  1074. } // ismemberof_group
  1075. // ....................................................................
  1076. /**
  1077. * Is user a member of one group of many
  1078. * Check user against a list of groups, return true if member of at
  1079. * least one of them. The list in $groupnames can be either a comma-delimited
  1080. * string of group names, OR an array of group names.
  1081. * @param mixed $groupnames_list Comma-delimited list OR array of group names
  1082. * @return bool True if user is member of at least one of the groups, else false
  1083. */
  1084. function ismemberof_group_in($groupnames_list) {
  1085. if (is_string($groupnames_list)) {
  1086. if (trim($groupnames_list) == "") {
  1087. return true;
  1088. }
  1089. $groupnames = explode(",", $groupnames_list);
  1090. }
  1091. else {
  1092. $groupnames = $groupnames_list;
  1093. }
  1094. $found = false;
  1095. if (count($groupnames) == 0) {
  1096. return true;
  1097. }
  1098. foreach ($groupnames as $groupname) {
  1099. $found = $this->ismemberof_group($groupname);
  1100. if ($found) break;
  1101. }
  1102. return $found;
  1103. } // ismemberof_group_in
  1104. // ....................................................................
  1105. /**
  1106. * Is user a member of a group with ID
  1107. * Return true if the user is a member of the group with given ID.
  1108. * @param string $groupid ID of the group we are checking user membership of
  1109. * @return bool True if the user is a member of the group, else false
  1110. */
  1111. function ismemberof_group_with_id($groupid) {
  1112. if (!isset($this->group_info)) {
  1113. $this->get_groups();
  1114. }
  1115. return (isset($this->group_info[$groupid]));
  1116. } // ismemberwith_groupid
  1117. // ....................................................................
  1118. /**
  1119. * Return true if the current user is a valid one. This is false when the
  1120. * user has not been authorised, or the user ID wasn't found etc. It is
  1121. * an error condition for this to be false.
  1122. * @return bool True if the current user object is valid
  1123. */
  1124. function isvalid() {
  1125. return $this->valid;
  1126. } // isvalid
  1127. // ....................................................................
  1128. /**
  1129. * Get group IDs list
  1130. * Return a string with the comma-delimited list of group ids which this
  1131. * user belongs to in it. This is useful for using in an SQL statement like:
  1132. * WHERE group_id IN (group_ids_list())
  1133. * for example. Note we only access the database to populate $this->group_info
  1134. * when we need to, not every session.
  1135. * @param string $delim Delimiter character (defaults to comma)
  1136. * @return string List of group ID's comma-delimited
  1137. */
  1138. function group_ids_list($delim=",") {
  1139. if (!isset($this->group_info)) {
  1140. $this->get_groups();
  1141. }
  1142. $gplist = array();
  1143. if (isset($this->group_info)) {
  1144. foreach ($this->group_info as $gid => $gdesc) {
  1145. $gplist[] = $gid;
  1146. }
  1147. }
  1148. return implode($delim, $gplist);
  1149. } // group_ids_list
  1150. // ....................................................................
  1151. /**
  1152. * Get group names list
  1153. * Return a string with the comma-delimited list of group names which this
  1154. * user belongs to in it. Eg. "Editor,Author,Admin"
  1155. * @param string $delim Delimiter character (defaults to comma)
  1156. * @return string List of group name's comma-delimited
  1157. */
  1158. function group_names_list($delim=",") {
  1159. if (!isset($this->group_info)) {
  1160. $this->get_groups();
  1161. }
  1162. $gplist = array();
  1163. if (isset($this->group_info)) {
  1164. foreach ($this->group_info as $gid => $gdesc) {
  1165. $gplist[] = $gdesc;
  1166. }
  1167. }
  1168. return implode($delim, $gplist);
  1169. } // group_names_list
  1170. // ....................................................................
  1171. /**
  1172. * Get friendly name
  1173. * Make a 'friendly' name from a full one. Good for "Dear... ,"
  1174. * @return string Friendly name for the current user
  1175. */
  1176. function friendlyName() {
  1177. if ($this->valid) {
  1178. $splitname = explode(" ", $this->name);
  1179. $mate = trim($splitname[0]);
  1180. if ($mate == "") $mate = $this->name;
  1181. return $mate;
  1182. }
  1183. else return "Invalid User";
  1184. } // friendlyName
  1185.  
  1186.  
  1187.  
  1188. } // user class
  1189. // ----------------------------------------------------------------------
  1190.  
  1191. /**
  1192. * The Authorised User class
  1193. * This derived class just allows us a different way of defining
  1194. * a new user, when we know their authorisation code.
  1195. * @package core
  1196. */
  1197. class authorised_user extends user {
  1198. // .....................................................................
  1199. /**
  1200. * Constructor
  1201. * Create a new authorised user object.
  1202. * @param string $auth_code Authorisation code of the user
  1203. */
  1204. function authorised_user($auth_code="") {
  1205. $this->user();
  1206. if ($auth_code != "") {
  1207. $this->get_user_by_auth_code($auth_code);
  1208. }
  1209. } // authorised_user
  1210.  
  1211.  
  1212.  
  1213. } // authorised_user class
  1214. // ----------------------------------------------------------------------
  1215.  
  1216. /**
  1217. * The Permissions class. This generic class manages permissions for a
  1218. * set of "agents" which are identified by a supplied "id". The permissions
  1219. * are the standard Create, Read, Update, Delete or any combination by
  1220. * ORing these values together.
  1221. * @package core
  1222. */
  1223. // ACCESS MODES
  1224. /** Permission to create items */
  1225. ("PERM_CREATE", 0x01);
  1226. /** Permission to read/view items */
  1227. ("PERM_READ", 0x02);
  1228. /** Permission to update/modify items */
  1229. ("PERM_UPDATE", 0x04);
  1230. /** Permission to delete items */
  1231. ("PERM_DELETE", 0x08);
  1232.  
  1233. /** All permitted */
  1234. ("PERM_ALL", 0x0f);
  1235. /** Nothing permitted */
  1236. ("PERM_NONE", 0x00);
  1237.  
  1238. // PERMISSION RETURN CODES
  1239. /** Permission is given */
  1240. ("PERM_ALLOWED", 1);
  1241. /** Permission is refused */
  1242. ("PERM_DISALLOWED", 2);
  1243. /** Permission is undefined */
  1244. ("PERM_UNDEFINED", 3);
  1245.  
  1246. /** The default agent ID */
  1247. ("DEFAULT_AGENT", "__perm_default_agent__");
  1248.  
  1249. // ......................................................................
  1250. /**
  1251. * The permissions class. This class encpasulates a set of permissions
  1252. * which can be managed and tested by the associated methods.
  1253. * @package core
  1254. */
  1255. class permissions {
  1256. /** Array of permisssions. This is an associative array with the
  1257. key being the identifier of an agent which can be permitted or
  1258. disallowed from accessing things, and the value being a
  1259. permission code as defined above. */
  1260. var $perms = array();
  1261. // .....................................................................
  1262. /**
  1263. * Constructor
  1264. * Create a new permissions object with an optional permissions set.
  1265. * @param mixed $perms If provided, must be an array of permissions
  1266. */
  1267. function permissions($perms=false) {
  1268. // Always include default perm..
  1269. $this->permit(DEFAULT_AGENT, PERM_READ);
  1270. if ( is_array($perms) ) {
  1271. $this->perms = $perms;
  1272. }
  1273. } // permissions
  1274. // .....................................................................
  1275. /**
  1276. * Assign the default permission. This is the permission which is applied
  1277. * if the supplied agent is not recognised.
  1278. * @param integer $perm The default permission to apply for unrecognised agents
  1279. */
  1280. function setdefault($perm) {
  1281. $this->permit(DEFAULT_AGENT, $perm);
  1282. } // setdefault
  1283. // .....................................................................
  1284. /**
  1285. * Assign the given agent(s) the given access permission. The first paramter
  1286. * is a (comma) delimited list of agent IDs to assign the permission to.
  1287. * @param mixed $agentids Agents to assign the permission to (array or delimited string)
  1288. * @param integer $perm The permission of combination of perms to assign
  1289. * @param string $delim The delimiter string separating agent IDs (default comma)
  1290. */
  1291. function permit($agentids, $perm, $delim=",") {
  1292. if (is_array($agentids)) $agents = $agentids;
  1293. else $agents = explode($delim, $agentids);
  1294. foreach ($agents as $agentid) {
  1295. $this->perms[$agentid] = $perm;
  1296. }
  1297. } // permit
  1298. // .....................................................................
  1299. /**
  1300. * Un-assign the given agent(s) the given access permission. The first paramter
  1301. * is a (comma) delimited list of agent IDs to unassign the permission from.
  1302. * @param mixed $agentids Agents to unassign the permission from (array or delimited string)
  1303. * @param integer $perm The permission of combination of perms to unassign
  1304. * @param string $delim The delimiter string separating agent IDs (default comma)
  1305. */
  1306. function unpermit($agentids, $perm, $delim=",") {
  1307. if (is_array($agentids)) $agents = $agentids;
  1308. else $agents = explode($delim, $agentids);
  1309. foreach ($agents as $agentid) {
  1310. if (isset($this->perms[$agentid])) {
  1311. $unperm = $this->perms[$agentid] & $perm;
  1312. $newperm = $this->perms[$agentid] ^ $unperm;
  1313. $this->perms[$agentid] = $newperm;
  1314. }
  1315. }
  1316. } // unpermit
  1317. // .....................................................................
  1318. /**
  1319. * Low-level method for returning the permission for the given agent and
  1320. * perm. We return one of three states: agent is allowed, agent is disallowed,
  1321. * or agent permission status is undefined/unknown. The latter would occur
  1322. * if the agent ID is unrecognised in this class (ie. not in the $perms array).
  1323. * @param integer $agentid The unique agent ID to return the permission of
  1324. * @param integer $perm The permission of combination of perms to assign
  1325. * @return integer The permission status: allowed, disallowed or undefined
  1326. */
  1327. function permission($agentid, $perm) {
  1328. if ( isset($this->perms[$agentid]) ) {
  1329. return ($perm & $this->perms[$agentid] ? PERM_ALLOWED : PERM_DISALLOWED );
  1330. }
  1331. else {
  1332. return PERM_UNDEFINED;
  1333. }
  1334. } // permission
  1335. // .....................................................................
  1336. /**
  1337. * This is the main method for querying permission access rights for a given
  1338. * agent. Returns a boolean value, true if the agent is permitted to access
  1339. * in the given way, else false. If the agent ID is unrecognised, then the
  1340. * method uses the 'default agent' permissions.
  1341. * @param integer $agentid The agent to query the access permission of
  1342. * @param integer $perm The access permission
  1343. * @return boolean True if the agent is permitted access in given ways
  1344. */
  1345. function ispermitted($agentid, $perm) {
  1346. $permission = $this->permission($agentid, $perm);
  1347. if ($permission == PERM_UNDEFINED) {
  1348. $permission = $this->permission(DEFAULT_AGENT, $perm);
  1349. }
  1350. return ($permission == PERM_ALLOWED);
  1351. } // ispermitted
  1352. // .....................................................................
  1353. /**
  1354. * This is a variant permitted query method, which takes a comma-delimited
  1355. * list of agent IDs, and returns true if ANY one or more of these has the
  1356. * required permissions. This facilitates passing of a group membership
  1357. * list for a given user, for example.
  1358. * @param mixed $agentids Agents to query the permission of (array or delimited string)
  1359. * @param integer $perm The access permission
  1360. * @param string $delim Delimiter character used (default is a comma)
  1361. * @return boolean True if the agent is permitted access in given ways
  1362. */
  1363. function anypermitted($agentids, $perm, $delim=",") {
  1364. $permitted = false;
  1365. if (is_array($agentids)) $agents = $agentids;
  1366. else $agents = explode($delim, $agentids);
  1367. foreach ($agents as $agentid) {
  1368. if ($this->ispermitted($agentid, $perm)) {
  1369. $permitted = true;
  1370. break;
  1371. }
  1372. }
  1373. return $permitted;
  1374. } // anypermitted
  1375. // .....................................................................
  1376. /**
  1377. * Decode permission as a string of the form 'crud'
  1378. * @param integer $perm The access permission to decode
  1379. * @access private
  1380. */
  1381. function decode($perm) {
  1382. $s = "";
  1383. $s .= ($perm & PERM_CREATE ? "c" : "-");
  1384. $s .= ($perm & PERM_READ ? "r" : "-");
  1385. $s .= ($perm & PERM_UPDATE ? "u" : "-");
  1386. $s .= ($perm & PERM_DELETE ? "d" : "-");
  1387. return $s;
  1388. } // decode
  1389. // .....................................................................
  1390. /** Dump these permissions as text. Mainly a debugging aid.
  1391. * @access private
  1392. */
  1393. function dump() {
  1394. $s = "";
  1395. reset($this->perms);
  1396. while (list($agentid, $perm) = each($this->perms)) {
  1397. if ($agentid != DEFAULT_AGENT) {
  1398. $s .= $agentid . "&nbsp;" . "(" . $this->decode($perm) . ") ";
  1399. }
  1400. }
  1401. if ($s == "") $s = "no perms";
  1402. return $s;
  1403. } // dump
  1404.  
  1405.  
  1406.  
  1407. } // permissions class
  1408. // ----------------------------------------------------------------------
  1409.  
  1410. ?>

Documentation generated by phpDocumentor 1.3.0RC3