Skip to content

Commit

Permalink
Fix the 'supports' method argument type of the security voter
Browse files Browse the repository at this point in the history
  • Loading branch information
francoispluchino authored and fabpot committed Jun 25, 2020
1 parent 3109317 commit 4e4c76f
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 14 deletions.
17 changes: 15 additions & 2 deletions Authorization/Voter/Voter.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,21 @@ public function vote(TokenInterface $token, $subject, array $attributes)
$vote = self::ACCESS_ABSTAIN;

foreach ($attributes as $attribute) {
if (!$this->supports($attribute, $subject)) {
continue;
try {
if (!$this->supports($attribute, $subject)) {
continue;
}
} catch (\TypeError $e) {
if (\PHP_VERSION_ID < 80000) {
if (0 === strpos($e->getMessage(), 'Argument 1 passed to')
&& false !== strpos($e->getMessage(), '::supports() must be of the type string')) {
continue;
}
} elseif (false !== strpos($e->getMessage(), 'supports(): Argument #1')) {
continue;
}

throw $e;
}

// as soon as at least one attribute is supported, default is to deny access
Expand Down
73 changes: 61 additions & 12 deletions Tests/Authorization/Voter/VoterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,34 +27,49 @@ protected function setUp(): void

public function getTests()
{
$voter = new VoterTest_Voter();
$integerVoter = new IntegerVoterTest_Voter();

return [
[['EDIT'], VoterInterface::ACCESS_GRANTED, new \stdClass(), 'ACCESS_GRANTED if attribute and class are supported and attribute grants access'],
[['CREATE'], VoterInterface::ACCESS_DENIED, new \stdClass(), 'ACCESS_DENIED if attribute and class are supported and attribute does not grant access'],
[$voter, ['EDIT'], VoterInterface::ACCESS_GRANTED, new \stdClass(), 'ACCESS_GRANTED if attribute and class are supported and attribute grants access'],
[$voter, ['CREATE'], VoterInterface::ACCESS_DENIED, new \stdClass(), 'ACCESS_DENIED if attribute and class are supported and attribute does not grant access'],

[$voter, ['DELETE', 'EDIT'], VoterInterface::ACCESS_GRANTED, new \stdClass(), 'ACCESS_GRANTED if one attribute is supported and grants access'],
[$voter, ['DELETE', 'CREATE'], VoterInterface::ACCESS_DENIED, new \stdClass(), 'ACCESS_DENIED if one attribute is supported and denies access'],

[$voter, ['CREATE', 'EDIT'], VoterInterface::ACCESS_GRANTED, new \stdClass(), 'ACCESS_GRANTED if one attribute grants access'],

[['DELETE', 'EDIT'], VoterInterface::ACCESS_GRANTED, new \stdClass(), 'ACCESS_GRANTED if one attribute is supported and grants access'],
[['DELETE', 'CREATE'], VoterInterface::ACCESS_DENIED, new \stdClass(), 'ACCESS_DENIED if one attribute is supported and denies access'],
[$voter, ['DELETE'], VoterInterface::ACCESS_ABSTAIN, new \stdClass(), 'ACCESS_ABSTAIN if no attribute is supported'],

[['CREATE', 'EDIT'], VoterInterface::ACCESS_GRANTED, new \stdClass(), 'ACCESS_GRANTED if one attribute grants access'],
[$voter, ['EDIT'], VoterInterface::ACCESS_ABSTAIN, $this, 'ACCESS_ABSTAIN if class is not supported'],

[['DELETE'], VoterInterface::ACCESS_ABSTAIN, new \stdClass(), 'ACCESS_ABSTAIN if no attribute is supported'],
[$voter, ['EDIT'], VoterInterface::ACCESS_ABSTAIN, null, 'ACCESS_ABSTAIN if object is null'],

[['EDIT'], VoterInterface::ACCESS_ABSTAIN, $this, 'ACCESS_ABSTAIN if class is not supported'],
[$voter, [], VoterInterface::ACCESS_ABSTAIN, new \stdClass(), 'ACCESS_ABSTAIN if no attributes were provided'],

[['EDIT'], VoterInterface::ACCESS_ABSTAIN, null, 'ACCESS_ABSTAIN if object is null'],
[$voter, [new StringableAttribute()], VoterInterface::ACCESS_GRANTED, new \stdClass(), 'ACCESS_GRANTED if attribute and class are supported and attribute grants access'],

[[], VoterInterface::ACCESS_ABSTAIN, new \stdClass(), 'ACCESS_ABSTAIN if no attributes were provided'],
[$voter, [new \stdClass()], VoterInterface::ACCESS_ABSTAIN, new \stdClass(), 'ACCESS_ABSTAIN if attributes were not strings'],

[$integerVoter, [42], VoterInterface::ACCESS_GRANTED, new \stdClass(), 'ACCESS_GRANTED if attribute is an integer'],
];
}

/**
* @dataProvider getTests
*/
public function testVote(array $attributes, $expectedVote, $object, $message)
public function testVote(VoterInterface $voter, array $attributes, $expectedVote, $object, $message)
{
$voter = new VoterTest_Voter();

$this->assertEquals($expectedVote, $voter->vote($this->token, $object, $attributes), $message);
}

public function testVoteWithTypeError()
{
$this->expectException('TypeError');
$this->expectExceptionMessage('Should error');
$voter = new TypeErrorVoterTest_Voter();
$voter->vote($this->token, new \stdClass(), ['EDIT']);
}
}

class VoterTest_Voter extends Voter
Expand All @@ -69,3 +84,37 @@ protected function supports(string $attribute, $object): bool
return $object instanceof \stdClass && \in_array($attribute, ['EDIT', 'CREATE']);
}
}

class IntegerVoterTest_Voter extends Voter
{
protected function voteOnAttribute($attribute, $object, TokenInterface $token): bool
{
return 42 === $attribute;
}

protected function supports($attribute, $object): bool
{
return $object instanceof \stdClass && \is_int($attribute);
}
}

class TypeErrorVoterTest_Voter extends Voter
{
protected function voteOnAttribute($attribute, $object, TokenInterface $token): bool
{
return false;
}

protected function supports($attribute, $object): bool
{
throw new \TypeError('Should error');
}
}

class StringableAttribute
{
public function __toString(): string
{
return 'EDIT';
}
}

0 comments on commit 4e4c76f

Please sign in to comment.