1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Vectorface\SnappyRouterTests\Plugin\AccessControl; |
4
|
|
|
|
5
|
|
|
use PHPUnit\Framework\TestCase; |
6
|
|
|
use Vectorface\SnappyRouter\Exception\AccessDeniedException; |
7
|
|
|
use Vectorface\SnappyRouter\Exception\InternalErrorException; |
8
|
|
|
use Vectorface\SnappyRouter\Handler\ControllerHandler; |
9
|
|
|
use Vectorface\SnappyRouter\Handler\PatternMatchHandler; |
10
|
|
|
use Vectorface\SnappyRouter\Handler\JsonRpcHandler; |
11
|
|
|
use Vectorface\SnappyRouter\Plugin\AccessControl\CrossOriginRequestPlugin; |
12
|
|
|
use Vectorface\SnappyRouterTests\Handler\JsonRpcHandlerTest; |
13
|
|
|
|
14
|
|
|
/** |
15
|
|
|
* Tests the router header plugin. |
16
|
|
|
* @copyright Copyright (c) 2014, VectorFace, Inc. |
17
|
|
|
* @author Dan Bruce <[email protected]> |
18
|
|
|
*/ |
19
|
|
|
class CrossOriginRequestPluginTest extends TestCase |
20
|
|
|
{ |
21
|
|
|
/** |
22
|
|
|
* An overview of how to use the plugin. |
23
|
|
|
* @test |
24
|
|
|
*/ |
25
|
|
|
public function synopsis() |
26
|
|
|
{ |
27
|
|
|
// make it appear that we are generating a cross origin request |
28
|
|
|
$_SERVER['HTTP_ORIGIN'] = 'cross.example.com'; |
29
|
|
|
// some dummy variables that are needed by the plugin |
30
|
|
|
$handler = new ControllerHandler(array()); |
31
|
|
|
$this->assertTrue($handler->isAppropriate('/testDummy', array(), array(), 'OPTIONS')); |
32
|
|
|
|
33
|
|
|
// configure the plugin to allow cross origin access to all methods |
34
|
|
|
// within the TestDummyController controller |
35
|
|
|
$plugin = new CrossOriginRequestPlugin(array( |
36
|
|
|
'whitelist' => array( |
37
|
|
|
'TestdummyController' => 'all' |
38
|
|
|
), |
39
|
|
|
'ignoreOrigins' => array( |
40
|
|
|
'www.example.com' |
41
|
|
|
), |
42
|
|
|
'Access-Control-Max-Age' => 3600, |
43
|
|
|
'Access-Control-Allow-Headers' => array('accept', 'content-type', 'content-length'), |
44
|
|
|
'Access-Control-Allow-Methods' => array('GET', 'POST', 'OPTIONS', 'PUT', 'DELETE') |
45
|
|
|
)); |
46
|
|
|
|
47
|
|
|
// if the plugin allows the cross origin request then no exception |
48
|
|
|
// should be thrown |
49
|
|
|
$this->assertNull($plugin->afterHandlerSelected($handler)); |
50
|
|
|
// a bit of cleanup |
51
|
|
|
unset($_SERVER['HTTP_ORIGIN']); |
52
|
|
|
} |
53
|
|
|
|
54
|
|
|
/** |
55
|
|
|
* Tests that a non-cross origin request simply bypasses the plugin. |
56
|
|
|
*/ |
57
|
|
|
public function testNonCrossOriginRequests() |
58
|
|
|
{ |
59
|
|
|
// make it appear that we are not generating a cross origin request |
60
|
|
|
unset($_SERVER['HTTP_ORIGIN']); |
61
|
|
|
// some dummy variables that are needed by the plugin |
62
|
|
|
$handler = new ControllerHandler(array()); |
63
|
|
|
$this->assertTrue($handler->isAppropriate('/testDummy', array(), array(), 'GET')); |
64
|
|
|
// an empty whitelist indicates that every cross origin request should |
65
|
|
|
// be blocked |
66
|
|
|
$plugin = new CrossOriginRequestPlugin(array( |
67
|
|
|
'whitelist' => array(), |
68
|
|
|
'ignoreOrigins' => array( |
69
|
|
|
'www.example.com' |
70
|
|
|
) |
71
|
|
|
)); |
72
|
|
|
|
73
|
|
|
// this request should not be a cross origin one so no exception should |
74
|
|
|
// be thrown |
75
|
|
|
$this->assertNull($plugin->afterHandlerSelected($handler)); |
76
|
|
|
|
77
|
|
|
// set the origin to a domain in the ignored whitelist |
78
|
|
|
$_SERVER['HTTP_ORIGIN'] = 'www.example.com'; |
79
|
|
|
$this->assertNull($plugin->afterHandlerSelected($handler)); |
80
|
|
|
// cleanup |
81
|
|
|
unset($_SERVER['HTTP_ORIGIN']); |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
/** |
85
|
|
|
* Tests that we get an exception if the whitelist is missing from the |
86
|
|
|
* plugin configuration. |
87
|
|
|
*/ |
88
|
|
|
public function testMissingWhitelistGeneratesException() |
89
|
|
|
{ |
90
|
|
|
// make it appear that we are generating a cross origin request |
91
|
|
|
$_SERVER['HTTP_ORIGIN'] = 'www.example.com'; |
92
|
|
|
// some dummy variables that are needed by the plugin |
93
|
|
|
$handler = new ControllerHandler(array()); |
94
|
|
|
$this->assertTrue($handler->isAppropriate('/testDummy', array(), array(), 'GET')); |
95
|
|
|
$plugin = new CrossOriginRequestPlugin(array()); |
96
|
|
|
try { |
97
|
|
|
$plugin->afterHandlerSelected($handler); |
98
|
|
|
$this->fail(); |
99
|
|
|
} catch (InternalErrorException $e) { |
100
|
|
|
$this->assertEquals(500, $e->getAssociatedStatusCode()); |
101
|
|
|
} |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
/** |
105
|
|
|
* Tests that access is denied to a service not listed in the whitelist. |
106
|
|
|
*/ |
107
|
|
View Code Duplication |
public function testAccessDeniedToServiceMissingFromWhitelist() |
|
|
|
|
108
|
|
|
{ |
109
|
|
|
// make it appear that we are generating a cross origin request |
110
|
|
|
$_SERVER['HTTP_ORIGIN'] = 'www.example.com'; |
111
|
|
|
// some dummy variables that are needed by the plugin |
112
|
|
|
$handler = new ControllerHandler(array()); |
113
|
|
|
$this->assertTrue($handler->isAppropriate('/testDummy', array(), array(), 'GET')); |
114
|
|
|
$plugin = new CrossOriginRequestPlugin(array( |
115
|
|
|
'whitelist' => array() |
116
|
|
|
)); |
117
|
|
|
try { |
118
|
|
|
$plugin->afterHandlerSelected($handler); |
119
|
|
|
} catch (AccessDeniedException $e) { |
120
|
|
|
$this->assertEquals(403, $e->getAssociatedStatusCode()); |
121
|
|
|
} |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
/** |
125
|
|
|
* Tests that access is denied to an action not listed in the whitelist of |
126
|
|
|
* the controller. |
127
|
|
|
*/ |
128
|
|
View Code Duplication |
public function testAccessDeniedToActionMissingFromWhitelist() |
|
|
|
|
129
|
|
|
{ |
130
|
|
|
// make it appear that we are generating a cross origin request |
131
|
|
|
$_SERVER['HTTP_ORIGIN'] = 'www.example.com'; |
132
|
|
|
// some dummy variables that are needed by the plugin |
133
|
|
|
$handler = new ControllerHandler(array()); |
134
|
|
|
$this->assertTrue($handler->isAppropriate('/testDummy', array(), array(), 'GET')); |
135
|
|
|
$plugin = new CrossOriginRequestPlugin(array( |
136
|
|
|
'whitelist' => array( |
137
|
|
|
'TestdummyController' => array() |
138
|
|
|
) |
139
|
|
|
)); |
140
|
|
|
try { |
141
|
|
|
$plugin->afterHandlerSelected($handler); |
142
|
|
|
} catch (AccessDeniedException $e) { |
143
|
|
|
$this->assertEquals(403, $e->getAssociatedStatusCode()); |
144
|
|
|
} |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
/** |
148
|
|
|
* Tests that the whitelist can be the string 'all' instead of an array |
149
|
|
|
* allowing access to any service. |
150
|
|
|
*/ |
151
|
|
View Code Duplication |
public function testWhitelistingAllActions() |
|
|
|
|
152
|
|
|
{ |
153
|
|
|
// make it appear that we are generating a cross origin request |
154
|
|
|
$_SERVER['HTTP_ORIGIN'] = 'www.example.com'; |
155
|
|
|
// some dummy variables that are needed by the plugin |
156
|
|
|
$handler = new ControllerHandler(array()); |
157
|
|
|
$this->assertTrue($handler->isAppropriate('/testDummy', array(), array(), 'GET')); |
158
|
|
|
$plugin = new CrossOriginRequestPlugin(array( |
159
|
|
|
'whitelist' => 'all' |
160
|
|
|
)); |
161
|
|
|
try { |
162
|
|
|
$plugin->afterHandlerSelected($handler); |
163
|
|
|
$this->assertTrue(true); |
164
|
|
|
} catch (AccessDeniedException $e) { |
165
|
|
|
$this->fail('Cross origin plugin should not have denied access.'); |
166
|
|
|
} |
167
|
|
|
} |
168
|
|
|
|
169
|
|
|
/** |
170
|
|
|
* Test that the plugin doesn't break with the PatternMatchHandler. |
171
|
|
|
*/ |
172
|
|
|
public function testCompatibilityWithPatternMatchHandler() |
173
|
|
|
{ |
174
|
|
|
// make it appear that we are generating a cross origin request |
175
|
|
|
$_SERVER['HTTP_ORIGIN'] = 'www.example.com'; |
176
|
|
|
// some dummy variables that are needed by the plugin |
177
|
|
|
$config = array( |
178
|
|
|
'routes' => array( |
179
|
|
|
'/testDummy' => function () { |
180
|
|
|
return true; |
181
|
|
|
} |
182
|
|
|
) |
183
|
|
|
); |
184
|
|
|
$handler = new PatternMatchHandler($config); |
185
|
|
|
$this->assertTrue($handler->isAppropriate('/testDummy', array(), array(), 'GET')); |
186
|
|
|
$plugin = new CrossOriginRequestPlugin(array( |
187
|
|
|
'whitelist' => 'all' |
188
|
|
|
)); |
189
|
|
|
try { |
190
|
|
|
$plugin->afterHandlerSelected($handler); |
191
|
|
|
$this->assertTrue(true); |
192
|
|
|
} catch (AccessDeniedException $e) { |
193
|
|
|
$this->fail('Cross origin plugin should not have denied access.'); |
194
|
|
|
} |
195
|
|
|
} |
196
|
|
|
|
197
|
|
|
/** |
198
|
|
|
* Test that the plugin doesn't break with the PatternMatchHandler. |
199
|
|
|
*/ |
200
|
|
|
public function testCompatibilityWithJsonRpcHandler() |
201
|
|
|
{ |
202
|
|
|
// make it appear that we are generating a cross origin request |
203
|
|
|
$_SERVER['HTTP_ORIGIN'] = 'www.example.com'; |
204
|
|
|
// some dummy variables that are needed by the plugin |
205
|
|
|
$config = array( |
206
|
|
|
'controllers' => array( |
207
|
|
|
'TestController' => 'Vectorface\\SnappyRouterTests\\Controller\\TestDummyController' |
208
|
|
|
) |
209
|
|
|
); |
210
|
|
|
$handler = new JsonRpcHandler($config); |
211
|
|
|
$payload = array('jsonrpc' => '2.0', 'method' => 'testAction', 'id' => '1'); |
212
|
|
|
JsonRpcHandlerTest::setRequestPayload($handler, $payload); |
213
|
|
|
$this->assertTrue($handler->isAppropriate('/testDummy', array(), array(), 'POST')); |
214
|
|
|
$plugin = new CrossOriginRequestPlugin(array( |
215
|
|
|
'whitelist' => array( |
216
|
|
|
'testDummy' => array('testAction') |
217
|
|
|
) |
218
|
|
|
)); |
219
|
|
|
try { |
220
|
|
|
$plugin->afterHandlerSelected($handler); |
221
|
|
|
$this->assertTrue(true); |
222
|
|
|
} catch (AccessDeniedException $e) { |
223
|
|
|
$this->fail('Cross origin plugin should not have denied access.'); |
224
|
|
|
} |
225
|
|
|
} |
226
|
|
|
} |
227
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.