package BurguillosInfo::Controller::UserConquer; use v5.34.1; use strict; use warnings; use utf8; use Mojo::Base 'Mojolicious::Controller', '-signatures'; use UUID::URandom qw/create_uuid_string/; use Crypt::Bcrypt qw/bcrypt bcrypt_check/; use Crypt::URandom qw/urandom/; use JSON; use BurguillosInfo::Schema; my $username_minimum_chars = 3; my $username_maximum_chars = 15; my $password_minimum_chars = 8; my $password_maximum_chars = 4096; sub get_self ($self) { my $user = $self->current_user; if ( !defined $user ) { return $self->_renderError( 401, 'No estás loggeado.' ); } return $self->render( json => $user->serialize_to_owner, status => 200 ); } sub create ($self) { my $input = $self->_expectJson; if ( !defined $input ) { return; } my $username = $input->{username}; my $password = $input->{password}; my $repeat_password = $input->{repeat_password}; return unless $self->_createCheckInput( $username, $password, $repeat_password ); return $self->_createUser( $username, $password ); } sub _expectJson ($self) { my $input; eval { $input = $self->req->json; }; if ($@) { say STDERR $@; $self->_renderError( 400, 'Se esperaba JSON.' ); return; } return $input; } sub login ($self) { my $input = $self->_expectJson; if ( !defined $input ) { return; } my $username = $input->{username}; my $password = $input->{password}; my $resultset_conquer_user = BurguillosInfo::Schema->Schema->resultset('ConquerUser'); my @tentative_users = $resultset_conquer_user->search( { username => $username } ); my $tentative_user = $tentative_users[0]; if ( !defined $tentative_user ) { $self->_renderError( 401, 'El usuario especificado no existe.' ); return; } if ( !bcrypt_check( $password, $tentative_user->encrypted_password ) ) { $self->_renderError( 401, 'Contraseña incorrecta.' ); return; } my $user = $tentative_user; $self->set_current_user($user); $self->render( json => { success => $JSON::true }, status => 200 ); } sub setCoordinates ($self) { my $input = $self->_expectJson; my $user = $self->current_user; if ( !defined $user ) { return $self->render( status => 401, json => { error => 'Debes estar loggeado para cambiar tus' . ' coordenadas.', } ); } if ( !defined $input ) { return; } if ( ref $input ne 'ARRAY' && scalar $input->@* == 2 ) { return $self->render( status => 400, json => { error => 'Mal formato de coordenadas, debe ser ' . 'un array de exactamente 2 números reales.', } ); } $user->coordinates($input); $user->update; return $self->render( status => 200, json => { ok => $JSON::true, } ); } sub _createUser ( $self, $username, $password ) { my $user; my $uuid = create_uuid_string(); my $new_salt = urandom(16); my $encrypted_password = bcrypt $password, '2b', 12, $new_salt; eval { $user = BurguillosInfo::Schema->Schema->resultset('ConquerUser')->new( { uuid => $uuid, encrypted_password => $encrypted_password, username => $username } ); $user->coordinates( [ 0, 0 ] ); $user->insert; }; if ($@) { if ( $@ =~ /Key \((.*?)\)=\((.*?)\) already exists\./ ) { return $self->_renderError( 400, "La clave $1 ($2) ya existe en la base de datos.", ); } say STDERR $@; return $self->_renderError( 400, 'No se pudo crear el usuario por razones desconocidas.' ); } $self->render( status => 200, json => $user->serialize_to_owner ); return 1; } sub _renderError ( $self, $status, $message ) { $self->render( status => $status, json => { error => $message } ); return 0; } sub _createCheckInput ( $self, $username, $password, $repeat_password ) { if ( !defined $username || $username !~ /^(?:\w|\d|[ÑÁÉÍÓÚñáéíóú ]){$username_minimum_chars,$username_maximum_chars}$/ ) { return $self->_renderError( 400, "Username invalido, las reglas son tamaño entre $username_minimum_chars y $username_maximum_chars" . ' carácteres y solo se podrán usar letras, números y espacios.' ); } if ( !defined $password || $password eq $username || $password !~ /^.{$password_minimum_chars,$password_maximum_chars}$/ || $password =~ /^\d+$/ ) { return $self->_renderError( 400, 'Contraseña invalida, las reglas son la contraseña debe ser' . ' distinta al nombre de usuario, la contraseña debe tener entre' . " $password_minimum_chars y $password_maximum_chars carácteres" . ' (Tu contraseña no se guardará en texto plano, el límite de' . " $password_maximum_chars caracteres es para evitar denegaciones" . ' de servicio), la contraseña no puede estar compuesta solo de números.', ); } if ( !defined $repeat_password || $password ne $repeat_password ) { $self->_renderError( 400, 'El campo de repetir contraseña debe coincidir de forma' . ' totalmente exacta con el campo de contraseña para asegurar' . ' que podrás recordar la contraseña y/o que no has cometido' . ' ningún error, si pierdes el acceso a tu cuenta no podrás' . ' recuperarlo de ningún modo.', ); return 0; } return 1; } 1;