Software /
code /
prosody-modules
File
mod_register_web/mod_register_web.lua @ 4432:e83284d4d5c2
mod_auth_ccert/README: Add setting to ensure Prosdy asks for client certificate
This used to be the default for all services, but since it triggers
annoying popups in web browsers it was inverted in Prosody and only s2s
enables it, so it needs to be explicitly enabled for c2s again.
See trunk 115b5e32d960
Thanks debacle
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Sat, 06 Feb 2021 21:34:25 +0100 |
parent | 3753:cf3247ec5e01 |
child | 4440:95262bd1bcb2 |
line wrap: on
line source
local captcha_options = module:get_option("captcha_options", {}); local nodeprep = require "util.encodings".stringprep.nodeprep; local usermanager = require "core.usermanager"; local datamanager = require "util.datamanager"; local http = require "net.http"; local path_sep = package.config:sub(1,1); local json = require "util.json".decode; local t_concat = table.concat; pcall(function () module:depends("register_limits"); end); module:depends"http"; local extra_fields = { nick = true; name = true; first = true; last = true; email = true; address = true; city = true; state = true; zip = true; phone = true; url = true; date = true; } local template_path = module:get_option_string("register_web_template", "templates"); function template(data) -- Like util.template, but deals with plain text return { apply = function(values) return (data:gsub("{([^}]+)}", values)); end } end local function get_template(name) local fh = assert(module:load_resource(template_path..path_sep..name..".html")); local data = assert(fh:read("*a")); fh:close(); return template(data); end local function render(template, data) return tostring(template.apply(data)); end local register_tpl = get_template "register"; local success_tpl = get_template "success"; -- COMPAT `or request.conn:ip()` if next(captcha_options) ~= nil then local recaptcha_tpl = get_template "recaptcha"; function generate_captcha(display_options) return recaptcha_tpl.apply(setmetatable({ recaptcha_display_error = display_options and display_options.recaptcha_error and ("&error="..display_options.recaptcha_error) or ""; }, { __index = function (_, k) if captcha_options[k] then return captcha_options[k]; end module:log("error", "Missing parameter from captcha_options: %s", k); end })); end function verify_captcha(request, form, callback) http.request("https://www.google.com/recaptcha/api/siteverify", { body = http.formencode { secret = captcha_options.recaptcha_private_key; remoteip = request.ip or request.conn:ip(); response = form["g-recaptcha-response"]; }; }, function (verify_result, code) local result = json(verify_result); if not result then module:log("warn", "Unable to decode response from recaptcha: [%d] %s", code, verify_result); callback(false, "Captcha API error"); elseif result.success == true then callback(true); else callback(false, t_concat(result["error-codes"])); end end); end else module:log("debug", "No Recaptcha options set, using fallback captcha") local random = math.random; local hmac_sha1 = require "util.hashes".hmac_sha1; local secret = require "util.uuid".generate() local ops = { '+', '-' }; local captcha_tpl = get_template "simplecaptcha"; function generate_captcha() local op = ops[random(1, #ops)]; local x, y = random(1, 9) repeat y = random(1, 9); until x ~= y; local answer; if op == '+' then answer = x + y; elseif op == '-' then if x < y then -- Avoid negative numbers x, y = y, x; end answer = x - y; end local challenge = hmac_sha1(secret, answer, true); return captcha_tpl.apply { op = op, x = x, y = y, challenge = challenge; }; end function verify_captcha(request, form, callback) if hmac_sha1(secret, form.captcha_reply or "", true) == form.captcha_challenge then callback(true); else callback(false, "Captcha verification failed"); end end end function generate_page(event, display_options) local request, response = event.request, event.response; response.headers.content_type = "text/html; charset=utf-8"; return render(register_tpl, { path = request.path; hostname = module.host; notice = display_options and display_options.register_error or ""; captcha = generate_captcha(display_options); }) end function register_user(form, origin) local username = form.username; local password = form.password; local confirm_password = form.confirm_password; local jid = nil; form.username, form.password, form.confirm_password = nil, nil, nil; local prepped_username = nodeprep(username, true); if not prepped_username then return nil, "Username contains forbidden characters"; end if #prepped_username == 0 then return nil, "The username field was empty"; end if usermanager.user_exists(prepped_username, module.host) then return nil, "Username already taken"; end local registering = { username = prepped_username , host = module.host, additional = form, ip = origin.ip or origin.conn:ip(), allowed = true } module:fire_event("user-registering", registering); if not registering.allowed then return nil, registering.reason or "Registration not allowed"; end if confirm_password ~= password then return nil, "Passwords don't match"; end local ok, err = usermanager.create_user(prepped_username, password, module.host); if ok then jid = prepped_username.."@"..module.host local extra_data = {}; for field in pairs(extra_fields) do local field_value = form[field]; if field_value and #field_value > 0 then extra_data[field] = field_value; end end if next(extra_data) ~= nil then datamanager.store(prepped_username, module.host, "account_details", extra_data); end module:fire_event("user-registered", { username = prepped_username, host = module.host, source = module.name, ip = origin.ip or origin.conn:ip(), }); end return jid, err; end function generate_success(event, jid) return render(success_tpl, { jid = jid }); end function generate_register_response(event, jid, err) event.response.headers.content_type = "text/html; charset=utf-8"; if jid then return generate_success(event, jid); else return generate_page(event, { register_error = err }); end end function handle_form(event) local request, response = event.request, event.response; local form = http.formdecode(request.body); verify_captcha(request, form, function (ok, err) if ok then local jid, register_err = register_user(form, request); response:send(generate_register_response(event, jid, register_err)); else response:send(generate_page(event, { register_error = err })); end end); return true; -- Leave connection open until we respond above end module:provides("http", { title = module:get_option_string("register_web_title", "Account Registration"); route = { GET = generate_page; ["GET /"] = generate_page; POST = handle_form; ["POST /"] = handle_form; }; });