Completed
Push — master ( 200aa9...ba6726 )
by Robbie
28s queued 11s
created

testAnyUserCanView()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\MFA\Tests\Controller;
4
5
use PHPUnit_Framework_MockObject_MockObject;
6
use SilverStripe\Admin\AdminRootController;
7
use SilverStripe\Control\Controller;
8
use SilverStripe\Control\HTTPRequest;
9
use SilverStripe\Core\Config\Config;
10
use SilverStripe\Core\Injector\Injector;
11
use SilverStripe\Dev\FunctionalTest;
12
use SilverStripe\MFA\Controller\AdminRegistrationController;
13
use SilverStripe\MFA\Model\RegisteredMethod;
14
use SilverStripe\MFA\Service\MethodRegistry;
15
use SilverStripe\MFA\Service\RegisteredMethodManager;
16
use SilverStripe\MFA\State\AvailableMethodDetails;
17
use SilverStripe\MFA\Store\SessionStore;
18
use SilverStripe\MFA\Tests\Stub\BasicMath\Method as BasicMathMethod;
19
use SilverStripe\Security\Member;
20
use SilverStripe\Security\SecurityToken;
21
use SilverStripe\SecurityExtensions\Service\SudoModeServiceInterface;
22
23
class AdminRegistrationControllerTest extends FunctionalTest
24
{
25
    protected static $fixture_file = 'AdminRegistrationControllerTest.yml';
26
27
    protected function setUp()
28
    {
29
        parent::setUp();
30
31
        MethodRegistry::config()->set('methods', [
32
            BasicMathMethod::class,
33
        ]);
34
35
        /** @var SudoModeServiceInterface&PHPUnit_Framework_MockObject_MockObject $sudoModeService */
36
        $sudoModeService = $this->createMock(SudoModeServiceInterface::class);
37
        $sudoModeService->expects($this->any())->method('check')->willReturn(true);
38
        Injector::inst()->registerService($sudoModeService, SudoModeServiceInterface::class);
39
    }
40
41
    public function testStartRegistrationAssertsValidMethod()
42
    {
43
        $this->logInAs($this->objFromFixture(Member::class, 'sally_smith'));
44
45
        $result = $this->get(Controller::join_links(AdminRootController::admin_url(), 'mfa', 'register/foo'));
46
47
        $this->assertSame(400, $result->getStatusCode());
48
        $this->assertContains('No such method is available', $result->getBody());
49
    }
50
51
    public function testStartRegistrationEnforcesSudoMode()
52
    {
53
        $this->logInAs($this->objFromFixture(Member::class, 'sally_smith'));
54
55
        /** @var SudoModeServiceInterface&PHPUnit_Framework_MockObject_MockObject $sudoModeService */
56
        $sudoModeService = $this->createMock(SudoModeServiceInterface::class);
57
        $sudoModeService->expects($this->any())->method('check')->willReturn(false);
58
        Injector::inst()->registerService($sudoModeService, SudoModeServiceInterface::class);
59
60
        $result = $this->get(Controller::join_links(AdminRootController::admin_url(), 'mfa', 'register/foo'));
61
62
        $this->assertSame(400, $result->getStatusCode());
63
        $this->assertContains('Invalid session. Please refresh and try again.', (string) $result->getBody());
64
    }
65
66
    public function testStartRegistrationReturns200Response()
67
    {
68
        $this->logInAs($this->objFromFixture(Member::class, 'sally_smith'));
69
        $method = new BasicMathMethod();
70
71
        $result = $this->get(
72
            Controller::join_links(
73
                AdminRootController::admin_url(),
74
                'mfa',
75
                'register',
76
                $method->getURLSegment()
77
            )
78
        );
79
80
        $this->assertSame(200, $result->getStatusCode());
81
    }
82
83
    public function testFinishRegistrationGracefullyHandlesInvalidSessions()
84
    {
85
        $this->logInAs($this->objFromFixture(Member::class, 'sally_smith'));
86
        $method = new BasicMathMethod();
87
88
        $result = $this->post(
89
            Controller::join_links(
90
                AdminRootController::admin_url(),
91
                'mfa',
92
                'register',
93
                $method->getURLSegment()
94
            ),
95
            ['dummy' => 'data']
96
        );
97
98
        $this->assertSame(400, $result->getStatusCode());
99
        $this->assertContains('Invalid session', $result->getBody());
100
    }
101
102
    public function testFinishRegistrationAssertsValidMethod()
103
    {
104
        /** @var Member $member */
105
        $member = $this->objFromFixture(Member::class, 'sally_smith');
106
        $this->logInAs($member);
107
        $method = new BasicMathMethod();
108
109
        $store = new SessionStore($member);
110
        $store->setMethod($method->getURLSegment());
111
        $this->session()->set(SessionStore::SESSION_KEY, $store);
112
113
        $result = $this->post(
114
            Controller::join_links(
115
                AdminRootController::admin_url(),
116
                'mfa',
117
                'register',
118
                'foo'
119
            ),
120
            ['dummy' => 'data']
121
        );
122
123
        $this->assertSame(400, $result->getStatusCode());
124
        $this->assertContains('No such method is available', $result->getBody());
125
    }
126
127
    public function testFinishRegistrationCompletesWhenValid()
128
    {
129
        /** @var Member $member */
130
        $member = $this->objFromFixture(Member::class, 'sally_smith');
131
        $this->logInAs($member);
132
        $method = new BasicMathMethod();
133
134
        $store = new SessionStore($member);
135
        $store->setMethod($method->getURLSegment());
136
        $this->session()->set(SessionStore::SESSION_KEY, $store);
137
138
        $result = $this->post(
139
            Controller::join_links(
140
                AdminRootController::admin_url(),
141
                'mfa',
142
                'register',
143
                $method->getURLSegment()
144
            ),
145
            ['dummy' => 'data'],
146
            null,
147
            $this->session(),
148
            json_encode(['number' => 7])
149
        );
150
151
        $this->assertSame(201, $result->getStatusCode());
152
    }
153
154
    public function testRemoveRegistrationChecksCSRF()
155
    {
156
        SecurityToken::enable();
157
158
        $controller = new AdminRegistrationController();
159
        $request = new HTTPRequest('GET', '');
160
        $response = $controller->removeRegisteredMethod($request);
161
162
        $this->assertSame(400, $response->getStatusCode());
163
        $this->assertContains('Request timed out', $response->getBody());
164
165
        $token = SecurityToken::inst();
166
        $request = new HTTPRequest('GET', '', [$token->getName() => $token->getValue()]);
167
168
        $response = $controller->removeRegisteredMethod($request);
169
170
        $this->assertNotContains('Request timed out', $response->getBody());
171
    }
172
173
    public function testRemoveRegistrationRequiresMethod()
174
    {
175
        $this->logInWithPermission();
176
177
        // Prep a mock for deleting methods
178
        $registeredMethodManager = $this->scaffoldRegisteredMethodManagerMock();
179
180
        $controller = new AdminRegistrationController();
181
182
        // Method not even provided
183
        $request = new HTTPRequest('GET', '');
184
        $response = $controller->removeRegisteredMethod($request);
185
186
        $this->assertSame(400, $response->getStatusCode());
187
        $this->assertContains('No such method is available', $response->getBody());
188
189
        // Method provided but non-existing
190
        $request = new HTTPRequest('GET', '');
191
        $request->setRouteParams(['Method' => 'fake123']);
192
        $response = $controller->removeRegisteredMethod($request);
193
194
        $this->assertSame(400, $response->getStatusCode());
195
        $this->assertContains('No such method is available', $response->getBody());
196
197
        // Existing method
198
        $request = new HTTPRequest('GET', '');
199
        $basicMathMethod = new BasicMathMethod();
200
        $request->setRouteParams(['Method' => $basicMathMethod->getURLSegment()]);
201
        $registeredMethodManager->expects($this->once())->method('deleteFromMember')->willReturn(true);
202
        $response = $controller->removeRegisteredMethod($request);
203
204
        $this->assertSame(200, $response->getStatusCode());
205
        $this->assertTrue(json_decode($response->getBody())->success);
206
    }
207
208
    public function testRemoveRegistrationSuccessIsReflectedInResponse()
209
    {
210
        $this->logInWithPermission();
211
212
        // Prep a mock for deleting methods
213
        $registeredMethodManager = $this->scaffoldRegisteredMethodManagerMock();
214
215
        $controller = new AdminRegistrationController();
216
217
        $request = new HTTPRequest('GET', '');
218
        $basicMathMethod = new BasicMathMethod();
219
        $request->setRouteParams(['Method' => $basicMathMethod->getURLSegment()]);
220
221
        $registeredMethodManager->expects($this->exactly(2))->method('deleteFromMember')->willReturn(true, false);
222
223
        $response = $controller->removeRegisteredMethod($request);
224
225
        $this->assertSame(200, $response->getStatusCode());
226
        $this->assertTrue(json_decode($response->getBody())->success);
227
228
        $response = $controller->removeRegisteredMethod($request);
229
230
        $this->assertSame(400, $response->getStatusCode());
231
        $this->assertContains('Could not delete the specified method from the user', $response->getBody());
232
    }
233
234
    public function testRemoveRegistrationSuccessResponseIncludesTheNowAvailableMethod()
235
    {
236
        $this->logInWithPermission();
237
238
        // Prep a mock for deleting methods
239
        $registeredMethodManager = $this->scaffoldRegisteredMethodManagerMock();
240
241
        $controller = new AdminRegistrationController();
242
243
        $request = new HTTPRequest('GET', '');
244
        $basicMathMethod = new BasicMathMethod();
245
        $request->setRouteParams(['Method' => $basicMathMethod->getURLSegment()]);
246
        $registeredMethodManager->expects($this->once())->method('deleteFromMember')->willReturn(true);
247
248
        $expectedAvailableMethod = new AvailableMethodDetails($basicMathMethod);
249
250
        $response = $controller->removeRegisteredMethod($request);
251
252
        $this->assertSame(
253
            $expectedAvailableMethod->jsonSerialize(),
254
            json_decode($response->getBody(), true)['availableMethod']
255
        );
256
    }
257
258
    public function testRemoveRegistrationSuccessIndicatesIfTheBackupMethodIsRegistered()
259
    {
260
        $this->logInWithPermission();
261
262
        // Prep a mock for deleting methods
263
        $registeredMethodManager = $this->scaffoldRegisteredMethodManagerMock();
264
265
        $controller = new AdminRegistrationController();
266
267
        $request = new HTTPRequest('GET', '');
268
        $basicMathMethod = new BasicMathMethod();
269
        $request->setRouteParams(['Method' => $basicMathMethod->getURLSegment()]);
270
        $registeredMethodManager->expects($this->any())->method('deleteFromMember')->willReturn(true);
271
272
        // Test when there's no backup method registered
273
        Config::modify()->set(MethodRegistry::class, 'default_backup_method', null);
274
        $response = $controller->removeRegisteredMethod($request);
275
        $this->assertFalse(json_decode($response->getBody())->hasBackupMethod);
276
277
        // Make "basic math" the backup method as it's the only available method
278
        Config::modify()->set(MethodRegistry::class, 'default_backup_method', BasicMathMethod::class);
279
280
        // Mock checking for the registered backup method when it's not registered (first) and then when it is (second)
281
        $registeredMethodManager
282
            ->expects($this->exactly(2))
283
            ->method('getFromMember')
284
            ->willReturn(null, new RegisteredMethod());
285
286
        $response = $controller->removeRegisteredMethod($request);
287
        $this->assertFalse(json_decode($response->getBody())->hasBackupMethod);
288
        $response = $controller->removeRegisteredMethod($request);
289
        $this->assertTrue(json_decode($response->getBody())->hasBackupMethod);
290
    }
291
292
    protected function scaffoldRegisteredMethodManagerMock()
293
    {
294
        $mock = $this->createMock(RegisteredMethodManager::class);
295
        Injector::inst()->registerService($mock, RegisteredMethodManager::class);
296
297
        return $mock;
298
    }
299
300
    public function testEnforcesSudoMode()
301
    {
302
        $sudoModeService = $this->createMock(SudoModeServiceInterface::class);
303
        $sudoModeService->expects($this->any())->method('check')->willReturn(false);
304
        Injector::inst()->registerService($sudoModeService, SudoModeServiceInterface::class);
305
306
        /** @var Member $member */
307
        $member = $this->objFromFixture(Member::class, 'sally_smith');
308
        $this->logInAs($member);
309
        $method = new BasicMathMethod();
310
311
        $store = new SessionStore($member);
312
        $store->setMethod($method->getURLSegment());
313
        $this->session()->set(SessionStore::SESSION_KEY, $store);
314
315
        $result = $this->post(
316
            Controller::join_links(
317
                AdminRootController::admin_url(),
318
                'mfa',
319
                'register',
320
                $method->getURLSegment()
321
            ),
322
            ['dummy' => 'data'],
323
            null,
324
            $this->session(),
325
            json_encode(['number' => 7])
326
        );
327
328
        $this->assertSame(400, $result->getStatusCode());
329
        $this->assertContains('Invalid session', $result->getBody());
330
    }
331
332
    /**
333
     * This controller allows any logged in user to access it, since its methods have their own permission
334
     * check validation already.
335
     *
336
     * See: https://github.com/silverstripe/silverstripe-mfa/issues/171
337
     */
338
    public function testAnyUserCanView()
339
    {
340
        $this->assertFalse(AdminRegistrationController::getRequiredPermissions());
341
    }
342
}
343