SymfonyAccessControlProviderTest   A
last analyzed

Complexity

Total Complexity 14

Size/Duplication

Total Lines 228
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 14
eloc 112
dl 0
loc 228
rs 10
c 0
b 0
f 0

12 Methods

Rating   Name   Duplication   Size   Complexity  
A createRuleArray() 0 12 1
A sameInstancesOfEqualTestsDataProvider() 0 32 1
A testLogRuntimeMatching() 0 18 1
A testSameInstancesOfEqualTests() 0 27 2
A getTestsDataProvider() 0 14 1
A testImportRules() 0 12 1
A testGetTestsWithoutMatches() 0 5 1
A testImportRulesWithExpression() 0 22 1
A setUp() 0 9 1
A testImportRulesWithInvalidExpressionException() 0 13 1
A testImportRulesWithExpressionWithoutExpressionLanguage() 0 8 1
A testGetTests() 0 12 2
1
<?php
2
3
/*
4
 *
5
 * (c) Yaroslav Honcharuk <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace Yarhon\RouteGuardBundle\Tests\Security\TestProvider;
12
13
use PHPUnit\Framework\TestCase;
14
use Psr\Log\LoggerInterface;
15
use Symfony\Component\Routing\Route;
16
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
17
use Symfony\Component\ExpressionLanguage\Expression;
18
use Symfony\Component\ExpressionLanguage\SyntaxError;
19
use Yarhon\RouteGuardBundle\Security\Http\RequestConstraint;
20
use Yarhon\RouteGuardBundle\Security\Http\RouteMatcher;
21
use Yarhon\RouteGuardBundle\Security\Test\TestBag;
22
use Yarhon\RouteGuardBundle\Security\Test\SymfonyAccessControlTest;
23
use Yarhon\RouteGuardBundle\Security\Http\RequestDependentTestBag;
24
use Yarhon\RouteGuardBundle\Security\Authorization\SymfonySecurityExpressionVoter;
25
use Yarhon\RouteGuardBundle\Security\TestProvider\SymfonyAccessControlProvider;
26
use Yarhon\RouteGuardBundle\Exception\LogicException;
27
use Yarhon\RouteGuardBundle\Exception\InvalidArgumentException;
28
29
/**
30
 * @author Yaroslav Honcharuk <[email protected]>
31
 */
