Skip to content

Commit 7d2b782

Browse files
committed
update publish workflow voters to leverage symfony voter abstract
1 parent 739965f commit 7d2b782

File tree

4 files changed

+108
-94
lines changed

4 files changed

+108
-94
lines changed

src/PublishWorkflow/Voter/PublishTimePeriodVoter.php

Lines changed: 21 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,18 @@
1111

1212
namespace Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\Voter;
1313

14+
use function is_subclass_of;
1415
use Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\PublishTimePeriodReadInterface;
1516
use Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\PublishWorkflowChecker;
1617
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
17-
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
18+
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
1819

1920
/**
2021
* Workflow voter for the PublishTimePeriodReadInterface.
2122
*
2223
* @author David Buchmann <[email protected]>
2324
*/
24-
class PublishTimePeriodVoter implements VoterInterface
25+
class PublishTimePeriodVoter extends Voter
2526
{
2627
/**
2728
* @var \DateTime
@@ -43,61 +44,36 @@ public function setCurrentTime(\DateTime $currentTime)
4344
$this->currentTime = $currentTime;
4445
}
4546

46-
/**
47-
* {@inheritdoc}
48-
*
49-
* @deprecated To be removed when Symfony 2 support is dropped
50-
*/
51-
public function supportsAttribute($attribute)
47+
protected function supports($attribute, $subject)
5248
{
53-
return PublishWorkflowChecker::VIEW_ATTRIBUTE === $attribute
54-
|| PublishWorkflowChecker::VIEW_ANONYMOUS_ATTRIBUTE === $attribute
55-
;
49+
return $subject instanceof PublishTimePeriodReadInterface
50+
&& $this->supportsAttribute($attribute);
5651
}
5752

5853
/**
59-
* {@inheritdoc}
60-
*
61-
* @deprecated To be removed when Symfony 2 support is dropped
62-
*/
63-
public function supportsClass($class)
64-
{
65-
return is_subclass_of($class, PublishTimePeriodReadInterface::class);
66-
}
67-
68-
/**
69-
* {@inheritdoc}
54+
* {@inheritDoc}
7055
*
7156
* @param PublishTimePeriodReadInterface $subject
7257
*/
73-
public function vote(TokenInterface $token, $subject, array $attributes)
58+
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
7459
{
75-
if (!\is_object($subject) || !$this->supportsClass(\get_class($subject))) {
76-
return self::ACCESS_ABSTAIN;
77-
}
78-
7960
$startDate = $subject->getPublishStartDate();
8061
$endDate = $subject->getPublishEndDate();
8162

82-
$decision = self::ACCESS_GRANTED;
83-
foreach ($attributes as $attribute) {
84-
if (!$this->supportsAttribute($attribute)) {
85-
// there was an unsupported attribute in the request.
86-
// now we only abstain or deny if we find a supported attribute
87-
// and the content is not publishable
88-
$decision = self::ACCESS_ABSTAIN;
89-
90-
continue;
91-
}
63+
return (null === $startDate || $this->currentTime >= $startDate)
64+
&& (null === $endDate || $this->currentTime <= $endDate);
65+
}
9266

93-
if (
94-
(null !== $startDate && $this->currentTime < $startDate)
95-
|| (null !== $endDate && $this->currentTime > $endDate)
96-
) {
97-
return self::ACCESS_DENIED;
98-
}
99-
}
67+
public function supportsAttribute(string $attribute): bool
68+
{
69+
return
70+
PublishWorkflowChecker::VIEW_ATTRIBUTE === $attribute
71+
|| PublishWorkflowChecker::VIEW_ANONYMOUS_ATTRIBUTE === $attribute
72+
;
73+
}
10074

101-
return $decision;
75+
public function supportsType(string $subjectType): bool
76+
{
77+
return is_subclass_of($subjectType, PublishTimePeriodReadInterface::class);
10278
}
10379
}

src/PublishWorkflow/Voter/PublishableVoter.php

Lines changed: 19 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -11,67 +11,45 @@
1111

1212
namespace Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\Voter;
1313

14+
use function is_subclass_of;
1415
use Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\PublishableReadInterface;
1516
use Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\PublishWorkflowChecker;
1617
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
17-
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
18+
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
1819

