Skip to content

[Security] add union types #41911

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,8 @@ public function getUser()
/**
* {@inheritdoc}
*/
public function setUser($user)
public function setUser(string|\Stringable|UserInterface $user)
{
if (!($user instanceof UserInterface || $user instanceof \Stringable || \is_string($user))) {
throw new \InvalidArgumentException('$user must be an instanceof UserInterface, an object implementing a __toString method, or a primitive string.');
}

if (null === $this->user) {
$changed = false;
} elseif ($this->user instanceof UserInterface) {
Expand Down Expand Up @@ -233,12 +229,7 @@ public function getAttribute(string $name)
return $this->attributes[$name];
}

/**
* Sets an attribute.
*
* @param mixed $value The attribute value
*/
public function setAttribute(string $name, $value)
public function setAttribute(string $name, mixed $value)
{
$this->attributes[$name] = $value;
}
Expand Down Expand Up @@ -267,9 +258,9 @@ final public function serialize(): string
/**
* @internal
*/
final public function unserialize($serialized)
final public function unserialize(string $serialized)
{
$this->__unserialize(\is_array($serialized) ? $serialized : unserialize($serialized));
$this->__unserialize(unserialize($serialized));
}

private function hasUserChanged(UserInterface $user): bool
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,10 @@ class AnonymousToken extends AbstractToken
private $secret;

/**
* @param string $secret A secret used to make sure the token is created by the app and not by a malicious client
* @param string|\Stringable|UserInterface $user
* @param string[] $roles
* @param string $secret A secret used to make sure the token is created by the app and not by a malicious client
* @param string[] $roles
*/
public function __construct(string $secret, $user, array $roles = [])
public function __construct(string $secret, string|\Stringable|UserInterface $user, array $roles = [])
{
parent::__construct($roles);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

namespace Symfony\Component\Security\Core\Authentication\Token;

use Symfony\Component\Security\Core\User\UserInterface;

/**
* @author Wouter de Jong <wouter@wouterj.nl>
*/
Expand All @@ -36,7 +38,7 @@ public function getUser()
return '';
}

public function setUser($user)
public function setUser(string|\Stringable|UserInterface $user)
{
throw new \BadMethodCallException('Cannot set user on a NullToken.');
}
Expand Down Expand Up @@ -87,7 +89,7 @@ public function getAttribute(string $name)
return null;
}

