Commit 869f61c2 authored by Jos Schellevis's avatar Jos Schellevis

Proxy server WIP

parent dbbceca0
...@@ -30,7 +30,7 @@ namespace OPNsense\Proxy\Api; ...@@ -30,7 +30,7 @@ namespace OPNsense\Proxy\Api;
use \OPNsense\Base\ApiControllerBase; use \OPNsense\Base\ApiControllerBase;
use \OPNsense\Core\Backend; use \OPNsense\Core\Backend;
use \OPNsense\Proxy\General; use \OPNsense\Proxy\Proxy;
/** /**
* Class ServiceController * Class ServiceController
...@@ -101,13 +101,13 @@ class ServiceController extends ApiControllerBase ...@@ -101,13 +101,13 @@ class ServiceController extends ApiControllerBase
// close session for long running action // close session for long running action
session_write_close(); session_write_close();
$mdlGeneral = new General(); $mdlProxy = new Proxy();
$backend = new Backend(); $backend = new Backend();
$runStatus = $this->statusAction(); $runStatus = $this->statusAction();
// stop squid when disabled // stop squid when disabled
if ($runStatus['status'] == "running" && $mdlGeneral->enabled->__toString() == 0) { if ($runStatus['status'] == "running" && $mdlProxy->general->enabled->__toString() == 0) {
$this->stopAction(); $this->stopAction();
} }
...@@ -115,7 +115,7 @@ class ServiceController extends ApiControllerBase ...@@ -115,7 +115,7 @@ class ServiceController extends ApiControllerBase
$backend->sendEvent("template reload OPNsense.Proxy"); $backend->sendEvent("template reload OPNsense.Proxy");
// (res)start daemon // (res)start daemon
if ($mdlGeneral->enabled->__toString() == 1) { if ($mdlProxy->general->enabled->__toString() == 1) {
if ($runStatus['status'] == "running") { if ($runStatus['status'] == "running") {
$backend->sendEvent("service reconfigure proxy"); $backend->sendEvent("service reconfigure proxy");
} else { } else {
......
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
namespace OPNsense\Proxy\Api; namespace OPNsense\Proxy\Api;
use \OPNsense\Base\ApiControllerBase; use \OPNsense\Base\ApiControllerBase;
use \OPNsense\Proxy\General; use \OPNsense\Proxy\Proxy;
use \OPNsense\Core\Config; use \OPNsense\Core\Config;
/** /**
...@@ -39,19 +39,40 @@ use \OPNsense\Core\Config; ...@@ -39,19 +39,40 @@ use \OPNsense\Core\Config;
class SettingsController extends ApiControllerBase class SettingsController extends ApiControllerBase
{ {
/** /**
* retrieve general settings * retrieve proxy settings
* @return array * @return array
*/ */
public function getAction() public function getAction()
{ {
$result = array(); $result = array();
if ($this->request->isGet()) { if ($this->request->isGet()) {
$mdlGeneral = new General(); $mdlProxy = new Proxy();
$selopt=array("lan"=>"LAN","wan"=>"WAN"); // Define array for selected interfaces
$mdlGeneral->interfaces->setSelectOptions($selopt); $selopt=Array();
// Get ConfigObject
$configObj = Config::getInstance()->object();
// Iterate over all interfaces configuration
// TODO: replace for <interfaces> helper
foreach ( $configObj->interfaces->children() as $key => $value ) {
// Check if interface is enabled, if tag is <enable/> treat as enabled.
if ( isset($value->enable) && ( $value->enable != '0' ) ) {
// Check if interface has static ip
if ($value->ipaddr != 'dhcp') {
if ($value->descr == '') {
$description = strtoupper($key); // Use interface name as description if none is given
} else {
$description = $value->descr;
}
$selopt[$key] = (string)$description; // Add Interface to selectable options.
}
}
}
$result['general'] = $mdlGeneral->getNodes(); $mdlProxy->forward->interfaces->setSelectOptions($selopt);
$result['proxy'] = $mdlProxy->getNodes();
} }
return $result; return $result;
...@@ -66,23 +87,23 @@ class SettingsController extends ApiControllerBase ...@@ -66,23 +87,23 @@ class SettingsController extends ApiControllerBase
public function setAction() public function setAction()
{ {
$result = array("result"=>"failed"); $result = array("result"=>"failed");
if ($this->request->hasPost("general")) { if ($this->request->hasPost("proxy")) {
// load model and update with provided data // load model and update with provided data
$mdlGeneral = new General(); $mdlProxy = new Proxy();
$mdlGeneral->setNodes($this->request->getPost("general")); $mdlProxy->setNodes($this->request->getPost("proxy"));
// perform validation // perform validation
$valMsgs = $mdlGeneral->performValidation(); $valMsgs = $mdlProxy->performValidation();
foreach ($valMsgs as $field => $msg) { foreach ($valMsgs as $field => $msg) {
if (!array_key_exists("validations", $result)) { if (!array_key_exists("validations", $result)) {
$result["validations"] = array(); $result["validations"] = array();
} }
$result["validations"]["general.".$msg->getField()] = $msg->getMessage(); $result["validations"]["proxy.".$msg->getField()] = $msg->getMessage();
} }
// serialize model to config // serialize model to config
if ($valMsgs->count() == 0) { if ($valMsgs->count() == 0) {
$mdlGeneral->serializeToConfig(); $mdlProxy->serializeToConfig();
} }
} }
......
...@@ -36,7 +36,7 @@ class IndexController extends \OPNsense\Base\IndexController ...@@ -36,7 +36,7 @@ class IndexController extends \OPNsense\Base\IndexController
{ {
public function indexAction() public function indexAction()
{ {
$this->view->title = "Proxy"; $this->view->title = "Proxy Server";
$this->view->pick('OPNsense/Proxy/index'); $this->view->pick('OPNsense/Proxy/index');
} }
} }
<model>
<mount>//OPNsense/proxy/general</mount>
<description>
(squid) proxy general settings
</description>
<items>
<enabled type="BooleanField">
<default>0</default>
<Required>Y</Required>
</enabled>
<interfaces type="CSVListField">
<Required>N</Required>
</interfaces>
<port type="IntegerField">
<default>3128</default>
<MinimumValue>1</MinimumValue>
<MaximumValue>65535</MaximumValue>
<Required>Y</Required>
</port>
</items>
</model>
...@@ -30,6 +30,6 @@ namespace OPNsense\Proxy; ...@@ -30,6 +30,6 @@ namespace OPNsense\Proxy;
use OPNsense\Base\BaseModel; use OPNsense\Base\BaseModel;
class General extends BaseModel class Proxy extends BaseModel
{ {
} }
<model>
<mount>//OPNsense/proxy</mount>
<description>
(squid) proxy settings
</description>
<items>
<general>
<enabled type="BooleanField">
<default>0</default>
<Required>Y</Required>
</enabled>
</general>
<forward>
<interfaces type="CSVListField">
<Required>N</Required>
</interfaces>
<port type="IntegerField">
<default>3128</default>
<MinimumValue>1</MinimumValue>
<MaximumValue>65535</MaximumValue>
<ValidationMessage>"Proxy port needs to be an integer value between 1 and 65535"</ValidationMessage>
<Required>Y</Required>
</port>
<addACLforInterfaceSubnets type="BooleanField">
<default>1</default>
<Required>Y</Required>
</addACLforInterfaceSubnets>
<transparentProxyMode type="BooleanField">
<default>0</default>
<Required>Y</Required>
</transparentProxyMode>
<alternateDNSservers type="CSVListField">
<Required>N</Required>
</alternateDNSservers>
</forward>
</items>
</model>
<script type="text/javascript"> <script type="text/javascript">
$( document ).ready(function() { $( document ).ready(function() {
data_get_map = {'frm_proxy':"/api/proxy/settings/get"};
// load initial data // load initial data
ajaxGet(url="/api/proxy/settings/get",sendData={},callback=function(data,status) { $.each(data_get_map, function(data_index, data_url) {
if (status == "success") { ajaxGet(url=data_url,sendData={},callback=function(data,status) {
setFormData('frm_general',data); if (status == "success") {
} $("form").each(function( index ) {
if ( $(this).attr('id').split('-')[0] == data_index) {
// related form found, load data
setFormData($(this).attr('id'),data);
}
});
}
});
}); });
// form event handlers // form event handlers
$("#save").click(function(){ $("#save_proxy-general").click(function(){
saveFormToEndpoint(url="/api/proxy/settings/set",formid="frm_general",callback_ok=function(){
// save data for General TAB
saveFormToEndpoint(url="/api/proxy/settings/set",formid="frm_proxy-general",callback_ok=function(){
// on correct save, perform reconfigure. set progress animation when reloading // on correct save, perform reconfigure. set progress animation when reloading
$("#frm_general_progress").addClass("fa fa-spinner fa-pulse"); $("#frm_proxy-general_progress").addClass("fa fa-spinner fa-pulse");
// //
ajaxCall(url="/api/proxy/service/reconfigure", sendData={}, callback=function(data,status){ ajaxCall(url="/api/proxy/service/reconfigure", sendData={}, callback=function(data,status){
// when done, disable progress animation. // when done, disable progress animation.
$("#frm_general_progress").removeClass("fa fa-spinner fa-pulse"); $("#frm_proxy-general_progress").removeClass("fa fa-spinner fa-pulse");
if (status != "success" || data['status'] != 'ok' ) { if (status != "success" || data['status'] != 'ok' ) {
// fix error handling // fix error handling
BootstrapDialog.show({ BootstrapDialog.show({
type:BootstrapDialog.TYPE_WARNING, type:BootstrapDialog.TYPE_WARNING,
title: 'Proxy', title: 'Proxy General TAB',
message: JSON.stringify(data) message: JSON.stringify(data)
}); });
} }
...@@ -31,54 +43,133 @@ ...@@ -31,54 +43,133 @@
}); });
}); });
$("#save_proxy-forward").click(function(){
// save data for Proxy TAB
saveFormToEndpoint(url="/api/proxy/settings/set",formid="frm_proxy-forward",callback_ok=function(){
// on correct save, perform reconfigure. set progress animation when reloading
$("#frm_proxy-forward_progress").addClass("fa fa-spinner fa-pulse");
//
ajaxCall(url="/api/proxy/service/reconfigure", sendData={}, callback=function(data,status){
// when done, disable progress animation.
$("#frm_proxy-forward_progress").removeClass("fa fa-spinner fa-pulse");
if (status != "success" || data['status'] != 'ok' ) {
// fix error handling
BootstrapDialog.show({
type:BootstrapDialog.TYPE_WARNING,
title: 'Proxy Server TAB',
message: JSON.stringify(data)
});
}
});
});
});
// handle help messages show/hide
$('[id*="show_all_help"]').click(function() {
$('[id*="show_all_help"]').toggleClass("fa-toggle-on fa-toggle-off");
$('[id*="show_all_help"]').toggleClass("text-success text-danger");
if ($('[id*="show_all_help"]').hasClass("fa-toggle-on")) {
$('[for*="help_for"]').addClass("show");
$('[for*="help_for"]').removeClass("hidden");
} else {
$('[for*="help_for"]').addClass("hidden");
$('[for*="help_for"]').removeClass("show");
}
});
setTimeout(function(){
$('select[class="tokenize"]').each(function(){
if ($(this).prop("size")==0) {
//number_of_items = $(this).children('option').length;
maxDropdownHeight=String(36*5)+"px"; // default number of items
} else {
number_of_items = $(this).prop("size");
maxDropdownHeight=String(36*number_of_items)+"px";
}
hint=$(this).data("hint");
width=$(this).data("width");
allownew=$(this).data("allownew");
maxTokenContainerHeight=$(this).data("maxheight");
$(this).tokenize({
displayDropdownOnFocus: true,
newElements: allownew,
placeholder:hint
});
$(this).parent().find('ul[class="TokensContainer"]').parent().css("width",width);
$(this).parent().find('ul[class="Dropdown"]').css("max-height", maxDropdownHeight);
if ( maxDropdownHeight != undefined ) {
$(this).parent().find('ul[class="TokensContainer"]').css("max-height", maxTokenContainerHeight);
}
})
},500);
}); });
</script> </script>
<ul class="nav nav-tabs nav-justified" role="tablist" id="maintabs"> <!-- TODO: explain TABS
<li class="active"><a data-toggle="tab" href="#tabGeneral">General</a></li> content_location,tab_name,
<li><a data-toggle="tab" href="#sectionB">Section B</a></li> field_array
</ul> activetab: content_location
<div class="content-box tab-content"> -->
<div id="tabGeneral" class="tab-pane fade in active"> <!-- TODO: explain usage of select_multiple
<form id="frm_general" class="form-inline"> special options:
<table class="table table-striped table-condensed table-responsive"> style: used as class, defined classes are: tokenize
<colgroup> hint: show default text used for tokenize select
<col class="col-md-3"/> allownew: set to "true" if new items can be added to the list, default is "false"
<col class="col-md-4"/> size: for tokenize this defines the max shown items (default = 5) of the dropdown list, if it does not fit a scrollbar is shown
<col class="col-md-5"/> maxheight: define max height of select box, default=170px to hold 5 items
</colgroup> -->
<tbody>
{{ partial("layout_partials/form_input_tr", {{ partial("layout_partials/base_tabs",
['id': 'general.enabled', ['tabs': {
'label':'enabled', ['proxy-general','General Proxy Settings',
'type':'checkbox', {['id': 'proxy.general.enabled',
'help':'test' 'label':'Enable proxy',
]) 'type':'checkbox',
}} 'help':'Enable or disable the proxy service.'
{{ partial("layout_partials/form_input_tr", ]}
['id': 'general.interfaces', ],
'label':'interfaces', ['proxy-forward','Forward Proxy',
'type':'select_multiple' {['id': 'proxy.forward.interfaces',
]) 'label':'Proxy interfaces',
}} 'type':'select_multiple',
{{ partial("layout_partials/form_input_tr", 'style':'tokenize',
['id': 'general.port', 'help':'Select interface(s) the proxy will bind to.',
'label':'port', 'hint':'Type or select interface'
'type':'text' ],
]) ['id': 'proxy.forward.port',
}} 'label':'Proxy port',
'type':'text',
<tr> 'help':'The port the proxy service will listen to.'
<td colspan="3"><button class="btn btn-primary" id="save" type="button">Apply <i id="frm_general_progress" class=""></i></button></td> ],
</tr> ['id': 'proxy.forward.addACLforInterfaceSubnets',
</tbody> 'label':'Allow interface subnets',
</table> 'type':'checkbox',
</form> 'help':'When enabled the subnets of the selected interfaces will be added to the allow access list.'
</div> ],
<div id="sectionB" class="tab-pane fade"> ['id': 'proxy.forward.transparentProxyMode',
'label':'Enable Transparent HTTP proxy',
</div> 'type':'checkbox',
</div> 'help':'Enable transparent proxe mode to forward all requests for destination port 80 to the proxy server without any additional configuration.'
],
['id': 'proxy.forward.alternateDNSservers',
'label':'Use alternate DNS-servers',
'type':'select_multiple',
'style':'tokenize',
'help':'Type IPs of alternative DNS servers you like to use.',
'hint':'Type or select interface',
'allownew':'true'
]}
]
},
'activetab':'proxy-general'
])
}}
<ul class="nav nav-tabs " role="tablist" id="maintabs">
{% for tab in tabs|default([]) %}
<li {% if activetab|default("") == tab[0] %} class="active" {% endif %}><a data-toggle="tab" href="#tab_{{tab[0]}}"><b>{{tab[1]}}</b></a></li>
{% endfor %}
</ul>
<div class="content-box tab-content">
{% for tab in tabs|default([]) %}
<div id="tab_{{tab[0]}}" class="tab-pane fade{% if activetab|default("") == tab[0] %} in active {% endif %}">
<form id="frm_{{tab[0]}}" class="form-inline">
<table class="table table-striped table-condensed table-responsive">
<colgroup>
<col class="col-md-3"/>
<col class="col-md-4"/>
<col class="col-md-5"/>
</colgroup>
<tbody>
<tr>
<td colspan="3" align="right">
<small>{{ lang._('toggle full help on/off') }} </small><a href="#"><i class="fa fa-toggle-off text-danger" id="show_all_help_{{tab[0]}}" type="button"></i></a>
</td>
</tr>
{% for field in tab[2]|default({})%}
{{ partial("layout_partials/form_input_tr",field)}}
{% endfor %}
<tr>
<td colspan="3"><button class="btn btn-primary" id="save_{{tab[0]}}" type="button">Apply <i id="frm_{{tab[0]}}_progress" class=""></i></button></td>
</tr>
</tbody>
</table>
</form>
</div>
{% endfor %}
</div>
<tr for="{{ id }}"> <tr for="{{ id }}">
<td > <td >
<div class="control-label" for="{{ id }}"> <div class="control-label" for="{{ id }}">
{{label}} {% if help|default(false) %}
{% if help|default(false) %} <a id="help_for_{{ id }}" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> {% endif %} <a id="help_for_{{ id }}" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a>
{% elseif help|default(false) == false %}
<i class="fa fa-info-circle text-muted"></i>
{% endif %}
<b>{{label}}</b>
</div> </div>
</td> </td>
<td > <td >
...@@ -11,11 +15,10 @@ ...@@ -11,11 +15,10 @@
{% elseif type == "checkbox" %} {% elseif type == "checkbox" %}
<input type="checkbox" id="{{ id }}" > <input type="checkbox" id="{{ id }}" >
{% elseif type == "select_multiple" %} {% elseif type == "select_multiple" %}
<select multiple="multiple" size="{{size|default(2)}}" id="{{ id }}"></select> <select multiple="multiple" {% if size|default(false) %}size="{{size}}"{% endif %} id="{{ id }}" {% if style|default(false) %}class="{{style}}" {% endif %} {% if hint|default(false) %}data-hint="{{hint}}"{% endif %} {% if maxheight|default(false) %}data-maxheight="{{maxheight}}"{% endif %} data-width="{{width|default("348px")}}" data-allownew="{{allownew|default("false")}}"></select>
{% endif %} {% endif %}
{% if help|default(false) %} {% if help|default(false) %}
<br/>
<small class="hidden" for="help_for_{{ id }}" >{{help}}</small> <small class="hidden" for="help_for_{{ id }}" >{{help}}</small>
{% endif %} {% endif %}
</td> </td>
......
<input type="{{field_type}}" value="{{field_content}}" name="{{field_name_prefix}}{{field_name}}" > <input type="{{field_type}}" value="{{field_content}}" name="{{field_name_prefix}}{{field_name}}" class="text-info">
...@@ -4,19 +4,21 @@ ...@@ -4,19 +4,21 @@
# setup listen configuration # setup listen configuration
{% if helpers.exists('OPNsense.proxy.general.port') %} {% if helpers.exists('OPNsense.proxy.general.port') %}
{% for interface in OPNsense.proxy.general.interfaces.split(",") %} {% for interface in OPNsense.proxy.general.interfaces.split(",") %}
{% for intf_key,intf_item in interfaces.iteritems() %} {% for intf_key,intf_item in interfaces.iteritems() %}
{% if intf_key == interface and intf_item.ipaddr != 'dhcp' %} {% if intf_key == interface and intf_item.ipaddr != 'dhcp' %}
http_port {{intf_item.ipaddr}}:{{ OPNsense.proxy.general.port }} http_port {{intf_item.ipaddr}}:{{ OPNsense.proxy.general.port }}
{% endif %} {% endif %}
{% endfor %} {% endfor %}
{# virtual ip's #} {# virtual ip's #}
{% for intf_key,intf_item in virtualip.iteritems() %} {% if helpers.exists('virtualip') %}
{% if intf_item.interface == interface and intf_item.mode == 'ipalias' %} {% for intf_key,intf_item in virtualip.iteritems() %}
{% if intf_item.interface == interface and intf_item.mode == 'ipalias' %}
http_port {{intf_item.subnet}}:{{ OPNsense.proxy.general.port }} http_port {{intf_item.subnet}}:{{ OPNsense.proxy.general.port }}
{% endif %} {% endif %}
{% endfor %} {% endfor %}
{% endfor %} {% endif %}
{% endfor %}
{% endif %} {% endif %}
......
...@@ -29,7 +29,9 @@ div.Tokenize ul.TokensContainer ...@@ -29,7 +29,9 @@ div.Tokenize ul.TokensContainer
{ {
cursor: text; cursor: text;
padding: 0 5px 0 0; padding: 0 5px 0 0;
height: 100px; height:auto;
min-height:34px;
max-height: 170px;
overflow-y: auto; overflow-y: auto;
background-color: white; background-color: white;
} }
...@@ -138,6 +140,9 @@ div.Tokenize ul.Dropdown ...@@ -138,6 +140,9 @@ div.Tokenize ul.Dropdown
border-radius: 0 0 6px 6px; border-radius: 0 0 6px 6px;
z-index: 20; z-index: 20;
height: auto;
overflow-x: hidden;
} }
div.Tokenize ul.Dropdown li div.Tokenize ul.Dropdown li
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment