From 751f019580e210ff22fc1ac0eea72cece854534a Mon Sep 17 00:00:00 2001 From: Kaarle Ritvanen Date: Tue, 18 Mar 2014 00:52:30 +0200 Subject: move permission checking from server to model hide all model data and functions inaccessible to the user --- server.lua | 101 +++++++++++++++++++++---------------------------------------- 1 file changed, 34 insertions(+), 67 deletions(-) (limited to 'server.lua') diff --git a/server.lua b/server.lua index 7e6620f..bbb7798 100644 --- a/server.lua +++ b/server.lua @@ -67,7 +67,7 @@ return function(env) end end - local session, user, txn_id + local session function reset_session_expiry() session.expires = os.time() + 600 end local sid = tonumber(env.HTTP_X_ACONF_AUTH_TOKEN) @@ -75,47 +75,20 @@ return function(env) session = sessions[sid] if not session then return 401 end reset_session_expiry() - user = session.user - txn_id = tonumber(env.HTTP_X_ACONF_TRANSACTION_ID) - end - - local parent_txn - if txn_id then - parent_txn = session.txns[txn_id] - if not parent_txn then - return 400, nil, 'Invalid transaction ID' - end - end - - local function new_txn(defer_validation) - return aconf.start_txn{ - allow_commit_defer=true, - defer_validation=defer_validation, - parent=parent_txn - } - end - - local txn = new_txn(true) - - local function fetch_user(name) - user = name and txn:fetch('/auth/users')[name] - end - if user then - fetch_user(user) - if not user then return 401 end end if path == '/login' then if method == 'POST' then if not data.username or not data.password then return 401 end - fetch_user(data.username) + local user = + aconf.start_txn():fetch('/auth/users')[data.username] if user and user:check_password(data.password) then local sid repeat sid = math.floor(math.random() * 2^32) until not sessions[sid] - session = {user=data.username, last_txn_id=0, txns={}} + session = {user=user, last_txn_id=0, txns={}} reset_session_expiry() sessions[sid] = session return 204, { @@ -126,7 +99,7 @@ return function(env) return 401 end - if not user then return 401 end + if not session then return 401 end if method == 'DELETE' then sessions[sid] = nil @@ -136,7 +109,7 @@ return function(env) return 405 end - if not user then return 401 end + if not session then return 401 end if path == '/save' then if not save_req then return 404 end @@ -147,6 +120,27 @@ return function(env) local success, code, hdr, res = aconf.call( function() + local txn_id = tonumber(env.HTTP_X_ACONF_TRANSACTION_ID) + local parent_txn + + if txn_id then + parent_txn = session.txns[txn_id] + if not parent_txn then + return 400, nil, 'Invalid transaction ID' + end + end + + local function new_txn(defer_validation) + return aconf.start_txn{ + allow_commit_defer=true, + defer_validation=defer_validation, + parent=parent_txn, + user=session.user + } + end + + local txn = new_txn(true) + if stringy.startswith(path, '/meta/') then if method ~= 'GET' then return 405 end return 200, nil, txn:meta(path:sub(6, -1)) @@ -167,30 +161,25 @@ return function(env) if type(obj) == 'function' then return 404 end if isinstance(obj, mnode.TreeNode) then - if not mnode.has_permission(obj, user, 'read') then - return 403 - end - local node = {} for k, v in mnode.pairs(obj) do local readable = true if isinstance(v, mnode.TreeNode) then - readable = mnode.has_permission(v, user, 'read') v = mnode.path(v) elseif isinstance(v, mbin.Data) then v = v.path end - if readable then node[k] = v end + node[k] = v end res = {data=node, meta=mnode.meta(obj)} - elseif mnode.has_permission(parent, user, 'read') then + else if isinstance(obj, mbin.Data) then obj = obj:encode() end res = {data=obj, meta=mnode.mmeta(parent, name)} - else return 403 end + end return 200, nil, res end @@ -199,10 +188,6 @@ return function(env) local obj = txn:fetch(path) if isinstance(obj, mnode.List) then - if not mnode.has_permission(obj, user, 'create') then - return 403 - end - local index if not isinstance(obj, mnode.Set) then index = data.index @@ -210,37 +195,17 @@ return function(env) end mnode.insert(obj, data, index) - elseif type(obj) == 'function' then - if not mnode.has_permission(parent, user, name) then - return 403 - end - res = obj(data) + elseif type(obj) == 'function' then res = obj(data) else return 405 end else - local obj = parent[name] - if obj ~= nil and not isinstance(obj, mnode.TreeNode) then - obj = parent - end - if method == 'DELETE' then - if obj == nil then return 404 end - if not mnode.has_permission(obj, user, 'delete') then - return 403 - end + if parent[name] == nil then return 404 end parent[name] = nil elseif method == 'PUT' then if isinstance(parent, mnode.Set) then return 405 end - local permission = 'modify' - if obj == nil then - obj = parent - permission = 'create' - end - if not mnode.has_permission(obj, user, permission) then - return 403 - end parent[name] = data else return 405 end @@ -272,6 +237,8 @@ return function(env) if success then return code, hdr, res, true end + if code.forbidden then return 403, nil, code.forbidden, true end + if code.conflict then return 409, nil, code.conflict, true end return 422, nil, code, true -- cgit v1.2.3