public function setAttribute(string $name, $value)
public function setAttribute(string $name, mixed $value)
{
throw new \BadMethodCallException('Cannot add attribute to NullToken.');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,9 @@ class PreAuthenticatedToken extends AbstractToken
private $firewallName;

/**
* @param string|\Stringable|UserInterface $user
* @param mixed $credentials
* @param string[] $roles
* @param string[] $roles
*/
public function __construct($user, $credentials, string $firewallName, array $roles = [])
public function __construct(string|\Stringable|UserInterface $user, mixed $credentials, string $firewallName, array $roles = [])
{
parent::__construct($roles);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

namespace Symfony\Component\Security\Core\Authentication\Token;

use Symfony\Component\Security\Core\User\UserInterface;

/**
* Token representing a user who temporarily impersonates another one.
*
Expand All @@ -22,13 +24,13 @@ class SwitchUserToken extends UsernamePasswordToken
private $originatedFromUri;

/**
* @param string|object $user The username (like a nickname, email address, etc.), or a UserInterface instance or an object implementing a __toString method
* @param mixed $credentials This usually is the password of the user
* @param string|null $originatedFromUri The URI where was the user at the switch
* @param $user The username (like a nickname, email address, etc.), or a UserInterface instance or an object implementing a __toString method
* @param $credentials This usually is the password of the user
* @param $originatedFromUri The URI where was the user at the switch
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it valid to omit the type?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for phpstorm at least it is

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://docs.phpdoc.org/3.0/guide/references/phpdoc/tags/param.html

@param [<Type>] [name] [<description>]

[…]

When provided it MAY contain a Type to indicate what is expected.

Yes, the type is optional. But I had to look it up myself as well and I'm not yet sure if I like that notation. Maybe we just need to get used to it. 🤔

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If popular IDEs support it, I'm fine with it personally.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once you start using PHP attributes for everything ... you want to use it even to describe arguments. I wish this worked:

#[Param('user', 'The username (like a nickname, email address, etc.), or a ...')]
#[Param('credentials', 'This usually is the password of the user')]
#[Param('originatedFromUri', 'The URI where was the user at the switch')]
public function __construct(string|\Stringable|UserInterface $user, mixed $credentials, /* ... */, string $originatedFromUri = null)
{
    // ...
}

Or even better, this:

public function __construct(
    #[Param('The username (like a nickname, email address, etc.), or a ...')]
    private string|\Stringable|UserInterface $user,
    #[Param('This usually is the password of the user')]
    private mixed $credentials,
    // ...
    #[Param('The URI where was the user at the switch')]
    private string $originatedFromUri = null)
{
}

But support for attributes in PhpDocumentor doesn't seem a priority: phpDocumentor/Reflection#185

*
* @throws \InvalidArgumentException
*/
public function __construct($user, $credentials, string $firewallName, array $roles, TokenInterface $originalToken, string $originatedFromUri = null)
public function __construct(string|\Stringable|UserInterface $user, mixed $credentials, string $firewallName, array $roles, TokenInterface $originalToken, string $originatedFromUri = null)
{
parent::__construct($user, $credentials, $firewallName, $roles);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,9 @@ public function getUser();
* The user can be a UserInterface instance, or an object implementing
* a __toString method or the username as a regular string.
*
* @param string|\Stringable|UserInterface $user
*
* @throws \InvalidArgumentException
*/
public function setUser($user);
public function setUser(string|\Stringable|UserInterface $user);

/**
* Returns whether the user is authenticated or not.
Expand Down Expand Up @@ -114,10 +112,8 @@ public function getAttribute(string $name);

/**
* Sets an attribute.
*
* @param mixed $value The attribute value
*/
public function setAttribute(string $name, $value);
public function setAttribute(string $name, mixed $value);

/**
* Returns all the necessary state of the object for serialization purposes.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,7 @@ class UsernamePasswordToken extends AbstractToken
private $credentials;
private $firewallName;

/**
* @param string|\Stringable|UserInterface $user The username (like a nickname, email address, etc.) or a UserInterface instance
* @param mixed $credentials
* @param string[] $roles
*
* @throws \InvalidArgumentException
*/
public function __construct($user, $credentials, string $firewallName, array $roles = [])
public function __construct(string|\Stringable|UserInterface $user, mixed $credentials, string $firewallName, array $roles = [])
{
parent::__construct($roles);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,8 @@ public function __construct(iterable $voters = [], string $strategy = self::STRA
*
* {@inheritdoc}
*/
public function decide(TokenInterface $token, array $attributes, $object = null/*, bool $allowMultipleAttributes = false*/)
public function decide(TokenInterface $token, array $attributes, mixed $object = null, bool $allowMultipleAttributes = false)
{
$allowMultipleAttributes = 3 < \func_num_args() && func_get_arg(3);

// Special case for AccessListener, do not remove the right side of the condition before 6.0
if (\count($attributes) > 1 && !$allowMultipleAttributes) {
throw new InvalidArgumentException(sprintf('Passing more than one Security attribute to "%s()" is not supported.', __METHOD__));
Expand All @@ -77,7 +75,7 @@ public function decide(TokenInterface $token, array $attributes, $object = null/
* If all voters abstained from voting, the decision will be based on the
* allowIfAllAbstainDecisions property value (defaults to false).
*/
private function decideAffirmative(TokenInterface $token, array $attributes, $object = null): bool
private function decideAffirmative(TokenInterface $token, array $attributes, mixed $object = null): bool
{
$deny = 0;
foreach ($this->voters as $voter) {
Expand Down Expand Up @@ -115,7 +113,7 @@ private function decideAffirmative(TokenInterface $token, array $attributes, $ob
* If all voters abstained from voting, the decision will be based on the
* allowIfAllAbstainDecisions property value (defaults to false).
*/
private function decideConsensus(TokenInterface $token, array $attributes, $object = null): bool
private function decideConsensus(TokenInterface $token, array $attributes, mixed $object = null): bool
{
$grant = 0;
$deny = 0;
Expand Down Expand Up @@ -152,7 +150,7 @@ private function decideConsensus(TokenInterface $token, array $attributes, $obje
* If all voters abstained from voting, the decision will be based on the
* allowIfAllAbstainDecisions property value (defaults to false).
*/
private function decideUnanimous(TokenInterface $token, array $attributes, $object = null): bool
private function decideUnanimous(TokenInterface $token, array $attributes, mixed $object = null): bool
{
$grant = 0;
foreach ($this->voters as $voter) {
Expand Down Expand Up @@ -186,7 +184,7 @@ private function decideUnanimous(TokenInterface $token, array $attributes, $obje
* If all voters abstained from voting, the decision will be based on the
* allowIfAllAbstainDecisions property value (defaults to false).
*/
private function decidePriority(TokenInterface $token, array $attributes, $object = null)
private function decidePriority(TokenInterface $token, array $attributes, mixed $object = null)
{
foreach ($this->voters as $voter) {
$result = $voter->vote($token, $object, $attributes);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,5 @@ interface AccessDecisionManagerInterface
*
* @return bool true if the access is granted, false otherwise
*/
public function decide(TokenInterface $token, array $attributes, $object = null);
public function decide(TokenInterface $token, array $attributes, mixed $object = null);
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public function __construct(TokenStorageInterface $tokenStorage, AuthenticationM
*
* @throws AuthenticationCredentialsNotFoundException when the token storage has no authentication token and $exceptionOnNoToken is set to true
*/
final public function isGranted($attribute, $subject = null): bool
final public function isGranted(mixed $attribute, mixed $subject = null): bool
{
if (null === ($token = $this->tokenStorage->getToken())) {
if ($this->exceptionOnNoToken) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,8 @@ interface AuthorizationCheckerInterface
* Checks if the attribute is granted against the current authentication token and optionally supplied subject.
*
* @param mixed $attribute A single attribute to vote on (can be of any type, string and instance of Expression are supported by the core)
* @param mixed $subject
*
* @return bool
*/
public function isGranted($attribute, $subject = null);
public function isGranted(mixed $attribute, mixed $subject = null);
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,8 @@ public function __construct(AccessDecisionManagerInterface $manager)

/**
* {@inheritdoc}
*
* @param bool $allowMultipleAttributes Whether to allow passing multiple values to the $attributes array
*/
public function decide(TokenInterface $token, array $attributes, $object = null/*, bool $allowMultipleAttributes = false*/): bool
public function decide(TokenInterface $token, array $attributes, mixed $object = null, bool $allowMultipleAttributes = false): bool
{
$currentDecisionLog = [
'attributes' => $attributes,
Expand All @@ -60,7 +58,7 @@ public function decide(TokenInterface $token, array $attributes, $object = null/

$this->currentLog[] = &$currentDecisionLog;

$result = $this->manager->decide($token, $attributes, $object, 3 < \func_num_args() && func_get_arg(3));
$result = $this->manager->decide($token, $attributes, $object, $allowMultipleAttributes);

$currentDecisionLog['result'] = $result;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public function __construct(AuthenticationTrustResolverInterface $authentication
/**
* {@inheritdoc}
*/
public function vote(TokenInterface $token, $subject, array $attributes)
public function vote(TokenInterface $token, mixed $subject, array $attributes)
{
if ($attributes === [self::PUBLIC_ACCESS]) {
return VoterInterface::ACCESS_GRANTED;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public function __construct(ExpressionLanguage $expressionLanguage, Authenticati
/**
* {@inheritdoc}
*/
public function vote(TokenInterface $token, $subject, array $attributes)
public function vote(TokenInterface $token, mixed $subject, array $attributes)
{
$result = VoterInterface::ACCESS_ABSTAIN;
$variables = null;
Expand All @@ -64,7 +64,7 @@ public function vote(TokenInterface $token, $subject, array $attributes)
return $result;
}

private function getVariables(TokenInterface $token, $subject): array
private function getVariables(TokenInterface $token, mixed $subject): array
{
$roleNames = $token->getRoleNames();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public function __construct(string $prefix = 'ROLE_')
/**
* {@inheritdoc}
*/
public function vote(TokenInterface $token, $subject, array $attributes)
public function vote(TokenInterface $token, mixed $subject, array $attributes)
{
$result = VoterInterface::ACCESS_ABSTAIN;
$roles = $this->extractRoles($token);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public function __construct(VoterInterface $voter, EventDispatcherInterface $eve
$this->eventDispatcher = $eventDispatcher;
}

public function vote(TokenInterface $token, $subject, array $attributes)
public function vote(TokenInterface $token, mixed $subject, array $attributes)
{
$result = $this->voter->vote($token, $subject, $attributes);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ abstract class Voter implements VoterInterface
/**
* {@inheritdoc}
*/
public function vote(TokenInterface $token, $subject, array $attributes)
public function vote(TokenInterface $token, mixed $subject, array $attributes)
{
// abstain vote by default in case none of the attributes are supported
$vote = self::ACCESS_ABSTAIN;
Expand Down Expand Up @@ -57,20 +57,17 @@ public function vote(TokenInterface $token, $subject, array $attributes)
/**
* Determines if the attribute and subject are supported by this voter.
*
* @param string $attribute An attribute
* @param mixed $subject The subject to secure, e.g. an object the user wants to access or any other PHP type
* @param $subject The subject to secure, e.g. an object the user wants to access or any other PHP type
*
* @return bool True if the attribute and subject are supported, false otherwise
*/
abstract protected function supports(string $attribute, $subject);
abstract protected function supports(string $attribute, mixed $subject);

/**
* Perform a single access check operation on a given attribute, subject and token.
* It is safe to assume that $attribute and $subject already passed the "supports()" method check.
*
* @param mixed $subject
*
* @return bool
*/
abstract protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token);
abstract protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token);
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,5 @@ interface VoterInterface
*
* @return int either ACCESS_GRANTED, ACCESS_ABSTAIN, or ACCESS_DENIED
*/
public function vote(TokenInterface $token, $subject, array $attributes);
public function vote(TokenInterface $token, mixed $subject, array $attributes);
}
4 changes: 2 additions & 2 deletions src/Symfony/Component/Security/Core/Event/VoteEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ final class VoteEvent extends Event
private $attributes;
private $vote;

public function __construct(VoterInterface $voter, $subject, array $attributes, int $vote)
public function __construct(VoterInterface $voter, mixed $subject, array $attributes, int $vote)
{
$this->voter = $voter;
$this->subject = $subject;
Expand All @@ -41,7 +41,7 @@ public function getVoter(): VoterInterface
return $this->voter;
}

public function getSubject()
public function getSubject(): mixed
{
return $this->subject;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,7 @@ public function getAttributes()
return $this->attributes;
}

/**
* @param array|string $attributes
*/
public function setAttributes($attributes)
public function setAttributes(array|string $attributes)
{
$this->attributes = (array) $attributes;
}
Expand All @@ -50,10 +47,7 @@ public function getSubject()
return $this->subject;
}

/**
* @param mixed $subject
*/
public function setSubject($subject)
public function setSubject(mixed $subject)
{
$this->subject = $subject;
}
Expand Down
5 changes: 1 addition & 4 deletions src/Symfony/Component/Security/Core/Security.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,8 @@ public function getUser(): ?UserInterface

/**
* Checks if the attributes are granted against the current authentication token and optionally supplied subject.
*
* @param mixed $attributes
* @param mixed $subject
*/
public function isGranted($attributes, $subject = null): bool
public function isGranted(mixed $attributes, mixed $subject = null): bool
{
return $this->container->get('security.authorization_checker')
->isGranted($attributes, $subject);
Expand Down
Loading