1920
/**
2021
* Workflow voter for the PublishableReadInterface.
2122
*
2223
* @author David Buchmann <[email protected]>
2324
*/
24-
class PublishableVoter implements VoterInterface
25+
class PublishableVoter extends Voter
2526
{
26-
/**
27-
* {@inheritdoc}
28-
*
29-
* @deprecated To be removed when Symfony 2 support is dropped
30-
*/
31-
public function supportsAttribute($attribute)
27+
protected function supports($attribute, $subject)
3228
{
33-
return PublishWorkflowChecker::VIEW_ATTRIBUTE === $attribute
34-
|| PublishWorkflowChecker::VIEW_ANONYMOUS_ATTRIBUTE === $attribute
35-
;
29+
return $subject instanceof PublishableReadInterface
30+
&& $this->supportsAttribute($attribute);
3631
}
3732

3833
/**
39-
* {@inheritdoc}
34+
* {@inheritDoc}
4035
*
41-
* @deprecated To be removed when Symfony 2 support is dropped
36+
* @param PublishableReadInterface $subject
4237
*/
43-
public function supportsClass($class)
38+
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
4439
{
45-
return is_subclass_of($class, PublishableReadInterface::class);
40+
return $subject->isPublishable();
4641
}
4742

48-
/**
49-
* {@inheritdoc}
50-
*
51-
* @param PublishableReadInterface $subject
52-
*/
53-
public function vote(TokenInterface $token, $subject, array $attributes)
43+
public function supportsAttribute(string $attribute): bool
5444
{
55-
if (!\is_object($subject) || !$this->supportsClass(\get_class($subject))) {
56-
return self::ACCESS_ABSTAIN;
57-
}
58-
59-
$decision = self::ACCESS_GRANTED;
60-
foreach ($attributes as $attribute) {
61-
if (!$this->supportsAttribute($attribute)) {
62-
// there was an unsupported attribute in the request.
63-
// now we only abstain or deny if we find a supported attribute
64-
// and the content is not publishable
65-
$decision = self::ACCESS_ABSTAIN;
66-
67-
continue;
68-
}
69-
70-
if (!$subject->isPublishable()) {
71-
return self::ACCESS_DENIED;
72-
}
73-
}
45+
return
46+
PublishWorkflowChecker::VIEW_ATTRIBUTE === $attribute
47+
|| PublishWorkflowChecker::VIEW_ANONYMOUS_ATTRIBUTE === $attribute
48+
;
49+
}
7450

75-
return $decision;
51+
public function supportsType(string $subjectType): bool
52+
{
53+
return is_subclass_of($subjectType, PublishableReadInterface::class);
7654
}
7755
}

tests/Unit/PublishWorkflow/Voter/PublishTimePeriodVoterTest.php

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,15 @@
1111

1212
namespace Symfony\Cmf\Bundle\CoreBundle\Tests\Unit\PublishWorkflow\Voter;
1313

14+
use function is_subclass_of;
1415
use PHPUnit\Framework\TestCase;
1516
use Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\PublishTimePeriodReadInterface;
1617
use Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\PublishWorkflowChecker;
1718
use Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\Voter\PublishTimePeriodVoter;
1819
use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
1920
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
21+
use Symfony\Component\Security\Core\Authorization\Voter\CacheableVoterInterface;
22+
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
2023
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
2124

