vendor/symfony/security-core/Authentication/Token/AbstractToken.php line 106

Open in your IDE?
  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\Core\Authentication\Token;
  11. use Symfony\Component\Security\Core\User\EquatableInterface;
  12. use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
  13. use Symfony\Component\Security\Core\User\UserInterface;
  14. /**
  15. * Base class for Token instances.
  16. *
  17. * @author Fabien Potencier <fabien@symfony.com>
  18. * @author Johannes M. Schmitt <schmittjoh@gmail.com>
  19. */
  20. abstract class AbstractToken implements TokenInterface
  21. {
  22. private $user;
  23. private $roleNames = [];
  24. private $authenticated = false;
  25. private $attributes = [];
  26. /**
  27. * @param string[] $roles An array of roles
  28. *
  29. * @throws \InvalidArgumentException
  30. */
  31. public function __construct(array $roles = [])
  32. {
  33. foreach ($roles as $role) {
  34. $this->roleNames[] = $role;
  35. }
  36. }
  37. /**
  38. * {@inheritdoc}
  39. */
  40. public function getRoleNames(): array
  41. {
  42. return $this->roleNames;
  43. }
  44. /**
  45. * {@inheritdoc}
  46. */
  47. public function getUsername(/* $legacy = true */)
  48. {
  49. if (1 === \func_num_args() && false === func_get_arg(0)) {
  50. return null;
  51. }
  52. trigger_deprecation('symfony/security-core', '5.3', 'Method "%s()" is deprecated, use getUserIdentifier() instead.', __METHOD__);
  53. if ($this->user instanceof UserInterface) {
  54. return method_exists($this->user, 'getUserIdentifier') ? $this->user->getUserIdentifier() : $this->user->getUsername();
  55. }
  56. return (string) $this->user;
  57. }
  58. /**
  59. * {@inheritdoc}
  60. */
  61. public function getUserIdentifier(): string
  62. {
  63. // method returns "null" in non-legacy mode if not overridden
  64. $username = $this->getUsername(false);
  65. if (null !== $username) {
  66. trigger_deprecation('symfony/security-core', '5.3', 'Method "%s::getUsername()" is deprecated, override "getUserIdentifier()" instead.', get_debug_type($this));
  67. }
  68. if ($this->user instanceof UserInterface) {
  69. // @deprecated since Symfony 5.3, change to $user->getUserIdentifier() in 6.0
  70. return method_exists($this->user, 'getUserIdentifier') ? $this->user->getUserIdentifier() : $this->user->getUsername();
  71. }
  72. return (string) $this->user;
  73. }
  74. /**
  75. * {@inheritdoc}
  76. */
  77. public function getUser()
  78. {
  79. return $this->user;
  80. }
  81. /**
  82. * {@inheritdoc}
  83. */
  84. public function setUser($user)
  85. {
  86. if (!($user instanceof UserInterface || (\is_object($user) && method_exists($user, '__toString')) || \is_string($user))) {
  87. throw new \InvalidArgumentException('$user must be an instanceof UserInterface, an object implementing a __toString method, or a primitive string.');
  88. }
  89. if (!$user instanceof UserInterface) {
  90. trigger_deprecation('symfony/security-core', '5.4', 'Using an object that is not an instance of "%s" as $user in "%s" is deprecated.', UserInterface::class, static::class);
  91. }
  92. // @deprecated since Symfony 5.4, remove the whole block if/elseif/else block in 6.0
  93. if (1 < \func_num_args() && !func_get_arg(1)) {
  94. // ContextListener checks if the user has changed on its own and calls `setAuthenticated()` subsequently,
  95. // avoid doing the same checks twice
  96. $changed = false;
  97. } elseif (null === $this->user) {
  98. $changed = false;
  99. } elseif ($this->user instanceof UserInterface) {
  100. if (!$user instanceof UserInterface) {
  101. $changed = true;
  102. } else {
  103. $changed = $this->hasUserChanged($user);
  104. }
  105. } elseif ($user instanceof UserInterface) {
  106. $changed = true;
  107. } else {
  108. $changed = (string) $this->user !== (string) $user;
  109. }
  110. // @deprecated since Symfony 5.4
  111. if ($changed) {
  112. $this->setAuthenticated(false, false);
  113. }
  114. $this->user = $user;
  115. }
  116. /**
  117. * {@inheritdoc}
  118. *
  119. * @deprecated since Symfony 5.4
  120. */
  121. public function isAuthenticated()
  122. {
  123. if (1 > \func_num_args() || func_get_arg(0)) {
  124. trigger_deprecation('symfony/security-core', '5.4', 'Method "%s()" is deprecated, return null from "getUser()" instead when a token is not authenticated.', __METHOD__);
  125. }
  126. return $this->authenticated;
  127. }
  128. /**
  129. * {@inheritdoc}
  130. */
  131. public function setAuthenticated(bool $authenticated)
  132. {
  133. if (2 > \func_num_args() || func_get_arg(1)) {
  134. trigger_deprecation('symfony/security-core', '5.4', 'Method "%s()" is deprecated', __METHOD__);
  135. }
  136. $this->authenticated = $authenticated;
  137. }
  138. /**
  139. * {@inheritdoc}
  140. */
  141. public function eraseCredentials()
  142. {
  143. if ($this->getUser() instanceof UserInterface) {
  144. $this->getUser()->eraseCredentials();
  145. }
  146. }
  147. /**
  148. * Returns all the necessary state of the object for serialization purposes.
  149. *
  150. * There is no need to serialize any entry, they should be returned as-is.
  151. * If you extend this method, keep in mind you MUST guarantee parent data is present in the state.
  152. * Here is an example of how to extend this method:
  153. * <code>
  154. * public function __serialize(): array
  155. * {
  156. * return [$this->childAttribute, parent::__serialize()];
  157. * }
  158. * </code>
  159. *
  160. * @see __unserialize()
  161. */
  162. public function __serialize(): array
  163. {
  164. return [$this->user, $this->authenticated, null, $this->attributes, $this->roleNames];
  165. }
  166. /**
  167. * Restores the object state from an array given by __serialize().
  168. *
  169. * There is no need to unserialize any entry in $data, they are already ready-to-use.
  170. * If you extend this method, keep in mind you MUST pass the parent data to its respective class.
  171. * Here is an example of how to extend this method:
  172. * <code>
  173. * public function __unserialize(array $data): void
  174. * {
  175. * [$this->childAttribute, $parentData] = $data;
  176. * parent::__unserialize($parentData);
  177. * }
  178. * </code>
  179. *
  180. * @see __serialize()
  181. */
  182. public function __unserialize(array $data): void
  183. {
  184. [$this->user, $this->authenticated, , $this->attributes, $this->roleNames] = $data;
  185. }
  186. /**
  187. * {@inheritdoc}
  188. */
  189. public function getAttributes()
  190. {
  191. return $this->attributes;
  192. }
  193. /**
  194. * {@inheritdoc}
  195. */
  196. public function setAttributes(array $attributes)
  197. {
  198. $this->attributes = $attributes;
  199. }
  200. /**
  201. * {@inheritdoc}
  202. */
  203. public function hasAttribute(string $name)
  204. {
  205. return \array_key_exists($name, $this->attributes);
  206. }
  207. /**
  208. * {@inheritdoc}
  209. */
  210. public function getAttribute(string $name)
  211. {
  212. if (!\array_key_exists($name, $this->attributes)) {
  213. throw new \InvalidArgumentException(sprintf('This token has no "%s" attribute.', $name));
  214. }
  215. return $this->attributes[$name];
  216. }
  217. /**
  218. * {@inheritdoc}
  219. */
  220. public function setAttribute(string $name, $value)
  221. {
  222. $this->attributes[$name] = $value;
  223. }
  224. /**
  225. * {@inheritdoc}
  226. */
  227. public function __toString()
  228. {
  229. $class = static::class;
  230. $class = substr($class, strrpos($class, '\\') + 1);
  231. $roles = [];
  232. foreach ($this->roleNames as $role) {
  233. $roles[] = $role;
  234. }
  235. return sprintf('%s(user="%s", authenticated=%s, roles="%s")', $class, $this->getUserIdentifier(), json_encode($this->authenticated), implode(', ', $roles));
  236. }
  237. /**
  238. * @internal
  239. */
  240. final public function serialize(): string
  241. {
  242. return serialize($this->__serialize());
  243. }
  244. /**
  245. * @internal
  246. */
  247. final public function unserialize($serialized)
  248. {
  249. $this->__unserialize(\is_array($serialized) ? $serialized : unserialize($serialized));
  250. }
  251. /**
  252. * @deprecated since Symfony 5.4
  253. */
  254. private function hasUserChanged(UserInterface $user): bool
  255. {
  256. if (!($this->user instanceof UserInterface)) {
  257. throw new \BadMethodCallException('Method "hasUserChanged" should be called when current user class is instance of "UserInterface".');
  258. }
  259. if ($this->user instanceof EquatableInterface) {
  260. return !(bool) $this->user->isEqualTo($user);
  261. }
  262. // @deprecated since Symfony 5.3, check for PasswordAuthenticatedUserInterface on both user objects before comparing passwords
  263. if ($this->user->getPassword() !== $user->getPassword()) {
  264. return true;
  265. }
  266. // @deprecated since Symfony 5.3, check for LegacyPasswordAuthenticatedUserInterface on both user objects before comparing salts
  267. if ($this->user->getSalt() !== $user->getSalt()) {
  268. return true;
  269. }
  270. $userRoles = array_map('strval', (array) $user->getRoles());
  271. if ($this instanceof SwitchUserToken) {
  272. $userRoles[] = 'ROLE_PREVIOUS_ADMIN';
  273. }
  274. if (\count($userRoles) !== \count($this->getRoleNames()) || \count($userRoles) !== \count(array_intersect($userRoles, $this->getRoleNames()))) {
  275. return true;
  276. }
  277. // @deprecated since Symfony 5.3, drop getUsername() in 6.0
  278. $userIdentifier = function ($user) {
  279. return method_exists($user, 'getUserIdentifier') ? $user->getUserIdentifier() : $user->getUsername();
  280. };
  281. if ($userIdentifier($this->user) !== $userIdentifier($user)) {
  282. return true;
  283. }
  284. return false;
  285. }
  286. }