Thu, 22 Jun 2023 21:31:36 +0100
web: Add a few new helper functions
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 | ||
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 | 63 | -- Cookies |
64 | ||
65 | local function add_header(headers, header, value) | |
66 | if headers[header] then | |
67 | headers[header] = headers[header] .. ", " .. value; | |
68 | else | |
69 | headers[header] = value; | |
70 | end | |
71 | end | |
72 | ||
73 | local function prefix_header(headers, header, value) | |
74 | if headers[header] then | |
75 | headers[header] = value .. ", " .. headers[header]; | |
76 | else | |
77 | headers[header] = value; | |
78 | end | |
79 | end | |
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 | 96 | local function set_cookie(headers, cookie, opts) |
97 | if opts then | |
98 | local params = {""}; | |
99 | if opts.path then | |
100 | table.insert(params, "Path="..opts.path); | |
101 | end | |
102 | if opts.ttl then | |
103 | table.insert(params, ("Max-Age=%d"):format(opts.ttl)); | |
104 | end | |
105 | if opts.http_only then | |
106 | table.insert(params, "HttpOnly"); | |
107 | end | |
108 | if opts.secure then | |
109 | table.insert(params, "Secure"); | |
110 | end | |
111 | ||
112 | if #params > 1 then | |
113 | cookie = cookie .. table.concat(params, "; "); | |
114 | end | |
115 | end | |
116 | ||
117 | prefix_header(headers, "set_cookie", cookie); | |
118 | end | |
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 | 133 | return { |
134 | unpack_cookies = unpack_cookies; | |
135 | validate_csrf = validate_csrf; | |
136 | parse_body_and_csrf = parse_body_and_csrf; | |
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 | 139 | add_header = add_header; |
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 | 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 | 146 | }; |