| 
									
										
										
										
											2021-07-03 03:53:33 +08:00
										 |  |  | <?php | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-18 00:56:55 +08:00
										 |  |  | namespace BookStack\Access\Controllers; | 
					
						
							| 
									
										
										
										
											2021-07-03 03:53:33 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-18 00:56:55 +08:00
										 |  |  | use BookStack\Access\LoginService; | 
					
						
							|  |  |  | use BookStack\Access\Mfa\BackupCodeService; | 
					
						
							|  |  |  | use BookStack\Access\Mfa\MfaSession; | 
					
						
							|  |  |  | use BookStack\Access\Mfa\MfaValue; | 
					
						
							|  |  |  | use BookStack\Activity\ActivityType; | 
					
						
							| 
									
										
										
										
											2021-08-02 23:35:37 +08:00
										 |  |  | use BookStack\Exceptions\NotFoundException; | 
					
						
							| 
									
										
										
										
											2023-05-19 03:53:39 +08:00
										 |  |  | use BookStack\Http\Controller; | 
					
						
							| 
									
										
										
										
											2021-07-03 03:53:33 +08:00
										 |  |  | use Exception; | 
					
						
							| 
									
										
										
										
											2021-08-02 23:35:37 +08:00
										 |  |  | use Illuminate\Http\Request; | 
					
						
							|  |  |  | use Illuminate\Validation\ValidationException; | 
					
						
							| 
									
										
										
										
											2021-07-03 03:53:33 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | class MfaBackupCodesController extends Controller | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2021-07-18 23:52:31 +08:00
										 |  |  |     use HandlesPartialLogins; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-03 03:53:33 +08:00
										 |  |  |     protected const SETUP_SECRET_SESSION_KEY = 'mfa-setup-backup-codes'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							| 
									
										
										
										
											2021-08-21 22:49:40 +08:00
										 |  |  |      * Show a view that generates and displays backup codes. | 
					
						
							| 
									
										
										
										
											2021-07-03 03:53:33 +08:00
										 |  |  |      */ | 
					
						
							|  |  |  |     public function generate(BackupCodeService $codeService) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $codes = $codeService->generateNewSet(); | 
					
						
							|  |  |  |         session()->put(self::SETUP_SECRET_SESSION_KEY, encrypt($codes)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $downloadUrl = 'data:application/octet-stream;base64,' . base64_encode(implode("\n\n", $codes)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-04 21:33:24 +08:00
										 |  |  |         $this->setPageTitle(trans('auth.mfa_gen_backup_codes_title')); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-03 03:53:33 +08:00
										 |  |  |         return view('mfa.backup-codes-generate', [ | 
					
						
							| 
									
										
										
										
											2021-08-21 22:49:40 +08:00
										 |  |  |             'codes'       => $codes, | 
					
						
							| 
									
										
										
										
											2021-07-03 03:53:33 +08:00
										 |  |  |             'downloadUrl' => $downloadUrl, | 
					
						
							|  |  |  |         ]); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Confirm the setup of backup codes, storing them against the user. | 
					
						
							| 
									
										
										
										
											2021-08-21 22:49:40 +08:00
										 |  |  |      * | 
					
						
							| 
									
										
										
										
											2021-07-03 03:53:33 +08:00
										 |  |  |      * @throws Exception | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function confirm() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (!session()->has(self::SETUP_SECRET_SESSION_KEY)) { | 
					
						
							|  |  |  |             return response('No generated codes found in the session', 500); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $codes = decrypt(session()->pull(self::SETUP_SECRET_SESSION_KEY)); | 
					
						
							| 
									
										
										
										
											2021-07-18 23:52:31 +08:00
										 |  |  |         MfaValue::upsertWithValue($this->currentOrLastAttemptedUser(), MfaValue::METHOD_BACKUP_CODES, json_encode($codes)); | 
					
						
							| 
									
										
										
										
											2021-07-03 03:53:33 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         $this->logActivity(ActivityType::MFA_SETUP_METHOD, 'backup-codes'); | 
					
						
							| 
									
										
										
										
											2021-08-21 22:14:24 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if (!auth()->check()) { | 
					
						
							|  |  |  |             $this->showSuccessNotification(trans('auth.mfa_setup_login_notification')); | 
					
						
							| 
									
										
										
										
											2021-09-01 05:03:51 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-21 22:14:24 +08:00
										 |  |  |             return redirect('/login'); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-03 03:53:33 +08:00
										 |  |  |         return redirect('/mfa/setup'); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-08-02 23:35:37 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Verify the MFA method submission on check. | 
					
						
							| 
									
										
										
										
											2021-08-21 22:49:40 +08:00
										 |  |  |      * | 
					
						
							| 
									
										
										
										
											2021-08-02 23:35:37 +08:00
										 |  |  |      * @throws NotFoundException | 
					
						
							|  |  |  |      * @throws ValidationException | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function verify(Request $request, BackupCodeService $codeService, MfaSession $mfaSession, LoginService $loginService) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $user = $this->currentOrLastAttemptedUser(); | 
					
						
							|  |  |  |         $codes = MfaValue::getValueForUser($user, MfaValue::METHOD_BACKUP_CODES) ?? '[]'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $this->validate($request, [ | 
					
						
							|  |  |  |             'code' => [ | 
					
						
							| 
									
										
										
										
											2021-11-05 08:26:55 +08:00
										 |  |  |                 'required', 'max:12', 'min:8', | 
					
						
							| 
									
										
										
										
											2021-08-02 23:35:37 +08:00
										 |  |  |                 function ($attribute, $value, $fail) use ($codeService, $codes) { | 
					
						
							|  |  |  |                     if (!$codeService->inputCodeExistsInSet($value, $codes)) { | 
					
						
							|  |  |  |                         $fail(trans('validation.backup_codes')); | 
					
						
							|  |  |  |                     } | 
					
						
							| 
									
										
										
										
											2021-08-21 22:49:40 +08:00
										 |  |  |                 }, | 
					
						
							|  |  |  |             ], | 
					
						
							| 
									
										
										
										
											2021-08-02 23:35:37 +08:00
										 |  |  |         ]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $updatedCodes = $codeService->removeInputCodeFromSet($request->get('code'), $codes); | 
					
						
							|  |  |  |         MfaValue::upsertWithValue($user, MfaValue::METHOD_BACKUP_CODES, $updatedCodes); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $mfaSession->markVerifiedForUser($user); | 
					
						
							| 
									
										
										
										
											2021-08-03 05:02:25 +08:00
										 |  |  |         $loginService->reattemptLoginFor($user); | 
					
						
							| 
									
										
										
										
											2021-08-02 23:35:37 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if ($codeService->countCodesInSet($updatedCodes) < 5) { | 
					
						
							| 
									
										
										
										
											2021-08-08 21:24:44 +08:00
										 |  |  |             $this->showWarningNotification(trans('auth.mfa_backup_codes_usage_limit_warning')); | 
					
						
							| 
									
										
										
										
											2021-08-02 23:35:37 +08:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return redirect()->intended(); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-07-03 03:53:33 +08:00
										 |  |  | } |