From 1bff00eba792d8ba552862a206035b8b4da39d06 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 27 Jan 2021 13:11:36 +0000 Subject: [PATCH] prosody: Broad update to upstream (prosody-modules) where appropriate --- ansible/files/prosody.cfg.lua | 17 +- ansible/tasks/prosody.yml | 26 +- .../mod_easy_invite/mod_easy_invite.lua | 244 ------------------ .../mod_http_acme_challenge.lua | 12 - .../mod_http_libjs/mod_http_libjs.lua | 13 - snikket-modules/mod_invites/mod_invites.lua | 140 ---------- .../mod_invites_api/mod_invites_api.lua | 112 -------- .../mod_invites_page/html/invite.html | 131 ---------- .../mod_invites_page/html/invite_invalid.html | 37 --- .../mod_invites_page/mod_invites_page.lua | 55 ---- .../mod_invites_register/html/register.html | 87 ------- .../html/register_error.html | 37 --- .../html/register_success.html | 79 ------ .../mod_invites_register.lua | 158 ------------ .../email_templates/invite_email.html | 11 - .../email_templates/invite_email.txt | 7 - .../mod_landing_page/html/index.html | 63 ----- .../mod_landing_page/mod_landing_page.lua | 94 ------- 18 files changed, 23 insertions(+), 1300 deletions(-) delete mode 100644 snikket-modules/mod_easy_invite/mod_easy_invite.lua delete mode 100644 snikket-modules/mod_http_acme_challenge/mod_http_acme_challenge.lua delete mode 100644 snikket-modules/mod_http_libjs/mod_http_libjs.lua delete mode 100644 snikket-modules/mod_invites/mod_invites.lua delete mode 100644 snikket-modules/mod_invites_api/mod_invites_api.lua delete mode 100644 snikket-modules/mod_invites_page/html/invite.html delete mode 100644 snikket-modules/mod_invites_page/html/invite_invalid.html delete mode 100644 snikket-modules/mod_invites_page/mod_invites_page.lua delete mode 100644 snikket-modules/mod_invites_register/html/register.html delete mode 100644 snikket-modules/mod_invites_register/html/register_error.html delete mode 100644 snikket-modules/mod_invites_register/html/register_success.html delete mode 100644 snikket-modules/mod_invites_register/mod_invites_register.lua delete mode 100644 snikket-modules/mod_landing_page/email_templates/invite_email.html delete mode 100644 snikket-modules/mod_landing_page/email_templates/invite_email.txt delete mode 100644 snikket-modules/mod_landing_page/html/index.html delete mode 100644 snikket-modules/mod_landing_page/mod_landing_page.lua diff --git a/ansible/files/prosody.cfg.lua b/ansible/files/prosody.cfg.lua index 4606ed4..b806d08 100644 --- a/ansible/files/prosody.cfg.lua +++ b/ansible/files/prosody.cfg.lua @@ -84,14 +84,25 @@ modules_enabled = { "welcome"; -- Welcome users who register accounts "http_files"; -- Serve static files from a directory over HTTP "reload_modules"; - "landing_page"; + + -- Invites + "groups_migration"; + "invites"; + "invites_adhoc"; + "invites_api"; + "invites_groups"; "invites_page"; "invites_register"; + "invites_register_api"; + "invites_tracking"; + "invites_api"; - "easy_invite"; "watchregistrations"; "firewall"; + -- Circles + "groups_migration"; + -- For the web portal "http_oauth2"; "http_admin_api"; @@ -113,7 +124,7 @@ legacy_ssl_ports = { 5223 } allow_registration = true registration_invite_only = true -invites_page = ENV_SNIKKET_INVITE_URL or ("https://"..DOMAIN.."/_/invite?{token}"); +invites_page = ENV_SNIKKET_INVITE_URL or ("https://"..DOMAIN.."/invite?{token}"); c2s_require_encryption = true s2s_require_encryption = true diff --git a/ansible/tasks/prosody.yml b/ansible/tasks/prosody.yml index 15d287e..c202fc7 100644 --- a/ansible/tasks/prosody.yml +++ b/ansible/tasks/prosody.yml @@ -78,7 +78,6 @@ - mod_cloud_notify_encrypted - mod_cloud_notify_priority_tag - mod_cloud_notify_filters - - mod_invite - mod_block_registrations - mod_compact_resource - mod_conversejs @@ -86,7 +85,6 @@ - mod_lastlog - mod_limit_auth - mod_password_policy - - mod_password_reset - mod_roster_allinall - mod_strict_https - mod_vcard_muc @@ -95,21 +93,21 @@ - mod_http_altconnect - mod_bookmarks - mod_default_bookmarks - - mod_muc_defaults - - mod_muc_local_only - mod_firewall - mod_turncredentials - mod_admin_notify - mod_http_oauth2 - mod_http_admin_api - mod_rest - -- name: Install Bootstrap and JS libs - apt: - name: - - libjs-bootstrap4 - - libjs-jquery - install_recommends: no + - mod_groups_migration + - mod_invites + - mod_invites_adhoc + - mod_invites_api + - mod_invites_groups + - mod_invites_page + - mod_invites_register + - mod_invites_register_api + - mod_invites_tracking - name: Enable wanted modules file: @@ -117,12 +115,6 @@ src: "/usr/local/lib/snikket-modules/{{item}}" dest: "/etc/prosody/modules/{{item}}" loop: - - mod_landing_page - - mod_invites - - mod_invites_page - - mod_invites_register - - mod_invites_api - - mod_easy_invite - mod_update_check - mod_update_notify - mod_authz_internal diff --git a/snikket-modules/mod_easy_invite/mod_easy_invite.lua b/snikket-modules/mod_easy_invite/mod_easy_invite.lua deleted file mode 100644 index 51e7453..0000000 --- a/snikket-modules/mod_easy_invite/mod_easy_invite.lua +++ /dev/null @@ -1,244 +0,0 @@ --- XEP-0401: Easy User Onboarding -local dataforms = require "util.dataforms"; -local datetime = require "util.datetime"; -local jid_bare = require "util.jid".bare; -local jid_split = require "util.jid".split; -local split_jid = require "util.jid".split; -local rostermanager = require "core.rostermanager"; -local st = require "util.stanza"; - -local invite_only = module:get_option_boolean("registration_invite_only", true); -local require_encryption = module:get_option_boolean("c2s_require_encryption", - module:get_option_boolean("require_encryption", false)); - -local new_adhoc = module:require("adhoc").new; - --- Whether local users can invite other users to create an account on this server -local allow_user_invites = module:get_option_boolean("allow_user_invites", true); - -local invites; -if prosody.shutdown then -- COMPAT hack to detect prosodyctl - invites = module:depends("invites"); -end - -local invite_result_form = dataforms.new({ - title = "Your Invite", - -- TODO instructions = something helpful - { - name = "uri"; - label = "Invite URI"; - -- TODO desc = something helpful - }, - { - name = "url" ; - var = "landing-url"; - label = "Invite landing URL"; - }, - { - name = "expire"; - label = "Token valid until"; - }, - }); - -module:depends("adhoc"); -module:provides("adhoc", new_adhoc("New Invite", "urn:xmpp:invite#invite", - function (_, data) - local username = split_jid(data.from); - local invite = invites.create_contact(username, allow_user_invites); - --TODO: check errors - return { - status = "completed"; - form = { - layout = invite_result_form; - values = { - uri = invite.uri; - url = invite.landing_page; - expire = datetime.datetime(invite.expires); - }; - }; - }; - end, "local_user")); - - --- TODO --- module:provides("adhoc", new_adhoc("Create account", "urn:xmpp:invite#create-account", function () end, "admin")); - --- XEP-0379: Pre-Authenticated Roster Subscription -module:hook("presence/bare", function (event) - local stanza = event.stanza; - if stanza.attr.type ~= "subscribe" then return end - - local preauth = stanza:get_child("preauth", "urn:xmpp:pars:0"); - if not preauth then return end - local token = preauth.attr.token; - if not token then return end - - local username, host = jid_split(stanza.attr.to); - - local invite, err = invites.get(token, username); - - if not invite then - module:log("debug", "Got invalid token, error: %s", err); - return; - end - - local contact = jid_bare(stanza.attr.from); - - module:log("debug", "Approving inbound subscription to %s from %s", username, contact); - if rostermanager.set_contact_pending_in(username, host, contact, stanza) then - if rostermanager.subscribed(username, host, contact) then - invite:use(); - rostermanager.roster_push(username, host, contact); - - -- Send back a subscription request (goal is mutual subscription) - if not rostermanager.is_user_subscribed(username, host, contact) - and not rostermanager.is_contact_pending_out(username, host, contact) then - module:log("debug", "Sending automatic subscription request to %s from %s", contact, username); - if rostermanager.set_contact_pending_out(username, host, contact) then - rostermanager.roster_push(username, host, contact); - module:send(st.presence({type = "subscribe", to = contact })); - else - module:log("warn", "Failed to set contact pending out for %s", username); - end - end - end - end -end, 1); - --- TODO sender side, magic automatic mutual subscription - -local invite_stream_feature = st.stanza("register", { xmlns = "urn:xmpp:invite" }):up(); -module:hook("stream-features", function(event) - local session, features = event.origin, event.features; - - -- Advertise to unauthorized clients only. - if session.type ~= "c2s_unauthed" or (require_encryption and not session.secure) then - return - end - - features:add_child(invite_stream_feature); -end); - --- Client is submitting a preauth token to allow registration -module:hook("stanza/iq/urn:xmpp:pars:0:preauth", function(event) - local preauth = event.stanza.tags[1]; - local token = preauth.attr.token; - local validated_invite = invites.get(token); - if not validated_invite then - local reply = st.error_reply(event.stanza, "cancel", "forbidden", "The invite token is invalid or expired"); - event.origin.send(reply); - return true; - end - event.origin.validated_invite = validated_invite; - local reply = st.reply(event.stanza); - event.origin.send(reply); - return true; -end); - --- Registration attempt - ensure a valid preauth token has been supplied -module:hook("user-registering", function (event) - local validated_invite = event.validated_invite or (event.session and event.session.validated_invite); - if invite_only and not validated_invite then - event.allowed = false; - event.reason = "Registration on this server is through invitation only"; - return; - end - if validated_invite.additional_data and validated_invite.additional_data.allow_reset then - event.allow_reset = validated_invite.additional_data.allow_reset; - end -end); - --- Make a *one-way* subscription. User will see when contact is online, --- contact will not see when user is online. -function subscribe(host, user_username, contact_username) - local user_jid = user_username.."@"..host; - local contact_jid = contact_username.."@"..host; - -- Update user's roster to say subscription request is pending... - rostermanager.set_contact_pending_out(user_username, host, contact_jid); - -- Update contact's roster to say subscription request is pending... - rostermanager.set_contact_pending_in(contact_username, host, user_jid); - -- Update contact's roster to say subscription request approved... - rostermanager.subscribed(contact_username, host, user_jid); - -- Update user's roster to say subscription request approved... - rostermanager.process_inbound_subscription_approval(user_username, host, contact_jid); -end - --- Make a mutual subscription between jid1 and jid2. Each JID will see --- when the other one is online. -function subscribe_both(host, user1, user2) - subscribe(host, user1, user2); - subscribe(host, user2, user1); -end - --- Registration successful, if there was a preauth token, mark it as used -module:hook("user-registered", function (event) - local validated_invite = event.validated_invite or (event.session and event.session.validated_invite); - if not validated_invite then - return; - end - local inviter_username = validated_invite.inviter; - local contact_username = event.username; - validated_invite:use(); - - if inviter_username then - module:log("debug", "Creating mutual subscription between %s and %s", inviter_username, contact_username); - subscribe_both(module.host, inviter_username, contact_username); - end - - if validated_invite.additional_data then - module:log("debug", "Importing roles from invite"); - local roles = validated_invite.additional_data.roles; - if roles then - module:open_store("roles"):set(contact_username, roles); - end - end -end); - --- Equivalent of user-registered but for when the account already existed --- (i.e. password reset) -module:hook("user-password-reset", function (event) - local validated_invite = event.validated_invite or (event.session and event.session.validated_invite); - if not validated_invite then - return; - end - validated_invite:use(); -end); - -local sm = require "core.storagemanager"; -function module.command(arg) - if #arg < 2 or arg[2] ~= "generate" then - print("usage: prosodyctl mod_easy_invite example.net generate"); - return; - end - - local host = arg[1]; - assert(hosts[host], "Host "..tostring(host).." does not exist"); - sm.initialize_host(host); - - -- Load mod_invites - invites = module:context(host):depends("invites"); - module:context(host):depends("invites_page"); - - table.remove(arg, 1); - table.remove(arg, 1); - - local invite, roles; - if arg[1] == "--reset" then - local nodeprep = require "util.encodings".stringprep.nodeprep; - local username = nodeprep(arg[2]); - if not username then - print("Please supply a valid username to generate a reset link for"); - return; - end - invite = invites.create_account_reset(username); - else - if arg[1] == "--admin" then - roles = { ["prosody:admin"] = true }; - elseif arg[1] == "--role" then - roles = { [arg[2]] = true }; - end - invite = invites.create_account(nil, { roles = roles }); - end - - print(invite.landing_page or invite.uri); -end diff --git a/snikket-modules/mod_http_acme_challenge/mod_http_acme_challenge.lua b/snikket-modules/mod_http_acme_challenge/mod_http_acme_challenge.lua deleted file mode 100644 index ee4d220..0000000 --- a/snikket-modules/mod_http_acme_challenge/mod_http_acme_challenge.lua +++ /dev/null @@ -1,12 +0,0 @@ -local serve = require "net.http.files".serve; - -module:set_global(); - -local path = module:get_option_string("acme_challenge_path", "/var/www/.well-known/acme-challenge"); - -module:provides("http", { - default_path = "/.well-known/acme-challenge"; - route = { - ["GET /*"] = serve({ path = path }); - } - }); diff --git a/snikket-modules/mod_http_libjs/mod_http_libjs.lua b/snikket-modules/mod_http_libjs/mod_http_libjs.lua deleted file mode 100644 index 16e75ee..0000000 --- a/snikket-modules/mod_http_libjs/mod_http_libjs.lua +++ /dev/null @@ -1,13 +0,0 @@ -local mime_map = module:shared("/*/http_files/mime").types or { - css = "text/css", - js = "application/javascript", -}; - -local libjs_path = module:get_option_string("libjs_path", "/usr/share/javascript"); - -module:provides("http", { - default_path = "/share"; - route = { - ["GET /*"] = require "net.http.files".serve({ path = libjs_path, mime_map = mime_map }); - } - }); diff --git a/snikket-modules/mod_invites/mod_invites.lua b/snikket-modules/mod_invites/mod_invites.lua deleted file mode 100644 index 8cd3c17..0000000 --- a/snikket-modules/mod_invites/mod_invites.lua +++ /dev/null @@ -1,140 +0,0 @@ -local id = require "util.id"; -local url = require "socket.url"; -local jid_node = require "util.jid".node; - -local invite_ttl = module:get_option_number("invite_expiry", 86400 * 7); - -local token_storage = module:open_store("invite_token", "map"); - -local function get_uri(action, jid, token, params) --> string - return url.build({ - scheme = "xmpp", - path = jid, - query = action..";preauth="..token..(params and (";"..params) or ""), - }); -end - -local function create_invite(invite_action, invite_jid, allow_registration, additional_data) - local token = id.medium(); - - local created_at = os.time(); - local expires = created_at + invite_ttl; - - local invite_params = (invite_action == "roster" and allow_registration) and "ibr=y" or nil; - - local invite = { - type = invite_action; - jid = invite_jid; - - token = token; - allow_registration = allow_registration; - additional_data = additional_data; - - uri = get_uri(invite_action, invite_jid, token, invite_params); - - created_at = created_at; - expires = expires; - }; - - module:fire_event("invite-created", invite); - - if allow_registration then - local ok, err = token_storage:set(nil, token, invite); - if not ok then - module:log("warn", "Failed to store account invite: %s", err); - return nil, "internal-server-error"; - end - end - - if invite_action == "roster" then - local username = jid_node(invite_jid); - local ok, err = token_storage:set(username, token, expires); - if not ok then - module:log("warn", "Failed to store subscription invite: %s", err); - return nil, "internal-server-error"; - end - end - - return invite; -end - --- Create invitation to register an account (optionally restricted to the specified username) -function create_account(account_username, additional_data) --luacheck: ignore 131/create_account - local jid = account_username and (account_username.."@"..module.host) or module.host; - return create_invite("register", jid, true, additional_data); -end - --- Create invitation to reset the password for an account -function create_account_reset(account_username) --luacheck: ignore 131/create_account_reset - return create_account(account_username, { allow_reset = account_username }); -end - --- Create invitation to become a contact of a local user -function create_contact(username, allow_registration, additional_data) --luacheck: ignore 131/create_contact - return create_invite("roster", username.."@"..module.host, allow_registration, additional_data); -end - - -local valid_invite_methods = {}; -local valid_invite_mt = { __index = valid_invite_methods }; - -function valid_invite_methods:use() - if self.username then - -- Also remove the contact invite if present, on the - -- assumption that they now have a mutual subscription - token_storage:set(self.username, self.token, nil); - end - token_storage:set(nil, self.token, nil); - return true; -end - --- Get a validated invite (or nil, err). Must call :use() on the --- returned invite after it is actually successfully used --- For "roster" invites, the username of the local user (who issued --- the invite) must be passed. --- If no username is passed, but the registration is a roster invite --- from a local user, the "inviter" field of the returned invite will --- be set to their username. -function get(token, username) - if not token then - return nil, "no-token"; - end - - local valid_until, inviter; - - -- Fetch from host store (account invite) - local token_info = token_storage:get(nil, token); - - if username then -- token being used for subscription - -- Fetch from user store (subscription invite) - valid_until = token_storage:get(username, token); - else -- token being used for account creation - valid_until = token_info and token_info.expires; - if token_info and token_info.type == "roster" then - username = jid_node(token_info.jid); - inviter = username; - end - end - - if not valid_until then - module:log("debug", "Got unknown token: %s", token); - return nil, "token-invalid"; - elseif os.time() > valid_until then - module:log("debug", "Got expired token: %s", token); - return nil, "token-expired"; - end - - return setmetatable({ - token = token; - username = username; - inviter = inviter; - type = token_info and token_info.type or "roster"; - uri = token_info and token_info.uri or get_uri("roster", username.."@"..module.host, token); - additional_data = token_info and token_info.additional_data or nil; - }, valid_invite_mt); -end - -function use(token) --luacheck: ignore 131/use - local invite = get(token); - return invite and invite:use(); -end diff --git a/snikket-modules/mod_invites_api/mod_invites_api.lua b/snikket-modules/mod_invites_api/mod_invites_api.lua deleted file mode 100644 index f6b7074..0000000 --- a/snikket-modules/mod_invites_api/mod_invites_api.lua +++ /dev/null @@ -1,112 +0,0 @@ -local http_formdecode = require "net.http".formdecode; - -local api_key_store; -local invites; --- COMPAT: workaround to avoid executing inside prosodyctl -if prosody.shutdown then - module:depends("http"); - api_key_store = module:open_store("invite_api_keys", "map"); - invites = module:depends("invites"); -end - -local function get_api_user(request, params) - local combined_key; - - local auth_header = request.headers.authorization; - - if not auth_header then - params = params or http_formdecode(request.url.query); - combined_key = params.key; - else - local auth_type, value = auth_header:match("^(%S+)%s(%S+)$"); - if auth_type ~= "Bearer" then - return; - end - combined_key = value; - end - - if not combined_key then - return; - end - - local key_id, key_token = combined_key:match("^([^/]+)/(.+)$"); - - if not key_id then - return; - end - - local api_user = api_key_store:get(nil, key_id); - - if not api_user or api_user.token ~= key_token then - return; - end - - -- TODO: key expiry, rate limiting, etc. - return api_user; -end - -function handle_request(event) - local query_params = http_formdecode(event.request.url.query); - - local api_user = get_api_user(event.request, query_params); - - if not api_user then - return 403; - end - - local invite = invites.create_account(nil, { source = "api/token/"..api_user.id }); - if not invite then - return 500; - end - - event.response.headers.Location = invite.landing_page or invite.uri; - - if query_params.redirect then - return 303; - end - return 201; -end - -if invites then - module:provides("http", { - route = { - ["GET"] = handle_request; - }; - }); -end - -function module.command(arg) - local host = table.remove(arg, 1); - if not prosody.hosts[host] then - print("Error: please supply a valid host"); - return 1; - end - require "core.storagemanager".initialize_host(host); - module.host = host; --luacheck: ignore 122/module - api_key_store = module:open_store("invite_api_keys", "map"); - - local command = table.remove(arg, 1); - if command == "create" then - local id = require "util.id".short(); - local token = require "util.id".long(); - api_key_store:set(nil, id, { - id = id; - token = token; - name = arg[1]; - created_at = os.time(); - }); - print(id.."/"..token); - elseif command == "delete" then - local id = table.remove(arg, 1); - if not api_key_store:get(nil, id) then - print("Error: key not found"); - return 1; - end - api_key_store:set(nil, id, nil); - elseif command == "list" then - local api_key_store_kv = module:open_store("invite_api_keys"); - for key_id, key_info in pairs(api_key_store_kv:get(nil)) do - print(key_id, key_info.name or ""); - end - end -end diff --git a/snikket-modules/mod_invites_page/html/invite.html b/snikket-modules/mod_invites_page/html/invite.html deleted file mode 100644 index 3f13460..0000000 --- a/snikket-modules/mod_invites_page/html/invite.html +++ /dev/null @@ -1,131 +0,0 @@ - - - - - - Invite to {site_name} | Snikket - - - - - - - - - - - - -
-
-
-

- Invite to {site_name}
-

-
Powered by Snikket
-
- {inviter?

You have been invited to chat on {site_name} using Snikket, - a secure, privacy-friendly chat app.

} - - {inviter&

You have been invited to chat with {inviter} using Snikket, - a secure, privacy-friendly chat app on {site_name}.

} - -
Get started
- -

Install the Snikket app on your Android device (iOS coming soon!)

- - - - - -

After installation the app should automatically open and prompt you to - create an account. If not, simply click the button below.

- -
App already installed?
- -
-
- This button works only if you have the app installed already! -
-
-
Alternatives
-

You can connect to Snikket using any XMPP-compatible software. If the button above does not - work with your app, you may need to register an account manually.

-
-
-
- - - - - - - - - diff --git a/snikket-modules/mod_invites_page/html/invite_invalid.html b/snikket-modules/mod_invites_page/html/invite_invalid.html deleted file mode 100644 index baf9403..0000000 --- a/snikket-modules/mod_invites_page/html/invite_invalid.html +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - Invite to {site_name} | Snikket - - - - - - - - - - - -
-
-
-

- Invite to {site_name}
-

-
Powered by Snikket
-
-
Invite expired
- -

Sorry, it looks like this invite code has expired!

- - -
-
-
- - - - diff --git a/snikket-modules/mod_invites_page/mod_invites_page.lua b/snikket-modules/mod_invites_page/mod_invites_page.lua deleted file mode 100644 index a298a9f..0000000 --- a/snikket-modules/mod_invites_page/mod_invites_page.lua +++ /dev/null @@ -1,55 +0,0 @@ -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; -}); -local render_url = require "util.interpolation".new("%b{}", url_escape, { - urlescape = url_escape; - noscheme = function (url) - return (url:gsub("^[^:]+:", "")); - end; -}); - -local site_name = module:get_option_string("site_name", module.host); - -if prosody.shutdown then - module:depends("http"); -end -local invites = module:depends("invites"); - --- Point at eg https://github.com/ge0rg/easy-xmpp-invitation -local base_url = module:get_option_string("invites_page", (module.http_url and module:http_url().."?{token}") or nil); - -local function add_landing_url(invite) - if not base_url then return; end - invite.landing_page = render_url(base_url, invite); -end - -module:hook("invite-created", add_landing_url); - - -function serve_invite_page(event) - local invite_page_template = assert(module:load_resource("html/invite.html")):read("*a"); - local invalid_invite_page_template = assert(module:load_resource("html/invite_invalid.html")):read("*a"); - - local invite = invites.get(event.request.url.query); - if not invite then - return render_html_template(invalid_invite_page_template, { site_name = site_name }); - end - - local invite_page = render_html_template(invite_page_template, { - site_name = site_name; - token = invite.token; - uri = invite.uri; - type = invite.type; - jid = invite.jid; - }); - return invite_page; -end - -module:provides("http", { - route = { - ["GET"] = serve_invite_page; - }; -}); diff --git a/snikket-modules/mod_invites_register/html/register.html b/snikket-modules/mod_invites_register/html/register.html deleted file mode 100644 index 8b61de9..0000000 --- a/snikket-modules/mod_invites_register/html/register.html +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - {site_name} | Snikket - - - - - - - - - - - -
-
-
-

