Completed
Push — master ( 4e92df...3d6026 )
by Robbie
12s
created

removeRegisteredMethod()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 45
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 26
dl 0
loc 45
rs 8.8817
c 0
b 0
f 0
cc 6
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 $ignore_menuitem = true;
0 ignored issues
show
introduced by
The private property $ignore_menuitem is not used, and could be removed.
Loading history...
33
34
    private static $url_handlers = [
0 ignored issues
show
introduced by
The private property $url_handlers is not used, and could be removed.
Loading history...
35
        'GET register/$Method' => 'startRegistration',
36
        'POST register/$Method' => 'finishRegistration',
37
        'GET remove/$Method' => 'removeRegisteredMethod',
38
    ];
39
40
    private static $allowed_actions = [
0 ignored issues
show
introduced by
The private property $allowed_actions is not used, and could be removed.
Loading history...
41
        'startRegistration',
42
        'finishRegistration',
43
        'removeRegisteredMethod',
44
    ];
45
46
    /**
47
     * Start a registration for a method on the currently logged in user
48
     *
49
     * @param HTTPRequest $request
50
     * @return HTTPResponse
51
     */
52
    public function startRegistration(HTTPRequest $request): HTTPResponse
53
    {
54
        // Create a fresh store from the current logged in user
55
        $member = Security::getCurrentUser();
56
        $store = $this->createStore($member);
57
58
        // Get the specified method
59
        $method = MethodRegistry::singleton()->getMethodByURLSegment($request->param('Method'));
60
61
        if (!$method) {
62
            return $this->jsonResponse(
63
                ['errors' => [_t(__CLASS__ . '.INVALID_METHOD', 'No such method is available')]],
64
                400
65
            );
66
        }
67
68
        $response = $this->createStartRegistrationResponse($store, $method, true);
69
        $store->save($request);
70
71
        return $response;
72
    }
73
74
    /**
75
     * Complete a registration for a method for the currently logged in user
76
     *
77
     * @param HTTPRequest $request
78
     * @return HTTPResponse
79
     */
80
    public function finishRegistration(HTTPRequest $request): HTTPResponse
81
    {
82
        $store = $this->getStore();
83
84
        if (!$store) {
0 ignored issues
show
introduced by
$store is of type SilverStripe\MFA\Store\StoreInterface, thus it always evaluated to true.
Loading history...
85
            return $this->jsonResponse(
86
                ['errors' => [_t(__CLASS__ . '.INVALID_SESSION', 'Invalid session. Please try again')]],
87
                400
88
            );
89
        }
90
91
        $method = MethodRegistry::singleton()->getMethodByURLSegment($request->param('Method'));
92
93
        if (!$method) {
94
            return $this->jsonResponse(
95
                ['errors' => [_t(__CLASS__ . '.INVALID_METHOD', 'No such method is available')]],
96
                400
97
            );
98
        }
99
100
        $result = $this->completeRegistrationRequest($store, $method, $request);
101
102
        if (!$result->isSuccessful()) {
103
            return $this->jsonResponse(['errors' => [$result->getMessage()]], 400);
104
        }
105
106
        $store::clear($request);
107
108
        return $this->jsonResponse([
109
            'success' => true,
110
            'method' => $result->getContext()['registeredMethod'] ?? null,
111
        ], 201);
112
    }
113
114
    /**
115
     * Remove the specified method from the currently logged in user
116
     *
117
     * @param HTTPRequest $request
118
     * @return HTTPResponse
119
     */
120
    public function removeRegisteredMethod(HTTPRequest $request): HTTPResponse
121
    {
122
        // Ensure CSRF protection
123
        if (!SecurityToken::inst()->checkRequest($request)) {
124
            return $this->jsonResponse(
125
                ['errors' => [_t(__CLASS__ . '.CSRF_FAILURE', 'Request timed out, please try again')]],
126
                400
127
            );
128
        }
129
130
        // Get the specified method
131
        $methodRegistry = MethodRegistry::singleton();
132
        $specifiedMethod = $request->param('Method');
133
134
        if (!$specifiedMethod || !($method = $methodRegistry->getMethodByURLSegment($specifiedMethod))) {
135
            return $this->jsonResponse(
136
                ['errors' => [_t(__CLASS__ . '.INVALID_METHOD', 'No such method is available')]],
137
                400
138
            );
139
        }
140
141
        // Remove the method from the user
142
        $member = Security::getCurrentUser();
143
        $registeredMethodManager = RegisteredMethodManager::singleton();
144
        $result = $registeredMethodManager->deleteFromMember($member, $method);
145
146
        if (!$result) {
147
            return $this->jsonResponse(
148
                ['errors' => [_t(
149
                    __CLASS__ . '.COULD_NOT_DELETE',
150
                    'Could not delete the specified method from the user'
151
                )]],
152
                400
153
            );
154
        }
155
156
        $backupMethod = $methodRegistry->getBackupMethod();
157
        return $this->jsonResponse([
158
            'success' => true,
159
            'availableMethod' => Injector::inst()->create(AvailableMethodDetailsInterface::class, $method),
160
            // Indicate if the user has a backup method registered to keep the UI up to date
161
            // Deleting methods may remove the backup method if there are no other methods remaining.
162
            'hasBackupMethod' => $backupMethod && $registeredMethodManager->getFromMember(
163
                $member,
164
                $backupMethod
165
            ),
166
        ]);
167
    }
168
169
    /**
170
     * Respond with the given array as a JSON response
171
     *
172
     * @param array $response
173
     * @param int $code The HTTP response code to set on the response
174
     * @return HTTPResponse
175
     */
176
    protected function jsonResponse(array $response, int $code = 200): HTTPResponse
177
    {
178
        return HTTPResponse::create(json_encode($response))
179
            ->addHeader('Content-Type', 'application/json')
180
            ->setStatusCode($code);
181
    }
182
}
183