Made authorization.
This commit is contained in:
parent
6b2aff910b
commit
f481ee765d
10
app/Contracts/FormRequestDto.php
Normal file
10
app/Contracts/FormRequestDto.php
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Contracts;
|
||||||
|
|
||||||
|
use App\Dto\Request\Dto;
|
||||||
|
|
||||||
|
interface FormRequestDto
|
||||||
|
{
|
||||||
|
public function getDto(): Dto;
|
||||||
|
}
|
27
app/Dto/Request/Authorization.php
Normal file
27
app/Dto/Request/Authorization.php
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Dto\Request;
|
||||||
|
|
||||||
|
final readonly class Authorization extends Dto
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private string $email,
|
||||||
|
private string $password,
|
||||||
|
private bool $remember = false
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public function getEmail(): string
|
||||||
|
{
|
||||||
|
return $this->email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPassword(): string
|
||||||
|
{
|
||||||
|
return $this->password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRemember(): bool
|
||||||
|
{
|
||||||
|
return $this->remember;
|
||||||
|
}
|
||||||
|
}
|
8
app/Dto/Request/Dto.php
Normal file
8
app/Dto/Request/Dto.php
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Dto\Request;
|
||||||
|
|
||||||
|
abstract readonly class Dto
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
47
app/Http/Controllers/AuthController.php
Normal file
47
app/Http/Controllers/AuthController.php
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Http\Requests\AuthorizationRequest;
|
||||||
|
use App\Services\AuthService;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Http\Response;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Illuminate\View\View;
|
||||||
|
use Illuminate\Http\RedirectResponse;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
final class AuthController extends Controller
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly AuthService $authService
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public function login(): View
|
||||||
|
{
|
||||||
|
return view('public/login');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function authorization(AuthorizationRequest $request)
|
||||||
|
{
|
||||||
|
$authorization = $request->getDto();
|
||||||
|
$result = $this->authService->authorization($authorization);
|
||||||
|
if (!$result->isSuccess()) {
|
||||||
|
if ($result->getCode() === Response::HTTP_UNAUTHORIZED) {
|
||||||
|
Log::warning('Unauthorized ' . $authorization->getEmail() . ' [' . $request->getClientIp() . ']');
|
||||||
|
}
|
||||||
|
return redirect()->route('login')->withInput()->withErrors($result->getMessage());
|
||||||
|
}
|
||||||
|
$request->session()->regenerate();
|
||||||
|
Log::notice('Logged in ' . $authorization->getEmail() . ' [' . $request->getClientIp() . ']');
|
||||||
|
return redirect()->route('home');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function logout(Request $request): RedirectResponse
|
||||||
|
{
|
||||||
|
Auth::logout();
|
||||||
|
$request->session()->invalidate();
|
||||||
|
$request->session()->regenerateToken();
|
||||||
|
return redirect(route('login'));
|
||||||
|
}
|
||||||
|
}
|
13
app/Http/Controllers/Controller.php
Normal file
13
app/Http/Controllers/Controller.php
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||||
|
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||||
|
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||||
|
use Illuminate\Routing\Controller as BaseController;
|
||||||
|
|
||||||
|
class Controller extends BaseController
|
||||||
|
{
|
||||||
|
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
|
||||||
|
}
|
23
app/Http/Controllers/Private/ProfileController.php
Normal file
23
app/Http/Controllers/Private/ProfileController.php
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Private;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\View\View;
|
||||||
|
|
||||||
|
final class ProfileController extends Controller
|
||||||
|
{
|
||||||
|
public function profile(): View
|
||||||
|
{
|
||||||
|
return view('private/profile/profile', [
|
||||||
|
'user' => Auth::user()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function settings(): View
|
||||||
|
{
|
||||||
|
return view('private/profile/settings', [
|
||||||
|
'user' => Auth::user()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
31
app/Http/Requests/AuthorizationRequest.php
Normal file
31
app/Http/Requests/AuthorizationRequest.php
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use App\Contracts\FormRequestDto;
|
||||||
|
use App\Dto\Request\Authorization;
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
|
final class AuthorizationRequest extends FormRequest implements FormRequestDto
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*/
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'email' => ['required', 'email', 'max:255'],
|
||||||
|
'password' => ['required', 'min:3'],
|
||||||
|
'remember' => ['nullable', 'boolean'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDto(): Authorization
|
||||||
|
{
|
||||||
|
return new Authorization(
|
||||||
|
email: $this->input('email'),
|
||||||
|
password: $this->input('password'),
|
||||||
|
remember: (bool) $this->input('remember', false)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
14
app/Repositories/UserRepository.php
Normal file
14
app/Repositories/UserRepository.php
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Repositories;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
final readonly class UserRepository
|
||||||
|
{
|
||||||
|
public function getUserByEmail(string $email): ?User
|
||||||
|
{
|
||||||
|
return User::query()->where('email', Str::lower($email))->first();
|
||||||
|
}
|
||||||
|
}
|
40
app/Services/AuthService.php
Normal file
40
app/Services/AuthService.php
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Services;
|
||||||
|
|
||||||
|
use App\Dto\Request\Authorization;
|
||||||
|
use App\Repositories\UserRepository;
|
||||||
|
use App\ServiceResults\ServiceResultArray;
|
||||||
|
use App\ServiceResults\ServiceResultError;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
|
||||||
|
final class AuthService extends Service
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly UserRepository $userRepository
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public function authorization(Authorization $authorization): ServiceResultError | ServiceResultArray
|
||||||
|
{
|
||||||
|
$user = $this->userRepository->getUserByEmail($authorization->getEmail());
|
||||||
|
if (is_null($user)) {
|
||||||
|
return $this->errUnauthorized(__('auth.failed'));
|
||||||
|
}
|
||||||
|
if (Hash::check($authorization->getPassword(), $user->password) !== true) {
|
||||||
|
return $this->errUnauthorized(__('auth.password'));
|
||||||
|
}
|
||||||
|
if ($user->is_active === false) {
|
||||||
|
return $this->errFobidden(__('auth.disabled'));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Auth::login($user, $authorization->getRemember());
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
report($e);
|
||||||
|
return $this->errService(__('Server Error'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->ok(__('auth.success'));
|
||||||
|
}
|
||||||
|
}
|
@ -5,32 +5,38 @@ namespace App\Services;
|
|||||||
|
|
||||||
use App\ServiceResults\ServiceResultArray;
|
use App\ServiceResults\ServiceResultArray;
|
||||||
use App\ServiceResults\ServiceResultError;
|
use App\ServiceResults\ServiceResultError;
|
||||||
|
use Illuminate\Http\Response;
|
||||||
|
|
||||||
abstract class Service
|
abstract class Service
|
||||||
{
|
{
|
||||||
final protected function errValidate(string $message, array $errors = []): ServiceResultError
|
final protected function errValidate(string $message, array $errors = []): ServiceResultError
|
||||||
{
|
{
|
||||||
return $this->error(422, $message, $errors);
|
return $this->error(Response::HTTP_UNPROCESSABLE_ENTITY, $message, $errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
final protected function errFobidden(string $message): ServiceResultError
|
final protected function errFobidden(string $message): ServiceResultError
|
||||||
{
|
{
|
||||||
return $this->error(403, $message);
|
return $this->error(Response::HTTP_FORBIDDEN, $message);
|
||||||
}
|
}
|
||||||
|
|
||||||
final protected function errNotFound(string $message): ServiceResultError
|
final protected function errNotFound(string $message): ServiceResultError
|
||||||
{
|
{
|
||||||
return $this->error(404, $message);
|
return $this->error(Response::HTTP_NOT_FOUND, $message);
|
||||||
}
|
}
|
||||||
|
|
||||||
final protected function errService(string $message): ServiceResultError
|
final protected function errService(string $message): ServiceResultError
|
||||||
{
|
{
|
||||||
return $this->error(500, $message);
|
return $this->error(Response::HTTP_INTERNAL_SERVER_ERROR, $message);
|
||||||
}
|
}
|
||||||
|
|
||||||
final protected function notAcceptable(string $message): ServiceResultError
|
final protected function notAcceptable(string $message): ServiceResultError
|
||||||
{
|
{
|
||||||
return $this->error(406, $message);
|
return $this->error(Response::HTTP_NOT_ACCEPTABLE, $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
final protected function errUnauthorized(string $message): ServiceResultError
|
||||||
|
{
|
||||||
|
return $this->error(Response::HTTP_UNAUTHORIZED, $message);
|
||||||
}
|
}
|
||||||
|
|
||||||
final protected function ok(string $message = 'OK'): ServiceResultArray
|
final protected function ok(string $message = 'OK'): ServiceResultArray
|
||||||
|
18
app/View/Components/Public/Layout.php
Normal file
18
app/View/Components/Public/Layout.php
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\View\Components\Public;
|
||||||
|
|
||||||
|
use Illuminate\View\Component;
|
||||||
|
use Illuminate\View\View;
|
||||||
|
|
||||||
|
final class Layout extends Component
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function render(): View
|
||||||
|
{
|
||||||
|
return view('public.layout.app');
|
||||||
|
}
|
||||||
|
}
|
8
config/rate_limiting.php
Normal file
8
config/rate_limiting.php
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
return [
|
||||||
|
/**
|
||||||
|
* Max limit of the hour.
|
||||||
|
*/
|
||||||
|
'login_max_request' => env('LOGIN_MAX_REQUEST', 50),
|
||||||
|
'login_max_email_request' => env('LOGIN_MAX_EMAIL_REQUEST', 10),
|
||||||
|
];
|
@ -6,4 +6,6 @@ return [
|
|||||||
'failed' => 'These credentials do not match our records.',
|
'failed' => 'These credentials do not match our records.',
|
||||||
'password' => 'The password is incorrect.',
|
'password' => 'The password is incorrect.',
|
||||||
'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
|
'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
|
||||||
|
'success' => 'The user is logged in.',
|
||||||
|
'disabled' => 'Disabled the user.',
|
||||||
];
|
];
|
||||||
|
@ -6,4 +6,6 @@ return [
|
|||||||
'failed' => 'Неверное имя пользователя или пароль.',
|
'failed' => 'Неверное имя пользователя или пароль.',
|
||||||
'password' => 'Некорректный пароль.',
|
'password' => 'Некорректный пароль.',
|
||||||
'throttle' => 'Слишком много попыток входа. Пожалуйста, попробуйте ещё раз через :seconds секунд.',
|
'throttle' => 'Слишком много попыток входа. Пожалуйста, попробуйте ещё раз через :seconds секунд.',
|
||||||
|
'success' => 'Пользователь вошел в систему.',
|
||||||
|
'disabled' => 'Отключили пользователя.',
|
||||||
];
|
];
|
||||||
|
28
resources/views/public/layout/app.blade.php
Normal file
28
resources/views/public/layout/app.blade.php
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
|
||||||
|
<title>@yield('meta_title', '')</title>
|
||||||
|
<meta name="keywords" content="@yield('meta_keywords', '')" />
|
||||||
|
<meta name="description" content="@yield('meta_description', '')" />
|
||||||
|
|
||||||
|
@vite('resources/volt/scss/app.scss')
|
||||||
|
|
||||||
|
<meta name="msapplication-TileColor" content="#ffffff">
|
||||||
|
<meta name="theme-color" content="#ffffff">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
<!-- Section -->
|
||||||
|
<section class="vh-lg-100 mt-5 mt-lg-0 bg-soft d-flex align-items-center">
|
||||||
|
<div class="container">
|
||||||
|
{{ $slot }}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
@vite('resources/volt/js/app.js')
|
||||||
|
</body>
|
||||||
|
</html>
|
60
resources/views/public/login.blade.php
Normal file
60
resources/views/public/login.blade.php
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
@section('meta_title', __('Sign in to our platform'))
|
||||||
|
<x-public.layout>
|
||||||
|
<div class="row justify-content-center form-bg-image" data-background-lg="{{ Vite::asset('resources/volt/images/illustrations/signin.svg') }}">
|
||||||
|
<div class="col-12 d-flex align-items-center justify-content-center">
|
||||||
|
<div class="bg-white shadow border-0 rounded border-light p-4 p-lg-5 w-100 fmxw-500">
|
||||||
|
<div class="text-center text-md-center mb-4 mt-md-0">
|
||||||
|
<h1 class="mb-0 h3">{{ __('Sign in to our platform') }}</h1>
|
||||||
|
</div>
|
||||||
|
@if ($errors->any())
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
<ul>
|
||||||
|
@foreach ($errors->all() as $error)
|
||||||
|
<li>{{ $error }}</li>
|
||||||
|
@endforeach
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
<form action="{{ route('authorization') }}" class="mt-4" method="post">
|
||||||
|
@csrf
|
||||||
|
<!-- Form -->
|
||||||
|
<div class="form-group mb-4">
|
||||||
|
<label for="email">{{ __('Your Email') }}</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<span class="input-group-text" id="basic-addon1">
|
||||||
|
<svg class="icon icon-xs text-gray-600" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M2.003 5.884L10 9.882l7.997-3.998A2 2 0 0016 4H4a2 2 0 00-1.997 1.884z"></path><path d="M18 8.118l-8 4-8-4V14a2 2 0 002 2h12a2 2 0 002-2V8.118z"></path></svg>
|
||||||
|
</span>
|
||||||
|
<input type="email" name="email" class="form-control" placeholder="example@company.com" id="email" autofocus required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- End of Form -->
|
||||||
|
<div class="form-group">
|
||||||
|
<!-- Form -->
|
||||||
|
<div class="form-group mb-4">
|
||||||
|
<label for="password">{{ __('Your Password') }}</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<span class="input-group-text" id="basic-addon2">
|
||||||
|
<svg class="icon icon-xs text-gray-600" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5 9V7a5 5 0 0110 0v2a2 2 0 012 2v5a2 2 0 01-2 2H5a2 2 0 01-2-2v-5a2 2 0 012-2zm8-2v2H7V7a3 3 0 016 0z" clip-rule="evenodd"></path></svg>
|
||||||
|
</span>
|
||||||
|
<input type="password" name="password" placeholder="Password" class="form-control" id="password" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- End of Form -->
|
||||||
|
<div class="d-flex justify-content-between align-items-top mb-4">
|
||||||
|
<div class="form-check">
|
||||||
|
<input name="remember" type="hidden" value="0">
|
||||||
|
<input class="form-check-input" name="remember" type="checkbox" value="1" id="remember">
|
||||||
|
<label class="form-check-label mb-0" for="remember">
|
||||||
|
{{ __('Remember me') }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d-grid">
|
||||||
|
<button type="submit" class="btn btn-gray-800">{{ __('Sign in') }}</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</x-public.layout>
|
@ -12,3 +12,17 @@ use Illuminate\Support\Facades\Route;
|
|||||||
| be assigned to the "web" middleware group. Make something great!
|
| be assigned to the "web" middleware group. Make something great!
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
Route::middleware('guest')->group(function () {
|
||||||
|
Route::get('login', [\App\Http\Controllers\AuthController::class, 'login'])->name('login');
|
||||||
|
Route::middleware(['throttle:login'])->post('login', [\App\Http\Controllers\AuthController::class, 'authorization'])->name('authorization');
|
||||||
|
});
|
||||||
|
Route::middleware(['auth', 'verified'])->group(function () {
|
||||||
|
Route::post('logout', [\App\Http\Controllers\AuthController::class, 'logout'])->name('logout');
|
||||||
|
Route::get('/', [\App\Http\Controllers\Private\DashboardController::class, 'index'])->name('home');
|
||||||
|
Route::prefix('profile')->as('profile.')
|
||||||
|
->group(function () {
|
||||||
|
Route::get('/', [\App\Http\Controllers\Private\ProfileController::class, 'profile'])->name('edit');
|
||||||
|
Route::get('settings', [\App\Http\Controllers\Private\ProfileController::class, 'settings'])->name('settings');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user