AuthExtComponent.php 8.0 KB

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