security.php 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. <?php
  2. /**
  3. * Security functions for user authentication and authorization.
  4. *
  5. * Usage example:
  6. * <code>
  7. * try {
  8. * $security = webbasics\Security::getInstance();
  9. *
  10. * // Authentication: can the origin of the request be trusted?
  11. *
  12. * // Verify that a user is logged in
  13. * $security->requireLogin();
  14. *
  15. * // Use a security token to verify that the request originated from a
  16. * // trusted page. This is recommended if, for example, the script makes
  17. * // changes to the database
  18. * $security->requireToken($_REQUEST['token']);
  19. *
  20. * // Authorization: is the user allowed to request this page?
  21. * $security->requireUserRole('admin');
  22. *
  23. * ...
  24. *
  25. * // Pass token to template so that it can be used in a submitted form or
  26. * // AJAX request
  27. * $template->set('token', $auth->generateToken());
  28. *
  29. * ...
  30. *
  31. * } catch(webbasics\AuthenticationFailed $e) {
  32. * die('Get lost hacker!');
  33. * } catch(webbasics\AuthorizationFailed $e) {
  34. * http_response_code(403);
  35. * die('You are not authorized to view this page.');
  36. * }
  37. * </code>
  38. *
  39. * Corresponding login controller example:
  40. * <code>
  41. * // Find the user using ActiveRecord (not part of the WebBasics library)
  42. * $user = User::first(array('username' => $_POST['username']));
  43. *
  44. * if (!$user)
  45. * die('Invalid username');
  46. *
  47. * // Current user is part of the
  48. * $security = webbasics\Security::getInstance();
  49. * $security->setUser($user);
  50. *
  51. * // Simple: use a plain password
  52. * if (!$security->attemptPassword($user, $_POST['password']))
  53. * die('Invalid password');
  54. *
  55. * // More secure: hash the password in a javascript function before
  56. * // submitting the login form
  57. * if (!$security->attemptPasswordHash($user, $_POST['password_hash']))
  58. * die('Invalid password');
  59. * </code>
  60. *
  61. * And the User model implementation used in the example above:
  62. * <code>
  63. * use ActiveRecord\Model;
  64. * use webbasics\AuthenticatedUser;
  65. * use webbasics\AuthorizedUser;
  66. *
  67. * class User extends Model implements AuthenticatedUser, AuthorizedUser {
  68. * function getUsername() {
  69. * return $this->username;
  70. * }
  71. *
  72. * function getPasswordHash() {
  73. * return $this->password;
  74. * }
  75. *
  76. * function getCookieToken() {
  77. * return $this->cookie_token;
  78. * }
  79. *
  80. * function setCookieToken($token) {
  81. * $this->update_attribute('cookie_token', $token);
  82. * }
  83. *
  84. * function getRegistrationToken() {
  85. * return $this->registration_token;
  86. * }
  87. *
  88. * function setRegistrationToken($token) {
  89. * $this->update_attribute('registration_token', $token);
  90. * }
  91. *
  92. * function getRole() {
  93. * return $this->role;
  94. * }
  95. * }
  96. * </code>
  97. *
  98. * @author Taddeus Kroes
  99. * @date 05-10-2012
  100. */
  101. namespace webbasics;
  102. require_once 'base.php';
  103. interface AuthenticatedUser {
  104. function getUsername();
  105. function getPasswordHash();
  106. function getCookieToken();
  107. function setCookieToken($token);
  108. function getRegistrationToken();
  109. function setRegistrationToken($token);
  110. }
  111. interface AuthorizedUser {
  112. function getRole();
  113. }
  114. class Security {
  115. const SESSION_TOKEN_NAME = 'auth_token';
  116. const SESSION_NAME_USERDATA = 'auth_userdata';
  117. private static $instance;
  118. private $user;
  119. static function getInstance() {
  120. if (self::$instance === null)
  121. self::$instance = new self;
  122. return self::$instance;
  123. }
  124. private function __construct() {}
  125. function generateToken() {
  126. $session = Session::getInstance();
  127. $token = sha1(self::generateRandomString(10));
  128. $session->set(self::SESSION_TOKEN_NAME, $token);
  129. return $token;
  130. }
  131. function requireToken($request_token) {
  132. if ($request_token != $this->getSavedToken())
  133. throw new AuthenticationFailed('invalid token "%s"', $request_token);
  134. }
  135. private function getSavedToken() {
  136. $session = Session::getInstance();
  137. if (!$session->isRegistered(self::SESSION_TOKEN_NAME))
  138. throw new AuthenticationError('no token saved in session');
  139. return $session->get(self::SESSION_TOKEN_NAME);
  140. }
  141. function sessionDataExists() {
  142. return Session::getInstance()->areRegistered(array(
  143. self::SESSION_TOKEN_NAME, self::SESSION_NAME_USERDATA));
  144. }
  145. function requireLogin() {
  146. }
  147. function requireUserRole() {
  148. }
  149. //function setUser(AuthenticatedUser $user) {
  150. // $this->user = $user;
  151. //}
  152. static function generateRandomString($length) {
  153. $CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUWXYZ01234567890123456789';
  154. $string = '';
  155. srand(time());
  156. for ($i = 0; $i < $length; $i++)
  157. $string .= $CHARS[rand(0, strlen($CHARS) - 1)];
  158. return $string;
  159. }
  160. }
  161. class AuthenticationError extends FormattedException {}
  162. class AuthenticationFailed extends FormattedException {}
  163. class AuthorizationError extends FormattedException {}
  164. class AuthorizationFailed extends FormattedException {}
  165. ?>