Firewall/Aliases: add network group type to combine aliases (and offer the user a fixed selector).

While here, also add a type filter to ease searching through larger lists.

for https://github.com/opnsense/core/issues/4006
This commit is contained in:
Ad Schellevis 2020-03-30 14:50:01 +02:00
parent a4c6003bc2
commit 7ee060456b
5 changed files with 142 additions and 9 deletions

View File

@ -51,10 +51,18 @@ class AliasController extends ApiMutableModelControllerBase
*/
public function searchItemAction()
{
$type = $this->request->get('type');
$filter_funct = null;
if (!empty($type)) {
$filter_funct = function($record) use ($type) {
return in_array($record->type, $type);
};
}
return $this->searchBase(
"aliases.alias",
array('enabled', 'name', 'description', 'type', 'content'),
"name"
"name",
$filter_funct
);
}
@ -202,6 +210,22 @@ class AliasController extends ApiMutableModelControllerBase
return $result;
}
/**
* list network alias types
* @return array indexed by country alias name
*/
public function listNetworkAliasesAction()
{
$result = array();
foreach ($this->getModel()->aliases->alias->iterateItems() as $alias) {
if (!in_array((string)$alias->type, ['external', 'port'])) {
$result[(string)$alias->name] = (string)$alias->name;
}
}
ksort($result);
return $result;
}
/**
* reconfigure aliases
*/

View File

@ -34,6 +34,7 @@
<url>URL (IPs)</url>
<urltable>URL Table (IPs)</urltable>
<geoip>GeoIP</geoip>
<networkgroup>Network group</networkgroup>
<external>External (advanced)</external>
</OptionValues>
</type>

View File

@ -157,6 +157,26 @@ class AliasContentField extends BaseField
return $messages;
}
/**
* Validate host options
* @param array $data to validate
* @return bool|Callback
* @throws \OPNsense\Base\ModelException
*/
private function validateNestedAlias($data)
{
$messages = array();
foreach ($this->getItems($data) as $host) {
if (!Util::isAlias($host)) {
$messages[] = sprintf(
gettext('Entry "%s" is not a valid alias.'),
$host
);
}
}
return $messages;
}
/**
* Validate network options
* @param array $data to validate
@ -239,6 +259,12 @@ class AliasContentField extends BaseField
}
]);
break;
case "networkgroup":
$validators[] = new CallbackValidator(["callback" => function ($data) {
return $this->validateNestedAlias($data);
}
]);
break;
default:
break;
}

View File

@ -32,14 +32,41 @@
<script>
$( document ).ready(function() {
$("#grid-aliases").UIBootgrid({
search:'/api/firewall/alias/searchItem',
get:'/api/firewall/alias/getItem/',
set:'/api/firewall/alias/setItem/',
add:'/api/firewall/alias/addItem/',
del:'/api/firewall/alias/delItem/',
toggle:'/api/firewall/alias/toggleItem/'
search:'/api/firewall/alias/searchItem',
get:'/api/firewall/alias/getItem/',
set:'/api/firewall/alias/setItem/',
add:'/api/firewall/alias/addItem/',
del:'/api/firewall/alias/delItem/',
toggle:'/api/firewall/alias/toggleItem/',
options:{
requestHandler: function(request){
let selected = $('#type_filter').find("option:selected").val();
if ( $('#type_filter').val().length > 0) {
request['type'] = $('#type_filter').val();
}
return request;
}
}
});
$("#type_filter").change(function(){
$('#grid-aliases').bootgrid('reload');
});
$("#grid-aliases").bootgrid().on("loaded.rs.jquery.bootgrid", function (e){
// network content field should only contain valid aliases, we need to fetch them separately
// since the form field misses context
ajaxGet("/api/firewall/alias/listNetworkAliases", {}, function(data){
$("#network_content").empty();
$.each(data, function(alias, value) {
$("#network_content").append($("<option/>").val(alias).text(value));
});
$("#network_content").selectpicker('refresh');
});
});
/**
* Open form with alias selected
*/
@ -141,6 +168,27 @@
});
});
/**
* hook network group type changes, replicate content
*/
$("#network_content").change(function(){
let $content = $("#alias\\.content");
$content.unbind('tokenize:tokens:change');
$content.tokenize2().trigger('tokenize:clear');
$("#network_content").each(function () {
$.each($(this).val(), function(key, item){
$content.tokenize2().trigger('tokenize:tokens:add', item);
});
});
$content.tokenize2().trigger('tokenize:select');
$content.tokenize2().trigger('tokenize:dropdown:hide');
// link on change event back
$content.on('tokenize:tokens:change', function(e, value){
$content.change();
});
});
/**
* Type selector, show correct type input.
*/
@ -154,6 +202,10 @@
break;
case 'external':
break;
case 'networkgroup':
$("#alias_type_networkgroup").show();
$("#alias\\.proto").selectpicker('hide');
break;
case 'urltable':
$("#row_alias\\.updatefreq").show();
default:
@ -169,7 +221,7 @@
});
/**
* push content changes to GeopIP selectors
* push content changes to GeopIP selectors and network groups
*/
$("#alias\\.content").change(function(){
var items = $(this).val();
@ -183,6 +235,14 @@
});
$(".geoip_select").selectpicker('refresh');
geoip_update_labels();
$("#network_content").each(function(){
var network_item = $(this);
network_item.val([]);
for (var i=0; i < items.length; ++i) {
network_item.find('option[value="' + $.escapeSelector(items[i]) + '"]').prop("selected", true);
}
});
$("#network_content").selectpicker('refresh');
});
/**
@ -382,6 +442,9 @@
history.pushState(null, null, e.target.hash);
});
// move filter into action header
$("#type_filter_container").detach().prependTo('#grid-aliases-header > .row > .actionBar > .actions');
});
</script>
@ -395,6 +458,21 @@
<div class="row">
<section class="col-xs-12">
<div class="content-box">
<div class="hidden">
<!-- filter per type container -->
<div id="type_filter_container" class="btn-group">
<select id="type_filter" data-title="{{ lang._('Filter type') }}" class="selectpicker" multiple="multiple" data-width="200px">
<option value="host">{{ lang._('Host(s)') }}</option>
<option value="network">{{ lang._('Network(s)') }}</option>
<option value="port">{{ lang._('Port(s)') }}</option>
<option value="url">{{ lang._('URL (IPs)') }}</option>
<option value="urltable">{{ lang._('URL Table (IPs)') }}</option>
<option value="geoip">{{ lang._('GeoIP') }}</option>
<option value="networkgroup">{{ lang._('Network group') }}</option>
<option value="external">{{ lang._('External (advanced)') }}</option>
</select>
</div>
</div>
<table id="grid-aliases" class="table table-condensed table-hover table-striped table-responsive" data-editDialog="DialogAlias" data-editAlert="aliasChangeMessage" data-store-selection="true">
<thead>
<tr>
@ -579,6 +657,10 @@
data-separator="#10">
</select>
</div>
<div class="alias_type" id="alias_type_networkgroup">
<select multiple="multiple" class="selectpicker" id="network_content" data-live-search="true">
</select>
</div>
<table class="table table-condensed alias_table alias_type" id="alias_type_geoip" style="display: none;">
<thead>
<tr>

View File

@ -263,7 +263,7 @@ class Alias(object):
""" fetch address parser to use, None if alias type is not handled here
:return: function or None
"""
if self._type in ['host', 'network']:
if self._type in ['host', 'network', 'networkgroup']:
return self._parse_address
elif self._type in ['url', 'urltable']:
return self._fetch_url