summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKaarle Ritvanen <kaarle.ritvanen@datakunkku.fi>2013-02-27 07:39:26 +0000
committerKaarle Ritvanen <kaarle.ritvanen@datakunkku.fi>2013-03-01 08:07:36 +0200
commit544ea51c3663070dadd588d9de3e22d6e045743e (patch)
tree913c45a5125939bd135a6d231eb1ca9a7ad56723
parenta3eea9d655584ddecb147ffd4686b561a03d344c (diff)
downloadaconf-544ea51c3663070dadd588d9de3e22d6e045743e.tar.bz2
aconf-544ea51c3663070dadd588d9de3e22d6e045743e.tar.xz
basic login session management
-rw-r--r--LICENSE2
-rw-r--r--acf/modules/aaa.lua3
-rw-r--r--config/aaa.json2
-rw-r--r--protocol.txt10
-rw-r--r--server.lua76
5 files changed, 74 insertions, 19 deletions
diff --git a/LICENSE b/LICENSE
index a0b36bf..da09525 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2012 Kaarle Ritvanen
+Copyright (c) 2012-2013 Kaarle Ritvanen
All rights reserved.
diff --git a/acf/modules/aaa.lua b/acf/modules/aaa.lua
index f8e28ca..1704376 100644
--- a/acf/modules/aaa.lua
+++ b/acf/modules/aaa.lua
@@ -1,5 +1,5 @@
--[[
-Copyright (c) 2012 Kaarle Ritvanen
+Copyright (c) 2012-2013 Kaarle Ritvanen
See LICENSE file for license details
--]]
@@ -14,6 +14,7 @@ User = M.new()
User.password = M.String
User.real_name = M.String
User.roles = M.Collection{type=M.Reference{scope='../../roles'}}
+function User:check_password(password) return password == self.password end
Authentication = M.new()
Authentication.users = M.Collection{type=User}
diff --git a/config/aaa.json b/config/aaa.json
index 0967ef4..248bd32 100644
--- a/config/aaa.json
+++ b/config/aaa.json
@@ -1 +1 @@
-{}
+{"users":{"admin":{"password":"admin"}}} \ No newline at end of file
diff --git a/protocol.txt b/protocol.txt
index 7e0f3c2..018006a 100644
--- a/protocol.txt
+++ b/protocol.txt
@@ -4,6 +4,16 @@ ACF2 HTTP Protocol
Load JavaScript client:
req: GET /
+Log in:
+req: POST /login
+ - body is a JSON object with username and password attributes
+resp: authentication token (in header as X-ACF-Auth-Token)
+ - use X-ACF-Auth-Token in the header of subsequent requests
+
+Log out:
+req: DELETE /login
+ X-ACF-Auth-Token: <token>
+
Start transaction:
req: POST /
resp: txn ID (in header as X-ACF-Transaction-ID)
diff --git a/server.lua b/server.lua
index 11bfe55..c6fe586 100644
--- a/server.lua
+++ b/server.lua
@@ -1,5 +1,5 @@
--[[
-Copyright (c) 2012 Kaarle Ritvanen
+Copyright (c) 2012-2013 Kaarle Ritvanen
See LICENSE file for license details
--]]
@@ -10,13 +10,7 @@ require 'json'
require 'stringy'
-local function handle(env, txn, path)
- local method = env.REQUEST_METHOD
- local data
- if env.CONTENT_LENGTH then
- data = json.decode(env.input:read(env.CONTENT_LENGTH))
- end
-
+local function handle(txn, method, path, data)
local parent, name
if path ~= '/' then
parent = txn:search(acf.path.parent(path))
@@ -60,30 +54,80 @@ local function handle(env, txn, path)
end
+-- TODO shared storage for login sessions
+local last_sid = 0
+local sessions = {}
+
-- TODO implement transactions as threads or store their state in
-- shared storage
-local last_id = 0
+local last_txn_id = 0
local txns = {}
+-- TODO expire stale sessions and transactions
+
return function(env)
local method = env.REQUEST_METHOD
local path = env.REQUEST_URI
- -- TODO login session management
+ local data
+ if env.CONTENT_LENGTH then
+ data = json.decode(env.input:read(env.CONTENT_LENGTH))
+ end
+
+ local sid = tonumber(env.HTTP_X_ACF_AUTH_TOKEN)
+ local user, txn_id
+ if sid then
+ user = sessions[sid]
+ if not user then return 401 end
+ txn_id = tonumber(env.HTTP_X_ACF_TRANSACTION_ID)
+ end
- local txn_id = tonumber(env.HTTP_X_ACF_TRANSACTION_ID)
local txn
if txn_id then
txn = txns[txn_id]
if not txn then return 404 end
else txn = acf.transaction.start() end
+ local function fetch_user(name)
+ user = name and txn:search('/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)
+ if user and user:check_password(data.password) then
+ last_sid = last_sid + 1
+ local sid = last_sid
+ sessions[sid] = data.username
+ return 200, {['X-ACF-Auth-Token']=sid}
+ end
+ return 401
+ end
+
+ if not user then return 401 end
+
+ if method == 'DELETE' then
+ sessions[sid] = nil
+ return 200
+ end
+
+ return 405
+ end
+
+ if not user then return 401 end
+
if stringy.startswith(path, '/config/') then
-- TODO catch and forward relevant errors to the client
- local code, hdr, body = handle(env,
- txn,
- string.sub(path, 8, -1))
+ local code, hdr, body = handle(txn,
+ method,
+ string.sub(path, 8, -1),
+ data)
if not txn_id and method ~= 'GET' and code == 200 then
txn:commit()
end
@@ -105,8 +149,8 @@ return function(env)
if method == 'DELETE' then return 405 end
- last_id = last_id + 1
- local txn_id = last_id
+ last_txn_id = last_txn_id + 1
+ local txn_id = last_txn_id
txns[txn_id] = txn
return 200, {['X-ACF-Transaction-ID']=txn_id}
end