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

Proxy server WIP

parent dbbceca0
......@@ -30,7 +30,7 @@ namespace OPNsense\Proxy\Api;
use \OPNsense\Base\ApiControllerBase;
use \OPNsense\Core\Backend;
use \OPNsense\Proxy\General;
use \OPNsense\Proxy\Proxy;
/**
* Class ServiceController
......@@ -101,13 +101,13 @@ class ServiceController extends ApiControllerBase
// close session for long running action
session_write_close();
$mdlGeneral = new General();
$mdlProxy = new Proxy();
$backend = new Backend();
$runStatus = $this->statusAction();
// stop squid when disabled
if ($runStatus['status'] == "running" && $mdlGeneral->enabled->__toString() == 0) {
if ($runStatus['status'] == "running" && $mdlProxy->general->enabled->__toString() == 0) {
$this->stopAction();
}
......@@ -115,7 +115,7 @@ class ServiceController extends ApiControllerBase
$backend->sendEvent("template reload OPNsense.Proxy");
// (res)start daemon
if ($mdlGeneral->enabled->__toString() == 1) {
if ($mdlProxy->general->enabled->__toString() == 1) {
if ($runStatus['status'] == "running") {
$backend->sendEvent("service reconfigure proxy");
} else {
......
......@@ -29,7 +29,7 @@
namespace OPNsense\Proxy\Api;
use \OPNsense\Base\ApiControllerBase;
use \OPNsense\Proxy\General;
use \OPNsense\Proxy\Proxy;
use \OPNsense\Core\Config;
/**
......@@ -39,19 +39,40 @@ use \OPNsense\Core\Config;
class SettingsController extends ApiControllerBase
{
/**
* retrieve general settings
* retrieve proxy settings
* @return array
*/
public function getAction()
{
$result = array();
if ($this->request->isGet()) {
$mdlGeneral = new General();
$selopt=array("lan"=>"LAN","wan"=>"WAN");
$mdlGeneral->interfaces->setSelectOptions($selopt);
$mdlProxy = new Proxy();
// Define array for selected interfaces
$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;
......@@ -66,23 +87,23 @@ class SettingsController extends ApiControllerBase
public function setAction()
{
$result = array("result"=>"failed");
if ($this->request->hasPost("general")) {
if ($this->request->hasPost("proxy")) {
// load model and update with provided data
$mdlGeneral = new General();
$mdlGeneral->setNodes($this->request->getPost("general"));
$mdlProxy = new Proxy();
$mdlProxy->setNodes($this->request->getPost("proxy"));
// perform validation
$valMsgs = $mdlGeneral->performValidation();
$valMsgs = $mdlProxy->performValidation();
foreach ($valMsgs as $field => $msg) {
if (!array_key_exists("validations", $result)) {
$result["validations"] = array();
}
$result["validations"]["general.".$msg->getField()] = $msg->getMessage();
$result["validations"]["proxy.".$msg->getField()] = $msg->getMessage();
}
// serialize model to config
if ($valMsgs->count() == 0) {
$mdlGeneral->serializeToConfig();
$mdlProxy->serializeToConfig();
}
}
......
......@@ -36,7 +36,7 @@ class IndexController extends \OPNsense\Base\IndexController
{
public function indexAction()
{
$this->view->title = "Proxy";
$this->view->title = "Proxy Server";
$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;
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">
$( document ).ready(function() {
data_get_map = {'frm_proxy':"/api/proxy/settings/get"};
// load initial data
ajaxGet(url="/api/proxy/settings/get",sendData={},callback=function(data,status) {
$.each(data_get_map, function(data_index, data_url) {
ajaxGet(url=data_url,sendData={},callback=function(data,status) {
if (status == "success") {
setFormData('frm_general',data);
$("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
$("#save").click(function(){
saveFormToEndpoint(url="/api/proxy/settings/set",formid="frm_general",callback_ok=function(){
$("#save_proxy-general").click(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
$("#frm_proxy-general_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-general_progress").removeClass("fa fa-spinner fa-pulse");
if (status != "success" || data['status'] != 'ok' ) {
// fix error handling
BootstrapDialog.show({
type:BootstrapDialog.TYPE_WARNING,
title: 'Proxy General TAB',
message: JSON.stringify(data)
});
}
});
});
});
$("#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_general_progress").addClass("fa fa-spinner fa-pulse");
$("#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_general_progress").removeClass("fa fa-spinner fa-pulse");
$("#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',
title: 'Proxy Server TAB',
message: JSON.stringify(data)
});
}
......@@ -32,53 +67,109 @@
});
});
// 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>
<ul class="nav nav-tabs nav-justified" role="tablist" id="maintabs">
<li class="active"><a data-toggle="tab" href="#tabGeneral">General</a></li>
<li><a data-toggle="tab" href="#sectionB">Section B</a></li>
</ul>
<div class="content-box tab-content">
<div id="tabGeneral" class="tab-pane fade in active">
<form id="frm_general" 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>
{{ partial("layout_partials/form_input_tr",
['id': 'general.enabled',
'label':'enabled',
<!-- TODO: explain TABS
content_location,tab_name,
field_array
activetab: content_location
-->
<!-- TODO: explain usage of select_multiple
special options:
style: used as class, defined classes are: tokenize
hint: show default text used for tokenize select
allownew: set to "true" if new items can be added to the list, default is "false"
size: for tokenize this defines the max shown items (default = 5) of the dropdown list, if it does not fit a scrollbar is shown
maxheight: define max height of select box, default=170px to hold 5 items
-->
{{ partial("layout_partials/base_tabs",
['tabs': {
['proxy-general','General Proxy Settings',
{['id': 'proxy.general.enabled',
'label':'Enable proxy',
'type':'checkbox',
'help':'test'
])
}}
{{ partial("layout_partials/form_input_tr",
['id': 'general.interfaces',
'label':'interfaces',
'type':'select_multiple'
])
}}
{{ partial("layout_partials/form_input_tr",
['id': 'general.port',
'label':'port',
'type':'text'
'help':'Enable or disable the proxy service.'
]}
],
['proxy-forward','Forward Proxy',
{['id': 'proxy.forward.interfaces',
'label':'Proxy interfaces',
'type':'select_multiple',
'style':'tokenize',
'help':'Select interface(s) the proxy will bind to.',
'hint':'Type or select interface'
],
['id': 'proxy.forward.port',
'label':'Proxy port',
'type':'text',
'help':'The port the proxy service will listen to.'
],
['id': 'proxy.forward.addACLforInterfaceSubnets',
'label':'Allow interface subnets',
'type':'checkbox',
'help':'When enabled the subnets of the selected interfaces will be added to the allow access list.'
],
['id': 'proxy.forward.transparentProxyMode',
'label':'Enable Transparent HTTP proxy',
'type':'checkbox',
'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'
])
}}
<tr>
<td colspan="3"><button class="btn btn-primary" id="save" type="button">Apply <i id="frm_general_progress" class=""></i></button></td>
</tr>
</tbody>
</table>
</form>
</div>
<div id="sectionB" class="tab-pane fade">
</div>
</div>
}}
<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 }}">
<td >
<div class="control-label" for="{{ id }}">
{{label}}
{% if help|default(false) %} <a id="help_for_{{ id }}" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> {% endif %}
{% if help|default(false) %}
<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>
</td>
<td >
......@@ -11,11 +15,10 @@
{% elseif type == "checkbox" %}
<input type="checkbox" id="{{ id }}" >
{% 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 %}
{% if help|default(false) %}
<br/>
<small class="hidden" for="help_for_{{ id }}" >{{help}}</small>
{% endif %}
</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">
......@@ -11,11 +11,13 @@ http_port {{intf_item.ipaddr}}:{{ OPNsense.proxy.general.port }}
{% endif %}
{% endfor %}
{# virtual ip's #}
{% if helpers.exists('virtualip') %}
{% 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 }}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
{% endif %}
......
......@@ -29,7 +29,9 @@ div.Tokenize ul.TokensContainer
{
cursor: text;
padding: 0 5px 0 0;
height: 100px;
height:auto;
min-height:34px;
max-height: 170px;
overflow-y: auto;
background-color: white;
}
......@@ -138,6 +140,9 @@ div.Tokenize ul.Dropdown
border-radius: 0 0 6px 6px;
z-index: 20;
height: auto;
overflow-x: hidden;
}
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