Annotate

mod_tos/mod_tos.lua @ 6199:fe8222112cf4

mod_conversejs: Serve base app at / This makes things slightly less awkward for the browser to figure out which URLs belong to a PWA. The app's "start URL" was previously without the '/' and therefore was not considered within the scope of the PWA. Now the canonical app URL will always have a '/'. Prosody/mod_http should take care of redirecting existing links without the trailing / to the new URL. If you have an installation at https://prosody/conversejs then it is now at https://prosody/conversejs/ (the first URL will now redirect to the second URL if you use it). The alternative would be to make the PWA scope include the parent, i.e. the whole of https://prosody/ in this case. This might get messy if other PWAs are provided by the same site or Prosody installation, however.
author Matthew Wild <mwild1@gmail.com>
date Tue, 11 Feb 2025 13:18:38 +0000
parent 4655:7f61d89a594d
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
4655
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
1 local array = require"util.array";
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
2 local st = require"util.stanza";
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
3
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
4 local tos_version = assert(module:get_option("tos_version"), "tos_version must be set")
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
5
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
6 local status_storage;
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
7 if prosody.process_type == "prosody" or prosody.shutdown then
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
8 status_storage = module:open_store("tos_status", "keyval")
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
9 end
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
10
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
11 local documents = array{};
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
12
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
13 local function validate_doc(doc)
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
14 if not doc.title or not doc.sources or #doc.sources < 1 then
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
15 return false, "document needs to have a title and at least one source"
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
16 end
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
17 for _, source in ipairs(doc.sources) do
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
18 if not source.url or not source.type then
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
19 return false, "document " .. doc.title .. " has a source without url or type"
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
20 end
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
21 end
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
22 return true, doc
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
23 end
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
24
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
25 for _, doc in ipairs(assert(module:get_option("tos_documents"), "tos_documents option is required")) do
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
26 local ok, doc_or_err = validate_doc(doc)
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
27 if not ok then
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
28 error("invalid TOS document: "..doc_or_err)
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
29 end
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
30 documents:push(doc);
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
31 end
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
32
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
33 local function send_tos_push(session)
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
34 local to = session.username .. "@" .. session.host .. "/" .. session.resource;
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
35 local push = st.message({
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
36 type = "headline",
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
37 to = to,
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
38 }, "In order to continue to use the service, you have to accept the current version of the Terms of Service.");
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
39 local tos = push:tag("tos-push", { xmlns = "urn:xmpp:tos:0" }):tag("tos", { version = tos_version });
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
40 for _, doc in ipairs(documents) do
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
41 local doc_tag = tos:tag("document"):text_tag("title", doc.title);
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
42 for _, source in ipairs(doc.sources) do
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
43 doc_tag:tag("source", { url = source.url, type = source.type }):up();
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
44 end
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
45 doc_tag:up();
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
46 end
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
47 tos:up():up();
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
48 module:send(push);
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
49 end
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
50
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
51 local function check_tos(event)
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
52 local user = event.origin.username
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
53 assert(user)
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
54 local tos_status = status_storage:get(user)
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
55 if tos_status and tos_status.version and tos_status.version == tos_version then
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
56 module:log("debug", "user %s has signed the current tos", user);
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
57 return
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
58 end
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
59 module:log("debug", "user %s has not signed the current tos, sending tos push", user);
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
60 send_tos_push(event.origin)
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
61 end
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
62
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
63 local function handle_accept_tos_iq(event)
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
64 local user = event.origin.username;
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
65 assert(user);
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
66 local accept = event.stanza.tags[1];
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
67 local version = accept.attr["version"];
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
68 module:log("debug", "user %s has accepted ToS version %s", user, version);
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
69 if version ~= tos_version then
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
70 local reply = st.error_reply(event.stanza, "modify", "not-allowed", "Only the most recent version of the ToS can be accepted");
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
71 module:log("debug", "%s is not the most recent version (%s), rejecting", version, tos_version);
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
72 event.origin.send(reply);
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
73 return true;
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
74 end
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
75
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
76 status_storage:set(user, { version = tos_version });
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
77 local reply = st.reply(event.stanza);
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
78 event.origin.send(reply);
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
79 return true;
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
80 end
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
81
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
82 module:hook("presence/initial", check_tos, -100);
7f61d89a594d mod_tos: Initial draft
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
83 module:hook("iq-set/bare/urn:xmpp:tos:0:accept", handle_accept_tos_iq);