Comparison

plugins/mod_http_file_share.lua @ 12708:9953ac7b0c15

mod_http_file_share: Switch to new util.jwt API Some changes/improvements in this commit: - Default token lifetime is now 3600s (from 300s) - Tokens are only validated once per upload - "iat"/"exp" are handled automatically by util.jwt
author Matthew Wild <mwild1@gmail.com>
date Mon, 11 Jul 2022 13:49:47 +0100
parent 12444:b33558969b3e
child 12722:cd993fd7b60d
comparison
equal deleted inserted replaced
12707:f75235110045 12708:9953ac7b0c15
10 local t_insert = table.insert; 10 local t_insert = table.insert;
11 local jid = require "util.jid"; 11 local jid = require "util.jid";
12 local st = require "util.stanza"; 12 local st = require "util.stanza";
13 local url = require "socket.url"; 13 local url = require "socket.url";
14 local dm = require "core.storagemanager".olddm; 14 local dm = require "core.storagemanager".olddm;
15 local jwt = require "util.jwt";
16 local errors = require "util.error"; 15 local errors = require "util.error";
17 local dataform = require "util.dataforms".new; 16 local dataform = require "util.dataforms".new;
18 local urlencode = require "util.http".urlencode; 17 local urlencode = require "util.http".urlencode;
19 local dt = require "util.datetime"; 18 local dt = require "util.datetime";
20 local hi = require "util.human.units"; 19 local hi = require "util.human.units";
41 local file_types = module:get_option_set(module.name .. "_allowed_file_types", {}); 40 local file_types = module:get_option_set(module.name .. "_allowed_file_types", {});
42 local safe_types = module:get_option_set(module.name .. "_safe_file_types", {"image/*","video/*","audio/*","text/plain"}); 41 local safe_types = module:get_option_set(module.name .. "_safe_file_types", {"image/*","video/*","audio/*","text/plain"});
43 local expiry = module:get_option_number(module.name .. "_expires_after", 7 * 86400); 42 local expiry = module:get_option_number(module.name .. "_expires_after", 7 * 86400);
44 local daily_quota = module:get_option_number(module.name .. "_daily_quota", file_size_limit*10); -- 100 MB / day 43 local daily_quota = module:get_option_number(module.name .. "_daily_quota", file_size_limit*10); -- 100 MB / day
45 local total_storage_limit = module:get_option_number(module.name.."_global_quota", unlimited); 44 local total_storage_limit = module:get_option_number(module.name.."_global_quota", unlimited);
45
46 local create_jwt, verify_jwt = require "util.jwt".init("HS256", secret);
46 47
47 local access = module:get_option_set(module.name .. "_access", {}); 48 local access = module:get_option_set(module.name .. "_access", {});
48 49
49 if not external_base_url then 50 if not external_base_url then
50 module:depends("http"); 51 module:depends("http");
167 168
168 return true; 169 return true;
169 end 170 end
170 171
171 function get_authz(slot, uploader, filename, filesize, filetype) 172 function get_authz(slot, uploader, filename, filesize, filetype)
172 local now = os.time(); 173 return create_jwt({
173 return jwt.sign(secret, {
174 -- token properties 174 -- token properties
175 sub = uploader; 175 sub = uploader;
176 iat = now;
177 exp = now+300;
178 176
179 -- slot properties 177 -- slot properties
180 slot = slot; 178 slot = slot;
181 expires = expiry >= 0 and (now+expiry) or nil; 179 expires = expiry >= 0 and (os.time()+expiry) or nil;
182 -- file properties 180 -- file properties
183 filename = filename; 181 filename = filename;
184 filesize = filesize; 182 filesize = filesize;
185 filetype = filetype; 183 filetype = filetype;
186 }); 184 });
247 return true; 245 return true;
248 end 246 end
249 247
250 function handle_upload(event, path) -- PUT /upload/:slot 248 function handle_upload(event, path) -- PUT /upload/:slot
251 local request = event.request; 249 local request = event.request;
252 local authz = request.headers.authorization; 250 local upload_info = request.http_file_share_upload_info;
253 if authz then 251
254 authz = authz:match("^Bearer (.*)") 252 if not upload_info then -- Initial handling of request
255 end 253 local authz = request.headers.authorization;
256 if not authz then 254 if authz then
257 module:log("debug", "Missing or malformed Authorization header"); 255 authz = authz:match("^Bearer (.*)")
258 event.response.headers.www_authenticate = "Bearer"; 256 end
259 return 401; 257 if not authz then
260 end 258 module:log("debug", "Missing or malformed Authorization header");
261 local authed, upload_info = jwt.verify(secret, authz); 259 event.response.headers.www_authenticate = "Bearer";
262 if not (authed and type(upload_info) == "table" and type(upload_info.exp) == "number") then 260 return 401;
263 module:log("debug", "Unauthorized or invalid token: %s, %q", authed, upload_info); 261 end
264 return 401; 262 local authed, authed_upload_info = verify_jwt(authz);
265 end 263 if not authed then
266 if not request.body_sink and upload_info.exp < os.time() then 264 module:log("debug", "Unauthorized or invalid token: %s, %q", authz, authed_upload_info);
267 module:log("debug", "Authorization token expired on %s", dt.datetime(upload_info.exp)); 265 return 401;
268 return 410; 266 end
269 end 267 if not path or upload_info.slot ~= path:match("^[^/]+") then
270 if not path or upload_info.slot ~= path:match("^[^/]+") then 268 module:log("debug", "Invalid upload slot: %q, path: %q", upload_info.slot, path);
271 module:log("debug", "Invalid upload slot: %q, path: %q", upload_info.slot, path); 269 return 400;
272 return 400; 270 end
273 end 271 if request.headers.content_length and tonumber(request.headers.content_length) ~= upload_info.filesize then
274 if request.headers.content_length and tonumber(request.headers.content_length) ~= upload_info.filesize then 272 return 413;
275 return 413; 273 -- Note: We don't know the size if the upload is streamed in chunked encoding,
276 -- Note: We don't know the size if the upload is streamed in chunked encoding, 274 -- so we also check the final file size on completion.
277 -- so we also check the final file size on completion. 275 end
276 upload_info = authed_upload_info;
277 request.http_file_share_upload_info = upload_info;
278 end 278 end
279 279
280 local filename = get_filename(upload_info.slot, true); 280 local filename = get_filename(upload_info.slot, true);
281 281
282 do 282 do