1 | <?php |
||||||
2 | /** |
||||||
3 | * TwoFactorController.php |
||||||
4 | * Copyright (c) 2019 [email protected] |
||||||
5 | * |
||||||
6 | * This file is part of Firefly III (https://github.com/firefly-iii). |
||||||
7 | * |
||||||
8 | * This program is free software: you can redistribute it and/or modify |
||||||
9 | * it under the terms of the GNU Affero General Public License as |
||||||
10 | * published by the Free Software Foundation, either version 3 of the |
||||||
11 | * License, or (at your option) any later version. |
||||||
12 | * |
||||||
13 | * This program is distributed in the hope that it will be useful, |
||||||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
16 | * GNU Affero General Public License for more details. |
||||||
17 | * |
||||||
18 | * You should have received a copy of the GNU Affero General Public License |
||||||
19 | * along with this program. If not, see <https://www.gnu.org/licenses/>. |
||||||
20 | */ |
||||||
21 | declare(strict_types=1); |
||||||
22 | |||||||
23 | namespace FireflyIII\Http\Controllers\Auth; |
||||||
24 | |||||||
25 | use FireflyIII\Http\Controllers\Controller; |
||||||
26 | use FireflyIII\User; |
||||||
27 | use Illuminate\Http\RedirectResponse; |
||||||
28 | use Illuminate\Http\Request; |
||||||
29 | use Illuminate\Routing\Redirector; |
||||||
30 | use PragmaRX\Google2FALaravel\Support\Authenticator; |
||||||
31 | use Preferences; |
||||||
32 | |||||||
33 | /** |
||||||
34 | * Class TwoFactorController. |
||||||
35 | */ |
||||||
36 | class TwoFactorController extends Controller |
||||||
37 | { |
||||||
38 | /** |
||||||
39 | * What to do if 2FA lost? |
||||||
40 | * |
||||||
41 | * @return mixed |
||||||
42 | */ |
||||||
43 | public function lostTwoFactor() |
||||||
44 | { |
||||||
45 | /** @var User $user */ |
||||||
46 | $user = auth()->user(); |
||||||
47 | $siteOwner = config('firefly.site_owner'); |
||||||
48 | $title = (string) trans('firefly.two_factor_forgot_title'); |
||||||
49 | |||||||
50 | return view('auth.lost-two-factor', compact('user', 'siteOwner', 'title')); |
||||||
51 | } |
||||||
52 | |||||||
53 | /** |
||||||
54 | * @param Request $request |
||||||
55 | * |
||||||
56 | * @return RedirectResponse|Redirector |
||||||
57 | */ |
||||||
58 | public function submitMFA(Request $request) |
||||||
59 | { |
||||||
60 | /** @var array $mfaHistory */ |
||||||
61 | $mfaHistory = Preferences::get('mfa_history', [])->data; |
||||||
0 ignored issues
–
show
Bug
Best Practice
introduced
by
![]() |
|||||||
62 | $mfaCode = $request->get('one_time_password'); |
||||||
63 | |||||||
64 | // is in history? then refuse to use it. |
||||||
65 | if ($this->inMFAHistory($mfaCode, $mfaHistory)) { |
||||||
0 ignored issues
–
show
It seems like
$mfaCode can also be of type null ; however, parameter $mfaCode of FireflyIII\Http\Controll...troller::inMFAHistory() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
66 | $this->filterMFAHistory(); |
||||||
67 | session()->flash('error', trans('firefly.wrong_mfa_code')); |
||||||
68 | |||||||
69 | return redirect(route('home')); |
||||||
70 | } |
||||||
71 | |||||||
72 | /** @var Authenticator $authenticator */ |
||||||
73 | $authenticator = app(Authenticator::class)->boot($request); |
||||||
74 | |||||||
75 | if ($authenticator->isAuthenticated()) { |
||||||
76 | // save MFA in preferences |
||||||
77 | $this->addToMFAHistory($mfaCode); |
||||||
0 ignored issues
–
show
It seems like
$mfaCode can also be of type null ; however, parameter $mfaCode of FireflyIII\Http\Controll...ller::addToMFAHistory() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
78 | |||||||
79 | // otp auth success! |
||||||
80 | return redirect(route('home')); |
||||||
81 | } |
||||||
82 | |||||||
83 | // could be user has a backup code. |
||||||
84 | if ($this->isBackupCode($mfaCode)) { |
||||||
0 ignored issues
–
show
It seems like
$mfaCode can also be of type null ; however, parameter $mfaCode of FireflyIII\Http\Controll...troller::isBackupCode() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
85 | $this->removeFromBackupCodes($mfaCode); |
||||||
0 ignored issues
–
show
It seems like
$mfaCode can also be of type null ; however, parameter $mfaCode of FireflyIII\Http\Controll...removeFromBackupCodes() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
86 | $authenticator->login(); |
||||||
87 | |||||||
88 | session()->flash('info', trans('firefly.mfa_backup_code')); |
||||||
89 | |||||||
90 | return redirect(route('home')); |
||||||
91 | } |
||||||
92 | |||||||
93 | session()->flash('error', trans('firefly.wrong_mfa_code')); |
||||||
94 | |||||||
95 | return redirect(route('home')); |
||||||
96 | } |
||||||
97 | |||||||
98 | /** |
||||||
99 | * @param string $mfaCode |
||||||
100 | */ |
||||||
101 | private function addToMFAHistory(string $mfaCode): void |
||||||
102 | { |
||||||
103 | /** @var array $mfaHistory */ |
||||||
104 | $mfaHistory = Preferences::get('mfa_history', [])->data; |
||||||
0 ignored issues
–
show
The method
FireflyIII\Support\Facades\Preferences::get() is not static, but was called statically.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
105 | $entry = [ |
||||||
106 | 'time' => time(), |
||||||
107 | 'code' => $mfaCode, |
||||||
108 | ]; |
||||||
109 | $mfaHistory[] = $entry; |
||||||
110 | |||||||
111 | Preferences::set('mfa_history', $mfaHistory); |
||||||
0 ignored issues
–
show
The method
FireflyIII\Support\Facades\Preferences::set() is not static, but was called statically.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
112 | $this->filterMFAHistory(); |
||||||
113 | } |
||||||
114 | |||||||
115 | /** |
||||||
116 | * Remove old entries from the preferences array. |
||||||
117 | */ |
||||||
118 | private function filterMFAHistory(): void |
||||||
119 | { |
||||||
120 | /** @var array $mfaHistory */ |
||||||
121 | $mfaHistory = Preferences::get('mfa_history', [])->data; |
||||||
0 ignored issues
–
show
The method
FireflyIII\Support\Facades\Preferences::get() is not static, but was called statically.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
122 | $newHistory = []; |
||||||
123 | $now = time(); |
||||||
124 | foreach ($mfaHistory as $entry) { |
||||||
125 | $time = $entry['time']; |
||||||
126 | $code = $entry['code']; |
||||||
127 | if ($now - $time <= 300) { |
||||||
128 | $newHistory[] = [ |
||||||
129 | 'time' => $time, |
||||||
130 | 'code' => $code, |
||||||
131 | ]; |
||||||
132 | } |
||||||
133 | } |
||||||
134 | Preferences::set('mfa_history', $newHistory); |
||||||
0 ignored issues
–
show
The method
FireflyIII\Support\Facades\Preferences::set() is not static, but was called statically.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
135 | } |
||||||
136 | |||||||
137 | /** |
||||||
138 | * Each MFA history has a timestamp and a code, saving the MFA entries for 5 minutes. So if the |
||||||
139 | * submitted MFA code has been submitted in the last 5 minutes, it won't work despite being valid. |
||||||
140 | * |
||||||
141 | * @param string $mfaCode |
||||||
142 | * @param array $mfaHistory |
||||||
143 | * |
||||||
144 | * @return bool |
||||||
145 | */ |
||||||
146 | private function inMFAHistory(string $mfaCode, array $mfaHistory): bool |
||||||
147 | { |
||||||
148 | $now = time(); |
||||||
149 | foreach ($mfaHistory as $entry) { |
||||||
150 | $time = $entry['time']; |
||||||
151 | $code = $entry['code']; |
||||||
152 | if ($code === $mfaCode && $now - $time <= 300) { |
||||||
153 | return true; |
||||||
154 | } |
||||||
155 | } |
||||||
156 | |||||||
157 | return false; |
||||||
158 | } |
||||||
159 | |||||||
160 | /** |
||||||
161 | * Checks if code is in users backup codes. |
||||||
162 | * |
||||||
163 | * @param string $mfaCode |
||||||
164 | * |
||||||
165 | * @return bool |
||||||
166 | */ |
||||||
167 | private function isBackupCode(string $mfaCode): bool |
||||||
168 | { |
||||||
169 | $list = Preferences::get('mfa_recovery', [])->data; |
||||||
0 ignored issues
–
show
The method
FireflyIII\Support\Facades\Preferences::get() is not static, but was called statically.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
170 | if (in_array($mfaCode, $list, true)) { |
||||||
0 ignored issues
–
show
$list of type string is incompatible with the type array expected by parameter $haystack of in_array() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
171 | return true; |
||||||
172 | } |
||||||
173 | |||||||
174 | return false; |
||||||
175 | } |
||||||
176 | |||||||
177 | /** |
||||||
178 | * Remove the used code from the list of backup codes. |
||||||
179 | * |
||||||
180 | * @param string $mfaCode |
||||||
181 | */ |
||||||
182 | private function removeFromBackupCodes(string $mfaCode): void |
||||||
183 | { |
||||||
184 | $list = Preferences::get('mfa_recovery', [])->data; |
||||||
0 ignored issues
–
show
The method
FireflyIII\Support\Facades\Preferences::get() is not static, but was called statically.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
185 | $newList = array_values(array_diff($list, [$mfaCode])); |
||||||
0 ignored issues
–
show
$list of type string is incompatible with the type array expected by parameter $array1 of array_diff() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
186 | Preferences::set('mfa_recovery', $newList); |
||||||
0 ignored issues
–
show
The method
FireflyIII\Support\Facades\Preferences::set() is not static, but was called statically.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
187 | } |
||||||
188 | } |
||||||
189 |