소스 검색

feat: Uses mod_external_services supporting urn:xmpp:extdisco:2. (#8455)

* feat: Uses mod_external_services supporting urn:xmpp:extdisco:2.

The old mod_turncredentials.lua is left to continue working for those using old installs.
New install will start using the new module which will no longer be needed with prosody 0.12.

https://hg.prosody.im/prosody-modules/file/4841cf3fded5/mod_external_services/mod_external_services.lua

* squash: Updates ljm to support urn:xmpp:extdisco:2.
master
Дамян Минков 4 년 전
부모
커밋
01c55bdb15
No account linked to committer's email address
4개의 변경된 파일242개의 추가작업 그리고 10개의 파일을 삭제
  1. 6
    7
      doc/debian/jitsi-meet-prosody/prosody.cfg.lua-jvb.example
  2. 2
    2
      package-lock.json
  3. 1
    1
      package.json
  4. 233
    0
      resources/prosody-plugins/mod_external_services.lua

+ 6
- 7
doc/debian/jitsi-meet-prosody/prosody.cfg.lua-jvb.example 파일 보기

@@ -3,12 +3,11 @@ plugin_paths = { "/usr/share/jitsi-meet/prosody-plugins/" }
3 3
 -- domain mapper options, must at least have domain base set to use the mapper
4 4
 muc_mapper_domain_base = "jitmeet.example.com";
5 5
 
6
-turncredentials_secret = "__turnSecret__";
7
-
8
-turncredentials = {
9
-    { type = "stun", host = "jitmeet.example.com", port = "3478" },
10
-    { type = "turn", host = "jitmeet.example.com", port = "3478", transport = "udp" },
11
-    { type = "turns", host = "jitmeet.example.com", port = "5349", transport = "tcp" }
6
+external_service_secret = "__turnSecret__";
7
+external_services = {
8
+     { type = "stun", host = "jitmeet.example.com", port = 3478 },
9
+     { type = "turn", host = "jitmeet.example.com", port = 3478, transport = "udp", secret = true, ttl = 86400, algorithm = "turn" },
10
+     { type = "turns", host = "jitmeet.example.com", port = 5349, transport = "tcp", secret = true, ttl = 86400, algorithm = "turn" }
12 11
 };
13 12
 
14 13
 cross_domain_bosh = false;
@@ -44,7 +43,7 @@ VirtualHost "jitmeet.example.com"
44 43
         "pubsub";
45 44
         "ping"; -- Enable mod_ping
46 45
         "speakerstats";
47
-        "turncredentials";
46
+        "external_services";
48 47
         "conference_duration";
49 48
         "muc_lobby_rooms";
50 49
     }

+ 2
- 2
package-lock.json 파일 보기

@@ -10816,8 +10816,8 @@
10816 10816
       }
10817 10817
     },
