Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Implemented stateless login for Auth

  • Loading branch information...
commit b7834a2b168d92199f831cd297086404ab41e9d0 1 parent 8209097
ADmad authored
11 lib/Cake/Controller/Component/Auth/BaseAuthenticate.php
@@ -155,4 +155,15 @@ public function getUser(CakeRequest $request) {
155 155 return false;
156 156 }
157 157
  158 +/**
  159 + * Handle unauthenticated access attempt.
  160 + *
  161 + * @param CakeRequest $request A request object.
  162 + * @param CakeResponse $response A response object.
  163 + * @return mixed Either true to indicate the unauthenticated request has been
  164 + * dealt with and no more action is required by AuthComponent or void (default).
  165 + */
  166 + public function unauthenticated(CakeRequest $request, CakeResponse $response) {
  167 + }
  168 +
158 169 }
28 lib/Cake/Controller/Component/Auth/BasicAuthenticate.php
@@ -82,23 +82,15 @@ public function __construct(ComponentCollection $collection, $settings) {
82 82 }
83 83
84 84 /**
85   - * Authenticate a user using basic HTTP auth. Will use the configured User model and attempt a
86   - * login using basic HTTP auth.
  85 + * Authenticate a user using HTTP auth. Will use the configured User model and attempt a
  86 + * login using HTTP auth.
87 87 *
88 88 * @param CakeRequest $request The request to authenticate with.
89 89 * @param CakeResponse $response The response to add headers to.
90 90 * @return mixed Either false on failure, or an array of user data on success.
91 91 */
92 92 public function authenticate(CakeRequest $request, CakeResponse $response) {
93   - $result = $this->getUser($request);
94   -
95   - if (empty($result)) {
96   - $response->header($this->loginHeaders());
97   - $response->statusCode(401);
98   - $response->send();
99   - return false;
100   - }
101   - return $result;
  93 + return $this->getUser($request);
102 94 }
103 95
104 96 /**
@@ -118,6 +110,20 @@ public function getUser(CakeRequest $request) {
118 110 }
119 111
120 112 /**
  113 + * Handles an unauthenticated access attempt by sending appropriate login headers
  114 + *
  115 + * @param CakeRequest $request A request object.
  116 + * @param CakeResponse $response A response object.
  117 + * @return boolean True
  118 + */
  119 + public function unauthenticated(CakeRequest $request, CakeResponse $response) {
  120 + $response->header($this->loginHeaders());
  121 + $response->statusCode(401);
  122 + $response->send();
  123 + return true;
  124 + }
  125 +
  126 +/**
121 127 * Generate the login headers
122 128 *
123 129 * @return string Headers for logging in.
27 lib/Cake/Controller/Component/Auth/DigestAuthenticate.php
@@ -14,7 +14,7 @@
14 14 * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
15 15 */
16 16
17   -App::uses('BaseAuthenticate', 'Controller/Component/Auth');
  17 +App::uses('BasicAuthenticate', 'Controller/Component/Auth');
