src/http.lua

changeset 12
dfa7cb60647e
parent 0
6279a7d40ae7
child 16
68a0c983bf49
equal deleted inserted replaced
11:635b385df3a2 12:dfa7cb60647e
13 local templates; 13 local templates;
14 local config; 14 local config;
15 15
16 local log = require "util.logger".init("web"); 16 local log = require "util.logger".init("web");
17 17
18 local usercookie = require"util.usercookie";
19 local secret = uuid.generate();
20
21 local check_auth = require "app.auth".check_auth;
22
23 local function set_auth_cookie(username, response)
24 local expires = config.cookie_ttl or 604800;
25 local cookie = usercookie.generate(username, os.time()+expires, secret);
26 cookie = "remember=".. cookie .. "; Path="..config.base_path
27 .."; Max-Age="..tostring(expires).."; HttpOnly";
28 return web.set_cookie(response.headers, cookie);
29 end
30
31 local csrf_token_len = #uuid.generate(); 18 local csrf_token_len = #uuid.generate();
32 19
33 local function check_csrf(event, viewdata) 20 local function check_csrf(event, viewdata)
34 local request, response = event.request, event.response; 21 local request, response = event.request, event.response;
35 web.unpack_cookies(request); 22 web.unpack_cookies(request);
42 viewdata.csrf_token = csrf_token; 29 viewdata.csrf_token = csrf_token;
43 web.set_cookie(response.headers, "csrf_token=" .. csrf_token .. "; Path="..config.base_path.."; HttpOnly"); 30 web.set_cookie(response.headers, "csrf_token=" .. csrf_token .. "; Path="..config.base_path.."; HttpOnly");
44 end 31 end
45 end 32 end
46 33
47 local function wrap_handler(f, t, path_prefix_len) 34 local function wrap_handler(f, default_tpl, path_prefix_len, check_auth)
48 return function (event) 35 return function (event)
49 log("debug", "Check auth..."); 36 log("debug", "Check auth...");
50 local authed = check_auth(event.request, config); 37 local request = event.request;
38 local authed, handler_override = check_auth(request, config);
39 if authed and not request.authenticated then
40 request.authenticated = authed;
41 end
51 log("debug", "Checked, %s", authed); 42 log("debug", "Checked, %s", authed);
52 event.config = config; 43 event.config = config;
53 local sub_path = nil; 44 local sub_path = nil;
54 if path_prefix_len then 45 if path_prefix_len then
55 sub_path = event.request.path:sub(path_prefix_len+2); 46 sub_path = event.request.path:sub(path_prefix_len+2);
56 end 47 end
57 local p = f(event, sub_path); 48
58 return promise.resolve(p):next(function (resp) 49 local h = handler_override or f;
59 if type(resp) == "table" then 50
51 local p, custom_tpl;
52 if web.is_response(h) then
53 p = h;
54 else
55 p, custom_tpl = (handler_override or f)(event, sub_path);
56 end
57
58 -- Process response (may be promises)
59 return promise.join(function (resp, tpl)
60 if type(resp) == "table" and not web.is_response(resp) then
60 local headers = event.response.headers; 61 local headers = event.response.headers;
61 local accept = event.request.headers.accept or ""; 62 local accept = event.request.headers.accept or "";
62 web.add_header(headers, "vary", "Accept, Cookie"); 63 web.add_header(headers, "vary", "Accept, Cookie");
63 if accept:find"application/json" then 64 if accept:find"application/json" then
64 headers.content_type = "application/json"; 65 headers.content_type = "application/json";
65 return json.encode(resp); 66 return json.encode(resp);
66 elseif t then 67 elseif tpl then
67 if authed or event.needs_csrf then check_csrf(event, resp); end 68 if authed or event.needs_csrf then check_csrf(event, resp); end
68 resp.authenticated = event.request.authenticated; 69 resp.authenticated = event.request.authenticated;
69 resp.config = config; 70 resp.config = config;
70 resp = render(t, resp); 71 resp = render(tpl, resp);
71 if not headers.content_type then 72 if not headers.content_type then
72 headers.content_type = "text/html; charset=utf-8"; 73 headers.content_type = "text/html; charset=utf-8";
73 end 74 end
74 else 75 else
75 return resp; 76 return resp;
76 end 77 end
77 end 78 end
78 return resp; 79 return resp;
79 end):catch(function (err) 80 end, p, custom_tpl or default_tpl):catch(function (err)
80 log("error", "Failed inside handler wrapper: %s", json.encode(err)); 81 log("error", "Failed inside handler wrapper: %s", json.encode(err));
81 return promise.reject(errors.wrap(err)); 82 return promise.reject(errors.wrap(err));
82 end); 83 end);
83 end 84 end
84 end 85 end
131 event.send = size_only; 132 event.send = size_only;
132 return handler(event); 133 return handler(event);
133 end 134 end
134 end 135 end
135 136
136 local function register_handlers(handlers, event_base, path_prefix) 137 local function register_handlers(handlers, event_base, path_prefix, check_auth)
137 for method_path, handler in pairs(handlers) do 138 for method_path, handler in pairs(handlers) do
138 if type(handler) == "table" then 139 if type(handler) == "table" then
139 register_handlers(handler, event_base, (path_prefix and (path_prefix.."/") or "")..method_path); 140 register_handlers(handler, event_base, (path_prefix and (path_prefix.."/") or "")..method_path, check_auth);
140 else 141 else
141 local method, path, wildcard = method_path:match"^(%w+)_(.-)(_?)$"; 142 local method, path, wildcard = method_path:match"^(%w+)_(.-)(_?)$";
142 method = method:upper(); 143 method = method:upper();
143 wildcard = wildcard == "_" and (path == "" and "*" or "/*") or ""; 144 wildcard = wildcard == "_" and (path == "" and "*" or "/*") or "";
144 local template_name = path; 145 local template_name = path;
159 local path_prefix_len; 160 local path_prefix_len;
160 if wildcard == "/*" then 161 if wildcard == "/*" then
161 path_prefix_len = #path+1; 162 path_prefix_len = #path+1;
162 end 163 end
163 164
164 handler = wrap_handler(handler, templates[template_name], path_prefix_len); 165 handler = wrap_handler(handler, templates[template_name], path_prefix_len, check_auth);
165 local head_event = event_base:format("HEAD", path, wildcard); 166 local head_event = event_base:format("HEAD", path, wildcard);
166 http_server.add_handler(head_event, head_handler(handler)); 167 http_server.add_handler(head_event, head_handler(handler));
167 168
168 local event_name = event_base:format(method, path, wildcard); 169 local event_name = event_base:format(method, path, wildcard);
169 http_server.add_handler(event_name, handler); 170 http_server.add_handler(event_name, handler);
179 180
180 local base_url = url.parse(config.base_url or "http://localhost:8006/"); 181 local base_url = url.parse(config.base_url or "http://localhost:8006/");
181 local base_path = url.parse_path(config.base_path or base_url.path or "/"); 182 local base_path = url.parse_path(config.base_path or base_url.path or "/");
182 base_path.is_directory = true; 183 base_path.is_directory = true;
183 base_url.path = url.build_path(base_path); 184 base_url.path = url.build_path(base_path);
185 config.templates = templates;
184 config.base_host = base_url.host; 186 config.base_host = base_url.host;
185 config.base_path = base_url.path; 187 config.base_path = base_url.path;
186 config.base_url = url.build(base_url); 188 config.base_url = url.build(base_url);
187 local event_base = ("%%s %s%s%%s%%s"):format(base_url.host, base_url.path); 189 local event_base = ("%%s %s%s%%s%%s"):format(base_url.host, base_url.path);
188 190
191 _G.CONFIG = config;
192 local check_auth = require "app.auth".check_auth;
189 local handlers = require "app.routes"; 193 local handlers = require "app.routes";
190 194
191 handlers.get_static_ = files.serve { path = "./static", http_base = base_url.path.."static" }; 195 handlers.get_static_ = files.serve { path = "./static", http_base = base_url.path.."static" };
192 196
193 http_server.add_host(base_url.host); 197 http_server.add_host(base_url.host);
194 http_server.set_default_host(base_url.host); 198 http_server.set_default_host(base_url.host);
195 http_server.add_handler("http-error", function (error) 199 http_server.add_handler("http-error", function (error)
196 return handle_error(error, config) 200 return handle_error(error, config)
197 end); 201 end);
198 202
199 register_handlers(handlers, event_base); 203 register_handlers(handlers, event_base, nil, check_auth);
200 204
201 local listen_port = config.listen_port or 8007; 205 local listen_port = config.listen_port or 8007;
202 local listen_interface = config.listen_interface or "*"; 206 local listen_interface = config.listen_interface or "*";
203 assert(http_server.listen_on(listen_port, listen_interface)); 207 assert(http_server.listen_on(listen_port, listen_interface));
204 log("info", "Serving web interface on http://localhost:%d/", listen_port); 208 log("info", "Serving web interface on http://localhost:%d/", listen_port);
205 end 209 end
206 210
207 return { 211 return {
208 init = init; 212 init = init;
209 render = render; 213 render = render;
214 set_auth_cookie = set_auth_cookie;
215 get_auth_cookie = get_auth_cookie;
210 } 216 }

mercurial