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