18 18
19 19 /**
20 20 * Digest Authentication adapter for AuthComponent.
@@ -55,7 +55,7 @@
55 55 * @package Cake.Controller.Component.Auth
56 56 * @since 2.0
57 57 */
58   -class DigestAuthenticate extends BaseAuthenticate {
  58 +class DigestAuthenticate extends BasicAuthenticate {
59 59
60 60 /**
61 61 * Settings for this object.
@@ -97,9 +97,6 @@ class DigestAuthenticate extends BaseAuthenticate {
97 97 */
98 98 public function __construct(ComponentCollection $collection, $settings) {
99 99 parent::__construct($collection, $settings);
100   - if (empty($this->settings['realm'])) {
101   - $this->settings['realm'] = env('SERVER_NAME');
102   - }
103 100 if (empty($this->settings['nonce'])) {
104 101 $this->settings['nonce'] = uniqid('');
105 102 }
@@ -109,26 +106,6 @@ public function __construct(ComponentCollection $collection, $settings) {
109 106 }
110 107
111 108 /**
112   - * Authenticate a user using Digest HTTP auth. Will use the configured User model and attempt a
113   - * login using Digest HTTP auth.
114   - *
115   - * @param CakeRequest $request The request to authenticate with.
116   - * @param CakeResponse $response The response to add headers to.
117   - * @return mixed Either false on failure, or an array of user data on success.
118   - */
119   - public function authenticate(CakeRequest $request, CakeResponse $response) {
120   - $user = $this->getUser($request);
121   -
122   - if (empty($user)) {
123   - $response->header($this->loginHeaders());
124   - $response->statusCode(401);
125   - $response->send();
126   - return false;
127   - }
128   - return $user;
129   - }
130   -
131   -/**
132 109 * Get a user based on information in the request. Used by cookie-less auth for stateless clients.
133 110 *
134 111 * @param CakeRequest $request Request object.
98 lib/Cake/Controller/Component/AuthComponent.php
@@ -157,8 +157,9 @@ class AuthComponent extends Component {
157 157 );
158 158
159 159 /**
160   - * The session key name where the record of the current user is stored. If
161   - * unspecified, it will be "Auth.User".
  160 + * The session key name where the record of the current user is stored. Default
  161 + * key is "Auth.User". If you are using only stateless authenticators set this
  162 + * to false to ensure session is not started.
162 163 *
163 164 * @var string
164 165 */
@@ -188,7 +189,7 @@ class AuthComponent extends Component {
188 189 * Normally, if a user is redirected to the $loginAction page, the location they
189 190 * were redirected from will be stored in the session so that they can be
190 191 * redirected back after a successful login. If this session value is not
191   - * set, the user will be redirected to the page specified in $loginRedirect.
  192 + * set, redirectUrl() method will return the url specified in $loginRedirect.
192 193 *
193 194 * @var mixed
194 195 * @link http://book.cakephp.org/2.0/en/core-libraries/components/authentication.html#AuthComponent::$loginRedirect
@@ -312,44 +313,43 @@ public function startup(Controller $controller) {
312 313
313 314 /**
314 315 * Checks whether current action is accessible without authentication.
315   - * If current action is login action referrer url is saved in session which is
316   - * later accessible using AuthComponent::redirectUrl().
317 316 *
318 317 * @param Controller $controller A reference to the instantiating controller object
319 318 * @return boolean True if action is accessible without authentication else false
320 319 */
321 320 protected function _isAllowed(Controller $controller) {
322 321 $action = strtolower($controller->request->params['action']);
323   -
324   - $url = '';
325   - if (isset($controller->request->url)) {
326   - $url = $controller->request->url;
327   - }
328   - $url = Router::normalize($url);
329   - $loginAction = Router::normalize($this->loginAction);
330   -
331   - if ($loginAction != $url && in_array($action, array_map('strtolower', $this->allowedActions))) {
332   - return true;
333   - }
334   -
335   - if ($loginAction == $url) {
336   - if (empty($controller->request->data)) {
337   - if (!$this->Session->check('Auth.redirect') && !$this->loginRedirect && env('HTTP_REFERER')) {
338   - $this->Session->write('Auth.redirect', $controller->referer(null, true));
339   - }
340   - }
  322 + if (in_array($action, array_map('strtolower', $this->allowedActions))) {
341 323 return true;
342 324 }
343 325 return false;
344 326 }
345 327
346 328 /**
347   - * Handle unauthenticated access attempt.
  329 + * Handles unauthenticated access attempt. First the `unathenticated()` method
  330 + * of the last authenticator in the chain will be called. The authenticator can
  331 + * handle sending response or redirection as appropriate and return `true` to
  332 + * indicate no furthur action is necessary. If authenticator returns null this
  333 + * method redirects user to login action. If it's an ajax request and
  334 + * $ajaxLogin is specified that element is rendered else a 403 http status code
  335 + * is returned.
348 336 *
349   - * @param Controller $controller A reference to the controller object
350   - * @return boolean Returns false
  337 + * @param Controller $controller A reference to the controller object.
  338 + * @return boolean True if current action is login action else false.
351 339 */
352 340 protected function _unauthenticated(Controller $controller) {
  341 + if (empty($this->_authenticateObjects)) {
  342 + $this->constructAuthenticate();
  343 + }
  344 + $auth = $this->_authenticateObjects[count($this->_authenticateObjects) - 1];
  345 + if ($auth->unauthenticated($this->request, $this->response)) {
  346 + return false;
  347 + }
  348 +
  349 + if ($this->_isLoginAction($controller)) {
  350 + return true;
  351 + }
  352 +
353 353 if (!$controller->request->is('ajax')) {
354 354 $this->flash($this->authError);
355 355 $this->Session->write('Auth.redirect', $controller->request->here());
@@ -367,11 +367,39 @@ protected function _unauthenticated(Controller $controller) {
367 367 }
368 368
369 369 /**
  370 + * Normalizes $loginAction and checks if current request url is same as login
  371 + * action. If current url is same as login action, referrer url is saved in session
  372 + * which is later accessible using redirectUrl().
  373 + *
  374 + * @param Controller $controller A reference to the controller object.
  375 + * @return boolean True if current action is login action else false.
  376 + */
  377 + protected function _isLoginAction(Controller $controller) {
  378 + $url = '';
  379 + if (isset($controller->request->url)) {
  380 + $url = $controller->request->url;
  381 + }
  382 + $url = Router::normalize($url);
  383 + $loginAction = Router::normalize($this->loginAction);
  384 +
  385 + if ($loginAction == $url) {
  386 + if (empty($controller->request->data)) {
  387 + if (!$this->Session->check('Auth.redirect') && !$this->loginRedirect && env('HTTP_REFERER')) {
  388 + $this->Session->write('Auth.redirect', $controller->referer(null, true));
  389 + }
  390 + }
  391 + return true;
  392 + }
  393 + return false;
  394 + }
  395 +
  396 +/**
370 397 * Handle unauthorized access attempt
371 398 *
372 399 * @param Controller $controller A reference to the controller object
373 400 * @return boolean Returns false
374 401 * @throws ForbiddenException
  402 + * @see AuthComponent::$unauthorizedRedirect
375 403 */
376 404 protected function _unauthorized(Controller $controller) {
377 405 if ($this->unauthorizedRedirect === false) {
@@ -395,7 +423,7 @@ protected function _unauthorized(Controller $controller) {
395 423 /**
396 424 * Attempts to introspect the correct values for object properties.
397 425 *
398   - * @return boolean
  426 + * @return boolean True
399 427 */
400 428 protected function _setDefaults() {
401 429 $defaults = array(
@@ -619,13 +647,12 @@ public function logout() {
619 647 * @link http://book.cakephp.org/2.0/en/core-libraries/components/authentication.html#accessing-the-logged-in-user
620 648 */
621 649 public static function user($key = null) {
622   - if (empty(self::$_user) && !CakeSession::check(self::$sessionKey)) {
623   - return null;
624   - }
625 650 if (!empty(self::$_user)) {
626 651 $user = self::$_user;
627   - } else {
  652 + } elseif (self::$sessionKey && CakeSession::check(self::$sessionKey)) {
628 653 $user = CakeSession::read(self::$sessionKey);
  654 + } else {
  655 + return null;
629 656 }
630 657 if ($key === null) {
631 658 return $user;
@@ -640,10 +667,6 @@ public static function user($key = null) {
640 667 * @return boolean true if a user can be found, false if one cannot.
641 668 */
642 669 protected function _getUser() {
643   - $user = $this->user();
644   - if ($user) {
645   - return true;
646   - }
647 670 if (empty($this->_authenticateObjects)) {
648 671 $this->constructAuthenticate();
649 672 }
@@ -654,6 +677,11 @@ protected function _getUser() {
654 677 return true;
655 678 }
656 679 }
  680 +
  681 + $user = $this->user();
  682 + if ($user) {
  683 + return true;
  684 + }
657 685 return false;
658 686 }
659 687
23 lib/Cake/Test/Case/Controller/Component/Auth/BasicAuthenticateTest.php
@@ -80,11 +80,10 @@ public function testConstructor() {
80 80 public function testAuthenticateNoData() {
81 81 $request = new CakeRequest('posts/index', false);
82 82
83   - $this->response->expects($this->once())
84   - ->method('header')
85   - ->with('WWW-Authenticate: Basic realm="localhost"');
  83 + $this->response->expects($this->never())
  84 + ->method('header');
86 85
87   - $this->assertFalse($this->auth->authenticate($request, $this->response));
  86 + $this->assertFalse($this->auth->getUser($request));
88 87 }
89 88
90 89 /**
@@ -96,10 +95,6 @@ public function testAuthenticateNoUsername() {
96 95 $request = new CakeRequest('posts/index', false);
97 96 $_SERVER['PHP_AUTH_PW'] = 'foobar';
98 97
99   - $this->response->expects($this->once())
100   - ->method('header')
101   - ->with('WWW-Authenticate: Basic realm="localhost"');
102   -
103 98 $this->assertFalse($this->auth->authenticate($request, $this->response));
104 99 }
105 100
@@ -113,10 +108,6 @@ public function testAuthenticateNoPassword() {
113 108 $_SERVER['PHP_AUTH_USER'] = 'mariano';
114 109 $_SERVER['PHP_AUTH_PW'] = null;
115 110
116   - $this->response->expects($this->once())
117   - ->method('header')
118   - ->with('WWW-Authenticate: Basic realm="localhost"');
119   -
120 111 $this->assertFalse($this->auth->authenticate($request, $this->response));
121 112 }
122 113
@@ -132,6 +123,8 @@ public function testAuthenticateInjection() {
132 123 $_SERVER['PHP_AUTH_USER'] = '> 1';
133 124 $_SERVER['PHP_AUTH_PW'] = "' OR 1 = 1";
134 125
  126 + $this->assertFalse($this->auth->getUser($request));
  127 +
135 128 $this->assertFalse($this->auth->authenticate($request, $this->response));
136 129 }
137 130
@@ -151,8 +144,8 @@ public function testAuthenticateChallenge() {
151 144 $this->response->expects($this->at(1))
152 145 ->method('send');
153 146
154   - $result = $this->auth->authenticate($request, $this->response);
155   - $this->assertFalse($result);
  147 + $result = $this->auth->unauthenticated($request, $this->response);
  148 + $this->assertTrue($result);
156 149 }
157 150
158 151 /**
@@ -201,7 +194,7 @@ public function testAuthenticateFailReChallenge() {
201 194 $this->response->expects($this->at(2))
202 195 ->method('send');
203 196
204   - $this->assertFalse($this->auth->authenticate($request, $this->response));
  197 + $this->assertTrue($this->auth->unauthenticated($request, $this->response));
205 198 }
206 199
207 200 }
15 lib/Cake/Test/Case/Controller/Component/Auth/DigestAuthenticateTest.php
@@ -94,11 +94,10 @@ public function testConstructor() {
94 94 public function testAuthenticateNoData() {
95 95 $request = new CakeRequest('posts/index', false);
96 96
97   - $this->response->expects($this->once())
98   - ->method('header')
99   - ->with('WWW-Authenticate: Digest realm="localhost",qop="auth",nonce="123",opaque="123abc"');
  97 + $this->response->expects($this->never())
  98 + ->method('header');
100 99
101   - $this->assertFalse($this->auth->authenticate($request, $this->response));
  100 + $this->assertFalse($this->auth->getUser($request, $this->response));
102 101 }
103 102
104 103 /**
@@ -133,7 +132,7 @@ public function testAuthenticateWrongUsername() {
133 132 $this->response->expects($this->at(2))
134 133 ->method('send');
135 134
136   - $this->assertFalse($this->auth->authenticate($request, $this->response));
  135 + $this->assertTrue($this->auth->unauthenticated($request, $this->response));
137 136 }
138 137
139 138 /**
@@ -156,8 +155,8 @@ public function testAuthenticateChallenge() {
156 155 $this->response->expects($this->at(2))
157 156 ->method('send');
158 157
159   - $result = $this->auth->authenticate($request, $this->response);
160   - $this->assertFalse($result);
  158 + $result = $this->auth->unauthenticated($request, $this->response);
  159 + $this->assertTrue($result);
161 160 }
162 161
163 162 /**
@@ -224,7 +223,7 @@ public function testAuthenticateFailReChallenge() {
224 223 $this->response->expects($this->at(2))
225 224 ->method('send');
226 225
227   - $this->assertFalse($this->auth->authenticate($request, $this->response));
  226 + $this->assertTrue($this->auth->unauthenticated($request, $this->response));
228 227 }
229 228
230 229 /**
74 lib/Cake/Test/Case/Controller/Component/AuthComponentTest.php
@@ -1348,4 +1348,78 @@ public function testUser() {
1348 1348 $result = $this->Auth->user('is_admin');
1349 1349 $this->assertFalse($result);
1350 1350 }
  1351 +
  1352 +/**
  1353 + * testStatelessAuthNoRedirect method
  1354 + *
  1355 + * @return void
  1356 + */
  1357 + public function testStatelessAuthNoRedirect() {
  1358 + if (CakeSession::id()) {
  1359 + session_destroy();
  1360 + CakeSession::$id = null;
  1361 + }
  1362 + $_SESSION = null;
  1363 +
  1364 + AuthComponent::$sessionKey = false;
  1365 + $this->Auth->authenticate = array('Basic');
  1366 + $this->Controller->request['action'] = 'admin_add';
  1367 +
  1368 + $this->Auth->response->expects($this->once())
  1369 + ->method('statusCode')
  1370 + ->with(401);
  1371 +
  1372 + $this->Auth->response->expects($this->once())
  1373 + ->method('send');
  1374 +
  1375 + $result = $this->Auth->startup($this->Controller);
  1376 + $this->assertFalse($result);
  1377 +
  1378 + $this->assertNull($this->Controller->testUrl);
  1379 + $this->assertNull(CakeSession::id());
  1380 + }
  1381 +
  1382 +/**
  1383 + * testStatelessAuthNoSessionStart method
  1384 + *
  1385 + * @return void
  1386 + */
  1387 + public function testStatelessAuthNoSessionStart() {
  1388 + if (CakeSession::id()) {
  1389 + session_destroy();
  1390 + CakeSession::$id = null;
  1391 + }
  1392 + $_SESSION = null;
  1393 +
  1394 + $_SERVER['PHP_AUTH_USER'] = 'mariano';
  1395 + $_SERVER['PHP_AUTH_PW'] = 'cake';
  1396 +
  1397 + $this->Auth->authenticate = array(
  1398 + 'Basic' => array('userModel' => 'AuthUser')
  1399 + );
  1400 + $this->Controller->request['action'] = 'admin_add';
  1401 +
  1402 + $result = $this->Auth->startup($this->Controller);
  1403 + $this->assertTrue($result);
  1404 +
  1405 + $this->assertNull(CakeSession::id());
  1406 + }
  1407 +
  1408 +/**
  1409 + * testStatelessAuthRedirect method
  1410 + *
  1411 + * @return void
  1412 + */
  1413 + public function testStatelessFollowedByStatefulAuth() {
  1414 + $this->Auth->authenticate = array('Basic', 'Form');
  1415 + $this->Controller->request['action'] = 'admin_add';
  1416 +
  1417 + $this->Auth->response->expects($this->never())->method('statusCode');
  1418 + $this->Auth->response->expects($this->never())->method('send');
  1419 +
  1420 + $result = $this->Auth->startup($this->Controller);
  1421 + $this->assertFalse($result);
  1422 +
  1423 + $this->assertEquals('/users/login', $this->Controller->testUrl);
  1424 + }
1351 1425 }

0 comments on commit b7834a2

Please sign in to comment.
Something went wrong with that request. Please try again.