Passed
Pull Request — master (#128)
by Guy
02:08
created

AdminRegistrationController::finishRegistration()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 32
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 18
dl 0
loc 32
rs 9.6666
c 0
b 0
f 0
cc 4
nc 4
nop 1
1
<?php declare(strict_types=1);
2
3
namespace SilverStripe\MFA\Controller;
4
5
use SilverStripe\Admin\LeftAndMain;
6
use SilverStripe\Control\HTTPRequest;
7
use SilverStripe\Control\HTTPResponse;
8
use SilverStripe\Core\Injector\Injector;
9
use SilverStripe\MFA\Extension\MemberExtension;
10
use SilverStripe\MFA\RequestHandler\BaseHandlerTrait;
11
use SilverStripe\MFA\RequestHandler\RegistrationHandlerTrait;
12
use SilverStripe\MFA\Service\MethodRegistry;
13
use SilverStripe\MFA\Service\RegisteredMethodManager;
14
use SilverStripe\MFA\Service\SchemaGenerator;
15
use SilverStripe\MFA\State\AvailableMethodDetailsInterface;
16
use SilverStripe\MFA\State\RegisteredMethodDetailsInterface;
17
use SilverStripe\Security\Member;
18
use SilverStripe\Security\Security;
19
use SilverStripe\Security\SecurityToken;
20
21
/**
22
 * This controller handles actions that a user may perform on MFA methods registered on their own account while logged
23
 * in. This includes deleting methods, registering new methods and replacing (re-registering) existing methods.
24
 */
25
class AdminRegistrationController extends LeftAndMain
26
{
27
    use RegistrationHandlerTrait;
28
    use BaseHandlerTrait;
29
30
    private static $url_segment = 'mfa';
0 ignored issues
show
introduced by
The private property $url_segment is not used, and could be removed.
Loading history...
31
32
    private static $url_handlers = [
0 ignored issues
show
introduced by
The private property $url_handlers is not used, and could be removed.
Loading history...
33
        'GET register/$Method' => 'startRegistration',
34
        'POST register/$Method' => 'finishRegistration',
35
        'GET remove/$Method' => 'removeRegisteredMethod',
36
    ];
37
38
    private static $allowed_actions = [
0 ignored issues
show
introduced by
The private property $allowed_actions is not used, and could be removed.
Loading history...
39
        'startRegistration',
40
        'finishRegistration',
41
        'removeRegisteredMethod',
42
    ];
43
44
    /**
45
     * Start a registration for a method on the currently logged in user
46
     *
47
     * @param HTTPRequest $request
48
     * @return HTTPResponse
49
     */
50
    public function startRegistration(HTTPRequest $request): HTTPResponse
51
    {
52
        // Create a fresh store from the current logged in user
53
        $member = Security::getCurrentUser();
54
        $store = $this->createStore($member);
55
56
        // Get the specified method
57
        $method = MethodRegistry::singleton()->getMethodByURLSegment($request->param('Method'));
58
59
        if (!$method) {
60
            return $this->jsonResponse(
61
                ['errors' => [_t(__CLASS__ . '.INVALID_METHOD', 'No such method is available')]],
62
                400
63
            );
64
        }
65
66
        $response = $this->createStartRegistrationResponse($store, $method, true);
67
        $store->save($request);
68
69
        return $response;
70
    }
71
72
    /**
73
     * Complete a registration for a method for the currently logged in user
74
     *
75
     * @param HTTPRequest $request
76
     * @return HTTPResponse
77
     */
78
    public function finishRegistration(HTTPRequest $request): HTTPResponse
79
    {
80
        $store = $this->getStore();
81
82
        if (!$store) {
0 ignored issues
show
introduced by
$store is of type SilverStripe\MFA\Store\StoreInterface, thus it always evaluated to true.
Loading history...
83
            return $this->jsonResponse(
84
                ['errors' => [_t(__CLASS__ . '.INVALID_SESSION', 'Invalid session. Please try again')]],
85
                400
86
            );
87
        }
88
89
        $method = MethodRegistry::singleton()->getMethodByURLSegment($request->param('Method'));
90
91
        if (!$method) {
92
            return $this->jsonResponse(
93
                ['errors' => [_t(__CLASS__ . '.INVALID_METHOD', 'No such method is available')]],
94
                400
95
            );
96
        }
97
98
        $result = $this->completeRegistrationRequest($store, $method, $request);
99
100
        if (!$result->isSuccessful()) {
101
            return $this->jsonResponse(['errors' => [$result->getMessage()]], 400);
102
        }
103
104
        $store::clear($request);
105
106
        return $this->jsonResponse([
107
            'success' => true,
108
            'method' => $result->getContext()['registeredMethod'] ?? null,
109
        ], 201);
110
    }
111
112
    /**
113
     * Remove the specified method from the currently logged in user
114
     *
115
     * @param HTTPRequest $request
116
     * @return HTTPResponse
117
     */
118
    public function removeRegisteredMethod(HTTPRequest $request): HTTPResponse
119
    {
120
        // Ensure CSRF protection
121
        if (!SecurityToken::inst()->checkRequest($request)) {
122
            return $this->jsonResponse(
123
                ['errors' => [_t(__CLASS__ . '.CSRF_FAILURE', 'Request timed out, please try again')]],
124
                400
125
            );
126
        }
127
128
        // Get the specified method
129
        $methodRegistry = MethodRegistry::singleton();
130
        $method = $methodRegistry->getMethodByURLSegment($request->param('Method'));
131
132
        if (!$method) {
133
            return $this->jsonResponse(
134
                ['errors' => [_t(__CLASS__ . '.INVALID_METHOD', 'No such method is available')]],
135
                400
136
            );
137
        }
138
139
        // Remove the method from the user
140
        $member = Security::getCurrentUser();
141
        $registeredMethodManager = RegisteredMethodManager::singleton();
142
        $result = $registeredMethodManager->deleteFromMember($member, $method);
143
144
        if (!$result) {
145
            return $this->jsonResponse(
146
                ['errors' => [_t(
147
                    __CLASS__ . '.COULD_NOT_DELETE',
148
                    'Could not delete the specified method from the user'
149
                )]],
150
                400
151
            );
152
        }
153
154
        return $this->jsonResponse([
155
            'success' => true,
156
            'availableMethod' => Injector::inst()->create(AvailableMethodDetailsInterface::class, $method),
157
            // Indicate if the user has a backup method registered to keep the UI up to date
158
            'hasBackupMethod' => (bool) $registeredMethodManager->getFromMember(
159
                $member,
160
                $methodRegistry->getBackupMethod()
161
            ),
162
        ]);
163
    }
164
165
    /**
166
     * Respond with the given array as a JSON response
167
     *
168
     * @param array $response
169
     * @param int $code The HTTP response code to set on the response
170
     * @return HTTPResponse
171
     */
172
    protected function jsonResponse(array $response, int $code = 200): HTTPResponse
173
    {
174
        return HTTPResponse::create(json_encode($response))
175
            ->addHeader('Content-Type', 'application/json')
176
            ->setStatusCode($code);
177
    }
178
}
179