Mercurial > prosody-modules
comparison mod_invites_register/mod_invites_register.lua @ 4106:d34572047488
mod_invites_register: New module to allow IBR with invite tokens
author | Matthew Wild <mwild1@gmail.com> |
---|---|
date | Fri, 11 Sep 2020 16:30:51 +0100 |
parents | |
children | d3c7be9e36d9 |
comparison
equal
deleted
inserted
replaced
4105:233e170eb027 | 4106:d34572047488 |
---|---|
1 local st = require "util.stanza"; | |
2 local jid_split = require "util.jid".split; | |
3 local jid_bare = require "util.jid".bare; | |
4 local rostermanager = require "core.rostermanager"; | |
5 | |
6 local require_encryption = module:get_option_boolean("c2s_require_encryption", | |
7 module:get_option_boolean("require_encryption", false)); | |
8 local invite_only = module:get_option_boolean("registration_invite_only", true); | |
9 | |
10 local invites; | |
11 if prosody.shutdown then -- COMPAT hack to detect prosodyctl | |
12 invites = module:depends("invites"); | |
13 end | |
14 | |
15 local invite_stream_feature = st.stanza("register", { xmlns = "urn:xmpp:invite" }):up(); | |
16 module:hook("stream-features", function(event) | |
17 local session, features = event.origin, event.features; | |
18 | |
19 -- Advertise to unauthorized clients only. | |
20 if session.type ~= "c2s_unauthed" or (require_encryption and not session.secure) then | |
21 return | |
22 end | |
23 | |
24 features:add_child(invite_stream_feature); | |
25 end); | |
26 | |
27 -- XEP-0379: Pre-Authenticated Roster Subscription | |
28 module:hook("presence/bare", function (event) | |
29 local stanza = event.stanza; | |
30 if stanza.attr.type ~= "subscribe" then return end | |
31 | |
32 local preauth = stanza:get_child("preauth", "urn:xmpp:pars:0"); | |
33 if not preauth then return end | |
34 local token = preauth.attr.token; | |
35 if not token then return end | |
36 | |
37 local username, host = jid_split(stanza.attr.to); | |
38 | |
39 local invite, err = invites.get(token, username); | |
40 | |
41 if not invite then | |
42 module:log("debug", "Got invalid token, error: %s", err); | |
43 return; | |
44 end | |
45 | |
46 local contact = jid_bare(stanza.attr.from); | |
47 | |
48 module:log("debug", "Approving inbound subscription to %s from %s", username, contact); | |
49 if rostermanager.set_contact_pending_in(username, host, contact, stanza) then | |
50 if rostermanager.subscribed(username, host, contact) then | |
51 invite:use(); | |
52 rostermanager.roster_push(username, host, contact); | |
53 | |
54 -- Send back a subscription request (goal is mutual subscription) | |
55 if not rostermanager.is_user_subscribed(username, host, contact) | |
56 and not rostermanager.is_contact_pending_out(username, host, contact) then | |
57 module:log("debug", "Sending automatic subscription request to %s from %s", contact, username); | |
58 if rostermanager.set_contact_pending_out(username, host, contact) then | |
59 rostermanager.roster_push(username, host, contact); | |
60 module:send(st.presence({type = "subscribe", to = contact })); | |
61 else | |
62 module:log("warn", "Failed to set contact pending out for %s", username); | |
63 end | |
64 end | |
65 end | |
66 end | |
67 end, 1); | |
68 | |
69 -- Client is submitting a preauth token to allow registration | |
70 module:hook("stanza/iq/urn:xmpp:pars:0:preauth", function(event) | |
71 local preauth = event.stanza.tags[1]; | |
72 local token = preauth.attr.token; | |
73 local validated_invite = invites.get(token); | |
74 if not validated_invite then | |
75 local reply = st.error_reply(event.stanza, "cancel", "forbidden", "The invite token is invalid or expired"); | |
76 event.origin.send(reply); | |
77 return true; | |
78 end | |
79 event.origin.validated_invite = validated_invite; | |
80 local reply = st.reply(event.stanza); | |
81 event.origin.send(reply); | |
82 return true; | |
83 end); | |
84 | |
85 -- Registration attempt - ensure a valid preauth token has been supplied | |
86 module:hook("user-registering", function (event) | |
87 local validated_invite = event.validated_invite or (event.session and event.session.validated_invite); | |
88 if invite_only and not validated_invite then | |
89 event.allowed = false; | |
90 event.reason = "Registration on this server is through invitation only"; | |
91 return; | |
92 end | |
93 if validated_invite.additional_data and validated_invite.additional_data.allow_reset then | |
94 event.allow_reset = validated_invite.additional_data.allow_reset; | |
95 end | |
96 end); | |
97 | |
98 -- Make a *one-way* subscription. User will see when contact is online, | |
99 -- contact will not see when user is online. | |
100 function subscribe(host, user_username, contact_username) | |
101 local user_jid = user_username.."@"..host; | |
102 local contact_jid = contact_username.."@"..host; | |
103 -- Update user's roster to say subscription request is pending... | |
104 rostermanager.set_contact_pending_out(user_username, host, contact_jid); | |
105 -- Update contact's roster to say subscription request is pending... | |
106 rostermanager.set_contact_pending_in(contact_username, host, user_jid); | |
107 -- Update contact's roster to say subscription request approved... | |
108 rostermanager.subscribed(contact_username, host, user_jid); | |
109 -- Update user's roster to say subscription request approved... | |
110 rostermanager.process_inbound_subscription_approval(user_username, host, contact_jid); | |
111 end | |
112 | |
113 -- Make a mutual subscription between jid1 and jid2. Each JID will see | |
114 -- when the other one is online. | |
115 function subscribe_both(host, user1, user2) | |
116 subscribe(host, user1, user2); | |
117 subscribe(host, user2, user1); | |
118 end | |
119 | |
120 -- Registration successful, if there was a preauth token, mark it as used | |
121 module:hook("user-registered", function (event) | |
122 local validated_invite = event.validated_invite or (event.session and event.session.validated_invite); | |
123 if not validated_invite then | |
124 return; | |
125 end | |
126 local inviter_username = validated_invite.inviter; | |
127 local contact_username = event.username; | |
128 validated_invite:use(); | |
129 | |
130 if inviter_username then | |
131 module:log("debug", "Creating mutual subscription between %s and %s", inviter_username, contact_username); | |
132 subscribe_both(module.host, inviter_username, contact_username); | |
133 end | |
134 | |
135 if validated_invite.additional_data then | |
136 module:log("debug", "Importing roles from invite"); | |
137 local roles = validated_invite.additional_data.roles; | |
138 if roles then | |
139 module:open_store("roles"):set(contact_username, roles); | |
140 end | |
141 end | |
142 end); | |
143 | |
144 -- Equivalent of user-registered but for when the account already existed | |
145 -- (i.e. password reset) | |
146 module:hook("user-password-reset", function (event) | |
147 local validated_invite = event.validated_invite or (event.session and event.session.validated_invite); | |
148 if not validated_invite then | |
149 return; | |
150 end | |
151 validated_invite:use(); | |
152 end); | |
153 |