mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-02-25 18:45:27 -06:00
commit
8828aa0621
@ -1,11 +1,15 @@
|
||||
<?php namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use Auth;
|
||||
use Config;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI;
|
||||
use FireflyIII\Http\Requests\TokenFormRequest;
|
||||
use Input;
|
||||
use Preferences;
|
||||
use Response;
|
||||
use Session;
|
||||
use View;
|
||||
use PragmaRX\Google2FA\Contracts\Google2FA;
|
||||
|
||||
/**
|
||||
* Class PreferencesController
|
||||
@ -43,12 +47,15 @@ class PreferencesController extends Controller
|
||||
$fiscalYearStartStr = Preferences::get('fiscalYearStart', '01-01')->data;
|
||||
$fiscalYearStart = date('Y') . '-' . $fiscalYearStartStr;
|
||||
$twoFactorAuthEnabled = Preferences::get('twoFactorAuthEnabled', 0)->data;
|
||||
|
||||
$hasTwoFactorAuthSecret = Preferences::get('twoFactorAuthSecret') != null && !empty(Preferences::get('twoFactorAuthSecret')->data);
|
||||
|
||||
$showIncomplete = env('SHOW_INCOMPLETE_TRANSLATIONS', false) === true;
|
||||
|
||||
return view(
|
||||
'preferences.index',
|
||||
compact(
|
||||
'budgetMaximum', 'language', 'accounts', 'frontPageAccounts', 'viewRange', 'customFiscalYear', 'fiscalYearStart', 'twoFactorAuthEnabled',
|
||||
'budgetMaximum', 'language', 'accounts', 'frontPageAccounts', 'viewRange', 'customFiscalYear', 'fiscalYearStart', 'twoFactorAuthEnabled', 'hasTwoFactorAuthSecret',
|
||||
'showIncomplete'
|
||||
)
|
||||
);
|
||||
@ -87,7 +94,14 @@ class PreferencesController extends Controller
|
||||
|
||||
// two factor auth
|
||||
$twoFactorAuthEnabled = (int)Input::get('twoFactorAuthEnabled');
|
||||
Preferences::set('twoFactorAuthEnabled', $twoFactorAuthEnabled);
|
||||
|
||||
$hasTwoFactorAuthSecret = Preferences::get('twoFactorAuthSecret') != null && !empty(Preferences::get('twoFactorAuthSecret')->data);
|
||||
|
||||
// If we already have a secret, just set the two factor auth enabled to 1, and let the user continue with the existing secret.
|
||||
if($hasTwoFactorAuthSecret)
|
||||
{
|
||||
Preferences::set('twoFactorAuthEnabled', $twoFactorAuthEnabled);
|
||||
}
|
||||
|
||||
// language:
|
||||
$lang = Input::get('language');
|
||||
@ -99,7 +113,44 @@ class PreferencesController extends Controller
|
||||
Session::flash('success', 'Preferences saved!');
|
||||
Preferences::mark();
|
||||
|
||||
// if we don't have a valid secret yet, redirect to the code page.
|
||||
if(!$hasTwoFactorAuthSecret)
|
||||
{
|
||||
return redirect(route('preferences.code'));
|
||||
}
|
||||
|
||||
return redirect(route('preferences'));
|
||||
}
|
||||
|
||||
/*
|
||||
* @param TokenFormRequest $request
|
||||
*
|
||||
* @return $this|\Illuminate\View\View
|
||||
*/
|
||||
public function postCode(TokenFormRequest $request)
|
||||
{
|
||||
Preferences::set('twoFactorAuthEnabled', 1);
|
||||
Preferences::set('twoFactorAuthSecret', $request->input('secret'));
|
||||
|
||||
Session::flash('success', 'Preferences saved!');
|
||||
Preferences::mark();
|
||||
|
||||
return redirect(route('preferences'));
|
||||
}
|
||||
|
||||
/*
|
||||
* @param Google2FA $google2fa
|
||||
*
|
||||
* @return $this|\Illuminate\View\View
|
||||
*/
|
||||
public function code(Google2FA $google2fa)
|
||||
{
|
||||
$secret = $google2fa->generateSecretKey(16, Auth::user()->id);
|
||||
|
||||
$image = $google2fa->getQRCodeInline("FireflyIII", null, $secret, 150);
|
||||
|
||||
|
||||
return view('preferences.code', compact('secret', 'image'));
|
||||
}
|
||||
|
||||
}
|
||||
|
39
app/Http/Requests/TokenFormRequest.php
Normal file
39
app/Http/Requests/TokenFormRequest.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Http\Requests;
|
||||
|
||||
use Auth;
|
||||
use Input;
|
||||
|
||||
/**
|
||||
* Class TokenFormRequest
|
||||
*
|
||||
*
|
||||
* @package FireflyIII\Http\Requests
|
||||
*/
|
||||
class TokenFormRequest extends Request
|
||||
{
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
// Only allow logged in users
|
||||
return Auth::check();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
|
||||
$rules = [
|
||||
'secret' => 'required',
|
||||
'code' => 'required|2faCode:secret',
|
||||
];
|
||||
|
||||
return $rules;
|
||||
}
|
||||
}
|
@ -378,6 +378,14 @@ Breadcrumbs::register(
|
||||
}
|
||||
);
|
||||
|
||||
Breadcrumbs::register(
|
||||
'preferences.code', function (BreadCrumbGenerator $breadcrumbs) {
|
||||
$breadcrumbs->parent('home');
|
||||
$breadcrumbs->push(trans('breadcrumbs.preferences'), route('preferences'));
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* PROFILE
|
||||
*/
|
||||
|
@ -246,6 +246,8 @@ Route::group(
|
||||
*/
|
||||
Route::get('/preferences', ['uses' => 'PreferencesController@index', 'as' => 'preferences']);
|
||||
Route::post('/preferences', ['uses' => 'PreferencesController@postIndex']);
|
||||
Route::get('/preferences/code', ['uses' => 'PreferencesController@code', 'as' => 'preferences.code']);
|
||||
Route::post('/preferences/code', ['uses' => 'PreferencesController@postCode']);
|
||||
|
||||
/**
|
||||
* Profile Controller
|
||||
|
@ -16,10 +16,13 @@ use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use FireflyIII\Rules\Triggers\TriggerInterface;
|
||||
use FireflyIII\User;
|
||||
use Input;
|
||||
use Illuminate\Contracts\Encryption\DecryptException;
|
||||
use Illuminate\Validation\Validator;
|
||||
use Log;
|
||||
use PragmaRX\Google2FA\Contracts\Google2FA;
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
use Session;
|
||||
|
||||
/**
|
||||
* Class FireflyValidator
|
||||
@ -43,6 +46,29 @@ class FireflyValidator extends Validator
|
||||
parent::__construct($translator, $data, $rules, $messages, $customAttributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $attribute
|
||||
* @param $value
|
||||
* @param $parameters
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function validate2faCode($attribute, $value, $parameters): bool
|
||||
{
|
||||
if (!is_string($value) || is_null($value) || strlen($value) <> 6) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Retrieve the secret from our hidden form field.
|
||||
$secret = Input::get($parameters[0]);
|
||||
|
||||
$google2fa = app('PragmaRX\Google2FA\Google2FA');
|
||||
|
||||
return $google2fa->verifyKey($secret, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $attribute
|
||||
* @param $value
|
||||
|
@ -22,7 +22,8 @@
|
||||
"rcrowe/twigbridge": "~0.9",
|
||||
"league/csv": "^7.1",
|
||||
"laravelcollective/html": "^5.2",
|
||||
"rmccue/requests": "^1.6"
|
||||
"rmccue/requests": "^1.6",
|
||||
"pragmarx/google2fa": "^0.7.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"fzaninotto/faker": "~1.4",
|
||||
|
@ -181,6 +181,7 @@ return [
|
||||
// Barryvdh\Debugbar\ServiceProvider::class,
|
||||
'DaveJamesMiller\Breadcrumbs\ServiceProvider',
|
||||
'TwigBridge\ServiceProvider',
|
||||
'PragmaRX\Google2FA\Vendor\Laravel\ServiceProvider',
|
||||
|
||||
],
|
||||
|
||||
@ -238,7 +239,7 @@ return [
|
||||
'ExpandedForm' => 'FireflyIII\Support\Facades\ExpandedForm',
|
||||
'Entrust' => 'Zizaco\Entrust\EntrustFacade',
|
||||
'Input' => 'Illuminate\Support\Facades\Input',
|
||||
|
||||
'Google2FA' => 'PragmaRX\Google2FA\Vendor\Laravel\Facade',
|
||||
|
||||
],
|
||||
|
||||
|
@ -230,6 +230,9 @@ return [
|
||||
'pref_two_factor_auth' => '2-step verification',
|
||||
'pref_two_factor_auth_help' => 'When you enable 2-step verification (also known as two-factor authentication), you add an extra layer of security to your account. You sign in with something you know (your password) and something you have (a verification code). Verification codes are generated by an application on your phone, such as Authy or Google Authenticator.',
|
||||
'pref_enable_two_factor_auth' => 'Enable 2-step verification',
|
||||
'pref_two_factor_auth_code' => 'Verify code',
|
||||
'pref_two_factor_auth_code_help' => 'Scan the QR code with an application on your phone such as Authy or Google Authenticator and enter the generated code.',
|
||||
'pref_two_factor_auth_reset_code' => 'Reset verification code',
|
||||
'pref_save_settings' => 'Save settings',
|
||||
|
||||
// profile:
|
||||
|
@ -68,4 +68,5 @@ return [
|
||||
'string' => 'The :attribute must be a string.',
|
||||
'url' => 'The :attribute format is invalid.',
|
||||
'timezone' => 'The :attribute must be a valid zone.',
|
||||
'2fa_code' => 'The :attribute field is invalid.',
|
||||
];
|
||||
|
@ -228,6 +228,9 @@ return [
|
||||
'pref_two_factor_auth' => 'Authenticatie in twee stappen',
|
||||
'pref_two_factor_auth_help' => 'Als je authenticatie in twee stappen (ook wel twee-factor authenticatie genoemd) inschakelt voeg je een extra beveiligingslaag toe aan je account. Je logt in met iets dat je weet (je wachtwoord) en iets dat je hebt (een verificatiecode). Verificatiecodes worden gegeneerd door apps op je telefoon, zoals Authy en Google Authenticator.',
|
||||
'pref_enable_two_factor_auth' => 'Authenticatie in twee stappen inschakelen',
|
||||
'pref_two_factor_auth_code' => 'Verifieer code',
|
||||
'pref_two_factor_auth_code_help' => 'Scan onderstaande QR code met een app op je telefoon zoals Authy of Google Authenticator en vul de code die gegenereerd wordt in.',
|
||||
'pref_two_factor_auth_reset_code' => 'Reset verificatiecode',
|
||||
'pref_save_settings' => 'Instellingen opslaan',
|
||||
|
||||
// profile:
|
||||
|
@ -68,4 +68,6 @@ return [
|
||||
'string' => 'Het :attribute moet een tekenreeks zijn.',
|
||||
'url' => ':attribute is geen geldige URL.',
|
||||
'timezone' => 'Het :attribute moet een geldige zone zijn.',
|
||||
'2fa_code' => ':attribute is ongeldig.',
|
||||
];
|
||||
|
||||
|
52
resources/views/preferences/code.twig
Normal file
52
resources/views/preferences/code.twig
Normal file
@ -0,0 +1,52 @@
|
||||
{% extends "./layout/default.twig" %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
{{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{{ Form.open({'class' : 'form-horizontal','id' : 'preferences.code'}) }}
|
||||
<input type="hidden" name="secret" value="{{ secret }}"/>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-6 col-md-6 col-sm-6">
|
||||
<div class="box">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">{{ 'pref_two_factor_auth_code'|_ }}</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<p class="text-info">
|
||||
{{ 'pref_two_factor_auth_code_help'|_ }}
|
||||
</p>
|
||||
<div class="form group">
|
||||
|
||||
<div class="col-sm-8 col-md-offset-4">
|
||||
<img src="{{ image }}" />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="form group">
|
||||
<div class="col-sm-8 col-md-offset-4">
|
||||
<p>{{ secret }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ ExpandedForm.text('code', code, {'label' : 'Code'}) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-sm-12">
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<button type="submit" class="btn btn-success btn-lg">{{ 'pref_save_settings'|_ }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- form close -->
|
||||
{{ Form.close|raw }}
|
||||
{% endblock %}
|
@ -145,19 +145,34 @@
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<p class="text-info">{{ 'pref_two_factor_auth_help'|_ }}</p>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-10">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" name="twoFactorAuthEnabled" value="1"
|
||||
{% if twoFactorAuthEnabled == '1' %} checked {% endif %}> {{ 'pref_enable_two_factor_auth'|_ }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-10">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" name="twoFactorAuthEnabled" value="1"
|
||||
{% if twoFactorAuthEnabled == '1' %} checked {% endif %}> {{ 'pref_enable_two_factor_auth'|_ }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if twoFactorAuthEnabled == '1' and hasTwoFactorAuthSecret == true %}
|
||||
|
||||
<div class="col-sm-10">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<a href="{{ URL.to('/preferences/code') }}">{{ 'pref_two_factor_auth_reset_code'|_ }}</a>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-sm-12">
|
||||
|
Loading…
Reference in New Issue
Block a user