122 lines
3.9 KiB
PHP
122 lines
3.9 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Auth;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\User;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Facades\Hash;
|
|
use Illuminate\Validation\Rule;
|
|
use Illuminate\Validation\ValidationException;
|
|
|
|
/**
|
|
* Logowanie SoftSPM_2 na istniejącej bazie `sys_users` (database-first).
|
|
*
|
|
* Strategia haseł (przejściowa):
|
|
* - Legacy trzyma USR_PASSWORD = md5(hasło).
|
|
* - Akceptujemy logowanie po MD5 (kompatybilność) ORAZ po bcrypt.
|
|
* - Po udanym logowaniu MD5, jeśli kolumna USR_PASSWORD pomieści bcrypt
|
|
* (>=60 znaków) i włączony jest flag SOFTSPM_REHASH - przepisujemy hash
|
|
* na bcrypt. Domyślnie WYŁĄCZONE, żeby nie uszkodzić zbyt krótkiej kolumny.
|
|
* Decyzja o rozszerzeniu kolumny / osobnej kolumnie do ustalenia.
|
|
*/
|
|
class LoginController extends Controller
|
|
{
|
|
/** Pokaż formularz logowania. */
|
|
public function show()
|
|
{
|
|
return view('auth.login');
|
|
}
|
|
|
|
/** Obsłuż próbę logowania. */
|
|
public function attempt(Request $request)
|
|
{
|
|
// Faza testów: oddział może być wymuszony w configu (pole ukryte w logowaniu).
|
|
$forcedDepartment = config('softspm.login_department');
|
|
|
|
$rules = [
|
|
'username' => ['required', 'string', 'max:100'],
|
|
'password' => ['required', 'string'],
|
|
'lang' => ['nullable', 'in:pl,en,de'],
|
|
];
|
|
if (! $forcedDepartment) {
|
|
$rules['department'] = ['required', 'string', Rule::in(config('softspm.departments'))];
|
|
}
|
|
$credentials = $request->validate($rules);
|
|
|
|
$department = $forcedDepartment ?: $credentials['department'];
|
|
|
|
$user = User::query()
|
|
->where('USR_USERNAME', $credentials['username'])
|
|
->where('USR_STATUS', '1')
|
|
->first();
|
|
|
|
if (! $user || ! $this->passwordMatches($credentials['password'], $user->USR_PASSWORD)) {
|
|
throw ValidationException::withMessages([
|
|
'username' => 'Nieprawidłowy login lub hasło.',
|
|
]);
|
|
}
|
|
|
|
// Opcjonalna migracja MD5 -> bcrypt (patrz docblock klasy).
|
|
$this->maybeRehash($user, $credentials['password']);
|
|
|
|
Auth::login($user);
|
|
$request->session()->regenerate();
|
|
|
|
// Oddział filtrujący listy obiektów (odpowiednik legacy 'syndis_eq_stock').
|
|
$request->session()->put('department', $department);
|
|
|
|
// Zapamiętaj wybrany język UI (i18n w kolejnym etapie).
|
|
if (! empty($credentials['lang'])) {
|
|
$request->session()->put('locale', $credentials['lang']);
|
|
}
|
|
|
|
return redirect()->intended(route('dashboard'));
|
|
}
|
|
|
|
/** Wyloguj. */
|
|
public function logout(Request $request)
|
|
{
|
|
Auth::logout();
|
|
$request->session()->invalidate();
|
|
$request->session()->regenerateToken();
|
|
|
|
return redirect()->route('login');
|
|
}
|
|
|
|
/**
|
|
* Weryfikacja hasła: bcrypt (nowe) lub md5 (legacy).
|
|
*/
|
|
private function passwordMatches(string $plain, ?string $stored): bool
|
|
{
|
|
if (empty($stored)) {
|
|
return false;
|
|
}
|
|
|
|
// Hash bcrypt zaczyna się od $2y$ / $2a$ - sprawdź najpierw.
|
|
if (str_starts_with($stored, '$2')) {
|
|
return Hash::check($plain, $stored);
|
|
}
|
|
|
|
// Legacy: md5 (32 znaki hex). hash_equals chroni przed timing-attack.
|
|
return hash_equals(strtolower($stored), md5($plain));
|
|
}
|
|
|
|
/**
|
|
* Po logowaniu MD5 przepisz hash na bcrypt - tylko gdy bezpiecznie.
|
|
*/
|
|
private function maybeRehash(User $user, string $plain): void
|
|
{
|
|
if (! config('softspm.rehash_passwords', false)) {
|
|
return;
|
|
}
|
|
if (str_starts_with((string) $user->USR_PASSWORD, '$2')) {
|
|
return; // już bcrypt
|
|
}
|
|
|
|
$user->USR_PASSWORD = Hash::make($plain);
|
|
$user->save();
|
|
}
|
|
}
|