32
class SymfonyAccessControlProviderTest extends TestCase
33
{
34
    private $expressionLanguage;
35
36
    private $routeMatcher;
37
38
    private $provider;
39
40
    private $route;
41
42
    public function setUp()
43
    {
44
        $this->expressionLanguage = $this->createMock(ExpressionLanguage::class);
45
46
        $this->routeMatcher = $this->createMock(RouteMatcher::class);
47
48
        $this->provider = new SymfonyAccessControlProvider($this->routeMatcher);
49
50
        $this->route = new Route('/');
51
    }
52
53
    /**
54
     * @dataProvider getTestsDataProvider
55
     */
56
    public function testGetTests($tests, $routeMatcherResults, $expected)
57
    {
58
        foreach ($tests as $test) {
59
            $this->provider->addRule(new RequestConstraint(), $test);
60
        }
61
62
        $this->routeMatcher->method('matches')
63
            ->willReturnOnConsecutiveCalls(...$routeMatcherResults);
64
65
        $testBag = $this->provider->getTests('index', $this->route);
66
67
        $this->assertEquals($expected, $testBag);
68
    }
69
70
    public function getTestsDataProvider()
71
    {
72
        return [
73
            [
74
                [new SymfonyAccessControlTest(['ROLE_ADMIN']), new SymfonyAccessControlTest(['ROLE_USER'])],
75
                [false, true],
76
                new TestBag([new SymfonyAccessControlTest(['ROLE_USER'])]),
77
            ],
78
            [
79
                [new SymfonyAccessControlTest(['ROLE_ADMIN']), new SymfonyAccessControlTest(['ROLE_USER'])],
80
                [new RequestConstraint('/admin'), true],
81
                new RequestDependentTestBag([
82
                    [[new SymfonyAccessControlTest(['ROLE_ADMIN'])], new RequestConstraint('/admin')],
83
                    [[new SymfonyAccessControlTest(['ROLE_USER'])], null],
84
                ]),
85
            ],
86
        ];
87
    }
88
89
    public function testLogRuntimeMatching()
90
    {
91
        $logger = $this->createMock(LoggerInterface::class);
92
        $this->provider->setLogger($logger);
93
94
        $logger->expects($this->once())
95
            ->method('warning')
96
            ->with('Route "index" (path "/") requires runtime matching to access_control rule(s) #0, #1 (zero-based), this would reduce performance.');
97
98
        $this->provider->addRule(new RequestConstraint(), new SymfonyAccessControlTest(['ROLE_ADMIN']));
99
        $this->provider->addRule(new RequestConstraint(), new SymfonyAccessControlTest(['ROLE_USER']));
100
101
        $requestConstraintForMap = new RequestConstraint();
102
103
        $this->routeMatcher->method('matches')
104
            ->willReturnOnConsecutiveCalls($requestConstraintForMap, true);
105
106
        $this->provider->getTests('index', $this->route);
107
    }
108
109
    public function testGetTestsWithoutMatches()
110
    {
111
        $testBag = $this->provider->getTests('index', $this->route);
112
113
        $this->assertNull($testBag);
114
    }
115
116
    public function testImportRules()
117
    {
118
        $rule = $this->createRuleArray();
119
        $rule['allow_if'] = null;
120
121
        $expectedConstraint = new RequestConstraint($rule['path'], $rule['host'], $rule['methods'], $rule['ips']);
122
        $expectedTest = new SymfonyAccessControlTest($rule['roles']);
123
124
        $this->provider->importRules([$rule]);
125
126
        $expectedRules = [[$expectedConstraint, $expectedTest]];
127
        $this->assertAttributeEquals($expectedRules, 'rules', $this->provider);
128
    }
129
130
    public function testImportRulesWithExpression()
131
    {
132
        $rule = $this->createRuleArray(['allow_if' => 'request.isSecure']);
133
134
        $names = SymfonySecurityExpressionVoter::getVariableNames();
135
136
        $this->provider->setExpressionLanguage($this->expressionLanguage);
137
138
        $this->expressionLanguage->expects($this->once())
139
            ->method('parse')
140
            ->with($rule['allow_if'], $names)
141
            ->willReturnCallback(function ($expressionString) {
142
                return new Expression($expressionString);
143
            });
144
145
        $expectedConstraint = new RequestConstraint($rule['path'], $rule['host'], $rule['methods'], $rule['ips']);
146
        $expectedTest = new SymfonyAccessControlTest(array_merge($rule['roles'], [new Expression('request.isSecure')]));
147
148
        $this->provider->importRules([$rule]);
149
150
        $expectedRules = [[$expectedConstraint, $expectedTest]];
151
        $this->assertAttributeEquals($expectedRules, 'rules', $this->provider);
152
    }
153
154
    public function testImportRulesWithInvalidExpressionException()
155
    {
156
        $rule = $this->createRuleArray(['allow_if' => 'request.isSecure']);
157
158
        $this->expressionLanguage->method('parse')
159
            ->willThrowException(new SyntaxError('syntax'));
160
161
        $this->provider->setExpressionLanguage($this->expressionLanguage);
162
163
        $this->expectException(InvalidArgumentException::class);
164
        $this->expectExceptionMessage('Cannot parse expression "request.isSecure" with following variables: "token", "user", "object", "subject", "roles", "trust_resolver", "request".');
165
166
        $this->provider->importRules([$rule]);
167
    }
168
169
    public function testImportRulesWithExpressionWithoutExpressionLanguage()
170
    {
171
        $rule = $this->createRuleArray(['allow_if' => 'request.isSecure']);
172
173
        $this->expectException(LogicException::class);
174
        $this->expectExceptionMessage('Cannot create expression because ExpressionLanguage is not provided.');
175
176
        $this->provider->importRules([$rule]);
177
    }
178
179
    /**
180
     * @dataProvider sameInstancesOfEqualTestsDataProvider
181
     */
182
    public function testSameInstancesOfEqualTests($ruleOne, $ruleTwo, $expected)
183
    {
184
        $ruleOne = $this->createRuleArray($ruleOne);
185
        $ruleTwo = $this->createRuleArray($ruleTwo);
186
187
        $this->provider->setExpressionLanguage($this->expressionLanguage);
188
189
        $this->expressionLanguage->method('parse')
190
            ->willReturnCallback(function ($expressionString) {
191
                return new Expression($expressionString);
192
            });
193
194
        $this->provider->importRules([$ruleOne, $ruleTwo]);
195
196
        $this->routeMatcher->method('matches')
197
            ->willReturnOnConsecutiveCalls(true, false, true);
198
199
        $testBag = $this->provider->getTests('index', $this->route);
200
        $testOne = $testBag->getTests()[0];
201
202
        $testBag = $this->provider->getTests('index', $this->route);
203
        $testTwo = $testBag->getTests()[0];
204
205
        if ($expected) {
206
            $this->assertSame($testOne, $testTwo);
207
        } else {
208
            $this->assertNotSame($testOne, $testTwo);
209
        }
210
    }
211
212
    public function sameInstancesOfEqualTestsDataProvider()
213
    {
214
        return [
215
            [
216
                ['roles' => ['ROLE_ADMIN']],
217
                ['roles' => ['ROLE_ADMIN']],
218
                true,
219
            ],
220
            [
221
                ['roles' => ['ROLE_ADMIN', 'ROLE_USER']],
222
                ['roles' => ['ROLE_USER', 'ROLE_ADMIN']],
223
                true,
224
            ],
225
            [
226
                ['roles' => ['ROLE_ADMIN', 'ROLE_USER']],
227
                ['roles' => ['ROLE_USER']],
228
                false,
229
            ],
230
            [
231
                ['roles' => ['ROLE_ADMIN'], 'allow_if' => 'request.isSecure'],
232
                ['roles' => ['ROLE_ADMIN']],
233
                false,
234
            ],
235
            [
236
                ['roles' => ['ROLE_ADMIN'], 'allow_if' => 'request.isSecure'],
237
                ['roles' => ['ROLE_ADMIN'], 'allow_if' => 'request.isSecure'],
238
                true,
239
            ],
240
            [
241
                ['roles' => ['ROLE_ADMIN'], 'allow_if' => 'request.isSecure'],
242
                ['roles' => ['ROLE_ADMIN'], 'allow_if' => 'not request.isSecure'],
243
                false,
244
            ],
245
        ];
246
    }
247
248
    private function createRuleArray(array $values = [])
249
    {
250
        $defaults = [
251
            'path' => null,
252
            'host' => null,
253
            'methods' => [],
254
            'ips' => [],
255
            'allow_if' => null,
256
            'roles' => [],
257
        ];
258
259
        return array_merge($defaults, $values);
260
    }
261
}
262