1
0
mirror of https://github.com/devcode-it/openstamanager.git synced 2025-02-23 06:47:40 +01:00

feat: Aggiunto reset password

This commit is contained in:
Maicol Battistini 2021-12-14 11:55:23 +01:00
parent 3020cf0293
commit f8bab49852
No known key found for this signature in database
GPG Key ID: 4FDB0F87CDB1D34A
5 changed files with 213 additions and 22 deletions

View File

@ -2,9 +2,14 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Auth\Events\PasswordReset;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Http\Response; use Illuminate\Http\Response;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException; use Illuminate\Validation\ValidationException;
use JetBrains\PhpStorm\ArrayShape; use JetBrains\PhpStorm\ArrayShape;
@ -39,21 +44,41 @@ class AuthController extends Controller
], Response::HTTP_BAD_REQUEST); ], Response::HTTP_BAD_REQUEST);
} }
#[ArrayShape(['username' => 'string', 'password' => 'string', 'remember' => 'string'])] public function forgotPassword(Request $request): Response|JsonResponse
private function rules(Request $request): array
{ {
$additional_validation = ''; $request->validate([
$db_field = 'username'; 'email' => 'required|email|exists:users,email',
if (filter_var($request->input('username'), FILTER_VALIDATE_EMAIL)) { ]);
$additional_validation = '|email';
$db_field = 'email';
}
return [ $response = Password::broker()->sendResetLink($request->input('email'));
'username' => "required|string|exists:users,$db_field|$additional_validation",
'password' => 'required|string', return $response === Password::RESET_LINK_SENT
'remember' => 'boolean', ? response()->noContent()
]; : \response()->json(['errors' => ['email' => [__($response)]]], Response::HTTP_BAD_REQUEST);
}
public function resetPassword(Request $request): JsonResponse|Response
{
$request->validate([
'token' => 'required|string',
'email' => 'required|email|exists:users,email',
'password' => ['required|string|confirmed', \Illuminate\Validation\Rules\Password::defaults()],
]);
$response = Password::broker()->reset(
$request->only(['email', 'password', 'password_confirmation', 'token']),
function (User $user, string $password) {
$user->password = Hash::make($password);
$user->setRememberToken(Str::random(60));
$user->save();
event(new PasswordReset($user));
}
);
return $response === Password::PASSWORD_RESET
? response()->noContent()
: response()->json(['errors' => ['email' => [__($response)]]], Response::HTTP_BAD_REQUEST);
} }
/** /**
@ -70,4 +95,21 @@ class AuthController extends Controller
return response()->noContent(); return response()->noContent();
} }
#[ArrayShape(['username' => 'string', 'password' => 'string', 'remember' => 'string'])]
private function rules(Request $request): array
{
$additional_validation = '';
$db_field = 'username';
if (filter_var($request->input('username'), FILTER_VALIDATE_EMAIL)) {
$additional_validation = '|email';
$db_field = 'email';
}
return [
'username' => "required|string|exists:users,$db_field|$additional_validation",
'password' => 'required|string',
'remember' => 'boolean',
];
}
} }

View File

@ -17,7 +17,7 @@ class User extends Authenticatable
* @var array * @var array
*/ */
protected $fillable = [ protected $fillable = [
'name', 'username',
'email', 'email',
'password', 'password',
]; ];

View File