- Secure communication on {site_name}
-

-
Powered by Snikket
-
-

{site_name} is using Snikket - a secure, privacy-friendly chat app.

- -
Create an account
- -

Creating an account will allow to communicate with other people using - the Snikket app or compatible software. If you already have the app installed, - we recommend that you continue the account creation process inside the app - by clicking on the button below:

- -
App already installed?
- -
-
- This button works only if you have the app installed already! -
-
- -
Create an account online
-

If you plan to use a legacy XMPP client, you can register an account online and enter your - credentials into any XMPP-compatible software.

- - {message&} - -
-
- -
-
- -
- @{domain} -
-
- Choose a username, this will become the first part of your new chat address. -
-
-
- -
- - Enter a secure password that you do not use anywhere else. -
-
-
- - -
-
-
-
-
- - - - diff --git a/snikket-modules/mod_invites_register/html/register_error.html b/snikket-modules/mod_invites_register/html/register_error.html deleted file mode 100644 index 5c12f95..0000000 --- a/snikket-modules/mod_invites_register/html/register_error.html +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - Invite to {site_name} | Snikket - - - - - - - - - - - -
-
-
-

- Invite to {site_name}
-

-
Powered by Snikket
-
-
Registration error
- -

{message?Sorry, there was a problem registering your account.}

