mirror of
https://github.com/Bubka/2FAuth.git
synced 2025-05-29 06:27:33 +02:00
Add possibility to delete the registered user and reset 2FAuth data
This commit is contained in:
parent
984e6d253c
commit
cdfda1591b
@ -2,20 +2,36 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers\Auth;
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Services\TwoFAccountService;
|
||||||
use App\Http\Requests\UserUpdateRequest;
|
use App\Http\Requests\UserUpdateRequest;
|
||||||
|
use App\Http\Requests\UserDeleteRequest;
|
||||||
use App\Api\v1\Resources\UserResource;
|
use App\Api\v1\Resources\UserResource;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Hash;
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Artisan;
|
||||||
use App\Exceptions\UnsupportedWithReverseProxyException;
|
use App\Exceptions\UnsupportedWithReverseProxyException;
|
||||||
|
use Exception;
|
||||||
|
|
||||||
class UserController extends Controller
|
class UserController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Create a new controller instance.
|
* The TwoFAccount Service instance.
|
||||||
*/
|
*/
|
||||||
public function __construct()
|
protected $twofaccountService;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new controller instance.
|
||||||
|
*
|
||||||
|
* @param \App\Services\TwoFAccountService $twofaccountService
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(TwoFAccountService $twofaccountService)
|
||||||
{
|
{
|
||||||
|
$this->twofaccountService = $twofaccountService;
|
||||||
$authGuard = config('auth.defaults.guard');
|
$authGuard = config('auth.defaults.guard');
|
||||||
|
|
||||||
if ($authGuard === 'reverse-proxy-guard') {
|
if ($authGuard === 'reverse-proxy-guard') {
|
||||||
@ -27,7 +43,7 @@ class UserController extends Controller
|
|||||||
/**
|
/**
|
||||||
* Update the user's profile information.
|
* Update the user's profile information.
|
||||||
*
|
*
|
||||||
* @param \App\Api\v1\Requests\UserUpdateRequest $request
|
* @param \App\Http\Requests\UserUpdateRequest $request
|
||||||
* @return \App\Api\v1\Resources\UserResource
|
* @return \App\Api\v1\Resources\UserResource
|
||||||
*/
|
*/
|
||||||
public function update(UserUpdateRequest $request)
|
public function update(UserUpdateRequest $request)
|
||||||
@ -48,4 +64,45 @@ class UserController extends Controller
|
|||||||
|
|
||||||
return new UserResource($user);
|
return new UserResource($user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the user's account.
|
||||||
|
*
|
||||||
|
* @param \App\Http\Requests\UserDeleteRequest $request
|
||||||
|
* @return \Illuminate\Http\JsonResponse
|
||||||
|
*/
|
||||||
|
public function delete(UserDeleteRequest $request)
|
||||||
|
{
|
||||||
|
$validated = $request->validated();
|
||||||
|
|
||||||
|
if (!Hash::check( $validated['password'], Auth::user()->password) ) {
|
||||||
|
return response()->json(['message' => __('errors.wrong_current_password')], 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
DB::transaction(function () {
|
||||||
|
DB::table('twofaccounts')->delete();
|
||||||
|
DB::table('groups')->delete();
|
||||||
|
DB::table('options')->delete();
|
||||||
|
DB::table('web_authn_credentials')->delete();
|
||||||
|
DB::table('web_authn_recoveries')->delete();
|
||||||
|
DB::table('oauth_access_tokens')->delete();
|
||||||
|
DB::table('oauth_auth_codes')->delete();
|
||||||
|
DB::table('oauth_clients')->delete();
|
||||||
|
DB::table('oauth_personal_access_clients')->delete();
|
||||||
|
DB::table('oauth_refresh_tokens')->delete();
|
||||||
|
DB::table('password_resets')->delete();
|
||||||
|
DB::table('users')->delete();
|
||||||
|
});
|
||||||
|
|
||||||
|
Artisan::call('passport:install --force');
|
||||||
|
Artisan::call('config:clear');
|
||||||
|
}
|
||||||
|
catch (\Throwable $e) {
|
||||||
|
return response()->json(['message' => __('errors.user_deletion_failed')], 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json(null, 204);
|
||||||
|
}
|
||||||
}
|
}
|
32
app/Http/Requests/UserDeleteRequest.php
Normal file
32
app/Http/Requests/UserDeleteRequest.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
|
||||||
|
class UserDeleteRequest extends FormRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorize()
|
||||||
|
{
|
||||||
|
return Auth::check();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function rules()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'password' => 'required|string',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,9 @@ use Illuminate\Support\Facades\Blade;
|
|||||||
use Illuminate\Support\Facades\Schema;
|
use Illuminate\Support\Facades\Schema;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
use Illuminate\Http\Resources\Json\JsonResource;
|
use Illuminate\Http\Resources\Json\JsonResource;
|
||||||
|
use Laravel\Passport\Console\ClientCommand;
|
||||||
|
use Laravel\Passport\Console\InstallCommand;
|
||||||
|
use Laravel\Passport\Console\KeysCommand;
|
||||||
|
|
||||||
|
|
||||||
class AppServiceProvider extends ServiceProvider
|
class AppServiceProvider extends ServiceProvider
|
||||||
@ -30,5 +33,11 @@ class AppServiceProvider extends ServiceProvider
|
|||||||
Blade::withoutComponentTags();
|
Blade::withoutComponentTags();
|
||||||
Schema::defaultStringLength(191);
|
Schema::defaultStringLength(191);
|
||||||
JsonResource::withoutWrapping();
|
JsonResource::withoutWrapping();
|
||||||
|
|
||||||
|
$this->commands([
|
||||||
|
InstallCommand::class,
|
||||||
|
ClientCommand::class,
|
||||||
|
KeysCommand::class,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="field is-grouped">
|
<div class="field is-grouped">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<v-button :isLoading="isBusy" :disabled="isDisabled" >{{ caption }}</v-button>
|
<v-button :color="color" :isLoading="isBusy" :disabled="isDisabled" >{{ caption }}</v-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="control" v-if="showCancelButton">
|
<div class="control" v-if="showCancelButton">
|
||||||
<router-link :to="{ name: cancelLandingView }" class="button is-text">{{ $t('commons.cancel') }}</router-link>
|
<router-link :to="{ name: cancelLandingView }" class="button is-text">{{ $t('commons.cancel') }}</router-link>
|
||||||
@ -44,6 +44,11 @@
|
|||||||
type: String,
|
type: String,
|
||||||
default: ''
|
default: ''
|
||||||
},
|
},
|
||||||
|
|
||||||
|
color: {
|
||||||
|
type: String,
|
||||||
|
default: 'is-link'
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
@ -22,6 +22,16 @@
|
|||||||
<form-buttons :isBusy="formPassword.isBusy" :caption="$t('auth.forms.change_password')" />
|
<form-buttons :isBusy="formPassword.isBusy" :caption="$t('auth.forms.change_password')" />
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</form>
|
</form>
|
||||||
|
<form @submit.prevent="submitDelete" @keydown="formDelete.onKeydown($event)">
|
||||||
|
<h4 class="title is-4 pt-6 has-text-danger">{{ $t('auth.forms.delete_account') }}</h4>
|
||||||
|
<div class="field is-size-7-mobile">
|
||||||
|
{{ $t('auth.forms.delete_your_account_and_reset_all_data')}}
|
||||||
|
</div>
|
||||||
|
<fieldset :disabled="isRemoteUser">
|
||||||
|
<form-field :form="formDelete" fieldName="password" inputType="password" :label="$t('auth.forms.current_password.label')" :help="$t('auth.forms.current_password.help')" />
|
||||||
|
<form-buttons :isBusy="formDelete.isBusy" :caption="$t('auth.forms.delete_your_account')" :color="'is-danger'" />
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
</form-wrapper>
|
</form-wrapper>
|
||||||
</div>
|
</div>
|
||||||
<vue-footer :showButtons="true">
|
<vue-footer :showButtons="true">
|
||||||
@ -52,6 +62,9 @@
|
|||||||
password : '',
|
password : '',
|
||||||
password_confirmation : '',
|
password_confirmation : '',
|
||||||
}),
|
}),
|
||||||
|
formDelete: new Form({
|
||||||
|
password : '',
|
||||||
|
}),
|
||||||
isRemoteUser: false,
|
isRemoteUser: false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -101,7 +114,31 @@
|
|||||||
this.$router.push({ name: 'genericError', params: { err: error.response } });
|
this.$router.push({ name: 'genericError', params: { err: error.response } });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
|
||||||
|
submitDelete(e) {
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
|
if(confirm(this.$t('auth.confirm.delete_account'))) {
|
||||||
|
|
||||||
|
this.formDelete.delete('/user', {returnError: true})
|
||||||
|
.then(response => {
|
||||||
|
|
||||||
|
this.$notify({ type: 'is-success', text: this.$t('auth.forms.user_account_successfully_deleted') })
|
||||||
|
this.$router.push({ name: 'register' });
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
if( error.response.status === 400 ) {
|
||||||
|
|
||||||
|
this.$notify({ type: 'is-danger', text: error.response.data.message })
|
||||||
|
}
|
||||||
|
else if( error.response.status !== 422 ) {
|
||||||
|
|
||||||
|
this.$router.push({ name: 'genericError', params: { err: error.response } });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
@ -33,6 +33,7 @@ return [
|
|||||||
'confirm' => [
|
'confirm' => [
|
||||||
'logout' => 'Are you sure you want to log out?',
|
'logout' => 'Are you sure you want to log out?',
|
||||||
'revoke_device' => 'Are you sure you want to revoke this device?',
|
'revoke_device' => 'Are you sure you want to revoke this device?',
|
||||||
|
'delete_account' => 'Are you sure you want to delete your account?',
|
||||||
],
|
],
|
||||||
'webauthn' => [
|
'webauthn' => [
|
||||||
'security_device' => 'a security device',
|
'security_device' => 'a security device',
|
||||||
@ -96,6 +97,10 @@ return [
|
|||||||
'register_punchline' => 'Welcome to 2FAuth.<br/>You need an account to go further. Fill this form to register yourself, and please, choose a strong password, 2FA data are sensitives.',
|
'register_punchline' => 'Welcome to 2FAuth.<br/>You need an account to go further. Fill this form to register yourself, and please, choose a strong password, 2FA data are sensitives.',
|
||||||
'reset_punchline' => '2FAuth will send you a password reset link to this address. Click the link in the received email to set a new password.',
|
'reset_punchline' => '2FAuth will send you a password reset link to this address. Click the link in the received email to set a new password.',
|
||||||
'name_this_device' => 'Name this device',
|
'name_this_device' => 'Name this device',
|
||||||
|
'delete_account' => 'Delete account',
|
||||||
|
'delete_your_account' => 'Delete your account',
|
||||||
|
'delete_your_account_and_reset_all_data' => 'This will reset 2FAuth. Your user account will be deleted as well as all 2FA data. There is no going back.',
|
||||||
|
'user_account_successfully_deleted' => 'User account successfully deleted',
|
||||||
],
|
],
|
||||||
|
|
||||||
];
|
];
|
||||||
|
@ -36,4 +36,5 @@ return [
|
|||||||
'aborted_by_user' => 'Aborted by user',
|
'aborted_by_user' => 'Aborted by user',
|
||||||
'security_device_unsupported' => 'Security device unsupported',
|
'security_device_unsupported' => 'Security device unsupported',
|
||||||
'unsupported_with_reverseproxy' => 'Not applicable when using an auth proxy',
|
'unsupported_with_reverseproxy' => 'Not applicable when using an auth proxy',
|
||||||
|
'user_deletion_failed' => 'User account deletion failed, no data have been deleted',
|
||||||
];
|
];
|
@ -42,6 +42,7 @@ Route::group(['middleware' => 'behind-auth'], function () {
|
|||||||
Route::put('user', 'Auth\UserController@update')->name('user.update');
|
Route::put('user', 'Auth\UserController@update')->name('user.update');
|
||||||
Route::patch('user/password', 'Auth\PasswordController@update')->name('user.password.update');
|
Route::patch('user/password', 'Auth\PasswordController@update')->name('user.password.update');
|
||||||
Route::get('user/logout', 'Auth\LoginController@logout')->name('user.logout');
|
Route::get('user/logout', 'Auth\LoginController@logout')->name('user.logout');
|
||||||
|
Route::delete('user', 'Auth\UserController@delete')->name('user.delete')->middleware('disableInDemoMode');
|
||||||
|
|
||||||
Route::get('oauth/personal-access-tokens', 'Auth\PersonalAccessTokenController@forUser')->name('passport.personal.tokens.index');
|
Route::get('oauth/personal-access-tokens', 'Auth\PersonalAccessTokenController@forUser')->name('passport.personal.tokens.index');
|
||||||
Route::post('oauth/personal-access-tokens', 'Auth\PersonalAccessTokenController@store')->name('passport.personal.tokens.store');
|
Route::post('oauth/personal-access-tokens', 'Auth\PersonalAccessTokenController@store')->name('passport.personal.tokens.store');
|
||||||
|
Loading…
x
Reference in New Issue
Block a user