2
This commit is contained in:
@@ -0,0 +1,411 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use App\Models\Jugador;
|
||||
use App\Models\Aficionado;
|
||||
use App\Models\AdminUser;
|
||||
use App\Models\Club;
|
||||
use App\Mail\WelcomeMail;
|
||||
use App\Mail\ResetPasswordMail;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class AuthController extends Controller
|
||||
{
|
||||
/**
|
||||
* Valida el token de Cloudflare Turnstile
|
||||
*/
|
||||
private function verifyTurnstile($token)
|
||||
{
|
||||
if (in_array(config('app.env'), ['local', 'testing']) && $token === '1x00000000000000000000AA') {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!$token) return false;
|
||||
|
||||
$response = Http::asForm()->post('https://challenges.cloudflare.com/turnstile/v0/siteverify', [
|
||||
'secret' => config('services.turnstile.secret_key'),
|
||||
'response' => $token,
|
||||
'remoteip' => request()->ip(),
|
||||
]);
|
||||
|
||||
return $response->successful() && $response->json('success');
|
||||
}
|
||||
|
||||
public function login(Request $request)
|
||||
{
|
||||
$tipo = $request->input('tipo');
|
||||
|
||||
if ($tipo === 'admin') {
|
||||
return $this->loginAdmin($request);
|
||||
}
|
||||
|
||||
return $this->loginPlayer($request);
|
||||
}
|
||||
|
||||
public function loginPlayer(Request $request)
|
||||
{
|
||||
if (!$this->verifyTurnstile($request->input('cf-turnstile-response'))) {
|
||||
return back()->with('login_error', 'Error de verificación de seguridad (Turnstile).')->with('login_tab', 'player');
|
||||
}
|
||||
|
||||
$dni = $request->input('dni');
|
||||
$password = $request->input('password');
|
||||
|
||||
$jugador = Jugador::where('documento', $dni)->where('activo', true)->first();
|
||||
|
||||
if ($jugador && $jugador->password && Hash::check($password, $jugador->password)) {
|
||||
$request->session()->put('user_logged_in', true);
|
||||
$request->session()->put('user_tipo', 'jugador');
|
||||
$request->session()->put('user_id', $jugador->id_jugador);
|
||||
$request->session()->put('user_name', $jugador->nombre . ' ' . $jugador->apellido);
|
||||
$request->session()->put('user_documento', $jugador->documento);
|
||||
$request->session()->put('user_ultimo_acceso', time());
|
||||
|
||||
return redirect()->intended('/');
|
||||
}
|
||||
|
||||
$aficionado = Aficionado::where('dni', $dni)->first();
|
||||
|
||||
if ($aficionado && $aficionado->password && Hash::check($password, $aficionado->password)) {
|
||||
$request->session()->put('user_logged_in', true);
|
||||
$request->session()->put('user_tipo', 'aficionado');
|
||||
$request->session()->put('user_id', $aficionado->id_aficionado);
|
||||
$request->session()->put('user_name', $aficionado->nombre . ' ' . $aficionado->apellido);
|
||||
$request->session()->put('user_documento', $aficionado->dni);
|
||||
$request->session()->put('user_ultimo_acceso', time());
|
||||
|
||||
return redirect()->intended('/');
|
||||
}
|
||||
|
||||
return back()->with('login_error', 'DNI o contraseña incorrectos')->with('login_tab', 'player');
|
||||
}
|
||||
|
||||
public function loginAdmin(Request $request)
|
||||
{
|
||||
if (!$this->verifyTurnstile($request->input('cf-turnstile-response'))) {
|
||||
return back()->with('login_error', 'Error de verificación de seguridad (Turnstile).')->with('login_tab', 'admin');
|
||||
}
|
||||
|
||||
$username = $request->input('username');
|
||||
$password = $request->input('password');
|
||||
|
||||
$admin = AdminUser::whereRaw('BINARY `username` = ?', [$username])->first();
|
||||
|
||||
if ($admin && Hash::check($password, $admin->password)) {
|
||||
$request->session()->put('admin_logged_in', true);
|
||||
$request->session()->put('admin_id', $admin->id);
|
||||
$request->session()->put('admin_username', $admin->username);
|
||||
$request->session()->put('admin_role', $admin->role);
|
||||
$request->session()->put('admin_id_club', $admin->id_club);
|
||||
|
||||
if ($admin->id_club && $admin->club) {
|
||||
$request->session()->put('admin_club_nombre', $admin->club->nombre);
|
||||
}
|
||||
|
||||
$request->session()->put('ultimo_acceso', time());
|
||||
|
||||
return redirect()->intended('/');
|
||||
}
|
||||
|
||||
return back()->with('login_error', 'Usuario o contraseña incorrectos')->with('login_tab', 'admin');
|
||||
}
|
||||
|
||||
public function logout(Request $request)
|
||||
{
|
||||
$isAdmin = $request->session()->get('admin_logged_in');
|
||||
|
||||
if ($isAdmin) {
|
||||
$request->session()->forget(['admin_logged_in', 'admin_id', 'admin_username', 'admin_role', 'ultimo_acceso']);
|
||||
$msg = 'Sesión de administrador cerrada correctamente.';
|
||||
} else {
|
||||
$request->session()->forget(['user_logged_in', 'user_tipo', 'user_id', 'user_name', 'user_documento', 'user_ultimo_acceso']);
|
||||
$msg = 'Sesión cerrada correctamente.';
|
||||
}
|
||||
|
||||
return redirect('/?logout_msg=' . urlencode($msg));
|
||||
}
|
||||
|
||||
public function showLoginForm()
|
||||
{
|
||||
return view('welcome');
|
||||
}
|
||||
|
||||
public function recuperar(Request $request)
|
||||
{
|
||||
if (!$this->verifyTurnstile($request->input('cf-turnstile-response'))) {
|
||||
return back()->with('mensaje', '⚠️ Error de verificación de seguridad (Captcha).');
|
||||
}
|
||||
|
||||
$dni = trim($request->input('dni'));
|
||||
$email = trim($request->input('email'));
|
||||
|
||||
if (empty($dni) || empty($email)) {
|
||||
return back()->with('mensaje', 'Debes ingresar tu DNI y correo electrónico.');
|
||||
}
|
||||
|
||||
$jugador = Jugador::where('documento', $dni)->where('email', $email)->first();
|
||||
$aficionado = Aficionado::where('dni', $dni)->where('email', $email)->first();
|
||||
|
||||
$usuario = $jugador ?: $aficionado;
|
||||
|
||||
if (!$usuario) {
|
||||
return back()->with('mensaje', 'No se encontró un usuario con ese DNI y correo.');
|
||||
}
|
||||
|
||||
$token = bin2hex(random_bytes(16));
|
||||
$expires = now()->addHour();
|
||||
|
||||
if ($jugador) {
|
||||
$jugador->update([
|
||||
'reset_token' => $token,
|
||||
'reset_expira' => $expires
|
||||
]);
|
||||
} else {
|
||||
$aficionado->update([
|
||||
'reset_token' => $token,
|
||||
'reset_expira' => $expires
|
||||
]);
|
||||
}
|
||||
try {
|
||||
Mail::to($usuario->email)->send(new ResetPasswordMail($usuario, $token));
|
||||
} catch (\Exception $e) {
|
||||
Log::error("Error enviando mail de recuperación: " . $e->getMessage());
|
||||
}
|
||||
|
||||
return back()->with('mensaje', '📩 Te enviamos un correo con las instrucciones para recuperar tu contraseña.');
|
||||
}
|
||||
|
||||
public function resetPasswordForm($token)
|
||||
{
|
||||
// Verificar que el token exista y no esté expirado
|
||||
$jugador = Jugador::where('reset_token', $token)->where('reset_expira', '>', now())->first();
|
||||
$aficionado = Aficionado::where('reset_token', $token)->where('reset_expira', '>', now())->first();
|
||||
|
||||
if (!$jugador && !$aficionado) {
|
||||
return redirect()->route('recuperar')->with('mensaje', '❌ El enlace es inválido o ya expiró. Solicitá uno nuevo.');
|
||||
}
|
||||
|
||||
return view('auth.reset_password', compact('token'));
|
||||
}
|
||||
|
||||
public function resetPassword(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'token' => 'required|string',
|
||||
'password' => 'required|confirmed|min:6',
|
||||
]);
|
||||
|
||||
$token = $request->input('token');
|
||||
|
||||
$jugador = Jugador::where('reset_token', $token)->where('reset_expira', '>', now())->first();
|
||||
$aficionado = Aficionado::where('reset_token', $token)->where('reset_expira', '>', now())->first();
|
||||
|
||||
$usuario = $jugador ?: $aficionado;
|
||||
|
||||
if (!$usuario) {
|
||||
return redirect()->route('recuperar')->with('mensaje', '❌ El enlace es inválido o ya expiró. Solicitá uno nuevo.');
|
||||
}
|
||||
|
||||
$usuario->update([
|
||||
'password' => bcrypt($request->input('password')),
|
||||
'reset_token' => null,
|
||||
'reset_expira' => null,
|
||||
]);
|
||||
|
||||
return redirect('/')->with('login_success', '✅ Contraseña cambiada correctamente. Ya podés iniciar sesión.');
|
||||
}
|
||||
|
||||
public function registroAficionado(Request $request)
|
||||
{
|
||||
if (!$this->verifyTurnstile($request->input('cf-turnstile-response'))) {
|
||||
return back()->with('registro_msg', '⚠️ Error de verificación de seguridad (Captcha).')->withInput();
|
||||
}
|
||||
|
||||
$data = $request->validate([
|
||||
'nombre' => 'required|string|max:100',
|
||||
'apellido' => 'required|string|max:100',
|
||||
'dni' => 'required|string|unique:aficionados,dni',
|
||||
'email' => 'required|email|unique:aficionados,email',
|
||||
'fecha_nacimiento' => 'nullable|date',
|
||||
'telefono' => 'nullable|string|max:50',
|
||||
'localidad' => 'nullable|string|max:100',
|
||||
'password' => 'required|confirmed|min:6',
|
||||
]);
|
||||
|
||||
$data['password'] = bcrypt($data['password']);
|
||||
$data['fecha_registro'] = now();
|
||||
|
||||
$aficionado = Aficionado::create($data);
|
||||
|
||||
try {
|
||||
Mail::to($aficionado->email)->send(new WelcomeMail($aficionado, 'aficionado'));
|
||||
} catch (\Exception $e) {
|
||||
Log::error("Error enviando mail de bienvenida a Aficionado: " . $e->getMessage());
|
||||
}
|
||||
|
||||
return redirect()->route('asociate')->with('mensaje', '✅ Te registraste correctamente. Ya podés iniciar sesión.');
|
||||
}
|
||||
|
||||
public function buscarJugador(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'nombre' => 'required|string',
|
||||
'apellido' => 'required|string',
|
||||
'dni' => 'required|string',
|
||||
'acepto' => 'required',
|
||||
]);
|
||||
|
||||
$dni = preg_replace('/[^0-9]/', '', $request->input('dni'));
|
||||
$nombre = strtoupper(trim($request->input('nombre')));
|
||||
$apellido = strtoupper(trim($request->input('apellido')));
|
||||
|
||||
// Buscar jugador por DNI
|
||||
$jugador = Jugador::where('documento', $dni)->first();
|
||||
|
||||
if (!$jugador) {
|
||||
// Verificar si ya está registrado como aficionado
|
||||
$aficionado = Aficionado::where('dni', $dni)->first();
|
||||
if ($aficionado) {
|
||||
return redirect()->route('asociate')->with('mensaje', '⚠️ Ya estás registrado como aficionado.');
|
||||
}
|
||||
// No existe - se debe registrar como aficionado
|
||||
return redirect()->route('asociate')->with('mensaje', '⚠️ No encontramos tu registro como jugador. Podés registrarte como aficionado.');
|
||||
}
|
||||
|
||||
// --- Lógica Smart Match ---
|
||||
$nombreEnBD = $this->normalizeString($jugador->nombre ?? '');
|
||||
$apellidoEnBD = $this->normalizeString($jugador->apellido ?? '');
|
||||
$terminosBD = explode(' ', $nombreEnBD . ' ' . $apellidoEnBD);
|
||||
|
||||
$palabrasNombreIn = explode(' ', $this->normalizeString($nombre));
|
||||
$palabrasApellidoIn = explode(' ', $this->normalizeString($apellido));
|
||||
|
||||
$nombreCoincide = false;
|
||||
foreach ($palabrasNombreIn as $p) {
|
||||
if ($this->isApproxMatch($p, $terminosBD)) {
|
||||
$nombreCoincide = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$apellidoCoincide = false;
|
||||
foreach ($palabrasApellidoIn as $p) {
|
||||
if ($this->isApproxMatch($p, $terminosBD)) {
|
||||
$apellidoCoincide = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$nombreCoincide || !$apellidoCoincide) {
|
||||
return redirect()->route('asociate')->with('mensaje', '⚠️ El DNI ingresado no coincide con el Nombre y Apellido proporcionados. Por favor, verifica tus datos.');
|
||||
}
|
||||
// --- Fin Smart Match ---
|
||||
|
||||
if ($jugador->activo) {
|
||||
return redirect()->route('asociate')->with('registro_msg', 'Este jugador ya está registrado en el sistema.');
|
||||
}
|
||||
|
||||
// Jugador encontrado e inactivo - mostrar formulario para completar
|
||||
$club = null;
|
||||
if ($jugador->id_club_actual) {
|
||||
$clubObj = \App\Models\Club::find($jugador->id_club_actual);
|
||||
$club = $clubObj ? $clubObj->nombre : null;
|
||||
}
|
||||
|
||||
$jugador_encontrado = [
|
||||
'documento' => $jugador->documento,
|
||||
'nombre' => $jugador->nombre,
|
||||
'apellido' => $jugador->apellido,
|
||||
'fecha_nacimiento' => $jugador->fecha_nacimiento,
|
||||
'club' => $club,
|
||||
'categoria' => $jugador->categoria,
|
||||
];
|
||||
|
||||
return view('auth.asociate', compact('jugador_encontrado'))->with('tab', 'jugador');
|
||||
}
|
||||
|
||||
public function completarRegistroJugador(Request $request)
|
||||
{
|
||||
if (!$this->verifyTurnstile($request->input('cf-turnstile-response'))) {
|
||||
return back()->with('registro_msg', '⚠️ Error de verificación de seguridad (Captcha).')->withInput();
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'dni' => 'required|string',
|
||||
'email' => 'required|email',
|
||||
'telefono' => 'nullable|string',
|
||||
'password' => 'required|confirmed|min:6',
|
||||
]);
|
||||
|
||||
$dni = preg_replace('/[^0-9]/', '', $request->input('dni'));
|
||||
|
||||
$jugador = Jugador::where('documento', $dni)->first();
|
||||
|
||||
if (!$jugador) {
|
||||
return redirect()->route('asociate')->with('registro_msg', 'Jugador no encontrado.');
|
||||
}
|
||||
|
||||
if ($jugador->activo) {
|
||||
return redirect()->route('asociate')->with('registro_msg', 'Este jugador ya está registrado.');
|
||||
}
|
||||
|
||||
$jugador->update([
|
||||
'email' => $request->input('email'),
|
||||
'telefono' => $request->input('telefono'),
|
||||
'password' => bcrypt($request->input('password')),
|
||||
'activo' => 1,
|
||||
'fecha_registro' => now(),
|
||||
]);
|
||||
|
||||
try {
|
||||
Mail::to($jugador->email)->send(new WelcomeMail($jugador, 'jugador'));
|
||||
} catch (\Exception $e) {
|
||||
Log::error("Error enviando mail de bienvenida a Jugador: " . $e->getMessage());
|
||||
}
|
||||
|
||||
return redirect()->route('asociate')->with('mensaje', '✅ Registro completado exitosamente. Ya podés iniciar sesión.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Normaliza un string para comparaciones (mayúsculas, sin acentos, sin espacios extras)
|
||||
*/
|
||||
private function normalizeString($str)
|
||||
{
|
||||
$unwanted_array = ['Š'=>'S', 'š'=>'s', 'Ž'=>'Z', 'ž'=>'z', 'À'=>'A', 'Á'=>'A', 'Â'=>'A', 'Ã'=>'A', 'Ä'=>'A', 'Å'=>'A', 'Æ'=>'A', 'Ç'=>'C', 'È'=>'E', 'É'=>'E',
|
||||
'Ê'=>'E', 'Ë'=>'E', 'Ì'=>'I', 'Í'=>'I', 'Î'=>'I', 'Ï'=>'I', 'Ñ'=>'N', 'Ò'=>'O', 'Ó'=>'O', 'Ô'=>'O', 'Õ'=>'O', 'Ö'=>'O', 'Ø'=>'O', 'Ù'=>'U',
|
||||
'Ú'=>'U', 'Û'=>'U', 'Ü'=>'U', 'Ý'=>'Y', 'Þ'=>'B', 'ß'=>'Ss', 'à'=>'a', 'á'=>'a', 'â'=>'a', 'ã'=>'a', 'ä'=>'a', 'å'=>'a', 'æ'=>'a', 'ç'=>'c',
|
||||
'è'=>'e', 'é'=>'e', 'ê'=>'e', 'ë'=>'e', 'ì'=>'i', 'í'=>'i', 'î'=>'i', 'ï'=>'i', 'ð'=>'o', 'ñ'=>'n', 'ò'=>'o', 'ó'=>'o', 'ô'=>'o', 'õ'=>'o',
|
||||
'ö'=>'o', 'ø'=>'o', 'ù'=>'u', 'ú'=>'u', 'û'=>'u', 'ý'=>'y', 'þ'=>'b', 'ÿ'=>'y'];
|
||||
|
||||
$str = strtr($str, $unwanted_array);
|
||||
return strtoupper(trim(preg_replace('/\s+/', ' ', $str)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Comprueba si una palabra coincide aproximadamente con alguna de la lista
|
||||
*/
|
||||
private function isApproxMatch($word, $list)
|
||||
{
|
||||
if (strlen($word) < 3) return false;
|
||||
|
||||
foreach ($list as $item) {
|
||||
if (strlen($item) < 3) continue;
|
||||
|
||||
// Coincidencia exacta o contenida
|
||||
if ($word === $item || strpos($item, $word) !== false || strpos($word, $item) !== false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Levenshtein para errores de tipeo (máximo 1 de distancia)
|
||||
if (levenshtein($word, $item) <= 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user