mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-12-02 05:43:40 +08:00
4597069083
Also added testing to cover as part of this in addition to adding the core backup code handling required. Also added the standardised translations for switching mfa mode and adding testing for this switching.
90 lines
3.1 KiB
PHP
90 lines
3.1 KiB
PHP
<?php
|
|
|
|
namespace BookStack\Http\Controllers\Auth;
|
|
|
|
use BookStack\Actions\ActivityType;
|
|
use BookStack\Auth\Access\LoginService;
|
|
use BookStack\Auth\Access\Mfa\BackupCodeService;
|
|
use BookStack\Auth\Access\Mfa\MfaSession;
|
|
use BookStack\Auth\Access\Mfa\MfaValue;
|
|
use BookStack\Exceptions\NotFoundException;
|
|
use BookStack\Http\Controllers\Controller;
|
|
use Exception;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Validation\ValidationException;
|
|
|
|
class MfaBackupCodesController extends Controller
|
|
{
|
|
use HandlesPartialLogins;
|
|
|
|
protected const SETUP_SECRET_SESSION_KEY = 'mfa-setup-backup-codes';
|
|
|
|
/**
|
|
* Show a view that generates and displays backup codes
|
|
*/
|
|
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));
|
|
|
|
return view('mfa.backup-codes-generate', [
|
|
'codes' => $codes,
|
|
'downloadUrl' => $downloadUrl,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Confirm the setup of backup codes, storing them against the user.
|
|
* @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));
|
|
MfaValue::upsertWithValue($this->currentOrLastAttemptedUser(), MfaValue::METHOD_BACKUP_CODES, json_encode($codes));
|
|
|
|
$this->logActivity(ActivityType::MFA_SETUP_METHOD, 'backup-codes');
|
|
return redirect('/mfa/setup');
|
|
}
|
|
|
|
/**
|
|
* Verify the MFA method submission on check.
|
|
* @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' => [
|
|
'required',
|
|
'max:12', 'min:8',
|
|
function ($attribute, $value, $fail) use ($codeService, $codes) {
|
|
if (!$codeService->inputCodeExistsInSet($value, $codes)) {
|
|
$fail(trans('validation.backup_codes'));
|
|
}
|
|
}
|
|
]
|
|
]);
|
|
|
|
$updatedCodes = $codeService->removeInputCodeFromSet($request->get('code'), $codes);
|
|
MfaValue::upsertWithValue($user, MfaValue::METHOD_BACKUP_CODES, $updatedCodes);
|
|
|
|
$mfaSession->markVerifiedForUser($user);
|
|
$loginService->reattemptLoginFor($user, 'mfa-backup_codes');
|
|
|
|
if ($codeService->countCodesInSet($updatedCodes) < 5) {
|
|
$this->showWarningNotification('You have less than 5 backup codes remaining, Please generate and store a new set before you run out of codes to prevent being locked out of your account.');
|
|
}
|
|
|
|
return redirect()->intended();
|
|
}
|
|
}
|