summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--acf/model/init.lua10
-rw-r--r--acf/model/node.lua31
-rw-r--r--acf/model/permission.lua4
-rw-r--r--acf/model/set.lua34
-rw-r--r--acf/modules/awall.lua44
-rw-r--r--acf/modules/net.lua8
-rwxr-xr-xdev-shell12
-rw-r--r--protocol.txt14
-rw-r--r--server.lua10
-rw-r--r--web/client.js4
10 files changed, 104 insertions, 67 deletions
diff --git a/acf/model/init.lua b/acf/model/init.lua
index 7b58190..19cb848 100644
--- a/acf/model/init.lua
+++ b/acf/model/init.lua
@@ -30,7 +30,7 @@ net = require('acf.model.net')
node = require('acf.model.node')
permission = require('acf.model.permission')
register = require('acf.model.root').register
-set = require('acf.model.set')
+node.Set = require('acf.model.set').Set
local object = require('acf.object')
local class = object.class
@@ -150,9 +150,13 @@ function Collection:load(context, create)
end
+List = class(Collection)
+function List:init(params) super(self, List):init(params, node.List) end
+
+
Set = class(Collection)
-function Set:init(params) super(self, Set):init(params, set.Set) end
-function Set.save_member(node, k, v) set.add(node, v) end
+function Set:init(params) super(self, Set):init(params, node.Set) end
+function Set.save_member(tn, k, v) node.insert(tn, v) end
-- experimental
diff --git a/acf/model/node.lua b/acf/model/node.lua
index 6546bdc..a58124d 100644
--- a/acf/model/node.lua
+++ b/acf/model/node.lua
@@ -147,6 +147,36 @@ function Collection:init(context, params)
end
+List = class(Collection)
+
+function List:init(context, params)
+ super(self, List):init(context, params)
+
+ local mt = getmetatable(self)
+ mt.meta.type = 'list'
+
+ local save = mt.save
+ function mt.save(k, v)
+ assert(type(k) == 'number')
+ if v == nil then
+ local len = #mt.members()
+ while k < len do
+ mt.save(k, mt.load(k + 1))
+ k = k + 1
+ end
+ end
+ save(k, v)
+ end
+
+ function mt.insert(v, i)
+ local len = #mt.members()
+ if not i then i = len + 1 end
+ for j = len,i,-1 do mt.save(j + 1, mt.load(j)) end
+ mt.save(i, v)
+ end
+end
+
+
-- experimental
Mixed = class(Collection)
@@ -172,6 +202,7 @@ end
addr = meta_func('addr')
has_permission = meta_func('has_permission')
+insert = meta_func('insert')
members = meta_func('members')
meta = meta_func('meta')
mmeta = meta_func('mmeta')
diff --git a/acf/model/permission.lua b/acf/model/permission.lua
index 9a6d1d4..aac84f7 100644
--- a/acf/model/permission.lua
+++ b/acf/model/permission.lua
@@ -5,13 +5,13 @@ See LICENSE file for license details
module(..., package.seeall)
-local add = require('acf.model.set').add
+local insert = require('acf.model.node').insert
local start = require('acf.transaction').start
function define(path, ...)
local txn = start()
local db = txn:search('/auth/permissions')
- for _, permission in ipairs(arg) do add(db, permission..path) end
+ for _, permission in ipairs(arg) do insert(db, permission..path) end
txn:commit()
end
diff --git a/acf/model/set.lua b/acf/model/set.lua
index 2ab9d6d..47eeaa8 100644
--- a/acf/model/set.lua
+++ b/acf/model/set.lua
@@ -10,40 +10,30 @@ local npairs = require('acf.model.node').pairs
local object = require('acf.object')
-local function find(set, value)
- for i, member in npairs(set) do
- if member == value then return i end
- end
-end
-
-
-Set = object.class(require('acf.model.node').Collection)
+Set = object.class(require('acf.model.node').List)
function Set:init(context, params)
assert(not object.isinstance(params.field, TreeNode))
object.super(self, Set):init(context, params)
+ local function find(value)
+ for i, member in npairs(self) do
+ if member == value then return i end
+ end
+ end
+
local mt = getmetatable(self)
mt.meta.type = 'set'
- function mt.get(k, create) return (create or find(self, k)) and k end
+ function mt.get(k, create) return (create or find(k)) and k end
function mt.__newindex(t, k, v)
assert(v == nil)
- local i = find(self, k)
+ local i = find(k)
if not i then return end
-
- local len = #mt.members()
- while i < len do
- mt.save(i, mt.load(i + 1))
- i = i + 1
- end
- mt.save(len, nil)
+ mt.save(i, nil)
end
-end
-
-function add(set, value)
- local mt = getmetatable(set)
- if not find(set, value) then mt.save(#mt.members() + 1, value) end
+ local insert = mt.insert
+ function mt.insert(v) if not find(v) then insert(v) end end
end
diff --git a/acf/modules/awall.lua b/acf/modules/awall.lua
index 91b6a84..9e456da 100644
--- a/acf/modules/awall.lua
+++ b/acf/modules/awall.lua
@@ -28,7 +28,7 @@ IPSet.range = M.Range{type=M.net.IPv4Address}
local Service = M.new()
Service.proto = M.String{required=true, ui_name='Protocol'}
-Service.port = M.Collection{type=M.Range{type=M.net.Port}}
+Service.port = M.Set{type=M.Range{type=M.net.Port}}
Service['icmp-type'] = M.String{ui_name='ICMP type'}
Service['ct-helper'] = M.String{ui_name='Connection tracking helper'}
@@ -51,22 +51,22 @@ LogClass.threshold = M.Integer
local IPSetReference = M.new()
IPSetReference.name = M.Reference{scope='../../../ipset', required=true}
-IPSetReference.args = M.Collection{
+IPSetReference.args = M.List{
type=Direction, required=true, ui_name='Arguments'
}
local Rule = M.new()
-Rule['in'] = M.Collection{
+Rule['in'] = M.Set{
type=M.Reference{scope='../../../zone'}, ui_name='Ingess zones'
}
-Rule.out = M.Collection{
+Rule.out = M.Set{
type=M.Reference{scope='../../../zone'}, ui_name='Egress zones'
}
-Rule.src = M.Collection{type=M.String, ui_name='Sources'}
-Rule.dest = M.Collection{type=M.String, ui_name='Destinations'}
+Rule.src = M.Set{type=M.String, ui_name='Sources'}
+Rule.dest = M.Set{type=M.String, ui_name='Destinations'}
Rule.ipset = M.Model{model=IPSetReference, ui_name='IP set'}
Rule.ipsec = Direction{ui_name='Require IPsec'}
-Rule.service = M.Collection{type=M.Reference{scope='../../../service'}}
+Rule.service = M.Set{type=M.Reference{scope='../../../service'}}
Rule.action = M.String{choice={'accept'}}
@@ -89,7 +89,7 @@ FilterRule['conn-limit'] = M.Model{model=Limit, ui_name='Connection limit'}
FilterRule['flow-limit'] = M.Model{model=Limit, ui_name='Flow limit'}
FilterRule.dnat = M.net.IPv4Address{ui_name='DNAT target'}
FilterRule['no-track'] = M.Boolean{default=false, ui_name='CT bypass'}
-FilterRule.related = M.Collection{type=Rule, ui_name='Related packet rules'}
+FilterRule.related = M.List{type=Rule, ui_name='Related packet rules'}
local DivertRule = M.new(Rule)
DivertRule['to-port'] = M.Range{type=M.net.Port, ui_name='Target port'}
@@ -106,40 +106,36 @@ ClampMSSRule.mss = M.Integer{ui_name='MSS'}
local AWall = M.new()
-- TODO differentiate lists?
-AWall.service = M.Collection{type=M.Collection{type=Service}}
+AWall.service = M.Collection{type=M.List{type=Service}}
AWall.zone = M.Collection{type=Zone}
AWall.log = M.Collection{
type=LogClass, ui_name='Log classes', ui_member='Log class'
}
-AWall.policy = M.Collection{
- type=PolicyRule, ui_name='Policies', ui_member='Policy'
-}
-AWall['packet-log'] = M.Collection{
+AWall.policy = M.List{type=PolicyRule, ui_name='Policies', ui_member='Policy'}
+AWall['packet-log'] = M.List{
type=PacketLogRule, ui_name='Logging', ui_member='Logging rule'
}
-AWall.filter = M.Collection{type=FilterRule}
-AWall.dnat = M.Collection{type=NATRule, ui_name='DNAT', ui_member='DNAT rule'}
-AWall.snat = M.Collection{type=NATRule, ui_name='SNAT', ui_member='SNAT rule'}
-AWall.mark = M.Collection{
+AWall.filter = M.List{type=FilterRule}
+AWall.dnat = M.List{type=NATRule, ui_name='DNAT', ui_member='DNAT rule'}
+AWall.snat = M.List{type=NATRule, ui_name='SNAT', ui_member='SNAT rule'}
+AWall.mark = M.List{
type=MarkRule, ui_name='Packet marking', ui_member='Packet marking rule'
}
-AWall['route-track'] = M.Collection{
+AWall['route-track'] = M.List{
type=MarkRule, ui_name='Route tracking', ui_member='Route tracking rule'
}
-AWall.tproxy = M.Collection{
+AWall.tproxy = M.List{
type=DivertRule,
ui_name='Transparent proxy',
ui_member='Transparent proxy rule'
}
-AWall['clamp-mss'] = M.Collection{
+AWall['clamp-mss'] = M.List{
type=ClampMSSRule, ui_name='MSS clamping', ui_member='MSS clamping rule'
}
-AWall['no-track'] = M.Collection{
+AWall['no-track'] = M.List{
type=Rule, ui_name='CT bypass', ui_member='Connection tracking bypass rule'
}
-AWall.ipset = M.Collection{
- type=IPSet, ui_name='IP sets', ui_member='IP set'
-}
+AWall.ipset = M.Collection{type=IPSet, ui_name='IP sets', ui_member='IP set'}
M.register(
'awall',
diff --git a/acf/modules/net.lua b/acf/modules/net.lua
index c21e4e7..8e814aa 100644
--- a/acf/modules/net.lua
+++ b/acf/modules/net.lua
@@ -10,15 +10,15 @@ local M = require('acf.model')
local Host = M.new()
Host.address = M.net.IPAddress{addr='ipaddr'}
Host.canonical = M.String{ui_name='Canonical name'}
-Host.alias = M.Collection{type=M.String, ui_name='Aliases', ui_member='Alias'}
+Host.alias = M.Set{type=M.String, ui_name='Aliases', ui_member='Alias'}
local Resolv = M.new()
-Resolv.servers = M.Collection{type=M.net.IPAddress, addr='nameserver'}
-Resolv['search-domains'] = M.Collection{type=M.String, addr='search/domain'}
+Resolv.servers = M.List{type=M.net.IPAddress, addr='nameserver'}
+Resolv['search-domains'] = M.List{type=M.String, addr='search/domain'}
local Net = M.new()
Net['host-name'] = M.String{addr='/augeas/etc/hostname/hostname'}
-Net.hosts = M.Collection{type=Host, addr='/augeas/etc/hosts'}
+Net.hosts = M.List{type=Host, addr='/augeas/etc/hosts'}
Net.resolver = M.Model{
model=Resolv, addr='/augeas/etc/resolv.conf', ui_name='DNS resolver'
}
diff --git a/dev-shell b/dev-shell
index e3bc936..b36cb4f 100755
--- a/dev-shell
+++ b/dev-shell
@@ -65,12 +65,12 @@ EOF
else
cat >&2 <<EOF
Available commands:
- Fetch object: get <path>
- Create/update object: put <path> <JSON content>
- Add member to set/perform action: post <path> <JSON content>
- Delete object: delete <path>
- Fetch metadata: meta <path>
- Start transaction: start
+ Fetch object: get <path>
+ Create/update object: put <path> <JSON content>
+ Add member to list/set or perform action: post <path> <JSON content>
+ Delete object: delete <path>
+ Fetch metadata: meta <path>
+ Start transaction: start
Example: put /awall/zone/internet '{"iface": ["eth0"]}'
diff --git a/protocol.txt b/protocol.txt
index 9a09511..523b474 100644
--- a/protocol.txt
+++ b/protocol.txt
@@ -36,10 +36,12 @@ resp: JSON object, with the following attributes:
data: JSON serialization of object
- primitive types as JSON primitives
- references as path names (relative to scope)
- - models, collections, and sets as JSON objects or arrays
- with members as attributes:
+ - models as JSON objects with members as attributes:
- primitive members as JSON primitives
- - reference, model, and collection members as path names
+ - reference, model, collection, list, and set members as
+ path names
+ - collections, lists, and sets as JSON arrays with members
+ as attributes (as for models)
meta: JSON object, with the following attributes
- name (last component of path name)
- ui-name (shown to user)
@@ -69,6 +71,12 @@ req: PUT /config/<obj_path>
in GET responses
- undefined model attributes are deleted
+Add object to a list:
+req: POST /config/<list_path>
+ - body is a JSON object with the following attributes:
+ data: object to be added, serialized as in GET responses
+ index: position, defaults to the end of the list
+
Delete object:
req: DELETE /config/<obj_path>
diff --git a/server.lua b/server.lua
index d03634b..b9c22fd 100644
--- a/server.lua
+++ b/server.lua
@@ -167,11 +167,17 @@ return function(env)
if method == 'POST' then
local obj = txn:search(path)
- if acf.object.isinstance(obj, acf.model.set.Set) then
+ if acf.object.isinstance(obj, mnode.List) then
if not mnode.has_permission(obj, user, 'create') then
return 403
end
- acf.model.set.add(obj, data)
+
+ local index
+ if not acf.object.isinstance(obj, mnode.Set) then
+ index = data.index
+ data = data.data
+ end
+ mnode.insert(obj, data, index)
elseif type(obj) == 'function' then
if not mnode.has_permission(parent, user, name) then
diff --git a/web/client.js b/web/client.js
index 6e25c63..61e1e62 100644
--- a/web/client.js
+++ b/web/client.js
@@ -153,7 +153,9 @@ $(function() {
var path = $.param.fragment();
function isTreeNode(meta) {
- return _.contains(["collection", "model", "set"], meta.type);
+ return _.contains(
+ ["collection", "list", "model", "set"], meta.type
+ );
}
function split(path) {