AuthExtComponent.php 7.1 KB

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