Passed
Pull Request — master (#156)
by Robbie
02:17
created

testEnforcesSudoMode()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 30
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 21
dl 0
loc 30
rs 9.584
c 0
b 0
f 0
cc 1
nc 1
nop 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;
0 ignored issues
show
Bug introduced by
The type SilverStripe\SecurityExt...udoModeServiceInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
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 testStartRegistrationReturns200Response()
52
    {
53
        $this->logInAs($this->objFromFixture(Member::class, 'sally_smith'));
54
        $method = new BasicMathMethod();
55
56
        $result = $this->get(
57
            Controller::join_links(
58
                AdminRootController::admin_url(),
59
                'mfa',
60
                'register',
61
                $method->getURLSegment()
62
            )
63
        );
64
65
        $this->assertSame(200, $result->getStatusCode());
66
    }
67
68
    public function testFinishRegistrationGracefullyHandlesInvalidSessions()
69
    {
70
        $this->logInAs($this->objFromFixture(Member::class, 'sally_smith'));
71
        $method = new BasicMathMethod();
72
73
        $result = $this->post(
74
            Controller::join_links(
75
                AdminRootController::admin_url(),
76
                'mfa',
77
                'register',
78
                $method->getURLSegment()
79
            ),
80
            ['dummy' => 'data']
81
        );
82
83
        $this->assertSame(400, $result->getStatusCode());
84
        $this->assertContains('Invalid session', $result->getBody());
85
    }
86
87
    public function testFinishRegistrationAssertsValidMethod()
88
    {
89
        /** @var Member $member */
90
        $member = $this->objFromFixture(Member::class, 'sally_smith');
91
        $this->logInAs($member);
92
        $method = new BasicMathMethod();
93
94
        $store = new SessionStore($member);
95
        $store->setMethod($method->getURLSegment());
96
        $this->session()->set(SessionStore::SESSION_KEY, $store);
97
98
        $result = $this->post(
99
            Controller::join_links(
100
                AdminRootController::admin_url(),
101
                'mfa',
102
                'register',
103
                'foo'
104
            ),
105
            ['dummy' => 'data']
106
        );
107
108
        $this->assertSame(400, $result->getStatusCode());
109
        $this->assertContains('No such method is available', $result->getBody());
110
    }
111
112
    public function testFinishRegistrationCompletesWhenValid()
113
    {
114
        /** @var Member $member */
115
        $member = $this->objFromFixture(Member::class, 'sally_smith');
116
        $this->logInAs($member);
117
        $method = new BasicMathMethod();
118
119
        $store = new SessionStore($member);
120
        $store->setMethod($method->getURLSegment());
121
        $this->session()->set(SessionStore::SESSION_KEY, $store);
122
123
        $result = $this->post(
124
            Controller::join_links(
125
                AdminRootController::admin_url(),
126
                'mfa',
127
                'register',
128
                $method->getURLSegment()
129
            ),
130
            ['dummy' => 'data'],
131
            null,
132
            $this->session(),
133
            json_encode(['number' => 7])
134
        );
135
136
        $this->assertSame(201, $result->getStatusCode());
137
    }
138
139
    public function testRemoveRegistrationChecksCSRF()
140
    {
141
        SecurityToken::enable();
142
143
        $controller = new AdminRegistrationController();
144
        $request = new HTTPRequest('GET', '');
145
        $response = $controller->removeRegisteredMethod($request);
146
147
        $this->assertSame(400, $response->getStatusCode());
148
        $this->assertContains('Request timed out', $response->getBody());
149
150
        $token = SecurityToken::inst();
151
        $request = new HTTPRequest('GET', '', [$token->getName() => $token->getValue()]);
152
153
        $response = $controller->removeRegisteredMethod($request);
154
155
        $this->assertNotContains('Request timed out', $response->getBody());
156
    }
157
158
    public function testRemoveRegistrationRequiresMethod()
159
    {
160
        $this->logInWithPermission();
161
162
        // Prep a mock for deleting methods
163
        $registeredMethodManager = $this->scaffoldRegisteredMethodManagerMock();
164
165
        $controller = new AdminRegistrationController();
166
167
        // Method not even provided
168
        $request = new HTTPRequest('GET', '');
169
        $response = $controller->removeRegisteredMethod($request);
170
171
        $this->assertSame(400, $response->getStatusCode());
172
        $this->assertContains('No such method is available', $response->getBody());
173
174
        // Method provided but non-existing
175
        $request = new HTTPRequest('GET', '');
176
        $request->setRouteParams(['Method' => 'fake123']);
177
        $response = $controller->removeRegisteredMethod($request);
178
179
        $this->assertSame(400, $response->getStatusCode());
180
        $this->assertContains('No such method is available', $response->getBody());
181
182
        // Existing method
183
        $request = new HTTPRequest('GET', '');
184
        $basicMathMethod = new BasicMathMethod();
185
        $request->setRouteParams(['Method' => $basicMathMethod->getURLSegment()]);
186
        $registeredMethodManager->expects($this->once())->method('deleteFromMember')->willReturn(true);
187
        $response = $controller->removeRegisteredMethod($request);
188
189
        $this->assertSame(200, $response->getStatusCode());
190
        $this->assertTrue(json_decode($response->getBody())->success);
191
    }
192
193
    public function testRemoveRegistrationSuccessIsReflectedInResponse()
194
    {
195
        $this->logInWithPermission();
196
197
        // Prep a mock for deleting methods
198
        $registeredMethodManager = $this->scaffoldRegisteredMethodManagerMock();
199
200
        $controller = new AdminRegistrationController();
201
202
        $request = new HTTPRequest('GET', '');
203
        $basicMathMethod = new BasicMathMethod();
204
        $request->setRouteParams(['Method' => $basicMathMethod->getURLSegment()]);
205
206
        $registeredMethodManager->expects($this->exactly(2))->method('deleteFromMember')->willReturn(true, false);
207
208
        $response = $controller->removeRegisteredMethod($request);
209
210
        $this->assertSame(200, $response->getStatusCode());
211
        $this->assertTrue(json_decode($response->getBody())->success);
212
213
        $response = $controller->removeRegisteredMethod($request);
214
215
        $this->assertSame(400, $response->getStatusCode());
216
        $this->assertContains('Could not delete the specified method from the user', $response->getBody());
217
    }
218
219
    public function testRemoveRegistrationSuccessResponseIncludesTheNowAvailableMethod()
220
    {
221
        $this->logInWithPermission();
222
223
        // Prep a mock for deleting methods
224
        $registeredMethodManager = $this->scaffoldRegisteredMethodManagerMock();
225
226
        $controller = new AdminRegistrationController();
227
228
        $request = new HTTPRequest('GET', '');
229
        $basicMathMethod = new BasicMathMethod();
230
        $request->setRouteParams(['Method' => $basicMathMethod->getURLSegment()]);
231
        $registeredMethodManager->expects($this->once())->method('deleteFromMember')->willReturn(true);
232
233
        $expectedAvailableMethod = new AvailableMethodDetails($basicMathMethod);
234
235
        $response = $controller->removeRegisteredMethod($request);
236
237
        $this->assertSame(
238
            $expectedAvailableMethod->jsonSerialize(),
239
            json_decode($response->getBody(), true)['availableMethod']
240
        );
241
    }
242
243
    public function testRemoveRegistrationSuccessIndicatesIfTheBackupMethodIsRegistered()
244
    {
245
        $this->logInWithPermission();
246
247
        // Prep a mock for deleting methods
248
        $registeredMethodManager = $this->scaffoldRegisteredMethodManagerMock();
249
250
        $controller = new AdminRegistrationController();
251
252
        $request = new HTTPRequest('GET', '');
253
        $basicMathMethod = new BasicMathMethod();
254
        $request->setRouteParams(['Method' => $basicMathMethod->getURLSegment()]);
255
        $registeredMethodManager->expects($this->any())->method('deleteFromMember')->willReturn(true);
256
257
        // Test when there's no backup method registered
258
        Config::modify()->set(MethodRegistry::class, 'default_backup_method', null);
259
        $response = $controller->removeRegisteredMethod($request);
260
        $this->assertFalse(json_decode($response->getBody())->hasBackupMethod);
261
262
        // Make "basic math" the backup method as it's the only available method
263
        Config::modify()->set(MethodRegistry::class, 'default_backup_method', BasicMathMethod::class);
264
265
        // Mock checking for the registered backup method when it's not registered (first) and then when it is (second)
266
        $registeredMethodManager
267
            ->expects($this->exactly(2))
268
            ->method('getFromMember')
269
            ->willReturn(null, new RegisteredMethod());
270
271
        $response = $controller->removeRegisteredMethod($request);
272
        $this->assertFalse(json_decode($response->getBody())->hasBackupMethod);
273
        $response = $controller->removeRegisteredMethod($request);
274
        $this->assertTrue(json_decode($response->getBody())->hasBackupMethod);
275
    }
276
277
    protected function scaffoldRegisteredMethodManagerMock()
278
    {
279
        $mock = $this->createMock(RegisteredMethodManager::class);
280
        Injector::inst()->registerService($mock, RegisteredMethodManager::class);
281
282
        return $mock;
283
    }
284
285
    public function testEnforcesSudoMode()
286
    {
287
        $sudoModeService = $this->createMock(SudoModeServiceInterface::class);
288
        $sudoModeService->expects($this->any())->method('check')->willReturn(false);
289
        Injector::inst()->registerService($sudoModeService, SudoModeServiceInterface::class);
290
291
        /** @var Member $member */
292
        $member = $this->objFromFixture(Member::class, 'sally_smith');
293
        $this->logInAs($member);
294
        $method = new BasicMathMethod();
295
296
        $store = new SessionStore($member);
297
        $store->setMethod($method->getURLSegment());
298
        $this->session()->set(SessionStore::SESSION_KEY, $store);
299
300
        $result = $this->post(
301
            Controller::join_links(
302
                AdminRootController::admin_url(),
303
                'mfa',
304
                'register',
305
                $method->getURLSegment()
306
            ),
307
            ['dummy' => 'data'],
308
            null,
309
            $this->session(),
310
            json_encode(['number' => 7])
311
        );
312
313
        $this->assertSame(400, $result->getStatusCode());
314
        $this->assertContains('Invalid session', $result->getBody());
315
    }
316
}
317