- - -
-
-
- - - - diff --git a/snikket-modules/mod_invites_register/html/register_success.html b/snikket-modules/mod_invites_register/html/register_success.html deleted file mode 100644 index b9097bb..0000000 --- a/snikket-modules/mod_invites_register/html/register_success.html +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - Invite to {site_name} | Snikket - - - - - - - - - - - - - -
-
-
-

- {site_name}
-

-
Powered by Snikket
-
-
Congratulations!
- -

You have created an account on {site_name}.

- -

To start chatting, you need to enter your new account - credentials into your chosen XMPP software.

- -

As a final reminder, your account details are shown below:

- - - -

Your password is stored encrypted on the server and will not be accessible after you close this page.

-
-
-
- - - - diff --git a/snikket-modules/mod_invites_register/mod_invites_register.lua b/snikket-modules/mod_invites_register/mod_invites_register.lua deleted file mode 100644 index 536e330..0000000 --- a/snikket-modules/mod_invites_register/mod_invites_register.lua +++ /dev/null @@ -1,158 +0,0 @@ -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; -}); - - -local site_name = module:get_option_string("site_name", module.host); - -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 invite = invites.get(event.request.url.query); - 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; - }); - 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 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 success_template = assert(module:load_resource("html/register_success.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; - }); - - return render_html_template(success_template, { - site_name = site_name; - username = prepped_username; - domain = module.host; - password = password; - }); - 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", { - route = { - ["GET"] = serve_register_page; - ["POST"] = handle_register_form; - }; -}); diff --git a/snikket-modules/mod_landing_page/email_templates/invite_email.html b/snikket-modules/mod_landing_page/email_templates/invite_email.html deleted file mode 100644 index fe3d36d..0000000 --- a/snikket-modules/mod_landing_page/email_templates/invite_email.html +++ /dev/null @@ -1,11 +0,0 @@ - - -TODO! - -Hello from {site_name}! - -You have been invited to join {site_name}. Simply click here to get started: view invite - -If you already have a compatible app installed, you can click here instead: open in app - - diff --git a/snikket-modules/mod_landing_page/email_templates/invite_email.txt b/snikket-modules/mod_landing_page/email_templates/invite_email.txt deleted file mode 100644 index 46cf722..0000000 --- a/snikket-modules/mod_landing_page/email_templates/invite_email.txt +++ /dev/null @@ -1,7 +0,0 @@ -TODO! - -Hello from {site_name}! - -You have been invited to join {site_name}. Simply click here to get started: {invite_page} - -If you already have a compatible app installed, you can click here instead: {invite_uri} diff --git a/snikket-modules/mod_landing_page/html/index.html b/snikket-modules/mod_landing_page/html/index.html deleted file mode 100644 index 9ed5a0a..0000000 --- a/snikket-modules/mod_landing_page/html/index.html +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - {site_name} | Snikket - - - - - - - - - - - -
-
-
-

- Secure communication on {site_name}
-

- -
-

{site_name} is using Snikket - a secure, privacy-friendly chat app.

- - {allow_email& -
Request invitation
- -

You may join the Snikket network by creating an account - on {site_name}. Registration is by invitation only, enter - your email address to request an invitation.

- -
-
- -
-
- -
- - Enter the email address we should deliver your invitation to. -
- Your email address will be kept private as per our Privacy Policy. -
-
-
-
- -
-
- } -
-
-
- - - - diff --git a/snikket-modules/mod_landing_page/mod_landing_page.lua b/snikket-modules/mod_landing_page/mod_landing_page.lua deleted file mode 100644 index b361940..0000000 --- a/snikket-modules/mod_landing_page/mod_landing_page.lua +++ /dev/null @@ -1,94 +0,0 @@ -local http_formdecode = require "net.http".formdecode; -local st = require "util.stanza"; -local render_html_template = require"util.interpolation".new("%b{}", st.xml_escape); -local render_text_template = require"util.interpolation".new("%b{}", function (s) return s; end); -local mime = require "mime"; -local ltn12 = require "ltn12"; - -local site_name = module:get_option_string("site_name", module.host); - --- Email templates -local email_template_preamble = "Problems viewing this email? View it online at {invite_page}"; -local email_template_text = assert(module:load_resource("email_templates/invite_email.txt")):read("*a"); -local email_template_html = assert(module:load_resource("email_templates/invite_email.html")):read("*a"); - -module:depends("http"); -module:depends("email"); -local invites = module:depends("invites"); - -local landing_page_template = assert(module:load_resource("html/index.html")):read("*a"); - -local landing_page = render_html_template(landing_page_template, { - site_name = site_name; -}); - -local function handle_form(event) - local request, response = event.request, event.response; - local form_data = http_formdecode(request.body); - - local email = form_data["email"]; - - response.headers.content_type = ""; - - local invite = invites.create_account(); - - local email_template_params = { - site_name = site_name; - invite_token = invite.token; - invite_uri = invite.uri; - invite_page = invite.landing_page; - }; - - local email_headers = { - Subject = "Your Snikket invitation"; - ["Content-Type"] = "multipart/mixed"; - }; - - local email_body = { - -- Optional text content prefixed to the entire email (visible even in - -- non-MIME clients) - preamble = render_text_template(email_template_preamble, email_template_params); - - -- Plain text version - [1] = { - headers = { - ["Content-Type"] = 'text/plain; charset="utf-8"'; - }; - body = mime.eol(0, render_text_template(email_template_text, email_template_params)); - }; - - -- HTML version - [2] = { - headers = { - ["content-type"] = 'text/html;charset="utf-8"', - ["content-transfer-encoding"] = "quoted-printable" - }, - body = ltn12.source.chain( - ltn12.source.string(render_html_template(email_template_html, email_template_params)), - ltn12.filter.chain( - mime.encode("quoted-printable", "text"), - mime.wrap() - ) - ); - }; - } - - - module:send_email({ --luacheck: ignore 143/module - to = email; - headers = email_headers; - body = email_body; - }); - - return render_html_template(landing_page_template, { - site_name = site_name; - message = "Ok! Check your inbox :)"; - }); -end - -module:provides("http", { - route = { - ["GET /"] = landing_page; - ["POST /invite-request"] = handle_form; - }; -});