PersistentTokenBasedRememberMeServicesTest.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. <?php
  2. namespace Symfony\Tests\Component\Security\Http\RememberMe;
  3. use Symfony\Component\Security\Core\Authentication\Token\RememberMeToken;
  4. use Symfony\Component\HttpFoundation\HeaderBag;
  5. use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
  6. use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentToken;
  7. use Symfony\Component\HttpFoundation\Request;
  8. use Symfony\Component\HttpFoundation\Response;
  9. use Symfony\Component\Security\Http\RememberMe\PersistentTokenBasedRememberMeServices;
  10. use Symfony\Component\Security\Core\Exception\TokenNotFoundException;
  11. use Symfony\Component\Security\Core\Exception\CookieTheftException;
  12. class PersistentTokenBasedRememberMeServicesTest extends \PHPUnit_Framework_TestCase
  13. {
  14. public function testAutoLoginReturnsNullWhenNoCookie()
  15. {
  16. $service = $this->getService(null, array('name' => 'foo'));
  17. $this->assertNull($service->autoLogin(new Request()));
  18. }
  19. /**
  20. * @expectedException Symfony\Component\Security\Core\Exception\AuthenticationException
  21. * @expectedMessage The cookie is invalid.
  22. */
  23. public function testAutoLoginThrowsExceptionOnInvalidCookie()
  24. {
  25. $service = $this->getService(null, array('name' => 'foo', 'always_remember_me' => false, 'remember_me_parameter' => 'foo'));
  26. $request = new Request;
  27. $request->request->set('foo', 'true');
  28. $request->cookies->set('foo', 'foo');
  29. $service->autoLogin($request);
  30. }
  31. /**
  32. * @expectedException Symfony\Component\Security\Core\Exception\TokenNotFoundException
  33. */
  34. public function testAutoLoginThrowsExceptionOnNonExistentToken()
  35. {
  36. $service = $this->getService(null, array('name' => 'foo', 'always_remember_me' => false, 'remember_me_parameter' => 'foo'));
  37. $request = new Request;
  38. $request->request->set('foo', 'true');
  39. $request->cookies->set('foo', $this->encodeCookie(array(
  40. $series = 'fooseries',
  41. $tokenValue = 'foovalue',
  42. )));
  43. $tokenProvider = $this->getMock('Symfony\Component\Security\Core\Authentication\RememberMe\TokenProviderInterface');
  44. $tokenProvider
  45. ->expects($this->once())
  46. ->method('loadTokenBySeries')
  47. ->will($this->throwException(new TokenNotFoundException('Token not found.')))
  48. ;
  49. $service->setTokenProvider($tokenProvider);
  50. $service->autoLogin($request);
  51. }
  52. /**
  53. * @expectedException Symfony\Component\Security\Core\Exception\UsernameNotFoundException
  54. */
  55. public function testAutoLoginThrowsExceptionOnNonExistentUser()
  56. {
  57. $userProvider = $this->getProvider();
  58. $service = $this->getService($userProvider, array('name' => 'foo', 'always_remember_me' => true, 'lifetime' => 3600));
  59. $request = new Request;
  60. $request->cookies->set('foo', $this->encodeCookie(array('fooseries', 'foovalue')));
  61. $tokenProvider = $this->getMock('Symfony\Component\Security\Core\Authentication\RememberMe\TokenProviderInterface');
  62. $tokenProvider
  63. ->expects($this->once())
  64. ->method('loadTokenBySeries')
  65. ->will($this->returnValue(new PersistentToken('fooclass', 'fooname', 'fooseries', 'foovalue', new \DateTime())))
  66. ;
  67. $service->setTokenProvider($tokenProvider);
  68. $userProvider
  69. ->expects($this->once())
  70. ->method('loadUserByUsername')
  71. ->will($this->throwException(new UsernameNotFoundException('user not found')))
  72. ;
  73. $service->autoLogin($request);
  74. }
  75. public function testAutoLoginThrowsExceptionOnStolenCookieAndRemovesItFromThePersistentBackend()
  76. {
  77. $userProvider = $this->getProvider();
  78. $service = $this->getService($userProvider, array('name' => 'foo', 'always_remember_me' => true));
  79. $request = new Request;
  80. $request->cookies->set('foo', $this->encodeCookie(array('fooseries', 'foovalue')));
  81. $tokenProvider = $this->getMock('Symfony\Component\Security\Core\Authentication\RememberMe\TokenProviderInterface');
  82. $service->setTokenProvider($tokenProvider);
  83. $tokenProvider
  84. ->expects($this->once())
  85. ->method('loadTokenBySeries')
  86. ->will($this->returnValue(new PersistentToken('fooclass', 'foouser', 'fooseries', 'anotherFooValue', new \DateTime())))
  87. ;
  88. $tokenProvider
  89. ->expects($this->once())
  90. ->method('deleteTokenBySeries')
  91. ->with($this->equalTo('fooseries'))
  92. ->will($this->returnValue(null))
  93. ;
  94. try {
  95. $service->autoLogin($request);
  96. } catch (CookieTheftException $theft) {
  97. return;
  98. }
  99. $this->fail('Expected CookieTheftException was not thrown.');
  100. }
  101. /**
  102. * @expectedException Symfony\Component\Security\Core\Exception\AuthenticationException
  103. * @expectedMessage The cookie has expired.
  104. */
  105. public function testAutoLoginDoesNotAcceptAnExpiredCookie()
  106. {
  107. $service = $this->getService(null, array('name' => 'foo', 'always_remember_me' => true, 'lifetime' => 3600));
  108. $request = new Request;
  109. $request->cookies->set('foo', $this->encodeCookie(array('fooseries', 'foovalue')));
  110. $tokenProvider = $this->getMock('Symfony\Component\Security\Core\Authentication\RememberMe\TokenProviderInterface');
  111. $tokenProvider
  112. ->expects($this->once())
  113. ->method('loadTokenBySeries')
  114. ->with($this->equalTo('fooseries'))
  115. ->will($this->returnValue(new PersistentToken('fooclass', 'username', 'fooseries', 'newFooValue', new \DateTime('yesterday'))))
  116. ;
  117. $service->setTokenProvider($tokenProvider);
  118. $service->autoLogin($request);
  119. }
  120. public function testAutoLogin()
  121. {
  122. $user = $this->getMock('Symfony\Component\Security\Core\User\AccountInterface');
  123. $user
  124. ->expects($this->once())
  125. ->method('getRoles')
  126. ->will($this->returnValue(array('ROLE_FOO')))
  127. ;
  128. $userProvider = $this->getProvider();
  129. $userProvider
  130. ->expects($this->once())
  131. ->method('loadUserByUsername')
  132. ->with($this->equalTo('foouser'))
  133. ->will($this->returnValue($user))
  134. ;
  135. $service = $this->getService($userProvider, array('name' => 'foo', 'always_remember_me' => true, 'lifetime' => 3600));
  136. $request = new Request;
  137. $request->cookies->set('foo', $this->encodeCookie(array('fooseries', 'foovalue')));
  138. $tokenProvider = $this->getMock('Symfony\Component\Security\Core\Authentication\RememberMe\TokenProviderInterface');
  139. $tokenProvider
  140. ->expects($this->once())
  141. ->method('loadTokenBySeries')
  142. ->with($this->equalTo('fooseries'))
  143. ->will($this->returnValue(new PersistentToken('fooclass', 'foouser', 'fooseries', 'foovalue', new \DateTime())))
  144. ;
  145. $service->setTokenProvider($tokenProvider);
  146. $returnedToken = $service->autoLogin($request);
  147. $this->assertInstanceOf('Symfony\Component\Security\Core\Authentication\Token\RememberMeToken', $returnedToken);
  148. $this->assertInstanceOf('Symfony\Component\Security\Core\Authentication\RememberMe\PersistentTokenInterface', $returnedToken->getPersistentToken());
  149. $this->assertSame($user, $returnedToken->getUser());
  150. $this->assertEquals('fookey', $returnedToken->getKey());
  151. }
  152. public function testLogout()
  153. {
  154. $service = $this->getService(null, array('name' => 'foo', 'path' => '/foo', 'domain' => 'foodomain.foo'));
  155. $request = new Request();
  156. $request->cookies->set('foo', $this->encodeCookie(array('fooseries', 'foovalue')));
  157. $response = new Response();
  158. $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
  159. $tokenProvider = $this->getMock('Symfony\Component\Security\Core\Authentication\RememberMe\TokenProviderInterface');
  160. $tokenProvider
  161. ->expects($this->once())
  162. ->method('deleteTokenBySeries')
  163. ->with($this->equalTo('fooseries'))
  164. ->will($this->returnValue(null))
  165. ;
  166. $service->setTokenProvider($tokenProvider);
  167. $this->assertFalse($response->headers->hasCookie('foo'));
  168. $service->logout($request, $response, $token);
  169. $cookie = $response->headers->getCookie('foo');
  170. $this->assertTrue($cookie->isCleared());
  171. $this->assertEquals('/foo', $cookie->getPath());
  172. $this->assertEquals('foodomain.foo', $cookie->getDomain());
  173. }
  174. public function testLogoutSimplyIgnoresNonSetRequestCookie()
  175. {
  176. $service = $this->getService(null, array('name' => 'foo', 'path' => null, 'domain' => null));
  177. $request = new Request;
  178. $response = new Response;
  179. $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
  180. $tokenProvider = $this->getMock('Symfony\Component\Security\Core\Authentication\RememberMe\TokenProviderInterface');
  181. $tokenProvider
  182. ->expects($this->never())
  183. ->method('deleteTokenBySeries')
  184. ;
  185. $service->setTokenProvider($tokenProvider);
  186. $this->assertFalse($response->headers->hasCookie('foo'));
  187. $service->logout($request, $response, $token);
  188. $cookie = $response->headers->getCookie('foo');
  189. $this->assertTrue($cookie->isCleared());
  190. $this->assertNull($cookie->getPath());
  191. $this->assertNull($cookie->getDomain());
  192. }
  193. public function testLogoutSimplyIgnoresInvalidCookie()
  194. {
  195. $service = $this->getService(null, array('name' => 'foo', 'path' => null, 'domain' => null));
  196. $request = new Request;
  197. $request->cookies->set('foo', 'somefoovalue');
  198. $response = new Response;
  199. $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
  200. $tokenProvider = $this->getMock('Symfony\Component\Security\Core\Authentication\RememberMe\TokenProviderInterface');
  201. $tokenProvider
  202. ->expects($this->never())
  203. ->method('deleteTokenBySeries')
  204. ;
  205. $service->setTokenProvider($tokenProvider);
  206. $this->assertFalse($response->headers->hasCookie('foo'));
  207. $service->logout($request, $response, $token);
  208. $this->assertTrue($response->headers->getCookie('foo')->isCleared());
  209. }
  210. public function testLoginFail()
  211. {
  212. $service = $this->getService(null, array('name' => 'foo', 'path' => null, 'domain' => null));
  213. $request = new Request();
  214. $response = new Response();
  215. $this->assertFalse($response->headers->hasCookie('foo'));
  216. $service->loginFail($request, $response);
  217. $this->assertTrue($response->headers->getCookie('foo')->isCleared());
  218. }
  219. public function testLoginSuccessRenewsRememberMeTokenWhenUsedForLogin()
  220. {
  221. $service = $this->getService(null, array('name' => 'foo', 'domain' => 'myfoodomain.foo', 'path' => '/foo/path', 'secure' => true, 'httponly' => true, 'lifetime' => 3600));
  222. $request = new Request;
  223. $response = new Response;
  224. $user = $this->getMock('Symfony\Component\Security\Core\User\AccountInterface');
  225. $user
  226. ->expects($this->once())
  227. ->method('getRoles')
  228. ->will($this->returnValue(array('ROLE_FOO')))
  229. ;
  230. $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\RememberMeToken', array(), array($user, 'fookey', 'fookey'));
  231. $token
  232. ->expects($this->once())
  233. ->method('getPersistentToken')
  234. ->will($this->returnValue(new PersistentToken('fooclass', 'foouser', 'fooseries', 'foovalue', new \DateTime())))
  235. ;
  236. $tokenProvider = $this->getMock('Symfony\Component\Security\Core\Authentication\RememberMe\TokenProviderInterface');
  237. $tokenProvider
  238. ->expects($this->once())
  239. ->method('updateToken')
  240. ->with($this->equalTo('fooseries'))
  241. ->will($this->returnValue(null))
  242. ;
  243. $service->setTokenProvider($tokenProvider);
  244. $this->assertFalse($response->headers->hasCookie('foo'));
  245. $service->loginSuccess($request, $response, $token);
  246. $cookie = $response->headers->getCookie('foo');
  247. $this->assertFalse($cookie->isCleared());
  248. $this->assertTrue($cookie->isSecure());
  249. $this->assertTrue($cookie->isHttpOnly());
  250. $this->assertTrue($cookie->getExpire() > time() + 3590 && $cookie->getExpire() < time() + 3610);
  251. $this->assertEquals('myfoodomain.foo', $cookie->getDomain());
  252. $this->assertEquals('/foo/path', $cookie->getPath());
  253. }
  254. /**
  255. * @expectedException RuntimeException
  256. * @expectedMessage RememberMeToken must contain a PersistentTokenInterface implementation when used as login.
  257. */
  258. public function testLoginSuccessThrowsExceptionWhenRememberMeTokenDoesNotContainPersistentTokenImplementation()
  259. {
  260. $service = $this->getService(null, array('always_remember_me' => true, 'name' => 'foo'));
  261. $request = new Request;
  262. $response = new Response;
  263. $user = $this->getMock('Symfony\Component\Security\Core\User\AccountInterface');
  264. $user
  265. ->expects($this->once())
  266. ->method('getRoles')
  267. ->will($this->returnValue(array('ROLE_FOO')))
  268. ;
  269. $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\RememberMeToken', array(), array($user, 'fookey', 'fookey'));
  270. $token
  271. ->expects($this->once())
  272. ->method('getPersistentToken')
  273. ->will($this->returnValue(null))
  274. ;
  275. $service->loginSuccess($request, $response, $token);
  276. }
  277. public function testLoginSuccessSetsCookieWhenLoggedInWithNonRememberMeTokenInterfaceImplementation()
  278. {
  279. $service = $this->getService(null, array('name' => 'foo', 'domain' => 'myfoodomain.foo', 'path' => '/foo/path', 'secure' => true, 'httponly' => true, 'lifetime' => 3600, 'always_remember_me' => true));
  280. $request = new Request;
  281. $response = new Response;
  282. $account = $this->getMock('Symfony\Component\Security\Core\User\AccountInterface');
  283. $account
  284. ->expects($this->once())
  285. ->method('getUsername')
  286. ->will($this->returnValue('foo'))
  287. ;
  288. $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
  289. $token
  290. ->expects($this->any())
  291. ->method('getUser')
  292. ->will($this->returnValue($account))
  293. ;
  294. $tokenProvider = $this->getMock('Symfony\Component\Security\Core\Authentication\RememberMe\TokenProviderInterface');
  295. $tokenProvider
  296. ->expects($this->once())
  297. ->method('createNewToken')
  298. ;
  299. $service->setTokenProvider($tokenProvider);
  300. $this->assertFalse($response->headers->hasCookie('foo'));
  301. $service->loginSuccess($request, $response, $token);
  302. $cookie = $response->headers->getCookie('foo');
  303. $this->assertFalse($cookie->isCleared());
  304. $this->assertTrue($cookie->isSecure());
  305. $this->assertTrue($cookie->isHttpOnly());
  306. $this->assertTrue($cookie->getExpire() > time() + 3590 && $cookie->getExpire() < time() + 3610);
  307. $this->assertEquals('myfoodomain.foo', $cookie->getDomain());
  308. $this->assertEquals('/foo/path', $cookie->getPath());
  309. }
  310. protected function encodeCookie(array $parts)
  311. {
  312. $service = $this->getService();
  313. $r = new \ReflectionMethod($service, 'encodeCookie');
  314. $r->setAccessible(true);
  315. return $r->invoke($service, $parts);
  316. }
  317. protected function getService($userProvider = null, $options = array(), $logger = null)
  318. {
  319. if (null === $userProvider) {
  320. $userProvider = $this->getProvider();
  321. }
  322. return new PersistentTokenBasedRememberMeServices(array($userProvider), 'fookey', 'fookey', $options, $logger);
  323. }
  324. protected function getProvider()
  325. {
  326. $provider = $this->getMock('Symfony\Component\Security\Core\User\UserProviderInterface');
  327. $provider
  328. ->expects($this->any())
  329. ->method('supportsClass')
  330. ->will($this->returnValue(true))
  331. ;
  332. return $provider;
  333. }
  334. }