src/web/web.lua

Tue, 09 Mar 2021 12:16:56 +0000

author
Matthew Wild <mwild1@gmail.com>
date
Tue, 09 Mar 2021 12:16:56 +0000
changeset 0
6279a7d40ae7
child 17
b284dc4816cd
permissions
-rw-r--r--

Initial commit

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

mercurial