feat: ✨ add users password and configure authentication based on them
This commit is contained in:
parent
4d5afbf9fd
commit
290754b785
44 changed files with 1507 additions and 641 deletions
153
app/Console/Commands/MakeUser.php
Normal file
153
app/Console/Commands/MakeUser.php
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Date;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Validation\Rules\Password;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Uwil\LaravelBackend\Support\UserRoleRegistry;
|
||||
|
||||
/**
|
||||
* Artisan command to create a support admin user.
|
||||
*/
|
||||
class MakeUser extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
#[\Override]
|
||||
protected $signature = 'support:make:user';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
#[\Override]
|
||||
protected $description = 'Create a new Support user.';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
$this->components->info("Creating a new <comment>Support user</comment>...");
|
||||
|
||||
$firstName = $this->askValid(
|
||||
'first_name',
|
||||
'First name',
|
||||
'Admin',
|
||||
['required', 'min:3', 'max:255'],
|
||||
);
|
||||
$lastName = $this->askValid(
|
||||
'last_name',
|
||||
'Last name',
|
||||
'Saïtama',
|
||||
['required', 'min:3', 'max:255'],
|
||||
);
|
||||
$email = $this->askValid(
|
||||
'email',
|
||||
'Email',
|
||||
'equipe.web@ldc.fr',
|
||||
['required', 'unique:users,email', 'email:rfc,strict,dns,spoof,filter'],
|
||||
);
|
||||
$role = $this->askRole();
|
||||
$password = $this->askPassword();
|
||||
|
||||
$user = new User([
|
||||
'first_name' => $firstName,
|
||||
'last_name' => $lastName,
|
||||
'email' => $email,
|
||||
'role' => $role->value,
|
||||
'published' => true,
|
||||
'published_at' => Date::now(),
|
||||
]);
|
||||
$user->saveOrFail();
|
||||
|
||||
$user->userPassword()->create([
|
||||
'password' => bcrypt($password),
|
||||
]);
|
||||
|
||||
$this->components->success("User <comment>{$firstName} {$lastName}</comment> created successfully.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask for a value and validate it, re-prompting on failure.
|
||||
*
|
||||
* @param string $field
|
||||
* @param string $label
|
||||
* @param string $default
|
||||
* @param array<int, string> $rules
|
||||
* @return string
|
||||
*/
|
||||
private function askValid(string $field, string $label, string $default, array $rules): string
|
||||
{
|
||||
try {
|
||||
$value = $this->ask(sprintf("Type the wanted user %s", $label), $default);
|
||||
$validator = Validator::make([$field => $value], [$field => $rules]);
|
||||
$validator->validate();
|
||||
return $value;
|
||||
} catch (ValidationException $e) {
|
||||
$this->components->error(
|
||||
collect($e->errors())->flatten()->first()
|
||||
);
|
||||
return $this->askValid($field, $label, $default, $rules);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask for a role and return the corresponding RoleEnum.
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
private function askRole(): object
|
||||
{
|
||||
$options = collect(UserRoleRegistry::all())->mapWithKeys(fn(object $role) => [
|
||||
Str::ucFirst($role->label) => $role
|
||||
]);
|
||||
|
||||
$choice = $this->choice('Role', $options->keys()->all(), Str::ucFirst(UserRoleRegistry::get('client')->label));
|
||||
$choiceKey = is_string($choice) ? $choice : (string) $choice[0];
|
||||
return $options->get($choiceKey) ?? UserRoleRegistry::get('client');
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask for a password and confirmation password.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function askPassword(): string
|
||||
{
|
||||
$password = $this->secret('Type the wanted user Password');
|
||||
$passwordConfirmation = $this->secret('Confirm the password');
|
||||
|
||||
try {
|
||||
Validator::make(
|
||||
[
|
||||
'password' => $password,
|
||||
'password_confirmation' => $passwordConfirmation,
|
||||
],
|
||||
[
|
||||
'password' => [
|
||||
'required',
|
||||
'confirmed',
|
||||
'max:255',
|
||||
Password::min(8)->letters()->mixedCase()->numbers()->symbols()->uncompromised(),
|
||||
],
|
||||
]
|
||||
)->validate();
|
||||
return $password;
|
||||
} catch (ValidationException $e) {
|
||||
$this->components->error(collect($e->errors())->flatten()->first());
|
||||
return $this->askPassword();
|
||||
}
|
||||
}
|
||||
}
|
||||
135
app/Http/Controllers/Auth/AuthenticatedSessionController.php
Normal file
135
app/Http/Controllers/Auth/AuthenticatedSessionController.php
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Requests\Auth\ForgotPasswordRequest;
|
||||
use App\Http\Requests\Auth\LoginRequest;
|
||||
use App\Http\Requests\Auth\ResetPasswordRequest;
|
||||
use App\Mails\Auth\ResetPassword;
|
||||
use App\Models\User;
|
||||
use App\Models\UserPassword;
|
||||
use Illuminate\Auth\Events\PasswordReset;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Routing\Controller;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Facades\Password;
|
||||
use Illuminate\Support\Str;
|
||||
use Inertia\Inertia;
|
||||
|
||||
/**
|
||||
* Manages backend authentication sessions (login/logout).
|
||||
*/
|
||||
class AuthenticatedSessionController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display the login view.
|
||||
*
|
||||
* @return \Inertia\Response
|
||||
*/
|
||||
public function login(): \Inertia\Response
|
||||
{
|
||||
return Inertia::render('back/Pages/Auth/Login');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming authentication request.
|
||||
*
|
||||
* @param \App\Http\Requests\Auth\LoginRequest $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function postLogin(LoginRequest $request): \Illuminate\Http\RedirectResponse
|
||||
{
|
||||
$request->authenticate();
|
||||
$request->session()->regenerate();
|
||||
|
||||
return redirect()->intended(route(config('uwil-backend.routes.name') . 'dashboard.index', absolute: false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy an authenticated session.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function logout(Request $request): \Illuminate\Http\RedirectResponse
|
||||
{
|
||||
Auth::guard('backend')->logout();
|
||||
|
||||
$request->session()->invalidate();
|
||||
$request->session()->regenerateToken();
|
||||
|
||||
return to_route(config('uwil-backend.routes.name') . 'login');
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the forgotten password view.
|
||||
*
|
||||
* @return \Inertia\Response
|
||||
*/
|
||||
public function forgotPassword(): \Inertia\Response
|
||||
{
|
||||
return Inertia::render('back/Pages/Auth/ForgotPassword');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming forgotten password request.
|
||||
*
|
||||
* @param \App\Http\Requests\Auth\ForgotPasswordRequest $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function postForgotPassword(ForgotPasswordRequest $request): \Illuminate\Http\RedirectResponse
|
||||
{
|
||||
$validatedData = $request->validated();
|
||||
$status = Password::broker('backend')->sendResetLink($validatedData);
|
||||
|
||||
return $status === Password::RESET_LINK_SENT
|
||||
? to_route(config('uwil-backend.routes.name') . 'login')
|
||||
: back();
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the view.
|
||||
*
|
||||
* @param string $token
|
||||
* @return \Inertia\Response
|
||||
*/
|
||||
public function resetPassword(string $token): \Inertia\Response
|
||||
{
|
||||
return Inertia::render('back/Pages/Auth/ResetPassword', [
|
||||
'token' => $token,
|
||||
'email' => request()->query('email', ''),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming resetting password request.
|
||||
*
|
||||
* @param \App\Http\Requests\Auth\ResetPasswordRequest $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function postResetPassword(ResetPasswordRequest $request): \Illuminate\Http\RedirectResponse
|
||||
{
|
||||
$status = Password::broker('backend')->reset(
|
||||
$request->validated(),
|
||||
function (User $user, string $password): void {
|
||||
$user->setRememberToken(Str::random(60));
|
||||
$user->save();
|
||||
|
||||
UserPassword::updateOrCreate(
|
||||
['user_id' => $user->getKey()],
|
||||
['password' => Hash::make($password)],
|
||||
);
|
||||
|
||||
event(new PasswordReset($user));
|
||||
|
||||
Mail::send(new ResetPassword($user));
|
||||
}
|
||||
);
|
||||
|
||||
return $status === Password::PASSWORD_RESET
|
||||
? to_route(config('uwil-backend.routes.name') . 'login')
|
||||
: back();
|
||||
}
|
||||
}
|
||||
|
|
@ -21,10 +21,9 @@ class HandleInertiaRequests extends Middleware
|
|||
...InertiaHelper::errorProps(parent::share($request)),
|
||||
// More props here.
|
||||
];
|
||||
|
||||
return array_replace_recursive(
|
||||
$props,
|
||||
InertiaSharedRegistry::resolve($request)
|
||||
InertiaSharedRegistry::resolve($request),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
35
app/Http/Requests/Auth/ForgotPasswordRequest.php
Normal file
35
app/Http/Requests/Auth/ForgotPasswordRequest.php
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Auth;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
/**
|
||||
* Handles backend forgotten password form validation.
|
||||
*/
|
||||
class ForgotPasswordRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'email' => 'required|string|email:rfc,strict,dns,spoof,filter',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get custom attributes for validator errors.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function attributes(): array
|
||||
{
|
||||
return [
|
||||
'email' => trans('validation.attributes.email'),
|
||||
];
|
||||
}
|
||||
}
|
||||
109
app/Http/Requests/Auth/LoginRequest.php
Normal file
109
app/Http/Requests/Auth/LoginRequest.php
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Auth;
|
||||
|
||||
use Illuminate\Auth\Events\Lockout;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\RateLimiter;
|
||||
use Illuminate\Validation\Rules\Password;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Uwil\LaravelBackend\Requests\Auth\LoginRequest as BackendLoginRequest;
|
||||
|
||||
/**
|
||||
* Handles backend login form validation, authentication and rate limiting.
|
||||
*/
|
||||
class LoginRequest extends BackendLoginRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
#[\Override]
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'email' => 'required|string|email:rfc,strict,dns,spoof,filter',
|
||||
'password' => [
|
||||
'required',
|
||||
'max:255',
|
||||
Password::min(8)
|
||||
->letters()
|
||||
->mixedCase()
|
||||
->numbers()
|
||||
->symbols()
|
||||
->uncompromised()
|
||||
],
|
||||
'remember' => 'boolean',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get custom attributes for validator errors.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function attributes(): array
|
||||
{
|
||||
return [
|
||||
'email' => trans('validation.attributes.email'),
|
||||
'password' => trans('validation.attributes.password'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to authenticate the request's credentials.
|
||||
*
|
||||
* @return void
|
||||
* @throws \Illuminate\Validation\ValidationException Wrong email.
|
||||
*/
|
||||
#[\Override]
|
||||
public function authenticate(): void
|
||||
{
|
||||
$this->ensureIsNotRateLimited();
|
||||
|
||||
$user = \App\Models\User::query()
|
||||
->where('email', $this->input('email'))
|
||||
->first();
|
||||
|
||||
if (
|
||||
!$user
|
||||
|| !$user->userPassword
|
||||
|| !Hash::check($this->input('password'), $user->userPassword->password)
|
||||
) {
|
||||
RateLimiter::hit($this->throttleKey());
|
||||
throw ValidationException::withMessages([
|
||||
'email' => trans('auth.failed'),
|
||||
]);
|
||||
}
|
||||
|
||||
Auth::guard('backend')->login($user, $this->boolean('remember'));
|
||||
RateLimiter::clear($this->throttleKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the login request is not rate limited.
|
||||
*
|
||||
* @return void
|
||||
* @throws \Illuminate\Validation\ValidationException Wrong email.
|
||||
*/
|
||||
#[\Override]
|
||||
public function ensureIsNotRateLimited(): void
|
||||
{
|
||||
if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) {
|
||||
return;
|
||||
}
|
||||
|
||||
event(new Lockout($this));
|
||||
|
||||
$seconds = RateLimiter::availableIn($this->throttleKey());
|
||||
|
||||
throw ValidationException::withMessages([
|
||||
'email' => trans('auth.throttle', [
|
||||
'seconds' => $seconds,
|
||||
'minutes' => ceil($seconds / 60),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
}
|
||||
47
app/Http/Requests/Auth/ResetPasswordRequest.php
Normal file
47
app/Http/Requests/Auth/ResetPasswordRequest.php
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Auth;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rules\Password;
|
||||
|
||||
/**
|
||||
* Handles backend resetting password form validation.
|
||||
*/
|
||||
class ResetPasswordRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$passwordRules = Password::min(8)->letters()->mixedCase()->numbers()->symbols()->uncompromised();
|
||||
return [
|
||||
'token' => 'required|string',
|
||||
'email' => 'required|string|email:rfc,strict,dns,spoof,filter',
|
||||
'password' => [
|
||||
'required',
|
||||
'required_with:password_confirmation',
|
||||
'confirmed',
|
||||
'max:255',
|
||||
$passwordRules,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get custom attributes for validator errors.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function attributes(): array
|
||||
{
|
||||
return [
|
||||
'token' => trans('validation.custom.token'),
|
||||
'email' => trans('validation.attributes.email'),
|
||||
'password' => trans('validation.attributes.password'),
|
||||
];
|
||||
}
|
||||
}
|
||||
51
app/Mails/Auth/LinkResetPasswordMail.php
Normal file
51
app/Mails/Auth/LinkResetPasswordMail.php
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
namespace App\Mails\Auth;
|
||||
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
use Illuminate\Mail\Mailables\Envelope;
|
||||
|
||||
class LinkResetPasswordMail extends Mailable
|
||||
{
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*/
|
||||
public function __construct(
|
||||
public string $url,
|
||||
public mixed $user,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Get the message envelope.
|
||||
*
|
||||
* @return \Illuminate\Mail\Mailables\Envelope
|
||||
*/
|
||||
public function envelope(): \Illuminate\Mail\Mailables\Envelope
|
||||
{
|
||||
return new Envelope(
|
||||
to: $this->user->getEmailForPasswordReset(),
|
||||
subject: sprintf(
|
||||
'%s - %s',
|
||||
trans('auth.email_title_reset_password'),
|
||||
config('app.name')
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message content definition.
|
||||
*
|
||||
* @return \Illuminate\Mail\Mailables\Content
|
||||
*/
|
||||
public function content(): \Illuminate\Mail\Mailables\Content
|
||||
{
|
||||
return new Content(
|
||||
markdown: 'back.mails.link-reset-password',
|
||||
with: [
|
||||
'url' => $this->url,
|
||||
'user' => $this->user,
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
60
app/Mails/Auth/ResetPassword.php
Normal file
60
app/Mails/Auth/ResetPassword.php
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
namespace App\Mails\Auth;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
use Illuminate\Mail\Mailables\Envelope;
|
||||
|
||||
class ResetPassword extends Mailable
|
||||
{
|
||||
/**
|
||||
* @var \App\Models\User $user
|
||||
*/
|
||||
private User $user;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*
|
||||
* @param \App\Models\User $user
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(User $user)
|
||||
{
|
||||
$this->user = clone $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message envelope.
|
||||
*
|
||||
* @return \Illuminate\Mail\Mailables\Envelope
|
||||
*/
|
||||
public function envelope(): \Illuminate\Mail\Mailables\Envelope
|
||||
{
|
||||
return new Envelope(
|
||||
to: $this->user->getEmailForPasswordReset(),
|
||||
subject: sprintf(
|
||||
'%s - %s',
|
||||
trans('auth.email_title_reset_complete'),
|
||||
config('app.name')
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message content definition.
|
||||
*
|
||||
* @return \Illuminate\Mail\Mailables\Content
|
||||
*/
|
||||
public function content(): \Illuminate\Mail\Mailables\Content
|
||||
{
|
||||
return new Content(
|
||||
markdown: 'back.mails.reset-password',
|
||||
with: [
|
||||
'first_name' => $this->user->first_name,
|
||||
'last_name' => $this->user->last_name,
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
61
app/Models/User.php
Normal file
61
app/Models/User.php
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Mails\Auth\LinkResetPasswordMail;
|
||||
use App\Models\UserPassword;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Uwil\LaravelBackend\Models\User as BackendUser;
|
||||
|
||||
/**
|
||||
* Extends the package User model with password-based authentication support.
|
||||
*
|
||||
* @property-read \App\Models\UserPassword|null $userPassword Associated password.
|
||||
* @see \Uwil\LaravelBackend\Models\User For all other properties and methods.
|
||||
*/
|
||||
class User extends BackendUser
|
||||
{
|
||||
/**
|
||||
* Get the password for authentication.
|
||||
* Retrieves the hashed password from the related user_passwords table.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
#[\Override]
|
||||
public function getAuthPassword(): string
|
||||
{
|
||||
return $this->userPassword()->value('password') ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the password reset notification to the user.
|
||||
*
|
||||
* @param string $token
|
||||
* @return void
|
||||
*/
|
||||
// phpstan-ignore-next-line
|
||||
// phpcs:ignore
|
||||
#[\Override]
|
||||
public function sendPasswordResetNotification($token): void
|
||||
{
|
||||
$url = sprintf(
|
||||
"%s?email=%s",
|
||||
url(route('back.reset-password', ['token' => $token], false)),
|
||||
urlencode($this->getEmailForPasswordReset())
|
||||
);
|
||||
|
||||
Mail::send(new LinkResetPasswordMail($url, $this));
|
||||
}
|
||||
|
||||
// * RELATIONSHIPS.
|
||||
|
||||
/**
|
||||
* Get the user's associated password.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasOne<\App\Models\UserPassword>
|
||||
*/
|
||||
public function userPassword(): \Illuminate\Database\Eloquent\Relations\HasOne
|
||||
{
|
||||
return $this->hasOne(UserPassword::class);
|
||||
}
|
||||
}
|
||||
25
app/Models/UserPassword.php
Normal file
25
app/Models/UserPassword.php
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @property \App\Models\User $user_id User associated.
|
||||
* @property string $password User password.
|
||||
* @property-read \Illuminate\Support\Carbon $created_at Date on which the password was created.
|
||||
* @property-read \Illuminate\Support\Carbon $updated_at Date on which the password was updated.
|
||||
*/
|
||||
class UserPassword extends Model
|
||||
{
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var list<string>
|
||||
*/
|
||||
#[\Override]
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'password',
|
||||
];
|
||||
}
|
||||
22
app/Providers/AppServiceProvider.php
Normal file
22
app/Providers/AppServiceProvider.php
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Uwil\LaravelBackend\Support\UserRoleRegistry;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
// Set new role for users.
|
||||
UserRoleRegistry::add(3, 'project-manager', trans('enums.user-role.project-manager'));
|
||||
UserRoleRegistry::add(4, 'developper', trans('enums.user-role.developper'));
|
||||
UserRoleRegistry::add(5, 'client', trans('enums.user-role.client'));
|
||||
}
|
||||
}
|
||||
|
|
@ -13,6 +13,7 @@ use Uwil\LaravelCommon\Helpers\InertiaHelper;
|
|||
return Application::configure(basePath: dirname(__DIR__))
|
||||
->withRouting(
|
||||
health: '/up',
|
||||
web: __DIR__ . '/../routes/auth.php',
|
||||
then: function () {
|
||||
Route::middleware(['web', 'throttle:5,2'])
|
||||
->post('/csp-report', function () {
|
||||
|
|
@ -55,13 +56,8 @@ return Application::configure(basePath: dirname(__DIR__))
|
|||
$middleware->preventRequestForgery(except: [
|
||||
'/csp-report',
|
||||
]);
|
||||
$middleware->group('saml', [
|
||||
\Uwil\LaravelBackend\Middlewares\EncryptCookies::class,
|
||||
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
|
||||
\Illuminate\Session\Middleware\StartSession::class,
|
||||
]);
|
||||
$middleware->alias([
|
||||
'auth' => \Uwil\LaravelBackend\Middlewares\Authenticate::class,
|
||||
'auth' => \Uwil\LaravelBackend\Middlewares\Authenticate::class,
|
||||
'guest' => \Uwil\LaravelBackend\Middlewares\RedirectIfAuthenticated::class,
|
||||
]);
|
||||
})
|
||||
|
|
@ -89,4 +85,8 @@ return Application::configure(basePath: dirname(__DIR__))
|
|||
->withInput($safeInput)
|
||||
->withErrors($e->errors());
|
||||
});
|
||||
})->create();
|
||||
})
|
||||
->withCommands([
|
||||
\App\Console\Commands\MakeUser::class,
|
||||
])
|
||||
->create();
|
||||
|
|
|
|||
|
|
@ -4,4 +4,5 @@ return [
|
|||
Uwil\LaravelCommon\Providers\AppServiceProvider::class,
|
||||
Uwil\LaravelCommon\Providers\TranslationServiceProvider::class,
|
||||
Uwil\K8S\Laravel\K8SServiceProvider::class,
|
||||
App\Providers\AppServiceProvider::class,
|
||||
];
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@
|
|||
"laravel/tinker": "^3.0",
|
||||
"monolog/monolog": "^3.0",
|
||||
"psr/log": "^3.0",
|
||||
"scaler-tech/laravel-saml2": "^2.7",
|
||||
"tightenco/ziggy": "^2.5",
|
||||
"uwil/admin-secure-laravel": "^5.1",
|
||||
"uwil/laravel-backend": "^5.0",
|
||||
|
|
|
|||
346
composer.lock
generated
346
composer.lock
generated
|
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "ced98c57935b85c05a2f58f91f5347de",
|
||||
"content-hash": "c7008e1b25e55e3dd4f28ec944534e12",
|
||||
"packages": [
|
||||
{
|
||||
"name": "brick/math",
|
||||
|
|
@ -1468,16 +1468,16 @@
|
|||
},
|
||||
{
|
||||
"name": "laravel/framework",
|
||||
"version": "v13.6.0",
|
||||
"version": "v13.7.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/framework.git",
|
||||
"reference": "416a93ea9c53161e0d4b8a44045f447b65a7d2f1"
|
||||
"reference": "f13b85b2cce7ef5e8f3bcdf2b6c6364bbdedae0b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/416a93ea9c53161e0d4b8a44045f447b65a7d2f1",
|
||||
"reference": "416a93ea9c53161e0d4b8a44045f447b65a7d2f1",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/f13b85b2cce7ef5e8f3bcdf2b6c6364bbdedae0b",
|
||||
"reference": "f13b85b2cce7ef5e8f3bcdf2b6c6364bbdedae0b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -1520,6 +1520,7 @@
|
|||
"symfony/mime": "^7.4.0 || ^8.0.0",
|
||||
"symfony/polyfill-php84": "^1.33",
|
||||
"symfony/polyfill-php85": "^1.33",
|
||||
"symfony/polyfill-php86": "^1.36",
|
||||
"symfony/process": "^7.4.5 || ^8.0.5",
|
||||
"symfony/routing": "^7.4.0 || ^8.0.0",
|
||||
"symfony/uid": "^7.4.0 || ^8.0.0",
|
||||
|
|
@ -1687,7 +1688,7 @@
|
|||
"issues": "https://github.com/laravel/framework/issues",
|
||||
"source": "https://github.com/laravel/framework"
|
||||
},
|
||||
"time": "2026-04-21T13:32:11+00:00"
|
||||
"time": "2026-04-28T17:18:25+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/prompts",
|
||||
|
|
@ -1806,16 +1807,16 @@
|
|||
},
|
||||
{
|
||||
"name": "laravel/serializable-closure",
|
||||
"version": "v2.0.12",
|
||||
"version": "v2.0.13",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/serializable-closure.git",
|
||||
"reference": "a6abb4e54f6fcd3138120b9ad497f0bd146f9919"
|
||||
"reference": "b566ee0dd251f3c4078bed003a7ce015f5ea6dce"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/serializable-closure/zipball/a6abb4e54f6fcd3138120b9ad497f0bd146f9919",
|
||||
"reference": "a6abb4e54f6fcd3138120b9ad497f0bd146f9919",
|
||||
"url": "https://api.github.com/repos/laravel/serializable-closure/zipball/b566ee0dd251f3c4078bed003a7ce015f5ea6dce",
|
||||
"reference": "b566ee0dd251f3c4078bed003a7ce015f5ea6dce",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -1863,7 +1864,7 @@
|
|||
"issues": "https://github.com/laravel/serializable-closure/issues",
|
||||
"source": "https://github.com/laravel/serializable-closure"
|
||||
},
|
||||
"time": "2026-04-14T13:33:34+00:00"
|
||||
"time": "2026-04-16T14:03:50+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/telescope",
|
||||
|
|
@ -3446,16 +3447,16 @@
|
|||
},
|
||||
{
|
||||
"name": "phpseclib/phpseclib",
|
||||
"version": "2.0.53",
|
||||
"version": "2.0.54",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpseclib/phpseclib.git",
|
||||
"reference": "2d1a664b940b9b8f367185307dc010d11a2790f3"
|
||||
"reference": "a96a835067c39ee7a709329fe70869817da18081"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/2d1a664b940b9b8f367185307dc010d11a2790f3",
|
||||
"reference": "2d1a664b940b9b8f367185307dc010d11a2790f3",
|
||||
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/a96a835067c39ee7a709329fe70869817da18081",
|
||||
"reference": "a96a835067c39ee7a709329fe70869817da18081",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -3536,7 +3537,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/phpseclib/phpseclib/issues",
|
||||
"source": "https://github.com/phpseclib/phpseclib/tree/2.0.53"
|
||||
"source": "https://github.com/phpseclib/phpseclib/tree/2.0.54"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -3552,7 +3553,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-04-10T01:30:02+00:00"
|
||||
"time": "2026-04-27T06:59:24+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/clock",
|
||||
|
|
@ -5663,20 +5664,20 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/options-resolver",
|
||||
"version": "v7.4.8",
|
||||
"version": "v8.0.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/options-resolver.git",
|
||||
"reference": "2888fcdc4dc2fd5f7c7397be78631e8af12e02b4"
|
||||
"reference": "b48bce0a70b914f6953dafbd10474df232ed4de8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/2888fcdc4dc2fd5f7c7397be78631e8af12e02b4",
|
||||
"reference": "2888fcdc4dc2fd5f7c7397be78631e8af12e02b4",
|
||||
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/b48bce0a70b914f6953dafbd10474df232ed4de8",
|
||||
"reference": "b48bce0a70b914f6953dafbd10474df232ed4de8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.2",
|
||||
"php": ">=8.4",
|
||||
"symfony/deprecation-contracts": "^2.5|^3"
|
||||
},
|
||||
"type": "library",
|
||||
|
|
@ -5710,7 +5711,7 @@
|
|||
"options"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/options-resolver/tree/v7.4.8"
|
||||
"source": "https://github.com/symfony/options-resolver/tree/v8.0.8"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -5730,11 +5731,11 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-03-24T13:12:05+00:00"
|
||||
"time": "2026-03-30T15:14:47+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
"version": "v1.36.0",
|
||||
"version": "v1.37.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-ctype.git",
|
||||
|
|
@ -5793,7 +5794,7 @@
|
|||
"portable"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.36.0"
|
||||
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.37.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -5817,16 +5818,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/polyfill-intl-grapheme",
|
||||
"version": "v1.36.0",
|
||||
"version": "v1.37.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-intl-grapheme.git",
|
||||
"reference": "ad1b7b9092976d6c948b8a187cec9faaea9ec1df"
|
||||
"reference": "4864388bfbd3001ce88e234fab652acd91fdc57e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/ad1b7b9092976d6c948b8a187cec9faaea9ec1df",
|
||||
"reference": "ad1b7b9092976d6c948b8a187cec9faaea9ec1df",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/4864388bfbd3001ce88e234fab652acd91fdc57e",
|
||||
"reference": "4864388bfbd3001ce88e234fab652acd91fdc57e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -5875,7 +5876,7 @@
|
|||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.36.0"
|
||||
"source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.37.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -5895,11 +5896,11 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-04-10T16:19:22+00:00"
|
||||
"time": "2026-04-26T13:13:48+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-intl-idn",
|
||||
"version": "v1.36.0",
|
||||
"version": "v1.37.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-intl-idn.git",
|
||||
|
|
@ -5962,7 +5963,7 @@
|
|||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.36.0"
|
||||
"source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.37.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -5986,7 +5987,7 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/polyfill-intl-normalizer",
|
||||
"version": "v1.36.0",
|
||||
"version": "v1.37.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
|
||||
|
|
@ -6047,7 +6048,7 @@
|
|||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.36.0"
|
||||
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.37.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -6071,7 +6072,7 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/polyfill-mbstring",
|
||||
"version": "v1.36.0",
|
||||
"version": "v1.37.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||
|
|
@ -6132,7 +6133,7 @@
|
|||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.36.0"
|
||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.37.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -6156,7 +6157,7 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php80",
|
||||
"version": "v1.36.0",
|
||||
"version": "v1.37.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php80.git",
|
||||
|
|
@ -6216,7 +6217,7 @@
|
|||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-php80/tree/v1.36.0"
|
||||
"source": "https://github.com/symfony/polyfill-php80/tree/v1.37.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -6240,7 +6241,7 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php84",
|
||||
"version": "v1.36.0",
|
||||
"version": "v1.37.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php84.git",
|
||||
|
|
@ -6296,7 +6297,7 @@
|
|||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-php84/tree/v1.36.0"
|
||||
"source": "https://github.com/symfony/polyfill-php84/tree/v1.37.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -6320,16 +6321,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php85",
|
||||
"version": "v1.36.0",
|
||||
"version": "v1.37.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php85.git",
|
||||
"reference": "2c408a6bb0313e6001a83628dc5506100474254e"
|
||||
"reference": "fcfa4973a9917cef23f2e38774da74a2b7d115ee"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/2c408a6bb0313e6001a83628dc5506100474254e",
|
||||
"reference": "2c408a6bb0313e6001a83628dc5506100474254e",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/fcfa4973a9917cef23f2e38774da74a2b7d115ee",
|
||||
"reference": "fcfa4973a9917cef23f2e38774da74a2b7d115ee",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -6376,7 +6377,7 @@
|
|||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-php85/tree/v1.36.0"
|
||||
"source": "https://github.com/symfony/polyfill-php85/tree/v1.37.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -6396,11 +6397,91 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-04-10T16:50:15+00:00"
|
||||
"time": "2026-04-26T13:10:57+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php86",
|
||||
"version": "v1.37.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php86.git",
|
||||
"reference": "33d8fc5a705481e21fe3a81212b26f9b1f61749c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php86/zipball/33d8fc5a705481e21fe3a81212b26f9b1f61749c",
|
||||
"reference": "33d8fc5a705481e21fe3a81212b26f9b1f61749c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"thanks": {
|
||||
"url": "https://github.com/symfony/polyfill",
|
||||
"name": "symfony/polyfill"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Php86\\": ""
|
||||
},
|
||||
"classmap": [
|
||||
"Resources/stubs"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill backporting some PHP 8.6+ features to lower PHP versions",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"polyfill",
|
||||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-php86/tree/v1.37.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nicolas-grekas",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-04-26T13:13:48+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-uuid",
|
||||
"version": "v1.36.0",
|
||||
"version": "v1.37.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-uuid.git",
|
||||
|
|
@ -6459,7 +6540,7 @@
|
|||
"uuid"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-uuid/tree/v1.36.0"
|
||||
"source": "https://github.com/symfony/polyfill-uuid/tree/v1.37.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -7357,16 +7438,16 @@
|
|||
},
|
||||
{
|
||||
"name": "uwil/admin-secure-laravel",
|
||||
"version": "5.1.0",
|
||||
"version": "5.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "git@lab.s-web.fr:ldc-web/projets/ldc-web-admin-secure-laravel.git",
|
||||
"reference": "0299d8397111b5a80ea3200a9fd387b94c34f208"
|
||||
"reference": "c54dad85ee051e103f50dc8f19d457703bc5ff03"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://lab.s-web.fr/api/v4/projects/20/packages/composer/archives/uwil/admin-secure-laravel.zip?sha=0299d8397111b5a80ea3200a9fd387b94c34f208",
|
||||
"reference": "0299d8397111b5a80ea3200a9fd387b94c34f208",
|
||||
"url": "https://lab.s-web.fr/api/v4/projects/20/packages/composer/archives/uwil/admin-secure-laravel.zip?sha=c54dad85ee051e103f50dc8f19d457703bc5ff03",
|
||||
"reference": "c54dad85ee051e103f50dc8f19d457703bc5ff03",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -7374,7 +7455,7 @@
|
|||
"illuminate/support": "~11|~12|~13",
|
||||
"laravel/framework": "~11|~12|~13",
|
||||
"php": "^8.3",
|
||||
"symfony/options-resolver": "^7.0"
|
||||
"symfony/options-resolver": "^7.0|^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"larastan/larastan": "^2.9",
|
||||
|
|
@ -7482,17 +7563,11 @@
|
|||
},
|
||||
{
|
||||
"name": "uwil/laravel-activity-logs",
|
||||
"version": "6.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "git@lab.s-web.fr:ldc-web/projets/laravel-activity-logs.git",
|
||||
"reference": "765bf7aab42af11e79a64ce1b2245a09abae6fe1"
|
||||
},
|
||||
"version": "6.0.1",
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://lab.s-web.fr/api/v4/projects/229/packages/composer/archives/uwil/laravel-activity-logs.zip?sha=765bf7aab42af11e79a64ce1b2245a09abae6fe1",
|
||||
"reference": "765bf7aab42af11e79a64ce1b2245a09abae6fe1",
|
||||
"shasum": ""
|
||||
"type": "path",
|
||||
"url": "packages/laravel-activity-logs",
|
||||
"reference": "988e15d19ea29ad3f9b077057330f226dc963d29"
|
||||
},
|
||||
"require": {
|
||||
"illuminate/support": "~13",
|
||||
|
|
@ -7502,7 +7577,10 @@
|
|||
"uwil/laravel-common": "^6.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"driftingly/rector-laravel": "^2.3",
|
||||
"larastan/larastan": "^3.9",
|
||||
"laravel/pint": "^1.29",
|
||||
"orchestra/testbench": "^11.0",
|
||||
"slevomat/coding-standard": "^8.3",
|
||||
"squizlabs/php_codesniffer": "^3.7"
|
||||
},
|
||||
|
|
@ -7519,6 +7597,20 @@
|
|||
"Uwil\\LaravelActivityLogs\\": "src/"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"post-autoload-dump": [
|
||||
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump"
|
||||
],
|
||||
"phpcs": [
|
||||
"./vendor/bin/phpcs"
|
||||
],
|
||||
"phpstan": [
|
||||
"./vendor/bin/phpstan"
|
||||
],
|
||||
"rector": [
|
||||
"./vendor/bin/rector"
|
||||
]
|
||||
},
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
|
|
@ -7531,21 +7623,19 @@
|
|||
"support": {
|
||||
"email": "digital@ldc.fr",
|
||||
"issues": "https://lab.s-web.fr/ldc-web/projets/laravel-activity-logs/-/issues"
|
||||
},
|
||||
"transport-options": {
|
||||
"symlink": true,
|
||||
"relative": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "uwil/laravel-backend",
|
||||
"version": "5.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "git@lab.s-web.fr:ldc-web/projets/laravel-backend.git",
|
||||
"reference": "9ec385e80de62ebcb0362911c56a9e7d917bcc80"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://lab.s-web.fr/api/v4/projects/228/packages/composer/archives/uwil/laravel-backend.zip?sha=9ec385e80de62ebcb0362911c56a9e7d917bcc80",
|
||||
"reference": "9ec385e80de62ebcb0362911c56a9e7d917bcc80",
|
||||
"shasum": ""
|
||||
"type": "path",
|
||||
"url": "packages/laravel-backend",
|
||||
"reference": "cabc9caa919d4fdcb509558f631416fbdafcc7d2"
|
||||
},
|
||||
"require": {
|
||||
"diglactic/laravel-breadcrumbs": "^10.0",
|
||||
|
|
@ -7554,15 +7644,16 @@
|
|||
"laravel/framework": "~13",
|
||||
"laravel/telescope": "^5.7",
|
||||
"php": "^8.5",
|
||||
"scaler-tech/laravel-saml2": "^2.7",
|
||||
"sentry/sentry-laravel": "^4.10",
|
||||
"uwil/laravel-activity-logs": "^6.0",
|
||||
"uwil/laravel-common": "^6.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"driftingly/rector-laravel": "^2.3",
|
||||
"larastan/larastan": "^3.9",
|
||||
"orchestra/testbench": "^11.0",
|
||||
"phpunit/php-code-coverage": "^11.0",
|
||||
"phpunit/phpunit": "^11.0",
|
||||
"laravel/pint": "^1.29",
|
||||
"orchestra/testbench": "^11.1",
|
||||
"slevomat/coding-standard": "^8.3",
|
||||
"squizlabs/php_codesniffer": "^3.7",
|
||||
"uwil/admin-secure-laravel": "^5.1"
|
||||
|
|
@ -7586,8 +7677,8 @@
|
|||
}
|
||||
},
|
||||
"scripts": {
|
||||
"test": [
|
||||
"vendor/bin/phpunit"
|
||||
"post-autoload-dump": [
|
||||
"@php ./vendor/bin/testbench package:discover --ansi"
|
||||
],
|
||||
"phpcs": [
|
||||
"./vendor/bin/phpcs"
|
||||
|
|
@ -7595,8 +7686,8 @@
|
|||
"phpstan": [
|
||||
"./vendor/bin/phpstan"
|
||||
],
|
||||
"post-autoload-dump": [
|
||||
"@php ./vendor/bin/testbench package:discover --ansi"
|
||||
"rector": [
|
||||
"./vendor/bin/rector"
|
||||
]
|
||||
},
|
||||
"license": [
|
||||
|
|
@ -7611,21 +7702,19 @@
|
|||
"support": {
|
||||
"email": "digital@ldc.fr",
|
||||
"issues": "https://lab.s-web.fr/ldc-web/projets/laravel-backend/-/issues"
|
||||
},
|
||||
"transport-options": {
|
||||
"symlink": true,
|
||||
"relative": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "uwil/laravel-common",
|
||||
"version": "6.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "git@lab.s-web.fr:ldc-web/projets/laravel-common.git",
|
||||
"reference": "dbaeb3f980a1c7a17c92601c090d8687c6bae8e6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://lab.s-web.fr/api/v4/projects/215/packages/composer/archives/uwil/laravel-common.zip?sha=dbaeb3f980a1c7a17c92601c090d8687c6bae8e6",
|
||||
"reference": "dbaeb3f980a1c7a17c92601c090d8687c6bae8e6",
|
||||
"shasum": ""
|
||||
"type": "path",
|
||||
"url": "packages/laravel-common",
|
||||
"reference": "db3afe457554ee103cac4a92cf601dc6a39780b1"
|
||||
},
|
||||
"require": {
|
||||
"diglactic/laravel-breadcrumbs": "^10.0",
|
||||
|
|
@ -7639,10 +7728,10 @@
|
|||
"uwil/laravel-i18n": "^3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"driftingly/rector-laravel": "^2.3",
|
||||
"larastan/larastan": "^3.9",
|
||||
"orchestra/testbench": "^11.0",
|
||||
"phpunit/php-code-coverage": "^11.0",
|
||||
"phpunit/phpunit": "^11.0",
|
||||
"laravel/pint": "^1.29",
|
||||
"orchestra/testbench": "^11.1",
|
||||
"slevomat/coding-standard": "^8.3",
|
||||
"squizlabs/php_codesniffer": "^3.7"
|
||||
},
|
||||
|
|
@ -7655,12 +7744,12 @@
|
|||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/Helpers/methods.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Uwil\\LaravelCommon\\": "src/"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"src/Helpers/methods.php"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
|
|
@ -7668,11 +7757,17 @@
|
|||
}
|
||||
},
|
||||
"scripts": {
|
||||
"test": [
|
||||
"vendor/bin/phpunit"
|
||||
],
|
||||
"post-autoload-dump": [
|
||||
"@php ./vendor/bin/testbench package:discover --ansi"
|
||||
],
|
||||
"phpcs": [
|
||||
"./vendor/bin/phpcs"
|
||||
],
|
||||
"phpstan": [
|
||||
"./vendor/bin/phpstan"
|
||||
],
|
||||
"rector": [
|
||||
"./vendor/bin/rector"
|
||||
]
|
||||
},
|
||||
"license": [
|
||||
|
|
@ -7687,21 +7782,19 @@
|
|||
"support": {
|
||||
"email": "digital@ldc.fr",
|
||||
"issues": "https://lab.s-web.fr/ldc-web/projets/laravel-common/-/issues"
|
||||
},
|
||||
"transport-options": {
|
||||
"symlink": true,
|
||||
"relative": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "uwil/laravel-i18n",
|
||||
"version": "3.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "git@lab.s-web.fr:ldc-web/projets/laravel-i18n.git",
|
||||
"reference": "a87ec12228efd4b13c4a7298631c6dc0f601be47"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://lab.s-web.fr/api/v4/projects/252/packages/composer/archives/uwil/laravel-i18n.zip?sha=a87ec12228efd4b13c4a7298631c6dc0f601be47",
|
||||
"reference": "a87ec12228efd4b13c4a7298631c6dc0f601be47",
|
||||
"shasum": ""
|
||||
"type": "path",
|
||||
"url": "packages/laravel-i18n",
|
||||
"reference": "1d14e78522ec29cf95897fbeb1bd0ffd5279c93a"
|
||||
},
|
||||
"require": {
|
||||
"illuminate/support": "~13",
|
||||
|
|
@ -7712,10 +7805,10 @@
|
|||
"uwil/laravel-common": "^6.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"driftingly/rector-laravel": "^2.3",
|
||||
"larastan/larastan": "^3.9",
|
||||
"laravel/pint": "^1.29",
|
||||
"orchestra/testbench": "^11.0",
|
||||
"phpunit/php-code-coverage": "^11.0",
|
||||
"phpunit/phpunit": "^11.0",
|
||||
"slevomat/coding-standard": "^8.3",
|
||||
"squizlabs/php_codesniffer": "^3.7"
|
||||
},
|
||||
|
|
@ -7733,14 +7826,17 @@
|
|||
}
|
||||
},
|
||||
"scripts": {
|
||||
"post-autoload-dump": [
|
||||
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump"
|
||||
],
|
||||
"phpcs": [
|
||||
"./vendor/bin/phpcs"
|
||||
],
|
||||
"phpstan": [
|
||||
"./vendor/bin/phpstan"
|
||||
],
|
||||
"post-autoload-dump": [
|
||||
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump"
|
||||
"rector": [
|
||||
"./vendor/bin/rector"
|
||||
]
|
||||
},
|
||||
"license": [
|
||||
|
|
@ -7755,6 +7851,10 @@
|
|||
"support": {
|
||||
"email": "digital@ldc.fr",
|
||||
"issues": "https://lab.s-web.fr/ldc-web/projets/laravel-i18n/-/issues"
|
||||
},
|
||||
"transport-options": {
|
||||
"symlink": true,
|
||||
"relative": true
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -7843,16 +7943,16 @@
|
|||
},
|
||||
{
|
||||
"name": "voku/portable-ascii",
|
||||
"version": "2.1.0",
|
||||
"version": "2.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/voku/portable-ascii.git",
|
||||
"reference": "d870a33f0f79d2b4579740b0620200221ee44aeb"
|
||||
"reference": "8e1051fe39379367aecf014f41744ce7539a856f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/voku/portable-ascii/zipball/d870a33f0f79d2b4579740b0620200221ee44aeb",
|
||||
"reference": "d870a33f0f79d2b4579740b0620200221ee44aeb",
|
||||
"url": "https://api.github.com/repos/voku/portable-ascii/zipball/8e1051fe39379367aecf014f41744ce7539a856f",
|
||||
"reference": "8e1051fe39379367aecf014f41744ce7539a856f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -7889,7 +7989,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/voku/portable-ascii/issues",
|
||||
"source": "https://github.com/voku/portable-ascii/tree/2.1.0"
|
||||
"source": "https://github.com/voku/portable-ascii/tree/2.1.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -7913,7 +8013,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-04-16T23:10:39+00:00"
|
||||
"time": "2026-04-26T05:33:54+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [
|
||||
|
|
@ -8944,11 +9044,11 @@
|
|||
},
|
||||
{
|
||||
"name": "phpstan/phpstan",
|
||||
"version": "2.1.51",
|
||||
"version": "2.1.53",
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/dc3b523c45e714c70de2ac5113b958223b55dc59",
|
||||
"reference": "dc3b523c45e714c70de2ac5113b958223b55dc59",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/ef67586798c003274797b288a68b221e4270dca7",
|
||||
"reference": "ef67586798c003274797b288a68b221e4270dca7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -8993,7 +9093,7 @@
|
|||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2026-04-21T18:22:01+00:00"
|
||||
"time": "2026-04-28T16:09:00+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
|
|
|
|||
|
|
@ -40,11 +40,6 @@ return [
|
|||
'driver' => 'session',
|
||||
'provider' => 'users',
|
||||
],
|
||||
|
||||
'frontend' => [
|
||||
'driver' => 'session',
|
||||
'provider' => 'customers',
|
||||
],
|
||||
],
|
||||
|
||||
/**
|
||||
|
|
@ -67,7 +62,7 @@ return [
|
|||
'providers' => [
|
||||
'users' => [
|
||||
'driver' => 'eloquent',
|
||||
'model' => env('AUTH_MODEL', Uwil\LaravelBackend\Models\User::class),
|
||||
'model' => env('AUTH_MODEL', App\Models\User::class),
|
||||
],
|
||||
],
|
||||
|
||||
|
|
@ -91,10 +86,10 @@ return [
|
|||
*/
|
||||
|
||||
'passwords' => [
|
||||
'users' => [
|
||||
'backend' => [
|
||||
'provider' => 'users',
|
||||
'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'),
|
||||
'expire' => 60,
|
||||
'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'),
|
||||
'expire' => 60,
|
||||
'throttle' => 60,
|
||||
],
|
||||
],
|
||||
|
|
|
|||
397
config/saml2.php
397
config/saml2.php
|
|
@ -1,397 +0,0 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/**
|
||||
*--------------------------------------------------------------------------
|
||||
* Tenant Model
|
||||
*--------------------------------------------------------------------------
|
||||
*
|
||||
* This will allow you to override the tenant model with your own.
|
||||
*
|
||||
*/
|
||||
|
||||
'tenantModel' => \Slides\Saml2\Models\Tenant::class,
|
||||
|
||||
/**
|
||||
*--------------------------------------------------------------------------
|
||||
* Use built-in routes
|
||||
*--------------------------------------------------------------------------
|
||||
*
|
||||
* If "useRoutes" set to true, the package defines five new routes:
|
||||
*
|
||||
* Method | URI | Name
|
||||
* -------|---------------------------------|------------------
|
||||
* POST | {routesPrefix}/{uuid}/acs | saml.acs
|
||||
* GET | {routesPrefix}/{uuid}/login | saml.login
|
||||
* GET | {routesPrefix}/{uuid}/logout | saml.logout
|
||||
* GET | {routesPrefix}/{uuid}/metadata | saml.metadata
|
||||
* GET | {routesPrefix}/{uuid}/sls | saml.sls
|
||||
*
|
||||
*/
|
||||
|
||||
'useRoutes' => true,
|
||||
|
||||
/**
|
||||
*--------------------------------------------------------------------------
|
||||
* Built-in routes prefix
|
||||
*--------------------------------------------------------------------------
|
||||
*
|
||||
* Here you may define the prefix for built-in routes.
|
||||
*
|
||||
*/
|
||||
|
||||
'routesPrefix' => '/saml2',
|
||||
|
||||
/**
|
||||
*--------------------------------------------------------------------------
|
||||
* Middle groups to use for the SAML routes
|
||||
*--------------------------------------------------------------------------
|
||||
*
|
||||
* Note, Laravel 5.2 requires a group which includes StartSession
|
||||
*
|
||||
*/
|
||||
|
||||
'routesMiddleware' => ['saml'],
|
||||
|
||||
/**
|
||||
*--------------------------------------------------------------------------
|
||||
* Signature validation
|
||||
*--------------------------------------------------------------------------
|
||||
*
|
||||
* Set to true if you want to use parameters from $_SERVER to validate the signature.
|
||||
*
|
||||
*/
|
||||
|
||||
'retrieveParametersFromServer' => false,
|
||||
|
||||
/**
|
||||
*--------------------------------------------------------------------------
|
||||
* Login redirection URL.
|
||||
*--------------------------------------------------------------------------
|
||||
*
|
||||
* The redirection URL after successful login.
|
||||
*
|
||||
*/
|
||||
|
||||
'loginRoute' => '/admin/login',
|
||||
|
||||
/**
|
||||
*--------------------------------------------------------------------------
|
||||
* Logout redirection URL.
|
||||
*--------------------------------------------------------------------------
|
||||
*
|
||||
* The redirection URL after successful logout.
|
||||
*
|
||||
*/
|
||||
|
||||
'logoutRoute' => '/admin/login',
|
||||
|
||||
/**
|
||||
*--------------------------------------------------------------------------
|
||||
* Login error redirection URL.
|
||||
*--------------------------------------------------------------------------
|
||||
*
|
||||
* The redirection URL after login failing.
|
||||
*
|
||||
*/
|
||||
|
||||
'errorRoute' => '/admin/login',
|
||||
|
||||
/**
|
||||
*--------------------------------------------------------------------------
|
||||
* Strict mode.
|
||||
*--------------------------------------------------------------------------
|
||||
*
|
||||
* If 'strict' is True, then the PHP Toolkit will reject unsigned
|
||||
* or unencrypted messages if it expects them signed or encrypted
|
||||
* Also will reject the messages if not strictly follow the SAML
|
||||
* standard: Destination, NameId, Conditions... are validated too.
|
||||
*
|
||||
*/
|
||||
|
||||
'strict' => true,
|
||||
|
||||
/**
|
||||
*--------------------------------------------------------------------------
|
||||
* Debug mode.
|
||||
*--------------------------------------------------------------------------
|
||||
*
|
||||
* When enabled, errors must be printed.
|
||||
*
|
||||
*/
|
||||
|
||||
'debug' => env('SAML2_DEBUG', env('APP_DEBUG', false)),
|
||||
|
||||
/**
|
||||
*--------------------------------------------------------------------------
|
||||
* Whether to use `X-Forwarded-*` headers to determine port/domain/protocol.
|
||||
*--------------------------------------------------------------------------
|
||||
*
|
||||
* If 'proxyVars' is True, then the Saml lib will trust proxy headers
|
||||
* e.g X-Forwarded-Proto / HTTP_X_FORWARDED_PROTO. This is useful if
|
||||
* your application is running behind a load balancer which terminates SSL.
|
||||
*
|
||||
*/
|
||||
|
||||
'proxyVars' => true,
|
||||
|
||||
/**
|
||||
*--------------------------------------------------------------------------
|
||||
* Service Provider configuration.
|
||||
*--------------------------------------------------------------------------
|
||||
*
|
||||
* General setting of the service provider.
|
||||
*
|
||||
*/
|
||||
|
||||
'sp' => [
|
||||
|
||||
/**
|
||||
*--------------------------------------------------------------------------
|
||||
* NameID format.
|
||||
*--------------------------------------------------------------------------
|
||||
*
|
||||
* Specifies constraints on the name identifier to be used to
|
||||
* represent the requested subject.
|
||||
*
|
||||
*/
|
||||
|
||||
'NameIDFormat' => 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
|
||||
|
||||
/**
|
||||
*--------------------------------------------------------------------------
|
||||
* SP Certificates.
|
||||
*--------------------------------------------------------------------------
|
||||
*
|
||||
* Usually x509cert and privateKey of the SP are provided by files placed at
|
||||
* the certs folder. But we can also provide them with the following parameters.
|
||||
*
|
||||
*/
|
||||
|
||||
'x509cert' => env('SAML2_SP_CERT_x509', ''),
|
||||
'privateKey' => env('SAML2_SP_CERT_PRIVATEKEY', ''),
|
||||
|
||||
/**
|
||||
*--------------------------------------------------------------------------
|
||||
* Identifier (URI) of the SP entity.
|
||||
*--------------------------------------------------------------------------
|
||||
*
|
||||
* Leave blank to use the 'saml.metadata' route.
|
||||
*
|
||||
*/
|
||||
|
||||
'entityId' => env('SAML2_SP_ENTITYID', ''),
|
||||
|
||||
/**
|
||||
*--------------------------------------------------------------------------
|
||||
* The Assertion Consumer Service (ACS) URL.
|
||||
*--------------------------------------------------------------------------
|
||||
*
|
||||
* URL Location where the <Response> from the IdP will be returned, using HTTP-POST binding.
|
||||
* Leave blank to use the 'saml.acs' route.
|
||||
*
|
||||
*/
|
||||
|
||||
'assertionConsumerService' => [
|
||||
'url' => '',
|
||||
],
|
||||
|
||||
/**
|
||||
*--------------------------------------------------------------------------
|
||||
* The Single Logout Service URL.
|
||||
*--------------------------------------------------------------------------
|
||||
*
|
||||
* Specifies info about where and how the <Logout Response> message MUST be
|
||||
* returned to the requester, in this case our SP.
|
||||
*
|
||||
* URL Location where the <Response> from the IdP will be returned, using HTTP-Redirect binding.
|
||||
* Leave blank to use the 'saml.sls' route.
|
||||
*
|
||||
*/
|
||||
|
||||
'singleLogoutService' => [
|
||||
'url' => ''
|
||||
],
|
||||
],
|
||||
|
||||
/**
|
||||
*--------------------------------------------------------------------------
|
||||
* OneLogin security settings.
|
||||
*--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'security' => [
|
||||
|
||||
'signatureAlgorithm' => 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256',
|
||||
'digestAlgorithm' => 'http://www.w3.org/2001/04/xmlenc#sha256',
|
||||
|
||||
/**
|
||||
*--------------------------------------------------------------------------
|
||||
* NameId encryption
|
||||
*--------------------------------------------------------------------------
|
||||
*
|
||||
* Indicates that the nameID of the <samlp:logoutRequest> sent by this SP
|
||||
* will be encrypted.
|
||||
*
|
||||
*/
|
||||
|
||||
'nameIdEncrypted' => true,
|
||||
|
||||
/**
|
||||
*--------------------------------------------------------------------------
|
||||
* AuthnRequest signage
|
||||
*--------------------------------------------------------------------------
|
||||
*
|
||||
* Indicates whether the <samlp:AuthnRequest> messages sent by
|
||||
* this SP will be signed. The Metadata of the SP will offer this info
|
||||
*
|
||||
*/
|
||||
|
||||
'authnRequestsSigned' => true,
|
||||
|
||||
/**
|
||||
*--------------------------------------------------------------------------
|
||||
* Logout request signage
|
||||
*--------------------------------------------------------------------------
|
||||
*
|
||||
* Indicates whether the <samlp:logoutRequest> messages sent by this SP
|
||||
* will be signed.
|
||||
*
|
||||
*/
|
||||
|
||||
'logoutRequestSigned' => true,
|
||||
|
||||
/**
|
||||
*--------------------------------------------------------------------------
|
||||
* Logout response signage
|
||||
*--------------------------------------------------------------------------
|
||||
*
|
||||
* Indicates whether the <samlp:logoutResponse> messages sent by this SP
|
||||
* will be signed.
|
||||
*
|
||||
*/
|
||||
|
||||
'logoutResponseSigned' => true,
|
||||
|
||||
/**
|
||||
*--------------------------------------------------------------------------
|
||||
* Whether need to sign metadata.
|
||||
*--------------------------------------------------------------------------
|
||||
*
|
||||
* The possible values:
|
||||
* - false
|
||||
* - true (use certs)
|
||||
* - array:
|
||||
* ```
|
||||
* [
|
||||
* 'keyFileName' => 'metadata.key',
|
||||
* 'certFileName' => 'metadata.crt'
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
|
||||
'signMetadata' => true,
|
||||
|
||||
/**
|
||||
*--------------------------------------------------------------------------
|
||||
* Requirement to sign messages.
|
||||
*--------------------------------------------------------------------------
|
||||
*
|
||||
* Indicates a requirement for the <samlp:Response>, <samlp:LogoutRequest> and
|
||||
* <samlp:LogoutResponse> elements received by this SP to be signed.
|
||||
*
|
||||
*/
|
||||
|
||||
'wantMessagesSigned' => true,
|
||||
|
||||
/**
|
||||
*--------------------------------------------------------------------------
|
||||
* Requirement to sign assertion elements.
|
||||
*--------------------------------------------------------------------------
|
||||
*
|
||||
* Indicates a requirement for the <saml:Assertion> elements received by
|
||||
* this SP to be signed.
|
||||
*
|
||||
*/
|
||||
|
||||
'wantAssertionsSigned' => true,
|
||||
|
||||
/**
|
||||
*--------------------------------------------------------------------------
|
||||
* Requirement to encrypt NameID.
|
||||
*--------------------------------------------------------------------------
|
||||
*
|
||||
* Indicates a requirement for the NameID received by this SP to be encrypted.
|
||||
*
|
||||
*/
|
||||
|
||||
'wantNameIdEncrypted' => false,
|
||||
|
||||
/**
|
||||
*--------------------------------------------------------------------------
|
||||
* Authentication context.
|
||||
*--------------------------------------------------------------------------
|
||||
*
|
||||
* Set to false and no AuthContext will be sent in the AuthNRequest,
|
||||
*
|
||||
* Set true or don't present this parameter and you will get an
|
||||
* AuthContext 'exact' 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport'
|
||||
*
|
||||
* Set an array with the possible auth context values:
|
||||
* ['urn:oasis:names:tc:SAML:2.0:ac:classes:Password', 'urn:oasis:names:tc:SAML:2.0:ac:classes:X509']
|
||||
*
|
||||
*/
|
||||
|
||||
'requestedAuthnContext' => true,
|
||||
],
|
||||
|
||||
/**
|
||||
*--------------------------------------------------------------------------
|
||||
* Contact information.
|
||||
*--------------------------------------------------------------------------
|
||||
*
|
||||
* It is recommended to supply a technical and support contacts.
|
||||
*
|
||||
*/
|
||||
|
||||
'contactPerson' => [
|
||||
'technical' => [
|
||||
'givenName' => env('SAML2_CONTACT_TECHNICAL_NAME', env('APP_NAME', 'Technical')),
|
||||
'emailAddress' => env('SAML2_CONTACT_TECHNICAL_EMAIL', 'no@reply.com')
|
||||
],
|
||||
'support' => [
|
||||
'givenName' => env('SAML2_CONTACT_SUPPORT_NAME', env('APP_NAME', 'Support')),
|
||||
'emailAddress' => env('SAML2_CONTACT_SUPPORT_EMAIL', 'no@reply.com')
|
||||
],
|
||||
],
|
||||
|
||||
/**
|
||||
*--------------------------------------------------------------------------
|
||||
* Organization information.
|
||||
*--------------------------------------------------------------------------
|
||||
*
|
||||
* The info in en_US lang is recommended, add more if required.
|
||||
*
|
||||
*/
|
||||
|
||||
'organization' => [
|
||||
'en-US' => [
|
||||
'name' => env('SAML2_ORGANIZATION_NAME', env('APP_NAME', 'Name')),
|
||||
'displayname' => env('SAML2_ORGANIZATION_NAME', env('APP_NAME', 'Name')),
|
||||
'url' => env('SAML2_ORGANIZATION_URL', env('APP_URL', 'http://url'))
|
||||
],
|
||||
],
|
||||
|
||||
/**
|
||||
* Default tenant UUID.
|
||||
*/
|
||||
|
||||
'default_tenant_uuid' => env('SAML2_DEFAULT_TENANT_UUID', null),
|
||||
|
||||
/**
|
||||
* Target domain.
|
||||
*/
|
||||
'domain' => env('SAML2_DOMAIN', "")
|
||||
];
|
||||
|
|
@ -42,7 +42,7 @@ return [
|
|||
*
|
||||
*/
|
||||
|
||||
'path' => env('TELESCOPE_PATH', 'admin/telescope'),
|
||||
'path' => env('TELESCOPE_PATH', config('uwil-backend.routes.prefix') . '/telescope'),
|
||||
|
||||
/**
|
||||
*--------------------------------------------------------------------------
|
||||
|
|
|
|||
32
config/uwil-backend.php
Normal file
32
config/uwil-backend.php
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/**
|
||||
*--------------------------------------------------------------------------
|
||||
* Routes
|
||||
*--------------------------------------------------------------------------
|
||||
*
|
||||
* This option may be used to edit routes informations.
|
||||
* - prefix: string.
|
||||
* - name: string.
|
||||
*/
|
||||
|
||||
'routes' => [
|
||||
'prefix' => '/',
|
||||
'name' => 'back.',
|
||||
],
|
||||
|
||||
/**
|
||||
*--------------------------------------------------------------------------
|
||||
* Saml
|
||||
*--------------------------------------------------------------------------
|
||||
*
|
||||
* This option may be used to active or not the saml connection.
|
||||
* - enabled: boolean.
|
||||
*/
|
||||
|
||||
'saml' => [
|
||||
'enabled' => false,
|
||||
],
|
||||
];
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('user_passwords', function (Blueprint $table): void {
|
||||
$table->id()->comment('Id of the user password.');;
|
||||
$table->foreignId('user_id')->unique()->comment('Associated user.')
|
||||
->constrained('users')->cascadeOnDelete();
|
||||
$table->string('password')->comment('Password of the user.');
|
||||
$table->timestamp('created_at')->nullable()->comment('Date the user password was created.');
|
||||
$table->timestamp('updated_at')->nullable()->comment('Date the user password was updated.');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('user_passwords');
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('password_reset_tokens', function (Blueprint $table): void {
|
||||
$table->string('email')->primary();
|
||||
$table->string('token');
|
||||
$table->timestamp('created_at')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('password_reset_tokens');
|
||||
}
|
||||
};
|
||||
44
lang/fr/auth.php
Normal file
44
lang/fr/auth.php
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
'failed' => 'Ces identifiants ne correspondent pas à nos enregistrements.',
|
||||
'password' => 'Le mot de passe fourni est incorrect.',
|
||||
'throttle' => 'Tentatives de connexion trop nombreuses. Veuillez réessayer dans :seconds secondes.',
|
||||
"login" => "Se connecter",
|
||||
"remember" => "Se souvenir de moi",
|
||||
"forgot_password" => "Mot de passe oublié",
|
||||
"forgot_password_desc" => "Renseignez votre email pour recevoir un lien de réinitialisation.",
|
||||
"description" => "Connectez-vous afin d'accéder à notre outil <strong>:appName</strong> !",
|
||||
"connected" => "Connecté !",
|
||||
"connected_desc" => "Vous êtes à présent authentifié au Support UWIL.",
|
||||
"send_link" => "Envoyer le lien",
|
||||
"mail_sent" => "Mail envoyé !",
|
||||
"mail_sent_desc" => "Si un compte est associé à l'adresse :email, un e-mail de réinitialisation du mot de passe
|
||||
vous sera envoyé dans quelques instants.",
|
||||
"reset_password" => "Réinitialisation du mot de passe",
|
||||
"reset_password_desc" => "Choisissez un nouveau mot de passe pour votre compte.",
|
||||
"reset_your_password" => "Réinitialiser votre mot de passe",
|
||||
"reset_sent" => "Réinitialisation du mot de passe effectué !",
|
||||
"reset_sent_desc" => "Mot de passe mis à jour avec succès. Vous pouvez dès à présent vous connecter
|
||||
avec vos nouveaux identifiants.",
|
||||
'email_hello' => 'Bonjour !',
|
||||
'email_get_reset_password' => 'Vous recevez cet e-mail parce que nous avons reçu une demande de réinitialisation
|
||||
du mot de passe de votre compte.',
|
||||
'email_reset_link_expire' => 'Ce lien expirera dans :count minutes.',
|
||||
'email_button_redirect' => 'Cliquez sur le bouton suivant afin de procéder au changement:',
|
||||
'email_reset_password' => 'Réinitialiser le mot de passe',
|
||||
'email_wrong_destination' => 'Si vous n\'avez pas demandé la réinitialisation de votre mot de passe, aucune
|
||||
autre action n\'est requise.',
|
||||
'email_cant_visualise' => 'Si vous avez des difficultés à cliquer sur le bouton \':actionText\' ou si vous ne
|
||||
le voyez pas, copiez ou cliquez sur le lien suivant:',
|
||||
'email_reset_complete' => 'La réinitialisation du mot de passe associé à votre compte a bien été
|
||||
effectuée.',
|
||||
'email_new_connection' => 'Vous pouvez désormais vour connecter avec votre nouvel identifiant et
|
||||
mot de passe en cliquant sur le bouton suivant:',
|
||||
'email_title_reset_password' => 'Réinitialisation du mot de passe',
|
||||
'email_title_reset_complete' => 'Mot de passe modifié',
|
||||
'reset_password' => 'Réinitialiser mot de passe',
|
||||
'reset_password_desc' => 'Formulaire de réinitialisation du mot de passe de votre compte au back office.',
|
||||
];
|
||||
|
|
@ -3,5 +3,5 @@
|
|||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
// "dashboard" => "Tableau de bord",
|
||||
"your_field" => "Votre :field",
|
||||
];
|
||||
|
|
|
|||
|
|
@ -6,5 +6,6 @@ return [
|
|||
"user-role" => [
|
||||
"project-manager" => "Chef de projet",
|
||||
"developper" => "Développeur",
|
||||
"client" => "Client",
|
||||
],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
"homepage" => "Bonjour Monde",
|
||||
"back_to_your_website" => "Revenir à la page d'accueil",
|
||||
];
|
||||
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||
|
||||
return [
|
||||
"custom" => [
|
||||
// "published" => "publié",
|
||||
"token" => "token",
|
||||
"new_password" => "nouveau mot de passe",
|
||||
],
|
||||
];
|
||||
|
|
@ -38,11 +38,6 @@ data:
|
|||
SCOUT_QUEUE: "${APP_SCOUT_QUEUE}"
|
||||
MEILISEARCH_HOST: "meilisearch.common-services:7700"
|
||||
MEILISEARCH_KEY: "${APP_MEILISEARCH_KEY}"
|
||||
SAML2_SP_CERT_x509: "${APP_SAML2_SP_CERT_x509}"
|
||||
SAML2_SP_CERT_PRIVATEKEY: "${APP_SAML2_SP_CERT_PRIVATEKEY}"
|
||||
SAML2_CONTACT_TECHNICAL_EMAIL: "${APP_SAML2_CONTACT_TECHNICAL_EMAIL}"
|
||||
SAML2_CONTACT_SUPPORT_EMAIL: "${APP_SAML2_CONTACT_SUPPORT_EMAIL}"
|
||||
SAML2_DEFAULT_TENANT_UUID: "${APP_SAML2_DEFAULT_TENANT_UUID}"
|
||||
UWIL_ADMIN_SECURE_CIDR_LIST: "${APP_GLOBAL_UWIL_ADMIN_SECURE_CIDR_LIST}"
|
||||
---
|
||||
apiVersion: v1
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import InputError from "@/back/Components/InputError";
|
|||
import { cn } from "@/lib/utils";
|
||||
|
||||
type SimpleSwitchProps = {
|
||||
id?: string;
|
||||
name: string;
|
||||
checked?: boolean;
|
||||
label?: ReactNode | string;
|
||||
|
|
@ -24,6 +25,7 @@ type SimpleSwitchProps = {
|
|||
|
||||
export default function SimpleSwitch({
|
||||
name,
|
||||
id = name,
|
||||
checked,
|
||||
label,
|
||||
leftHelper,
|
||||
|
|
@ -48,7 +50,7 @@ export default function SimpleSwitch({
|
|||
<div className="flex items-center gap-3">
|
||||
{leftHelper && <small>{leftHelper}</small>}
|
||||
<Switch
|
||||
id={name}
|
||||
id={id}
|
||||
name={name}
|
||||
onCheckedChange={(value) => setData(name, value)}
|
||||
required={required}
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ export default function AppFooter() {
|
|||
const appName = (usePage().props.appEnv as AppEnv).name;
|
||||
const year = new Date().getFullYear();
|
||||
return (
|
||||
<section className="relative flex flex-col justify-center items-center w-full pt-4 z-2">
|
||||
<section className="relative flex flex-col justify-center items-center w-full py-4 z-2">
|
||||
<Separator />
|
||||
<p className="text-sm pt-4">
|
||||
<p className="text-sm text-muted-foreground pt-4">
|
||||
uwil/{str(appName).slug().value()} - UWIL © {year}
|
||||
</p>
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -46,8 +46,8 @@ import { SidebarRegistry } from "@/back/Components/Layout/Sidebar/SidebarRegistr
|
|||
import Views from "@/back/lib/actions";
|
||||
import str from "@/hooks/use-string";
|
||||
import useTrans from "@/hooks/use-translations";
|
||||
import { AppEnv, LaravelEnum } from "@/types/global";
|
||||
import { AuthProp } from "@/back/types/global";
|
||||
import { AppEnv } from "@/types/global";
|
||||
import { AuthProp, UserModel } from "@/back/types/global";
|
||||
import { GetTranslation } from "@/lib/utils";
|
||||
|
||||
export type NavListLinksProps = {
|
||||
|
|
@ -124,10 +124,11 @@ export default function AppSidebar() {
|
|||
const { appEnv, auth, enumUserRole } = usePage().props as unknown as {
|
||||
appEnv: AppEnv;
|
||||
auth: AuthProp;
|
||||
enumUserRole: LaravelEnum;
|
||||
enumUserRole: UserModel["role"][];
|
||||
};
|
||||
const { state } = useSidebar();
|
||||
const prevStateRef = useRef(state);
|
||||
const isConceptor = enumUserRole.find((role) => role.name === "conceptor");
|
||||
const isCollapsed = state === "collapsed";
|
||||
|
||||
// Translations.
|
||||
|
|
@ -165,7 +166,7 @@ export default function AppSidebar() {
|
|||
},
|
||||
{
|
||||
title: str(transAdmin).plural().value(),
|
||||
show: auth.user.role === enumUserRole.conceptor.value,
|
||||
show: auth.user.role.value === isConceptor!.value,
|
||||
links: [
|
||||
...SidebarRegistry.getAll(auth, "admin"),
|
||||
{
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ function LayoutContent({
|
|||
exit={{ opacity: 0 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
style={{ maxWidth: `calc(100% - ${sidebarVar})` }}
|
||||
className="relative flex flex-col justify-between transition-[max-width] duration-200 ease-linear w-full px-8 pb-4"
|
||||
className="relative flex flex-col justify-between transition-[max-width] duration-200 ease-linear w-full px-8"
|
||||
>
|
||||
<div className="container flex flex-col justify-between mx-auto">
|
||||
<AppHeader />
|
||||
|
|
|
|||
|
|
@ -2,20 +2,26 @@
|
|||
import { ReactNode } from "react";
|
||||
// * Components shadcn
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { Toaster } from "@/components/ui/toaster";
|
||||
// * Other
|
||||
import ApplicationLogo from "@/back/Components/ApplicationLogo";
|
||||
import AppSettings from "@/back/Components/Layout/Settings/AppSettings";
|
||||
import { ThemeProvider } from "@/back/providers/ThemeProvider";
|
||||
import AppFooter from "@/back/Components/Layout/AppFooter";
|
||||
|
||||
export default function GuestLayout({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<ThemeProvider defaultTheme="light" storageKey="vite-ui-theme">
|
||||
<AppSettings className="container absolute top-0 start-1/2 transform -translate-x-1/2 text-right my-4" />
|
||||
<div className="flex min-h-screen flex-col justify-center items-center pt-6">
|
||||
<ApplicationLogo />
|
||||
<Card className="text-center shadow-md sm:max-w-md sm:rounded-lg bg-secondary w-full mt-6">
|
||||
{children}
|
||||
</Card>
|
||||
<div className="flex flex-col min-h-screen">
|
||||
<AppSettings className="container absolute top-0 start-1/2 transform -translate-x-1/2 text-right my-4" />
|
||||
<section className="flex flex-1 flex-col justify-center items-center pt-4">
|
||||
<ApplicationLogo />
|
||||
<Card className="text-center shadow-md sm:max-w-md sm:rounded-lg bg-secondary w-full mt-6">
|
||||
{children}
|
||||
</Card>
|
||||
</section>
|
||||
<Toaster />
|
||||
<AppFooter />
|
||||
</div>
|
||||
</ThemeProvider>
|
||||
);
|
||||
|
|
|
|||
87
resources/ts/back/Pages/Auth/ForgotPassword.tsx
Normal file
87
resources/ts/back/Pages/Auth/ForgotPassword.tsx
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
// * Packages.
|
||||
import { Head, Link, useForm } from "@inertiajs/react";
|
||||
import { route } from "ziggy-js";
|
||||
import { LoaderCircleIcon, MoveLeftIcon, SendIcon } from "lucide-react";
|
||||
import { SubmitEvent } from "react";
|
||||
// * Components shadcn.
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
// * Other.
|
||||
import GuestLayout from "@/back/Layouts/GuestLayout";
|
||||
import SimpleString from "@/back/Components/Inputs/SimpleString";
|
||||
import useTrans from "@/hooks/use-translations";
|
||||
import { toast } from "@/hooks/use-toast";
|
||||
|
||||
export default function ForgotPassword() {
|
||||
const { post, setData, data, processing } = useForm({
|
||||
email: "",
|
||||
});
|
||||
|
||||
const mailSent = useTrans("auth.mail_sent");
|
||||
const mailSentDesc = useTrans("auth.mail_sent_desc", {
|
||||
params: { email: data.email },
|
||||
});
|
||||
|
||||
const submit = (event: SubmitEvent) => {
|
||||
event.preventDefault();
|
||||
post(route("back.forgot-password.post"), {
|
||||
onSuccess: () => {
|
||||
toast({
|
||||
title: mailSent,
|
||||
description: mailSentDesc,
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<GuestLayout>
|
||||
<CardHeader>
|
||||
<Head title={useTrans("auth.forgot_password")} />
|
||||
<CardTitle>
|
||||
<h1 className="text-2xl text-center font-extrabold tracking-tight">
|
||||
{useTrans("auth.forgot_password")}
|
||||
</h1>
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
{useTrans("auth.forgot_password_desc")}
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<form onSubmit={submit} className="space-y-3">
|
||||
<SimpleString
|
||||
id="email"
|
||||
name="email"
|
||||
type="email"
|
||||
label="Votre email"
|
||||
placeholder="ex: john.doe@ldc.fr"
|
||||
wordCounter={false}
|
||||
setData={setData}
|
||||
/>
|
||||
<div className="flex flex-row items-center justify-between gap-y-3">
|
||||
<Link
|
||||
href={route("back.login")}
|
||||
className="flex justify-center items-center gap-1 rounded-md text-sm text-gray-600 underline hover:text-gray-900 focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2"
|
||||
>
|
||||
<MoveLeftIcon size={15} />
|
||||
{useTrans("auth.login")}
|
||||
</Link>
|
||||
<Button type="submit" disabled={processing}>
|
||||
{useTrans("auth.send_link")}
|
||||
{processing ? (
|
||||
<LoaderCircleIcon className="animate-spin" />
|
||||
) : (
|
||||
<SendIcon />
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</CardContent>
|
||||
</GuestLayout>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
// * Packages
|
||||
import { Head, usePage } from "@inertiajs/react";
|
||||
import { SquareArrowOutUpRightIcon } from "lucide-react";
|
||||
// * Packages.
|
||||
import { Head, Link, useForm, usePage } from "@inertiajs/react";
|
||||
import { route } from "ziggy-js";
|
||||
// * Components shadcn
|
||||
import { SubmitEvent } from "react";
|
||||
import { LoaderCircleIcon, LogInIcon, MoveRightIcon } from "lucide-react";
|
||||
// * Components shadcn.
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
CardContent,
|
||||
|
|
@ -10,14 +11,40 @@ import {
|
|||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
// * Other
|
||||
// * Other.
|
||||
import GuestLayout from "@/back/Layouts/GuestLayout";
|
||||
import InputError from "@/back/Components/InputError";
|
||||
import SimpleString from "@/back/Components/Inputs/SimpleString";
|
||||
import useTrans from "@/hooks/use-translations";
|
||||
import { AppEnv } from "@/types/global";
|
||||
import { toast } from "@/hooks/use-toast";
|
||||
import SimpleSwitch from "@/back/Components/Inputs/SimpleSwitch";
|
||||
|
||||
export default function Login({ tenantUUID }: { tenantUUID: string }) {
|
||||
export default function Login() {
|
||||
const appName = (usePage().props.appEnv as AppEnv).name;
|
||||
|
||||
/** Translations. */
|
||||
const transConnected = useTrans("auth.connected");
|
||||
const transConnectedDesc = useTrans("auth.connected_desc");
|
||||
|
||||
/** Form section. */
|
||||
const { post, setData, data, processing } = useForm({
|
||||
email: null,
|
||||
password: null,
|
||||
remember: false,
|
||||
});
|
||||
|
||||
const submit = (event: SubmitEvent) => {
|
||||
event.preventDefault();
|
||||
post(route("back.login.post"), {
|
||||
onSuccess: () => {
|
||||
toast({
|
||||
title: transConnected,
|
||||
description: transConnectedDesc,
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<GuestLayout>
|
||||
<CardHeader>
|
||||
|
|
@ -27,25 +54,72 @@ export default function Login({ tenantUUID }: { tenantUUID: string }) {
|
|||
{useTrans("uwil-backend::back.login_title")}
|
||||
</h1>
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
<p
|
||||
// eslint-disable-next-line react/no-danger
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: useTrans("uwil-backend::back.login_desc", {
|
||||
params: { appName: String(appName) },
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
</CardDescription>
|
||||
<CardDescription
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: useTrans("auth.description", {
|
||||
params: { appName: String(appName) },
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<InputError all />
|
||||
<Button type="button" asChild>
|
||||
<a href={route("saml.login", { uuid: tenantUUID })}>
|
||||
{useTrans("uwil-backend::back.login_btn")}
|
||||
<SquareArrowOutUpRightIcon />
|
||||
</a>
|
||||
</Button>
|
||||
<form onSubmit={submit} className="space-y-3">
|
||||
<SimpleString
|
||||
id="email"
|
||||
name="email"
|
||||
type="email"
|
||||
label={useTrans("back.your_field", {
|
||||
params: { field: useTrans("validation.attributes.email") },
|
||||
})}
|
||||
placeholder="ex: john.doe@ldc.fr"
|
||||
wordCounter={false}
|
||||
required
|
||||
setData={setData}
|
||||
/>
|
||||
<SimpleString
|
||||
id="password"
|
||||
name="password"
|
||||
type="password"
|
||||
label={useTrans("back.your_field", {
|
||||
params: { field: useTrans("validation.attributes.password") },
|
||||
})}
|
||||
placeholder="********"
|
||||
wordCounter={false}
|
||||
required
|
||||
setData={setData}
|
||||
/>
|
||||
<div className="flex flex-row justify-start items-center space-x-2 space-y-0">
|
||||
<SimpleSwitch
|
||||
id="remember-switch"
|
||||
name="remember"
|
||||
checked={data.remember}
|
||||
setData={setData}
|
||||
/>
|
||||
<label
|
||||
htmlFor="remember-switch"
|
||||
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
{useTrans("auth.remember")}
|
||||
</label>
|
||||
</div>
|
||||
<div className="flex flex-row items-center justify-between gap-y-3">
|
||||
<Link
|
||||
href={route("back.forgot-password")}
|
||||
className="flex justify-center items-center gap-1 rounded-md text-sm text-gray-600 underline hover:text-gray-900 focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2"
|
||||
>
|
||||
{useTrans("auth.forgot_password")}
|
||||
<MoveRightIcon size={15} />
|
||||
</Link>
|
||||
<Button type="submit" disabled={processing}>
|
||||
{useTrans("auth.login")}
|
||||
{processing ? (
|
||||
<LoaderCircleIcon className="animate-spin" />
|
||||
) : (
|
||||
<LogInIcon />
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</CardContent>
|
||||
</GuestLayout>
|
||||
);
|
||||
|
|
|
|||
114
resources/ts/back/Pages/Auth/ResetPassword.tsx
Normal file
114
resources/ts/back/Pages/Auth/ResetPassword.tsx
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
// * Packages.
|
||||
import { SubmitEvent } from "react";
|
||||
import { Head, useForm } from "@inertiajs/react";
|
||||
import { route } from "ziggy-js";
|
||||
import { LoaderCircleIcon, ShieldCheckIcon } from "lucide-react";
|
||||
// * Components shadcn.
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
// * Other.
|
||||
import GuestLayout from "@/back/Layouts/GuestLayout";
|
||||
import SimpleString from "@/back/Components/Inputs/SimpleString";
|
||||
import useTrans from "@/hooks/use-translations";
|
||||
import { toast } from "@/hooks/use-toast";
|
||||
|
||||
type ResetPasswordProps = {
|
||||
token: string;
|
||||
email: string;
|
||||
};
|
||||
|
||||
export default function ResetPassword({ token, email }: ResetPasswordProps) {
|
||||
const resetSent = useTrans("auth.reset_sent");
|
||||
const resetSentDesc = useTrans("auth.reset_sent_desc");
|
||||
|
||||
const { post, setData, processing } = useForm({
|
||||
token,
|
||||
email,
|
||||
password: "",
|
||||
password_confirmation: "",
|
||||
});
|
||||
|
||||
const submit = (event: SubmitEvent) => {
|
||||
event.preventDefault();
|
||||
post(route("back.reset-password.post"), {
|
||||
onSuccess: () => {
|
||||
toast({
|
||||
title: resetSent,
|
||||
description: resetSentDesc,
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<GuestLayout>
|
||||
<CardHeader>
|
||||
<Head title={useTrans("auth.reset_password")} />
|
||||
<CardTitle>
|
||||
<h1 className="text-2xl text-center font-extrabold tracking-tight">
|
||||
{useTrans("auth.reset_password")}
|
||||
</h1>
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
{useTrans("auth.reset_password_desc")}
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<form onSubmit={submit} className="space-y-3">
|
||||
<SimpleString
|
||||
id="email"
|
||||
name="email"
|
||||
type="email"
|
||||
label={useTrans("back.your_field", {
|
||||
params: { field: useTrans("validation.attributes.email") },
|
||||
})}
|
||||
placeholder="ex: john.doe@ldc.fr"
|
||||
wordCounter={false}
|
||||
value={email}
|
||||
setData={() => {}}
|
||||
disabled={true}
|
||||
/>
|
||||
<SimpleString
|
||||
id="password"
|
||||
name="password"
|
||||
type="password"
|
||||
label={useTrans("back.your_field", {
|
||||
params: { field: useTrans("validation.custom.new_password") },
|
||||
})}
|
||||
placeholder="********"
|
||||
wordCounter={false}
|
||||
setData={setData}
|
||||
/>
|
||||
<SimpleString
|
||||
id="password_confirmation"
|
||||
name="password_confirmation"
|
||||
type="password"
|
||||
label={useTrans("back.your_field", {
|
||||
params: {
|
||||
field: useTrans("validation.attributes.password_confirmation"),
|
||||
},
|
||||
})}
|
||||
placeholder="********"
|
||||
wordCounter={false}
|
||||
setData={setData}
|
||||
/>
|
||||
<div className="flex justify-center">
|
||||
<Button type="submit" disabled={processing}>
|
||||
{useTrans("auth.reset_your_password")}
|
||||
{processing ? (
|
||||
<LoaderCircleIcon className="animate-spin" />
|
||||
) : (
|
||||
<ShieldCheckIcon />
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</CardContent>
|
||||
</GuestLayout>
|
||||
);
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@ export default function Default({ status }: { status: number }) {
|
|||
<ThemeProvider defaultTheme="light" storageKey="vite-ui-theme">
|
||||
<TooltipProvider>
|
||||
<Head title={String(status)} />
|
||||
<div className="flex min-h-screen flex-col justify-center items-center px-8 pb-4">
|
||||
<div className="flex min-h-screen flex-col justify-center items-center px-8">
|
||||
<div className="container grid grid-cols-1 gap-6 text-center mx-auto my-auto">
|
||||
<ApplicationLogo />
|
||||
<h1 className="text-4xl font-extrabold tracking-tight lg:text-5xl">
|
||||
|
|
@ -30,9 +30,6 @@ export default function Default({ status }: { status: number }) {
|
|||
{useTrans("uwil-backend::back.dashboard")}
|
||||
</Link>
|
||||
</Button>
|
||||
<Button variant="outline" asChild>
|
||||
<a href="/">{useTrans("uwil-backend::back.your_website")}</a>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<AppFooter />
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import FieldsetWrapper from "@/back/Components/FieldsetWrapper";
|
|||
import useTrans from "@/hooks/use-translations";
|
||||
import str from "@/hooks/use-string";
|
||||
import InputError from "@/back/Components/InputError";
|
||||
import { LaravelEnum } from "@/types/global";
|
||||
import { AuthProp, UserModel } from "@/back/types/global";
|
||||
import SubmitButton from "@/back/Components/SubmitButton";
|
||||
import useCrudSubmit from "@/back/hooks/use-crud-submit";
|
||||
|
|
@ -28,8 +27,8 @@ export default function Form({
|
|||
method: number;
|
||||
}) {
|
||||
const userModel = (usePage().props.auth as AuthProp).user;
|
||||
const enumUserRole = usePage().props.enumUserRole as LaravelEnum;
|
||||
const isConceptor = userModel.role === enumUserRole.conceptor.value;
|
||||
const enumUserRole = usePage().props.enumUserRole as UserModel["role"][];
|
||||
const isConceptor = enumUserRole.find((role) => role.name === "conceptor");
|
||||
const filterRoles = (value: number) => {
|
||||
return (
|
||||
Object.entries(enumUserRole)
|
||||
|
|
@ -46,7 +45,7 @@ export default function Form({
|
|||
email: model?.email,
|
||||
published: model?.published ?? false,
|
||||
published_at: model?.published_at,
|
||||
role: model?.role,
|
||||
role: model && model.role ? model.role.value : undefined,
|
||||
});
|
||||
|
||||
const submit = useCrudSubmit({
|
||||
|
|
@ -141,26 +140,26 @@ export default function Form({
|
|||
.ucFirst()
|
||||
.value()}
|
||||
placeholder="ex: john.doe@gmail.com"
|
||||
disabled={!isConceptor}
|
||||
wordCounter={isConceptor}
|
||||
value={!isConceptor ? model?.email : data.email}
|
||||
disabled={!isConceptor!.value}
|
||||
wordCounter={Boolean(isConceptor!.value)}
|
||||
value={!isConceptor!.value ? model?.email : data.email}
|
||||
setData={setData}
|
||||
/>
|
||||
{isConceptor && (
|
||||
{Boolean(isConceptor!.value) && (
|
||||
<EnumSelect
|
||||
name="role"
|
||||
options={
|
||||
model?.role && userModel.role !== enumUserRole.conceptor.value
|
||||
? filterRoles(model?.role)
|
||||
model?.role && userModel.role.value !== isConceptor!.value
|
||||
? filterRoles(model?.role.value)
|
||||
: enumUserRole
|
||||
}
|
||||
label={transAssociateToRole}
|
||||
selected={data.role ? String(data.role) : undefined}
|
||||
setData={setData}
|
||||
selected={data.role ? data.role : undefined}
|
||||
setData={(name, value) => setData(name, value?.value ?? value)}
|
||||
/>
|
||||
)}
|
||||
</FieldsetWrapper>
|
||||
{isConceptor && (
|
||||
{userModel.role.value === isConceptor!.value && (
|
||||
<FieldsetWrapper
|
||||
className="gap-4"
|
||||
title={transFormTitleVisibility}
|
||||
|
|
|
|||
|
|
@ -13,12 +13,11 @@ import DataTableFilterButton from "@/back/Components/Table/DataTableFilterButton
|
|||
import BtnOptionnalLinkWithTooltip from "@/back/Components/BtnOptionnalLinkWithTooltip";
|
||||
import Views from "@/back/lib/actions";
|
||||
import { GetFormatedDate, GetTranslation } from "@/lib/utils";
|
||||
import { LaravelEnum } from "@/types/global";
|
||||
import { AuthProp, Resource, UserModel } from "@/back/types/global";
|
||||
import str from "@/hooks/use-string";
|
||||
import useTrans from "@/hooks/use-translations";
|
||||
|
||||
const getColumns = (enumUserRole: LaravelEnum) => {
|
||||
const getColumns = () => {
|
||||
return [
|
||||
{
|
||||
accessorKey: "first_name",
|
||||
|
|
@ -80,19 +79,14 @@ const getColumns = (enumUserRole: LaravelEnum) => {
|
|||
field={column.id}
|
||||
/>
|
||||
),
|
||||
cell: ({ row }: { row: Row<UserModel> }) => (
|
||||
<Badge variant="outline">
|
||||
{str(
|
||||
String(
|
||||
Object.values(enumUserRole).find(
|
||||
(role) => role.value === row.getValue("role"),
|
||||
)?.label,
|
||||
),
|
||||
)
|
||||
.ucFirst()
|
||||
.value()}
|
||||
</Badge>
|
||||
),
|
||||
cell: ({ row }: { row: Row<UserModel> }) => {
|
||||
const rowRole = row.getValue("role") as UserModel["role"];
|
||||
return (
|
||||
<Badge variant="outline">
|
||||
{str(String(rowRole?.label ?? '')).ucFirst().value()}
|
||||
</Badge>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "published",
|
||||
|
|
@ -160,9 +154,8 @@ export default function Index({
|
|||
userModels: Resource;
|
||||
searchFields: string;
|
||||
}) {
|
||||
const { auth, enumUserRole } = usePage().props as unknown as {
|
||||
const { auth } = usePage().props as unknown as {
|
||||
auth: AuthProp;
|
||||
enumUserRole: LaravelEnum;
|
||||
};
|
||||
const { create } = auth.policies.users;
|
||||
const modelName = usePage().props.modelName as string;
|
||||
|
|
@ -173,7 +166,7 @@ export default function Index({
|
|||
Views.DELETION,
|
||||
];
|
||||
|
||||
const columns = getColumns(enumUserRole);
|
||||
const columns = getColumns();
|
||||
const transCrudAddEntry = useTrans("uwil-backend::back.crud_add_entry");
|
||||
const afterNode = () => {
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -11,12 +11,11 @@ import ModelActions from "@/back/Components/ModelActions";
|
|||
import BtnOptionnalLinkWithTooltip from "@/back/Components/BtnOptionnalLinkWithTooltip";
|
||||
import Views from "@/back/lib/actions";
|
||||
import { GetFormatedDate, GetTranslation } from "@/lib/utils";
|
||||
import { LaravelEnum } from "@/types/global";
|
||||
import { AuthProp, SingleResource, UserModel } from "@/back/types/global";
|
||||
import str from "@/hooks/use-string";
|
||||
import useTrans from "@/hooks/use-translations";
|
||||
|
||||
const getColumns = (enumUserRole: LaravelEnum) => {
|
||||
const getColumns = () => {
|
||||
return [
|
||||
{
|
||||
accessorKey: "first_name",
|
||||
|
|
@ -54,16 +53,9 @@ const getColumns = (enumUserRole: LaravelEnum) => {
|
|||
header: str(GetTranslation(`uwil-backend::validation.attributes.role`))
|
||||
.ucFirst()
|
||||
.value(),
|
||||
cell: ({ value }: { value: number }) => (
|
||||
cell: ({ value }: { value: UserModel["role"] }) => (
|
||||
<Badge variant="outline">
|
||||
{str(
|
||||
String(
|
||||
Object.values(enumUserRole).find((role) => role.value === value)
|
||||
?.label,
|
||||
),
|
||||
)
|
||||
.ucFirst()
|
||||
.value()}
|
||||
{str(String(value?.label ?? '')).ucFirst().value()}
|
||||
</Badge>
|
||||
),
|
||||
},
|
||||
|
|
@ -133,15 +125,14 @@ const getColumns = (enumUserRole: LaravelEnum) => {
|
|||
};
|
||||
|
||||
export default function Show({ data }: { data: SingleResource }) {
|
||||
const { auth, enumUserRole } = usePage().props as unknown as {
|
||||
const { auth } = usePage().props as unknown as {
|
||||
auth: AuthProp;
|
||||
enumUserRole: LaravelEnum;
|
||||
};
|
||||
const { viewAny } = auth.policies.users;
|
||||
const userModel = data.data as UserModel;
|
||||
const actionModels = [Views.EDITION, Views.DUPLICATION, Views.DELETION];
|
||||
|
||||
const columns = getColumns(enumUserRole);
|
||||
const columns = getColumns();
|
||||
|
||||
const transCrudBackList = useTrans("uwil-backend::back.crud_back_to_list");
|
||||
const beforeNode = () => {
|
||||
|
|
|
|||
2
resources/ts/back/types/global.d.ts
vendored
2
resources/ts/back/types/global.d.ts
vendored
|
|
@ -25,7 +25,7 @@ type UserModel = {
|
|||
first_name: string;
|
||||
last_name: string;
|
||||
email: string;
|
||||
role: number;
|
||||
role: { value: number; label: string; name: string };
|
||||
published: boolean;
|
||||
published_at: Date;
|
||||
created_at: Date;
|
||||
|
|
|
|||
23
resources/views/back/mails/link-reset-password.blade.php
Normal file
23
resources/views/back/mails/link-reset-password.blade.php
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
{{-- blade-formatter-disable --}}
|
||||
@component('mail::message')
|
||||
|
||||
# {{ __('auth.email_hello') }}
|
||||
|
||||
{{ __('auth.email_get_reset_password') }}
|
||||
{{ __('auth.email_reset_link_expire', ['count' => config('auth.passwords.users.expire')]) }}
|
||||
|
||||
{{ __('auth.email_button_redirect') }}
|
||||
@component('mail::button', ['url' => $url])
|
||||
<strong>{{ __('auth.email_reset_password') }}</strong>
|
||||
@endcomponent
|
||||
|
||||
{{ __('auth.email_wrong_destination') }}
|
||||
|
||||
{{ config('app.name') }}.
|
||||
|
||||
<hr>
|
||||
|
||||
<small>{{ __('auth.email_cant_visualise', ['actionText' => __('auth.email_reset_password')]) }}
|
||||
<a href="{{ $url }}" target="_blank">{{ $url }}</a></small>
|
||||
|
||||
@endcomponent
|
||||
20
resources/views/back/mails/reset-password.blade.php
Normal file
20
resources/views/back/mails/reset-password.blade.php
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
{{-- blade-formatter-disable --}}
|
||||
@component('mail::message')
|
||||
|
||||
# {{ __('auth.email_hello') }}
|
||||
|
||||
{{ __('auth.email_reset_complete') }}
|
||||
|
||||
{{ __('auth.email_new_connection') }}
|
||||
@component('mail::button', ['url' => route('back.login')])
|
||||
<strong>{{ __('auth.login') }}</strong>
|
||||
@endcomponent
|
||||
|
||||
{{ config('app.name') }}.
|
||||
|
||||
<hr>
|
||||
|
||||
<small>{{ __('auth.email_cant_visualise', ['actionText' => __('auth.login')]) }}
|
||||
<a href="{{ route('back.login') }}" target="_blank">{{ route('back.login') }}</a></small>
|
||||
|
||||
@endcomponent
|
||||
30
routes/auth.php
Normal file
30
routes/auth.php
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
use App\Http\Controllers\Auth\AuthenticatedSessionController;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::prefix(config('uwil-backend.routes.prefix'))
|
||||
->name(config('uwil-backend.routes.name'))
|
||||
->middleware(['web', 'admin-secure'])
|
||||
->group(function (): void {
|
||||
Route::middleware('guest:backend')->group(function (): void {
|
||||
Route::get('login', [AuthenticatedSessionController::class, 'login'])
|
||||
->name('login');
|
||||
Route::post('login', [AuthenticatedSessionController::class, 'postLogin'])
|
||||
->name('login.post');
|
||||
|
||||
Route::get('forgot-password', [AuthenticatedSessionController::class, 'forgotPassword'])
|
||||
->name('forgot-password');
|
||||
Route::post('forgot-password', [AuthenticatedSessionController::class, 'postForgotPassword'])
|
||||
->name('forgot-password.post');
|
||||
|
||||
Route::get('reset-password/{token}', [AuthenticatedSessionController::class, 'resetPassword'])
|
||||
->name('reset-password');
|
||||
Route::post('reset-password', [AuthenticatedSessionController::class, 'postResetPassword'])
|
||||
->name('reset-password.post');
|
||||
});
|
||||
Route::middleware(['auth:backend', 'verified'])->group(function (): void {
|
||||
Route::get('logout', [AuthenticatedSessionController::class, 'logout'])
|
||||
->name('logout');
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue