Initial commit

This commit is contained in:
2026-09-03 12:21:17 +02:00
commit 6d40da0410
155 changed files with 21554 additions and 0 deletions
@@ -0,0 +1,121 @@
<?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();
}
}