2225
class PublishTimePeriodVoterTest extends TestCase
@@ -75,22 +78,20 @@ public function providePublishWorkflowChecker()
7578
'startDate' => null,
7679
'endDate' => null,
7780
],
78-
// unsupported attribute
79-
[
80-
'expected' => VoterInterface::ACCESS_ABSTAIN,
81+
'at least one supported attribute' => [
82+
'expected' => VoterInterface::ACCESS_GRANTED,
8183
'startDate' => new \DateTime('01/01/2000'),
8284
'endDate' => new \DateTime('01/01/2030'),
8385
'attributes' => [PublishWorkflowChecker::VIEW_ATTRIBUTE, 'other'],
8486
],
85-
// Test overwrite current time
86-
[
87+
'Test overwrite current time to past end date' => [
8788
'expected' => VoterInterface::ACCESS_DENIED,
8889
'startDate' => null,
8990
'endDate' => new \DateTime('01/01/2030'),
9091
'attributes' => PublishWorkflowChecker::VIEW_ATTRIBUTE,
9192
'currentTime' => new \DateTime('02/02/2030'),
9293
],
93-
[
94+
'Test overwrite current time to before end date' => [
9495
'expected' => VoterInterface::ACCESS_GRANTED,
9596
'startDate' => null,
9697
'endDate' => new \DateTime('01/01/2000'),
@@ -140,4 +141,32 @@ public function testNonClassSubject()
140141
$result = $this->voter->vote($this->token, [1, 2, 3], [PublishWorkflowChecker::VIEW_ATTRIBUTE]);
141142
$this->assertEquals(VoterInterface::ACCESS_ABSTAIN, $result);
142143
}
144+
145+
public function testCachableVoterSupportsAttributes()
146+
{
147+
if (!$this->voter instanceof CacheableVoterInterface) {
148+
$this->assertFalse(
149+
is_subclass_of(Voter::class, CacheableVoterInterface::class),
150+
'Voter cache is supported and expected to be implemented'
151+
);
152+
}
153+
154+
$this->assertTrue($this->voter->supportsAttribute(PublishWorkflowChecker::VIEW_ATTRIBUTE));
155+
$this->assertTrue($this->voter->supportsAttribute(PublishWorkflowChecker::VIEW_ANONYMOUS_ATTRIBUTE));
156+
$this->assertFalse($this->voter->supportsAttribute('other'));
157+
}
158+
159+
public function testCachableVoterSupportsSubjectType()
160+
{
161+
if (!$this->voter instanceof CacheableVoterInterface) {
162+
$this->assertFalse(
163+
is_subclass_of(Voter::class, CacheableVoterInterface::class),
164+
'Voter cache is supported and expected to be implemented'
165+
);
166+
}
167+
168+
$doc = $this->createMock(PublishTimePeriodReadInterface::class);
169+
$this->assertTrue($this->voter->supportsType(\get_class($doc)));
170+
$this->assertFalse($this->voter->supportsType(static::class));
171+
}
143172
}

tests/Unit/PublishWorkflow/Voter/PublishableVoterTest.php

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,15 @@
1111

1212
namespace Symfony\Cmf\Bundle\CoreBundle\Tests\Unit\PublishWorkflow\Voter;
1313

14+
use function is_subclass_of;
1415
use PHPUnit\Framework\TestCase;
1516
use Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\PublishableReadInterface;
1617
use Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\PublishWorkflowChecker;
1718
use Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\Voter\PublishableVoter;
1819
use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
1920
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
21+
use Symfony\Component\Security\Core\Authorization\Voter\CacheableVoterInterface;
22+
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
2023
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
2124

2225
class PublishableVoterTest extends TestCase
@@ -68,8 +71,8 @@ public function providePublishWorkflowChecker()
6871
'isPublishable' => true,
6972
'attributes' => 'other',
7073
],
71-
[
72-
'expected' => VoterInterface::ACCESS_ABSTAIN,
74+
'at least one supported attribute' => [
75+
'expected' => VoterInterface::ACCESS_GRANTED,
7376
'isPublishable' => true,
7477
'attributes' => [PublishWorkflowChecker::VIEW_ATTRIBUTE, 'other'],
7578
],
@@ -108,4 +111,32 @@ public function testNonClassSubject()
108111
$result = $this->voter->vote($this->token, [1, 2, 3], [PublishWorkflowChecker::VIEW_ATTRIBUTE]);
109112
$this->assertEquals(VoterInterface::ACCESS_ABSTAIN, $result);
110113
}
114+
115+
public function testCachableVoterSupportsAttributes()
116+
{
117+
if (!$this->voter instanceof CacheableVoterInterface) {
118+
$this->assertFalse(
119+
is_subclass_of(Voter::class, CacheableVoterInterface::class),
120+
'Voter cache is supported and expected to be implemented'
121+
);
122+
}
123+
124+
$this->assertTrue($this->voter->supportsAttribute(PublishWorkflowChecker::VIEW_ATTRIBUTE));
125+
$this->assertTrue($this->voter->supportsAttribute(PublishWorkflowChecker::VIEW_ANONYMOUS_ATTRIBUTE));
126+
$this->assertFalse($this->voter->supportsAttribute('other'));
127+
}
128+
129+
public function testCachableVoterSupportsSubjectType()
130+
{
131+
if (!$this->voter instanceof CacheableVoterInterface) {
132+
$this->assertFalse(
133+
is_subclass_of(Voter::class, CacheableVoterInterface::class),
134+
'Voter cache is supported and expected to be implemented'
135+
);
136+
}
137+
138+
$doc = $this->createMock(PublishableReadInterface::class);
139+
$this->assertTrue($this->voter->supportsType(\get_class($doc)));
140+
$this->assertFalse($this->voter->supportsType(static::class));
141+
}
111142
}

0 commit comments

Comments
 (0)