Software /
code /
prosody-modules
Comparison
mod_rest/mod_rest.lua @ 3876:75b330d4fa6f
mod_rest: Add support for HTTP Basic username and password authentication
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Sat, 01 Feb 2020 13:03:18 +0100 |
parent | 3874:505ae524b635 |
child | 3879:3b31ff7b4c7c |
comparison
equal
deleted
inserted
replaced
3875:93f71ab6cb00 | 3876:75b330d4fa6f |
---|---|
14 | 14 |
15 local allow_any_source = module:get_host_type() == "component"; | 15 local allow_any_source = module:get_host_type() == "component"; |
16 local validate_from_addresses = module:get_option_boolean("validate_from_addresses", true); | 16 local validate_from_addresses = module:get_option_boolean("validate_from_addresses", true); |
17 local secret = assert(module:get_option_string("rest_credentials"), "rest_credentials is a required setting"); | 17 local secret = assert(module:get_option_string("rest_credentials"), "rest_credentials is a required setting"); |
18 local auth_type = assert(secret:match("^%S+"), "Format of rest_credentials MUST be like 'Bearer secret'"); | 18 local auth_type = assert(secret:match("^%S+"), "Format of rest_credentials MUST be like 'Bearer secret'"); |
19 assert(auth_type == "Bearer", "Only 'Bearer' is supported in rest_credentials"); | 19 assert(auth_type == "Bearer" or auth_type == "Basic", "Only 'Bearer' and 'Basic' are supported in rest_credentials"); |
20 | 20 |
21 local jsonmap = module:require"jsonmap"; | 21 local jsonmap = module:require"jsonmap"; |
22 -- Bearer token | 22 -- Bearer token |
23 local function check_credentials(request) | 23 local function check_credentials(request) |
24 return request.headers.authorization == secret; | 24 return request.headers.authorization == secret; |
25 end | |
26 if secret == "Basic" and module:get_host_type() == "local" then | |
27 local um = require "core.usermanager"; | |
28 local encodings = require "util.encodings"; | |
29 local base64 = encodings.base64; | |
30 | |
31 function check_credentials(request) | |
32 local creds = string.match(request.headers.authorization, "^Basic%s+([A-Za-z0-9+/]+=?=?)%s*$"); | |
33 if not creds then return false; end | |
34 creds = base64.decode(creds); | |
35 if not creds then return false; end | |
36 local username, password = string.match(creds, "^([^:]+):(.*)$"); | |
37 if not username then return false; end | |
38 username, password = encodings.stringprep.nodeprep(username), encodings.stringprep.saslprep(password); | |
39 if not username then return false; end | |
40 module:log("debug", "usermanager.test_password(%q, %q, %q)", username, module.host, string.rep("*", #password)) | |
41 if not um.test_password(username, module.host, password) then | |
42 return false; | |
43 end | |
44 return jid.join(username, module.host); | |
45 end | |
25 end | 46 end |
26 | 47 |
27 local function parse(mimetype, data) | 48 local function parse(mimetype, data) |
28 mimetype = mimetype and mimetype:match("^[^; ]*"); | 49 mimetype = mimetype and mimetype:match("^[^; ]*"); |
29 if mimetype == "application/xmpp+xml" then | 50 if mimetype == "application/xmpp+xml" then |
62 return tostring(s); | 83 return tostring(s); |
63 end | 84 end |
64 | 85 |
65 local function handle_post(event) | 86 local function handle_post(event) |
66 local request, response = event.request, event.response; | 87 local request, response = event.request, event.response; |
88 local from = module.host; | |
67 if not request.headers.authorization then | 89 if not request.headers.authorization then |
68 response.headers.www_authenticate = ("%s realm=%q"):format(auth_type, module.host.."/"..module.name); | 90 response.headers.www_authenticate = ("%s realm=%q"):format(auth_type, module.host.."/"..module.name); |
69 return 401; | 91 return 401; |
70 elseif not check_credentials(request) then | 92 else |
71 return 401; | 93 local authz = check_credentials(request); |
94 if not authz then | |
95 return 401; | |
96 end | |
97 if type(authz) == "string" then | |
98 from = authz; | |
99 end | |
72 end | 100 end |
73 local payload, err = parse(request.headers.content_type, request.body); | 101 local payload, err = parse(request.headers.content_type, request.body); |
74 if not payload then | 102 if not payload then |
75 -- parse fail | 103 -- parse fail |
76 return errors.new({ code = 400, text = "Failed to parse payload" }, { error = err, type = request.headers.content_type, data = request.body }); | 104 return errors.new({ code = 400, text = "Failed to parse payload" }, { error = err, type = request.headers.content_type, data = request.body }); |
82 end | 110 end |
83 local to = jid.prep(payload.attr.to); | 111 local to = jid.prep(payload.attr.to); |
84 if not to then | 112 if not to then |
85 return errors.new({ code = 422, text = "Invalid destination JID" }); | 113 return errors.new({ code = 422, text = "Invalid destination JID" }); |
86 end | 114 end |
87 local from = module.host; | |
88 if allow_any_source and payload.attr.from then | 115 if allow_any_source and payload.attr.from then |
89 from = jid.prep(payload.attr.from); | 116 from = jid.prep(payload.attr.from); |
90 if not from then | 117 if not from then |
91 return errors.new({ code = 422, text = "Invalid source JID" }); | 118 return errors.new({ code = 422, text = "Invalid source JID" }); |
92 end | 119 end |