burguillos.info/lib/BurguillosInfo/Controller/UserConquer.pm

236 lines
7.1 KiB
Perl

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 setTeamForUser($self) {
my $user = $self->current_user;
if (!defined $user) {
return $self->_renderError(401, 'No estás loggeado.');
}
my $input = $self->_expectJson;
if (!defined $input) {
return;
}
my $node_uuid = $input->{node};
my $team_uuid = $input->{team};
my $resultset_team = BurguillosInfo::Schema->Schema->resultset('ConquerTeam');
my $resultset_node = BurguillosInfo::Schema->Schema->resultset('ConquerNode');
my @teams = $resultset_team->search({uuid => $team_uuid});
my @nodes = $resultset_node->search({uuid => $node_uuid});
if (scalar @teams < 1) {
return $self->render(status => 404, json => {
error => 'No se encontró ese equipo.',
});
}
if (scalar @nodes < 1) {
return $self->render(status => 404, json => {
error => 'No se encontró este nodo.',
});
}
my $team = $teams[0];
my $node = $nodes[0];
if (!$node->is_near($user)) {
return $self->render(status => 400, json => {
error => 'Estás demasiado lejos del nodo.',
});
}
$user = $user->get_from_storage;
$user->team_object($team);
$user->update;
return $self->render(json => {
ok => $JSON::true,
});
}
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;