@ -21,6 +21,7 @@ import {
export default class LoginPage extends Page { export default class LoginPage extends Page {
loading: Cash; loading: Cash;
forgotPasswordLoading: Cash;
view(vnode) { view(vnode) {
return ( return (
@ -46,9 +47,14 @@ export default class LoginPage extends Page {
style="float: right;" style="float: right;"
onclick={this.onLoginButtonClicked.bind(this)} onclick={this.onLoginButtonClicked.bind(this)}
/> />
<mwc-button dense label="Password dimenticata" style="margin-top: 16px;"> <LoadingButton
<Mdi icon="lock-question" slot="icon"/> dense
</mwc-button> id="forgot-password-button"
label="Password dimenticata"
icon="lock-question"
style="margin-top: 16px;"
onclick={this.onForgotPasswordButtonClicked.bind(this)}
/>
</form> </form>
</mwc-card> </mwc-card>
); );
@ -58,6 +64,7 @@ export default class LoginPage extends Page {
super.oncreate(vnode); super.oncreate(vnode);
this.loading = $(this.element).find('#login-button mwc-circular-progress'); this.loading = $(this.element).find('#login-button mwc-circular-progress');
this.forgotPasswordLoading = $(this.element).find('#forgot-password-button mwc-circular-progress');
} }
async onLoginButtonClicked(event: PointerEvent) { async onLoginButtonClicked(event: PointerEvent) {
@ -81,6 +88,7 @@ export default class LoginPage extends Page {
data: formData data: formData
}); });
} catch (error) { } catch (error) {
// noinspection ES6MissingAwait
showSnackbar(Object.values(error.data.errors).join(' '), false); showSnackbar(Object.values(error.data.errors).join(' '), false);
this.loading.hide(); this.loading.hide();
return; return;
@ -88,4 +96,31 @@ export default class LoginPage extends Page {
window.location.href = window.route('dashboard'); window.location.href = window.route('dashboard');
} }
async onForgotPasswordButtonClicked() {
this.forgotPasswordLoading.show();
const field: HTMLFormElement = document.querySelector('#username');
field.type = 'email';
if (!field.reportValidity()) {
field.type = 'text';
return;
}
field.type = 'text';
try {
await redaxios.post(window.route('password.forgot'), {
email: field.value,
_token: $('meta[name="csrf-token"]').attr('content')
});
} catch (error) {
// noinspection ES6MissingAwait
showSnackbar(Object.values(error.data.errors).join(' '), false);
this.loading.hide();
return;
}
// noinspection ES6MissingAwait
showSnackbar(__('La password è stata inviata alla tua email'));
this.loading.hide();
}
} }

View File

@ -0,0 +1,103 @@
// noinspection DuplicatedCode
import '@maicol07/mwc-card';
import '@maicol07/mwc-layout-grid';
import '../WebComponents/TextField';
import {Inertia} from '@inertiajs/inertia';
import type {TextField} from '@material/mwc-textfield';
import type {Cash} from 'cash-dom';
import redaxios from 'redaxios';
// eslint-disable-next-line import/no-absolute-path
import logoUrl from '/images/logo_completo.png';
import LoadingButton from '../Components/LoadingButton.jsx';
import Mdi from '../Components/Mdi.jsx';
import Page from '../Components/Page.jsx';
import {
getFormData,
isFormValid,
showSnackbar
} from '../utils';
export default class ResetPasswordPage extends Page {
loading: Cash;
parameters: URLSearchParams;
oninit(vnode) {
super.oninit(vnode);
this.parameters = new URLSearchParams(window.location.search);
}
view(vnode) {
return (
<mwc-card outlined className="center ext-container ext-container-small">
<img src={logoUrl} className="center stretch" alt={__('OpenSTAManager')}/>
<form id="reset-password" style="padding: 16px; text-align: center;">
<h3 style="margin-top: 0;">{__('Reimposta password')}</h3>
<input hidden id="email" name="email" value={this.parameters.get('email')}/>
<input hidden id="token" name="token" value={this.parameters.get('token')}/>
<text-field label={__('Password')} id="password" name="password" required type="password">
<Mdi icon="lock-outline" slot="icon"/>
</text-field>
<text-field label={__('Conferma password')} id="password_confirm" name="password_confirm" type="password" required style="margin-top: 16px;">
<Mdi icon="repeat-variant" slot="icon"/>
</text-field>
<LoadingButton
type="submit"
raised
id="reset-password-button"
label={__('Resetta password')}
icon="lock-reset"
style="float: right;"
onclick={this.onResetPasswordButtonClicked.bind(this)}
/>
</form>
</mwc-card>
);
}
oncreate(vnode) {
super.oncreate(vnode);
this.loading = $(this.element).find('#reset-password mwc-circular-progress');
}
async onResetPasswordButtonClicked(event: PointerEvent) {
event.preventDefault();
this.loading.show();
const form = $(this.element).find('#reset-password');
const password: TextField = form.find('#password').get(0);
const passwordConfirm: TextField = form.find('#password_confirm').get(0);
passwordConfirm.setCustomValidity(
password.value !== passwordConfirm.value ? __('Le password non corrispondono') : ''
);
if (!isFormValid(form)) {
this.loading.hide();
return;
}
const formData = getFormData(form);
formData._token = $('meta[name="csrf-token"]').attr('content');
try {
await redaxios.put(window.route('password.resetPassword'), formData);
} catch (error) {
// noinspection ES6MissingAwait
showSnackbar(Object.values(error.data.errors).join(' '), false);
this.loading.hide();
return;
}
Inertia.visit('/');
// noinspection ES6MissingAwait
showSnackbar(__('Reset della password effettuato con successo. Puoi ora accedere.'));
}
}

View File

@ -37,16 +37,27 @@ Route::name('auth.')
->middleware('guest') ->middleware('guest')
->group(static function () { ->group(static function () {
Route::inertia('login', 'LoginPage') Route::inertia('login', 'LoginPage')
->name('login'); ->name('login');
/*Route::inertia('password-request', '')
->name('password-request');*/
Route::post('login', [AuthController::class, 'authenticate']) Route::post('login', [AuthController::class, 'authenticate'])
->name('authenticate'); ->name('authenticate');
/*Route::post('logout', 'Auth\LoginController@logout') /*Route::post('logout', 'Auth\LoginController@logout')
->name('auth.logout');*/ ->name('auth.logout');*/
}); });
Route::name('password.')
->middleware('guest')
->group(static function () {
Route::post('forgot', [AuthController::class, 'forgot'])
->name('forgot');
Route::inertia('reset', 'ResetPasswordPage')
->name('reset');
Route::post('reset', [AuthController::class, 'resetPassword'])
->name('resetPassword');
});
Route::name('setup.')->group(static function () { Route::name('setup.')->group(static function () {
Route::inertia('setup', 'SetupPage', [ Route::inertia('setup', 'SetupPage', [
'languages' => cache()->rememberForever('app.languages', fn () => array_map( 'languages' => cache()->rememberForever('app.languages', fn () => array_map(