aboutsummaryrefslogtreecommitdiffstats
path: root/plugins/mod_http_file_share.lua
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/mod_http_file_share.lua')
-rw-r--r--plugins/mod_http_file_share.lua108
1 files changed, 55 insertions, 53 deletions
diff --git a/plugins/mod_http_file_share.lua b/plugins/mod_http_file_share.lua
index b6200628..cfc647d4 100644
--- a/plugins/mod_http_file_share.lua
+++ b/plugins/mod_http_file_share.lua
@@ -8,17 +8,16 @@
-- Again, from the top!
local t_insert = table.insert;
-local jid = require "util.jid";
-local st = require "util.stanza";
+local jid = require "prosody.util.jid";
+local st = require "prosody.util.stanza";
local url = require "socket.url";
-local dm = require "core.storagemanager".olddm;
-local jwt = require "util.jwt";
-local errors = require "util.error";
-local dataform = require "util.dataforms".new;
-local urlencode = require "util.http".urlencode;
-local dt = require "util.datetime";
-local hi = require "util.human.units";
-local cache = require "util.cache";
+local dm = require "prosody.core.storagemanager".olddm;
+local errors = require "prosody.util.error";
+local dataform = require "prosody.util.dataforms".new;
+local urlencode = require "prosody.util.http".urlencode;
+local dt = require "prosody.util.datetime";
+local hi = require "prosody.util.human.units";
+local cache = require "prosody.util.cache";
local lfs = require "lfs";
local unknown = math.abs(0/0);
@@ -35,17 +34,21 @@ local uploads = module:open_store("uploads", "archive");
local persist_stats = module:open_store("upload_stats", "map");
-- id, <request>, time, owner
-local secret = module:get_option_string(module.name.."_secret", require"util.id".long());
+local secret = module:get_option_string(module.name.."_secret", require"prosody.util.id".long());
local external_base_url = module:get_option_string(module.name .. "_base_url");
-local file_size_limit = module:get_option_number(module.name .. "_size_limit", 10 * 1024 * 1024); -- 10 MB
+local file_size_limit = module:get_option_integer(module.name .. "_size_limit", 10 * 1024 * 1024, 0); -- 10 MB
local file_types = module:get_option_set(module.name .. "_allowed_file_types", {});
local safe_types = module:get_option_set(module.name .. "_safe_file_types", {"image/*","video/*","audio/*","text/plain"});
-local expiry = module:get_option_number(module.name .. "_expires_after", 7 * 86400);
-local daily_quota = module:get_option_number(module.name .. "_daily_quota", file_size_limit*10); -- 100 MB / day
-local total_storage_limit = module:get_option_number(module.name.."_global_quota", unlimited);
+local expiry = module:get_option_period(module.name .. "_expires_after", "1w");
+local daily_quota = module:get_option_integer(module.name .. "_daily_quota", file_size_limit*10, 0); -- 100 MB / day
+local total_storage_limit = module:get_option_integer(module.name.."_global_quota", unlimited, 0);
+
+local create_jwt, verify_jwt = require"prosody.util.jwt".init("HS256", secret, secret, { default_ttl = 600 });
local access = module:get_option_set(module.name .. "_access", {});
+module:default_permission("prosody:registered", ":upload");
+
if not external_base_url then
module:depends("http");
end
@@ -76,12 +79,12 @@ local measure_upload_cache_size = module:measure("upload_cache", "amount");
local measure_quota_cache_size = module:measure("quota_cache", "amount");
local measure_total_storage_usage = module:measure("total_storage", "amount", { unit = "bytes" });
-do
+module:on_ready(function ()
local total, err = persist_stats:get(nil, "total");
if not err then
total_storage_usage = tonumber(total) or 0;
end
-end
+end)
module:hook_global("stats-update", function ()
measure_upload_cache_size(upload_cache:count());
@@ -135,7 +138,7 @@ end
function may_upload(uploader, filename, filesize, filetype) -- > boolean, error
local uploader_host = jid.host(uploader);
- if not ((access:empty() and prosody.hosts[uploader_host]) or access:contains(uploader) or access:contains(uploader_host)) then
+ if not (module:may(":upload", uploader) or access:contains(uploader) or access:contains(uploader_host)) then
return false, upload_errors.new("access");
end
@@ -169,16 +172,13 @@ function may_upload(uploader, filename, filesize, filetype) -- > boolean, error
end
function get_authz(slot, uploader, filename, filesize, filetype)
-local now = os.time();
- return jwt.sign(secret, {
+ return create_jwt({
-- token properties
sub = uploader;
- iat = now;
- exp = now+300;
-- slot properties
slot = slot;
- expires = expiry >= 0 and (now+expiry) or nil;
+ expires = expiry < math.huge and (os.time()+expiry) or nil;
-- file properties
filename = filename;
filesize = filesize;
@@ -249,32 +249,34 @@ end
function handle_upload(event, path) -- PUT /upload/:slot
local request = event.request;
- local authz = request.headers.authorization;
- if authz then
- authz = authz:match("^Bearer (.*)")
- end
- if not authz then
- module:log("debug", "Missing or malformed Authorization header");
- event.response.headers.www_authenticate = "Bearer";
- return 401;
- end
- local authed, upload_info = jwt.verify(secret, authz);
- if not (authed and type(upload_info) == "table" and type(upload_info.exp) == "number") then
- module:log("debug", "Unauthorized or invalid token: %s, %q", authed, upload_info);
- return 401;
- end
- if not request.body_sink and upload_info.exp < os.time() then
- module:log("debug", "Authorization token expired on %s", dt.datetime(upload_info.exp));
- return 410;
- end
- if not path or upload_info.slot ~= path:match("^[^/]+") then
- module:log("debug", "Invalid upload slot: %q, path: %q", upload_info.slot, path);
- return 400;
- end
- if request.headers.content_length and tonumber(request.headers.content_length) ~= upload_info.filesize then
- return 413;
- -- Note: We don't know the size if the upload is streamed in chunked encoding,
- -- so we also check the final file size on completion.
+ local upload_info = request.http_file_share_upload_info;
+
+ if not upload_info then -- Initial handling of request
+ local authz = request.headers.authorization;
+ if authz then
+ authz = authz:match("^Bearer (.*)")
+ end
+ if not authz then
+ module:log("debug", "Missing or malformed Authorization header");
+ event.response.headers.www_authenticate = "Bearer";
+ return 401;
+ end
+ local authed, authed_upload_info = verify_jwt(authz);
+ if not authed then
+ module:log("debug", "Unauthorized or invalid token: %s, %q", authz, authed_upload_info);
+ return 401;
+ end
+ if not path or authed_upload_info.slot ~= path:match("^[^/]+") then
+ module:log("debug", "Invalid upload slot: %q, path: %q", authed_upload_info.slot, path);
+ return 400;
+ end
+ if request.headers.content_length and tonumber(request.headers.content_length) ~= authed_upload_info.filesize then
+ return 413;
+ -- Note: We don't know the size if the upload is streamed in chunked encoding,
+ -- so we also check the final file size on completion.
+ end
+ upload_info = authed_upload_info;
+ request.http_file_share_upload_info = upload_info;
end
local filename = get_filename(upload_info.slot, true);
@@ -450,11 +452,11 @@ function handle_download(event, path) -- GET /uploads/:slot+filename
return response:send_file(handle);
end
-if expiry >= 0 and not external_base_url then
+if expiry < math.huge and not external_base_url then
-- TODO HTTP DELETE to the external endpoint?
- local array = require "util.array";
- local async = require "util.async";
- local ENOENT = require "util.pposix".ENOENT;
+ local array = require "prosody.util.array";
+ local async = require "prosody.util.async";
+ local ENOENT = require "prosody.util.pposix".ENOENT;
local function sleep(t)
local wait, done = async.waiter();