Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
// This file is part of Moodle - http://moodle.org/
3
//
4
// Moodle is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// Moodle is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
 
17
namespace tool_mfa;
18
 
19
/**
20
 * Tests for MFA secret manager class.
21
 *
22
 * @package     tool_mfa
23
 * @author      Peter Burnett <peterburnett@catalyst-au.net>
24
 * @copyright   Catalyst IT
25
 * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26
 */
27
class secret_manager_test extends \advanced_testcase {
28
 
29
    /**
30
     * Tests create factor's secret
31
     *
32
     * @covers ::create_secret
33
     */
11 efrain 34
    public function test_create_secret(): void {
1 efrain 35
        global $DB;
36
 
37
        $this->resetAfterTest(true);
38
        $this->setUser($this->getDataGenerator()->create_user());
39
 
40
        // Test adding secret to DB.
41
        $secman = new \tool_mfa\local\secret_manager('mock');
42
 
43
        // Mutate the sessionid using reflection.
44
        $reflectedsessionid = new \ReflectionProperty($secman, 'sessionid');
45
        $reflectedsessionid->setValue($secman, 'fakesession');
46
 
47
        $sec1 = $secman->create_secret(1800, false);
48
        $count1 = $DB->count_records('tool_mfa_secrets', ['factor' => 'mock']);
49
        $record1 = $DB->get_record('tool_mfa_secrets', []);
50
        $this->assertEquals(1, $count1);
51
        $this->assertNotEquals('', $sec1);
52
        $this->assertTrue(empty($record1->sessionid));
53
        $sec2 = $secman->create_secret(1800, false);
54
        $count2 = $DB->count_records('tool_mfa_secrets', ['factor' => 'mock']);
55
        $this->assertEquals(1, $count2);
56
        $this->assertEquals('', $sec2);
57
        $DB->delete_records('tool_mfa_secrets', []);
58
 
59
        // Now adding secret to session.
60
        $sec1 = $secman->create_secret(1800, true);
61
        $count1 = $DB->count_records('tool_mfa_secrets', ['factor' => 'mock']);
62
        $record1 = $DB->get_record('tool_mfa_secrets', []);
63
        $this->assertEquals(1, $count1);
64
        $this->assertNotEquals('', $sec1);
65
        $this->assertEquals('fakesession', $record1->sessionid);
66
        $sec2 = $secman->create_secret(1800, true);
67
        $this->assertEquals('', $sec2);
68
        $DB->delete_records('tool_mfa_secrets', []);
69
 
70
        // Now test adding a forced code.
71
        $sec1 = $secman->create_secret(1800, false);
72
        $count1 = $DB->count_records('tool_mfa_secrets', ['factor' => 'mock']);
73
        $this->assertEquals(1, $count1);
74
        $this->assertNotEquals('', $sec1);
75
        $sec2 = $secman->create_secret(1800, false, 'code');
76
        $count2 = $DB->count_records('tool_mfa_secrets', ['factor' => 'mock']);
77
        $this->assertEquals(2, $count2);
78
        $this->assertEquals('code', $sec2);
79
        $DB->delete_records('tool_mfa_secrets', []);
80
    }
81
 
82
    /**
83
     * Tests add factor's secret to database
84
     *
85
     * @covers ::get_record
86
     * @covers ::delete_records
87
     */
11 efrain 88
    public function test_add_secret_to_db(): void {
1 efrain 89
        global $DB, $USER;
90
 
91
        $this->resetAfterTest(true);
92
        $secman = new \tool_mfa\local\secret_manager('mock');
93
        $this->setUser($this->getDataGenerator()->create_user());
94
        $sid = 'fakeid';
95
 
96
        // Let's make stuff public using reflection.
97
        $reflectedscanner = new \ReflectionClass($secman);
98
        $reflectedmethod = $reflectedscanner->getMethod('add_secret_to_db');
99
 
100
        // Now add a secret and confirm it creates the correct record.
101
        $reflectedmethod->invoke($secman, 'code', 1800);
102
        $record = $DB->get_record('tool_mfa_secrets', []);
103
        $this->assertEquals('code', $record->secret);
104
        $this->assertEquals($USER->id, $record->userid);
105
        $this->assertEquals('mock', $record->factor);
106
        $this->assertGreaterThanOrEqual(time(), (int) $record->expiry);
107
        $this->assertEquals(0, $record->revoked);
108
        $DB->delete_records('tool_mfa_secrets', []);
109
 
110
        // Now add a sessionid and confirm it is added correctly.
111
        $reflectedmethod->invoke($secman, 'code', 1800, $sid);
112
        $record = $DB->get_record('tool_mfa_secrets', []);
113
        $this->assertEquals('code', $record->secret);
114
        $this->assertGreaterThanOrEqual(time(), (int) $record->expiry);
115
        $this->assertEquals(0, $record->revoked);
116
        $this->assertEquals($sid, $record->sessionid);
117
    }
118
 
119
    /**
120
     * Tests validating factor's secret
121
     *
122
     * @covers ::validate_secret
123
     * @covers ::create_secret
124
     */
11 efrain 125
    public function test_validate_secret(): void {
1 efrain 126
        global $DB;
127
 
128
        // Test adding a code and getting it returned, then validated.
129
        $this->resetAfterTest(true);
130
        $this->setUser($this->getDataGenerator()->create_user());
131
        $secman = new \tool_mfa\local\secret_manager('mock');
132
 
133
        $secret = $secman->create_secret(1800, false);
134
        $this->assertEquals(\tool_mfa\local\secret_manager::VALID, $secman->validate_secret($secret));
135
        $DB->delete_records('tool_mfa_secrets', []);
136
 
137
        // Test a manual forced code.
138
        $secret = $secman->create_secret(1800, false, 'code');
139
        $this->assertEquals(\tool_mfa\local\secret_manager::VALID, $secman->validate_secret($secret));
140
        $this->assertEquals('code', $secret);
141
        $DB->delete_records('tool_mfa_secrets', []);
142
 
143
        // Test bad codes.
144
        $secret = $secman->create_secret(1800, false);
145
        $this->assertEquals(\tool_mfa\local\secret_manager::NONVALID, $secman->validate_secret('nonvalid'));
146
        $DB->delete_records('tool_mfa_secrets', []);
147
 
148
        // Test validate when no secrets present.
149
        $this->assertEquals(\tool_mfa\local\secret_manager::NONVALID, $secman->validate_secret('nonvalid'));
150
 
151
        // Test revoked secrets.
152
        $secret = $secman->create_secret(1800, false);
153
        $DB->set_field('tool_mfa_secrets', 'revoked', 1, []);
154
        $this->assertEquals(\tool_mfa\local\secret_manager::REVOKED, $secman->validate_secret($secret));
155
        $DB->delete_records('tool_mfa_secrets', []);
156
 
157
        // Test expired secrets.
158
        $secret = $secman->create_secret(-1, false);
159
        $this->assertEquals(\tool_mfa\local\secret_manager::NONVALID, $secman->validate_secret($secret));
160
        $DB->delete_records('tool_mfa_secrets', []);
161
 
162
        // Session locked code from the same session id.
163
        // Mutate the sessionid using reflection.
164
        $reflectedsessionid = new \ReflectionProperty($secman, 'sessionid');
165
        $reflectedsessionid->setValue($secman, 'fakesession');
166
 
167
        $secret = $secman->create_secret(1800, true);
168
        $this->assertEquals(\tool_mfa\local\secret_manager::VALID, $secman->validate_secret($secret));
169
        $DB->delete_records('tool_mfa_secrets', []);
170
 
171
        // Now test a session locked code from a different sessionid.
172
        $secret = $secman->create_secret(1800, true);
173
        $reflectedsessionid->setValue($secman, 'diffsession');
174
        $this->assertEquals(\tool_mfa\local\secret_manager::NONVALID, $secman->validate_secret($secret));
175
        $DB->delete_records('tool_mfa_secrets', []);
176
    }
177
 
178
    /**
179
     * Tests revoking factor's secret
180
     *
181
     * @covers ::validate_secret
182
     * @covers ::create_secret
183
     * @covers ::revoke_secret
184
     */
11 efrain 185
    public function test_revoke_secret(): void {
1 efrain 186
        global $DB, $SESSION;
187
 
188
        $this->resetAfterTest(true);
189
        $secman = new \tool_mfa\local\secret_manager('mock');
190
        $this->setUser($this->getDataGenerator()->create_user());
191
 
192
        // Session secrets.
193
        $secret = $secman->create_secret(1800, true);
194
        $secman->revoke_secret($secret);
195
        $this->assertEquals(\tool_mfa\local\secret_manager::REVOKED, $secman->validate_secret($secret));
196
        unset($SESSION->tool_mfa_secrets_mock);
197
 
198
        // DB secrets.
199
        $secret = $secman->create_secret(1800, false);
200
        $secman->revoke_secret($secret);
201
        $this->assertEquals(\tool_mfa\local\secret_manager::REVOKED, $secman->validate_secret($secret));
202
        $DB->delete_records('tool_mfa_secrets', []);
203
 
204
        // Revoke a non-valid secret.
205
        $secret = $secman->create_secret(1800, false);
206
        $secman->revoke_secret('nonvalid');
207
        $this->assertEquals(\tool_mfa\local\secret_manager::NONVALID, $secman->validate_secret('nonvalid'));
208
    }
209
 
210
    /**
211
     * Tests checking if factor has an active secret
212
     *
213
     * @covers ::create_secret
214
     * @covers ::revoke_secret
215
     */
11 efrain 216
    public function test_has_active_secret(): void {
1 efrain 217
        global $DB;
218
 
219
        $this->resetAfterTest(true);
220
        $secman = new \tool_mfa\local\secret_manager('mock');
221
        $this->setUser($this->getDataGenerator()->create_user());
222
 
223
        // Let's make stuff public using reflection.
224
        $reflectedscanner = new \ReflectionClass($secman);
225
 
226
        $reflectedmethod = $reflectedscanner->getMethod('has_active_secret');
227
 
228
        // DB secrets.
229
        $this->assertFalse($reflectedmethod->invoke($secman));
230
        $secman->create_secret(1800, false);
231
        $this->assertTrue($reflectedmethod->invoke($secman));
232
        $DB->delete_records('tool_mfa_secrets', []);
233
        $secman->create_secret(-1, false);
234
        $this->assertFalse($reflectedmethod->invoke($secman));
235
        $DB->delete_records('tool_mfa_secrets', []);
236
        $secret = $secman->create_secret(1800, false);
237
        $secman->revoke_secret($secret);
238
        $this->assertFalse($reflectedmethod->invoke($secman));
239
 
240
        // Now check a secret with session involvement.
241
        // Mutate the sessionid using reflection.
242
        $reflectedsessionid = new \ReflectionProperty($secman, 'sessionid');
243
        $reflectedsessionid->setValue($secman, 'fakesession');
244
 
245
        $this->assertFalse($reflectedmethod->invoke($secman, true));
246
        $secman->create_secret(1800, true);
247
        $this->assertTrue($reflectedmethod->invoke($secman, true));
248
        $DB->delete_records('tool_mfa_secrets', []);
249
        $secman->create_secret(-1, true);
250
        $this->assertFalse($reflectedmethod->invoke($secman, true));
251
        $DB->delete_records('tool_mfa_secrets', []);
252
        $secret = $secman->create_secret(1800, true);
253
        $secman->revoke_secret($secret);
254
        $this->assertFalse($reflectedmethod->invoke($secman, true));
255
        $DB->delete_records('tool_mfa_secrets', []);
256
        $secret = $secman->create_secret(1800, true);
257
         $reflectedsessionid->setValue($secman, 'diffsession');
258
        $this->assertFalse($reflectedmethod->invoke($secman, true));
259
    }
260
 
261
    /**
262
     * Tests with cleanup temporal secrets
263
     *
264
     * @covers ::cleanup_temp_secrets
265
     */
266
    public function test_cleanup_temp_secrets(): void {
267
        global $DB;
268
 
269
        $this->resetAfterTest(true);
270
        $secman = new \tool_mfa\local\secret_manager('mock');
271
        $user = $this->getDataGenerator()->create_user();
272
        $this->setUser($user);
273
 
274
        // Create secrets.
275
        $secman->create_secret(1800, true);
276
        $secman->create_secret(1800, true);
277
 
278
        // Cleanup current user secrets.
279
        $secman->cleanup_temp_secrets();
280
 
281
        // Check there are no secrets of the current user.
282
        $records = $DB->get_records('tool_mfa_secrets', ['userid' => $user->id]);
283
        $this->assertEmpty($records);
284
    }
285
}