Wed, 24 Nov 2021 16:18:19 +0000
Added tag v2.1 for changeset e5f706093df7
0 | 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 | }; |