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