|
1 <?php |
|
2 |
|
3 namespace Symfony\Component\Security\Http\RememberMe; |
|
4 |
|
5 use Symfony\Component\Security\Core\Authentication\RememberMe\TokenProviderInterface; |
|
6 use Symfony\Component\HttpFoundation\Cookie; |
|
7 use Symfony\Component\HttpFoundation\Response; |
|
8 use Symfony\Component\HttpFoundation\Request; |
|
9 use Symfony\Component\Security\Core\Exception\AuthenticationException; |
|
10 use Symfony\Component\Security\Core\Exception\CookieTheftException; |
|
11 use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentToken; |
|
12 use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; |
|
13 use Symfony\Component\Security\Core\Authentication\Token\RememberMeToken; |
|
14 |
|
15 /* |
|
16 * This file is part of the Symfony package. |
|
17 * |
|
18 * (c) Fabien Potencier <fabien@symfony.com> |
|
19 * |
|
20 * For the full copyright and license information, please view the LICENSE |
|
21 * file that was distributed with this source code. |
|
22 */ |
|
23 |
|
24 /** |
|
25 * Concrete implementation of the RememberMeServicesInterface which needs |
|
26 * an implementation of TokenProviderInterface for providing remember-me |
|
27 * capabilities. |
|
28 * |
|
29 * @author Johannes M. Schmitt <schmittjoh@gmail.com> |
|
30 */ |
|
31 class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices |
|
32 { |
|
33 private $tokenProvider; |
|
34 |
|
35 /** |
|
36 * Sets the token provider |
|
37 * |
|
38 * @param TokenProviderInterface $tokenProvider |
|
39 * @return void |
|
40 */ |
|
41 public function setTokenProvider(TokenProviderInterface $tokenProvider) |
|
42 { |
|
43 $this->tokenProvider = $tokenProvider; |
|
44 } |
|
45 |
|
46 /** |
|
47 * {@inheritDoc} |
|
48 */ |
|
49 public function logout(Request $request, Response $response, TokenInterface $token) |
|
50 { |
|
51 parent::logout($request, $response, $token); |
|
52 |
|
53 if (null !== ($cookie = $request->cookies->get($this->options['name'])) |
|
54 && count($parts = $this->decodeCookie($cookie)) === 2 |
|
55 ) { |
|
56 list($series, $tokenValue) = $parts; |
|
57 $this->tokenProvider->deleteTokenBySeries($series); |
|
58 } |
|
59 } |
|
60 |
|
61 /** |
|
62 * {@inheritDoc} |
|
63 */ |
|
64 protected function processAutoLoginCookie(array $cookieParts, Request $request) |
|
65 { |
|
66 if (count($cookieParts) !== 2) { |
|
67 throw new AuthenticationException('The cookie is invalid.'); |
|
68 } |
|
69 |
|
70 list($series, $tokenValue) = $cookieParts; |
|
71 $persistentToken = $this->tokenProvider->loadTokenBySeries($series); |
|
72 |
|
73 if ($persistentToken->getTokenValue() !== $tokenValue) { |
|
74 $this->tokenProvider->deleteTokenBySeries($series); |
|
75 |
|
76 throw new CookieTheftException('This token was already used. The account is possibly compromised.'); |
|
77 } |
|
78 |
|
79 if ($persistentToken->getLastUsed()->getTimestamp() + $this->options['lifetime'] < time()) { |
|
80 throw new AuthenticationException('The cookie has expired.'); |
|
81 } |
|
82 |
|
83 $series = $persistentToken->getSeries(); |
|
84 $tokenValue = $this->generateRandomValue(); |
|
85 $this->tokenProvider->updateToken($series, $tokenValue, new \DateTime()); |
|
86 $request->attributes->set(self::COOKIE_ATTR_NAME, |
|
87 new Cookie( |
|
88 $this->options['name'], |
|
89 $this->encodeCookie(array($series, $tokenValue)), |
|
90 time() + $this->options['lifetime'], |
|
91 $this->options['path'], |
|
92 $this->options['domain'], |
|
93 $this->options['secure'], |
|
94 $this->options['httponly'] |
|
95 ) |
|
96 ); |
|
97 |
|
98 return $this->getUserProvider($persistentToken->getClass())->loadUserByUsername($persistentToken->getUsername()); |
|
99 } |
|
100 |
|
101 /** |
|
102 * {@inheritDoc} |
|
103 */ |
|
104 protected function onLoginSuccess(Request $request, Response $response, TokenInterface $token) |
|
105 { |
|
106 $series = $this->generateRandomValue(); |
|
107 $tokenValue = $this->generateRandomValue(); |
|
108 |
|
109 $this->tokenProvider->createNewToken( |
|
110 new PersistentToken( |
|
111 get_class($user = $token->getUser()), |
|
112 $user->getUsername(), |
|
113 $series, |
|
114 $tokenValue, |
|
115 new \DateTime() |
|
116 ) |
|
117 ); |
|
118 |
|
119 $response->headers->setCookie( |
|
120 new Cookie( |
|
121 $this->options['name'], |
|
122 $this->encodeCookie(array($series, $tokenValue)), |
|
123 time() + $this->options['lifetime'], |
|
124 $this->options['path'], |
|
125 $this->options['domain'], |
|
126 $this->options['secure'], |
|
127 $this->options['httponly'] |
|
128 ) |
|
129 ); |
|
130 } |
|
131 |
|
132 /** |
|
133 * Generates a cryptographically strong random value |
|
134 * |
|
135 * @return string |
|
136 */ |
|
137 protected function generateRandomValue() |
|
138 { |
|
139 if (function_exists('openssl_random_pseudo_bytes')) { |
|
140 $bytes = openssl_random_pseudo_bytes(64, $strong); |
|
141 |
|
142 if (true === $strong && false !== $bytes) { |
|
143 return base64_encode($bytes); |
|
144 } |
|
145 } |
|
146 |
|
147 if (null !== $this->logger) { |
|
148 $this->logger->warn('Could not produce a cryptographically strong random value. Please install/update the OpenSSL extension.'); |
|
149 } |
|
150 |
|
151 return base64_encode(hash('sha512', uniqid(mt_rand(), true), true)); |
|
152 } |
|
153 } |