10818 10818
     "lib-jitsi-meet": {
10819
-      "version": "github:jitsi/lib-jitsi-meet#94ac35ae818093896e639e74f5fc389b488206a0",
10820
-      "from": "github:jitsi/lib-jitsi-meet#94ac35ae818093896e639e74f5fc389b488206a0",
10819
+      "version": "github:jitsi/lib-jitsi-meet#831716c1601da259905e58a9db23ebdcea3f1ad8",
10820
+      "from": "github:jitsi/lib-jitsi-meet#831716c1601da259905e58a9db23ebdcea3f1ad8",
10821 10821
       "requires": {
10822 10822
         "@jitsi/js-utils": "1.0.2",
10823 10823
         "@jitsi/sdp-interop": "1.0.3",

+ 1
- 1
package.json 파일 보기

@@ -56,7 +56,7 @@
56 56
     "jquery-i18next": "1.2.1",
57 57
     "js-md5": "0.6.1",
58 58
     "jwt-decode": "2.2.0",
59
-    "lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#94ac35ae818093896e639e74f5fc389b488206a0",
59
+    "lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#831716c1601da259905e58a9db23ebdcea3f1ad8",
60 60
     "libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d",
61 61
     "lodash": "4.17.19",
62 62
     "moment": "2.19.4",

+ 233
- 0
resources/prosody-plugins/mod_external_services.lua 파일 보기

@@ -0,0 +1,233 @@
1
+
2
+local dt = require "util.datetime";
3
+local base64 = require "util.encodings".base64;
4
+local hashes = require "util.hashes";
5
+local st = require "util.stanza";
6
+local jid = require "util.jid";
7
+local array = require "util.array";
8
+
9
+local default_host = module:get_option_string("external_service_host", module.host);
10
+local default_port = module:get_option_number("external_service_port");
11
+local default_secret = module:get_option_string("external_service_secret");
12
+local default_ttl = module:get_option_number("external_service_ttl", 86400);
13
+
14
+local configured_services = module:get_option_array("external_services", {});
15
+
16
+local access = module:get_option_set("external_service_access", {});
17
+
18
+-- https://tools.ietf.org/html/draft-uberti-behave-turn-rest-00
19
+local function behave_turn_rest_credentials(srv, item, secret)
20
+	local ttl = default_ttl;
21
+	if type(item.ttl) == "number" then
22
+		ttl = item.ttl;
23
+	end
24
+	local expires = srv.expires or os.time() + ttl;
25
+	local username;
26
+	if type(item.username) == "string" then
27
+		username = string.format("%d:%s", expires, item.username);
28
+	else
29
+		username = string.format("%d", expires);
30
+	end
31
+	srv.username = username;
32
+	srv.password = base64.encode(hashes.hmac_sha1(secret, srv.username));
33
+end
34
+
35
+local algorithms = {
36
+	turn = behave_turn_rest_credentials;
37
+}
38
+
39
+-- filter config into well-defined service records
40
+local function prepare(item)
41
+	if type(item) ~= "table" then
42
+		module:log("error", "Service definition is not a table: %q", item);
43
+		return nil;
44
+	end
45
+
46
+	local srv = {
47
+		type = nil;
48
+		transport = nil;
49
+		host = default_host;
50
+		port = default_port;
51
+		username = nil;
52
+		password = nil;
53
+		restricted = nil;
54
+		expires = nil;
55
+	};
56
+
57
+	if type(item.type) == "string" then
58
+		srv.type = item.type;
59
+	else
60
+		module:log("error", "Service missing mandatory 'type' field: %q", item);
61
+		return nil;
62
+	end
63
+	if type(item.transport) == "string" then
64
+		srv.transport = item.transport;
65
+	end
66
+	if type(item.host) == "string" then
67
+		srv.host = item.host;
68
+	end
69
+	if type(item.port) == "number" then
70
+		srv.port = item.port;
71
+	end
72
+	if type(item.username) == "string" then
73
+		srv.username = item.username;
74
+	end
75
+	if type(item.password) == "string" then
76
+		srv.password = item.password;
77
+		srv.restricted = true;
78
+	end
79
+	if item.restricted == true then
80
+		srv.restricted = true;
81
+	end
82
+	if type(item.expires) == "number" then
83
+		srv.expires = item.expires;
84
+	elseif type(item.ttl) == "number" then
85
+		srv.expires = os.time() + item.ttl;
86
+	end
87
+	if (item.secret == true and default_secret) or type(item.secret) == "string" then
88
+		local secret_cb = item.credentials_cb or algorithms[item.algorithm] or algorithms[srv.type];
89
+		local secret = item.secret;
90
+		if secret == true then
91
+			secret = default_secret;
92
+		end
93
+		if secret_cb then
94
+			secret_cb(srv, item, secret);
95
+			srv.restricted = true;
96
+		end
97
+	end
98
+	return srv;
99
+end
100
+
101
+function module.load()
102
+	-- Trigger errors on startup
103
+	local services = configured_services / prepare;
104
+	if #services == 0 then
105
+		module:log("warn", "No services configured or all had errors");
106
+	end
107
+end
108
+
109
+-- Ensure only valid items are added in events
110
+local services_mt = {
111
+	__index = getmetatable(array()).__index;
112
+	__newindex = function (self, i, v)
113
+		rawset(self, i, assert(prepare(v), "Invalid service entry added"));
114
+	end;
115
+}
116
+
117
+local function handle_services(event)
118
+	local origin, stanza = event.origin, event.stanza;
119
+	local action = stanza.tags[1];
120
+
121
+	local user_bare = jid.bare(stanza.attr.from);
122
+	local user_host = jid.host(user_bare);
123
+	if not ((access:empty() and origin.type == "c2s") or access:contains(user_bare) or access:contains(user_host)) then
124
+		origin.send(st.error_reply(stanza, "auth", "forbidden"));
125
+		return true;
126
+	end
127
+
128
+	local reply = st.reply(stanza):tag("services", { xmlns = action.attr.xmlns });
129
+	local extras = module:get_host_items("external_service");
130
+	local services = ( configured_services + extras ) / prepare;
131
+
132
+	local requested_type = action.attr.type;
133
+	if requested_type then
134
+		services:filter(function(item)
135
+			return item.type == requested_type;
136
+		end);
137
+	end
138
+
139
+	setmetatable(services, services_mt);
140
+
141
+	module:fire_event("external_service/services", {
142
+			origin = origin;
143
+			stanza = stanza;
144
+			reply = reply;
145
+			requested_type = requested_type;
146
+			services = services;
147
+		});
148
+
149
+	for _, srv in ipairs(services) do
150
+		reply:tag("service", {
151
+				type = srv.type;
152
+				transport = srv.transport;
153
+				host = srv.host;
154
+				port = srv.port and string.format("%d", srv.port) or nil;
155
+				username = srv.username;
156
+				password = srv.password;
157
+				expires = srv.expires and dt.datetime(srv.expires) or nil;
158
+				restricted = srv.restricted and "1" or nil;
159
+			}):up();
160
+	end
161
+
162
+	origin.send(reply);
163
+	return true;
164
+end
165
+
166
+local function handle_credentials(event)
167
+	local origin, stanza = event.origin, event.stanza;
168
+	local action = stanza.tags[1];
169
+
170
+	if origin.type ~= "c2s" then
171
+		origin.send(st.error_reply(stanza, "auth", "forbidden"));
172
+		return true;
173
+	end
174
+
175
+	local reply = st.reply(stanza):tag("credentials", { xmlns = action.attr.xmlns });
176
+	local extras = module:get_host_items("external_service");
177
+	local services = ( configured_services + extras ) / prepare;
178
+	services:filter(function (item)
179
+		return item.restricted;
180
+	end)
181
+
182
+	local requested_credentials = {};
183
+	for service in action:childtags("service") do
184
+		table.insert(requested_credentials, {
185
+				type = service.attr.type;
186
+				host = service.attr.host;
187
+				port = tonumber(service.attr.port);
188
+			});
189
+	end
190
+
191
+	setmetatable(services, services_mt);
192
+	setmetatable(requested_credentials, services_mt);
193
+
194
+	module:fire_event("external_service/credentials", {
195
+			origin = origin;
196
+			stanza = stanza;
197
+			reply = reply;
198
+			requested_credentials = requested_credentials;
199
+			services = services;
200
+		});
201
+
202
+	for req_srv in action:childtags("service") do
203
+		for _, srv in ipairs(services) do
204
+			if srv.type == req_srv.attr.type and srv.host == req_srv.attr.host
205
+				and not req_srv.attr.port or srv.port == tonumber(req_srv.attr.port) then
206
+				reply:tag("service", {
207
+						type = srv.type;
208
+						transport = srv.transport;
209
+						host = srv.host;
210
+						port = srv.port and string.format("%d", srv.port) or nil;
211
+						username = srv.username;
212
+						password = srv.password;
213
+						expires = srv.expires and dt.datetime(srv.expires) or nil;
214
+						restricted = srv.restricted and "1" or nil;
215
+					}):up();
216
+			end
217
+		end
218
+	end
219
+
220
+	origin.send(reply);
221
+	return true;
222
+end
223
+
224
+-- XEP-0215 v0.7
225
+module:add_feature("urn:xmpp:extdisco:2");
226
+module:hook("iq-get/host/urn:xmpp:extdisco:2:services", handle_services);
227
+module:hook("iq-get/host/urn:xmpp:extdisco:2:credentials", handle_credentials);
228
+
229
+-- COMPAT XEP-0215 v0.6
230
+-- Those still on the old version gets to deal with undefined attributes until they upgrade.
231
+module:add_feature("urn:xmpp:extdisco:1");
232
+module:hook("iq-get/host/urn:xmpp:extdisco:1:services", handle_services);
233
+module:hook("iq-get/host/urn:xmpp:extdisco:1:credentials", handle_credentials);

Loading…
취소
저장