From e6483ee220bf7337358e9c11b675f27ec91ba4eb Mon Sep 17 00:00:00 2001 From: Irina Peshinskaya Date: Sun, 2 Dec 2018 12:32:20 +0100 Subject: [PATCH] TT#46856 Add openvpn toggle from admin panel Change-Id: I128e49117bd211edc44e2bf286c80cfc5a677b6a --- lib/NGCP/Panel/Controller/Administrator.pm | 35 +++++ lib/NGCP/Panel/Controller/Root.pm | 4 + lib/NGCP/Panel/Utils/Admin.pm | 146 ++++++++++++++++++ lib/NGCP/Panel/Utils/Message.pm | 23 ++- share/layout/body.tt | 56 ++++--- share/templates/administrator/openvpn.tt | 37 +++++ .../administrator/openvpn_wrapper.tt | 21 +++ 7 files changed, 300 insertions(+), 22 deletions(-) create mode 100644 share/templates/administrator/openvpn.tt create mode 100644 share/templates/administrator/openvpn_wrapper.tt diff --git a/lib/NGCP/Panel/Controller/Administrator.pm b/lib/NGCP/Panel/Controller/Administrator.pm index 736a701f48..7b305df81b 100644 --- a/lib/NGCP/Panel/Controller/Administrator.pm +++ b/lib/NGCP/Panel/Controller/Administrator.pm @@ -369,6 +369,41 @@ sub api_key :Chained('base') :PathPart('api_key') :Args(0) { ); } +sub toggle_openvpn :Chained('list_admin') :PathPart('openvpn/toggle') :Args(1) { + my ($self, $c, $set_active) = @_; + + unless ($set_active eq 'confirm') { + my ($message, $error) = NGCP::Panel::Utils::Admin::toggle_openvpn($c, $set_active); + if ( $message ) { + NGCP::Panel::Utils::Message::info( + c => $c, + desc => $c->loc($message), + #modal info screen + stash => 1, + flash => 0, + ); + } + if ( $error ) { + NGCP::Panel::Utils::Message::error( + c => $c, + error => $error, + desc => $c->loc($error), + #modal info screen, we don't need to show error later on some sporadic screen + stash => 1, + flash => 0, + ); + } + } else { + $c->stash( + confirm => 1, + ); + } + $c->stash( + template => 'administrator/openvpn.tt', + ); + $c->detach( $c->view('TT') ); +} + 1; __END__ diff --git a/lib/NGCP/Panel/Controller/Root.pm b/lib/NGCP/Panel/Controller/Root.pm index 06b021ed67..c7f614ef1e 100644 --- a/lib/NGCP/Panel/Controller/Root.pm +++ b/lib/NGCP/Panel/Controller/Root.pm @@ -283,6 +283,10 @@ sub auto :Private { my $topmenu_templates = []; if ($c->user->roles eq 'admin') { $topmenu_templates = ['widgets/admin_topmenu_settings.tt']; + if (!$c->stash->{openvpn_info}) { + my $openvpn_info = NGCP::Panel::Utils::Admin::check_openvpn_status($c); + $c->stash(openvpn_info => $openvpn_info); + } } elsif ($c->user->roles eq 'reseller') { $topmenu_templates = ['widgets/reseller_topmenu_settings.tt']; } elsif ($c->user->roles eq 'subscriberadmin') { diff --git a/lib/NGCP/Panel/Utils/Admin.pm b/lib/NGCP/Panel/Utils/Admin.pm index 97314534bb..aac906d03e 100644 --- a/lib/NGCP/Panel/Utils/Admin.pm +++ b/lib/NGCP/Panel/Utils/Admin.pm @@ -4,6 +4,8 @@ use Sipwise::Base; use Crypt::Eksblowfish::Bcrypt qw/bcrypt_hash en_base64 de_base64/; use Data::Entropy::Algorithms qw/rand_bits/; use IO::Compress::Zip qw/zip/; +use IPC::System::Simple qw/capturex/; + sub get_special_admin_login { return 'sipwise'; @@ -140,4 +142,148 @@ sub generate_client_cert { return { serial => $serial, file => $zipped_file }; } +sub check_openvpn_status { + my ($c, $params) = @_; + $params //= {}; + #possible params: + # - no_check_availability - check availability is expensive. If we did it already, we can skip it + # - check_status - check particular command, active or enabled + my $ret = { + allowed => 0, #based on role + available => 0, #unavailable + enabled => 0, #disabled + active => 0, #inactive + }; + my $config = $c->config->{openvpn} // {}; + my $systemctl_cmd = $config->{command}; + #default service is openvpn@ovpn, where ovpn profile is a openvpn config located at /etc/openvpn/ovpn.conf + my $openvpn_service = $config->{service}; + if (!$config->{allowed}) { + return $ret; + } + if ($params->{no_check_availability} || check_openvpn_availability($c)) { + $ret->{available} = 1; + if ( !$params->{check_status} || $params->{check_status} eq 'enabled') { + my $output = cmd($c, undef, $systemctl_cmd, 'is-enabled', $openvpn_service); + if ($output eq 'enabled') {#what we will see on localized OS? should "enabled" be localized? + $ret->{enabled} = 1; + } elsif ($output ne 'disabled') { + #all other is-enabled responses, except "enabled" and "disabled" mean that service is not available at all + $ret->{available} = 0; + } + } + if ($ret->{available}) { + if ( !$params->{check_status} || $params->{check_status} eq 'active') { + my $output = cmd($c, undef, $systemctl_cmd, 'is-active', $openvpn_service); + if ($output eq 'active') { + $ret->{active} = 1; + } + } + } + } + #testing + #$ret->{active} = 1; + return $ret; +} + +sub check_openvpn_availability { + my ($c) = @_; + my $res = 0; + my $config = $c->config->{openvpn} // {}; + my $systemctl_cmd = $config->{command}; + #default service is openvpn@ovpn, where ovpn profile is a openvpn config located at /etc/openvpn/ovpn.conf + my $openvpn_service = $config->{service}; + my $output = cmd($c, {no_debug_output =>1 }, $systemctl_cmd, 'list-unit-files'); + #$c->log->debug( $output ); + if ($output =~/^openvpn.service/m) { + $res = 1; + } + return $res; +} + +sub toggle_openvpn { + my ($c, $set_active) = @_; + my ($message, $error); + my $config = $c->config->{openvpn} // {}; + my $systemctl_cmd = $config->{command}; + #default service is openvpn@ovpn, where ovpn profile is a openvpn config located at /etc/openvpn/ovpn.conf + my $openvpn_service = $config->{service}; + my $status_in = check_openvpn_status($c); + my $status_out; + if (!$status_in->{allowed}) { + $error = $c->loc('Openvpn service is not enabled or host role is not allowed.'); + } elsif (!$status_in->{available}) { + $error = $c->loc('Openvpn service is not avaialbe on the system.'); + } else { + if ($set_active) { + if ( $status_in->{active} ) { + $message = $c->loc('Openvpn connection is already opened.'); + } else { + my $status_enabled = { enabled => $status_in->{enabled} }; + if (!$status_enabled->{enabled}) { + $error = cmd($c, undef, $systemctl_cmd, 'enable', $openvpn_service); + if (!$error) { + my $status_enabled = check_openvpn_status($c, { + no_check_availability => 1, + check_status => 'enabled', + }, + ); + } + } + if ($status_enabled->{enabled}) { + $error = cmd($c, undef, $systemctl_cmd, 'start', $openvpn_service); + if (!$error) { + $status_out = check_openvpn_status($c, { + no_check_availability => 1, + }, + ); + if ($status_out->{active}) { + $message = $c->loc('Openvpn connection open.'); + } else { + $error = $c->loc('Can not open openvpn connection.'); + } + } + } else { + $error = $c->loc('Can not enable openvpn.'); + } + } + } else { #requested to close connection + if ( !$status_in->{active} ) { + $message = $c->loc('Openvpn connection is already closed.'); + } else { + $error = cmd($c, undef, $systemctl_cmd, 'stop', $openvpn_service); + } + if (!$error) { + $status_out = check_openvpn_status($c, { + no_check_availability => 1, + }, + ); + if (!$status_out->{active}) { + $message = $c->loc('Openvpn connection closed.'); + } else { + $error = $c->loc('Can not close openvpn connection.'); + } + } + } + } + return $message, $error; +} + +sub cmd { + my($c, $params, $cmd, @cmd_args) = @_; + $params //= {}; + my $cmd_full = $cmd.' '.join(' ', @cmd_args); + $c->log->debug( $cmd_full ); + my $output = ''; + try { + $output = capturex([0..1,3], $cmd, @cmd_args); + $output =~s/^\s+|\s+$//g; + $c->log->debug( "output=$output;" ) unless $params->{no_debug_output}; + } catch ($e) { + $c->log->debug( "error=$e;" ); + return $e; + } + return $output; +} + 1; diff --git a/lib/NGCP/Panel/Utils/Message.pm b/lib/NGCP/Panel/Utils/Message.pm index c1f8ff7456..70a7ca6c41 100644 --- a/lib/NGCP/Panel/Utils/Message.pm +++ b/lib/NGCP/Panel/Utils/Message.pm @@ -180,11 +180,22 @@ sub error { my $rc = $c->log->error( sprintf $logstr, @{$log_params}{qw(r_ip called tx_id r_user data)}, $msg, $log_msg); if ($type eq 'panel') { - $c->flash(messages => [{ type => $usr_type, + if (!defined $params{flash} || $params{flash} ) { + $c->flash(messages => [{ type => $usr_type, text => sprintf '%s [%s]', $usr_text, $log_params->{tx_id}, }]); + } + if ($params{stash} ) { + my $messages = $c->stash->{messages} // []; + push @$messages, { type => $usr_type, + text => sprintf '%s [%s]', + $usr_text, + $log_params->{tx_id}, + }; + $c->stash(messages => $messages); + } $c->stash(panel_error_message => $msg); } return $rc; @@ -230,7 +241,15 @@ sub info { sprintf $logstr, @{$log_params}{qw(r_ip called tx_id r_user data)}, $msg, $log_msg); if ($type eq 'panel') { - $c->flash(messages => [{ type => $usr_type, text => $usr_text }]); + #flash is on by default + if (!defined $params{flash} || $params{flash} ) { + $c->flash(messages => [{ type => $usr_type, text => $usr_text }]); + } + if ($params{stash} ) { + my $messages = $c->stash->{messages} // []; + push @$messages, { type => $usr_type, text => $usr_text }; + $c->stash(messages => $messages); + } } return $rc; } diff --git a/share/layout/body.tt b/share/layout/body.tt index 95aac2e0a4..3a8185b73b 100644 --- a/share/layout/body.tt +++ b/share/layout/body.tt @@ -18,6 +18,7 @@ [% login_name = c.user.webusername _'@'_ c.user.domain.domain %] [%- END -%] [% c.loc("Logged in as [_1]", login_name) %] + [%- ELSE -%] [% c.loc("Not logged in") %] [%- END -%] @@ -25,20 +26,31 @@
  • [% IF c.user && c.session.user_tz_name; '(' _ c.session.user_tz_name _ ')'; END; %]
  • +[%- IF ( c.user && ( c.user.roles == 'admin' || c.user.is_superuser ) ) && openvpn_info.allowed -%] +[%- IF !openvpn_info.active %] +
  • + Openvpn +
  • +[%ELSE-%] +
  • + Openvpn +
  • +[%END-%] +[%END-%] + +
  • [% c.loc('Logout') %]
  • @@ -68,13 +80,13 @@
    -
    -
    -
    -

    [% template.title or site_config.title %]

    -
    -
    -
    +
    +
    +
    +

    [% template.title or site_config.title %]

    +
    +
    +
    @@ -106,7 +118,7 @@ + + +[%PROCESS 'administrator/openvpn_wrapper.tt' %] + [% # vim: set tabstop=4 syntax=html expandtab: -%] diff --git a/share/templates/administrator/openvpn.tt b/share/templates/administrator/openvpn.tt new file mode 100644 index 0000000000..54c4a4a7ce --- /dev/null +++ b/share/templates/administrator/openvpn.tt @@ -0,0 +1,37 @@ + +[%IF confirm -%] +[%- IF c.user && c.user.roles == 'admin' || c.user.is_superuser -%] + +[%- IF !openvpn_info.active %] + Open connection? +[%ELSE-%] + Close connection? +[%END-%] + +[%END-%] +[%ELSE%] +[%IF messages.size -%] + [% FOREACH m IN messages -%] +
    [% m.text %]
    + [% END -%] +[% END -%] +[% END -%] + + + \ No newline at end of file diff --git a/share/templates/administrator/openvpn_wrapper.tt b/share/templates/administrator/openvpn_wrapper.tt new file mode 100644 index 0000000000..850452cd57 --- /dev/null +++ b/share/templates/administrator/openvpn_wrapper.tt @@ -0,0 +1,21 @@ + + + \ No newline at end of file