1: <?php
2:
3:
4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26:
27:
28: App::import(array('Router', 'Security'));
29:
30: 31: 32: 33: 34: 35: 36: 37:
38: class AuthComponent extends Object {
39: 40: 41: 42: 43: 44:
45: var $_loggedIn = false;
46: 47: 48: 49: 50: 51:
52: var $components = array('Session', 'RequestHandler');
53: 54: 55: 56: 57: 58:
59: var $authenticate = null;
60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70:
71: var $authorize = false;
72: 73: 74: 75: 76: 77: 78:
79: var $ajaxLogin = null;
80: 81: 82: 83: 84: 85:
86: var $userModel = 'User';
87: 88: 89: 90: 91: 92: 93:
94: var $userScope = array();
95: 96: 97: 98: 99: 100: 101:
102: var $fields = array('username' => 'username', 'password' => 'password');
103: 104: 105: 106: 107: 108: 109:
110: var $sessionKey = null;
111: 112: 113: 114: 115: 116: 117: 118: 119:
120: var $actionPath = null;
121: 122: 123: 124: 125: 126: 127:
128: var $loginAction = null;
129: 130: 131: 132: 133: 134: 135: 136: 137:
138: var $loginRedirect = null;
139: 140: 141: 142: 143: 144: 145: 146: 147: 148:
149: var $logoutRedirect = null;
150: 151: 152: 153: 154: 155:
156: var $object = null;
157: 158: 159: 160: 161: 162: 163:
164: var $loginError = null;
165: 166: 167: 168: 169: 170: 171:
172: var $authError = null;
173: 174: 175: 176: 177: 178:
179: var $autoRedirect = true;
180: 181: 182: 183: 184: 185: 186:
187: var $allowedActions = array();
188: 189: 190: 191: 192: 193: 194:
195: var $actionMap = array(
196: 'index' => 'read',
197: 'add' => 'create',
198: 'edit' => 'update',
199: 'view' => 'read',
200: 'remove' => 'delete'
201: );
202: 203: 204: 205: 206: 207:
208: var $data = array();
209: 210: 211: 212: 213: 214:
215: var $params = array();
216: 217: 218: 219: 220: 221:
222: var $_methods = array();
223: 224: 225: 226: 227: 228: 229:
230: function initialize(&$controller) {
231: $this->params = $controller->params;
232: $crud = array('create', 'read', 'update', 'delete');
233: $this->actionMap = array_merge($this->actionMap, array_combine($crud, $crud));
234: $this->_methods = $controller->methods;
235:
236: $admin = Configure::read('Routing.admin');
237: if (!empty($admin)) {
238: $this->actionMap = array_merge($this->actionMap, array(
239: $admin . '_index' => 'read',
240: $admin . '_add' => 'create',
241: $admin . '_edit' => 'update',
242: $admin . '_view' => 'read',
243: $admin . '_remove' => 'delete',
244: $admin . '_create' => 'create',
245: $admin . '_read' => 'read',
246: $admin . '_update' => 'update',
247: $admin . '_delete' => 'delete'
248: ));
249: }
250: if (Configure::read() > 0) {
251: App::import('Debugger');
252: Debugger::checkSessionKey();
253: }
254: }
255: 256: 257: 258: 259: 260: 261: 262:
263: function startup(&$controller) {
264: $isErrorOrTests = (
265: strtolower($controller->name) == 'cakeerror' ||
266: (strtolower($controller->name) == 'tests' && Configure::read() > 0)
267: );
268: if ($isErrorOrTests) {
269: return true;
270: }
271:
272: $methods = array_flip($controller->methods);
273: $action = strtolower($controller->params['action']);
274: $isMissingAction = (
275: $controller->scaffold === false &&
276: !isset($methods[$action])
277: );
278:
279: if ($isMissingAction) {
280: return true;
281: }
282:
283: if (!$this->__setDefaults()) {
284: return false;
285: }
286:
287: $this->data = $controller->data = $this->hashPasswords($controller->data);
288: $url = '';
289:
290: if (isset($controller->params['url']['url'])) {
291: $url = $controller->params['url']['url'];
292: }
293: $url = Router::normalize($url);
294: $loginAction = Router::normalize($this->loginAction);
295:
296: $allowedActions = array_map('strtolower', $this->allowedActions);
297: $isAllowed = (
298: $this->allowedActions == array('*') ||
299: in_array($action, $allowedActions)
300: );
301:
302: if ($loginAction != $url && $isAllowed) {
303: return true;
304: }
305:
306: if ($loginAction == $url) {
307: if (empty($controller->data) || !isset($controller->data[$this->userModel])) {
308: if (!$this->Session->check('Auth.redirect') && env('HTTP_REFERER')) {
309: $this->Session->write('Auth.redirect', $controller->referer(null, true));
310: }
311: return false;
312: }
313:
314: $isValid = !empty($controller->data[$this->userModel][$this->fields['username']]) &&
315: !empty($controller->data[$this->userModel][$this->fields['password']]);
316:
317: if ($isValid) {
318: $username = $controller->data[$this->userModel][$this->fields['username']];
319: $password = $controller->data[$this->userModel][$this->fields['password']];
320:
321: $data = array(
322: $this->userModel . '.' . $this->fields['username'] => $username,
323: $this->userModel . '.' . $this->fields['password'] => $password
324: );
325:
326: if ($this->login($data)) {
327: if ($this->autoRedirect) {
328: $controller->redirect($this->redirect(), null, true);
329: }
330: return true;
331: }
332: }
333:
334: $this->Session->setFlash($this->loginError, 'default', array(), 'auth');
335: $controller->data[$this->userModel][$this->fields['password']] = null;
336: return false;
337: } else {
338: if (!$this->user()) {
339: if (!$this->RequestHandler->isAjax()) {
340: $this->Session->setFlash($this->authError, 'default', array(), 'auth');
341: if (!empty($controller->params['url']) && count($controller->params['url']) >= 2) {
342: $query = $controller->params['url'];
343: unset($query['url'], $query['ext']);
344: $url .= Router::queryString($query, array());
345: }
346: $this->Session->write('Auth.redirect', $url);
347: $controller->redirect($loginAction);
348: return false;
349: } elseif (!empty($this->ajaxLogin)) {
350: $controller->viewPath = 'elements';
351: echo $controller->render($this->ajaxLogin, $this->RequestHandler->ajaxLayout);
352: $this->_stop();
353: return false;
354: } else {
355: $controller->redirect(null, 403);
356: }
357: }
358: }
359:
360: if (!$this->authorize) {
361: return true;
362: }
363:
364: extract($this->__authType());
365: switch ($type) {
366: case 'controller':
367: $this->object =& $controller;
368: break;
369: case 'crud':
370: case 'actions':
371: if (isset($controller->Acl)) {
372: $this->Acl =& $controller->Acl;
373: } else {
374: $err = 'Could not find AclComponent. Please include Acl in ';
375: $err .= 'Controller::$components.';
376: trigger_error(__($err, true), E_USER_WARNING);
377: }
378: break;
379: case 'model':
380: if (!isset($object)) {
381: $hasModel = (
382: isset($controller->{$controller->modelClass}) &&
383: is_object($controller->{$controller->modelClass})
384: );
385: $isUses = (
386: !empty($controller->uses) && isset($controller->{$controller->uses[0]}) &&
387: is_object($controller->{$controller->uses[0]})
388: );
389:
390: if ($hasModel) {
391: $object = $controller->modelClass;
392: } elseif ($isUses) {
393: $object = $controller->uses[0];
394: }
395: }
396: $type = array('model' => $object);
397: break;
398: }
399:
400: if ($this->isAuthorized($type)) {
401: return true;
402: }
403:
404: $this->Session->setFlash($this->authError, 'default', array(), 'auth');
405: $controller->redirect($controller->referer(), null, true);
406: return false;
407: }
408: 409: 410: 411: 412: 413: 414: 415:
416: function __setDefaults() {
417: if (empty($this->userModel)) {
418: trigger_error(__("Could not find \$userModel. Please set AuthComponent::\$userModel in beforeFilter().", true), E_USER_WARNING);
419: return false;
420: }
421: $defaults = array(
422: 'loginAction' => array(
423: 'controller' => Inflector::underscore(Inflector::pluralize($this->userModel)),
424: 'action' => 'login'
425: ),
426: 'sessionKey' => 'Auth.' . $this->userModel,
427: 'logoutRedirect' => $this->loginAction,
428: 'loginError' => __('Login failed. Invalid username or password.', true),
429: 'authError' => __('You are not authorized to access that location.', true)
430: );
431: foreach ($defaults as $key => $value) {
432: if (empty($this->{$key})) {
433: $this->{$key} = $value;
434: }
435: }
436: return true;
437: }
438: 439: 440: 441: 442: 443: 444: 445: 446: 447: 448: 449: 450: 451: 452: 453: 454: 455: 456: 457: 458:
459: function isAuthorized($type = null, $object = null, $user = null) {
460: if (empty($user) && !$this->user()) {
461: return false;
462: } elseif (empty($user)) {
463: $user = $this->user();
464: }
465:
466: extract($this->__authType($type));
467:
468: if (!$object) {
469: $object = $this->object;
470: }
471:
472: $valid = false;
473: switch ($type) {
474: case 'controller':
475: $valid = $object->isAuthorized();
476: break;
477: case 'actions':
478: $valid = $this->Acl->check($user, $this->action());
479: break;
480: case 'crud':
481: $this->mapActions();
482: if (!isset($this->actionMap[$this->params['action']])) {
483: $err = 'Auth::startup() - Attempted access of un-mapped action "%1$s" in';
484: $err .= ' controller "%2$s"';
485: trigger_error(
486: sprintf(__($err, true), $this->params['action'], $this->params['controller']),
487: E_USER_WARNING
488: );
489: } else {
490: $valid = $this->Acl->check(
491: $user,
492: $this->action(':controller'),
493: $this->actionMap[$this->params['action']]
494: );
495: }
496: break;
497: case 'model':
498: $this->mapActions();
499: $action = $this->params['action'];
500: if (isset($this->actionMap[$action])) {
501: $action = $this->actionMap[$action];
502: }
503: if (is_string($object)) {
504: $object = $this->getModel($object);
505: }
506: case 'object':
507: if (!isset($action)) {
508: $action = $this->action(':action');
509: }
510: if (empty($object)) {
511: trigger_error(sprintf(__('Could not find %s. Set AuthComponent::$object in beforeFilter() or pass a valid object', true), get_class($object)), E_USER_WARNING);
512: return;
513: }
514: if (method_exists($object, 'isAuthorized')) {
515: $valid = $object->isAuthorized($user, $this->action(':controller'), $action);
516: } elseif ($object) {
517: trigger_error(sprintf(__('%s::isAuthorized() is not defined.', true), get_class($object)), E_USER_WARNING);
518: }
519: break;
520: case null:
521: case false:
522: return true;
523: break;
524: default:
525: trigger_error(__('Auth::isAuthorized() - $authorize is set to an incorrect value. Allowed settings are: "actions", "crud", "model" or null.', true), E_USER_WARNING);
526: break;
527: }
528: return $valid;
529: }
530: 531: 532: 533: 534: 535: 536:
537: function __authType($auth = null) {
538: if ($auth == null) {
539: $auth = $this->authorize;
540: }
541: $object = null;
542: if (is_array($auth)) {
543: $type = key($auth);
544: $object = $auth[$type];
545: } else {
546: $type = $auth;
547: return compact('type');
548: }
549: return compact('type', 'object');
550: }
551: 552: 553: 554: 555: 556: 557: 558: 559: 560:
561: function allow() {
562: $args = func_get_args();
563: if (empty($args) || $args == array('*')) {
564: $this->allowedActions = $this->_methods;
565: } else {
566: if (isset($args[0]) && is_array($args[0])) {
567: $args = $args[0];
568: }
569: $this->allowedActions = array_merge($this->allowedActions, $args);
570: }
571: }
572: 573: 574: 575: 576: 577: 578: 579: 580: 581:
582: function deny() {
583: $args = func_get_args();
584: foreach ($args as $arg) {
585: $i = array_search($arg, $this->allowedActions);
586: if (is_int($i)) {
587: unset($this->allowedActions[$i]);
588: }
589: }
590: $this->allowedActions = array_values($this->allowedActions);
591: }
592: 593: 594: 595: 596: 597: 598:
599: function mapActions($map = array()) {
600: $crud = array('create', 'read', 'update', 'delete');
601: foreach ($map as $action => $type) {
602: if (in_array($action, $crud) && is_array($type)) {
603: foreach ($type as $typedAction) {
604: $this->actionMap[$typedAction] = $action;
605: }
606: } else {
607: $this->actionMap[$action] = $type;
608: }
609: }
610: }
611: 612: 613: 614: 615: 616: 617: 618: 619: 620: 621: 622:
623: function login($data = null) {
624: $this->__setDefaults();
625: $this->_loggedIn = false;
626:
627: if (empty($data)) {
628: $data = $this->data;
629: }
630:
631: if ($user = $this->identify($data)) {
632: $this->Session->write($this->sessionKey, $user);
633: $this->_loggedIn = true;
634: }
635: return $this->_loggedIn;
636: }
637: 638: 639: 640: 641: 642: 643: 644:
645: function logout() {
646: $this->__setDefaults();
647: $this->Session->del($this->sessionKey);
648: $this->Session->del('Auth.redirect');
649: $this->_loggedIn = false;
650: return Router::normalize($this->logoutRedirect);
651: }
652: 653: 654: 655: 656: 657: 658:
659: function user($key = null) {
660: $this->__setDefaults();
661: if (!$this->Session->check($this->sessionKey)) {
662: return null;
663: }
664:
665: if ($key == null) {
666: return array($this->userModel => $this->Session->read($this->sessionKey));
667: } else {
668: $user = $this->Session->read($this->sessionKey);
669: if (isset($user[$key])) {
670: return $user[$key];
671: }
672: return null;
673: }
674: }
675: 676: 677: 678: 679: 680: 681:
682: function redirect($url = null) {
683: if (!is_null($url)) {
684: $redir = $url;
685: $this->Session->write('Auth.redirect', $redir);
686: } elseif ($this->Session->check('Auth.redirect')) {
687: $redir = $this->Session->read('Auth.redirect');
688: $this->Session->delete('Auth.redirect');
689:
690: if (Router::normalize($redir) == Router::normalize($this->loginAction)) {
691: $redir = $this->loginRedirect;
692: }
693: } else {
694: $redir = $this->loginRedirect;
695: }
696: return Router::normalize($redir);
697: }
698: 699: 700: 701: 702: 703: 704: 705: 706: 707: 708: 709: 710:
711: function validate($object, $user = null, $action = null) {
712: if (empty($user)) {
713: $user = $this->user();
714: }
715: if (empty($user)) {
716: return false;
717: }
718: return $this->Acl->check($user, $object, $action);
719: }
720: 721: 722: 723: 724: 725: 726: 727: 728:
729: function action($action = ':controller/:action') {
730: return str_replace(
731: array(':controller', ':action'),
732: array(Inflector::camelize($this->params['controller']), $this->params['action']),
733: $this->actionPath . $action
734: );
735: }
736: 737: 738: 739: 740: 741: 742: 743:
744: function &getModel($name = null) {
745: $model = null;
746: if (!$name) {
747: $name = $this->userModel;
748: }
749:
750: if (PHP5) {
751: $model = ClassRegistry::init($name);
752: } else {
753: $model =& ClassRegistry::init($name);
754: }
755:
756: if (empty($model)) {
757: trigger_error(__('Auth::getModel() - Model is not set or could not be found', true), E_USER_WARNING);
758: return null;
759: }
760:
761: return $model;
762: }
763: 764: 765: 766: 767: 768: 769: 770: 771:
772: function identify($user = null, $conditions = null) {
773: if ($conditions === false) {
774: $conditions = null;
775: } elseif (is_array($conditions)) {
776: $conditions = array_merge((array)$this->userScope, $conditions);
777: } else {
778: $conditions = $this->userScope;
779: }
780: if (empty($user)) {
781: $user = $this->user();
782: if (empty($user)) {
783: return null;
784: }
785: } elseif (is_object($user) && is_a($user, 'Model')) {
786: if (!$user->exists()) {
787: return null;
788: }
789: $user = $user->read();
790: $user = $user[$this->userModel];
791: } elseif (is_array($user) && isset($user[$this->userModel])) {
792: $user = $user[$this->userModel];
793: }
794:
795: if (is_array($user) && (isset($user[$this->fields['username']]) || isset($user[$this->userModel . '.' . $this->fields['username']]))) {
796:
797: if (isset($user[$this->fields['username']]) && !empty($user[$this->fields['username']]) && !empty($user[$this->fields['password']])) {
798: if (trim($user[$this->fields['username']]) == '=' || trim($user[$this->fields['password']]) == '=') {
799: return false;
800: }
801: $find = array(
802: $this->userModel.'.'.$this->fields['username'] => $user[$this->fields['username']],
803: $this->userModel.'.'.$this->fields['password'] => $user[$this->fields['password']]
804: );
805: } elseif (isset($user[$this->userModel . '.' . $this->fields['username']]) && !empty($user[$this->userModel . '.' . $this->fields['username']])) {
806: if (trim($user[$this->userModel . '.' . $this->fields['username']]) == '=' || trim($user[$this->userModel . '.' . $this->fields['password']]) == '=') {
807: return false;
808: }
809: $find = array(
810: $this->userModel.'.'.$this->fields['username'] => $user[$this->userModel . '.' . $this->fields['username']],
811: $this->userModel.'.'.$this->fields['password'] => $user[$this->userModel . '.' . $this->fields['password']]
812: );
813: } else {
814: return false;
815: }
816: $model =& $this->getModel();
817: $data = $model->find(array_merge($find, $conditions), null, null, 0);
818: if (empty($data) || empty($data[$this->userModel])) {
819: return null;
820: }
821: } elseif (!empty($user) && is_string($user)) {
822: $model =& $this->getModel();
823: $data = $model->find(array_merge(array($model->escapeField() => $user), $conditions));
824:
825: if (empty($data) || empty($data[$this->userModel])) {
826: return null;
827: }
828: }
829:
830: if (!empty($data)) {
831: if (!empty($data[$this->userModel][$this->fields['password']])) {
832: unset($data[$this->userModel][$this->fields['password']]);
833: }
834: return $data[$this->userModel];
835: }
836: return null;
837: }
838: 839: 840: 841: 842: 843: 844:
845: function hashPasswords($data) {
846: if (is_object($this->authenticate) && method_exists($this->authenticate, 'hashPasswords')) {
847: return $this->authenticate->hashPasswords($data);
848: }
849:
850: if (is_array($data) && isset($data[$this->userModel])) {
851: if (isset($data[$this->userModel][$this->fields['username']]) && isset($data[$this->userModel][$this->fields['password']])) {
852: $data[$this->userModel][$this->fields['password']] = $this->password($data[$this->userModel][$this->fields['password']]);
853: }
854: }
855: return $data;
856: }
857: 858: 859: 860: 861: 862: 863:
864: function password($password) {
865: return Security::hash($password, null, true);
866: }
867: 868: 869: 870: 871: 872:
873: function shutdown(&$controller) {
874: if ($this->_loggedIn) {
875: $this->Session->del('Auth.redirect');
876: }
877: }
878: }
879: ?>