- Port mapping support under internet > port mapping

- Exposed host
- Firewall: enable disable etc..
This commit is contained in:
Martin Schröder 2015-04-28 16:59:06 +02:00 committed by Martin Schröder
parent 13cffee58e
commit e0dee6003c
18 changed files with 373 additions and 19 deletions

View file

@ -1 +1,6 @@
.nav, .pagination, .carousel, .panel-title a { cursor: pointer; }
/* needed for modals */
.modal-backdrop {
z-index: 0;
}

View file

@ -36,6 +36,7 @@
<script src="/lib/js/translations.js"></script>
<script src="/lib/js/bootstrap.min.js"></script>
<script src="/lib/js/angular-ui-switch.min.js"></script>
<script src="/lib/js/angular-modal-service.min.js"></script>
<!-- ###---### -->
<script src="js/app.js"></script>

View file

@ -78,6 +78,7 @@ angular.module("luci", [
"ui.bootstrap",
"ui.router",
'ui.select',
'angularModalService',
"uiSwitch",
"ngAnimate",
"gettext"

View file

@ -61,8 +61,11 @@ angular.module("luci")
"luci2.ui.menu",
"uci.state",
"uci.set",
"uci.add",
"uci.delete",
"uci.commit",
"uci.rollback",
"uci.revert",
"uci.configs"
// the rest is automatically retreived from session now!
/*"session.destroy",

View file

@ -38,7 +38,7 @@ angular.module("luci")
// create the rpc method
obj[path[0]] = function(data){
var func = (function(data){
if(!data) data = {};
if(!data) data = { };
var deferred = $.Deferred();
$.jsonRPC.withOptions({
namespace: "",
@ -46,7 +46,7 @@ angular.module("luci")
}, function(){
var sid = "00000000000000000000000000000000";
if($rootScope.sid) sid = $rootScope.sid;
//data.ubus_rpc_session = sid;
this.request('call', {
params: [ sid, namespace, method, data],
success: function(result){

View file

@ -22,7 +22,7 @@
// uci module for interacting with uci tables
angular.module("luci")
.factory('$uci', function($rpc){
.factory('$uci', function($rpc, $rootScope){
function initReq(path){
var parts = path.split(".");
var req = {};
@ -50,11 +50,64 @@ angular.module("luci")
var req = initReq(path);
req.values = values;
$rpc.uci.set(req).done(function(state){
$rpc.uci.commit(req).done(function(){
deferred.resolve();
}).fail(function(){
deferred.reject();
});
deferred.resolve();
}).fail(function(){
deferred.reject();
});
return deferred.promise();
},
// add a new rule to the uci config
add: function(config, type, values){
var deferred = $.Deferred();
$rpc.uci.add({
"config": config,
"type": type,
"values": values
}).done(function(state){
values[".name"] = state.section;
deferred.resolve(state.section);
}).fail(function(){
deferred.reject();
});
return deferred.promise();
},
// commit config changes
commit: function(path, values){
var deferred = $.Deferred();
var req = initReq(path);
$rpc.uci.commit(req).done(function(state){
deferred.resolve();
}).fail(function(){
deferred.reject();
});
return deferred.promise();
},
// revert uncommitted changes
revert: function(path, values){
var deferred = $.Deferred();
var req = initReq(path);
$rpc.uci.revert(req).done(function(state){
deferred.resolve();
}).fail(function(){
deferred.reject();
});
return deferred.promise();
},
// rollback
rollback: function(){
var deferred = $.Deferred();
$rpc.uci.rollback().done(function(state){
deferred.resolve();
}).fail(function(){
deferred.reject();
});
return deferred.promise();
},
delete: function(path){
var deferred = $.Deferred();
var req = initReq(path);
$rpc.uci.delete(req).done(function(state){
deferred.resolve();
}).fail(function(){
deferred.reject();
});

View file

@ -0,0 +1,3 @@
/*angular-modal-service v0.6.6 - https://github.com/dwmkerr/angular-modal-service */
!function(){"use strict";var e=angular.module("angularModalService",[]);e.factory("ModalService",["$document","$compile","$controller","$http","$rootScope","$q","$templateCache",function(e,n,l,t,o,r,a){function c(){var e=this,c=function(e,n){var l=r.defer();if(e)l.resolve(e);else if(n){var o=a.get(n);void 0!==o?l.resolve(o):t({method:"GET",url:n,cache:!0}).then(function(e){a.put(n,e.data),l.resolve(e.data)},function(e){l.reject(e)})}else l.reject("No template or templateUrl has been specified.");return l.promise};e.showModal=function(e){var t=r.defer(),a=e.controller;return a?(e.controllerAs&&(a=a+" as "+e.controllerAs),c(e.template,e.templateUrl).then(function(c){var u=o.$new(),s=r.defer(),p={$scope:u,close:function(e,n){(void 0===n||null===n)&&(n=0),window.setTimeout(function(){s.resolve(e),u.$destroy(),m.remove(),p.close=null,t=null,s=null,$=null,p=null,m=null,u=null},n)}};if(e.inputs)for(var d in e.inputs)p[d]=e.inputs[d];var f=angular.element(c),v=n(f),m=v(u);p.$element=m;var h=l(a,p);e.appendElement?e.appendElement.append(m):i.append(m);var $={controller:h,scope:u,element:m,close:s.promise};t.resolve($)}).then(null,function(e){t.reject(e)}),t.promise):(t.reject("No controller has been specified."),t.promise)}}var i=e.find("body");return new c}])}();
//# sourceMappingURL=angular-modal-service.min.js.map

View file

@ -11,7 +11,9 @@
"widgets/luci.progress",
"widgets/luci.table",
"widgets/luci.top_bar",
"widgets/uci.wireless.interface"
"widgets/uci.wireless.interface",
"widgets/uci.firewall.nat.rule.edit",
"widgets/core.modal"
],
"pages": {
"overview": {

View file

@ -0,0 +1,15 @@
<div class="modal fade">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">{{ title }}</h4>
</div>
<div class="modal-body" ng-transclude></div>
<div class="modal-footer">
<button class="btn btn-primary" ng-show="acceptLabel" ng-click="onAccept()">{{acceptLabel}}</button>
<button class="btn btn-default" ng-show="dismissLabel" ng-click="onDismiss()">{{dismissLabel}}</button>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,59 @@
$juci.module("core")
.directive('modal', function () {
var plugin_root = $juci.module("core").plugin_root;
return {
templateUrl: plugin_root + "/widgets/core.modal.html",
restrict: 'E',
transclude: true,
replace:true,
scope: {
acceptLabel: "@acceptLabel",
dismissLabel: "@dismissLabel",
onAccept: "&onAccept",
onDismiss: "&onDismiss",
ngShow: "=ngShow",
title: "=title"
},
controller: "ModalController",
link: function postLink(scope, element, attrs) {
//scope.title = attrs.title;
//scope.acceptLabel = attrs.acceptLabel;
//scope.dismissLabel = attrs.dismissLabel;
scope.element = element;
scope.$watch("ngShow", function(value){
if(value == true)
$(element).modal('show');
else
$(element).modal('hide');
});
$(element).on('shown.bs.modal', function(){
scope.$apply(function(){
scope.$parent[attrs.ngShow] = true;
});
});
$(element).on('hidden.bs.modal', function(){
scope.$apply(function(){
scope.$parent[attrs.ngShow] = false;
});
});
}
};
})
.controller('ModalController', function($scope) {
/*$scope.onDismiss = function(){
console.log("Modal Dismissed");
}
$scope.onAccept = function(){
console.log("Modal accepted!");
}*/
/*console.log("controller: "+$scope.ngShow);
$scope.$watch("ngShow", function(value){
console.log("visible: "+$scope.ngShow);
if(value == true)
$($scope.element).modal('show');
else
$($scope.element).modal('hide');
});*/
});

View file

@ -0,0 +1,63 @@
<form name="ruleForm" class="form-horizontal">
<div class="form-group">
<label for="email" class="control-label col-xs-3">Device</label>
<div class="col-xs-9">
<ui-select ng-model="rule.dest_ip"
theme="bootstrap"
search-enabled="false"
on-select="onChangeSection($item, $model)">
<ui-select-match placeholder="Select device...">{{devices[$select.selected].hostname}}</ui-select-match>
<ui-select-choices repeat="c in clients"
refresh-delay="0">
<div >{{devices[c].hostname}}</div>
</ui-select-choices>
</ui-select>
</div>
</div>
<div class="form-group">
<label for="email" class="control-label col-xs-3">Local IP</label>
<div class="col-xs-9"><input type="text" class="form-control" ng-model="rule.dest_ip" ng-pattern="patterns.ipaddress"/></div>
</div>
<div class="form-group">
<label for="email" class="control-label col-xs-3">Protocol</label>
<div class="col-xs-9">
<ui-select ng-model="rule.proto"
theme="bootstrap"
search-enabled="false"
on-select="onChangeSection($item, $model)">
<ui-select-match placeholder="Select protocol...">{{$select.selected}}</ui-select-match>
<ui-select-choices repeat="c in protocols"
refresh-delay="0">
<div >{{c}}</div>
</ui-select-choices>
</ui-select>
</div>
</div>
<div class="form-group">
<label for="email" class="control-label col-xs-3">Type</label>
<div class="col-xs-9">
<input type="radio" ng-model="portIsRange" ng-value="1"/> Range
<input type="radio" ng-model="portIsRange" ng-value="0"/> Single
</div>
</div>
<div class="form-group" ng-show="portIsRange">
<label for="email" class="control-label col-xs-3">Public port range</label>
<div class="col-xs-9"><input type="text" class="form-control" ng-model="rule.src_dport" ng-pattern="patterns.port"/></div>
</div>
<div class="form-group" ng-show="portIsRange">
<label for="email" class="control-label col-xs-3">Private port range</label>
<div class="col-xs-9"><input type="text" class="form-control" ng-model="rule.dest_port" ng-pattern="patterns.port"/></div>
</div>
<div class="form-group" ng-show="!portIsRange">
<label for="email" class="control-label col-xs-3">Public port</label>
<div class="col-xs-9"><input type="text" class="form-control" ng-model="rule.src_dport" ng-pattern="patterns.port"/></div>
</div>
<div class="form-group" ng-show="!portIsRange">
<label for="email" class="control-label col-xs-3">Private port</label>
<div class="col-xs-9"><input type="text" class="form-control" ng-model="rule.dest_port" ng-pattern="patterns.port"/></div>
</div>
<div class="btn-group btn-group-justified">
<button type="submit" class="btn btn-default " ng-click="onAccept(rule)" ng-disabled="ruleForm.$invalid">Submit</button>
<button type="submit" class="btn btn-default " ng-click="onCancel(rule)" ng-disabled="ruleForm.$invalid">Cancel</button>
</div>
</form>

View file

@ -0,0 +1,33 @@
$juci.module("core")
.directive("uciFirewallNatRuleEdit", function($compile, $parse){
var plugin_root = $juci.module("core").plugin_root;
return {
templateUrl: plugin_root+"/widgets/uci.firewall.nat.rule.edit.html",
scope: {
rule: "=ngModel"
},
controller: "uciFirewallNatRuleEdit",
replace: true,
require: "^ngModel"
};
}).controller("uciFirewallNatRuleEdit", function($scope, $uci, $rpc){
$scope.portIsRange = 1;
$scope.protocols = ["udp", "tcp"];
$scope.patterns = {
ipaddress: /^(?!0)(?!.*\.$)((1?\d?\d|25[0-5]|2[0-4]\d)(\.|$)){4}$/,
port: /^\d{1,5}$/
}
$rpc.router.clients().done(function(clients){
//alert(JSON.stringify(Object.keys(clients).map(function(x) { return clients[x]; })));
$scope.clients = [];
$scope.devices = {};
Object.keys(clients).map(function(x) {
var c = clients[x];
if(c.connected){
$scope.devices[c.ipaddr] = c;
$scope.clients.push(c.ipaddr);
}
});
$scope.$apply();
});
});

View file

@ -27,8 +27,10 @@ $juci.module("router")
};
$scope.onSave = function(){
$uci.set("firewall.dmz", $scope.dmz).done(function(){
$scope.info_message = $tr("STR_SETTINGSSAVED");
$scope.$apply();
$uci.commit("firewall.dmz").done(function(){
$scope.info_message = $tr("STR_SETTINGSSAVED");
$scope.$apply();
});
}).fail(function(){
$scope.error_message = $tr("STR_SETTINGSSAVEFAILED");
});

View file

@ -6,13 +6,13 @@
<tr>
<td>
<div class="col-md-11"><strong style="font-size: 1.5em">Firewall</strong></div>
<div class="col-md-1"> <switch id="enabled" name="enabled" ng-model="firewallEnabled" class="green"></switch></div>
<div class="col-md-1"><switch id="enabled" name="enabled" ng-model="settings.firewall" ng-change="onSave" class="green"></switch></div>
</td>
</tr>
<tr>
<td>
<div class="col-md-11"><strong style="font-size: 1.5em">Allow Ping to WAN interface</strong></div>
<div class="col-md-1"> <switch id="enabled" name="enabled" ng-model="allowWANPing" class="green"></switch></div>
<div class="col-md-1"><switch id="enabled" name="enabled" ng-model="settings.ping_wan" ng-change="onSave" class="green"></switch></div>
</td>
</tr>
</table>

View file

@ -1,5 +1,16 @@
$juci.module("router")
.controller("InternetFirewallPageCtrl", function($scope){
$scope.firewallEnabled = 1;
$scope.allowWANPing = 1;
.controller("InternetFirewallPageCtrl", function($scope, $uci){
$uci.show("firewall.settings").done(function(settings){
$scope.settings = settings;
settings.firewall = Number(settings.firewall);
settings.ping_wan = Number(settings.ping_wan);
$scope.onSave = function(){
$uci.set("firewall.settings", $scope.settings).done(function(){
});
}
$scope.$apply();
});
});

View file

@ -1,5 +1,34 @@
<luci-layout-with-sidebar>
<div ng-controller="InternetPortMappingPageCtrl">
Port mapping settings
<h2 translate>Port Mapping</h2>
<p translate>Port mapping allows remote computers to connect to a specific device within your private network.</p>
<table class="table">
<thead><th translate>Local IP</th><th translate>Protocol</th><th translate>Local port</th><th translate>Public Port</th><th></th><th></th></thead>
<tr ng-repeat="r in redirects">
<td>{{ r.dest_ip }}</td>
<td>{{ r.proto }}</td>
<td>{{ r.dest_port }}</td>
<td>{{ r.src_dport }}</td>
<td style="width: 1%"><button class="btn btn-default" ng-click="onEditRule(r)"><i class="fa fa-pencil" style="{{editColor}}"></i></button></td>
<td style="width: 1%"><button class="btn btn-default" ng-click="onDeleteRule(r)"><i class="fa fa-trash-o"></i></button></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td style="width: 1%"></td>
<td style="width: 1%"><button class="btn btn-default" ng-click="onAddRule()"><i class="fa fa-plus"></i></button></td>
</tr>
</table>
<form class="form-horizontal">
<div class="btn-toolbar pull-right">
<button class="btn btn-primary" ng-click="onCommit()">Save</button>
<button class="btn btn-default" ng-click="onCancel()">Cancel</button>
</div>
</form>
<modal title="modalTitle" ng-show="showModal" on-accept="onAcceptModal()" on-dismiss="onDismissModal()" dismiss-label="Cancel" accept-label="Save">
<uci-firewall-nat-rule-edit ng-model="rule"></uci-firewall-nat-rule-edit>
</modal>
</div>
</luci-layout-with-sidebar>

View file

@ -1,4 +1,72 @@
$juci.module("router")
.controller("InternetPortMappingPageCtrl", function($scope){
.controller("InternetPortMappingPageCtrl", function($scope, $uci, ModalService, $rpc){
function reload(){
$uci.show("firewall").done(function(firewall){
$scope.redirects = Object.keys(firewall).filter(function(x) { return firewall[x][".type"] == "redirect"; }).map(function(x){ return firewall[x]; });
$scope.$apply();
});
} reload();
$scope.showModal = 0;
$scope.onAcceptModal = function(){
var rule = $scope.rule;
Object.keys($scope.redirects).map(function(idx) { var x = $scope.redirects[idx]; if(x[".name"] == rule[".name"]) $scope.redirects[idx] = rule; });
console.log(JSON.stringify(rule));
if(!rule[".name"]){
// set up the rule in uci
$uci.add("firewall", "redirect", rule).done(function(x){
console.log("Added new section: "+x);
if(x.section) rule[".name"] = x.section;
});
} else {
$uci.set("firewall."+rule[".name"], rule).always(function(firewall){
});
}
$scope.showModal = 0;
}
$scope.onDismissModal = function(){
$scope.showModal = 0;
}
$scope.onAddRule = function(){
$scope.rule = {};
$scope.redirects.push($scope.rule);
$scope.showModal = 1;
}
$scope.onEditRule = function(rule){
$scope.rule = Object.create(rule);
$scope.modalTitle = "Edit port mapping ("+(rule['.name'] || 'new')+")";
$scope.showModal = 1;
}
$scope.onDeleteRule = function(rule){
function removeFromList(){
if($scope.redirects) {
$scope.redirects = $scope.redirects.filter(function(x){ return x !== rule; });
$scope.$apply();
}
}
console.log("Deleting rule: "+rule[".name"]);
if(rule[".name"]){
$uci.delete("firewall."+rule[".name"]).always(function(){
removeFromList();
});
} else {
retmoveFromList();
}
}
$scope.onCommit = function(){
if(!$scope.redirects) return;
$uci.commit("firewall").always(function(){
$scope.$apply();
console.log("Saved firewall settings!");
});
}
$scope.onCancel = function(){
$uci.rollback().always(function(){
reload();
});
}
});

View file

@ -82,7 +82,13 @@ app.post('/ubus', function(req, res) {
body: data
}, function (error, response, body) {
if(error){
doLocalRPC();
console.log("ERROR: "+error);
res.write(JSON.stringify({
jsonrpc: "2.0",
result: [1, error]
}));
res.end();
//doLocalRPC();
return;
}
var json = JSON.stringify(body);