Add feature to build rules based on users, groups on Proxy/Squid (#337)

This commit is contained in:
Alexander Shursha
2017-10-27 16:08:05 +02:00
committed by Franco Fichtner
parent 31dd6f6d51
commit 5469067606
15 changed files with 913 additions and 0 deletions
@@ -0,0 +1,6 @@
rm -f /usr/local/etc/squid/pre-auth/ProxyUserACL.conf
rm -f /usr/local/etc/squid/groupACL_*.txt
rm -f /usr/local/etc/squid/userACL_*.txt
if [ -f /var/run/squid/squid.pid ]; then
service squid reload
fi
+5
View File
@@ -0,0 +1,5 @@
/usr/local/opnsense/scripts/OPNsense/ProxyUserACL/reconfigure.php
if [ -f /var/run/squid/squid.pid ]; then
service squid reload
fi
+9
View File
@@ -0,0 +1,9 @@
PLUGIN_NAME= web-proxy-useracl
PLUGIN_VERSION= 0.0.1
PLUGIN_COMMENT= Group & User Squid ACL
PLUGIN_DEPENDS=
PLUGIN_MAINTAINER= kekek2@ya.ru
PLUGIN_WWW= http://smart-soft.ru
PLUGIN_DEVEL= yes
.include "../../Mk/plugins.mk"
@@ -0,0 +1,42 @@
<?php
/**
* Copyright (C) 2017 Smart-Soft
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
function proxy_useracl_configure()
{
return [
'webproxy' => ['proxy_useracl_squid_hook:2'],
];
}
function proxy_useracl_squid_hook()
{
configd_run('template reload OPNsense/ProxyUserACL');
configd_run('proxyuseracl reconfigure');
}
@@ -0,0 +1,378 @@
<?php
/**
* Copyright (C) 2017 Smart-Soft
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
namespace OPNsense\ProxyUserACL\Api;
use \OPNsense\Base\ApiMutableModelControllerBase;
use \OPNsense\Core\Config;
use \OPNsense\Base\UIModelGrid;
use \OPNsense\Auth\AuthenticationFactory;
use \OPNsense\Proxy\Proxy;
/**
* Class SettingsController Handles settings related API actions for the ProxyUserACL
* @package OPNsense\ProxySSO
*/
class SettingsController extends ApiMutableModelControllerBase
{
static protected $internalModelName = 'proxyuseracl';
static protected $internalModelClass = '\OPNsense\ProxyUserACL\ProxyUserACL';
/**
*
* search ACL
* @return array
*/
public function searchACLAction()
{
$this->sessionClose();
$mdlProxyUserACL = $this->getModel();
foreach ($mdlProxyUserACL->general->ACLs->ACL->getNodes() as $uuid => $acl) {
$mdlProxyUserACL->general->ACLs->ACL->{$uuid}->Domains = $this->decode($mdlProxyUserACL->general->ACLs->ACL->{$uuid}->Domains);
}
$grid = new UIModelGrid($mdlProxyUserACL->general->ACLs->ACL);
return $grid->fetchBindRequest($this->request, array("Group", "Name", "Domains", "Black", "Priority", "uuid"),
"Priority");
}
/**
*
* add ACL
* @return array
*/
public function addACLAction()
{
$result = array("result" => "failed");
if ($this->request->isPost() && $this->request->hasPost("ACL")) {
$result = array("result" => "failed", "validations" => array());
$mdlProxyUserACL = $this->getModel();
$post = $this->request->getPost("ACL");
$post["Hex"] = $this->strToHex($post["Name"]);
$count = count($mdlProxyUserACL->general->ACLs->ACL->getNodes());
if ($post["Priority"] > $count) {
$post["Priority"] = $count;
}
foreach ($mdlProxyUserACL->general->ACLs->ACL->sortedBy("Priority", true) as $acl) {
$key = $acl->getAttributes()["uuid"];
$priority = (string)$mdlProxyUserACL->general->ACLs->ACL->{$key}->Priority;
if ($priority < $post["Priority"]) {
break;
}
$mdlProxyUserACL->general->ACLs->ACL->{$key}->Priority = (string)($priority + 1);
}
$node = $mdlProxyUserACL->general->ACLs->ACL->Add();
$post["Domains"] = \OPNsense\Proxy\Api\SettingsController::encode($post["Domains"]);
$node->setNodes($post);
$find = $this->checkName($post["Name"], $post["Group"]);
if ($find !== true) {
$result["validations"]["ACL.Name"] = $find;
}
$valMsgs = $mdlProxyUserACL->performValidation();
foreach ($valMsgs as $field => $msg) {
$fieldnm = str_replace($node->__reference, "ACL", $msg->getField());
$result["validations"][$fieldnm] = $msg->getMessage();
}
if (count($result['validations']) <= 0) {
// save config if validated correctly
$mdlProxyUserACL->serializeToConfig();
Config::getInstance()->save();
return array("result" => "saved");
}
return $result;
}
return $result;
}
/**
*
* get ACL
* @return array
*/
public function getACLAction($uuid = null)
{
$mdlProxyUserACL = $this->getModel();
if ($uuid == null) {
// generate new node, but don't save to disc
$node = $mdlProxyUserACL->general->ACLs->ACL->add();
return array("ACL" => $node->getNodes());
}
$node = $mdlProxyUserACL->getNodeByReference('general.ACLs.ACL.' . $uuid);
if ($node != null) {
// return node
$node->Domains = $this->decode((string)$node->Domains);
return array("ACL" => $node->getNodes());
}
return array();
}
/**
*
* set ACL
* @return array
*/
public function setACLAction($uuid)
{
$result = array("result" => "failed");
if ($this->request->isPost() && $this->request->hasPost("ACL")) {
$mdlProxyUserACL = $this->getModel();
if ($uuid != null) {
$node = $mdlProxyUserACL->getNodeByReference('general.ACLs.ACL.' . $uuid);
if ($node != null) {
$result = array("result" => "failed", "validations" => array());
$ACLInfo = $this->request->getPost("ACL");
$ACLInfo["Hex"] = $this->strToHex($ACLInfo["Name"]);
$ACLInfo["Domains"] = \OPNsense\Proxy\Api\SettingsController::encode($ACLInfo["Domains"]);
$old_priority = (string)$node->Priority;
$new_priority = $ACLInfo["Priority"];
if ($new_priority < $old_priority) {
if ($new_priority < 0) {
$new_priority = 0;
}
foreach ($mdlProxyUserACL->general->ACLs->ACL->sortedBy("Priority", true) as $acl) {
$key = $acl->getAttributes()["uuid"];
$priority = (string)$mdlProxyUserACL->general->ACLs->ACL->{$key}->Priority;
if ($priority < $new_priority) {
break;
}
if ($priority >= $old_priority) {
continue;
}
$mdlProxyUserACL->general->ACLs->ACL->{$key}->Priority = (string)($priority + 1);
}
} elseif (($new_priority > $old_priority)) {
$count = count($mdlProxyUserACL->general->ACLs->ACL->getNodes());
if ($new_priority >= $count) {
$new_priority = $count - 1;
$ACLInfo["Priority"] = $new_priority;
}
foreach ($mdlProxyUserACL->general->ACLs->ACL->sortedBy("Priority") as $acl) {
$key = $acl->getAttributes()["uuid"];
$priority = (string)$mdlProxyUserACL->general->ACLs->ACL->{$key}->Priority;
if ($priority > $new_priority) {
break;
}
if ($priority <= $old_priority) {
continue;
}
$mdlProxyUserACL->general->ACLs->ACL->{$key}->Priority = (string)($priority - 1);
}
}
$node->setNodes($ACLInfo);
$find = $this->checkName($ACLInfo["Name"], $ACLInfo["Group"]);
if ($find !== true) {
$result["validations"]["ACL.Name"] = $find;
}
$valMsgs = $mdlProxyUserACL->performValidation();
foreach ($valMsgs as $field => $msg) {
$fieldnm = str_replace($node->__reference, "ACL", $msg->getField());
$result["validations"][$fieldnm] = $msg->getMessage();
}
if (count($result['validations']) > 0) {
return $result;
}
// save config if validated correctly
$mdlProxyUserACL->serializeToConfig();
Config::getInstance()->save();
return array("result" => "saved");
}
}
}
return $result;
}
/**
*
* del ACL
* @return array
*/
public function delACLAction($uuid)
{
$result = array("result" => "failed");
if ($this->request->isPost() && $uuid != null) {
$mdlProxyUserACL = $this->getModel();
if ($mdlProxyUserACL->general->ACLs->ACL->del($uuid)) {
// if item is removed, serialize to config and save
$this->repackPriority();
$mdlProxyUserACL->serializeToConfig();
Config::getInstance()->save();
$result['result'] = 'deleted';
} else {
$result['result'] = 'not found';
}
}
return $result;
}
/**
*
* Change ACL priority
* @param $uuid item unique id
* @return array
*/
public function updownACLAction($uuid)
{
$result = array("result" => "failed");
if ($this->request->isPost() && $uuid != null && $this->request->hasPost("command")) {
$mdlProxyUserACL = $this->getModel();
$count = $this->repackPriority();
$nodes = $mdlProxyUserACL->general->ACLs->ACL->getNodes();
$acl = $nodes[$uuid];
$priority = $acl["Priority"];
switch ($this->request->getPost("command")) {
case "up":
$new_priority = $priority - 1;
if ($new_priority < 0) {
return array("result" => "success");
}
break;
case "down":
$new_priority = $priority + 1;
if ($new_priority >= $count) {
return array("result" => "success");
}
break;
default:
return array("result" => "failed");
}
foreach ($nodes as $key => $node) {
if ($node["Priority"] == $new_priority) {
$mdlProxyUserACL->general->ACLs->ACL->{$key}->Priority = (string)$priority;
$mdlProxyUserACL->general->ACLs->ACL->{$uuid}->Priority = (string)$new_priority;
$mdlProxyUserACL->serializeToConfig();
Config::getInstance()->save();
return array('result' => 'success');
}
}
}
return $result;
}
private function checkName($user, $search)
{
$authFactory = new AuthenticationFactory();
$servers = $authFactory->listServers();
foreach (explode(',', (new Proxy())->forward->authentication->method) as $method) {
if ($method == "") {
return gettext("No authentication method selected");
}
$server = $servers[$method];
switch ($server["type"]) {
case "ldap":
if (!isset($server["ldap_binddn"])) {
return gettext("LDAP user name is not specified");
}
if (!isset($server["ldap_bindpw"])) {
return gettext("LDAP user password is not specified");
}
$ldapBindURL = strstr($server['ldap_urltype'], "Standard") ? "ldap://" : "ldaps://";
$ldapBindURL .= strpos($server['host'], "::") !== false ? "[{$server['host']}]" : $server['host'];
$ldapBindURL .= !empty($server['ldap_port']) ? ":{$server['ldap_port']}" : "";
$ldap_auth_server = $authFactory->get($server["name"]);
if ($ldap_auth_server->connect($ldapBindURL, $server["ldap_binddn"],
$server["ldap_bindpw"]) == false) {
return gettext("Error connecting to LDAP server");
}
try {
$users = $ldap_auth_server->searchUsers($user, $server["ldap_attr_user"]);
} catch (\Exception $e) {
break;
}
if ($users !== false && count($users) > 0) {
return true;
}
break;
case "local":
foreach (Config::getInstance()->object()->system->{"$search"} as $item) {
if ($user == (string)$item->name) {
return true;
}
}
break;
default:
break;
}
}
return sprintf(gettext('The %s %s does not exist'), $search, $user);
}
private function repackPriority()
{
$mdlProxyUserACL = $this->getModel();
$count = 0;
foreach ($mdlProxyUserACL->general->ACLs->ACL->sortedBy("Priority") as $node) {
$key = $node->getAttributes()["uuid"];
$mdlProxyUserACL->general->ACLs->ACL->{$key}->Priority = (string)$count++;
}
return $count;
}
private function strToHex($string)
{
$hex = '';
for ($i = 0; $i < strlen($string); $i++) {
$hex .= dechex(ord($string[$i]));
}
return $hex;
}
private function decode($domains)
{
$result = array();
foreach (explode(",", $domains) as $domain) {
if ($domain != "") {
$result[] = idn_to_utf8($domain);
}
}
return implode(",", $result);
}
}
@@ -0,0 +1,43 @@
<?php
/**
* Copyright (C) 2017 Smart-Soft
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
namespace OPNsense\ProxyUserACL;
class IndexController extends \OPNsense\Base\IndexController
{
public function indexAction()
{
// set page title, used by the standard template in layouts/default.volt.
$this->view->title = gettext("Group and User ACL settings");
// pick the template to serve to our users.
$this->view->pick('OPNsense/ProxyUserACL/index');
$this->view->formDialogACL = $this->getForm("dialogACL");
}
}
@@ -0,0 +1,44 @@
<form>
<field>
<id>ACL.Name</id>
<label>Name</label>
<type>text</type>
<help>Enter a name of user/group. Group name is case sensitive.</help>
</field>
<field>
<id>ACL.Priority</id>
<label>Priority</label>
<type>text</type>
<help>Rule priority</help>
</field>
<field>
<id>ACL.Group</id>
<label>Group/User</label>
<type>dropdown</type>
<help>Group or User ACL</help>
</field>
<field>
<id>ACL.Black</id>
<label>Black/White</label>
<type>dropdown</type>
<help>Black or White list</help>
</field>
<field>
<id>ACL.Domains</id>
<label>Domains</label>
<type>select_multiple</type>
<style>tokenize</style>
<allownew>true</allownew>
<help><![CDATA[Destination domains.<br/>
You may use a regular expression, use a comma or press Enter for new item.<br/>
<div class="alert alert-info">
<b>Examples:</b><br/>
<b class="text-primary">mydomain.com</b> -> matches on <b>*.mydomain.com</b><br/>
<b class="text-primary">^https?:\/\/([a-zA-Z]+)\.mydomain\.</b> -> matches on <b>http(s)://textONLY.mydomain.*</b><br/>
<b class="text-primary">\.gif$</b> -> matches on <b>\*.gif</b> but not on <b class="text-danger">\*.gif\test</b><br/>
<b class="text-primary">\[0-9]+\.gif$</b> -> matches on <b>\123.gif</b> but not on <b class="text-danger">\test.gif</b><br/>
</div>
<div class="text-info"><b>TIP: </b>You can also paste a comma separated list into this field.</div>]]></help>
<hint>Regular expressions are allowed.</hint>
</field>
</form>
@@ -0,0 +1,8 @@
<menu>
<Services>
<WebProxy>
<ProxyUserACL VisibleName="Group and User ACL" cssClass="fa fa-shield fa-fw" order="30" url="/ui/proxyuseracl/"/>
</WebProxy>
</Services>
</menu>
@@ -0,0 +1,37 @@
<?php
/**
* Copyright (C) 2017 Smart-Soft
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
namespace OPNsense\ProxyUserACL;
use OPNsense\Base\BaseModel;
class ProxyUserACL extends BaseModel
{
}
@@ -0,0 +1,44 @@
<model>
<mount>//OPNsense/ProxyUserACL</mount>
<version>1.0.0</version>
<description>
Group and User ACL settings
</description>
<items>
<general>
<ACLs>
<ACL type="ArrayField">
<Name type="TextField">
<Required>Y</Required>
</Name>
<Hex type="TextField">
<Required>Y</Required>
</Hex>
<Domains type="CSVListField">
<Required>N</Required>
</Domains>
<Black type="OptionField">
<Required>Y</Required>
<default>Black</default>
<OptionValues>
<deny>Black</deny>
<allow>White</allow>
</OptionValues>
</Black>
<Priority type="IntegerField">
<default>0</default>
<Required>Y</Required>
</Priority>
<Group type="OptionField">
<Required>Y</Required>
<default>group</default>
<OptionValues>
<group>Group</group>
<user>User</user>
</OptionValues>
</Group>
</ACL>
</ACLs>
</general>
</items>
</model>
@@ -0,0 +1,155 @@
{#
Copyright (C) 2017 Smart-Soft
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
#}
<script type="text/javascript">
$(document).ready(function () {
grid = $("#grid-acl").UIBootgrid(
{
'search': '/api/proxyuseracl/settings/searchACL',
'get': '/api/proxyuseracl/settings/getACL/',
'set': '/api/proxyuseracl/settings/setACL/',
'add': '/api/proxyuseracl/settings/addACL/',
'del': '/api/proxyuseracl/settings/delACL/',
'toggle': '/api/proxyuseracl/settings/toggleACL/',
options: {
formatters: {
"commands": function (column, row) {
return "<button type=\"button\" class=\"btn btn-xs btn-default command-edit\" data-row-id=\"" + row.uuid + "\"><span class=\"fa fa-pencil\"></span></button> " +
"<button type=\"button\" class=\"btn btn-xs btn-default command-copy\" data-row-id=\"" + row.uuid + "\"><span class=\"fa fa-clone\"></span></button>" +
"<button type=\"button\" class=\"btn btn-xs btn-default command-delete\" data-row-id=\"" + row.uuid + "\"><span class=\"fa fa-trash-o\"></span></button>";
},
"rowtoggle": function (column, row) {
if (parseInt(row[column.id], 2) == 1) {
return "<span style=\"cursor: pointer;\" class=\"fa fa-check-square-o command-toggle\" data-value=\"1\" data-row-id=\"" + row.uuid + "\"></span>";
} else {
return "<span style=\"cursor: pointer;\" class=\"fa fa-square-o command-toggle\" data-value=\"0\" data-row-id=\"" + row.uuid + "\"></span>";
}
},
"boolean": function (column, row) {
if (parseInt(row[column.id], 2) == 1) {
return "<span class=\"fa fa-check\" data-value=\"1\" data-row-id=\"" + row.uuid + "\"></span>";
} else {
return "<span class=\"fa fa-times\" data-value=\"0\" data-row-id=\"" + row.uuid + "\"></span>";
}
},
"updown": function (column, row) {
return "<button type=\"button\" class=\"btn btn-xs btn-default command-updown\" data-row-id=\"" + row.uuid + "\" data-command=\"up\"><span class=\"fa fa-arrow-up\"></span></button> " +
"<button type=\"button\" class=\"btn btn-xs btn-default command-updown\" data-row-id=\"" + row.uuid + "\" data-command=\"down\"><span class=\"fa fa-arrow-down\"></span></button>";
},
}
}
}
);
grid.on("loaded.rs.jquery.bootgrid", function () {
grid.find(".command-updown").on("click", function () {
ajaxCall(url = "/api/proxyuseracl/settings/updownACL/" + $(this).data("row-id"), sendData = {"command": $(this).data("command")}, callback = function () {
$("#grid-acl").bootgrid("reload");
});
}).end();
grid.find("*[data-action=add]").click(function () {
$("#btn_DialogACL_save_progress").removeClass("fa fa-spinner fa-pulse");
$("#btn_DialogACL_save").click(function () {
$("#btn_DialogACL_save_progress").addClass("fa fa-spinner fa-pulse");
var old_handleFormValidation = window.handleFormValidation;
window.handleFormValidation = function (parent, validationErrors) {
$("#btn_DialogACL_save_progress").removeClass("fa fa-spinner fa-pulse");
window.handleFormValidation = old_handleFormValidation;
handleFormValidation(parent, validationErrors);
}
});
}).end();
});
$("#reconfigureAct").click(function () {
$("#reconfigureAct_progress").addClass("fa fa-spinner fa-pulse");
ajaxCall(url = "/api/proxy/service/reconfigure", sendData = {}, callback = function (data, status) {
// when done, disable progress animation.
$("#reconfigureAct_progress").removeClass("fa fa-spinner fa-pulse");
if (status != "success" || data['status'] != 'ok') {
BootstrapDialog.show({
type: BootstrapDialog.TYPE_WARNING,
title: "Error reconfiguring proxy",
message: data['status'],
draggable: true
});
}
});
});
});
</script>
<div id="acl">
<table id="acl-content">
<tr>
<td colspan="2">
<table id="grid-acl" class="table table-condensed table-hover table-striped table-responsive"
data-editDialog="DialogACL">
<thead>
<tr>
<th data-column-id="Priority" data-width="10em" data-type="string" data-sortable="false"
data-visible="true">{{ lang._('Number') }}</th>
<th data-column-id="Group" data-width="10em" data-type="string"
data-sortable="false">{{ lang._('Group') }}</th>
<th data-column-id="Black" data-width="10em" data-type="string"
data-sortable="false">{{ lang._('Black') }}</th>
<th data-column-id="Name" data-type="string"
data-sortable="false">{{ lang._('Name') }}</th>
<th data-column-id="Domains" data-type="string" data-sortable="false"
data-visible="true">{{ lang._('Domains') }}</th>
<th data-column-id="updown" data-width="7em" data-formatter="updown"
data-sortable="false">{{ lang._('Priority') }}</th>
<th data-column-id="commands" data-width="7em" data-formatter="commands"
data-sortable="false">{{ lang._('Commands') }}</th>
</tr>
</thead>
<tbody>
</tbody>
<tfoot>
<tr>
<td></td>
<td>
<button data-action="add" type="button" class="btn btn-xs btn-default"><span
class="fa fa-plus"></span></button>
<button data-action="deleteSelected" type="button" class="btn btn-xs btn-default"><span
class="fa fa-trash-o"></span></button>
</td>
</tr>
</tfoot>
</table>
</td>
</tr>
</table>
</div>
<button class="btn btn-primary" id="reconfigureAct" type="button"><b>{{ lang._('Apply') }}</b><i
id="reconfigureAct_progress" class=""></i></button>
{{ partial("layout_partials/base_dialog",['fields':formDialogACL,'id':'DialogACL','label':lang._('Edit user/group white and black lists')]) }}
@@ -0,0 +1,43 @@
#!/usr/bin/env php
<?php
/**
* Copyright (C) 2017 Smart-Soft
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
require_once('script/load_phalcon.php');
use \OPNsense\ProxyUserACL\ProxyUserACL;
use \OPNsense\Core\Config;
$mdlProxyUserACL = new ProxyUserACL();
$domain = strtoupper((string) Config::getInstance()->object()->system->domain);
array_map('unlink', glob("/usr/local/etc/squid/ACL_*.txt"));
foreach ($mdlProxyUserACL->getNodeByReference('general.ACLs.ACL')->getNodes() as $acl) {
file_put_contents("/usr/local/etc/squid/ACL_" . $acl["Priority"] . ".txt", $acl["Name"] . "\n" . ($acl["Group"]["user"]["selected"] == "1" ? $acl["Name"] . "@" . $domain . "\n" : ""));
}
@@ -0,0 +1,5 @@
[reconfigure]
command:/usr/local/opnsense/scripts/OPNsense/ProxyUserACL/reconfigure.php
parameters:
type:script
message:reconfigure proxy
@@ -0,0 +1 @@
ProxyUserACL.conf:/usr/local/etc/squid/auth/ProxyUserACL.conf
@@ -0,0 +1,93 @@
{% set ldap = [] %}
{% set local = [] %}
{% if helpers.exists('OPNsense.proxy.forward.authentication.method') and OPNsense.proxy.forward.authentication.method != '' %}
{% for method in OPNsense.proxy.forward.authentication.method.split(",") %}
{% if method == "Local Database" %}
{% if local.append("1") %}
{% endif %}
{% else %}
{% for server in helpers.toList('system.authserver') %}
{% if server.type == 'ldap' and server.name == method %}
{% if ldap.append(server) %}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
{% endif %}
{% if helpers.exists('OPNsense.ProxyUserACL.general.ACLs.ACL') %}
{% for ACL in helpers.toList('OPNsense.ProxyUserACL.general.ACLs.ACL') %}
{% if ACL.Group == "group" %}
{% if ldap|length == 1 %}
{% if helpers.exists('OPNsense.ProxySSO.EnableSSO') and OPNsense.ProxySSO.EnableSSO == '1' %}
external_acl_type ext_group_ldap_{{ ACL.Priority}} ttl=300 negative_ttl=60 %LOGIN /usr/local/libexec/squid/ext_kerberos_ldap_group_acl -a -t {{ ACL.Hex }} -D {{ system.domain|upper }}
acl group_ldap_{{ACL.Priority}} external ext_group_ldap_{{ ACL.Priority }}
{% else %}
{% for authcn in ldap[0].ldap_authcn.split(";") %}
{% if ldap[0].ldap_attr_user == 'cn' %}
external_acl_type ext_ldap_{{ ACL.Priority }}_{{ loop.index }} ttl=300 negative_ttl=60 %LOGIN /usr/local/libexec/squid/ext_ldap_group_acl -R -b "{{ldap[0].ldap_basedn}}" -f "(&(cn=%a)(memberUid=%u))" -D "{{ldap[0].ldap_binddn}}" -w "{{ldap[0].ldap_bindpw}}" -p "{{ldap[0].ldap_port}}" "{{ldap[0].host}}"
{% else %}
external_acl_type ext_ldap_{{ ACL.Priority }}_{{ loop.index }} ttl=300 negative_ttl=60 %LOGIN /usr/local/libexec/squid/ext_ldap_group_acl -R -b "{{ldap[0].ldap_basedn}}" -f "(&({{ldap[0].ldap_attr_user}}=%u)(memberOf=cn=%a,{{authcn}}))" -D "{{ldap[0].ldap_binddn}}" -w "{{ldap[0].ldap_bindpw}}" -p "{{ldap[0].ldap_port}}" "{{ldap[0].host}}"
{% endif %}
acl group_ldap_{{ACL.Priority}}_{{ loop.index }} external ext_ldap_{{ ACL.Priority }}_{{ loop.index }} "/usr/local/etc/squid/ACL_{{ ACL.Priority }}.txt"
{% endfor %}
{% endif %}
{% endif %}
{% if local|length == 1 %}
external_acl_type ext_group_local_{{ ACL.Priority }} ttl=300 negative_ttl=60 %LOGIN /usr/local/libexec/squid/ext_unix_group_acl -p
acl group_local_{{ACL.Priority}} external ext_group_local_{{ ACL.Priority }} "/usr/local/etc/squid/ACL_{{ ACL.Priority }}.txt"
{% endif %}
{% else %}
acl user_{{ACL.Priority}} proxy_auth "/usr/local/etc/squid/ACL_{{ ACL.Priority }}.txt"
{% endif %}
{% if ldap|length == 1 or local|length == 1 %}
{% for element in ACL.Domains.split(",") %}
{% if '^' in element or '\\' in element or '$' in element or '[' in element %}
acl domains_{{ACL.Priority}} url_regex {{element}}
{% else %}
acl domains_{{ACL.Priority}} url_regex {{element|replace(".","\.")}}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
{% endif %}
{% if helpers.exists('OPNsense.ProxyUserACL.general.ACLs.ACL') and (ldap|length == 1 or local|length == 1) %}
{% for priority in range(0,helpers.toList('OPNsense.ProxyUserACL.general.ACLs.ACL')|length) %}
{% for ACL in helpers.toList('OPNsense.ProxyUserACL.general.ACLs.ACL') %}
{% if ACL.Priority == priority|string %}
{% if ACL.Group == "group" %}
{% if ldap|length == 1 %}
{% if helpers.exists('OPNsense.proxy.forward.icap.enable') and OPNsense.proxy.forward.icap.enable == '1' %}
adaptation_access response_mod {{ACL.Black}} group_ldap_{{ACL.Priority}} domains_{{ACL.Priority}}
adaptation_access request_mod {{ACL.Black}} group_ldap_{{ACL.Priority}} domains_{{ACL.Priority}}
{% endif %}
{% if helpers.exists('OPNsense.ProxySSO.EnableSSO') and OPNsense.ProxySSO.EnableSSO == '1' %}
http_access {{ACL.Black}} group_ldap_{{ACL.Priority}} domains_{{ACL.Priority}}
{% else %}
{% for authcn in ldap[0].ldap_authcn.split(";") %}
http_access {{ACL.Black}} group_ldap_{{ACL.Priority}}_{{ loop.index }} domains_{{ACL.Priority}}
{% endfor %}
{% endif %}
{% endif %}
{% if local|length == 1 %}
{% if helpers.exists('OPNsense.proxy.forward.icap.enable') and OPNsense.proxy.forward.icap.enable == '1' %}
adaptation_access response_mod {{ACL.Black}} group_local_{{ACL.Priority}} domains_{{ACL.Priority}}
adaptation_access request_mod {{ACL.Black}} group_local_{{ACL.Priority}} domains_{{ACL.Priority}}
{% endif %}
http_access {{ACL.Black}} group_local_{{ACL.Priority}} domains_{{ACL.Priority}}
{% endif %}
{% else %}
{% if helpers.exists('OPNsense.proxy.forward.icap.enable') and OPNsense.proxy.forward.icap.enable == '1' %}
adaptation_access response_mod {{ACL.Black}} user_{{ACL.Priority}} domains_{{ACL.Priority}}
adaptation_access request_mod {{ACL.Black}} user_{{ACL.Priority}} domains_{{ACL.Priority}}
{% endif %}
http_access {{ACL.Black}} user_{{ACL.Priority}} domains_{{ACL.Priority}}
{% endif %}
{% break %}
{% endif %}
{% endfor %}
{% endfor %}
{% endif %}