./ct_report/coverage/mongoose_config_spec.COVER.html

1 -module(mongoose_config_spec).
2
3 %% entry point - returns the entire spec
4 -export([root/0]).
5
6 %% spec parts used by modules and services
7 -export([wpool_items/0,
8 iqdisc/0]).
9
10 %% callbacks for the 'process' step
11 -export([process_root/1,
12 process_host/1,
13 process_general/1,
14 process_sm_backend/1,
15 process_ctl_access_rule/1,
16 process_listener/2,
17 process_verify_peer/1,
18 process_tls_sni/1,
19 process_xmpp_tls/1,
20 process_fast_tls/1,
21 process_http_handler/2,
22 process_sasl_external/1,
23 process_sasl_mechanism/1,
24 process_auth/1,
25 process_pool/2,
26 process_cassandra_auth/1,
27 process_rdbms_connection/1,
28 process_riak_tls/1,
29 process_cassandra_server/1,
30 process_riak_credentials/1,
31 process_iqdisc/1,
32 process_acl_condition/1,
33 process_s2s_host_policy/1,
34 process_s2s_address/1,
35 process_domain_cert/1]).
36
37 -include("mongoose_config_spec.hrl").
38
39 -type config_node() :: config_section() | config_list() | config_option().
40 -type config_section() :: #section{}.
41 -type config_list() :: #list{}.
42 -type config_option() :: #option{}.
43
44 -type option_type() :: boolean | binary | string | atom | int_or_infinity
45 | int_or_atom | integer | float.
46
47 -type wrapper() :: top_level_config_wrapper() | config_part_wrapper().
48
49 %% Wrap the value in a top-level config option
50 -type top_level_config_wrapper() ::
51 % Config option, see the type below for details
52 config_option_wrapper()
53
54 % Config option, the key is replaced with NewKey
55 | {config_option_wrapper(), NewKey :: atom()}.
56
57 -type config_option_wrapper() ::
58 global_config % [{Key, Value}]
59 | host_config. % Inside host_config: [{{Key, Host}, Value}]
60 % Otherwise: one such option for each configured host
61
62 %% Wrap the value in config part - key-value pair or just a value
63 -type config_part_wrapper() ::
64 default % [{Key, Value}] for section items, [Value] for list items
65 | item % [Value]
66 | remove % [] - the item is ignored
67 | none % just Value - injects elements of Value into the parent section/list
68 | {kv, NewKey :: term()} % [{NewKey, Value}] - replaces the key with NewKey
69 | prepend_key. % [{Key, V1, ..., Vn}] when Value = {V1, ..., Vn}
70
71 %% This option allows to put list/section items in a map
72 -type format_items() ::
73 none % keep the processed items unchanged
74 | map. % convert the processed items (which have to be a KV list) to a map
75
76 -export_type([config_node/0, config_section/0, config_list/0, config_option/0,
77 wrapper/0, format_items/0, option_type/0]).
78
79 %% Config processing functions are annotated with TOML paths
80 %% Path syntax: dotted, like TOML keys with the following additions:
81 %% - '[]' denotes an element in a list
82 %% - '( ... )' encloses an optional prefix
83 %% - '*' is a wildcard for names - usually that name is passed as an argument
84 %% If the path is the same as for the previous function, it is not repeated.
85 %%
86 %% Example: (host_config[].)access.*
87 %% Meaning: either a key in the 'access' section, e.g.
88 %% [access]
89 %% local = ...
90 %% or the same, but prefixed for a specific host, e.g.
91 %% [[host_config]]
92 %% host = "myhost"
93 %% host_config.access
94 %% local = ...
95
96 root() ->
97 82 General = general(),
98 82 Listen = listen(),
99 82 Auth = auth(),
100 82 Modules = modules(),
101 82 S2S = s2s(),
102 82 #section{
103 items = #{<<"general">> => General#section{required = [<<"default_server_domain">>],
104 process = fun ?MODULE:process_general/1,
105 defaults = general_defaults()},
106 <<"listen">> => Listen#section{include = always},
107 <<"auth">> => Auth#section{include = always},
108 <<"outgoing_pools">> => outgoing_pools(),
109 <<"services">> => services(),
110 <<"modules">> => Modules#section{include = always},
111 <<"shaper">> => shaper(),
112 <<"acl">> => acl(),
113 <<"access">> => access(),
114 <<"s2s">> => S2S#section{include = always},
115 <<"host_config">> => #list{items = host_config(),
116 wrap = none}
117 },
118 required = [<<"general">>],
119 process = fun ?MODULE:process_root/1,
120 wrap = none
121 }.
122
123 %% path: host_config[]
124 host_config() ->
125 82 #section{
126 items = #{%% Host is only validated here - it is stored in the path,
127 %% see mongoose_config_parser_toml:item_key/1
128 %%
129 %% for every configured host the host_type of the same name
130 %% is declared automatically. As host_config section is now
131 %% used for changing configuration of the host_type, we don't
132 %% need host option any more. but to stay compatible with an
133 %% old config format we keep host option as well. now it is
134 %% just a synonym to host_type.
135 <<"host">> => #option{type = binary,
136 validate = non_empty,
137 wrap = remove},
138
139 <<"host_type">> => #option{type = binary,
140 validate = non_empty,
141 wrap = remove},
142
143 %% Sections below are allowed in host_config,
144 %% but only options with 'wrap = host_config' are accepted.
145 %% Options with 'wrap = global_config' would be caught by
146 %% mongoose_config_parser_toml:wrap/3
147 <<"general">> => general(),
148 <<"auth">> => auth(),
149 <<"modules">> => modules(),
150 <<"acl">> => acl(),
151 <<"access">> => access(),
152 <<"s2s">> => s2s()
153 },
154 wrap = none
155 }.
156
157 %% path: general
158 general() ->
159 164 #section{
160 items = #{<<"loglevel">> => #option{type = atom,
161 validate = loglevel,
162 wrap = global_config},
163 <<"hosts">> => #list{items = #option{type = binary,
164 validate = non_empty,
165 process = fun ?MODULE:process_host/1},
166 validate = unique,
167 wrap = global_config},
168 <<"host_types">> => #list{items = #option{type = binary,
169 validate = non_empty},
170 validate = unique,
171 wrap = global_config},
172 <<"default_server_domain">> => #option{type = binary,
173 validate = non_empty,
174 process = fun ?MODULE:process_host/1,
175 wrap = global_config},
176 <<"registration_timeout">> => #option{type = int_or_infinity,
177 validate = positive,
178 wrap = global_config},
179 <<"language">> => #option{type = binary,
180 validate = non_empty,
181 wrap = global_config},
182 <<"all_metrics_are_global">> => #option{type = boolean,
183 wrap = global_config},
184 <<"sm_backend">> => #option{type = atom,
185 validate = {module, ejabberd_sm},
186 process = fun ?MODULE:process_sm_backend/1,
187 wrap = global_config},
188 <<"max_fsm_queue">> => #option{type = integer,
189 validate = positive,
190 wrap = global_config},
191 <<"http_server_name">> => #option{type = string,
192 wrap = {global_config, cowboy_server_name}},
193 <<"rdbms_server_type">> => #option{type = atom,
194 validate = {enum, [mssql, pgsql]},
195 wrap = global_config},
196 <<"route_subdomains">> => #option{type = atom,
197 validate = {enum, [s2s]},
198 wrap = host_config},
199 <<"mongooseimctl_access_commands">> => #section{
200 items = #{default => ctl_access_rule()},
201 wrap = global_config},
202 <<"routing_modules">> => #list{items = #option{type = atom,
203 validate = module},
204 wrap = global_config},
205 <<"replaced_wait_timeout">> => #option{type = integer,
206 validate = positive,
207 wrap = host_config},
208 <<"hide_service_name">> => #option{type = boolean,
209 wrap = global_config},
210 <<"domain_certfile">> => #list{items = domain_cert(),
211 format_items = map,
212 wrap = global_config}
213 },
214 wrap = none
215 }.
216
217 general_defaults() ->
218 82 #{<<"loglevel">> => warning,
219 <<"hosts">> => [],
220 <<"host_types">> => [],
221 <<"registration_timeout">> => 600,
222 <<"language">> => <<"en">>,
223 <<"all_metrics_are_global">> => false,
224 <<"sm_backend">> => {mnesia, []},
225 <<"rdbms_server_type">> => generic,
226 <<"mongooseimctl_access_commands">> => [],
227 <<"routing_modules">> => mongoose_router:default_routing_modules(),
228 <<"replaced_wait_timeout">> => 2000,
229 <<"hide_service_name">> => false}.
230
231 ctl_access_rule() ->
232 164 #section{
233 items = #{<<"commands">> => #list{items = #option{type = string}},
234 <<"argument_restrictions">> => #section{
235 items = #{default => #option{type = string}}
236 }
237 },
238 process = fun ?MODULE:process_ctl_access_rule/1,
239 wrap = prepend_key
240 }.
241
242 %% path: general.domain_certfile
243 domain_cert() ->
244 164 #section{
245 items = #{<<"domain">> => #option{type = binary,
246 validate = non_empty},
247 <<"certfile">> => #option{type = string,
248 validate = filename}},
249 required = all,
250 format_items = map,
251 process = fun ?MODULE:process_domain_cert/1
252 }.
253
254 %% path: listen
255 listen() ->
256 82 Keys = [<<"http">>, <<"c2s">>, <<"s2s">>, <<"service">>],
257 82 #section{
258 328 items = maps:from_list([{Key, #list{items = listener(Key),
259 82 wrap = none}} || Key <- Keys]),
260 process = fun mongoose_listener_config:verify_unique_listeners/1,
261 wrap = global_config
262 }.
263
264 %% path: listen.*[]
265 listener(Type) ->
266 328 ExtraItems = listener_items(Type),
267 328 #section{
268 items = ExtraItems#{<<"port">> => #option{type = integer,
269 validate = port},
270 <<"ip_address">> => #option{type = string,
271 validate = ip_address},
272 <<"proto">> => #option{type = atom,
273 validate = {enum, [tcp, udp]}},
274 <<"ip_version">> => #option{type = integer,
275 validate = {enum, [4, 6]}}
276 },
277 format_items = map,
278 required = [<<"port">>],
279 defaults = #{<<"proto">> => tcp},
280 process = fun ?MODULE:process_listener/2
281 }.
282
283 listener_items(<<"http">>) ->
284 82 #{<<"tls">> => http_listener_tls(),
285 <<"transport">> => http_transport(),
286 <<"protocol">> => http_protocol(),
287 <<"handlers">> => http_handlers()
288 };
289 listener_items(Type) ->
290 246 ExtraItems = xmpp_listener_items(Type),
291 246 ExtraItems#{<<"hibernate_after">> => #option{type = integer,
292 validate = non_negative},
293 <<"max_stanza_size">> => #option{type = integer,
294 validate = positive},
295 <<"backlog">> => #option{type = integer,
296 validate = non_negative},
297 <<"proxy_protocol">> => #option{type = boolean},
298 <<"num_acceptors">> => #option{type = integer,
299 validate = positive,
300 wrap = {kv, acceptors_num}}
301 }.
302
303 xmpp_listener_items(<<"c2s">>) ->
304 82 #{<<"access">> => #option{type = atom,
305 validate = non_empty},
306 <<"shaper">> => #option{type = atom,
307 validate = non_empty},
308 <<"xml_socket">> => #option{type = boolean},
309 <<"zlib">> => #option{type = integer,
310 validate = positive},
311 <<"max_fsm_queue">> => #option{type = integer,
312 validate = positive},
313 <<"allowed_auth_methods">> => #list{items = #option{type = atom,
314 validate = {module, ejabberd_auth}}},
315 <<"tls">> => c2s_tls()};
316 xmpp_listener_items(<<"s2s">>) ->
317 82 #{<<"shaper">> => #option{type = atom,
318 validate = non_empty},
319 <<"tls">> => s2s_tls()};
320 xmpp_listener_items(<<"service">>) ->
321 164 #{<<"access">> => #option{type = atom,
322 validate = non_empty},
323 <<"shaper_rule">> => #option{type = atom,
324 validate = non_empty},
325 <<"check_from">> => #option{type = boolean,
326 wrap = {kv, service_check_from}},
327 <<"hidden_components">> => #option{type = boolean},
328 <<"conflict_behaviour">> => #option{type = atom,
329 validate = {enum, [kick_old, disconnect]}},
330 <<"password">> => #option{type = string,
331 validate = non_empty},
332 <<"max_fsm_queue">> => #option{type = integer,
333 validate = positive}}.
334
335 %% path: listen.c2s[].tls
336 c2s_tls() ->
337 82 #section{
338 items = #{
339 %% common
340 <<"module">> => #option{type = atom,
341 validate = {enum, [fast_tls, just_tls]}},
342 <<"mode">> => #option{type = atom,
343 validate = {enum, [tls, starttls, starttls_required]}},
344 <<"verify_peer">> => #option{type = boolean,
345 process = fun ?MODULE:process_verify_peer/1},
346 <<"certfile">> => #option{type = string,
347 validate = filename},
348 <<"cacertfile">> => #option{type = string,
349 validate = filename},
350 <<"dhfile">> => #option{type = string,
351 validate = filename},
352 <<"ciphers">> => #option{type = string},
353
354 %% fast_tls
355 <<"protocol_options">> => #list{items = #option{type = string,
356 validate = non_empty}},
357
358 %% just_tls
359 <<"verify_mode">> => #option{type = atom,
360 validate = {enum, [peer, selfsigned_peer, none]}},
361 <<"disconnect_on_failure">> => #option{type = boolean},
362 <<"crl_files">> => #list{items = #option{type = string,
363 validate = non_empty},
364 wrap = {kv, crlfiles}},
365 <<"password">> => #option{type = string},
366 <<"server_name_indication">> => #option{type = boolean},
367 <<"versions">> => #list{items = #option{type = atom}}
368 },
369 process = fun ?MODULE:process_xmpp_tls/1
370 }.
371
372 %% path: listen.s2s[].tls
373 s2s_tls() ->
374 82 #section{
375 items = #{<<"cacertfile">> => #option{type = string,
376 validate = filename},
377 <<"dhfile">> => #option{type = string,
378 validate = filename},
379 <<"ciphers">> => #option{type = string},
380 <<"protocol_options">> => #list{items = #option{type = string,
381 validate = non_empty}}
382 },
383 process = fun ?MODULE:process_fast_tls/1
384 }.
385
386 %% path: listen.http[].tls
387 http_listener_tls() ->
388 82 Items = tls_items(),
389 82 #section{
390 items = Items#{<<"verify_mode">> => #option{type = atom,
391 validate = {enum, [peer, selfsigned_peer, none]}}
392 },
393 wrap = {kv, ssl},
394 process = fun ?MODULE:process_tls_sni/1
395 }.
396
397 %% path: listen.http[].transport
398 http_transport() ->
399 82 #section{
400 items = #{<<"num_acceptors">> => #option{type = integer,
401 validate = positive},
402 <<"max_connections">> => #option{type = int_or_infinity,
403 validate = non_negative}
404 },
405 wrap = {kv, transport_options}
406 }.
407
408 %% path: listen.http[].protocol
409 http_protocol() ->
410 82 #section{
411 items = #{<<"compress">> => #option{type = boolean}},
412 wrap = {kv, protocol_options}
413 }.
414
415 %% path: listen.http[].handlers
416 http_handlers() ->
417 82 Keys = [<<"mod_websockets">>,
418 <<"lasse_handler">>,
419 <<"cowboy_static">>,
420 <<"mongoose_api">>,
421 <<"mongoose_api_admin">>,
422 <<"mongoose_domain_handler">>,
423 <<"mongoose_graphql_cowboy_handler">>,
424 default],
425 82 #section{
426 656 items = maps:from_list([{Key, #list{items = http_handler(Key),
427 82 wrap = none}} || Key <- Keys]),
428 validate_keys = module,
429 wrap = {kv, modules}
430 }.
431
432 %% path: listen.http[].handlers.*[]
433 http_handler(Key) ->
434 656 ExtraItems = http_handler_items(Key),
435 656 RequiredKeys = case http_handler_required(Key) of
436 246 all -> all;
437 410 [] -> [<<"host">>, <<"path">>]
438 end,
439 656 #section{
440 items = ExtraItems#{<<"host">> => #option{type = string,
441 validate = non_empty},
442 <<"path">> => #option{type = string}
443 },
444 required = RequiredKeys,
445 process = fun ?MODULE:process_http_handler/2
446 }.
447
448 http_handler_items(<<"mod_websockets">>) ->
449 82 #{<<"timeout">> => #option{type = int_or_infinity,
450 validate = non_negative},
451 <<"ping_rate">> => #option{type = integer,
452 validate = positive},
453 <<"max_stanza_size">> => #option{type = int_or_infinity,
454 validate = positive},
455 <<"service">> => #section{items = xmpp_listener_items(<<"service">>),
456 wrap = {kv, ejabberd_service}}};
457 http_handler_items(<<"lasse_handler">>) ->
458 82 #{<<"module">> => #option{type = atom,
459 validate = module}};
460 http_handler_items(<<"cowboy_static">>) ->
461 82 #{<<"type">> => #option{type = atom},
462 <<"app">> => #option{type = atom},
463 <<"content_path">> => #option{type = string}};
464 http_handler_items(<<"mongoose_api">>) ->
465 82 #{<<"handlers">> => #list{items = #option{type = atom,
466 validate = module}}};
467 http_handler_items(<<"mongoose_api_admin">>) ->
468 82 #{<<"username">> => #option{type = binary},
469 <<"password">> => #option{type = binary}};
470 http_handler_items(<<"mongoose_domain_handler">>) ->
471 82 #{<<"username">> => #option{type = binary},
472 <<"password">> => #option{type = binary}};
473 http_handler_items(<<"mongoose_graphql_cowboy_handler">>) ->
474 82 #{<<"username">> => #option{type = binary},
475 <<"password">> => #option{type = binary},
476 <<"schema_endpoint">> => #option{type = binary}};
477 http_handler_items(_) ->
478 82 #{}.
479
480 82 http_handler_required(<<"lasse_handler">>) -> all;
481 82 http_handler_required(<<"cowboy_static">>) -> all;
482 82 http_handler_required(<<"mongoose_api">>) -> all;
483 410 http_handler_required(_) -> [].
484
485 %% path: (host_config[].)auth
486 auth() ->
487 164 Items = maps:from_list([{a2b(Method), ejabberd_auth:config_spec(Method)} ||
488 164 Method <- all_auth_methods()]),
489 164 #section{
490 items = Items#{<<"methods">> => #list{items = #option{type = atom,
491 validate = {module, ejabberd_auth}}},
492 <<"password">> => auth_password(),
493 <<"sasl_external">> =>
494 #list{items = #option{type = atom,
495 process = fun ?MODULE:process_sasl_external/1}},
496 <<"sasl_mechanisms">> =>
497 #list{items = #option{type = atom,
498 validate = {module, cyrsasl},
499 process = fun ?MODULE:process_sasl_mechanism/1}}
500 },
501 format_items = map,
502 defaults = #{<<"sasl_external">> => [standard],
503 <<"sasl_mechanisms">> => cyrsasl:default_modules()},
504 process = fun ?MODULE:process_auth/1,
505 wrap = host_config
506 }.
507
508 %% path: (host_config[].)auth.password
509 auth_password() ->
510 164 #section{
511 items = #{<<"format">> => #option{type = atom,
512 validate = {enum, [scram, plain]}},
513 <<"hash">> => #list{items = #option{type = atom,
514 validate = {enum, [sha, sha224, sha256,
515 sha384, sha512]}},
516 validate = unique_non_empty
517 },
518 <<"scram_iterations">> => #option{type = integer,
519 validate = positive}
520 },
521 format_items = map,
522 defaults = #{<<"format">> => scram,
523 <<"scram_iterations">> => mongoose_scram:iterations()},
524 include = always
525 }.
526
527 %% path: outgoing_pools
528 outgoing_pools() ->
529 82 PoolTypes = [<<"cassandra">>, <<"elastic">>, <<"http">>, <<"ldap">>,
530 <<"rabbit">>, <<"rdbms">>, <<"redis">>, <<"riak">>],
531 82 Items = [{Type, #section{items = #{default => outgoing_pool(Type)},
532 validate_keys = non_empty,
533 82 wrap = none}} || Type <- PoolTypes],
534 82 #section{
535 items = maps:from_list(Items),
536 wrap = global_config
537 }.
538
539 %% path: outgoing_pools.*.*
540 outgoing_pool(Type) ->
541 656 WPool = wpool_items(),
542 656 #section{
543 items = WPool#{<<"scope">> => #option{type = atom,
544 validate = {enum, [global, host, single_host]}},
545 <<"host">> => #option{type = binary,
546 validate = non_empty},
547 <<"connection">> => outgoing_pool_connection(Type)
548 },
549 process = fun ?MODULE:process_pool/2,
550 format_items = map,
551 wrap = item,
552 defaults = maps:merge(#{<<"scope">> => global}, wpool_defaults(Type))
553 }.
554
555 wpool_items() ->
556 820 #{<<"workers">> => #option{type = integer,
557 validate = positive},
558 <<"strategy">> => #option{type = atom,
559 validate = {enum, wpool_strategy_values()}},
560 <<"call_timeout">> => #option{type = integer,
561 validate = positive}
562 }.
563
564 wpool_defaults(<<"cassandra">>) ->
565 82 maps:merge(wpool_defaults(), #{<<"workers">> => 20});
566 wpool_defaults(<<"rdbms">>) ->
567 82 maps:merge(wpool_defaults(), #{<<"call_timeout">> => 60000});
568 wpool_defaults(_) ->
569 492 wpool_defaults().
570
571 wpool_defaults() ->
572 656 #{<<"workers">> => 10,
573 <<"strategy">> => best_worker,
574 <<"call_timeout">> => 5000}.
575
576 %% path: outgoing_pools.*.*.connection
577 outgoing_pool_connection(<<"cassandra">>) ->
578 82 #section{
579 items = #{<<"servers">> => #list{items = cassandra_server()},
580 <<"keyspace">> => #option{type = atom,
581 validate = non_empty},
582 <<"auth">> => #section{items = #{<<"plain">> => cassandra_auth_plain()},
583 required = all,
584 process = fun ?MODULE:process_cassandra_auth/1},
585 <<"tls">> => #section{items = tls_items(),
586 wrap = {kv, ssl},
587 process = fun ?MODULE:process_tls_sni/1}
588 },
589 format_items = map,
590 include = always,
591 defaults = #{<<"servers">> => [{"localhost", 9042}],
592 <<"keyspace">> => mongooseim}
593 };
594 outgoing_pool_connection(<<"elastic">>) ->
595 82 #section{
596 items = #{<<"host">> => #option{type = string,
597 validate = non_empty},
598 <<"port">> => #option{type = integer,
599 validate = port}
600 },
601 format_items = map,
602 include = always,
603 defaults = #{<<"host">> => "localhost",
604 <<"port">> => 9200}
605 };
606 outgoing_pool_connection(<<"http">>) ->
607 82 #section{
608 items = #{<<"host">> => #option{type = string,
609 validate = non_empty,
610 wrap = {kv, server}},
611 <<"path_prefix">> => #option{type = string,
612 validate = non_empty},
613 <<"request_timeout">> => #option{type = integer,
614 validate = non_negative},
615 <<"tls">> => #section{items = tls_items(),
616 wrap = {kv, http_opts},
617 process = fun ?MODULE:process_tls_sni/1}
618 },
619 format_items = map,
620 include = always,
621 defaults = #{<<"path_prefix">> => "/",
622 <<"request_timeout">> => 2000}
623 };
624 outgoing_pool_connection(<<"ldap">>) ->
625 82 #section{
626 items = #{<<"port">> => #option{type = integer,
627 validate = port},
628 <<"rootdn">> => #option{type = binary},
629 <<"password">> => #option{type = binary},
630 <<"encrypt">> => #option{type = atom,
631 validate = {enum, [none, tls]}},
632 <<"servers">> => #list{items = #option{type = string}},
633 <<"connect_interval">> => #option{type = integer,
634 validate = positive},
635 <<"tls">> => #section{items = tls_items(),
636 wrap = {kv, tls_options},
637 process = fun ?MODULE:process_tls_sni/1}
638 },
639 format_items = map,
640 include = always,
641 defaults = #{<<"rootdn">> => <<>>,
642 <<"password">> => <<>>,
643 <<"encrypt">> => none,
644 <<"servers">> => ["localhost"],
645 <<"connect_interval">> => 10000}
646 };
647 outgoing_pool_connection(<<"rabbit">>) ->
648 82 #section{
649 items = #{<<"amqp_host">> => #option{type = string,
650 validate = non_empty},
651 <<"amqp_port">> => #option{type = integer,
652 validate = port},
653 <<"amqp_username">> => #option{type = binary,
654 validate = non_empty},
655 <<"amqp_password">> => #option{type = binary,
656 validate = non_empty},
657 <<"confirms_enabled">> => #option{type = boolean},
658 <<"max_worker_queue_len">> => #option{type = int_or_infinity,
659 validate = non_negative}
660 },
661 format_items = map,
662 include = always,
663 defaults = #{<<"confirms_enabled">> => false,
664 <<"max_worker_queue_len">> => 1000}
665 };
666 outgoing_pool_connection(<<"rdbms">>) ->
667 82 #section{
668 items = #{<<"driver">> => #option{type = atom,
669 validate = {enum, [odbc, pgsql, mysql]}},
670 <<"keepalive_interval">> => #option{type = integer,
671 validate = positive},
672
673 % odbc
674 <<"settings">> => #option{type = string},
675
676 % mysql, pgsql
677 <<"host">> => #option{type = string,
678 validate = non_empty},
679 <<"database">> => #option{type = string,
680 validate = non_empty},
681 <<"username">> => #option{type = string,
682 validate = non_empty},
683 <<"password">> => #option{type = string,
684 validate = non_empty},
685 <<"port">> => #option{type = integer,
686 validate = port},
687 <<"tls">> => sql_tls()
688 },
689 process = fun ?MODULE:process_rdbms_connection/1,
690 format_items = map
691 };
692 outgoing_pool_connection(<<"redis">>) ->
693 82 #section{
694 items = #{<<"host">> => #option{type = string,
695 validate = non_empty},
696 <<"port">> => #option{type = integer,
697 validate = port},
698 <<"database">> => #option{type = integer,
699 validate = non_negative},
700 <<"password">> => #option{type = string}
701 },
702 format_items = map,
703 include = always,
704 defaults = #{<<"host">> => "127.0.0.1",
705 <<"port">> => 6379,
706 <<"database">> => 0,
707 <<"password">> => ""}
708 };
709 outgoing_pool_connection(<<"riak">>) ->
710 82 #section{
711 items = #{<<"address">> => #option{type = string,
712 validate = non_empty},
713 <<"port">> => #option{type = integer,
714 validate = port},
715 <<"credentials">> => riak_credentials(),
716 <<"tls">> => #section{items = tls_items(),
717 process = fun ?MODULE:process_riak_tls/1,
718 wrap = none}},
719 format_items = map
720 }.
721
722 cassandra_server() ->
723 82 #section{
724 items = #{<<"ip_address">> => #option{type = string,
725 validate = non_empty},
726 <<"port">> => #option{type = integer,
727 validate = port}},
728 required = [<<"ip_address">>],
729 process = fun ?MODULE:process_cassandra_server/1
730 }.
731
732 %% path: outgoing_pools.cassandra.*.connection.auth.plain
733 cassandra_auth_plain() ->
734 82 #section{
735 items = #{<<"username">> => #option{type = binary},
736 <<"password">> => #option{type = binary}},
737 required = all
738 }.
739
740 %% path: outgoing_pools.riak.*.connection.credentials
741 riak_credentials() ->
742 82 #section{
743 items = #{<<"user">> => #option{type = string,
744 validate = non_empty},
745 <<"password">> => #option{type = string,
746 validate = non_empty}},
747 required = all,
748 process = fun ?MODULE:process_riak_credentials/1
749 }.
750
751 %% path: outgoing_pools.rdbms.*.connection.tls
752 sql_tls() ->
753 82 Items = tls_items(),
754 82 #section{
755 items = Items#{<<"required">> => #option{type = boolean}},
756 process = fun ?MODULE:process_tls_sni/1
757 }.
758
759 tls_items() ->
760 492 #{<<"verify_peer">> => #option{type = boolean,
761 process = fun ?MODULE:process_verify_peer/1,
762 wrap = {kv, verify}},
763 <<"certfile">> => #option{type = string,
764 validate = non_empty},
765 <<"cacertfile">> => #option{type = string,
766 validate = non_empty},
767 <<"dhfile">> => #option{type = string,
768 validate = non_empty},
769 <<"keyfile">> => #option{type = string,
770 validate = non_empty},
771 <<"password">> => #option{type = string},
772 <<"server_name_indication">> => #option{type = boolean},
773 <<"server_name_indication_host">> => #option{type = string, validate = non_empty},
774 <<"server_name_indication_protocol">> => #option{type = atom,
775 validate = {enum, [default, https]}},
776 <<"ciphers">> => #option{type = string},
777 <<"versions">> => #list{items = #option{type = atom}}
778 }.
779
780 %% path: (host_config[].)services
781 services() ->
782 82 Services = [{a2b(Service), mongoose_service:config_spec(Service)}
783 82 || Service <- configurable_services()],
784 82 #section{
785 items = maps:from_list(Services),
786 wrap = global_config
787 }.
788
789 configurable_services() ->
790 82 [service_admin_extra,
791 service_mongoose_system_metrics,
792 service_domain_db].
793
794 %% path: (host_config[].)modules
795 modules() ->
796 164 Modules = [{a2b(Module), gen_mod:config_spec(Module)}
797 164 || Module <- configurable_modules()],
798 164 Items = maps:from_list(Modules),
799 164 #section{
800 items = Items#{default => #section{items = #{}}},
801 validate_keys = module,
802 format_items = map,
803 wrap = host_config
804 }.
805
806 configurable_modules() ->
807 164 [mod_adhoc,
808 mod_auth_token,
809 mod_blocking,
810 mod_bosh,
811 mod_cache_users,
812 mod_caps,
813 mod_carboncopy,
814 mod_csi,
815 mod_disco,
816 mod_event_pusher,
817 mod_extdisco,
818 mod_global_distrib,
819 mod_http_upload,
820 mod_inbox,
821 mod_jingle_sip,
822 mod_keystore,
823 mod_last,
824 mod_mam_meta,
825 mod_muc,
826 mod_muc_light,
827 mod_muc_log,
828 mod_offline,
829 mod_ping,
830 mod_privacy,
831 mod_private,
832 mod_pubsub,
833 mod_push_service_mongoosepush,
834 mod_register,
835 mod_roster,
836 mod_shared_roster_ldap,
837 mod_smart_markers,
838 mod_sic,
839 mod_stream_management,
840 mod_time,
841 mod_vcard,
842 mod_version,
843 mod_domain_isolation].
844
845 %% path: (host_config[].)modules.*.iqdisc
846 iqdisc() ->
847 2952 #section{
848 items = #{<<"type">> => #option{type = atom,
849 validate = {enum, [no_queue, one_queue, parallel, queues]}},
850 <<"workers">> => #option{type = integer,
851 validate = positive}},
852 required = [<<"type">>],
853 process = fun ?MODULE:process_iqdisc/1
854 }.
855
856 process_iqdisc(KVs) ->
857
:-(
{[[{type, Type}]], WorkersOpts} = proplists:split(KVs, [type]),
858
:-(
iqdisc(Type, WorkersOpts).
859
860
:-(
iqdisc(queues, [{workers, N}]) -> {queues, N};
861
:-(
iqdisc(Type, []) -> Type.
862
863 %% path: shaper
864 shaper() ->
865 82 #section{
866 items = #{default =>
867 #section{
868 items = #{<<"max_rate">> => #option{type = integer,
869 validate = positive}},
870 required = all,
871 format_items = map
872 }
873 },
874 validate_keys = non_empty,
875 format_items = map,
876 wrap = global_config
877 }.
878
879 %% path: (host_config[].)acl
880 acl() ->
881 164 #section{
882 items = #{default => #list{items = acl_item()}},
883 format_items = map,
884 wrap = host_config
885 }.
886
887 %% path: (host_config[].)acl.*[]
888 acl_item() ->
889 164 Match = #option{type = atom,
890 validate = {enum, [all, none, current_domain, any_hosted_domain]}},
891 164 Cond = #option{type = binary,
892 process = fun ?MODULE:process_acl_condition/1},
893 164 #section{
894 items = #{<<"match">> => Match,
895 <<"user">> => Cond,
896 <<"server">> => Cond,
897 <<"resource">> => Cond,
898 <<"user_regexp">> => Cond,
899 <<"server_regexp">> => Cond,
900 <<"resource_regexp">> => Cond,
901 <<"user_glob">> => Cond,
902 <<"server_glob">> => Cond,
903 <<"resource_glob">> => Cond
904 },
905 defaults = #{<<"match">> => current_domain},
906 format_items = map
907 }.
908
909 %% path: (host_config[].)access
910 access() ->
911 164 #section{
912 items = #{default => #list{items = access_rule_item()}},
913 format_items = map,
914 wrap = host_config
915 }.
916
917 %% path: (host_config[].)access.*[]
918 access_rule_item() ->
919 164 #section{
920 items = #{<<"acl">> => #option{type = atom,
921 validate = non_empty},
922 <<"value">> => #option{type = int_or_atom}
923 },
924 required = all,
925 format_items = map
926 }.
927
928 %% path: (host_config[].)s2s
929 s2s() ->
930 164 #section{
931 items = #{<<"default_policy">> => #option{type = atom,
932 validate = {enum, [allow, deny]}},
933 <<"host_policy">> => #list{items = s2s_host_policy(),
934 format_items = map},
935 <<"use_starttls">> => #option{type = atom,
936 validate = {enum, [false, optional, required,
937 required_trusted]}},
938 <<"certfile">> => #option{type = string,
939 validate = filename},
940 <<"shared">> => #option{type = binary,
941 validate = non_empty},
942 <<"address">> => #list{items = s2s_address(),
943 format_items = map},
944 <<"ciphers">> => #option{type = string},
945 <<"max_retry_delay">> => #option{type = integer,
946 validate = positive},
947 <<"outgoing">> => s2s_outgoing(),
948 <<"dns">> => s2s_dns()},
949 defaults = #{<<"default_policy">> => allow,
950 <<"use_starttls">> => false,
951 <<"ciphers">> => ejabberd_tls:default_ciphers(),
952 <<"max_retry_delay">> => 300},
953 format_items = map,
954 wrap = host_config
955 }.
956
957 %% path: (host_config[].)s2s.dns
958 s2s_dns() ->
959 164 #section{
960 items = #{<<"timeout">> => #option{type = integer,
961 validate = positive},
962 <<"retries">> => #option{type = integer,
963 validate = positive}},
964 format_items = map,
965 include = always,
966 defaults = #{<<"timeout">> => 10,
967 <<"retries">> => 2}
968 }.
969
970 %% path: (host_config[].)s2s.outgoing
971 s2s_outgoing() ->
972 164 #section{
973 items = #{<<"port">> => #option{type = integer,
974 validate = port},
975 <<"ip_versions">> =>
976 #list{items = #option{type = integer,
977 validate = {enum, [4, 6]}},
978 validate = unique_non_empty},
979 <<"connection_timeout">> => #option{type = int_or_infinity,
980 validate = positive}
981 },
982 format_items = map,
983 include = always,
984 defaults = #{<<"port">> => 5269,
985 <<"ip_versions">> => [4, 6],
986 <<"connection_timeout">> => 10000}
987 }.
988
989 %% path: (host_config[].)s2s.host_policy[]
990 s2s_host_policy() ->
991 164 #section{
992 items = #{<<"host">> => #option{type = binary,
993 validate = non_empty},
994 <<"policy">> => #option{type = atom,
995 validate = {enum, [allow, deny]}}
996 },
997 required = all,
998 format_items = map,
999 process = fun ?MODULE:process_s2s_host_policy/1
1000 }.
1001
1002 %% path: (host_config[].)s2s.address[]
1003 s2s_address() ->
1004 164 #section{
1005 items = #{<<"host">> => #option{type = binary,
1006 validate = non_empty},
1007 <<"ip_address">> => #option{type = string,
1008 validate = ip_address},
1009 <<"port">> => #option{type = integer,
1010 validate = port}
1011 },
1012 required = [<<"host">>, <<"ip_address">>],
1013 format_items = map,
1014 process = fun ?MODULE:process_s2s_address/1
1015 }.
1016
1017 %% Callbacks for 'process'
1018
1019 %% Check that all auth methods and modules enabled for any host type support dynamic domains
1020 process_root(Items) ->
1021 82 case proplists:lookup(host_types, Items) of
1022 {_, [_|_] = HostTypes} ->
1023 73 HTItems = lists:filter(fun(Item) -> is_host_type_item(Item, HostTypes) end, Items),
1024 73 case {unsupported_auth_methods(HTItems), unsupported_modules(HTItems)} of
1025 {[], []} ->
1026 73 Items;
1027 {Methods, Modules} ->
1028
:-(
error(#{what => dynamic_domains_not_supported,
1029 text => ("Dynamic modules not supported by the specified authentication "
1030 "methods and/or extension modules"),
1031 unsupported_auth_methods => Methods,
1032 unsupported_modules => Modules})
1033 end;
1034 _ ->
1035 9 Items
1036 end.
1037
1038 unsupported_auth_methods(KVs) ->
1039 73 [Method || Method <- extract_auth_methods(KVs),
1040 178 not ejabberd_auth:does_method_support(Method, dynamic_domains)].
1041
1042 unsupported_modules(KVs) ->
1043 73 [Module || Module <- extract_modules(KVs),
1044 940 not gen_mod:does_module_support(Module, dynamic_domains)].
1045
1046 extract_auth_methods(KVs) ->
1047 73 lists:usort(lists:flatmap(fun({{auth, _}, Auth}) -> maps:get(methods, Auth);
1048 470 (_) -> []
1049 end, KVs)).
1050
1051 extract_modules(KVs) ->
1052 73 lists:usort(lists:flatmap(fun({{modules, _}, Modules}) -> maps:keys(Modules);
1053 470 (_) -> []
1054 end, KVs)).
1055
1056 is_host_type_item({{_, HostType}, _}, HostTypes) ->
1057 680 HostType =:= global orelse lists:member(HostType, HostTypes);
1058 is_host_type_item(_, _) ->
1059 1282 false.
1060
1061 process_ctl_access_rule(KVs) ->
1062
:-(
Commands = proplists:get_value(commands, KVs, all),
1063
:-(
ArgRestrictions = proplists:get_value(argument_restrictions, KVs, []),
1064
:-(
{Commands, ArgRestrictions}.
1065
1066 process_sm_backend(Backend) ->
1067 82 {Backend, []}.
1068
1069 process_host(Host) ->
1070 325 Node = jid:nodeprep(Host),
1071 325 true = Node =/= error,
1072 325 Node.
1073
1074 process_general(General) ->
1075 82 hosts_and_host_types_are_unique_and_non_empty(General),
1076 82 General.
1077
1078 hosts_and_host_types_are_unique_and_non_empty(General) ->
1079 82 AllHostTypes = get_all_hosts_and_host_types(General),
1080 82 true = lists:sort(AllHostTypes) =:= lists:usort(AllHostTypes),
1081 82 true = [] =/= AllHostTypes.
1082
1083 get_all_hosts_and_host_types(General) ->
1084 82 lists:flatmap(fun({Key, Value}) when Key =:= hosts;
1085 Key =:= host_types ->
1086 164 Value;
1087 (_) ->
1088 1025 []
1089 end, General).
1090
1091
:-(
process_verify_peer(false) -> verify_none;
1092 218 process_verify_peer(true) -> verify_peer.
1093
1094 process_xmpp_tls(KVs) ->
1095 130 Module = proplists:get_value(module, KVs, fast_tls),
1096 130 case proplists:get_keys(KVs) -- (tls_keys(Module) ++ common_tls_keys()) of
1097 130 [] -> strip_tls_keys(process_xmpp_tls(Module, proplists:delete(module, KVs)));
1098
:-(
ExcessKeys -> error(#{what => {unexpected_tls_options, Module, ExcessKeys}})
1099 end.
1100
1101 tls_keys(just_tls) ->
1102 19 [verify_mode, disconnect_on_failure, crlfiles, password, versions,
1103 server_name_indication, server_name_indication_host, server_name_indication_protocol];
1104 tls_keys(fast_tls) ->
1105 111 [protocol_options].
1106
1107 common_tls_keys() ->
1108 130 [module, mode, verify_peer, certfile, cacertfile, dhfile, ciphers].
1109
1110 process_xmpp_tls(just_tls, KVs) ->
1111 19 KVsWithSNI = process_tls_sni(KVs),
1112 19 {[VM, DoF], Opts} = proplists:split(KVsWithSNI, [verify_mode, disconnect_on_failure]),
1113 19 {External, Internal} = lists:partition(fun is_external_tls_opt/1, Opts),
1114 19 SSLOpts = ssl_opts(verify_fun(VM, DoF) ++ Internal),
1115 19 [{tls_module, just_tls}] ++ SSLOpts ++ External;
1116 process_xmpp_tls(fast_tls, KVs) ->
1117 111 process_fast_tls(KVs).
1118
1119 process_fast_tls(KVs) ->
1120 193 proplists:substitute_aliases([{cacertfile, cafile}], KVs).
1121
1122 strip_tls_keys(Opts) ->
1123 130 lists:map(fun strip_tls_key/1, Opts).
1124
1125 130 strip_tls_key({mode, V}) -> V;
1126 18 strip_tls_key({verify_peer, V}) -> V;
1127 316 strip_tls_key(KV) -> KV.
1128
1129 7 verify_fun([], []) -> [];
1130 6 verify_fun([{verify_mode, VM}], []) -> [{verify_fun, {VM, true}}];
1131 6 verify_fun([{verify_mode, VM}], [{disconnect_on_failure, DoF}]) -> [{verify_fun, {VM, DoF}}].
1132
1133 19 is_external_tls_opt({mode, _}) -> true;
1134 12 is_external_tls_opt({verify_peer, _}) -> true;
1135
:-(
is_external_tls_opt({crlfiles, _}) -> true;
1136 50 is_external_tls_opt({_, _}) -> false.
1137
1138
:-(
ssl_opts([]) -> [];
1139 19 ssl_opts(Opts) -> [{ssl_options, Opts}].
1140
1141 process_listener([item, Type | _], Opts) ->
1142 957 mongoose_listener_config:ensure_ip_options(Opts#{module => listener_module(Type)}).
1143
1144 574 listener_module(<<"http">>) -> ejabberd_cowboy;
1145 162 listener_module(<<"c2s">>) -> ejabberd_c2s;
1146 82 listener_module(<<"s2s">>) -> ejabberd_s2s_in;
1147 139 listener_module(<<"service">>) -> ejabberd_service.
1148
1149 process_http_handler([item, Type | _], KVs) ->
1150 1558 {[[{host, Host}], [{path, Path}]], Opts} = proplists:split(KVs, [host, path]),
1151 1558 HandlerOpts = process_http_handler_opts(Type, Opts),
1152 1558 {Host, Path, binary_to_atom(Type, utf8), HandlerOpts}.
1153
1154 process_http_handler_opts(<<"lasse_handler">>, [{module, Module}]) ->
1155 82 [Module];
1156 process_http_handler_opts(<<"cowboy_static">>, Opts) ->
1157 82 {[[{type, Type}], [{app, App}], [{content_path, Path}]], []} =
1158 proplists:split(Opts, [type, app, content_path]),
1159 82 {Type, App, Path, [{mimetypes, cow_mimetypes, all}]};
1160 process_http_handler_opts(<<"mongoose_api_admin">>, Opts) ->
1161 82 {[UserOpts, PassOpts], []} = proplists:split(Opts, [username, password]),
1162 82 case {UserOpts, PassOpts} of
1163 82 {[], []} -> [];
1164
:-(
{[{username, User}], [{password, Pass}]} -> [{auth, {User, Pass}}]
1165 end;
1166 process_http_handler_opts(<<"mongoose_domain_handler">>, Opts) ->
1167 82 {[UserOpts, PassOpts], []} = proplists:split(Opts, [username, password]),
1168 82 case {UserOpts, PassOpts} of
1169 82 {[], []} -> ok;
1170
:-(
{[{username, _User}], [{password, _Pass}]} -> ok;
1171
:-(
_ -> error(#{what => both_username_and_password_required,
1172 handler => mongoose_domain_handler, opts => Opts})
1173 end,
1174 82 Opts;
1175 process_http_handler_opts(<<"mongoose_graphql_cowboy_handler">>, Opts) ->
1176 164 {[UserOpts, PassOpts, SchemaOpts], []} = proplists:split(Opts, [username, password, schema_endpoint]),
1177 164 case SchemaOpts of
1178
:-(
[] -> error(#{what => schema_endpoint_required,
1179 handler => mongoose_graphql_cowboy_handler, opts => Opts});
1180 164 _ -> ok
1181 end,
1182 164 case {UserOpts, PassOpts} of
1183 82 {[], []} -> ok;
1184 82 {[{username, _User}], [{password, _Pass}]} -> ok;
1185
:-(
_ -> error(#{what => both_username_and_password_required,
1186 handler => mongoose_graphql_cowboy_handler, opts => Opts})
1187 end,
1188 164 Opts;
1189 82 process_http_handler_opts(<<"cowboy_swagger_redirect_handler">>, []) -> #{};
1190 82 process_http_handler_opts(<<"cowboy_swagger_json_handler">>, []) -> #{};
1191 902 process_http_handler_opts(_, Opts) -> Opts.
1192
1193 process_sasl_external(V) when V =:= standard;
1194 V =:= common_name;
1195 V =:= auth_id ->
1196 21 V;
1197 process_sasl_external(M) ->
1198 3 mongoose_config_validator:validate(M, atom, module),
1199 3 {mod, M}.
1200
1201 process_sasl_mechanism(V) ->
1202 18 list_to_atom("cyrsasl_" ++ atom_to_list(V)).
1203
1204 process_auth(Opts = #{methods := Methods}) ->
1205
:-(
[check_auth_method(Method, Opts) || Method <- Methods],
1206
:-(
Opts;
1207 process_auth(Opts) ->
1208 219 MethodsFromSections = lists:filter(fun(K) -> maps:is_key(K, Opts) end, all_auth_methods()),
1209 219 Opts#{methods => MethodsFromSections}.
1210
1211 all_auth_methods() ->
1212 383 [anonymous, dummy, external, http, internal, jwt, ldap, pki, rdbms, riak].
1213
1214 check_auth_method(Method, Opts) ->
1215
:-(
case maps:is_key(Method, Opts) of
1216
:-(
true -> ok;
1217
:-(
false -> error(#{what => missing_section_for_auth_method, auth_method => Method})
1218 end.
1219
1220 process_pool([Tag, Type|_], AllOpts = #{scope := ScopeIn}) ->
1221 246 Scope = pool_scope(ScopeIn, maps:get(host, AllOpts, none)),
1222 246 Connection = maps:get(connection, AllOpts, #{}),
1223 246 Opts = maps:without([scope, host, connection], AllOpts),
1224 246 #{type => b2a(Type),
1225 scope => Scope,
1226 tag => b2a(Tag),
1227 opts => Opts,
1228 conn_opts => Connection}.
1229
1230 pool_scope(single_host, none) ->
1231
:-(
error(#{what => pool_single_host_not_specified,
1232 text => <<"\"host\" option is required if \"single_host\" is used.">>});
1233
:-(
pool_scope(single_host, Host) -> Host;
1234
:-(
pool_scope(host, none) -> host;
1235 246 pool_scope(global, none) -> global.
1236
1237 process_cassandra_server(KVs) ->
1238
:-(
{[[{ip_address, IPAddr}]], Opts} = proplists:split(KVs, [ip_address]),
1239
:-(
case Opts of
1240
:-(
[] -> IPAddr;
1241
:-(
[{port, Port}] -> {IPAddr, Port}
1242 end.
1243
1244 process_cassandra_auth([{plain, KVs}]) ->
1245
:-(
{[[{username, User}], [{password, Pass}]], []} = proplists:split(KVs, [username, password]),
1246
:-(
{cqerl_auth_plain_handler, [{User, Pass}]}.
1247
1248 process_rdbms_connection(Map) ->
1249
:-(
KIMap = maps:with([keepalive_interval], Map),
1250
:-(
maps:merge(KIMap, #{server => rdbms_server(Map)}).
1251
1252 rdbms_server(Opts = #{driver := odbc}) ->
1253
:-(
maps:get(settings, Opts);
1254 rdbms_server(Opts = #{host := Host, database := DB, username := User, password := Pass, driver := Driver}) ->
1255
:-(
PortOpts = maps:get(port, Opts, none),
1256
:-(
TLSOpts = maps:get(tls, Opts, none),
1257
:-(
list_to_tuple([Driver, Host] ++ db_port(PortOpts) ++
1258 [DB, User, Pass] ++ db_tls(Driver, TLSOpts)).
1259
1260
:-(
db_port(none) -> [];
1261
:-(
db_port(Port) -> [Port].
1262
1263
:-(
db_tls(_, none) -> [];
1264 db_tls(Driver, KVs) ->
1265
:-(
{[ModeOpts], Opts} = proplists:split(KVs, [required]),
1266
:-(
[ssl_mode(Driver, ModeOpts) ++ ssl_opts(Driver, Opts)].
1267
1268
:-(
ssl_mode(pgsql, [{required, true}]) -> [{ssl, required}];
1269
:-(
ssl_mode(pgsql, [{required, false}]) -> [{ssl, true}];
1270
:-(
ssl_mode(pgsql, []) -> [{ssl, true}];
1271
:-(
ssl_mode(mysql, []) -> [].
1272
1273
:-(
ssl_opts(pgsql, []) -> [];
1274
:-(
ssl_opts(pgsql, Opts) -> [{ssl_opts, Opts}];
1275
:-(
ssl_opts(mysql, Opts) -> Opts.
1276
1277 process_riak_tls(KVs) ->
1278
:-(
KVsWithSNI = process_tls_sni(KVs),
1279
:-(
{[CACertFileOpts], SSLOpts} = proplists:split(KVsWithSNI, [cacertfile]),
1280
:-(
riak_ssl(SSLOpts) ++ CACertFileOpts.
1281
1282
:-(
riak_ssl([]) -> [];
1283
:-(
riak_ssl(Opts) -> [{ssl_opts, Opts}].
1284
1285 process_tls_sni(KVs) ->
1286 % the SSL library expects either the atom `disable` or a string with the SNI host
1287 % as value for `server_name_indication`
1288 347 SNIKeys = [server_name_indication, server_name_indication_host, server_name_indication_protocol],
1289 347 {[SNIOpt, SNIHostOpt, SNIProtocol], SSLOpts} = proplists:split(KVs, SNIKeys),
1290 347 case {SNIOpt, SNIHostOpt, SNIProtocol} of
1291 {[], [], []} ->
1292 347 SSLOpts;
1293 {[{server_name_indication, false}], _, _} ->
1294
:-(
[{server_name_indication, disable} | SSLOpts];
1295 {[{server_name_indication, true}],
1296 [{server_name_indication_host, SNIHost}],
1297 [{server_name_indication_protocol, https}]} ->
1298
:-(
[{server_name_indication, SNIHost},
1299 {customize_hostname_check, [{match_fun, public_key:pkix_verify_hostname_match_fun(https)}]}
1300 | SSLOpts];
1301 {[{server_name_indication, true}],
1302 [{server_name_indication_host, SNIHost}],
1303 _} ->
1304
:-(
[{server_name_indication, SNIHost} | SSLOpts]
1305 end.
1306
1307 process_riak_credentials(KVs) ->
1308
:-(
{[[{user, User}], [{password, Pass}]], []} = proplists:split(KVs, [user, password]),
1309
:-(
{User, Pass}.
1310
1311 492 b2a(B) -> binary_to_atom(B, utf8).
1312
1313 7954 a2b(A) -> atom_to_binary(A, utf8).
1314
1315 wpool_strategy_values() ->
1316 820 [best_worker, random_worker, next_worker, available_worker, next_available_worker].
1317
1318 process_acl_condition(Value) ->
1319
:-(
case jid:nodeprep(Value) of
1320
:-(
error -> error(#{what => incorrect_acl_condition_value,
1321 text => <<"Value could not be parsed as a JID node part">>});
1322
:-(
Node -> Node
1323 end.
1324
1325 process_s2s_host_policy(#{host := S2SHost, policy := Policy}) ->
1326
:-(
{S2SHost, Policy}.
1327
1328 process_s2s_address(M) ->
1329 84 maps:take(host, M).
1330
1331 process_domain_cert(#{domain := Domain, certfile := Certfile}) ->
1332
:-(
{Domain, Certfile}.
Line Hits Source