TokenBasedRememberMeServicesTest.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Security\Http\Tests\RememberMe;
  11. use PHPUnit\Framework\TestCase;
  12. use Symfony\Component\HttpFoundation\Request;
  13. use Symfony\Component\HttpFoundation\Response;
  14. use Symfony\Component\HttpFoundation\ResponseHeaderBag;
  15. use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
  16. use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface;
  17. use Symfony\Component\Security\Http\RememberMe\TokenBasedRememberMeServices;
  18. class TokenBasedRememberMeServicesTest extends TestCase
  19. {
  20. public function testAutoLoginReturnsNullWhenNoCookie()
  21. {
  22. $service = $this->getService(null, array('name' => 'foo'));
  23. $this->assertNull($service->autoLogin(new Request()));
  24. }
  25. public function testAutoLoginThrowsExceptionOnInvalidCookie()
  26. {
  27. $service = $this->getService(null, array('name' => 'foo', 'path' => null, 'domain' => null, 'always_remember_me' => false, 'remember_me_parameter' => 'foo'));
  28. $request = new Request();
  29. $request->request->set('foo', 'true');
  30. $request->cookies->set('foo', 'foo');
  31. $this->assertNull($service->autoLogin($request));
  32. $this->assertTrue($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME)->isCleared());
  33. }
  34. public function testAutoLoginThrowsExceptionOnNonExistentUser()
  35. {
  36. $userProvider = $this->getProvider();
  37. $service = $this->getService($userProvider, array('name' => 'foo', 'path' => null, 'domain' => null, 'always_remember_me' => true, 'lifetime' => 3600));
  38. $request = new Request();
  39. $request->cookies->set('foo', $this->getCookie('fooclass', 'foouser', time() + 3600, 'foopass'));
  40. $userProvider
  41. ->expects($this->once())
  42. ->method('loadUserByUsername')
  43. ->will($this->throwException(new UsernameNotFoundException('user not found')))
  44. ;
  45. $this->assertNull($service->autoLogin($request));
  46. $this->assertTrue($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME)->isCleared());
  47. }
  48. public function testAutoLoginDoesNotAcceptCookieWithInvalidHash()
  49. {
  50. $userProvider = $this->getProvider();
  51. $service = $this->getService($userProvider, array('name' => 'foo', 'path' => null, 'domain' => null, 'always_remember_me' => true, 'lifetime' => 3600));
  52. $request = new Request();
  53. $request->cookies->set('foo', base64_encode('class:'.base64_encode('foouser').':123456789:fooHash'));
  54. $user = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserInterface')->getMock();
  55. $user
  56. ->expects($this->once())
  57. ->method('getPassword')
  58. ->will($this->returnValue('foopass'))
  59. ;
  60. $userProvider
  61. ->expects($this->once())
  62. ->method('loadUserByUsername')
  63. ->with($this->equalTo('foouser'))
  64. ->will($this->returnValue($user))
  65. ;
  66. $this->assertNull($service->autoLogin($request));
  67. $this->assertTrue($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME)->isCleared());
  68. }
  69. public function testAutoLoginDoesNotAcceptAnExpiredCookie()
  70. {
  71. $userProvider = $this->getProvider();
  72. $service = $this->getService($userProvider, array('name' => 'foo', 'path' => null, 'domain' => null, 'always_remember_me' => true, 'lifetime' => 3600));
  73. $request = new Request();
  74. $request->cookies->set('foo', $this->getCookie('fooclass', 'foouser', time() - 1, 'foopass'));
  75. $user = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserInterface')->getMock();
  76. $user
  77. ->expects($this->once())
  78. ->method('getPassword')
  79. ->will($this->returnValue('foopass'))
  80. ;
  81. $userProvider
  82. ->expects($this->once())
  83. ->method('loadUserByUsername')
  84. ->with($this->equalTo('foouser'))
  85. ->will($this->returnValue($user))
  86. ;
  87. $this->assertNull($service->autoLogin($request));
  88. $this->assertTrue($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME)->isCleared());
  89. }
  90. /**
  91. * @dataProvider provideUsernamesForAutoLogin
  92. *
  93. * @param string $username
  94. */
  95. public function testAutoLogin($username)
  96. {
  97. $user = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserInterface')->getMock();
  98. $user
  99. ->expects($this->once())
  100. ->method('getRoles')
  101. ->will($this->returnValue(array('ROLE_FOO')))
  102. ;
  103. $user
  104. ->expects($this->once())
  105. ->method('getPassword')
  106. ->will($this->returnValue('foopass'))
  107. ;
  108. $userProvider = $this->getProvider();
  109. $userProvider
  110. ->expects($this->once())
  111. ->method('loadUserByUsername')
  112. ->with($this->equalTo($username))
  113. ->will($this->returnValue($user))
  114. ;
  115. $service = $this->getService($userProvider, array('name' => 'foo', 'always_remember_me' => true, 'lifetime' => 3600));
  116. $request = new Request();
  117. $request->cookies->set('foo', $this->getCookie('fooclass', $username, time() + 3600, 'foopass'));
  118. $returnedToken = $service->autoLogin($request);
  119. $this->assertInstanceOf('Symfony\Component\Security\Core\Authentication\Token\RememberMeToken', $returnedToken);
  120. $this->assertSame($user, $returnedToken->getUser());
  121. $this->assertEquals('foosecret', $returnedToken->getSecret());
  122. }
  123. public function provideUsernamesForAutoLogin()
  124. {
  125. return array(
  126. array('foouser', 'Simple username'),
  127. array('foo'.TokenBasedRememberMeServices::COOKIE_DELIMITER.'user', 'Username might contain the delimiter'),
  128. );
  129. }
  130. public function testLogout()
  131. {
  132. $service = $this->getService(null, array('name' => 'foo', 'path' => null, 'domain' => null, 'secure' => true, 'httponly' => false));
  133. $request = new Request();
  134. $response = new Response();
  135. $token = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock();
  136. $service->logout($request, $response, $token);
  137. $cookie = $request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME);
  138. $this->assertTrue($cookie->isCleared());
  139. $this->assertEquals('/', $cookie->getPath());
  140. $this->assertNull($cookie->getDomain());
  141. $this->assertTrue($cookie->isSecure());
  142. $this->assertFalse($cookie->isHttpOnly());
  143. }
  144. public function testLoginFail()
  145. {
  146. $service = $this->getService(null, array('name' => 'foo', 'path' => '/foo', 'domain' => 'foodomain.foo'));
  147. $request = new Request();
  148. $service->loginFail($request);
  149. $cookie = $request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME);
  150. $this->assertTrue($cookie->isCleared());
  151. $this->assertEquals('/foo', $cookie->getPath());
  152. $this->assertEquals('foodomain.foo', $cookie->getDomain());
  153. }
  154. public function testLoginSuccessIgnoresTokensWhichDoNotContainAnUserInterfaceImplementation()
  155. {
  156. $service = $this->getService(null, array('name' => 'foo', 'always_remember_me' => true, 'path' => null, 'domain' => null));
  157. $request = new Request();
  158. $response = new Response();
  159. $token = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock();
  160. $token
  161. ->expects($this->once())
  162. ->method('getUser')
  163. ->will($this->returnValue('foo'))
  164. ;
  165. $cookies = $response->headers->getCookies();
  166. $this->assertCount(0, $cookies);
  167. $service->loginSuccess($request, $response, $token);
  168. $cookies = $response->headers->getCookies();
  169. $this->assertCount(0, $cookies);
  170. }
  171. public function testLoginSuccess()
  172. {
  173. $service = $this->getService(null, array('name' => 'foo', 'domain' => 'myfoodomain.foo', 'path' => '/foo/path', 'secure' => true, 'httponly' => true, 'lifetime' => 3600, 'always_remember_me' => true));
  174. $request = new Request();
  175. $response = new Response();
  176. $token = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock();
  177. $user = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserInterface')->getMock();
  178. $user
  179. ->expects($this->once())
  180. ->method('getPassword')
  181. ->will($this->returnValue('foopass'))
  182. ;
  183. $user
  184. ->expects($this->once())
  185. ->method('getUsername')
  186. ->will($this->returnValue('foouser'))
  187. ;
  188. $token
  189. ->expects($this->atLeastOnce())
  190. ->method('getUser')
  191. ->will($this->returnValue($user))
  192. ;
  193. $cookies = $response->headers->getCookies();
  194. $this->assertCount(0, $cookies);
  195. $service->loginSuccess($request, $response, $token);
  196. $cookies = $response->headers->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
  197. $cookie = $cookies['myfoodomain.foo']['/foo/path']['foo'];
  198. $this->assertFalse($cookie->isCleared());
  199. $this->assertTrue($cookie->isSecure());
  200. $this->assertTrue($cookie->isHttpOnly());
  201. $this->assertTrue($cookie->getExpiresTime() > time() + 3590 && $cookie->getExpiresTime() < time() + 3610);
  202. $this->assertEquals('myfoodomain.foo', $cookie->getDomain());
  203. $this->assertEquals('/foo/path', $cookie->getPath());
  204. }
  205. protected function getCookie($class, $username, $expires, $password)
  206. {
  207. $service = $this->getService();
  208. $r = new \ReflectionMethod($service, 'generateCookieValue');
  209. $r->setAccessible(true);
  210. return $r->invoke($service, $class, $username, $expires, $password);
  211. }
  212. protected function encodeCookie(array $parts)
  213. {
  214. $service = $this->getService();
  215. $r = new \ReflectionMethod($service, 'encodeCookie');
  216. $r->setAccessible(true);
  217. return $r->invoke($service, $parts);
  218. }
  219. protected function getService($userProvider = null, $options = array(), $logger = null)
  220. {
  221. if (null === $userProvider) {
  222. $userProvider = $this->getProvider();
  223. }
  224. $service = new TokenBasedRememberMeServices(array($userProvider), 'foosecret', 'fookey', $options, $logger);
  225. return $service;
  226. }
  227. protected function getProvider()
  228. {
  229. $provider = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserProviderInterface')->getMock();
  230. $provider
  231. ->expects($this->any())
  232. ->method('supportsClass')
  233. ->will($this->returnValue(true))
  234. ;
  235. return $provider;
  236. }
  237. }