src/web/web.lua

changeset 0
6279a7d40ae7
child 17
b284dc4816cd
equal deleted inserted replaced
-1:000000000000 0:6279a7d40ae7
1 local http_util = require "util.http";
2 local usercookie = require"util.usercookie";
3 local uuid = require "util.uuid";
4
5 local function unpack_cookies(request)
6 if not request.cookies then
7 request.cookies = usercookie.decode(request.headers.cookie);
8 end
9 end
10
11 local post_parsers = {
12 ["application/x-www-form-urlencoded"] = http_util.formdecode;
13 };
14
15 local function parse_body(request)
16 local content_type = request.headers.content_type;
17 if not content_type then
18 --log("warn", "No Content-Type header sent");
19 return nil, 400;
20 end
21 local post_parser = post_parsers[content_type];
22 if not post_parser then
23 --log("warn", "Don't know how to parse %s", content_type);
24 return nil, 415;
25 end
26 local post_body = post_parser(request.body);
27 if type(post_body) ~= "table" then
28 --log("warn", "Could not parse %s %q, got %s", content_type, request.body, type(post_body));
29 return nil, 415;
30 end
31 return post_body;
32 end
33
34 local csrf_token_len = #uuid.generate();
35
36 local function validate_csrf(csrf_token, request)
37 if request.headers.origin == nil and request.headers.referer == nil then
38 return true; -- Probably a non-browser request
39 end
40 if not (csrf_token and #csrf_token == csrf_token_len) then
41 return false;
42 end
43 unpack_cookies(request);
44 return request.cookies.csrf_token == csrf_token;
45 end
46
47 local function parse_body_and_csrf(request)
48 local post_body, err = parse_body(request);
49 if not post_body then return nil, err; end
50 if not validate_csrf(post_body.csrf_token, request) then
51 --log("warn", "CSRF error (token: '%s', cookie: '%s')",
52 -- tostring(post_body.csrf_token), tostring(request.headers.cookie));
53 return nil, 400;
54 end
55 return post_body;
56 end
57
58 -- Cookies
59
60 local function add_header(headers, header, value)
61 if headers[header] then
62 headers[header] = headers[header] .. ", " .. value;
63 else
64 headers[header] = value;
65 end
66 end
67
68 local function prefix_header(headers, header, value)
69 if headers[header] then
70 headers[header] = value .. ", " .. headers[header];
71 else
72 headers[header] = value;
73 end
74 end
75
76 local function set_cookie(headers, cookie, opts)
77 if opts then
78 local params = {""};
79 if opts.path then
80 table.insert(params, "Path="..opts.path);
81 end
82 if opts.ttl then
83 table.insert(params, ("Max-Age=%d"):format(opts.ttl));
84 end
85 if opts.http_only then
86 table.insert(params, "HttpOnly");
87 end
88 if opts.secure then
89 table.insert(params, "Secure");
90 end
91
92 if #params > 1 then
93 cookie = cookie .. table.concat(params, "; ");
94 end
95 end
96
97 prefix_header(headers, "set_cookie", cookie);
98 end
99
100 return {
101 unpack_cookies = unpack_cookies;
102 validate_csrf = validate_csrf;
103 parse_body_and_csrf = parse_body_and_csrf;
104 parse_body = parse_body;
105 add_header = add_header;
106 prefix_header = prefix_header;
107 set_cookie = set_cookie;
108 };

mercurial