mirror of
https://github.com/lucaspalomodevelop/core.git
synced 2026-03-20 11:26:13 +00:00
VPN / IPSec / Tunnel settings - Change overview page to support pagination lowering load times on large setups (https://github.com/opnsense/core/issues/5279)
o add legacy control buttons (edit, clone) o refactor LegacySubsystemController to include "enable" status and simplify applyConfigAction to be more or less the same as its mvc cousins o add alternate id fields for phase1/2 search actions o add toggle phase[1|2] actions o add toggle IPsec enable action o copy legacy "apply changes" dialog from key_pairs.volt
This commit is contained in:
parent
3be0173e55
commit
42e8f99918
@ -1,6 +1,7 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2021 Deciso B.V.
|
||||
* Copyright (C) 2019 Pascal Mathis <mail@pascalmathis.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
@ -30,6 +31,8 @@ namespace OPNsense\IPsec\Api;
|
||||
|
||||
use OPNsense\Base\ApiControllerBase;
|
||||
use OPNsense\Core\Backend;
|
||||
use OPNsense\Core\Config;
|
||||
|
||||
|
||||
/**
|
||||
* Class LegacySubsystemController
|
||||
@ -45,6 +48,7 @@ class LegacySubsystemController extends ApiControllerBase
|
||||
public function statusAction()
|
||||
{
|
||||
return [
|
||||
'enabled' => isset(Config::getInstance()->object()->ipsec->enable),
|
||||
'isDirty' => file_exists('/tmp/ipsec.dirty') // is_subsystem_dirty('ipsec')
|
||||
];
|
||||
}
|
||||
@ -56,28 +60,14 @@ class LegacySubsystemController extends ApiControllerBase
|
||||
*/
|
||||
public function applyConfigAction()
|
||||
{
|
||||
try {
|
||||
if (!$this->request->isPost()) {
|
||||
throw new \Exception(gettext('Request method not allowed, expected POST'));
|
||||
$result = ["status" => "failed"];
|
||||
if ($this->request->isPost()) {
|
||||
$bckresult = trim((new Backend())->configdRun('ipsec reconfigure'));
|
||||
if ($bckresult === 'OK') {
|
||||
$result['message'] = gettext('The changes have been applied successfully.');
|
||||
$result['status'] = "ok";
|
||||
@unlink('/tmp/ipsec.dirty');
|
||||
}
|
||||
|
||||
$backend = new Backend();
|
||||
$bckresult = trim($backend->configdRun('ipsec reconfigure'));
|
||||
if ($bckresult !== 'OK') {
|
||||
throw new \Exception($bckresult);
|
||||
}
|
||||
|
||||
// clear_subsystem_dirty('ipsec')
|
||||
if (!@unlink('/tmp/ipsec.dirty')) {
|
||||
throw new \Exception(gettext('Could not remove /tmp/ipsec.dirty to mark subsystem as clean'));
|
||||
}
|
||||
|
||||
return ['message' => gettext('The changes have been applied successfully.')];
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception(sprintf(
|
||||
gettext('Unable to apply IPsec subsystem configuration: %s'),
|
||||
$e->getMessage()
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -144,6 +144,7 @@ class TunnelController extends ApiControllerBase
|
||||
}
|
||||
$item = [
|
||||
"id" => intval((string)$p1->ikeid), // ikeid should be unique
|
||||
"seqid" => $idx,
|
||||
"enabled" => empty((string)$p1->disabled) ? "1" : "0",
|
||||
"protocol" => $p1->protocol == "inet" ? "IPv4" : "IPv6",
|
||||
"iketype" => $ph1type[(string)$p1->iketype],
|
||||
@ -272,6 +273,7 @@ class TunnelController extends ApiControllerBase
|
||||
}
|
||||
$item = [
|
||||
"id" => $p2idx,
|
||||
"uniqid" => (string)$p2->uniqid, // XXX: a bit convoluted, should probably replace id at some point
|
||||
"ikeid" => $ikeid,
|
||||
"enabled" => empty((string)$p2->disabled) ? "1" : "0",
|
||||
"protocol" => $p2->protocol == "esp" ? "ESP" : "AH",
|
||||
@ -303,7 +305,7 @@ class TunnelController extends ApiControllerBase
|
||||
if (!empty($phase)) {
|
||||
$idx = 0;
|
||||
foreach ($phase as $p) {
|
||||
if(intval((string)$p->ikeid) == intval($ikeid)) {
|
||||
if (intval((string)$p->ikeid) == intval($ikeid)) {
|
||||
$phase_ids[$phid][] = $idx;
|
||||
}
|
||||
$idx++;
|
||||
@ -326,6 +328,41 @@ class TunnelController extends ApiControllerBase
|
||||
return ['status' => 'failed'];
|
||||
}
|
||||
|
||||
/**
|
||||
* toggle if phase 1 is enabled
|
||||
*/
|
||||
public function togglePhase1Action($ikeid, $enabled = null)
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$this->sessionClose();
|
||||
Config::getInstance()->lock();
|
||||
$config = Config::getInstance()->object();
|
||||
if (!empty($config->ipsec->phase1)) {
|
||||
$idx = 0;
|
||||
foreach ($config->ipsec->phase1 as $p1) {
|
||||
if (intval((string)$p1->ikeid) == intval($ikeid)) {
|
||||
if ($enabled == "0" || $enabled == "1") {
|
||||
$new_status = $enabled == "1" ? "0" : "1";
|
||||
} else {
|
||||
$new_status = $config->ipsec->phase1[$idx]->disabled == "1" ? "0" : "1";
|
||||
}
|
||||
if ($new_status == "1") {
|
||||
$config->ipsec->phase1[$idx]->disabled = $new_status;
|
||||
} elseif (isset($config->ipsec->phase1[$idx]->disabled)) {
|
||||
unset($config->ipsec->phase1[$idx]->disabled);
|
||||
}
|
||||
|
||||
Config::getInstance()->save();
|
||||
@touch("/tmp/ipsec.dirty");
|
||||
return ['status' => 'ok', 'disabled' => $new_status];
|
||||
}
|
||||
$idx++;
|
||||
}
|
||||
}
|
||||
return ['status' => 'not_found'];
|
||||
}
|
||||
return ['status' => 'failed'];
|
||||
}
|
||||
|
||||
/**
|
||||
* delete phase 2 entry
|
||||
@ -333,20 +370,73 @@ class TunnelController extends ApiControllerBase
|
||||
public function delPhase2Action($seqid)
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$phase_ids = [];
|
||||
$this->sessionClose();
|
||||
Config::getInstance()->lock();
|
||||
$config = Config::getInstance()->object();
|
||||
if (isset($config->ipsec->phase2[intval($seqid)])) {
|
||||
if ((string)intval($seqid) == $seqid && isset($config->ipsec->phase2[intval($seqid)])) {
|
||||
unset($config->ipsec->phase2[intval($seqid)]);
|
||||
Config::getInstance()->save();
|
||||
if (!empty($phase_ids[0])) {
|
||||
@touch("/tmp/ipsec.dirty");
|
||||
}
|
||||
@touch("/tmp/ipsec.dirty");
|
||||
return ['status' => 'ok'];
|
||||
}
|
||||
return ['status' => 'not_found'];
|
||||
}
|
||||
return ['status' => 'failed'];
|
||||
}
|
||||
|
||||
/**
|
||||
* toggle if phase 2 is enabled
|
||||
*/
|
||||
public function togglePhase2Action($seqid, $enabled = null)
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$this->sessionClose();
|
||||
Config::getInstance()->lock();
|
||||
$config = Config::getInstance()->object();
|
||||
if ((string)intval($seqid) == $seqid && isset($config->ipsec->phase2[intval($seqid)])) {
|
||||
if ($enabled == "0" || $enabled == "1") {
|
||||
$new_status = $enabled == "1" ? "0" : "1";
|
||||
} else {
|
||||
$new_status = $config->ipsec->phase2[intval($seqid)]->disabled == "1" ? "0" : "1";
|
||||
}
|
||||
if ($new_status == "1") {
|
||||
$config->ipsec->phase2[intval($seqid)]->disabled = $new_status;
|
||||
} elseif (isset($config->ipsec->phase2[intval($seqid)]->disabled)) {
|
||||
unset($config->ipsec->phase2[intval($seqid)]->disabled);
|
||||
}
|
||||
|
||||
Config::getInstance()->save();
|
||||
@touch("/tmp/ipsec.dirty");
|
||||
return ['status' => 'ok', 'disabled' => $new_status];
|
||||
}
|
||||
return ['status' => 'not_found'];
|
||||
}
|
||||
return ['status' => 'failed'];
|
||||
}
|
||||
|
||||
/**
|
||||
* toggle if IPsec is enabled
|
||||
*/
|
||||
public function toggleAction($enabled = null)
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$this->sessionClose();
|
||||
Config::getInstance()->lock();
|
||||
$config = Config::getInstance()->object();
|
||||
if ($enabled == "0" || $enabled == "1") {
|
||||
$new_status = $enabled == "1";
|
||||
} else {
|
||||
$new_status = !isset($config->ipsec->enable);
|
||||
}
|
||||
if ($new_status) {
|
||||
$config->ipsec->enable = true;
|
||||
} elseif (isset($config->ipsec->enable)) {
|
||||
unset($config->ipsec->enable);
|
||||
}
|
||||
Config::getInstance()->save();
|
||||
@touch("/tmp/ipsec.dirty");
|
||||
return ['status' => 'ok'];
|
||||
}
|
||||
return ['status' => 'failed'];
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,89 @@
|
||||
|
||||
<script>
|
||||
$(function () {
|
||||
function attach_legacy_actions() {
|
||||
$(".legacy_action").unbind('click').click(function(e){
|
||||
e.preventDefault();
|
||||
if ($(this).data('scope') === 'phase1') {
|
||||
if ($(this).hasClass('command-add')) {
|
||||
window.location = '/vpn_ipsec_phase1.php';
|
||||
} else if ($(this).hasClass('command-edit')) {
|
||||
window.location = '/vpn_ipsec_phase1.php?p1index=' + $(this).data('row-id');
|
||||
} else if ($(this).hasClass('command-copy')) {
|
||||
window.location = '/vpn_ipsec_phase1.php?dup=' + $(this).data('row-id');
|
||||
}
|
||||
} else {
|
||||
if ($(this).hasClass('command-add')) {
|
||||
window.location = '/vpn_ipsec_phase2.php?ikeid=' + $(this).data('row-ikeid');
|
||||
} else if ($(this).hasClass('command-edit')) {
|
||||
window.location = '/vpn_ipsec_phase2.php?p2index=' + $(this).data('row-uniqid');
|
||||
} else if ($(this).hasClass('command-copy')) {
|
||||
window.location = '/vpn_ipsec_phase2.php?dup=' + $(this).data('row-uniqid');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const $applyLegacyConfig = $('#applyLegacyConfig');
|
||||
const $applyLegacyConfigProgress = $('#applyLegacyConfigProgress');
|
||||
const $responseMsg = $('#responseMsg');
|
||||
const $dirtySubsystemMsg = $('#dirtySubsystemMsg');
|
||||
|
||||
// Helper method to fetch the current status of the legacy subsystem for viewing/hiding the "pending changes" alert
|
||||
function updateLegacyStatus() {
|
||||
ajaxCall('/api/ipsec/legacy-subsystem/status', {}, function (data, status) {
|
||||
$("#enable").prop('checked', data['enabled']);
|
||||
$("#enable").prop('disabled', false);
|
||||
$("#enable").removeClass("pending");
|
||||
if (data['isDirty']) {
|
||||
$responseMsg.addClass('hidden');
|
||||
$dirtySubsystemMsg.removeClass('hidden');
|
||||
} else {
|
||||
$dirtySubsystemMsg.addClass('hidden');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Apply config in legacy subsystem
|
||||
$applyLegacyConfig.on('click', function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
$applyLegacyConfig.prop('disabled', true);
|
||||
$applyLegacyConfigProgress.addClass('fa fa-spinner fa-pulse');
|
||||
|
||||
ajaxCall('/api/ipsec/legacy-subsystem/applyConfig', {}, function (data, status) {
|
||||
// Preliminarily hide the "pending changes" alert and display the response message if available
|
||||
if (data['message']) {
|
||||
$dirtySubsystemMsg.addClass('hidden');
|
||||
$responseMsg.removeClass('hidden').text(data['message']);
|
||||
}
|
||||
|
||||
// Reset the state of the "apply changes" button
|
||||
$applyLegacyConfig.prop('disabled', false);
|
||||
$applyLegacyConfigProgress.removeClass('fa fa-spinner fa-pulse');
|
||||
|
||||
// Fetch the current legacy subsystem status to ensure changes have been processed
|
||||
updateLegacyStatus();
|
||||
updateServiceControlUI('ipsec');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
const formatters = {
|
||||
"commands": function (column, row) {
|
||||
return '<button type="button" class="btn btn-xs btn-default command-edit bootgrid-tooltip" data-row-id="' + row.id + '"><span class="fa fa-fw fa-pencil"></span></button> ' +
|
||||
'<button type="button" class="btn btn-xs btn-default command-copy bootgrid-tooltip" data-row-id="' + row.id + '"><span class="fa fa-fw fa-clone"></span></button>' +
|
||||
'<button type="button" class="btn btn-xs btn-default command-delete bootgrid-tooltip" data-row-id="' + row.id + '"><span class="fa fa-fw fa-trash-o"></span></button>';
|
||||
let btns = '';
|
||||
let data_tags = "";
|
||||
if (row.remote_gateway !== undefined) {
|
||||
// phase 1
|
||||
data_tags = 'data-row-id="' + row.seqid + '" data-scope="phase1" data-row-ikeid="'+row.id+'" ';
|
||||
btns = '<button type="button" data-scope="phase2" class="btn btn-xs btn-primary legacy_action command-add bootgrid-tooltip" title="{{ lang._('add phase 2 entry') }}" ' + data_tags + '><span class="fa fa-fw fa-plus"></span></button> '
|
||||
} else {
|
||||
data_tags = 'data-row-id="' + row.id + '" data-scope="phase2" data-row-uniqid="' + row.uniqid + '"';
|
||||
}
|
||||
btns = btns + '<button type="button" class="btn btn-xs legacy_action btn-default command-edit bootgrid-tooltip" ' + data_tags + '><span class="fa fa-fw fa-pencil"></span></button> ' +
|
||||
'<button type="button" class="btn btn-xs btn-default legacy_action command-copy bootgrid-tooltip" ' + data_tags + '><span class="fa fa-fw fa-clone"></span></button>' +
|
||||
'<button type="button" class="btn btn-xs btn-default command-delete bootgrid-tooltip" ' + data_tags + '><span class="fa fa-fw fa-trash-o"></span></button>';
|
||||
return btns;
|
||||
},
|
||||
"gateway": function (column, row) {
|
||||
if (row.mobile) {
|
||||
@ -19,15 +97,16 @@
|
||||
},
|
||||
"rowtoggle": function (column, row) {
|
||||
if (parseInt(row[column.id], 2) === 1) {
|
||||
return '<span style="cursor: pointer;" class="fa fa-fw fa-check-square-o command-toggle bootgrid-tooltip" data-value="1" data-row-id="' + row.uuid + '"></span>';
|
||||
return '<span style="cursor: pointer;" class="fa fa-fw fa-check-square-o command-toggle bootgrid-tooltip" data-value="1" data-row-id="' + row.id + '"></span>';
|
||||
} else {
|
||||
return '<span style="cursor: pointer;" class="fa fa-fw fa-square-o command-toggle bootgrid-tooltip" data-value="0" data-row-id="' + row.uuid + '"></span>';
|
||||
return '<span style="cursor: pointer;" class="fa fa-fw fa-square-o command-toggle bootgrid-tooltip" data-value="0" data-row-id="' + row.id + '"></span>';
|
||||
}
|
||||
}
|
||||
};
|
||||
const $grid_phase1 = $('#grid-phase1').UIBootgrid({
|
||||
search: '/api/ipsec/tunnel/search_phase1',
|
||||
del: '/api/ipsec/tunnel/del_phase1/',
|
||||
toggle: '/api/ipsec/tunnel/toggle_phase1/',
|
||||
options: {
|
||||
formatters: formatters,
|
||||
multiSelect: false,
|
||||
@ -43,48 +122,65 @@
|
||||
if (ids.length > 0) {
|
||||
$("#grid-phase1").bootgrid('select', [ids[0].id]);
|
||||
}
|
||||
attach_legacy_actions();
|
||||
updateLegacyStatus();
|
||||
});
|
||||
const $grid_phase2 = $('#grid-phase2').UIBootgrid({
|
||||
search: '/api/ipsec/tunnel/search_phase2',
|
||||
del: '/api/ipsec/tunnel/del_phase2/',
|
||||
toggle: '/api/ipsec/tunnel/toggle_phase2/',
|
||||
options: {
|
||||
formatters: formatters,
|
||||
useRequestHandlerOnGet: true,
|
||||
requestHandler: function(request) {
|
||||
let ids = [];
|
||||
let rows = $("#grid-phase1").bootgrid("getSelectedRows");
|
||||
let current_rows = $("#grid-phase1").bootgrid("getCurrentRows");
|
||||
$.each(rows, function(key, seq){
|
||||
if (current_rows[seq] !== undefined) {
|
||||
ids.push(current_rows[seq].id);
|
||||
}
|
||||
});
|
||||
if (ids.length > 0) {
|
||||
request['ikeid'] = ids[0];
|
||||
} else {
|
||||
request['ikeid'] = "__not_found__";
|
||||
}
|
||||
let ids = $("#grid-phase1").bootgrid("getSelectedRows");
|
||||
request['ikeid'] = ids.length > 0 ? ids[0] : "__not_found__";
|
||||
return request;
|
||||
}
|
||||
}
|
||||
}).on("loaded.rs.jquery.bootgrid", function (e) {
|
||||
attach_legacy_actions();
|
||||
updateLegacyStatus();
|
||||
});
|
||||
$("#enable").click(function(){
|
||||
if (!$(this).hasClass("pending")) {
|
||||
$(this).addClass("pending");
|
||||
$(this).prop('disabled', true);
|
||||
ajaxCall('/api/ipsec/tunnel/toggle', {}, function (data, status) {
|
||||
updateLegacyStatus();
|
||||
});
|
||||
}
|
||||
});
|
||||
updateServiceControlUI('ipsec');
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
<div class="alert alert-info alert-dismissible hidden" role="alert" id="responseMsg"></div>
|
||||
<div class="alert alert-info hidden" role="alert" id="dirtySubsystemMsg">
|
||||
<button class="btn btn-primary pull-right" type="button" id="applyLegacyConfig">
|
||||
<i id="applyLegacyConfigProgress" class=""></i>
|
||||
{{ lang._('Apply changes') }}
|
||||
</button>
|
||||
<div>
|
||||
{{ lang._('The IPsec tunnel configuration has been changed.') }}<br/>
|
||||
{{ lang._('You must apply the changes in order for them to take effect.') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-content content-box col-xs-12 __mb">
|
||||
<table id="grid-phase1" class="table table-condensed table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-column-id="enabled" data-width="6em" data-type="string" data-formatter="rowtoggle">{{ lang._('Enabled') }}</th>
|
||||
<th data-column-id="id" data-type="numeric" data-identifier="true" data-visible="false">{{ lang._('ikeid') }}</th>
|
||||
<th data-column-id="seqid" data-type="numeric" data-visible="false">{{ lang._('seqid') }}</th>
|
||||
<th data-column-id="type" data-type="string" data-width="7em">{{ lang._('Type') }}</th>
|
||||
<th data-column-id="remote_gateway" data-formatter="gateway" data-width="20em" data-type="string">{{ lang._('Remote Gateway') }}</th>
|
||||
<th data-column-id="mode" data-width="10em" data-type="string">{{ lang._('Mode') }}</th>
|
||||
<th data-column-id="proposal" data-width="20em" data-type="string">{{ lang._('Phase 1 Proposal') }}</th>
|
||||
<th data-column-id="authentication" data-type="string">{{ lang._('Authentication') }}</th>
|
||||
<th data-column-id="description" data-type="string">{{ lang._('Description') }}</th>
|
||||
<th data-column-id="commands" data-width="7em" data-formatter="commands" data-sortable="false">{{ lang._('Commands') }}</th>
|
||||
<th data-column-id="commands" data-width="12em" data-formatter="commands" data-sortable="false">{{ lang._('Commands') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -93,7 +189,7 @@
|
||||
<tr>
|
||||
<td colspan=7></td>
|
||||
<td>
|
||||
<button data-action="add" type="button" class="btn btn-xs btn-primary">
|
||||
<button data-action="add" type="button" title="{{ lang._('add phase 1 entry') }}" data-scope="phase1" class="btn btn-xs btn-primary legacy_action command-add">
|
||||
<span class="fa fa-fw fa-plus"></span>
|
||||
</button>
|
||||
<button data-action="deleteSelected" type="button" class="btn btn-xs btn-default">
|
||||
@ -109,6 +205,7 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-column-id="id" data-type="numeric" data-identifier="true" data-visible="false">ID</th>
|
||||
<th data-column-id="uniqid" data-type="string" data-visible="false">{{ lang._('uniqid') }}</th>
|
||||
<th data-column-id="enabled" data-width="6em" data-type="string" data-formatter="rowtoggle">{{ lang._('Enabled') }}</th>
|
||||
<th data-column-id="type" data-width="8em" data-type="string" data-formatter="mode_type">{{ lang._('Type') }}</th>
|
||||
<th data-column-id="local_subnet" data-width="18em" data-type="string">{{ lang._('Local Subnet') }}</th>
|
||||
@ -120,19 +217,6 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan=6></td>
|
||||
<td>
|
||||
<button data-action="add" type="button" class="btn btn-xs btn-primary">
|
||||
<span class="fa fa-fw fa-plus"></span>
|
||||
</button>
|
||||
<button data-action="deleteSelected" type="button" class="btn btn-xs btn-default">
|
||||
<span class="fa fa-fw fa-trash-o"></span>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
<div class="tab-content content-box col-xs-12 __mb">
|
||||
@ -140,16 +224,10 @@
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<input name="enable" type="checkbox" id="enable" value="yes" checked="checked"/>
|
||||
<input name="enable" class="pending" type="checkbox" id="enable"/>
|
||||
<strong>{{ lang._('Enable IPsec') }}</strong>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<input type="submit" name="save" class="btn btn-primary" value="{{ lang._('Save') }}" />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user