1
0
mirror of https://github.com/devcode-it/openstamanager.git synced 2025-02-23 14:57:46 +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;
use App\Models\User;
use Illuminate\Auth\Events\PasswordReset;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
use JetBrains\PhpStorm\ArrayShape;
@ -39,21 +44,41 @@ class AuthController extends Controller
], Response::HTTP_BAD_REQUEST);
}
#[ArrayShape(['username' => 'string', 'password' => 'string', 'remember' => 'string'])]
private function rules(Request $request): array
public function forgotPassword(Request $request): Response|JsonResponse
{
$additional_validation = '';
$db_field = 'username';
if (filter_var($request->input('username'), FILTER_VALIDATE_EMAIL)) {
$additional_validation = '|email';
$db_field = 'email';
$request->validate([
'email' => 'required|email|exists:users,email',
]);
$response = Password::broker()->sendResetLink($request->input('email'));
return $response === Password::RESET_LINK_SENT
? response()->noContent()
: \response()->json(['errors' => ['email' => [__($response)]]], Response::HTTP_BAD_REQUEST);
}
return [
'username' => "required|string|exists:users,$db_field|$additional_validation",
'password' => 'required|string',
'remember' => 'boolean',
];
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();
}
#[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
*/
protected $fillable = [
'name',
'username',
'email',
'password',
];

View File

@ -21,6 +21,7 @@ import {
export default class LoginPage extends Page {
loading: Cash;
forgotPasswordLoading: Cash;
view(vnode) {
return (
@ -46,9 +47,14 @@ export default class LoginPage extends Page {
style="float: right;"
onclick={this.onLoginButtonClicked.bind(this)}
/>
<mwc-button dense label="Password dimenticata" style="margin-top: 16px;">
<Mdi icon="lock-question" slot="icon"/>
</mwc-button>
<LoadingButton
dense
id="forgot-password-button"
label="Password dimenticata"
icon="lock-question"
style="margin-top: 16px;"
onclick={this.onForgotPasswordButtonClicked.bind(this)}
/>
</form>
</mwc-card>
);
@ -58,6 +64,7 @@ export default class LoginPage extends Page {
super.oncreate(vnode);
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) {
@ -81,6 +88,7 @@ export default class LoginPage extends Page {
data: formData
});
} catch (error) {
// noinspection ES6MissingAwait
showSnackbar(Object.values(error.data.errors).join(' '), false);
this.loading.hide();
return;
@ -88,4 +96,31 @@ export default class LoginPage extends Page {
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

@ -38,15 +38,26 @@ Route::name('auth.')
->group(static function () {
Route::inertia('login', 'LoginPage')
->name('login');
/*Route::inertia('password-request', '')
->name('password-request');*/
Route::post('login', [AuthController::class, 'authenticate'])
->name('authenticate');
/*Route::post('logout', 'Auth\LoginController@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::inertia('setup', 'SetupPage', [
'languages' => cache()->rememberForever('app.languages', fn () => array_map(