Completed
Push — master ( cdb114...273f6a )
by Abdelrahman
02:18
created

TwoFactorSettingsController::disableTotp()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 9
nc 1
nop 1
dl 0
loc 16
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * NOTICE OF LICENSE
5
 *
6
 * Part of the Rinvex Fort Package.
7
 *
8
 * This source file is subject to The MIT License (MIT)
9
 * that is bundled with this package in the LICENSE file.
10
 *
11
 * Package: Rinvex Fort Package
12
 * License: The MIT License (MIT)
13
 * Link:    https://rinvex.com
14
 */
15
16
namespace Rinvex\Fort\Http\Controllers\Frontend;
17
18
use Carbon\Carbon;
19
use Illuminate\Support\MessageBag;
20
use Illuminate\Support\Facades\Auth;
21
use Illuminate\Support\ViewErrorBag;
22
use Rinvex\Fort\Services\TwoFactorTotpProvider;
23
use Rinvex\Fort\Contracts\UserRepositoryContract;
24
use Rinvex\Fort\Http\Requests\Frontend\TwoFactorTotp;
25
use Rinvex\Fort\Http\Requests\Frontend\TwoFactorPhone;
26
use Rinvex\Fort\Http\Controllers\AuthorizedController;
27
28
class TwoFactorSettingsController extends AuthorizedController
29
{
30
    /**
31
     * The user repository instance.
32
     *
33
     * @var \Rinvex\Fort\Contracts\UserRepositoryContract
34
     */
35
    protected $userRepository;
36
37
    /**
38
     * Create a new Two-Factor update controller instance.
39
     *
40
     * @param \Rinvex\Fort\Contracts\UserRepositoryContract $userRepository
41
     *
42
     * @return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
43
     */
44
    public function __construct(UserRepositoryContract $userRepository)
45
    {
46
        parent::__construct();
47
48
        $this->userRepository = $userRepository;
49
    }
50
51
    /**
52
     * Show the Two-Factor TOTP enable form.
53
     *
54
     * @param \Rinvex\Fort\Http\Requests\Frontend\TwoFactorTotp    $request
55
     * @param \Rinvex\Fort\Services\TwoFactorTotpProvider $totpProvider
56
     *
57
     * @return \Illuminate\Http\Response
0 ignored issues
show
Documentation introduced by
Should the return type not be \Illuminate\View\View|\I...\Contracts\View\Factory?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
58
     */
59
    public function enableTotp(TwoFactorTotp $request, TwoFactorTotpProvider $totpProvider)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
60
    {
61
        $currentUser = $this->currentUser();
62
        $settings    = $currentUser->getTwoFactor();
63
64
        if (array_get($settings, 'totp.enabled') && ! session()->get('rinvex.fort.alert.success') && ! session()->get('errors')) {
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 130 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
65
            $messageBag = new MessageBag([trans('rinvex.fort::frontend/messages.verification.twofactor.totp.already')]);
66
            $errors     = (new ViewErrorBag())->put('default', $messageBag);
67
        }
68
69
        if (! $secret = array_get($settings, 'totp.secret')) {
70
            array_set($settings, 'totp.enabled', false);
71
            array_set($settings, 'totp.secret', $secret = $totpProvider->generateSecretKey());
72
73
            $this->userRepository->update($currentUser, [
74
                'two_factor' => $settings,
75
            ]);
76
        }
77
78
        $qrCode = $totpProvider->getQRCodeInline(config('rinvex.fort.twofactor.issuer'), $currentUser->email, $secret);
0 ignored issues
show
Bug introduced by
Accessing email on the interface Rinvex\Fort\Contracts\AuthenticatableContract suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
79
80
        return view('rinvex.fort::frontend.user.twofactor', compact('secret', 'qrCode', 'settings', 'errors'));
81
    }
82
83
    /**
84
     * Process the Two-Factor TOTP enable form.
85
     *
86
     * @param \Rinvex\Fort\Http\Requests\Frontend\TwoFactorTotp    $request
87
     * @param \Rinvex\Fort\Services\TwoFactorTotpProvider $totpProvider
88
     *
89
     * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse
90
     */
91
    public function updateTotp(TwoFactorTotp $request, TwoFactorTotpProvider $totpProvider)
92
    {
93
        $currentUser = $this->currentUser();
94
        $settings    = $currentUser->getTwoFactor();
95
        $secret      = array_get($settings, 'totp.secret');
96
        $backup      = array_get($settings, 'totp.backup');
97
        $backupAt    = array_get($settings, 'totp.backup_at');
98
99
        if ($totpProvider->verifyKey($secret, $request->get('token'))) {
100
            array_set($settings, 'totp.enabled', true);
101
            array_set($settings, 'totp.secret', $secret);
102
            array_set($settings, 'totp.backup', $backup ?: $this->generateTotpBackups());
103
            array_set($settings, 'totp.backup_at', $backupAt ?: (new Carbon())->toDateTimeString());
104
105
            // Update Two-Factor settings
106
            $this->userRepository->update($currentUser, [
107
                'two_factor' => $settings,
108
            ]);
109
110
            return intend([
111
                'back' => true,
112
                'with' => ['rinvex.fort.alert.success' => trans('rinvex.fort::frontend/messages.verification.twofactor.totp.enabled')],
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 135 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
113
            ]);
114
        }
115
116
        return intend([
117
            'back'       => true,
118
            'withErrors' => ['token' => trans('rinvex.fort::frontend/messages.verification.twofactor.totp.invalid_token')],
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 123 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
119
        ]);
120
    }
121
122
    /**
123
     * Process the Two-Factor TOTP disable.
124
     *
125
     * @param \Rinvex\Fort\Http\Requests\Frontend\TwoFactorTotp $request
126
     *
127
     * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse
128
     */
129
    public function disableTotp(TwoFactorTotp $request)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
130
    {
131
        $currentUser = $this->currentUser();
132
        $settings    = $currentUser->getTwoFactor();
133
134
        array_set($settings, 'totp', []);
135
136
        $this->userRepository->update($currentUser, [
137
            'two_factor' => $settings,
138
        ]);
139
140
        return intend([
141
            'intended' => route('rinvex.fort.frontend.user.settings'),
142
            'with'     => ['rinvex.fort.alert.success' => trans('rinvex.fort::frontend/messages.verification.twofactor.totp.disabled')],
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 136 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
143
        ]);
144
    }
145
146
    /**
147
     * Process the Two-Factor Phone enable.
148
     *
149
     * @param \Rinvex\Fort\Http\Requests\Frontend\TwoFactorPhone $request
150
     *
151
     * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse
152
     */
153
    public function enablePhone(TwoFactorPhone $request)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
154
    {
155
        $currentUser = $this->currentUser();
156
157
        if (! $currentUser->phone || ! $currentUser->phone_verified) {
0 ignored issues
show
Bug introduced by
Accessing phone on the interface Rinvex\Fort\Contracts\AuthenticatableContract suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
Bug introduced by
Accessing phone_verified on the interface Rinvex\Fort\Contracts\AuthenticatableContract suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
158
            return intend([
159
                'intended'   => route('rinvex.fort.frontend.user.settings'),
160
                'withErrors' => ['phone' => trans('rinvex.fort::frontend/messages.account.phone_required')],
161
            ]);
162
        }
163
164
        $settings = $currentUser->getTwoFactor();
165
166
        array_set($settings, 'phone.enabled', true);
167
168
        $this->userRepository->update($currentUser, [
169
            'two_factor' => $settings,
170
        ]);
171
172
        return intend([
173
            'intended' => route('rinvex.fort.frontend.user.settings'),
174
            'with'     => ['rinvex.fort.alert.success' => trans('rinvex.fort::frontend/messages.verification.twofactor.phone.enabled')],
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 136 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
175
        ]);
176
    }
177
178
    /**
179
     * Process the Two-Factor Phone disable.
180
     *
181
     * @param \Rinvex\Fort\Http\Requests\Frontend\TwoFactorPhone $request
182
     *
183
     * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse
184
     */
185
    public function disablePhone(TwoFactorPhone $request)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
186
    {
187
        $currentUser = $this->currentUser();
188
        $settings    = $currentUser->getTwoFactor();
189
190
        array_set($settings, 'phone.enabled', false);
191
192
        $this->userRepository->update($currentUser, [
193
            'two_factor' => $settings,
194
        ]);
195
196
        return intend([
197
            'intended' => route('rinvex.fort.frontend.user.settings'),
198
            'with'     => ['rinvex.fort.alert.success' => trans('rinvex.fort::frontend/messages.verification.twofactor.phone.disabled')],
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 137 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
199
        ]);
200
    }
201
202
    /**
203
     * Process the Two-Factor OTP backup.
204
     *
205
     * @param \Rinvex\Fort\Http\Requests\Frontend\TwoFactorTotp $request
206
     *
207
     * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse
208
     */
209
    public function backupTotp(TwoFactorTotp $request)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
210
    {
211
        $currentUser = $this->currentUser();
212
        $settings    = $currentUser->getTwoFactor();
213
214
        if (! array_get($settings, 'totp.enabled')) {
215
            return intend([
216
                'intended'   => route('rinvex.fort.frontend.user.settings'),
217
                'withErrors' => ['rinvex.fort.verification.twofactor.totp.cant_backup' => trans('rinvex.fort::frontend/messages.verification.twofactor.totp.cant_backup')],
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 171 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
218
            ]);
219
        }
220
221
        array_set($settings, 'totp.backup', $this->generateTotpBackups());
222
        array_set($settings, 'totp.backup_at', (new Carbon())->toDateTimeString());
223
224
        $this->userRepository->update($currentUser, [
225
            'two_factor' => $settings,
226
        ]);
227
228
        return intend([
229
            'back' => true,
230
            'with' => ['rinvex.fort.alert.success' => trans('rinvex.fort::frontend/messages.verification.twofactor.totp.rebackup')],
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 132 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
231
        ]);
232
    }
233
234
    /**
235
     * Generate Two-Factor OTP backup codes.
236
     *
237
     * @return array
238
     */
239
    protected function generateTotpBackups()
240
    {
241
        $backup = [];
242
243
        for ($x = 0; $x <= 9; $x++) {
244
            $backup[] = str_pad(random_int(0, 9999999999), 10, 0, STR_PAD_BOTH);
245
        }
246
247
        return $backup;
248
    }
249
250
    /**
251
     * Get current user.
252
     *
253
     * @return \Rinvex\Fort\Contracts\AuthenticatableContract
254
     */
255
    protected function currentUser()
256
    {
257
        return Auth::guard($this->getGuard())->user();
258
    }
259
}
260