PageRenderTime 54ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/RestAPI/vendor/symfony/security/Http/RememberMe/AbstractRememberMeServices.php

https://gitlab.com/martinstti/silex-microframework-rest
PHP | 334 lines | 168 code | 44 blank | 122 comment | 24 complexity | dd03f35d93a10180c34ee11484b8411b MD5 | raw file
  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\RememberMe;
  11. use Symfony\Component\Security\Core\Exception\AuthenticationException;
  12. use Symfony\Component\Security\Core\User\UserInterface;
  13. use Symfony\Component\Security\Core\Authentication\Token\RememberMeToken;
  14. use Symfony\Component\Security\Http\Logout\LogoutHandlerInterface;
  15. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  16. use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
  17. use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
  18. use Symfony\Component\Security\Core\Exception\CookieTheftException;
  19. use Symfony\Component\HttpFoundation\Response;
  20. use Symfony\Component\HttpFoundation\Request;
  21. use Symfony\Component\HttpFoundation\Cookie;
  22. use Psr\Log\LoggerInterface;
  23. use Symfony\Component\Security\Http\ParameterBagUtils;
  24. /**
  25. * Base class implementing the RememberMeServicesInterface.
  26. *
  27. * @author Johannes M. Schmitt <schmittjoh@gmail.com>
  28. */
  29. abstract class AbstractRememberMeServices implements RememberMeServicesInterface, LogoutHandlerInterface
  30. {
  31. const COOKIE_DELIMITER = ':';
  32. protected $logger;
  33. protected $options = array(
  34. 'secure' => false,
  35. 'httponly' => true,
  36. );
  37. private $providerKey;
  38. private $secret;
  39. private $userProviders;
  40. /**
  41. * Constructor.
  42. *
  43. * @param array $userProviders
  44. * @param string $secret
  45. * @param string $providerKey
  46. * @param array $options
  47. * @param LoggerInterface $logger
  48. *
  49. * @throws \InvalidArgumentException
  50. */
  51. public function __construct(array $userProviders, $secret, $providerKey, array $options = array(), LoggerInterface $logger = null)
  52. {
  53. if (empty($secret)) {
  54. throw new \InvalidArgumentException('$secret must not be empty.');
  55. }
  56. if (empty($providerKey)) {
  57. throw new \InvalidArgumentException('$providerKey must not be empty.');
  58. }
  59. if (0 === count($userProviders)) {
  60. throw new \InvalidArgumentException('You must provide at least one user provider.');
  61. }
  62. $this->userProviders = $userProviders;
  63. $this->secret = $secret;
  64. $this->providerKey = $providerKey;
  65. $this->options = array_merge($this->options, $options);
  66. $this->logger = $logger;
  67. }
  68. /**
  69. * Returns the parameter that is used for checking whether remember-me
  70. * services have been requested.
  71. *
  72. * @return string
  73. */
  74. public function getRememberMeParameter()
  75. {
  76. return $this->options['remember_me_parameter'];
  77. }
  78. /**
  79. * @deprecated Since version 2.8, to be removed in 3.0. Use getSecret() instead.
  80. */
  81. public function getKey()
  82. {
  83. @trigger_error(__method__.'() is deprecated since version 2.8 and will be removed in 3.0. Use getSecret() instead.', E_USER_DEPRECATED);
  84. return $this->getSecret();
  85. }
  86. /**
  87. * @return string
  88. */
  89. public function getSecret()
  90. {
  91. return $this->secret;
  92. }
  93. /**
  94. * Implementation of RememberMeServicesInterface. Detects whether a remember-me
  95. * cookie was set, decodes it, and hands it to subclasses for further processing.
  96. *
  97. * @param Request $request
  98. *
  99. * @return TokenInterface|null
  100. *
  101. * @throws CookieTheftException
  102. * @throws \RuntimeException
  103. */
  104. final public function autoLogin(Request $request)
  105. {
  106. if (null === $cookie = $request->cookies->get($this->options['name'])) {
  107. return;
  108. }
  109. if (null !== $this->logger) {
  110. $this->logger->debug('Remember-me cookie detected.');
  111. }
  112. $cookieParts = $this->decodeCookie($cookie);
  113. try {
  114. $user = $this->processAutoLoginCookie($cookieParts, $request);
  115. if (!$user instanceof UserInterface) {
  116. throw new \RuntimeException('processAutoLoginCookie() must return a UserInterface implementation.');
  117. }
  118. if (null !== $this->logger) {
  119. $this->logger->info('Remember-me cookie accepted.');
  120. }
  121. return new RememberMeToken($user, $this->providerKey, $this->secret);
  122. } catch (CookieTheftException $e) {
  123. $this->cancelCookie($request);
  124. throw $e;
  125. } catch (UsernameNotFoundException $e) {
  126. if (null !== $this->logger) {
  127. $this->logger->info('User for remember-me cookie not found.');
  128. }
  129. } catch (UnsupportedUserException $e) {
  130. if (null !== $this->logger) {
  131. $this->logger->warning('User class for remember-me cookie not supported.');
  132. }
  133. } catch (AuthenticationException $e) {
  134. if (null !== $this->logger) {
  135. $this->logger->debug('Remember-Me authentication failed.', array('exception' => $e));
  136. }
  137. }
  138. $this->cancelCookie($request);
  139. }
  140. /**
  141. * Implementation for LogoutHandlerInterface. Deletes the cookie.
  142. *
  143. * @param Request $request
  144. * @param Response $response
  145. * @param TokenInterface $token
  146. */
  147. public function logout(Request $request, Response $response, TokenInterface $token)
  148. {
  149. $this->cancelCookie($request);
  150. }
  151. /**
  152. * Implementation for RememberMeServicesInterface. Deletes the cookie when
  153. * an attempted authentication fails.
  154. *
  155. * @param Request $request
  156. */
  157. final public function loginFail(Request $request)
  158. {
  159. $this->cancelCookie($request);
  160. $this->onLoginFail($request);
  161. }
  162. /**
  163. * Implementation for RememberMeServicesInterface. This is called when an
  164. * authentication is successful.
  165. *
  166. * @param Request $request
  167. * @param Response $response
  168. * @param TokenInterface $token The token that resulted in a successful authentication
  169. */
  170. final public function loginSuccess(Request $request, Response $response, TokenInterface $token)
  171. {
  172. // Make sure any old remember-me cookies are cancelled
  173. $this->cancelCookie($request);
  174. if (!$token->getUser() instanceof UserInterface) {
  175. if (null !== $this->logger) {
  176. $this->logger->debug('Remember-me ignores token since it does not contain a UserInterface implementation.');
  177. }
  178. return;
  179. }
  180. if (!$this->isRememberMeRequested($request)) {
  181. if (null !== $this->logger) {
  182. $this->logger->debug('Remember-me was not requested.');
  183. }
  184. return;
  185. }
  186. if (null !== $this->logger) {
  187. $this->logger->debug('Remember-me was requested; setting cookie.');
  188. }
  189. // Remove attribute from request that sets a NULL cookie.
  190. // It was set by $this->cancelCookie()
  191. // (cancelCookie does other things too for some RememberMeServices
  192. // so we should still call it at the start of this method)
  193. $request->attributes->remove(self::COOKIE_ATTR_NAME);
  194. $this->onLoginSuccess($request, $response, $token);
  195. }
  196. /**
  197. * Subclasses should validate the cookie and do any additional processing
  198. * that is required. This is called from autoLogin().
  199. *
  200. * @param array $cookieParts
  201. * @param Request $request
  202. *
  203. * @return UserInterface
  204. */
  205. abstract protected function processAutoLoginCookie(array $cookieParts, Request $request);
  206. /**
  207. * @param Request $request
  208. */
  209. protected function onLoginFail(Request $request)
  210. {
  211. }
  212. /**
  213. * This is called after a user has been logged in successfully, and has
  214. * requested remember-me capabilities. The implementation usually sets a
  215. * cookie and possibly stores a persistent record of it.
  216. *
  217. * @param Request $request
  218. * @param Response $response
  219. * @param TokenInterface $token
  220. */
  221. abstract protected function onLoginSuccess(Request $request, Response $response, TokenInterface $token);
  222. final protected function getUserProvider($class)
  223. {
  224. foreach ($this->userProviders as $provider) {
  225. if ($provider->supportsClass($class)) {
  226. return $provider;
  227. }
  228. }
  229. throw new UnsupportedUserException(sprintf('There is no user provider that supports class "%s".', $class));
  230. }
  231. /**
  232. * Decodes the raw cookie value.
  233. *
  234. * @param string $rawCookie
  235. *
  236. * @return array
  237. */
  238. protected function decodeCookie($rawCookie)
  239. {
  240. return explode(self::COOKIE_DELIMITER, base64_decode($rawCookie));
  241. }
  242. /**
  243. * Encodes the cookie parts.
  244. *
  245. * @param array $cookieParts
  246. *
  247. * @return string
  248. *
  249. * @throws \InvalidArgumentException When $cookieParts contain the cookie delimiter. Extending class should either remove or escape it.
  250. */
  251. protected function encodeCookie(array $cookieParts)
  252. {
  253. foreach ($cookieParts as $cookiePart) {
  254. if (false !== strpos($cookiePart, self::COOKIE_DELIMITER)) {
  255. throw new \InvalidArgumentException(sprintf('$cookieParts should not contain the cookie delimiter "%s"', self::COOKIE_DELIMITER));
  256. }
  257. }
  258. return base64_encode(implode(self::COOKIE_DELIMITER, $cookieParts));
  259. }
  260. /**
  261. * Deletes the remember-me cookie.
  262. *
  263. * @param Request $request
  264. */
  265. protected function cancelCookie(Request $request)
  266. {
  267. if (null !== $this->logger) {
  268. $this->logger->debug('Clearing remember-me cookie.', array('name' => $this->options['name']));
  269. }
  270. $request->attributes->set(self::COOKIE_ATTR_NAME, new Cookie($this->options['name'], null, 1, $this->options['path'], $this->options['domain'], $this->options['secure'], $this->options['httponly']));
  271. }
  272. /**
  273. * Checks whether remember-me capabilities were requested.
  274. *
  275. * @param Request $request
  276. *
  277. * @return bool
  278. */
  279. protected function isRememberMeRequested(Request $request)
  280. {
  281. if (true === $this->options['always_remember_me']) {
  282. return true;
  283. }
  284. $parameter = ParameterBagUtils::getRequestParameterValue($request, $this->options['remember_me_parameter']);
  285. if (null === $parameter && null !== $this->logger) {
  286. $this->logger->debug('Did not send remember-me cookie.', array('parameter' => $this->options['remember_me_parameter']));
  287. }
  288. return $parameter === 'true' || $parameter === 'on' || $parameter === '1' || $parameter === 'yes';
  289. }
  290. }