diff --git a/src/Controller/Component/AuthComponent.php b/src/Controller/Component/AuthComponent.php index 66d20a6c504..274fe0d49ab 100644 --- a/src/Controller/Component/AuthComponent.php +++ b/src/Controller/Component/AuthComponent.php @@ -364,15 +364,10 @@ protected function _unauthenticated(Controller $controller) return $result; } - if (!$this->storage()->redirectUrl()) { - $this->storage()->redirectUrl($this->request->here(false)); - } - if (!$controller->request->is('ajax')) { $this->flash($this->_config['authError']); - $this->storage()->redirectUrl($controller->request->here(false)); - return $controller->redirect($this->_config['loginAction']); + return $controller->redirect($this->_loginActionRedirectUrl()); } if (!empty($this->_config['ajaxLogin'])) { @@ -390,6 +385,23 @@ protected function _unauthenticated(Controller $controller) return $this->response; } + /** + * @return array|string + */ + protected function _loginActionRedirectUrl() + { + $currentUrl = $this->request->here(false); + + $loginAction = $this->_config['loginAction']; + if (is_array($loginAction)) { + $loginAction['?']['redirect'] = $currentUrl; + } else { + $loginAction .= '?redirect=' . rawurlencode($currentUrl); + } + + return $loginAction; + } + /** * Normalizes config `loginAction` and checks if current request URL is same as login action. * @@ -660,7 +672,6 @@ public function logout() } $user = (array)$this->user(); $this->dispatchEvent('Auth.logout', [$user]); - $this->storage()->redirectUrl(false); $this->storage()->delete(); return Router::normalize($this->_config['logoutRedirect']); @@ -700,8 +711,6 @@ protected function _getUser() { $user = $this->user(); if ($user) { - $this->storage()->redirectUrl(false); - return true; } @@ -745,25 +754,27 @@ protected function _getUser() */ public function redirectUrl($url = null) { - if ($url !== null) { - $redir = $url; - $this->storage()->redirectUrl($redir); - } elseif ($redir = $this->storage()->redirectUrl()) { - $this->storage()->redirectUrl(false); + $redirectUrl = $this->request->query('redirect'); + if ($redirectUrl && (substr($redirectUrl, 0, 1) !== '/')) { + $redirectUrl = null; + } - if (Router::normalize($redir) === Router::normalize($this->_config['loginAction'])) { - $redir = $this->_config['loginRedirect']; + if ($url !== null) { + $redirectUrl = $url; + } elseif ($redirectUrl) { + if (Router::normalize($redirectUrl) === Router::normalize($this->_config['loginAction'])) { + $redirectUrl = $this->_config['loginRedirect']; } } elseif ($this->_config['loginRedirect']) { - $redir = $this->_config['loginRedirect']; + $redirectUrl = $this->_config['loginRedirect']; } else { - $redir = '/'; + $redirectUrl = '/'; } - if (is_array($redir)) { - return Router::url($redir + ['_base' => false]); + if (is_array($redirectUrl)) { + return Router::url($redirectUrl + ['_base' => false]); } - return $redir; + return $redirectUrl; } /** diff --git a/tests/TestCase/Controller/Component/AuthComponentTest.php b/tests/TestCase/Controller/Component/AuthComponentTest.php index f7e82ba88d4..049443150d3 100644 --- a/tests/TestCase/Controller/Component/AuthComponentTest.php +++ b/tests/TestCase/Controller/Component/AuthComponentTest.php @@ -23,6 +23,7 @@ use Cake\Network\Request; use Cake\Network\Response; use Cake\ORM\TableRegistry; +use Cake\Routing\Route\InflectedRoute; use Cake\Routing\Router; use Cake\TestSuite\TestCase; use Cake\Utility\Security; @@ -62,7 +63,7 @@ public function setUp() Configure::write('App.namespace', 'TestApp'); Router::scope('/', function ($routes) { - $routes->fallbacks('InflectedRoute'); + $routes->fallbacks(InflectedRoute::class); }); $request = new Request(); @@ -200,29 +201,6 @@ public function testIdentifyArrayAccess() $this->assertSame($AuthLoginFormAuthenticate, $this->Auth->authenticationProvider()); } - /** - * testRedirectVarClearing method - * - * @return void - * @triggers Controller.startup $this->Controller - */ - public function testRedirectVarClearing() - { - $this->Controller->request['controller'] = 'auth_test'; - $this->Controller->request['action'] = 'add'; - $this->Controller->request->here = '/auth_test/add'; - $this->assertNull($this->Auth->session->read('Auth.redirect')); - - $this->Auth->config('authenticate', ['Form']); - $event = new Event('Controller.startup', $this->Controller); - $this->Auth->startup($event); - $this->assertEquals('/auth_test/add', $this->Auth->session->read('Auth.redirect')); - - $this->Auth->storage()->write(['username' => 'admad']); - $this->Auth->startup($event, $this->Controller); - $this->assertNull($this->Auth->session->read('Auth.redirect')); - } - /** * testAuthorizeFalse method * @@ -602,8 +580,6 @@ public function testAllowedActionsSetWithAllowMethod() */ public function testLoginRedirect() { - $url = '/auth_test/camelCase'; - $this->Auth->session->write('Auth', [ 'AuthUsers' => ['id' => '1', 'username' => 'nate'] ]); @@ -630,9 +606,8 @@ public function testLoginRedirect() 'Auth', ['AuthUsers' => ['id' => '1', 'username' => 'nate']] ); - $this->Controller->testUrl = null; $this->Auth->request->addParams(Router::parse($url)); - $this->Auth->request->env('HTTP_REFERER', false); + $this->Auth->request->here = $url; $this->Auth->config('authorize', 'controller'); @@ -640,9 +615,12 @@ public function testLoginRedirect() 'controller' => 'AuthTest', 'action' => 'login' ]); $event = new Event('Controller.startup', $this->Controller); - $this->Auth->startup($event); - $expected = Router::normalize('/auth_test/login'); - $this->assertEquals($expected, $this->Controller->testUrl); + $response = $this->Auth->startup($event); + $expected = Router::url([ + 'controller' => 'AuthTest', 'action' => 'login', '?' => ['redirect' => $url] + ], true); + $redirectHeader = $response->header()['Location']; + $this->assertEquals($expected, $redirectHeader); // Auth.redirect gets set when accessing a protected action without being authenticated $this->Auth->session->delete('Auth'); @@ -651,11 +629,20 @@ public function testLoginRedirect() $this->Auth->request->url = $this->Auth->request->here = Router::normalize($url); $this->Auth->config('loginAction', ['controller' => 'AuthTest', 'action' => 'login']); $event = new Event('Controller.startup', $this->Controller); - $this->Auth->startup($event); - $expected = Router::normalize('posts/view/1'); - $this->assertEquals($expected, $this->Auth->session->read('Auth.redirect')); + $response = $this->Auth->startup($event); + + $this->assertInstanceOf('Cake\Network\Response', $response); + $expected = Router::url(['controller' => 'AuthTest', 'action' => 'login', '?' => ['redirect' => '/posts/view/1']], true); + $redirectHeader = $response->header()['Location']; + $this->assertEquals($expected, $redirectHeader); + } - // QueryString parameters are preserved when setting Auth.redirect + /** + * @return void + */ + public function testLoginRedirectQueryString() + { + // QueryString parameters are preserved when redirecting with redirect key $this->Auth->session->delete('Auth'); $url = '/posts/view/29'; $this->Auth->request->addParams(Router::parse($url)); @@ -667,11 +654,18 @@ public function testLoginRedirect() $this->Auth->config('loginAction', ['controller' => 'AuthTest', 'action' => 'login']); $event = new Event('Controller.startup', $this->Controller); - $this->Auth->startup($event); - $expected = Router::normalize('posts/view/29?print=true&refer=menu'); - $this->assertEquals($expected, $this->Auth->session->read('Auth.redirect')); + $response = $this->Auth->startup($event); + + $expected = Router::url(['controller' => 'AuthTest', 'action' => 'login', '?' => ['redirect' => '/posts/view/29?print=true&refer=menu']], true); + $redirectHeader = $response->header()['Location']; + $this->assertEquals($expected, $redirectHeader); + } - // Different base urls. + /** + * @return void + */ + public function testLoginRedirectDifferentBaseUrl() + { $appConfig = Configure::read('App'); Configure::write('App', [ @@ -688,31 +682,16 @@ public function testLoginRedirect() $this->Auth->request->addParams(Router::parse($url)); $this->Auth->request->url = Router::normalize($url); - $this->Auth->config('loginAction', ['controller' => 'users', 'action' => 'login']); + $this->Auth->config('loginAction', ['controller' => 'Users', 'action' => 'login']); $event = new Event('Controller.startup', $this->Controller); - $this->Auth->startup($event); - $expected = Router::normalize('/posts/add'); - $this->assertEquals($expected, $this->Auth->session->read('Auth.redirect')); + $response = $this->Auth->startup($event); - $this->Auth->session->delete('Auth'); - Configure::write('App', $appConfig); - - // External Authed Action - $this->Auth->session->delete('Auth'); - $url = '/posts/view/1'; - $request = new Request($url); - $request->env('HTTP_REFERER', 'http://webmail.example.com/view/message'); - $request->query = []; - $this->Auth->request = $this->Controller->request = $request; - $this->Auth->request->addParams(Router::parse($url)); - $this->Auth->request->url = $this->Auth->request->here = Router::normalize($url); - $this->Auth->config('loginAction', ['controller' => 'AuthTest', 'action' => 'login']); - $event = new Event('Controller.startup', $this->Controller); - $this->Auth->startup($event); - $expected = Router::normalize('/posts/view/1'); - $this->assertEquals($expected, $this->Auth->session->read('Auth.redirect')); + $expected = Router::url(['controller' => 'Users', 'action' => 'login', '?' => ['redirect' => '/posts/add']], true); + $redirectHeader = $response->header()['Location']; + $this->assertEquals($expected, $redirectHeader); $this->Auth->session->delete('Auth'); + Configure::write('App', $appConfig); } /** @@ -984,16 +963,16 @@ public function testAdminRoute() $event = new Event('Controller.startup', $this->Controller); Router::reload(); Router::prefix('admin', function ($routes) { - $routes->fallbacks('InflectedRoute'); + $routes->fallbacks(InflectedRoute::class); }); Router::scope('/', function ($routes) { - $routes->fallbacks('InflectedRoute'); + $routes->fallbacks(InflectedRoute::class); }); $url = '/admin/auth_test/add'; $this->Auth->request->addParams(Router::parse($url)); - $this->Auth->request->query['url'] = ltrim($url, '/'); $this->Auth->request->base = ''; + $this->Auth->request->here = $url; Router::setRequestInfo($this->Auth->request); @@ -1003,8 +982,15 @@ public function testAdminRoute() 'action' => 'login' ]); - $this->Auth->startup($event); - $this->assertEquals('/admin/auth_test/login', $this->Controller->testUrl); + $response = $this->Auth->startup($event); + $redirectHeader = $response->header()['Location']; + $expected = Router::url([ + 'prefix' => 'admin', + 'controller' => 'auth_test', + 'action' => 'login', + '?' => ['redirect' => '/admin/auth_test/add'] + ], true); + $this->assertEquals($expected, $redirectHeader); } /** @@ -1068,10 +1054,10 @@ public function testLoginActionRedirect() $event = new Event('Controller.startup', $this->Controller); Router::reload(); Router::prefix('admin', function ($routes) { - $routes->fallbacks('InflectedRoute'); + $routes->fallbacks(InflectedRoute::class); }); Router::scope('/', function ($routes) { - $routes->fallbacks('InflectedRoute'); + $routes->fallbacks(InflectedRoute::class); }); $url = '/admin/auth_test/login'; @@ -1168,13 +1154,11 @@ public function testComponentSettings() public function testLogout() { $this->Auth->session->write('Auth.User.id', '1'); - $this->Auth->session->write('Auth.redirect', '/Users/login'); $this->Auth->config('logoutRedirect', '/'); $result = $this->Auth->logout(); $this->assertEquals('/', $result); $this->assertNull($this->Auth->session->read('Auth.AuthUsers')); - $this->assertNull($this->Auth->session->read('Auth.redirect')); } /** @@ -1332,66 +1316,56 @@ public function testRedirectSet() $value = ['controller' => 'users', 'action' => 'home']; $result = $this->Auth->redirectUrl($value); $this->assertEquals('/users/home', $result); - $this->assertEquals($value, $this->Auth->session->read('Auth.redirect')); - - $request = new Request(); - $request->base = '/base'; - Router::setRequestInfo($request); - - $result = $this->Auth->redirectUrl($value); - $this->assertEquals('/users/home', $result); } /** - * test redirect using Auth.redirect from the session. + * Tests redirect using redirect key from the query string. * * @return void */ - public function testRedirectSessionRead() + public function testRedirectQueryStringRead() { $this->Auth->config('loginAction', ['controller' => 'users', 'action' => 'login']); - $this->Auth->session->write('Auth.redirect', '/users/home'); + $this->Auth->request->query = ['redirect' => '/users/home']; $result = $this->Auth->redirectUrl(); $this->assertEquals('/users/home', $result); - $this->assertFalse($this->Auth->session->check('Auth.redirect')); } /** - * test redirectUrl with duplicate base. + * Tests redirectUrl with duplicate base. * * @return void */ - public function testRedirectSessionReadDuplicateBase() + public function testRedirectQueryStringReadDuplicateBase() { $this->Auth->request->webroot = '/waves/'; $this->Auth->request->base = '/waves'; - Router::setRequestInfo($this->Auth->request); + $this->Auth->request->query = ['redirect' => '/waves/add']; - $this->Auth->session->write('Auth.redirect', '/waves/add'); + Router::setRequestInfo($this->Auth->request); $result = $this->Auth->redirectUrl(); $this->assertEquals('/waves/add', $result); } /** - * test that redirect does not return loginAction if that is what's stored in Auth.redirect. + * test that redirect does not return loginAction if that is what's passed as redirect. * instead loginRedirect should be used. * * @return void */ - public function testRedirectSessionReadEqualToLoginAction() + public function testRedirectQueryStringReadEqualToLoginAction() { $this->Auth->config([ 'loginAction' => ['controller' => 'users', 'action' => 'login'], 'loginRedirect' => ['controller' => 'users', 'action' => 'home'] ]); - $this->Auth->session->write('Auth.redirect', ['controller' => 'users', 'action' => 'login']); + $this->Auth->request->query = ['redirect' => '/users/login']; $result = $this->Auth->redirectUrl(); $this->assertEquals('/users/home', $result); - $this->assertFalse($this->Auth->session->check('Auth.redirect')); } /** @@ -1422,7 +1396,6 @@ public function testRedirectUrlWithBaseSet() $result = $this->Auth->redirectUrl(); $this->assertEquals('/users/home', $result); - $this->assertFalse($this->Auth->session->check('Auth.redirect')); Configure::write('App', $App); Router::reload(); @@ -1506,7 +1479,7 @@ public function testStatelessFollowedByStatefulAuth() $this->assertInstanceOf('Cake\Network\Response', $this->Auth->startup($event)); - $this->assertEquals('/users/login', $this->Controller->testUrl); + $this->assertEquals('/users/login?redirect=%2Fauth_test', $this->Controller->testUrl); } /**