BeastBB/lib/BeastBB/DAO/UserManager.pm

396 lines
11 KiB
Perl

package BeastBB::DAO::UserManager;
use 5.32.1;
use strict;
use warnings;
use Carp;
use Params::ValidationCompiler 'validation_for';
use Types::Standard qw/Bool Str Int/;
use Crypt::Bcrypt::Easy;
use Const::Fast;
use DateTime;
use DateTime::Format::Pg;
use BeastBB::Types (
'$MATRIX_ADDRESS_REGEX', 'IsClassTypeGenerator',
'$MATRIX_ADDRESS_TYPE'
);
use BeastBB::DAO::GroupManager;
use BeastBB::Model::User;
use BeastBB::Response;
const my $MINIMUM_PASSWORD_LENGHT => 8;
{
my $validator = validation_for(
params => {
app => { type => IsClassTypeGenerator('Mojolicious::Controller') },
}
);
sub new {
my $class = shift;
my %params = $validator->(@_);
return bless \%params, $class;
}
}
{
my $validator = validation_for(
params => {
id_user => { type => Int, optional => 1 },
username => { type => Str, optional => 1 },
matrix_address => { type => $MATRIX_ADDRESS_TYPE, optional => 1 },
recover_group => { type => Bool, default => 0 },
}
);
sub Get {
my $self = shift;
my %params = $validator->(@_);
my $id_user = $params{id_user};
my $username = $params{username};
my $matrix_address = $params{matrix_address};
my $recover_group = $params{recover_group};
confess
'You should pass id_user xor matrix_address xor username to Get.'
unless ( defined $id_user xor defined $username
xor defined $matrix_address );
my $maybe_user_hash = $self->_RecoverUserFromDatabase(
( ( defined $id_user ) ? ( id_user => $id_user ) : () ),
( ( defined $username ) ? ( username => $username ) : () ),
(
( defined $matrix_address )
? ( matrix_address => $matrix_address )
: ()
)
);
return $maybe_user_hash if $maybe_user_hash->IsError;
my $user_hash = $maybe_user_hash->Content;
my $id_group = delete $user_hash->{id_group};
my $user = BeastBB::Model::User->new(%$user_hash);
if ($recover_group) {
my $maybe_recovered_group =
$self->_RecoverUserGroup( id_group => $id_group, user => $user );
return $maybe_recovered_group if $maybe_recovered_group->IsError;
}
return BeastBB::Response->new( content => $user );
}
}
{
my $validator = validation_for(
params => {
id_group => { type => Int },
user => { type => IsClassTypeGenerator('BeastBB::Model::User') },
}
);
sub _RecoverUserGroup {
my $self = shift;
my %params = $validator->(@_);
my $id_group = $params{id_group};
my $user = $params{user};
my $app = $self->_App;
my $group_manager = BeastBB::DAO::GroupManager->new( app => $app );
my $maybe_group = $group_manager->Get( id_group => $id_group );
if ( $maybe_group->IsError ) {
return $maybe_group;
}
my $group = $maybe_group->Content;
$user->Group($group);
return BeastBB::Response->new;
}
}
{
my $validator = validation_for(
params => {
id_user => { type => Int, optional => 1 },
username => { type => Str, optional => 1 },
matrix_address => { type => $MATRIX_ADDRESS_TYPE, optional => 1 },
}
);
sub _RecoverUserFromDatabase {
my $self = shift;
my %params = $validator->(@_);
my $app = $self->_App;
my $database = $app->db;
my $pg = $database->Pg->db;
my ( $id_user, $username, $matrix_address ) =
@params{ 'id_user', 'username', 'matrix_address' };
my $results = $pg->select(
'user',
[
'id_user', 'username', 'matrix_address', 'password_bcrypt',
'is_confirmed', 'creation_date', 'id_group', 'last_connection',
],
{
( ( defined $id_user ) ? ( id_user => $id_user ) : () ),
( ( defined $username ) ? ( username => $username ) : () ),
(
( defined $matrix_address )
? ( matrix_address => $matrix_address )
: ()
)
}
);
if ( !$results->rows ) {
return BeastBB::Response->new(
is_error => 1,
error_message => 'No such user found.',
);
}
my $user_hash = $results->hash;
$user_hash->{creation_date} = DateTime::Format::Pg->parse_datetime(
$user_hash->{creation_date} );
$user_hash->{last_connection} =
DateTime::Format::Pg->parse_datetime(
$user_hash->{last_connection} );
return BeastBB::Response->new( content => $user_hash );
}
}
{
my $validator = validation_for(
params => {
username => { type => Str },
matrix_address => { type => Str },
password => { type => Str },
repeat_password => { type => Str },
is_confirmed => { type => Bool },
groupname => { type => Str },
}
);
sub Create {
my $self = shift;
my %params = $validator->(@_);
my (
$username, $matrix_address, $password, $repeat_password,
$is_confirmed, $groupname
)
= @params{
'username', 'matrix_address', 'password',
'repeat_password', 'is_confirmed', 'groupname'
};
my $app = $self->_App;
my $database = $app->db;
my $pg = $database->Pg->db;
my $response_check_user_creation_requeriments =
$self->CheckUserCreationRequeriments(
matrix_address => $matrix_address,
password => $password,
repeat_password => $repeat_password
);
return $response_check_user_creation_requeriments
if $response_check_user_creation_requeriments->IsError;
my $result =
$pg->select( 'group', 'id_group', { groupname => $groupname } );
if ( !$result->rows ) {
return BeastBB::Response->new(
is_error => 1,
error_message => "Unable to find the group $groupname."
);
}
my $id_group = $result->hash->{id_group};
my $result_create_user = $pg->insert(
'user',
{
username => $username,
password_bcrypt => bcrypt->crypt($password),
matrix_address => $matrix_address,
is_confirmed => $is_confirmed ? 1 : 0,
creation_date => "" . DateTime->now,
last_connection => "" . DateTime->now,
id_group => $id_group,
},
{
returning => ['id_user'],
}
);
if ( !$result_create_user->rows ) {
return BeastBB::Response->new(
is_error => 1,
error_message => "Unable to create user $username.",
);
}
return BeastBB::Response->new(
content => $result_create_user->hash->{id_user} );
}
}
{
my $validator = validation_for(
params => {
matrix_address => { type => Str },
password => { type => Str },
repeat_password => { type => Str },
}
);
sub CheckUserCreationRequeriments {
my $self = shift;
my %params = $validator->(@_);
my ( $matrix_address, $password, $repeat_password ) =
@params{ 'matrix_address', 'password', 'repeat_password' };
my $response =
$self->CheckMatrixAddress( matrix_address => $matrix_address );
return $response if $response->IsError;
$response = $self->CheckPasswordLength( password => $password );
return $response if $response->IsError;
$response = $self->CheckPasswordNotOnlyNumeric( password => $password );
return $response if $response->IsError;
$response = $self->CheckTwoPasswordsEqual(
password => $password,
repeat_password => $repeat_password
);
return $response;
}
}
{
my $validator = validation_for(
params => {
password => { type => Str },
repeat_password => { type => Str },
}
);
sub CheckTwoPasswordsEqual {
my $self = shift;
my %params = $validator->(@_);
my $password = $params{password};
my $repeat_password = $params{repeat_password};
my $error_message;
if ( $password ne $repeat_password ) {
$error_message = 'Password and repeat password not matching.';
}
return BeastBB::Response->new(
(
( defined $error_message )
? (
is_error => 1,
error_message => $error_message
)
: ()
)
);
}
}
{
my $validator = validation_for(
params => {
password => { type => Str },
}
);
sub CheckPasswordNotOnlyNumeric {
my $self = shift;
my %params = $validator->(@_);
my $password = $params{'password'};
my $error_message;
if ( $password =~ /^\d+$/ ) {
$error_message = "Password is numeric, it is not allowed.";
}
return BeastBB::Response->new(
(
( defined $error_message )
? (
is_error => 1,
error_message => $error_message
)
: ()
)
);
}
}
{
my $validator = validation_for(
params => {
password => { type => Str },
}
);
sub CheckPasswordLength {
my $self = shift;
my %params = $validator->(@_);
my $password = $params{'password'};
my $error_message;
if ( !length($password) > $MINIMUM_PASSWORD_LENGHT ) {
$error_message =
"Password has less than $MINIMUM_PASSWORD_LENGHT characters.";
}
return BeastBB::Response->new(
(
( defined $error_message )
? (
is_error => 1,
error_message => $error_message
)
: ()
)
);
}
}
{
my $validator = validation_for(
params => { matrix_address => { type => Str } },
);
sub CheckMatrixAddress {
my $self = shift;
my %params = $validator->(@_);
my $matrix_address = $params{matrix_address};
my $error_message;
if ( $matrix_address !~ /$MATRIX_ADDRESS_REGEX/ ) {
$error_message = "This does not look like a Matrix address.";
}
return BeastBB::Response->new(
(
( defined $error_message )
? (
is_error => 1,
error_message => $error_message
)
: ()
)
);
}
}
sub _App {
my $self = shift;
return $self->{app};
}
1;