src/web/web.lua

Thu, 22 Jun 2023 21:31:36 +0100

author
Matthew Wild <mwild1@gmail.com>
date
Thu, 22 Jun 2023 21:31:36 +0100
changeset 17
b284dc4816cd
parent 0
6279a7d40ae7
permissions
-rw-r--r--

web: Add a few new helper functions

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
17
b284dc4816cd web: Add a few new helper functions
Matthew Wild <mwild1@gmail.com>
parents: 0
diff changeset
58 local function parse_query(request)
b284dc4816cd web: Add a few new helper functions
Matthew Wild <mwild1@gmail.com>
parents: 0
diff changeset
59 local q = request.url.query;
b284dc4816cd web: Add a few new helper functions
Matthew Wild <mwild1@gmail.com>
parents: 0
diff changeset
60 return q and http_util.formdecode(q) or nil;
b284dc4816cd web: Add a few new helper functions
Matthew Wild <mwild1@gmail.com>
parents: 0
diff changeset
61 end
b284dc4816cd web: Add a few new helper functions
Matthew Wild <mwild1@gmail.com>
parents: 0
diff changeset
62
0
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
63 -- Cookies
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
64
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
65 local function add_header(headers, header, value)
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
66 if headers[header] then
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
67 headers[header] = headers[header] .. ", " .. value;
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
68 else
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
69 headers[header] = value;
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
70 end
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
71 end
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
72
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
73 local function prefix_header(headers, header, value)
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
74 if headers[header] then
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
75 headers[header] = value .. ", " .. headers[header];
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
76 else
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
77 headers[header] = value;
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
78 end
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
79 end
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
80
17
b284dc4816cd web: Add a few new helper functions
Matthew Wild <mwild1@gmail.com>
parents: 0
diff changeset
81 local response_mt = {};
b284dc4816cd web: Add a few new helper functions
Matthew Wild <mwild1@gmail.com>
parents: 0
diff changeset
82
b284dc4816cd web: Add a few new helper functions
Matthew Wild <mwild1@gmail.com>
parents: 0
diff changeset
83 local function redirect(to, code)
b284dc4816cd web: Add a few new helper functions
Matthew Wild <mwild1@gmail.com>
parents: 0
diff changeset
84 return setmetatable({
b284dc4816cd web: Add a few new helper functions
Matthew Wild <mwild1@gmail.com>
parents: 0
diff changeset
85 status_code = code or 303;
b284dc4816cd web: Add a few new helper functions
Matthew Wild <mwild1@gmail.com>
parents: 0
diff changeset
86 headers = {
b284dc4816cd web: Add a few new helper functions
Matthew Wild <mwild1@gmail.com>
parents: 0
diff changeset
87 Location = to;
b284dc4816cd web: Add a few new helper functions
Matthew Wild <mwild1@gmail.com>
parents: 0
diff changeset
88 }
b284dc4816cd web: Add a few new helper functions
Matthew Wild <mwild1@gmail.com>
parents: 0
diff changeset
89 }, response_mt);
b284dc4816cd web: Add a few new helper functions
Matthew Wild <mwild1@gmail.com>
parents: 0
diff changeset
90 end
b284dc4816cd web: Add a few new helper functions
Matthew Wild <mwild1@gmail.com>
parents: 0
diff changeset
91
b284dc4816cd web: Add a few new helper functions
Matthew Wild <mwild1@gmail.com>
parents: 0
diff changeset
92 local function is_response(obj)
b284dc4816cd web: Add a few new helper functions
Matthew Wild <mwild1@gmail.com>
parents: 0
diff changeset
93 return getmetatable(obj) == response_mt;
b284dc4816cd web: Add a few new helper functions
Matthew Wild <mwild1@gmail.com>
parents: 0
diff changeset
94 end
b284dc4816cd web: Add a few new helper functions
Matthew Wild <mwild1@gmail.com>
parents: 0
diff changeset
95
0
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
96 local function set_cookie(headers, cookie, opts)
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
97 if opts then
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
98 local params = {""};
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
99 if opts.path then
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
100 table.insert(params, "Path="..opts.path);
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
101 end
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
102 if opts.ttl then
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
103 table.insert(params, ("Max-Age=%d"):format(opts.ttl));
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
104 end
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
105 if opts.http_only then
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
106 table.insert(params, "HttpOnly");
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
107 end
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
108 if opts.secure then
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
109 table.insert(params, "Secure");
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
110 end
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
111
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
112 if #params > 1 then
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
113 cookie = cookie .. table.concat(params, "; ");
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
114 end
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
115 end
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
116
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
117 prefix_header(headers, "set_cookie", cookie);
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
118 end
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
119
17
b284dc4816cd web: Add a few new helper functions
Matthew Wild <mwild1@gmail.com>
parents: 0
diff changeset
120 local function set_auth_cookie(username, response, secret)
b284dc4816cd web: Add a few new helper functions
Matthew Wild <mwild1@gmail.com>
parents: 0
diff changeset
121 local expires = config.cookie_ttl or 604800;
b284dc4816cd web: Add a few new helper functions
Matthew Wild <mwild1@gmail.com>
parents: 0
diff changeset
122 local cookie = usercookie.generate(username, os.time()+expires, secret);
b284dc4816cd web: Add a few new helper functions
Matthew Wild <mwild1@gmail.com>
parents: 0
diff changeset
123 cookie = "__Host-auth=".. cookie .. "; Path="..config.base_path
b284dc4816cd web: Add a few new helper functions
Matthew Wild <mwild1@gmail.com>
parents: 0
diff changeset
124 .."; Max-Age="..tostring(expires).."; Secure; HttpOnly";
b284dc4816cd web: Add a few new helper functions
Matthew Wild <mwild1@gmail.com>
parents: 0
diff changeset
125 return set_cookie(response.headers, cookie);
b284dc4816cd web: Add a few new helper functions
Matthew Wild <mwild1@gmail.com>
parents: 0
diff changeset
126 end
b284dc4816cd web: Add a few new helper functions
Matthew Wild <mwild1@gmail.com>
parents: 0
diff changeset
127
b284dc4816cd web: Add a few new helper functions
Matthew Wild <mwild1@gmail.com>
parents: 0
diff changeset
128 local function verify_auth_cookie(request, secret)
b284dc4816cd web: Add a few new helper functions
Matthew Wild <mwild1@gmail.com>
parents: 0
diff changeset
129 unpack_cookies(request);
b284dc4816cd web: Add a few new helper functions
Matthew Wild <mwild1@gmail.com>
parents: 0
diff changeset
130 request.cookies.auth = usercookie.verify(request.cookies["__Host-auth"], secret);
b284dc4816cd web: Add a few new helper functions
Matthew Wild <mwild1@gmail.com>
parents: 0
diff changeset
131 end
b284dc4816cd web: Add a few new helper functions
Matthew Wild <mwild1@gmail.com>
parents: 0
diff changeset
132
0
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
133 return {
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
134 unpack_cookies = unpack_cookies;
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
135 validate_csrf = validate_csrf;
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
136 parse_body_and_csrf = parse_body_and_csrf;
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
137 parse_body = parse_body;
17
b284dc4816cd web: Add a few new helper functions
Matthew Wild <mwild1@gmail.com>
parents: 0
diff changeset
138 parse_query = parse_query;
0
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
139 add_header = add_header;
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
140 prefix_header = prefix_header;
17
b284dc4816cd web: Add a few new helper functions
Matthew Wild <mwild1@gmail.com>
parents: 0
diff changeset
141 redirect = redirect;
0
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
142 set_cookie = set_cookie;
17
b284dc4816cd web: Add a few new helper functions
Matthew Wild <mwild1@gmail.com>
parents: 0
diff changeset
143 set_auth_cookie = set_auth_cookie;
b284dc4816cd web: Add a few new helper functions
Matthew Wild <mwild1@gmail.com>
parents: 0
diff changeset
144 verify_auth_cookie = verify_auth_cookie;
b284dc4816cd web: Add a few new helper functions
Matthew Wild <mwild1@gmail.com>
parents: 0
diff changeset
145 is_response = is_response;
0
6279a7d40ae7 Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
146 };

mercurial