From 0c945e1177689e88f8d8ea37f7e0b218be7a4d9c Mon Sep 17 00:00:00 2001 From: sergiotarxz Date: Sat, 13 Nov 2021 03:15:18 +0100 Subject: [PATCH] Adding endpoint to add services. --- cualsea-server/lib/Cualsea/Server/DB.pm | 23 ++- .../lib/Cualsea/Server/MessageController.pm | 162 +++++++++++++++--- 2 files changed, 158 insertions(+), 27 deletions(-) diff --git a/cualsea-server/lib/Cualsea/Server/DB.pm b/cualsea-server/lib/Cualsea/Server/DB.pm index 386240b..0beaca8 100644 --- a/cualsea-server/lib/Cualsea/Server/DB.pm +++ b/cualsea-server/lib/Cualsea/Server/DB.pm @@ -32,14 +32,35 @@ sub dbh { run_migrations($dbh); $migrations_run = 1; } + return $dbh; } sub run_migrations { my $dbh = shift; + my $current_migration = _get_current_migration_number($dbh); + say $current_migration; + say scalar @migrations; + if ($current_migration < scalar @migrations) { + my @needed_migrations = @migrations[$current_migration .. $#migrations]; + for my $migration (@needed_migrations) { + $dbh->do($migration); + if (!(0+$dbh->do('UPDATE options SET value = ? WHERE key = "migration"', undef, ++$current_migration))) { + $dbh->do('INSERT INTO options (key, value) VALUES ("migration", ?)', undef, $current_migration); + } + } + } +} +sub _get_current_migration_number { + my $dbh = shift; + local $dbh->{RaiseError} = 0; my $migration = $dbh->selectrow_hashref(<<'EOF', {}); SELECT value FROM options WHERE key = 'migration' EOF - + my $value = 0; + if (defined $migration) { + $value = $migration->{value}; + } + return $value; } 1; diff --git a/cualsea-server/lib/Cualsea/Server/MessageController.pm b/cualsea-server/lib/Cualsea/Server/MessageController.pm index ccb69eb..d3b29a6 100644 --- a/cualsea-server/lib/Cualsea/Server/MessageController.pm +++ b/cualsea-server/lib/Cualsea/Server/MessageController.pm @@ -7,33 +7,75 @@ use warnings; use Data::Dumper; use Socket qw/SOL_SOCKET SO_PEERCRED/; +use List::Util qw/any/; +use Carp qw/carp/; use Params::ValidationCompiler qw/validation_for/; use Types::Standard qw/Object HashRef Str/; +use Try::Tiny; use Cualsea::MessageManager; +use Cualsea::Server::DB; -my %commands = { +sub handle_add { + my %params = @_; + my $name = $params{name}; + my $init = $params{init}; + my $pidfile = $params{pidfile}; + my $binpath = $params{binpath}; + if ( !-e $binpath || $init ne 'sysvinit' || !_validate_init_name($name) ) + { + return { + is_error => 1, + desc => "Parameters didn't pass contraints on add", + status => 400 + }; + } + my $dbh = Cualsea::Server::DB->dbh(); + + eval { + $dbh->do( << 'EOF', undef, $name, $init, $pidfile, $binpath ); +INSERT INTO services (name, init, pidfile, binpath) VALUES (?, ?, ?, ?); +EOF + }; + if ($@) { + if ($@ =~ /UNIQUE/i) { + my $return_value = { + status => 400, + is_error => 1, + desc => 'Endpoint already exists.', + }; + return $return_value; + } + carp $@; + } + return { + is_error => 0, + desc => "Succesful creation of init service $name.", + status => 200 + }; +} + +my %COMMANDS = ( add => { params => [ 'name', - 'init', # Only sysvinit supported + 'init', # Only sysvinit supported 'pidfile', 'binpath' ], validator => validation_for( params => { - name => { type => Str }, - init => { type => Str }, + name => { type => Str }, + init => { type => Str }, pidfile => { type => Str }, binpath => { type => Str }, } ), + handle => \&handle_add, }, del => { - params => [ - 'name' - ], + params => ['name'], validator => validation_for( params => { name => { type => Str }, @@ -41,15 +83,11 @@ my %commands = { ), }, report => { - params => [], - validator => validation_for( - params => {}, - ), + params => [], + validator => sub { }, }, start => { - params => [ - 'name', - ], + params => ['name'], validator => validation_for( params => { name => { type => Str }, @@ -57,9 +95,7 @@ my %commands = { ), }, stop => { - params => [ - 'name', - ], + params => ['name'], validator => validation_for( params => { name => { type => Str }, @@ -67,16 +103,32 @@ my %commands = { ), }, restart => { - params => [ - 'name', - ], + params => ['name'], validator => validation_for( params => { name => { type => Str }, } ), } +); + +sub _validate_init_name { + my $init_name = shift; + if ( $init_name =~ /^\.$/ ) { + return 0; + } + if ( $init_name =~ /^\.\.$/ ) { + return 0; + } + if ( $init_name =~ /\// ) { + return 0; + } + if ( !-e "/etc/init.d/$init_name" ) { + return 0; + } + return 1; } + sub new { my $class = shift; return bless {}, $class; @@ -95,14 +147,72 @@ sub new { my $socket = $params{socket}; my $message_manager = Cualsea::MessageManager->new( socket => $socket ); my $message = $message_manager->read_message; - if ( !$self->check_if_user_has_permissions( socket => $socket ) ) { + if ( !$self->check_if_user_has_permissions( socket => $socket ) ) { $self->write_no_permission( message_manager => $message_manager ); } if ( !$self->check_is_command( message => $message ) ) { $self->write_malformed( message_manager => $message_manager ); return; } - $message_manager->write_message( message => $message ); + $message_manager->write_message( + message => $self->_handle_command( message => $message ) ); + } +} + +{ + my $validator = validation_for( + params => { + message => { type => HashRef }, + } + ); + + sub _handle_command { + my $self = shift; + my %params = $validator->(@_); + my $message = $params{message}; + my $command = $message->{command}; + my $params = $message->{arguments} // []; + say "Got parameters:"; + say Data::Dumper::Dumper $params; + if ( !exists $COMMANDS{$command} ) { + return { is_error => 1, status => 404 }; + } + + my $command_info = $COMMANDS{$command}; + + my $params_info = $command_info->{params}; + print Data::Dumper::Dumper $params_info; + + # We want to forget about $params arrayref + # and previous hash as soon as possible. + %params = (); + for my $param_info_index ( scalar @$params_info ? (0..$#$params_info) : () ) { + + my $param_info = $params_info->[$param_info_index]; + # May not come defined, + if ( $param_info_index > scalar $#$params ) { + last; + } + say "$param_info => @{[$params->[$param_info_index]]}"; + $params{$param_info} = $params->[$param_info_index]; + } + my $validator = $command_info->{validator}; + my $return_error; + try { + $validator->(%params); + } + catch { + $return_error = { is_error => 1, status => 400, desc => $_->error }; + }; + if ( defined $return_error ) { + return $return_error; + } + + my $handle = $command_info->{handle}; + if ( !defined $handle ) { + return { is_error => 1, status => 501 }; + } + return $handle->(%params); } } @@ -117,11 +227,11 @@ sub new { my $self = shift; my %params = $validator->(@_); my $socket = $params{socket}; - my ($pid, $uid, $gid) = unpack 'LLL', $socket->sockopt(SO_PEERCRED); + my ( $pid, $uid, $gid ) = unpack 'LLL', $socket->sockopt(SO_PEERCRED); my $user = getpwuid($uid); - while (my ($name, undef, undef, $members) = getgrent()) { - if ($name eq 'cualsea') { - return List::Util::any { $_ eq $user } split ',', $members; + while ( my ( $name, undef, undef, $members ) = getgrent() ) { + if ( $name eq 'cualsea' ) { + return any { $_ eq $user } split ',', $members; last; } }