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 } |