AuthExtComponent.php 9.7 KB

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