From 7afdb563b208459bb308c7ffc4f671ee23b0ca26 Mon Sep 17 00:00:00 2001 From: Kaarle Ritvanen Date: Fri, 31 May 2013 14:05:17 +0300 Subject: new data type: List --- acf/model/init.lua | 10 +++++++--- acf/model/node.lua | 31 +++++++++++++++++++++++++++++++ acf/model/permission.lua | 4 ++-- acf/model/set.lua | 34 ++++++++++++---------------------- acf/modules/awall.lua | 44 ++++++++++++++++++++------------------------ acf/modules/net.lua | 8 ++++---- dev-shell | 12 ++++++------ protocol.txt | 14 +++++++++++--- server.lua | 10 ++++++++-- web/client.js | 4 +++- 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 < - Create/update object: put - Add member to set/perform action: post - Delete object: delete - Fetch metadata: meta - Start transaction: start + Fetch object: get + Create/update object: put + Add member to list/set or perform action: post + Delete object: delete + Fetch metadata: meta + 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/ in GET responses - undefined model attributes are deleted +Add object to a list: +req: POST /config/ + - 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/ 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) { -- cgit v1.2.3