diff mod_invites_register_web/mod_invites_register_web.lua @ 4093:a2116f5a7c8f

mod_invites_register_web: New module to allow web registration with an invite token
author Matthew Wild <mwild1@gmail.com>
date Fri, 11 Sep 2020 13:51:54 +0100
parents
children f49e3ea99785
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_invites_register_web/mod_invites_register_web.lua	Fri Sep 11 13:51:54 2020 +0100
@@ -0,0 +1,183 @@
+local id = require "util.id";
+local http_formdecode = require "net.http".formdecode;
+local usermanager = require "core.usermanager";
+local nodeprep = require "util.encodings".stringprep.nodeprep;
+local st = require "util.stanza";
+local url_escape = require "util.http".urlencode;
+local render_html_template = require"util.interpolation".new("%b{}", st.xml_escape, {
+	urlescape = url_escape;
+});
+
+
+module:depends("register_apps");
+
+local site_name = module:get_option_string("site_name", module.host);
+local site_apps = module:shared("register_apps/apps");
+
+module:depends("http");
+module:depends("easy_invite");
+local invites = module:depends("invites");
+local invites_page = module:depends("invites_page");
+
+function serve_register_page(event)
+	local register_page_template = assert(module:load_resource("html/register.html")):read("*a");
+
+	local query_params = http_formdecode(event.request.url.query);
+
+	local invite = invites.get(query_params.t);
+	if not invite then
+		return {
+			status_code = 303;
+			headers = {
+				["Location"] = invites.module:http_url().."?"..event.request.url.query;
+			};
+		};
+	end
+
+	local invite_page = render_html_template(register_page_template, {
+		site_name = site_name;
+		token = invite.token;
+		domain = module.host;
+		uri = invite.uri;
+		type = invite.type;
+		jid = invite.jid;
+		inviter = invite.inviter;
+		app = query_params.c and site_apps[query_params.c];
+	});
+	return invite_page;
+end
+
+function handle_register_form(event)
+	local request, response = event.request, event.response;
+	local form_data = http_formdecode(request.body);
+	local user, password, token = form_data["user"], form_data["password"], form_data["token"];
+	local app_id = form_data["app_id"];
+
+	local register_page_template = assert(module:load_resource("html/register.html")):read("*a");
+	local error_template = assert(module:load_resource("html/register_error.html")):read("*a");
+
+	local invite = invites.get(token);
+	if not invite then
+		return {
+			status_code = 303;
+			headers = {
+				["Location"] = invites_page.module:http_url().."?"..event.request.url.query;
+			};
+		};
+	end
+
+	response.headers.content_type = "text/html; charset=utf-8";
+
+	if not user or #user == 0 or not password or #password == 0 or not token then
+		return render_html_template(register_page_template, {
+			site_name = site_name;
+			token = invite.token;
+			domain = module.host;
+			uri = invite.uri;
+			type = invite.type;
+			jid = invite.jid;
+
+			msg_class = "alert-warning";
+			message = "Please fill in all fields.";
+		});
+	end
+
+	-- Shamelessly copied from mod_register_web.
+	local prepped_username = nodeprep(user);
+
+	if not prepped_username or #prepped_username == 0 then
+		return render_html_template(register_page_template, {
+			site_name = site_name;
+			token = invite.token;
+			domain = module.host;
+			uri = invite.uri;
+			type = invite.type;
+			jid = invite.jid;
+
+			msg_class = "alert-warning";
+			message = "This username contains invalid characters.";
+		});
+	end
+
+	if usermanager.user_exists(prepped_username, module.host) then
+		return render_html_template(register_page_template, {
+			site_name = site_name;
+			token = invite.token;
+			domain = module.host;
+			uri = invite.uri;
+			type = invite.type;
+			jid = invite.jid;
+
+			msg_class = "alert-warning";
+			message = "This username is already in use.";
+		});
+	end
+
+	local registering = {
+		validated_invite = invite;
+		username = prepped_username;
+		host = module.host;
+		allowed = true;
+	};
+
+	module:fire_event("user-registering", registering);
+
+	if not registering.allowed then
+		return render_html_template(error_template, {
+			site_name = site_name;
+			msg_class = "alert-danger";
+			message = registering.reason or "Registration is not allowed.";
+		});
+	end
+
+	local ok, err = usermanager.create_user(prepped_username, password, module.host);
+
+	if ok then
+		module:fire_event("user-registered", {
+			username = prepped_username;
+			host = module.host;
+			source = "mod_"..module.name;
+			validated_invite = invite;
+		});
+
+		local app_info = site_apps[app_id];
+
+		local success_template;
+		if app_info then
+			-- If recognised app, we serve a page that includes setup instructions
+			success_template = assert(module:load_resource("html/register_success_setup.html")):read("*a");
+		else
+			success_template = assert(module:load_resource("html/register_success.html")):read("*a");
+		end
+
+		-- Due to the credentials being served here, ensure that
+		-- the browser or any intermediary does not cache the page
+		event.response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate";
+		event.response.headers["Pragma"] = "no-cache";
+		event.response.headers["Expires"] = "0";
+
+		return render_html_template(success_template, {
+			site_name = site_name;
+			username = prepped_username;
+			domain = module.host;
+			password = password;
+			app = app_info;
+		});
+	else
+		local err_id = id.short();
+		module:log("warn", "Registration failed (%s): %s", err_id, tostring(err));
+		return render_html_template(error_template, {
+			site_name = site_name;
+			msg_class = "alert-danger";
+			message = ("An unknown error has occurred (%s)"):format(err_id);
+		});
+	end
+end
+
+module:provides("http", {
+	default_path = "register";
+	route = {
+		["GET"] = serve_register_page;
+		["POST"] = handle_register_form;
+	};
+});