1 | <?php |
||
2 | /* vim: set expandtab sw=4 ts=4 sts=4: */ |
||
3 | /** |
||
4 | * tests for TwoFactor class |
||
5 | * |
||
6 | * @package PhpMyAdmin-test |
||
7 | */ |
||
8 | declare(strict_types=1); |
||
9 | |||
10 | namespace PhpMyAdmin\Tests; |
||
11 | |||
12 | use PhpMyAdmin\TwoFactor; |
||
13 | use Samyoul\U2F\U2FServer\RegistrationRequest; |
||
14 | use Samyoul\U2F\U2FServer\SignRequest; |
||
15 | |||
16 | /** |
||
17 | * Tests behaviour of TwoFactor class |
||
18 | * |
||
19 | * @package PhpMyAdmin-test |
||
20 | */ |
||
21 | class TwoFactorTest extends PmaTestCase |
||
22 | { |
||
23 | /** |
||
24 | * @return void |
||
25 | */ |
||
26 | protected function setUp(): void |
||
27 | { |
||
28 | $GLOBALS['server'] = 1; |
||
29 | $GLOBALS['db'] = 'db'; |
||
30 | $GLOBALS['table'] = 'table'; |
||
31 | $GLOBALS['PMA_PHP_SELF'] = 'index.php'; |
||
32 | $GLOBALS['cfg']['Server']['DisableIS'] = false; |
||
33 | } |
||
34 | |||
35 | /** |
||
36 | * Creates TwoFactor mock with custom configuration |
||
37 | * |
||
38 | * @param string $user Username |
||
39 | * @param array $config Two factor authentication configuration |
||
40 | * |
||
41 | * @return TwoFactor |
||
42 | */ |
||
43 | public function getTwoFactorMock($user, $config) |
||
44 | { |
||
45 | if (! isset($config['backend'])) { |
||
46 | $config['backend'] = ''; |
||
47 | } |
||
48 | if (! isset($config['settings'])) { |
||
49 | $config['settings'] = []; |
||
50 | } |
||
51 | $result = $this->getMockBuilder('PhpMyAdmin\TwoFactor') |
||
52 | ->setMethods(['readConfig']) |
||
53 | ->disableOriginalConstructor() |
||
54 | ->getMock(); |
||
55 | $result->method('readConfig')->willReturn($config); |
||
56 | $result->__construct($user); |
||
57 | return $result; |
||
58 | } |
||
59 | |||
60 | /** |
||
61 | * @return void |
||
62 | */ |
||
63 | public function testNone() |
||
64 | { |
||
65 | $object = $this->getTwoFactorMock('user', ['type' => 'db']); |
||
66 | $backend = $object->backend; |
||
0 ignored issues
–
show
Bug
Best Practice
introduced
by
Loading history...
|
|||
67 | $this->assertEquals('', $backend::$id); |
||
68 | // Is always valid |
||
69 | $this->assertTrue($object->check(true)); |
||
70 | // Test session persistence |
||
71 | $this->assertTrue($object->check()); |
||
72 | $this->assertTrue($object->check()); |
||
73 | $this->assertEquals('', $object->render()); |
||
74 | $this->assertTrue($object->configure('')); |
||
75 | $this->assertEquals('', $object->setup()); |
||
76 | } |
||
77 | |||
78 | /** |
||
79 | * @return void |
||
80 | */ |
||
81 | public function testSimple() |
||
82 | { |
||
83 | $GLOBALS['cfg']['DBG']['simple2fa'] = true; |
||
84 | $object = $this->getTwoFactorMock('user', ['type' => 'db', 'backend' => 'simple']); |
||
85 | $backend = $object->backend; |
||
0 ignored issues
–
show
The property
backend does not exist on PhpMyAdmin\TwoFactor . Since you implemented __get , consider adding a @property annotation.
Loading history...
|
|||
86 | $this->assertEquals('simple', $backend::$id); |
||
87 | $GLOBALS['cfg']['DBG']['simple2fa'] = false; |
||
88 | |||
89 | unset($_POST['2fa_confirm']); |
||
90 | $this->assertFalse($object->check(true)); |
||
91 | |||
92 | $_POST['2fa_confirm'] = 1; |
||
93 | $this->assertTrue($object->check(true)); |
||
94 | unset($_POST['2fa_confirm']); |
||
95 | |||
96 | /* Test rendering */ |
||
97 | $this->assertNotEquals('', $object->render()); |
||
98 | $this->assertEquals('', $object->setup()); |
||
99 | } |
||
100 | |||
101 | /** |
||
102 | * @return void |
||
103 | */ |
||
104 | public function testLoad() |
||
105 | { |
||
106 | $object = new TwoFactor('user'); |
||
107 | $backend = $object->backend; |
||
0 ignored issues
–
show
The property
backend does not exist on PhpMyAdmin\TwoFactor . Since you implemented __get , consider adding a @property annotation.
Loading history...
|
|||
108 | $this->assertEquals('', $backend::$id); |
||
109 | } |
||
110 | |||
111 | /** |
||
112 | * @return void |
||
113 | */ |
||
114 | public function testConfigureSimple() |
||
115 | { |
||
116 | $GLOBALS['cfg']['DBG']['simple2fa'] = true; |
||
117 | $object = new TwoFactor('user'); |
||
118 | $this->assertTrue($object->configure('simple')); |
||
119 | $backend = $object->backend; |
||
0 ignored issues
–
show
The property
backend does not exist on PhpMyAdmin\TwoFactor . Since you implemented __get , consider adding a @property annotation.
Loading history...
|
|||
120 | $this->assertEquals('simple', $backend::$id); |
||
121 | $this->assertTrue($object->configure('')); |
||
122 | $backend = $object->backend; |
||
123 | $this->assertEquals('', $backend::$id); |
||
124 | $GLOBALS['cfg']['DBG']['simple2fa'] = false; |
||
125 | $object = new TwoFactor('user'); |
||
126 | $this->assertFalse($object->configure('simple')); |
||
127 | } |
||
128 | |||
129 | /** |
||
130 | * @return void |
||
131 | */ |
||
132 | public function testApplication() |
||
133 | { |
||
134 | $object = new TwoFactor('user'); |
||
135 | if (! in_array('application', $object->available)) { |
||
0 ignored issues
–
show
The property
available does not exist on PhpMyAdmin\TwoFactor . Since you implemented __get , consider adding a @property annotation.
Loading history...
|
|||
136 | $this->markTestSkipped('google2fa not available'); |
||
137 | } |
||
138 | /* Without providing code this should fail */ |
||
139 | unset($_POST['2fa_code']); |
||
140 | $this->assertFalse($object->configure('application')); |
||
141 | |||
142 | /* Invalid code */ |
||
143 | $_POST['2fa_code'] = 'invalid'; |
||
144 | $this->assertFalse($object->configure('application')); |
||
145 | |||
146 | /* Generate valid code */ |
||
147 | $google2fa = $object->backend->google2fa; |
||
0 ignored issues
–
show
The property
backend does not exist on PhpMyAdmin\TwoFactor . Since you implemented __get , consider adding a @property annotation.
Loading history...
|
|||
148 | $_POST['2fa_code'] = $google2fa->oathHotp( |
||
149 | $object->config['settings']['secret'], |
||
150 | $google2fa->getTimestamp() |
||
151 | ); |
||
152 | $this->assertTrue($object->configure('application')); |
||
153 | unset($_POST['2fa_code']); |
||
154 | |||
155 | /* Check code */ |
||
156 | unset($_POST['2fa_code']); |
||
157 | $this->assertFalse($object->check(true)); |
||
158 | $_POST['2fa_code'] = 'invalid'; |
||
159 | $this->assertFalse($object->check(true)); |
||
160 | $_POST['2fa_code'] = $google2fa->oathHotp( |
||
161 | $object->config['settings']['secret'], |
||
162 | $google2fa->getTimestamp() |
||
163 | ); |
||
164 | $this->assertTrue($object->check(true)); |
||
165 | unset($_POST['2fa_code']); |
||
166 | |||
167 | /* Test rendering */ |
||
168 | $this->assertNotEquals('', $object->render()); |
||
169 | $this->assertNotEquals('', $object->setup()); |
||
170 | } |
||
171 | |||
172 | /** |
||
173 | * @return void |
||
174 | */ |
||
175 | public function testKey() |
||
176 | { |
||
177 | $object = new TwoFactor('user'); |
||
178 | if (! in_array('key', $object->available)) { |
||
0 ignored issues
–
show
The property
available does not exist on PhpMyAdmin\TwoFactor . Since you implemented __get , consider adding a @property annotation.
Loading history...
|
|||
179 | $this->markTestSkipped('u2f-php-server not available'); |
||
180 | } |
||
181 | $_SESSION['registrationRequest'] = null; |
||
182 | /* Without providing code this should fail */ |
||
183 | unset($_POST['u2f_registration_response']); |
||
184 | $this->assertFalse($object->configure('key')); |
||
185 | |||
186 | /* Invalid code */ |
||
187 | $_POST['u2f_registration_response'] = 'invalid'; |
||
188 | $this->assertFalse($object->configure('key')); |
||
189 | |||
190 | /* Invalid code */ |
||
191 | $_POST['u2f_registration_response'] = '[]'; |
||
192 | $this->assertFalse($object->configure('key')); |
||
193 | |||
194 | /* Without providing code this should fail */ |
||
195 | unset($_POST['u2f_authentication_response']); |
||
196 | $this->assertFalse($object->check(true)); |
||
197 | |||
198 | /* Invalid code */ |
||
199 | $_POST['u2f_authentication_response'] = 'invalid'; |
||
200 | $this->assertFalse($object->check(true)); |
||
201 | |||
202 | /* Invalid code */ |
||
203 | $_POST['u2f_authentication_response'] = '[]'; |
||
204 | $this->assertFalse($object->check(true)); |
||
205 | |||
206 | /* Test rendering */ |
||
207 | $this->assertNotEquals('', $object->render()); |
||
208 | $this->assertNotEquals('', $object->setup()); |
||
209 | } |
||
210 | |||
211 | /** |
||
212 | * Test getting AppId |
||
213 | * |
||
214 | * @return void |
||
215 | */ |
||
216 | public function testKeyAppId() |
||
217 | { |
||
218 | $object = new TwoFactor('user'); |
||
219 | $GLOBALS['PMA_Config']->set('PmaAbsoluteUri', 'http://demo.example.com'); |
||
220 | $this->assertEquals('http://demo.example.com', $object->backend->getAppId(true)); |
||
0 ignored issues
–
show
The property
backend does not exist on PhpMyAdmin\TwoFactor . Since you implemented __get , consider adding a @property annotation.
Loading history...
|
|||
221 | $this->assertEquals('demo.example.com', $object->backend->getAppId(false)); |
||
222 | $GLOBALS['PMA_Config']->set('PmaAbsoluteUri', 'https://demo.example.com:123'); |
||
223 | $this->assertEquals('https://demo.example.com:123', $object->backend->getAppId(true)); |
||
224 | $this->assertEquals('demo.example.com', $object->backend->getAppId(false)); |
||
225 | $GLOBALS['PMA_Config']->set('PmaAbsoluteUri', ''); |
||
226 | $GLOBALS['PMA_Config']->set('is_https', true); |
||
227 | $_SERVER['HTTP_HOST'] = 'pma.example.com'; |
||
228 | $this->assertEquals('https://pma.example.com', $object->backend->getAppId(true)); |
||
229 | $this->assertEquals('pma.example.com', $object->backend->getAppId(false)); |
||
230 | $GLOBALS['PMA_Config']->set('is_https', false); |
||
231 | $this->assertEquals('http://pma.example.com', $object->backend->getAppId(true)); |
||
232 | $this->assertEquals('pma.example.com', $object->backend->getAppId(false)); |
||
233 | } |
||
234 | |||
235 | /** |
||
236 | * Test based on upstream test data: |
||
237 | * https://github.com/Yubico/php-u2flib-server |
||
238 | * |
||
239 | * @return void |
||
240 | */ |
||
241 | public function testKeyAuthentication() |
||
242 | { |
||
243 | $object = new TwoFactor('user'); |
||
244 | if (! in_array('key', $object->available)) { |
||
0 ignored issues
–
show
The property
available does not exist on PhpMyAdmin\TwoFactor . Since you implemented __get , consider adding a @property annotation.
Loading history...
|
|||
245 | $this->markTestSkipped('u2f-php-server not available'); |
||
246 | } |
||
247 | $_SESSION['registrationRequest'] = new RegistrationRequest('yKA0x075tjJ-GE7fKTfnzTOSaNUOWQxRd9TWz5aFOg8', 'http://demo.example.com'); |
||
248 | unset($_POST['u2f_registration_response']); |
||
249 | $this->assertFalse($object->configure('key')); |
||
250 | |||
251 | $_POST['u2f_registration_response'] = ''; |
||
252 | $this->assertFalse($object->configure('key')); |
||
253 | |||
254 | $_POST['u2f_registration_response'] = '{ "registrationData": "BQQtEmhWVgvbh-8GpjsHbj_d5FB9iNoRL8mNEq34-ANufKWUpVdIj6BSB_m3eMoZ3GqnaDy3RA5eWP8mhTkT1Ht3QAk1GsmaPIQgXgvrBkCQoQtMFvmwYPfW5jpRgoMPFxquHS7MTt8lofZkWAK2caHD-YQQdaRBgd22yWIjPuWnHOcwggLiMIHLAgEBMA0GCSqGSIb3DQEBCwUAMB0xGzAZBgNVBAMTEll1YmljbyBVMkYgVGVzdCBDQTAeFw0xNDA1MTUxMjU4NTRaFw0xNDA2MTQxMjU4NTRaMB0xGzAZBgNVBAMTEll1YmljbyBVMkYgVGVzdCBFRTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABNsK2_Uhx1zOY9ym4eglBg2U5idUGU-dJK8mGr6tmUQflaNxkQo6IOc-kV4T6L44BXrVeqN-dpCPr-KKlLYw650wDQYJKoZIhvcNAQELBQADggIBAJVAa1Bhfa2Eo7TriA_jMA8togoA2SUE7nL6Z99YUQ8LRwKcPkEpSpOsKYWJLaR6gTIoV3EB76hCiBaWN5HV3-CPyTyNsM2JcILsedPGeHMpMuWrbL1Wn9VFkc7B3Y1k3OmcH1480q9RpYIYr-A35zKedgV3AnvmJKAxVhv9GcVx0_CewHMFTryFuFOe78W8nFajutknarupekDXR4tVcmvj_ihJcST0j_Qggeo4_3wKT98CgjmBgjvKCd3Kqg8n9aSDVWyaOZsVOhZj3Fv5rFu895--D4qiPDETozJIyliH-HugoQpqYJaTX10mnmMdCa6aQeW9CEf-5QmbIP0S4uZAf7pKYTNmDQ5z27DVopqaFw00MIVqQkae_zSPX4dsNeeoTTXrwUGqitLaGap5ol81LKD9JdP3nSUYLfq0vLsHNDyNgb306TfbOenRRVsgQS8tJyLcknSKktWD_Qn7E5vjOXprXPrmdp7g5OPvrbz9QkWa1JTRfo2n2AXV02LPFc-UfR9bWCBEIJBxvmbpmqt0MnBTHWnth2b0CU_KJTDCY3kAPLGbOT8A4KiI73pRW-e9SWTaQXskw3Ei_dHRILM_l9OXsqoYHJ4Dd3tbfvmjoNYggSw4j50l3unI9d1qR5xlBFpW5sLr8gKX4bnY4SR2nyNiOQNLyPc0B0nW502aMEUCIQDTGOX-i_QrffJDY8XvKbPwMuBVrOSO-ayvTnWs_WSuDQIgZ7fMAvD_Ezyy5jg6fQeuOkoJi8V2naCtzV-HTly8Nww=", "clientData": "eyAiY2hhbGxlbmdlIjogInlLQTB4MDc1dGpKLUdFN2ZLVGZuelRPU2FOVU9XUXhSZDlUV3o1YUZPZzgiLCAib3JpZ2luIjogImh0dHA6XC9cL2RlbW8uZXhhbXBsZS5jb20iLCAidHlwIjogIm5hdmlnYXRvci5pZC5maW5pc2hFbnJvbGxtZW50IiB9", "errorCode": 0 }'; |
||
255 | $this->assertTrue($object->configure('key')); |
||
256 | |||
257 | unset($_POST['u2f_authentication_response']); |
||
258 | $this->assertFalse($object->check(true)); |
||
259 | |||
260 | $_POST['u2f_authentication_response'] = ''; |
||
261 | $this->assertFalse($object->check(true)); |
||
262 | |||
263 | $_SESSION['authenticationRequest'] = [new SignRequest([ |
||
264 | 'challenge' => 'fEnc9oV79EaBgK5BoNERU5gPKM2XGYWrz4fUjgc0Q7g', |
||
265 | 'keyHandle' => 'CTUayZo8hCBeC-sGQJChC0wW-bBg99bmOlGCgw8XGq4dLsxO3yWh9mRYArZxocP5hBB1pEGB3bbJYiM-5acc5w', |
||
266 | 'appId' => 'http://demo.example.com', |
||
267 | ]), |
||
268 | ]; |
||
269 | $this->assertFalse($object->check(true)); |
||
270 | $_POST['u2f_authentication_response'] = '{ "signatureData": "AQAAAAQwRQIhAI6FSrMD3KUUtkpiP0jpIEakql-HNhwWFngyw553pS1CAiAKLjACPOhxzZXuZsVO8im-HStEcYGC50PKhsGp_SUAng==", "clientData": "eyAiY2hhbGxlbmdlIjogImZFbmM5b1Y3OUVhQmdLNUJvTkVSVTVnUEtNMlhHWVdyejRmVWpnYzBRN2ciLCAib3JpZ2luIjogImh0dHA6XC9cL2RlbW8uZXhhbXBsZS5jb20iLCAidHlwIjogIm5hdmlnYXRvci5pZC5nZXRBc3NlcnRpb24iIH0=", "keyHandle": "CTUayZo8hCBeC-sGQJChC0wW-bBg99bmOlGCgw8XGq4dLsxO3yWh9mRYArZxocP5hBB1pEGB3bbJYiM-5acc5w", "errorCode": 0 }'; |
||
271 | $this->assertTrue($object->check(true)); |
||
272 | } |
||
273 | |||
274 | /** |
||
275 | * Test listing of available backends. |
||
276 | * |
||
277 | * @return void |
||
278 | */ |
||
279 | public function testBackends() |
||
280 | { |
||
281 | $GLOBALS['cfg']['DBG']['simple2fa'] = true; |
||
282 | $object = new TwoFactor('user'); |
||
283 | $backends = $object->getAllBackends(); |
||
284 | $this->assertCount( |
||
285 | count($object->available) + 1, |
||
0 ignored issues
–
show
The property
available does not exist on PhpMyAdmin\TwoFactor . Since you implemented __get , consider adding a @property annotation.
Loading history...
|
|||
286 | $backends |
||
287 | ); |
||
288 | $GLOBALS['cfg']['DBG']['simple2fa'] = false; |
||
289 | } |
||
290 | } |
||
291 |