1 |
|
%% Main module other parts of MongooseIM should use to access the domain |
2 |
|
%% management. |
3 |
|
-module(mongoose_domain_api). |
4 |
|
|
5 |
|
-include("mongoose_logger.hrl"). |
6 |
|
|
7 |
|
-export([get_host_type/1]). |
8 |
|
|
9 |
|
%% external domain API for GraphQL or REST handlers |
10 |
|
-export([insert_domain/2, |
11 |
|
delete_domain/2, |
12 |
|
request_delete_domain/2, |
13 |
|
disable_domain/1, |
14 |
|
enable_domain/1, |
15 |
|
get_domain_details/1, |
16 |
|
check_host_type_and_get_domains/1]). |
17 |
|
|
18 |
|
%% external domain admin API for GraphQL or REST handlers |
19 |
|
-export([set_domain_password/2, |
20 |
|
delete_domain_password/1]). |
21 |
|
|
22 |
|
%% domain API |
23 |
|
-export([get_domain_host_type/1, |
24 |
|
get_all_static/0, |
25 |
|
get_domains_by_host_type/1]). |
26 |
|
|
27 |
|
%% domain admin API |
28 |
|
-export([check_domain_password/2]). |
29 |
|
|
30 |
|
%% subdomain API |
31 |
|
-export([register_subdomain/3, |
32 |
|
unregister_subdomain/2, |
33 |
|
get_subdomain_host_type/1, |
34 |
|
get_subdomain_info/1, |
35 |
|
get_all_subdomains_for_domain/1]). |
36 |
|
|
37 |
|
%% Helper for remove_domain |
38 |
|
-export([remove_domain_wrapper/3, |
39 |
|
do_delete_domain_in_progress/2]). |
40 |
|
|
41 |
|
%% For testing |
42 |
|
-export([get_all_dynamic/0]). |
43 |
|
|
44 |
|
-ignore_xref([get_all_static/0]). |
45 |
|
-ignore_xref([get_all_dynamic/0]). |
46 |
|
|
47 |
|
-type status() :: enabled | disabled | deleting. |
48 |
|
-type domain() :: jid:lserver(). |
49 |
|
-type host_type() :: mongooseim:host_type(). |
50 |
|
-type subdomain_pattern() :: mongoose_subdomain_utils:subdomain_pattern(). |
51 |
|
-type remove_domain_acc() :: #{failed := [module()]}. |
52 |
|
|
53 |
|
-type domain_info() :: #{domain := domain(), host_type => host_type(), status => status()}. |
54 |
|
|
55 |
|
-type insert_result() :: {ok, domain_info()} | |
56 |
|
{static | unknown_host_type | duplicate, iodata()}. |
57 |
|
-type delete_result() :: {ok, domain_info()} | |
58 |
|
{static | unknown_host_type | not_found | wrong_host_type, iodata()}. |
59 |
|
-type set_status_result() :: {ok, domain_info()} | |
60 |
|
{static | unknown_host_type | not_found | deleted, iodata()}. |
61 |
|
-type get_domains_result() :: {ok, [domain()]} | {unknown_host_type, iodata()}. |
62 |
|
-type get_domain_details_result() :: {ok, domain_info()} | {static | not_found, iodata()}. |
63 |
|
|
64 |
|
-export_type([status/0, remove_domain_acc/0]). |
65 |
|
|
66 |
|
-spec insert_domain(domain(), host_type()) -> insert_result(). |
67 |
|
insert_domain(Domain, HostType) -> |
68 |
89 |
M = #{domain => Domain, host_type => HostType}, |
69 |
89 |
fold(M, [fun check_domain/1, fun check_host_type/1, |
70 |
|
fun do_insert_domain/1, fun force_check/1, fun return_domain/1]). |
71 |
|
|
72 |
|
-spec delete_domain(domain(), host_type()) -> delete_result(). |
73 |
|
delete_domain(Domain, HostType) -> |
74 |
33 |
delete_domain(Domain, HostType, sync). |
75 |
|
|
76 |
|
-spec request_delete_domain(domain(), host_type()) -> delete_result(). |
77 |
|
request_delete_domain(Domain, HostType) -> |
78 |
11 |
delete_domain(Domain, HostType, async). |
79 |
|
|
80 |
|
-spec delete_domain(domain(), host_type(), sync | async) -> delete_result(). |
81 |
|
delete_domain(Domain, HostType, RequestType) -> |
82 |
44 |
M = #{domain => Domain, host_type => HostType, request_type => RequestType}, |
83 |
44 |
fold(M, [fun check_domain/1, fun check_host_type/1, fun set_domain_for_deletion/1, |
84 |
|
fun force_check/1, fun do_delete_domain/1, fun return_domain/1]). |
85 |
|
|
86 |
|
-spec disable_domain(domain()) -> set_status_result(). |
87 |
|
disable_domain(Domain) -> |
88 |
19 |
M = #{domain => Domain, status => disabled}, |
89 |
19 |
fold(M, [fun check_domain/1, fun set_status/1, fun force_check/1, fun return_domain/1]). |
90 |
|
|
91 |
|
-spec enable_domain(domain()) -> set_status_result(). |
92 |
|
enable_domain(Domain) -> |
93 |
19 |
M = #{domain => Domain, status => enabled}, |
94 |
19 |
fold(M, [fun check_domain/1, fun set_status/1, fun force_check/1, fun return_domain/1]). |
95 |
|
|
96 |
|
-spec check_host_type_and_get_domains(host_type()) -> get_domains_result(). |
97 |
|
check_host_type_and_get_domains(HostType) -> |
98 |
6 |
M = #{host_type => HostType}, |
99 |
6 |
fold(M, [fun check_host_type/1, fun get_domains/1]). |
100 |
|
|
101 |
|
-spec get_domain_details(domain()) -> get_domain_details_result(). |
102 |
|
get_domain_details(Domain) -> |
103 |
15 |
M = #{domain => Domain}, |
104 |
15 |
fold(M, [fun check_domain/1, fun select_domain/1, fun return_domain/1]). |
105 |
|
|
106 |
|
check_domain(M = #{domain := Domain}) -> |
107 |
186 |
case mongoose_domain_core:is_static(Domain) of |
108 |
|
true -> |
109 |
21 |
{static, <<"Domain is static">>}; |
110 |
|
false -> |
111 |
165 |
M |
112 |
|
end. |
113 |
|
|
114 |
|
check_host_type(M = #{host_type := HostType}) -> |
115 |
128 |
case mongoose_domain_core:is_host_type_allowed(HostType) of |
116 |
|
true -> |
117 |
118 |
M; |
118 |
|
false -> |
119 |
10 |
{unknown_host_type, <<"Unknown host type">>} |
120 |
|
end. |
121 |
|
|
122 |
|
select_domain(M = #{domain := Domain}) -> |
123 |
13 |
case mongoose_domain_sql:select_domain(Domain) of |
124 |
|
{ok, DomainDetails} -> |
125 |
5 |
maps:merge(M, DomainDetails); |
126 |
|
{error, not_found} -> |
127 |
8 |
{not_found, <<"Given domain does not exist">>} |
128 |
|
end. |
129 |
|
|
130 |
|
do_insert_domain(M = #{domain := Domain, host_type := HostType}) -> |
131 |
79 |
case mongoose_domain_sql:insert_domain(Domain, HostType) of |
132 |
|
ok -> |
133 |
71 |
M; |
134 |
|
{error, duplicate} -> |
135 |
6 |
{duplicate, <<"Domain already exists">>} |
136 |
|
end. |
137 |
|
|
138 |
|
set_domain_for_deletion(M = #{domain := Domain, host_type := HostType}) -> |
139 |
35 |
case mongoose_domain_sql:set_domain_for_deletion(Domain, HostType) of |
140 |
|
ok -> |
141 |
22 |
M; |
142 |
|
{error, wrong_host_type} -> |
143 |
5 |
{wrong_host_type, <<"Wrong host type was provided">>}; |
144 |
|
{error, not_found} -> |
145 |
8 |
{not_found, <<"Given domain does not exist">>} |
146 |
|
end. |
147 |
|
|
148 |
|
force_check(M) -> |
149 |
110 |
service_domain_db:force_check_for_updates(), |
150 |
110 |
M. |
151 |
|
|
152 |
|
do_delete_domain(M = #{domain := Domain, host_type := HostType, request_type := RequestType}) -> |
153 |
22 |
mongoose_domain_sql:delete_domain_admin(Domain), |
154 |
22 |
case RequestType of |
155 |
|
sync -> |
156 |
17 |
do_delete_domain_in_progress(Domain, HostType), |
157 |
15 |
M#{status => deleted}; |
158 |
|
async -> |
159 |
5 |
mongoose_domain_db_cleaner:request_delete_domain(Domain, HostType), |
160 |
5 |
M#{status => deleting} |
161 |
|
end. |
162 |
|
|
163 |
|
set_status(M = #{domain := Domain, status := Status}) -> |
164 |
30 |
case mongoose_domain_sql:set_status(Domain, Status) of |
165 |
|
{error, unknown_host_type} -> |
166 |
2 |
{unknown_host_type, <<"Unknown host type">>}; |
167 |
|
{error, domain_deleted} -> |
168 |
1 |
{deleted, <<"Domain has been deleted">>}; |
169 |
|
{error, not_found} -> |
170 |
8 |
{not_found, <<"Given domain does not exist">>}; |
171 |
|
ok -> |
172 |
17 |
M |
173 |
|
end. |
174 |
|
|
175 |
|
return_domain(M) -> |
176 |
113 |
{ok, maps:with([domain, host_type, status], M)}. |
177 |
|
|
178 |
|
get_domains(#{host_type := HostType}) -> |
179 |
4 |
{ok, get_domains_by_host_type(HostType)}. |
180 |
|
|
181 |
|
-spec do_delete_domain_in_progress(domain(), host_type()) -> ok. |
182 |
|
do_delete_domain_in_progress(Domain, HostType) -> |
183 |
22 |
#{failed := []} = mongoose_hooks:remove_domain(HostType, Domain), |
184 |
22 |
ok = mongoose_domain_sql:delete_domain(Domain, HostType). |
185 |
|
|
186 |
|
%% Domain should be nameprepped using `jid:nameprep' |
187 |
|
-spec get_host_type(domain()) -> |
188 |
|
{ok, host_type()} | {error, not_found}. |
189 |
|
get_host_type(Domain) -> |
190 |
60961 |
case get_domain_host_type(Domain) of |
191 |
55660 |
{ok, HostType} -> {ok, HostType}; |
192 |
|
{error, not_found} -> |
193 |
5301 |
get_subdomain_host_type(Domain) |
194 |
|
end. |
195 |
|
|
196 |
|
%% Domain should be nameprepped using `jid:nameprep' |
197 |
|
-spec get_domain_host_type(domain()) -> |
198 |
|
{ok, host_type()} | {error, not_found}. |
199 |
|
get_domain_host_type(Domain) -> |
200 |
128950 |
mongoose_domain_core:get_host_type(Domain). |
201 |
|
|
202 |
|
%% Subdomain should be nameprepped using `jid:nameprep' |
203 |
|
-spec get_subdomain_host_type(domain()) -> |
204 |
|
{ok, host_type()} | {error, not_found}. |
205 |
|
get_subdomain_host_type(Subdomain) -> |
206 |
10854 |
mongoose_subdomain_core:get_host_type(Subdomain). |
207 |
|
|
208 |
|
%% Subdomain should be nameprepped using `jid:nameprep' |
209 |
|
-spec get_subdomain_info(domain()) -> |
210 |
|
{ok, mongoose_subdomain_core:subdomain_info()} | {error, not_found}. |
211 |
|
get_subdomain_info(Subdomain) -> |
212 |
3656 |
mongoose_subdomain_core:get_subdomain_info(Subdomain). |
213 |
|
|
214 |
|
%% Get the list of the host_types provided during initialisation |
215 |
|
%% This has complexity N, where N is the number of online domains. |
216 |
|
-spec get_all_static() -> [{domain(), host_type()}]. |
217 |
|
get_all_static() -> |
218 |
2 |
mongoose_domain_core:get_all_static(). |
219 |
|
|
220 |
|
%% Get domains, loaded from DB to this node |
221 |
|
-spec get_all_dynamic() -> [{domain(), host_type()}]. |
222 |
|
get_all_dynamic() -> |
223 |
1 |
mongoose_domain_core:get_all_dynamic(). |
224 |
|
|
225 |
|
%% Get the list of the host_types provided during initialisation |
226 |
|
%% This has complexity N, where N is the number of online domains. |
227 |
|
-spec get_domains_by_host_type(host_type()) -> [domain()]. |
228 |
|
get_domains_by_host_type(HostType) -> |
229 |
107 |
mongoose_domain_core:get_domains_by_host_type(HostType). |
230 |
|
|
231 |
|
-type password() :: binary(). |
232 |
|
|
233 |
|
-spec check_domain_password(domain(), password()) -> ok | {error, wrong_password | not_found}. |
234 |
|
check_domain_password(Domain, Password) -> |
235 |
468 |
case mongoose_domain_sql:select_domain_admin(Domain) of |
236 |
|
{ok, {Domain, PassDetails}} -> |
237 |
467 |
case do_check_domain_password(Password, PassDetails) of |
238 |
|
true -> |
239 |
450 |
ok; |
240 |
|
false -> |
241 |
17 |
{error, wrong_password} |
242 |
|
end; |
243 |
|
{error, not_found} -> |
244 |
1 |
{error, not_found} |
245 |
|
end. |
246 |
|
|
247 |
|
do_check_domain_password(Password, PassDetails) -> |
248 |
467 |
case mongoose_scram:deserialize(PassDetails) of |
249 |
|
{ok, Scram} -> |
250 |
467 |
mongoose_scram:check_password(Password, Scram); |
251 |
|
{error, _Reason} -> |
252 |
:-( |
false |
253 |
|
end. |
254 |
|
|
255 |
|
-spec set_domain_password(domain(), password()) -> {ok | not_found, iodata()}. |
256 |
|
set_domain_password(Domain, Password) -> |
257 |
34 |
case get_host_type(Domain) of |
258 |
|
{ok, _} -> |
259 |
31 |
ok = mongoose_domain_sql:set_domain_admin(Domain, Password), |
260 |
31 |
{ok, <<"Domain password set successfully">>}; |
261 |
|
{error, not_found} -> |
262 |
3 |
{not_found, <<"Given domain does not exist or is disabled">>} |
263 |
|
end. |
264 |
|
|
265 |
|
-spec delete_domain_password(domain()) -> {ok, iodata()}. |
266 |
|
delete_domain_password(Domain) -> |
267 |
9 |
case mongoose_domain_sql:delete_domain_admin(Domain) of |
268 |
|
ok -> |
269 |
5 |
{ok, <<"Domain password deleted successfully">>}; |
270 |
|
{error, not_found} -> |
271 |
4 |
{not_found, <<"Domain password does not exist">>} |
272 |
|
end. |
273 |
|
|
274 |
|
-spec register_subdomain(host_type(), subdomain_pattern(), |
275 |
|
mongoose_packet_handler:t()) -> |
276 |
|
ok | {error, already_registered | subdomain_already_exists}. |
277 |
|
register_subdomain(HostType, SubdomainPattern, PacketHandler) -> |
278 |
601 |
mongoose_subdomain_core:register_subdomain(HostType, SubdomainPattern, |
279 |
|
PacketHandler). |
280 |
|
|
281 |
|
-spec unregister_subdomain(host_type(), subdomain_pattern()) -> ok. |
282 |
|
unregister_subdomain(HostType, SubdomainPattern) -> |
283 |
603 |
mongoose_subdomain_core:unregister_subdomain(HostType, SubdomainPattern). |
284 |
|
|
285 |
|
-spec get_all_subdomains_for_domain(domain()) -> |
286 |
|
[mongoose_subdomain_core:subdomain_info()]. |
287 |
|
get_all_subdomains_for_domain(Domain) -> |
288 |
43 |
mongoose_subdomain_core:get_all_subdomains_for_domain(Domain). |
289 |
|
|
290 |
|
-spec remove_domain_wrapper(remove_domain_acc(), fun(() -> remove_domain_acc()), module()) -> |
291 |
|
{ok | stop, remove_domain_acc()}. |
292 |
|
remove_domain_wrapper(Acc, F, Module) -> |
293 |
54 |
try F() of |
294 |
53 |
Acc -> {ok, Acc} |
295 |
|
catch C:R:S -> |
296 |
1 |
?LOG_ERROR(#{what => hook_failed, |
297 |
|
text => <<"Error running hook">>, |
298 |
|
module => Module, |
299 |
:-( |
class => C, reason => R, stacktrace => S}), |
300 |
1 |
{stop, Acc#{failed := [Module | maps:get(failed, Acc)]}} |
301 |
|
end. |
302 |
|
|
303 |
|
fold({_, _} = Result, _) -> |
304 |
186 |
Result; |
305 |
|
fold(M, [Step | Rest]) when is_map(M) -> |
306 |
720 |
fold(Step(M), Rest). |