AuthExtComponent.php 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. <?php
  2. if (!defined('USER_ROLE_KEY')) {
  3. define('USER_ROLE_KEY', 'Role');
  4. }
  5. if (!defined('CLASS_USER')) {
  6. define('CLASS_USER', 'User');
  7. }
  8. App::uses('AuthComponent', 'Controller/Component');
  9. /**
  10. * Extends AuthComponent with the following addons:
  11. * - allows multiple roles per user
  12. * - auto-raises login counter and sets last_login date
  13. * - preps the session data according to completeAuth() method (adds parent data etc)
  14. * - dynamic login scope validation
  15. *
  16. * @author Mark Scherer
  17. * @cakephp 2.x
  18. * @license MIT
  19. */
  20. class AuthExtComponent extends AuthComponent {
  21. public $intermediateModel = 'RoleUser';
  22. public $roleModel = 'Role';
  23. public $fieldKey = 'role_id';
  24. public $loginAction = array('controller' => 'account', 'action' => 'login', 'admin' => false, 'plugin' => false);
  25. public $loginRedirect = array('controller' => 'overview', 'action' => 'home', 'admin' => false, 'plugin' => false);
  26. public $autoRedirect = false;
  27. public $loginError = null;
  28. protected $_defaultConfig = array(
  29. 'multi' => null, # null=auto - yes/no multiple roles (HABTM table between users and roles)
  30. 'parentModelAlias' => USER_ROLE_KEY,
  31. 'userModel' => CLASS_USER //TODO: allow plugin syntax
  32. );
  33. /**
  34. * Merge in Configure::read('Auth') settings
  35. *
  36. * @param ComponentCollection $Collection
  37. * @param array $config
  38. */
  39. public function __construct(ComponentCollection $Collection, $config = array()) {
  40. $defaults = (array)Configure::read('Auth') + $this->_defaultConfig;
  41. $config += $defaults;
  42. parent::__construct($Collection, $config);
  43. }
  44. public function initialize(Controller $Controller) {
  45. $this->Controller = $Controller;
  46. parent::initialize($Controller);
  47. }
  48. /**
  49. * AuthExtComponent::login()
  50. *
  51. * @overwrite
  52. * @param mixed $user
  53. * @return bool Success
  54. */
  55. public function login($user = null) {
  56. $Model = $this->getModel();
  57. $this->_setDefaults();
  58. if (empty($user)) {
  59. $user = $this->identify($this->Controller->request, $this->Controller->response);
  60. }
  61. $user = $this->completeAuth($user);
  62. if (empty($user)) {
  63. $this->loginError = __('invalidLoginCredentials');
  64. return false;
  65. }
  66. // custom checks
  67. if (isset($user['active'])) {
  68. if (empty($user['active'])) {
  69. $this->loginError = __('Account not active yet');
  70. return false;
  71. }
  72. if (!empty($user['suspended'])) {
  73. $this->loginError = __('Account temporarily locked');
  74. if (!empty($user['suspended_reason'])) {
  75. $this->loginError .= BR . BR . __('Reason') . ':' . BR . nl2br(h($user['suspended_reason']));
  76. }
  77. return false;
  78. }
  79. } else {
  80. if (isset($user['status']) && empty($user['status'])) {
  81. $this->loginError = __('Account not active yet');
  82. return false;
  83. }
  84. if (isset($user['status']) && defined('User::STATUS_PENDING') && $user['status'] == User::STATUS_PENDING) {
  85. $this->loginError = __('Account not active yet');
  86. return false;
  87. }
  88. if (isset($user['status']) && defined('User::STATUS_SUSPENDED') && $user['status'] == User::STATUS_SUSPENDED) {
  89. $this->loginError = __('Account temporarily locked');
  90. if (!empty($user['suspended_reason'])) {
  91. $this->loginError .= BR . BR . __('Reason') . ':' . BR . nl2br(h($user['suspended_reason']));
  92. }
  93. return false;
  94. }
  95. if (isset($user['status']) && defined('User::STATUS_DEL') && $user['status'] == User::STATUS_DEL) {
  96. $this->loginError = __('Account deleted');
  97. if (!empty($user['suspended_reason'])) {
  98. $this->loginError .= BR . BR . __('Reason') . ':' . BR . nl2br(h($user['suspended_reason']));
  99. }
  100. return false;
  101. }
  102. if (isset($user['status']) && defined('User::STATUS_ACTIVE') && $user['status'] != User::STATUS_ACTIVE) {
  103. $this->loginError = __('Unknown Error');
  104. return false;
  105. }
  106. }
  107. if (isset($user['email_confirmed']) && empty($user['email_confirmed'])) {
  108. $this->loginError = __('Email not active yet');
  109. return false;
  110. }
  111. if ($user) {
  112. // update login counter
  113. if (isset($user['logins'])) {
  114. $user['logins'] = $user['logins'] + 1;
  115. if (method_exists($Model, 'loginUpdate')) {
  116. $Model->loginUpdate($user);
  117. }
  118. }
  119. $this->Session->renew();
  120. $this->Session->write(self::$sessionKey, $user);
  121. }
  122. return $this->loggedIn();
  123. }
  124. /**
  125. * Gather session data.
  126. *
  127. * @return array User
  128. */
  129. public function completeAuth($user) {
  130. $Model = $this->getModel();
  131. $userArray = $user;
  132. if (!is_array($userArray)) {
  133. $user = $Model->get($user);
  134. if (!$user) {
  135. return array();
  136. }
  137. $userArray = array_shift($user);
  138. }
  139. if (isset($Model->hasAndBelongsToMany[$this->roleModel]['className'])) {
  140. $with = $Model->hasAndBelongsToMany[$this->roleModel]['className'];
  141. } elseif (isset($Model->hasMany[$this->intermediateModel]['className'])) {
  142. $with = $Model->hasMany[$this->intermediateModel]['className'];
  143. } elseif (isset($Model->belongsTo[$this->roleModel]['className'])) {
  144. $with = $Model->belongsTo[$this->roleModel]['className'];
  145. }
  146. if (empty($with) && $this->settings['parentModelAlias'] !== false) {
  147. trigger_error('No relation from user to role found');
  148. return $user;
  149. }
  150. // roles
  151. if (!empty($with)) {
  152. list($plugin, $withModel) = pluginSplit($with);
  153. if (!isset($this->{$withModel})) {
  154. $this->{$withModel} = ClassRegistry::init($with);
  155. }
  156. // only for multi
  157. if ($this->settings['multi'] || !isset($userArray['role_id'])) {
  158. $parentModelAlias = $this->settings['parentModelAlias'];
  159. $userArray[$parentModelAlias] = array(); # default: no roles!
  160. $roles = $this->{$withModel}->find('list', array('fields' => array($withModel . '.role_id'), 'conditions' => array($withModel . '.user_id' => $userArray['id'])));
  161. if (!empty($roles)) {
  162. // add the suplemental roles id under the Auth session key
  163. $userArray[$parentModelAlias] = $roles;
  164. }
  165. }
  166. }
  167. if (method_exists($Model, 'completeAuth')) {
  168. $userArray = $Model->completeAuth($userArray);
  169. }
  170. return $userArray;
  171. }
  172. /**
  173. * Main execution method. Handles redirecting of invalid users, and processing
  174. * of login form data.
  175. *
  176. * @overwrite
  177. * @param Controller $controller A reference to the instantiating controller object
  178. * @return bool
  179. */
  180. public function startup(Controller $controller) {
  181. if ($controller->name === 'CakeError') {
  182. return true;
  183. }
  184. $methods = array_flip(array_map('strtolower', $controller->methods));
  185. // fix: reverse camelCase first
  186. $action = strtolower(Inflector::underscore($controller->request->params['action']));
  187. $isMissingAction = (
  188. $controller->scaffold === false &&
  189. !isset($methods[$action])
  190. );
  191. if ($isMissingAction) {
  192. return true;
  193. }
  194. if (!$this->_setDefaults()) {
  195. return false;
  196. }
  197. if ($this->_isAllowed($controller)) {
  198. return true;
  199. }
  200. if (!$this->_getUser()) {
  201. return $this->_unauthenticated($controller);
  202. }
  203. if (empty($this->authorize) || $this->isAuthorized($this->user())) {
  204. return true;
  205. }
  206. return $this->_unauthorized($controller);
  207. }
  208. /**
  209. * Returns the current User model
  210. *
  211. * @return object User
  212. */
  213. public function getModel() {
  214. $model = $this->settings['userModel'];
  215. return ClassRegistry::init($model);
  216. }
  217. }