From 9b579d2d1ea8be2a483d5cd1eeccacb912ca23d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Fri, 23 Oct 2020 15:15:34 +0200 Subject: [PATCH 001/104] Do not use the 'cfg' format in config_parser_SUITE Compare the parsed TOML options directly with the expected values. The files with the expected values were generated from the 'cfg' config files. The parsed TOML options are saved after the test for easier comparison with the expected values. --- test/config_parser_SUITE.erl | 58 +- .../miscellaneous.cfg | 51 -- .../miscellaneous.options | 68 +++ test/config_parser_SUITE_data/modules.cfg | 260 --------- test/config_parser_SUITE_data/modules.options | 547 ++++++++++++++++++ .../mongooseim-pgsql.cfg | 193 ------ .../mongooseim-pgsql.options | 229 ++++++++ .../outgoing_pools.cfg | 42 -- .../outgoing_pools.options | 43 ++ test/config_parser_SUITE_data/s2s_only.cfg | 17 - .../config_parser_SUITE_data/s2s_only.options | 17 + 11 files changed, 932 insertions(+), 593 deletions(-) delete mode 100644 test/config_parser_SUITE_data/miscellaneous.cfg create mode 100644 test/config_parser_SUITE_data/miscellaneous.options delete mode 100644 test/config_parser_SUITE_data/modules.cfg create mode 100644 test/config_parser_SUITE_data/modules.options delete mode 100644 test/config_parser_SUITE_data/mongooseim-pgsql.cfg create mode 100644 test/config_parser_SUITE_data/mongooseim-pgsql.options delete mode 100644 test/config_parser_SUITE_data/outgoing_pools.cfg create mode 100644 test/config_parser_SUITE_data/outgoing_pools.options delete mode 100644 test/config_parser_SUITE_data/s2s_only.cfg create mode 100644 test/config_parser_SUITE_data/s2s_only.options diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index 1847eb8b53..e44ca136b5 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -25,7 +25,7 @@ -define(_errf(Config), ?add_loc(fun() -> ?errf(Config) end)). all() -> - [{group, equivalence}, + [{group, file}, {group, general}, {group, listen}, {group, auth}, @@ -36,11 +36,11 @@ all() -> {group, services}]. groups() -> - [{equivalence, [parallel], [sample_pgsql, - miscellaneous, - s2s, - modules, - outgoing_pools]}, + [{file, [parallel], [sample_pgsql, + miscellaneous, + s2s, + modules, + outgoing_pools]}, {general, [parallel], [loglevel, hosts, registration_timeout, @@ -215,19 +215,19 @@ end_per_suite(_Config) -> ok. sample_pgsql(Config) -> - test_equivalence_between_files(Config, "mongooseim-pgsql.cfg", "mongooseim-pgsql.toml"). + test_config_file(Config, "mongooseim-pgsql"). miscellaneous(Config) -> - test_equivalence_between_files(Config, "miscellaneous.cfg", "miscellaneous.toml"). + test_config_file(Config, "miscellaneous"). s2s(Config) -> - test_equivalence_between_files(Config, "s2s_only.cfg", "s2s_only.toml"). + test_config_file(Config, "s2s_only"). modules(Config) -> - test_equivalence_between_files(Config, "modules.cfg", "modules.toml"). + test_config_file(Config, "modules"). outgoing_pools(Config) -> - test_equivalence_between_files(Config, "outgoing_pools.cfg", "outgoing_pools.toml"). + test_config_file(Config, "outgoing_pools"). %% tests: general loglevel(_Config) -> @@ -3016,15 +3016,27 @@ parse(M) -> (_) -> true end, Config). -%% helpers for 'equivalence' tests +%% helpers for file tests + +test_config_file(Config, File) -> + OptionsPath = ejabberd_helper:data(Config, File ++ ".options"), + {ok, ExpectedOpts} = file:consult(OptionsPath), + + TOMLPath = ejabberd_helper:data(Config, File ++ ".toml"), + State = mongoose_config_parser_toml:parse_file(TOMLPath), + TOMLOpts = mongoose_config_parser:state_to_opts(State), + + %% Save the parsed TOML options + %% - for debugging + %% - to update tests after a config change - always check the diff! + FormattedOpts = [io_lib:format("~p.~n", [Opt]) || Opt <- TOMLOpts], + file:write_file(OptionsPath ++ "-parsed", lists:sort(FormattedOpts)), + + compare_config(ExpectedOpts, TOMLOpts). compare_config(C1, C2) -> compare_unordered_lists(C1, C2, fun handle_config_option/2). -filter_config(#config{key = required_files}) -> - false; % not supported yet in TOML -filter_config(_) -> true. - handle_config_option(#config{key = K1, value = V1}, #config{key = K2, value = V2}) -> ?eq(K1, K2), @@ -3149,20 +3161,6 @@ compare_ordered_lists([H1|T1], [H2|T2], F) -> compare_ordered_lists([], [], _) -> ok. -test_equivalence_between_files(Config, File1, File2) -> - CfgPath = ejabberd_helper:data(Config, File1), - State1 = mongoose_config_parser_cfg:parse_file(CfgPath), - Hosts1 = mongoose_config_parser:state_to_host_opts(State1), - Opts1 = mongoose_config_parser:state_to_opts(State1), - - TOMLPath = ejabberd_helper:data(Config, File2), - State2 = mongoose_config_parser_toml:parse_file(TOMLPath), - Hosts2 = mongoose_config_parser:state_to_host_opts(State2), - Opts2 = mongoose_config_parser:state_to_opts(State2), - ?eq(Hosts1, Hosts2), - compare_unordered_lists(lists:filter(fun filter_config/1, Opts1), Opts2, - fun handle_config_option/2). - parse_with_host(Config) -> [F] = parse(Config), apply(F, [?HOST]). diff --git a/test/config_parser_SUITE_data/miscellaneous.cfg b/test/config_parser_SUITE_data/miscellaneous.cfg deleted file mode 100644 index cbed6ffbd6..0000000000 --- a/test/config_parser_SUITE_data/miscellaneous.cfg +++ /dev/null @@ -1,51 +0,0 @@ -{hosts, ["localhost", - "anonymous.localhost" - ] }. - -{cowboy_server_name, "Apache"}. -{rdbms_server_type, mssql}. -override_global. -override_local. -override_acls. -{pgsql_users_number_estimate, true}. -{route_subdomains, s2s}. -{mongooseimctl_access_commands, [{local, ["join_cluster"], [{node, "mongooseim@prime"}]}]}. -{routing_modules, [mongoose_router_global, mongoose_router_localdomain]}. -{replaced_wait_timeout, 2000}. -{hide_service_name, true}. -{extauth_instances, 1}. -{anonymous_protocol, sasl_anon}. -{allow_multiple_connections, true}. -{auth_opts, [ - {ldap_pool_tag, default}, - {ldap_bind_pool_tag, bind}, - {ldap_base, "ou=Users,dc=esl,dc=com"}, - {ldap_uids, ["uid", {"uid2", "%u"}]}, - {ldap_filter, "(&(objectClass=shadowAccount)(memberOf=Jabber Users))"}, - {ldap_dn_filter, {"(&(name=%s)(owner=%D)(user=%u@%d))", ["sn"]}}, - {ldap_local_filter, {equal, {"accountStatus",["enabled"]}}}, - {ldap_deref, never}, - {extauth_program, "/usr/bin/authenticator"}, - {basic_auth, "admin:admin"}, - {jwt_secret, "secret123"}, - {jwt_algorithm, "RS256"}, - {jwt_username_key, user}, - {bucket_type, <<"user_bucket">>} -]}. - -{listen, - [ - { 5280, ejabberd_cowboy, [ - {transport_options, [{num_acceptors, 10}, {max_connections, 1024}]}, - {modules, [ - {"_", "/ws-xmpp", mod_websockets, [{ejabberd_service, [ - {access, all}, - {shaper_rule, fast}, - {password, "secret"}, - {max_fsm_queue, 1000}]} - ]} - ]} - ]}]}. -{services, [ - {service_mongoose_system_metrics, [report, {initial_report, 300000}, {periodic_report, 10800000}, {tracking_id, "UA-123456789"}]} -]}. diff --git a/test/config_parser_SUITE_data/miscellaneous.options b/test/config_parser_SUITE_data/miscellaneous.options new file mode 100644 index 0000000000..69b435f4b9 --- /dev/null +++ b/test/config_parser_SUITE_data/miscellaneous.options @@ -0,0 +1,68 @@ +{config,hosts,[<<"localhost">>,<<"anonymous.localhost">>]}. +{local_config,cowboy_server_name,"Apache"}. +{local_config,listen, + [{{5280,{0,0,0,0},tcp}, + ejabberd_cowboy, + [{transport_options,[{num_acceptors,10},{max_connections,1024}]}, + {modules, + [{"_","/ws-xmpp",mod_websockets, + [{ejabberd_service, + [{access,all}, + {shaper_rule,fast}, + {password,"secret"}, + {max_fsm_queue,1000}]}]}]}]}]}. +{local_config,mongooseimctl_access_commands, + [{local,["join_cluster"],[{node,"mongooseim@prime"}]}]}. +{local_config,rdbms_server_type,mssql}. +{local_config,routing_modules, + [mongoose_router_global,mongoose_router_localdomain]}. +{local_config,services, + [{service_mongoose_system_metrics, + [report, + {initial_report,300000}, + {periodic_report,10800000}, + {tracking_id,"UA-123456789"}]}]}. +{local_config,{allow_multiple_connections,<<"anonymous.localhost">>},true}. +{local_config,{allow_multiple_connections,<<"localhost">>},true}. +{local_config,{anonymous_protocol,<<"anonymous.localhost">>},sasl_anon}. +{local_config,{anonymous_protocol,<<"localhost">>},sasl_anon}. +{local_config,{auth_opts,<<"anonymous.localhost">>}, + [{ldap_pool_tag,default}, + {ldap_bind_pool_tag,bind}, + {ldap_base,"ou=Users,dc=esl,dc=com"}, + {ldap_uids,["uid",{"uid2","%u"}]}, + {ldap_filter,"(&(objectClass=shadowAccount)(memberOf=Jabber Users))"}, + {ldap_dn_filter,{"(&(name=%s)(owner=%D)(user=%u@%d))",["sn"]}}, + {ldap_local_filter,{equal,{"accountStatus",["enabled"]}}}, + {ldap_deref,never}, + {extauth_program,"/usr/bin/authenticator"}, + {basic_auth,"admin:admin"}, + {jwt_secret,"secret123"}, + {jwt_algorithm,"RS256"}, + {jwt_username_key,user}, + {bucket_type,<<"user_bucket">>}]}. +{local_config,{auth_opts,<<"localhost">>}, + [{ldap_pool_tag,default}, + {ldap_bind_pool_tag,bind}, + {ldap_base,"ou=Users,dc=esl,dc=com"}, + {ldap_uids,["uid",{"uid2","%u"}]}, + {ldap_filter,"(&(objectClass=shadowAccount)(memberOf=Jabber Users))"}, + {ldap_dn_filter,{"(&(name=%s)(owner=%D)(user=%u@%d))",["sn"]}}, + {ldap_local_filter,{equal,{"accountStatus",["enabled"]}}}, + {ldap_deref,never}, + {extauth_program,"/usr/bin/authenticator"}, + {basic_auth,"admin:admin"}, + {jwt_secret,"secret123"}, + {jwt_algorithm,"RS256"}, + {jwt_username_key,user}, + {bucket_type,<<"user_bucket">>}]}. +{local_config,{extauth_instances,<<"anonymous.localhost">>},1}. +{local_config,{extauth_instances,<<"localhost">>},1}. +{local_config,{hide_service_name,<<"anonymous.localhost">>},true}. +{local_config,{hide_service_name,<<"localhost">>},true}. +{local_config,{pgsql_users_number_estimate,<<"anonymous.localhost">>},true}. +{local_config,{pgsql_users_number_estimate,<<"localhost">>},true}. +{local_config,{replaced_wait_timeout,<<"anonymous.localhost">>},2000}. +{local_config,{replaced_wait_timeout,<<"localhost">>},2000}. +{local_config,{route_subdomains,<<"anonymous.localhost">>},s2s}. +{local_config,{route_subdomains,<<"localhost">>},s2s}. diff --git a/test/config_parser_SUITE_data/modules.cfg b/test/config_parser_SUITE_data/modules.cfg deleted file mode 100644 index 76f2d570cc..0000000000 --- a/test/config_parser_SUITE_data/modules.cfg +++ /dev/null @@ -1,260 +0,0 @@ -{hosts, [ - "localhost", - "dummy_host" -]}. -{modules, - [ - {mod_adhoc, [{iqdisc, one_queue}, {report_commands_node, true}]}, - {mod_auth_token, [{{validity_period, access}, {13, minutes}}, - {{validity_period, refresh}, {13, days}}]}, - {mod_bosh, [ - {inactivity, 20}, - {max_wait, infinity}, - {server_acks, true}, - {backend, mnesia}, - {maxpause, 120} - ]}, - {mod_caps, [{cache_size, 1000}, {cache_life_time, 86}]}, - {mod_carboncopy, [{iqdisc, no_queue}]}, - {mod_csi, [{buffer_max, 40}]}, - {mod_disco, [ - {iqdisc, one_queue}, - {extra_domains, [<<"some_domain">>, <<"another_domain">>]}, - {server_info, [{all, "abuse-address", ["admin@example.com"]}, {[mod_muc, mod_disco], "friendly-spirits", ["spirit1@localhost", "spirit2@localhost"]}]}, - {users_can_see_hidden_services, true} - ]}, - {mod_event_pusher, [ - {backends, [ - {sns, [ - {access_key_id, "AKIAIOSFODNN7EXAMPLE"}, - {secret_access_key, "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"}, - {region, "eu-west-1"}, - {account_id, "123456789012"}, - {sns_host, "sns.eu-west-1.amazonaws.com"}, - {muc_host, "conference.HOST"}, - {plugin_module, mod_event_pusher_sns_defaults}, - {presence_updates_topic, "user_presence_updated"}, - {pm_messages_topic, "user_message_sent"}, - {muc_messages_topic, "user_messagegroup_sent"}, - {pool_size, 100}, - {publish_retry_count, 2}, - {publish_retry_time_ms, 50} - ]}, - {push, [ - {backend, mnesia}, - {wpool, [{workers, 200}]}, - {plugin_module, mod_event_pusher_push_plugin_defaults}, - {virtual_pubsub_hosts, ["host1", "host2"]} - ]}, - {http, [ - {pool_name, http_pool}, - {path, "/notifications"}, - {callback_module, mod_event_pusher_http_defaults} - ]}, - {rabbit, [ - {presence_exchange, [{name, <<"presence">>}, - {type, <<"topic">>}]}, - {chat_msg_exchange, [{name, <<"chat_msg">>}, - {sent_topic, <<"chat_msg_sent">>}, - {recv_topic, <<"chat_msg_recv">>}]}, - {groupchat_msg_exchange, [{name, <<"groupchat_msg">>}, - {sent_topic, <<"groupchat_msg_sent">>}, - {recv_topic, <<"groupchat_msg_recv">>}]} - ]} - ]} - ]}, - {mod_extdisco, [ - [{host, "stun1"}, - {password, "password"}, - {port, 3478}, - {transport, "udp"}, - {type, stun}, - {username, "username"}], - [{host, "stun2"}, - {password, "password"}, - {port, 2222}, - {transport, "tcp"}, - {type, stun}, - {username, "username"}], - [{host, "192.168.0.1"}, - {type, turn}] - ]}, - {mod_http_upload, [ - {host, "upload.@HOST@"}, - {backend, s3}, - {expiration_time, 120}, - {s3, [ - {bucket_url, "https://s3-eu-west-1.amazonaws.com/mybucket"}, - {region, "eu-west-1"}, - {add_acl, true}, - {access_key_id, "AKIAIOSFODNN7EXAMPLE"}, - {secret_access_key, "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"} - ]} - ]}, - {mod_inbox, [{backend, rdbms}, - {reset_markers, [displayed]}, - {aff_changes, true}, - {remove_on_kicked, true}, - {groupchat, [muclight]} - ]}, - {mod_global_distrib, [ - {global_host, "example.com"}, - {local_host, "datacenter1.example.com"}, - {connections, [ - {endpoints, [{"172.16.0.2", 5555}]}, - {advertised_endpoints, [{"172.16.0.2", 5555}]}, - {connections_per_endpoint, 30}, - {tls_opts, [ - {certfile, "priv/dc1.pem"}, - {cafile, "priv/ca.pem"} - ]} - ]}, - {cache, [ - {domain_lifetime_seconds, 60} - ]}, - {bounce, [ - {resend_after_ms, 300}, - {max_retries, 3} - ]}, - {redis, [ - {pool, global_distrib} - ]} - ]}, - {mod_jingle_sip, [ - {proxy_host, "localhost"}, - {proxy_port, 5600}, - {listen_port, 5600}, - {local_host, "localhost"}, - {sdp_origin, "127.0.0.1"} - ]}, - {mod_keystore, [{keys, [{access_secret, ram}, - {access_psk, {file, "priv/access_psk"}}, - {provision_psk, {file, "priv/provision_psk"}} - ]}, - {ram_key_size, 1000} - ]}, - {mod_last, [{backend, mnesia}, {iqdisc, {queues, 10}}]}, - {mod_mam_meta, [ - {backend, rdbms}, - {no_stanzaid_element, true}, - {is_archivable_message, mod_mam_utils}, - {archive_chat_markers, true}, - {full_text_search, true}, - - - {pm, [{user_prefs_store, rdbms}, {full_text_search, false}]}, - {muc, [ - {host, "muc.example.com"}, - {rdbms_message_format, simple}, - {async_writer, false}, - {user_prefs_store, mnesia} - ]} - ]}, - {mod_muc, [ - {host, "muc.example.com"}, - {access, muc}, - {access_create, muc_create}, - {http_auth_pool, my_auth_pool}, - {default_room_options, [ - {password_protected, true}, - {affiliations, [ - {{<<"alice">>, <<"localhost">>, <<"resource1">>}, member}, - {{<<"bob">>, <<"localhost">>, <<"resource2">>}, owner} - ]} - ]} - ]}, - {mod_muc_log, - [ - {outdir, "www/muc"}, - {access_log, muc}, - {top_link, {"/", "Home"}}, - {cssfile, <<"path/to/css/file">>} - ]}, - {mod_muc_light, [ - {host, "muclight.example.com"}, - {equal_occupants, true}, - {legacy_mode, true}, - {rooms_per_user, 10}, - {blocking, false}, - {all_can_configure, true}, - {all_can_invite, true}, - {max_occupants, 50}, - {rooms_per_page, 5}, - {rooms_in_rosters, true}, - {config_schema, [{"roomname", "The Room"}, {"display-lines", 30, display_lines, integer}]} - ]}, - {mod_offline, [ - {access_max_user_messages, max_user_offline_messages}, - {backend, riak}, - {bucket_type, <<"offline">>} - ]}, - {mod_ping, [ - {send_pings, true}, - {ping_interval, 60}, - {timeout_action, none}, - {ping_req_timeout, 32} - ]}, - {mod_pubsub, [ - {access_createnode, pubsub_createnode}, - {ignore_pep_from_offline, false}, - {backend, rdbms}, - {last_item_cache, mnesia}, - {max_items_node, 1000}, - {plugins, [<<"flat">>, <<"pep">>]}, - {pep_mapping, [{"urn:xmpp:microblog:0", "mb"}]} - ]}, - {mod_push_service_mongoosepush, [ - {pool_name, mongoose_push_http}, - {api_version, "v3"}, - {max_http_connections, 100} - ]}, - {mod_register, [ - {welcome_message, {"Subject", "Body"}}, - {access, all}, - {registration_watchers, [<<"JID1">>, <<"JID2">>]}, - {password_strength, 32} - ]}, - {mod_revproxy, - [{routes, [{"www.erlang-solutions.com", "/admin", "_", - "https://www.erlang-solutions.com/"}, - {":var.com", "/:var", "http://localhost:8080/"}, - {":domain.com", "/", "_", "http://localhost:8080/:domain"}] - }]}, - {mod_roster, [ - {versioning, true}, - {store_current_id, true} - ]}, - {mod_shared_roster_ldap, [ - {ldap_base, "ou=Users,dc=ejd,dc=com"}, - {ldap_groupattr, "ou"}, - {ldap_memberattr, "cn"},{ldap_userdesc, "cn"}, - {ldap_filter, "(objectClass=inetOrgPerson)"}, - {ldap_rfilter, "(objectClass=inetOrgPerson)"}, - {ldap_group_cache_validity, 1}, - {ldap_user_cache_validity, 1}]}, - {mod_stream_management, [{buffer_max, 30}, - {ack_freq, 1}, - {resume_timeout, 600}, - {stale_h, [{enabled, true}, - {stale_h_repeat_after, 1800}, - {stale_h_geriatric, 3600}]} - ]}, - {mod_vcard, [ - {matches, 1}, - {search, true}, - {host, "directory.example.com"}, - {ldap_vcard_map, [ - {<<"FAMILY">>, <<"%s">>, [<<"sn">>]}, - {<<"FN">>, <<"%s">>, [<<"displayName">>]} - ]}, - {ldap_search_fields, [ - {<<"User">>, <<"%u">>}, - {<<"Full Name">>, <<"displayName">>} - ]}, - {ldap_search_reported, [ - {<<"Full Name">>, <<"FN">>}, - {<<"Given Name">>, <<"FIRST">>} - ]} - ]}, - {mod_version, [{os_info, true}]} -]}. diff --git a/test/config_parser_SUITE_data/modules.options b/test/config_parser_SUITE_data/modules.options new file mode 100644 index 0000000000..56ab1e5402 --- /dev/null +++ b/test/config_parser_SUITE_data/modules.options @@ -0,0 +1,547 @@ +{config,hosts,[<<"localhost">>,<<"dummy_host">>]}. +{local_config, + {modules,<<"dummy_host">>}, + [{mod_jingle_sip, + [{proxy_host,"localhost"}, + {proxy_port,5600}, + {listen_port,5600}, + {local_host,"localhost"}, + {sdp_origin,"127.0.0.1"}]}, + {mod_shared_roster_ldap, + [{ldap_base,"ou=Users,dc=ejd,dc=com"}, + {ldap_groupattr,"ou"}, + {ldap_memberattr,"cn"}, + {ldap_userdesc,"cn"}, + {ldap_filter,"(objectClass=inetOrgPerson)"}, + {ldap_rfilter,"(objectClass=inetOrgPerson)"}, + {ldap_group_cache_validity,1}, + {ldap_user_cache_validity,1}]}, + {mod_mam_rdbms_user,[muc,pm]}, + {mod_global_distrib, + [{global_host,"example.com"}, + {local_host,"datacenter1.example.com"}, + {connections, + [{endpoints,[{"172.16.0.2",5555}]}, + {advertised_endpoints,[{"172.16.0.2",5555}]}, + {connections_per_endpoint,30}, + {tls_opts,[{certfile,"priv/dc1.pem"},{cafile,"priv/ca.pem"}]}]}, + {cache,[{domain_lifetime_seconds,60}]}, + {bounce,[{resend_after_ms,300},{max_retries,3}]}, + {redis,[{pool,global_distrib}]}]}, + {mod_register, + [{welcome_message,{"Subject","Body"}}, + {access,all}, + {registration_watchers,[<<"JID1">>,<<"JID2">>]}, + {password_strength,32}]}, + {mod_mam_rdbms_async_pool_writer,[pm]}, + {mod_adhoc,[{iqdisc,one_queue},{report_commands_node,true}]}, + {mod_event_pusher_sns, + [{access_key_id,"AKIAIOSFODNN7EXAMPLE"}, + {secret_access_key,"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"}, + {region,"eu-west-1"}, + {account_id,"123456789012"}, + {sns_host,"sns.eu-west-1.amazonaws.com"}, + {muc_host,"conference.HOST"}, + {plugin_module,mod_event_pusher_sns_defaults}, + {presence_updates_topic,"user_presence_updated"}, + {pm_messages_topic,"user_message_sent"}, + {muc_messages_topic,"user_messagegroup_sent"}, + {pool_size,100}, + {publish_retry_count,2}, + {publish_retry_time_ms,50}]}, + {mod_muc, + [{host,"muc.example.com"}, + {access,muc}, + {access_create,muc_create}, + {http_auth_pool,my_auth_pool}, + {default_room_options, + [{password_protected,true}, + {affiliations, + [{{<<"alice">>,<<"localhost">>,<<"resource1">>},member}, + {{<<"bob">>,<<"localhost">>,<<"resource2">>},owner}]}]}]}, + {mod_ping, + [{send_pings,true}, + {ping_interval,60}, + {timeout_action,none}, + {ping_req_timeout,32}]}, + {mod_mam, + [{archive_chat_markers,true}, + {full_text_search,false}, + {is_archivable_message,mod_mam_utils}, + {no_stanzaid_element,true}]}, + {mod_disco, + [{iqdisc,one_queue}, + {extra_domains,[<<"some_domain">>,<<"another_domain">>]}, + {server_info, + [{all,"abuse-address",["admin@example.com"]}, + {[mod_muc,mod_disco], + "friendly-spirits", + ["spirit1@localhost","spirit2@localhost"]}]}, + {users_can_see_hidden_services,true}]}, + {mod_event_pusher_http, + [{configs, + [[{pool_name,http_pool}, + {path,"/notifications"}, + {callback_module,mod_event_pusher_http_defaults}]]}]}, + {mod_event_pusher_hook_translator,[]}, + {mod_mam_mnesia_prefs,[muc]}, + {mod_mam_muc, + [{archive_chat_markers,true}, + {full_text_search,true}, + {host,"muc.example.com"}, + {is_archivable_message,mod_mam_utils}]}, + {mod_event_pusher_rabbit, + [{presence_exchange,[{name,<<"presence">>},{type,<<"topic">>}]}, + {chat_msg_exchange, + [{name,<<"chat_msg">>}, + {sent_topic,<<"chat_msg_sent">>}, + {recv_topic,<<"chat_msg_recv">>}]}, + {groupchat_msg_exchange, + [{name,<<"groupchat_msg">>}, + {sent_topic,<<"groupchat_msg_sent">>}, + {recv_topic,<<"groupchat_msg_recv">>}]}]}, + {mod_vcard, + [{matches,1}, + {search,true}, + {host,"directory.example.com"}, + {ldap_vcard_map, + [{<<"FAMILY">>,<<"%s">>,[<<"sn">>]}, + {<<"FN">>,<<"%s">>,[<<"displayName">>]}]}, + {ldap_search_fields, + [{<<"User">>,<<"%u">>},{<<"Full Name">>,<<"displayName">>}]}, + {ldap_search_reported, + [{<<"Full Name">>,<<"FN">>},{<<"Given Name">>,<<"FIRST">>}]}]}, + {mod_keystore, + [{keys, + [{access_secret,ram}, + {access_psk,{file,"priv/access_psk"}}, + {provision_psk,{file,"priv/provision_psk"}}]}, + {ram_key_size,1000}]}, + {mod_mam_rdbms_arch,[no_writer,pm]}, + {mod_bosh, + [{inactivity,20}, + {max_wait,infinity}, + {server_acks,true}, + {backend,mnesia}, + {maxpause,120}]}, + {mod_push_service_mongoosepush, + [{pool_name,mongoose_push_http}, + {api_version,"v3"}, + {max_http_connections,100}]}, + {mod_auth_token, + [{{validity_period,access},{13,minutes}}, + {{validity_period,refresh},{13,days}}]}, + {mod_extdisco, + [[{host,"stun1"}, + {password,"password"}, + {port,3478}, + {transport,"udp"}, + {type,stun}, + {username,"username"}], + [{host,"stun2"}, + {password,"password"}, + {port,2222}, + {transport,"tcp"}, + {type,stun}, + {username,"username"}], + [{host,"192.168.0.1"},{type,turn}]]}, + {mod_mam_cache_user,[muc,pm]}, + {mod_roster,[{versioning,true},{store_current_id,true}]}, + {mod_http_upload, + [{host,"upload.@HOST@"}, + {backend,s3}, + {expiration_time,120}, + {s3,[{bucket_url,"https://s3-eu-west-1.amazonaws.com/mybucket"}, + {region,"eu-west-1"}, + {add_acl,true}, + {access_key_id,"AKIAIOSFODNN7EXAMPLE"}, + {secret_access_key, + "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"}]}]}, + {mod_last,[{backend,mnesia},{iqdisc,{queues,10}}]}, + {mod_offline, + [{access_max_user_messages,max_user_offline_messages}, + {backend,riak}, + {bucket_type,<<"offline">>}]}, + {mod_mam_muc_rdbms_arch, + [muc, + {db_jid_format,mam_jid_rfc}, + {db_message_format,mam_message_xml}]}, + {mod_csi,[{buffer_max,40}]}, + {mod_caps,[{cache_size,1000},{cache_life_time,86}]}, + {mod_pubsub, + [{access_createnode,pubsub_createnode}, + {ignore_pep_from_offline,false}, + {backend,rdbms}, + {last_item_cache,mnesia}, + {max_items_node,1000}, + {plugins,[<<"flat">>,<<"pep">>]}, + {pep_mapping,[{"urn:xmpp:microblog:0","mb"}]}]}, + {mod_mam_rdbms_prefs,[pm]}, + {mod_mam_meta, + [{backend,rdbms}, + {no_stanzaid_element,true}, + {is_archivable_message,mod_mam_utils}, + {archive_chat_markers,true}, + {full_text_search,true}, + {pm,[{user_prefs_store,rdbms},{full_text_search,false}]}, + {muc, + [{host,"muc.example.com"}, + {rdbms_message_format,simple}, + {async_writer,false}, + {user_prefs_store,mnesia}]}]}, + {mod_muc_light, + [{config_schema, + [{"roomname","The Room"}, + {"display-lines",30,display_lines,integer}]}, + {rooms_in_rosters,true}, + {rooms_per_page,5}, + {max_occupants,50}, + {all_can_invite,true}, + {all_can_configure,true}, + {blocking,false}, + {rooms_per_user,10}, + {legacy_mode,true}, + {equal_occupants,true}, + {host,"muclight.example.com"}]}, + {mod_stream_management, + [{buffer_max,30}, + {ack_freq,1}, + {resume_timeout,600}, + {stale_h, + [{enabled,true}, + {stale_h_repeat_after,1800}, + {stale_h_geriatric,3600}]}]}, + {mod_muc_log, + [{outdir,"www/muc"}, + {access_log,muc}, + {top_link,{"/","Home"}}, + {cssfile,<<"path/to/css/file">>}]}, + {mod_inbox, + [{backend,rdbms}, + {reset_markers,[displayed]}, + {aff_changes,true}, + {remove_on_kicked,true}, + {groupchat,[muclight]}]}, + {mod_event_pusher_push, + [{backend,mnesia}, + {wpool,[{workers,200}]}, + {plugin_module,mod_event_pusher_push_plugin_defaults}, + {virtual_pubsub_hosts,["host1","host2"]}]}, + {mod_event_pusher, + [{backends, + [{sns, + [{access_key_id,"AKIAIOSFODNN7EXAMPLE"}, + {secret_access_key, + "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"}, + {region,"eu-west-1"}, + {account_id,"123456789012"}, + {sns_host,"sns.eu-west-1.amazonaws.com"}, + {muc_host,"conference.HOST"}, + {plugin_module,mod_event_pusher_sns_defaults}, + {presence_updates_topic,"user_presence_updated"}, + {pm_messages_topic,"user_message_sent"}, + {muc_messages_topic,"user_messagegroup_sent"}, + {pool_size,100}, + {publish_retry_count,2}, + {publish_retry_time_ms,50}]}, + {push, + [{backend,mnesia}, + {wpool,[{workers,200}]}, + {plugin_module,mod_event_pusher_push_plugin_defaults}, + {virtual_pubsub_hosts,["host1","host2"]}]}, + {http, + [{pool_name,http_pool}, + {path,"/notifications"}, + {callback_module,mod_event_pusher_http_defaults}]}, + {rabbit, + [{presence_exchange, + [{name,<<"presence">>},{type,<<"topic">>}]}, + {chat_msg_exchange, + [{name,<<"chat_msg">>}, + {sent_topic,<<"chat_msg_sent">>}, + {recv_topic,<<"chat_msg_recv">>}]}, + {groupchat_msg_exchange, + [{name,<<"groupchat_msg">>}, + {sent_topic,<<"groupchat_msg_sent">>}, + {recv_topic,<<"groupchat_msg_recv">>}]}]}]}]}, + {mod_revproxy, + [{routes, + [{"www.erlang-solutions.com","/admin","_", + "https://www.erlang-solutions.com/"}, + {":var.com","/:var","http://localhost:8080/"}, + {":domain.com","/","_","http://localhost:8080/:domain"}]}]}, + {mod_carboncopy,[{iqdisc,no_queue}]}, + {mod_version,[{os_info,true}]}]}. +{local_config, + {modules,<<"localhost">>}, + [{mod_jingle_sip, + [{proxy_host,"localhost"}, + {proxy_port,5600}, + {listen_port,5600}, + {local_host,"localhost"}, + {sdp_origin,"127.0.0.1"}]}, + {mod_shared_roster_ldap, + [{ldap_base,"ou=Users,dc=ejd,dc=com"}, + {ldap_groupattr,"ou"}, + {ldap_memberattr,"cn"}, + {ldap_userdesc,"cn"}, + {ldap_filter,"(objectClass=inetOrgPerson)"}, + {ldap_rfilter,"(objectClass=inetOrgPerson)"}, + {ldap_group_cache_validity,1}, + {ldap_user_cache_validity,1}]}, + {mod_mam_rdbms_user,[muc,pm]}, + {mod_global_distrib, + [{global_host,"example.com"}, + {local_host,"datacenter1.example.com"}, + {connections, + [{endpoints,[{"172.16.0.2",5555}]}, + {advertised_endpoints,[{"172.16.0.2",5555}]}, + {connections_per_endpoint,30}, + {tls_opts,[{certfile,"priv/dc1.pem"},{cafile,"priv/ca.pem"}]}]}, + {cache,[{domain_lifetime_seconds,60}]}, + {bounce,[{resend_after_ms,300},{max_retries,3}]}, + {redis,[{pool,global_distrib}]}]}, + {mod_register, + [{welcome_message,{"Subject","Body"}}, + {access,all}, + {registration_watchers,[<<"JID1">>,<<"JID2">>]}, + {password_strength,32}]}, + {mod_mam_rdbms_async_pool_writer,[pm]}, + {mod_adhoc,[{iqdisc,one_queue},{report_commands_node,true}]}, + {mod_event_pusher_sns, + [{access_key_id,"AKIAIOSFODNN7EXAMPLE"}, + {secret_access_key,"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"}, + {region,"eu-west-1"}, + {account_id,"123456789012"}, + {sns_host,"sns.eu-west-1.amazonaws.com"}, + {muc_host,"conference.HOST"}, + {plugin_module,mod_event_pusher_sns_defaults}, + {presence_updates_topic,"user_presence_updated"}, + {pm_messages_topic,"user_message_sent"}, + {muc_messages_topic,"user_messagegroup_sent"}, + {pool_size,100}, + {publish_retry_count,2}, + {publish_retry_time_ms,50}]}, + {mod_muc, + [{host,"muc.example.com"}, + {access,muc}, + {access_create,muc_create}, + {http_auth_pool,my_auth_pool}, + {default_room_options, + [{password_protected,true}, + {affiliations, + [{{<<"alice">>,<<"localhost">>,<<"resource1">>},member}, + {{<<"bob">>,<<"localhost">>,<<"resource2">>},owner}]}]}]}, + {mod_ping, + [{send_pings,true}, + {ping_interval,60}, + {timeout_action,none}, + {ping_req_timeout,32}]}, + {mod_mam, + [{archive_chat_markers,true}, + {full_text_search,false}, + {is_archivable_message,mod_mam_utils}, + {no_stanzaid_element,true}]}, + {mod_disco, + [{iqdisc,one_queue}, + {extra_domains,[<<"some_domain">>,<<"another_domain">>]}, + {server_info, + [{all,"abuse-address",["admin@example.com"]}, + {[mod_muc,mod_disco], + "friendly-spirits", + ["spirit1@localhost","spirit2@localhost"]}]}, + {users_can_see_hidden_services,true}]}, + {mod_event_pusher_http, + [{configs, + [[{pool_name,http_pool}, + {path,"/notifications"}, + {callback_module,mod_event_pusher_http_defaults}]]}]}, + {mod_event_pusher_hook_translator,[]}, + {mod_mam_mnesia_prefs,[muc]}, + {mod_mam_muc, + [{archive_chat_markers,true}, + {full_text_search,true}, + {host,"muc.example.com"}, + {is_archivable_message,mod_mam_utils}]}, + {mod_event_pusher_rabbit, + [{presence_exchange,[{name,<<"presence">>},{type,<<"topic">>}]}, + {chat_msg_exchange, + [{name,<<"chat_msg">>}, + {sent_topic,<<"chat_msg_sent">>}, + {recv_topic,<<"chat_msg_recv">>}]}, + {groupchat_msg_exchange, + [{name,<<"groupchat_msg">>}, + {sent_topic,<<"groupchat_msg_sent">>}, + {recv_topic,<<"groupchat_msg_recv">>}]}]}, + {mod_vcard, + [{matches,1}, + {search,true}, + {host,"directory.example.com"}, + {ldap_vcard_map, + [{<<"FAMILY">>,<<"%s">>,[<<"sn">>]}, + {<<"FN">>,<<"%s">>,[<<"displayName">>]}]}, + {ldap_search_fields, + [{<<"User">>,<<"%u">>},{<<"Full Name">>,<<"displayName">>}]}, + {ldap_search_reported, + [{<<"Full Name">>,<<"FN">>},{<<"Given Name">>,<<"FIRST">>}]}]}, + {mod_keystore, + [{keys, + [{access_secret,ram}, + {access_psk,{file,"priv/access_psk"}}, + {provision_psk,{file,"priv/provision_psk"}}]}, + {ram_key_size,1000}]}, + {mod_mam_rdbms_arch,[no_writer,pm]}, + {mod_bosh, + [{inactivity,20}, + {max_wait,infinity}, + {server_acks,true}, + {backend,mnesia}, + {maxpause,120}]}, + {mod_push_service_mongoosepush, + [{pool_name,mongoose_push_http}, + {api_version,"v3"}, + {max_http_connections,100}]}, + {mod_auth_token, + [{{validity_period,access},{13,minutes}}, + {{validity_period,refresh},{13,days}}]}, + {mod_extdisco, + [[{host,"stun1"}, + {password,"password"}, + {port,3478}, + {transport,"udp"}, + {type,stun}, + {username,"username"}], + [{host,"stun2"}, + {password,"password"}, + {port,2222}, + {transport,"tcp"}, + {type,stun}, + {username,"username"}], + [{host,"192.168.0.1"},{type,turn}]]}, + {mod_mam_cache_user,[muc,pm]}, + {mod_roster,[{versioning,true},{store_current_id,true}]}, + {mod_http_upload, + [{host,"upload.@HOST@"}, + {backend,s3}, + {expiration_time,120}, + {s3,[{bucket_url,"https://s3-eu-west-1.amazonaws.com/mybucket"}, + {region,"eu-west-1"}, + {add_acl,true}, + {access_key_id,"AKIAIOSFODNN7EXAMPLE"}, + {secret_access_key, + "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"}]}]}, + {mod_last,[{backend,mnesia},{iqdisc,{queues,10}}]}, + {mod_offline, + [{access_max_user_messages,max_user_offline_messages}, + {backend,riak}, + {bucket_type,<<"offline">>}]}, + {mod_mam_muc_rdbms_arch, + [muc, + {db_jid_format,mam_jid_rfc}, + {db_message_format,mam_message_xml}]}, + {mod_csi,[{buffer_max,40}]}, + {mod_caps,[{cache_size,1000},{cache_life_time,86}]}, + {mod_pubsub, + [{access_createnode,pubsub_createnode}, + {ignore_pep_from_offline,false}, + {backend,rdbms}, + {last_item_cache,mnesia}, + {max_items_node,1000}, + {plugins,[<<"flat">>,<<"pep">>]}, + {pep_mapping,[{"urn:xmpp:microblog:0","mb"}]}]}, + {mod_mam_rdbms_prefs,[pm]}, + {mod_mam_meta, + [{backend,rdbms}, + {no_stanzaid_element,true}, + {is_archivable_message,mod_mam_utils}, + {archive_chat_markers,true}, + {full_text_search,true}, + {pm,[{user_prefs_store,rdbms},{full_text_search,false}]}, + {muc, + [{host,"muc.example.com"}, + {rdbms_message_format,simple}, + {async_writer,false}, + {user_prefs_store,mnesia}]}]}, + {mod_muc_light, + [{config_schema, + [{"roomname","The Room"}, + {"display-lines",30,display_lines,integer}]}, + {rooms_in_rosters,true}, + {rooms_per_page,5}, + {max_occupants,50}, + {all_can_invite,true}, + {all_can_configure,true}, + {blocking,false}, + {rooms_per_user,10}, + {legacy_mode,true}, + {equal_occupants,true}, + {host,"muclight.example.com"}]}, + {mod_stream_management, + [{buffer_max,30}, + {ack_freq,1}, + {resume_timeout,600}, + {stale_h, + [{enabled,true}, + {stale_h_repeat_after,1800}, + {stale_h_geriatric,3600}]}]}, + {mod_muc_log, + [{outdir,"www/muc"}, + {access_log,muc}, + {top_link,{"/","Home"}}, + {cssfile,<<"path/to/css/file">>}]}, + {mod_inbox, + [{backend,rdbms}, + {reset_markers,[displayed]}, + {aff_changes,true}, + {remove_on_kicked,true}, + {groupchat,[muclight]}]}, + {mod_event_pusher_push, + [{backend,mnesia}, + {wpool,[{workers,200}]}, + {plugin_module,mod_event_pusher_push_plugin_defaults}, + {virtual_pubsub_hosts,["host1","host2"]}]}, + {mod_event_pusher, + [{backends, + [{sns, + [{access_key_id,"AKIAIOSFODNN7EXAMPLE"}, + {secret_access_key, + "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"}, + {region,"eu-west-1"}, + {account_id,"123456789012"}, + {sns_host,"sns.eu-west-1.amazonaws.com"}, + {muc_host,"conference.HOST"}, + {plugin_module,mod_event_pusher_sns_defaults}, + {presence_updates_topic,"user_presence_updated"}, + {pm_messages_topic,"user_message_sent"}, + {muc_messages_topic,"user_messagegroup_sent"}, + {pool_size,100}, + {publish_retry_count,2}, + {publish_retry_time_ms,50}]}, + {push, + [{backend,mnesia}, + {wpool,[{workers,200}]}, + {plugin_module,mod_event_pusher_push_plugin_defaults}, + {virtual_pubsub_hosts,["host1","host2"]}]}, + {http, + [{pool_name,http_pool}, + {path,"/notifications"}, + {callback_module,mod_event_pusher_http_defaults}]}, + {rabbit, + [{presence_exchange, + [{name,<<"presence">>},{type,<<"topic">>}]}, + {chat_msg_exchange, + [{name,<<"chat_msg">>}, + {sent_topic,<<"chat_msg_sent">>}, + {recv_topic,<<"chat_msg_recv">>}]}, + {groupchat_msg_exchange, + [{name,<<"groupchat_msg">>}, + {sent_topic,<<"groupchat_msg_sent">>}, + {recv_topic,<<"groupchat_msg_recv">>}]}]}]}]}, + {mod_revproxy, + [{routes, + [{"www.erlang-solutions.com","/admin","_", + "https://www.erlang-solutions.com/"}, + {":var.com","/:var","http://localhost:8080/"}, + {":domain.com","/","_","http://localhost:8080/:domain"}]}]}, + {mod_carboncopy,[{iqdisc,no_queue}]}, + {mod_version,[{os_info,true}]}]}. diff --git a/test/config_parser_SUITE_data/mongooseim-pgsql.cfg b/test/config_parser_SUITE_data/mongooseim-pgsql.cfg deleted file mode 100644 index a7a7eb86f6..0000000000 --- a/test/config_parser_SUITE_data/mongooseim-pgsql.cfg +++ /dev/null @@ -1,193 +0,0 @@ -{loglevel, warning}. -{hosts, ["localhost", - "anonymous.localhost", - "localhost.bis" - ] }. -{listen, - [ - { 5280, ejabberd_cowboy, [ - {transport_options, [{num_acceptors, 10}, {max_connections, 1024}]}, - {modules, [ - {"_", "/http-bind", mod_bosh}, - {"_", "/ws-xmpp", mod_websockets, [{ejabberd_service, [ - {access, all}, - {shaper_rule, fast}, - {password, "secret"}]} - ]} - ]} - ]}, - { 5285, ejabberd_cowboy, [ - {transport_options, [{num_acceptors, 10}, {max_connections, 1024}]}, - {ssl, [{certfile, "tools/ssl/mongooseim/cert.pem"}, {keyfile, "tools/ssl/mongooseim/key.pem"}, {password, ""}]}, - {modules, [ - {"_", "/http-bind", mod_bosh}, - {"_", "/ws-xmpp", mod_websockets, [ - {timeout, infinity}, - {ping_rate, none}, - {max_stanza_size, 100} - ]}, - {"localhost", "/api", mongoose_api_admin, [{auth, {<<"ala">>, <<"makotaipsa">>}}]}, - {"localhost", "/api/contacts/{:jid}", mongoose_api_client, []} - ]} - - ]}, - { { 8088, "127.0.0.1"} , ejabberd_cowboy, [ - {transport_options, [{num_acceptors, 10}, {max_connections, 1024}]}, - {modules, [ - {"localhost", "/api", mongoose_api_admin, []} - ]} - ]}, - { 8089 , ejabberd_cowboy, [ - {transport_options, [{num_acceptors, 10}, {max_connections, 1024}]}, - {protocol_options, [{compress, true}]}, - {ssl, [{certfile, "tools/ssl/mongooseim/cert.pem"}, {keyfile, "tools/ssl/mongooseim/key.pem"}, {password, ""}]}, - {modules, [ - {"_", "/api/sse", lasse_handler, [mongoose_client_api_sse]}, - {"_", "/api/messages/[:with]", mongoose_client_api_messages, []}, - {"_", "/api/contacts/[:jid]", mongoose_client_api_contacts, []}, - {"_", "/api/rooms/[:id]", mongoose_client_api_rooms, []}, - {"_", "/api/rooms/[:id]/config", mongoose_client_api_rooms_config, []}, - {"_", "/api/rooms/:id/users/[:user]", mongoose_client_api_rooms_users, []}, - {"_", "/api/rooms/[:id]/messages", mongoose_client_api_rooms_messages, []}, - {"_", "/api-docs", cowboy_swagger_redirect_handler, #{}}, - {"_", "/api-docs/swagger.json", cowboy_swagger_json_handler, #{}}, - {"_", "/api-docs/[...]", cowboy_static, {priv_dir, cowboy_swagger, "swagger", [{mimetypes, cow_mimetypes, all}]}} - ]} - ]}, - { { 5288, "127.0.0.1"} , ejabberd_cowboy, [ - {transport_options, [{num_acceptors, 10}, {max_connections, 1024}]}, - {modules, [ - {"localhost", "/api", mongoose_api, [{handlers, [mongoose_api_metrics, - mongoose_api_users]}]} - ]} - ]}, - { 5222, ejabberd_c2s, [ - {certfile, "tools/ssl/mongooseim/server.pem"}, starttls, - {zlib, 10000}, - {access, c2s}, - {shaper, c2s_shaper}, - {max_stanza_size, 65536} - ,{dhfile, "tools/ssl/mongooseim/dh_server.pem"} - ]}, - { 5223, ejabberd_c2s, [ - {zlib, 4096}, - {access, c2s}, - {shaper, c2s_shaper}, - {max_stanza_size, 65536} - ]}, - { 5269, ejabberd_s2s_in, [ - {shaper, s2s_shaper}, - {max_stanza_size, 131072} - ,{dhfile, "tools/ssl/mongooseim/dh_server.pem"} - ]} - ,{ 8888, ejabberd_service, [ - {access, all}, - {shaper_rule, fast}, - {ip, {127, 0, 0, 1}}, - {password, "secret"} - ]}, - { 8666, ejabberd_service, [ - {access, all}, - {conflict_behaviour, kick_old}, - {shaper_rule, fast}, - {ip, {127, 0, 0, 1}}, - {password, "secret"} - ]}, - { 8189, ejabberd_service, [ - {access, all}, - {hidden_components, true}, - {shaper_rule, fast}, - {ip, {127, 0, 0, 1}}, - {password, "secret"} - ]} - ]}. -{s2s_use_starttls, optional}. -{s2s_certfile, "tools/ssl/mongooseim/server.pem"}. -{s2s_default_policy, allow }. -{outgoing_s2s_port, 5299 }. -{ {s2s_addr, "fed1"}, {127,0,0,1} }. -{sm_backend, {mnesia, []} }. -{auth_method, rdbms }. -{auth_opts, [ - {password_format, {scram, [sha256]}} - , {scram_iterations, 64} - , {cyrsasl_external, standard} - ]}. -{host_config, "anonymous.localhost", [{auth_method, anonymous}, - {allow_multiple_connections, true}, - {anonymous_protocol, both}, - {auth_opts, []}]}. -{outgoing_pools, [ - {redis, <<"localhost">>, global_distrib, [{workers, 10}], []}, - {rdbms, global, default, [{workers, 5}], - [{server, {pgsql, "localhost", "ejabberd", "ejabberd", "mongooseim_secret", - [{ssl, required}, {ssl_opts, [{verify, verify_peer}, - {cacertfile, "priv/ssl/cacert.pem"}, {server_name_indication, disable}]}]}}]} - ]}. -{shaper, normal, {maxrate, 1000}}. -{shaper, fast, {maxrate, 50000}}. -{max_fsm_queue, 1000}. -{acl, local, {user_regexp, ""}}. -{access, max_user_sessions, [{10, all}]}. -{access, max_user_offline_messages, [{5000, admin}, {100, all}]}. -{access, local, [{allow, local}]}. -{access, c2s, [{deny, blocked}, - {allow, all}]}. -{access, c2s_shaper, [{none, admin}, - {normal, all}]}. -{access, s2s_shaper, [{fast, all}]}. -{access, muc_admin, [{allow, admin}]}. -{access, muc_create, [{allow, local}]}. -{access, muc, [{allow, all}]}. -{access, register, [{allow, all}]}. -{registration_timeout, infinity}. -{access, mam_set_prefs, [{default, all}]}. -{access, mam_get_prefs, [{default, all}]}. -{access, mam_lookup_messages, [{default, all}]}. -{shaper, mam_shaper, {maxrate, 1}}. -{shaper, mam_global_shaper, {maxrate, 1000}}. -{access, mam_set_prefs_shaper, [{mam_shaper, all}]}. -{access, mam_get_prefs_shaper, [{mam_shaper, all}]}. -{access, mam_lookup_messages_shaper, [{mam_shaper, all}]}. -{access, mam_set_prefs_global_shaper, [{mam_global_shaper, all}]}. -{access, mam_get_prefs_global_shaper, [{mam_global_shaper, all}]}. -{access, mam_lookup_messages_global_shaper, [{mam_global_shaper, all}]}. -{language, "en"}. -{all_metrics_are_global, false }. -{services, - [ - {service_admin_extra, [{submods, [node, accounts, sessions, vcard, gdpr, upload, - roster, last, private, stanza, stats]}]}, - {service_mongoose_system_metrics, [{initial_report, 300000}, - {periodic_report, 10800000}]} - ] -}. -{modules, - [ - {mod_adhoc, []}, - {mod_amp, []}, - {mod_disco, [{users_can_see_hidden_services, false}]}, - {mod_commands, []}, - {mod_muc_commands, []}, - {mod_muc_light_commands, []}, - {mod_last, [{backend, rdbms}]}, - {mod_stream_management, [ - ]}, - {mod_offline, [{backend, rdbms}]}, - {mod_privacy, [{backend, rdbms}]}, - {mod_blocking, []}, - {mod_private, [{backend, rdbms}]}, - {mod_register, [ - {welcome_message, {"Hello", "I am MongooseIM"}}, - {ip_access, [{allow, "127.0.0.0/8"}, - {deny, "0.0.0.0/0"}]}, - {access, register} - ]}, - {mod_roster, [{backend, rdbms}]}, - {mod_sic, []}, - {mod_vcard, [ {backend, rdbms}, - {host, "vjud.@HOST@"} - ]}, - {mod_bosh, []}, - {mod_carboncopy, []} - ]}. diff --git a/test/config_parser_SUITE_data/mongooseim-pgsql.options b/test/config_parser_SUITE_data/mongooseim-pgsql.options new file mode 100644 index 0000000000..b91c85d37c --- /dev/null +++ b/test/config_parser_SUITE_data/mongooseim-pgsql.options @@ -0,0 +1,229 @@ +{acl,{local,global},{user_regexp,<<>>}}. +{config,hosts,[<<"localhost">>,<<"anonymous.localhost">>,<<"localhost.bis">>]}. +{config,language,<<"en">>}. +{config,sm_backend,{mnesia,[]}}. +{config,{access,c2s,global},[{deny,blocked},{allow,all}]}. +{config,{access,c2s_shaper,global},[{none,admin},{normal,all}]}. +{config,{access,local,global},[{allow,local}]}. +{config,{access,mam_get_prefs,global},[{default,all}]}. +{config,{access,mam_get_prefs_global_shaper,global},[{mam_global_shaper,all}]}. +{config,{access,mam_get_prefs_shaper,global},[{mam_shaper,all}]}. +{config,{access,mam_lookup_messages,global},[{default,all}]}. +{config,{access,mam_lookup_messages_global_shaper,global}, + [{mam_global_shaper,all}]}. +{config,{access,mam_lookup_messages_shaper,global},[{mam_shaper,all}]}. +{config,{access,mam_set_prefs,global},[{default,all}]}. +{config,{access,mam_set_prefs_global_shaper,global},[{mam_global_shaper,all}]}. +{config,{access,mam_set_prefs_shaper,global},[{mam_shaper,all}]}. +{config,{access,max_user_offline_messages,global},[{5000,admin},{100,all}]}. +{config,{access,max_user_sessions,global},[{10,all}]}. +{config,{access,muc,global},[{allow,all}]}. +{config,{access,muc_admin,global},[{allow,admin}]}. +{config,{access,muc_create,global},[{allow,local}]}. +{config,{access,register,global},[{allow,all}]}. +{config,{access,s2s_shaper,global},[{fast,all}]}. +{config,{shaper,fast,global},{maxrate,50000}}. +{config,{shaper,mam_global_shaper,global},{maxrate,1000}}. +{config,{shaper,mam_shaper,global},{maxrate,1}}. +{config,{shaper,normal,global},{maxrate,1000}}. +{local_config, + {modules,<<"anonymous.localhost">>}, + [{mod_register, + [{welcome_message,{"Hello","I am MongooseIM"}}, + {ip_access,[{allow,"127.0.0.0/8"},{deny,"0.0.0.0/0"}]}, + {access,register}]}, + {mod_adhoc,[]}, + {mod_sic,[]}, + {mod_private,[{backend,rdbms}]}, + {mod_privacy,[{backend,rdbms}]}, + {mod_disco,[{users_can_see_hidden_services,false}]}, + {mod_commands,[]}, + {mod_muc_light_commands,[]}, + {mod_vcard,[{backend,rdbms},{host,"vjud.@HOST@"}]}, + {mod_blocking,[]}, + {mod_bosh,[]}, + {mod_roster,[{backend,rdbms}]}, + {mod_last,[{backend,rdbms}]}, + {mod_offline,[{backend,rdbms}]}, + {mod_amp,[]}, + {mod_muc_commands,[]}, + {mod_stream_management,[]}, + {mod_carboncopy,[]}]}. +{local_config, + {modules,<<"localhost">>}, + [{mod_register, + [{welcome_message,{"Hello","I am MongooseIM"}}, + {ip_access,[{allow,"127.0.0.0/8"},{deny,"0.0.0.0/0"}]}, + {access,register}]}, + {mod_adhoc,[]}, + {mod_sic,[]}, + {mod_private,[{backend,rdbms}]}, + {mod_privacy,[{backend,rdbms}]}, + {mod_disco,[{users_can_see_hidden_services,false}]}, + {mod_commands,[]}, + {mod_muc_light_commands,[]}, + {mod_vcard,[{backend,rdbms},{host,"vjud.@HOST@"}]}, + {mod_blocking,[]}, + {mod_bosh,[]}, + {mod_roster,[{backend,rdbms}]}, + {mod_last,[{backend,rdbms}]}, + {mod_offline,[{backend,rdbms}]}, + {mod_amp,[]}, + {mod_muc_commands,[]}, + {mod_stream_management,[]}, + {mod_carboncopy,[]}]}. +{local_config, + {modules,<<"localhost.bis">>}, + [{mod_register, + [{welcome_message,{"Hello","I am MongooseIM"}}, + {ip_access,[{allow,"127.0.0.0/8"},{deny,"0.0.0.0/0"}]}, + {access,register}]}, + {mod_adhoc,[]}, + {mod_sic,[]}, + {mod_private,[{backend,rdbms}]}, + {mod_privacy,[{backend,rdbms}]}, + {mod_disco,[{users_can_see_hidden_services,false}]}, + {mod_commands,[]}, + {mod_muc_light_commands,[]}, + {mod_vcard,[{backend,rdbms},{host,"vjud.@HOST@"}]}, + {mod_blocking,[]}, + {mod_bosh,[]}, + {mod_roster,[{backend,rdbms}]}, + {mod_last,[{backend,rdbms}]}, + {mod_offline,[{backend,rdbms}]}, + {mod_amp,[]}, + {mod_muc_commands,[]}, + {mod_stream_management,[]}, + {mod_carboncopy,[]}]}. +{local_config,all_metrics_are_global,false}. +{local_config,listen, + [{{5280,{0,0,0,0},tcp}, + ejabberd_cowboy, + [{transport_options,[{num_acceptors,10},{max_connections,1024}]}, + {modules, + [{"_","/http-bind",mod_bosh}, + {"_","/ws-xmpp",mod_websockets, + [{ejabberd_service, + [{access,all},{shaper_rule,fast},{password,"secret"}]}]}]}]}, + {{5285,{0,0,0,0},tcp}, + ejabberd_cowboy, + [{transport_options,[{num_acceptors,10},{max_connections,1024}]}, + {ssl, + [{certfile,"tools/ssl/mongooseim/cert.pem"}, + {keyfile,"tools/ssl/mongooseim/key.pem"}, + {password,[]}]}, + {modules, + [{"_","/http-bind",mod_bosh}, + {"_","/ws-xmpp",mod_websockets, + [{timeout,infinity},{ping_rate,none},{max_stanza_size,100}]}, + {"localhost","/api",mongoose_api_admin, + [{auth,{<<"ala">>,<<"makotaipsa">>}}]}, + {"localhost","/api/contacts/{:jid}",mongoose_api_client,[]}]}]}, + {{8088,{127,0,0,1},tcp}, + ejabberd_cowboy, + [{transport_options,[{num_acceptors,10},{max_connections,1024}]}, + {modules,[{"localhost","/api",mongoose_api_admin,[]}]}]}, + {{8089,{0,0,0,0},tcp}, + ejabberd_cowboy, + [{transport_options,[{num_acceptors,10},{max_connections,1024}]}, + {protocol_options,[{compress,true}]}, + {ssl, + [{certfile,"tools/ssl/mongooseim/cert.pem"}, + {keyfile,"tools/ssl/mongooseim/key.pem"}, + {password,[]}]}, + {modules, + [{"_","/api/sse",lasse_handler,[mongoose_client_api_sse]}, + {"_","/api/messages/[:with]",mongoose_client_api_messages,[]}, + {"_","/api/contacts/[:jid]",mongoose_client_api_contacts,[]}, + {"_","/api/rooms/[:id]",mongoose_client_api_rooms,[]}, + {"_","/api/rooms/[:id]/config",mongoose_client_api_rooms_config, + []}, + {"_","/api/rooms/:id/users/[:user]", + mongoose_client_api_rooms_users,[]}, + {"_","/api/rooms/[:id]/messages", + mongoose_client_api_rooms_messages,[]}, + {"_","/api-docs",cowboy_swagger_redirect_handler,#{}}, + {"_","/api-docs/swagger.json",cowboy_swagger_json_handler,#{}}, + {"_","/api-docs/[...]",cowboy_static, + {priv_dir,cowboy_swagger,"swagger", + [{mimetypes,cow_mimetypes,all}]}}]}]}, + {{5288,{127,0,0,1},tcp}, + ejabberd_cowboy, + [{transport_options,[{num_acceptors,10},{max_connections,1024}]}, + {modules, + [{"localhost","/api",mongoose_api, + [{handlers,[mongoose_api_metrics,mongoose_api_users]}]}]}]}, + {{5222,{0,0,0,0},tcp}, + ejabberd_c2s, + [{certfile,"tools/ssl/mongooseim/server.pem"}, + starttls, + {zlib,10000}, + {access,c2s}, + {shaper,c2s_shaper}, + {max_stanza_size,65536}, + {dhfile,"tools/ssl/mongooseim/dh_server.pem"}]}, + {{5223,{0,0,0,0},tcp}, + ejabberd_c2s, + [{zlib,4096},{access,c2s},{shaper,c2s_shaper},{max_stanza_size,65536}]}, + {{5269,{0,0,0,0},tcp}, + ejabberd_s2s_in, + [{shaper,s2s_shaper}, + {max_stanza_size,131072}, + {dhfile,"tools/ssl/mongooseim/dh_server.pem"}]}, + {{8888,{127,0,0,1},tcp}, + ejabberd_service, + [{access,all},{shaper_rule,fast},{password,"secret"}]}, + {{8666,{127,0,0,1},tcp}, + ejabberd_service, + [{access,all}, + {conflict_behaviour,kick_old}, + {shaper_rule,fast}, + {password,"secret"}]}, + {{8189,{127,0,0,1},tcp}, + ejabberd_service, + [{access,all}, + {hidden_components,true}, + {shaper_rule,fast}, + {password,"secret"}]}]}. +{local_config,loglevel,warning}. +{local_config,max_fsm_queue,1000}. +{local_config,outgoing_pools, + [{redis,<<"localhost">>,global_distrib,[{workers,10}],[]}, + {rdbms,global,default, + [{workers,5}], + [{server, + {pgsql,"localhost","ejabberd","ejabberd","mongooseim_secret", + [{ssl,required}, + {ssl_opts, + [{verify,verify_peer}, + {cacertfile,"priv/ssl/cacert.pem"}, + {server_name_indication,disable}]}]}}]}]}. +{local_config,outgoing_s2s_port,5299}. +{local_config,registration_timeout,infinity}. +{local_config,s2s_certfile,"tools/ssl/mongooseim/server.pem"}. +{local_config,s2s_use_starttls,optional}. +{local_config,services, + [{service_admin_extra, + [{submods, + [node,accounts,sessions,vcard,gdpr,upload,roster,last,private, + stanza,stats]}]}, + {service_mongoose_system_metrics, + [{initial_report,300000},{periodic_report,10800000}]}]}. +{local_config,{allow_multiple_connections,<<"anonymous.localhost">>},true}. +{local_config,{anonymous_protocol,<<"anonymous.localhost">>},both}. +{local_config,{auth_method,<<"anonymous.localhost">>},anonymous}. +{local_config,{auth_method,<<"localhost">>},rdbms}. +{local_config,{auth_method,<<"localhost.bis">>},rdbms}. +{local_config,{auth_opts,<<"anonymous.localhost">>},[]}. +{local_config,{auth_opts,<<"localhost">>}, + [{password_format,{scram,[sha256]}}, + {scram_iterations,64}, + {cyrsasl_external,standard}]}. +{local_config,{auth_opts,<<"localhost.bis">>}, + [{password_format,{scram,[sha256]}}, + {scram_iterations,64}, + {cyrsasl_external,standard}]}. +{local_config,{s2s_addr,<<"fed1">>},{127,0,0,1}}. +{local_config,{s2s_default_policy,<<"anonymous.localhost">>},allow}. +{local_config,{s2s_default_policy,<<"localhost">>},allow}. +{local_config,{s2s_default_policy,<<"localhost.bis">>},allow}. diff --git a/test/config_parser_SUITE_data/outgoing_pools.cfg b/test/config_parser_SUITE_data/outgoing_pools.cfg deleted file mode 100644 index fc4ef4780a..0000000000 --- a/test/config_parser_SUITE_data/outgoing_pools.cfg +++ /dev/null @@ -1,42 +0,0 @@ -{hosts, ["localhost", - "anonymous.localhost", - "localhost.bis" - ] }. - -{outgoing_pools, [ - {redis, <<"localhost">>, global_distrib, [{workers, 10}], []}, - {rdbms, global, default, [{workers, 5}], - [{server, {pgsql, "localhost", "ejabberd", "ejabberd", "mongooseim_secret", - [{ssl, required}, {ssl_opts, [{verify, verify_peer}, - {cacertfile, "priv/ssl/cacert.pem"}, {server_name_indication, disable}]}]}}, - {keepalive_interval, 30}]}, - {http, global, mongoose_push_http, - [{workers, 50}], - [{server, "https://localhost:8443"}, - {path_prefix, "/"}, - {request_timeout, 2000} - ]}, - {riak, global, default, [{workers, 20}, {strategy, next_worker}], - [{address, "127.0.0.1"}, {port, 8087}, - {credentials, "username", "pass"}, - {cacertfile, "path/to/cacert.pem"}]}, - {cassandra, global, default, [], - [ - {servers, [{"cassandra_server1.example.com", 9042}, - {"cassandra_server2.example.com", 9042}] }, - {keyspace, "big_mongooseim"} - ]}, - {elastic, global, default, [], [{host, "localhost"}]}, - {rabbit, host, event_pusher, [{workers, 20}], - [{amqp_host, "localhost"}, - {amqp_port, 5672}, - {amqp_username, "guest"}, - {amqp_password, "guest"}, - {confirms_enabled, true}, - {max_worker_queue_len, 100}]}, - {ldap, host, default, [{workers, 5}], - [{servers, ["ldap-server.example.com"]}, - {rootdn, "cn=admin,dc=example,dc=com"}, - {password, "ldap-admin-password"}] - } - ]}. diff --git a/test/config_parser_SUITE_data/outgoing_pools.options b/test/config_parser_SUITE_data/outgoing_pools.options new file mode 100644 index 0000000000..ea2592fc51 --- /dev/null +++ b/test/config_parser_SUITE_data/outgoing_pools.options @@ -0,0 +1,43 @@ +{config,hosts,[<<"localhost">>,<<"anonymous.localhost">>,<<"localhost.bis">>]}. +{local_config,outgoing_pools, + [{redis,<<"localhost">>,global_distrib,[{workers,10}],[]}, + {rdbms,global,default, + [{workers,5}], + [{server, + {pgsql,"localhost","ejabberd","ejabberd","mongooseim_secret", + [{ssl,required}, + {ssl_opts, + [{verify,verify_peer}, + {cacertfile,"priv/ssl/cacert.pem"}, + {server_name_indication,disable}]}]}}, + {keepalive_interval,30}]}, + {http,global,mongoose_push_http, + [{workers,50}], + [{server,"https://localhost:8443"}, + {path_prefix,"/"}, + {request_timeout,2000}]}, + {riak,global,default, + [{workers,20},{strategy,next_worker}], + [{address,"127.0.0.1"}, + {port,8087}, + {credentials,"username","pass"}, + {cacertfile,"path/to/cacert.pem"}]}, + {cassandra,global,default,[], + [{servers, + [{"cassandra_server1.example.com",9042}, + {"cassandra_server2.example.com",9042}]}, + {keyspace,"big_mongooseim"}]}, + {elastic,global,default,[],[{host,"localhost"}]}, + {rabbit,host,event_pusher, + [{workers,20}], + [{amqp_host,"localhost"}, + {amqp_port,5672}, + {amqp_username,"guest"}, + {amqp_password,"guest"}, + {confirms_enabled,true}, + {max_worker_queue_len,100}]}, + {ldap,host,default, + [{workers,5}], + [{servers,["ldap-server.example.com"]}, + {rootdn,"cn=admin,dc=example,dc=com"}, + {password,"ldap-admin-password"}]}]}. diff --git a/test/config_parser_SUITE_data/s2s_only.cfg b/test/config_parser_SUITE_data/s2s_only.cfg deleted file mode 100644 index fc287ead1c..0000000000 --- a/test/config_parser_SUITE_data/s2s_only.cfg +++ /dev/null @@ -1,17 +0,0 @@ -{hosts, [ - "localhost", - "dummy_host" -]}. - -{s2s_use_starttls, optional}. -{s2s_certfile, "tools/ssl/mongooseim/server.pem"}. -{s2s_default_policy, allow }. -{outgoing_s2s_port, 5299 }. -{ {s2s_addr, "fed1"}, {127,0,0,1} }. -{s2s_ciphers, "TLSv1.2:TLSv1.3"}. -{domain_certfile, "example.org", "/path/to/example_org.pem"}. -{domain_certfile, "example.com", "/path/to/example_com.pem"}. -{outgoing_s2s_options, [ipv4, ipv6], 10000}. -{s2s_shared, <<"shared secret">>}. -{s2s_dns_options, [{timeout, 30}, {retries, 1}]}. -{s2s_max_retry_delay, 30}. diff --git a/test/config_parser_SUITE_data/s2s_only.options b/test/config_parser_SUITE_data/s2s_only.options new file mode 100644 index 0000000000..28c698d12f --- /dev/null +++ b/test/config_parser_SUITE_data/s2s_only.options @@ -0,0 +1,17 @@ +{config,hosts,[<<"localhost">>,<<"dummy_host">>]}. +{local_config,outgoing_s2s_families,[ipv4,ipv6]}. +{local_config,outgoing_s2s_port,5299}. +{local_config,outgoing_s2s_timeout,10000}. +{local_config,s2s_certfile,"tools/ssl/mongooseim/server.pem"}. +{local_config,s2s_ciphers,"TLSv1.2:TLSv1.3"}. +{local_config,s2s_dns_options,[{timeout,30},{retries,1}]}. +{local_config,s2s_use_starttls,optional}. +{local_config,{domain_certfile,"example.com"},"/path/to/example_com.pem"}. +{local_config,{domain_certfile,"example.org"},"/path/to/example_org.pem"}. +{local_config,{s2s_addr,<<"fed1">>},{127,0,0,1}}. +{local_config,{s2s_default_policy,<<"dummy_host">>},allow}. +{local_config,{s2s_default_policy,<<"localhost">>},allow}. +{local_config,{s2s_max_retry_delay,<<"dummy_host">>},30}. +{local_config,{s2s_max_retry_delay,<<"localhost">>},30}. +{local_config,{s2s_shared,<<"dummy_host">>},<<"shared secret">>}. +{local_config,{s2s_shared,<<"localhost">>},<<"shared secret">>}. From a10b54c05283bc0ece0dc668e35493b8a83160c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Fri, 23 Oct 2020 15:23:31 +0200 Subject: [PATCH 002/104] Do not use the 'cfg' format in ejabberd_config_SUITE 1. Remove the 'split_config' case as including is not supported in TOML - It is easier to merge config files with TOML, so this feature is most likely not required anymore (unless new demand arises). 2. Remove tests for 'node_specific_options' - This is not supported in TOML. - It is not clear if something is going to be implemented instead, any future work will be done as a part of the config reloading rework (or dropping it altogether). 3. Update example config files to TOML, simplify when possible --- test/ejabberd_config_SUITE.erl | 123 +--- .../fake.domain.one/host.cfg | 2 - .../fake.domain.two/host.cfg | 3 - .../mongooseim.default.cfg | 600 ----------------- .../mongooseim.gd.toml | 7 + .../mongooseim.hosts.cfg | 8 - .../mongooseim.loglevel_err.toml | 3 + .../mongooseim.minimal.toml | 2 + .../mongooseim.no_listeners.cfg | 65 -- .../mongooseim.no_listeners.gd.node1_v1.cfg | 44 -- .../mongooseim.no_listeners.loglevel_err.cfg | 65 -- ...isteners.node_specific_module_node1_v1.cfg | 63 -- ...isteners.node_specific_module_node2_v1.cfg | 58 -- ...im.no_listeners.node_specific_node1_v1.cfg | 70 -- ...im.no_listeners.node_specific_node1_v2.cfg | 71 -- ...im.no_listeners.node_specific_node2_v1.cfg | 70 -- ...im.no_listeners.node_specific_node2_v2.cfg | 71 -- .../mongooseim.split.cfg | 625 ------------------ .../mongooseim.with_mod_offline.cfg | 601 ----------------- ...ooseim.with_mod_offline.different_opts.cfg | 601 ----------------- ...oseim.with_mod_offline.different_opts.toml | 5 + .../mongooseim.with_mod_offline.toml | 4 + 22 files changed, 43 insertions(+), 3118 deletions(-) delete mode 100644 test/ejabberd_config_SUITE_data/fake.domain.one/host.cfg delete mode 100644 test/ejabberd_config_SUITE_data/fake.domain.two/host.cfg delete mode 100644 test/ejabberd_config_SUITE_data/mongooseim.default.cfg create mode 100644 test/ejabberd_config_SUITE_data/mongooseim.gd.toml delete mode 100644 test/ejabberd_config_SUITE_data/mongooseim.hosts.cfg create mode 100644 test/ejabberd_config_SUITE_data/mongooseim.loglevel_err.toml create mode 100644 test/ejabberd_config_SUITE_data/mongooseim.minimal.toml delete mode 100644 test/ejabberd_config_SUITE_data/mongooseim.no_listeners.cfg delete mode 100644 test/ejabberd_config_SUITE_data/mongooseim.no_listeners.gd.node1_v1.cfg delete mode 100644 test/ejabberd_config_SUITE_data/mongooseim.no_listeners.loglevel_err.cfg delete mode 100644 test/ejabberd_config_SUITE_data/mongooseim.no_listeners.node_specific_module_node1_v1.cfg delete mode 100644 test/ejabberd_config_SUITE_data/mongooseim.no_listeners.node_specific_module_node2_v1.cfg delete mode 100644 test/ejabberd_config_SUITE_data/mongooseim.no_listeners.node_specific_node1_v1.cfg delete mode 100644 test/ejabberd_config_SUITE_data/mongooseim.no_listeners.node_specific_node1_v2.cfg delete mode 100644 test/ejabberd_config_SUITE_data/mongooseim.no_listeners.node_specific_node2_v1.cfg delete mode 100644 test/ejabberd_config_SUITE_data/mongooseim.no_listeners.node_specific_node2_v2.cfg delete mode 100644 test/ejabberd_config_SUITE_data/mongooseim.split.cfg delete mode 100644 test/ejabberd_config_SUITE_data/mongooseim.with_mod_offline.cfg delete mode 100644 test/ejabberd_config_SUITE_data/mongooseim.with_mod_offline.different_opts.cfg create mode 100644 test/ejabberd_config_SUITE_data/mongooseim.with_mod_offline.different_opts.toml create mode 100644 test/ejabberd_config_SUITE_data/mongooseim.with_mod_offline.toml diff --git a/test/ejabberd_config_SUITE.erl b/test/ejabberd_config_SUITE.erl index daf2729c11..92ef2d4c50 100644 --- a/test/ejabberd_config_SUITE.erl +++ b/test/ejabberd_config_SUITE.erl @@ -17,8 +17,7 @@ all() -> [smoke, {group, reload_local}, - {group, reload_cluster}, - split_config]. + {group, reload_cluster}]. groups() -> [{reload_local, [], [coalesce_multiple_local_config_options, @@ -26,8 +25,6 @@ groups() -> delete_a_module, reload_a_module]}, {reload_cluster, [], [cluster_smoke, - change_module_option_with_node_param_opts, - change_module_option_with_node_specific_mods, module_deps_work_correctly_with_reload_cluster, try_to_reload_with_different_options_on_nodes]} ]. @@ -68,7 +65,7 @@ end_per_group(_GroupName, _Config) -> smoke(Config) -> % when - start_ejabberd_with_config(Config, "mongooseim.default.cfg"), + start_ejabberd_with_config(Config, "mongooseim.minimal.toml"), % then ?assert(lists:keymember(mongooseim, 1, application:which_applications())), % cleanup @@ -80,11 +77,11 @@ coalesce_multiple_local_config_options(_Config) -> add_a_module(C) -> % given a running server with a specific module off - copy(data(C, "mongooseim.default.cfg"), data(C, "mongooseim.cfg")), - start_ejabberd_with_config(C, "mongooseim.cfg"), + copy(data(C, "mongooseim.minimal.toml"), data(C, "mongooseim.toml")), + start_ejabberd_with_config(C, "mongooseim.toml"), ?eq(false, gen_mod:is_loaded(<<"localhost">>, mod_offline)), % when adding the module to the configuration - copy(data(C, "mongooseim.with_mod_offline.cfg"), data(C, "mongooseim.cfg")), + copy(data(C, "mongooseim.with_mod_offline.toml"), data(C, "mongooseim.toml")), ejabberd_config:reload_local(), % then the new module gets started ?eq(true, gen_mod:is_loaded(<<"localhost">>, mod_offline)), @@ -93,11 +90,11 @@ add_a_module(C) -> delete_a_module(C) -> % given a running server with a specific module on - copy(data(C, "mongooseim.with_mod_offline.cfg"), data(C, "mongooseim.cfg")), - start_ejabberd_with_config(C, "mongooseim.cfg"), + copy(data(C, "mongooseim.with_mod_offline.toml"), data(C, "mongooseim.toml")), + start_ejabberd_with_config(C, "mongooseim.toml"), ?eq(true, gen_mod:is_loaded(<<"localhost">>, mod_offline)), % when deleting the module from the configuration - copy(data(C, "mongooseim.default.cfg"), data(C, "mongooseim.cfg")), + copy(data(C, "mongooseim.minimal.toml"), data(C, "mongooseim.toml")), ejabberd_config:reload_local(), % then the module is stopped ?eq(false, gen_mod:is_loaded(<<"localhost">>, mod_offline)), @@ -106,13 +103,13 @@ delete_a_module(C) -> reload_a_module(C) -> % given a running server with a specific module on - copy(data(C, "mongooseim.with_mod_offline.cfg"), data(C, "mongooseim.cfg")), - start_ejabberd_with_config(C, "mongooseim.cfg"), + copy(data(C, "mongooseim.with_mod_offline.toml"), data(C, "mongooseim.toml")), + start_ejabberd_with_config(C, "mongooseim.toml"), ?eq(true, gen_mod:is_loaded(<<"localhost">>, mod_offline)), OfflineProcBefore = get_module_pid(mod_offline_localhost), % when changing the module configuration - copy(data(C, "mongooseim.with_mod_offline.different_opts.cfg"), - data(C, "mongooseim.cfg")), + copy(data(C, "mongooseim.with_mod_offline.different_opts.toml"), + data(C, "mongooseim.toml")), ejabberd_config:reload_local(), % then the module is reloaded ?eq(true, gen_mod:is_loaded(<<"localhost">>, mod_offline)), @@ -121,54 +118,13 @@ reload_a_module(C) -> % cleanup ok = stop_ejabberd(). -split_config(Config) -> - % given - given_vhost_config_split_into_multiple_files(Config), - % when - application:load(mongooseim), - application:set_env(mongooseim, config, suite_priv(Config, "etc/mongooseim.cfg")), - {ok, _} = start_ejabberd(Config), - % then - then_vhost_config_works(Config), - % cleanup - ok = stop_ejabberd(). - -given_vhost_config_split_into_multiple_files(C) -> - [ ok = filelib:ensure_dir(D) || D <- [suite_priv(C, "etc/"), - suite_priv(C, "etc/fake.domain.one/"), - suite_priv(C, "etc/fake.domain.two/")] ], - copy(data(C, "mongooseim.split.cfg"), suite_priv(C, "etc/mongooseim.cfg")), - copy(data(C, "mongooseim.hosts.cfg"), suite_priv(C, "etc/mongooseim.hosts.cfg")), - copy(data(C, "fake.domain.one/host.cfg"), - suite_priv(C, "etc/fake.domain.one/host.cfg")), - copy(data(C, "fake.domain.one/host.cfg"), - suite_priv(C, "etc/fake.domain.one/certfile.pem")), - copy(data(C, "fake.domain.two/host.cfg"), - suite_priv(C, "etc/fake.domain.two/host.cfg")), - copy(data(C, "fake.domain.two/host.cfg"), - suite_priv(C, "etc/fake.domain.two/certfile.pem")). - -then_vhost_config_works(_C) -> - ?eq([{config, hosts, [<<"fake.domain.one">>, <<"fake.domain.two">>]}], - ets:lookup(config, hosts)), - ?eq(false, is_empty(ets:lookup(local_config, - {domain_certfile, <<"fake.domain.one">>}))), - ?eq(false, is_empty(ets:lookup(local_config, - {domain_certfile, <<"fake.domain.two">>}))), - ?eq(true, gen_mod:is_loaded(<<"fake.domain.one">>, mod_ping)), - ?eq(true, gen_mod:is_loaded(<<"fake.domain.two">>, mod_roster)), - ?eq(true, gen_mod:is_loaded(<<"fake.domain.two">>, mod_offline)), - ?eq(false, gen_mod:is_loaded(<<"fake.domain.one">>, mod_roster)), - ?eq(false, gen_mod:is_loaded(<<"fake.domain.one">>, mod_offline)), - ?eq(false, gen_mod:is_loaded(<<"fake.domain.two">>, mod_ping)). - cluster_smoke(C) -> SlaveNode = slave_node(C), - copy(data(C, "mongooseim.no_listeners.cfg"), data(C, "mongooseim.cfg")), - {ok, _} = start_ejabberd_with_config(C, "mongooseim.cfg"), - {ok, _} = start_remote_ejabberd_with_config(SlaveNode, C, "mongooseim.cfg"), + copy(data(C, "mongooseim.minimal.toml"), data(C, "mongooseim.toml")), + {ok, _} = start_ejabberd_with_config(C, "mongooseim.toml"), + {ok, _} = start_remote_ejabberd_with_config(SlaveNode, C, "mongooseim.toml"), maybe_join_cluster(SlaveNode), - [_,_] = ejabberd_config:config_states(), + [_, _] = ejabberd_config:config_states(), % cleanup ok = stop_ejabberd(), stop_remote_ejabberd(SlaveNode), @@ -176,54 +132,23 @@ cluster_smoke(C) -> try_to_reload_with_different_options_on_nodes(C) -> SlaveNode = slave_node(C), - copy(data(C, "mongooseim.no_listeners.cfg"), data(C, "mongooseim_n1.cfg")), - copy(data(C, "mongooseim.no_listeners.cfg"), data(C, "mongooseim_n2.cfg")), - {ok, _} = start_ejabberd_with_config(C, "mongooseim_n1.cfg"), - {ok, _} = start_remote_ejabberd_with_config(SlaveNode, C, "mongooseim_n2.cfg"), + copy(data(C, "mongooseim.minimal.toml"), data(C, "mongooseim_n1.toml")), + copy(data(C, "mongooseim.minimal.toml"), data(C, "mongooseim_n2.toml")), + {ok, _} = start_ejabberd_with_config(C, "mongooseim_n1.toml"), + {ok, _} = start_remote_ejabberd_with_config(SlaveNode, C, "mongooseim_n2.toml"), maybe_join_cluster(SlaveNode), - copy(data(C, "mongooseim.no_listeners.loglevel_err.cfg"), data(C, "mongooseim_n2.cfg")), + copy(data(C, "mongooseim.loglevel_err.toml"), data(C, "mongooseim_n2.toml")), ?assertError(#{failed_checks := [inconsistent_ondisc_local_versions]}, ejabberd_config:reload_cluster()), ok = stop_ejabberd(), stop_remote_ejabberd(SlaveNode), ok. -change_module_option_with_node_param_opts(C) -> - SlaveNode = slave_node(C), - copy(data(C, "mongooseim.no_listeners.node_specific_node1_v1.cfg"), data(C, "mongooseim_n1.cfg")), - copy(data(C, "mongooseim.no_listeners.node_specific_node2_v1.cfg"), data(C, "mongooseim_n2.cfg")), - {ok, _} = start_ejabberd_with_config(C, "mongooseim_n1.cfg"), - {ok, _} = start_remote_ejabberd_with_config(SlaveNode, C, "mongooseim_n2.cfg"), - maybe_join_cluster(SlaveNode), - copy(data(C, "mongooseim.no_listeners.node_specific_node1_v2.cfg"), data(C, "mongooseim_n1.cfg")), - copy(data(C, "mongooseim.no_listeners.node_specific_node2_v2.cfg"), data(C, "mongooseim_n2.cfg")), - {ok,_} = ejabberd_config:reload_cluster(), - % cleanup - ok = stop_ejabberd(), - stop_remote_ejabberd(SlaveNode), - ok. - -change_module_option_with_node_specific_mods(C) -> - SlaveNode = slave_node(C), - copy(data(C, "mongooseim.no_listeners.node_specific_module_node1_v1.cfg"), data(C, "mongooseim_n1.cfg")), - copy(data(C, "mongooseim.no_listeners.node_specific_module_node2_v1.cfg"), data(C, "mongooseim_n2.cfg")), - {ok, _} = start_ejabberd_with_config(C, "mongooseim_n1.cfg"), - {ok, _} = start_remote_ejabberd_with_config(SlaveNode, C, "mongooseim_n2.cfg"), - maybe_join_cluster(SlaveNode), -% copy(data(C, "mongooseim.no_listeners.node_specific_module_node1_v2.cfg"), data(C, "mongooseim_n1.cfg")), -% copy(data(C, "mongooseim.no_listeners.node_specific_module_node2_v2.cfg"), data(C, "mongooseim_n2.cfg")), - {ok,_} = ejabberd_config:reload_cluster(), - % cleanup - ok = stop_ejabberd(), - stop_remote_ejabberd(SlaveNode), - ok. - module_deps_work_correctly_with_reload_cluster(C) -> %% Just to ensure mnesia:clear_table(config), stop_ejabberd(), - copy(data(C, "mongooseim.no_listeners.gd.node1_v1.cfg"), data(C, "mongooseim_n1.cfg")), mock_gd_modules(), - {ok, _} = start_ejabberd_with_config(C, "mongooseim_n1.cfg"), + {ok, _} = start_ejabberd_with_config(C, "mongooseim.gd.toml"), ejabberd_config:assert_local_config_reloaded(), %% Cleaning in end_per_testcase ok. @@ -255,10 +180,6 @@ mock_module(M) -> is_empty([]) -> true; is_empty(_) -> false. -get_mongooseim_cfg(Config, Name) -> - DataDir = proplists:get_value(data_dir, Config), - filename:join([DataDir, Name]). - start_ejabberd_with_config(Config, ConfigFile) -> use_config_file(Config, ConfigFile), {ok, _} = start_ejabberd(Config). diff --git a/test/ejabberd_config_SUITE_data/fake.domain.one/host.cfg b/test/ejabberd_config_SUITE_data/fake.domain.one/host.cfg deleted file mode 100644 index 44ab60d462..0000000000 --- a/test/ejabberd_config_SUITE_data/fake.domain.one/host.cfg +++ /dev/null @@ -1,2 +0,0 @@ -{host_config, "fake.domain.one", - [{modules, [{mod_ping, []}]}]}. diff --git a/test/ejabberd_config_SUITE_data/fake.domain.two/host.cfg b/test/ejabberd_config_SUITE_data/fake.domain.two/host.cfg deleted file mode 100644 index 40c2a4dfda..0000000000 --- a/test/ejabberd_config_SUITE_data/fake.domain.two/host.cfg +++ /dev/null @@ -1,3 +0,0 @@ -{host_config, "fake.domain.two", - [{{add, modules}, [{mod_offline, []}, - {mod_roster, []}]}]}. diff --git a/test/ejabberd_config_SUITE_data/mongooseim.default.cfg b/test/ejabberd_config_SUITE_data/mongooseim.default.cfg deleted file mode 100644 index e54250db16..0000000000 --- a/test/ejabberd_config_SUITE_data/mongooseim.default.cfg +++ /dev/null @@ -1,600 +0,0 @@ -%%% -%%% ejabberd configuration file -%%% -%%%' - -%%% The parameters used in this configuration file are explained in more detail -%%% in the ejabberd Installation and Operation Guide. -%%% Please consult the Guide in case of doubts, it is included with -%%% your copy of ejabberd, and is also available online at -%%% http://www.process-one.net/en/ejabberd/docs/ - -%%% This configuration file contains Erlang terms. -%%% In case you want to understand the syntax, here are the concepts: -%%% -%%% - The character to comment a line is % -%%% -%%% - Each term ends in a dot, for example: -%%% override_global. -%%% -%%% - A tuple has a fixed definition, its elements are -%%% enclosed in {}, and separated with commas: -%%% {loglevel, 4}. -%%% -%%% - A list can have as many elements as you want, -%%% and is enclosed in [], for example: -%%% [http_poll, web_admin, tls] -%%% -%%% Pay attention that list elements are delimited with commas, -%%% but no comma is allowed after the last list element. This will -%%% give a syntax error unlike in more lenient languages (e.g. Python). -%%% -%%% - A keyword of ejabberd is a word in lowercase. -%%% Strings are enclosed in "" and can contain spaces, dots, ... -%%% {language, "en"}. -%%% {ldap_rootdn, "dc=example,dc=com"}. -%%% -%%% - This term includes a tuple, a keyword, a list, and two strings: -%%% {hosts, ["jabber.example.net", "im.example.com"]}. -%%% -%%% - This config is preprocessed during release generation by a tool which -%%% interprets double curly braces as substitution markers, so avoid this -%%% syntax in this file (though it's valid Erlang). -%%% -%%% So this is OK (though arguably looks quite ugly): -%%% { {s2s_addr, "example-host.net"}, {127,0,0,1} }. -%%% -%%% And I can't give an example of what's not OK exactly because -%%% of this rule. -%%% - - -%%%. ======================= -%%%' OVERRIDE STORED OPTIONS - -%% -%% Override the old values stored in the database. -%% - -%% -%% Override global options (shared by all ejabberd nodes in a cluster). -%% -%%override_global. - -%% -%% Override local options (specific for this particular ejabberd node). -%% -%%override_local. - -%% -%% Remove the Access Control Lists before new ones are added. -%% -%%override_acls. - -%%%. ========= -%%%' GDPR RELATED -%% when set to true, user data removals will be applied on all modules that implements GDPR behaviour - -%% even the disabled ones! -%% -{gdpr_removal_for_disabled_modules, false}. - -%%%. ========= -%%%' DEBUGGING - -%% -%% loglevel: Verbosity of log files generated by ejabberd. -%% 0: No ejabberd log at all (not recommended) -%% 1: Critical -%% 2: Error -%% 3: Warning -%% 4: Info -%% 5: Debug -%% -{loglevel, 3}. - -%%%. ================ -%%%' SERVED HOSTNAMES - -%% -%% hosts: Domains served by ejabberd. -%% You can define one or several, for example: -%% {hosts, ["example.net", "example.com", "example.org"]}. -%% -{hosts, ["localhost"] }. - -%% -%% route_subdomains: Delegate subdomains to other XMPP servers. -%% For example, if this ejabberd serves example.org and you want -%% to allow communication with an XMPP server called im.example.org. -%% -%%{route_subdomains, s2s}. - - -%%%. =============== -%%%' LISTENING PORTS - -%% -%% listen: The ports ejabberd will listen on, which service each is handled -%% by and what options to start it with. -%% -{listen, - [ - - { 5280, ejabberd_cowboy, [ - {num_acceptors, 10}, - {max_connections, 1024}, - %% Uncomment for HTTPS - %{cert, "priv/server.crt"}, - %{key, "priv/server.key"}, - %{key_pass, ""}, - {modules, [ - %% Modules used here should also be listed in the MODULES section. - {"_", "/http-bind", mod_bosh}, - {"_", "/ws-xmpp", mod_websockets}, - {"localhost", "/api", mongoose_api, [{handlers, [mongoose_api_metrics, - mongoose_api_users]}]} - ]} - ]}, - - { 5222, ejabberd_c2s, [ - - %% - %% If TLS is compiled in and you installed a SSL - %% certificate, specify the full path to the - %% file and uncomment this line: - %% - %%{certfile, "/path/to/ssl.pem"}, starttls, - %%{zlib, 10000}, - %% https://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS - %% {ciphers, "TLSv1.2:TLSv1.3"}, - {access, c2s}, - {shaper, c2s_shaper}, - {max_stanza_size, 65536} - ]}, - - - - %% - %% To enable the old SSL connection method on port 5223: - %% - %%{5223, ejabberd_c2s, [ - %% {access, c2s}, - %% {shaper, c2s_shaper}, - %% {certfile, "/path/to/ssl.pem"}, tls, - %% {max_stanza_size, 65536} - %% ]}, - - { 5269, ejabberd_s2s_in, [ - {shaper, s2s_shaper}, - {max_stanza_size, 131072} - ]} - - %% - %% ejabberd_service: Interact with external components (transports, ...) - %% - %%{8888, ejabberd_service, [ - %% {access, all}, - %% {shaper_rule, fast}, - %% {ip, {127, 0, 0, 1}}, - %% {hosts, ["icq.example.org", "sms.example.org"], - %% [{password, "secret"}] - %% } - %% ]}, - - %% - %% ejabberd_stun: Handles STUN Binding requests - %% - %%{ {3478, udp}, ejabberd_stun, []} - - ]}. - -%% -%% s2s_use_starttls: Enable STARTTLS + Dialback for S2S connections. -%% Allowed values are: false optional required required_trusted -%% You must specify a certificate file. -%% -%%{s2s_use_starttls, optional}. - -%% -%% s2s_certfile: Specify a certificate file. -%% -%%{s2s_certfile, "/path/to/ssl.pem"}. - -%% https://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS -%% {s2s_ciphers, "TLSv1.2:TLSv1.3"}. - -%% -%% domain_certfile: Specify a different certificate for each served hostname. -%% -%%{domain_certfile, "example.org", "/path/to/example_org.pem"}. -%%{domain_certfile, "example.com", "/path/to/example_com.pem"}. - -%% -%% S2S whitelist or blacklist -%% -%% Default s2s policy for undefined hosts. -%% -{s2s_default_policy, deny }. - -%% -%% Allow or deny communication with specific servers. -%% -%%{ {s2s_host, "goodhost.org"}, allow}. -%%{ {s2s_host, "badhost.org"}, deny}. - -{outgoing_s2s_port, 5269 }. - -%% -%% IP addresses predefined for specific hosts to skip DNS lookups. -%% Ports defined here take precedence over outgoing_s2s_port. -%% Examples: -%% -%% { {s2s_addr, "example-host.net"}, {127,0,0,1} }. -%% { {s2s_addr, "example-host.net"}, { {127,0,0,1}, 5269 } }. -%% { {s2s_addr, "example-host.net"}, { {127,0,0,1}, 5269 } }. - -%% -%% Outgoing S2S options -%% -%% Preferred address families (which to try first) and connect timeout -%% in milliseconds. -%% -%%{outgoing_s2s_options, [ipv4, ipv6], 10000}. - -%%%. ============== -%%%' SESSION BACKEND - -{sm_backend, {mnesia, []} }. - -%%%. ============== -%%%' AUTHENTICATION - -{auth_method, internal}. - -%%%. ============== -%%%' DATABASE SETUP - - -%%%. =============== -%%%' TRAFFIC SHAPERS - -%% -%% The "normal" shaper limits traffic speed to 1000 B/s -%% -{shaper, normal, {maxrate, 1000}}. - -%% -%% The "fast" shaper limits traffic speed to 50000 B/s -%% -{shaper, fast, {maxrate, 50000}}. - -%% -%% This option specifies the maximum number of elements in the queue -%% of the FSM. Refer to the documentation for details. -%% -{max_fsm_queue, 1000}. - - -%%%. ==================== -%%%' ACCESS CONTROL LISTS - -%% -%% The 'admin' ACL grants administrative privileges to XMPP accounts. -%% You can put here as many accounts as you want. -%% -%{acl, admin, {user, "alice", "localhost"}}. -%{acl, admin, {user, "a", "localhost"}}. - -%% -%% Blocked users -%% -%%{acl, blocked, {user, "baduser", "example.org"}}. -%%{acl, blocked, {user, "test"}}. - -%% -%% Local users: don't modify this line. -%% -{acl, local, {user_regexp, ""}}. - -%% -%% More examples of ACLs -%% -%%{acl, jabberorg, {server, "jabber.org"}}. -%%{acl, aleksey, {user, "aleksey", "jabber.ru"}}. -%%{acl, test, {user_regexp, "^test"}}. -%%{acl, test, {user_glob, "test*"}}. - -%% -%% Define specific ACLs in a virtual host. -%% -%%{host_config, "localhost", -%% [ -%% {acl, admin, {user, "bob-local", "localhost"}} -%% ] -%%}. - - -%%%. ============ -%%%' ACCESS RULES - -%% Maximum number of simultaneous sessions allowed for a single user: -{access, max_user_sessions, [{10, all}]}. - -%% Maximum number of offline messages that users can have: -{access, max_user_offline_messages, [{5000, admin}, {100, all}]}. - -%% This rule allows access only for local users: -{access, local, [{allow, local}]}. - -%% Only non-blocked users can use c2s connections: -{access, c2s, [{deny, blocked}, - {allow, all}]}. - -%% For C2S connections, all users except admins use the "normal" shaper -{access, c2s_shaper, [{none, admin}, - {normal, all}]}. - -%% All S2S connections use the "fast" shaper -{access, s2s_shaper, [{fast, all}]}. - -%% Admins of this server are also admins of the MUC service: -{access, muc_admin, [{allow, admin}]}. - -%% Only accounts of the local ejabberd server can create rooms: -{access, muc_create, [{allow, local}]}. - -%% All users are allowed to use the MUC service: -{access, muc, [{allow, all}]}. - -%% In-band registration allows registration of any possible username. -%% To disable in-band registration, replace 'allow' with 'deny'. -{access, register, [{allow, all}]}. - -%% By default the frequency of account registrations from the same IP -%% is limited to 1 account every 10 minutes. To disable, specify: infinity -{registration_timeout, infinity}. - -%% Default settings for MAM. -%% To set non-standard value, replace 'default' with 'allow' or 'deny'. -%% Only user can access his/her archive by default. -%% An online user can read room's archive by default. -%% Only an owner can change settings by default. -%% Empty list (i.e. `[]`) means `[{deny, all}]`. -{access, mam_set_prefs, [{default, all}]}. -{access, mam_get_prefs, [{default, all}]}. -{access, mam_lookup_messages, [{default, all}]}. - -%% 1 command of the specified type per second. -{shaper, mam_shaper, {maxrate, 1}}. -%% This shaper is primeraly for Mnesia overload protection during stress testing. -%% The limit is 1000 operations of each type per second. -{shaper, mam_global_shaper, {maxrate, 1000}}. - -{access, mam_set_prefs_shaper, [{mam_shaper, all}]}. -{access, mam_get_prefs_shaper, [{mam_shaper, all}]}. -{access, mam_lookup_messages_shaper, [{mam_shaper, all}]}. - -{access, mam_set_prefs_global_shaper, [{mam_global_shaper, all}]}. -{access, mam_get_prefs_global_shaper, [{mam_global_shaper, all}]}. -{access, mam_lookup_messages_global_shaper, [{mam_global_shaper, all}]}. - - -%% -%% Define specific Access Rules in a virtual host. -%% -%%{host_config, "localhost", -%% [ -%% {access, c2s, [{allow, admin}, {deny, all}]}, -%% {access, register, [{deny, all}]} -%% ] -%%}. - - -%%%. ================ -%%%' DEFAULT LANGUAGE - -%% -%% language: Default language used for server messages. -%% -{language, "en"}. - -%% -%% Set a different default language in a virtual host. -%% -%%{host_config, "localhost", -%% [{language, "ru"}] -%%}. - - -%%%. ======= -%%%' MODULES - -%% -%% Modules enabled in all ejabberd virtual hosts. -%% For list of possible modules options, check documentation. -%% If module comes in two versions, like mod_last and mod_last_rdbms, -%% use only one of them. -%% -{modules, - [ - %{mod_admin_extra, [{submods, [node, accounts, sessions, vcard, - % roster, last, private, stanza, stats]}]}, - {mod_adhoc, []}, - {mod_disco, []}, - {mod_last, []}, - {mod_stream_management, [ - % default 100 - % size of a buffer of unacked messages - % {buffer_max, 100} - - % default 1 - server sends the ack request after each stanza - % {ack_freq, 1} - - % default: 600 seconds - % {resume_timeout, 600} - ]}, - {mod_muc, [ - {host, "muc.@HOST@"}, - {access, muc}, - {access_create, muc_create} - ]}, - {mod_muc_log, - [ - {outdir, "/tmp/muclogs"}, - {access_log, muc} - ]}, - {mod_privacy, []}, - {mod_register, [ - %% - %% Set the minimum informational entropy for passwords. - %% - %%{password_strength, 32}, - - %% - %% After successful registration, the user receives - %% a message with this subject and body. - %% - {welcome_message, {"", ""}}, - - %% - %% When a user registers, send a notification to - %% these XMPP accounts. - %% - %%{registration_watchers, ["admin1@example.org"]}, - - %% - %% Only clients in the server machine can register accounts - %% - {ip_access, [{allow, "127.0.0.0/8"}, - {deny, "0.0.0.0/0"}]}, - - %% - %% Local c2s or remote s2s users cannot register accounts - %% - %%{access_from, deny}, - - {access, register} - ]}, - {mod_roster, []}, - {mod_sic, []}, - {mod_vcard, [ {allow_return_all, true}, -{search_all_hosts, true} -%{matches, 1}, -%{search, true}, -%{host, directory.@HOST@} -]}, - {mod_bosh, []} - - %% - %% Message Archive Management (MAM) for registered users. - %% - - %% A module for storing preferences in RDBMS (used by default). - %% Enable for private message archives. -% {mod_mam_rdbms_prefs, [pm]}, - %% Enable for multiuser message archives. -% {mod_mam_rdbms_prefs, [muc]}, - %% Enable for both private and multiuser message archives. -% {mod_mam_rdbms_prefs, [pm, muc]}, - - %% A module for storing preferences in Mnesia (recommended). - %% This module will be called each time, as a message is routed. - %% That is why, Mnesia is better for this job. -% {mod_mam_mnesia_prefs, [pm, muc]}, - - %% A back-end for storing messages. - %% Synchronious writer (used by default). - %% This writer is easy to debug, but writing performance is low. -% {mod_mam_rdbms_arch, [pm]}, - - %% Enable the module with a custom writer. -% {mod_mam_rdbms_arch, [no_writer, pm]}, - - %% A pool of asynchronious writers (recommended). - %% Messages will be grouped together based on archive id. -% {mod_mam_rdbms_async_pool_writer, [pm]}, - - %% A module for converting an archive id to an integer. - %% Extract information using RDBMS. -% {mod_mam_rdbms_user, [pm, muc]}, - - %% Cache information about users (recommended). - %% Requires mod_mam_rdbms_user or alternative. -% {mod_mam_cache_user, [pm, muc]}, - - %% Enable MAM. -% {mod_mam, []}, - - - %% - %% Message Archive Management (MAM) for multi-user chats (MUC). - %% Enable XEP-0313 for "muc.@HOST@". - %% - - %% A back-end for storing messages (default for MUC). - %% Modules mod_mam_muc_* are optimized for MUC. - %% - %% Synchronious writer (used by default for MUC). - %% This module is easy to debug, but performance is low. -% {mod_mam_muc_rdbms_arch, []}, -% {mod_mam_muc_rdbms_arch, [no_writer]}, - - %% Asynchronious writer for RDBMS (recommended for MUC). - %% Messages will be grouped and inserted all at once. -% {mod_mam_muc_rdbms_async_pool_writer, []}, - - %% Load mod_mam_rdbms_user too. - - %% Enable MAM for MUC -% {mod_mam_muc, [{host, "muc.@HOST@"}]} - - - %% - %% MAM configuration examples - %% - - %% Only MUC, no user-defined preferences, good performance. -% {mod_mam_rdbms_user, [muc]}, -% {mod_mam_cache_user, [muc]}, -% {mod_mam_muc_rdbms_arch, [no_writer]}, -% {mod_mam_muc_rdbms_async_pool_writer, []}, -% {mod_mam_muc, [{host, "muc.@HOST@"}]} - - %% Only archives for c2c messages, good performance. -% {mod_mam_rdbms_user, [pm]}, -% {mod_mam_cache_user, [pm]}, -% {mod_mam_mnesia_prefs, [pm]}, -% {mod_mam_rdbms_arch, [pm, no_writer]}, -% {mod_mam_rdbms_async_pool_writer, [pm]}, -% {mod_mam, []} - - %% Basic configuration for c2c messages, bad performance, easy to debug. -% {mod_mam_rdbms_user, [pm]}, -% {mod_mam_rdbms_prefs, [pm]}, -% {mod_mam_rdbms_arch, [pm]}, -% {mod_mam, []} - - ]}. - - -%% -%% Enable modules with custom options in a specific virtual host -%% -%%{host_config, "localhost", -%% [{ {add, modules}, -%% [ -%% {mod_some_module, []} -%% ] -%% } -%% ]}. - -%%%. -%%%' - -%%% $Id$ - -%%% Local Variables: -%%% mode: erlang -%%% End: -%%% vim: set filetype=erlang tabstop=8 foldmarker=%%%',%%%. foldmethod=marker: -%%%. diff --git a/test/ejabberd_config_SUITE_data/mongooseim.gd.toml b/test/ejabberd_config_SUITE_data/mongooseim.gd.toml new file mode 100644 index 0000000000..2ba6a9cae1 --- /dev/null +++ b/test/ejabberd_config_SUITE_data/mongooseim.gd.toml @@ -0,0 +1,7 @@ +[general] + hosts = ["localhost"] + loglevel = "error" + +[modules.mod_global_distrib] + global_host = "mim.localhost" + local_host = "c1-mim.localhost" diff --git a/test/ejabberd_config_SUITE_data/mongooseim.hosts.cfg b/test/ejabberd_config_SUITE_data/mongooseim.hosts.cfg deleted file mode 100644 index ea293223a8..0000000000 --- a/test/ejabberd_config_SUITE_data/mongooseim.hosts.cfg +++ /dev/null @@ -1,8 +0,0 @@ -{hosts, ["fake.domain.one", - "fake.domain.two"]}. - -{include_config_file, "log_private/etc/fake.domain.one/host.cfg"}. -{domain_certfile, "log_private/etc/fake.domain.one/certfile.pem"}. - -{include_config_file, "log_private/etc/fake.domain.two/host.cfg"}. -{domain_certfile, "log_private/etc/fake.domain.two/certfile.pem"}. diff --git a/test/ejabberd_config_SUITE_data/mongooseim.loglevel_err.toml b/test/ejabberd_config_SUITE_data/mongooseim.loglevel_err.toml new file mode 100644 index 0000000000..02fb92bfbf --- /dev/null +++ b/test/ejabberd_config_SUITE_data/mongooseim.loglevel_err.toml @@ -0,0 +1,3 @@ +[general] + hosts = ["localhost"] + loglevel = "error" diff --git a/test/ejabberd_config_SUITE_data/mongooseim.minimal.toml b/test/ejabberd_config_SUITE_data/mongooseim.minimal.toml new file mode 100644 index 0000000000..f9fb4f83dc --- /dev/null +++ b/test/ejabberd_config_SUITE_data/mongooseim.minimal.toml @@ -0,0 +1,2 @@ +[general] + hosts = ["localhost"] diff --git a/test/ejabberd_config_SUITE_data/mongooseim.no_listeners.cfg b/test/ejabberd_config_SUITE_data/mongooseim.no_listeners.cfg deleted file mode 100644 index 03931152e3..0000000000 --- a/test/ejabberd_config_SUITE_data/mongooseim.no_listeners.cfg +++ /dev/null @@ -1,65 +0,0 @@ -{loglevel, 3}. -{hosts, ["localhost"] }. -%% To avoid eaddinuse, we don't define any listeners -{listen, []}. -{s2s_default_policy, deny }. -{outgoing_s2s_port, 5269 }. -{sm_backend, {mnesia, []} }. -{auth_method, internal}. -{shaper, normal, {maxrate, 1000}}. -{shaper, fast, {maxrate, 50000}}. -{max_fsm_queue, 1000}. -{acl, local, {user_regexp, ""}}. -{access, max_user_sessions, [{10, all}]}. -{access, max_user_offline_messages, [{5000, admin}, {100, all}]}. -{access, local, [{allow, local}]}. -{access, c2s, [{deny, blocked}, {allow, all}]}. -{access, c2s_shaper, [{none, admin}, {normal, all}]}. -{access, s2s_shaper, [{fast, all}]}. -{access, muc_admin, [{allow, admin}]}. -{access, muc_create, [{allow, local}]}. -{access, muc, [{allow, all}]}. -{access, register, [{allow, all}]}. -{registration_timeout, infinity}. - -{access, mam_set_prefs, [{default, all}]}. -{access, mam_get_prefs, [{default, all}]}. -{access, mam_lookup_messages, [{default, all}]}. - -{shaper, mam_shaper, {maxrate, 1}}. -{shaper, mam_global_shaper, {maxrate, 1000}}. - -{access, mam_set_prefs_shaper, [{mam_shaper, all}]}. -{access, mam_get_prefs_shaper, [{mam_shaper, all}]}. -{access, mam_lookup_messages_shaper, [{mam_shaper, all}]}. - -{access, mam_set_prefs_global_shaper, [{mam_global_shaper, all}]}. -{access, mam_get_prefs_global_shaper, [{mam_global_shaper, all}]}. -{access, mam_lookup_messages_global_shaper, [{mam_global_shaper, all}]}. - -{language, "en"}. -{modules, - [ - {mod_adhoc, []}, - {mod_disco, []}, - {mod_last, []}, - {mod_stream_management, [ ]}, - {mod_muc, [ - {host, "muc.@HOST@"}, - {access, muc}, - {access_create, muc_create} - ]}, - {mod_muc_log, [ {outdir, "/tmp/muclogs"}, {access_log, muc} ]}, - {mod_privacy, []}, - {mod_register, [ - {welcome_message, {"", ""}}, - {ip_access, [{allow, "127.0.0.0/8"}, - {deny, "0.0.0.0/0"}]}, - {access, register} - ]}, - {mod_roster, []}, - {mod_sic, []}, - {mod_vcard, [ {allow_return_all, true}, {search_all_hosts, true} ]}, - {mod_bosh, []} - - ]}. diff --git a/test/ejabberd_config_SUITE_data/mongooseim.no_listeners.gd.node1_v1.cfg b/test/ejabberd_config_SUITE_data/mongooseim.no_listeners.gd.node1_v1.cfg deleted file mode 100644 index f7143fec95..0000000000 --- a/test/ejabberd_config_SUITE_data/mongooseim.no_listeners.gd.node1_v1.cfg +++ /dev/null @@ -1,44 +0,0 @@ -{loglevel, 3}. -{hosts, ["localhost", "mim.localhost", "c1-mim.localhost"] }. -%% To avoid EADDINUSE (address in use error), we don't define any listeners -{listen, []}. -{sm_backend, {mnesia, []} }. -{auth_method, internal}. - -{modules, - [ - %% GD uses gen_mod_deps, which starts modules on it's own - - {mod_global_distrib, [ - {global_host, "mim.localhost"}, - {local_host, "c1-mim.localhost"}, - {connections, [ - {endpoints, [ - {"127.0.0.1", 5555} - ]}, - {advertised_endpoints, [ - {"c1-mim-1.localhost", 5555} - ]}, - {connections_per_endpoint, 10}, - {tls_opts, [ - {protocol_options, ["no_sslv2|no_sslv3|no_tlsv1|no_tlsv1_1|cipher_server_preference"]}, - {ciphers, "ECDHE-ECDSA-AES256-GCM-SHA384"}, - {dhfile, "priv/ssl/fake_localhost_dh_server.pem"}, - {certfile, "priv/ssl/fake_localhost_full_chain.pem"}, - {cafile, "priv/ssl/fake_localhost_full_chain.pem"} - ]} - ]}, - {cache, [ - {domain_lifetime_seconds, 60} - ]}, - {bounce, [ - {resend_after_ms, 300}, - {max_retries, 3} - ]}, - {redis , [ - {server, "127.0.0.1"}, - {pool_size, 1} - ]} - ]} - - ]}. diff --git a/test/ejabberd_config_SUITE_data/mongooseim.no_listeners.loglevel_err.cfg b/test/ejabberd_config_SUITE_data/mongooseim.no_listeners.loglevel_err.cfg deleted file mode 100644 index 865d300ae5..0000000000 --- a/test/ejabberd_config_SUITE_data/mongooseim.no_listeners.loglevel_err.cfg +++ /dev/null @@ -1,65 +0,0 @@ -{loglevel, 2}. -{hosts, ["localhost"] }. -%% To avoid eaddinuse, we don't define any listeners -{listen, []}. -{s2s_default_policy, deny }. -{outgoing_s2s_port, 5269 }. -{sm_backend, {mnesia, []} }. -{auth_method, internal}. -{shaper, normal, {maxrate, 1000}}. -{shaper, fast, {maxrate, 50000}}. -{max_fsm_queue, 1000}. -{acl, local, {user_regexp, ""}}. -{access, max_user_sessions, [{10, all}]}. -{access, max_user_offline_messages, [{5000, admin}, {100, all}]}. -{access, local, [{allow, local}]}. -{access, c2s, [{deny, blocked}, {allow, all}]}. -{access, c2s_shaper, [{none, admin}, {normal, all}]}. -{access, s2s_shaper, [{fast, all}]}. -{access, muc_admin, [{allow, admin}]}. -{access, muc_create, [{allow, local}]}. -{access, muc, [{allow, all}]}. -{access, register, [{allow, all}]}. -{registration_timeout, infinity}. - -{access, mam_set_prefs, [{default, all}]}. -{access, mam_get_prefs, [{default, all}]}. -{access, mam_lookup_messages, [{default, all}]}. - -{shaper, mam_shaper, {maxrate, 1}}. -{shaper, mam_global_shaper, {maxrate, 1000}}. - -{access, mam_set_prefs_shaper, [{mam_shaper, all}]}. -{access, mam_get_prefs_shaper, [{mam_shaper, all}]}. -{access, mam_lookup_messages_shaper, [{mam_shaper, all}]}. - -{access, mam_set_prefs_global_shaper, [{mam_global_shaper, all}]}. -{access, mam_get_prefs_global_shaper, [{mam_global_shaper, all}]}. -{access, mam_lookup_messages_global_shaper, [{mam_global_shaper, all}]}. - -{language, "en"}. -{modules, - [ - {mod_adhoc, []}, - {mod_disco, []}, - {mod_last, []}, - {mod_stream_management, [ ]}, - {mod_muc, [ - {host, "muc.@HOST@"}, - {access, muc}, - {access_create, muc_create} - ]}, - {mod_muc_log, [ {outdir, "/tmp/muclogs"}, {access_log, muc} ]}, - {mod_privacy, []}, - {mod_register, [ - {welcome_message, {"", ""}}, - {ip_access, [{allow, "127.0.0.0/8"}, - {deny, "0.0.0.0/0"}]}, - {access, register} - ]}, - {mod_roster, []}, - {mod_sic, []}, - {mod_vcard, [ {allow_return_all, true}, {search_all_hosts, true} ]}, - {mod_bosh, []} - - ]}. diff --git a/test/ejabberd_config_SUITE_data/mongooseim.no_listeners.node_specific_module_node1_v1.cfg b/test/ejabberd_config_SUITE_data/mongooseim.no_listeners.node_specific_module_node1_v1.cfg deleted file mode 100644 index be91ac5f89..0000000000 --- a/test/ejabberd_config_SUITE_data/mongooseim.no_listeners.node_specific_module_node1_v1.cfg +++ /dev/null @@ -1,63 +0,0 @@ -{loglevel, 3}. -{hosts, ["localhost"] }. -%% To avoid eaddinuse, we don't define any listeners -{listen, []}. -{s2s_default_policy, deny }. -{outgoing_s2s_port, 5269 }. -{sm_backend, {mnesia, []} }. -{auth_method, internal}. -{shaper, normal, {maxrate, 1000}}. -{shaper, fast, {maxrate, 50000}}. -{max_fsm_queue, 1000}. -{acl, local, {user_regexp, ""}}. -{access, max_user_sessions, [{10, all}]}. -{access, max_user_offline_messages, [{5000, admin}, {100, all}]}. -{access, local, [{allow, local}]}. -{access, c2s, [{deny, blocked}, {allow, all}]}. -{access, c2s_shaper, [{none, admin}, {normal, all}]}. -{access, s2s_shaper, [{fast, all}]}. -{access, muc_admin, [{allow, admin}]}. -{access, muc_create, [{allow, local}]}. -{access, muc, [{allow, all}]}. -{access, register, [{allow, all}]}. -{registration_timeout, infinity}. - -{access, mam_set_prefs, [{default, all}]}. -{access, mam_get_prefs, [{default, all}]}. -{access, mam_lookup_messages, [{default, all}]}. - -{shaper, mam_shaper, {maxrate, 1}}. -{shaper, mam_global_shaper, {maxrate, 1000}}. - -{access, mam_set_prefs_shaper, [{mam_shaper, all}]}. -{access, mam_get_prefs_shaper, [{mam_shaper, all}]}. -{access, mam_lookup_messages_shaper, [{mam_shaper, all}]}. - -{access, mam_set_prefs_global_shaper, [{mam_global_shaper, all}]}. -{access, mam_get_prefs_global_shaper, [{mam_global_shaper, all}]}. -{access, mam_lookup_messages_global_shaper, [{mam_global_shaper, all}]}. - -{node_specific_options, [ - [h,'_',module,mod_register] - ]}. - -{language, "en"}. -{modules, - [ - {mod_adhoc, []}, - {mod_disco, []}, - {mod_last, []}, - {mod_stream_management, [ ]}, - {mod_privacy, []}, - {mod_register, [ - {welcome_message, {"", ""}}, - {ip_access, [{allow, "127.0.0.0/8"}, - {deny, "0.0.0.0/0"}]}, - {access, register} - ]}, - {mod_roster, []}, - {mod_sic, []}, - {mod_vcard, [ {allow_return_all, true}, {search_all_hosts, true} ]}, - {mod_bosh, []} - - ]}. diff --git a/test/ejabberd_config_SUITE_data/mongooseim.no_listeners.node_specific_module_node2_v1.cfg b/test/ejabberd_config_SUITE_data/mongooseim.no_listeners.node_specific_module_node2_v1.cfg deleted file mode 100644 index e8d1909040..0000000000 --- a/test/ejabberd_config_SUITE_data/mongooseim.no_listeners.node_specific_module_node2_v1.cfg +++ /dev/null @@ -1,58 +0,0 @@ -{loglevel, 3}. -{hosts, ["localhost"] }. -%% To avoid eaddinuse, we don't define any listeners -{listen, []}. -{s2s_default_policy, deny }. -{outgoing_s2s_port, 5269 }. -{sm_backend, {mnesia, []} }. -{auth_method, internal}. -{shaper, normal, {maxrate, 1000}}. -{shaper, fast, {maxrate, 50000}}. -{max_fsm_queue, 1000}. -{acl, local, {user_regexp, ""}}. -{access, max_user_sessions, [{10, all}]}. -{access, max_user_offline_messages, [{5000, admin}, {100, all}]}. -{access, local, [{allow, local}]}. -{access, c2s, [{deny, blocked}, {allow, all}]}. -{access, c2s_shaper, [{none, admin}, {normal, all}]}. -{access, s2s_shaper, [{fast, all}]}. -{access, muc_admin, [{allow, admin}]}. -{access, muc_create, [{allow, local}]}. -{access, muc, [{allow, all}]}. -{access, register, [{allow, all}]}. -{registration_timeout, infinity}. - -{access, mam_set_prefs, [{default, all}]}. -{access, mam_get_prefs, [{default, all}]}. -{access, mam_lookup_messages, [{default, all}]}. - -{shaper, mam_shaper, {maxrate, 1}}. -{shaper, mam_global_shaper, {maxrate, 1000}}. - -{access, mam_set_prefs_shaper, [{mam_shaper, all}]}. -{access, mam_get_prefs_shaper, [{mam_shaper, all}]}. -{access, mam_lookup_messages_shaper, [{mam_shaper, all}]}. - -{access, mam_set_prefs_global_shaper, [{mam_global_shaper, all}]}. -{access, mam_get_prefs_global_shaper, [{mam_global_shaper, all}]}. -{access, mam_lookup_messages_global_shaper, [{mam_global_shaper, all}]}. - -{node_specific_options, [ - [h,'_',module,mod_register] - ]}. - -{language, "en"}. -{modules, - [ - {mod_adhoc, []}, - {mod_disco, []}, - {mod_last, []}, - {mod_stream_management, [ ]}, - {mod_privacy, []}, - %% no mod_register on this node - {mod_roster, []}, - {mod_sic, []}, - {mod_vcard, [ {allow_return_all, true}, {search_all_hosts, true} ]}, - {mod_bosh, []} - - ]}. diff --git a/test/ejabberd_config_SUITE_data/mongooseim.no_listeners.node_specific_node1_v1.cfg b/test/ejabberd_config_SUITE_data/mongooseim.no_listeners.node_specific_node1_v1.cfg deleted file mode 100644 index edb70b2bc9..0000000000 --- a/test/ejabberd_config_SUITE_data/mongooseim.no_listeners.node_specific_node1_v1.cfg +++ /dev/null @@ -1,70 +0,0 @@ -{loglevel, 3}. -{hosts, ["localhost"] }. -%% To avoid eaddinuse, we don't define any listeners -{listen, []}. -{s2s_default_policy, deny }. -{outgoing_s2s_port, 5269 }. -{sm_backend, {mnesia, []} }. -{auth_method, internal}. -{shaper, normal, {maxrate, 1000}}. -{shaper, fast, {maxrate, 50000}}. -{max_fsm_queue, 1000}. -{acl, local, {user_regexp, ""}}. -{access, max_user_sessions, [{10, all}]}. -{access, max_user_offline_messages, [{5000, admin}, {100, all}]}. -{access, local, [{allow, local}]}. -{access, c2s, [{deny, blocked}, {allow, all}]}. -{access, c2s_shaper, [{none, admin}, {normal, all}]}. -{access, s2s_shaper, [{fast, all}]}. -{access, muc_admin, [{allow, admin}]}. -{access, muc_create, [{allow, local}]}. -{access, muc, [{allow, all}]}. -{access, register, [{allow, all}]}. -{registration_timeout, infinity}. - -{access, mam_set_prefs, [{default, all}]}. -{access, mam_get_prefs, [{default, all}]}. -{access, mam_lookup_messages, [{default, all}]}. - -{shaper, mam_shaper, {maxrate, 1}}. -{shaper, mam_global_shaper, {maxrate, 1000}}. - -{access, mam_set_prefs_shaper, [{mam_shaper, all}]}. -{access, mam_get_prefs_shaper, [{mam_shaper, all}]}. -{access, mam_lookup_messages_shaper, [{mam_shaper, all}]}. - -{access, mam_set_prefs_global_shaper, [{mam_global_shaper, all}]}. -{access, mam_get_prefs_global_shaper, [{mam_global_shaper, all}]}. -{access, mam_lookup_messages_global_shaper, [{mam_global_shaper, all}]}. - -{node_specific_options, [ - [h,'_',module_opt,mod_muc_log, outdir] - ]}. - -{language, "en"}. -{modules, - [ - {mod_adhoc, []}, - {mod_disco, []}, - {mod_last, []}, - {mod_stream_management, [ ]}, - {mod_muc, [ - {host, "muc.@HOST@"}, - {access, muc}, - {access_create, muc_create} - ]}, - %% outdir is node-specific - {mod_muc_log, [ {outdir, "/tmp/muclogs_node1"}, {access_log, muc} ]}, - {mod_privacy, []}, - {mod_register, [ - {welcome_message, {"", ""}}, - {ip_access, [{allow, "127.0.0.0/8"}, - {deny, "0.0.0.0/0"}]}, - {access, register} - ]}, - {mod_roster, []}, - {mod_sic, []}, - {mod_vcard, [ {allow_return_all, true}, {search_all_hosts, true} ]}, - {mod_bosh, []} - - ]}. diff --git a/test/ejabberd_config_SUITE_data/mongooseim.no_listeners.node_specific_node1_v2.cfg b/test/ejabberd_config_SUITE_data/mongooseim.no_listeners.node_specific_node1_v2.cfg deleted file mode 100644 index 301bb7d871..0000000000 --- a/test/ejabberd_config_SUITE_data/mongooseim.no_listeners.node_specific_node1_v2.cfg +++ /dev/null @@ -1,71 +0,0 @@ -{loglevel, 3}. -{hosts, ["localhost"] }. -%% To avoid eaddinuse, we don't define any listeners -{listen, []}. -{s2s_default_policy, deny }. -{outgoing_s2s_port, 5269 }. -{sm_backend, {mnesia, []} }. -{auth_method, internal}. -{shaper, normal, {maxrate, 1000}}. -{shaper, fast, {maxrate, 50000}}. -{max_fsm_queue, 1000}. -{acl, local, {user_regexp, ""}}. -{access, max_user_sessions, [{10, all}]}. -{access, max_user_offline_messages, [{5000, admin}, {100, all}]}. -{access, local, [{allow, local}]}. -{access, c2s, [{deny, blocked}, {allow, all}]}. -{access, c2s_shaper, [{none, admin}, {normal, all}]}. -{access, s2s_shaper, [{fast, all}]}. -{access, muc_admin, [{allow, admin}]}. -{access, muc_create, [{allow, local}]}. -{access, muc, [{allow, all}]}. -{access, register, [{allow, all}]}. -{registration_timeout, infinity}. - -{access, mam_set_prefs, [{default, all}]}. -{access, mam_get_prefs, [{default, all}]}. -{access, mam_lookup_messages, [{default, all}]}. - -{shaper, mam_shaper, {maxrate, 1}}. -{shaper, mam_global_shaper, {maxrate, 1000}}. - -{access, mam_set_prefs_shaper, [{mam_shaper, all}]}. -{access, mam_get_prefs_shaper, [{mam_shaper, all}]}. -{access, mam_lookup_messages_shaper, [{mam_shaper, all}]}. - -{access, mam_set_prefs_global_shaper, [{mam_global_shaper, all}]}. -{access, mam_get_prefs_global_shaper, [{mam_global_shaper, all}]}. -{access, mam_lookup_messages_global_shaper, [{mam_global_shaper, all}]}. - -{node_specific_options, [ - [h,'_',module_opt,mod_muc_log, outdir] - ]}. - -{language, "en"}. -{modules, - [ - {mod_adhoc, []}, - {mod_disco, []}, - {mod_last, []}, - {mod_stream_management, [ ]}, - {mod_muc, [ - %% Change host in version 2 - {host, "conference.@HOST@"}, - {access, muc}, - {access_create, muc_create} - ]}, - %% outdir is node-specific - {mod_muc_log, [ {outdir, "/tmp/muclogs_node1"}, {access_log, muc} ]}, - {mod_privacy, []}, - {mod_register, [ - {welcome_message, {"", ""}}, - {ip_access, [{allow, "127.0.0.0/8"}, - {deny, "0.0.0.0/0"}]}, - {access, register} - ]}, - {mod_roster, []}, - {mod_sic, []}, - {mod_vcard, [ {allow_return_all, true}, {search_all_hosts, true} ]}, - {mod_bosh, []} - - ]}. diff --git a/test/ejabberd_config_SUITE_data/mongooseim.no_listeners.node_specific_node2_v1.cfg b/test/ejabberd_config_SUITE_data/mongooseim.no_listeners.node_specific_node2_v1.cfg deleted file mode 100644 index fd61feadab..0000000000 --- a/test/ejabberd_config_SUITE_data/mongooseim.no_listeners.node_specific_node2_v1.cfg +++ /dev/null @@ -1,70 +0,0 @@ -{loglevel, 3}. -{hosts, ["localhost"] }. -%% To avoid eaddinuse, we don't define any listeners -{listen, []}. -{s2s_default_policy, deny }. -{outgoing_s2s_port, 5269 }. -{sm_backend, {mnesia, []} }. -{auth_method, internal}. -{shaper, normal, {maxrate, 1000}}. -{shaper, fast, {maxrate, 50000}}. -{max_fsm_queue, 1000}. -{acl, local, {user_regexp, ""}}. -{access, max_user_sessions, [{10, all}]}. -{access, max_user_offline_messages, [{5000, admin}, {100, all}]}. -{access, local, [{allow, local}]}. -{access, c2s, [{deny, blocked}, {allow, all}]}. -{access, c2s_shaper, [{none, admin}, {normal, all}]}. -{access, s2s_shaper, [{fast, all}]}. -{access, muc_admin, [{allow, admin}]}. -{access, muc_create, [{allow, local}]}. -{access, muc, [{allow, all}]}. -{access, register, [{allow, all}]}. -{registration_timeout, infinity}. - -{access, mam_set_prefs, [{default, all}]}. -{access, mam_get_prefs, [{default, all}]}. -{access, mam_lookup_messages, [{default, all}]}. - -{shaper, mam_shaper, {maxrate, 1}}. -{shaper, mam_global_shaper, {maxrate, 1000}}. - -{access, mam_set_prefs_shaper, [{mam_shaper, all}]}. -{access, mam_get_prefs_shaper, [{mam_shaper, all}]}. -{access, mam_lookup_messages_shaper, [{mam_shaper, all}]}. - -{access, mam_set_prefs_global_shaper, [{mam_global_shaper, all}]}. -{access, mam_get_prefs_global_shaper, [{mam_global_shaper, all}]}. -{access, mam_lookup_messages_global_shaper, [{mam_global_shaper, all}]}. - -{node_specific_options, [ - [h,'_',module_opt,mod_muc_log, outdir] - ]}. - -{language, "en"}. -{modules, - [ - {mod_adhoc, []}, - {mod_disco, []}, - {mod_last, []}, - {mod_stream_management, [ ]}, - {mod_muc, [ - {host, "muc.@HOST@"}, - {access, muc}, - {access_create, muc_create} - ]}, - %% outdir is node-specific - {mod_muc_log, [ {outdir, "/tmp/muclogs_node2"}, {access_log, muc} ]}, - {mod_privacy, []}, - {mod_register, [ - {welcome_message, {"", ""}}, - {ip_access, [{allow, "127.0.0.0/8"}, - {deny, "0.0.0.0/0"}]}, - {access, register} - ]}, - {mod_roster, []}, - {mod_sic, []}, - {mod_vcard, [ {allow_return_all, true}, {search_all_hosts, true} ]}, - {mod_bosh, []} - - ]}. diff --git a/test/ejabberd_config_SUITE_data/mongooseim.no_listeners.node_specific_node2_v2.cfg b/test/ejabberd_config_SUITE_data/mongooseim.no_listeners.node_specific_node2_v2.cfg deleted file mode 100644 index 0eb4dcb8b1..0000000000 --- a/test/ejabberd_config_SUITE_data/mongooseim.no_listeners.node_specific_node2_v2.cfg +++ /dev/null @@ -1,71 +0,0 @@ -{loglevel, 3}. -{hosts, ["localhost"] }. -%% To avoid eaddinuse, we don't define any listeners -{listen, []}. -{s2s_default_policy, deny }. -{outgoing_s2s_port, 5269 }. -{sm_backend, {mnesia, []} }. -{auth_method, internal}. -{shaper, normal, {maxrate, 1000}}. -{shaper, fast, {maxrate, 50000}}. -{max_fsm_queue, 1000}. -{acl, local, {user_regexp, ""}}. -{access, max_user_sessions, [{10, all}]}. -{access, max_user_offline_messages, [{5000, admin}, {100, all}]}. -{access, local, [{allow, local}]}. -{access, c2s, [{deny, blocked}, {allow, all}]}. -{access, c2s_shaper, [{none, admin}, {normal, all}]}. -{access, s2s_shaper, [{fast, all}]}. -{access, muc_admin, [{allow, admin}]}. -{access, muc_create, [{allow, local}]}. -{access, muc, [{allow, all}]}. -{access, register, [{allow, all}]}. -{registration_timeout, infinity}. - -{access, mam_set_prefs, [{default, all}]}. -{access, mam_get_prefs, [{default, all}]}. -{access, mam_lookup_messages, [{default, all}]}. - -{shaper, mam_shaper, {maxrate, 1}}. -{shaper, mam_global_shaper, {maxrate, 1000}}. - -{access, mam_set_prefs_shaper, [{mam_shaper, all}]}. -{access, mam_get_prefs_shaper, [{mam_shaper, all}]}. -{access, mam_lookup_messages_shaper, [{mam_shaper, all}]}. - -{access, mam_set_prefs_global_shaper, [{mam_global_shaper, all}]}. -{access, mam_get_prefs_global_shaper, [{mam_global_shaper, all}]}. -{access, mam_lookup_messages_global_shaper, [{mam_global_shaper, all}]}. - -{node_specific_options, [ - [h,'_',module_opt,mod_muc_log, outdir] - ]}. - -{language, "en"}. -{modules, - [ - {mod_adhoc, []}, - {mod_disco, []}, - {mod_last, []}, - {mod_stream_management, [ ]}, - {mod_muc, [ - %% Change host in version 2 - {host, "conference.@HOST@"}, - {access, muc}, - {access_create, muc_create} - ]}, - %% outdir is node-specific - {mod_muc_log, [ {outdir, "/tmp/muclogs_node2"}, {access_log, muc} ]}, - {mod_privacy, []}, - {mod_register, [ - {welcome_message, {"", ""}}, - {ip_access, [{allow, "127.0.0.0/8"}, - {deny, "0.0.0.0/0"}]}, - {access, register} - ]}, - {mod_roster, []}, - {mod_sic, []}, - {mod_vcard, [ {allow_return_all, true}, {search_all_hosts, true} ]}, - {mod_bosh, []} - - ]}. diff --git a/test/ejabberd_config_SUITE_data/mongooseim.split.cfg b/test/ejabberd_config_SUITE_data/mongooseim.split.cfg deleted file mode 100644 index 7c6ecd7fbc..0000000000 --- a/test/ejabberd_config_SUITE_data/mongooseim.split.cfg +++ /dev/null @@ -1,625 +0,0 @@ -%%% -%%% ejabberd configuration file -%%% -%%%' - -%%% The parameters used in this configuration file are explained in more detail -%%% in the ejabberd Installation and Operation Guide. -%%% Please consult the Guide in case of doubts, it is included with -%%% your copy of ejabberd, and is also available online at -%%% http://www.process-one.net/en/ejabberd/docs/ - -%%% This configuration file contains Erlang terms. -%%% In case you want to understand the syntax, here are the concepts: -%%% -%%% - The character to comment a line is % -%%% -%%% - Each term ends in a dot, for example: -%%% override_global. -%%% -%%% - A tuple has a fixed definition, its elements are -%%% enclosed in {}, and separated with commas: -%%% {loglevel, 4}. -%%% -%%% - A list can have as many elements as you want, -%%% and is enclosed in [], for example: -%%% [http_poll, web_admin, tls] -%%% -%%% Pay attention that list elements are delimited with commas, -%%% but no comma is allowed after the last list element. This will -%%% give a syntax error unlike in more lenient languages (e.g. Python). -%%% -%%% - A keyword of ejabberd is a word in lowercase. -%%% Strings are enclosed in "" and can contain spaces, dots, ... -%%% {language, "en"}. -%%% {ldap_rootdn, "dc=example,dc=com"}. -%%% -%%% - This term includes a tuple, a keyword, a list, and two strings: -%%% {hosts, ["jabber.example.net", "im.example.com"]}. -%%% -%%% - This config is preprocessed during release generation by a tool which -%%% interprets double curly braces as substitution markers, so avoid this -%%% syntax in this file (though it's valid Erlang). -%%% -%%% So this is OK (though arguably looks quite ugly): -%%% { {s2s_addr, "example-host.net"}, {127,0,0,1} }. -%%% -%%% And I can't give an example of what's not OK exactly because -%%% of this rule. -%%% - - -%%%. ======================= -%%%' OVERRIDE STORED OPTIONS - -%% -%% Override the old values stored in the database. -%% - -%% -%% Override global options (shared by all ejabberd nodes in a cluster). -%% -%%override_global. - -%% -%% Override local options (specific for this particular ejabberd node). -%% -%%override_local. - -%% -%% Remove the Access Control Lists before new ones are added. -%% -%%override_acls. - -%%%. ========= -%%%' GDPR RELATED -%% when set to true, user data removals will be applied on all modules that implements GDPR behaviour - -%% even the disabled ones! -%% -{gdpr_removal_for_disabled_modules, false}. - -%%%. ========= -%%%' DEBUGGING - -%% -%% loglevel: Verbosity of log files generated by ejabberd. -%% 0: No ejabberd log at all (not recommended) -%% 1: Critical -%% 2: Error -%% 3: Warning -%% 4: Info -%% 5: Debug -%% -{loglevel, 3}. - -%% -%% alarms: an optional alarm handler, subscribed to system events -%% long_gc: minimum GC time in ms for long_gc alarm -%% large_heap: minimum process heap size for large_heap alarm -%% handlers: a list of alarm handlers -%% - alarms_basic_handler: logs alarms and stores a brief alarm summary -%% - alarms_folsom_handler: stores alarm details in folsom metrics -%% -%% Example: -%% {alarms, -%% [{long_gc, 10000}, -%% {large_heap, 1000000}, -%% {handlers, [alarms_basic_handler, -%% alarms_folsom_handler]}] -%% }. - - -%%%. ================ -%%%' SERVED HOSTNAMES - -%% -%% hosts: Domains served by ejabberd. -%% You can define one or several, for example: -%% {hosts, ["example.net", "example.com", "example.org"]}. - -%% See include_config_file: mongooseim.hosts.cfg after the modules section for host definitions. - -%% -%% route_subdomains: Delegate subdomains to other XMPP servers. -%% For example, if this ejabberd serves example.org and you want -%% to allow communication with an XMPP server called im.example.org. -%% -%%{route_subdomains, s2s}. - - -%%%. =============== -%%%' LISTENING PORTS - -%% -%% listen: The ports ejabberd will listen on, which service each is handled -%% by and what options to start it with. -%% -{listen, - [ - - { 5280, ejabberd_cowboy, [ - {num_acceptors, 10}, - {max_connections, 1024}, - %% Uncomment for HTTPS - %{cert, "priv/server.crt"}, - %{key, "priv/server.key"}, - %{key_pass, ""}, - {modules, [ - %% Modules used here should also be listed in the MODULES section. - {"_", "/http-bind", mod_bosh}, - {"_", "/ws-xmpp", mod_websockets}, - {"localhost", "/api", mongoose_api, [{handlers, [mongoose_api_metrics, - mongoose_api_users]}]} - ]} - ]}, - - { 5222, ejabberd_c2s, [ - - %% - %% If TLS is compiled in and you installed a SSL - %% certificate, specify the full path to the - %% file and uncomment this line: - %% - %%{certfile, "/path/to/ssl.pem"}, starttls, - %%{zlib, 10000}, - %% https://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS - %% {ciphers, "TLSv1.2:TLSv1.3"}, - {access, c2s}, - {shaper, c2s_shaper}, - {max_stanza_size, 65536} - ]}, - - - - %% - %% To enable the old SSL connection method on port 5223: - %% - %%{5223, ejabberd_c2s, [ - %% {access, c2s}, - %% {shaper, c2s_shaper}, - %% {certfile, "/path/to/ssl.pem"}, tls, - %% {max_stanza_size, 65536} - %% ]}, - - { 5269, ejabberd_s2s_in, [ - {shaper, s2s_shaper}, - {max_stanza_size, 131072} - ]} - - %% - %% ejabberd_service: Interact with external components (transports, ...) - %% - %%{8888, ejabberd_service, [ - %% {access, all}, - %% {shaper_rule, fast}, - %% {ip, {127, 0, 0, 1}}, - %% {hosts, ["icq.example.org", "sms.example.org"], - %% [{password, "secret"}] - %% } - %% ]}, - - %% - %% ejabberd_stun: Handles STUN Binding requests - %% - %%{ {3478, udp}, ejabberd_stun, []} - - ]}. - -%% -%% s2s_use_starttls: Enable STARTTLS + Dialback for S2S connections. -%% Allowed values are: false optional required required_trusted -%% You must specify a certificate file. -%% -%%{s2s_use_starttls, optional}. - -%% -%% s2s_certfile: Specify a certificate file. -%% -%%{s2s_certfile, "/path/to/ssl.pem"}. - -%% https://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS -%% {s2s_ciphers, "TLSv1.2:TLSv1.3"}. - -%% -%% domain_certfile: Specify a different certificate for each served hostname. -%% -%%{domain_certfile, "example.org", "/path/to/example_org.pem"}. -%%{domain_certfile, "example.com", "/path/to/example_com.pem"}. - -%% -%% S2S whitelist or blacklist -%% -%% Default s2s policy for undefined hosts. -%% -{s2s_default_policy, deny }. - -%% -%% Allow or deny communication with specific servers. -%% -%%{ {s2s_host, "goodhost.org"}, allow}. -%%{ {s2s_host, "badhost.org"}, deny}. - -{outgoing_s2s_port, 5269 }. - -%% -%% IP addresses predefined for specific hosts to skip DNS lookups. -%% Ports defined here take precedence over outgoing_s2s_port. -%% Examples: -%% -%% { {s2s_addr, "example-host.net"}, {127,0,0,1} }. -%% { {s2s_addr, "example-host.net"}, { {127,0,0,1}, 5269 } }. -%% { {s2s_addr, "example-host.net"}, { {127,0,0,1}, 5269 } }. - -%% -%% Outgoing S2S options -%% -%% Preferred address families (which to try first) and connect timeout -%% in milliseconds. -%% -%%{outgoing_s2s_options, [ipv4, ipv6], 10000}. - -%%%. ============== -%%%' SESSION BACKEND - -{sm_backend, {mnesia, []} }. - -%%%. ============== -%%%' AUTHENTICATION - -{auth_method, internal}. - -%%%. ============== -%%%' DATABASE SETUP - - -%%%. =============== -%%%' TRAFFIC SHAPERS - -%% -%% The "normal" shaper limits traffic speed to 1000 B/s -%% -{shaper, normal, {maxrate, 1000}}. - -%% -%% The "fast" shaper limits traffic speed to 50000 B/s -%% -{shaper, fast, {maxrate, 50000}}. - -%% -%% This option specifies the maximum number of elements in the queue -%% of the FSM. Refer to the documentation for details. -%% -{max_fsm_queue, 1000}. - - -%%%. ==================== -%%%' ACCESS CONTROL LISTS - -%% -%% The 'admin' ACL grants administrative privileges to XMPP accounts. -%% You can put here as many accounts as you want. -%% -%{acl, admin, {user, "alice", "localhost"}}. -%{acl, admin, {user, "a", "localhost"}}. - -%% -%% Blocked users -%% -%%{acl, blocked, {user, "baduser", "example.org"}}. -%%{acl, blocked, {user, "test"}}. - -%% -%% Local users: don't modify this line. -%% -{acl, local, {user_regexp, ""}}. - -%% -%% More examples of ACLs -%% -%%{acl, jabberorg, {server, "jabber.org"}}. -%%{acl, aleksey, {user, "aleksey", "jabber.ru"}}. -%%{acl, test, {user_regexp, "^test"}}. -%%{acl, test, {user_glob, "test*"}}. - -%% -%% Define specific ACLs in a virtual host. -%% -%%{host_config, "localhost", -%% [ -%% {acl, admin, {user, "bob-local", "localhost"}} -%% ] -%%}. - - -%%%. ============ -%%%' ACCESS RULES - -%% Maximum number of simultaneous sessions allowed for a single user: -{access, max_user_sessions, [{10, all}]}. - -%% Maximum number of offline messages that users can have: -{access, max_user_offline_messages, [{5000, admin}, {100, all}]}. - -%% This rule allows access only for local users: -{access, local, [{allow, local}]}. - -%% Only non-blocked users can use c2s connections: -{access, c2s, [{deny, blocked}, - {allow, all}]}. - -%% For C2S connections, all users except admins use the "normal" shaper -{access, c2s_shaper, [{none, admin}, - {normal, all}]}. - -%% All S2S connections use the "fast" shaper -{access, s2s_shaper, [{fast, all}]}. - -%% Admins of this server are also admins of the MUC service: -{access, muc_admin, [{allow, admin}]}. - -%% Only accounts of the local ejabberd server can create rooms: -{access, muc_create, [{allow, local}]}. - -%% All users are allowed to use the MUC service: -{access, muc, [{allow, all}]}. - -%% In-band registration allows registration of any possible username. -%% To disable in-band registration, replace 'allow' with 'deny'. -{access, register, [{allow, all}]}. - -%% By default the frequency of account registrations from the same IP -%% is limited to 1 account every 10 minutes. To disable, specify: infinity -{registration_timeout, infinity}. - -%% Default settings for MAM. -%% To set non-standard value, replace 'default' with 'allow' or 'deny'. -%% Only user can access his/her archive by default. -%% An online user can read room's archive by default. -%% Only an owner can change settings by default. -%% Empty list (i.e. `[]`) means `[{deny, all}]`. -{access, mam_set_prefs, [{default, all}]}. -{access, mam_get_prefs, [{default, all}]}. -{access, mam_lookup_messages, [{default, all}]}. - -%% 1 command of the specified type per second. -{shaper, mam_shaper, {maxrate, 1}}. -%% This shaper is primeraly for Mnesia overload protection during stress testing. -%% The limit is 1000 operations of each type per second. -{shaper, mam_global_shaper, {maxrate, 1000}}. - -{access, mam_set_prefs_shaper, [{mam_shaper, all}]}. -{access, mam_get_prefs_shaper, [{mam_shaper, all}]}. -{access, mam_lookup_messages_shaper, [{mam_shaper, all}]}. - -{access, mam_set_prefs_global_shaper, [{mam_global_shaper, all}]}. -{access, mam_get_prefs_global_shaper, [{mam_global_shaper, all}]}. -{access, mam_lookup_messages_global_shaper, [{mam_global_shaper, all}]}. - - -%% -%% Define specific Access Rules in a virtual host. -%% -%%{host_config, "localhost", -%% [ -%% {access, c2s, [{allow, admin}, {deny, all}]}, -%% {access, register, [{deny, all}]} -%% ] -%%}. - - -%%%. ================ -%%%' DEFAULT LANGUAGE - -%% -%% language: Default language used for server messages. -%% -{language, "en"}. - -%% -%% Set a different default language in a virtual host. -%% -%%{host_config, "localhost", -%% [{language, "ru"}] -%%}. - - -%%%. ======= -%%%' MODULES - -%% -%% Modules enabled in all ejabberd virtual hosts. -%% For list of possible modules options, check documentation. -%% If module comes in two versions, like mod_last and mod_last_rdbms, -%% use only one of them. -%% -{modules, - [ - %{mod_admin_extra, [{submods, [node, accounts, sessions, vcard, - % roster, last, private, stanza, stats]}]}, - {mod_adhoc, []}, - {mod_disco, []}, - {mod_last, []}, - {mod_stream_management, [ - % default 100 - % size of a buffer of unacked messages - % {buffer_max, 100} - - % default 1 - server sends the ack request after each stanza - % {ack_freq, 1} - - % default: 600 seconds - % {resume_timeout, 600} - ]}, - {mod_muc, [ - {host, "muc.@HOST@"}, - {access, muc}, - {access_create, muc_create} - ]}, - {mod_muc_log, - [ - {outdir, "/tmp/muclogs"}, - {access_log, muc} - ]}, - %{mod_offline, [{access_max_user_messages, max_user_offline_messages}]}, - {mod_privacy, []}, - {mod_register, [ - %% - %% Set the minimum informational entropy for passwords. - %% - %%{password_strength, 32}, - - %% - %% After successful registration, the user receives - %% a message with this subject and body. - %% - {welcome_message, {"", ""}}, - - %% - %% When a user registers, send a notification to - %% these XMPP accounts. - %% - %%{registration_watchers, ["admin1@example.org"]}, - - %% - %% Only clients in the server machine can register accounts - %% - {ip_access, [{allow, "127.0.0.0/8"}, - {deny, "0.0.0.0/0"}]}, - - %% - %% Local c2s or remote s2s users cannot register accounts - %% - %%{access_from, deny}, - - {access, register} - ]}, - {mod_sic, []}, - {mod_vcard, [ {allow_return_all, true}, -{search_all_hosts, true} -%{matches, 1}, -%{search, true}, -%{host, directory.@HOST@} -]}, - {mod_bosh, []} - - %% - %% Message Archive Management (MAM) for registered users. - %% - - %% A module for storing preferences in RDBMS (used by default). - %% Enable for private message archives. -% {mod_mam_rdbms_prefs, [pm]}, - %% Enable for multiuser message archives. -% {mod_mam_rdbms_prefs, [muc]}, - %% Enable for both private and multiuser message archives. -% {mod_mam_rdbms_prefs, [pm, muc]}, - - %% A module for storing preferences in Mnesia (recommended). - %% This module will be called each time, as a message is routed. - %% That is why, Mnesia is better for this job. -% {mod_mam_mnesia_prefs, [pm, muc]}, - - %% A back-end for storing messages. - %% Synchronious writer (used by default). - %% This writer is easy to debug, but writing performance is low. -% {mod_mam_rdbms_arch, [pm]}, - - %% Enable the module with a custom writer. -% {mod_mam_rdbms_arch, [no_writer, pm]}, - - %% A pool of asynchronious writers (recommended). - %% Messages will be grouped together based on archive id. -% {mod_mam_rdbms_async_pool_writer, [pm]}, - - %% A module for converting an archive id to an integer. - %% Extract information using RDBMS. -% {mod_mam_rdbms_user, [pm, muc]}, - - %% Cache information about users (recommended). - %% Requires mod_mam_rdbms_user or alternative. -% {mod_mam_cache_user, [pm, muc]}, - - %% Enable MAM. -% {mod_mam, []}, - - - %% - %% Message Archive Management (MAM) for multi-user chats (MUC). - %% Enable XEP-0313 for "muc.@HOST@". - %% - - %% A back-end for storing messages (default for MUC). - %% Modules mod_mam_muc_* are optimized for MUC. - %% - %% Synchronious writer (used by default for MUC). - %% This module is easy to debug, but performance is low. -% {mod_mam_muc_rdbms_arch, []}, -% {mod_mam_muc_rdbms_arch, [no_writer]}, - - %% Asynchronious writer for RDBMS (recommended for MUC). - %% Messages will be grouped and inserted all at once. -% {mod_mam_muc_rdbms_async_pool_writer, []}, - - %% Load mod_mam_rdbms_user too. - - %% Enable MAM for MUC -% {mod_mam_muc, [{host, "muc.@HOST@"}]} - - - %% - %% MAM configuration examples - %% - - %% Only MUC, no user-defined preferences, good performance. -% {mod_mam_rdbms_user, [muc]}, -% {mod_mam_cache_user, [muc]}, -% {mod_mam_muc_rdbms_arch, [no_writer]}, -% {mod_mam_muc_rdbms_async_pool_writer, []}, -% {mod_mam_muc, [{host, "muc.@HOST@"}]} - - %% Only archives for c2c messages, good performance. -% {mod_mam_rdbms_user, [pm]}, -% {mod_mam_cache_user, [pm]}, -% {mod_mam_mnesia_prefs, [pm]}, -% {mod_mam_rdbms_arch, [pm, no_writer]}, -% {mod_mam_rdbms_async_pool_writer, [pm]}, -% {mod_mam, []} - - %% Basic configuration for c2c messages, bad performance, easy to debug. -% {mod_mam_rdbms_user, [pm]}, -% {mod_mam_rdbms_prefs, [pm]}, -% {mod_mam_rdbms_arch, [pm]}, -% {mod_mam, []} - - ]}. - - -%% -%% Enable modules with custom options in a specific virtual host -%% -%%{host_config, "localhost", -%% [{ {add, modules}, -%% [ -%% {mod_some_module, []} -%% ] -%% } -%% ]}. - -%%%. -%%%' EXTERNAL CONFIG FILES - -%% -%% External config files defining modules must be included after the global modules section. -%% -{include_config_file, "log_private/etc/mongooseim.hosts.cfg"}. - -%%%. -%%%' - -%%% $Id$ - -%%% Local Variables: -%%% mode: erlang -%%% End: -%%% vim: set filetype=erlang tabstop=8 foldmarker=%%%',%%%. foldmethod=marker: -%%%. diff --git a/test/ejabberd_config_SUITE_data/mongooseim.with_mod_offline.cfg b/test/ejabberd_config_SUITE_data/mongooseim.with_mod_offline.cfg deleted file mode 100644 index 5a57275c42..0000000000 --- a/test/ejabberd_config_SUITE_data/mongooseim.with_mod_offline.cfg +++ /dev/null @@ -1,601 +0,0 @@ -%%% -%%% ejabberd configuration file -%%% -%%%' - -%%% The parameters used in this configuration file are explained in more detail -%%% in the ejabberd Installation and Operation Guide. -%%% Please consult the Guide in case of doubts, it is included with -%%% your copy of ejabberd, and is also available online at -%%% http://www.process-one.net/en/ejabberd/docs/ - -%%% This configuration file contains Erlang terms. -%%% In case you want to understand the syntax, here are the concepts: -%%% -%%% - The character to comment a line is % -%%% -%%% - Each term ends in a dot, for example: -%%% override_global. -%%% -%%% - A tuple has a fixed definition, its elements are -%%% enclosed in {}, and separated with commas: -%%% {loglevel, 4}. -%%% -%%% - A list can have as many elements as you want, -%%% and is enclosed in [], for example: -%%% [http_poll, web_admin, tls] -%%% -%%% Pay attention that list elements are delimited with commas, -%%% but no comma is allowed after the last list element. This will -%%% give a syntax error unlike in more lenient languages (e.g. Python). -%%% -%%% - A keyword of ejabberd is a word in lowercase. -%%% Strings are enclosed in "" and can contain spaces, dots, ... -%%% {language, "en"}. -%%% {ldap_rootdn, "dc=example,dc=com"}. -%%% -%%% - This term includes a tuple, a keyword, a list, and two strings: -%%% {hosts, ["jabber.example.net", "im.example.com"]}. -%%% -%%% - This config is preprocessed during release generation by a tool which -%%% interprets double curly braces as substitution markers, so avoid this -%%% syntax in this file (though it's valid Erlang). -%%% -%%% So this is OK (though arguably looks quite ugly): -%%% { {s2s_addr, "example-host.net"}, {127,0,0,1} }. -%%% -%%% And I can't give an example of what's not OK exactly because -%%% of this rule. -%%% - - -%%%. ======================= -%%%' OVERRIDE STORED OPTIONS - -%% -%% Override the old values stored in the database. -%% - -%% -%% Override global options (shared by all ejabberd nodes in a cluster). -%% -%%override_global. - -%% -%% Override local options (specific for this particular ejabberd node). -%% -%%override_local. - -%% -%% Remove the Access Control Lists before new ones are added. -%% -%%override_acls. - -%%%. ========= -%%%' GDPR RELATED -%% when set to true, user data removals be applied on all modules that implements GDPR behaviour - -%% even the disabled ones! -%% -{gdpr_removal_for_disabled_modules, false}. - -%%%. ========= -%%%' DEBUGGING - -%% -%% loglevel: Verbosity of log files generated by ejabberd. -%% 0: No ejabberd log at all (not recommended) -%% 1: Critical -%% 2: Error -%% 3: Warning -%% 4: Info -%% 5: Debug -%% -{loglevel, 3}. - -%%%. ================ -%%%' SERVED HOSTNAMES - -%% -%% hosts: Domains served by ejabberd. -%% You can define one or several, for example: -%% {hosts, ["example.net", "example.com", "example.org"]}. -%% -{hosts, ["localhost"] }. - -%% -%% route_subdomains: Delegate subdomains to other XMPP servers. -%% For example, if this ejabberd serves example.org and you want -%% to allow communication with an XMPP server called im.example.org. -%% -%%{route_subdomains, s2s}. - - -%%%. =============== -%%%' LISTENING PORTS - -%% -%% listen: The ports ejabberd will listen on, which service each is handled -%% by and what options to start it with. -%% -{listen, - [ - - { 5280, ejabberd_cowboy, [ - {num_acceptors, 10}, - {max_connections, 1024}, - %% Uncomment for HTTPS - %{cert, "priv/server.crt"}, - %{key, "priv/server.key"}, - %{key_pass, ""}, - {modules, [ - %% Modules used here should also be listed in the MODULES section. - {"_", "/http-bind", mod_bosh}, - {"_", "/ws-xmpp", mod_websockets}, - {"localhost", "/api", mongoose_api, [{handlers, [mongoose_api_metrics, - mongoose_api_users]}]} - ]} - ]}, - - { 5222, ejabberd_c2s, [ - - %% - %% If TLS is compiled in and you installed a SSL - %% certificate, specify the full path to the - %% file and uncomment this line: - %% - %%{certfile, "/path/to/ssl.pem"}, starttls, - %%{zlib, 10000}, - %% https://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS - %% {ciphers, "TLSv1.2:TLSv1.3"}, - {access, c2s}, - {shaper, c2s_shaper}, - {max_stanza_size, 65536} - ]}, - - - - %% - %% To enable the old SSL connection method on port 5223: - %% - %%{5223, ejabberd_c2s, [ - %% {access, c2s}, - %% {shaper, c2s_shaper}, - %% {certfile, "/path/to/ssl.pem"}, tls, - %% {max_stanza_size, 65536} - %% ]}, - - { 5269, ejabberd_s2s_in, [ - {shaper, s2s_shaper}, - {max_stanza_size, 131072} - ]} - - %% - %% ejabberd_service: Interact with external components (transports, ...) - %% - %%{8888, ejabberd_service, [ - %% {access, all}, - %% {shaper_rule, fast}, - %% {ip, {127, 0, 0, 1}}, - %% {hosts, ["icq.example.org", "sms.example.org"], - %% [{password, "secret"}] - %% } - %% ]}, - - %% - %% ejabberd_stun: Handles STUN Binding requests - %% - %%{ {3478, udp}, ejabberd_stun, []} - - ]}. - -%% -%% s2s_use_starttls: Enable STARTTLS + Dialback for S2S connections. -%% Allowed values are: false optional required required_trusted -%% You must specify a certificate file. -%% -%%{s2s_use_starttls, optional}. - -%% -%% s2s_certfile: Specify a certificate file. -%% -%%{s2s_certfile, "/path/to/ssl.pem"}. - -%% https://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS -%% {s2s_ciphers, "TLSv1.2:TLSv1.3"}. - -%% -%% domain_certfile: Specify a different certificate for each served hostname. -%% -%%{domain_certfile, "example.org", "/path/to/example_org.pem"}. -%%{domain_certfile, "example.com", "/path/to/example_com.pem"}. - -%% -%% S2S whitelist or blacklist -%% -%% Default s2s policy for undefined hosts. -%% -{s2s_default_policy, deny }. - -%% -%% Allow or deny communication with specific servers. -%% -%%{ {s2s_host, "goodhost.org"}, allow}. -%%{ {s2s_host, "badhost.org"}, deny}. - -{outgoing_s2s_port, 5269 }. - -%% -%% IP addresses predefined for specific hosts to skip DNS lookups. -%% Ports defined here take precedence over outgoing_s2s_port. -%% Examples: -%% -%% { {s2s_addr, "example-host.net"}, {127,0,0,1} }. -%% { {s2s_addr, "example-host.net"}, { {127,0,0,1}, 5269 } }. -%% { {s2s_addr, "example-host.net"}, { {127,0,0,1}, 5269 } }. - -%% -%% Outgoing S2S options -%% -%% Preferred address families (which to try first) and connect timeout -%% in milliseconds. -%% -%%{outgoing_s2s_options, [ipv4, ipv6], 10000}. - -%%%. ============== -%%%' SESSION BACKEND - -{sm_backend, {mnesia, []} }. - -%%%. ============== -%%%' AUTHENTICATION - -{auth_method, internal}. - -%%%. ============== -%%%' DATABASE SETUP - - -%%%. =============== -%%%' TRAFFIC SHAPERS - -%% -%% The "normal" shaper limits traffic speed to 1000 B/s -%% -{shaper, normal, {maxrate, 1000}}. - -%% -%% The "fast" shaper limits traffic speed to 50000 B/s -%% -{shaper, fast, {maxrate, 50000}}. - -%% -%% This option specifies the maximum number of elements in the queue -%% of the FSM. Refer to the documentation for details. -%% -{max_fsm_queue, 1000}. - - -%%%. ==================== -%%%' ACCESS CONTROL LISTS - -%% -%% The 'admin' ACL grants administrative privileges to XMPP accounts. -%% You can put here as many accounts as you want. -%% -%{acl, admin, {user, "alice", "localhost"}}. -%{acl, admin, {user, "a", "localhost"}}. - -%% -%% Blocked users -%% -%%{acl, blocked, {user, "baduser", "example.org"}}. -%%{acl, blocked, {user, "test"}}. - -%% -%% Local users: don't modify this line. -%% -{acl, local, {user_regexp, ""}}. - -%% -%% More examples of ACLs -%% -%%{acl, jabberorg, {server, "jabber.org"}}. -%%{acl, aleksey, {user, "aleksey", "jabber.ru"}}. -%%{acl, test, {user_regexp, "^test"}}. -%%{acl, test, {user_glob, "test*"}}. - -%% -%% Define specific ACLs in a virtual host. -%% -%%{host_config, "localhost", -%% [ -%% {acl, admin, {user, "bob-local", "localhost"}} -%% ] -%%}. - - -%%%. ============ -%%%' ACCESS RULES - -%% Maximum number of simultaneous sessions allowed for a single user: -{access, max_user_sessions, [{10, all}]}. - -%% Maximum number of offline messages that users can have: -{access, max_user_offline_messages, [{5000, admin}, {100, all}]}. - -%% This rule allows access only for local users: -{access, local, [{allow, local}]}. - -%% Only non-blocked users can use c2s connections: -{access, c2s, [{deny, blocked}, - {allow, all}]}. - -%% For C2S connections, all users except admins use the "normal" shaper -{access, c2s_shaper, [{none, admin}, - {normal, all}]}. - -%% All S2S connections use the "fast" shaper -{access, s2s_shaper, [{fast, all}]}. - -%% Admins of this server are also admins of the MUC service: -{access, muc_admin, [{allow, admin}]}. - -%% Only accounts of the local ejabberd server can create rooms: -{access, muc_create, [{allow, local}]}. - -%% All users are allowed to use the MUC service: -{access, muc, [{allow, all}]}. - -%% In-band registration allows registration of any possible username. -%% To disable in-band registration, replace 'allow' with 'deny'. -{access, register, [{allow, all}]}. - -%% By default the frequency of account registrations from the same IP -%% is limited to 1 account every 10 minutes. To disable, specify: infinity -{registration_timeout, infinity}. - -%% Default settings for MAM. -%% To set non-standard value, replace 'default' with 'allow' or 'deny'. -%% Only user can access his/her archive by default. -%% An online user can read room's archive by default. -%% Only an owner can change settings by default. -%% Empty list (i.e. `[]`) means `[{deny, all}]`. -{access, mam_set_prefs, [{default, all}]}. -{access, mam_get_prefs, [{default, all}]}. -{access, mam_lookup_messages, [{default, all}]}. - -%% 1 command of the specified type per second. -{shaper, mam_shaper, {maxrate, 1}}. -%% This shaper is primeraly for Mnesia overload protection during stress testing. -%% The limit is 1000 operations of each type per second. -{shaper, mam_global_shaper, {maxrate, 1000}}. - -{access, mam_set_prefs_shaper, [{mam_shaper, all}]}. -{access, mam_get_prefs_shaper, [{mam_shaper, all}]}. -{access, mam_lookup_messages_shaper, [{mam_shaper, all}]}. - -{access, mam_set_prefs_global_shaper, [{mam_global_shaper, all}]}. -{access, mam_get_prefs_global_shaper, [{mam_global_shaper, all}]}. -{access, mam_lookup_messages_global_shaper, [{mam_global_shaper, all}]}. - - -%% -%% Define specific Access Rules in a virtual host. -%% -%%{host_config, "localhost", -%% [ -%% {access, c2s, [{allow, admin}, {deny, all}]}, -%% {access, register, [{deny, all}]} -%% ] -%%}. - - -%%%. ================ -%%%' DEFAULT LANGUAGE - -%% -%% language: Default language used for server messages. -%% -{language, "en"}. - -%% -%% Set a different default language in a virtual host. -%% -%%{host_config, "localhost", -%% [{language, "ru"}] -%%}. - - -%%%. ======= -%%%' MODULES - -%% -%% Modules enabled in all ejabberd virtual hosts. -%% For list of possible modules options, check documentation. -%% If module comes in two versions, like mod_last and mod_last_rdbms, -%% use only one of them. -%% -{modules, - [ - %{mod_admin_extra, [{submods, [node, accounts, sessions, vcard, - % roster, last, private, stanza, stats]}]}, - {mod_adhoc, []}, - {mod_disco, []}, - {mod_last, []}, - {mod_stream_management, [ - % default 100 - % size of a buffer of unacked messages - % {buffer_max, 100} - - % default 1 - server sends the ack request after each stanza - % {ack_freq, 1} - - % default: 600 seconds - % {resume_timeout, 600} - ]}, - {mod_muc, [ - {host, "muc.@HOST@"}, - {access, muc}, - {access_create, muc_create} - ]}, - {mod_muc_log, - [ - {outdir, "/tmp/muclogs"}, - {access_log, muc} - ]}, - {mod_offline, [{access_max_user_messages, max_user_offline_messages}]}, - {mod_privacy, []}, - {mod_register, [ - %% - %% Set the minimum informational entropy for passwords. - %% - %%{password_strength, 32}, - - %% - %% After successful registration, the user receives - %% a message with this subject and body. - %% - {welcome_message, {"", ""}}, - - %% - %% When a user registers, send a notification to - %% these XMPP accounts. - %% - %%{registration_watchers, ["admin1@example.org"]}, - - %% - %% Only clients in the server machine can register accounts - %% - {ip_access, [{allow, "127.0.0.0/8"}, - {deny, "0.0.0.0/0"}]}, - - %% - %% Local c2s or remote s2s users cannot register accounts - %% - %%{access_from, deny}, - - {access, register} - ]}, - {mod_roster, []}, - {mod_sic, []}, - {mod_vcard, [ {allow_return_all, true}, -{search_all_hosts, true} -%{matches, 1}, -%{search, true}, -%{host, directory.@HOST@} -]}, - {mod_bosh, []} - - %% - %% Message Archive Management (MAM) for registered users. - %% - - %% A module for storing preferences in RDBMS (used by default). - %% Enable for private message archives. -% {mod_mam_rdbms_prefs, [pm]}, - %% Enable for multiuser message archives. -% {mod_mam_rdbms_prefs, [muc]}, - %% Enable for both private and multiuser message archives. -% {mod_mam_rdbms_prefs, [pm, muc]}, - - %% A module for storing preferences in Mnesia (recommended). - %% This module will be called each time, as a message is routed. - %% That is why, Mnesia is better for this job. -% {mod_mam_mnesia_prefs, [pm, muc]}, - - %% A back-end for storing messages. - %% Synchronious writer (used by default). - %% This writer is easy to debug, but writing performance is low. -% {mod_mam_rdbms_arch, [pm]}, - - %% Enable the module with a custom writer. -% {mod_mam_rdbms_arch, [no_writer, pm]}, - - %% A pool of asynchronious writers (recommended). - %% Messages will be grouped together based on archive id. -% {mod_mam_rdbms_async_pool_writer, [pm]}, - - %% A module for converting an archive id to an integer. - %% Extract information using RDBMS. -% {mod_mam_rdbms_user, [pm, muc]}, - - %% Cache information about users (recommended). - %% Requires mod_mam_rdbms_user or alternative. -% {mod_mam_cache_user, [pm, muc]}, - - %% Enable MAM. -% {mod_mam, []}, - - - %% - %% Message Archive Management (MAM) for multi-user chats (MUC). - %% Enable XEP-0313 for "muc.@HOST@". - %% - - %% A back-end for storing messages (default for MUC). - %% Modules mod_mam_muc_* are optimized for MUC. - %% - %% Synchronious writer (used by default for MUC). - %% This module is easy to debug, but performance is low. -% {mod_mam_muc_rdbms_arch, []}, -% {mod_mam_muc_rdbms_arch, [no_writer]}, - - %% Asynchronious writer for RDBMS (recommended for MUC). - %% Messages will be grouped and inserted all at once. -% {mod_mam_muc_rdbms_async_pool_writer, []}, - - %% Load mod_mam_rdbms_user too. - - %% Enable MAM for MUC -% {mod_mam_muc, [{host, "muc.@HOST@"}]} - - - %% - %% MAM configuration examples - %% - - %% Only MUC, no user-defined preferences, good performance. -% {mod_mam_rdbms_user, [muc]}, -% {mod_mam_cache_user, [muc]}, -% {mod_mam_muc_rdbms_arch, [no_writer]}, -% {mod_mam_muc_rdbms_async_pool_writer, []}, -% {mod_mam_muc, [{host, "muc.@HOST@"}]} - - %% Only archives for c2c messages, good performance. -% {mod_mam_rdbms_user, [pm]}, -% {mod_mam_cache_user, [pm]}, -% {mod_mam_mnesia_prefs, [pm]}, -% {mod_mam_rdbms_arch, [pm, no_writer]}, -% {mod_mam_rdbms_async_pool_writer, [pm]}, -% {mod_mam, []} - - %% Basic configuration for c2c messages, bad performance, easy to debug. -% {mod_mam_rdbms_user, [pm]}, -% {mod_mam_rdbms_prefs, [pm]}, -% {mod_mam_rdbms_arch, [pm]}, -% {mod_mam, []} - - ]}. - - -%% -%% Enable modules with custom options in a specific virtual host -%% -%%{host_config, "localhost", -%% [{ {add, modules}, -%% [ -%% {mod_some_module, []} -%% ] -%% } -%% ]}. - -%%%. -%%%' - -%%% $Id$ - -%%% Local Variables: -%%% mode: erlang -%%% End: -%%% vim: set filetype=erlang tabstop=8 foldmarker=%%%',%%%. foldmethod=marker: -%%%. diff --git a/test/ejabberd_config_SUITE_data/mongooseim.with_mod_offline.different_opts.cfg b/test/ejabberd_config_SUITE_data/mongooseim.with_mod_offline.different_opts.cfg deleted file mode 100644 index 08296daa01..0000000000 --- a/test/ejabberd_config_SUITE_data/mongooseim.with_mod_offline.different_opts.cfg +++ /dev/null @@ -1,601 +0,0 @@ -%%% -%%% ejabberd configuration file -%%% -%%%' - -%%% The parameters used in this configuration file are explained in more detail -%%% in the ejabberd Installation and Operation Guide. -%%% Please consult the Guide in case of doubts, it is included with -%%% your copy of ejabberd, and is also available online at -%%% http://www.process-one.net/en/ejabberd/docs/ - -%%% This configuration file contains Erlang terms. -%%% In case you want to understand the syntax, here are the concepts: -%%% -%%% - The character to comment a line is % -%%% -%%% - Each term ends in a dot, for example: -%%% override_global. -%%% -%%% - A tuple has a fixed definition, its elements are -%%% enclosed in {}, and separated with commas: -%%% {loglevel, 4}. -%%% -%%% - A list can have as many elements as you want, -%%% and is enclosed in [], for example: -%%% [http_poll, web_admin, tls] -%%% -%%% Pay attention that list elements are delimited with commas, -%%% but no comma is allowed after the last list element. This will -%%% give a syntax error unlike in more lenient languages (e.g. Python). -%%% -%%% - A keyword of ejabberd is a word in lowercase. -%%% Strings are enclosed in "" and can contain spaces, dots, ... -%%% {language, "en"}. -%%% {ldap_rootdn, "dc=example,dc=com"}. -%%% -%%% - This term includes a tuple, a keyword, a list, and two strings: -%%% {hosts, ["jabber.example.net", "im.example.com"]}. -%%% -%%% - This config is preprocessed during release generation by a tool which -%%% interprets double curly braces as substitution markers, so avoid this -%%% syntax in this file (though it's valid Erlang). -%%% -%%% So this is OK (though arguably looks quite ugly): -%%% { {s2s_addr, "example-host.net"}, {127,0,0,1} }. -%%% -%%% And I can't give an example of what's not OK exactly because -%%% of this rule. -%%% - - -%%%. ======================= -%%%' OVERRIDE STORED OPTIONS - -%% -%% Override the old values stored in the database. -%% - -%% -%% Override global options (shared by all ejabberd nodes in a cluster). -%% -%%override_global. - -%% -%% Override local options (specific for this particular ejabberd node). -%% -%%override_local. - -%% -%% Remove the Access Control Lists before new ones are added. -%% -%%override_acls. - -%%%. ========= -%%%' GDPR RELATED -%% when set to true, user data removals will be applied on all modules that implements GDPR behaviour - -%% even the disabled ones! -%% -{gdpr_removal_for_disabled_modules, false}. - -%%%. ========= -%%%' DEBUGGING - -%% -%% loglevel: Verbosity of log files generated by ejabberd. -%% 0: No ejabberd log at all (not recommended) -%% 1: Critical -%% 2: Error -%% 3: Warning -%% 4: Info -%% 5: Debug -%% -{loglevel, 3}. - -%%%. ================ -%%%' SERVED HOSTNAMES - -%% -%% hosts: Domains served by ejabberd. -%% You can define one or several, for example: -%% {hosts, ["example.net", "example.com", "example.org"]}. -%% -{hosts, ["localhost"] }. - -%% -%% route_subdomains: Delegate subdomains to other XMPP servers. -%% For example, if this ejabberd serves example.org and you want -%% to allow communication with an XMPP server called im.example.org. -%% -%%{route_subdomains, s2s}. - - -%%%. =============== -%%%' LISTENING PORTS - -%% -%% listen: The ports ejabberd will listen on, which service each is handled -%% by and what options to start it with. -%% -{listen, - [ - - { 5280, ejabberd_cowboy, [ - {num_acceptors, 10}, - {max_connections, 1024}, - %% Uncomment for HTTPS - %{cert, "priv/server.crt"}, - %{key, "priv/server.key"}, - %{key_pass, ""}, - {modules, [ - %% Modules used here should also be listed in the MODULES section. - {"_", "/http-bind", mod_bosh}, - {"_", "/ws-xmpp", mod_websockets}, - {"localhost", "/api", mongoose_api, [{handlers, [mongoose_api_metrics, - mongoose_api_users]}]} - ]} - ]}, - - { 5222, ejabberd_c2s, [ - - %% - %% If TLS is compiled in and you installed a SSL - %% certificate, specify the full path to the - %% file and uncomment this line: - %% - %%{certfile, "/path/to/ssl.pem"}, starttls, - %%{zlib, 10000}, - %% https://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS - %% {ciphers, "TLSv1.2:TLSv1.3"}, - {access, c2s}, - {shaper, c2s_shaper}, - {max_stanza_size, 65536} - ]}, - - - - %% - %% To enable the old SSL connection method on port 5223: - %% - %%{5223, ejabberd_c2s, [ - %% {access, c2s}, - %% {shaper, c2s_shaper}, - %% {certfile, "/path/to/ssl.pem"}, tls, - %% {max_stanza_size, 65536} - %% ]}, - - { 5269, ejabberd_s2s_in, [ - {shaper, s2s_shaper}, - {max_stanza_size, 131072} - ]} - - %% - %% ejabberd_service: Interact with external components (transports, ...) - %% - %%{8888, ejabberd_service, [ - %% {access, all}, - %% {shaper_rule, fast}, - %% {ip, {127, 0, 0, 1}}, - %% {hosts, ["icq.example.org", "sms.example.org"], - %% [{password, "secret"}] - %% } - %% ]}, - - %% - %% ejabberd_stun: Handles STUN Binding requests - %% - %%{ {3478, udp}, ejabberd_stun, []} - - ]}. - -%% -%% s2s_use_starttls: Enable STARTTLS + Dialback for S2S connections. -%% Allowed values are: false optional required required_trusted -%% You must specify a certificate file. -%% -%%{s2s_use_starttls, optional}. - -%% -%% s2s_certfile: Specify a certificate file. -%% -%%{s2s_certfile, "/path/to/ssl.pem"}. - -%% https://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS -%% {s2s_ciphers, "TLSv1.2:TLSv1.3"}. - -%% -%% domain_certfile: Specify a different certificate for each served hostname. -%% -%%{domain_certfile, "example.org", "/path/to/example_org.pem"}. -%%{domain_certfile, "example.com", "/path/to/example_com.pem"}. - -%% -%% S2S whitelist or blacklist -%% -%% Default s2s policy for undefined hosts. -%% -{s2s_default_policy, deny }. - -%% -%% Allow or deny communication with specific servers. -%% -%%{ {s2s_host, "goodhost.org"}, allow}. -%%{ {s2s_host, "badhost.org"}, deny}. - -{outgoing_s2s_port, 5269 }. - -%% -%% IP addresses predefined for specific hosts to skip DNS lookups. -%% Ports defined here take precedence over outgoing_s2s_port. -%% Examples: -%% -%% { {s2s_addr, "example-host.net"}, {127,0,0,1} }. -%% { {s2s_addr, "example-host.net"}, { {127,0,0,1}, 5269 } }. -%% { {s2s_addr, "example-host.net"}, { {127,0,0,1}, 5269 } }. - -%% -%% Outgoing S2S options -%% -%% Preferred address families (which to try first) and connect timeout -%% in milliseconds. -%% -%%{outgoing_s2s_options, [ipv4, ipv6], 10000}. - -%%%. ============== -%%%' SESSION BACKEND - -{sm_backend, {mnesia, []} }. - -%%%. ============== -%%%' AUTHENTICATION - -{auth_method, internal}. - -%%%. ============== -%%%' DATABASE SETUP - - -%%%. =============== -%%%' TRAFFIC SHAPERS - -%% -%% The "normal" shaper limits traffic speed to 1000 B/s -%% -{shaper, normal, {maxrate, 1000}}. - -%% -%% The "fast" shaper limits traffic speed to 50000 B/s -%% -{shaper, fast, {maxrate, 50000}}. - -%% -%% This option specifies the maximum number of elements in the queue -%% of the FSM. Refer to the documentation for details. -%% -{max_fsm_queue, 1000}. - - -%%%. ==================== -%%%' ACCESS CONTROL LISTS - -%% -%% The 'admin' ACL grants administrative privileges to XMPP accounts. -%% You can put here as many accounts as you want. -%% -%{acl, admin, {user, "alice", "localhost"}}. -%{acl, admin, {user, "a", "localhost"}}. - -%% -%% Blocked users -%% -%%{acl, blocked, {user, "baduser", "example.org"}}. -%%{acl, blocked, {user, "test"}}. - -%% -%% Local users: don't modify this line. -%% -{acl, local, {user_regexp, ""}}. - -%% -%% More examples of ACLs -%% -%%{acl, jabberorg, {server, "jabber.org"}}. -%%{acl, aleksey, {user, "aleksey", "jabber.ru"}}. -%%{acl, test, {user_regexp, "^test"}}. -%%{acl, test, {user_glob, "test*"}}. - -%% -%% Define specific ACLs in a virtual host. -%% -%%{host_config, "localhost", -%% [ -%% {acl, admin, {user, "bob-local", "localhost"}} -%% ] -%%}. - - -%%%. ============ -%%%' ACCESS RULES - -%% Maximum number of simultaneous sessions allowed for a single user: -{access, max_user_sessions, [{10, all}]}. - -%% Maximum number of offline messages that users can have: -{access, max_user_offline_messages, [{5000, admin}, {100, all}]}. - -%% This rule allows access only for local users: -{access, local, [{allow, local}]}. - -%% Only non-blocked users can use c2s connections: -{access, c2s, [{deny, blocked}, - {allow, all}]}. - -%% For C2S connections, all users except admins use the "normal" shaper -{access, c2s_shaper, [{none, admin}, - {normal, all}]}. - -%% All S2S connections use the "fast" shaper -{access, s2s_shaper, [{fast, all}]}. - -%% Admins of this server are also admins of the MUC service: -{access, muc_admin, [{allow, admin}]}. - -%% Only accounts of the local ejabberd server can create rooms: -{access, muc_create, [{allow, local}]}. - -%% All users are allowed to use the MUC service: -{access, muc, [{allow, all}]}. - -%% In-band registration allows registration of any possible username. -%% To disable in-band registration, replace 'allow' with 'deny'. -{access, register, [{allow, all}]}. - -%% By default the frequency of account registrations from the same IP -%% is limited to 1 account every 10 minutes. To disable, specify: infinity -{registration_timeout, infinity}. - -%% Default settings for MAM. -%% To set non-standard value, replace 'default' with 'allow' or 'deny'. -%% Only user can access his/her archive by default. -%% An online user can read room's archive by default. -%% Only an owner can change settings by default. -%% Empty list (i.e. `[]`) means `[{deny, all}]`. -{access, mam_set_prefs, [{default, all}]}. -{access, mam_get_prefs, [{default, all}]}. -{access, mam_lookup_messages, [{default, all}]}. - -%% 1 command of the specified type per second. -{shaper, mam_shaper, {maxrate, 1}}. -%% This shaper is primeraly for Mnesia overload protection during stress testing. -%% The limit is 1000 operations of each type per second. -{shaper, mam_global_shaper, {maxrate, 1000}}. - -{access, mam_set_prefs_shaper, [{mam_shaper, all}]}. -{access, mam_get_prefs_shaper, [{mam_shaper, all}]}. -{access, mam_lookup_messages_shaper, [{mam_shaper, all}]}. - -{access, mam_set_prefs_global_shaper, [{mam_global_shaper, all}]}. -{access, mam_get_prefs_global_shaper, [{mam_global_shaper, all}]}. -{access, mam_lookup_messages_global_shaper, [{mam_global_shaper, all}]}. - - -%% -%% Define specific Access Rules in a virtual host. -%% -%%{host_config, "localhost", -%% [ -%% {access, c2s, [{allow, admin}, {deny, all}]}, -%% {access, register, [{deny, all}]} -%% ] -%%}. - - -%%%. ================ -%%%' DEFAULT LANGUAGE - -%% -%% language: Default language used for server messages. -%% -{language, "en"}. - -%% -%% Set a different default language in a virtual host. -%% -%%{host_config, "localhost", -%% [{language, "ru"}] -%%}. - - -%%%. ======= -%%%' MODULES - -%% -%% Modules enabled in all ejabberd virtual hosts. -%% For list of possible modules options, check documentation. -%% If module comes in two versions, like mod_last and mod_last_rdbms, -%% use only one of them. -%% -{modules, - [ - %{mod_admin_extra, [{submods, [node, accounts, sessions, vcard, - % roster, last, private, stanza, stats]}]}, - {mod_adhoc, []}, - {mod_disco, []}, - {mod_last, []}, - {mod_stream_management, [ - % default 100 - % size of a buffer of unacked messages - % {buffer_max, 100} - - % default 1 - server sends the ack request after each stanza - % {ack_freq, 1} - - % default: 600 seconds - % {resume_timeout, 600} - ]}, - {mod_muc, [ - {host, "muc.@HOST@"}, - {access, muc}, - {access_create, muc_create} - ]}, - {mod_muc_log, - [ - {outdir, "/tmp/muclogs"}, - {access_log, muc} - ]}, - {mod_offline, [fake_opt_one, fake_opt_two]}, - {mod_privacy, []}, - {mod_register, [ - %% - %% Set the minimum informational entropy for passwords. - %% - %%{password_strength, 32}, - - %% - %% After successful registration, the user receives - %% a message with this subject and body. - %% - {welcome_message, {"", ""}}, - - %% - %% When a user registers, send a notification to - %% these XMPP accounts. - %% - %%{registration_watchers, ["admin1@example.org"]}, - - %% - %% Only clients in the server machine can register accounts - %% - {ip_access, [{allow, "127.0.0.0/8"}, - {deny, "0.0.0.0/0"}]}, - - %% - %% Local c2s or remote s2s users cannot register accounts - %% - %%{access_from, deny}, - - {access, register} - ]}, - {mod_roster, []}, - {mod_sic, []}, - {mod_vcard, [ {allow_return_all, true}, -{search_all_hosts, true} -%{matches, 1}, -%{search, true}, -%{host, directory.@HOST@} -]}, - {mod_bosh, []} - - %% - %% Message Archive Management (MAM) for registered users. - %% - - %% A module for storing preferences in RDBMS (used by default). - %% Enable for private message archives. -% {mod_mam_rdbms_prefs, [pm]}, - %% Enable for multiuser message archives. -% {mod_mam_rdbms_prefs, [muc]}, - %% Enable for both private and multiuser message archives. -% {mod_mam_rdbms_prefs, [pm, muc]}, - - %% A module for storing preferences in Mnesia (recommended). - %% This module will be called each time, as a message is routed. - %% That is why, Mnesia is better for this job. -% {mod_mam_mnesia_prefs, [pm, muc]}, - - %% A back-end for storing messages. - %% Synchronious writer (used by default). - %% This writer is easy to debug, but writing performance is low. -% {mod_mam_rdbms_arch, [pm]}, - - %% Enable the module with a custom writer. -% {mod_mam_rdbms_arch, [no_writer, pm]}, - - %% A pool of asynchronious writers (recommended). - %% Messages will be grouped together based on archive id. -% {mod_mam_rdbms_async_pool_writer, [pm]}, - - %% A module for converting an archive id to an integer. - %% Extract information using RDBMS. -% {mod_mam_rdbms_user, [pm, muc]}, - - %% Cache information about users (recommended). - %% Requires mod_mam_rdbms_user or alternative. -% {mod_mam_cache_user, [pm, muc]}, - - %% Enable MAM. -% {mod_mam, []}, - - - %% - %% Message Archive Management (MAM) for multi-user chats (MUC). - %% Enable XEP-0313 for "muc.@HOST@". - %% - - %% A back-end for storing messages (default for MUC). - %% Modules mod_mam_muc_* are optimized for MUC. - %% - %% Synchronious writer (used by default for MUC). - %% This module is easy to debug, but performance is low. -% {mod_mam_muc_rdbms_arch, []}, -% {mod_mam_muc_rdbms_arch, [no_writer]}, - - %% Asynchronious writer for RDBMS (recommended for MUC). - %% Messages will be grouped and inserted all at once. -% {mod_mam_muc_rdbms_async_pool_writer, []}, - - %% Load mod_mam_rdbms_user too. - - %% Enable MAM for MUC -% {mod_mam_muc, [{host, "muc.@HOST@"}]} - - - %% - %% MAM configuration examples - %% - - %% Only MUC, no user-defined preferences, good performance. -% {mod_mam_rdbms_user, [muc]}, -% {mod_mam_cache_user, [muc]}, -% {mod_mam_muc_rdbms_arch, [no_writer]}, -% {mod_mam_muc_rdbms_async_pool_writer, []}, -% {mod_mam_muc, [{host, "muc.@HOST@"}]} - - %% Only archives for c2c messages, good performance. -% {mod_mam_rdbms_user, [pm]}, -% {mod_mam_cache_user, [pm]}, -% {mod_mam_mnesia_prefs, [pm]}, -% {mod_mam_rdbms_arch, [pm, no_writer]}, -% {mod_mam_rdbms_async_pool_writer, [pm]}, -% {mod_mam, []} - - %% Basic configuration for c2c messages, bad performance, easy to debug. -% {mod_mam_rdbms_user, [pm]}, -% {mod_mam_rdbms_prefs, [pm]}, -% {mod_mam_rdbms_arch, [pm]}, -% {mod_mam, []} - - ]}. - - -%% -%% Enable modules with custom options in a specific virtual host -%% -%%{host_config, "localhost", -%% [{ {add, modules}, -%% [ -%% {mod_some_module, []} -%% ] -%% } -%% ]}. - -%%%. -%%%' - -%%% $Id$ - -%%% Local Variables: -%%% mode: erlang -%%% End: -%%% vim: set filetype=erlang tabstop=8 foldmarker=%%%',%%%. foldmethod=marker: -%%%. diff --git a/test/ejabberd_config_SUITE_data/mongooseim.with_mod_offline.different_opts.toml b/test/ejabberd_config_SUITE_data/mongooseim.with_mod_offline.different_opts.toml new file mode 100644 index 0000000000..d0a3839f01 --- /dev/null +++ b/test/ejabberd_config_SUITE_data/mongooseim.with_mod_offline.different_opts.toml @@ -0,0 +1,5 @@ +[general] + hosts = ["localhost"] + +[modules.mod_offline] + access_max_user_messages = "max_user_offline_messages" diff --git a/test/ejabberd_config_SUITE_data/mongooseim.with_mod_offline.toml b/test/ejabberd_config_SUITE_data/mongooseim.with_mod_offline.toml new file mode 100644 index 0000000000..0ff45c6cc8 --- /dev/null +++ b/test/ejabberd_config_SUITE_data/mongooseim.with_mod_offline.toml @@ -0,0 +1,4 @@ +[general] + hosts = ["localhost"] + +[modules.mod_offline] From 345355748d41a1fb6b1633440840eb3741dac495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Fri, 23 Oct 2020 15:32:59 +0200 Subject: [PATCH 003/104] Do not use the 'cfg' format in ejabberd_listener_SUITE - Convert the files to TOML, simplify when possible. --- test/ejabberd_listener_SUITE.erl | 6 +- .../mongooseim.alt.cfg | 126 ------------------ .../mongooseim.alt.toml | 24 ++++ .../mongooseim.basic.cfg | 126 ------------------ .../mongooseim.basic.toml | 24 ++++ 5 files changed, 51 insertions(+), 255 deletions(-) delete mode 100644 test/ejabberd_listener_SUITE_data/mongooseim.alt.cfg create mode 100644 test/ejabberd_listener_SUITE_data/mongooseim.alt.toml delete mode 100644 test/ejabberd_listener_SUITE_data/mongooseim.basic.cfg create mode 100644 test/ejabberd_listener_SUITE_data/mongooseim.basic.toml diff --git a/test/ejabberd_listener_SUITE.erl b/test/ejabberd_listener_SUITE.erl index 76cc6ea6cf..5db1b26b82 100644 --- a/test/ejabberd_listener_SUITE.erl +++ b/test/ejabberd_listener_SUITE.erl @@ -145,8 +145,8 @@ tcp_port_ip() -> tcp_start_stop_reload(C) -> %% start server - copy(data(C, "mongooseim.basic.cfg"), data(C, "mongooseim.cfg")), - ejabberd_helper:start_ejabberd_with_config(C, "mongooseim.cfg"), + copy(data(C, "mongooseim.basic.toml"), data(C, "mongooseim.toml")), + ejabberd_helper:start_ejabberd_with_config(C, "mongooseim.toml"), ?assert(lists:keymember(mongooseim, 1, application:which_applications())), %% make sure all ports are open lists:map(fun assert_open/1, ?DEFAULT_PORTS), @@ -157,7 +157,7 @@ tcp_start_stop_reload(C) -> ejabberd_listener:start_listeners(), lists:map(fun assert_open/1, ?DEFAULT_PORTS), %% alternative configuration differs only in that s2s listens on 5296 instea of 5269 - copy(data(C, "mongooseim.alt.cfg"), data(C, "mongooseim.cfg")), + copy(data(C, "mongooseim.alt.toml"), data(C, "mongooseim.toml")), %% we want to make sure that connection to an unchanged port survives reload UnchPort = 5222, {ok, Sock} = gen_tcp:connect("localhost", UnchPort,[{active, false}, {packet, 2}]), diff --git a/test/ejabberd_listener_SUITE_data/mongooseim.alt.cfg b/test/ejabberd_listener_SUITE_data/mongooseim.alt.cfg deleted file mode 100644 index 9e9f2cb750..0000000000 --- a/test/ejabberd_listener_SUITE_data/mongooseim.alt.cfg +++ /dev/null @@ -1,126 +0,0 @@ -{loglevel, 3}. - -{hosts, ["localhost"] }. - -{listen, - [ - { 5280, ejabberd_cowboy, [ - {num_acceptors, 10}, - {max_connections, 1024}, - {modules, [ - {"_", "/http-bind", mod_bosh}, - {"_", "/ws-xmpp", mod_websockets}, - {"localhost", "/api", mongoose_api, [{handlers, [mongoose_api_metrics, - mongoose_api_users]}]} - ]} - ]}, - - { 5222, ejabberd_c2s, [ - {access, c2s}, - {shaper, c2s_shaper}, - {max_stanza_size, 65536} - ]}, - - { 5296, ejabberd_s2s_in, [ - {shaper, s2s_shaper}, - {max_stanza_size, 131072} - ]} - ]}. - -{s2s_default_policy, deny }. - -{outgoing_s2s_port, 5269 }. - -{sm_backend, {mnesia, []} }. - -{auth_method, internal}. - -{shaper, normal, {maxrate, 1000}}. - -{shaper, fast, {maxrate, 50000}}. - -{max_fsm_queue, 1000}. - -{acl, local, {user_regexp, ""}}. - -{access, max_user_sessions, [{10, all}]}. - -{access, max_user_offline_messages, [{5000, admin}, {100, all}]}. - -{access, local, [{allow, local}]}. - -{access, c2s, [{deny, blocked}, - {allow, all}]}. - -{access, c2s_shaper, [{none, admin}, - {normal, all}]}. - -{access, s2s_shaper, [{fast, all}]}. - -{access, muc_admin, [{allow, admin}]}. - -{access, muc_create, [{allow, local}]}. - -{access, muc, [{allow, all}]}. - -{access, register, [{allow, all}]}. - -{registration_timeout, infinity}. - -{access, mam_set_prefs, [{default, all}]}. -{access, mam_get_prefs, [{default, all}]}. -{access, mam_lookup_messages, [{default, all}]}. - -{shaper, mam_shaper, {maxrate, 1}}. -{shaper, mam_global_shaper, {maxrate, 1000}}. - -{access, mam_set_prefs_shaper, [{mam_shaper, all}]}. -{access, mam_get_prefs_shaper, [{mam_shaper, all}]}. -{access, mam_lookup_messages_shaper, [{mam_shaper, all}]}. - -{access, mam_set_prefs_global_shaper, [{mam_global_shaper, all}]}. -{access, mam_get_prefs_global_shaper, [{mam_global_shaper, all}]}. -{access, mam_lookup_messages_global_shaper, [{mam_global_shaper, all}]}. - -{language, "en"}. - -{modules, - [ - {mod_adhoc, []}, - {mod_disco, []}, - {mod_last, []}, - {mod_stream_management, []}, - {mod_muc, [ - {host, "muc.@HOST@"}, - {access, muc}, - {access_create, muc_create} - ]}, - {mod_muc_log, - [ - {outdir, "/tmp/muclogs"}, - {access_log, muc} - ]}, - {mod_privacy, []}, - {mod_register, [ - {welcome_message, {"", ""}}, - - {ip_access, [{allow, "127.0.0.0/8"}, - {deny, "0.0.0.0/0"}]}, - - {access, register} - ]}, - {mod_roster, []}, - {mod_sic, []}, - {mod_vcard, [ {allow_return_all, true}, - {search_all_hosts, true} - ]}, - {mod_bosh, []} - - ]}. - - -%%% Local Variables: -%%% mode: erlang -%%% End: -%%% vim: set filetype=erlang tabstop=8 foldmarker=%%%',%%%. foldmethod=marker: -%%%. diff --git a/test/ejabberd_listener_SUITE_data/mongooseim.alt.toml b/test/ejabberd_listener_SUITE_data/mongooseim.alt.toml new file mode 100644 index 0000000000..608ef3dbe5 --- /dev/null +++ b/test/ejabberd_listener_SUITE_data/mongooseim.alt.toml @@ -0,0 +1,24 @@ +[general] + hosts = ["localhost"] + +[[listen.http]] + port = 5280 + + [[listen.http.handlers.mod_bosh]] + host = "_" + path = "/http-bind" + + [[listen.http.handlers.mod_websockets]] + host = "_" + path = "/ws-xmpp" + + [[listen.http.handlers.mongoose_api]] + host = "localhost" + path = "/api" + handlers = ["mongoose_api_metrics", "mongoose_api_users"] + +[[listen.c2s]] + port = 5222 + +[[listen.s2s]] + port = 5296 diff --git a/test/ejabberd_listener_SUITE_data/mongooseim.basic.cfg b/test/ejabberd_listener_SUITE_data/mongooseim.basic.cfg deleted file mode 100644 index 12723c95cf..0000000000 --- a/test/ejabberd_listener_SUITE_data/mongooseim.basic.cfg +++ /dev/null @@ -1,126 +0,0 @@ -{loglevel, 3}. - -{hosts, ["localhost"] }. - -{listen, - [ - { 5280, ejabberd_cowboy, [ - {num_acceptors, 10}, - {max_connections, 1024}, - {modules, [ - {"_", "/http-bind", mod_bosh}, - {"_", "/ws-xmpp", mod_websockets}, - {"localhost", "/api", mongoose_api, [{handlers, [mongoose_api_metrics, - mongoose_api_users]}]} - ]} - ]}, - - { 5222, ejabberd_c2s, [ - {access, c2s}, - {shaper, c2s_shaper}, - {max_stanza_size, 65536} - ]}, - - { 5269, ejabberd_s2s_in, [ - {shaper, s2s_shaper}, - {max_stanza_size, 131072} - ]} - ]}. - -{s2s_default_policy, deny }. - -{outgoing_s2s_port, 5269 }. - -{sm_backend, {mnesia, []} }. - -{auth_method, internal}. - -{shaper, normal, {maxrate, 1000}}. - -{shaper, fast, {maxrate, 50000}}. - -{max_fsm_queue, 1000}. - -{acl, local, {user_regexp, ""}}. - -{access, max_user_sessions, [{10, all}]}. - -{access, max_user_offline_messages, [{5000, admin}, {100, all}]}. - -{access, local, [{allow, local}]}. - -{access, c2s, [{deny, blocked}, - {allow, all}]}. - -{access, c2s_shaper, [{none, admin}, - {normal, all}]}. - -{access, s2s_shaper, [{fast, all}]}. - -{access, muc_admin, [{allow, admin}]}. - -{access, muc_create, [{allow, local}]}. - -{access, muc, [{allow, all}]}. - -{access, register, [{allow, all}]}. - -{registration_timeout, infinity}. - -{access, mam_set_prefs, [{default, all}]}. -{access, mam_get_prefs, [{default, all}]}. -{access, mam_lookup_messages, [{default, all}]}. - -{shaper, mam_shaper, {maxrate, 1}}. -{shaper, mam_global_shaper, {maxrate, 1000}}. - -{access, mam_set_prefs_shaper, [{mam_shaper, all}]}. -{access, mam_get_prefs_shaper, [{mam_shaper, all}]}. -{access, mam_lookup_messages_shaper, [{mam_shaper, all}]}. - -{access, mam_set_prefs_global_shaper, [{mam_global_shaper, all}]}. -{access, mam_get_prefs_global_shaper, [{mam_global_shaper, all}]}. -{access, mam_lookup_messages_global_shaper, [{mam_global_shaper, all}]}. - -{language, "en"}. - -{modules, - [ - {mod_adhoc, []}, - {mod_disco, []}, - {mod_last, []}, - {mod_stream_management, []}, - {mod_muc, [ - {host, "muc.@HOST@"}, - {access, muc}, - {access_create, muc_create} - ]}, - {mod_muc_log, - [ - {outdir, "/tmp/muclogs"}, - {access_log, muc} - ]}, - {mod_privacy, []}, - {mod_register, [ - {welcome_message, {"", ""}}, - - {ip_access, [{allow, "127.0.0.0/8"}, - {deny, "0.0.0.0/0"}]}, - - {access, register} - ]}, - {mod_roster, []}, - {mod_sic, []}, - {mod_vcard, [ {allow_return_all, true}, - {search_all_hosts, true} - ]}, - {mod_bosh, []} - - ]}. - - -%%% Local Variables: -%%% mode: erlang -%%% End: -%%% vim: set filetype=erlang tabstop=8 foldmarker=%%%',%%%. foldmethod=marker: -%%%. diff --git a/test/ejabberd_listener_SUITE_data/mongooseim.basic.toml b/test/ejabberd_listener_SUITE_data/mongooseim.basic.toml new file mode 100644 index 0000000000..37ca75769d --- /dev/null +++ b/test/ejabberd_listener_SUITE_data/mongooseim.basic.toml @@ -0,0 +1,24 @@ +[general] + hosts = ["localhost"] + +[[listen.http]] + port = 5280 + + [[listen.http.handlers.mod_bosh]] + host = "_" + path = "/http-bind" + + [[listen.http.handlers.mod_websockets]] + host = "_" + path = "/ws-xmpp" + + [[listen.http.handlers.mongoose_api]] + host = "localhost" + path = "/api" + handlers = ["mongoose_api_metrics", "mongoose_api_users"] + +[[listen.c2s]] + port = 5222 + +[[listen.s2s]] + port = 5269 From c1b143f2e64d07bbfd1ffc1183a9d8c1fb311f98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Fri, 23 Oct 2020 15:35:04 +0200 Subject: [PATCH 004/104] Do not use the 'cfg' format in cowboy_SUITE This test suite checks the 'mod_cowboy' handler for the HTTP listener. It is NOT supported in the new TOML configuration format. There is a need for a final decision here: 1. Either drop this functionality entirely (and remove the code) 2. Or implement the TOML support for it (and keep the code) The changes in this commit are reduced to minimum: the reload test is removed with the config files that are impossible to convert to TOML. --- test/cowboy_SUITE.erl | 35 +- .../cowboy_SUITE_data/mongooseim.onlyhttp.cfg | 625 ------------------ test/cowboy_SUITE_data/mongooseim.onlyws.cfg | 625 ------------------ 3 files changed, 1 insertion(+), 1284 deletions(-) delete mode 100644 test/cowboy_SUITE_data/mongooseim.onlyhttp.cfg delete mode 100644 test/cowboy_SUITE_data/mongooseim.onlyws.cfg diff --git a/test/cowboy_SUITE.erl b/test/cowboy_SUITE.erl index 5deac5dcce..d5f7d5e3ce 100644 --- a/test/cowboy_SUITE.erl +++ b/test/cowboy_SUITE.erl @@ -32,8 +32,7 @@ all() -> [{group, routing}, - start_cowboy_returns_error_eaddrinuse, - conf_reload]. + start_cowboy_returns_error_eaddrinuse]. groups() -> [{routing, [sequence], [http_requests, @@ -223,38 +222,6 @@ start_cowboy_returns_error_eaddrinuse(_C) -> Result = ejabberd_cowboy:start_cowboy(a_ref_2, Opts), {error, eaddrinuse} = Result. -conf_reload(Config) -> - %% Given initial configuration - HTTPHost = "http://localhost:5280", - Path = <<"/">>, - Method = "GET", - Headers1 = [], - Headers2 = ws_headers(<<"xmpp">>), - Body = [], - - copy(data(Config, "mongooseim.onlyhttp.cfg"), data(Config, "mongooseim.cfg")), - start_ejabberd_with_config(Config, "mongooseim.cfg"), - - %% When making requests for http and ws - Response1 = execute_request(HTTPHost, Path, Method, Headers1, Body), - Response2 = execute_request(HTTPHost, Path, Method, Headers2, Body), - - %% Then http returns 200 and ws returns 404 - assert_status_code(Response1, 200), - assert_status_code(Response2, 404), - - %% Given new configuration - copy(data(Config, "mongooseim.onlyws.cfg"), data(Config, "mongooseim.cfg")), - ejabberd_config:reload_local(), - - %% When making requests for http and ws - Response3 = execute_request(HTTPHost, Path, Method, Headers1, Body), - - %% Then http returns 404 and ws works fine - assert_status_code(Response3, 404), - - ok = stop_ejabberd(). - %%-------------------------------------------------------------------- %% Helpers %%-------------------------------------------------------------------- diff --git a/test/cowboy_SUITE_data/mongooseim.onlyhttp.cfg b/test/cowboy_SUITE_data/mongooseim.onlyhttp.cfg deleted file mode 100644 index 2299460878..0000000000 --- a/test/cowboy_SUITE_data/mongooseim.onlyhttp.cfg +++ /dev/null @@ -1,625 +0,0 @@ -%%% -%%% ejabberd configuration file -%%% -%%%' - -%%% The parameters used in this configuration file are explained in more detail -%%% in the ejabberd Installation and Operation Guide. -%%% Please consult the Guide in case of doubts, it is included with -%%% your copy of ejabberd, and is also available online at -%%% http://www.process-one.net/en/ejabberd/docs/ - -%%% This configuration file contains Erlang terms. -%%% In case you want to understand the syntax, here are the concepts: -%%% -%%% - The character to comment a line is % -%%% -%%% - Each term ends in a dot, for example: -%%% override_global. -%%% -%%% - A tuple has a fixed definition, its elements are -%%% enclosed in {}, and separated with commas: -%%% {loglevel, 4}. -%%% -%%% - A list can have as many elements as you want, -%%% and is enclosed in [], for example: -%%% [http_poll, web_admin, tls] -%%% -%%% Pay attention that list elements are delimited with commas, -%%% but no comma is allowed after the last list element. This will -%%% give a syntax error unlike in more lenient languages (e.g. Python). -%%% -%%% - A keyword of ejabberd is a word in lowercase. -%%% Strings are enclosed in "" and can contain spaces, dots, ... -%%% {language, "en"}. -%%% {ldap_rootdn, "dc=example,dc=com"}. -%%% -%%% - This term includes a tuple, a keyword, a list, and two strings: -%%% {hosts, ["jabber.example.net", "im.example.com"]}. -%%% -%%% - This config is preprocessed during release generation by a tool which -%%% interprets double curly braces as substitution markers, so avoid this -%%% syntax in this file (though it's valid Erlang). -%%% -%%% So this is OK (though arguably looks quite ugly): -%%% { {s2s_addr, "example-host.net"}, {127,0,0,1} }. -%%% -%%% And I can't give an example of what's not OK exactly because -%%% of this rule. -%%% - - -%%%. ======================= -%%%' OVERRIDE STORED OPTIONS - -%% -%% Override the old values stored in the database. -%% - -%% -%% Override global options (shared by all ejabberd nodes in a cluster). -%% -%%override_global. - -%% -%% Override local options (specific for this particular ejabberd node). -%% -%%override_local. - -%% -%% Remove the Access Control Lists before new ones are added. -%% -%%override_acls. - -%%%. ========= -%%%' GDPR RELATED -%% when set to true, user data removals will be applied on all modules that implements GDPR behaviour - -%% even the disabled ones! -%% -{gdpr_removal_for_disabled_modules, false}. - -%%%. ========= -%%%' DEBUGGING - -%% -%% loglevel: Verbosity of log files generated by ejabberd. -%% 0: No ejabberd log at all (not recommended) -%% 1: Critical -%% 2: Error -%% 3: Warning -%% 4: Info -%% 5: Debug -%% -{loglevel, 3}. - -%% -%% alarms: an optional alarm handler, subscribed to system events -%% long_gc: minimum GC time in ms for long_gc alarm -%% large_heap: minimum process heap size for large_heap alarm -%% handlers: a list of alarm handlers -%% - alarms_basic_handler: logs alarms and stores a brief alarm summary -%% - alarms_folsom_handler: stores alarm details in folsom metrics -%% -%% Example: -%% {alarms, -%% [{long_gc, 10000}, -%% {large_heap, 1000000}, -%% {handlers, [alarms_basic_handler, -%% alarms_folsom_handler]}] -%% }. - -%% -%% watchdog_admins: Only useful for developers: if an ejabberd process -%% consumes a lot of memory, send live notifications to these XMPP -%% accounts. Requires alarms (see above). -%% -%%{watchdog_admins, ["bob@example.com"]}. - - -%%%. ================ -%%%' SERVED HOSTNAMES - -%% -%% hosts: Domains served by ejabberd. -%% You can define one or several, for example: -%% {hosts, ["example.net", "example.com", "example.org"]}. -%% -{hosts, ["localhost"] }. - -%% -%% route_subdomains: Delegate subdomains to other XMPP servers. -%% For example, if this ejabberd serves example.org and you want -%% to allow communication with an XMPP server called im.example.org. -%% -%%{route_subdomains, s2s}. - - -%%%. =============== -%%%' LISTENING PORTS - -%% -%% listen: The ports ejabberd will listen on, which service each is handled -%% by and what options to start it with. -%% -{listen, - [ - - { 5280, ejabberd_cowboy, [ - {num_acceptors, 10}, - {max_connections, 1024}, - %% Uncomment for HTTPS - %{cert, "priv/server.crt"}, - %{key, "priv/server.key"}, - %{key_pass, ""}, - {modules, [ - %% Modules used here should also be listed in the MODULES section. - {"_", "/[...]", mod_cowboy, [{http, dummy_http_handler}]} - ]} - ]}, - - { 5222, ejabberd_c2s, [ - - %% - %% If TLS is compiled in and you installed a SSL - %% certificate, specify the full path to the - %% file and uncomment this line: - %% - %%{certfile, "/path/to/ssl.pem"}, starttls, - %%{zlib, 10000}, - %% https://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS - %% {ciphers, "TLSv1.2:TLSv1.3"}, - {access, c2s}, - {shaper, c2s_shaper}, - {max_stanza_size, 65536} - ]}, - - - - %% - %% To enable the old SSL connection method on port 5223: - %% - %%{5223, ejabberd_c2s, [ - %% {access, c2s}, - %% {shaper, c2s_shaper}, - %% {certfile, "/path/to/ssl.pem"}, tls, - %% {max_stanza_size, 65536} - %% ]}, - - { 5269, ejabberd_s2s_in, [ - {shaper, s2s_shaper}, - {max_stanza_size, 131072} - ]} - - %% - %% ejabberd_service: Interact with external components (transports, ...) - %% - %%{8888, ejabberd_service, [ - %% {access, all}, - %% {shaper_rule, fast}, - %% {ip, {127, 0, 0, 1}}, - %% {hosts, ["icq.example.org", "sms.example.org"], - %% [{password, "secret"}] - %% } - %% ]}, - - %% - %% ejabberd_stun: Handles STUN Binding requests - %% - %%{ {3478, udp}, ejabberd_stun, []} - - ]}. - -%% -%% s2s_use_starttls: Enable STARTTLS + Dialback for S2S connections. -%% Allowed values are: false optional required required_trusted -%% You must specify a certificate file. -%% -%%{s2s_use_starttls, optional}. - -%% -%% s2s_certfile: Specify a certificate file. -%% -%%{s2s_certfile, "/path/to/ssl.pem"}. - -%% https://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS -%% {s2s_ciphers, "TLSv1.2:TLSv1.3"}. - -%% -%% domain_certfile: Specify a different certificate for each served hostname. -%% -%%{domain_certfile, "example.org", "/path/to/example_org.pem"}. -%%{domain_certfile, "example.com", "/path/to/example_com.pem"}. - -%% -%% S2S whitelist or blacklist -%% -%% Default s2s policy for undefined hosts. -%% -{s2s_default_policy, deny }. - -%% -%% Allow or deny communication with specific servers. -%% -%%{ {s2s_host, "goodhost.org"}, allow}. -%%{ {s2s_host, "badhost.org"}, deny}. - -{outgoing_s2s_port, 5269 }. - -%% -%% IP addresses predefined for specific hosts to skip DNS lookups. -%% Ports defined here take precedence over outgoing_s2s_port. -%% Examples: -%% -%% { {s2s_addr, "example-host.net"}, {127,0,0,1} }. -%% { {s2s_addr, "example-host.net"}, { {127,0,0,1}, 5269 } }. -%% { {s2s_addr, "example-host.net"}, { {127,0,0,1}, 5269 } }. - -%% -%% Outgoing S2S options -%% -%% Preferred address families (which to try first) and connect timeout -%% in milliseconds. -%% -%%{outgoing_s2s_options, [ipv4, ipv6], 10000}. - -%%%. ============== -%%%' SESSION BACKEND - -{sm_backend, {mnesia, []} }. - -%%%. ============== -%%%' AUTHENTICATION - -{auth_method, internal}. - -%%%. ============== -%%%' DATABASE SETUP - - -%%%. =============== -%%%' TRAFFIC SHAPERS - -%% -%% The "normal" shaper limits traffic speed to 1000 B/s -%% -{shaper, normal, {maxrate, 1000}}. - -%% -%% The "fast" shaper limits traffic speed to 50000 B/s -%% -{shaper, fast, {maxrate, 50000}}. - -%% -%% This option specifies the maximum number of elements in the queue -%% of the FSM. Refer to the documentation for details. -%% -{max_fsm_queue, 1000}. - - -%%%. ==================== -%%%' ACCESS CONTROL LISTS - -%% -%% The 'admin' ACL grants administrative privileges to XMPP accounts. -%% You can put here as many accounts as you want. -%% -%{acl, admin, {user, "alice", "localhost"}}. -%{acl, admin, {user, "a", "localhost"}}. - -%% -%% Blocked users -%% -%%{acl, blocked, {user, "baduser", "example.org"}}. -%%{acl, blocked, {user, "test"}}. - -%% -%% Local users: don't modify this line. -%% -{acl, local, {user_regexp, ""}}. - -%% -%% More examples of ACLs -%% -%%{acl, jabberorg, {server, "jabber.org"}}. -%%{acl, aleksey, {user, "aleksey", "jabber.ru"}}. -%%{acl, test, {user_regexp, "^test"}}. -%%{acl, test, {user_glob, "test*"}}. - -%% -%% Define specific ACLs in a virtual host. -%% -%%{host_config, "localhost", -%% [ -%% {acl, admin, {user, "bob-local", "localhost"}} -%% ] -%%}. - - -%%%. ============ -%%%' ACCESS RULES - -%% Maximum number of simultaneous sessions allowed for a single user: -{access, max_user_sessions, [{10, all}]}. - -%% Maximum number of offline messages that users can have: -{access, max_user_offline_messages, [{5000, admin}, {100, all}]}. - -%% This rule allows access only for local users: -{access, local, [{allow, local}]}. - -%% Only non-blocked users can use c2s connections: -{access, c2s, [{deny, blocked}, - {allow, all}]}. - -%% For C2S connections, all users except admins use the "normal" shaper -{access, c2s_shaper, [{none, admin}, - {normal, all}]}. - -%% All S2S connections use the "fast" shaper -{access, s2s_shaper, [{fast, all}]}. - -%% Admins of this server are also admins of the MUC service: -{access, muc_admin, [{allow, admin}]}. - -%% Only accounts of the local ejabberd server can create rooms: -{access, muc_create, [{allow, local}]}. - -%% All users are allowed to use the MUC service: -{access, muc, [{allow, all}]}. - -%% In-band registration allows registration of any possible username. -%% To disable in-band registration, replace 'allow' with 'deny'. -{access, register, [{allow, all}]}. - -%% By default the frequency of account registrations from the same IP -%% is limited to 1 account every 10 minutes. To disable, specify: infinity -{registration_timeout, infinity}. - -%% Default settings for MAM. -%% To set non-standard value, replace 'default' with 'allow' or 'deny'. -%% Only user can access his/her archive by default. -%% An online user can read room's archive by default. -%% Only an owner can change settings by default. -%% Empty list (i.e. `[]`) means `[{deny, all}]`. -{access, mam_set_prefs, [{default, all}]}. -{access, mam_get_prefs, [{default, all}]}. -{access, mam_lookup_messages, [{default, all}]}. - -%% 1 command of the specified type per second. -{shaper, mam_shaper, {maxrate, 1}}. -%% This shaper is primeraly for Mnesia overload protection during stress testing. -%% The limit is 1000 operations of each type per second. -{shaper, mam_global_shaper, {maxrate, 1000}}. - -{access, mam_set_prefs_shaper, [{mam_shaper, all}]}. -{access, mam_get_prefs_shaper, [{mam_shaper, all}]}. -{access, mam_lookup_messages_shaper, [{mam_shaper, all}]}. - -{access, mam_set_prefs_global_shaper, [{mam_global_shaper, all}]}. -{access, mam_get_prefs_global_shaper, [{mam_global_shaper, all}]}. -{access, mam_lookup_messages_global_shaper, [{mam_global_shaper, all}]}. - - -%% -%% Define specific Access Rules in a virtual host. -%% -%%{host_config, "localhost", -%% [ -%% {access, c2s, [{allow, admin}, {deny, all}]}, -%% {access, register, [{deny, all}]} -%% ] -%%}. - - -%%%. ================ -%%%' DEFAULT LANGUAGE - -%% -%% language: Default language used for server messages. -%% -{language, "en"}. - -%% -%% Set a different default language in a virtual host. -%% -%%{host_config, "localhost", -%% [{language, "ru"}] -%%}. - - -%%%. ======= -%%%' MODULES - -%% -%% Modules enabled in all ejabberd virtual hosts. -%% For list of possible modules options, check documentation. -%% If module comes in two versions, like mod_last and mod_last_rdbms, -%% use only one of them. -%% -{modules, - [ - %{mod_admin_extra, [{submods, [node, accounts, sessions, vcard, - % roster, last, private, stanza, stats]}]}, - {mod_adhoc, []}, - {mod_disco, []}, - {mod_last, []}, - {mod_stream_management, [ - % default 100 - % size of a buffer of unacked messages - % {buffer_max, 100} - - % default 1 - server sends the ack request after each stanza - % {ack_freq, 1} - - % default: 600 seconds - % {resume_timeout, 600} - ]}, - {mod_muc, [ - {host, "muc.@HOST@"}, - {access, muc}, - {access_create, muc_create} - ]}, - {mod_muc_log, - [ - {outdir, "/tmp/muclogs"}, - {access_log, muc} - ]}, - {mod_privacy, []}, - {mod_register, [ - %% - %% Set the minimum informational entropy for passwords. - %% - %%{password_strength, 32}, - - %% - %% After successful registration, the user receives - %% a message with this subject and body. - %% - {welcome_message, {"", ""}}, - - %% - %% When a user registers, send a notification to - %% these XMPP accounts. - %% - %%{registration_watchers, ["admin1@example.org"]}, - - %% - %% Only clients in the server machine can register accounts - %% - {ip_access, [{allow, "127.0.0.0/8"}, - {deny, "0.0.0.0/0"}]}, - - %% - %% Local c2s or remote s2s users cannot register accounts - %% - %%{access_from, deny}, - - {access, register} - ]}, - {mod_roster, []}, - {mod_sic, []}, - {mod_vcard, [ {allow_return_all, true}, -{search_all_hosts, true} -%{matches, 1}, -%{search, true}, -%{host, directory.@HOST@} -]} - - %% - %% Message Archive Management (MAM) for registered users. - %% - - %% A module for storing preferences in RDBMS (used by default). - %% Enable for private message archives. -% {mod_mam_rdbms_prefs, [pm]}, - %% Enable for multiuser message archives. -% {mod_mam_rdbms_prefs, [muc]}, - %% Enable for both private and multiuser message archives. -% {mod_mam_rdbms_prefs, [pm, muc]}, - - %% A module for storing preferences in Mnesia (recommended). - %% This module will be called each time, as a message is routed. - %% That is why, Mnesia is better for this job. -% {mod_mam_mnesia_prefs, [pm, muc]}, - - %% A back-end for storing messages. - %% Synchronious writer (used by default). - %% This writer is easy to debug, but writing performance is low. -% {mod_mam_rdbms_arch, [pm]}, - - %% Enable the module with a custom writer. -% {mod_mam_rdbms_arch, [no_writer, pm]}, - - %% Asynchronious writer for RDBMS (recommended). - %% Messages will be grouped and inserted all at once. -% {mod_mam_rdbms_async_writer, [pm]}, - - %% A pool of asynchronious writers (recommended). - %% Messages will be grouped together based on archive id. -% {mod_mam_rdbms_async_pool_writer, [pm]}, - - %% A module for converting an archive id to an integer. - %% Extract information using RDBMS. -% {mod_mam_rdbms_user, [pm, muc]}, - - %% Cache information about users (recommended). - %% Requires mod_mam_rdbms_user or alternative. -% {mod_mam_cache_user, [pm, muc]}, - - %% Enable MAM. -% {mod_mam, []}, - - - %% - %% Message Archive Management (MAM) for multi-user chats (MUC). - %% Enable XEP-0313 for "muc.@HOST@". - %% - - %% A back-end for storing messages (default for MUC). - %% Modules mod_mam_muc_* are optimized for MUC. - %% - %% Synchronious writer (used by default for MUC). - %% This module is easy to debug, but performance is low. -% {mod_mam_muc_rdbms_arch, []}, -% {mod_mam_muc_rdbms_arch, [no_writer]}, - - %% Asynchronious writer for RDBMS (recommended for MUC). - %% Messages will be grouped and inserted all at once. -% {mod_mam_muc_rdbms_async_writer, []}, -% {mod_mam_muc_rdbms_async_pool_writer, []}, - - %% Load mod_mam_rdbms_user too. - - %% Enable MAM for MUC -% {mod_mam_muc, [{host, "muc.@HOST@"}]} - - - %% - %% MAM configuration examples - %% - - %% Only MUC, no user-defined preferences, good performance. -% {mod_mam_rdbms_user, [muc]}, -% {mod_mam_cache_user, [muc]}, -% {mod_mam_muc_rdbms_arch, [no_writer]}, -% {mod_mam_muc_rdbms_async_pool_writer, []}, -% {mod_mam_muc, [{host, "muc.@HOST@"}]} - - %% Only archives for c2c messages, good performance. -% {mod_mam_rdbms_user, [pm]}, -% {mod_mam_cache_user, [pm]}, -% {mod_mam_mnesia_prefs, [pm]}, -% {mod_mam_rdbms_arch, [pm, no_writer]}, -% {mod_mam_rdbms_async_pool_writer, [pm]}, -% {mod_mam, []} - - %% Basic configuration for c2c messages, bad performance, easy to debug. -% {mod_mam_rdbms_user, [pm]}, -% {mod_mam_rdbms_prefs, [pm]}, -% {mod_mam_rdbms_arch, [pm]}, -% {mod_mam, []} - - ]}. - - -%% -%% Enable modules with custom options in a specific virtual host -%% -%%{host_config, "localhost", -%% [{ {add, modules}, -%% [ -%% {mod_some_module, []} -%% ] -%% } -%% ]}. - -%%%. -%%%' - -%%% $Id$ - -%%% Local Variables: -%%% mode: erlang -%%% End: -%%% vim: set filetype=erlang tabstop=8 foldmarker=%%%',%%%. foldmethod=marker: -%%%. diff --git a/test/cowboy_SUITE_data/mongooseim.onlyws.cfg b/test/cowboy_SUITE_data/mongooseim.onlyws.cfg deleted file mode 100644 index 81fd385b0d..0000000000 --- a/test/cowboy_SUITE_data/mongooseim.onlyws.cfg +++ /dev/null @@ -1,625 +0,0 @@ -%%% -%%% ejabberd configuration file -%%% -%%%' - -%%% The parameters used in this configuration file are explained in more detail -%%% in the ejabberd Installation and Operation Guide. -%%% Please consult the Guide in case of doubts, it is included with -%%% your copy of ejabberd, and is also available online at -%%% http://www.process-one.net/en/ejabberd/docs/ - -%%% This configuration file contains Erlang terms. -%%% In case you want to understand the syntax, here are the concepts: -%%% -%%% - The character to comment a line is % -%%% -%%% - Each term ends in a dot, for example: -%%% override_global. -%%% -%%% - A tuple has a fixed definition, its elements are -%%% enclosed in {}, and separated with commas: -%%% {loglevel, 4}. -%%% -%%% - A list can have as many elements as you want, -%%% and is enclosed in [], for example: -%%% [http_poll, web_admin, tls] -%%% -%%% Pay attention that list elements are delimited with commas, -%%% but no comma is allowed after the last list element. This will -%%% give a syntax error unlike in more lenient languages (e.g. Python). -%%% -%%% - A keyword of ejabberd is a word in lowercase. -%%% Strings are enclosed in "" and can contain spaces, dots, ... -%%% {language, "en"}. -%%% {ldap_rootdn, "dc=example,dc=com"}. -%%% -%%% - This term includes a tuple, a keyword, a list, and two strings: -%%% {hosts, ["jabber.example.net", "im.example.com"]}. -%%% -%%% - This config is preprocessed during release generation by a tool which -%%% interprets double curly braces as substitution markers, so avoid this -%%% syntax in this file (though it's valid Erlang). -%%% -%%% So this is OK (though arguably looks quite ugly): -%%% { {s2s_addr, "example-host.net"}, {127,0,0,1} }. -%%% -%%% And I can't give an example of what's not OK exactly because -%%% of this rule. -%%% - - -%%%. ======================= -%%%' OVERRIDE STORED OPTIONS - -%% -%% Override the old values stored in the database. -%% - -%% -%% Override global options (shared by all ejabberd nodes in a cluster). -%% -%%override_global. - -%% -%% Override local options (specific for this particular ejabberd node). -%% -%%override_local. - -%% -%% Remove the Access Control Lists before new ones are added. -%% -%%override_acls. - -%%%. ========= -%%%' GDPR RELATED -%% when set to true, user data removals will be applied on all modules that implements GDPR behaviour - -%% even the disabled ones! -%% -{gdpr_removal_for_disabled_modules, false}. - -%%%. ========= -%%%' DEBUGGING - -%% -%% loglevel: Verbosity of log files generated by ejabberd. -%% 0: No ejabberd log at all (not recommended) -%% 1: Critical -%% 2: Error -%% 3: Warning -%% 4: Info -%% 5: Debug -%% -{loglevel, 3}. - -%% -%% alarms: an optional alarm handler, subscribed to system events -%% long_gc: minimum GC time in ms for long_gc alarm -%% large_heap: minimum process heap size for large_heap alarm -%% handlers: a list of alarm handlers -%% - alarms_basic_handler: logs alarms and stores a brief alarm summary -%% - alarms_folsom_handler: stores alarm details in folsom metrics -%% -%% Example: -%% {alarms, -%% [{long_gc, 10000}, -%% {large_heap, 1000000}, -%% {handlers, [alarms_basic_handler, -%% alarms_folsom_handler]}] -%% }. - -%% -%% watchdog_admins: Only useful for developers: if an ejabberd process -%% consumes a lot of memory, send live notifications to these XMPP -%% accounts. Requires alarms (see above). -%% -%%{watchdog_admins, ["bob@example.com"]}. - - -%%%. ================ -%%%' SERVED HOSTNAMES - -%% -%% hosts: Domains served by ejabberd. -%% You can define one or several, for example: -%% {hosts, ["example.net", "example.com", "example.org"]}. -%% -{hosts, ["localhost"] }. - -%% -%% route_subdomains: Delegate subdomains to other XMPP servers. -%% For example, if this ejabberd serves example.org and you want -%% to allow communication with an XMPP server called im.example.org. -%% -%%{route_subdomains, s2s}. - - -%%%. =============== -%%%' LISTENING PORTS - -%% -%% listen: The ports ejabberd will listen on, which service each is handled -%% by and what options to start it with. -%% -{listen, - [ - - { 5280, ejabberd_cowboy, [ - {num_acceptors, 10}, - {max_connections, 1024}, - %% Uncomment for HTTPS - %{cert, "priv/server.crt"}, - %{key, "priv/server.key"}, - %{key_pass, ""}, - {modules, [ - %% Modules used here should also be listed in the MODULES section. - {"_", "/[...]", mod_cowboy, [{ws, xmpp, dummy_ws1_handler}]} - ]} - ]}, - - { 5222, ejabberd_c2s, [ - - %% - %% If TLS is compiled in and you installed a SSL - %% certificate, specify the full path to the - %% file and uncomment this line: - %% - %%{certfile, "/path/to/ssl.pem"}, starttls, - %%{zlib, 10000}, - %% https://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS - %% {ciphers, "TLSv1.2:TLSv1.3"}, - {access, c2s}, - {shaper, c2s_shaper}, - {max_stanza_size, 65536} - ]}, - - - - %% - %% To enable the old SSL connection method on port 5223: - %% - %%{5223, ejabberd_c2s, [ - %% {access, c2s}, - %% {shaper, c2s_shaper}, - %% {certfile, "/path/to/ssl.pem"}, tls, - %% {max_stanza_size, 65536} - %% ]}, - - { 5269, ejabberd_s2s_in, [ - {shaper, s2s_shaper}, - {max_stanza_size, 131072} - ]} - - %% - %% ejabberd_service: Interact with external components (transports, ...) - %% - %%{8888, ejabberd_service, [ - %% {access, all}, - %% {shaper_rule, fast}, - %% {ip, {127, 0, 0, 1}}, - %% {hosts, ["icq.example.org", "sms.example.org"], - %% [{password, "secret"}] - %% } - %% ]}, - - %% - %% ejabberd_stun: Handles STUN Binding requests - %% - %%{ {3478, udp}, ejabberd_stun, []} - - ]}. - -%% -%% s2s_use_starttls: Enable STARTTLS + Dialback for S2S connections. -%% Allowed values are: false optional required required_trusted -%% You must specify a certificate file. -%% -%%{s2s_use_starttls, optional}. - -%% -%% s2s_certfile: Specify a certificate file. -%% -%%{s2s_certfile, "/path/to/ssl.pem"}. - -%% https://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS -%% {s2s_ciphers, "TLSv1.2:TLSv1.3"}. - -%% -%% domain_certfile: Specify a different certificate for each served hostname. -%% -%%{domain_certfile, "example.org", "/path/to/example_org.pem"}. -%%{domain_certfile, "example.com", "/path/to/example_com.pem"}. - -%% -%% S2S whitelist or blacklist -%% -%% Default s2s policy for undefined hosts. -%% -{s2s_default_policy, deny }. - -%% -%% Allow or deny communication with specific servers. -%% -%%{ {s2s_host, "goodhost.org"}, allow}. -%%{ {s2s_host, "badhost.org"}, deny}. - -{outgoing_s2s_port, 5269 }. - -%% -%% IP addresses predefined for specific hosts to skip DNS lookups. -%% Ports defined here take precedence over outgoing_s2s_port. -%% Examples: -%% -%% { {s2s_addr, "example-host.net"}, {127,0,0,1} }. -%% { {s2s_addr, "example-host.net"}, { {127,0,0,1}, 5269 } }. -%% { {s2s_addr, "example-host.net"}, { {127,0,0,1}, 5269 } }. - -%% -%% Outgoing S2S options -%% -%% Preferred address families (which to try first) and connect timeout -%% in milliseconds. -%% -%%{outgoing_s2s_options, [ipv4, ipv6], 10000}. - -%%%. ============== -%%%' SESSION BACKEND - -{sm_backend, {mnesia, []} }. - -%%%. ============== -%%%' AUTHENTICATION - -{auth_method, internal}. - -%%%. ============== -%%%' DATABASE SETUP - - -%%%. =============== -%%%' TRAFFIC SHAPERS - -%% -%% The "normal" shaper limits traffic speed to 1000 B/s -%% -{shaper, normal, {maxrate, 1000}}. - -%% -%% The "fast" shaper limits traffic speed to 50000 B/s -%% -{shaper, fast, {maxrate, 50000}}. - -%% -%% This option specifies the maximum number of elements in the queue -%% of the FSM. Refer to the documentation for details. -%% -{max_fsm_queue, 1000}. - - -%%%. ==================== -%%%' ACCESS CONTROL LISTS - -%% -%% The 'admin' ACL grants administrative privileges to XMPP accounts. -%% You can put here as many accounts as you want. -%% -%{acl, admin, {user, "alice", "localhost"}}. -%{acl, admin, {user, "a", "localhost"}}. - -%% -%% Blocked users -%% -%%{acl, blocked, {user, "baduser", "example.org"}}. -%%{acl, blocked, {user, "test"}}. - -%% -%% Local users: don't modify this line. -%% -{acl, local, {user_regexp, ""}}. - -%% -%% More examples of ACLs -%% -%%{acl, jabberorg, {server, "jabber.org"}}. -%%{acl, aleksey, {user, "aleksey", "jabber.ru"}}. -%%{acl, test, {user_regexp, "^test"}}. -%%{acl, test, {user_glob, "test*"}}. - -%% -%% Define specific ACLs in a virtual host. -%% -%%{host_config, "localhost", -%% [ -%% {acl, admin, {user, "bob-local", "localhost"}} -%% ] -%%}. - - -%%%. ============ -%%%' ACCESS RULES - -%% Maximum number of simultaneous sessions allowed for a single user: -{access, max_user_sessions, [{10, all}]}. - -%% Maximum number of offline messages that users can have: -{access, max_user_offline_messages, [{5000, admin}, {100, all}]}. - -%% This rule allows access only for local users: -{access, local, [{allow, local}]}. - -%% Only non-blocked users can use c2s connections: -{access, c2s, [{deny, blocked}, - {allow, all}]}. - -%% For C2S connections, all users except admins use the "normal" shaper -{access, c2s_shaper, [{none, admin}, - {normal, all}]}. - -%% All S2S connections use the "fast" shaper -{access, s2s_shaper, [{fast, all}]}. - -%% Admins of this server are also admins of the MUC service: -{access, muc_admin, [{allow, admin}]}. - -%% Only accounts of the local ejabberd server can create rooms: -{access, muc_create, [{allow, local}]}. - -%% All users are allowed to use the MUC service: -{access, muc, [{allow, all}]}. - -%% In-band registration allows registration of any possible username. -%% To disable in-band registration, replace 'allow' with 'deny'. -{access, register, [{allow, all}]}. - -%% By default the frequency of account registrations from the same IP -%% is limited to 1 account every 10 minutes. To disable, specify: infinity -{registration_timeout, infinity}. - -%% Default settings for MAM. -%% To set non-standard value, replace 'default' with 'allow' or 'deny'. -%% Only user can access his/her archive by default. -%% An online user can read room's archive by default. -%% Only an owner can change settings by default. -%% Empty list (i.e. `[]`) means `[{deny, all}]`. -{access, mam_set_prefs, [{default, all}]}. -{access, mam_get_prefs, [{default, all}]}. -{access, mam_lookup_messages, [{default, all}]}. - -%% 1 command of the specified type per second. -{shaper, mam_shaper, {maxrate, 1}}. -%% This shaper is primeraly for Mnesia overload protection during stress testing. -%% The limit is 1000 operations of each type per second. -{shaper, mam_global_shaper, {maxrate, 1000}}. - -{access, mam_set_prefs_shaper, [{mam_shaper, all}]}. -{access, mam_get_prefs_shaper, [{mam_shaper, all}]}. -{access, mam_lookup_messages_shaper, [{mam_shaper, all}]}. - -{access, mam_set_prefs_global_shaper, [{mam_global_shaper, all}]}. -{access, mam_get_prefs_global_shaper, [{mam_global_shaper, all}]}. -{access, mam_lookup_messages_global_shaper, [{mam_global_shaper, all}]}. - - -%% -%% Define specific Access Rules in a virtual host. -%% -%%{host_config, "localhost", -%% [ -%% {access, c2s, [{allow, admin}, {deny, all}]}, -%% {access, register, [{deny, all}]} -%% ] -%%}. - - -%%%. ================ -%%%' DEFAULT LANGUAGE - -%% -%% language: Default language used for server messages. -%% -{language, "en"}. - -%% -%% Set a different default language in a virtual host. -%% -%%{host_config, "localhost", -%% [{language, "ru"}] -%%}. - - -%%%. ======= -%%%' MODULES - -%% -%% Modules enabled in all ejabberd virtual hosts. -%% For list of possible modules options, check documentation. -%% If module comes in two versions, like mod_last and mod_last_rdbms, -%% use only one of them. -%% -{modules, - [ - %{mod_admin_extra, [{submods, [node, accounts, sessions, vcard, - % roster, last, private, stanza, stats]}]}, - {mod_adhoc, []}, - {mod_disco, []}, - {mod_last, []}, - {mod_stream_management, [ - % default 100 - % size of a buffer of unacked messages - % {buffer_max, 100} - - % default 1 - server sends the ack request after each stanza - % {ack_freq, 1} - - % default: 600 seconds - % {resume_timeout, 600} - ]}, - {mod_muc, [ - {host, "muc.@HOST@"}, - {access, muc}, - {access_create, muc_create} - ]}, - {mod_muc_log, - [ - {outdir, "/tmp/muclogs"}, - {access_log, muc} - ]}, - {mod_privacy, []}, - {mod_register, [ - %% - %% Set the minimum informational entropy for passwords. - %% - %%{password_strength, 32}, - - %% - %% After successful registration, the user receives - %% a message with this subject and body. - %% - {welcome_message, {"", ""}}, - - %% - %% When a user registers, send a notification to - %% these XMPP accounts. - %% - %%{registration_watchers, ["admin1@example.org"]}, - - %% - %% Only clients in the server machine can register accounts - %% - {ip_access, [{allow, "127.0.0.0/8"}, - {deny, "0.0.0.0/0"}]}, - - %% - %% Local c2s or remote s2s users cannot register accounts - %% - %%{access_from, deny}, - - {access, register} - ]}, - {mod_roster, []}, - {mod_sic, []}, - {mod_vcard, [ {allow_return_all, true}, -{search_all_hosts, true} -%{matches, 1}, -%{search, true}, -%{host, directory.@HOST@} -]} - - %% - %% Message Archive Management (MAM) for registered users. - %% - - %% A module for storing preferences in RDBMS (used by default). - %% Enable for private message archives. -% {mod_mam_rdbms_prefs, [pm]}, - %% Enable for multiuser message archives. -% {mod_mam_rdbms_prefs, [muc]}, - %% Enable for both private and multiuser message archives. -% {mod_mam_rdbms_prefs, [pm, muc]}, - - %% A module for storing preferences in Mnesia (recommended). - %% This module will be called each time, as a message is routed. - %% That is why, Mnesia is better for this job. -% {mod_mam_mnesia_prefs, [pm, muc]}, - - %% A back-end for storing messages. - %% Synchronious writer (used by default). - %% This writer is easy to debug, but writing performance is low. -% {mod_mam_rdbms_arch, [pm]}, - - %% Enable the module with a custom writer. -% {mod_mam_rdbms_arch, [no_writer, pm]}, - - %% Asynchronious writer for RDBMS (recommended). - %% Messages will be grouped and inserted all at once. -% {mod_mam_rdbms_async_writer, [pm]}, - - %% A pool of asynchronious writers (recommended). - %% Messages will be grouped together based on archive id. -% {mod_mam_rdbms_async_pool_writer, [pm]}, - - %% A module for converting an archive id to an integer. - %% Extract information using RDBMS. -% {mod_mam_rdbms_user, [pm, muc]}, - - %% Cache information about users (recommended). - %% Requires mod_mam_rdbms_user or alternative. -% {mod_mam_cache_user, [pm, muc]}, - - %% Enable MAM. -% {mod_mam, []}, - - - %% - %% Message Archive Management (MAM) for multi-user chats (MUC). - %% Enable XEP-0313 for "muc.@HOST@". - %% - - %% A back-end for storing messages (default for MUC). - %% Modules mod_mam_muc_* are optimized for MUC. - %% - %% Synchronious writer (used by default for MUC). - %% This module is easy to debug, but performance is low. -% {mod_mam_muc_rdbms_arch, []}, -% {mod_mam_muc_rdbms_arch, [no_writer]}, - - %% Asynchronious writer for RDBMS (recommended for MUC). - %% Messages will be grouped and inserted all at once. -% {mod_mam_muc_rdbms_async_writer, []}, -% {mod_mam_muc_rdbms_async_pool_writer, []}, - - %% Load mod_mam_rdbms_user too. - - %% Enable MAM for MUC -% {mod_mam_muc, [{host, "muc.@HOST@"}]} - - - %% - %% MAM configuration examples - %% - - %% Only MUC, no user-defined preferences, good performance. -% {mod_mam_rdbms_user, [muc]}, -% {mod_mam_cache_user, [muc]}, -% {mod_mam_muc_rdbms_arch, [no_writer]}, -% {mod_mam_muc_rdbms_async_pool_writer, []}, -% {mod_mam_muc, [{host, "muc.@HOST@"}]} - - %% Only archives for c2c messages, good performance. -% {mod_mam_rdbms_user, [pm]}, -% {mod_mam_cache_user, [pm]}, -% {mod_mam_mnesia_prefs, [pm]}, -% {mod_mam_rdbms_arch, [pm, no_writer]}, -% {mod_mam_rdbms_async_pool_writer, [pm]}, -% {mod_mam, []} - - %% Basic configuration for c2c messages, bad performance, easy to debug. -% {mod_mam_rdbms_user, [pm]}, -% {mod_mam_rdbms_prefs, [pm]}, -% {mod_mam_rdbms_arch, [pm]}, -% {mod_mam, []} - - ]}. - - -%% -%% Enable modules with custom options in a specific virtual host -%% -%%{host_config, "localhost", -%% [{ {add, modules}, -%% [ -%% {mod_some_module, []} -%% ] -%% } -%% ]}. - -%%%. -%%%' - -%%% $Id$ - -%%% Local Variables: -%%% mode: erlang -%%% End: -%%% vim: set filetype=erlang tabstop=8 foldmarker=%%%',%%%. foldmethod=marker: -%%%. From ef5eb332dabb12f21eb347c1602896de7aebe421 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Fri, 23 Oct 2020 15:42:30 +0200 Subject: [PATCH 005/104] Do not use the 'cfg' format in revproxy_SUITE This test suite checks the functionality of mod_revproxy, which requires the 'mod_cowboy' handler for the HTTP listener. It is NOT supported in the new TOML configuration format. Similarly to 'mod_cowboy', this code should be either removed or the missing TOML support should be added. As there is no final decision yet (and to keep the steps small), the changes in this commit are reduced to minimum: the reload test is removed with the config files that are impossible to convert to TOML. --- test/revproxy_SUITE.erl | 35 +- .../mongooseim.norules.cfg | 625 ----------------- .../mongooseim.onerule.cfg | 628 ------------------ 3 files changed, 1 insertion(+), 1287 deletions(-) delete mode 100644 test/revproxy_SUITE_data/mongooseim.norules.cfg delete mode 100644 test/revproxy_SUITE_data/mongooseim.onerule.cfg diff --git a/test/revproxy_SUITE.erl b/test/revproxy_SUITE.erl index d3a240151b..55be19e9b0 100644 --- a/test/revproxy_SUITE.erl +++ b/test/revproxy_SUITE.erl @@ -34,8 +34,7 @@ all() -> [{group, compile_routes}, {group, match_routes}, {group, generate_upstream}, - {group, requests_http}, - conf_reload]. + {group, requests_http}]. groups() -> [{compile_routes, [sequence], [compile_example_routes, @@ -145,38 +144,6 @@ example_dynamic_compile(_Config) -> %% Then Expected = mod_revproxy_dynamic:rules(). -%%-------------------------------------------------------------------- -%% Configuration reload test -%%-------------------------------------------------------------------- -conf_reload(Config) -> - %% Given initial configuration - Host = "http://localhost:5280", - Path = <<"/">>, - Method = "GET", - Headers = [], - Body = [], - - copy(data("mongooseim.onerule.cfg", Config), data("mongooseim.cfg", Config)), - start_ejabberd_with_config(Config, "mongooseim.cfg"), - - %% When making request for http - Response1 = execute_request(Host, Path, Method, Headers, Body), - - %% Then it returns 200 - true = is_status_code(Response1, 200), - - %% Given new configuration - copy(data("mongooseim.norules.cfg", Config), data("mongooseim.cfg", Config)), - ejabberd_config:reload_local(), - - %% When request is replayed - Response2 = execute_request(Host, Path, Method, Headers, Body), - - %% Then it returns 404 - true = is_status_code(Response2, 404), - - ok = stop_ejabberd(). - %%-------------------------------------------------------------------- %% HTTP requests tests %%-------------------------------------------------------------------- diff --git a/test/revproxy_SUITE_data/mongooseim.norules.cfg b/test/revproxy_SUITE_data/mongooseim.norules.cfg deleted file mode 100644 index 131eee1089..0000000000 --- a/test/revproxy_SUITE_data/mongooseim.norules.cfg +++ /dev/null @@ -1,625 +0,0 @@ -%%% -%%% ejabberd configuration file -%%% -%%%' - -%%% The parameters used in this configuration file are explained in more detail -%%% in the ejabberd Installation and Operation Guide. -%%% Please consult the Guide in case of doubts, it is included with -%%% your copy of ejabberd, and is also available online at -%%% http://www.process-one.net/en/ejabberd/docs/ - -%%% This configuration file contains Erlang terms. -%%% In case you want to understand the syntax, here are the concepts: -%%% -%%% - The character to comment a line is % -%%% -%%% - Each term ends in a dot, for example: -%%% override_global. -%%% -%%% - A tuple has a fixed definition, its elements are -%%% enclosed in {}, and separated with commas: -%%% {loglevel, 4}. -%%% -%%% - A list can have as many elements as you want, -%%% and is enclosed in [], for example: -%%% [http_poll, web_admin, tls] -%%% -%%% Pay attention that list elements are delimited with commas, -%%% but no comma is allowed after the last list element. This will -%%% give a syntax error unlike in more lenient languages (e.g. Python). -%%% -%%% - A keyword of ejabberd is a word in lowercase. -%%% Strings are enclosed in "" and can contain spaces, dots, ... -%%% {language, "en"}. -%%% {ldap_rootdn, "dc=example,dc=com"}. -%%% -%%% - This term includes a tuple, a keyword, a list, and two strings: -%%% {hosts, ["jabber.example.net", "im.example.com"]}. -%%% -%%% - This config is preprocessed during release generation by a tool which -%%% interprets double curly braces as substitution markers, so avoid this -%%% syntax in this file (though it's valid Erlang). -%%% -%%% So this is OK (though arguably looks quite ugly): -%%% { {s2s_addr, "example-host.net"}, {127,0,0,1} }. -%%% -%%% And I can't give an example of what's not OK exactly because -%%% of this rule. -%%% - - -%%%. ======================= -%%%' OVERRIDE STORED OPTIONS - -%% -%% Override the old values stored in the database. -%% - -%% -%% Override global options (shared by all ejabberd nodes in a cluster). -%% -%%override_global. - -%% -%% Override local options (specific for this particular ejabberd node). -%% -%%override_local. - -%% -%% Remove the Access Control Lists before new ones are added. -%% -%%override_acls. - -%%%. ========= -%%%' GDPR RELATED -%% when set to true, user data removals will be applied on all modules that implements GDPR behaviour - -%% even the disabled ones! -%% -{gdpr_removal_for_disabled_modules, false}. - -%%%. ========= -%%%' DEBUGGING - -%% -%% loglevel: Verbosity of log files generated by ejabberd. -%% 0: No ejabberd log at all (not recommended) -%% 1: Critical -%% 2: Error -%% 3: Warning -%% 4: Info -%% 5: Debug -%% -{loglevel, 3}. - -%% -%% alarms: an optional alarm handler, subscribed to system events -%% long_gc: minimum GC time in ms for long_gc alarm -%% large_heap: minimum process heap size for large_heap alarm -%% handlers: a list of alarm handlers -%% - alarms_basic_handler: logs alarms and stores a brief alarm summary -%% - alarms_folsom_handler: stores alarm details in folsom metrics -%% -%% Example: -%% {alarms, -%% [{long_gc, 10000}, -%% {large_heap, 1000000}, -%% {handlers, [alarms_basic_handler, -%% alarms_folsom_handler]}] -%% }. - -%% -%% watchdog_admins: Only useful for developers: if an ejabberd process -%% consumes a lot of memory, send live notifications to these XMPP -%% accounts. Requires alarms (see above). -%% -%%{watchdog_admins, ["bob@example.com"]}. - - -%%%. ================ -%%%' SERVED HOSTNAMES - -%% -%% hosts: Domains served by ejabberd. -%% You can define one or several, for example: -%% {hosts, ["example.net", "example.com", "example.org"]}. -%% -{hosts, ["localhost"] }. - -%% -%% route_subdomains: Delegate subdomains to other XMPP servers. -%% For example, if this ejabberd serves example.org and you want -%% to allow communication with an XMPP server called im.example.org. -%% -%%{route_subdomains, s2s}. - - -%%%. =============== -%%%' LISTENING PORTS - -%% -%% listen: The ports ejabberd will listen on, which service each is handled -%% by and what options to start it with. -%% -{listen, - [ - - { 5280, ejabberd_cowboy, [ - {num_acceptors, 10}, - {max_connections, 1024}, - %% Uncomment for HTTPS - %{cert, "priv/server.crt"}, - %{key, "priv/server.key"}, - %{key_pass, ""}, - {modules, [ - %% Modules used here should also be listed in the MODULES section. - {"_", "/[...]", mod_cowboy, [{http, mod_revproxy, []}]} - ]} - ]}, - - { 5222, ejabberd_c2s, [ - - %% - %% If TLS is compiled in and you installed a SSL - %% certificate, specify the full path to the - %% file and uncomment this line: - %% - %%{certfile, "/path/to/ssl.pem"}, starttls, - %%{zlib, 10000}, - %% https://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS - %% {ciphers, "TLSv1.2:TLSv1.3"}, - {access, c2s}, - {shaper, c2s_shaper}, - {max_stanza_size, 65536} - ]}, - - - - %% - %% To enable the old SSL connection method on port 5223: - %% - %%{5223, ejabberd_c2s, [ - %% {access, c2s}, - %% {shaper, c2s_shaper}, - %% {certfile, "/path/to/ssl.pem"}, tls, - %% {max_stanza_size, 65536} - %% ]}, - - { 5269, ejabberd_s2s_in, [ - {shaper, s2s_shaper}, - {max_stanza_size, 131072} - ]} - - %% - %% ejabberd_service: Interact with external components (transports, ...) - %% - %%{8888, ejabberd_service, [ - %% {access, all}, - %% {shaper_rule, fast}, - %% {ip, {127, 0, 0, 1}}, - %% {hosts, ["icq.example.org", "sms.example.org"], - %% [{password, "secret"}] - %% } - %% ]}, - - %% - %% ejabberd_stun: Handles STUN Binding requests - %% - %%{ {3478, udp}, ejabberd_stun, []} - - ]}. - -%% -%% s2s_use_starttls: Enable STARTTLS + Dialback for S2S connections. -%% Allowed values are: false optional required required_trusted -%% You must specify a certificate file. -%% -%%{s2s_use_starttls, optional}. - -%% -%% s2s_certfile: Specify a certificate file. -%% -%%{s2s_certfile, "/path/to/ssl.pem"}. - -%% https://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS -%% {s2s_ciphers, "TLSv1.2:TLSv1.3"}. - -%% -%% domain_certfile: Specify a different certificate for each served hostname. -%% -%%{domain_certfile, "example.org", "/path/to/example_org.pem"}. -%%{domain_certfile, "example.com", "/path/to/example_com.pem"}. - -%% -%% S2S whitelist or blacklist -%% -%% Default s2s policy for undefined hosts. -%% -{s2s_default_policy, deny }. - -%% -%% Allow or deny communication with specific servers. -%% -%%{ {s2s_host, "goodhost.org"}, allow}. -%%{ {s2s_host, "badhost.org"}, deny}. - -{outgoing_s2s_port, 5269 }. - -%% -%% IP addresses predefined for specific hosts to skip DNS lookups. -%% Ports defined here take precedence over outgoing_s2s_port. -%% Examples: -%% -%% { {s2s_addr, "example-host.net"}, {127,0,0,1} }. -%% { {s2s_addr, "example-host.net"}, { {127,0,0,1}, 5269 } }. -%% { {s2s_addr, "example-host.net"}, { {127,0,0,1}, 5269 } }. - -%% -%% Outgoing S2S options -%% -%% Preferred address families (which to try first) and connect timeout -%% in milliseconds. -%% -%%{outgoing_s2s_options, [ipv4, ipv6], 10000}. - -%%%. ============== -%%%' SESSION BACKEND - -{sm_backend, {mnesia, []} }. - -%%%. ============== -%%%' AUTHENTICATION - -{auth_method, internal}. - -%%%. ============== -%%%' DATABASE SETUP - - -%%%. =============== -%%%' TRAFFIC SHAPERS - -%% -%% The "normal" shaper limits traffic speed to 1000 B/s -%% -{shaper, normal, {maxrate, 1000}}. - -%% -%% The "fast" shaper limits traffic speed to 50000 B/s -%% -{shaper, fast, {maxrate, 50000}}. - -%% -%% This option specifies the maximum number of elements in the queue -%% of the FSM. Refer to the documentation for details. -%% -{max_fsm_queue, 1000}. - - -%%%. ==================== -%%%' ACCESS CONTROL LISTS - -%% -%% The 'admin' ACL grants administrative privileges to XMPP accounts. -%% You can put here as many accounts as you want. -%% -%{acl, admin, {user, "alice", "localhost"}}. -%{acl, admin, {user, "a", "localhost"}}. - -%% -%% Blocked users -%% -%%{acl, blocked, {user, "baduser", "example.org"}}. -%%{acl, blocked, {user, "test"}}. - -%% -%% Local users: don't modify this line. -%% -{acl, local, {user_regexp, ""}}. - -%% -%% More examples of ACLs -%% -%%{acl, jabberorg, {server, "jabber.org"}}. -%%{acl, aleksey, {user, "aleksey", "jabber.ru"}}. -%%{acl, test, {user_regexp, "^test"}}. -%%{acl, test, {user_glob, "test*"}}. - -%% -%% Define specific ACLs in a virtual host. -%% -%%{host_config, "localhost", -%% [ -%% {acl, admin, {user, "bob-local", "localhost"}} -%% ] -%%}. - - -%%%. ============ -%%%' ACCESS RULES - -%% Maximum number of simultaneous sessions allowed for a single user: -{access, max_user_sessions, [{10, all}]}. - -%% Maximum number of offline messages that users can have: -{access, max_user_offline_messages, [{5000, admin}, {100, all}]}. - -%% This rule allows access only for local users: -{access, local, [{allow, local}]}. - -%% Only non-blocked users can use c2s connections: -{access, c2s, [{deny, blocked}, - {allow, all}]}. - -%% For C2S connections, all users except admins use the "normal" shaper -{access, c2s_shaper, [{none, admin}, - {normal, all}]}. - -%% All S2S connections use the "fast" shaper -{access, s2s_shaper, [{fast, all}]}. - -%% Admins of this server are also admins of the MUC service: -{access, muc_admin, [{allow, admin}]}. - -%% Only accounts of the local ejabberd server can create rooms: -{access, muc_create, [{allow, local}]}. - -%% All users are allowed to use the MUC service: -{access, muc, [{allow, all}]}. - -%% In-band registration allows registration of any possible username. -%% To disable in-band registration, replace 'allow' with 'deny'. -{access, register, [{allow, all}]}. - -%% By default the frequency of account registrations from the same IP -%% is limited to 1 account every 10 minutes. To disable, specify: infinity -{registration_timeout, infinity}. - -%% Default settings for MAM. -%% To set non-standard value, replace 'default' with 'allow' or 'deny'. -%% Only user can access his/her archive by default. -%% An online user can read room's archive by default. -%% Only an owner can change settings by default. -%% Empty list (i.e. `[]`) means `[{deny, all}]`. -{access, mam_set_prefs, [{default, all}]}. -{access, mam_get_prefs, [{default, all}]}. -{access, mam_lookup_messages, [{default, all}]}. - -%% 1 command of the specified type per second. -{shaper, mam_shaper, {maxrate, 1}}. -%% This shaper is primeraly for Mnesia overload protection during stress testing. -%% The limit is 1000 operations of each type per second. -{shaper, mam_global_shaper, {maxrate, 1000}}. - -{access, mam_set_prefs_shaper, [{mam_shaper, all}]}. -{access, mam_get_prefs_shaper, [{mam_shaper, all}]}. -{access, mam_lookup_messages_shaper, [{mam_shaper, all}]}. - -{access, mam_set_prefs_global_shaper, [{mam_global_shaper, all}]}. -{access, mam_get_prefs_global_shaper, [{mam_global_shaper, all}]}. -{access, mam_lookup_messages_global_shaper, [{mam_global_shaper, all}]}. - - -%% -%% Define specific Access Rules in a virtual host. -%% -%%{host_config, "localhost", -%% [ -%% {access, c2s, [{allow, admin}, {deny, all}]}, -%% {access, register, [{deny, all}]} -%% ] -%%}. - - -%%%. ================ -%%%' DEFAULT LANGUAGE - -%% -%% language: Default language used for server messages. -%% -{language, "en"}. - -%% -%% Set a different default language in a virtual host. -%% -%%{host_config, "localhost", -%% [{language, "ru"}] -%%}. - - -%%%. ======= -%%%' MODULES - -%% -%% Modules enabled in all ejabberd virtual hosts. -%% For list of possible modules options, check documentation. -%% If module comes in two versions, like mod_last and mod_last_rdbms, -%% use only one of them. -%% -{modules, - [ - {mod_revproxy, []}, - %{mod_admin_extra, [{submods, [node, accounts, sessions, vcard, - % roster, last, private, stanza, stats]}]}, - {mod_adhoc, []}, - {mod_disco, []}, - {mod_last, []}, - {mod_stream_management, [ - % default 100 - % size of a buffer of unacked messages - % {buffer_max, 100} - - % default 1 - server sends the ack request after each stanza - % {ack_freq, 1} - - % default: 600 seconds - % {resume_timeout, 600} - ]}, - {mod_muc, [ - {host, "muc.@HOST@"}, - {access, muc}, - {access_create, muc_create} - ]}, - {mod_muc_log, - [ - {outdir, "/tmp/muclogs"}, - {access_log, muc} - ]}, - {mod_privacy, []}, - {mod_register, [ - %% - %% Set the minimum informational entropy for passwords. - %% - %%{password_strength, 32}, - - %% - %% After successful registration, the user receives - %% a message with this subject and body. - %% - {welcome_message, {"", ""}}, - - %% - %% When a user registers, send a notification to - %% these XMPP accounts. - %% - %%{registration_watchers, ["admin1@example.org"]}, - - %% - %% Only clients in the server machine can register accounts - %% - {ip_access, [{allow, "127.0.0.0/8"}, - {deny, "0.0.0.0/0"}]}, - - %% - %% Local c2s or remote s2s users cannot register accounts - %% - %%{access_from, deny}, - - {access, register} - ]}, - {mod_roster, []}, - {mod_sic, []}, - {mod_vcard, [ {allow_return_all, true}, -{search_all_hosts, true} -%{matches, 1}, -%{search, true}, -%{host, directory.@HOST@} -]} - %% - %% Message Archive Management (MAM) for registered users. - %% - - %% A module for storing preferences in RDBMS (used by default). - %% Enable for private message archives. -% {mod_mam_rdbms_prefs, [pm]}, - %% Enable for multiuser message archives. -% {mod_mam_rdbms_prefs, [muc]}, - %% Enable for both private and multiuser message archives. -% {mod_mam_rdbms_prefs, [pm, muc]}, - - %% A module for storing preferences in Mnesia (recommended). - %% This module will be called each time, as a message is routed. - %% That is why, Mnesia is better for this job. -% {mod_mam_mnesia_prefs, [pm, muc]}, - - %% A back-end for storing messages. - %% Synchronious writer (used by default). - %% This writer is easy to debug, but writing performance is low. -% {mod_mam_rdbms_arch, [pm]}, - - %% Enable the module with a custom writer. -% {mod_mam_rdbms_arch, [no_writer, pm]}, - - %% Asynchronious writer for RDBMS (recommended). - %% Messages will be grouped and inserted all at once. -% {mod_mam_rdbms_async_writer, [pm]}, - - %% A pool of asynchronious writers (recommended). - %% Messages will be grouped together based on archive id. -% {mod_mam_rdbms_async_pool_writer, [pm]}, - - %% A module for converting an archive id to an integer. - %% Extract information using RDBMS. -% {mod_mam_rdbms_user, [pm, muc]}, - - %% Cache information about users (recommended). - %% Requires mod_mam_rdbms_user or alternative. -% {mod_mam_cache_user, [pm, muc]}, - - %% Enable MAM. -% {mod_mam, []}, - - - %% - %% Message Archive Management (MAM) for multi-user chats (MUC). - %% Enable XEP-0313 for "muc.@HOST@". - %% - - %% A back-end for storing messages (default for MUC). - %% Modules mod_mam_muc_* are optimized for MUC. - %% - %% Synchronious writer (used by default for MUC). - %% This module is easy to debug, but performance is low. -% {mod_mam_muc_rdbms_arch, []}, -% {mod_mam_muc_rdbms_arch, [no_writer]}, - - %% Asynchronious writer for RDBMS (recommended for MUC). - %% Messages will be grouped and inserted all at once. -% {mod_mam_muc_rdbms_async_writer, []}, -% {mod_mam_muc_rdbms_async_pool_writer, []}, - - %% Load mod_mam_rdbms_user too. - - %% Enable MAM for MUC -% {mod_mam_muc, [{host, "muc.@HOST@"}]} - - - %% - %% MAM configuration examples - %% - - %% Only MUC, no user-defined preferences, good performance. -% {mod_mam_rdbms_user, [muc]}, -% {mod_mam_cache_user, [muc]}, -% {mod_mam_muc_rdbms_arch, [no_writer]}, -% {mod_mam_muc_rdbms_async_pool_writer, []}, -% {mod_mam_muc, [{host, "muc.@HOST@"}]} - - %% Only archives for c2c messages, good performance. -% {mod_mam_rdbms_user, [pm]}, -% {mod_mam_cache_user, [pm]}, -% {mod_mam_mnesia_prefs, [pm]}, -% {mod_mam_rdbms_arch, [pm, no_writer]}, -% {mod_mam_rdbms_async_pool_writer, [pm]}, -% {mod_mam, []} - - %% Basic configuration for c2c messages, bad performance, easy to debug. -% {mod_mam_rdbms_user, [pm]}, -% {mod_mam_rdbms_prefs, [pm]}, -% {mod_mam_rdbms_arch, [pm]}, -% {mod_mam, []} - - ]}. - - -%% -%% Enable modules with custom options in a specific virtual host -%% -%%{host_config, "localhost", -%% [{ {add, modules}, -%% [ -%% {mod_some_module, []} -%% ] -%% } -%% ]}. - -%%%. -%%%' - -%%% $Id$ - -%%% Local Variables: -%%% mode: erlang -%%% End: -%%% vim: set filetype=erlang tabstop=8 foldmarker=%%%',%%%. foldmethod=marker: -%%%. diff --git a/test/revproxy_SUITE_data/mongooseim.onerule.cfg b/test/revproxy_SUITE_data/mongooseim.onerule.cfg deleted file mode 100644 index 6a547b7ea0..0000000000 --- a/test/revproxy_SUITE_data/mongooseim.onerule.cfg +++ /dev/null @@ -1,628 +0,0 @@ -%%% -%%% ejabberd configuration file -%%% -%%%' - -%%% The parameters used in this configuration file are explained in more detail -%%% in the ejabberd Installation and Operation Guide. -%%% Please consult the Guide in case of doubts, it is included with -%%% your copy of ejabberd, and is also available online at -%%% http://www.process-one.net/en/ejabberd/docs/ - -%%% This configuration file contains Erlang terms. -%%% In case you want to understand the syntax, here are the concepts: -%%% -%%% - The character to comment a line is % -%%% -%%% - Each term ends in a dot, for example: -%%% override_global. -%%% -%%% - A tuple has a fixed definition, its elements are -%%% enclosed in {}, and separated with commas: -%%% {loglevel, 4}. -%%% -%%% - A list can have as many elements as you want, -%%% and is enclosed in [], for example: -%%% [http_poll, web_admin, tls] -%%% -%%% Pay attention that list elements are delimited with commas, -%%% but no comma is allowed after the last list element. This will -%%% give a syntax error unlike in more lenient languages (e.g. Python). -%%% -%%% - A keyword of ejabberd is a word in lowercase. -%%% Strings are enclosed in "" and can contain spaces, dots, ... -%%% {language, "en"}. -%%% {ldap_rootdn, "dc=example,dc=com"}. -%%% -%%% - This term includes a tuple, a keyword, a list, and two strings: -%%% {hosts, ["jabber.example.net", "im.example.com"]}. -%%% -%%% - This config is preprocessed during release generation by a tool which -%%% interprets double curly braces as substitution markers, so avoid this -%%% syntax in this file (though it's valid Erlang). -%%% -%%% So this is OK (though arguably looks quite ugly): -%%% { {s2s_addr, "example-host.net"}, {127,0,0,1} }. -%%% -%%% And I can't give an example of what's not OK exactly because -%%% of this rule. -%%% - - -%%%. ======================= -%%%' OVERRIDE STORED OPTIONS - -%% -%% Override the old values stored in the database. -%% - -%% -%% Override global options (shared by all ejabberd nodes in a cluster). -%% -%%override_global. - -%% -%% Override local options (specific for this particular ejabberd node). -%% -%%override_local. - -%% -%% Remove the Access Control Lists before new ones are added. -%% -%%override_acls. - -%%%. ========= -%%%' GDPR RELATED -%% when set to true, user data removals will be applied on all modules that implements GDPR behaviour - -%% even the disabled ones! -%% -{gdpr_removal_for_disabled_modules, false}. - -%%%. ========= -%%%' DEBUGGING - -%% -%% loglevel: Verbosity of log files generated by ejabberd. -%% 0: No ejabberd log at all (not recommended) -%% 1: Critical -%% 2: Error -%% 3: Warning -%% 4: Info -%% 5: Debug -%% -{loglevel, 3}. - -%% -%% alarms: an optional alarm handler, subscribed to system events -%% long_gc: minimum GC time in ms for long_gc alarm -%% large_heap: minimum process heap size for large_heap alarm -%% handlers: a list of alarm handlers -%% - alarms_basic_handler: logs alarms and stores a brief alarm summary -%% - alarms_folsom_handler: stores alarm details in folsom metrics -%% -%% Example: -%% {alarms, -%% [{long_gc, 10000}, -%% {large_heap, 1000000}, -%% {handlers, [alarms_basic_handler, -%% alarms_folsom_handler]}] -%% }. - -%% -%% watchdog_admins: Only useful for developers: if an ejabberd process -%% consumes a lot of memory, send live notifications to these XMPP -%% accounts. Requires alarms (see above). -%% -%%{watchdog_admins, ["bob@example.com"]}. - - -%%%. ================ -%%%' SERVED HOSTNAMES - -%% -%% hosts: Domains served by ejabberd. -%% You can define one or several, for example: -%% {hosts, ["example.net", "example.com", "example.org"]}. -%% -{hosts, ["localhost"] }. - -%% -%% route_subdomains: Delegate subdomains to other XMPP servers. -%% For example, if this ejabberd serves example.org and you want -%% to allow communication with an XMPP server called im.example.org. -%% -%%{route_subdomains, s2s}. - - -%%%. =============== -%%%' LISTENING PORTS - -%% -%% listen: The ports ejabberd will listen on, which service each is handled -%% by and what options to start it with. -%% -{listen, - [ - - { 5280, ejabberd_cowboy, [ - {num_acceptors, 10}, - {max_connections, 1024}, - %% Uncomment for HTTPS - %{cert, "priv/server.crt"}, - %{key, "priv/server.key"}, - %{key_pass, ""}, - {modules, [ - %% Modules used here should also be listed in the MODULES section. - {"_", "/[...]", mod_cowboy, [{http, mod_revproxy, []}]} - ]} - ]}, - - { 5222, ejabberd_c2s, [ - - %% - %% If TLS is compiled in and you installed a SSL - %% certificate, specify the full path to the - %% file and uncomment this line: - %% - %%{certfile, "/path/to/ssl.pem"}, starttls, - %%{zlib, 10000}, - %% https://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS - %% {ciphers, "TLSv1.2:TLSv1.3"}, - {access, c2s}, - {shaper, c2s_shaper}, - {max_stanza_size, 65536} - ]}, - - - - %% - %% To enable the old SSL connection method on port 5223: - %% - %%{5223, ejabberd_c2s, [ - %% {access, c2s}, - %% {shaper, c2s_shaper}, - %% {certfile, "/path/to/ssl.pem"}, tls, - %% {max_stanza_size, 65536} - %% ]}, - - { 5269, ejabberd_s2s_in, [ - {shaper, s2s_shaper}, - {max_stanza_size, 131072} - ]} - - %% - %% ejabberd_service: Interact with external components (transports, ...) - %% - %%{8888, ejabberd_service, [ - %% {access, all}, - %% {shaper_rule, fast}, - %% {ip, {127, 0, 0, 1}}, - %% {hosts, ["icq.example.org", "sms.example.org"], - %% [{password, "secret"}] - %% } - %% ]}, - - %% - %% ejabberd_stun: Handles STUN Binding requests - %% - %%{ {3478, udp}, ejabberd_stun, []} - - ]}. - -%% -%% s2s_use_starttls: Enable STARTTLS + Dialback for S2S connections. -%% Allowed values are: false optional required required_trusted -%% You must specify a certificate file. -%% -%%{s2s_use_starttls, optional}. - -%% -%% s2s_certfile: Specify a certificate file. -%% -%%{s2s_certfile, "/path/to/ssl.pem"}. - -%% https://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS -%% {s2s_ciphers, "TLSv1.2:TLSv1.3"}. - -%% -%% domain_certfile: Specify a different certificate for each served hostname. -%% -%%{domain_certfile, "example.org", "/path/to/example_org.pem"}. -%%{domain_certfile, "example.com", "/path/to/example_com.pem"}. - -%% -%% S2S whitelist or blacklist -%% -%% Default s2s policy for undefined hosts. -%% -{s2s_default_policy, deny }. - -%% -%% Allow or deny communication with specific servers. -%% -%%{ {s2s_host, "goodhost.org"}, allow}. -%%{ {s2s_host, "badhost.org"}, deny}. - -{outgoing_s2s_port, 5269 }. - -%% -%% IP addresses predefined for specific hosts to skip DNS lookups. -%% Ports defined here take precedence over outgoing_s2s_port. -%% Examples: -%% -%% { {s2s_addr, "example-host.net"}, {127,0,0,1} }. -%% { {s2s_addr, "example-host.net"}, { {127,0,0,1}, 5269 } }. -%% { {s2s_addr, "example-host.net"}, { {127,0,0,1}, 5269 } }. - -%% -%% Outgoing S2S options -%% -%% Preferred address families (which to try first) and connect timeout -%% in milliseconds. -%% -%%{outgoing_s2s_options, [ipv4, ipv6], 10000}. - -%%%. ============== -%%%' SESSION BACKEND - -{sm_backend, {mnesia, []} }. - -%%%. ============== -%%%' AUTHENTICATION - -{auth_method, internal}. - -%%%. ============== -%%%' DATABASE SETUP - - -%%%. =============== -%%%' TRAFFIC SHAPERS - -%% -%% The "normal" shaper limits traffic speed to 1000 B/s -%% -{shaper, normal, {maxrate, 1000}}. - -%% -%% The "fast" shaper limits traffic speed to 50000 B/s -%% -{shaper, fast, {maxrate, 50000}}. - -%% -%% This option specifies the maximum number of elements in the queue -%% of the FSM. Refer to the documentation for details. -%% -{max_fsm_queue, 1000}. - - -%%%. ==================== -%%%' ACCESS CONTROL LISTS - -%% -%% The 'admin' ACL grants administrative privileges to XMPP accounts. -%% You can put here as many accounts as you want. -%% -%{acl, admin, {user, "alice", "localhost"}}. -%{acl, admin, {user, "a", "localhost"}}. - -%% -%% Blocked users -%% -%%{acl, blocked, {user, "baduser", "example.org"}}. -%%{acl, blocked, {user, "test"}}. - -%% -%% Local users: don't modify this line. -%% -{acl, local, {user_regexp, ""}}. - -%% -%% More examples of ACLs -%% -%%{acl, jabberorg, {server, "jabber.org"}}. -%%{acl, aleksey, {user, "aleksey", "jabber.ru"}}. -%%{acl, test, {user_regexp, "^test"}}. -%%{acl, test, {user_glob, "test*"}}. - -%% -%% Define specific ACLs in a virtual host. -%% -%%{host_config, "localhost", -%% [ -%% {acl, admin, {user, "bob-local", "localhost"}} -%% ] -%%}. - - -%%%. ============ -%%%' ACCESS RULES - -%% Maximum number of simultaneous sessions allowed for a single user: -{access, max_user_sessions, [{10, all}]}. - -%% Maximum number of offline messages that users can have: -{access, max_user_offline_messages, [{5000, admin}, {100, all}]}. - -%% This rule allows access only for local users: -{access, local, [{allow, local}]}. - -%% Only non-blocked users can use c2s connections: -{access, c2s, [{deny, blocked}, - {allow, all}]}. - -%% For C2S connections, all users except admins use the "normal" shaper -{access, c2s_shaper, [{none, admin}, - {normal, all}]}. - -%% All S2S connections use the "fast" shaper -{access, s2s_shaper, [{fast, all}]}. - -%% Admins of this server are also admins of the MUC service: -{access, muc_admin, [{allow, admin}]}. - -%% Only accounts of the local ejabberd server can create rooms: -{access, muc_create, [{allow, local}]}. - -%% All users are allowed to use the MUC service: -{access, muc, [{allow, all}]}. - -%% In-band registration allows registration of any possible username. -%% To disable in-band registration, replace 'allow' with 'deny'. -{access, register, [{allow, all}]}. - -%% By default the frequency of account registrations from the same IP -%% is limited to 1 account every 10 minutes. To disable, specify: infinity -{registration_timeout, infinity}. - -%% Default settings for MAM. -%% To set non-standard value, replace 'default' with 'allow' or 'deny'. -%% Only user can access his/her archive by default. -%% An online user can read room's archive by default. -%% Only an owner can change settings by default. -%% Empty list (i.e. `[]`) means `[{deny, all}]`. -{access, mam_set_prefs, [{default, all}]}. -{access, mam_get_prefs, [{default, all}]}. -{access, mam_lookup_messages, [{default, all}]}. - -%% 1 command of the specified type per second. -{shaper, mam_shaper, {maxrate, 1}}. -%% This shaper is primeraly for Mnesia overload protection during stress testing. -%% The limit is 1000 operations of each type per second. -{shaper, mam_global_shaper, {maxrate, 1000}}. - -{access, mam_set_prefs_shaper, [{mam_shaper, all}]}. -{access, mam_get_prefs_shaper, [{mam_shaper, all}]}. -{access, mam_lookup_messages_shaper, [{mam_shaper, all}]}. - -{access, mam_set_prefs_global_shaper, [{mam_global_shaper, all}]}. -{access, mam_get_prefs_global_shaper, [{mam_global_shaper, all}]}. -{access, mam_lookup_messages_global_shaper, [{mam_global_shaper, all}]}. - - -%% -%% Define specific Access Rules in a virtual host. -%% -%%{host_config, "localhost", -%% [ -%% {access, c2s, [{allow, admin}, {deny, all}]}, -%% {access, register, [{deny, all}]} -%% ] -%%}. - - -%%%. ================ -%%%' DEFAULT LANGUAGE - -%% -%% language: Default language used for server messages. -%% -{language, "en"}. - -%% -%% Set a different default language in a virtual host. -%% -%%{host_config, "localhost", -%% [{language, "ru"}] -%%}. - - -%%%. ======= -%%%' MODULES - -%% -%% Modules enabled in all ejabberd virtual hosts. -%% For list of possible modules options, check documentation. -%% If module comes in two versions, like mod_last and mod_last_rdbms, -%% use only one of them. -%% -{modules, - [ - {mod_revproxy, [{routes, - [{"_", "_", "_", "http://localhost:1234"}] - }] - }, - %{mod_admin_extra, [{submods, [node, accounts, sessions, vcard, - % roster, last, private, stanza, stats]}]}, - {mod_adhoc, []}, - {mod_disco, []}, - {mod_last, []}, - {mod_stream_management, [ - % default 100 - % size of a buffer of unacked messages - % {buffer_max, 100} - - % default 1 - server sends the ack request after each stanza - % {ack_freq, 1} - - % default: 600 seconds - % {resume_timeout, 600} - ]}, - {mod_muc, [ - {host, "muc.@HOST@"}, - {access, muc}, - {access_create, muc_create} - ]}, - {mod_muc_log, - [ - {outdir, "/tmp/muclogs"}, - {access_log, muc} - ]}, - {mod_privacy, []}, - {mod_register, [ - %% - %% Set the minimum informational entropy for passwords. - %% - %%{password_strength, 32}, - - %% - %% After successful registration, the user receives - %% a message with this subject and body. - %% - {welcome_message, {"", ""}}, - - %% - %% When a user registers, send a notification to - %% these XMPP accounts. - %% - %%{registration_watchers, ["admin1@example.org"]}, - - %% - %% Only clients in the server machine can register accounts - %% - {ip_access, [{allow, "127.0.0.0/8"}, - {deny, "0.0.0.0/0"}]}, - - %% - %% Local c2s or remote s2s users cannot register accounts - %% - %%{access_from, deny}, - - {access, register} - ]}, - {mod_roster, []}, - {mod_sic, []}, - {mod_vcard, [ {allow_return_all, true}, -{search_all_hosts, true} -%{matches, 1}, -%{search, true}, -%{host, directory.@HOST@} -]} - %% - %% Message Archive Management (MAM) for registered users. - %% - - %% A module for storing preferences in RDBMS (used by default). - %% Enable for private message archives. -% {mod_mam_rdbms_prefs, [pm]}, - %% Enable for multiuser message archives. -% {mod_mam_rdbms_prefs, [muc]}, - %% Enable for both private and multiuser message archives. -% {mod_mam_rdbms_prefs, [pm, muc]}, - - %% A module for storing preferences in Mnesia (recommended). - %% This module will be called each time, as a message is routed. - %% That is why, Mnesia is better for this job. -% {mod_mam_mnesia_prefs, [pm, muc]}, - - %% A back-end for storing messages. - %% Synchronious writer (used by default). - %% This writer is easy to debug, but writing performance is low. -% {mod_mam_rdbms_arch, [pm]}, - - %% Enable the module with a custom writer. -% {mod_mam_rdbms_arch, [no_writer, pm]}, - - %% Asynchronious writer for RDBMS (recommended). - %% Messages will be grouped and inserted all at once. -% {mod_mam_rdbms_async_writer, [pm]}, - - %% A pool of asynchronious writers (recommended). - %% Messages will be grouped together based on archive id. -% {mod_mam_rdbms_async_pool_writer, [pm]}, - - %% A module for converting an archive id to an integer. - %% Extract information using RDBMS. -% {mod_mam_rdbms_user, [pm, muc]}, - - %% Cache information about users (recommended). - %% Requires mod_mam_rdbms_user or alternative. -% {mod_mam_cache_user, [pm, muc]}, - - %% Enable MAM. -% {mod_mam, []}, - - - %% - %% Message Archive Management (MAM) for multi-user chats (MUC). - %% Enable XEP-0313 for "muc.@HOST@". - %% - - %% A back-end for storing messages (default for MUC). - %% Modules mod_mam_muc_* are optimized for MUC. - %% - %% Synchronious writer (used by default for MUC). - %% This module is easy to debug, but performance is low. -% {mod_mam_muc_rdbms_arch, []}, -% {mod_mam_muc_rdbms_arch, [no_writer]}, - - %% Asynchronious writer for RDBMS (recommended for MUC). - %% Messages will be grouped and inserted all at once. -% {mod_mam_muc_rdbms_async_writer, []}, -% {mod_mam_muc_rdbms_async_pool_writer, []}, - - %% Load mod_mam_rdbms_user too. - - %% Enable MAM for MUC -% {mod_mam_muc, [{host, "muc.@HOST@"}]} - - - %% - %% MAM configuration examples - %% - - %% Only MUC, no user-defined preferences, good performance. -% {mod_mam_rdbms_user, [muc]}, -% {mod_mam_cache_user, [muc]}, -% {mod_mam_muc_rdbms_arch, [no_writer]}, -% {mod_mam_muc_rdbms_async_pool_writer, []}, -% {mod_mam_muc, [{host, "muc.@HOST@"}]} - - %% Only archives for c2c messages, good performance. -% {mod_mam_rdbms_user, [pm]}, -% {mod_mam_cache_user, [pm]}, -% {mod_mam_mnesia_prefs, [pm]}, -% {mod_mam_rdbms_arch, [pm, no_writer]}, -% {mod_mam_rdbms_async_pool_writer, [pm]}, -% {mod_mam, []} - - %% Basic configuration for c2c messages, bad performance, easy to debug. -% {mod_mam_rdbms_user, [pm]}, -% {mod_mam_rdbms_prefs, [pm]}, -% {mod_mam_rdbms_arch, [pm]}, -% {mod_mam, []} - - ]}. - - -%% -%% Enable modules with custom options in a specific virtual host -%% -%%{host_config, "localhost", -%% [{ {add, modules}, -%% [ -%% {mod_some_module, []} -%% ] -%% } -%% ]}. - -%%%. -%%%' - -%%% $Id$ - -%%% Local Variables: -%%% mode: erlang -%%% End: -%%% vim: set filetype=erlang tabstop=8 foldmarker=%%%',%%%. foldmethod=marker: -%%%. From 77570ff75c92bacb682ffa7d6d54f00b1b2206e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Fri, 23 Oct 2020 15:57:45 +0200 Subject: [PATCH 006/104] Do not use the 'cfg' format in mongoose_config_SUITE 1. Use the expected options directly 2. Remove the test for 'required_files' This functionality is replaced by validators in TOML. 3. Remove the test for macros There are no macros in TOML. --- test/mongoose_config_SUITE.erl | 260 ++++++++++----------------------- 1 file changed, 77 insertions(+), 183 deletions(-) diff --git a/test/mongoose_config_SUITE.erl b/test/mongoose_config_SUITE.erl index ea821cfbe8..ce1dd7128c 100644 --- a/test/mongoose_config_SUITE.erl +++ b/test/mongoose_config_SUITE.erl @@ -3,11 +3,11 @@ -compile([export_all]). -include_lib("eunit/include/eunit.hrl"). +-include("ejabberd_config.hrl"). all() -> [ does_pattern_match_case, flat_state_case, - parse_config_with_underscore_pattern_case, node_specific_options_presents_case, node_specific_options_missing_case, states_to_reloading_context_case, @@ -16,8 +16,7 @@ all() -> [ get_config_diff_case, flat_module_subopts_case, expand_opts_case, - expand_module_subopts_case, - parse_config_with_required_files_case + expand_module_subopts_case ]. init_per_suite(C) -> @@ -82,37 +81,36 @@ match_cases() -> ]. - flat_state_case(_C) -> - State = mongoose_config_parser_cfg:parse_terms(cool_mod_mam_config()), + State = state(cool_mod_mam_config()), ?assertEqual(cool_mod_mam_config_flat(), mongoose_config_reload:state_to_flat_local_opts(State)). cool_mod_mam_config() -> - [{hosts, ["localhost"]}, - {modules, [{mod_mam, [{pool, cool_pool}]}]}]. + [#config{key = hosts, value = [<<"localhost">>]}, + #local_config{key = {modules, <<"localhost">>}, value = [{mod_mam, [{pool, cool_pool}]}]}]. cool_mod_mam_config_flat() -> - [{[h,<<"localhost">>,modules],'FLAT'}, - {[h,<<"localhost">>,module,mod_mam],'FLAT'}, - {[h,<<"localhost">>,module_opt,mod_mam,pool],cool_pool}]. + [{[h, <<"localhost">>, modules], 'FLAT'}, + {[h, <<"localhost">>, module, mod_mam], 'FLAT'}, + {[h, <<"localhost">>, module_opt, mod_mam, pool], cool_pool}]. flat_module_subopts_case(_C) -> - State = mongoose_config_parser_cfg:parse_terms(gd_config()), + State = state(gd_config()), FlatOpts = mongoose_config_reload:state_to_flat_local_opts(State), - NumConnsKey = [h,<<"localhost">>,module_subopt,mod_global_distrib, - connections,connections_per_endpoint], - ConnsKey = [h,<<"localhost">>,module_opt,mod_global_distrib, + NumConnsKey = [h, <<"localhost">>, module_subopt, mod_global_distrib, + connections, connections_per_endpoint], + ConnsKey = [h, <<"localhost">>, module_opt, mod_global_distrib, connections], - RedisServerKey = [h,<<"localhost">>,module_subopt,mod_global_distrib, - redis,server], + RedisServerKey = [h, <<"localhost">>, module_subopt, mod_global_distrib, + redis, server], ?assertEqual(22, proplists:get_value(NumConnsKey, FlatOpts)), ?assertEqual('FLAT', proplists:get_value(ConnsKey, FlatOpts)), ?assertEqual("172.16.0.3", proplists:get_value(RedisServerKey, FlatOpts)), ok. expand_opts_case(_C) -> - State = mongoose_config_parser_cfg:parse_terms(cool_mod_mam_config()), + State = state(cool_mod_mam_config()), FlatOpts = mongoose_config_reload:state_to_flat_local_opts(State), ExpandedOpts = mongoose_config_flat:expand_all_opts(FlatOpts), CatOpts = mongoose_config_reload:state_to_categorized_options(State), @@ -123,7 +121,7 @@ expand_opts_case(_C) -> ok. expand_module_subopts_case(_C) -> - State = mongoose_config_parser_cfg:parse_terms(gd_config()), + State = state(gd_config()), FlatOpts = mongoose_config_reload:state_to_flat_local_opts(State), ExpandedOpts = mongoose_config_flat:expand_all_opts(FlatOpts), CatOpts = mongoose_config_reload:state_to_categorized_options(State), @@ -134,92 +132,72 @@ expand_module_subopts_case(_C) -> ok. gd_config() -> - [{hosts, ["localhost"]}, - {modules, [ - - {mod_global_distrib, [ - {global_host, "example.com"}, - {local_host, "datacenter1.example.com"}, - {connections, [ - {endpoints, [{"172.16.0.2", 5555}]}, - {connections_per_endpoint, 22}, - {tls_opts, [ - {certfile, "/home/user/dc1.pem"}, - {cafile, "/home/user/ca.pem"} - ]} - ]}, - {cache, [ - {domain_lifetime_seconds, 60} - ]}, - {bounce, [ - {resend_after_ms, 300}, - {max_retries, 3} - ]}, - {redis, [ - {pool_size, 24}, - {server, "172.16.0.3"} - ]} - ]} - - ]} - ]. - -auth_config() -> - [{hosts, ["localhost", "anonymous.localhost"]}, - {auth_method, internal}, - {host_config,"anonymous.localhost", - [{auth_method,anonymous}]}]. + [#config{key = hosts, value = [<<"localhost">>]}, + #local_config{key = {modules, <<"localhost">>}, + value = [global_distrib_spec()]}]. + +global_distrib_spec() -> + {mod_global_distrib, + [{global_host, "example.com"}, + {local_host, "datacenter1.example.com"}, + {connections, [{endpoints, [{"172.16.0.2", 5555}]}, + {connections_per_endpoint, 22}, + {tls_opts, [{certfile, "/home/user/dc1.pem"}, + {cafile, "/home/user/ca.pem"}]} + ]}, + {cache, [{domain_lifetime_seconds, 60}]}, + {bounce, [{resend_after_ms, 300}, + {max_retries, 3}]}, + {redis, [{pool_size, 24}, + {server, "172.16.0.3"}]} + ]}. auth_config_states() -> [auth_config_node1_config_v1()]. auth_config_node1_config_v1() -> - State = mongoose_config_parser_cfg:parse_terms(auth_config()), + State = state(auth_config()), #{mongoose_node => mim1, - config_file => "/etc/mongooseim.cfg", + config_file => "/etc/mongooseim.toml", loaded_categorized_options => mongoose_config_reload:state_to_categorized_options(State), ondisc_config_state => State, missing_files => [], required_files => []}. auth_host_local_config() -> - State = auth_config_state(), + State = state(auth_config()), CatOpts = mongoose_config_reload:state_to_categorized_options(State), maps:get(host_config, CatOpts). -auth_config_state() -> - Terms = auth_config(), - mongoose_config_parser_cfg:parse_terms(Terms). - -%% Check that underscore is not treated as a config macro by config parser -parse_config_with_underscore_pattern_case(_C) -> - mongoose_config_parser_cfg:parse_terms(node_specific_cool_mod_mam_config()). +auth_config() -> + [#config{key = hosts, value = [<<"localhost">>]}, + #local_config{key = {auth_method, <<"localhost">>}, value = internal}, + #local_config{key = {auth_method, <<"anonymous.localhost">>}, value = anonymous}]. %% Check that we can convert state into node_specific_options list node_specific_options_presents_case(_C) -> - State = mongoose_config_parser_cfg:parse_terms(node_specific_cool_mod_mam_config()), + State = state(node_specific_cool_mod_mam_config()), NodeOpts = mongoose_config_parser:state_to_global_opt(node_specific_options, State, missing), - ?assertEqual([ [h,'_',module_opt,mod_mam,pool] ], - NodeOpts). + ?assertEqual([[h, '_', module_opt, mod_mam, pool] ], NodeOpts). %% Check that we would not crash if node_specific_options is not defined node_specific_options_missing_case(_C) -> - State = mongoose_config_parser_cfg:parse_terms(cool_mod_mam_config()), + State = state(cool_mod_mam_config()), NodeOpts = mongoose_config_parser:state_to_global_opt(node_specific_options, State, missing), ?assertEqual(missing, NodeOpts). node_specific_cool_mod_mam_config() -> - [{hosts, ["localhost"]}, - {node_specific_options, [ [h,'_',module_opt,mod_mam,pool] ]}, - {modules, [{mod_mam, [{pool, cool_pool}]}]}]. + [#config{key = hosts, value = [<<"localhost">>]}, + #config{key = node_specific_options, value = [[h, '_', module_opt, mod_mam, pool]]}, + #local_config{key = {modules, <<"localhost">>}, value = [{mod_mam, [{pool, cool_pool}]}]}]. states_to_reloading_context_case(_C) -> - Context = mongoose_config_reload:states_to_reloading_context(example_config_states()), + _Context = mongoose_config_reload:states_to_reloading_context(example_config_states()), % ct:pal("Context ~p", [Context]), ok. %% Check that auth_method is treated correctly auth_method_and_cluster_reload_case(_C) -> - Context = mongoose_config_reload:states_to_reloading_context(auth_config_states()), + _Context = mongoose_config_reload:states_to_reloading_context(auth_config_states()), % ct:pal("Auth context ~p", [Context]), ok. @@ -247,18 +225,18 @@ example_config_states() -> %% node1_config_v1 configuration both in memory and on disc config_node1_config_v1() -> - State = mongoose_config_parser_cfg:parse_terms(node1_config_v1()), + State = state(node_config(<<"secret">>, node1_pool)), #{mongoose_node => mim1, - config_file => "/etc/mongooseim.cfg", + config_file => "/etc/mongooseim.toml", loaded_categorized_options => mongoose_config_reload:state_to_categorized_options(State), ondisc_config_state => State, missing_files => [], required_files => []}. %% node2_config_v1 configuration both in memory and on disc config_node2_config_v1() -> - State = mongoose_config_parser_cfg:parse_terms(node2_config_v1()), + State = state(node_config(<<"secret">>, node1_pool)), #{mongoose_node => mim2, - config_file => "/etc/mongooseim.cfg", + config_file => "/etc/mongooseim.toml", loaded_categorized_options => mongoose_config_reload:state_to_categorized_options(State), ondisc_config_state => State, missing_files => [], required_files => []}. @@ -266,10 +244,10 @@ config_node2_config_v1() -> %% node1_config_v1 configuration in memory %% node1_config_v2 configuration on disc config_node1_config_v2() -> - State_v1 = mongoose_config_parser_cfg:parse_terms(node1_config_v1()), - State_v2 = mongoose_config_parser_cfg:parse_terms(node1_config_v2()), + State_v1 = state(node_config(<<"secret">>, node1_pool)), + State_v2 = state(node_config(<<"secret123">>, node1_pool)), #{mongoose_node => mim1, - config_file => "/etc/mongooseim.cfg", + config_file => "/etc/mongooseim.toml", loaded_categorized_options => mongoose_config_reload:state_to_categorized_options(State_v1), ondisc_config_state => State_v2, missing_files => [], required_files => []}. @@ -277,107 +255,34 @@ config_node1_config_v2() -> %% node2_config_v1 configuration in memory %% node2_config_v2 configuration on disc config_node2_config_v2() -> - State_v1 = mongoose_config_parser_cfg:parse_terms(node2_config_v1()), - State_v2 = mongoose_config_parser_cfg:parse_terms(node2_config_v2()), + State_v1 = state(node_config(<<"secret">>, node2_pool)), + State_v2 = state(node_config(<<"secret123">>, node2_pool)), #{mongoose_node => mim2, - config_file => "/etc/mongooseim.cfg", + config_file => "/etc/mongooseim.toml", loaded_categorized_options => mongoose_config_reload:state_to_categorized_options(State_v1), ondisc_config_state => State_v2, missing_files => [], required_files => []}. -node1_config_v1() -> - [ - {hosts, ["localhost"]}, - {node_specific_options, - [ - [h,'_',module_opt,mod_mam,pool], - [h,'_',module_opt,mod_mam_muc,pool] - ]}, - {modules, - [ - {mod_mam, - [ - {pool, node1_pool}, - {iqdisc, parallel}, - {password, <<"secret">>} - ]}, - {mod_mam_muc, [{pool, node1_pool}]} - ]} - ]. - -node1_config_v2() -> - %% Different more secure password was applied in version 2 - [ - {hosts, ["localhost"]}, - {node_specific_options, - [ - [h,'_',module_opt,mod_mam,pool], - [h,'_',module_opt,mod_mam_muc,pool] - ]}, - {modules, - [ - {mod_mam, - [ - {pool, node1_pool}, - {iqdisc, parallel}, - {password, <<"secret123">>} - ]}, - {mod_mam_muc, [{pool, node1_pool}]} - ]} - ]. - -node2_config_v1() -> - %% Pools are different for different nodes - [ - {hosts, ["localhost"]}, - {node_specific_options, - [ - [h,'_',module_opt,mod_mam,pool], - [h,'_',module_opt,mod_mam_muc,pool] - ]}, - {modules, - [ - {mod_mam, - [ - {pool, node2_pool}, - {iqdisc, parallel}, - {password, <<"secret">>} - ]}, - {mod_mam_muc, [{pool, node1_pool}]} - ]} - ]. - -node2_config_v2() -> - %% Pools are different for different nodes - %% Different more secure password was applied in version 2 - [ - {hosts, ["localhost"]}, - {node_specific_options, - [ - [h,'_',module_opt,mod_mam,pool], - [h,'_',module_opt,mod_mam_muc,pool] - ]}, - {modules, - [ - {mod_mam, - [ - {pool, node2_pool}, - {iqdisc, parallel}, - {password, <<"secret123">>} - ]}, - {mod_mam_muc, [{pool, node1_pool}]} - ]} - ]. - +node_config(Password, PoolName) -> + [#config{key = hosts, + value = [<<"localhost">>]}, + #config{key = node_specific_options, + value = [[h, '_', module_opt, mod_mam, pool], + [h, '_', module_opt, mod_mam_muc, pool]]}, + #local_config{key = {modules, <<"localhost">>}, + value = [{mod_mam, [{pool, PoolName}, + {iqdisc, parallel}, + {password, Password}]}, + {mod_mam_muc, [{pool, PoolName}]}]}]. get_config_diff_case(_C) -> %% Calculate changes to node1 reload_local to transit from v1 to v2 - State_v1 = mongoose_config_parser_cfg:parse_terms(node1_config_v1()), - State_v2 = mongoose_config_parser_cfg:parse_terms(node1_config_v2()), + State_v1 = state(node_config(<<"secret">>, node1_pool)), + State_v2 = state(node_config(<<"secret123">>, node2_pool)), CatOptions = mongoose_config_reload:state_to_categorized_options(State_v1), Diff = mongoose_config_reload:get_config_diff(State_v2, CatOptions), #{local_hosts_changes := #{ to_reload := ToReload }} = Diff, - [{ {modules,<<"localhost">>}, OldModules, NewModules }] = ToReload, + [{{modules, <<"localhost">>}, OldModules, NewModules}] = ToReload, ?assertEqual(<<"secret">>, get_module_opt(mod_mam, password, OldModules)), ?assertEqual(<<"secret123">>, get_module_opt(mod_mam, password, NewModules)), ok. @@ -386,17 +291,6 @@ get_module_opt(Module, Key, Modules) -> {Module, Opts} = lists:keyfind(Module, 1, Modules), proplists:get_value(Key, Opts). - -config_with_required_files() -> - [ - {hosts, ["localhost"]}, - {s2s_certfile, "priv/ssl/fake_server.pem"}, - {domain_certfile, "localhost", "priv/ssl/localhost_server.pem"} - ]. - -parse_config_with_required_files_case(_C) -> - State = mongoose_config_parser_cfg:parse_terms(config_with_required_files()), - ?assertEqual(["priv/ssl/localhost_server.pem", - "priv/ssl/fake_server.pem"], - mongoose_config_parser:state_to_required_files(State)), - ok. +state(Opts) -> + NewState = mongoose_config_parser:new_state(), + mongoose_config_parser:set_opts(Opts, NewState). From 97f616805011761747066d3e256119f518581cb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Fri, 23 Oct 2020 15:50:27 +0200 Subject: [PATCH 007/104] Do not use the 'cfg' format in big_tests Remove the equivalence test, as there are small tests for all options. --- big_tests/test.config | 134 -------------------- big_tests/tests/config_format_SUITE.erl | 155 ------------------------ big_tests/tests/ejabberd_node_utils.erl | 14 +-- 3 files changed, 6 insertions(+), 297 deletions(-) delete mode 100644 big_tests/tests/config_format_SUITE.erl diff --git a/big_tests/test.config b/big_tests/test.config index 2d60568e08..442278c41b 100644 --- a/big_tests/test.config +++ b/big_tests/test.config @@ -389,140 +389,6 @@ scope = \"global\""}, {mod_offline, "[modules.mod_offline]"} ]} - ]}, - {cfg, % preset vars for the 'cfg' format - used only by the config equivalence tests - [ - {internal_mnesia, - %% dbs variable is used by ./tools/test_runner/presets_to_dbs.sh script - [{dbs, [redis, minio]}, - {sm_backend, "{mnesia, []}"}, - {auth_method, "internal"}, - {outgoing_pools, "{outgoing_pools, [ - {redis, global, global_distrib, [{workers, 10}], []} - ]}."}, - {mod_offline, "{mod_offline, []},"}]}, - {pgsql_mnesia, - [{dbs, [redis, pgsql]}, - {sm_backend, "{mnesia, []}"}, - {auth_method, "rdbms"}, - {outgoing_pools, "{outgoing_pools, [ - {redis, global, global_distrib, [{workers, 10}], []}, - {rdbms, global, default, [{workers, 5}], - [{server, {pgsql, \"localhost\", \"ejabberd\", \"ejabberd\", \"mongooseim_secret\", - [{ssl, required}, {ssl_opts, [{verify, verify_peer}, - {cacertfile, \"priv/ssl/cacert.pem\"}, {server_name_indication, disable}]}]}}]} - ]}."}, - {mod_last, "{mod_last, [{backend, rdbms}]},"}, - {mod_privacy, "{mod_privacy, [{backend, rdbms}]},"}, - {mod_private, "{mod_private, [{backend, rdbms}]},"}, - {mod_offline, "{mod_offline, [{backend, rdbms}]},"}, - {mod_vcard, "{mod_vcard, [{backend, rdbms}, {host, \"vjud.@HOST@\"}]},"}, - {mod_roster, "{mod_roster, [{backend, rdbms}]},"}]}, - {odbc_mssql_mnesia, - [{dbs, [redis, mssql]}, - {sm_backend, "{mnesia, []}"}, - {auth_method, "rdbms"}, - {rdbms_server_type, "{rdbms_server_type, mssql}."}, - {outgoing_pools, "{outgoing_pools, [ - {redis, global, global_distrib, [{workers, 10}], []}, - {rdbms, global, default, [{workers, 5}], - [{server, \"DSN=mongoose-mssql;UID=sa;PWD=mongooseim_secret+ESL123\"}]} - ]}."}, - {mod_last, "{mod_last, [{backend, rdbms}]},"}, - {mod_privacy, "{mod_privacy, [{backend, rdbms}]},"}, - {mod_private, "{mod_private, [{backend, rdbms}]},"}, - {mod_offline, "{mod_offline, [{backend, rdbms}]},"}, - {mod_vcard, "{mod_vcard, [{backend, rdbms}, {host, \"vjud.@HOST@\"}]},"}, - {mod_roster, "{mod_roster, [{backend, rdbms}]},"}]}, - {mysql_redis, - [{dbs, [redis, mysql]}, - {sm_backend, "{redis, []}"}, - {auth_method, "rdbms"}, - {outgoing_pools, "{outgoing_pools, [ - {redis, global, global_distrib, [{workers, 10}], []}, - {redis, global, default, [{workers, 10}, {strategy, random_worker}], []}, - {rdbms, global, default, [{workers, 5}], - [{server, {mysql, \"localhost\", \"ejabberd\", \"ejabberd\", \"mongooseim_secret\", - [{versions, ['tlsv1.2']}, {verify, verify_peer}, {cacertfile, \"priv/ssl/cacert.pem\"}]}}]} - ]}."}, - {mod_last, "{mod_last, [{backend, rdbms}]},"}, - {mod_privacy, "{mod_privacy, [{backend, rdbms}]},"}, - {mod_private, "{mod_private, [{backend, mysql}]},"}, - {mod_offline, "{mod_offline, [{backend, rdbms}]},"}, - {mod_vcard, "{mod_vcard, [{backend, rdbms}, {host, \"vjud.@HOST@\"}]},"}, - {mod_roster, "{mod_roster, [{backend, rdbms}]},"}]}, - {ldap_mnesia, - [{dbs, [redis, ldap]}, - {sm_backend, "{mnesia, []}"}, - {auth_method, "ldap"}, - {outgoing_pools, "{outgoing_pools, [ - {redis, global, global_distrib, [{workers, 10}], []}, - {ldap, global, default, [{workers, 5}], [{port, 3636}, - {rootdn, \"cn=admin,dc=esl,dc=com\"}, - {password, \"mongooseim_secret\"}, - {encrypt, tls}, - {tls_options, [{versions, ['tlsv1.2']}, - {verify, verify_peer}, - {cacertfile, \"priv/ssl/cacert.pem\"}, - {certfile, \"priv/ssl/fake_cert.pem\"}, - {keyfile, \"priv/ssl/fake_key.pem\"}]}]}, - {ldap, global, bind, [{workers, 5}], [{port, 3636}, - {encrypt, tls}, - {tls_options, [{versions, ['tlsv1.2']}, - {verify, verify_peer}, - {cacertfile, \"priv/ssl/cacert.pem\"}, - {certfile, \"priv/ssl/fake_cert.pem\"}, - {keyfile, \"priv/ssl/fake_key.pem\"}]}]} - ]}."}, - {mod_offline, "{mod_offline, []},"}, - {password_format, "{password_format, scram}"}, - {auth_ldap, ", {ldap_base, \"ou=Users,dc=esl,dc=com\"}, - {ldap_filter, \"(objectClass=inetOrgPerson)\"}" - }, - {mod_vcard,"{mod_vcard, [{backend, ldap}, {host, \"vjud.@HOST@\"},\n" - "{ldap_base, \"ou=Users,dc=esl,dc=com\"},\n" - "{ldap_filter,\"(objectClass=inetOrgPerson)\"}\n" - "]},"} - ]}, - {riak_mnesia, - [{dbs, [redis, riak]}, - {sm_backend, "{mnesia, []}"}, - {auth_method, "riak"}, - %% Specify a list of ciphers to avoid - %% "no function clause matching tls_v1:enum_to_oid(28)" error - %% on Riak's side running with Erlang R16. - %% https://github.com/basho/riak-erlang-client/issues/232#issuecomment-178612129 - %% We also set ciphers in tools/setup_riak on the server side. - {outgoing_pools, "{outgoing_pools, [ - {redis, global, global_distrib, [{workers, 10}], []}, - {riak, global, default, [{workers, 5}, - {strategy, next_worker}], - [{address, \"127.0.0.1\"},{port, 8087}, - {ssl_opts, [{ciphers, [\"AES256-SHA\", \"DHE-RSA-AES128-SHA256\"]}, - {server_name_indication, disable}]}, - {credentials, \"ejabberd\", \"mongooseim_secret\"}, - {cacertfile, \"priv/ssl/cacert.pem\"}]} - ]}."}, - {mod_roster, "{mod_roster, [{backend, riak}]},"}, - {mod_private, "{mod_private, [{backend, riak}]},"}, - {mod_vcard, "{mod_vcard, [{backend, riak}, {host, \"vjud.@HOST@\"}]},"}, - {mod_offline, "{mod_offline, [{backend, riak}]},"}, - {mod_last, "{mod_last, [{backend, riak}]},"}, - {mod_privacy, "{mod_privacy, [{backend, riak}]},"} - ]}, - {elasticsearch_and_cassandra_mnesia, - [{dbs, [redis, elasticsearch, cassandra]}, - {sm_backend, "{mnesia, []}"}, - {outgoing_pools, "{outgoing_pools, [ - {redis, global, global_distrib, [{workers, 10}], []}, - {cassandra, global, default, [{workers, 20}], - [{ssl,[{cacertfile, \"priv/ssl/cacert.pem\"}, - {verify, verify_peer}] }]}, - {elastic, global, default, [], []} - ]}."}, - {auth_method, "internal"}, - {mod_offline, "{mod_offline, []},"} - ]} ]} ]}. diff --git a/big_tests/tests/config_format_SUITE.erl b/big_tests/tests/config_format_SUITE.erl deleted file mode 100644 index 3e8d4cc529..0000000000 --- a/big_tests/tests/config_format_SUITE.erl +++ /dev/null @@ -1,155 +0,0 @@ --module(config_format_SUITE). - --compile(export_all). - --include_lib("common_test/include/ct.hrl"). --include_lib("eunit/include/eunit.hrl"). - --import(distributed_helper, [mim/0, - require_rpc_nodes/1, - rpc/4]). - --define(eq(Expected, Actual), - ?assertEqual(Expected, Actual)). - -all() -> - [equivalence]. - -suite() -> - distributed_helper:require_rpc_nodes([mim]) ++ escalus:suite(). - -init_per_suite(Config0) -> - Config1 = escalus:init_per_suite(Config0), - ejabberd_node_utils:init(Config1). - -end_per_suite(Config) -> - escalus:end_per_suite(Config). - -init_per_testcase(CaseName, Config0) -> - Config1 = escalus:init_per_testcase(CaseName, Config0), - ejabberd_node_utils:backup_config_file(Config1), - Config1. - -end_per_testcase(CaseName, Config) -> - escalus:end_per_testcase(CaseName, Config), - ejabberd_node_utils:restore_config_file(Config), - ejabberd_node_utils:restart_application(mongooseim). - -%% Test cases - -equivalence(Config) -> - TOMLOpts = get_config_tables(), - ejabberd_node_utils:modify_config_file(mim, [], Config, cfg), - ejabberd_node_utils:restart_application(mongooseim), - CfgOpts = get_config_tables(), - compare_unordered_lists(CfgOpts, TOMLOpts, fun handle_config_option/2). - -%% Test case helpers - -get_config_tables() -> - Tables = [config, local_config, acl], - Opts = lists:flatmap(fun(Tab) -> rpc(mim(), ets, tab2list, [Tab]) end, Tables), - lists:filter(fun filter_config/1, Opts). - -filter_config({config, required_files, _}) -> false; % not supported in TOML -filter_config({local_config, node_start, _}) -> false; -filter_config(_) -> true. - -handle_config_option({Config, K1, V1}, {Config, K2, V2}) when Config =:= config; - Config =:= local_config -> - ?eq(K1, K2), - compare_values(K1, V1, V2); -handle_config_option(Opt1, Opt2) -> - ?eq(Opt1, Opt2). - -compare_values(listen, V1, V2) -> - compare_unordered_lists(V1, V2, fun handle_listener/2); -compare_values({auth_opts, _}, V1, V2) -> - compare_unordered_lists(V1, V2, fun handle_auth_opt/2); -compare_values(outgoing_pools, V1, V2) -> - compare_unordered_lists(V1, V2, fun handle_conn_pool/2); -compare_values({modules, _}, V1, V2) -> - compare_unordered_lists(V1, V2, fun handle_item_with_opts/2); -compare_values({services, _}, V1, V2) -> - compare_unordered_lists(V1, V2, fun handle_item_with_opts/2); -compare_values({auth_method, _}, V1, V2) when is_atom(V1) -> - ?eq([V1], V2); -compare_values({s2s_addr, _}, {_, _, _, _} = IP1, IP2) -> - ?eq(inet:ntoa(IP1), IP2); -compare_values(K, V1, V2) -> - ?eq({K, V1}, {K, V2}). - -handle_listener({P1, M1, O1}, {P2, M2, O2}) -> - ?eq(P1, P2), - ?eq(M1, M2), - compare_unordered_lists(O1, O2, fun handle_listener_option/2). - -handle_listener_option({modules, M1}, {modules, M2}) -> - compare_unordered_lists(M1, M2, fun handle_listener_module/2); -handle_listener_option({transport_options, O1}, {transport_options, O2}) -> - compare_unordered_lists(O1, O2); -handle_listener_option(V1, V2) -> ?eq(V1, V2). - -handle_listener_module({H1, P1, M1}, M2) -> - handle_listener_module({H1, P1, M1, []}, M2); -handle_listener_module({H1, P1, M1, O1}, {H2, P2, M2, O2}) -> - ?eq(H1, H2), - ?eq(P1, P2), - ?eq(M1, M2), - compare_listener_module_options(M1, O1, O2). - -compare_listener_module_options(mod_websockets, - [{ejabberd_service, S1}], [{ejabberd_service, S2}]) -> - compare_unordered_lists(S1, S2); -compare_listener_module_options(_, O1, O2) -> - ?eq(O1, O2). - -handle_auth_opt({cyrsasl_external, M}, {cyrsasl_external, [M]}) -> ok; -handle_auth_opt(V1, V2) -> ?eq(V1, V2). - -handle_item_with_opts({M1, O1}, {M2, O2}) -> - ?eq(M1, M2), - compare_unordered_lists(O1, O2). - -handle_conn_pool({Type1, Scope1, Tag1, POpts1, COpts1}, - {Type2, Scope2, Tag2, POpts2, COpts2}) -> - ?eq(Type1, Type2), - ?eq(Scope1, Scope2), - ?eq(Tag1, Tag2), - compare_unordered_lists(POpts1, POpts2), - compare_unordered_lists(COpts1, COpts2, fun handle_conn_opt/2). - -handle_conn_opt({server, {D1, H1, DB1, U1, P1, O1}}, - {server, {D2, H2, DB2, U2, P2, O2}}) -> - ?eq(D1, D2), - ?eq(H1, H2), - ?eq(DB1, DB2), - ?eq(U1, U2), - ?eq(P1, P2), - compare_unordered_lists(O1, O2, fun handle_db_server_opt/2); -handle_conn_opt({tls_options, Opts1}, {tls_options, Opts2}) -> - compare_unordered_lists(Opts1, Opts2); -handle_conn_opt(V1, V2) -> ?eq(V1, V2). - -handle_db_server_opt({ssl_opts, O1}, {ssl_opts, O2}) -> - compare_unordered_lists(O1, O2); -handle_db_server_opt(V1, V2) -> ?eq(V1, V2). - -compare_unordered_lists(L1, L2) -> - compare_unordered_lists(L1, L2, fun(V1, V2) -> ?eq(V1, V2) end). - -compare_unordered_lists(L1, L2, F) -> - SL1 = lists:sort(L1), - SL2 = lists:sort(L2), - compare_ordered_lists(SL1, SL2, F). - -compare_ordered_lists([H1|T1], [H1|T2], F) -> - compare_ordered_lists(T1, T2, F); -compare_ordered_lists([H1|T1], [H2|T2], F) -> - try F(H1, H2) - catch C:R:S -> - ct:fail({C, R, S}) - end, - compare_ordered_lists(T1, T2, F); -compare_ordered_lists([], [], _) -> - ok. diff --git a/big_tests/tests/ejabberd_node_utils.erl b/big_tests/tests/ejabberd_node_utils.erl index 2264d0a980..23f2dad734 100644 --- a/big_tests/tests/ejabberd_node_utils.erl +++ b/big_tests/tests/ejabberd_node_utils.erl @@ -45,8 +45,7 @@ config_vars_path(File, Config) -> ctl_path(Node, Config) -> filename:join([cwd(Node, Config), "bin", "mongooseimctl"]). -config_file_name(toml) -> "mongooseim.toml"; -config_file_name(cfg) -> "mongooseim.cfg". +config_file_name(toml) -> "mongooseim.toml". -type ct_config() :: list({Key :: term(), Value :: term()}). @@ -133,12 +132,12 @@ file_exists(Filename) -> file_exists(Node, Filename) -> call_fun(Node, filelib, is_file, [Filename]). -%% @doc Modifies default ejabberd config file: `etc/mongooseim.cfg'. +%% @doc Modifies default ejabberd config file: `etc/mongooseim.toml'. %% %% This function assumes that the config file was generated from template -%% file in `rel/files/mongooseim.cfg' using variables from `rel/vars.config'. +%% file in `rel/files/mongooseim.toml' using variables from `rel/vars-toml.config'. %% The modification procedure overrides given variables provided in -%% `rel/vars.config'. +%% `rel/vars-toml.config'. %% %% For example to change `hosts' value in the configuration file one %% has to call the function as follows: @@ -150,7 +149,7 @@ file_exists(Node, Filename) -> modify_config_file(CfgVarsToChange, Config) -> modify_config_file(mim, CfgVarsToChange, Config, toml). --spec modify_config_file(Host, [{ConfigVariable, Value}], ct_config(), toml | cfg) -> ok when +-spec modify_config_file(Host, [{ConfigVariable, Value}], ct_config(), toml) -> ok when Host :: atom(), ConfigVariable :: atom(), Value :: string(). @@ -203,8 +202,7 @@ get_config_path(RPCSpec) -> set_config_path(RPCSpec, Path) -> ejabberd_node_utils:call_fun(RPCSpec, os, putenv, ["EJABBERD_CONFIG_PATH", Path]). -vars_file(toml) -> "vars-toml.config"; -vars_file(cfg) -> "vars.config". +vars_file(toml) -> "vars-toml.config". preset_vars(Config, Format) -> case proplists:get_value(preset, Config) of From 2b9d62911ca3bb8c427fe2b2ca75932c940801cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Fri, 23 Oct 2020 16:02:07 +0200 Subject: [PATCH 008/104] Do not use the 'cfg' format in tools --- tools/test_runner/apply_templates.erl | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/test_runner/apply_templates.erl b/tools/test_runner/apply_templates.erl index 20c6c1134b..5278dfc63b 100644 --- a/tools/test_runner/apply_templates.erl +++ b/tools/test_runner/apply_templates.erl @@ -43,7 +43,6 @@ simple_templates() -> {"rel/files/app.config", "etc/app.config"}, {"rel/files/vm.args", "etc/vm.args"}, {"rel/files/vm.dist.args", "etc/vm.dist.args"}, - {"rel/files/mongooseim.cfg", "etc/mongooseim.cfg"}, {"rel/files/mongooseim.toml", "etc/mongooseim.toml"} ]. From a1f7697ca3cb87ef298766442353375b91923843 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Fri, 23 Oct 2020 15:54:37 +0200 Subject: [PATCH 009/104] Remove config files with the 'cfg' format --- Makefile | 8 +- rel/fed1.vars.config | 41 -- rel/files/mongooseim.cfg | 911 --------------------------------------- rel/mim1.vars.config | 99 ----- rel/mim2.vars.config | 68 --- rel/mim3.vars.config | 61 --- rel/reg1.vars.config | 52 --- rel/vars.config.in | 83 ---- 8 files changed, 2 insertions(+), 1321 deletions(-) delete mode 100644 rel/fed1.vars.config delete mode 100755 rel/files/mongooseim.cfg delete mode 100644 rel/mim1.vars.config delete mode 100644 rel/mim2.vars.config delete mode 100644 rel/mim3.vars.config delete mode 100644 rel/reg1.vars.config delete mode 100644 rel/vars.config.in diff --git a/Makefile b/Makefile index b162fdf254..2f4ff57904 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,6 @@ clean: -rm -rf asngen -rm -rf _build -rm rel/configure.vars.config - -rm rel/vars.config -rm rel/vars-toml.config # REBAR_CT_EXTRA_ARGS comes from a test runner @@ -26,7 +25,7 @@ ct: eunit: @$(RUN) $(REBAR) eunit -rel: certs configure.out rel/vars.config rel/vars-toml.config +rel: certs configure.out rel/vars-toml.config . ./configure.out && $(REBAR) as prod release shell: certs etc/mongooseim.cfg @@ -39,9 +38,6 @@ rock: elif [ "$(BRANCH)" ]; then tools/rock_changed.sh $(BRANCH); \ else tools/rock_changed.sh; fi -rel/vars.config: rel/vars.config.in rel/configure.vars.config - cat $^ > $@ - rel/vars-toml.config: rel/vars-toml.config.in rel/configure.vars.config cat $^ > $@ @@ -58,7 +54,7 @@ devrel: $(DEVNODES) print_devnodes: @echo $(DEVNODES) -$(DEVNODES): certs configure.out rel/vars.config rel/vars-toml.config +$(DEVNODES): certs configure.out rel/vars-toml.config @echo "building $@" (. ./configure.out && \ DEVNODE=true $(RUN) $(REBAR) as $@ release) diff --git a/rel/fed1.vars.config b/rel/fed1.vars.config deleted file mode 100644 index b9663e6626..0000000000 --- a/rel/fed1.vars.config +++ /dev/null @@ -1,41 +0,0 @@ -{node_name, "fed1@localhost"}. - -{c2s_port, 5242}. -{incoming_s2s_port, 5299}. -{cowboy_port, 5282}. -{cowboy_secure_port, 5287}. -{outgoing_s2s_port, 5269}. -{http_api_endpoint_port, 5294}. -{http_api_old_endpoint_port, 5293}. -{http_api_client_endpoint_port, 8095}. - -%% This node is for s2s testing. -%% "localhost" host should NOT be defined. -{hosts, "[ \"fed1\" ]"}. -{sm_backend, "{mnesia, []}"}. -{auth_method, "internal"}. -{s2s_addr, "{ {s2s_addr, \"localhost\"}, {127,0,0,1} }. { {s2s_addr, \"pubsub.localhost\"}, {127,0,0,1} }. { {s2s_addr, \"muc.localhost\"}, {127,0,0,1} }. { {s2s_addr, \"localhost.bis\"}, {127,0,0,1} }."}. -{s2s_default_policy, allow}. -{highload_vm_args, ""}. -{ejabberd_service, ""}. -{mod_roster, "{mod_roster, []},"}. -{s2s_use_starttls, "{s2s_use_starttls, optional}."}. -{s2s_certfile, "{s2s_certfile, \"priv/ssl/fake_server.pem\"}."}. -{tls_config, "{certfile, \"priv/ssl/fake_server.pem\"}, starttls, {ciphers, \"ECDHE-RSA-AES256-GCM-SHA384\"},"}. -{tls_module, ""}. -{secondary_c2s, ""}. -{http_api_old_endpoint, "{ {{ http_api_old_endpoint_port }}, \"127.0.0.1\"}"}. -{http_api_endpoint, "{ {{ http_api_endpoint_port }}, \"127.0.0.1\"}"}. -{http_api_client_endpoint, "{{ http_api_client_endpoint_port }}"}. -{all_metrics_are_global, false}. -{c2s_dhfile, ",{dhfile, \"priv/ssl/fake_dh_server.pem\"}"}. -{s2s_dhfile, ",{dhfile, \"priv/ssl/fake_dh_server.pem\"}"}. - -{mongooseim_runner_user, []}. -{mongooseim_script_dir, "$(cd ${0%/*} && pwd)"}. -{mongooseim_etc_dir, "$RUNNER_BASE_DIR/etc"}. -{mongooseim_log_dir, "log"}. -{mongooseim_mdb_dir, "$RUNNER_BASE_DIR/Mnesia.$NODE"}. -{mongooseim_mdb_dir_toggle, "%"}. -{mongooseim_lock_dir, "$MIM_DIR/var/lock"}. -{mongooseim_nodetool_etc_dir, "etc"}. diff --git a/rel/files/mongooseim.cfg b/rel/files/mongooseim.cfg deleted file mode 100755 index 82f1380d29..0000000000 --- a/rel/files/mongooseim.cfg +++ /dev/null @@ -1,911 +0,0 @@ -%%% -%%% ejabberd configuration file -%%% -%%%' - -%%% The parameters used in this configuration file are explained in more detail -%%% in the ejabberd Installation and Operation Guide. -%%% Please consult the Guide in case of doubts, it is included with -%%% your copy of ejabberd, and is also available online at -%%% http://www.process-one.net/en/ejabberd/docs/ - -%%% This configuration file contains Erlang terms. -%%% In case you want to understand the syntax, here are the concepts: -%%% -%%% - The character to comment a line is % -%%% -%%% - Each term ends in a dot, for example: -%%% override_global. -%%% -%%% - A tuple has a fixed definition, its elements are -%%% enclosed in {}, and separated with commas: -%%% {loglevel, 4}. -%%% -%%% - A list can have as many elements as you want, -%%% and is enclosed in [], for example: -%%% [http_poll, web_admin, tls] -%%% -%%% Pay attention that list elements are delimited with commas, -%%% but no comma is allowed after the last list element. This will -%%% give a syntax error unlike in more lenient languages (e.g. Python). -%%% -%%% - A keyword of ejabberd is a word in lowercase. -%%% Strings are enclosed in "" and can contain spaces, dots, ... -%%% {language, "en"}. -%%% {ldap_rootdn, "dc=example,dc=com"}. -%%% -%%% - This term includes a tuple, a keyword, a list, and two strings: -%%% {hosts, ["jabber.example.net", "im.example.com"]}. -%%% -%%% - This config is preprocessed during release generation by a tool which -%%% interprets double curly braces as substitution markers, so avoid this -%%% syntax in this file (though it's valid Erlang). -%%% -%%% So this is OK (though arguably looks quite ugly): -%%% { {s2s_addr, "example-host.net"}, {127,0,0,1} }. -%%% -%%% And I can't give an example of what's not OK exactly because -%%% of this rule. -%%% - - -%%%. ======================= -%%%' OVERRIDE STORED OPTIONS - -%% -%% Override the old values stored in the database. -%% - -%% -%% Override global options (shared by all ejabberd nodes in a cluster). -%% -%%override_global. - -%% -%% Override local options (specific for this particular ejabberd node). -%% -%%override_local. - -%% -%% Remove the Access Control Lists before new ones are added. -%% -%%override_acls. - -%%%. ========= -%%%' DEBUGGING - -%% -%% loglevel: Verbosity of log files generated by ejabberd. -%% 0: No ejabberd log at all (not recommended) -%%% -1: none; -%%% 0: emergency; -%%% 1: alert; -%%% 2: critical; -%%% 3: error; -%%% 4: warning; -%%% 5: notice; -%%% 6: info; -%%% 7: debug. -%%% 8: all. -%% -{loglevel, warning}. - -%%%. ================ -%%%' SERVED HOSTNAMES - -%% -%% hosts: Domains served by ejabberd. -%% You can define one or several, for example: -%% {hosts, ["example.net", "example.com", "example.org"]}. -%% -{hosts, {{{hosts}}} }. - -%% -%% route_subdomains: Delegate subdomains to other XMPP servers. -%% For example, if this ejabberd serves example.org and you want -%% to allow communication with an XMPP server called im.example.org. -%% -%%{route_subdomains, s2s}. - - -%%%. =============== -%%%' LISTENING PORTS - -%% -%% listen: The ports ejabberd will listen on, which service each is handled -%% by and what options to start it with. -%% -{listen, - [ - %% BOSH and WS endpoints over HTTP - { {{{cowboy_port}}}, ejabberd_cowboy, [ - {transport_options, [{max_connections, 1024}, {num_acceptors, 10}]}, - {modules, [ - - {"_", "/http-bind", mod_bosh}, - {"_", "/ws-xmpp", mod_websockets, [{ejabberd_service, [ - {access, all}, - {shaper_rule, fast}, - {password, "secret"}]} - %% Uncomment to enable connection dropping or/and server-side pings - %{timeout, 600000}, {ping_rate, 2000} - ]} - %% Uncomment to serve static files - %{"_", "/static/[...]", cowboy_static, - % {dir, "/var/www", [{mimetypes, cow_mimetypes, all}]} - %}, - - %% Example usage of mod_revproxy - - %% {"_", "/[...]", mod_revproxy, [{timeout, 5000}, - %% % time limit for upstream to respond - %% {body_length, 8000000}, - %% % maximum body size (may be infinity) - %% {custom_headers, [{<<"header">>,<<"value">>}]} - %% % list of extra headers that are send to upstream - %% ]} - - %% Example usage of mod_cowboy - - %% {"_", "/[...]", mod_cowboy, [{http, mod_revproxy, - %% [{timeout, 5000}, - %% % time limit for upstream to respond - %% {body_length, 8000000}, - %% % maximum body size (may be infinity) - %% {custom_headers, [{<<"header">>,<<"value">>}]} - %% % list of extra headers that are send to upstream - %% ]}, - %% {ws, xmpp, mod_websockets} - %% ]} - ]} - ]}, - - %% BOSH and WS endpoints over HTTPS - { {{{cowboy_secure_port}}}, ejabberd_cowboy, [ - {transport_options, [{max_connections, 1024}, {num_acceptors, 10}]}, - {{{https_config}}} - {modules, [ - {"_", "/http-bind", mod_bosh}, - {"_", "/ws-xmpp", mod_websockets, [ - %% Uncomment to enable connection dropping or/and server-side pings - %{timeout, 600000}, {ping_rate, 60000} - ]} - %% Uncomment to serve static files - %{"_", "/static/[...]", cowboy_static, - % {dir, "/var/www", [{mimetypes, cow_mimetypes, all}]} - %}, - ]} - ]}, - - %% MongooseIM HTTP API it's important to start it on localhost - %% or some private interface only (not accessible from the outside) - %% At least start it on different port which will be hidden behind firewall - - { {{{http_api_endpoint}}} , ejabberd_cowboy, [ - {transport_options, [{max_connections, 1024}, {num_acceptors, 10}]}, - {modules, [ - {"localhost", "/api", mongoose_api_admin, []} - ]} - ]}, - - { {{{http_api_client_endpoint}}} , ejabberd_cowboy, [ - {transport_options, [{max_connections, 1024}, {num_acceptors, 10}]}, - {protocol_options, [{compress, true}]}, - {{{https_config}}} - {modules, [ - {"_", "/api/sse", lasse_handler, [mongoose_client_api_sse]}, - {"_", "/api/messages/[:with]", mongoose_client_api_messages, []}, - {"_", "/api/contacts/[:jid]", mongoose_client_api_contacts, []}, - {"_", "/api/rooms/[:id]", mongoose_client_api_rooms, []}, - {"_", "/api/rooms/[:id]/config", mongoose_client_api_rooms_config, []}, - {"_", "/api/rooms/:id/users/[:user]", mongoose_client_api_rooms_users, []}, - {"_", "/api/rooms/[:id]/messages", mongoose_client_api_rooms_messages, []}, - %% Swagger - {"_", "/api-docs", cowboy_swagger_redirect_handler, #{}}, - {"_", "/api-docs/swagger.json", cowboy_swagger_json_handler, #{}}, - {"_", "/api-docs/[...]", cowboy_static, {priv_dir, cowboy_swagger, "swagger", [{mimetypes, cow_mimetypes, all}]}} - ]} - ]}, - - %% Following HTTP API is deprected, the new one abouve should be used instead - - { {{{http_api_old_endpoint}}} , ejabberd_cowboy, [ - {transport_options, [{max_connections, 1024}, {num_acceptors, 10}]}, - {modules, [ - {"localhost", "/api", mongoose_api, [{handlers, [mongoose_api_metrics, - mongoose_api_users]}]} - ]} - ]}, - - { {{{c2s_port}}}, ejabberd_c2s, [ - - %% - %% If TLS is compiled in and you installed a SSL - %% certificate, specify the full path to the - %% file and uncomment this line: - %% - {{{tls_config}}} - {{{tls_module}}} - {{{zlib}}} - %% https://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS - %% {ciphers, "TLSv1.2:TLSv1.3"}, - {access, c2s}, - {shaper, c2s_shaper}, - {max_stanza_size, 65536} - {{{c2s_dhfile}}} - ]}, - - {{{secondary_c2s}}} - - %% - %% To enable the old SSL connection method on port 5223: - %% - %%{5223, ejabberd_c2s, [ - %% {access, c2s}, - %% {shaper, c2s_shaper}, - %% {certfile, "/path/to/ssl.pem"}, tls, - %% {max_stanza_size, 65536} - %% ]}, - - { {{{incoming_s2s_port}}}, ejabberd_s2s_in, [ - {shaper, s2s_shaper}, - {max_stanza_size, 131072} - {{{s2s_dhfile}}} - ]} - - %% - %% ejabberd_service: Interact with external components (transports, ...) - %% - {{{ejabberd_service}}} - - %% - %% ejabberd_stun: Handles STUN Binding requests - %% - %%{ {3478, udp}, ejabberd_stun, []} - - ]}. - -%% -%% s2s_use_starttls: Enable STARTTLS + Dialback for S2S connections. -%% Allowed values are: false optional required required_trusted -%% You must specify a certificate file. -%% -{{{s2s_use_starttls}}} -%% -%% s2s_certfile: Specify a certificate file. -%% -{{{s2s_certfile}}} - -%% https://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS -%% {s2s_ciphers, "TLSv1.2:TLSv1.3"}. - -%% -%% domain_certfile: Specify a different certificate for each served hostname. -%% -%%{domain_certfile, "example.org", "/path/to/example_org.pem"}. -%%{domain_certfile, "example.com", "/path/to/example_com.pem"}. - -%% -%% S2S whitelist or blacklist -%% -%% Default s2s policy for undefined hosts. -%% -{s2s_default_policy, {{{s2s_default_policy}}} }. - -%% -%% Allow or deny communication with specific servers. -%% -%%{ {s2s_host, "goodhost.org"}, allow}. -%%{ {s2s_host, "badhost.org"}, deny}. - -{outgoing_s2s_port, {{{outgoing_s2s_port}}} }. - -%% -%% IP addresses predefined for specific hosts to skip DNS lookups. -%% Ports defined here take precedence over outgoing_s2s_port. -%% Examples: -%% -%% { {s2s_addr, "example-host.net"}, {127,0,0,1} }. -%% { {s2s_addr, "example-host.net"}, { {127,0,0,1}, 5269 } }. -{{{s2s_addr}}} - -%% -%% Outgoing S2S options -%% -%% Preferred address families (which to try first) and connect timeout -%% in milliseconds. -%% -%%{outgoing_s2s_options, [ipv4, ipv6], 10000}. -%% -%%%. ============== -%%%' SESSION BACKEND - -%%{sm_backend, {mnesia, []}}. - -%% Requires {redis, global, default, ..., ...} outgoing pool -%%{sm_backend, {redis, []}}. - -{sm_backend, {{{sm_backend}}} }. - - -%%%. ============== -%%%' AUTHENTICATION - -%% Advertised SASL mechanisms -{{{sasl_mechanisms}}} - -%% -%% auth_method: Method used to authenticate the users. -%% The default method is the internal. -%% If you want to use a different method, -%% comment this line and enable the correct ones. -%% -{auth_method, {{{auth_method}}} }. -{auth_opts, [ - %% Store the plain passwords or hashed for SCRAM: - %% {password_format, scram} % default - %% {password_format, plain} - {{{password_format}}} - %% {scram_iterations, 10000} % default - {{{scram_iterations}}} - %% - %% For auth_http: - %% {basic_auth, "user:password"} - %% {path_prefix, "/"} % default - %% auth_http requires {http, Host | global, auth, ..., ...} outgoing pool. - %% - %% For auth_external - %% {extauth_program, "/path/to/authentication/script"} - %% - %% For auth_jwt - %% {jwt_secret_source, "/path/to/file"}, - %% {jwt_algorithm, "RS256"}, - %% {jwt_username_key, user} - %% - %% For cyrsasl_external - %% {authenticate_with_cn, false} - {{{cyrsasl_external}}} - %% - %% For auth_ldap - %% {ldap_base, "dc=example,dc=com"}, - %% {ldap_filter, "(objectClass=shadowAccount)"}, - %% {ldap_uids, [{"mail", "%u@mail.example.org"}]}. - {{{auth_ldap}}} - ]}. - -%% -%% Authentication using external script -%% Make sure the script is executable by ejabberd. -%% -%%{auth_method, external}. - -%% -%% Authentication using RDBMS -%% Remember to setup a database in the next section. -%% -%%{auth_method, rdbms}. - -%% -%% Authentication using LDAP -%% Requires connection setup in the 'outgoing_pools' section -%% -%%{auth_method, ldap}. -%% - -%% -%% Anonymous login support: -%% auth_method: anonymous -%% anonymous_protocol: sasl_anon | login_anon | both -%% allow_multiple_connections: true | false -%% -%%{host_config, "public.example.org", [{auth_method, anonymous}, -%% {allow_multiple_connections, false}, -%% {anonymous_protocol, sasl_anon}]}. -%% -%% To use both anonymous and internal authentication: -%% -%%{host_config, "public.example.org", [{auth_method, [internal, anonymous]}]}. -{{{host_config}}} - -%%%. ============== -%%%' OUTGOING CONNECTIONS (e.g. DB) - -%% Here you may configure all outgoing connections used by MongooseIM, -%% e.g. to RDBMS (such as MySQL), Riak or external HTTP components. -%% Default MongooseIM configuration uses only Mnesia (non-Mnesia extensions are disabled), -%% so no options here are uncommented out of the box. -%% This section includes configuration examples; for comprehensive guide -%% please consult MongooseIM documentation, page "Outgoing connections": -%% - doc/advanced-configuration/outgoing-connections.md -%% - https://mongooseim.readthedocs.io/en/latest/advanced-configuration/outgoing-connections/ - -{{{outgoing_pools}}} - -%% More examples that may be added to outgoing_pools list: -%% -%% == MySQL == -%% {rdbms, global, default, [{workers, 10}], -%% [{server, {mysql, "server", 3306, "database", "username", "password"}}, -%% {keepalive_interval, 10}]}, -%% keepalive_interval is optional - -%% == PostgreSQL == -%% {rdbms, global, default, [{workers, 10}], -%% [{server, {pgsql, "server", 5432, "database", "username", "password"}}]}, - -%% == ODBC (MSSQL) == -%% {rdbms, global, default, [{workers, 10}], -%% [{server, "DSN=mongooseim;UID=mongooseim;PWD=mongooseim"}]}, - -%% == Elastic Search == -%% {elastic, global, default, [], [{host, "elastic.host.com"}, {port, 9042}]}, - -%% == Riak == -%% {riak, global, default, [{workers, 20}], [{address, "127.0.0.1"}, {port, 8087}]}, - -%% == HTTP == -%% {http, global, conn1, [{workers, 50}], [{server, "http://server:8080"}]}, - -%% == Cassandra == -%% {cassandra, global, default, [{workers, 100}], -%% [ -%% {servers, [ -%% {"cassandra_server1.example.com", 9042}, -%% {"cassandra_server2.example.com", 9042}, -%% {"cassandra_server3.example.com", 9042}, -%% {"cassandra_server4.example.com", 9042} -%% ]}, -%% {keyspace, "big_mongooseim"} -%% ]}, - -%% == LDAP == -%% {ldap, global, default, [{workers, 5}], [{servers, ["ldap-server.example.com"]}, -%% {rootdn, "cn=admin,dc=example,dc=com"}, -%% {password, "secret"}]} - -%% == Extra options == -%% -%% If you use PostgreSQL, have a large database, and need a -%% faster but inexact replacement for "select count(*) from users" -%% -%%{pgsql_users_number_estimate, true}. -%% -%% rdbms_server_type specifies what database is used over the RDBMS layer -%% Can take values mssql, pgsql, mysql -%% In some cases (for example for MAM with pgsql) it is required to set proper value. -%% -{{{rdbms_server_type}}} - -%%%. =============== -%%%' TRAFFIC SHAPERS - -%% -%% The "normal" shaper limits traffic speed to 1000 B/s -%% -{shaper, normal, {maxrate, 1000}}. - -%% -%% The "fast" shaper limits traffic speed to 50000 B/s -%% -{shaper, fast, {maxrate, 50000}}. - -%% -%% This option specifies the maximum number of elements in the queue -%% of the FSM. Refer to the documentation for details. -%% -{max_fsm_queue, 1000}. - -%%%. ==================== -%%%' ACCESS CONTROL LISTS - -%% -%% The 'admin' ACL grants administrative privileges to XMPP accounts. -%% You can put here as many accounts as you want. -%% -%{acl, admin, {user, "alice", "localhost"}}. -%{acl, admin, {user, "a", "localhost"}}. - -%% -%% Blocked users -%% -%%{acl, blocked, {user, "baduser", "example.org"}}. -%%{acl, blocked, {user, "test"}}. - -%% -%% Local users: don't modify this line. -%% -{acl, local, {user_regexp, ""}}. - -%% -%% More examples of ACLs -%% -%%{acl, jabberorg, {server, "jabber.org"}}. -%%{acl, aleksey, {user, "aleksey", "jabber.ru"}}. -%%{acl, test, {user_regexp, "^test"}}. -%%{acl, test, {user_glob, "test*"}}. - -%% -%% Define specific ACLs in a virtual host. -%% -%%{host_config, "localhost", -%% [ -%% {acl, admin, {user, "bob-local", "localhost"}} -%% ] -%%}. - -%%%. ============ -%%%' ACCESS RULES - -%% Maximum number of simultaneous sessions allowed for a single user: -{access, max_user_sessions, [{10, all}]}. - -%% Maximum number of offline messages that users can have: -{access, max_user_offline_messages, [{5000, admin}, {100, all}]}. - -%% This rule allows access only for local users: -{access, local, [{allow, local}]}. - -%% Only non-blocked users can use c2s connections: -{access, c2s, [{deny, blocked}, - {allow, all}]}. - -%% For C2S connections, all users except admins use the "normal" shaper -{access, c2s_shaper, [{none, admin}, - {normal, all}]}. - -%% All S2S connections use the "fast" shaper -{access, s2s_shaper, [{fast, all}]}. - -%% Admins of this server are also admins of the MUC service: -{access, muc_admin, [{allow, admin}]}. - -%% Only accounts of the local ejabberd server can create rooms: -{access, muc_create, [{allow, local}]}. - -%% All users are allowed to use the MUC service: -{access, muc, [{allow, all}]}. - -%% In-band registration allows registration of any possible username. -%% To disable in-band registration, replace 'allow' with 'deny'. -{access, register, [{allow, all}]}. - -%% By default the frequency of account registrations from the same IP -%% is limited to 1 account every 10 minutes. To disable, specify: infinity -{registration_timeout, infinity}. - -%% Default settings for MAM. -%% To set non-standard value, replace 'default' with 'allow' or 'deny'. -%% Only user can access his/her archive by default. -%% An online user can read room's archive by default. -%% Only an owner can change settings and purge messages by default. -%% Empty list (i.e. `[]`) means `[{deny, all}]`. -{access, mam_set_prefs, [{default, all}]}. -{access, mam_get_prefs, [{default, all}]}. -{access, mam_lookup_messages, [{default, all}]}. - -%% 1 command of the specified type per second. -{shaper, mam_shaper, {maxrate, 1}}. -%% This shaper is primeraly for Mnesia overload protection during stress testing. -%% The limit is 1000 operations of each type per second. -{shaper, mam_global_shaper, {maxrate, 1000}}. - -{access, mam_set_prefs_shaper, [{mam_shaper, all}]}. -{access, mam_get_prefs_shaper, [{mam_shaper, all}]}. -{access, mam_lookup_messages_shaper, [{mam_shaper, all}]}. - -{access, mam_set_prefs_global_shaper, [{mam_global_shaper, all}]}. -{access, mam_get_prefs_global_shaper, [{mam_global_shaper, all}]}. -{access, mam_lookup_messages_global_shaper, [{mam_global_shaper, all}]}. - -%% -%% Define specific Access Rules in a virtual host. -%% -%%{host_config, "localhost", -%% [ -%% {access, c2s, [{allow, admin}, {deny, all}]}, -%% {access, register, [{deny, all}]} -%% ] -%%}. - -%%%. ================ -%%%' DEFAULT LANGUAGE - -%% -%% language: Default language used for server messages. -%% -{language, "en"}. - -%% -%% Set a different default language in a virtual host. -%% -%%{host_config, "localhost", -%% [{language, "ru"}] -%%}. - -%%%. ================ -%%%' MISCELLANEOUS - -{all_metrics_are_global, {{{all_metrics_are_global}}} }. - -{{{cowboy_server_name}}} - -%%%. ======== -%%%' SERVICES - -%% Unlike modules, services are started per node and provide either features which are not -%% related to any particular host, or backend stuff which is used by modules. -%% This is handled by `mongoose_service` module. - -{services, - [ - {service_admin_extra, [{submods, [node, accounts, sessions, vcard, gdpr, upload, - roster, last, private, stanza, stats]}]}, - {service_mongoose_system_metrics, [{initial_report, 300000}, - {periodic_report, 10800000}]} - ] -}. - -%%%. ======= -%%%' MODULES - -%% -%% Modules enabled in all mongooseim virtual hosts. -%% For list of possible modules options, check documentation. -%% -{modules, - [ - - %% The format for a single route is as follows: - %% {Host, Path, Method, Upstream} - %% - %% "_" can be used as wildcard for Host, Path and Method - %% Upstream can be either host (just http(s)://host:port) or uri - %% The difference is that host upstreams append whole path while - %% uri upstreams append only remainder that follows the matched Path - %% (this behaviour is similar to nginx's proxy_pass rules) - %% - %% Bindings can be used to match certain parts of host or path. - %% They will be later overlaid with parts of the upstream uri. - %% - %% {mod_revproxy, - %% [{routes, [{"www.erlang-solutions.com", "/admin", "_", - %% "https://www.erlang-solutions.com/"}, - %% {":var.com", "/:var", "_", "http://localhost:8080/"}, - %% {":domain.com", "/", "_", "http://localhost:8080/:domain"}] - %% }]}, - -% {mod_http_upload, [ - %% Set max file size in bytes. Defaults to 10 MB. - %% Disabled if value is `undefined`. -% {max_file_size, 1024}, - %% Use S3 storage backend -% {backend, s3}, - %% Set options for S3 backend -% {s3, [ -% {bucket_url, "http://localhost:9000/mybucket/"}, -% {region, "eu-west-1"}, -% {access_key_id, "AKIAIAOAONIULXQGMOUA"}, -% {secret_access_key, "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"} -% ]} -% ]}, - {mod_adhoc, []}, - {{{mod_amp}}} - {mod_disco, [{users_can_see_hidden_services, false}]}, -% {mod_extdisco, [ -% [{type, stun}, -% {host, "127.0.0.1"}, -% {port, "3478"}, -% {transport, "udp"}, -% {username, "username"}, -% {password, "secret"}] -% ]}, - {mod_commands, []}, - {mod_muc_commands, []}, - {mod_muc_light_commands, []}, - {{{mod_last}}} - {mod_stream_management, [ - % default 100 - % size of a buffer of unacked messages - % {buffer_max, 100} - - % default 1 - server sends the ack request after each stanza - % {ack_freq, 1} - - % default: 600 seconds - % {resume_timeout, 600} - - % default: [{enabled, false}] - % {stale_h, [{enabled, false}] - % {stale_h, [{enabled, true}, - % {stale_h_repeat_after, 1800}, - % {stale_h_geriatric, 3600}]} - ]}, - %% {mod_muc_light, [{host, "muclight.@HOST@"}]}, - %% {mod_muc, [{host, "muc.@HOST@"}, - %% {access, muc}, - %% {access_create, muc_create} - %% ]}, - %% {mod_muc_log, [ - %% {outdir, "/tmp/muclogs"}, - %% {access_log, muc} - %% ]}, - {{{mod_offline}}} - {{{mod_privacy}}} - {{{mod_blocking}}} - {{{mod_private}}} -% {mod_private, [{backend, mnesia}]}, -% {mod_private, [{backend, rdbms}]}, - {mod_register, [ - %% - %% Set the minimum informational entropy for passwords. - %% - %%{password_strength, 32}, - - %% - %% After successful registration, the user receives - %% a message with this subject and body. - %% - {welcome_message, {"", ""}}, - - %% - %% When a user registers, send a notification to - %% these XMPP accounts. - %% - - - %% - %% Only clients in the server machine can register accounts - %% - {ip_access, [{allow, "127.0.0.0/8"}, - {deny, "0.0.0.0/0"}]}, - - %% - %% Local c2s or remote s2s users cannot register accounts - %% - %%{access_from, deny}, - - {access, register} - ]}, - {{{mod_roster}}} - {mod_sic, []}, - {{{mod_vcard}}} - {mod_bosh, []}, - {mod_carboncopy, []} - - %% - %% Message Archive Management (MAM, XEP-0313) for registered users and - %% Multi-User chats (MUCs). - %% - -% {mod_mam_meta, [ - %% Use RDBMS backend (default) -% {backend, rdbms}, - - %% Do not store user preferences (default) -% {user_prefs_store, false}, - %% Store user preferences in RDBMS -% {user_prefs_store, rdbms}, - %% Store user preferences in Mnesia (recommended). - %% The preferences store will be called each time, as a message is routed. - %% That is why Mnesia is better suited for this job. -% {user_prefs_store, mnesia}, - - %% Enables a pool of asynchronous writers. (default) - %% Messages will be grouped together based on archive id. -% {async_writer, true}, - - %% Cache information about users (default) -% {cache_users, true}, - - %% Enable archivization for private messages (default) -% {pm, [ - %% Top-level options can be overriden here if needed, for example: -% {async_writer, false} -% ]}, - - %% - %% Message Archive Management (MAM) for multi-user chats (MUC). - %% Enable XEP-0313 for "muc.@HOST@". - %% -% {muc, [ -% {host, "muc.@HOST@"} - %% As with pm, top-level options can be overriden for MUC archive -% ]}, -% - %% Do not use a element (by default stanzaid is used) -% no_stanzaid_element, -% ]}, - - - %% - %% MAM configuration examples - %% - - %% Only MUC, no user-defined preferences, good performance. -% {mod_mam_meta, [ -% {backend, rdbms}, -% {pm, false}, -% {muc, [ -% {host, "muc.@HOST@"} -% ]} -% ]}, - - %% Only archives for c2c messages, good performance. -% {mod_mam_meta, [ -% {backend, rdbms}, -% {pm, [ -% {user_prefs_store, mnesia} -% ]} -% ]}, - - %% Basic configuration for c2c messages, bad performance, easy to debug. -% {mod_mam_meta, [ -% {backend, rdbms}, -% {async_writer, false}, -% {cache_users, false} -% ]}, - - %% Cassandra archive for c2c and MUC conversations. - %% No custom settings supported (always archive). -% {mod_mam_meta, [ -% {backend, cassandra}, -% {user_prefs_store, cassandra}, -% {muc, [{host, "muc.@HOST@"}]} -% ]} - -% {mod_event_pusher, [ -% {backends, [ -% %% -% %% Configuration for Amazon SNS notifications. -% %% -% {sns, [ -% %% AWS credentials, region and host configuration -% {access_key_id, "AKIAJAZYHOIPY6A2PESA"}, -% {secret_access_key, "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"}, -% {region, "eu-west-1"}, -% {account_id, "251423380551"}, -% {region, "eu-west-1"}, -% {sns_host, "sns.eu-west-1.amazonaws.com"}, -% -% %% Messages from this MUC host will be sent to the SNS topic -% {muc_host, "muc.@HOST@"}, -% -% %% Plugin module for defining custom message attributes and user identification -% {plugin_module, mod_event_pusher_sns_defaults}, -% -% %% Topic name configurations. Removing a topic will disable this specific SNS notification -% {presence_updates_topic, "user_presence_updated-dev-1"}, %% For presence updates -% {pm_messages_topic, "user_message_sent-dev-1"}, %% For private chat messages -% {muc_messages_topic, "user_messagegroup_sent-dev-1"} %% For group chat messages -% -% %% Pool options -% {pool_size, 100}, %% Worker pool size for publishing notifications -% {publish_retry_count, 2}, %% Retry count in case of publish error -% {publish_retry_time_ms, 50} %% Base exponential backoff time (in ms) for publish errors -% ]} -% ]} - -]}. - -{{{module_host_config}}} -%% -%% Enable modules with custom options in a specific virtual host -%% -%%{host_config, "localhost", -%% [{ {add, modules}, -%% [ -%% {mod_some_module, []} -%% ] -%% } -%% ]}. - -%%%. -%%%' - -%%% $Id$ - -%%% Local Variables: -%%% mode: erlang -%%% End: -%%% vim: set filetype=erlang tabstop=8 foldmarker=%%%',%%%. foldmethod=marker: -%%%. diff --git a/rel/mim1.vars.config b/rel/mim1.vars.config deleted file mode 100644 index af6e631dcf..0000000000 --- a/rel/mim1.vars.config +++ /dev/null @@ -1,99 +0,0 @@ -{node_name, "mongooseim@localhost"}. - -{c2s_port, 5222}. -{c2s_tls_port, 5223}. -{outgoing_s2s_port, 5299}. -{incoming_s2s_port, 5269}. -{cowboy_port, 5280}. -{cowboy_secure_port, 5285}. -{service_port, 8888}. -{kicking_service_port, 8666}. -{hidden_service_port, 8189}. -{http_api_endpoint_port, 8088}. -{http_api_old_endpoint_port, 5288}. -{http_api_client_endpoint_port, 8089}. -{metrics_rest_port, 5288}. -{http_notifications_port, 8000}. -{gd_endpoint_port, 5555}. - -{hosts, "[\"localhost\", - \"anonymous.localhost\", - \"localhost.bis\" - ]"}. -{host_config, -"{host_config, \"anonymous.localhost\", [{auth_method, anonymous}, - {allow_multiple_connections, true}, - {anonymous_protocol, both}, - {auth_opts, []}]}." }. -{sm_backend, "{mnesia, []}"}. -{auth_method, "internal"}. -{password_format, "{password_format, {scram, [sha256]}}"}. -{scram_iterations, ", {scram_iterations, 64}"}. -{auth_ldap,""}. -{s2s_addr, "{ {s2s_addr, \"fed1\"}, {127,0,0,1} }."}. -{s2s_default_policy, allow}. -% Disable highload args to save memory for dev builds -{highload_vm_args, ""}. -{secondary_c2s, - "{ {{ c2s_tls_port }}, ejabberd_c2s, [ - {zlib, 4096}, - {access, c2s}, - {shaper, c2s_shaper}, - {max_stanza_size, 65536} - ]},"}. -{mod_amp, "{mod_amp, []},"}. -{ejabberd_service, ",{ {{ service_port }}, ejabberd_service, [\n" - " {access, all},\n" - " {shaper_rule, fast},\n" - " {ip, {127, 0, 0, 1}},\n" - " {password, \"secret\"}\n" - " ]},\n" - "{ {{ kicking_service_port }}, ejabberd_service, [\n" - " {access, all},\n" - " {conflict_behaviour, kick_old},\n" - " {shaper_rule, fast},\n" - " {ip, {127, 0, 0, 1}},\n" - " {password, \"secret\"}\n" - " ]},\n" - "{ {{ hidden_service_port }}, ejabberd_service, [\n" - " {access, all},\n" - " {hidden_components, true},\n" - " {shaper_rule, fast},\n" - " {ip, {127, 0, 0, 1}},\n" - " {password, \"secret\"}\n" - " ]}" - }. -{mod_last, "{mod_last, []},"}. -{mod_offline, "{mod_offline, [{access_max_user_messages, max_user_offline_messages}]},"}. -{mod_privacy, "{mod_privacy, []},"}. -{mod_blocking, "{mod_blocking, []},"}. -{mod_private, "{mod_private, []},"}. -{mongoose_admin, "{mongoose_admin, []},"}. -{mod_roster, "{mod_roster, []},"}. -{mod_vcard, "{mod_vcard, [ %{matches, 1},\n" - "%{search, true},\n" - "{host, \"vjud.@HOST@\"}\n" - "]},"}. -{module_host_config, ""}. -{all_metrics_are_global, false}. - -{tls_config, "{certfile, \"priv/ssl/fake_server.pem\"}, starttls,"}. -{tls_module, ""}. -{https_config, "{ssl, [{certfile, \"priv/ssl/fake_cert.pem\"}, {keyfile, \"priv/ssl/fake_key.pem\"}, {password, \"\"}]},"}. -{zlib, "{zlib, 10000},"}. -{http_api_old_endpoint, "{ {{ http_api_old_endpoint_port }}, \"127.0.0.1\"}"}. -{http_api_endpoint, "{ {{ http_api_endpoint_port }}, \"127.0.0.1\"}"}. -{http_api_client_endpoint, "{{ http_api_client_endpoint_port }}"}. -{s2s_use_starttls, "{s2s_use_starttls, optional}."}. -{s2s_certfile, "{s2s_certfile, \"priv/ssl/fake_server.pem\"}."}. -{c2s_dhfile, ",{dhfile, \"priv/ssl/fake_dh_server.pem\"}"}. -{s2s_dhfile, ",{dhfile, \"priv/ssl/fake_dh_server.pem\"}"}. - -{mongooseim_runner_user, []}. -{mongooseim_script_dir, "$(cd ${0%/*} && pwd)"}. -{mongooseim_etc_dir, "$RUNNER_BASE_DIR/etc"}. -{mongooseim_log_dir, "log"}. -{mongooseim_mdb_dir, "$RUNNER_BASE_DIR/Mnesia.$NODE"}. -{mongooseim_mdb_dir_toggle, "%"}. -{mongooseim_lock_dir, "$MIM_DIR/var/lock"}. -{mongooseim_nodetool_etc_dir, "etc"}. diff --git a/rel/mim2.vars.config b/rel/mim2.vars.config deleted file mode 100644 index 73a6805bfd..0000000000 --- a/rel/mim2.vars.config +++ /dev/null @@ -1,68 +0,0 @@ -{node_name, "ejabberd2@localhost"}. - -{c2s_port, 5232}. -{c2s_tls_port, 5233}. -{incoming_s2s_port, 5279}. -{cowboy_port, 5281}. -{cowboy_secure_port, 5286}. -{outgoing_s2s_port, 5269}. -{http_api_old_endpoint_port, 5289}. -{http_api_endpoint_port, 8090}. -{http_api_client_endpoint_port, 8091}. -{service_port, 8899}. -{metrics_rest_port, 5289}. -{gd_endpoint_port, 6666}. - -{hosts, "[\"localhost\", - \"anonymous.localhost\", - \"localhost.bis\" - ]"}. -{sm_backend, "{mnesia, []}"}. -{auth_method, "internal"}. -{s2s_addr, "{ {s2s_addr, \"localhost2\"}, {127,0,0,1} }."}. -{s2s_default_policy, allow}. -{highload_vm_args, ""}. -{mod_last, "{mod_last, []},"}. -{mod_privacy, "{mod_privacy, []},"}. -{mod_blocking, "{mod_blocking, []},"}. -{mod_private, "{mod_private, []},"}. -{mod_roster, "{mod_roster, []},"}. -{mod_vcard, "{mod_vcard, [ %{matches, 1},\n" - "%{search, true},\n" - "{host, \"vjud.@HOST@\"}\n" - "]},"}. -{http_api_old_endpoint, "{ {{ http_api_old_endpoint_port }}, \"127.0.0.1\"}"}. -{http_api_endpoint, "{ {{ http_api_endpoint_port }}, \"127.0.0.1\"}"}. -{http_api_client_endpoint, "{{ http_api_client_endpoint_port }}"}. -{s2s_use_starttls, "{s2s_use_starttls, optional}."}. -{s2s_certfile, "{s2s_certfile, \"priv/ssl/fake_server.pem\"}."}. -{tls_config, "{certfile, \"priv/ssl/fake_server.pem\"}, starttls, {ciphers, \"ECDHE-RSA-AES256-GCM-SHA384\"},"}. -{tls_module, ""}. -{secondary_c2s, - "{ {{ c2s_tls_port }}, ejabberd_c2s, [ - {zlib, 4096}, - {access, c2s}, - {shaper, c2s_shaper}, - {max_stanza_size, 65536}, - {certfile, \"priv/ssl/fake_server.pem\"}, tls, - {ciphers, \"ECDHE-RSA-AES256-GCM-SHA384\"} - ]},"}. -{ejabberd_service, ",{ {{ service_port }}, ejabberd_service, [\n" - " {access, all},\n" - " {shaper_rule, fast},\n" - " {ip, {127, 0, 0, 1}},\n" - " {password, \"secret\"}\n" - " ]}"}. -{all_metrics_are_global, true}. -{cowboy_server_name, "{cowboy_server_name, \"Classified\"}."}. -{c2s_dhfile, ",{dhfile, \"priv/ssl/fake_dh_server.pem\"}"}. -{s2s_dhfile, ",{dhfile, \"priv/ssl/fake_dh_server.pem\"}"}. - -{mongooseim_runner_user, []}. -{mongooseim_script_dir, "$(cd ${0%/*} && pwd)"}. -{mongooseim_etc_dir, "$RUNNER_BASE_DIR/etc"}. -{mongooseim_log_dir, "log"}. -{mongooseim_mdb_dir, "$RUNNER_BASE_DIR/Mnesia.$NODE"}. -{mongooseim_mdb_dir_toggle, "%"}. -{mongooseim_lock_dir, "$MIM_DIR/var/lock"}. -{mongooseim_nodetool_etc_dir, "etc"}. diff --git a/rel/mim3.vars.config b/rel/mim3.vars.config deleted file mode 100644 index bda33888b9..0000000000 --- a/rel/mim3.vars.config +++ /dev/null @@ -1,61 +0,0 @@ -{node_name, "mongooseim3@localhost"}. - -{c2s_port, 5262}. -{c2s_tls_port, 5263}. -{outgoing_s2s_port, 5295}. -{incoming_s2s_port, 5291}. -{cowboy_port, 5283}. -{cowboy_secure_port, 5290}. -{http_api_old_endpoint_port, 5292}. -{http_api_endpoint_port, 8092}. -{http_api_client_endpoint_port, 8093}. - -{hosts, "[\"localhost\", - \"anonymous.localhost\", - \"localhost.bis\" - ]"}. -{sm_backend, "{mnesia, []}"}. -{auth_method, "internal"}. -{s2s_addr, "{ {s2s_addr, \"localhost2\"}, {127,0,0,1} }."}. -{s2s_default_policy, allow}. -{highload_vm_args, ""}. -{ejabberd_service, ""}. -{mod_last, "{mod_last, []},"}. -{mod_privacy, "{mod_privacy, []},"}. -{mod_blocking, "{mod_blocking, []},"}. -{mod_private, "{mod_private, []},"}. -{mod_roster, "{mod_roster, []},"}. -{mod_http_notification, "{mod_http_notification, []},"}. -{mod_vcard, "{mod_vcard, [ %{matches, 1},\n" - "%{search, true},\n" - "{host, \"vjud.@HOST@\"}\n" - "]},"}. -{s2s_use_starttls, "{s2s_use_starttls, optional}."}. -{s2s_certfile, "{s2s_certfile, \"priv/ssl/fake_server.pem\"}."}. -{tls_config, "{certfile, \"priv/ssl/fake_server.pem\"}, starttls, {ciphers, \"ECDHE-RSA-AES256-GCM-SHA384\"},"}. -{tls_module, ""}. -{secondary_c2s, - "{ {{ c2s_tls_port }}, ejabberd_c2s, [ - {zlib, 4096}, - {access, c2s}, - {shaper, c2s_shaper}, - {max_stanza_size, 65536}, - {certfile, \"priv/ssl/fake_server.pem\"}, tls, - {tls_module, just_tls}, - {ssl_options,[{ciphers, [#{cipher => aes_256_gcm, key_exchange => ecdhe_rsa, mac => aead, prf => sha384}]}]} - ]},"}. -{http_api_old_endpoint, "{ {{ http_api_old_endpoint_port }}, \"127.0.0.1\"}"}. -{http_api_endpoint, "{ {{ http_api_endpoint_port }}, \"127.0.0.1\"}"}. -{http_api_client_endpoint, "{{ http_api_client_endpoint_port }}"}. -{all_metrics_are_global, false}. -{c2s_dhfile, ",{dhfile, \"priv/ssl/fake_dh_server.pem\"}"}. -{s2s_dhfile, ",{dhfile, \"priv/ssl/fake_dh_server.pem\"}"}. - -{mongooseim_runner_user, []}. -{mongooseim_script_dir, "$(cd ${0%/*} && pwd)"}. -{mongooseim_etc_dir, "$RUNNER_BASE_DIR/etc"}. -{mongooseim_log_dir, "log"}. -{mongooseim_mdb_dir, "$RUNNER_BASE_DIR/Mnesia.$NODE"}. -{mongooseim_mdb_dir_toggle, "%"}. -{mongooseim_lock_dir, "$MIM_DIR/var/lock"}. -{mongooseim_nodetool_etc_dir, "etc"}. diff --git a/rel/reg1.vars.config b/rel/reg1.vars.config deleted file mode 100644 index b68db72f9a..0000000000 --- a/rel/reg1.vars.config +++ /dev/null @@ -1,52 +0,0 @@ -{node_name, "reg1@localhost"}. - -{c2s_port, 5252}. -{incoming_s2s_port, 5298}. -{cowboy_port, 5272}. -{cowboy_secure_port, 5277}. -{outgoing_s2s_port, 5269}. -{service_port, 9990}. -{http_api_endpoint_port, 8074}. -{http_api_old_endpoint_port, 5273}. -{http_api_client_endpoint_port, 8075}. -{gd_endpoint_port, 7777}. -{gd_extra_endpoint_port, 10000}. -{gd_supplementary_endpoint_port, 10001}. - -%% This node is for global distribution testing. -%% reg is short for region. -%% Both local and global hosts should be defined. -%% "localhost" is a global host. -%% "reg1" is a local host. -{hosts, "[ \"reg1\", \"localhost\" ]"}. -{sm_backend, "{mnesia, []}"}. -{auth_method, "internal"}. -{s2s_addr, "{ {s2s_addr, \"localhost\"}, {127,0,0,1} }. { {s2s_addr, \"localhost.bis\"}, {127,0,0,1} }."}. -{s2s_default_policy, allow}. -{highload_vm_args, ""}. -{ejabberd_service, ",{ {{ service_port }}, ejabberd_service, [\n" - " {access, all},\n" - " {shaper_rule, fast},\n" - " {ip, {127, 0, 0, 1}},\n" - " {password, \"secret\"}\n" - " ]}"}. -{mod_roster, "{mod_roster, []},"}. -{s2s_use_starttls, "{s2s_use_starttls, optional}."}. -{s2s_certfile, "{s2s_certfile, \"priv/ssl/fake_server.pem\"}."}. -{tls_config, "{certfile, \"priv/ssl/fake_server.pem\"}, starttls, {ciphers, \"ECDHE-RSA-AES256-GCM-SHA384\"},"}. -{secondary_c2s, ""}. -{http_api_old_endpoint, "{ {{ http_api_old_endpoint_port }}, \"127.0.0.1\"}"}. -{http_api_endpoint, "{ {{ http_api_endpoint_port }}, \"127.0.0.1\"}"}. -{http_api_client_endpoint, "{{ http_api_client_endpoint_port }}"}. -{all_metrics_are_global, false}. -{c2s_dhfile, ",{dhfile, \"priv/ssl/fake_dh_server.pem\"}"}. -{s2s_dhfile, ",{dhfile, \"priv/ssl/fake_dh_server.pem\"}"}. - -{mongooseim_runner_user, []}. -{mongooseim_script_dir, "$(cd ${0%/*} && pwd)"}. -{mongooseim_etc_dir, "$RUNNER_BASE_DIR/etc"}. -{mongooseim_log_dir, "log"}. -{mongooseim_mdb_dir, "$RUNNER_BASE_DIR/Mnesia.$NODE"}. -{mongooseim_mdb_dir_toggle, "%"}. -{mongooseim_lock_dir, "$MIM_DIR/var/lock"}. -{mongooseim_nodetool_etc_dir, "etc"}. diff --git a/rel/vars.config.in b/rel/vars.config.in deleted file mode 100644 index bac5a98fc0..0000000000 --- a/rel/vars.config.in +++ /dev/null @@ -1,83 +0,0 @@ -{node_name, "mongooseim@localhost"}. - -{c2s_port, 5222}. -{outgoing_s2s_port, 5269}. -{incoming_s2s_port, 5269}. -{cowboy_port, 5280}. -{cowboy_secure_port, 5285}. - -% These need to be defined before https://github.com/erlware/relx/pull/747 is merged -{c2s_tls_port, ""}. -{service_port, ""}. -{kicking_service_port, ""}. -{hidden_service_port, ""}. -{http_api_endpoint_port, ""}. -{http_api_old_endpoint_port, ""}. -{http_api_client_endpoint_port, ""}. - -{hosts, "[\"localhost\"]"}. -{host_config, ""}. -{auth_ldap, ""}. -{s2s_addr, "%% { {s2s_addr, \"example-host.net\"}, { {127,0,0,1}, 5269 } }."}. -{s2s_default_policy, deny}. -% More processes and more ports. -% But it increases memory usage. -{highload_vm_args, "+P 10000000 -env ERL_MAX_PORTS 250000"}. -{mod_amp, ""}. -{ejabberd_service, ",{8888, ejabberd_service, [\n" - " {access, all},\n" - " {shaper_rule, fast},\n" - " {ip, {127, 0, 0, 1}},\n" - " {password, \"secret\"}\n" - " ]}"}. -{mod_last, "{mod_last, []},"}. -{mod_offline, "{mod_offline, [{access_max_user_messages, max_user_offline_messages}]},"}. -{mod_privacy, "{mod_privacy, []},"}. -{mod_blocking, "{mod_blocking, []},"}. -{mod_private, "{mod_private, []},"}. -{mod_roster, "{mod_roster, []},"}. -{mod_vcard, "{mod_vcard, [%{matches, 1},\n" - "%{search, true},\n" - "%{ldap_search_operator, 'or'}, %% either 'or' or 'and'\n" - "%{ldap_binary_search_fields, [<<\"PHOTO\">>]},\n" - "%% list of binary search fields (as in vcard after mapping)\n" - "{host, \"vjud.@HOST@\"}\n" - "]},"}. - -{sm_backend, "{mnesia, []}"}. -{auth_method, "internal"}. -{password_format, "{password_format, scram}"}. -{cyrsasl_external, ", {cyrsasl_external, standard}"}. -{tls_config, "{certfile, \"priv/ssl/fake_server.pem\"}, starttls,"}. -{tls_module, ""}. -{https_config, "{ssl, [{certfile, \"priv/ssl/fake_cert.pem\"}, {keyfile, \"priv/ssl/fake_key.pem\"}, {password, \"\"}]},"}. %% Applies to Websockets, BOSH and metrics; PEM format -{zlib, "%%{zlib, 10000},"}. %% Second element of a tuple is inflated data size limit; 0 for no limit -{registration_watchers, "%{registration_watchers, [\"admin@localhost\"]},"}. -{outgoing_pools, " -%{outgoing_pools, [ -% {riak, global, default, [{workers, 5}], [{address, \"127.0.0.1\"}, {port, 8087}]}, -% {elastic, global, default, [], [{host, \"elastic.host.com\"}, {port, 9042}]}, -% {http, global, conn1, [{workers, 50}], [{server, \"http://server:8080\"}]}, -% {cassandra, global, default, [{workers, 100}], [{servers, [{\"server1\", 9042}]}, {keyspace, \"big_mongooseim\"}]}, -% {rdbms, global, default, [{workers, 10}], [{server, {mysql, \"server\", 3306, \"database\", \"username\", \"password\"}}]} -%]}."}. -{rdbms_server_type, "%% {rdbms_server_type, pgsql}."}. -{http_api_old_endpoint, "{5288, \"127.0.0.1\"}"}. -{http_api_endpoint, "{8088, \"127.0.0.1\"}"}. -{http_api_client_endpoint, "8089"}. -{s2s_use_starttls, "{s2s_use_starttls, optional}."}. -{s2s_certfile, "{s2s_certfile, \"priv/ssl/fake_server.pem\"}."}. -{sasl_mechanisms, "%%{sasl_mechanisms, [cyrsasl_scram_sha1, cyrsasl_plain]}."}. - -{all_metrics_are_global, "false"}. - -%% Defined in Makefile by appending configure.vars.config -%% Uncomment for manual release generation. -%{mongooseim_runner_user, ""}. -%{mongooseim_script_dir, "$(cd ${0%/*} && pwd)"}. -%{mongooseim_etc_dir, "$RUNNER_BASE_DIR/etc"}. -%{mongooseim_log_dir, "log"}. -%{mongooseim_mdb_dir, "$RUNNER_BASE_DIR/Mnesia.$NODE"}. -%{mongooseim_mdb_dir_toggle, "%"}. -%{mongooseim_lock_dir, "$MIM_DIR/var/lock"}. -%{mongooseim_nodetool_etc_dir, "etc"}. From 919e96aceab73359a755f101fb84fdd07c9d49c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Fri, 23 Oct 2020 15:56:16 +0200 Subject: [PATCH 010/104] Remove the config file not used by any test --- test/cassandra_SUITE_data/mongooseim.cfg | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 test/cassandra_SUITE_data/mongooseim.cfg diff --git a/test/cassandra_SUITE_data/mongooseim.cfg b/test/cassandra_SUITE_data/mongooseim.cfg deleted file mode 100644 index 771e2c318d..0000000000 --- a/test/cassandra_SUITE_data/mongooseim.cfg +++ /dev/null @@ -1,7 +0,0 @@ -{loglevel, 3}. -{hosts, ["localhost"] }. -{listen, []}. -{sm_backend, {mnesia, []}}. -{cassandra_server, []}. -{auth_method, internal}. -{modules, []}. From 629ca5da9237655572489db681804d592eeae382 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Fri, 23 Oct 2020 16:03:09 +0200 Subject: [PATCH 011/104] Remove remaining references to the 'cfg' format from comments --- src/config/mongoose_config_reload.erl | 6 +++--- src/ejabberd_cowboy.erl | 2 +- src/mod_carboncopy.erl | 3 +-- src/mod_last_riak.erl | 2 +- src/mod_stream_management.erl | 4 ++-- src/offline/mod_offline_riak.erl | 2 +- tools/setup_riak | 2 +- 7 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/config/mongoose_config_reload.erl b/src/config/mongoose_config_reload.erl index 74f8c31e7a..a006e35085 100644 --- a/src/config/mongoose_config_reload.erl +++ b/src/config/mongoose_config_reload.erl @@ -269,7 +269,7 @@ calculate_inconsistent_opts(Data=#{extended_node_states := ExtNodeStates}) -> Data#{inconsistent_loaded_local_common_options => Inconsistent}. %% Return a list with inconsistent options for each node. -%% Useful for inconsistent mongooseim.cfg debugging (when just hash is not enough). +%% Useful for inconsistent mongooseim.toml debugging (when just hash is not enough). inconsistent_node_opts(NodeOpts) -> {Nodes, OptLists} = lists:unzip(NodeOpts), OptSets = lists:map(fun sets:from_list/1, OptLists), @@ -352,7 +352,7 @@ cluster_reload_version_checks(Data) -> "Global configs should be the same for all nodes in cluster. " "ondisc_global_versions contains more than one unique config version. " "cluster_reload is not allowed to continue. " - "How to fix: ensure that mongooseim.cfg-s are the same for all nodes.", + "How to fix: ensure that mongooseim.toml-s are the same for all nodes.", %% To pass the check it should be ... all_same(loaded_global_versions, Data)), @@ -373,7 +373,7 @@ cluster_reload_version_checks(Data) -> "Local configs should be the same for all nodes in cluster. " "ondisc_local_versions contains more than one unique config version. " "cluster_reload is not allowed to continue. " - "How to fix: ensure that mongooseim.cfg-s are the same for all nodes.", + "How to fix: ensure that mongooseim.toml-s are the same for all nodes.", %% To pass the check it should be ... all_same(ondisc_local_versions, Data)), diff --git a/src/ejabberd_cowboy.erl b/src/ejabberd_cowboy.erl index 2a20bd60e6..5d106edfaa 100644 --- a/src/ejabberd_cowboy.erl +++ b/src/ejabberd_cowboy.erl @@ -289,7 +289,7 @@ maybe_insert_max_connections(TransportOpts, Opts) -> %% @doc %% Store trails, this need for generate swagger documentation %% Add to Trails each of modules where used trails behaviour -%% The modules must be added into `mongooseim.cfg`in `swagger` section +%% The modules must be added into `mongooseim.toml` in `swagger` section %% @end %% ------------------------------------------------------------------- trails_store(Modules) -> diff --git a/src/mod_carboncopy.erl b/src/mod_carboncopy.erl index 793a52dc4d..6b8214eb7a 100644 --- a/src/mod_carboncopy.erl +++ b/src/mod_carboncopy.erl @@ -3,8 +3,7 @@ %%% Author : Eric Cestari %%% Purpose : Message Carbons XEP-0280 0.8 %%% Created : 5 May 2008 by Mickael Remond -%%% Usage : Add the following line in the `modules` section of mongooseim.cfg: -%%% {mod_carboncopy, []} +%%% Usage : Add `mod_carboncopy` to the `modules` section of mongooseim.toml %%% %%% %%% ejabberd, Copyright (C) 2002-2014 ProcessOne diff --git a/src/mod_last_riak.erl b/src/mod_last_riak.erl index 7ff10c7df1..1cef3d2919 100644 --- a/src/mod_last_riak.erl +++ b/src/mod_last_riak.erl @@ -12,7 +12,7 @@ %%% @doc Riak backend for last activity XEP %%% %%% The backend uses the existing riak connection pool, which is "globally" defined in -%%% the ejebberd.cfg file. Therefore, we don't need to start anything in the init +%%% the mongooseim.toml file. Therefore, we don't need to start anything in the init %%% function. %%% %%% The module follows the approach taken by the other riak backends - it creates diff --git a/src/mod_stream_management.erl b/src/mod_stream_management.erl index 7570a2c030..e868a5fba9 100644 --- a/src/mod_stream_management.erl +++ b/src/mod_stream_management.erl @@ -12,7 +12,7 @@ remove_smid/5, session_cleanup/5]). -%% `mongooseim.cfg' options (don't use outside of tests) +%% `mongooseim.toml' options (don't use outside of tests) -export([get_buffer_max/1, set_buffer_max/1, get_ack_freq/1, @@ -118,7 +118,7 @@ do_remove_smid(Acc, SID) -> mongoose_acc:set(stream_mgmt, smid, MaybeSMID, Acc). %% -%% `mongooseim.cfg' options (don't use outside of tests) +%% `mongooseim.toml' options (don't use outside of tests) %% -spec get_buffer_max(pos_integer() | infinity | no_buffer) diff --git a/src/offline/mod_offline_riak.erl b/src/offline/mod_offline_riak.erl index 99b7e2a88e..b7eaa4256f 100644 --- a/src/offline/mod_offline_riak.erl +++ b/src/offline/mod_offline_riak.erl @@ -11,7 +11,7 @@ %%% @doc Riak backend for last activity XEP %%% %%% The backend uses the existing riak connection pool, which is "globally" defined in -%%% the ejebberd.cfg file. Therefore, we don't need to start anything in the init +%%% the mongooseim.toml file. Therefore, we don't need to start anything in the init %%% function. %%% %%% The module follows the approach taken by the other riak backends - it creates diff --git a/tools/setup_riak b/tools/setup_riak index 716de36d69..05c9da0c73 100755 --- a/tools/setup_riak +++ b/tools/setup_riak @@ -122,7 +122,7 @@ if [ "$RIAK_SECURITY" == "enabled" ]; then # "no function clause matching tls_v1:enum_to_oid(28)" error # on Riak's side running with Erlang R16. # https://github.com/basho/riak-erlang-client/issues/232#issuecomment-178612129 -# We also set ciphers in mongooseim.cfg +# We also set ciphers in mongooseim.toml # (see test.config presets in the big test directory). $RIAK_ADMIN security ciphers "AES256-SHA:DHE-RSA-AES128-SHA256" From 5101677077cca4f19d17e8373a7da58667b24cb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Fri, 23 Oct 2020 15:53:05 +0200 Subject: [PATCH 012/104] Remove the 'cfg' config parser code --- src/config/mongoose_config_parser.erl | 6 +- src/config/mongoose_config_parser_cfg.erl | 407 ---------------------- src/config/mongoose_config_terms.erl | 147 -------- 3 files changed, 3 insertions(+), 557 deletions(-) delete mode 100644 src/config/mongoose_config_parser_cfg.erl delete mode 100644 src/config/mongoose_config_terms.erl diff --git a/src/config/mongoose_config_parser.erl b/src/config/mongoose_config_parser.erl index 86466d5808..c0933f9d51 100644 --- a/src/config/mongoose_config_parser.erl +++ b/src/config/mongoose_config_parser.erl @@ -1,5 +1,5 @@ %% @doc Parsing and processing of MongooseIM config files -%% - parser backends: 'cfg' and 'toml' +%% - parser backends: only 'toml' %% - config state management -module(mongoose_config_parser). @@ -62,8 +62,8 @@ parse_file(FileName) -> ParserModule = parser_module(filename:extension(FileName)), ParserModule:parse_file(FileName). -parser_module(".toml") -> mongoose_config_parser_toml; -parser_module(".cfg") -> mongoose_config_parser_cfg. +%% Only the TOML format is supported +parser_module(".toml") -> mongoose_config_parser_toml. %% State API diff --git a/src/config/mongoose_config_parser_cfg.erl b/src/config/mongoose_config_parser_cfg.erl deleted file mode 100644 index 1b61afb39c..0000000000 --- a/src/config/mongoose_config_parser_cfg.erl +++ /dev/null @@ -1,407 +0,0 @@ -%% @doc Config parsing and processing for the 'cfg' format --module(mongoose_config_parser_cfg). - --behaviour(mongoose_config_parser). - --export([parse_file/1]). - -%% For tests --export([parse_terms/1]). - --include("mongoose.hrl"). --include("ejabberd_config.hrl"). - --type macro() :: {macro_key(), macro_value()}. - -%% The atom must have all characters in uppercase. --type macro_key() :: atom(). - --type macro_value() :: term(). - --type known_term() :: override_global - | override_local - | override_acls - | {acl, _, _} - | {alarms, _} - | {access, _, _} - | {shaper, _, _} - | {host, _} - | {hosts, _} - | {host_config, _, _} - | {listen, _} - | {language, _} - | {sm_backend, _} - | {outgoing_s2s_port, integer()} - | {outgoing_s2s_options, _, integer()} - | {{s2s_addr, _}, _} - | {s2s_dns_options, [tuple()]} - | {s2s_use_starttls, integer()} - | {s2s_certfile, _} - | {domain_certfile, _, _} - | {node_type, _} - | {cluster_nodes, _} - | {registration_timeout, integer()} - | {mongooseimctl_access_commands, list()} - | {loglevel, _} - | {max_fsm_queue, _} - | {sasl_mechanisms, _} - | host_term(). - --type host_term() :: {acl, _, _} - | {access, _, _} - | {shaper, _, _} - | {host, _} - | {hosts, _}. - -%%-------------------------------------------------------------------- -%% Configuration parsing -%%-------------------------------------------------------------------- - --spec parse_file(FileName :: string()) -> mongoose_config_parser:state(). -parse_file(FileName) -> - Terms = mongoose_config_terms:get_plain_terms_file(FileName), - parse_terms(Terms). - --spec parse_terms(term()) -> mongoose_config_parser:state(). -parse_terms(Terms) -> - State = just_parse_terms(Terms), - State2 = mongoose_config_parser:dedup_state_opts(State), - mongoose_config_parser:add_dep_modules(State2). - -just_parse_terms(Terms) -> - State = lists:foldl(fun search_hosts_and_pools/2, mongoose_config_parser:new_state(), Terms), - TermsWExpandedMacros = replace_macros(Terms), - lists:foldl(fun process_term/2, State, TermsWExpandedMacros). - --spec search_hosts_and_pools({host|hosts, - [mongoose_config_parser:host()] | mongoose_config_parser:host()}, - mongoose_config_parser:state()) -> any(). -search_hosts_and_pools({host, Host}, State) -> - search_hosts_and_pools({hosts, [Host]}, State); -search_hosts_and_pools({hosts, Hosts}, State) -> - case mongoose_config_parser:state_to_host_opts(State) of - [] -> - add_hosts_to_option(Hosts, State); - OldHosts -> - ?LOG_ERROR(#{what => too_many_hosts_definitions, - new_hosts => Hosts, old_hosts => OldHosts}), - exit(#{issue => "too many hosts definitions", - new_hosts => Hosts, - old_hosts => OldHosts}) - end; -search_hosts_and_pools(_Term, State) -> - State. - --spec add_hosts_to_option(Hosts :: [mongoose_config_parser:host()], - State :: mongoose_config_parser:state()) -> - mongoose_config_parser:state(). -add_hosts_to_option(Hosts, State) -> - PrepHosts = normalize_hosts(Hosts), - add_option(hosts, PrepHosts, mongoose_config_parser:set_hosts(PrepHosts, State)). - --spec normalize_hosts([mongoose_config_parser:host()]) -> [binary() | tuple()]. -normalize_hosts(Hosts) -> - normalize_hosts(Hosts, []). - - -normalize_hosts([], PrepHosts) -> - lists:reverse(PrepHosts); -normalize_hosts([Host | Hosts], PrepHosts) -> - case jid:nodeprep(host_to_binary(Host)) of - error -> - ?LOG_ERROR(#{what => invalid_hostname_in_config, hostname => Host}), - erlang:error(#{issue => invalid_hostname, - hostname => Host}); - PrepHost -> - normalize_hosts(Hosts, [PrepHost | PrepHosts]) - end. - -host_to_binary(Host) -> - unicode:characters_to_binary(Host). - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%% Support for Macro - -%% @doc Replace the macros with their defined values. --spec replace_macros(Terms :: [term()]) -> [term()]. -replace_macros(Terms) -> - {TermsOthers, Macros} = split_terms_macros(Terms), - replace(TermsOthers, Macros). - - -%% @doc Split Terms into normal terms and macro definitions. --spec split_terms_macros(Terms :: [term()]) -> {[term()], [macro()]}. -split_terms_macros(Terms) -> - lists:foldl(fun split_terms_macros_fold/2, {[], []}, Terms). - --spec split_terms_macros_fold(any(), Acc) -> Acc when - Acc :: {[term()], [{Key :: any(), Value :: any()}]}. -split_terms_macros_fold({define_macro, Key, Value} = Term, {TOs, Ms}) -> - case is_macro_name(Key) of - true -> - {TOs, Ms ++ [{Key, Value}]}; - false -> - exit({macro_not_properly_defined, Term}) - end; -split_terms_macros_fold(Term, {TOs, Ms}) -> - {TOs ++ [Term], Ms}. - - -%% @doc Recursively replace in Terms macro usages with the defined value. --spec replace(Terms :: [term()], - Macros :: [macro()]) -> [term()]. -replace([], _) -> - []; -replace([Term | Terms], Macros) -> - [replace_term(Term, Macros) | replace(Terms, Macros)]. - - -replace_term(Key, Macros) when is_atom(Key) -> - case is_macro_name(Key) of - true -> - case proplists:get_value(Key, Macros) of - undefined -> exit({undefined_macro, Key}); - Value -> Value - end; - false -> - Key - end; -replace_term({use_macro, Key, Value}, Macros) -> - proplists:get_value(Key, Macros, Value); -replace_term(Term, Macros) when is_list(Term) -> - replace(Term, Macros); -replace_term(Term, Macros) when is_tuple(Term) -> - List = tuple_to_list(Term), - List2 = replace(List, Macros), - list_to_tuple(List2); -replace_term(Term, _) -> - Term. - -%% Check is the term is a config macro --spec is_macro_name(atom()) -> boolean(). -is_macro_name(Atom) when is_atom(Atom) -> - is_all_uppercase(Atom) andalso has_any_uppercase(Atom); -is_macro_name(_) -> - false. - --spec is_all_uppercase(atom()) -> boolean(). -is_all_uppercase(Atom) -> - String = erlang:atom_to_list(Atom), - lists:all(fun(C) when C >= $a, C =< $z -> false; - (_) -> true - end, String). - -has_any_uppercase(Atom) -> - String = erlang:atom_to_list(Atom), - lists:any(fun(C) when C >= $A, C =< $Z -> true; - (_) -> false - end, String). - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%% Process terms - --spec process_term(Term :: known_term(), - State :: mongoose_config_parser:state()) -> mongoose_config_parser:state(). -process_term(Term, State) -> - case Term of - override_global -> - mongoose_config_parser:override_global(State); - override_local -> - mongoose_config_parser:override_local(State); - override_acls -> - mongoose_config_parser:override_acls(State); - {acl, _ACLName, _ACLData} -> - process_host_term(Term, global, State); - {alarms, Env} -> - add_option(alarms, Env, State); - {access, _RuleName, _Rules} -> - process_host_term(Term, global, State); - {shaper, _Name, _Data} -> - %%lists:foldl(fun(Host, S) -> process_host_term(Term, Host, S) end, - %% State, State#state.hosts); - process_host_term(Term, global, State); - {host, _Host} -> - State; - {hosts, _Hosts} -> - State; - {host_config, Host, Terms} -> - lists:foldl(fun(T, S) -> - process_host_term(T, list_to_binary(Host), S) end, - State, Terms); - {listen, Listeners} -> - Listeners2 = - lists:map( - fun({PortIP, Module, Opts}) -> - {Port, IPT, _, _, Proto, OptsClean} = - ejabberd_listener:parse_listener_portip(PortIP, Opts), - {{Port, IPT, Proto}, Module, OptsClean} - end, - Listeners), - add_option(listen, Listeners2, State); - {language, Val} -> - add_option(language, list_to_binary(Val), State); - {sm_backend, Val} -> - add_option(sm_backend, Val, State); - {rdbms_server_type, Val} -> - add_option(rdbms_server_type, Val, State); - {outgoing_s2s_port, Port} -> - add_option(outgoing_s2s_port, Port, State); - {outgoing_s2s_options, Methods, Timeout} -> - State1 = add_option(outgoing_s2s_families, Methods, State), - add_option(outgoing_s2s_timeout, Timeout, State1); - {{s2s_addr, Host}, Addr} -> - add_option({s2s_addr, list_to_binary(Host)}, Addr, State); - {{global_distrib_addr, Host}, Addr} -> - add_option({global_distrib_addr, list_to_binary(Host)}, Addr, State); - {s2s_dns_options, PropList} -> - add_option(s2s_dns_options, PropList, State); - {s2s_use_starttls, Port} -> - add_option(s2s_use_starttls, Port, State); - {s2s_ciphers, Ciphers} -> - add_option(s2s_ciphers, Ciphers, State); - {s2s_certfile, CertFile} -> - State2 = compact_global_option(required_files, [CertFile], State), - add_option(s2s_certfile, CertFile, State2); - {domain_certfile, Domain, CertFile} -> - State2 = compact_global_option(required_files, [CertFile], State), - add_option({domain_certfile, Domain}, CertFile, State2); - {node_type, NodeType} -> - add_option(node_type, NodeType, State); - {cluster_nodes, Nodes} -> - add_option(cluster_nodes, Nodes, State); - {watchdog_admins, Admins} -> - add_option(watchdog_admins, Admins, State); - {watchdog_large_heap, LH} -> - add_option(watchdog_large_heap, LH, State); - {registration_timeout, Timeout} -> - add_option(registration_timeout, Timeout, State); - {mongooseimctl_access_commands, ACs} -> - add_option(mongooseimctl_access_commands, ACs, State); - {routing_modules, Mods} -> - add_option(routing_modules, Mods, State); - {loglevel, Loglevel} -> - add_option(loglevel, Loglevel, State); - {max_fsm_queue, N} -> - add_option(max_fsm_queue, N, State); - {sasl_mechanisms, Mechanisms} -> - add_option(sasl_mechanisms, Mechanisms, State); - {all_metrics_are_global, Value} -> - add_option(all_metrics_are_global, Value, State); - {cowboy_server_name, Value} -> - add_option(cowboy_server_name, Value, State); - {services, Value} -> - add_option(services, Value, State); - {_Opt, _Val} -> - lists:foldl(fun(Host, S) -> process_host_term(Term, Host, S) end, - State, mongoose_config_parser:state_to_host_opts(State)) - end. - --spec process_host_term(Term :: host_term(), - Host :: acl:host(), - State :: mongoose_config_parser:state()) -> mongoose_config_parser:state(). -process_host_term(Term, Host, State) -> - case Term of - {acl, ACLName, ACLData} -> - OptRec = acl:to_record(Host, ACLName, ACLData), - append_option(OptRec, State); - {access, RuleName, Rules} -> - append_global_opt({access, RuleName, Host}, Rules, State); - {shaper, Name, Data} -> - append_global_opt({shaper, Name, Host}, Data, State); - {host, Host} -> - State; - {hosts, _Hosts} -> - State; - {outgoing_pools, Pools} when is_list(Pools) -> - add_option(outgoing_pools, Pools, State); - {node_specific_options, NodeOpts} -> - add_option(node_specific_options, NodeOpts, State); - {Opt, Val} -> - add_option({Opt, Host}, Val, State) - end. - --spec add_option(Opt :: mongoose_config_parser:key(), - Val :: mongoose_config_parser:value(), - State :: mongoose_config_parser:state()) -> mongoose_config_parser:state(). -add_option(Opt, Val, State) -> - Table = opt_table(Opt), - add_option(Table, Opt, Val, State). - -add_option(config, Opt, Val, State) -> - append_global_opt(Opt, Val, State); -add_option(local_config, {{add, OptName}, Host}, Val, State) -> - compact_option({OptName, Host}, Val, State); -add_option(local_config, Opt, Val, State) -> - append_local_opt(Opt, Val, State). - -append_global_opt(OptName, OptValue, State) -> - OptRec = #config{key = OptName, value = OptValue}, - append_option(OptRec, State). - -append_local_opt(OptName, OptValue, State) -> - OptRec = #local_config{key = OptName, value = OptValue}, - append_option(OptRec, State). - -append_option(OptRec, State) -> - Opts = mongoose_config_parser:get_opts(State), - mongoose_config_parser:set_opts([OptRec | Opts], State). - -%% Merges two values of a local option -compact_option(Opt, Val, State) -> - Opts = mongoose_config_parser:get_opts(State), - Opts2 = compact(Opt, Val, Opts, []), - mongoose_config_parser:set_opts(Opts2, State). - -compact({OptName, Host} = Opt, Val, [], Os) -> - %% The option is defined for host using host_config before the global option. - %% The host_option can be overwritten. - %% TODO or maybe not. We need a test. - ?LOG_WARNING(#{what => host_config_option_can_be_overwritten, - text => <<"define global options before host options">>, - option_name => OptName, host => Host}), - [#local_config{key = Opt, value = Val}] ++ Os; -%% Traverse the list of the options already parsed -compact(Opt, Val, [#local_config{key = Opt, value = OldVal} | Os1], Os2) -> - %% If the key of a local_config matches the Opt that wants to be added - OptRec = #local_config{key = Opt, value = Val ++ OldVal}, - %% Then prepend the new value to the list of old values - Os2 ++ [OptRec] ++ Os1; -compact(Opt, Val, [O | Os1], Os2) -> - compact(Opt, Val, Os1, Os2 ++ [O]). - - -%% Merges two values of a global option -compact_global_option(Opt, Val, State) when is_list(Val) -> - Opts2 = compact_global(Opt, Val, mongoose_config_parser:get_opts(State), []), - mongoose_config_parser:set_opts(Opts2, State). - -compact_global(Opt, Val, [], Os) -> - [#config{key = Opt, value = Val}] ++ Os; -%% Traverse the list of the options already parsed -compact_global(Opt, Val, [#config{key = Opt, value = OldVal} | Os1], Os2) -> - %% If the key of a local_config matches the Opt that wants to be added - OptRec = #config{key = Opt, value = Val ++ OldVal}, - %% Then prepend the new value to the list of old values - Os2 ++ [OptRec] ++ Os1; -compact_global(Opt, Val, [O | Os1], Os2) -> - compact_global(Opt, Val, Os1, Os2 ++ [O]). - - -opt_table(Opt) -> - case is_global_option(Opt) of - true -> - config; - false -> - local_config - end. - -is_global_option(Opt) -> - lists:member(Opt, global_options()). - -global_options() -> - [ - hosts, - language, - sm_backend, - node_specific_options - ]. diff --git a/src/config/mongoose_config_terms.erl b/src/config/mongoose_config_terms.erl deleted file mode 100644 index 0f48ec249f..0000000000 --- a/src/config/mongoose_config_terms.erl +++ /dev/null @@ -1,147 +0,0 @@ -%%% @doc Parsing of the 'cfg' file to Erlang terms --module(mongoose_config_terms). - --export([get_plain_terms_file/1]). - --include("mongoose.hrl"). --include("ejabberd_config.hrl"). - -%% @doc Read an ejabberd configuration file and return the terms. -%% Input is an absolute or relative path to an ejabberd config file. -%% Returns a list of plain terms, -%% in which the options 'include_config_file' were parsed -%% and the terms in those files were included. --spec get_plain_terms_file(string()) -> [term()]. -get_plain_terms_file(File1) -> - File = mongoose_config_utils:get_absolute_path(File1), - case file:consult(File) of - {ok, Terms} -> - include_config_files(Terms); - {error, {LineNumber, erl_parse, _ParseMessage} = Reason} -> - ExitText = describe_config_problem(File, Reason, LineNumber), - ?LOG_ERROR(#{what => ejabberd_config_file_loading_failed, - file => File, line => LineNumber, reason => Reason}), - mongoose_config_utils:exit_or_halt(ExitText); - {error, Reason} -> - ExitText = describe_config_problem(File, Reason), - ?LOG_ERROR(#{what => mim_config_file_loading_failed, - file => File, reason => Reason}), - mongoose_config_utils:exit_or_halt(ExitText) - end. - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%% Support for 'include_config_file' - -%% @doc Include additional configuration files in the list of terms. --spec include_config_files([term()]) -> [term()]. -include_config_files(Terms) -> - Filenames = config_filenames_to_include(Terms), - Configs = lists:map(fun(Filename) -> - {Filename, get_plain_terms_file(Filename)} - end, Filenames), - include_config_files(Terms, Configs). - -config_filenames_to_include([{include_config_file, Filename} | Terms]) -> - [Filename|config_filenames_to_include(Terms)]; -config_filenames_to_include([{include_config_file, Filename, _Options} | Terms]) -> - [Filename|config_filenames_to_include(Terms)]; -config_filenames_to_include([_Other | Terms]) -> - config_filenames_to_include(Terms); -config_filenames_to_include([]) -> - []. - -include_config_files(Terms, Configs) -> - include_config_files(Terms, Configs, []). - -include_config_files([], _Configs, Res) -> - Res; -include_config_files([{include_config_file, Filename} | Terms], Configs, Res) -> - include_config_files([{include_config_file, Filename, []} | Terms], - Configs, Res); -include_config_files([{include_config_file, Filename, Options} | Terms], - Configs, Res) -> - IncludedTerms = find_plain_terms_for_file(Filename, Configs), - Disallow = proplists:get_value(disallow, Options, []), - IncludedTerms2 = delete_disallowed(Disallow, IncludedTerms), - AllowOnly = proplists:get_value(allow_only, Options, all), - IncludedTerms3 = keep_only_allowed(AllowOnly, IncludedTerms2), - include_config_files(Terms, Configs, Res ++ IncludedTerms3); -include_config_files([Term | Terms], Configs, Res) -> - include_config_files(Terms, Configs, Res ++ [Term]). - -find_plain_terms_for_file(Filename, Configs) -> - case lists:keyfind(Filename, 1, Configs) of - false -> - %% Terms were not provided by caller for this file - erlang:error({config_not_found, Filename}); - {Filename, Terms} -> - Terms - end. - -%% @doc Filter from the list of terms the disallowed. -%% Returns a sublist of Terms without the ones which first element is -%% included in Disallowed. --spec delete_disallowed(Disallowed :: [atom()], - Terms :: [term()]) -> [term()]. -delete_disallowed(Disallowed, Terms) -> - lists:foldl( - fun(Dis, Ldis) -> - delete_disallowed2(Dis, Ldis) - end, - Terms, - Disallowed). - -delete_disallowed2(Disallowed, [H | T]) -> - case element(1, H) of - Disallowed -> - ?LOG_WARNING(#{what => ignore_disallowed_option, option => Disallowed}), - delete_disallowed2(Disallowed, T); - _ -> - [H | delete_disallowed2(Disallowed, T)] - end; -delete_disallowed2(_, []) -> - []. - -%% @doc Keep from the list only the allowed terms. -%% Returns a sublist of Terms with only the ones which first element is -%% included in Allowed. --spec keep_only_allowed(Allowed :: [atom()], - Terms :: [term()]) -> [term()]. -keep_only_allowed(all, Terms) -> - Terms; -keep_only_allowed(Allowed, Terms) -> - {As, NAs} = lists:partition( - fun(Term) -> - lists:member(element(1, Term), Allowed) - end, - Terms), - [?LOG_WARNING(#{what => ignore_disallowed_option, option => NA}) - || NA <- NAs], - As. - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%% Errors reading the config file - --type config_problem() :: atom() | {integer(), atom() | tuple(), _}. % spec me better - --spec describe_config_problem(Filename :: string(), - Reason :: config_problem()) -> string(). -describe_config_problem(Filename, Reason) -> - Text1 = lists:flatten("Problem loading MongooseIM config file " ++ Filename), - Text2 = lists:flatten(" : " ++ file:format_error(Reason)), - ExitText = Text1 ++ Text2, - ExitText. - - --spec describe_config_problem(Filename :: string(), - Reason :: config_problem(), - Line :: pos_integer()) -> string(). -describe_config_problem(Filename, Reason, LineNumber) -> - Text1 = lists:flatten("Problem loading ejabberd config file " ++ Filename), - Text2 = lists:flatten(" approximately in the line " - ++ file:format_error(Reason)), - ExitText = Text1 ++ Text2, - Lines = mongoose_config_utils:get_config_lines(Filename, LineNumber, 10, 3), - ?LOG_ERROR(#{what => mim_config_file_loading_failed, lines => Lines, - text => <<"The following lines from your configuration file might be relevant to the error">>}), - ExitText. From 225ba39bb9d51ffc28397d3856192370679afd0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Thu, 5 Nov 2020 17:39:20 +0100 Subject: [PATCH 013/104] Sort expected TOML options for easier debugging --- test/config_parser_SUITE.erl | 8 +- .../miscellaneous.options | 50 +- test/config_parser_SUITE_data/modules.options | 520 +++++++++--------- .../mongooseim-pgsql.options | 270 ++++----- .../outgoing_pools.options | 56 +- .../config_parser_SUITE_data/s2s_only.options | 4 +- 6 files changed, 455 insertions(+), 453 deletions(-) diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index 69176b2b90..139a578786 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -3025,11 +3025,13 @@ test_config_file(Config, File) -> %% Save the parsed TOML options %% - for debugging %% - to update tests after a config change - always check the diff! - FormattedOpts = [io_lib:format("~p.~n", [Opt]) || Opt <- TOMLOpts], - file:write_file(OptionsPath ++ "-parsed", lists:sort(FormattedOpts)), - + save_opts(OptionsPath ++ ".parsed", TOMLOpts), compare_config(ExpectedOpts, TOMLOpts). +save_opts(Path, Opts) -> + FormattedOpts = [io_lib:format("~p.~n", [Opt]) || Opt <- lists:sort(Opts)], + file:write_file(Path, FormattedOpts). + compare_config(C1, C2) -> compare_unordered_lists(C1, C2, fun handle_config_option/2). diff --git a/test/config_parser_SUITE_data/miscellaneous.options b/test/config_parser_SUITE_data/miscellaneous.options index 69b435f4b9..5876cff55b 100644 --- a/test/config_parser_SUITE_data/miscellaneous.options +++ b/test/config_parser_SUITE_data/miscellaneous.options @@ -3,14 +3,14 @@ {local_config,listen, [{{5280,{0,0,0,0},tcp}, ejabberd_cowboy, - [{transport_options,[{num_acceptors,10},{max_connections,1024}]}, - {modules, + [{modules, [{"_","/ws-xmpp",mod_websockets, [{ejabberd_service, [{access,all}, - {shaper_rule,fast}, + {max_fsm_queue,1000}, {password,"secret"}, - {max_fsm_queue,1000}]}]}]}]}]}. + {shaper_rule,fast}]}]}]}, + {transport_options,[{max_connections,1024},{num_acceptors,10}]}]}]}. {local_config,mongooseimctl_access_commands, [{local,["join_cluster"],[{node,"mongooseim@prime"}]}]}. {local_config,rdbms_server_type,mssql}. @@ -18,43 +18,43 @@ [mongoose_router_global,mongoose_router_localdomain]}. {local_config,services, [{service_mongoose_system_metrics, - [report, - {initial_report,300000}, + [{initial_report,300000}, {periodic_report,10800000}, + report, {tracking_id,"UA-123456789"}]}]}. {local_config,{allow_multiple_connections,<<"anonymous.localhost">>},true}. {local_config,{allow_multiple_connections,<<"localhost">>},true}. {local_config,{anonymous_protocol,<<"anonymous.localhost">>},sasl_anon}. {local_config,{anonymous_protocol,<<"localhost">>},sasl_anon}. {local_config,{auth_opts,<<"anonymous.localhost">>}, - [{ldap_pool_tag,default}, - {ldap_bind_pool_tag,bind}, - {ldap_base,"ou=Users,dc=esl,dc=com"}, - {ldap_uids,["uid",{"uid2","%u"}]}, - {ldap_filter,"(&(objectClass=shadowAccount)(memberOf=Jabber Users))"}, - {ldap_dn_filter,{"(&(name=%s)(owner=%D)(user=%u@%d))",["sn"]}}, - {ldap_local_filter,{equal,{"accountStatus",["enabled"]}}}, - {ldap_deref,never}, - {extauth_program,"/usr/bin/authenticator"}, + [{extauth_program,"/usr/bin/authenticator"}, {basic_auth,"admin:admin"}, - {jwt_secret,"secret123"}, {jwt_algorithm,"RS256"}, + {jwt_secret,"secret123"}, {jwt_username_key,user}, - {bucket_type,<<"user_bucket">>}]}. -{local_config,{auth_opts,<<"localhost">>}, - [{ldap_pool_tag,default}, - {ldap_bind_pool_tag,bind}, {ldap_base,"ou=Users,dc=esl,dc=com"}, - {ldap_uids,["uid",{"uid2","%u"}]}, - {ldap_filter,"(&(objectClass=shadowAccount)(memberOf=Jabber Users))"}, + {ldap_bind_pool_tag,bind}, + {ldap_deref,never}, {ldap_dn_filter,{"(&(name=%s)(owner=%D)(user=%u@%d))",["sn"]}}, + {ldap_filter,"(&(objectClass=shadowAccount)(memberOf=Jabber Users))"}, {ldap_local_filter,{equal,{"accountStatus",["enabled"]}}}, - {ldap_deref,never}, - {extauth_program,"/usr/bin/authenticator"}, + {ldap_pool_tag,default}, + {ldap_uids,["uid",{"uid2","%u"}]}, + {bucket_type,<<"user_bucket">>}]}. +{local_config,{auth_opts,<<"localhost">>}, + [{extauth_program,"/usr/bin/authenticator"}, {basic_auth,"admin:admin"}, - {jwt_secret,"secret123"}, {jwt_algorithm,"RS256"}, + {jwt_secret,"secret123"}, {jwt_username_key,user}, + {ldap_base,"ou=Users,dc=esl,dc=com"}, + {ldap_bind_pool_tag,bind}, + {ldap_deref,never}, + {ldap_dn_filter,{"(&(name=%s)(owner=%D)(user=%u@%d))",["sn"]}}, + {ldap_filter,"(&(objectClass=shadowAccount)(memberOf=Jabber Users))"}, + {ldap_local_filter,{equal,{"accountStatus",["enabled"]}}}, + {ldap_pool_tag,default}, + {ldap_uids,["uid",{"uid2","%u"}]}, {bucket_type,<<"user_bucket">>}]}. {local_config,{extauth_instances,<<"anonymous.localhost">>},1}. {local_config,{extauth_instances,<<"localhost">>},1}. diff --git a/test/config_parser_SUITE_data/modules.options b/test/config_parser_SUITE_data/modules.options index e53c5f9292..e8258328a9 100644 --- a/test/config_parser_SUITE_data/modules.options +++ b/test/config_parser_SUITE_data/modules.options @@ -2,76 +2,76 @@ {local_config, {modules,<<"dummy_host">>}, [{mod_jingle_sip, - [{proxy_host,"localhost"}, - {proxy_port,5600}, - {listen_port,5600}, + [{listen_port,5600}, {local_host,"localhost"}, + {proxy_host,"localhost"}, + {proxy_port,5600}, {sdp_origin,"127.0.0.1"}]}, {mod_shared_roster_ldap, [{ldap_base,"ou=Users,dc=ejd,dc=com"}, + {ldap_filter,"(objectClass=inetOrgPerson)"}, + {ldap_group_cache_validity,1}, {ldap_groupattr,"ou"}, {ldap_memberattr,"cn"}, - {ldap_userdesc,"cn"}, - {ldap_filter,"(objectClass=inetOrgPerson)"}, {ldap_rfilter,"(objectClass=inetOrgPerson)"}, - {ldap_group_cache_validity,1}, - {ldap_user_cache_validity,1}]}, + {ldap_user_cache_validity,1}, + {ldap_userdesc,"cn"}]}, {mod_mam_rdbms_user,[muc,pm]}, {mod_global_distrib, - [{global_host,"example.com"}, - {local_host,"datacenter1.example.com"}, + [{bounce,[{max_retries,3},{resend_after_ms,300}]}, + {cache,[{domain_lifetime_seconds,60}]}, {connections, - [{endpoints,[{"172.16.0.2",5555}]}, - {advertised_endpoints,[{"172.16.0.2",5555}]}, + [{advertised_endpoints,[{"172.16.0.2",5555}]}, {connections_per_endpoint,30}, - {tls_opts,[{certfile,"priv/dc1.pem"},{cafile,"priv/ca.pem"}]}]}, - {cache,[{domain_lifetime_seconds,60}]}, - {bounce,[{resend_after_ms,300},{max_retries,3}]}, + {endpoints,[{"172.16.0.2",5555}]}, + {tls_opts,[{cafile,"priv/ca.pem"},{certfile,"priv/dc1.pem"}]}]}, + {global_host,"example.com"}, + {local_host,"datacenter1.example.com"}, {redis,[{pool,global_distrib}]}]}, {mod_register, - [{welcome_message,{"Subject","Body"}}, - {access,all}, + [{access,all}, + {password_strength,32}, {registration_watchers,[<<"JID1">>,<<"JID2">>]}, - {password_strength,32}]}, + {welcome_message,{"Subject","Body"}}]}, {mod_mam_rdbms_async_pool_writer,[pm]}, {mod_adhoc,[{iqdisc,one_queue},{report_commands_node,true}]}, + {mod_muc, + [{access,muc}, + {access_create,muc_create}, + {default_room_options, + [{affiliations, + [{{<<"alice">>,<<"localhost">>,<<"resource1">>},member}, + {{<<"bob">>,<<"localhost">>,<<"resource2">>},owner}]}, + {password_protected,true}]}, + {host,"muc.example.com"}, + {http_auth_pool,my_auth_pool}]}, {mod_event_pusher_sns, [{access_key_id,"AKIAIOSFODNN7EXAMPLE"}, - {secret_access_key,"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"}, - {region,"eu-west-1"}, {account_id,"123456789012"}, - {sns_host,"sns.eu-west-1.amazonaws.com"}, {muc_host,"conference.HOST"}, + {muc_messages_topic,"user_messagegroup_sent"}, {plugin_module,mod_event_pusher_sns_defaults}, - {presence_updates_topic,"user_presence_updated"}, {pm_messages_topic,"user_message_sent"}, - {muc_messages_topic,"user_messagegroup_sent"}, {pool_size,100}, + {presence_updates_topic,"user_presence_updated"}, {publish_retry_count,2}, - {publish_retry_time_ms,50}]}, - {mod_muc, - [{host,"muc.example.com"}, - {access,muc}, - {access_create,muc_create}, - {http_auth_pool,my_auth_pool}, - {default_room_options, - [{password_protected,true}, - {affiliations, - [{{<<"alice">>,<<"localhost">>,<<"resource1">>},member}, - {{<<"bob">>,<<"localhost">>,<<"resource2">>},owner}]}]}]}, + {publish_retry_time_ms,50}, + {region,"eu-west-1"}, + {secret_access_key,"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"}, + {sns_host,"sns.eu-west-1.amazonaws.com"}]}, {mod_ping, - [{send_pings,true}, - {ping_interval,60}, - {timeout_action,none}, - {ping_req_timeout,32}]}, + [{ping_interval,60}, + {ping_req_timeout,32}, + {send_pings,true}, + {timeout_action,none}]}, {mod_mam, [{archive_chat_markers,true}, {full_text_search,false}, {is_archivable_message,mod_mam_utils}, {no_stanzaid_element,true}]}, {mod_disco, - [{iqdisc,one_queue}, - {extra_domains,[<<"some_domain">>,<<"another_domain">>]}, + [{extra_domains,[<<"some_domain">>,<<"another_domain">>]}, + {iqdisc,one_queue}, {server_info, [{all,"abuse-address",["admin@example.com"]}, {[mod_muc,mod_disco], @@ -80,9 +80,9 @@ {users_can_see_hidden_services,true}]}, {mod_event_pusher_http, [{configs, - [[{pool_name,http_pool}, + [[{callback_module,mod_event_pusher_http_defaults}, {path,"/notifications"}, - {callback_module,mod_event_pusher_http_defaults}]]}]}, + {pool_name,http_pool}]]}]}, {mod_event_pusher_hook_translator,[]}, {mod_mam_mnesia_prefs,[muc]}, {mod_mam_muc, @@ -91,26 +91,26 @@ {host,"muc.example.com"}, {is_archivable_message,mod_mam_utils}]}, {mod_event_pusher_rabbit, - [{presence_exchange,[{name,<<"presence">>},{type,<<"topic">>}]}, - {chat_msg_exchange, + [{chat_msg_exchange, [{name,<<"chat_msg">>}, - {sent_topic,<<"chat_msg_sent">>}, - {recv_topic,<<"chat_msg_recv">>}]}, + {recv_topic,<<"chat_msg_recv">>}, + {sent_topic,<<"chat_msg_sent">>}]}, {groupchat_msg_exchange, [{name,<<"groupchat_msg">>}, - {sent_topic,<<"groupchat_msg_sent">>}, - {recv_topic,<<"groupchat_msg_recv">>}]}]}, + {recv_topic,<<"groupchat_msg_recv">>}, + {sent_topic,<<"groupchat_msg_sent">>}]}, + {presence_exchange,[{name,<<"presence">>},{type,<<"topic">>}]}]}, {mod_vcard, - [{matches,1}, - {search,true}, - {host,"directory.example.com"}, - {ldap_vcard_map, - [{<<"FAMILY">>,<<"%s">>,[<<"sn">>]}, - {<<"FN">>,<<"%s">>,[<<"displayName">>]}]}, + [{host,"directory.example.com"}, {ldap_search_fields, [{<<"User">>,<<"%u">>},{<<"Full Name">>,<<"displayName">>}]}, {ldap_search_reported, - [{<<"Full Name">>,<<"FN">>},{<<"Given Name">>,<<"FIRST">>}]}]}, + [{<<"Full Name">>,<<"FN">>},{<<"Given Name">>,<<"FIRST">>}]}, + {ldap_vcard_map, + [{<<"FAMILY">>,<<"%s">>,[<<"sn">>]}, + {<<"FN">>,<<"%s">>,[<<"displayName">>]}]}, + {matches,1}, + {search,true}]}, {mod_keystore, [{keys, [{access_secret,ram}, @@ -121,17 +121,18 @@ {mod_bosh, [{inactivity,20}, {max_wait,infinity}, - {server_acks,true}, - {maxpause,120}]}, + {maxpause,120}, + {server_acks,true}]}, {mod_push_service_mongoosepush, - [{pool_name,mongoose_push_http}, - {api_version,"v3"}, - {max_http_connections,100}]}, + [{api_version,"v3"}, + {max_http_connections,100}, + {pool_name,mongoose_push_http}]}, {mod_auth_token, [{{validity_period,access},{13,minutes}}, {{validity_period,refresh},{13,days}}]}, {mod_extdisco, - [[{host,"stun1"}, + [[{host,"192.168.0.1"},{type,turn}], + [{host,"stun1"}, {password,"password"}, {port,3478}, {transport,"udp"}, @@ -142,18 +143,17 @@ {port,2222}, {transport,"tcp"}, {type,stun}, - {username,"username"}], - [{host,"192.168.0.1"},{type,turn}]]}, + {username,"username"}]]}, {mod_mam_cache_user,[muc,pm]}, - {mod_roster,[{versioning,true},{store_current_id,true}]}, + {mod_roster,[{store_current_id,true},{versioning,true}]}, {mod_http_upload, - [{host,"upload.@HOST@"}, - {backend,s3}, + [{backend,s3}, {expiration_time,120}, - {s3,[{bucket_url,"https://s3-eu-west-1.amazonaws.com/mybucket"}, - {region,"eu-west-1"}, + {host,"upload.@HOST@"}, + {s3,[{access_key_id,"AKIAIOSFODNN7EXAMPLE"}, {add_acl,true}, - {access_key_id,"AKIAIOSFODNN7EXAMPLE"}, + {bucket_url,"https://s3-eu-west-1.amazonaws.com/mybucket"}, + {region,"eu-west-1"}, {secret_access_key, "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"}]}]}, {mod_last,[{backend,mnesia},{iqdisc,{queues,10}}]}, @@ -166,102 +166,102 @@ {db_jid_format,mam_jid_rfc}, {db_message_format,mam_message_xml}]}, {mod_csi,[{buffer_max,40}]}, - {mod_caps,[{cache_size,1000},{cache_life_time,86}]}, + {mod_caps,[{cache_life_time,86},{cache_size,1000}]}, {mod_pubsub, [{access_createnode,pubsub_createnode}, - {ignore_pep_from_offline,false}, {backend,rdbms}, + {ignore_pep_from_offline,false}, {last_item_cache,mnesia}, {max_items_node,1000}, - {plugins,[<<"flat">>,<<"pep">>]}, - {pep_mapping,[{"urn:xmpp:microblog:0","mb"}]}]}, + {pep_mapping,[{"urn:xmpp:microblog:0","mb"}]}, + {plugins,[<<"flat">>,<<"pep">>]}]}, {mod_mam_rdbms_prefs,[pm]}, {mod_mam_meta, - [{backend,rdbms}, - {no_stanzaid_element,true}, - {is_archivable_message,mod_mam_utils}, - {archive_chat_markers,true}, + [{archive_chat_markers,true}, + {backend,rdbms}, {full_text_search,true}, - {pm,[{user_prefs_store,rdbms},{full_text_search,false}]}, + {is_archivable_message,mod_mam_utils}, {muc, - [{host,"muc.example.com"}, + [{async_writer,false}, + {host,"muc.example.com"}, {rdbms_message_format,simple}, - {async_writer,false}, - {user_prefs_store,mnesia}]}]}, + {user_prefs_store,mnesia}]}, + {no_stanzaid_element,true}, + {pm,[{full_text_search,false},{user_prefs_store,rdbms}]}]}, {mod_muc_light, - [{config_schema, - [{"roomname","The Room"}, - {"display-lines",30,display_lines,integer}]}, - {rooms_in_rosters,true}, + [{rooms_per_user,10}, {rooms_per_page,5}, + {rooms_in_rosters,true}, {max_occupants,50}, - {all_can_invite,true}, - {all_can_configure,true}, - {blocking,false}, - {rooms_per_user,10}, {legacy_mode,true}, + {host,"muclight.example.com"}, {equal_occupants,true}, - {host,"muclight.example.com"}]}, + {config_schema, + [{"roomname","The Room"}, + {"display-lines",30,display_lines,integer}]}, + {blocking,false}, + {all_can_invite,true}, + {all_can_configure,true}]}, {mod_stream_management, - [{buffer_max,30}, - {ack_freq,1}, + [{ack_freq,1}, + {buffer_max,30}, {resume_timeout,600}, {stale_h, [{enabled,true}, - {stale_h_repeat_after,1800}, - {stale_h_geriatric,3600}]}]}, + {stale_h_geriatric,3600}, + {stale_h_repeat_after,1800}]}]}, {mod_muc_log, - [{outdir,"www/muc"}, - {access_log,muc}, - {top_link,{"/","Home"}}, - {cssfile,<<"path/to/css/file">>}]}, + [{access_log,muc}, + {cssfile,<<"path/to/css/file">>}, + {outdir,"www/muc"}, + {top_link,{"/","Home"}}]}, {mod_inbox, - [{reset_markers,[displayed]}, - {aff_changes,true}, + [{aff_changes,true}, + {groupchat,[muclight]}, {remove_on_kicked,true}, - {groupchat,[muclight]}]}, + {reset_markers,[displayed]}]}, {mod_event_pusher_push, [{backend,mnesia}, - {wpool,[{workers,200}]}, {plugin_module,mod_event_pusher_push_plugin_defaults}, - {virtual_pubsub_hosts,["host1","host2"]}]}, + {virtual_pubsub_hosts,["host1","host2"]}, + {wpool,[{workers,200}]}]}, {mod_event_pusher, [{backends, - [{sns, - [{access_key_id,"AKIAIOSFODNN7EXAMPLE"}, - {secret_access_key, - "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"}, - {region,"eu-west-1"}, - {account_id,"123456789012"}, - {sns_host,"sns.eu-west-1.amazonaws.com"}, - {muc_host,"conference.HOST"}, - {plugin_module,mod_event_pusher_sns_defaults}, - {presence_updates_topic,"user_presence_updated"}, - {pm_messages_topic,"user_message_sent"}, - {muc_messages_topic,"user_messagegroup_sent"}, - {pool_size,100}, - {publish_retry_count,2}, - {publish_retry_time_ms,50}]}, + [{http, + [{callback_module,mod_event_pusher_http_defaults}, + {path,"/notifications"}, + {pool_name,http_pool}]}, {push, [{backend,mnesia}, - {wpool,[{workers,200}]}, {plugin_module,mod_event_pusher_push_plugin_defaults}, - {virtual_pubsub_hosts,["host1","host2"]}]}, - {http, - [{pool_name,http_pool}, - {path,"/notifications"}, - {callback_module,mod_event_pusher_http_defaults}]}, + {virtual_pubsub_hosts,["host1","host2"]}, + {wpool,[{workers,200}]}]}, {rabbit, - [{presence_exchange, - [{name,<<"presence">>},{type,<<"topic">>}]}, - {chat_msg_exchange, + [{chat_msg_exchange, [{name,<<"chat_msg">>}, - {sent_topic,<<"chat_msg_sent">>}, - {recv_topic,<<"chat_msg_recv">>}]}, + {recv_topic,<<"chat_msg_recv">>}, + {sent_topic,<<"chat_msg_sent">>}]}, {groupchat_msg_exchange, [{name,<<"groupchat_msg">>}, - {sent_topic,<<"groupchat_msg_sent">>}, - {recv_topic,<<"groupchat_msg_recv">>}]}]}]}]}, + {recv_topic,<<"groupchat_msg_recv">>}, + {sent_topic,<<"groupchat_msg_sent">>}]}, + {presence_exchange, + [{name,<<"presence">>},{type,<<"topic">>}]}]}, + {sns, + [{access_key_id,"AKIAIOSFODNN7EXAMPLE"}, + {account_id,"123456789012"}, + {muc_host,"conference.HOST"}, + {muc_messages_topic,"user_messagegroup_sent"}, + {plugin_module,mod_event_pusher_sns_defaults}, + {pm_messages_topic,"user_message_sent"}, + {pool_size,100}, + {presence_updates_topic,"user_presence_updated"}, + {publish_retry_count,2}, + {publish_retry_time_ms,50}, + {region,"eu-west-1"}, + {secret_access_key, + "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"}, + {sns_host,"sns.eu-west-1.amazonaws.com"}]}]}]}, {mod_revproxy, [{routes, [{"www.erlang-solutions.com","/admin","_", @@ -273,76 +273,76 @@ {local_config, {modules,<<"localhost">>}, [{mod_jingle_sip, - [{proxy_host,"localhost"}, - {proxy_port,5600}, - {listen_port,5600}, + [{listen_port,5600}, {local_host,"localhost"}, + {proxy_host,"localhost"}, + {proxy_port,5600}, {sdp_origin,"127.0.0.1"}]}, {mod_shared_roster_ldap, [{ldap_base,"ou=Users,dc=ejd,dc=com"}, + {ldap_filter,"(objectClass=inetOrgPerson)"}, + {ldap_group_cache_validity,1}, {ldap_groupattr,"ou"}, {ldap_memberattr,"cn"}, - {ldap_userdesc,"cn"}, - {ldap_filter,"(objectClass=inetOrgPerson)"}, {ldap_rfilter,"(objectClass=inetOrgPerson)"}, - {ldap_group_cache_validity,1}, - {ldap_user_cache_validity,1}]}, + {ldap_user_cache_validity,1}, + {ldap_userdesc,"cn"}]}, {mod_mam_rdbms_user,[muc,pm]}, {mod_global_distrib, - [{global_host,"example.com"}, - {local_host,"datacenter1.example.com"}, + [{bounce,[{max_retries,3},{resend_after_ms,300}]}, + {cache,[{domain_lifetime_seconds,60}]}, {connections, - [{endpoints,[{"172.16.0.2",5555}]}, - {advertised_endpoints,[{"172.16.0.2",5555}]}, + [{advertised_endpoints,[{"172.16.0.2",5555}]}, {connections_per_endpoint,30}, - {tls_opts,[{certfile,"priv/dc1.pem"},{cafile,"priv/ca.pem"}]}]}, - {cache,[{domain_lifetime_seconds,60}]}, - {bounce,[{resend_after_ms,300},{max_retries,3}]}, + {endpoints,[{"172.16.0.2",5555}]}, + {tls_opts,[{cafile,"priv/ca.pem"},{certfile,"priv/dc1.pem"}]}]}, + {global_host,"example.com"}, + {local_host,"datacenter1.example.com"}, {redis,[{pool,global_distrib}]}]}, {mod_register, - [{welcome_message,{"Subject","Body"}}, - {access,all}, + [{access,all}, + {password_strength,32}, {registration_watchers,[<<"JID1">>,<<"JID2">>]}, - {password_strength,32}]}, + {welcome_message,{"Subject","Body"}}]}, {mod_mam_rdbms_async_pool_writer,[pm]}, {mod_adhoc,[{iqdisc,one_queue},{report_commands_node,true}]}, + {mod_muc, + [{access,muc}, + {access_create,muc_create}, + {default_room_options, + [{affiliations, + [{{<<"alice">>,<<"localhost">>,<<"resource1">>},member}, + {{<<"bob">>,<<"localhost">>,<<"resource2">>},owner}]}, + {password_protected,true}]}, + {host,"muc.example.com"}, + {http_auth_pool,my_auth_pool}]}, {mod_event_pusher_sns, [{access_key_id,"AKIAIOSFODNN7EXAMPLE"}, - {secret_access_key,"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"}, - {region,"eu-west-1"}, {account_id,"123456789012"}, - {sns_host,"sns.eu-west-1.amazonaws.com"}, {muc_host,"conference.HOST"}, + {muc_messages_topic,"user_messagegroup_sent"}, {plugin_module,mod_event_pusher_sns_defaults}, - {presence_updates_topic,"user_presence_updated"}, {pm_messages_topic,"user_message_sent"}, - {muc_messages_topic,"user_messagegroup_sent"}, {pool_size,100}, + {presence_updates_topic,"user_presence_updated"}, {publish_retry_count,2}, - {publish_retry_time_ms,50}]}, - {mod_muc, - [{host,"muc.example.com"}, - {access,muc}, - {access_create,muc_create}, - {http_auth_pool,my_auth_pool}, - {default_room_options, - [{password_protected,true}, - {affiliations, - [{{<<"alice">>,<<"localhost">>,<<"resource1">>},member}, - {{<<"bob">>,<<"localhost">>,<<"resource2">>},owner}]}]}]}, + {publish_retry_time_ms,50}, + {region,"eu-west-1"}, + {secret_access_key,"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"}, + {sns_host,"sns.eu-west-1.amazonaws.com"}]}, {mod_ping, - [{send_pings,true}, - {ping_interval,60}, - {timeout_action,none}, - {ping_req_timeout,32}]}, + [{ping_interval,60}, + {ping_req_timeout,32}, + {send_pings,true}, + {timeout_action,none}]}, {mod_mam, [{archive_chat_markers,true}, {full_text_search,false}, {is_archivable_message,mod_mam_utils}, {no_stanzaid_element,true}]}, {mod_disco, - [{iqdisc,one_queue}, - {extra_domains,[<<"some_domain">>,<<"another_domain">>]}, + [{extra_domains,[<<"some_domain">>,<<"another_domain">>]}, + {iqdisc,one_queue}, {server_info, [{all,"abuse-address",["admin@example.com"]}, {[mod_muc,mod_disco], @@ -351,9 +351,9 @@ {users_can_see_hidden_services,true}]}, {mod_event_pusher_http, [{configs, - [[{pool_name,http_pool}, + [[{callback_module,mod_event_pusher_http_defaults}, {path,"/notifications"}, - {callback_module,mod_event_pusher_http_defaults}]]}]}, + {pool_name,http_pool}]]}]}, {mod_event_pusher_hook_translator,[]}, {mod_mam_mnesia_prefs,[muc]}, {mod_mam_muc, @@ -362,26 +362,26 @@ {host,"muc.example.com"}, {is_archivable_message,mod_mam_utils}]}, {mod_event_pusher_rabbit, - [{presence_exchange,[{name,<<"presence">>},{type,<<"topic">>}]}, - {chat_msg_exchange, + [{chat_msg_exchange, [{name,<<"chat_msg">>}, - {sent_topic,<<"chat_msg_sent">>}, - {recv_topic,<<"chat_msg_recv">>}]}, + {recv_topic,<<"chat_msg_recv">>}, + {sent_topic,<<"chat_msg_sent">>}]}, {groupchat_msg_exchange, [{name,<<"groupchat_msg">>}, - {sent_topic,<<"groupchat_msg_sent">>}, - {recv_topic,<<"groupchat_msg_recv">>}]}]}, + {recv_topic,<<"groupchat_msg_recv">>}, + {sent_topic,<<"groupchat_msg_sent">>}]}, + {presence_exchange,[{name,<<"presence">>},{type,<<"topic">>}]}]}, {mod_vcard, - [{matches,1}, - {search,true}, - {host,"directory.example.com"}, - {ldap_vcard_map, - [{<<"FAMILY">>,<<"%s">>,[<<"sn">>]}, - {<<"FN">>,<<"%s">>,[<<"displayName">>]}]}, + [{host,"directory.example.com"}, {ldap_search_fields, [{<<"User">>,<<"%u">>},{<<"Full Name">>,<<"displayName">>}]}, {ldap_search_reported, - [{<<"Full Name">>,<<"FN">>},{<<"Given Name">>,<<"FIRST">>}]}]}, + [{<<"Full Name">>,<<"FN">>},{<<"Given Name">>,<<"FIRST">>}]}, + {ldap_vcard_map, + [{<<"FAMILY">>,<<"%s">>,[<<"sn">>]}, + {<<"FN">>,<<"%s">>,[<<"displayName">>]}]}, + {matches,1}, + {search,true}]}, {mod_keystore, [{keys, [{access_secret,ram}, @@ -392,17 +392,18 @@ {mod_bosh, [{inactivity,20}, {max_wait,infinity}, - {server_acks,true}, - {maxpause,120}]}, + {maxpause,120}, + {server_acks,true}]}, {mod_push_service_mongoosepush, - [{pool_name,mongoose_push_http}, - {api_version,"v3"}, - {max_http_connections,100}]}, + [{api_version,"v3"}, + {max_http_connections,100}, + {pool_name,mongoose_push_http}]}, {mod_auth_token, [{{validity_period,access},{13,minutes}}, {{validity_period,refresh},{13,days}}]}, {mod_extdisco, - [[{host,"stun1"}, + [[{host,"192.168.0.1"},{type,turn}], + [{host,"stun1"}, {password,"password"}, {port,3478}, {transport,"udp"}, @@ -413,18 +414,17 @@ {port,2222}, {transport,"tcp"}, {type,stun}, - {username,"username"}], - [{host,"192.168.0.1"},{type,turn}]]}, + {username,"username"}]]}, {mod_mam_cache_user,[muc,pm]}, - {mod_roster,[{versioning,true},{store_current_id,true}]}, + {mod_roster,[{store_current_id,true},{versioning,true}]}, {mod_http_upload, - [{host,"upload.@HOST@"}, - {backend,s3}, + [{backend,s3}, {expiration_time,120}, - {s3,[{bucket_url,"https://s3-eu-west-1.amazonaws.com/mybucket"}, - {region,"eu-west-1"}, + {host,"upload.@HOST@"}, + {s3,[{access_key_id,"AKIAIOSFODNN7EXAMPLE"}, {add_acl,true}, - {access_key_id,"AKIAIOSFODNN7EXAMPLE"}, + {bucket_url,"https://s3-eu-west-1.amazonaws.com/mybucket"}, + {region,"eu-west-1"}, {secret_access_key, "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"}]}]}, {mod_last,[{backend,mnesia},{iqdisc,{queues,10}}]}, @@ -437,102 +437,102 @@ {db_jid_format,mam_jid_rfc}, {db_message_format,mam_message_xml}]}, {mod_csi,[{buffer_max,40}]}, - {mod_caps,[{cache_size,1000},{cache_life_time,86}]}, + {mod_caps,[{cache_life_time,86},{cache_size,1000}]}, {mod_pubsub, [{access_createnode,pubsub_createnode}, - {ignore_pep_from_offline,false}, {backend,rdbms}, + {ignore_pep_from_offline,false}, {last_item_cache,mnesia}, {max_items_node,1000}, - {plugins,[<<"flat">>,<<"pep">>]}, - {pep_mapping,[{"urn:xmpp:microblog:0","mb"}]}]}, + {pep_mapping,[{"urn:xmpp:microblog:0","mb"}]}, + {plugins,[<<"flat">>,<<"pep">>]}]}, {mod_mam_rdbms_prefs,[pm]}, {mod_mam_meta, - [{backend,rdbms}, - {no_stanzaid_element,true}, - {is_archivable_message,mod_mam_utils}, - {archive_chat_markers,true}, + [{archive_chat_markers,true}, + {backend,rdbms}, {full_text_search,true}, - {pm,[{user_prefs_store,rdbms},{full_text_search,false}]}, + {is_archivable_message,mod_mam_utils}, {muc, - [{host,"muc.example.com"}, + [{async_writer,false}, + {host,"muc.example.com"}, {rdbms_message_format,simple}, - {async_writer,false}, - {user_prefs_store,mnesia}]}]}, + {user_prefs_store,mnesia}]}, + {no_stanzaid_element,true}, + {pm,[{full_text_search,false},{user_prefs_store,rdbms}]}]}, {mod_muc_light, - [{config_schema, - [{"roomname","The Room"}, - {"display-lines",30,display_lines,integer}]}, - {rooms_in_rosters,true}, + [{rooms_per_user,10}, {rooms_per_page,5}, + {rooms_in_rosters,true}, {max_occupants,50}, - {all_can_invite,true}, - {all_can_configure,true}, - {blocking,false}, - {rooms_per_user,10}, {legacy_mode,true}, + {host,"muclight.example.com"}, {equal_occupants,true}, - {host,"muclight.example.com"}]}, + {config_schema, + [{"roomname","The Room"}, + {"display-lines",30,display_lines,integer}]}, + {blocking,false}, + {all_can_invite,true}, + {all_can_configure,true}]}, {mod_stream_management, - [{buffer_max,30}, - {ack_freq,1}, + [{ack_freq,1}, + {buffer_max,30}, {resume_timeout,600}, {stale_h, [{enabled,true}, - {stale_h_repeat_after,1800}, - {stale_h_geriatric,3600}]}]}, + {stale_h_geriatric,3600}, + {stale_h_repeat_after,1800}]}]}, {mod_muc_log, - [{outdir,"www/muc"}, - {access_log,muc}, - {top_link,{"/","Home"}}, - {cssfile,<<"path/to/css/file">>}]}, + [{access_log,muc}, + {cssfile,<<"path/to/css/file">>}, + {outdir,"www/muc"}, + {top_link,{"/","Home"}}]}, {mod_inbox, - [{reset_markers,[displayed]}, - {aff_changes,true}, + [{aff_changes,true}, + {groupchat,[muclight]}, {remove_on_kicked,true}, - {groupchat,[muclight]}]}, + {reset_markers,[displayed]}]}, {mod_event_pusher_push, [{backend,mnesia}, - {wpool,[{workers,200}]}, {plugin_module,mod_event_pusher_push_plugin_defaults}, - {virtual_pubsub_hosts,["host1","host2"]}]}, + {virtual_pubsub_hosts,["host1","host2"]}, + {wpool,[{workers,200}]}]}, {mod_event_pusher, [{backends, - [{sns, - [{access_key_id,"AKIAIOSFODNN7EXAMPLE"}, - {secret_access_key, - "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"}, - {region,"eu-west-1"}, - {account_id,"123456789012"}, - {sns_host,"sns.eu-west-1.amazonaws.com"}, - {muc_host,"conference.HOST"}, - {plugin_module,mod_event_pusher_sns_defaults}, - {presence_updates_topic,"user_presence_updated"}, - {pm_messages_topic,"user_message_sent"}, - {muc_messages_topic,"user_messagegroup_sent"}, - {pool_size,100}, - {publish_retry_count,2}, - {publish_retry_time_ms,50}]}, + [{http, + [{callback_module,mod_event_pusher_http_defaults}, + {path,"/notifications"}, + {pool_name,http_pool}]}, {push, [{backend,mnesia}, - {wpool,[{workers,200}]}, {plugin_module,mod_event_pusher_push_plugin_defaults}, - {virtual_pubsub_hosts,["host1","host2"]}]}, - {http, - [{pool_name,http_pool}, - {path,"/notifications"}, - {callback_module,mod_event_pusher_http_defaults}]}, + {virtual_pubsub_hosts,["host1","host2"]}, + {wpool,[{workers,200}]}]}, {rabbit, - [{presence_exchange, - [{name,<<"presence">>},{type,<<"topic">>}]}, - {chat_msg_exchange, + [{chat_msg_exchange, [{name,<<"chat_msg">>}, - {sent_topic,<<"chat_msg_sent">>}, - {recv_topic,<<"chat_msg_recv">>}]}, + {recv_topic,<<"chat_msg_recv">>}, + {sent_topic,<<"chat_msg_sent">>}]}, {groupchat_msg_exchange, [{name,<<"groupchat_msg">>}, - {sent_topic,<<"groupchat_msg_sent">>}, - {recv_topic,<<"groupchat_msg_recv">>}]}]}]}]}, + {recv_topic,<<"groupchat_msg_recv">>}, + {sent_topic,<<"groupchat_msg_sent">>}]}, + {presence_exchange, + [{name,<<"presence">>},{type,<<"topic">>}]}]}, + {sns, + [{access_key_id,"AKIAIOSFODNN7EXAMPLE"}, + {account_id,"123456789012"}, + {muc_host,"conference.HOST"}, + {muc_messages_topic,"user_messagegroup_sent"}, + {plugin_module,mod_event_pusher_sns_defaults}, + {pm_messages_topic,"user_message_sent"}, + {pool_size,100}, + {presence_updates_topic,"user_presence_updated"}, + {publish_retry_count,2}, + {publish_retry_time_ms,50}, + {region,"eu-west-1"}, + {secret_access_key, + "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"}, + {sns_host,"sns.eu-west-1.amazonaws.com"}]}]}]}, {mod_revproxy, [{routes, [{"www.erlang-solutions.com","/admin","_", diff --git a/test/config_parser_SUITE_data/mongooseim-pgsql.options b/test/config_parser_SUITE_data/mongooseim-pgsql.options index b91c85d37c..dfe2476eb2 100644 --- a/test/config_parser_SUITE_data/mongooseim-pgsql.options +++ b/test/config_parser_SUITE_data/mongooseim-pgsql.options @@ -26,178 +26,109 @@ {config,{shaper,mam_global_shaper,global},{maxrate,1000}}. {config,{shaper,mam_shaper,global},{maxrate,1}}. {config,{shaper,normal,global},{maxrate,1000}}. -{local_config, - {modules,<<"anonymous.localhost">>}, - [{mod_register, - [{welcome_message,{"Hello","I am MongooseIM"}}, - {ip_access,[{allow,"127.0.0.0/8"},{deny,"0.0.0.0/0"}]}, - {access,register}]}, - {mod_adhoc,[]}, - {mod_sic,[]}, - {mod_private,[{backend,rdbms}]}, - {mod_privacy,[{backend,rdbms}]}, - {mod_disco,[{users_can_see_hidden_services,false}]}, - {mod_commands,[]}, - {mod_muc_light_commands,[]}, - {mod_vcard,[{backend,rdbms},{host,"vjud.@HOST@"}]}, - {mod_blocking,[]}, - {mod_bosh,[]}, - {mod_roster,[{backend,rdbms}]}, - {mod_last,[{backend,rdbms}]}, - {mod_offline,[{backend,rdbms}]}, - {mod_amp,[]}, - {mod_muc_commands,[]}, - {mod_stream_management,[]}, - {mod_carboncopy,[]}]}. -{local_config, - {modules,<<"localhost">>}, - [{mod_register, - [{welcome_message,{"Hello","I am MongooseIM"}}, - {ip_access,[{allow,"127.0.0.0/8"},{deny,"0.0.0.0/0"}]}, - {access,register}]}, - {mod_adhoc,[]}, - {mod_sic,[]}, - {mod_private,[{backend,rdbms}]}, - {mod_privacy,[{backend,rdbms}]}, - {mod_disco,[{users_can_see_hidden_services,false}]}, - {mod_commands,[]}, - {mod_muc_light_commands,[]}, - {mod_vcard,[{backend,rdbms},{host,"vjud.@HOST@"}]}, - {mod_blocking,[]}, - {mod_bosh,[]}, - {mod_roster,[{backend,rdbms}]}, - {mod_last,[{backend,rdbms}]}, - {mod_offline,[{backend,rdbms}]}, - {mod_amp,[]}, - {mod_muc_commands,[]}, - {mod_stream_management,[]}, - {mod_carboncopy,[]}]}. -{local_config, - {modules,<<"localhost.bis">>}, - [{mod_register, - [{welcome_message,{"Hello","I am MongooseIM"}}, - {ip_access,[{allow,"127.0.0.0/8"},{deny,"0.0.0.0/0"}]}, - {access,register}]}, - {mod_adhoc,[]}, - {mod_sic,[]}, - {mod_private,[{backend,rdbms}]}, - {mod_privacy,[{backend,rdbms}]}, - {mod_disco,[{users_can_see_hidden_services,false}]}, - {mod_commands,[]}, - {mod_muc_light_commands,[]}, - {mod_vcard,[{backend,rdbms},{host,"vjud.@HOST@"}]}, - {mod_blocking,[]}, - {mod_bosh,[]}, - {mod_roster,[{backend,rdbms}]}, - {mod_last,[{backend,rdbms}]}, - {mod_offline,[{backend,rdbms}]}, - {mod_amp,[]}, - {mod_muc_commands,[]}, - {mod_stream_management,[]}, - {mod_carboncopy,[]}]}. {local_config,all_metrics_are_global,false}. {local_config,listen, - [{{5280,{0,0,0,0},tcp}, + [{{5222,{0,0,0,0},tcp}, + ejabberd_c2s, + [{access,c2s}, + {max_stanza_size,65536}, + {shaper,c2s_shaper}, + {certfile,"tools/ssl/mongooseim/server.pem"}, + {dhfile,"tools/ssl/mongooseim/dh_server.pem"}, + starttls, + {zlib,10000}]}, + {{5223,{0,0,0,0},tcp}, + ejabberd_c2s, + [{access,c2s},{max_stanza_size,65536},{shaper,c2s_shaper},{zlib,4096}]}, + {{5280,{0,0,0,0},tcp}, ejabberd_cowboy, - [{transport_options,[{num_acceptors,10},{max_connections,1024}]}, - {modules, - [{"_","/http-bind",mod_bosh}, + [{modules, + [{"_","/http-bind",mod_bosh,[]}, {"_","/ws-xmpp",mod_websockets, [{ejabberd_service, - [{access,all},{shaper_rule,fast},{password,"secret"}]}]}]}]}, + [{access,all},{password,"secret"},{shaper_rule,fast}]}]}]}, + {transport_options,[{max_connections,1024},{num_acceptors,10}]}]}, {{5285,{0,0,0,0},tcp}, ejabberd_cowboy, - [{transport_options,[{num_acceptors,10},{max_connections,1024}]}, + [{modules, + [{"_","/http-bind",mod_bosh,[]}, + {"_","/ws-xmpp",mod_websockets, + [{max_stanza_size,100},{ping_rate,none},{timeout,infinity}]}, + {"localhost","/api",mongoose_api_admin, + [{auth,{<<"ala">>,<<"makotaipsa">>}}]}, + {"localhost","/api/contacts/{:jid}",mongoose_api_client,[]}]}, {ssl, [{certfile,"tools/ssl/mongooseim/cert.pem"}, {keyfile,"tools/ssl/mongooseim/key.pem"}, {password,[]}]}, - {modules, - [{"_","/http-bind",mod_bosh}, - {"_","/ws-xmpp",mod_websockets, - [{timeout,infinity},{ping_rate,none},{max_stanza_size,100}]}, - {"localhost","/api",mongoose_api_admin, - [{auth,{<<"ala">>,<<"makotaipsa">>}}]}, - {"localhost","/api/contacts/{:jid}",mongoose_api_client,[]}]}]}, + {transport_options,[{max_connections,1024},{num_acceptors,10}]}]}, {{8088,{127,0,0,1},tcp}, ejabberd_cowboy, - [{transport_options,[{num_acceptors,10},{max_connections,1024}]}, - {modules,[{"localhost","/api",mongoose_api_admin,[]}]}]}, + [{modules,[{"localhost","/api",mongoose_api_admin,[]}]}, + {transport_options,[{max_connections,1024},{num_acceptors,10}]}]}, {{8089,{0,0,0,0},tcp}, ejabberd_cowboy, - [{transport_options,[{num_acceptors,10},{max_connections,1024}]}, - {protocol_options,[{compress,true}]}, - {ssl, - [{certfile,"tools/ssl/mongooseim/cert.pem"}, - {keyfile,"tools/ssl/mongooseim/key.pem"}, - {password,[]}]}, - {modules, - [{"_","/api/sse",lasse_handler,[mongoose_client_api_sse]}, - {"_","/api/messages/[:with]",mongoose_client_api_messages,[]}, + [{modules, + [{"_","/api-docs/[...]",cowboy_static, + {priv_dir,cowboy_swagger,"swagger", + [{mimetypes,cow_mimetypes,all}]}}, + {"_","/api-docs/swagger.json",cowboy_swagger_json_handler,#{}}, + {"_","/api-docs",cowboy_swagger_redirect_handler,#{}}, + {"_","/api/sse",lasse_handler,[mongoose_client_api_sse]}, {"_","/api/contacts/[:jid]",mongoose_client_api_contacts,[]}, + {"_","/api/messages/[:with]",mongoose_client_api_messages,[]}, {"_","/api/rooms/[:id]",mongoose_client_api_rooms,[]}, {"_","/api/rooms/[:id]/config",mongoose_client_api_rooms_config, []}, - {"_","/api/rooms/:id/users/[:user]", - mongoose_client_api_rooms_users,[]}, {"_","/api/rooms/[:id]/messages", mongoose_client_api_rooms_messages,[]}, - {"_","/api-docs",cowboy_swagger_redirect_handler,#{}}, - {"_","/api-docs/swagger.json",cowboy_swagger_json_handler,#{}}, - {"_","/api-docs/[...]",cowboy_static, - {priv_dir,cowboy_swagger,"swagger", - [{mimetypes,cow_mimetypes,all}]}}]}]}, + {"_","/api/rooms/:id/users/[:user]", + mongoose_client_api_rooms_users,[]}]}, + {protocol_options,[{compress,true}]}, + {ssl, + [{certfile,"tools/ssl/mongooseim/cert.pem"}, + {keyfile,"tools/ssl/mongooseim/key.pem"}, + {password,[]}]}, + {transport_options,[{max_connections,1024},{num_acceptors,10}]}]}, {{5288,{127,0,0,1},tcp}, ejabberd_cowboy, - [{transport_options,[{num_acceptors,10},{max_connections,1024}]}, - {modules, + [{modules, [{"localhost","/api",mongoose_api, - [{handlers,[mongoose_api_metrics,mongoose_api_users]}]}]}]}, - {{5222,{0,0,0,0},tcp}, - ejabberd_c2s, - [{certfile,"tools/ssl/mongooseim/server.pem"}, - starttls, - {zlib,10000}, - {access,c2s}, - {shaper,c2s_shaper}, - {max_stanza_size,65536}, - {dhfile,"tools/ssl/mongooseim/dh_server.pem"}]}, - {{5223,{0,0,0,0},tcp}, - ejabberd_c2s, - [{zlib,4096},{access,c2s},{shaper,c2s_shaper},{max_stanza_size,65536}]}, + [{handlers,[mongoose_api_metrics,mongoose_api_users]}]}]}, + {transport_options,[{max_connections,1024},{num_acceptors,10}]}]}, {{5269,{0,0,0,0},tcp}, ejabberd_s2s_in, - [{shaper,s2s_shaper}, - {max_stanza_size,131072}, + [{max_stanza_size,131072}, + {shaper,s2s_shaper}, {dhfile,"tools/ssl/mongooseim/dh_server.pem"}]}, {{8888,{127,0,0,1},tcp}, ejabberd_service, - [{access,all},{shaper_rule,fast},{password,"secret"}]}, + [{access,all},{password,"secret"},{shaper_rule,fast}]}, {{8666,{127,0,0,1},tcp}, ejabberd_service, [{access,all}, {conflict_behaviour,kick_old}, - {shaper_rule,fast}, - {password,"secret"}]}, + {password,"secret"}, + {shaper_rule,fast}]}, {{8189,{127,0,0,1},tcp}, ejabberd_service, [{access,all}, {hidden_components,true}, - {shaper_rule,fast}, - {password,"secret"}]}]}. + {password,"secret"}, + {shaper_rule,fast}]}]}. {local_config,loglevel,warning}. {local_config,max_fsm_queue,1000}. {local_config,outgoing_pools, - [{redis,<<"localhost">>,global_distrib,[{workers,10}],[]}, - {rdbms,global,default, + [{rdbms,global,default, [{workers,5}], [{server, {pgsql,"localhost","ejabberd","ejabberd","mongooseim_secret", [{ssl,required}, {ssl_opts, - [{verify,verify_peer}, - {cacertfile,"priv/ssl/cacert.pem"}, - {server_name_indication,disable}]}]}}]}]}. + [{cacertfile,"priv/ssl/cacert.pem"}, + {server_name_indication,disable}, + {verify,verify_peer}]}]}}]}, + {redis,<<"localhost">>,global_distrib,[{workers,10}],[]}]}. {local_config,outgoing_s2s_port,5299}. {local_config,registration_timeout,infinity}. {local_config,s2s_certfile,"tools/ssl/mongooseim/server.pem"}. @@ -211,19 +142,88 @@ [{initial_report,300000},{periodic_report,10800000}]}]}. {local_config,{allow_multiple_connections,<<"anonymous.localhost">>},true}. {local_config,{anonymous_protocol,<<"anonymous.localhost">>},both}. -{local_config,{auth_method,<<"anonymous.localhost">>},anonymous}. -{local_config,{auth_method,<<"localhost">>},rdbms}. -{local_config,{auth_method,<<"localhost.bis">>},rdbms}. +{local_config,{auth_method,<<"anonymous.localhost">>},[anonymous]}. +{local_config,{auth_method,<<"localhost">>},[rdbms]}. +{local_config,{auth_method,<<"localhost.bis">>},[rdbms]}. {local_config,{auth_opts,<<"anonymous.localhost">>},[]}. {local_config,{auth_opts,<<"localhost">>}, [{password_format,{scram,[sha256]}}, - {scram_iterations,64}, - {cyrsasl_external,standard}]}. + {cyrsasl_external,[standard]}, + {scram_iterations,64}]}. {local_config,{auth_opts,<<"localhost.bis">>}, [{password_format,{scram,[sha256]}}, - {scram_iterations,64}, - {cyrsasl_external,standard}]}. -{local_config,{s2s_addr,<<"fed1">>},{127,0,0,1}}. + {cyrsasl_external,[standard]}, + {scram_iterations,64}]}. +{local_config, + {modules,<<"anonymous.localhost">>}, + [{mod_register, + [{access,register}, + {ip_access,[{allow,"127.0.0.0/8"},{deny,"0.0.0.0/0"}]}, + {welcome_message,{"Hello","I am MongooseIM"}}]}, + {mod_adhoc,[]}, + {mod_sic,[]}, + {mod_private,[{backend,rdbms}]}, + {mod_privacy,[{backend,rdbms}]}, + {mod_disco,[{users_can_see_hidden_services,false}]}, + {mod_commands,[]}, + {mod_muc_light_commands,[]}, + {mod_vcard,[{backend,rdbms},{host,"vjud.@HOST@"}]}, + {mod_blocking,[]}, + {mod_bosh,[]}, + {mod_roster,[{backend,rdbms}]}, + {mod_last,[{backend,rdbms}]}, + {mod_offline,[{backend,rdbms}]}, + {mod_amp,[]}, + {mod_muc_commands,[]}, + {mod_stream_management,[]}, + {mod_carboncopy,[]}]}. +{local_config, + {modules,<<"localhost">>}, + [{mod_register, + [{access,register}, + {ip_access,[{allow,"127.0.0.0/8"},{deny,"0.0.0.0/0"}]}, + {welcome_message,{"Hello","I am MongooseIM"}}]}, + {mod_adhoc,[]}, + {mod_sic,[]}, + {mod_private,[{backend,rdbms}]}, + {mod_privacy,[{backend,rdbms}]}, + {mod_disco,[{users_can_see_hidden_services,false}]}, + {mod_commands,[]}, + {mod_muc_light_commands,[]}, + {mod_vcard,[{backend,rdbms},{host,"vjud.@HOST@"}]}, + {mod_blocking,[]}, + {mod_bosh,[]}, + {mod_roster,[{backend,rdbms}]}, + {mod_last,[{backend,rdbms}]}, + {mod_offline,[{backend,rdbms}]}, + {mod_amp,[]}, + {mod_muc_commands,[]}, + {mod_stream_management,[]}, + {mod_carboncopy,[]}]}. +{local_config, + {modules,<<"localhost.bis">>}, + [{mod_register, + [{access,register}, + {ip_access,[{allow,"127.0.0.0/8"},{deny,"0.0.0.0/0"}]}, + {welcome_message,{"Hello","I am MongooseIM"}}]}, + {mod_adhoc,[]}, + {mod_sic,[]}, + {mod_private,[{backend,rdbms}]}, + {mod_privacy,[{backend,rdbms}]}, + {mod_disco,[{users_can_see_hidden_services,false}]}, + {mod_commands,[]}, + {mod_muc_light_commands,[]}, + {mod_vcard,[{backend,rdbms},{host,"vjud.@HOST@"}]}, + {mod_blocking,[]}, + {mod_bosh,[]}, + {mod_roster,[{backend,rdbms}]}, + {mod_last,[{backend,rdbms}]}, + {mod_offline,[{backend,rdbms}]}, + {mod_amp,[]}, + {mod_muc_commands,[]}, + {mod_stream_management,[]}, + {mod_carboncopy,[]}]}. +{local_config,{s2s_addr,<<"fed1">>},"127.0.0.1"}. {local_config,{s2s_default_policy,<<"anonymous.localhost">>},allow}. {local_config,{s2s_default_policy,<<"localhost">>},allow}. {local_config,{s2s_default_policy,<<"localhost.bis">>},allow}. diff --git a/test/config_parser_SUITE_data/outgoing_pools.options b/test/config_parser_SUITE_data/outgoing_pools.options index ea2592fc51..e4c19d31fe 100644 --- a/test/config_parser_SUITE_data/outgoing_pools.options +++ b/test/config_parser_SUITE_data/outgoing_pools.options @@ -1,43 +1,43 @@ {config,hosts,[<<"localhost">>,<<"anonymous.localhost">>,<<"localhost.bis">>]}. {local_config,outgoing_pools, - [{redis,<<"localhost">>,global_distrib,[{workers,10}],[]}, - {rdbms,global,default, - [{workers,5}], - [{server, - {pgsql,"localhost","ejabberd","ejabberd","mongooseim_secret", - [{ssl,required}, - {ssl_opts, - [{verify,verify_peer}, - {cacertfile,"priv/ssl/cacert.pem"}, - {server_name_indication,disable}]}]}}, - {keepalive_interval,30}]}, + [{cassandra,global,default,[], + [{keyspace,"big_mongooseim"}, + {servers, + [{"cassandra_server1.example.com",9042}, + {"cassandra_server2.example.com",9042}]}]}, + {elastic,global,default,[],[{host,"localhost"}]}, {http,global,mongoose_push_http, [{workers,50}], [{server,"https://localhost:8443"}, {path_prefix,"/"}, {request_timeout,2000}]}, - {riak,global,default, - [{workers,20},{strategy,next_worker}], - [{address,"127.0.0.1"}, - {port,8087}, - {credentials,"username","pass"}, - {cacertfile,"path/to/cacert.pem"}]}, - {cassandra,global,default,[], - [{servers, - [{"cassandra_server1.example.com",9042}, - {"cassandra_server2.example.com",9042}]}, - {keyspace,"big_mongooseim"}]}, - {elastic,global,default,[],[{host,"localhost"}]}, + {ldap,host,default, + [{workers,5}], + [{password,"ldap-admin-password"}, + {rootdn,"cn=admin,dc=example,dc=com"}, + {servers,["ldap-server.example.com"]}]}, {rabbit,host,event_pusher, [{workers,20}], [{amqp_host,"localhost"}, + {amqp_password,"guest"}, {amqp_port,5672}, {amqp_username,"guest"}, - {amqp_password,"guest"}, {confirms_enabled,true}, {max_worker_queue_len,100}]}, - {ldap,host,default, + {rdbms,global,default, [{workers,5}], - [{servers,["ldap-server.example.com"]}, - {rootdn,"cn=admin,dc=example,dc=com"}, - {password,"ldap-admin-password"}]}]}. + [{server, + {pgsql,"localhost","ejabberd","ejabberd","mongooseim_secret", + [{ssl,required}, + {ssl_opts, + [{cacertfile,"priv/ssl/cacert.pem"}, + {server_name_indication,disable}, + {verify,verify_peer}]}]}}, + {keepalive_interval,30}]}, + {redis,<<"localhost">>,global_distrib,[{workers,10}],[]}, + {riak,global,default, + [{strategy,next_worker},{workers,20}], + [{address,"127.0.0.1"}, + {cacertfile,"path/to/cacert.pem"}, + {credentials,"username","pass"}, + {port,8087}]}]}. diff --git a/test/config_parser_SUITE_data/s2s_only.options b/test/config_parser_SUITE_data/s2s_only.options index 28c698d12f..5486d39db8 100644 --- a/test/config_parser_SUITE_data/s2s_only.options +++ b/test/config_parser_SUITE_data/s2s_only.options @@ -4,11 +4,11 @@ {local_config,outgoing_s2s_timeout,10000}. {local_config,s2s_certfile,"tools/ssl/mongooseim/server.pem"}. {local_config,s2s_ciphers,"TLSv1.2:TLSv1.3"}. -{local_config,s2s_dns_options,[{timeout,30},{retries,1}]}. +{local_config,s2s_dns_options,[{retries,1},{timeout,30}]}. {local_config,s2s_use_starttls,optional}. {local_config,{domain_certfile,"example.com"},"/path/to/example_com.pem"}. {local_config,{domain_certfile,"example.org"},"/path/to/example_org.pem"}. -{local_config,{s2s_addr,<<"fed1">>},{127,0,0,1}}. +{local_config,{s2s_addr,<<"fed1">>},"127.0.0.1"}. {local_config,{s2s_default_policy,<<"dummy_host">>},allow}. {local_config,{s2s_default_policy,<<"localhost">>},allow}. {local_config,{s2s_max_retry_delay,<<"dummy_host">>},30}. From d6ac587772fadfefcc7eb8db46fdad38c92842dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Fri, 6 Nov 2020 18:26:19 +0100 Subject: [PATCH 014/104] Declarative specification of the 'general' section - Temporary code for supporting both 'handler' specifications - Type specs will follow when they stop changing - Code for the decalrative handlers will be removed last Functional changes: - The 'general.mongooseim_access_commands.commands' option no longer accepts the string "all" for type consistency. Now this option has to be omitted to enable all commands. --- include/ejabberd_config.hrl | 13 ++ src/config/mongoose_config_parser_toml.erl | 202 ++++++++++-------- src/config/mongoose_config_spec.erl | 111 ++++++++++ src/config/mongoose_config_validator_toml.erl | 78 +++---- test/config_parser_SUITE.erl | 26 ++- 5 files changed, 283 insertions(+), 147 deletions(-) create mode 100644 src/config/mongoose_config_spec.erl diff --git a/include/ejabberd_config.hrl b/include/ejabberd_config.hrl index d47c9a11ee..fd38cdbcdb 100644 --- a/include/ejabberd_config.hrl +++ b/include/ejabberd_config.hrl @@ -27,4 +27,17 @@ value :: mongoose_config_parser:value() }). +-record(section, {items, + validate = any, + process, + format = default}). +-record(list, {items, + validate = any, + process, + format = default}). +-record(option, {type, + validate = any, + process, + format = default}). + -endif. diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index 50da742644..205b3c36fb 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -118,64 +118,6 @@ process_section([<<"host_config">>] = Path, Content) -> process_section(Path, Content) -> parse_section(Path, Content). -%% path: (host_config[].)general.* --spec process_general(path(), toml_value()) -> [config()]. -process_general([<<"loglevel">>|_], V) -> - [#local_config{key = loglevel, value = b2a(V)}]; -process_general([<<"hosts">>|_] = Path, Hosts) -> - [#config{key = hosts, value = parse_list(Path, Hosts)}]; -process_general([<<"registration_timeout">>|_], V) -> - [#local_config{key = registration_timeout, value = int_or_infinity(V)}]; -process_general([<<"language">>|_], V) -> - [#config{key = language, value = V}]; -process_general([<<"all_metrics_are_global">>|_], V) -> - [#local_config{key = all_metrics_are_global, value = V}]; -process_general([<<"sm_backend">>|_], V) -> - [#config{key = sm_backend, value = {b2a(V), []}}]; -process_general([<<"max_fsm_queue">>|_], V) -> - [#local_config{key = max_fsm_queue, value = V}]; -process_general([<<"http_server_name">>|_], V) -> - [#local_config{key = cowboy_server_name, value = b2l(V)}]; -process_general([<<"rdbms_server_type">>|_], V) -> - [#local_config{key = rdbms_server_type, value = b2a(V)}]; -process_general([<<"override">>|_] = Path, Value) -> - parse_list(Path, Value); -process_general([<<"pgsql_users_number_estimate">>|_], V) -> - ?HOST_F([#local_config{key = {pgsql_users_number_estimate, Host}, value = V}]); -process_general([<<"route_subdomains">>|_], V) -> - ?HOST_F([#local_config{key = {route_subdomains, Host}, value = b2a(V)}]); -process_general([<<"mongooseimctl_access_commands">>|_] = Path, Rules) -> - [#local_config{key = mongooseimctl_access_commands, value = parse_section(Path, Rules)}]; -process_general([<<"routing_modules">>|_] = Path, Mods) -> - [#local_config{key = routing_modules, value = parse_list(Path, Mods)}]; -process_general([<<"replaced_wait_timeout">>|_], V) -> - ?HOST_F([#local_config{key = {replaced_wait_timeout, Host}, value = V}]); -process_general([<<"hide_service_name">>|_], V) -> - ?HOST_F([#local_config{key = {hide_service_name, Host}, value = V}]). - --spec process_host(path(), toml_value()) -> [option()]. -process_host(_Path, Val) -> - [jid:nodeprep(Val)]. - --spec process_override(path(), toml_value()) -> [option()]. -process_override(_Path, Override) -> - [{override, b2a(Override)}]. - --spec ctl_access_rule(path(), toml_section()) -> [option()]. -ctl_access_rule([Rule|_] = Path, Section) -> - limit_keys([<<"commands">>, <<"argument_restrictions">>], Section), - [{b2a(Rule), - parse_kv(Path, <<"commands">>, Section), - parse_kv(Path, <<"argument_restrictions">>, Section, #{})}]. - --spec ctl_access_commands(path(), toml_value()) -> option(). -ctl_access_commands(_Path, <<"all">>) -> all; -ctl_access_commands(Path, Commands) -> parse_list(Path, Commands). - --spec ctl_access_arg_restriction(path(), toml_value()) -> [option()]. -ctl_access_arg_restriction([Key|_], Value) -> - [{b2a(Key), b2l(Value)}]. - %% path: listen.*[] -spec process_listener(path(), toml_section()) -> [option()]. process_listener([_, Type|_] = Path, Content) -> @@ -1760,16 +1702,110 @@ handle(Path, Value) -> Error; (StepName, AccIn) -> try_call(handle_step(StepName, AccIn), StepName, Path, Value) - end, Path, [handle, parse, validate]). + end, Path, [handle, parse, validate, process, format]). handle_step(handle, _) -> fun(Path, _Value) -> handler(Path) end; +handle_step(parse, Spec) when is_tuple(Spec) -> + fun(Path, Value) -> + ParsedValue = case Spec of + #section{} when is_map(Value) -> + parse_section(Path, Value); + #list{} when is_list(Value) -> + parse_list(Path, Value); + #option{type = Type} when not is_list(Value), not is_map(Value) -> + convert(Value, Type) + end, + case extract_errors(ParsedValue) of + [] -> {ParsedValue, Spec}; + Errors -> Errors + end + end; handle_step(parse, Handler) -> Handler; +handle_step(validate, {ParsedValue, Spec}) -> + fun(_Path, _Value) -> + validate(ParsedValue, Spec), + {ParsedValue, Spec} + end; handle_step(validate, ParsedValue) -> fun(Path, _Value) -> mongoose_config_validator_toml:validate(Path, ParsedValue), ParsedValue + end; +handle_step(process, {ParsedValue, Spec}) -> + fun(_Path, _Value) -> + {process_value(ParsedValue, Spec), Spec} + end; +handle_step(process, V) -> + fun(_, _) -> V end; +handle_step(format, {ParsedValue, Spec}) -> + fun(Path, _Value) -> + format(Path, ParsedValue, format_spec(Spec)) + end; +handle_step(format, V) -> + fun(_, _) -> V end. + +validate(Value, #section{validate = Validator}) -> + mongoose_config_validator_toml:validate_section(Value, Validator); +validate(Value, #list{validate = Validator}) -> + mongoose_config_validator_toml:validate_list(Value, Validator); +validate(Value, #option{type = Type, validate = Validator}) -> + mongoose_config_validator_toml:validate(Value, Type, Validator). + +process_value(V, #section{process = undefined}) -> V; +process_value(V, #list{process = undefined}) -> V; +process_value(V, #option{process = undefined}) -> V; +process_value(V, #section{process = Process}) -> Process(V); +process_value(V, #list{process = Process}) -> Process(V); +process_value(V, #option{process = Process}) -> Process(V). + +convert(V, boolean) -> V; +convert(V, binary) -> V; +convert(V, string) -> binary_to_list(V); +convert(V, atom) -> b2a(V); +convert(<<"infinity">>, int_or_infinity) -> infinity; %% TODO maybe use TOML '+inf' +convert(V, int_or_infinity) -> V; +convert(V, integer) -> V. + +format_spec(#section{format = Format}) -> Format; +format_spec(#list{format = Format}) -> Format; +format_spec(#option{format = Format}) -> Format. + +format([Key|_] = Path, V, host_local_config) -> + format(Path, V, {host_local_config, b2a(Key)}); +format([Key|_] = Path, V, local_config) -> + format(Path, V, {local_config, b2a(Key)}); +format([Key|_] = Path, V, config) -> + format(Path, V, {config, b2a(Key)}); +format(Path, V, {host_local_config, Key}) -> + case get_host(Path) of + global -> ?HOST_F([#local_config{key = {Key, Host}, value = V}]); + Host -> [#local_config{key = {Key, Host}, value = V}] + end; +format(Path, V, {local_config, Key}) -> + global = get_host(Path), + [#local_config{key = Key, value = V}]; +format(Path, V, {config, Key}) -> + global = get_host(Path), + [#config{key = Key, value = V}]; +format(Path, V, override) -> + global = get_host(Path), + [{override, V}]; +format([item|_], V, default) -> + [V]; +format([Key|_], V, default) -> + [{b2a(Key), V}]; +format([Key|_], V, prepend_key) -> + L = [b2a(Key) | tuple_to_list(V)], + [list_to_tuple(L)]; +format(_Path, V, none) -> + V. + +get_host(Path) -> + case lists:reverse(Path) of + [<<"host_config">>, {host, Host} | _] -> Host; + _ -> global end. -spec try_call(fun((path(), any()) -> option()), atom(), path(), toml_value()) -> option(). @@ -1790,7 +1826,9 @@ try_call(F, StepName, Path, Value) -> -spec error_text(atom()) -> string(). error_text(handle) -> "Unexpected option in the TOML configuration file"; error_text(parse) -> "Malformed option in the TOML configuration file"; -error_text(validate) -> "Incorrect option value in the TOML configuration file". +error_text(validate) -> "Incorrect option value in the TOML configuration file"; +error_text(process) -> "Unable to process a value the TOML configuration file"; +error_text(format) -> "Unable to format an option in the TOML configuration file". -spec error_fields(any()) -> map(). error_fields(#{what := Reason} = M) -> maps:remove(what, M#{reason => Reason}); @@ -1806,26 +1844,11 @@ node_to_string({host, _}) -> []; node_to_string({tls, TLSAtom}) -> [atom_to_list(TLSAtom)]; node_to_string(Node) -> [binary_to_list(Node)]. --spec handler(path()) -> fun((path(), toml_value()) -> option()). +-spec handler(path()) -> + fun((path(), toml_value()) -> option()) | mongoose_config_spec:config_node(). handler([]) -> fun parse_root/2; handler([_]) -> fun process_section/2; -%% general -handler([_, <<"general">>]) -> fun process_general/2; -handler([_, <<"hosts">>, <<"general">>]) -> fun process_host/2; -handler([_, <<"override">>, <<"general">>]) -> fun process_override/2; -handler([_, <<"mongooseimctl_access_commands">>, <<"general">>]) -> fun ctl_access_rule/2; -handler([<<"commands">>, _, <<"mongooseimctl_access_commands">>, <<"general">>]) -> - fun ctl_access_commands/2; -handler([_, <<"commands">>, _, <<"mongooseimctl_access_commands">>, <<"general">>]) -> - fun(_, Val) -> [b2l(Val)] end; -handler([<<"argument_restrictions">>, _, <<"mongooseimctl_access_commands">>, <<"general">>]) -> - fun parse_section/2; -handler([_, <<"argument_restrictions">>, _, <<"mongooseimctl_access_commands">>, <<"general">>]) -> - fun ctl_access_arg_restriction/2; -handler([_, <<"routing_modules">>, <<"general">>]) -> - fun(_, Val) -> [b2a(Val)] end; - %% listen handler([_, <<"listen">>]) -> fun parse_list/2; handler([_, _, <<"listen">>]) -> fun process_listener/2; @@ -2056,18 +2079,29 @@ handler([_, _, <<"host_config">>]) -> fun process_section/2; handler([_, <<"general">>, _, <<"host_config">>] = P) -> handler_for_host(P); handler([_, <<"s2s">>, _, <<"host_config">>] = P) -> handler_for_host(P); handler(Path) -> - [<<"host_config">>, {host, _} | Rest] = lists:reverse(Path), - handler(lists:reverse(Rest)). + subtree_handler(initial, lists:reverse(Path)). + +subtree_handler(initial, [<<"host_config">>, {host, _} | Subtree]) -> + subtree_handler(subtree, Subtree); +subtree_handler(_, [<<"general">>|_] = Path) -> + mongoose_config_spec:handler(Path); +subtree_handler(subtree, Subtree) -> + handler(lists:reverse(Subtree)). %% 1. Strip host_config, choose the handler for the remaining path %% 2. Wrap the handler in a fun that calls the resulting function F for the current host --spec handler_for_host(path()) -> fun((path(), toml_value()) -> option()). +-spec handler_for_host(path()) -> + fun((path(), toml_value()) -> option()) | mongoose_config_spec:config_node(). handler_for_host(Path) -> [<<"host_config">>, {host, Host} | Rest] = lists:reverse(Path), - Handler = handler(lists:reverse(Rest)), - fun(PathArg, ValueArg) -> - ConfigFunctions = Handler(PathArg, ValueArg), - lists:flatmap(fun(F) -> F(Host) end, ConfigFunctions) + case handler(lists:reverse(Rest)) of + Handler when is_function(Handler) -> + fun(PathArg, ValueArg) -> + ConfigFunctions = Handler(PathArg, ValueArg), + lists:flatmap(fun(F) -> F(Host) end, ConfigFunctions) + end; + Spec -> + Spec end. -spec key(toml_key(), path(), toml_value()) -> tuple() | toml_key(). diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl new file mode 100644 index 0000000000..9b40ee41c4 --- /dev/null +++ b/src/config/mongoose_config_spec.erl @@ -0,0 +1,111 @@ +-module(mongoose_config_spec). + +-compile(export_all). + +-include("ejabberd_config.hrl"). + +-type config_node() :: #section{} | #option{} | #list{}. + +handler(Path) -> + handler(Path, root()). + +handler([Node], #section{items = Items}) when is_map(Items) -> + maps:get(Node, Items); +handler([_Node], #section{items = Item}) -> + Item; +handler([item], #list{items = Item}) -> + Item; +handler([Node|Rest], #section{items = Items}) when is_map(Items) -> + Item = maps:get(Node, Items), + handler(Rest, Item); +handler([_Node|Rest], #section{items = Item}) -> + handler(Rest, Item); +handler([item|Rest], #list{items = Items}) -> + handler(Rest, Items). + +root() -> + #section{ + items = #{<<"general">> => general()}, + process = fun ?MODULE:process_root/1 + }. + +general() -> + #section{ + items = #{<<"loglevel">> => #option{type = atom, + validate = loglevel, + format = local_config}, + <<"hosts">> => #list{items = #option{type = binary, + validate = non_empty, + process = fun ?MODULE:prepare_host/1}, + validate = unique_non_empty, + format = config}, + <<"registration_timeout">> => #option{type = int_or_infinity, + validate = timeout, + format = local_config}, + <<"language">> => #option{type = binary, + validate = non_empty, + format = config}, + <<"all_metrics_are_global">> => #option{type = boolean, + format = local_config}, + <<"sm_backend">> => #option{type = atom, + validate = {module, ejabberd_sm_}, + process = fun ?MODULE:process_sm_backend/1, + format = config}, + <<"max_fsm_queue">> => #option{type = integer, + validate = positive, + format = local_config}, + <<"http_server_name">> => #option{type = string, + format = {local_config, cowboy_server_name}}, + <<"rdbms_server_type">> => #option{type = atom, + validate = {enum, [mssql, pgsql]}, + format = local_config}, + <<"override">> => #list{items = #option{type = atom, + validate = {enum, [local, global, acls]}, + format = override}, + validate = unique_non_empty, + format = none}, + <<"pgsql_users_number_estimate">> => #option{type = boolean, + format = host_local_config}, + <<"route_subdomains">> => #option{type = atom, + validate = {enum, [s2s]}, + format = host_local_config}, + <<"mongooseimctl_access_commands">> => #section{items = ctl_access_rule(), + format = local_config}, + <<"routing_modules">> => #list{items = #option{type = atom, + validate = module}, + format = local_config}, + <<"replaced_wait_timeout">> => #option{type = integer, + validate = positive, + format = host_local_config}, + <<"hide_service_name">> => #option{type = boolean, + format = host_local_config} + } + }. + +ctl_access_rule() -> + #section{ + items = #{<<"commands">> => #list{items = #option{type = string}}, + <<"argument_restrictions">> => #section{items = #option{type = string}} + }, + process = fun ?MODULE:process_ctl_access_rule/1, + format = prepend_key + }. + +process_root(KVs) -> + true = lists:any(fun(#local_config{key = hosts}) -> true; + (_) -> false + end, KVs), + KVs. + +process_ctl_access_rule(KVs) -> + Commands = proplists:get_value(commands, KVs, all), + ArgRestrictions = proplists:get_value(argument_restrictions, KVs, []), + {Commands, ArgRestrictions}. + +process_sm_backend(Backend) -> + {Backend, []}. + +prepare_host(Host) -> + Node = jid:nodeprep(Host), + true = Node =/= error, + Node. diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index f9d1e6a92c..e0949feeda 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -1,6 +1,8 @@ -module(mongoose_config_validator_toml). --export([validate/2]). +-export([validate/2, + validate/3]). +-compile(export_all). -include("mongoose.hrl"). -include("ejabberd_config.hrl"). @@ -14,59 +16,6 @@ validate(Path, [F]) when is_function(F, 1) -> validate(Path, F(?HOST)); -%% general -validate([<<"loglevel">>, <<"general">>], - [#local_config{value = Val}]) -> validate_loglevel(Val); -validate([item, <<"hosts">>, <<"general">>], - [Value]) -> - validate_non_empty_binary(Value); -validate([<<"hosts">>, <<"general">>], - [#config{value = Val}]) -> - validate_hosts(Val); -validate([<<"registration_timeout">>, <<"general">>], - [#local_config{value = Val}]) -> - validate_timeout(Val); -validate([<<"language">>, <<"general">>], - [#config{value = Value}]) -> - validate_non_empty_binary(Value); -validate([<<"all_metrics_are_global">>, <<"general">>], - [#local_config{value = Val}]) -> - validate_boolean(Val); -validate([<<"sm_backend">>, <<"general">>], - [#config{value = {Backend, []}}]) -> - validate_module(list_to_atom("ejabberd_sm_" ++ atom_to_list(Backend))); -validate([<<"max_fsm_queue">>, <<"general">>], - [#local_config{value = Value}]) -> - validate_positive_integer(Value); -validate([<<"rdbms_server_type">>, <<"general">>], - [#local_config{value = Value}]) -> - validate_enum(Value, [mssql, pgsql]); -validate([item, <<"override">>, <<"general">>], - [{override, Value}]) -> - validate_enum(Value, [local, global, acls]); -validate([<<"override">>, <<"general">>], - Items) -> - validate_unique_items(Items); -validate([<<"pgsql_users_number_estimate">>, <<"general">>|Path], - [#local_config{value = Value}]) -> - validate_root_or_host_config(Path), - validate_boolean(Value); -validate([<<"route_subdomains">>, <<"general">>|Path], - [#local_config{value = Value}]) -> - validate_root_or_host_config(Path), - validate_enum(Value, [s2s]); -validate([item, <<"routing_modules">>, <<"general">>], - [Value]) -> - validate_module(Value); -validate([<<"replaced_wait_timeout">>, <<"general">>|Path], - [#local_config{value = Value}]) -> - validate_root_or_host_config(Path), - validate_positive_integer(Value); -validate([<<"hide_service_name">>, <<"general">>|Path], - [#local_config{value = Value}]) -> - validate_root_or_host_config(Path), - validate_boolean(Value); - %% listen validate([item, _Type, <<"listen">>], [{{Port, _IPT, _Proto}, _Module, _Opts}]) -> @@ -1578,6 +1527,27 @@ validate([<<"timeout_action">>, <<"mod_ping">>, <<"modules">>|_], validate(_Path, _Value) -> ok. +validate(V, boolean, any) -> validate_boolean(V); +validate(V, binary, domain) -> validate_binary_domain(V); +validate(V, binary, non_empty) -> validate_non_empty_binary(V); +validate(V, integer, positive) -> validate_positive_integer(V); +validate(V, int_or_infinity, timeout) -> validate_timeout(V); +validate(V, string, url) -> validate_url(V); +validate(V, string, non_empty) -> validate_non_empty_string(V); +validate(V, atom, module) -> validate_module(V); +validate(V, atom, {module, Prefix}) -> + validate_module(list_to_atom(atom_to_list(Prefix) ++ atom_to_list(V))); +validate(V, atom, loglevel) -> validate_loglevel(V); +validate(V, _, {enum, Values}) -> validate_enum(V, Values); +validate(_V, _, any) -> ok. + +validate_list([_|_], non_empty) -> ok; +validate_list(L = [_|_], unique_non_empty) -> + validate_unique_items(L); +validate_list(L, any) when is_list(L) -> ok. + +validate_section([_|_], non_empty) -> ok; +validate_section(L, any) when is_list(L) -> ok. %% validators diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index 139a578786..93617cda80 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -312,18 +312,26 @@ mongooseimctl_access_commands(_Config) -> parse(#{<<"general">> => #{<<"mongooseimctl_access_commands">> => #{<<"local">> => AccessRule}}})), ?eq([#local_config{key = mongooseimctl_access_commands, - value = [{local, all, []}] + value = [{local, all, [{node, "mim1@host1"}]}] + }], + parse(#{<<"general">> => #{<<"mongooseimctl_access_commands">> => + #{<<"local">> => maps:remove(<<"commands">>, + AccessRule)}}})), + ?eq([#local_config{key = mongooseimctl_access_commands, + value = [{local, ["join_cluster"], []}] }], parse(#{<<"general">> => #{<<"mongooseimctl_access_commands">> => - #{<<"local">> => #{<<"commands">> => <<"all">>}}}})), - ?err(parse(#{<<"general">> => - #{<<"mongooseimctl_access_commands">> => - #{<<"local">> => #{<<"argument_restrictions">> => - #{<<"node">> => <<"mim1@host1">>}}} - }})), + #{<<"local">> => maps:remove(<<"argument_restrictions">>, + AccessRule)}}})), + ?eq([#local_config{key = mongooseimctl_access_commands, + value = [{local, all, []}] + }], + parse(#{<<"general">> => #{<<"mongooseimctl_access_commands">> => #{<<"local">> => #{}}}})), + ?err(parse(#{<<"general">> => #{<<"mongooseimctl_access_commands">> => + #{<<"local">> => #{<<"commands">> => <<"all">>}}}})), ?err(parse(#{<<"general">> => #{<<"mongooseimctl_access_commands">> => - #{<<"local">> => #{<<"commands">> => <<"none">>}} - }})). + #{<<"local">> => #{<<"argument_restrictions">> => + [<<"none">>]}}}})). routing_modules(_Config) -> ?eq([#local_config{key = routing_modules, value = [mongoose_router_global, From 2df5dfa3d575b3ad26619c3fbedf08c76db829dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Tue, 10 Nov 2020 14:44:36 +0100 Subject: [PATCH 015/104] Document the changed format of mongooseim_access_commands --- doc/advanced-configuration/general.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/doc/advanced-configuration/general.md b/doc/advanced-configuration/general.md index 0caf2a03e3..e9fab2068a 100644 --- a/doc/advanced-configuration/general.md +++ b/doc/advanced-configuration/general.md @@ -69,8 +69,8 @@ User access rules are configured mainly in the [`acl`](acl.md) and [`access`](ac ## `general.mongooseimctl_access_commands` * **Scope:** local * **Syntax:** TOML table, whose **keys** are the names of the access rules defined in the [`access`](access.md) config section and **values** specify allowed administration commands. Each value is a table with the following nested options: - * `commands`: mandatory, a list of strings representing the allowed commands, or the string `"all"` - * `argument_restrictions`: optional, a table whose keys are the argument names and the values are strings representing the allowed values + * `commands`: optional, a list of strings representing the allowed commands. When not specified, all commands are allowed. + * `argument_restrictions`: optional, a table whose keys are the argument names and the values are strings representing the allowed values. When not specified, there are no restrictions. * **Default:** not set By default all admin operations are permitted with the `mongooseimctl` command without authentication. You can change that by setting this option for a specific access rule. When the rule returns the value `"allow"`, the user is permitted to use the specified commands with the optional restrictions. @@ -79,7 +79,6 @@ By default all admin operations are permitted with the `mongooseimctl` command w ``` [general.mongooseimctl_access_commands.admin] - commands = "all" ``` The `admin` rule needs to be defined in the `access` section. From 8eaa5005cd2d41874c6976cb9cf2b6a1be3fb4f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Fri, 13 Nov 2020 15:47:10 +0100 Subject: [PATCH 016/104] Don't stop MongooseIM in the parser backend Motivation: - make test failures easier to debug (no crashes) - call 'exit' at one point in the code (instead of two) --- src/config/mongoose_config_parser.erl | 8 +++++++- src/config/mongoose_config_parser_toml.erl | 12 ++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/config/mongoose_config_parser.erl b/src/config/mongoose_config_parser.erl index c0933f9d51..0a3ef4228b 100644 --- a/src/config/mongoose_config_parser.erl +++ b/src/config/mongoose_config_parser.erl @@ -60,7 +60,13 @@ -spec parse_file(FileName :: string()) -> state(). parse_file(FileName) -> ParserModule = parser_module(filename:extension(FileName)), - ParserModule:parse_file(FileName). + try + ParserModule:parse_file(FileName) + catch + error:{config_error, ExitMsg, Errors} -> + [?LOG_ERROR(Error) || Error <- Errors], + mongoose_config_utils:exit_or_halt(ExitMsg) + end. %% Only the TOML format is supported parser_module(".toml") -> mongoose_config_parser_toml. diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index 205b3c36fb..adf2e5bcb8 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -42,9 +42,7 @@ parse_file(FileName) -> process(Content); {error, Error} -> Text = tomerl:format_error(Error), - ?LOG_ERROR(#{what => toml_parsing_failed, - text => Text}), - mongoose_config_utils:exit_or_halt("Could not load the TOML configuration file") + error(config_error([#{what => toml_parsing_failed, text => Text}])) end. -spec process(toml_section()) -> mongoose_config_parser:state(). @@ -60,11 +58,13 @@ process(Content) -> case extract_errors(AllOpts) of [] -> build_state(Hosts, AllOpts, Overrides); - [#{text := Text}|_] = Errors -> - [?LOG_ERROR(Error) || Error <- Errors], - mongoose_config_utils:exit_or_halt(Text) + Errors -> + error(config_error(Errors)) end. +config_error(Errors) -> + {config_error, "Could not read the TOML configuration file", Errors}. + %% Config processing functions are annotated with TOML paths %% Path syntax: dotted, like TOML keys with the following additions: %% - '[]' denotes an element in a list From d3bd41a21ee48a71aa3f7533407ebb60a6a88a62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Fri, 13 Nov 2020 15:49:38 +0100 Subject: [PATCH 017/104] Introduce new options to the config specs These options are necessary for the listeners: - 'required' - to specify required keys in a section - 'validate_keys' - to validate arbitrary sections keys - 'default' key in sections - to mix arbitrary and fixed keys - 'process' is now allowed to accept the TOML path as the 2nd argument --- include/ejabberd_config.hrl | 2 + src/config/mongoose_config_parser_toml.erl | 43 ++++++++++++++++------ src/config/mongoose_config_spec.erl | 27 ++++++++------ 3 files changed, 49 insertions(+), 23 deletions(-) diff --git a/include/ejabberd_config.hrl b/include/ejabberd_config.hrl index fd38cdbcdb..f70b95d790 100644 --- a/include/ejabberd_config.hrl +++ b/include/ejabberd_config.hrl @@ -28,6 +28,8 @@ }). -record(section, {items, + validate_keys = any, + required = [], validate = any, process, format = default}). diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index adf2e5bcb8..c65bae968b 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -1709,7 +1709,9 @@ handle_step(handle, _) -> handle_step(parse, Spec) when is_tuple(Spec) -> fun(Path, Value) -> ParsedValue = case Spec of - #section{} when is_map(Value) -> + #section{} = Spec when is_map(Value) -> + check_required_keys(Spec, Value), + validate_keys(Spec, Value), parse_section(Path, Value); #list{} when is_list(Value) -> parse_list(Path, Value); @@ -1734,8 +1736,9 @@ handle_step(validate, ParsedValue) -> ParsedValue end; handle_step(process, {ParsedValue, Spec}) -> - fun(_Path, _Value) -> - {process_value(ParsedValue, Spec), Spec} + fun(Path, _Value) -> + ProcessedValue = process(Path, ParsedValue, process_spec(Spec)), + {ProcessedValue, Spec} end; handle_step(process, V) -> fun(_, _) -> V end; @@ -1746,6 +1749,17 @@ handle_step(format, {ParsedValue, Spec}) -> handle_step(format, V) -> fun(_, _) -> V end. +check_required_keys(#section{required = all, items = Items}, Section) -> + ensure_keys(maps:keys(Items), Section); +check_required_keys(#section{required = Required}, Section) -> + ensure_keys(Required, Section). + +validate_keys(#section{validate_keys = undefined}, _Section) -> ok; +validate_keys(#section{validate_keys = Validator}, Section) -> + lists:foreach(fun(Key) -> + mongoose_config_validator_toml:validate(b2a(Key), atom, Validator) + end, maps:keys(Section)). + validate(Value, #section{validate = Validator}) -> mongoose_config_validator_toml:validate_section(Value, Validator); validate(Value, #list{validate = Validator}) -> @@ -1753,12 +1767,13 @@ validate(Value, #list{validate = Validator}) -> validate(Value, #option{type = Type, validate = Validator}) -> mongoose_config_validator_toml:validate(Value, Type, Validator). -process_value(V, #section{process = undefined}) -> V; -process_value(V, #list{process = undefined}) -> V; -process_value(V, #option{process = undefined}) -> V; -process_value(V, #section{process = Process}) -> Process(V); -process_value(V, #list{process = Process}) -> Process(V); -process_value(V, #option{process = Process}) -> Process(V). +process_spec(#section{process = Process}) -> Process; +process_spec(#list{process = Process}) -> Process; +process_spec(#option{process = Process}) -> Process. + +process(_Path, V, undefined) -> V; +process(_Path, V, F) when is_function(F, 1) -> F(V); +process(Path, V, F) when is_function(F, 2) -> F(Path, V). convert(V, boolean) -> V; convert(V, binary) -> V; @@ -1792,10 +1807,14 @@ format(Path, V, {config, Key}) -> format(Path, V, override) -> global = get_host(Path), [{override, V}]; -format([item|_], V, default) -> +format([item|_] = Path, V, default) -> + format(Path, V, item); +format([Key|_] = Path, V, default) -> + format(Path, V, {kv, b2a(Key)}); +format(_Path, V, {kv, Key}) -> + [{Key, V}]; +format(_Path, V, item) -> [V]; -format([Key|_], V, default) -> - [{b2a(Key), V}]; format([Key|_], V, prepend_key) -> L = [b2a(Key) | tuple_to_list(V)], [list_to_tuple(L)]; diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index 9b40ee41c4..dbebcfb9aa 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -9,16 +9,18 @@ handler(Path) -> handler(Path, root()). -handler([Node], #section{items = Items}) when is_map(Items) -> - maps:get(Node, Items); -handler([_Node], #section{items = Item}) -> - Item; +handler([Node], #section{items = Items}) when is_binary(Node) -> + case maps:is_key(Node, Items) of + true -> maps:get(Node, Items); + false -> maps:get(default, Items) + end; handler([item], #list{items = Item}) -> Item; -handler([Node|Rest], #section{items = Items}) when is_map(Items) -> - Item = maps:get(Node, Items), - handler(Rest, Item); -handler([_Node|Rest], #section{items = Item}) -> +handler([Node|Rest], #section{items = Items}) when is_binary(Node) -> + Item = case maps:is_key(Node, Items) of + true -> maps:get(Node, Items); + false -> maps:get(default, Items) + end, handler(Rest, Item); handler([item|Rest], #list{items = Items}) -> handler(Rest, Items). @@ -69,8 +71,9 @@ general() -> <<"route_subdomains">> => #option{type = atom, validate = {enum, [s2s]}, format = host_local_config}, - <<"mongooseimctl_access_commands">> => #section{items = ctl_access_rule(), - format = local_config}, + <<"mongooseimctl_access_commands">> => #section{ + items = #{default => ctl_access_rule()}, + format = local_config}, <<"routing_modules">> => #list{items = #option{type = atom, validate = module}, format = local_config}, @@ -85,7 +88,9 @@ general() -> ctl_access_rule() -> #section{ items = #{<<"commands">> => #list{items = #option{type = string}}, - <<"argument_restrictions">> => #section{items = #option{type = string}} + <<"argument_restrictions">> => #section{ + items = #{default => #option{type = string}} + } }, process = fun ?MODULE:process_ctl_access_rule/1, format = prepend_key From d60c693760178315f51f4fe07d13da1e0707635e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Fri, 13 Nov 2020 16:02:26 +0100 Subject: [PATCH 018/104] Make the 'listen' section spec declarative Functional changes: - Dropped the Erlang format for ciphers Motivation: - simplicity - TOML type consistency (section vs string) - the 'list of strings' syntax was undocumented both in MongooseIM and in the OTP docs - Removed 'ping_rate = "none"' for type consistency --- rel/mim3.vars-toml.config | 7 +- src/config/mongoose_config_parser_toml.erl | 237 +----------- src/config/mongoose_config_spec.erl | 346 +++++++++++++++++- src/config/mongoose_config_validator_toml.erl | 107 +----- 4 files changed, 347 insertions(+), 350 deletions(-) diff --git a/rel/mim3.vars-toml.config b/rel/mim3.vars-toml.config index f5569edb43..952755046a 100644 --- a/rel/mim3.vars-toml.config +++ b/rel/mim3.vars-toml.config @@ -35,12 +35,7 @@ tls.certfile = \"priv/ssl/fake_server.pem\" tls.mode = \"tls\" tls.module = \"just_tls\" - - [[listen.c2s.tls.ciphers]] - cipher = \"aes_256_gcm\" - key_exchange = \"ecdhe_rsa\" - mac = \"aead\" - prf = \"sha384\""}. + tls.ciphers = \"ECDHE-RSA-AES256-GCM-SHA384\""}. {http_api_old_endpoint, "ip_address = \"127.0.0.1\" port = {{ http_api_old_endpoint_port }}"}. diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index c65bae968b..5ef1e42075 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -118,197 +118,6 @@ process_section([<<"host_config">>] = Path, Content) -> process_section(Path, Content) -> parse_section(Path, Content). -%% path: listen.*[] --spec process_listener(path(), toml_section()) -> [option()]. -process_listener([_, Type|_] = Path, Content) -> - Options = maps:without([<<"port">>, <<"ip_address">>], Content), - PortIP = listener_portip(Content), - parse_section(Path, Options, - fun(Opts) -> - {Port, IPT, _, _, Proto, OptsClean} = - ejabberd_listener:parse_listener_portip(PortIP, Opts), - [{{Port, IPT, Proto}, listener_module(Type), OptsClean}] - end). - --spec listener_portip(toml_section()) -> option(). -listener_portip(#{<<"port">> := Port, <<"ip_address">> := Addr}) -> {Port, b2l(Addr)}; -listener_portip(#{<<"port">> := Port}) -> Port. - --spec listener_module(toml_key()) -> option(). -listener_module(<<"http">>) -> ejabberd_cowboy; -listener_module(<<"c2s">>) -> ejabberd_c2s; -listener_module(<<"s2s">>) -> ejabberd_s2s_in; -listener_module(<<"service">>) -> ejabberd_service. - -%% path: listen.http[].* --spec http_listener_opt(path(), toml_value()) -> [option()]. -http_listener_opt([<<"tls">>|_] = Path, Opts) -> - [{ssl, parse_section(Path, Opts)}]; -http_listener_opt([<<"transport">>|_] = Path, Opts) -> - [{transport_options, parse_section(Path, Opts)}]; -http_listener_opt([<<"protocol">>|_] = Path, Opts) -> - [{protocol_options, parse_section(Path, Opts)}]; -http_listener_opt([<<"handlers">>|_] = Path, Handlers) -> - [{modules, parse_section(Path, Handlers)}]; -http_listener_opt(P, V) -> listener_opt(P, V). - -%% path: listen.c2s[].* --spec c2s_listener_opt(path(), toml_value()) -> [option()]. -c2s_listener_opt([<<"access">>|_], V) -> [{access, b2a(V)}]; -c2s_listener_opt([<<"shaper">>|_], V) -> [{shaper, b2a(V)}]; -c2s_listener_opt([<<"xml_socket">>|_], V) -> [{xml_socket, V}]; -c2s_listener_opt([<<"zlib">>|_], V) -> [{zlib, V}]; -c2s_listener_opt([<<"max_fsm_queue">>|_], V) -> [{max_fsm_queue, V}]; -c2s_listener_opt([{tls, _}|_] = P, V) -> listener_tls_opts(P, V); -c2s_listener_opt(P, V) -> xmpp_listener_opt(P, V). - -%% path: listen.s2s[].* --spec s2s_listener_opt(path(), toml_value()) -> [option()]. -s2s_listener_opt([<<"shaper">>|_], V) -> [{shaper, b2a(V)}]; -s2s_listener_opt([<<"tls">>|_] = P, V) -> parse_section(P, V); -s2s_listener_opt(P, V) -> xmpp_listener_opt(P, V). - -%% path: listen.service[].*, -%% listen.http[].handlers.mod_websockets[].service.* --spec service_listener_opt(path(), toml_value()) -> [option()]. -service_listener_opt([<<"access">>|_], V) -> [{access, b2a(V)}]; -service_listener_opt([<<"shaper_rule">>|_], V) -> [{shaper_rule, b2a(V)}]; -service_listener_opt([<<"check_from">>|_], V) -> [{service_check_from, V}]; -service_listener_opt([<<"hidden_components">>|_], V) -> [{hidden_components, V}]; -service_listener_opt([<<"conflict_behaviour">>|_], V) -> [{conflict_behaviour, b2a(V)}]; -service_listener_opt([<<"password">>|_], V) -> [{password, b2l(V)}]; -service_listener_opt([<<"max_fsm_queue">>|_], V) -> [{max_fsm_queue, V}]; -service_listener_opt(P, V) -> xmpp_listener_opt(P, V). - -%% path: listen.c2s[].*, listen.s2s[].*, listen.service[].* --spec xmpp_listener_opt(path(), toml_value()) -> [option()]. -xmpp_listener_opt([<<"hibernate_after">>|_], V) -> [{hibernate_after, V}]; -xmpp_listener_opt([<<"max_stanza_size">>|_], V) -> [{max_stanza_size, V}]; -xmpp_listener_opt([<<"backlog">>|_], N) -> [{backlog, N}]; -xmpp_listener_opt([<<"proxy_protocol">>|_], V) -> [{proxy_protocol, V}]; -xmpp_listener_opt([<<"num_acceptors">>|_], V) -> [{acceptors_num, V}]; -xmpp_listener_opt(Path, V) -> listener_opt(Path, V). - -%% path: listen.*[].* --spec listener_opt(path(), toml_value()) -> [option()]. -listener_opt([<<"proto">>|_], Proto) -> [{proto, b2a(Proto)}]; -listener_opt([<<"ip_version">>|_], 6) -> [inet6]; -listener_opt([<<"ip_version">>|_], 4) -> [inet]. - -%% path: listen.http[].tls.* --spec https_option(path(), toml_value()) -> [option()]. -https_option([<<"verify_mode">>|_], Value) -> [{verify_mode, b2a(Value)}]; -https_option(Path, Value) -> tls_option(Path, Value). - -%% path: listen.c2s[].tls.* --spec c2s_tls_option(path(), toml_value()) -> option(). -c2s_tls_option([<<"mode">>|_], V) -> [b2a(V)]; -c2s_tls_option([<<"verify_peer">>|_], V) -> [verify_peer(V)]; -c2s_tls_option([<<"protocol_options">>, {tls, fast_tls}|_] = Path, V) -> - [{protocol_options, parse_list(Path, V)}]; -c2s_tls_option([_, {tls, fast_tls}|_] = Path, V) -> fast_tls_option(Path, V); -c2s_tls_option([<<"verify_mode">>, {tls, just_tls}|_], V) -> b2a(V); -c2s_tls_option([<<"disconnect_on_failure">>, {tls, just_tls}|_], V) -> V; -c2s_tls_option([<<"crl_files">>, {tls, just_tls}|_] = Path, V) -> [{crlfiles, parse_list(Path, V)}]; -c2s_tls_option([_, {tls, just_tls}|_] = Path, V) -> tls_option(Path, V). - -%% path: listen.s2s[].tls.* --spec s2s_tls_option(path(), toml_value()) -> [option()]. -s2s_tls_option([<<"protocol_options">>|_] = Path, V) -> - [{protocol_options, parse_list(Path, V)}]; -s2s_tls_option([Opt|_] = Path, Val) when Opt =:= <<"cacertfile">>; - Opt =:= <<"dhfile">>; - Opt =:= <<"ciphers">> -> - fast_tls_option(Path, Val). - -%% path: listen.http[].transport.* --spec cowboy_transport_opt(path(), toml_value()) -> [option()]. -cowboy_transport_opt([<<"num_acceptors">>|_], N) -> [{num_acceptors, N}]; -cowboy_transport_opt([<<"max_connections">>|_], N) -> [{max_connections, int_or_infinity(N)}]. - -%% path: listen.http[].protocol.* --spec cowboy_protocol_opt(path(), toml_value()) -> [option()]. -cowboy_protocol_opt([<<"compress">>|_], V) -> [{compress, V}]. - -%% path: listen.http[].handlers.*[] --spec cowboy_module(path(), toml_section()) -> [option()]. -cowboy_module([_, Type|_] = Path, #{<<"host">> := Host, <<"path">> := ModPath} = Options) -> - Opts = maps:without([<<"host">>, <<"path">>], Options), - ModuleOpts = cowboy_module_options(Path, Opts), - [{b2l(Host), b2l(ModPath), b2a(Type), ModuleOpts}]. - --spec cowboy_module_options(path(), toml_section()) -> [option()]. -cowboy_module_options([_, <<"mod_websockets">>|_] = Path, Opts) -> - parse_section(Path, Opts); -cowboy_module_options([_, <<"lasse_handler">>|_], Opts) -> - limit_keys([<<"module">>], Opts), - #{<<"module">> := Module} = Opts, - [b2a(Module)]; -cowboy_module_options([_, <<"cowboy_static">>|_], Opts) -> - limit_keys([<<"type">>, <<"app">>, <<"content_path">>], Opts), - #{<<"type">> := Type, - <<"app">> := App, - <<"content_path">> := Path} = Opts, - {b2a(Type), b2a(App), b2l(Path), [{mimetypes, cow_mimetypes, all}]}; -cowboy_module_options([_, <<"cowboy_swagger_redirect_handler">>|_], Opts) -> - Opts = #{}; -cowboy_module_options([_, <<"cowboy_swagger_json_handler">>|_], Opts) -> - Opts = #{}; -cowboy_module_options([_, <<"mongoose_api">>|_] = Path, Opts) -> - #{<<"handlers">> := _} = Opts, - parse_section(Path, Opts); -cowboy_module_options([_, <<"mongoose_api_admin">>|_], - #{<<"username">> := User, <<"password">> := Pass}) -> - [{auth, {User, Pass}}]; -cowboy_module_options([_, <<"mongoose_api_admin">>|_], #{}) -> - []; -cowboy_module_options([_, <<"mongoose_api_client">>|_], #{}) -> - []; -cowboy_module_options(_, Opts) -> - limit_keys([], Opts), - []. - -%% path: listen.http[].handlers.mod_websockets[].* --spec websockets_option(path(), toml_value()) -> [option()]. -websockets_option([<<"timeout">>|_], V) -> - [{timeout, int_or_infinity(V)}]; -websockets_option([<<"ping_rate">>|_], <<"none">>) -> - [{ping_rate, none}]; -websockets_option([<<"ping_rate">>|_], V) -> - [{ping_rate, V}]; -websockets_option([<<"max_stanza_size">>|_], V) -> - [{max_stanza_size, int_or_infinity(V)}]; -websockets_option([<<"service">>|_] = Path, Value) -> - [{ejabberd_service, parse_section(Path, Value)}]. - -%% path: listen.http[].handlers.mongoose_api[].* --spec mongoose_api_option(path(), toml_value()) -> [option()]. -mongoose_api_option([<<"handlers">>|_] = Path, Value) -> - [{handlers, parse_list(Path, Value)}]. - -%% path: listen.c2s[].tls --spec listener_tls_opts(path(), toml_section()) -> [option()]. -listener_tls_opts([{tls, just_tls}|_] = Path, M) -> - VM = just_tls_verify_fun(Path, M), - Common = maps:with([<<"mode">>, <<"verify_peer">>, <<"crl_files">>], M), - OptsM = maps:without([<<"module">>, - <<"mode">>, <<"verify_peer">>, <<"crl_files">>, - <<"verify_mode">>, <<"disconnect_on_failure">>], M), - SSLOpts = case VM ++ parse_section(Path, OptsM) of - [] -> []; - Opts -> [{ssl_options, Opts}] - end, - [{tls_module, just_tls}] ++ SSLOpts ++ parse_section(Path, Common); -listener_tls_opts([{tls, fast_tls}|_] = Path, M) -> - parse_section(Path, maps:without([<<"module">>], M)). - --spec just_tls_verify_fun(path(), toml_section()) -> [option()]. -just_tls_verify_fun(Path, #{<<"verify_mode">> := _} = M) -> - VMode = parse_kv(Path, <<"verify_mode">>, M), - Disconnect = parse_kv(Path, <<"disconnect_on_failure">>, M, true), - [{verify_fun, {VMode, Disconnect}}]; -just_tls_verify_fun(_, _) -> []. - %% path: (host_config[].)auth.* -spec auth_option(path(), toml_value()) -> [option()]. auth_option([<<"methods">>|_] = Path, Methods) -> @@ -1667,12 +1476,6 @@ parse_kv(Path, K, Section, Default) -> Key = key(K, Path, Value), handle([Key|Path], Value). --spec parse_kv(path(), toml_key(), toml_section()) -> option(). -parse_kv(Path, K, Section) -> - #{K := Value} = Section, - Key = key(K, Path, Value), - handle([Key|Path], Value). - %% Parse with post-processing, this needs to be eliminated by fixing the internal config structure -spec parse_section(path(), toml_section(), fun(([option()]) -> option())) -> option(). parse_section(Path, V, PostProcessF) -> @@ -1868,37 +1671,6 @@ node_to_string(Node) -> [binary_to_list(Node)]. handler([]) -> fun parse_root/2; handler([_]) -> fun process_section/2; -%% listen -handler([_, <<"listen">>]) -> fun parse_list/2; -handler([_, _, <<"listen">>]) -> fun process_listener/2; -handler([_, _, <<"http">>, <<"listen">>]) -> fun http_listener_opt/2; -handler([_, _, <<"c2s">>, <<"listen">>]) -> fun c2s_listener_opt/2; -handler([_, _, <<"s2s">>, <<"listen">>]) -> fun s2s_listener_opt/2; -handler([_, <<"tls">>, _, <<"s2s">>, <<"listen">>]) -> fun s2s_tls_option/2; -handler([_, _, <<"service">>, <<"listen">>]) -> fun service_listener_opt/2; -handler([_, {tls, _}, _, <<"c2s">>, <<"listen">>]) -> fun c2s_tls_option/2; -handler([_, <<"versions">>, {tls, just_tls}, _, <<"c2s">>, <<"listen">>]) -> - fun(_, Val) -> [b2a(Val)] end; -handler([_, <<"ciphers">>, {tls, just_tls}, _, <<"c2s">>, <<"listen">>]) -> - fun tls_cipher/2; -handler([_, <<"crl_files">>, {tls, just_tls}, _, <<"c2s">>, <<"listen">>]) -> - fun(_, Val) -> [b2l(Val)] end; -handler([_, <<"protocol_options">>, _TLS, _, _, <<"listen">>]) -> - fun(_, Val) -> [b2l(Val)] end; -handler([_, <<"tls">>, _, <<"http">>, <<"listen">>]) -> fun https_option/2; -handler([_, <<"transport">>, _, <<"http">>, <<"listen">>]) -> fun cowboy_transport_opt/2; -handler([_, <<"protocol">>, _, <<"http">>, <<"listen">>]) -> fun cowboy_protocol_opt/2; -handler([_, <<"handlers">>, _, <<"http">>, <<"listen">>]) -> fun parse_list/2; -handler([_, _, <<"handlers">>, _, <<"http">>, <<"listen">>]) -> fun cowboy_module/2; -handler([_, _, <<"mongoose_api">>, <<"handlers">>, _, <<"http">>, <<"listen">>]) -> - fun mongoose_api_option/2; -handler([_, <<"handlers">>, _, <<"mongoose_api">>, <<"handlers">>, _, <<"http">>, <<"listen">>]) -> - fun(_, Val) -> [b2a(Val)] end; -handler([_, _, <<"mod_websockets">>, <<"handlers">>, _, <<"http">>, <<"listen">>]) -> - fun websockets_option/2; -handler([_, <<"service">>, _, <<"mod_websockets">>, <<"handlers">>, _, <<"http">>, <<"listen">>]) -> - fun service_listener_opt/2; - %% auth handler([_, <<"auth">>]) -> fun auth_option/2; handler([_, <<"anonymous">>, <<"auth">>]) -> fun auth_anonymous_option/2; @@ -2102,7 +1874,8 @@ handler(Path) -> subtree_handler(initial, [<<"host_config">>, {host, _} | Subtree]) -> subtree_handler(subtree, Subtree); -subtree_handler(_, [<<"general">>|_] = Path) -> +subtree_handler(_, [Section|_] = Path) when Section =:= <<"general">>; + Section =:= <<"listen">> -> mongoose_config_spec:handler(Path); subtree_handler(subtree, Subtree) -> handler(lists:reverse(Subtree)). @@ -2124,12 +1897,6 @@ handler_for_host(Path) -> end. -spec key(toml_key(), path(), toml_value()) -> tuple() | toml_key(). -key(<<"tls">>, [item, <<"c2s">>, <<"listen">>], M) -> - %% store the tls module in path as both of them need different options - case maps:get(<<"module">>, M, <<"fast_tls">>) of - <<"just_tls">> -> {tls, just_tls}; - <<"fast_tls">> -> {tls, fast_tls} - end; key(<<"connection">>, [_, <<"rdbms">>, <<"outgoing_pools">>], M) -> %% store the db driver in path as 'odbc' and 'mysql'/'pgsql' need different options Driver = maps:get(<<"driver">>, M), diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index dbebcfb9aa..e11f527119 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -27,10 +27,13 @@ handler([item|Rest], #list{items = Items}) -> root() -> #section{ - items = #{<<"general">> => general()}, - process = fun ?MODULE:process_root/1 + items = #{<<"general">> => general(), + <<"listen">> => listen() + }, + required = [<<"general">>] }. +%% path: general general() -> #section{ items = #{<<"loglevel">> => #option{type = atom, @@ -42,7 +45,7 @@ general() -> validate = unique_non_empty, format = config}, <<"registration_timeout">> => #option{type = int_or_infinity, - validate = timeout, + validate = positive, format = local_config}, <<"language">> => #option{type = binary, validate = non_empty, @@ -82,7 +85,8 @@ general() -> format = host_local_config}, <<"hide_service_name">> => #option{type = boolean, format = host_local_config} - } + }, + required = [<<"hosts">>] }. ctl_access_rule() -> @@ -96,11 +100,242 @@ ctl_access_rule() -> format = prepend_key }. -process_root(KVs) -> - true = lists:any(fun(#local_config{key = hosts}) -> true; - (_) -> false - end, KVs), - KVs. +%% path: listen +listen() -> + Keys = [<<"http">>, <<"c2s">>, <<"s2s">>, <<"service">>], + #section{ + items = maps:from_list([{Key, #list{items = listener(Key), + format = none}} || Key <- Keys]), + format = local_config + }. + +%% path: listen.*[] +listener(Type) -> + ExtraItems = listener_items(Type), + #section{ + items = ExtraItems#{<<"port">> => #option{type = integer, + validate = port}, + <<"ip_address">> => #option{type = string, + validate = ip_address}, + <<"proto">> => #option{type = atom, + validate = {enum, [tcp, udp, ws, wss]}}, + <<"ip_version">> => #option{type = integer, + validate = {enum, [4, 6]}, + process = fun ?MODULE:process_ip_version/1, + format = item}}, + required = [<<"port">>], + process = fun ?MODULE:process_listener/2 + }. + +listener_items(<<"http">>) -> + #{<<"tls">> => http_listener_tls(), + <<"transport">> => http_transport(), + <<"protocol">> => http_protocol(), + <<"handlers">> => http_handlers() + }; +listener_items(Type) -> + ExtraItems = xmpp_listener_items(Type), + ExtraItems#{<<"hibernate_after">> => #option{type = integer, + validate = non_negative}, + <<"max_stanza_size">> => #option{type = integer, + validate = positive}, + <<"backlog">> => #option{type = integer, + validate = non_negative}, + <<"proxy_protocol">> => #option{type = boolean}, + <<"num_acceptors">> => #option{type = integer, + validate = positive, + format = {kv, acceptors_num}} + }. + +xmpp_listener_items(<<"c2s">>) -> + #{<<"access">> => #option{type = atom, + validate = non_empty}, + <<"shaper">> => #option{type = atom, + validate = non_empty}, + <<"xml_socket">> => #option{type = boolean}, + <<"zlib">> => #option{type = integer, + validate = positive}, + <<"max_fsm_queue">> => #option{type = integer, + validate = positive}, + <<"tls">> => c2s_tls()}; +xmpp_listener_items(<<"s2s">>) -> + #{<<"shaper">> => #option{type = atom, + validate = non_empty}, + <<"tls">> => s2s_tls()}; +xmpp_listener_items(<<"service">>) -> + #{<<"access">> => #option{type = atom, + validate = non_empty}, + <<"shaper_rule">> => #option{type = atom, + validate = non_empty}, + <<"check_from">> => #option{type = boolean, + format = {kv, service_check_from}}, + <<"hidden_components">> => #option{type = boolean}, + <<"conflict_behaviour">> => #option{type = atom, + validate = {enum, [kick_old, disconnect]}}, + <<"password">> => #option{type = string, + validate = non_empty}, + <<"max_fsm_queue">> => #option{type = integer, + validate = positive}}. + +%% path: listen.c2s[].tls +c2s_tls() -> + #section{ + items = #{ + %% common + <<"module">> => #option{type = atom, + validate = {enum, [fast_tls, just_tls]}}, + <<"mode">> => #option{type = atom, + validate = {enum, [tls, starttls, starttls_required]}}, + <<"verify_peer">> => #option{type = boolean, + process = fun ?MODULE:process_verify_peer/1}, + <<"certfile">> => #option{type = string, + validate = non_empty}, + <<"cacertfile">> => #option{type = string, + validate = non_empty}, + <<"dhfile">> => #option{type = string, + validate = non_empty}, + <<"ciphers">> => #option{type = string}, + + %% fast_tls + <<"protocol_options">> => #list{items = #option{type = string, + validate = non_empty}}, + + %% just_tls + <<"verify_mode">> => #option{type = atom, + validate = {enum, [peer, selfsigned_peer, none]}}, + <<"disconnect_on_failure">> => #option{type = boolean}, + <<"crl_files">> => #list{items = #option{type = string, + validate = non_empty}, + format = {kv, crlfiles}}, + <<"password">> => #option{type = string}, + <<"server_name_indication">> => #option{type = boolean, + process = fun ?MODULE:process_sni/1}, + <<"versions">> => #list{items = #option{type = atom}} + }, + process = fun ?MODULE:process_xmpp_tls/1, + format = none + }. + +%% path: listen.s2s[].tls +s2s_tls() -> + #section{ + items = #{<<"cacertfile">> => #option{type = string, + validate = non_empty}, + <<"dhfile">> => #option{type = string, + validate = non_empty}, + <<"ciphers">> => #option{type = string}, + <<"protocol_options">> => #list{items = #option{type = string, + validate = non_empty}} + }, + process = fun ?MODULE:process_fast_tls/1, + format = none + }. + +%% path: listen.http[].tls +http_listener_tls() -> + #section{ + items = #{<<"verify_peer">> => #option{type = boolean, + process = fun ?MODULE:process_verify_peer/1, + format = {kv, verify}}, + <<"certfile">> => #option{type = string, + validate = non_empty}, + <<"cacertfile">> => #option{type = string, + validate = non_empty}, + <<"dhfile">> => #option{type = string, + validate = non_empty}, + <<"keyfile">> => #option{type = string, + validate = non_empty}, + <<"password">> => #option{type = string}, + <<"server_name_indication">> => #option{type = boolean, + process = fun ?MODULE:process_sni/1}, + <<"ciphers">> => #option{type = string}, + <<"versions">> => #list{items = #option{type = atom}}, + <<"verify_mode">> => #option{type = atom, + validate = {enum, [peer, selfsigned_peer, none]}} + }, + format = {kv, ssl} + }. + +%% path: listen.http[].transport +http_transport() -> + #section{ + items = #{<<"num_acceptors">> => #option{type = integer, + validate = positive}, + <<"max_connections">> => #option{type = int_or_infinity, + validate = non_negative} + }, + format = {kv, transport_options} + }. + +%% path: listen.http[].protocol +http_protocol() -> + #section{ + items = #{<<"compress">> => #option{type = boolean}}, + format = {kv, protocol_options} + }. + +%% path: listen.http[].handlers +http_handlers() -> + Keys = [<<"mod_websockets">>, + <<"lasse_handler">>, + <<"cowboy_static">>, + <<"mongoose_api">>, + <<"mongoose_api_admin">>, + default], + #section{ + items = maps:from_list([{Key, #list{items = http_handler(Key), + format = none}} || Key <- Keys]), + validate_keys = module, + format = {kv, modules} + }. + +%% path: listen.http[].handlers.*[] +http_handler(Key) -> + ExtraItems = http_handler_items(Key), + RequiredKeys = case http_handler_required(Key) of + all -> all; + [] -> [<<"host">>, <<"path">>] + end, + #section{ + items = ExtraItems#{<<"host">> => #option{type = string, + validate = non_empty}, + <<"path">> => #option{type = string} + }, + required = RequiredKeys, + process = fun ?MODULE:process_http_handler/2 + }. + +http_handler_items(<<"mod_websockets">>) -> + #{<<"timeout">> => #option{type = int_or_infinity, + validate = non_negative}, + <<"ping_rate">> => #option{type = integer, + validate = positive}, + <<"max_stanza_size">> => #option{type = int_or_infinity, + validate = positive}, + <<"service">> => #section{items = xmpp_listener_items(<<"service">>), + format = {kv, ejabberd_service}}}; +http_handler_items(<<"lasse_handler">>) -> + #{<<"module">> => #option{type = atom, + validate = module}}; +http_handler_items(<<"cowboy_static">>) -> + #{<<"type">> => #option{type = atom}, + <<"app">> => #option{type = atom}, + <<"content_path">> => #option{type = string}}; +http_handler_items(<<"mongoose_api">>) -> + #{<<"handlers">> => #list{items = #option{type = atom, + validate = module}}}; +http_handler_items(<<"mongoose_api_admin">>) -> + #{<<"username">> => #option{type = binary}, + <<"password">> => #option{type = binary}}; +http_handler_items(_) -> + #{}. + +http_handler_required(<<"lasse_handler">>) -> all; +http_handler_required(<<"cowboy_static">>) -> all; +http_handler_required(<<"mongoose_api">>) -> all; +http_handler_required(_) -> []. + +%% Callbacks for 'process' process_ctl_access_rule(KVs) -> Commands = proplists:get_value(commands, KVs, all), @@ -114,3 +349,96 @@ prepare_host(Host) -> Node = jid:nodeprep(Host), true = Node =/= error, Node. + +process_sni(false) -> + disable. + +process_lasse_handler([{module, Module}]) -> + [Module]. + +process_verify_peer(false) -> verify_none; +process_verify_peer(true) -> verify_peer. + +process_xmpp_tls(KVs) -> + Module = proplists:get_value(module, KVs, fast_tls), + case proplists:get_keys(KVs) -- (tls_keys(Module) ++ common_tls_keys()) of + [] -> strip_tls_keys(process_xmpp_tls(Module, proplists:delete(module, KVs))); + ExcessKeys -> error(#{what => {unexpected_tls_options, Module, ExcessKeys}}) + end. + +tls_keys(just_tls) -> + [verify_mode, disconnect_on_failure, crlfiles, password, server_name_indication, versions]; +tls_keys(fast_tls) -> + [protocol_options]. + +common_tls_keys() -> + [module, mode, verify_peer, certfile, cacertfile, dhfile, ciphers]. + +process_xmpp_tls(just_tls, KVs) -> + {[VM, DoF], Opts} = proplists:split(KVs, [verify_mode, disconnect_on_failure]), + {External, Internal} = lists:partition(fun is_external_tls_opt/1, Opts), + SSLOpts = ssl_opts(verify_fun(VM, DoF) ++ Internal), + [{tls_module, just_tls}] ++ SSLOpts ++ External; +process_xmpp_tls(fast_tls, KVs) -> + process_fast_tls(KVs). + +process_fast_tls(KVs) -> + proplists:substitute_aliases([{cacertfile, cafile}], KVs). + +strip_tls_keys(Opts) -> + lists:map(fun strip_tls_key/1, Opts). + +strip_tls_key({mode, V}) -> V; +strip_tls_key({verify_peer, V}) -> V; +strip_tls_key(KV) -> KV. + +verify_fun([], []) -> []; +verify_fun([{verify_mode, VM}], []) -> [{verify_fun, {VM, true}}]; +verify_fun([{verify_mode, VM}], [{disconnect_on_failure, DoF}]) -> [{verify_fun, {VM, DoF}}]. + +is_external_tls_opt({mode, _}) -> true; +is_external_tls_opt({verify_peer, _}) -> true; +is_external_tls_opt({crlfiles, _}) -> true; +is_external_tls_opt({_, _}) -> false. + +ssl_opts([]) -> []; +ssl_opts(Opts) -> [{ssl_options, Opts}]. + +process_ip_version(4) -> inet; +process_ip_version(6) -> inet6. + +process_listener([item, Type | _], KVs) -> + {[PortOpts, IPOpts], Opts} = proplists:split(KVs, [port, ip_address]), + PortIP = listener_portip(PortOpts, IPOpts), + {Port, IPT, _, _, Proto, OptsClean} = + ejabberd_listener:parse_listener_portip(PortIP, Opts), + {{Port, IPT, Proto}, listener_module(Type), OptsClean}. + +listener_portip([{port, Port}], []) -> Port; +listener_portip([{port, Port}], [{ip_address, Addr}]) -> {Port, Addr}. + +listener_module(<<"http">>) -> ejabberd_cowboy; +listener_module(<<"c2s">>) -> ejabberd_c2s; +listener_module(<<"s2s">>) -> ejabberd_s2s_in; +listener_module(<<"service">>) -> ejabberd_service. + +process_http_handler([item, Type | _], KVs) -> + {[[{host, Host}], [{path, Path}]], Opts} = proplists:split(KVs, [host, path]), + HandlerOpts = process_http_handler_opts(Type, Opts), + {Host, Path, binary_to_atom(Type, utf8), HandlerOpts}. + +process_http_handler_opts(<<"lasse_handler">>, [{module, Module}]) -> + [Module]; +process_http_handler_opts(<<"cowboy_static">>, Opts) -> + {[[{type, Type}], [{app, App}], [{content_path, Path}]], []} = + proplists:split(Opts, [type, app, content_path]), + {Type, App, Path, [{mimetypes, cow_mimetypes, all}]}; +process_http_handler_opts(<<"mongoose_api_admin">>, Opts) -> + {[UserOpts, PassOpts], []} = proplists:split(Opts, [username, password]), + case {UserOpts, PassOpts} of + {[], []} -> []; + {[{username, User}], [{password, Pass}]} -> [{auth, {User, Pass}}] + end; +process_http_handler_opts(<<"cowboy_swagger_redirect_handler">>, []) -> #{}; +process_http_handler_opts(<<"cowboy_swagger_json_handler">>, []) -> #{}; +process_http_handler_opts(_, Opts) -> Opts. diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index e0949feeda..3826cda6a5 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -16,101 +16,6 @@ validate(Path, [F]) when is_function(F, 1) -> validate(Path, F(?HOST)); -%% listen -validate([item, _Type, <<"listen">>], - [{{Port, _IPT, _Proto}, _Module, _Opts}]) -> - validate_port(Port); -validate([<<"backlog">>, item, _Type, <<"listen">>], - [{backlog, Value}]) -> - validate_non_negative_integer(Value); -validate([<<"proxy_protocol">>, item, _Type, <<"listen">>], - [{proxy_protocol, Value}]) -> - validate_boolean(Value); -validate([<<"num_acceptors">>, item, _Type, <<"listen">>], - [{acceptors_num, Value}]) -> - validate_positive_integer(Value); -validate([<<"access">>, item, _Type, <<"listen">>], - [{access, Value}]) -> - validate_non_empty_atom(Value); -validate([<<"shaper">>, item, _Type, <<"listen">>], - [{shaper, Value}]) -> - validate_non_empty_atom(Value); -validate([<<"shaper_rule">>, item, <<"service">>, <<"listen">>], - [{shaper_rule, Value}]) -> - validate_non_empty_atom(Value); -validate([<<"xml_socket">>, item, <<"c2s">>, <<"listen">>], - [{xml_socket, Value}]) -> - validate_boolean(Value); -validate([<<"zlib">>, item, <<"c2s">>, <<"listen">>], - [{zlib, Value}]) -> - validate_positive_integer(Value); -validate([<<"hibernate_after">>, item, _, <<"listen">>], - [{hibernate_after, Value}]) -> - validate_non_negative_integer(Value); -validate([<<"mode">>, {tls, _}, item, <<"c2s">>, <<"listen">>], - [Value]) -> - validate_enum(Value, [tls, starttls, starttls_required]); -validate([<<"verify_mode">>, {tls, just_tls}, item, <<"c2s">>, <<"listen">>], - Value) -> - validate_enum(Value, [peer, selfsigned_peer, none]); -validate([<<"disconnect_on_failure">>, {tls, just_tls}, item, <<"c2s">>, <<"listen">>], - Value) -> - validate_boolean(Value); -validate([item, <<"crl_files">>, {tls, just_tls}, item, <<"c2s">>, <<"listen">>], - [Value]) -> - validate_non_empty_string(Value); -validate([item, <<"protocol_options">>, _TLS, item, _Type, <<"listen">>], - [Value]) -> - validate_non_empty_string(Value); -validate([FileType, _TLS, item, _Type, <<"listen">>], - [{_, Value}]) when FileType =:= <<"certfile">>; - FileType =:= <<"cacertfile">>; - FileType =:= <<"dhfile">> -> - validate_non_empty_string(Value); -validate([<<"max_stanza_size">>, item, _Type, <<"listen">>], - [{max_stanza_size, Value}]) -> - validate_positive_integer(Value); -validate([<<"max_fsm_queue">>, item, _Type, <<"listen">>], - [{max_fsm_queue, Value}]) -> - validate_positive_integer(Value); -validate([<<"check_from">>, item, <<"service">>, <<"listen">>], - [{service_check_from, Value}]) -> - validate_boolean(Value); -validate([<<"hidden_components">>, item, <<"service">>, <<"listen">>], - [{hidden_components, Value}]) -> - validate_boolean(Value); -validate([<<"conflict_behaviour">>, item, <<"service">>, <<"listen">>], - [{conflict_behaviour, Value}]) -> - validate_enum(Value, [kick_old, disconnect]); -validate([<<"password">>, item, <<"service">>, <<"listen">>], - [{password, Value}]) -> - validate_non_empty_string(Value); -validate([<<"verify_mode">>, <<"tls">>, item, <<"http">>, <<"listen">>], - [{verify_mode, Value}]) -> - validate_enum(Value, [peer, selfsigned_peer, none]); -validate([<<"num_acceptors">>, <<"transport">>, item, <<"http">>, <<"listen">>], - [{num_acceptors, Value}]) -> - validate_positive_integer(Value); -validate([<<"max_connections">>, <<"transport">>, item, <<"http">>, <<"listen">>], - [{max_connections, Value}]) -> - validate_non_negative_integer_or_infinity(Value); -validate([<<"compress">>, <<"protocol">>, item, <<"http">>, <<"listen">>], - [{compress, Value}]) -> - validate_boolean(Value); -validate([item, <<"lasse_handler">>, <<"handlers">>, item, <<"http">>, <<"listen">>], - [{Host, _Path, lasse_handler, Opts}]) -> - validate_non_empty_string(Host), - [Module] = Opts, - validate_module(Module); -validate([item, <<"handlers">>, - item, <<"mongoose_api">>, <<"handlers">>, item, <<"http">>, <<"listen">>], - [Value]) -> - validate_module(Value); -validate([item, _TypeBin, <<"handlers">>, item, <<"http">>, <<"listen">>], - [{Host, _Path, Type, _Opts}]) -> - validate_non_empty_string(Host), - validate_module(Type); - %% auth validate([item, <<"methods">>, <<"auth">>|Path], [Value]) -> @@ -338,7 +243,7 @@ validate([<<"ip_versions">>, <<"outgoing">>, <<"s2s">>], validate_non_empty_list(Value); validate([<<"connection_timeout">>, <<"outgoing">>, <<"s2s">>], [#local_config{value = Value}]) -> - validate_timeout(Value); + validate_positive_integer_or_infinity(Value); validate([<<"use_starttls">>, <<"s2s">>], [#local_config{value = Value}]) -> validate_enum(Value, [false, optional, required, required_trusted]); @@ -1530,14 +1435,19 @@ validate(_Path, _Value) -> validate(V, boolean, any) -> validate_boolean(V); validate(V, binary, domain) -> validate_binary_domain(V); validate(V, binary, non_empty) -> validate_non_empty_binary(V); +validate(V, integer, non_negative) -> validate_non_negative_integer(V); validate(V, integer, positive) -> validate_positive_integer(V); -validate(V, int_or_infinity, timeout) -> validate_timeout(V); +validate(V, integer, port) -> validate_port(V); +validate(V, int_or_infinity, non_negative) -> validate_non_negative_integer_or_infinity(V); +validate(V, int_or_infinity, positive) -> validate_positive_integer_or_infinity(V); validate(V, string, url) -> validate_url(V); +validate(V, string, ip_address) -> validate_ip_address(V); validate(V, string, non_empty) -> validate_non_empty_string(V); validate(V, atom, module) -> validate_module(V); validate(V, atom, {module, Prefix}) -> validate_module(list_to_atom(atom_to_list(Prefix) ++ atom_to_list(V))); validate(V, atom, loglevel) -> validate_loglevel(V); +validate(V, atom, non_empty) -> validate_non_empty_atom(V); validate(V, _, {enum, Values}) -> validate_enum(V, Values); validate(_V, _, any) -> ok. @@ -1565,9 +1475,6 @@ validate_unique_items(Items) -> L = sets:size(sets:from_list(Items)), L = length(Items). -validate_timeout(infinity) -> ok; -validate_timeout(Timeout) when is_integer(Timeout), Timeout > 0 -> ok. - validate_boolean(Value) when is_boolean(Value) -> ok. validate_module(Mod) -> From f7b437bca63d10d0789e7d0ab4c680aab38aea3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Fri, 13 Nov 2020 16:10:25 +0100 Subject: [PATCH 019/104] Update tests with the config changes for the 'listen' section --- test/config_parser_SUITE.erl | 38 ++++++------------- .../mongooseim-pgsql.options | 2 +- .../mongooseim-pgsql.toml | 2 +- 3 files changed, 14 insertions(+), 28 deletions(-) diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index 93617cda80..280f0c2f13 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -371,9 +371,7 @@ listen_proto(_Config) -> ?eq([#local_config{key = listen, value = [{{5222, {0, 0, 0, 0}, udp}, ejabberd_c2s, [{proto, udp}]}]}], parse_listener(<<"c2s">>, #{<<"proto">> => <<"udp">>})), - %% 'ejabberd_listener:normalize_proto/1' shows a warning message and falls back to 'tcp' - ?eq(listener_config(ejabberd_c2s, [{proto, pigeon}]), - parse_listener(<<"c2s">>, #{<<"proto">> => <<"pigeon">>})). + ?err(parse_listener(<<"c2s">>, #{<<"proto">> => <<"pigeon">>})). listen_ip_version(_Config) -> ?eq(listener_config(ejabberd_c2s, [inet]), @@ -553,35 +551,23 @@ listen_tls_dhfile(_Config) -> parse_listener(<<"http">>, #{<<"tls">> => #{<<"dhfile">> => <<"dh.pem">>}})), ?err(parse_listener(<<"c2s">>, #{<<"tls">> => #{<<"dhfile">> => <<>>}})). - listen_tls_ciphers(_Config) -> - %% fast_tls ciphers may contain versions as well - ?eq(listener_config(ejabberd_c2s, [{ciphers, "TLSv1.2:TLSv1.3"}]), - parse_listener(<<"c2s">>, - #{<<"tls">> => #{<<"ciphers">> => <<"TLSv1.2:TLSv1.3">>}})), - ?eq(listener_config(ejabberd_s2s_in, [{ciphers, "TLSv1.2:TLSv1.3"}]), - parse_listener(<<"s2s">>, - #{<<"tls">> => #{<<"ciphers">> => <<"TLSv1.2:TLSv1.3">>}})), - ?eq(listener_config(ejabberd_c2s, [{tls_module, just_tls}, - {ssl_options, [{ciphers, ["TLS_AES_256_GCM_SHA384"]}]}]), + ?eq(listener_config(ejabberd_c2s, [{ciphers, "TLS_AES_256_GCM_SHA384"}]), parse_listener(<<"c2s">>, - #{<<"tls">> => #{<<"module">> => <<"just_tls">>, - <<"ciphers">> => [<<"TLS_AES_256_GCM_SHA384">>]}})), + #{<<"tls">> => #{<<"ciphers">> => <<"TLS_AES_256_GCM_SHA384">>}})), ?eq(listener_config(ejabberd_c2s, [{tls_module, just_tls}, - {ssl_options, [{ciphers, [#{cipher => aes_256_gcm, - key_exchange => any, - mac => aead, - prf => sha384}]}]}]), + {ssl_options, [{ciphers, "TLS_AES_256_GCM_SHA384"}]}]), parse_listener(<<"c2s">>, #{<<"tls">> => #{<<"module">> => <<"just_tls">>, - <<"ciphers">> => [#{<<"cipher">> => <<"aes_256_gcm">>, - <<"key_exchange">> => <<"any">>, - <<"mac">> => <<"aead">>, - <<"prf">> => <<"sha384">>}]}})), + <<"ciphers">> => <<"TLS_AES_256_GCM_SHA384">>}})), + ?eq(listener_config(ejabberd_s2s_in, [{ciphers, "TLS_AES_256_GCM_SHA384"}]), + parse_listener(<<"s2s">>, + #{<<"tls">> => #{<<"ciphers">> => <<"TLS_AES_256_GCM_SHA384">>}})), + ?eq(listener_config(ejabberd_cowboy, [{ssl, [{ciphers, "TLS_AES_256_GCM_SHA384"}]}]), + parse_listener(<<"http">>, + #{<<"tls">> => #{<<"ciphers">> => <<"TLS_AES_256_GCM_SHA384">>}})), ?err(parse_listener(<<"c2s">>, - #{<<"tls">> => - #{<<"module">> => <<"just_tls">>, - <<"ciphers">> => [#{<<"cipher">> => <<"aes_256_gcm">>}]}})). + #{<<"tls">> => #{<<"ciphers">> => [<<"TLS_AES_256_GCM_SHA384">>]}})). listen_tls_versions(_Config) -> ?eq(listener_config(ejabberd_c2s, [{tls_module, just_tls}, diff --git a/test/config_parser_SUITE_data/mongooseim-pgsql.options b/test/config_parser_SUITE_data/mongooseim-pgsql.options index dfe2476eb2..d30c51e9f0 100644 --- a/test/config_parser_SUITE_data/mongooseim-pgsql.options +++ b/test/config_parser_SUITE_data/mongooseim-pgsql.options @@ -53,7 +53,7 @@ [{modules, [{"_","/http-bind",mod_bosh,[]}, {"_","/ws-xmpp",mod_websockets, - [{max_stanza_size,100},{ping_rate,none},{timeout,infinity}]}, + [{max_stanza_size,100},{ping_rate,120000},{timeout,infinity}]}, {"localhost","/api",mongoose_api_admin, [{auth,{<<"ala">>,<<"makotaipsa">>}}]}, {"localhost","/api/contacts/{:jid}",mongoose_api_client,[]}]}, diff --git a/test/config_parser_SUITE_data/mongooseim-pgsql.toml b/test/config_parser_SUITE_data/mongooseim-pgsql.toml index 80ca1058d5..aaca319546 100644 --- a/test/config_parser_SUITE_data/mongooseim-pgsql.toml +++ b/test/config_parser_SUITE_data/mongooseim-pgsql.toml @@ -55,7 +55,7 @@ host = "_" path = "/ws-xmpp" timeout = "infinity" - ping_rate = "none" + ping_rate = 120_000 max_stanza_size = 100 [[listen.http]] From 35bde30116de5a567883be07656d238ed118ed09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Mon, 16 Nov 2020 08:01:50 +0100 Subject: [PATCH 020/104] Update docs with the config changes for the 'listen' section --- doc/advanced-configuration/listen.md | 31 +++++++++++----------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/doc/advanced-configuration/listen.md b/doc/advanced-configuration/listen.md index 9b6ef42bb4..57e725ef22 100644 --- a/doc/advanced-configuration/listen.md +++ b/doc/advanced-configuration/listen.md @@ -182,6 +182,13 @@ Path to the X509 PEM file with a CA chain that will be used to verify clients. I Path to the Diffie-Hellman parameter file. +#### `listen.c2s.tls.ciphers` +* **Syntax:** string with the OpenSSL cipher suite specification +* **Default:** for `fast_tls` the default is`"TLSv1.2:TLSv1.3"`. For `just_tls` this option is not set by default - all supported suites are accepted. +* **Example:** `tls.ciphers = "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384"` + +Cipher suites to use with StartTLS or TLS. Please refer to the [OpenSSL documentation](http://www.openssl.org/docs/man1.0.2/apps/ciphers.html) for the cipher string format. For `fast_tls`, this string can be used to specify versions as well. For `just_tls`, see the [Erlang/OTP SSL documentation](https://erlang.org/doc/man/ssl.html#type-ciphers) for allowed values. + #### `listen.c2s.tls.protocol_options` - only for `fast_tls` * **Syntax:** array of strings * **Default:** `["no_sslv2", "no_sslv3", "no_tlsv1", "no_tlsv1_1"]` @@ -189,20 +196,6 @@ Path to the Diffie-Hellman parameter file. A list of OpenSSL options for FastTLS. You can find the mappings between supported options and actual OpenSSL flags in the `fast_tls` [source code](https://github.com/processone/fast_tls/blob/master/c_src/options.h). -#### `listen.c2s.tls.ciphers` - for `fast_tls` -* **Syntax:** string with the OpenSSL cipher suite specification -* **Default:** `"TLSv1.2:TLSv1.3"` -* **Example:** `tls.ciphers = "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384"` - -Cipher suites to use with StartTLS or TLS. Please refer to the [OpenSSL documentation](http://www.openssl.org/docs/man1.0.2/apps/ciphers.html) for the cipher string format. - -#### `listen.c2s.tls.ciphers` - for `just_tls` -* **Syntax:** array of tables with the following keys: `cipher`, `key_exchange`, `mac`, `prf` and string values. -* **Default:** not set, all supported cipher suites are accepted -* **Example:** `tls.ciphers = "[{cipher = "aes_25_gcm", key_exchange = "any", mac = "aead", "prf = sha384"}]"` - -Cipher suites to use with StartTLS or TLS. For allowed values, see the [Erlang/OTP SSL documentation](https://erlang.org/doc/man/ssl.html#type-ciphers) - #### `listen.c2s.tls.verify_mode` - only for `just_tls` * **Syntax:** string, one of `"peer"`, `"selfsigned_peer"`, `"none"` * **Default:** not set (equivalent to `"peer"` in the current version of Erlang/OTP) @@ -464,8 +457,8 @@ You can pass the following optional parameters: The time (in milliseconds) after which an inactive user is disconnected. #### `listen.http.handlers.mod_websockets.ping_rate` -* **Syntax:** positive integer or the string `"none"` -* **Default:** `"none"` +* **Syntax:** positive integer +* **Default:** not set - pings disabled * **Example:** `ping_rate = 10_000` The time between pings sent by server. By setting this option you enable server-side pinging. @@ -600,11 +593,11 @@ Path to the X509 PEM file with a CA chain that will be used to verify clients. I Path to the Diffie-Hellman parameter file. #### `listen.http.tls.ciphers` -* **Syntax:** array of tables with the following keys: `cipher`, `key_exchange`, `mac`, `prf` and string values. +* **Syntax:** string with the OpenSSL cipher suite specification * **Default:** not set, all supported cipher suites are accepted -* **Example:** `tls.ciphers = "[{cipher = "aes_25_gcm", key_exchange = "any", mac = "aead", "prf = sha384"}]"` +* **Example:** `tls.ciphers = "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384"` -Cipher suites to use. For allowed values, see the [Erlang/OTP SSL documentation](https://erlang.org/doc/man/ssl.html#type-ciphers) +Cipher suites to use. Please refer to the [OpenSSL documentation](http://www.openssl.org/docs/man1.0.2/apps/ciphers.html) for the cipher string format. For allowed values, see the [Erlang/OTP OpenSSL documentation](http://www.openssl.org/docs/man1.0.2/apps/ciphers.html). #### `listen.http.tls.versions` * **Syntax:** array of strings From b821a7f0318c82eb9044b6c5c2d02441329c9b34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Tue, 17 Nov 2020 13:55:54 +0100 Subject: [PATCH 021/104] Remove unused functions --- src/config/mongoose_config_utils.erl | 43 ---------------------------- 1 file changed, 43 deletions(-) diff --git a/src/config/mongoose_config_utils.erl b/src/config/mongoose_config_utils.erl index 22fcd91557..cd9638b48d 100644 --- a/src/config/mongoose_config_utils.erl +++ b/src/config/mongoose_config_utils.erl @@ -4,10 +4,6 @@ -module(mongoose_config_utils). -export([exit_or_halt/1]). -export([is_file_readable/1]). --export([get_absolute_path/1]). --export([get_config_lines/4]). - --type config_line() :: [[any()] | non_neg_integer(), ...]. % spec me better -include_lib("kernel/include/file.hrl"). @@ -34,42 +30,3 @@ is_file_readable(Path) -> {error, _Reason} -> false end. - -%% @doc Convert configuration filename to absolute path. -%% Input is an absolute or relative path to an ejabberd configuration file. -%% And returns an absolute path to the configuration file. --spec get_absolute_path(string()) -> string(). -get_absolute_path(File) -> - case filename:pathtype(File) of - absolute -> - File; - relative -> - {ok, Cwd} = file:get_cwd(), - filename:absname_join(Cwd, File) - end. - --spec get_config_lines(Filename :: string(), - TargetNumber :: integer(), - PreContext :: 10, - PostContext :: 3) -> [config_line()]. -get_config_lines(Filename, TargetNumber, PreContext, PostContext) -> - {ok, Fd} = file:open(Filename, [read]), - LNumbers = lists:seq(TargetNumber - PreContext, TargetNumber + PostContext), - NextL = io:get_line(Fd, no_prompt), - R = get_config_lines2(Fd, NextL, 1, LNumbers, []), - file:close(Fd), - R. - -get_config_lines2(_Fd, eof, _CurrLine, _LNumbers, R) -> - lists:reverse(R); -get_config_lines2(_Fd, _NewLine, _CurrLine, [], R) -> - lists:reverse(R); -get_config_lines2(Fd, Data, CurrLine, [NextWanted | LNumbers], R) when is_list(Data) -> - NextL = io:get_line(Fd, no_prompt), - case CurrLine >= NextWanted of - true -> - Line2 = [integer_to_list(CurrLine), ": " | Data], - get_config_lines2(Fd, NextL, CurrLine + 1, LNumbers, [Line2 | R]); - false -> - get_config_lines2(Fd, NextL, CurrLine + 1, [NextWanted | LNumbers], R) - end. From 437e08796504c43bbd64bc2f3987361c18e503bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Tue, 17 Nov 2020 13:59:02 +0100 Subject: [PATCH 022/104] Specify the 'auth' config section declaratively Other changes: - Use the declarative handlers for entire sections, not only for the options inside (top-level specs were unused before) - Fix minor formatting issues, e.g. the 'general' section needs 'format = none' not to be wrapped in {general, ...} - Add new option to 'format': 'foreach' Motivation: some auth opts are aggregated, so it is not possible to handle them with a one-by-one formatter --- src/config/mongoose_config_parser_toml.erl | 191 +-------------- src/config/mongoose_config_spec.erl | 228 +++++++++++++++++- src/config/mongoose_config_validator_toml.erl | 87 ------- 3 files changed, 236 insertions(+), 270 deletions(-) diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index 5ef1e42075..518ab19924 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -94,16 +94,6 @@ parse_root(Path, Content) -> %% path: * -spec process_section(path(), toml_section() | [toml_section()]) -> config_list(). -process_section([<<"general">>] = Path, Content) -> - ensure_keys([<<"hosts">>], Content), - parse_section(Path, Content); -process_section([<<"listen">>] = Path, Content) -> - Listeners = parse_section(Path, Content), - [#local_config{key = listen, value = Listeners}]; -process_section([<<"auth">>|_] = Path, Content) -> - parse_section(Path, Content, fun(AuthOpts) -> - ?HOST_F(partition_auth_opts(AuthOpts, Host)) - end); process_section([<<"outgoing_pools">>] = Path, Content) -> Pools = parse_section(Path, Content), [#local_config{key = outgoing_pools, value = Pools}]; @@ -118,147 +108,6 @@ process_section([<<"host_config">>] = Path, Content) -> process_section(Path, Content) -> parse_section(Path, Content). -%% path: (host_config[].)auth.* --spec auth_option(path(), toml_value()) -> [option()]. -auth_option([<<"methods">>|_] = Path, Methods) -> - [{auth_method, parse_list(Path, Methods)}]; -auth_option([<<"password">>|_] = Path, #{<<"hash">> := Hashes}) -> - [{password_format, {scram, parse_list([<<"hash">> | Path], Hashes)}}]; -auth_option([<<"password">>|_], #{<<"format">> := V}) -> - [{password_format, b2a(V)}]; -auth_option([<<"scram_iterations">>|_], V) -> - [{scram_iterations, V}]; -auth_option([<<"sasl_external">>|_] = Path, V) -> - [{cyrsasl_external, parse_list(Path, V)}]; -auth_option([<<"sasl_mechanisms">>|_] = Path, V) -> - [{sasl_mechanisms, parse_list(Path, V)}]; -auth_option([<<"jwt">>|_] = Path, V) -> - ensure_keys([<<"secret">>, <<"algorithm">>, <<"username_key">>], V), - parse_section(Path, V); -auth_option(Path, V) -> - parse_section(Path, V). - -%% path: (host_config[].)auth.anonymous.* -auth_anonymous_option([<<"allow_multiple_connections">>|_], V) -> - [{allow_multiple_connections, V}]; -auth_anonymous_option([<<"protocol">>|_], V) -> - [{anonymous_protocol, b2a(V)}]. - -%% path: (host_config[].)auth.ldap.* --spec auth_ldap_option(path(), toml_section()) -> [option()]. -auth_ldap_option([<<"pool_tag">>|_], V) -> - [{ldap_pool_tag, b2a(V)}]; -auth_ldap_option([<<"bind_pool_tag">>|_], V) -> - [{ldap_bind_pool_tag, b2a(V)}]; -auth_ldap_option([<<"base">>|_], V) -> - [{ldap_base, b2l(V)}]; -auth_ldap_option([<<"uids">>|_] = Path, V) -> - [{ldap_uids, parse_list(Path, V)}]; -auth_ldap_option([<<"filter">>|_], V) -> - [{ldap_filter, b2l(V)}]; -auth_ldap_option([<<"dn_filter">>|_] = Path, V) -> - parse_section(Path, V, fun process_dn_filter/1); -auth_ldap_option([<<"local_filter">>|_] = Path, V) -> - parse_section(Path, V, fun process_local_filter/1); -auth_ldap_option([<<"deref">>|_], V) -> - [{ldap_deref, b2a(V)}]. - -process_dn_filter(Opts) -> - {_, Filter} = proplists:lookup(filter, Opts), - {_, Attrs} = proplists:lookup(attributes, Opts), - [{ldap_dn_filter, {Filter, Attrs}}]. - -process_local_filter(Opts) -> - {_, Op} = proplists:lookup(operation, Opts), - {_, Attribute} = proplists:lookup(attribute, Opts), - {_, Values} = proplists:lookup(values, Opts), - [{ldap_local_filter, {Op, {Attribute, Values}}}]. - --spec auth_ldap_uids(path(), toml_section()) -> [option()]. -auth_ldap_uids(_, #{<<"attr">> := Attr, <<"format">> := Format}) -> - [{b2l(Attr), b2l(Format)}]; -auth_ldap_uids(_, #{<<"attr">> := Attr}) -> - [b2l(Attr)]. - --spec auth_ldap_dn_filter(path(), toml_value()) -> [option()]. -auth_ldap_dn_filter([<<"filter">>|_], V) -> - [{filter, b2l(V)}]; -auth_ldap_dn_filter([<<"attributes">>|_] = Path, V) -> - Attrs = parse_list(Path, V), - [{attributes, Attrs}]. - --spec auth_ldap_local_filter(path(), toml_value()) -> [option()]. -auth_ldap_local_filter([<<"operation">>|_], V) -> - [{operation, b2a(V)}]; -auth_ldap_local_filter([<<"attribute">>|_], V) -> - [{attribute, b2l(V)}]; -auth_ldap_local_filter([<<"values">>|_] = Path, V) -> - Attrs = parse_list(Path, V), - [{values, Attrs}]. - -%% path: (host_config[].)auth.external.* --spec auth_external_option(path(), toml_value()) -> [option()]. -auth_external_option([<<"instances">>|_], V) -> - [{extauth_instances, V}]; -auth_external_option([<<"program">>|_], V) -> - [{extauth_program, b2l(V)}]. - -%% path: (host_config[].)auth.http.* --spec auth_http_option(path(), toml_value()) -> [option()]. -auth_http_option([<<"basic_auth">>|_], V) -> - [{basic_auth, b2l(V)}]. - -%% path: (host_config[].)auth.jwt.* --spec auth_jwt_option(path(), toml_value()) -> [option()]. -auth_jwt_option([<<"secret">>|_] = Path, V) -> - [Item] = parse_section(Path, V), % expect exactly one option - [Item]; -auth_jwt_option([<<"algorithm">>|_], V) -> - [{jwt_algorithm, b2l(V)}]; -auth_jwt_option([<<"username_key">>|_], V) -> - [{jwt_username_key, b2a(V)}]. - -%% path: (host_config[].)auth.jwt.secret.* --spec auth_jwt_secret(path(), toml_value()) -> [option()]. -auth_jwt_secret([<<"file">>|_], V) -> - [{jwt_secret_source, b2l(V)}]; -auth_jwt_secret([<<"env">>|_], V) -> - [{jwt_secret_source, {env, b2l(V)}}]; -auth_jwt_secret([<<"value">>|_], V) -> - [{jwt_secret, b2l(V)}]. - -%% path: (host_config[].)auth.riak.* --spec auth_riak_option(path(), toml_value()) -> [option()]. -auth_riak_option([<<"bucket_type">>|_], V) -> - [{bucket_type, V}]. - -%% path: (host_config[].)auth.sasl_external[] --spec sasl_external(path(), toml_value()) -> [option()]. -sasl_external(_, <<"standard">>) -> [standard]; -sasl_external(_, <<"common_name">>) -> [common_name]; -sasl_external(_, <<"auth_id">>) -> [auth_id]; -sasl_external(_, M) -> [{mod, b2a(M)}]. - -%% path: (host_config[].)auth.sasl_mechanism[] -%% auth.sasl_mechanisms.* --spec sasl_mechanism(path(), toml_value()) -> [option()]. -sasl_mechanism(_, V) -> - [b2a(<<"cyrsasl_", V/binary>>)]. - --spec partition_auth_opts([{atom(), any()}], ejabberd:server()) -> [config()]. -partition_auth_opts(AuthOpts, Host) -> - {InnerOpts, OuterOpts} = lists:partition(fun({K, _}) -> is_inner_auth_opt(K) end, AuthOpts), - [#local_config{key = {auth_opts, Host}, value = InnerOpts} | - [#local_config{key = {K, Host}, value = V} || {K, V} <- OuterOpts]]. - --spec is_inner_auth_opt(atom()) -> boolean(). -is_inner_auth_opt(auth_method) -> false; -is_inner_auth_opt(allow_multiple_connections) -> false; -is_inner_auth_opt(anonymous_protocol) -> false; -is_inner_auth_opt(sasl_mechanisms) -> false; -is_inner_auth_opt(extauth_instances) -> false; -is_inner_auth_opt(_) -> true. - %% path: outgoing_pools.*.* -spec process_pool(path(), toml_section()) -> [option()]. process_pool([Tag, Type|_] = Path, M) -> @@ -1590,6 +1439,8 @@ format_spec(#section{format = Format}) -> Format; format_spec(#list{format = Format}) -> Format; format_spec(#option{format = Format}) -> Format. +format(Path, L, {foreach, Format}) when is_atom(Format) -> + lists:flatmap(fun({K, V}) -> format(Path, V, {Format, K}) end, L); format([Key|_] = Path, V, host_local_config) -> format(Path, V, {host_local_config, b2a(Key)}); format([Key|_] = Path, V, local_config) -> @@ -1669,26 +1520,9 @@ node_to_string(Node) -> [binary_to_list(Node)]. -spec handler(path()) -> fun((path(), toml_value()) -> option()) | mongoose_config_spec:config_node(). handler([]) -> fun parse_root/2; -handler([_]) -> fun process_section/2; - -%% auth -handler([_, <<"auth">>]) -> fun auth_option/2; -handler([_, <<"anonymous">>, <<"auth">>]) -> fun auth_anonymous_option/2; -handler([_, <<"ldap">>, <<"auth">>]) -> fun auth_ldap_option/2; -handler([_, <<"external">>, <<"auth">>]) -> fun auth_external_option/2; -handler([_, <<"http">>, <<"auth">>]) -> fun auth_http_option/2; -handler([_, <<"jwt">>, <<"auth">>]) -> fun auth_jwt_option/2; -handler([_, <<"secret">>, <<"jwt">>, <<"auth">>]) -> fun auth_jwt_secret/2; -handler([_, <<"riak">>, <<"auth">>]) -> fun auth_riak_option/2; -handler([_, <<"uids">>, <<"ldap">>, <<"auth">>]) -> fun auth_ldap_uids/2; -handler([_, <<"dn_filter">>, <<"ldap">>, <<"auth">>]) -> fun auth_ldap_dn_filter/2; -handler([_, <<"local_filter">>, <<"ldap">>, <<"auth">>]) -> fun auth_ldap_local_filter/2; -handler([_, <<"attributes">>, _, <<"ldap">>, <<"auth">>]) -> fun(_, V) -> [b2l(V)] end; -handler([_, <<"values">>, _, <<"ldap">>, <<"auth">>]) -> fun(_, V) -> [b2l(V)] end; -handler([_, <<"methods">>, <<"auth">>]) -> fun(_, Val) -> [b2a(Val)] end; -handler([_, <<"hash">>, <<"password">>, <<"auth">>]) -> fun(_, Val) -> [b2a(Val)] end; -handler([_, <<"sasl_external">>, <<"auth">>]) -> fun sasl_external/2; -handler([_, <<"sasl_mechanisms">>, <<"auth">>]) -> fun sasl_mechanism/2; +handler([Section]) when Section =/= <<"general">>, + Section =/= <<"listen">>, + Section =/= <<"auth">> -> fun process_section/2; %% outgoing_pools handler([_, <<"outgoing_pools">>]) -> fun parse_section/2; @@ -1870,15 +1704,12 @@ handler([_, _, <<"host_config">>]) -> fun process_section/2; handler([_, <<"general">>, _, <<"host_config">>] = P) -> handler_for_host(P); handler([_, <<"s2s">>, _, <<"host_config">>] = P) -> handler_for_host(P); handler(Path) -> - subtree_handler(initial, lists:reverse(Path)). - -subtree_handler(initial, [<<"host_config">>, {host, _} | Subtree]) -> - subtree_handler(subtree, Subtree); -subtree_handler(_, [Section|_] = Path) when Section =:= <<"general">>; - Section =:= <<"listen">> -> - mongoose_config_spec:handler(Path); -subtree_handler(subtree, Subtree) -> - handler(lists:reverse(Subtree)). + reverse_handler(lists:reverse(Path)). + +reverse_handler([<<"host_config">>, {host, _} | Subtree]) -> + handler(lists:reverse(Subtree)); +reverse_handler(Path) -> + mongoose_config_spec:handler(Path). %% 1. Strip host_config, choose the handler for the remaining path %% 2. Wrap the handler in a fun that calls the resulting function F for the current host diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index e11f527119..806f407aac 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -6,6 +6,8 @@ -type config_node() :: #section{} | #option{} | #list{}. +-export_type([config_node/0]). + handler(Path) -> handler(Path, root()). @@ -28,9 +30,11 @@ handler([item|Rest], #list{items = Items}) -> root() -> #section{ items = #{<<"general">> => general(), - <<"listen">> => listen() + <<"listen">> => listen(), + <<"auth">> => auth() }, - required = [<<"general">>] + required = [<<"general">>], + format = none }. %% path: general @@ -86,7 +90,8 @@ general() -> <<"hide_service_name">> => #option{type = boolean, format = host_local_config} }, - required = [<<"hosts">>] + required = [<<"hosts">>], + format = none }. ctl_access_rule() -> @@ -335,6 +340,175 @@ http_handler_required(<<"cowboy_static">>) -> all; http_handler_required(<<"mongoose_api">>) -> all; http_handler_required(_) -> []. +%% path: (host_config[].)auth +auth() -> + #section{ + items = #{<<"methods">> => #list{items = #option{type = atom, + validate = {module, ejabberd_auth_}}, + format = {kv, auth_method}}, + <<"password">> => auth_password(), + <<"scram_iterations">> => #option{type = integer, + validate = positive}, + <<"sasl_external">> => + #list{items = #option{type = atom, + process = fun ?MODULE:process_sasl_external/1}, + format = {kv, cyrsasl_external}}, + <<"sasl_mechanisms">> => + #list{items = #option{type = atom, + validate = {module, cyrsasl_}, + process = fun ?MODULE:process_sasl_mechanism/1}}, + <<"anonymous">> => auth_anonymous(), + <<"external">> => auth_external(), + <<"http">> => auth_http(), + <<"jwt">> => auth_jwt(), + <<"ldap">> => auth_ldap(), + <<"riak">> => auth_riak()}, + process = fun ?MODULE:process_auth/1, + format = {foreach, host_local_config} + }. + +%% path: (host_config[].)auth.password +auth_password() -> + #section{ + items = #{<<"format">> => #option{type = atom, + validate = {enum, [scram, plain]}}, + <<"hash">> => #list{items = #option{type = atom, + validate = {enum, [sha, sha224, sha256, + sha384, sha512]}}, + validate = unique_non_empty + } + }, + process = fun ?MODULE:process_auth_password/1, + format = {kv, password_format} + }. + +%% path: (host_config[].)auth.anonymous +auth_anonymous() -> + #section{ + items = #{<<"allow_multiple_connections">> => #option{type = boolean}, + <<"protocol">> => #option{type = atom, + validate = {enum, [sasl_anon, login_anon, both]}, + format = {kv, anonymous_protocol}} + }, + format = none + }. + +%% path: (host_config[].)auth.external +auth_external() -> + #section{ + items = #{<<"instances">> => #option{type = integer, + validate = positive, + format = {kv, extauth_instances}}, + <<"program">> => #option{type = string, + validate = non_empty, + format = {kv, extauth_program}} + }, + format = none + }. + +%% path: (host_config[].)auth.http +auth_http() -> + #section{ + items = #{<<"basic_auth">> => #option{type = string}}, + format = none + }. + +%% path: (host_config[].)auth.jwt +auth_jwt() -> + #section{ + items = #{<<"secret">> => auth_jwt_secret(), + <<"algorithm">> => #option{type = string, + validate = {enum, ["HS256", "RS256", "ES256", + "HS386", "RS386", "ES386", + "HS512", "RS512", "ES512"]}, + format = {kv, jwt_algorithm}}, + <<"username_key">> => #option{type = atom, + validate = non_empty, + format = {kv, jwt_username_key}} + }, + required = all, + format = none + }. + +%% path: (host_config[].)auth.jwt.secret +auth_jwt_secret() -> + #section{ + items = #{<<"file">> => #option{type = string, + validate = non_empty}, + <<"env">> => #option{type = string, + validate = non_empty}, + <<"value">> => #option{type = string}}, + process = fun ?MODULE:process_jwt_secret/1, + format = item + }. + +%% path: (host_config[].)auth.ldap +auth_ldap() -> + #section{ + items = #{<<"pool_tag">> => #option{type = atom, + validate = non_empty, + format = {kv, ldap_pool_tag}}, + <<"bind_pool_tag">> => #option{type = atom, + validate = non_empty, + format = {kv, ldap_bind_pool_tag}}, + <<"base">> => #option{type = string, + format = {kv, ldap_base}}, + <<"uids">> => #list{items = ldap_uids(), + format = {kv, ldap_uids}}, + <<"filter">> => #option{type = string, + format = {kv, ldap_filter}}, + <<"dn_filter">> => ldap_dn_filter(), + <<"local_filter">> => ldap_local_filter(), + <<"deref">> => #option{type = atom, + validate = {enum, [never, always, finding, searching]}, + format = {kv, ldap_deref}} + }, + format = none + }. + +%% path: (host_config[].)auth.ldap.uids +ldap_uids() -> + #section{ + items = #{<<"attr">> => #option{type = string}, + <<"format">> => #option{type = string}}, + process = fun ?MODULE:process_ldap_uids/1, + required = [<<"attr">>] + }. + +%% path: (host_config[].)auth.ldap.dn_filter +ldap_dn_filter() -> + #section{ + items = #{<<"filter">> => #option{type = string}, + <<"attributes">> => #list{items = #option{type = string}} + }, + required = all, + process = fun ?MODULE:process_ldap_dn_filter/1, + format = {kv, ldap_dn_filter} + }. + +%% path: (host_config[].)auth.ldap.local_filter +ldap_local_filter() -> + #section{ + items = #{<<"operation">> => #option{type = atom, + validate = {enum, [equal, notequal]}}, + <<"attribute">> => #option{type = string, + validate = non_empty}, + <<"values">> => #list{items = #option{type = string}, + validate = non_empty} + }, + required = all, + process = fun ?MODULE:process_ldap_local_filter/1, + format = {kv, ldap_local_filter} + }. + +%% path: (host_config[].)auth.riak +auth_riak() -> + #section{ + items = #{<<"bucket_type">> => #option{type = binary, + validate = non_empty}}, + format = none + }. + %% Callbacks for 'process' process_ctl_access_rule(KVs) -> @@ -442,3 +616,51 @@ process_http_handler_opts(<<"mongoose_api_admin">>, Opts) -> process_http_handler_opts(<<"cowboy_swagger_redirect_handler">>, []) -> #{}; process_http_handler_opts(<<"cowboy_swagger_json_handler">>, []) -> #{}; process_http_handler_opts(_, Opts) -> Opts. + +process_auth(Opts) -> + %% some options need to be wrapped in 'auth_opts' - this needs simplifying in the future + OuterKeys = [auth_method, allow_multiple_connections, anonymous_protocol, sasl_mechanisms, + extauth_instances], + {OuterOpts, InnerOpts} = lists:partition(fun({K, _}) -> lists:member(K, OuterKeys) end, Opts), + [{auth_opts, InnerOpts} | OuterOpts]. + +process_sasl_external(V) when V =:= standard; + V =:= common_name; + V =:= auth_id -> + V; +process_sasl_external(M) -> + mongoose_config_validator_toml:validate(M, atom, module), + {mod, M}. + +process_sasl_mechanism(V) -> + list_to_atom("cyrsasl_" ++ atom_to_list(V)). + +process_jwt_secret([{file, V}]) -> {jwt_secret_source, V}; +process_jwt_secret([{env, V}]) -> {jwt_secret_source, {env, V}}; +process_jwt_secret([{value, V}]) -> {jwt_secret, V}. + +process_auth_password(KVs) -> + {[FormatOpts, HashOpts], []} = proplists:split(KVs, [format, hash]), + case {FormatOpts, HashOpts} of + {[{format, Format}], []} -> Format; + {[{format, scram}], [{hash, Hashes}]} -> {scram, Hashes}; + {[], [{hash, Hashes}]} -> {scram, Hashes} + end. + +process_ldap_dn_filter(KVs) -> + {_, Filter} = proplists:lookup(filter, KVs), + {_, Attrs} = proplists:lookup(attributes, KVs), + {Filter, Attrs}. + +process_ldap_local_filter(KVs) -> + {_, Op} = proplists:lookup(operation, KVs), + {_, Attribute} = proplists:lookup(attribute, KVs), + {_, Values} = proplists:lookup(values, KVs), + {Op, {Attribute, Values}}. + +process_ldap_uids(KVs) -> + {[AttrOpts, FormatOpts], []} = proplists:split(KVs, [attr, format]), + case {AttrOpts, FormatOpts} of + {[{attr, Attr}], []} -> Attr; + {[{attr, Attr}], [{format, Format}]} -> {Attr, Format} + end. diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index 3826cda6a5..2b0049124e 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -16,90 +16,6 @@ validate(Path, [F]) when is_function(F, 1) -> validate(Path, F(?HOST)); -%% auth -validate([item, <<"methods">>, <<"auth">>|Path], - [Value]) -> - validate_root_or_host_config(Path), - validate_module(list_to_atom("ejabberd_auth_" ++ atom_to_list(Value))); -validate([<<"password">>, <<"auth">>|Path], - [{password_format, Value}]) -> - validate_root_or_host_config(Path), - validate_password_format(Value); -validate([item, <<"hash">>, <<"password">>, <<"auth">>|Path], - [Value]) -> - validate_root_or_host_config(Path), - validate_enum(Value, [sha, sha224, sha256, sha384, sha512]); -validate([<<"scram_iterations">>, <<"auth">>|Path], - [{scram_iterations, Value}]) -> - validate_root_or_host_config(Path), - validate_positive_integer(Value); -validate([item, <<"sasl_external">>, <<"auth">>|Path], - [{mod, Module}]) -> - validate_root_or_host_config(Path), - validate_module(Module); -validate([<<"allow_multiple_connections">>, <<"anonymous">>, <<"auth">>|Path], - [{allow_multiple_connections, Value}]) -> - validate_root_or_host_config(Path), - validate_boolean(Value); -validate([<<"protocol">>, <<"anonymous">>, <<"auth">>|Path], - [{anonymous_protocol, Value}]) -> - validate_root_or_host_config(Path), - validate_enum(Value, [sasl_anon, login_anon, both]); -validate([Pool, <<"ldap">>, <<"auth">>|Path], - [{_, Value}]) when Pool =:= <<"pool_tag">>; - Pool =:= <<"bind_pool_tag">> -> - validate_root_or_host_config(Path), - validate_non_empty_atom(Value); -validate([<<"operation">>, <<"local_filter">>, <<"ldap">>, <<"auth">>|Path], - [{operation, Value}]) -> - validate_root_or_host_config(Path), - validate_enum(Value, [equal, not_equal]); -validate([<<"attribute">>, <<"local_filter">>, <<"ldap">>, <<"auth">>|Path], - [{attribute, Value}]) -> - validate_root_or_host_config(Path), - validate_non_empty_string(Value); -validate([<<"values">>, <<"local_filter">>, <<"ldap">>, <<"auth">>|Path], - [{values, Value}]) -> - validate_root_or_host_config(Path), - validate_non_empty_list(Value); -validate([<<"deref">>, <<"ldap">>, <<"auth">>|Path], - [{ldap_deref, Value}]) -> - validate_root_or_host_config(Path), - validate_enum(Value, [never, always, finding, searching]); -validate([item, <<"sasl_mechanisms">>, <<"auth">>|Path], - [Value]) -> - validate_root_or_host_config(Path), - validate_module(Value); -validate([<<"instances">>, <<"external">>, <<"auth">>|Path], - [{extauth_instances, Value}]) -> - validate_root_or_host_config(Path), - validate_positive_integer(Value); -validate([<<"program">>, <<"external">>, <<"auth">>|Path], - [{extauth_program, Value}]) -> - validate_root_or_host_config(Path), - validate_non_empty_string(Value); -validate([<<"file">>, <<"secret">>, <<"jwt">>, <<"auth">>|Path], - [{jwt_secret_source, Value}]) -> - validate_root_or_host_config(Path), - validate_non_empty_string(Value); -validate([<<"env">>, <<"secret">>, <<"jwt">>, <<"auth">>|Path], - [{jwt_secret_source, {env, Value}}]) -> - validate_root_or_host_config(Path), - validate_non_empty_string(Value); -validate([<<"algorithm">>, <<"jwt">>, <<"auth">>|Path], - [{jwt_algorithm, Value}]) -> - validate_root_or_host_config(Path), - validate_enum(Value, ["HS256", "RS256", "ES256", "HS386", "RS386", "ES386", - "HS512", "RS512", "ES512"]); -validate([<<"username_key">>, <<"jwt">>, <<"auth">>|Path], - [{jwt_username_key, Value}]) -> - validate_root_or_host_config(Path), - validate_non_empty_atom(Value); -validate([<<"bucket_type">>, <<"riak">>, <<"auth">>|Path], - [{bucket_type, Value}]) -> - validate_root_or_host_config(Path), - validate_non_empty_binary(Value); - %% outgoing_pools validate([_Tag, _Type, <<"outgoing_pools">>], [{TypeAtom, Scope, TagAtom, _Options, _ConnectionOptions}]) -> @@ -1523,9 +1439,6 @@ validate_non_empty_string(Value) when is_list(Value), Value =/= "" -> ok. validate_non_empty_list(Value) when is_list(Value), Value =/= [] -> ok. -validate_password_format({scram, [_|_]}) -> ok; -validate_password_format(Value) -> validate_enum(Value, [scram, plain]). - validate_pool_scope(Value) when is_binary(Value) -> validate_non_empty_binary(Value); validate_pool_scope(Value) -> validate_enum(Value, [host, global]). From 87c04a9320c907f1a2b1d4736dd6954b43941c65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Wed, 18 Nov 2020 11:05:26 +0100 Subject: [PATCH 023/104] Replace each block template in 'mongooseim.toml' with a section --- rel/files/mongooseim.toml | 64 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/rel/files/mongooseim.toml b/rel/files/mongooseim.toml index ff03b23bbc..a8ecc9a287 100644 --- a/rel/files/mongooseim.toml +++ b/rel/files/mongooseim.toml @@ -6,8 +6,12 @@ all_metrics_are_global = {{{all_metrics_are_global}}} sm_backend = {{{sm_backend}}} max_fsm_queue = 1000 + {{#http_server_name}} {{{http_server_name}}} + {{/http_server_name}} + {{#rdbms_server_type}} {{{rdbms_server_type}}} + {{/rdbms_server_type}} [[listen.http]] port = {{{http_port}}} @@ -31,7 +35,9 @@ port = {{{https_port}}} transport.num_acceptors = 10 transport.max_connections = 1024 + {{#https_config}} {{{https_config}}} + {{/https_config}} [[listen.http.handlers.mod_bosh]] host = "_" @@ -42,7 +48,9 @@ path = "/ws-xmpp" [[listen.http]] + {{#http_api_endpoint}} {{{http_api_endpoint}}} + {{/http_api_endpoint}} transport.num_acceptors = 10 transport.max_connections = 1024 @@ -51,11 +59,15 @@ path = "/api" [[listen.http]] + {{#http_api_client_endpoint}} {{{http_api_client_endpoint}}} + {{/http_api_client_endpoint}} transport.num_acceptors = 10 transport.max_connections = 1024 protocol.compress = true + {{#https_config}} {{{https_config}}} + {{/https_config}} [[listen.http.handlers.lasse_handler]] host = "_" @@ -102,7 +114,9 @@ content_path = "swagger" [[listen.http]] + {{#http_api_old_endpoint}} {{{http_api_old_endpoint}}} + {{/http_api_old_endpoint}} transport.num_acceptors = 10 transport.max_connections = 1024 @@ -113,34 +127,60 @@ [[listen.c2s]] port = {{{c2s_port}}} + {{#tls_config}} {{{tls_config}}} + {{/tls_config}} + {{#tls_module}} {{{tls_module}}} + {{/tls_module}} + {{#proxy_protocol}} {{{proxy_protocol}}} + {{/proxy_protocol}} + {{#zlib}} {{{zlib}}} + {{/zlib}} access = "c2s" shaper = "c2s_shaper" max_stanza_size = 65536 + {{#c2s_dhfile}} {{{c2s_dhfile}}} + {{/c2s_dhfile}} +{{#secondary_c2s}} {{{secondary_c2s}}} +{{/secondary_c2s}} [[listen.s2s]] port = {{{incoming_s2s_port}}} shaper = "s2s_shaper" max_stanza_size = 131072 + {{#s2s_dhfile}} {{{s2s_dhfile}}} + {{/s2s_dhfile}} +{{#listen_service}} {{{listen_service}}} +{{/listen_service}} [auth] + {{#auth_ldap}} {{{auth_ldap}}} + {{/auth_ldap}} methods = [{{{auth_method}}}] + {{#password_format}} {{{password_format}}} + {{/password_format}} + {{#scram_iterations}} {{{scram_iterations}}} + {{/scram_iterations}} sasl_external = [{{{cyrsasl_external}}}] + {{#sasl_mechanisms}} {{{sasl_mechanisms}}} + {{/sasl_mechanisms}} +{{#outgoing_pools}} {{{outgoing_pools}}} +{{/outgoing_pools}} #[outgoing_pools.redis.global_distrib] # scope = "single_host" # host = "localhost" @@ -171,7 +211,9 @@ [modules.mod_adhoc] +{{#mod_amp}} {{{mod_amp}}} +{{/mod_amp}} [modules.mod_disco] users_can_see_hidden_services = false @@ -182,17 +224,27 @@ [modules.mod_muc_light_commands] +{{#mod_last}} {{{mod_last}}} +{{/mod_last}} [modules.mod_stream_management] +{{#mod_offline}} {{{mod_offline}}} +{{/mod_offline}} +{{#mod_privacy}} {{{mod_privacy}}} +{{/mod_privacy}} +{{#mod_blocking}} {{{mod_blocking}}} +{{/mod_blocking}} +{{#mod_private}} {{{mod_private}}} +{{/mod_private}} [modules.mod_register] welcome_message = {body = "", subject = ""} @@ -202,11 +254,15 @@ ] access = "register" +{{#mod_roster}} {{{mod_roster}}} +{{/mod_roster}} [modules.mod_sic] +{{#mod_vcard}} {{{mod_vcard}}} +{{/mod_vcard}} [modules.mod_bosh] @@ -310,14 +366,22 @@ ] [s2s] + {{#s2s_use_starttls}} {{{s2s_use_starttls}}} + {{/s2s_use_starttls}} + {{#s2s_certfile}} {{{s2s_certfile}}} + {{/s2s_certfile}} default_policy = {{{s2s_default_policy}}} outgoing.port = {{{outgoing_s2s_port}}} + {{#s2s_addr}} {{{s2s_addr}}} + {{/s2s_addr}} +{{#host_config}} {{{host_config}}} +{{/host_config}} #[[host_config]] # host = "anonymous.localhost" # From bfd99898f1a4356c4a6c78d5277d0b88a85afcb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Wed, 18 Nov 2020 12:14:29 +0100 Subject: [PATCH 024/104] Do not put empty defaults in sections --- rel/vars-toml.config.in | 8 -------- 1 file changed, 8 deletions(-) diff --git a/rel/vars-toml.config.in b/rel/vars-toml.config.in index c2e1b81dbe..ec65792ea2 100644 --- a/rel/vars-toml.config.in +++ b/rel/vars-toml.config.in @@ -11,11 +11,7 @@ % TOML config {hosts, "\"localhost\""}. -{host_config, ""}. -{auth_ldap, ""}. -{s2s_addr, ""}. {s2s_default_policy, "\"deny\""}. -{mod_amp, ""}. {listen_service, "[[listen.service]] port = 8888 access = \"all\" @@ -36,12 +32,9 @@ {cyrsasl_external, "\"standard\""}. {tls_config, "tls.certfile = \"priv/ssl/fake_server.pem\" tls.mode = \"starttls\""}. -{tls_module, ""}. {https_config, "tls.certfile = \"priv/ssl/fake_cert.pem\" tls.keyfile = \"priv/ssl/fake_key.pem\" tls.password = \"\""}. -{zlib, ""}. -{outgoing_pools, ""}. {http_api_old_endpoint, "ip_address = \"127.0.0.1\" port = 5288"}. {http_api_endpoint, "ip_address = \"127.0.0.1\" @@ -49,7 +42,6 @@ {http_api_client_endpoint, "port = 8089"}. {s2s_use_starttls, "use_starttls = \"optional\""}. {s2s_certfile, "certfile = \"priv/ssl/fake_server.pem\""}. -{sasl_mechanisms, ""}. {all_metrics_are_global, "false"}. %% Defined in Makefile by appending configure.vars.config From 98d6f20bb347e04cde89dee2b85a1713cc45e8db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Wed, 18 Nov 2020 12:15:16 +0100 Subject: [PATCH 025/104] Reorganize TOML template to avoid empty lines Also: make comments conditional - remove them if a section is present --- rel/files/mongooseim.toml | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/rel/files/mongooseim.toml b/rel/files/mongooseim.toml index a8ecc9a287..43fb81ec82 100644 --- a/rel/files/mongooseim.toml +++ b/rel/files/mongooseim.toml @@ -148,8 +148,8 @@ {{#secondary_c2s}} {{{secondary_c2s}}} -{{/secondary_c2s}} +{{/secondary_c2s}} [[listen.s2s]] port = {{{incoming_s2s_port}}} shaper = "s2s_shaper" @@ -160,8 +160,8 @@ {{#listen_service}} {{{listen_service}}} -{{/listen_service}} +{{/listen_service}} [auth] {{#auth_ldap}} {{{auth_ldap}}} @@ -181,6 +181,7 @@ {{#outgoing_pools}} {{{outgoing_pools}}} {{/outgoing_pools}} +{{^outgoing_pools}} #[outgoing_pools.redis.global_distrib] # scope = "single_host" # host = "localhost" @@ -200,6 +201,7 @@ # tls.verify_peer = true # tls.cacertfile = "priv/ssl/cacert.pem" # tls.server_name_indication = false +{{/outgoing_pools}} [services.service_admin_extra] submods = ["node", "accounts", "sessions", "vcard", "gdpr", "upload", @@ -213,8 +215,8 @@ {{#mod_amp}} {{{mod_amp}}} -{{/mod_amp}} +{{/mod_amp}} [modules.mod_disco] users_can_see_hidden_services = false @@ -226,26 +228,26 @@ {{#mod_last}} {{{mod_last}}} -{{/mod_last}} +{{/mod_last}} [modules.mod_stream_management] {{#mod_offline}} {{{mod_offline}}} -{{/mod_offline}} +{{/mod_offline}} {{#mod_privacy}} {{{mod_privacy}}} -{{/mod_privacy}} +{{/mod_privacy}} {{#mod_blocking}} {{{mod_blocking}}} -{{/mod_blocking}} +{{/mod_blocking}} {{#mod_private}} {{{mod_private}}} -{{/mod_private}} +{{/mod_private}} [modules.mod_register] welcome_message = {body = "", subject = ""} ip_access = [ @@ -256,14 +258,14 @@ {{#mod_roster}} {{{mod_roster}}} -{{/mod_roster}} +{{/mod_roster}} [modules.mod_sic] {{#mod_vcard}} {{{mod_vcard}}} -{{/mod_vcard}} +{{/mod_vcard}} [modules.mod_bosh] [modules.mod_carboncopy] @@ -374,7 +376,6 @@ {{/s2s_certfile}} default_policy = {{{s2s_default_policy}}} outgoing.port = {{{outgoing_s2s_port}}} - {{#s2s_addr}} {{{s2s_addr}}} {{/s2s_addr}} @@ -382,6 +383,7 @@ {{#host_config}} {{{host_config}}} {{/host_config}} +{{^host_config}} #[[host_config]] # host = "anonymous.localhost" # @@ -389,3 +391,4 @@ # methods = ["anonymous"] # anonymous.allow_multiple_connections = true # anonymous.protocol = "both" +{{/host_config}} From 75851bdd5b12e629d68f8f78ccfa486f674fd953 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Thu, 19 Nov 2020 08:17:40 +0100 Subject: [PATCH 026/104] Clean up template vars - Remove unused variables - Rearrange sections to remove remaining empty lines - Simplify blocks that can be parameters (values) --- doc/advanced-configuration/release-options.md | 8 +-- rel/fed1.vars-toml.config | 11 ++-- rel/files/mongooseim.toml | 65 +++++++++---------- rel/mim1.vars-toml.config | 13 ++-- rel/mim2.vars-toml.config | 11 ++-- rel/mim3.vars-toml.config | 8 +-- rel/reg1.vars-toml.config | 4 +- rel/vars-toml.config.in | 4 +- 8 files changed, 56 insertions(+), 68 deletions(-) diff --git a/doc/advanced-configuration/release-options.md b/doc/advanced-configuration/release-options.md index e1b04d8226..d3f8898a01 100644 --- a/doc/advanced-configuration/release-options.md +++ b/doc/advanced-configuration/release-options.md @@ -195,7 +195,7 @@ These options are inserted into the `rel/files/mongooseim.toml` template. * **Type:** parameter * **Option:** [`general.sm_backend`](../../advanced-configuration/general#generalsm_backend) * **Syntax:** string -* **Example:** `{sm_backend, \""redis\""}.` +* **Example:** `{sm_backend, "\"redis\""}.` ## tls_config @@ -220,7 +220,7 @@ These options are inserted into the `rel/files/mongooseim.toml` template. ## zlib -* **Type:** block +* **Type:** parameter * **Option:** [`listen.c2s.zlib`](../../advanced-configuration/listen#listenc2szlib) -* **Syntax:** TOML key-value pair -* **Example:** `{zlib, "zlib = 10_000"}.` +* **Syntax:** positive integer +* **Example:** `{zlib, "10_000"}.` diff --git a/rel/fed1.vars-toml.config b/rel/fed1.vars-toml.config index 17ca364f05..7d19c030c2 100644 --- a/rel/fed1.vars-toml.config +++ b/rel/fed1.vars-toml.config @@ -15,23 +15,25 @@ {s2s_addr, "[[s2s.address]] host = \"localhost\" ip_address = \"127.0.0.1\" + [[s2s.address]] host = \"pubsub.localhost\" ip_address = \"127.0.0.1\" + [[s2s.address]] host = \"muc.localhost\" ip_address = \"127.0.0.1\" + [[s2s.address]] host = \"localhost.bis\" ip_address = \"127.0.0.1\""}. {s2s_default_policy, "\"allow\""}. {highload_vm_args, ""}. -{listen_service, ""}. +{listen_service, false}. {tls_config, "tls.certfile = \"priv/ssl/fake_server.pem\" tls.mode = \"starttls\" tls.ciphers = \"ECDHE-RSA-AES256-GCM-SHA384\""}. -{secondary_c2s, ""}. {http_api_old_endpoint, "ip_address = \"127.0.0.1\" port = {{ http_api_old_endpoint_port }}"}. @@ -39,6 +41,5 @@ port = {{ http_api_endpoint_port }}"}. {http_api_client_endpoint, "port = {{ http_api_client_endpoint_port }}"}. -{c2s_dhfile, "tls.dhfile = \"priv/ssl/fake_dh_server.pem\""}. -{s2s_dhfile, "tls.dhfile = \"priv/ssl/fake_dh_server.pem\""}. - +{c2s_dhfile, "\"priv/ssl/fake_dh_server.pem\""}. +{s2s_dhfile, "\"priv/ssl/fake_dh_server.pem\""}. diff --git a/rel/files/mongooseim.toml b/rel/files/mongooseim.toml index 43fb81ec82..ba105c5c26 100644 --- a/rel/files/mongooseim.toml +++ b/rel/files/mongooseim.toml @@ -7,10 +7,10 @@ sm_backend = {{{sm_backend}}} max_fsm_queue = 1000 {{#http_server_name}} - {{{http_server_name}}} + http_server_name = {{{http_server_name}}} {{/http_server_name}} {{#rdbms_server_type}} - {{{rdbms_server_type}}} + rdbms_server_type = {{{rdbms_server_type}}} {{/rdbms_server_type}} [[listen.http]] @@ -127,41 +127,35 @@ [[listen.c2s]] port = {{{c2s_port}}} - {{#tls_config}} - {{{tls_config}}} - {{/tls_config}} - {{#tls_module}} - {{{tls_module}}} - {{/tls_module}} - {{#proxy_protocol}} - {{{proxy_protocol}}} - {{/proxy_protocol}} {{#zlib}} - {{{zlib}}} + zlib = {{{zlib}}} {{/zlib}} access = "c2s" shaper = "c2s_shaper" max_stanza_size = 65536 + {{#tls_config}} + {{{tls_config}}} + {{/tls_config}} {{#c2s_dhfile}} - {{{c2s_dhfile}}} + tls.dhfile = {{{c2s_dhfile}}} {{/c2s_dhfile}} - {{#secondary_c2s}} -{{{secondary_c2s}}} +{{{secondary_c2s}}} {{/secondary_c2s}} + [[listen.s2s]] port = {{{incoming_s2s_port}}} shaper = "s2s_shaper" max_stanza_size = 131072 {{#s2s_dhfile}} - {{{s2s_dhfile}}} + tls.dhfile = {{{s2s_dhfile}}} {{/s2s_dhfile}} - {{#listen_service}} -{{{listen_service}}} +{{{listen_service}}} {{/listen_service}} + [auth] {{#auth_ldap}} {{{auth_ldap}}} @@ -171,11 +165,11 @@ {{{password_format}}} {{/password_format}} {{#scram_iterations}} - {{{scram_iterations}}} + scram_iterations = {{{scram_iterations}}} {{/scram_iterations}} sasl_external = [{{{cyrsasl_external}}}] {{#sasl_mechanisms}} - {{{sasl_mechanisms}}} + sasl_mechanisms = [{{{sasl_mechanisms}}}] {{/sasl_mechanisms}} {{#outgoing_pools}} @@ -212,11 +206,11 @@ periodic_report = 10_800_000 [modules.mod_adhoc] - {{#mod_amp}} -{{{mod_amp}}} +{{{mod_amp}}} {{/mod_amp}} + [modules.mod_disco] users_can_see_hidden_services = false @@ -225,29 +219,29 @@ [modules.mod_muc_commands] [modules.mod_muc_light_commands] - {{#mod_last}} -{{{mod_last}}} +{{{mod_last}}} {{/mod_last}} -[modules.mod_stream_management] +[modules.mod_stream_management] {{#mod_offline}} -{{{mod_offline}}} +{{{mod_offline}}} {{/mod_offline}} {{#mod_privacy}} -{{{mod_privacy}}} +{{{mod_privacy}}} {{/mod_privacy}} {{#mod_blocking}} -{{{mod_blocking}}} +{{{mod_blocking}}} {{/mod_blocking}} {{#mod_private}} -{{{mod_private}}} +{{{mod_private}}} {{/mod_private}} + [modules.mod_register] welcome_message = {body = "", subject = ""} ip_access = [ @@ -255,17 +249,17 @@ {address = "0.0.0.0/0", policy = "deny"} ] access = "register" - {{#mod_roster}} -{{{mod_roster}}} +{{{mod_roster}}} {{/mod_roster}} -[modules.mod_sic] +[modules.mod_sic] {{#mod_vcard}} -{{{mod_vcard}}} +{{{mod_vcard}}} {{/mod_vcard}} + [modules.mod_bosh] [modules.mod_carboncopy] @@ -369,14 +363,15 @@ [s2s] {{#s2s_use_starttls}} - {{{s2s_use_starttls}}} + use_starttls = {{{s2s_use_starttls}}} {{/s2s_use_starttls}} {{#s2s_certfile}} - {{{s2s_certfile}}} + certfile = {{{s2s_certfile}}} {{/s2s_certfile}} default_policy = {{{s2s_default_policy}}} outgoing.port = {{{outgoing_s2s_port}}} {{#s2s_addr}} + {{{s2s_addr}}} {{/s2s_addr}} diff --git a/rel/mim1.vars-toml.config b/rel/mim1.vars-toml.config index 3333698b47..bc2c4ea375 100644 --- a/rel/mim1.vars-toml.config +++ b/rel/mim1.vars-toml.config @@ -4,10 +4,7 @@ {kicking_service_port, 8666}. {hidden_service_port, 8189}. -{hosts, "\"localhost\", - \"anonymous.localhost\", - \"localhost.bis\" - "}. +{hosts, "\"localhost\", \"anonymous.localhost\", \"localhost.bis\""}. {host_config, "[[host_config]] host = \"anonymous.localhost\" @@ -18,7 +15,7 @@ anonymous.protocol = \"both\""}. {password_format, "password.format = \"scram\" password.hash = [\"sha256\"]"}. -{scram_iterations, "scram_iterations = 64"}. +{scram_iterations, 64}. {s2s_addr, "[[s2s.address]] host = \"fed1\" ip_address = \"127.0.0.1\""}. @@ -60,6 +57,6 @@ {mod_amp, "[modules.mod_amp]"}. {mod_private, "[modules.mod_private]"}. -{zlib, "zlib = 10_000"}. -{c2s_dhfile, "tls.dhfile = \"priv/ssl/fake_dh_server.pem\""}. -{s2s_dhfile, "tls.dhfile = \"priv/ssl/fake_dh_server.pem\""}. +{zlib, "10_000"}. +{c2s_dhfile, "\"priv/ssl/fake_dh_server.pem\""}. +{s2s_dhfile, "\"priv/ssl/fake_dh_server.pem\""}. diff --git a/rel/mim2.vars-toml.config b/rel/mim2.vars-toml.config index 977272ed64..43022b02a1 100644 --- a/rel/mim2.vars-toml.config +++ b/rel/mim2.vars-toml.config @@ -10,10 +10,7 @@ {http_api_client_endpoint_port, 8091}. {service_port, 8899}. -{hosts, "\"localhost\", - \"anonymous.localhost\", - \"localhost.bis\" - "}. +{hosts, "\"localhost\", \"anonymous.localhost\", \"localhost.bis\""}. {s2s_addr, "[[s2s.address]] host = \"localhost2\" ip_address = \"127.0.0.1\""}. @@ -49,7 +46,7 @@ password = \"secret\""}. {all_metrics_are_global, "true"}. -{http_server_name, "http_server_name = \"Classified\""}. +{http_server_name, "\"Classified\""}. -{c2s_dhfile, "tls.dhfile = \"priv/ssl/fake_dh_server.pem\""}. -{s2s_dhfile, "tls.dhfile = \"priv/ssl/fake_dh_server.pem\""}. +{c2s_dhfile, "\"priv/ssl/fake_dh_server.pem\""}. +{s2s_dhfile, "\"priv/ssl/fake_dh_server.pem\""}. diff --git a/rel/mim3.vars-toml.config b/rel/mim3.vars-toml.config index 952755046a..2c9b8596be 100644 --- a/rel/mim3.vars-toml.config +++ b/rel/mim3.vars-toml.config @@ -10,9 +10,7 @@ {http_api_endpoint_port, 8092}. {http_api_client_endpoint_port, 8093}. -{hosts, "\"localhost\", - \"anonymous.localhost\", - \"localhost.bis\""}. +{hosts, "\"localhost\", \"anonymous.localhost\", \"localhost.bis\""}. {s2s_addr, "[[s2s.address]] host = \"localhost2\" @@ -43,5 +41,5 @@ port = {{ http_api_endpoint_port }}"}. {http_api_client_endpoint, "port = {{ http_api_client_endpoint_port }}"}. -{c2s_dhfile, "tls.dhfile = \"priv/ssl/fake_dh_server.pem\""}. -{s2s_dhfile, "tls.dhfile = \"priv/ssl/fake_dh_server.pem\""}. +{c2s_dhfile, "\"priv/ssl/fake_dh_server.pem\""}. +{s2s_dhfile, "\"priv/ssl/fake_dh_server.pem\""}. diff --git a/rel/reg1.vars-toml.config b/rel/reg1.vars-toml.config index 2b82df4229..e6d62a3cbf 100644 --- a/rel/reg1.vars-toml.config +++ b/rel/reg1.vars-toml.config @@ -41,6 +41,6 @@ port = {{ http_api_endpoint_port }}"}. {http_api_client_endpoint, "port = {{ http_api_client_endpoint_port }}"}. -{c2s_dhfile, "tls.dhfile = \"priv/ssl/fake_dh_server.pem\""}. -{s2s_dhfile, "tls.dhfile = \"priv/ssl/fake_dh_server.pem\""}. +{c2s_dhfile, "\"priv/ssl/fake_dh_server.pem\""}. +{s2s_dhfile, "\"priv/ssl/fake_dh_server.pem\""}. diff --git a/rel/vars-toml.config.in b/rel/vars-toml.config.in index ec65792ea2..5b1f5ce602 100644 --- a/rel/vars-toml.config.in +++ b/rel/vars-toml.config.in @@ -40,8 +40,8 @@ {http_api_endpoint, "ip_address = \"127.0.0.1\" port = 8088"}. {http_api_client_endpoint, "port = 8089"}. -{s2s_use_starttls, "use_starttls = \"optional\""}. -{s2s_certfile, "certfile = \"priv/ssl/fake_server.pem\""}. +{s2s_use_starttls, "\"optional\""}. +{s2s_certfile, "\"priv/ssl/fake_server.pem\""}. {all_metrics_are_global, "false"}. %% Defined in Makefile by appending configure.vars.config From 9f375f78e8710bf1e8255b95f085977a06b0710a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Thu, 19 Nov 2020 08:20:20 +0100 Subject: [PATCH 027/104] Make big tests compatible with the new templates - Update simplified variables - Binarize strings to avoid bbmustache iterating over their elements. This is necessary after introducing sections. rebar3 does this automatically using relx, but the tests don't. --- big_tests/run_common_test.erl | 9 ++++++++- big_tests/test.config | 2 +- big_tests/tests/ejabberd_node_utils.erl | 9 ++++++++- big_tests/tests/sasl_external_SUITE.erl | 6 +++--- tools/test_runner/apply_templates.erl | 8 +++++++- 5 files changed, 27 insertions(+), 7 deletions(-) diff --git a/big_tests/run_common_test.erl b/big_tests/run_common_test.erl index be20a43ba4..03f29aaf59 100644 --- a/big_tests/run_common_test.erl +++ b/big_tests/run_common_test.erl @@ -294,7 +294,7 @@ enable_preset_on_node(Node, PresetVars, HostVarsFilePrefix) -> ok. template_config(Template, Vars) -> - MergedVars = merge_vars(Vars), + MergedVars = ensure_binary_strings(merge_vars(Vars)), %% Render twice to replace variables in variables Tmp = bbmustache:render(Template, MergedVars, [{key_type, atom}]), bbmustache:render(Tmp, MergedVars, [{key_type, atom}]). @@ -306,6 +306,13 @@ merge_vars([Vars1, Vars2|Rest]) -> merge_vars([Vars|Rest]); merge_vars([Vars]) -> Vars. +%% bbmustache tries to iterate over lists, so we need to make them binaries +ensure_binary_strings(Vars) -> + lists:map(fun({dbs, V}) -> {dbs, V}; + ({K, V}) when is_list(V) -> {K, list_to_binary(V)}; + ({K, V}) -> {K, V} + end, Vars). + call(Node, M, F, A) -> case rpc:call(Node, M, F, A) of {badrpc, Reason} -> diff --git a/big_tests/test.config b/big_tests/test.config index e2ad26e173..dcd822c7da 100644 --- a/big_tests/test.config +++ b/big_tests/test.config @@ -245,7 +245,7 @@ {odbc_mssql_mnesia, [{dbs, [redis, mssql]}, {auth_method, "\"rdbms\""}, - {rdbms_server_type, "rdbms_server_type = \"mssql\""}, + {rdbms_server_type, "\"mssql\""}, {outgoing_pools, "[outgoing_pools.redis.global_distrib] scope = \"global\" workers = 10 diff --git a/big_tests/tests/ejabberd_node_utils.erl b/big_tests/tests/ejabberd_node_utils.erl index 23f2dad734..eb2b5a1fcb 100644 --- a/big_tests/tests/ejabberd_node_utils.erl +++ b/big_tests/tests/ejabberd_node_utils.erl @@ -172,7 +172,7 @@ modify_config_file(Host, VarsToChange, Config, Format) -> ok = ejabberd_node_utils:call_fun(RPCSpec, file, write_file, [NewCfgPath, TemplatedConfig]). template_config(Template, Vars) -> - MergedVars = merge_vars(Vars), + MergedVars = ensure_binary_strings(merge_vars(Vars)), %% Render twice to replace variables in variables Tmp = bbmustache:render(Template, MergedVars, [{key_type, atom}]), bbmustache:render(Tmp, MergedVars, [{key_type, atom}]). @@ -184,6 +184,13 @@ merge_vars([Vars1, Vars2|Rest]) -> merge_vars([Vars|Rest]); merge_vars([Vars]) -> Vars. +%% bbmustache tries to iterate over lists, so we need to make them binaries +ensure_binary_strings(Vars) -> + lists:map(fun({dbs, V}) -> {dbs, V}; + ({K, V}) when is_list(V) -> {K, list_to_binary(V)}; + ({K, V}) -> {K, V} + end, Vars). + update_config_path(RPCSpec, Format) -> CurrentCfgPath = get_config_path(RPCSpec), CurrentConfigFileName = filename:basename(CurrentCfgPath), diff --git a/big_tests/tests/sasl_external_SUITE.erl b/big_tests/tests/sasl_external_SUITE.erl index b411418f42..1dc966205b 100644 --- a/big_tests/tests/sasl_external_SUITE.erl +++ b/big_tests/tests/sasl_external_SUITE.erl @@ -150,12 +150,12 @@ modify_config_and_restart(CyrsaslExternalConfig, Config) -> AuthMethods = escalus_config:get_config(auth_methods, Config, [{auth_method, "\"pki\""}]), CACertFile = filename:join([path_helper:repo_dir(Config), "tools", "ssl", "ca-clients", "cacert.pem"]), - NewConfigValues = [{tls_config, "tls.certfile = \"priv/ssl/fake_server.pem\"\n" + NewConfigValues = [{tls_config, "tls.module = \"" ++ TLSModule ++ "\"\n" + " tls.certfile = \"priv/ssl/fake_server.pem\"\n" " tls.mode = \"starttls\"\n" " tls.verify_peer = true\n" " tls.cacertfile = \"" ++ CACertFile ++ "\"" ++ SSLOpts}, - {tls_module, "tls.module = \"" ++ TLSModule ++ "\""}, {https_config, "tls.certfile = \"priv/ssl/fake_cert.pem\"\n" " tls.keyfile = \"priv/ssl/fake_key.pem\"\n" " tls.password = \"\"\n" @@ -163,7 +163,7 @@ modify_config_and_restart(CyrsaslExternalConfig, Config) -> " tls.cacertfile = \"" ++ CACertFile ++ "\"" ++ VerifyMode}, {cyrsasl_external, CyrsaslExternalConfig}, - {sasl_mechanisms, "sasl_mechanisms = [\"external\"]"} | AuthMethods], + {sasl_mechanisms, "\"external\""} | AuthMethods], ejabberd_node_utils:modify_config_file(NewConfigValues, Config), ejabberd_node_utils:restart_application(mongooseim). diff --git a/tools/test_runner/apply_templates.erl b/tools/test_runner/apply_templates.erl index 5278dfc63b..382a21be71 100644 --- a/tools/test_runner/apply_templates.erl +++ b/tools/test_runner/apply_templates.erl @@ -23,7 +23,13 @@ overlay_vars(Node) -> Vars = consult_map("rel/vars-toml.config"), NodeVars = consult_map("rel/" ++ atom_to_list(Node) ++ ".vars-toml.config"), %% NodeVars overrides Vars - maps:merge(Vars, NodeVars). + ensure_binary_strings(maps:merge(Vars, NodeVars)). + +%% bbmustache tries to iterate over lists, so we need to make them binaries +ensure_binary_strings(Vars) -> + maps:map(fun(_K, V) when is_list(V) -> list_to_binary(V); + (_K, V) -> V + end, Vars). consult_map(File) -> {ok, Vars} = file:consult(File), From dec03b0af7a596a4b57ad07b61ee0763c55d01ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Mon, 23 Nov 2020 09:58:07 +0100 Subject: [PATCH 028/104] Parse the 'outgoing_pools' section in a declarative way Functional changes: - TLS options for 'riak': 'certfile', 'cacertfile' and 'keyfile' were extracted out of 'ssl_opts', but only 'cacertfile' is accepted by 'mongoose_wpool_riak'. This is fixed now - only 'cacertfile' is extracted. - 'tls.ciphers' follow the string format used in the 'listen' section (the same format and code is used for both sections) - Riak option format disambiguated to match the documented one. Now there is one format for: - 'user' and 'password' - 'cacertfile', 'certfile' and 'keyfile' - 'generic' pool type not configurable in TOML anymore. This is an internal pool type, the user should never declare it. --- src/config/mongoose_config_parser_toml.erl | 257 +-------------- src/config/mongoose_config_spec.erl | 299 ++++++++++++++++-- src/config/mongoose_config_validator_toml.erl | 125 -------- 3 files changed, 284 insertions(+), 397 deletions(-) diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index 518ab19924..1b2df024ee 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -94,9 +94,6 @@ parse_root(Path, Content) -> %% path: * -spec process_section(path(), toml_section() | [toml_section()]) -> config_list(). -process_section([<<"outgoing_pools">>] = Path, Content) -> - Pools = parse_section(Path, Content), - [#local_config{key = outgoing_pools, value = Pools}]; process_section([<<"services">>] = Path, Content) -> Services = parse_section(Path, Content), [#local_config{key = services, value = Services}]; @@ -108,173 +105,12 @@ process_section([<<"host_config">>] = Path, Content) -> process_section(Path, Content) -> parse_section(Path, Content). -%% path: outgoing_pools.*.* --spec process_pool(path(), toml_section()) -> [option()]. -process_pool([Tag, Type|_] = Path, M) -> - Scope = pool_scope(M), - Options = parse_section(Path, maps:without([<<"scope">>, <<"host">>, <<"connection">>], M)), - ConnectionOptions = parse_kv(Path, <<"connection">>, M, #{}), - [{b2a(Type), Scope, b2a(Tag), Options, ConnectionOptions}]. - --spec pool_scope(toml_section()) -> option(). -pool_scope(#{<<"scope">> := <<"single_host">>, <<"host">> := Host}) -> Host; -pool_scope(#{<<"scope">> := Scope}) -> b2a(Scope); -pool_scope(#{}) -> global. - -%% path: outgoing_pools.*.*.*, -%% (host_config[].)modules.mod_event_pusher.backend.push.wpool.* +%% path: (host_config[].)modules.mod_event_pusher.backend.push.wpool.* -spec pool_option(path(), toml_value()) -> [option()]. pool_option([<<"workers">>|_], V) -> [{workers, V}]; pool_option([<<"strategy">>|_], V) -> [{strategy, b2a(V)}]; pool_option([<<"call_timeout">>|_], V) -> [{call_timeout, V}]. -%% path: outgoing_pools.*.connection --spec connection_options(path(), toml_section()) -> [option()]. -connection_options([{connection, Driver}, _, <<"rdbms">>|_] = Path, M) -> - Options = parse_section(Path, maps:with([<<"keepalive_interval">>], M)), - ServerOptions = parse_section(Path, maps:without([<<"keepalive_interval">>], M)), - Server = rdbms_server(Driver, ServerOptions), - [{server, Server} | Options]; -connection_options([_, _, <<"riak">>|_] = Path, Options = #{<<"username">> := UserName, - <<"password">> := Password}) -> - M = maps:without([<<"username">>, <<"password">>], Options), - [{credentials, b2l(UserName), b2l(Password)} | parse_section(Path, M)]; -connection_options(Path, Options) -> - parse_section(Path, Options). - --spec rdbms_server(atom(), [option()]) -> option(). -rdbms_server(odbc, Opts) -> - [{settings, Settings}] = Opts, - Settings; -rdbms_server(Driver, Opts) -> - {_, Host} = proplists:lookup(host, Opts), - {_, Database} = proplists:lookup(database, Opts), - {_, UserName} = proplists:lookup(username, Opts), - {_, Password} = proplists:lookup(password, Opts), - case {proplists:get_value(port, Opts, no_port), - proplists:get_value(tls, Opts, no_tls)} of - {no_port, no_tls} -> {Driver, Host, Database, UserName, Password}; - {Port, no_tls} -> {Driver, Host, Port, Database, UserName, Password}; - {no_port, TLS} -> {Driver, Host, Database, UserName, Password, TLS}; - {Port, TLS} -> {Driver, Host, Port, Database, UserName, Password, TLS} - end. - -%% path: outgoing_pools.rdbms.*.connection.* --spec odbc_option(path(), toml_value()) -> [option()]. -odbc_option([<<"settings">>|_], V) -> [{settings, b2l(V)}]; -odbc_option(Path, V) -> rdbms_option(Path, V). - --spec sql_server_option(path(), toml_value()) -> [option()]. -sql_server_option([<<"host">>|_], V) -> [{host, b2l(V)}]; -sql_server_option([<<"database">>|_], V) -> [{database, b2l(V)}]; -sql_server_option([<<"username">>|_], V) -> [{username, b2l(V)}]; -sql_server_option([<<"password">>|_], V) -> [{password, b2l(V)}]; -sql_server_option([<<"port">>|_], V) -> [{port, V}]; -sql_server_option([<<"tls">>, {connection, mysql} | _] = Path, Opts) -> - [{tls, parse_section(Path, Opts)}]; -sql_server_option([<<"tls">>, {connection, pgsql} | _] = Path, Opts) -> - % true means try to establish encryption and proceed plain if failed - % required means fail if encryption is not possible - % false would mean do not even try, but we do not let the user do it - {SSLMode, Opts1} = case maps:take(<<"required">>, Opts) of - {true, M} -> {required, M}; - {false, M} -> {true, M}; - error -> {true, Opts} - end, - SSLOpts = case parse_section(Path, Opts1) of - [] -> []; - SSLOptList -> [{ssl_opts, SSLOptList}] - end, - [{tls, [{ssl, SSLMode} | SSLOpts]}]; -sql_server_option(Path, V) -> rdbms_option(Path, V). - --spec rdbms_option(path(), toml_value()) -> [option()]. -rdbms_option([<<"keepalive_interval">>|_], V) -> [{keepalive_interval, V}]; -rdbms_option([<<"driver">>|_], _V) -> []. - -%% path: outgoing_pools.http.*.connection.* --spec http_option(path(), toml_value()) -> [option()]. -http_option([<<"host">>|_], V) -> [{server, b2l(V)}]; -http_option([<<"path_prefix">>|_], V) -> [{path_prefix, b2l(V)}]; -http_option([<<"request_timeout">>|_], V) -> [{request_timeout, V}]; -http_option([<<"tls">>|_] = Path, Options) -> [{http_opts, parse_section(Path, Options)}]. - -%% path: outgoing_pools.redis.*.connection.* --spec redis_option(path(), toml_value()) -> [option()]. -redis_option([<<"host">>|_], Host) -> [{host, b2l(Host)}]; -redis_option([<<"port">>|_], Port) -> [{port, Port}]; -redis_option([<<"database">>|_], Database) -> [{database, Database}]; -redis_option([<<"password">>|_], Password) -> [{password, b2l(Password)}]. - -%% path: outgoing_pools.ldap.*.connection.* --spec ldap_option(path(), toml_value()) -> [option()]. -ldap_option([<<"host">>|_], Host) -> [{host, b2l(Host)}]; -ldap_option([<<"port">>|_], Port) -> [{port, Port}]; -ldap_option([<<"rootdn">>|_], RootDN) -> [{rootdn, b2l(RootDN)}]; -ldap_option([<<"password">>|_], Password) -> [{password, b2l(Password)}]; -ldap_option([<<"encrypt">>|_], <<"tls">>) -> [{encrypt, tls}]; -ldap_option([<<"encrypt">>|_], <<"none">>) -> [{encrypt, none}]; -ldap_option([<<"servers">>|_] = Path, V) -> [{servers, parse_list(Path, V)}]; -ldap_option([<<"connect_interval">>|_], V) -> [{connect_interval, V}]; -ldap_option([<<"tls">>|_] = Path, Options) -> [{tls_options, parse_section(Path, Options)}]. - -%% path: outgoing_pools.riak.*.connection.* --spec riak_option(path(), toml_value()) -> [option()]. -riak_option([<<"address">>|_], Addr) -> [{address, b2l(Addr)}]; -riak_option([<<"port">>|_], Port) -> [{port, Port}]; -riak_option([<<"credentials">>|_] = Path, V) -> - parse_section(Path, V, fun process_riak_credentials/1); -riak_option([<<"cacertfile">>|_], Path) -> [{cacertfile, b2l(Path)}]; -riak_option([<<"certfile">>|_], Path) -> [{certfile, b2l(Path)}]; -riak_option([<<"keyfile">>|_], Path) -> [{keyfile, b2l(Path)}]; -riak_option([<<"tls">>|_] = Path, Options) -> - Ssl = parse_section(Path, Options), - {RootOpts, SslOpts} = proplists:split(Ssl, [cacertfile, certfile, keyfile]), - case SslOpts of - [] ->lists:flatten(RootOpts); - _ -> [{ssl_opts, SslOpts} | lists:flatten(RootOpts)] - end. - -process_riak_credentials(Creds) -> - {_, User} = proplists:lookup(user, Creds), - {_, Pass} = proplists:lookup(password, Creds), - [{credentials, User, Pass}]. - -%% path: outgoing_pools.riak.*.connection.credentials.* --spec riak_credentials(path(), toml_value()) -> [option()]. -riak_credentials([<<"user">>|_], V) -> [{user, b2l(V)}]; -riak_credentials([<<"password">>|_], V) -> [{password, b2l(V)}]. - -%% path: outgoing_pools.cassandra.*.connnection.* --spec cassandra_option(path(), toml_value()) -> [option()]. -cassandra_option([<<"servers">>|_] = Path, V) -> [{servers, parse_list(Path, V)}]; -cassandra_option([<<"keyspace">>|_], KeySpace) -> [{keyspace, b2l(KeySpace)}]; -cassandra_option([<<"tls">>|_] = Path, Options) -> [{ssl, parse_section(Path, Options)}]; -cassandra_option([<<"auth">>|_] = Path, Options) -> - [AuthConfig] = parse_section(Path, Options), - [{auth, AuthConfig}]; -cassandra_option([<<"plain">>|_], #{<<"username">> := User, <<"password">> := Pass}) -> - [{cqerl_auth_plain_handler, [{User, Pass}]}]. - -%% path: outgoing_pools.cassandra.*.connection.servers[] --spec cassandra_server(path(), toml_section()) -> [option()]. -cassandra_server(_, #{<<"ip_address">> := IPAddr, <<"port">> := Port}) -> [{b2l(IPAddr), Port}]; -cassandra_server(_, #{<<"ip_address">> := IPAddr}) -> [b2l(IPAddr)]. - -%% path: outgoing_pools.elastic.*.connection.* --spec elastic_option(path(), toml_value()) -> [option()]. -elastic_option([<<"host">>|_], Host) -> [{host, b2l(Host)}]; -elastic_option([<<"port">>|_], Port) -> [{port, Port}]. - -%% path: outgoing_pools.rabbit.*.connection.* --spec rabbit_option(path(), toml_value()) -> [option()]. -rabbit_option([<<"amqp_host">>|_], V) -> [{amqp_host, b2l(V)}]; -rabbit_option([<<"amqp_port">>|_], V) -> [{amqp_port, V}]; -rabbit_option([<<"amqp_username">>|_], V) -> [{amqp_username, b2l(V)}]; -rabbit_option([<<"amqp_password">>|_], V) -> [{amqp_password, b2l(V)}]; -rabbit_option([<<"confirms_enabled">>|_], V) -> [{confirms_enabled, V}]; -rabbit_option([<<"max_worker_queue_len">>|_], V) -> [{max_worker_queue_len, int_or_infinity(V)}]. - %% path: services.* -spec process_service(path(), toml_section()) -> [option()]. process_service([S|_] = Path, Opts) -> @@ -1249,26 +1085,7 @@ process_host_item(Path, M) -> {_Host, Sections} = maps:take(<<"host">>, M), parse_section(Path, Sections). -%% path: listen.http[].tls.*, -%% listen.c2s[].tls.*, -%% outgoing_pools.rdbms.connection.tls.*, -%% outgoing_pools.ldap.connection.tls.*, -%% outgoing_pools.riak.connection.tls.*, -%% outgoing_pools.cassandra.connection.tls.* --spec tls_option(path(), toml_value()) -> [option()]. -tls_option([<<"verify_peer">>|_], V) -> [{verify, verify_peer(V)}]; -tls_option([<<"certfile">>|_], V) -> [{certfile, b2l(V)}]; -tls_option([<<"cacertfile">>|_], V) -> [{cacertfile, b2l(V)}]; -tls_option([<<"dhfile">>|_], V) -> [{dhfile, b2l(V)}]; -tls_option([<<"keyfile">>|_], V) -> [{keyfile, b2l(V)}]; -tls_option([<<"password">>|_], V) -> [{password, b2l(V)}]; -tls_option([<<"server_name_indication">>|_], false) -> [{server_name_indication, disable}]; -tls_option([<<"ciphers">>|_] = Path, L) -> [{ciphers, parse_list(Path, L)}]; -tls_option([<<"versions">>|_] = Path, L) -> [{versions, parse_list(Path, L)}]. - -%% path: listen.http[].tls.*, -%% listen.c2s[].tls.*,, -%% (host_config[].)modules.mod_global_distrib.connections.tls.* +%% path: (host_config[].)modules.mod_global_distrib.connections.tls.* -spec fast_tls_option(path(), toml_value()) -> [option()]. fast_tls_option([<<"certfile">>|_], V) -> [{certfile, b2l(V)}]; fast_tls_option([<<"cacertfile">>|_], V) -> [{cafile, b2l(V)}]; @@ -1280,18 +1097,6 @@ mod_global_distrib_tls_option([<<"enabled">>|_], V) -> mod_global_distrib_tls_option(P, V) -> fast_tls_option(P, V). --spec verify_peer(boolean()) -> option(). -verify_peer(false) -> verify_none; -verify_peer(true) -> verify_peer. - --spec tls_cipher(path(), toml_value()) -> [option()]. -tls_cipher(_, #{<<"key_exchange">> := KEx, - <<"cipher">> := Cipher, - <<"mac">> := MAC, - <<"prf">> := PRF}) -> - [#{key_exchange => b2a(KEx), cipher => b2a(Cipher), mac => b2a(MAC), prf => b2a(PRF)}]; -tls_cipher(_, Cipher) -> [b2l(Cipher)]. - set_overrides(Overrides, State) -> lists:foldl(fun({override, Scope}, CurrentState) -> mongoose_config_parser:override(Scope, CurrentState) @@ -1319,12 +1124,6 @@ ensure_keys(Keys, Section) -> MissingKeys -> error(#{what => missing_mandatory_keys, missing_keys => MissingKeys}) end. --spec parse_kv(path(), toml_key(), toml_section(), option()) -> option(). -parse_kv(Path, K, Section, Default) -> - Value = maps:get(K, Section, Default), - Key = key(K, Path, Value), - handle([Key|Path], Value). - %% Parse with post-processing, this needs to be eliminated by fixing the internal config structure -spec parse_section(path(), toml_section(), fun(([option()]) -> option())) -> option(). parse_section(Path, V, PostProcessF) -> @@ -1337,8 +1136,7 @@ parse_section(Path, V, PostProcessF) -> -spec parse_section(path(), toml_section()) -> [option()]. parse_section(Path, M) -> lists:flatmap(fun({K, V}) -> - Key = key(K, Path, V), - handle([Key|Path], V) + handle([K|Path], V) end, lists:sort(maps:to_list(M))). -spec parse_list(path(), [toml_value()]) -> [option()]. @@ -1522,46 +1320,8 @@ node_to_string(Node) -> [binary_to_list(Node)]. handler([]) -> fun parse_root/2; handler([Section]) when Section =/= <<"general">>, Section =/= <<"listen">>, - Section =/= <<"auth">> -> fun process_section/2; - -%% outgoing_pools -handler([_, <<"outgoing_pools">>]) -> fun parse_section/2; -handler([_, _, <<"outgoing_pools">>]) -> fun process_pool/2; -handler([<<"connection">>, _, _, <<"outgoing_pools">>]) -> fun connection_options/2; -handler([{connection, _}, _, - <<"rdbms">>, <<"outgoing_pools">>]) -> fun connection_options/2; -handler([_, _, _, <<"outgoing_pools">>]) -> fun pool_option/2; -handler([_, {connection, odbc}, _, - <<"rdbms">>, <<"outgoing_pools">>]) -> fun odbc_option/2; -handler([_, {connection, _}, _, - <<"rdbms">>, <<"outgoing_pools">>]) -> fun sql_server_option/2; -handler([_, <<"connection">>, _, - <<"http">>, <<"outgoing_pools">>]) -> fun http_option/2; -handler([_, <<"connection">>, _, - <<"redis">>, <<"outgoing_pools">>]) -> fun redis_option/2; -handler([_, <<"connection">>, _, - <<"ldap">>, <<"outgoing_pools">>]) -> fun ldap_option/2; -handler([_, <<"servers">>, <<"connection">>, _, - <<"ldap">>, <<"outgoing_pools">>]) -> fun(_, V) -> [b2l(V)] end; -handler([_, <<"connection">>, _, - <<"riak">>, <<"outgoing_pools">>]) -> fun riak_option/2; -handler([_, <<"credentials">>, <<"connection">>, _, - <<"riak">>, <<"outgoing_pools">>]) -> fun riak_credentials/2; -handler([_, <<"connection">>, _, - <<"cassandra">>, <<"outgoing_pools">>]) -> fun cassandra_option/2; -handler([_, <<"auth">>, <<"connection">>, _, - <<"cassandra">>, <<"outgoing_pools">>]) -> fun cassandra_option/2; -handler([_, <<"servers">>, <<"connection">>, _, - <<"cassandra">>, <<"outgoing_pools">>]) -> fun cassandra_server/2; -handler([_, <<"connection">>, _, - <<"elastic">>, <<"outgoing_pools">>]) -> fun elastic_option/2; -handler([_, <<"connection">>, _, - <<"rabbit">>, <<"outgoing_pools">>]) -> fun rabbit_option/2; -handler([_, <<"tls">>, _, _, _, <<"outgoing_pools">>]) -> fun tls_option/2; -handler([_, <<"versions">>, <<"tls">>, _, _, _, <<"outgoing_pools">>]) -> - fun(_, Val) -> [b2a(Val)] end; -handler([_, <<"ciphers">>, <<"tls">>, _, _, _, <<"outgoing_pools">>]) -> - fun tls_cipher/2; + Section =/= <<"auth">>, + Section =/= <<"outgoing_pools">> -> fun process_section/2; %% services handler([_, <<"services">>]) -> fun process_service/2; @@ -1727,13 +1487,6 @@ handler_for_host(Path) -> Spec end. --spec key(toml_key(), path(), toml_value()) -> tuple() | toml_key(). -key(<<"connection">>, [_, <<"rdbms">>, <<"outgoing_pools">>], M) -> - %% store the db driver in path as 'odbc' and 'mysql'/'pgsql' need different options - Driver = maps:get(<<"driver">>, M), - {connection, b2a(Driver)}; -key(Key, _Path, _) -> Key. - -spec item_key(path(), toml_value()) -> tuple() | item. item_key([<<"host_config">>], #{<<"host">> := Host}) -> {host, Host}; item_key(_, _) -> item. diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index 806f407aac..d846741de4 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -31,7 +31,8 @@ root() -> #section{ items = #{<<"general">> => general(), <<"listen">> => listen(), - <<"auth">> => auth() + <<"auth">> => auth(), + <<"outgoing_pools">> => outgoing_pools() }, required = [<<"general">>], format = none @@ -238,26 +239,11 @@ s2s_tls() -> %% path: listen.http[].tls http_listener_tls() -> + Items = tls_items(), #section{ - items = #{<<"verify_peer">> => #option{type = boolean, - process = fun ?MODULE:process_verify_peer/1, - format = {kv, verify}}, - <<"certfile">> => #option{type = string, - validate = non_empty}, - <<"cacertfile">> => #option{type = string, - validate = non_empty}, - <<"dhfile">> => #option{type = string, - validate = non_empty}, - <<"keyfile">> => #option{type = string, - validate = non_empty}, - <<"password">> => #option{type = string}, - <<"server_name_indication">> => #option{type = boolean, - process = fun ?MODULE:process_sni/1}, - <<"ciphers">> => #option{type = string}, - <<"versions">> => #list{items = #option{type = atom}}, - <<"verify_mode">> => #option{type = atom, - validate = {enum, [peer, selfsigned_peer, none]}} - }, + items = Items#{<<"verify_mode">> => #option{type = atom, + validate = {enum, [peer, selfsigned_peer, none]}} + }, format = {kv, ssl} }. @@ -509,6 +495,206 @@ auth_riak() -> format = none }. +%% path: outgoing_pools +outgoing_pools() -> + PoolTypes = [<<"cassandra">>, <<"elastic">>, <<"http">>, <<"ldap">>, + <<"rabbit">>, <<"rdbms">>, <<"redis">>, <<"riak">>], + Items = [{Type, #section{items = #{default => outgoing_pool(Type)}, + validate_keys = non_empty, + format = none}} || Type <- PoolTypes], + #section{ + items = maps:from_list(Items), + format = local_config + }. + +%% path: outgoing_pools.*.* +outgoing_pool(Type) -> + #section{ + items = #{<<"scope">> => #option{type = atom, + validate = {enum, [global, host, single_host]}}, + <<"host">> => #option{type = binary, + validate = non_empty}, + <<"connection">> => outgoing_pool_connection(Type), + <<"workers">> => #option{type = integer, + validate = positive}, + <<"strategy">> => #option{type = atom, + validate = {enum, wpool_strategy_values()}}, + <<"call_timeout">> => #option{type = integer, + validate = positive} + }, + process = fun ?MODULE:process_pool/2, + format = item + }. + +%% path: outgoing_pools.*.*.connection +outgoing_pool_connection(<<"cassandra">>) -> + #section{ + items = #{<<"servers">> => #list{items = cassandra_server()}, + <<"keyspace">> => #option{type = string, + validate = non_empty}, + <<"auth">> => #section{items = #{<<"plain">> => cassandra_auth_plain()}, + required = all, + process = fun ?MODULE:process_cassandra_auth/1}, + <<"tls">> => #section{items = tls_items(), + format = {kv, ssl}} + } + }; +outgoing_pool_connection(<<"elastic">>) -> + #section{ + items = #{<<"host">> => #option{type = string, + validate = non_empty}, + <<"port">> => #option{type = integer, + validate = port} + } + }; +outgoing_pool_connection(<<"http">>) -> + #section{ + items = #{<<"host">> => #option{type = string, + validate = non_empty, + format = {kv, server}}, + <<"path_prefix">> => #option{type = string, + validate = non_empty}, + <<"request_timeout">> => #option{type = integer, + validate = non_negative}, + <<"tls">> => #section{items = tls_items(), + format = {kv, http_opts}} + } + }; +outgoing_pool_connection(<<"ldap">>) -> + #section{ + items = #{<<"host">> => #option{type = string, + validate = non_empty}, + <<"port">> => #option{type = integer, + validate = port}, + <<"rootdn">> => #option{type = string}, + <<"password">> => #option{type = string}, + <<"encrypt">> => #option{type = atom, + validate = {enum, [none, tls]}}, + <<"servers">> => #list{items = #option{type = string}}, + <<"connect_interval">> => #option{type = integer, + validate = positive}, + <<"tls">> => #section{items = tls_items(), + format = {kv, tls_options}} + } + }; +outgoing_pool_connection(<<"rabbit">>) -> + #section{ + items = #{<<"amqp_host">> => #option{type = string, + validate = non_empty}, + <<"amqp_port">> => #option{type = integer, + validate = port}, + <<"amqp_username">> => #option{type = string, + validate = non_empty}, + <<"amqp_password">> => #option{type = string, + validate = non_empty}, + <<"confirms_enabled">> => #option{type = boolean}, + <<"max_worker_queue_len">> => #option{type = int_or_infinity, + validate = non_negative} + } + }; +outgoing_pool_connection(<<"rdbms">>) -> + #section{ + items = #{<<"driver">> => #option{type = atom, + validate = {enum, [odbc, pgsql, mysql]}}, + <<"keepalive_interval">> => #option{type = integer, + validate = positive}, + + % odbc + <<"settings">> => #option{type = string}, + + % mysql, pgsql + <<"host">> => #option{type = string, + validate = non_empty}, + <<"database">> => #option{type = string, + validate = non_empty}, + <<"username">> => #option{type = string, + validate = non_empty}, + <<"password">> => #option{type = string, + validate = non_empty}, + <<"port">> => #option{type = integer, + validate = port}, + <<"tls">> => sql_tls() + }, + process = fun ?MODULE:process_rdbms_connection/1 + }; +outgoing_pool_connection(<<"redis">>) -> + #section{ + items = #{<<"host">> => #option{type = string, + validate = non_empty}, + <<"port">> => #option{type = integer, + validate = port}, + <<"database">> => #option{type = integer, + validate = non_negative}, + <<"password">> => #option{type = string} + } + }; +outgoing_pool_connection(<<"riak">>) -> + #section{ + items = #{<<"address">> => #option{type = string, + validate = non_empty}, + <<"port">> => #option{type = integer, + validate = port}, + <<"credentials">> => riak_credentials(), + <<"tls">> => #section{items = tls_items(), + process = fun ?MODULE:process_riak_tls/1, + format = none}} + }. + +cassandra_server() -> + #section{ + items = #{<<"ip_address">> => #option{type = string, + validate = non_empty}, + <<"port">> => #option{type = integer, + validate = port}}, + required = [<<"ip_address">>], + process = fun ?MODULE:process_cassandra_server/1 + }. + +%% path: outgoing_pools.cassandra.*.connection.auth.plain +cassandra_auth_plain() -> + #section{ + items = #{<<"username">> => #option{type = binary}, + <<"password">> => #option{type = binary}}, + required = all + }. + +%% path: outgoing_pools.riak.*.connection.credentials +riak_credentials() -> + #section{ + items = #{<<"user">> => #option{type = string, + validate = non_empty}, + <<"password">> => #option{type = string, + validate = non_empty}}, + required = all, + process = fun ?MODULE:process_riak_credentials/1, + format = prepend_key + }. + +sql_tls() -> + Items = tls_items(), + #section{ + items = Items#{<<"required">> => #option{type = boolean}} + }. + +tls_items() -> + #{<<"verify_peer">> => #option{type = boolean, + process = fun ?MODULE:process_verify_peer/1, + format = {kv, verify}}, + <<"certfile">> => #option{type = string, + validate = non_empty}, + <<"cacertfile">> => #option{type = string, + validate = non_empty}, + <<"dhfile">> => #option{type = string, + validate = non_empty}, + <<"keyfile">> => #option{type = string, + validate = non_empty}, + <<"password">> => #option{type = string}, + <<"server_name_indication">> => #option{type = boolean, + process = fun ?MODULE:process_sni/1}, + <<"ciphers">> => #option{type = string}, + <<"versions">> => #list{items = #option{type = atom}} + }. + %% Callbacks for 'process' process_ctl_access_rule(KVs) -> @@ -664,3 +850,76 @@ process_ldap_uids(KVs) -> {[{attr, Attr}], []} -> Attr; {[{attr, Attr}], [{format, Format}]} -> {Attr, Format} end. + +process_pool([Tag, Type|_], KVs) -> + {[ScopeOpts, HostOpts, ConnOpts], Opts} = proplists:split(KVs, [scope, host, connection]), + Scope = pool_scope(ScopeOpts, HostOpts), + Connection = pool_connection(ConnOpts), + {b2a(Type), Scope, b2a(Tag), Opts, Connection}. + +pool_scope([{scope, single_host}], [{host, Host}]) -> Host; +pool_scope([{scope, host}], []) -> host; +pool_scope([{scope, global}], []) -> global; +pool_scope([], []) -> global. + +pool_connection([{connection, Opts}]) -> Opts; +pool_connection([]) -> []. + +process_cassandra_server(KVs) -> + {[[{ip_address, IPAddr}]], Opts} = proplists:split(KVs, [ip_address]), + case Opts of + [] -> IPAddr; + [{port, Port}] -> {IPAddr, Port} + end. + +process_cassandra_auth([{plain, KVs}]) -> + {[[{username, User}], [{password, Pass}]], []} = proplists:split(KVs, [username, password]), + {cqerl_auth_plain_handler, [{User, Pass}]}. + +process_rdbms_connection(KVs) -> + {[[{driver, Driver}], KeepaliveIntervalOpts], Opts} = + proplists:split(KVs, [driver, keepalive_interval]), + [{server, rdbms_server(Driver, Opts)} | KeepaliveIntervalOpts]. + +rdbms_server(odbc, Opts) -> + [{settings, Settings}] = Opts, + Settings; +rdbms_server(Driver, Opts) -> + {[[{host, Host}], [{database, DB}], [{username, User}], [{password, Pass}], + PortOpts, TLSOpts], []} = + proplists:split(Opts, [host, database, username, password, port, tls]), + list_to_tuple([Driver, Host] ++ db_port(PortOpts) ++ + [DB, User, Pass] ++ db_tls(Driver, TLSOpts)). + +db_port([{port, Port}]) -> [Port]; +db_port([]) -> []. + +db_tls(Driver, [{tls, KVs}]) -> + {[ModeOpts], Opts} = proplists:split(KVs, [required]), + [ssl_mode(Driver, ModeOpts) ++ ssl_opts(Driver, Opts)]; +db_tls(_, []) -> []. + +ssl_mode(pgsql, [{required, true}]) -> [{ssl, required}]; +ssl_mode(pgsql, [{required, false}]) -> [{ssl, true}]; +ssl_mode(pgsql, []) -> [{ssl, true}]; +ssl_mode(mysql, []) -> []. + +ssl_opts(pgsql, []) -> []; +ssl_opts(pgsql, Opts) -> [{ssl_opts, Opts}]; +ssl_opts(mysql, Opts) -> Opts. + +process_riak_tls(KVs) -> + {[CACertFileOpts], SSLOpts} = proplists:split(KVs, [cacertfile]), + riak_ssl(SSLOpts) ++ CACertFileOpts. + +riak_ssl([]) -> []; +riak_ssl(Opts) -> [{ssl_opts, Opts}]. + +process_riak_credentials(KVs) -> + {[[{user, User}], [{password, Pass}]], []} = proplists:split(KVs, [user, password]), + {User, Pass}. + +b2a(B) -> binary_to_atom(B, utf8). + +wpool_strategy_values() -> + [best_worker, random_worker, next_worker, available_worker, next_available_worker]. diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index 2b0049124e..4dc803482b 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -16,128 +16,6 @@ validate(Path, [F]) when is_function(F, 1) -> validate(Path, F(?HOST)); -%% outgoing_pools -validate([_Tag, _Type, <<"outgoing_pools">>], - [{TypeAtom, Scope, TagAtom, _Options, _ConnectionOptions}]) -> - validate_enum(TypeAtom, [redis, riak, http, rdbms, cassandra, elastic, generic, rabbit, ldap]), - validate_pool_scope(Scope), - validate_non_empty_atom(TagAtom); -validate([<<"workers">>, _Tag, _Type, <<"outgoing_pools">>], - [{workers, Value}]) -> - validate_positive_integer(Value); -validate([<<"strategy">>, _Tag, _Type, <<"outgoing_pools">>], - [{strategy, Value}]) -> - validate_wpool_strategy(Value); -validate([<<"call_timeout">>, _Tag, _Type, <<"outgoing_pools">>], - [{call_timeout, Value}]) -> - validate_positive_integer(Value); -validate([<<"keepalive_interval">>, _Conn, _Tag, <<"rdbms">>, <<"outgoing_pools">>], - [{keepalive_interval, Value}]) -> - validate_positive_integer(Value); -validate([{connection, Driver}, _Tag, <<"rdbms">>, <<"outgoing_pools">>], - [_Value]) -> - validate_enum(Driver, [odbc, pgsql, mysql]); -validate([Key, {connection, _}, _Tag, <<"rdbms">>, <<"outgoing_pools">>], - [{_, Value}]) when Key =:= <<"host">>; - Key =:= <<"database">>; - Key =:= <<"username">>; - Key =:= <<"password">> -> - validate_non_empty_string(Value); -validate([<<"port">>, {connection, _}, _Tag, <<"rdbms">>, <<"outgoing_pools">>], - [{port, Value}]) -> - validate_port(Value); -validate([<<"host">>, _Conn, _Tag, <<"http">>, <<"outgoing_pools">>], - [{server, Value}]) -> - validate_non_empty_string(Value); -validate([<<"path_prefix">>, _Conn, _Tag, <<"http">>, <<"outgoing_pools">>], - [{path_prefix, Value}]) -> - validate_non_empty_string(Value); -validate([<<"request_timeout">>, _Conn, _Tag, <<"http">>, <<"outgoing_pools">>], - [{request_timeout, Value}]) -> - validate_non_negative_integer(Value); -validate([<<"host">>, _Conn, _Tag, <<"redis">>, <<"outgoing_pools">>], - [{host, Value}]) -> - validate_non_empty_string(Value); -validate([<<"port">>, _Conn, _Tag, <<"redis">>, <<"outgoing_pools">>], - [{port, Value}]) -> - validate_port(Value); -validate([<<"database">>, _Conn, _Tag, <<"redis">>, <<"outgoing_pools">>], - [{database, Value}]) -> - validate_non_negative_integer(Value); -validate([<<"password">>, _Conn, _Tag, <<"redis">>, <<"outgoing_pools">>], - [{host, Value}]) -> - validate_string(Value); -validate([<<"address">>, _Conn, _Tag, <<"riak">>, <<"outgoing_pools">>], - [{address, Value}]) -> - validate_non_empty_string(Value); -validate([<<"port">>, _Conn, _Tag, <<"riak">>, <<"outgoing_pools">>], - [{port, Value}]) -> - validate_port(Value); -validate([<<"credentials">>, _Conn, _Tag, <<"riak">>, <<"outgoing_pools">>], - [{credentials, User, Password}]) -> - validate_non_empty_string(User), - validate_non_empty_string(Password); -validate([<<"cacertfile">>, _Conn, _Tag, <<"riak">>, <<"outgoing_pools">>], - [{cacertfile, Value}]) -> - validate_non_empty_string(Value); -validate([<<"certfile">>, _Conn, _Tag, <<"riak">>, <<"outgoing_pools">>], - [{certfile, Value}]) -> - validate_non_empty_string(Value); -validate([<<"keyfile">>, _Conn, _Tag, <<"riak">>, <<"outgoing_pools">>], - [{keyfile, Value}]) -> - validate_non_empty_string(Value); -validate([<<"servers">>, _Conn, _Tag, <<"cassandra">>, <<"outgoing_pools">>], - [{servers, Value}]) -> - [{validate_non_empty_string(Host), validate_port(Port)} || {Host, Port} <- Value]; -validate([<<"keyspace">>, _Conn, _Tag, <<"cassandra">>, <<"outgoing_pools">>], - [{keyspace, Value}]) -> - validate_non_empty_string(Value); -validate([<<"host">>, _Conn, _Tag, <<"elastic">>, <<"outgoing_pools">>], - [{host, Value}]) -> - validate_non_empty_string(Value); -validate([<<"port">>, _Conn, _Tag, <<"elastic">>, <<"outgoing_pools">>], - [{host, Value}]) -> - validate_port(Value); -validate([<<"amqp_host">>, _Conn, _Tag, <<"rabbit">>, <<"outgoing_pools">>], - [{amqp_host, Value}]) -> - validate_non_empty_string(Value); -validate([<<"amqp_port">>, _Conn, _Tag, <<"rabbit">>, <<"outgoing_pools">>], - [{amqp_port, Value}]) -> - validate_port(Value); -validate([<<"amqp_username">>, _Conn, _Tag, <<"rabbit">>, <<"outgoing_pools">>], - [{amqp_username, Value}]) -> - validate_non_empty_string(Value); -validate([<<"amqp_password">>, _Conn, _Tag, <<"rabbit">>, <<"outgoing_pools">>], - [{amqp_password, Value}]) -> - validate_non_empty_string(Value); -validate([<<"confirms_enabled">>, _Conn, _Tag, <<"rabbit">>, <<"outgoing_pools">>], - [{confirms_enabled, Value}]) -> - validate_boolean(Value); -validate([<<"max_worker_queue_len">>, _Conn, _Tag, <<"rabbit">>, <<"outgoing_pools">>], - [{max_worker_queue_len, Value}]) -> - validate_non_negative_integer_or_infinity(Value); -validate([<<"host">>, _Conn, _Tag, <<"ldap">>, <<"outgoing_pools">>], - [{host, Value}]) -> - validate_non_empty_string(Value); -validate([<<"port">>, _Conn, _Tag, <<"ldap">>, <<"outgoing_pools">>], - [{port, Value}]) -> - validate_port(Value); -validate([<<"servers">>, _Conn, _Tag, <<"ldap">>, <<"outgoing_pools">>], - [{servers, Value}]) -> - [validate_non_empty_string(Server) || Server <- Value]; -validate([<<"encrypt">>, _Conn, _Tag, <<"ldap">>, <<"outgoing_pools">>], - [{encrypt, Value}]) -> - validate_enum(Value, [tls, none]); -validate([<<"rootdn">>, _Conn, _Tag, <<"ldap">>, <<"outgoing_pools">>], - [{rootdn, Value}]) -> - validate_string(Value); -validate([<<"password">>, _Conn, _Tag, <<"ldap">>, <<"outgoing_pools">>], - [{password, Value}]) -> - validate_string(Value); -validate([<<"connect_interval">>, _Conn, _Tag, <<"ldap">>, <<"outgoing_pools">>], - [{connect_interval, Value}]) -> - validate_positive_integer(Value); - %% shaper validate([_, <<"shaper">>|Path], [#config{value = {maxrate, Value}}]) -> @@ -1439,9 +1317,6 @@ validate_non_empty_string(Value) when is_list(Value), Value =/= "" -> ok. validate_non_empty_list(Value) when is_list(Value), Value =/= [] -> ok. -validate_pool_scope(Value) when is_binary(Value) -> validate_non_empty_binary(Value); -validate_pool_scope(Value) -> validate_enum(Value, [host, global]). - validate_root_or_host_config([]) -> ok; validate_root_or_host_config([{host, _}, <<"host_config">>]) -> ok. From 380334277113bda9be3ffa9f2c8eae3816285387 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Mon, 23 Nov 2020 10:12:33 +0100 Subject: [PATCH 029/104] Amend the tests after fixing the riak TLS options - Put 'cacertfile' in the 'tls' section for tests. It worked outside 'tls' before, but it was against the docs. - Make sure the TLS options except 'cacertfile' are put in 'ssl_opts'. - Update the format of riak options in test.config. Most values there did not follow the docs. --- big_tests/test.config | 8 +++---- test/config_parser_SUITE.erl | 24 +++++++------------ .../outgoing_pools.options | 8 +++++-- .../outgoing_pools.toml | 19 +++++++++------ 4 files changed, 30 insertions(+), 29 deletions(-) diff --git a/big_tests/test.config b/big_tests/test.config index dcd822c7da..1abec315d1 100644 --- a/big_tests/test.config +++ b/big_tests/test.config @@ -356,11 +356,11 @@ strategy = \"next_worker\" connection.address = \"127.0.0.1\" connection.port = 8087 - connection.username = \"ejabberd\" - connection.password = \"mongooseim_secret\" - connection.tls.ciphers = [\"AES256-SHA\", \"DHE-RSA-AES128-SHA256\"] + connection.credentials.user = \"ejabberd\" + connection.credentials.password = \"mongooseim_secret\" + connection.tls.ciphers = \"AES256-SHA:DHE-RSA-AES128-SHA256\" connection.tls.server_name_indication = false - connection.cacertfile = \"priv/ssl/cacert.pem\""}, + connection.tls.cacertfile = \"priv/ssl/cacert.pem\""}, {mod_last, "[modules.mod_last] backend = \"riak\""}, {mod_privacy, "[modules.mod_privacy] diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index 280f0c2f13..dde84eff8d 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -136,8 +136,6 @@ groups() -> pool_riak_port, pool_riak_credentials, pool_riak_cacertfile, - pool_riak_certfile, - pool_riak_keyfile, pool_riak_tls, pool_cassandra_servers, pool_cassandra_keyspace, @@ -1054,21 +1052,15 @@ pool_riak_cacertfile(_Config) -> parse_pool_conn(<<"riak">>, #{<<"tls">> => #{<<"cacertfile">> => <<"path/to/cacert.pem">>}})), ?err(parse_pool_conn(<<"riak">>, #{<<"cacertfile">> => <<"">>})). -pool_riak_certfile(_Config) -> - ?eq(pool_config({riak, global, default, [], [{certfile, "path/to/cert.pem"}]}), - parse_pool_conn(<<"riak">>, #{<<"tls">> => #{<<"certfile">> => <<"path/to/cert.pem">>}})), - ?err(parse_pool_conn(<<"riak">>, #{<<"certfile">> => <<"">>})). - -pool_riak_keyfile(_Config) -> - ?eq(pool_config({riak, global, default, [], [{keyfile, "path/to/key.pem"}]}), - parse_pool_conn(<<"riak">>, #{<<"tls">> => #{<<"keyfile">> => <<"path/to/key.pem">>}})), - ?err(parse_pool_conn(<<"riak">>, #{<<"keyfile">> => <<"">>})). - pool_riak_tls(_Config) -> - %% one option tested here as they are all checked by 'listen_tls_*' tests - ?eq(pool_config({riak, global, default, [], [{ssl_opts, [{dhfile, "cert.pem"} - ]}]}), - parse_pool_conn(<<"riak">>, #{<<"tls">> => #{<<"dhfile">> => <<"cert.pem">>}})), + %% make sure these options are not extracted out of 'ssl_opts' + %% all the TLS options are checked by 'listen_tls_*' tests + ?eq(pool_config({riak, global, default, [], [{ssl_opts, [{certfile, "path/to/cert.pem"}, + {dhfile, "cert.pem"}, + {keyfile, "path/to/key.pem"}]}]}), + parse_pool_conn(<<"riak">>, #{<<"tls">> => #{<<"certfile">> => <<"path/to/cert.pem">>, + <<"dhfile">> => <<"cert.pem">>, + <<"keyfile">> => <<"path/to/key.pem">>}})), ?err(parse_pool_conn(<<"riak">>, #{<<"tls">> => #{<<"dhfile">> => true}})), ?err(parse_pool_conn(<<"riak">>, #{<<"tls">> => <<"secure">>})). diff --git a/test/config_parser_SUITE_data/outgoing_pools.options b/test/config_parser_SUITE_data/outgoing_pools.options index e4c19d31fe..1d4cf1ebd8 100644 --- a/test/config_parser_SUITE_data/outgoing_pools.options +++ b/test/config_parser_SUITE_data/outgoing_pools.options @@ -38,6 +38,10 @@ {riak,global,default, [{strategy,next_worker},{workers,20}], [{address,"127.0.0.1"}, - {cacertfile,"path/to/cacert.pem"}, {credentials,"username","pass"}, - {port,8087}]}]}. + {port,8087}, + {ssl_opts, + [{certfile,"path/to/cert.pem"}, + {keyfile,"path/to/key.pem"}, + {verify,verify_peer}]}, + {cacertfile,"path/to/cacert.pem"}]}]}. diff --git a/test/config_parser_SUITE_data/outgoing_pools.toml b/test/config_parser_SUITE_data/outgoing_pools.toml index 36ba211a7c..409b34f994 100644 --- a/test/config_parser_SUITE_data/outgoing_pools.toml +++ b/test/config_parser_SUITE_data/outgoing_pools.toml @@ -36,13 +36,18 @@ request_timeout = 2000 [outgoing_pools.riak.default] - scope = "global" - workers = 20 - strategy = "next_worker" - connection.address = "127.0.0.1" - connection.port = 8087 - connection.credentials = {user = "username", password = "pass"} - connection.cacertfile = "path/to/cacert.pem" + scope = "global" + workers = 20 + strategy = "next_worker" + + [outgoing_pools.riak.default.connection] + address = "127.0.0.1" + port = 8087 + credentials = {user = "username", password = "pass"} + tls.certfile = "path/to/cert.pem" + tls.keyfile = "path/to/key.pem" + tls.cacertfile = "path/to/cacert.pem" + tls.verify_peer = true [outgoing_pools.cassandra.default] scope = "global" From 391607ca6b42c880be5326b19dcb5291295d8fae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Mon, 23 Nov 2020 13:52:27 +0100 Subject: [PATCH 030/104] Update the docs after the format change of 'tls.ciphers' --- doc/advanced-configuration/outgoing-connections.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/doc/advanced-configuration/outgoing-connections.md b/doc/advanced-configuration/outgoing-connections.md index d53feb2654..04297d7f0e 100644 --- a/doc/advanced-configuration/outgoing-connections.md +++ b/doc/advanced-configuration/outgoing-connections.md @@ -9,7 +9,6 @@ The interface for outgoing connections management was unified and is now availab * `rdbms` - pool of connections to an RDBMS database * `rabbit` - pool of connections to a RabbitMQ server * `ldap` - pool of connections to an LDAP server -* `generic` - pool of generic workers not associated directly with a particular connection * **Syntax:** Each pool is specified in a subsection starting with `[outgoing_pools.type.tag]`, where `type` is one of available connection types and `tag` is an arbitrary value uniquely identifying the pool within its type. This allows you to create multiple dedicated pools of the same type. @@ -419,11 +418,11 @@ Path to the X509 PEM file with the private key. Password to the X509 PEM file with the private key. #### `outgoing_pools.*.*.connection.tls.ciphers` -* **Syntax:** array of tables with the following keys: `cipher`, `key_exchange`, `mac`, `prf` and string values. +* **Syntax:** string with the OpenSSL cipher suite specification * **Default:** not set, all supported cipher suites are accepted -* **Example:** `tls.ciphers = "[{cipher = "aes_25_gcm", key_exchange = "any", mac = "aead", "prf = sha384"}]"` +* **Example:** `tls.ciphers = "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384"` -Cipher suites to use. For allowed values, see the [Erlang/OTP SSL documentation](https://erlang.org/doc/man/ssl.html#type-ciphers) +Cipher suites to use. Please refer to the [OpenSSL documentation](http://www.openssl.org/docs/man1.0.2/apps/ciphers.html) for the cipher string format. For allowed values, see the [Erlang/OTP SSL documentation](https://erlang.org/doc/man/ssl.html#type-ciphers). #### `outgoing_pools.*.*.connection.tls.versions` * **Syntax:** list of strings From 1493a4024d042f0e05f63a948b50abf5d2c5e464 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Mon, 23 Nov 2020 10:14:34 +0100 Subject: [PATCH 031/104] Fix code formatting issues Regarding TOML, make the format consistent for all pools. There are no official guidelines for TOML indentation, but let's keep it consistent across all config files. --- test/config_parser_SUITE.erl | 14 ++++-- .../mongooseim-pgsql.toml | 2 +- .../outgoing_pools.toml | 47 ++++++++++--------- 3 files changed, 36 insertions(+), 27 deletions(-) diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index dde84eff8d..72723dc7a9 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -1045,11 +1045,12 @@ pool_riak_credentials(_Config) -> parse_pool_conn(<<"riak">>, #{<<"credentials">> => #{<<"user">> => <<"user">>, <<"password">> => <<"pass">>}})), ?err(parse_pool_conn(<<"riak">>, #{<<"credentials">> => #{<<"user">> => <<"user">>}})), - ?err(parse_pool_conn(<<"riak">>, #{<<"credentials">> => #{<<"user">> => <<"">>, <<"password">> => 011001}})). + ?err(parse_pool_conn(<<"riak">>, #{<<"credentials">> => + #{<<"user">> => <<"">>, <<"password">> => 011001}})). pool_riak_cacertfile(_Config) -> - ?eq(pool_config({riak, global, default, [], [{cacertfile, "path/to/cacert.pem"}]}), - parse_pool_conn(<<"riak">>, #{<<"tls">> => #{<<"cacertfile">> => <<"path/to/cacert.pem">>}})), + ?eq(pool_config({riak, global, default, [], [{cacertfile, "cacert.pem"}]}), + parse_pool_conn(<<"riak">>, #{<<"tls">> => #{<<"cacertfile">> => <<"cacert.pem">>}})), ?err(parse_pool_conn(<<"riak">>, #{<<"cacertfile">> => <<"">>})). pool_riak_tls(_Config) -> @@ -1066,7 +1067,8 @@ pool_riak_tls(_Config) -> pool_cassandra_servers(_Config) -> ?eq(pool_config({cassandra, global, default, [], - [{servers, [{"cassandra_server1.example.com", 9042}, {"cassandra_server2.example.com", 9042}]}]}), + [{servers, [{"cassandra_server1.example.com", 9042}, + {"cassandra_server2.example.com", 9042}]}]}), parse_pool_conn(<<"cassandra">>, #{<<"servers">> => [ #{<<"ip_address">> => <<"cassandra_server1.example.com">>, <<"port">> => 9042}, #{<<"ip_address">> => <<"cassandra_server2.example.com">>, <<"port">> => 9042} @@ -1080,7 +1082,9 @@ pool_cassandra_keyspace(_Config) -> ?err(parse_pool_conn(<<"cassandra">>, #{<<"keyspace">> => <<"">>})). pool_cassandra_auth(_Config) -> - ?eq(pool_config({cassandra, global, default, [], [{auth, {cqerl_auth_plain_handler, [{<<"auser">>, <<"secretpass">>}]}}]}), + ?eq(pool_config({cassandra, global, default, [], [{auth, {cqerl_auth_plain_handler, + [{<<"auser">>, <<"secretpass">>}] + }}]}), parse_pool_conn(<<"cassandra">>, #{<<"auth">> => #{<<"plain">> => #{<<"username">> => <<"auser">>, <<"password">> => <<"secretpass">>}}})), diff --git a/test/config_parser_SUITE_data/mongooseim-pgsql.toml b/test/config_parser_SUITE_data/mongooseim-pgsql.toml index aaca319546..dc42bd6c67 100644 --- a/test/config_parser_SUITE_data/mongooseim-pgsql.toml +++ b/test/config_parser_SUITE_data/mongooseim-pgsql.toml @@ -46,7 +46,7 @@ [[listen.http.handlers.mongoose_api_client]] host = "localhost" path = "/api/contacts/{:jid}" - + [[listen.http.handlers.mod_bosh]] host = "_" path = "/http-bind" diff --git a/test/config_parser_SUITE_data/outgoing_pools.toml b/test/config_parser_SUITE_data/outgoing_pools.toml index 409b34f994..9b08c74984 100644 --- a/test/config_parser_SUITE_data/outgoing_pools.toml +++ b/test/config_parser_SUITE_data/outgoing_pools.toml @@ -50,31 +50,36 @@ tls.verify_peer = true [outgoing_pools.cassandra.default] - scope = "global" - connection.servers = [ - {ip_address = "cassandra_server1.example.com", port = 9042}, - {ip_address = "cassandra_server2.example.com", port = 9042} - ] - connection.keyspace = "big_mongooseim" + scope = "global" + [outgoing_pools.cassandra.default.connection] + servers = [ + {ip_address = "cassandra_server1.example.com", port = 9042}, + {ip_address = "cassandra_server2.example.com", port = 9042} + ] + keyspace = "big_mongooseim" [outgoing_pools.elastic.default] - scope = "global" - connection.host = "localhost" + scope = "global" + connection.host = "localhost" [outgoing_pools.rabbit.event_pusher] - scope = "host" - workers = 20 - connection.amqp_host = "localhost" - connection.amqp_port = 5672 - connection.amqp_username = "guest" - connection.amqp_password = "guest" - connection.confirms_enabled = true - connection.max_worker_queue_len = 100 + scope = "host" + workers = 20 + + [outgoing_pools.rabbit.event_pusher.connection] + amqp_host = "localhost" + amqp_port = 5672 + amqp_username = "guest" + amqp_password = "guest" + confirms_enabled = true + max_worker_queue_len = 100 [outgoing_pools.ldap.default] - scope = "host" - workers = 5 - connection.servers = ["ldap-server.example.com"] - connection.rootdn = "cn=admin,dc=example,dc=com" - connection.password = "ldap-admin-password" + scope = "host" + workers = 5 + + [outgoing_pools.ldap.default.connection] + servers = ["ldap-server.example.com"] + rootdn = "cn=admin,dc=example,dc=com" + password = "ldap-admin-password" From 4e4097f34e2745b853740c1fb3e6006d8bbda2cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Tue, 24 Nov 2020 10:19:54 +0100 Subject: [PATCH 032/104] Make the specs for 'shaper', 'acl' and 'access' declarative - The host-or-global semantic requires separate handling - Acl records require separate handling - Access rules can have integers or atoms for values. In the future this can be solved by dividing them into three categories: - policy rules: atom values (allow, deny, default) - shaper rules: atom values (shaper names) - limit rules: integer values It is not done now as this work is not directly related to the current task and it is not trivial. --- src/config/mongoose_config_parser_toml.erl | 80 ++----------- src/config/mongoose_config_spec.erl | 107 +++++++++++++++++- src/config/mongoose_config_validator_toml.erl | 6 - 3 files changed, 116 insertions(+), 77 deletions(-) diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index 1b2df024ee..8cc19976eb 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -937,64 +937,6 @@ welcome_message([<<"subject">>|_], Value) -> welcome_message([<<"body">>|_], Value) -> [{body, b2l(Value)}]. -%% path: (host_config[].)shaper.* --spec process_shaper(path(), toml_section()) -> [config()]. -process_shaper([Name, _|Path], #{<<"max_rate">> := MaxRate}) -> - [#config{key = {shaper, b2a(Name), host(Path)}, value = {maxrate, MaxRate}}]. - -%% path: (host_config[].)acl.* --spec process_acl(path(), toml_value()) -> [config()]. -process_acl([item, ACLName, _|Path], Content) -> - [acl:to_record(host(Path), b2a(ACLName), acl_data(Content))]. - --spec acl_data(toml_value()) -> option(). -acl_data(#{<<"match">> := <<"all">>}) -> all; -acl_data(#{<<"match">> := <<"none">>}) -> none; -acl_data(M) -> - {AclName, AclKeys} = find_acl(M, lists:sort(maps:keys(M)), acl_keys()), - list_to_tuple([AclName | lists:map(fun(K) -> maps:get(K, M) end, AclKeys)]). - -find_acl(M, SortedMapKeys, [{AclName, AclKeys}|Rest]) -> - case lists:sort(AclKeys) of - SortedMapKeys -> {AclName, AclKeys}; - _ -> find_acl(M, SortedMapKeys, Rest) - end. - -acl_keys() -> - [{user, [<<"user">>, <<"server">>]}, - {user, [<<"user">>]}, - {server, [<<"server">>]}, - {resource, [<<"resource">>]}, - {user_regexp, [<<"user_regexp">>, <<"server">>]}, - {node_regexp, [<<"user_regexp">>, <<"server_regexp">>]}, - {user_regexp, [<<"user_regexp">>]}, - {server_regexp, [<<"server_regexp">>]}, - {resource_regexp, [<<"resource_regexp">>]}, - {user_glob, [<<"user_glob">>, <<"server">>]}, - {node_glob, [<<"user_glob">>, <<"server_glob">>]}, - {user_glob, [<<"user_glob">>]}, - {server_glob, [<<"server_glob">>]}, - {resource_glob, [<<"resource_glob">>]} - ]. - -%% path: (host_config[].)access.* --spec process_access_rule(path(), toml_value()) -> [config()]. -process_access_rule([Name, _|HostPath] = Path, Contents) -> - Rules = parse_list(Path, Contents), - [#config{key = {access, b2a(Name), host(HostPath)}, value = Rules}]. - -%% path: (host_config[].)access.*[] --spec process_access_rule_item(path(), toml_section()) -> [option()]. -process_access_rule_item(_, #{<<"acl">> := ACL, <<"value">> := Value}) -> - [{access_rule_value(Value), b2a(ACL)}]. - -host([]) -> global; -host([{host, Host}, _]) -> Host. - --spec access_rule_value(toml_value()) -> option(). -access_rule_value(B) when is_binary(B) -> b2a(B); -access_rule_value(V) -> V. - %% path: (host_config[].)s2s.* -spec process_s2s_option(path(), toml_value()) -> config_list(). process_s2s_option([<<"dns">>|_] = Path, V) -> @@ -1231,6 +1173,8 @@ convert(V, string) -> binary_to_list(V); convert(V, atom) -> b2a(V); convert(<<"infinity">>, int_or_infinity) -> infinity; %% TODO maybe use TOML '+inf' convert(V, int_or_infinity) -> V; +convert(V, int_or_atom) when is_integer(V) -> V; +convert(V, int_or_atom) -> b2a(V); convert(V, integer) -> V. format_spec(#section{format = Format}) -> Format; @@ -1253,6 +1197,10 @@ format(Path, V, {host_local_config, Key}) -> format(Path, V, {local_config, Key}) -> global = get_host(Path), [#local_config{key = Key, value = V}]; +format([Key|_] = Path, V, {host_or_global_config, Tag}) -> + [#config{key = {Tag, b2a(Key), get_host(Path)}, value = V}]; +format([item, Key|_] = Path, V, host_or_global_acl) -> + [acl:to_record(get_host(Path), b2a(Key), V)]; format(Path, V, {config, Key}) -> global = get_host(Path), [#config{key = Key, value = V}]; @@ -1318,10 +1266,10 @@ node_to_string(Node) -> [binary_to_list(Node)]. -spec handler(path()) -> fun((path(), toml_value()) -> option()) | mongoose_config_spec:config_node(). handler([]) -> fun parse_root/2; -handler([Section]) when Section =/= <<"general">>, - Section =/= <<"listen">>, - Section =/= <<"auth">>, - Section =/= <<"outgoing_pools">> -> fun process_section/2; +handler([Section]) when Section =:= <<"services">>; + Section =:= <<"modules">>; + Section =:= <<"s2s">>; + Section =:= <<"host_config">> -> fun process_section/2; %% services handler([_, <<"services">>]) -> fun process_service/2; @@ -1437,14 +1385,6 @@ handler([_, <<"ldap_binary_search_fields">>, <<"mod_vcard">>, <<"modules">>]) -> handler([_, <<"submods">>, <<"service_admin_extra">>, <<"services">>]) -> fun service_admin_extra_submods/2; - -%% shaper, acl, access -handler([_, <<"shaper">>]) -> fun process_shaper/2; -handler([_, <<"acl">>]) -> fun parse_list/2; -handler([_, _, <<"acl">>]) -> fun process_acl/2; -handler([_, <<"access">>]) -> fun process_access_rule/2; -handler([_, _, <<"access">>]) -> fun process_access_rule_item/2; - %% s2s handler([_, <<"s2s">>]) -> fun process_s2s_option/2; handler([_, <<"dns">>, <<"s2s">>]) -> fun s2s_dns_opt/2; diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index d846741de4..b8d5d505be 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -32,7 +32,10 @@ root() -> items = #{<<"general">> => general(), <<"listen">> => listen(), <<"auth">> => auth(), - <<"outgoing_pools">> => outgoing_pools() + <<"outgoing_pools">> => outgoing_pools(), + <<"shaper">> => shaper(), + <<"acl">> => acl(), + <<"access">> => access() }, required = [<<"general">>], format = none @@ -670,6 +673,7 @@ riak_credentials() -> format = prepend_key }. +%% path: outgoing_pools.rdbms.*.connection.tls sql_tls() -> Items = tls_items(), #section{ @@ -695,6 +699,72 @@ tls_items() -> <<"versions">> => #list{items = #option{type = atom}} }. +%% path: (host_config[].)shaper +shaper() -> + #section{ + items = #{default => + #section{ + items = #{<<"max_rate">> => #option{type = integer, + validate = positive, + format = {kv, maxrate}}}, + required = all, + process = fun ?MODULE:process_shaper/1, + format = {host_or_global_config, shaper} + } + }, + validate_keys = non_empty, + format = none + }. + +%% path: (host_config[].)acl +acl() -> + #section{ + items = #{default => #list{items = acl_item(), + format = none} + }, + format = none + }. + +%% path: (host_config[].)acl.*[] +acl_item() -> + #section{ + items = #{<<"match">> => #option{type = atom, + validate = {enum, [all, none]}}, + <<"user">> => #option{type = string}, + <<"server">> => #option{type = string}, + <<"resource">> => #option{type = string}, + <<"user_regexp">> => #option{type = string}, + <<"server_regexp">> => #option{type = string}, + <<"resource_regexp">> => #option{type = string}, + <<"user_glob">> => #option{type = string}, + <<"server_glob">> => #option{type = string}, + <<"resource_glob">> => #option{type = string} + }, + validate_keys = non_empty, + process = fun ?MODULE:process_acl_item/1, + format = host_or_global_acl + }. + +%% path: (host_config[].)access +access() -> + #section{ + items = #{default => #list{items = access_rule_item(), + format = {host_or_global_config, access}} + }, + format = none + }. + +%% path: (host_config[].)access.*[] +access_rule_item() -> + #section{ + items = #{<<"acl">> => #option{type = atom, + validate = non_empty}, + <<"value">> => #option{type = int_or_atom} + }, + required = all, + process = fun ?MODULE:process_access_rule_item/1 + }. + %% Callbacks for 'process' process_ctl_access_rule(KVs) -> @@ -923,3 +993,38 @@ b2a(B) -> binary_to_atom(B, utf8). wpool_strategy_values() -> [best_worker, random_worker, next_worker, available_worker, next_available_worker]. + +process_shaper([MaxRate]) -> + MaxRate. + +process_acl_item([{match, V}]) -> V; +process_acl_item(KVs) -> + {AclName, AclKeys} = find_acl(KVs, lists:sort(proplists:get_keys(KVs)), acl_keys()), + list_to_tuple([AclName | lists:map(fun(K) -> proplists:get_value(K, KVs) end, AclKeys)]). + +find_acl(KVs, SortedKeys, [{AclName, AclKeys}|Rest]) -> + case lists:sort(AclKeys) of + SortedKeys -> {AclName, AclKeys}; + _ -> find_acl(KVs, SortedKeys, Rest) + end. + +acl_keys() -> + [{user, [user, server]}, + {user, [user]}, + {server, [server]}, + {resource, [resource]}, + {user_regexp, [user_regexp, server]}, + {node_regexp, [user_regexp, server_regexp]}, + {user_regexp, [user_regexp]}, + {server_regexp, [server_regexp]}, + {resource_regexp, [resource_regexp]}, + {user_glob, [user_glob, server]}, + {node_glob, [user_glob, server_glob]}, + {user_glob, [user_glob]}, + {server_glob, [server_glob]}, + {resource_glob, [resource_glob]} + ]. + +process_access_rule_item(KVs) -> + {[[{acl, Acl}], [{value, Value}]], []} = proplists:split(KVs, [acl, value]), + {Value, Acl}. diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index 4dc803482b..0b56a4f9c0 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -16,12 +16,6 @@ validate(Path, [F]) when is_function(F, 1) -> validate(Path, F(?HOST)); -%% shaper -validate([_, <<"shaper">>|Path], - [#config{value = {maxrate, Value}}]) -> - validate_root_or_host_config(Path), - validate_positive_integer(Value); - %% s2s validate([<<"timeout">>, <<"dns">>, <<"s2s">>], [{timeout, Value}]) -> From 376665ddfbd0d1e32cd531897027beb570dfa372 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Tue, 24 Nov 2020 17:30:08 +0100 Subject: [PATCH 033/104] Make the specs for 's2s' declarative - Use the 'foreach' format for lists of config records. Alternatively, a new format could be added and used per-item, but with 'foreach' the resulting KV list can be checked for unique keys automatically - see the next commit. --- src/config/mongoose_config_parser_toml.erl | 96 -------------- src/config/mongoose_config_spec.erl | 120 +++++++++++++++++- src/config/mongoose_config_validator_toml.erl | 60 +-------- 3 files changed, 121 insertions(+), 155 deletions(-) diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index 8cc19976eb..a885306f40 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -937,90 +937,6 @@ welcome_message([<<"subject">>|_], Value) -> welcome_message([<<"body">>|_], Value) -> [{body, b2l(Value)}]. -%% path: (host_config[].)s2s.* --spec process_s2s_option(path(), toml_value()) -> config_list(). -process_s2s_option([<<"dns">>|_] = Path, V) -> - [#local_config{key = s2s_dns_options, value = parse_section(Path, V)}]; -process_s2s_option([<<"outgoing">>|_] = Path, V) -> - parse_section(Path, V); -process_s2s_option([<<"use_starttls">>|_], V) -> - [#local_config{key = s2s_use_starttls, value = b2a(V)}]; -process_s2s_option([<<"certfile">>|_], V) -> - [#local_config{key = s2s_certfile, value = b2l(V)}]; -process_s2s_option([<<"default_policy">>|_], V) -> - ?HOST_F([#local_config{key = {s2s_default_policy, Host}, value = b2a(V)}]); -process_s2s_option([<<"host_policy">>|_] = Path, V) -> - parse_list(Path, V); -process_s2s_option([<<"address">>|_] = Path, V) -> - parse_list(Path, V); -process_s2s_option([<<"ciphers">>|_], V) -> - [#local_config{key = s2s_ciphers, value = b2l(V)}]; -process_s2s_option([<<"domain_certfile">>|_] = Path, V) -> - parse_list(Path, V); -process_s2s_option([<<"shared">>|_], V) -> - ?HOST_F([#local_config{key = {s2s_shared, Host}, value = V}]); -process_s2s_option([<<"max_retry_delay">>|_], V) -> - ?HOST_F([#local_config{key = {s2s_max_retry_delay, Host}, value = V}]). - -%% path: s2s.dns.* --spec s2s_dns_opt(path(), toml_value()) -> [option()]. -s2s_dns_opt([<<"timeout">>|_], Value) -> [{timeout, Value}]; -s2s_dns_opt([<<"retries">>|_], Value) -> [{retries, Value}]. - -%% path: s2s.outgoing.* --spec outgoing_s2s_opt(path(), toml_value()) -> [config()]. -outgoing_s2s_opt([<<"port">>|_], Value) -> - [#local_config{key = outgoing_s2s_port, value = Value}]; -outgoing_s2s_opt([<<"ip_versions">>|_] = Path, Value) -> - [#local_config{key = outgoing_s2s_families, value = parse_list(Path, Value)}]; -outgoing_s2s_opt([<<"connection_timeout">>|_], Value) -> - [#local_config{key = outgoing_s2s_timeout, value = int_or_infinity(Value)}]. - -%% path: s2s.outgoing.ip_versions[] --spec s2s_address_family(path(), toml_value()) -> [option()]. -s2s_address_family(_, 4) -> [ipv4]; -s2s_address_family(_, 6) -> [ipv6]. - -%% path: s2s.host_policy[] --spec s2s_host_policy(path(), toml_section()) -> config_list(). -s2s_host_policy(Path, M) -> - parse_section(Path, M, fun process_host_policy/1). - -process_host_policy(Opts) -> - {_, S2SHost} = proplists:lookup(host, Opts), - {_, Policy} = proplists:lookup(policy, Opts), - ?HOST_F([#local_config{key = {{s2s_host, S2SHost}, Host}, value = Policy}]). - -%% path: s2s.host_policy[].* --spec s2s_host_policy_opt(path(), toml_value()) -> [option()]. -s2s_host_policy_opt([<<"host">>|_], V) -> [{host, V}]; -s2s_host_policy_opt([<<"policy">>|_], V) -> [{policy, b2a(V)}]. - -%% path: s2s.address[] --spec s2s_address(path(), toml_section()) -> [config()]. -s2s_address(Path, M) -> - parse_section(Path, M, fun process_s2s_address/1). - -process_s2s_address(Opts) -> - {_, Host} = proplists:lookup(host, Opts), - {_, IPAddress} = proplists:lookup(ip_address, Opts), - Addr = case proplists:lookup(port, Opts) of - {_, Port} -> {IPAddress, Port}; - none -> IPAddress - end, - [#local_config{key = {s2s_addr, Host}, value = Addr}]. - -%% path: s2s.address[].* --spec s2s_addr_opt(path(), toml_value()) -> [option()]. -s2s_addr_opt([<<"host">>|_], V) -> [{host, V}]; -s2s_addr_opt([<<"ip_address">>|_], V) -> [{ip_address, b2l(V)}]; -s2s_addr_opt([<<"port">>|_], V) -> [{port, V}]. - -%% path: s2s.domain_certfile[] --spec s2s_domain_cert(path(), toml_section()) -> [config()]. -s2s_domain_cert(_, #{<<"domain">> := Dom, <<"certfile">> := Cert}) -> - [#local_config{key = {domain_certfile, b2l(Dom)}, value = b2l(Cert)}]. - %% path: host_config[] -spec process_host_item(path(), toml_section()) -> config_list(). process_host_item(Path, M) -> @@ -1268,7 +1184,6 @@ node_to_string(Node) -> [binary_to_list(Node)]. handler([]) -> fun parse_root/2; handler([Section]) when Section =:= <<"services">>; Section =:= <<"modules">>; - Section =:= <<"s2s">>; Section =:= <<"host_config">> -> fun process_section/2; %% services @@ -1385,17 +1300,6 @@ handler([_, <<"ldap_binary_search_fields">>, <<"mod_vcard">>, <<"modules">>]) -> handler([_, <<"submods">>, <<"service_admin_extra">>, <<"services">>]) -> fun service_admin_extra_submods/2; -%% s2s -handler([_, <<"s2s">>]) -> fun process_s2s_option/2; -handler([_, <<"dns">>, <<"s2s">>]) -> fun s2s_dns_opt/2; -handler([_, <<"outgoing">>, <<"s2s">>]) -> fun outgoing_s2s_opt/2; -handler([_, <<"ip_versions">>, <<"outgoing">>, <<"s2s">>]) -> fun s2s_address_family/2; -handler([_, <<"host_policy">>, <<"s2s">>]) -> fun s2s_host_policy/2; -handler([_, _, <<"host_policy">>, <<"s2s">>]) -> fun s2s_host_policy_opt/2; -handler([_, <<"address">>, <<"s2s">>]) -> fun s2s_address/2; -handler([_, _, <<"address">>, <<"s2s">>]) -> fun s2s_addr_opt/2; -handler([_, <<"domain_certfile">>, <<"s2s">>]) -> fun s2s_domain_cert/2; - %% host_config handler([_, <<"host_config">>]) -> fun process_host_item/2; handler([<<"auth">>, _, <<"host_config">>] = P) -> handler_for_host(P); diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index b8d5d505be..40303148ea 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -35,7 +35,8 @@ root() -> <<"outgoing_pools">> => outgoing_pools(), <<"shaper">> => shaper(), <<"acl">> => acl(), - <<"access">> => access() + <<"access">> => access(), + <<"s2s">> => s2s() }, required = [<<"general">>], format = none @@ -765,6 +766,105 @@ access_rule_item() -> process = fun ?MODULE:process_access_rule_item/1 }. +%% path: (host_config[].)s2s +s2s() -> + #section{ + items = #{<<"dns">> => s2s_dns(), + <<"outgoing">> => s2s_outgoing(), + <<"use_starttls">> => #option{type = atom, + validate = {enum, [false, optional, required, + required_trusted]}, + format = {local_config, s2s_use_starttls}}, + <<"certfile">> => #option{type = string, + validate = non_empty, + format = {local_config, s2s_certfile}}, + <<"default_policy">> => #option{type = atom, + validate = {enum, [allow, deny]}, + format = {host_local_config, s2s_default_policy}}, + <<"host_policy">> => #list{items = s2s_host_policy(), + format = {foreach, host_local_config}}, + <<"address">> => #list{items = s2s_address(), + format = {foreach, local_config}}, + <<"ciphers">> => #option{type = string, + format = {local_config, s2s_ciphers}}, + <<"domain_certfile">> => #list{items = s2s_domain_cert(), + format = {foreach, local_config}}, + <<"shared">> => #option{type = binary, + validate = non_empty, + format = {host_local_config, s2s_shared}}, + <<"max_retry_delay">> => #option{type = integer, + validate = positive, + format = {host_local_config, s2s_max_retry_delay}} + }, + format = none + }. + +%% path: (host_config[].)s2s.dns +s2s_dns() -> + #section{ + items = #{<<"timeout">> => #option{type = integer, + validate = positive}, + <<"retries">> => #option{type = integer, + validate = positive}}, + format = {local_config, s2s_dns_options} + }. + +%% path: (host_config[].)s2s.outgoing +s2s_outgoing() -> + #section{ + items = #{<<"port">> => #option{type = integer, + validate = port, + format = {local_config, outgoing_s2s_port}}, + <<"ip_versions">> => + #list{items = #option{type = integer, + validate = {enum, [4, 6]}, + process = fun ?MODULE:process_s2s_address_family/1}, + validate = unique_non_empty, + format = {local_config, outgoing_s2s_families}}, + <<"connection_timeout">> => #option{type = int_or_infinity, + validate = positive, + format = {local_config, outgoing_s2s_timeout}} + }, + format = none + }. + +%% path: (host_config[].)s2s.host_policy[] +s2s_host_policy() -> + #section{ + items = #{<<"host">> => #option{type = binary, + validate = non_empty}, + <<"policy">> => #option{type = atom, + validate = {enum, [allow, deny]}} + }, + required = all, + process = fun ?MODULE:process_s2s_host_policy/1 + }. + +%% path: (host_config[].)s2s.address[] +s2s_address() -> + #section{ + items = #{<<"host">> => #option{type = binary, + validate = non_empty}, + <<"ip_address">> => #option{type = string, + validate = ip_address}, + <<"port">> => #option{type = integer, + validate = port} + }, + required = [<<"host">>, <<"ip_address">>], + process = fun ?MODULE:process_s2s_address/1 + }. + +%% path: (host_config[].)s2s.domain_certfile[] +s2s_domain_cert() -> + #section{ + items = #{<<"domain">> => #option{type = string, + validate = non_empty}, + <<"certfile">> => #option{type = string, + validate = non_empty}}, + required = all, + process = fun ?MODULE:process_s2s_domain_cert/1 + }. + %% Callbacks for 'process' process_ctl_access_rule(KVs) -> @@ -1028,3 +1128,21 @@ acl_keys() -> process_access_rule_item(KVs) -> {[[{acl, Acl}], [{value, Value}]], []} = proplists:split(KVs, [acl, value]), {Value, Acl}. + +process_s2s_address_family(4) -> ipv4; +process_s2s_address_family(6) -> ipv6. + +process_s2s_host_policy(KVs) -> + {[[{host, S2SHost}], [{policy, Policy}]], []} = proplists:split(KVs, [host, policy]), + {{s2s_host, S2SHost}, Policy}. + +process_s2s_address(KVs) -> + {[[{host, S2SHost}], [{ip_address, IPAddr}]], Opts} = proplists:split(KVs, [host, ip_address]), + {{s2s_addr, S2SHost}, s2s_address(IPAddr, Opts)}. + +s2s_address(IPAddress, []) -> IPAddress; +s2s_address(IPAddress, [{port, Port}]) -> {IPAddress, Port}. + +process_s2s_domain_cert(KVs) -> + {[[{domain, Domain}], [{certfile, Certfile}]], []} = proplists:split(KVs, [domain, certfile]), + {{domain_certfile, Domain}, Certfile}. diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index 0b56a4f9c0..2a6d51f710 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -16,62 +16,6 @@ validate(Path, [F]) when is_function(F, 1) -> validate(Path, F(?HOST)); -%% s2s -validate([<<"timeout">>, <<"dns">>, <<"s2s">>], - [{timeout, Value}]) -> - validate_positive_integer(Value); -validate([<<"retries">>, <<"dns">>, <<"s2s">>], - [{retries, Value}]) -> - validate_positive_integer(Value); -validate([<<"port">>, <<"outgoing">>, <<"s2s">>], - [#local_config{value = Value}]) -> - validate_port(Value); -validate([<<"ip_versions">>, <<"outgoing">>, <<"s2s">>], - [#local_config{value = Value}]) -> - validate_non_empty_list(Value); -validate([<<"connection_timeout">>, <<"outgoing">>, <<"s2s">>], - [#local_config{value = Value}]) -> - validate_positive_integer_or_infinity(Value); -validate([<<"use_starttls">>, <<"s2s">>], - [#local_config{value = Value}]) -> - validate_enum(Value, [false, optional, required, required_trusted]); -validate([<<"certfile">>, <<"s2s">>], - [#local_config{value = Value}]) -> - validate_non_empty_string(Value); -validate([<<"default_policy">>, <<"s2s">>|Path], - [#local_config{value = Value}]) -> - validate_root_or_host_config(Path), - validate_enum(Value, [allow, deny]); -validate([<<"host">>, item, <<"host_policy">>, <<"s2s">>|Path], - [{host, Value}]) -> - validate_root_or_host_config(Path), - validate_non_empty_binary(Value); -validate([<<"policy">>, item, <<"host_policy">>, <<"s2s">>|Path], - [{policy, Value}]) -> - validate_root_or_host_config(Path), - validate_enum(Value, [allow, deny]); -validate([<<"host">>, item, <<"address">>, <<"s2s">>], - [{host, Value}]) -> - validate_non_empty_binary(Value); -validate([<<"ip_address">>, item, <<"address">>, <<"s2s">>], - [{ip_address, Value}]) -> - validate_ip_address(Value); -validate([<<"port">>, item, <<"address">>, <<"s2s">>], - [{port, Value}]) -> - validate_port(Value); -validate([item, <<"domain_certfile">>, <<"s2s">>], - [#local_config{key = {domain_certfile, Domain}, value = Certfile}]) -> - validate_non_empty_string(Domain), - validate_non_empty_string(Certfile); -validate([<<"shared">>, <<"s2s">>|Path], - [#local_config{value = Value}]) -> - validate_root_or_host_config(Path), - validate_non_empty_binary(Value); -validate([<<"max_retry_delay">>, <<"s2s">>|Path], - [#local_config{value = Value}]) -> - validate_root_or_host_config(Path), - validate_positive_integer(Value); - %% Services validate([item, <<"submods">>, <<"service_admin_extra">>, <<"services">>], [Value]) -> @@ -1240,8 +1184,8 @@ validate(V, _, {enum, Values}) -> validate_enum(V, Values); validate(_V, _, any) -> ok. validate_list([_|_], non_empty) -> ok; -validate_list(L = [_|_], unique_non_empty) -> - validate_unique_items(L); +validate_list(L = [_|_], unique_non_empty) -> validate_unique_items(L); +validate_list(L, unique) -> validate_unique_items(L); validate_list(L, any) when is_list(L) -> ok. validate_section([_|_], non_empty) -> ok; From 0793c9c53b9283405f2c25a6d4fdf90df06a04b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Tue, 24 Nov 2020 17:33:16 +0100 Subject: [PATCH 034/104] Ensure unique keys in 'foreach' formatting Motivation: keys are unique for sections, but for lists there are just items - no notion of keys. 'foreach' processes a list as a KV-structure, so it makes sense to enforce key uniqueness here. Also: test the case of multiple s2s options in one file. --- src/config/mongoose_config_parser_toml.erl | 6 ++++-- test/config_parser_SUITE.erl | 10 +++++++--- test/config_parser_SUITE_data/s2s_only.options | 5 +++++ test/config_parser_SUITE_data/s2s_only.toml | 14 ++++++++++++++ 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index a885306f40..fdb41e77c8 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -1097,8 +1097,10 @@ format_spec(#section{format = Format}) -> Format; format_spec(#list{format = Format}) -> Format; format_spec(#option{format = Format}) -> Format. -format(Path, L, {foreach, Format}) when is_atom(Format) -> - lists:flatmap(fun({K, V}) -> format(Path, V, {Format, K}) end, L); +format(Path, KVs, {foreach, Format}) when is_atom(Format) -> + Keys = lists:map(fun({K, _}) -> K end, KVs), + mongoose_config_validator_toml:validate_list(Keys, unique), + lists:flatmap(fun({K, V}) -> format(Path, V, {Format, K}) end, KVs); format([Key|_] = Path, V, host_local_config) -> format(Path, V, {host_local_config, b2a(Key)}); format([Key|_] = Path, V, local_config) -> diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index 72723dc7a9..44f8e0ab8e 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -1288,7 +1288,9 @@ s2s_host_policy(_Config) -> err_host_config(#{<<"s2s">> => #{<<"host_policy">> => [maps:without([<<"host">>], Policy)]}}), err_host_config(#{<<"s2s">> => #{<<"host_policy">> => [maps:without([<<"policy">>], Policy)]}}), err_host_config(#{<<"s2s">> => #{<<"host_policy">> => [Policy#{<<"host">> => <<>>}]}}), - err_host_config(#{<<"s2s">> => #{<<"host_policy">> => [Policy#{<<"policy">> => <<"huh">>}]}}). + err_host_config(#{<<"s2s">> => #{<<"host_policy">> => [Policy#{<<"policy">> => <<"huh">>}]}}), + err_host_config(#{<<"s2s">> => #{<<"host_policy">> => [Policy, + Policy#{<<"policy">> => <<"deny">>}]}}). s2s_address(_Config) -> Addr = #{<<"host">> => <<"host1">>, @@ -1302,7 +1304,8 @@ s2s_address(_Config) -> ?err(parse(#{<<"s2s">> => #{<<"address">> => [maps:without([<<"ip_address">>], Addr)]}})), ?err(parse(#{<<"s2s">> => #{<<"address">> => [Addr#{<<"host">> => <<>>}]}})), ?err(parse(#{<<"s2s">> => #{<<"address">> => [Addr#{<<"ip_address">> => <<"host2">>}]}})), - ?err(parse(#{<<"s2s">> => #{<<"address">> => [Addr#{<<"port">> => <<"seaport">>}]}})). + ?err(parse(#{<<"s2s">> => #{<<"address">> => [Addr#{<<"port">> => <<"seaport">>}]}})), + ?err(parse(#{<<"s2s">> => #{<<"address">> => [Addr, maps:remove(<<"port">>, Addr)]}})). s2s_ciphers(_Config) -> ?eq([#local_config{key = s2s_ciphers, value = "TLSv1.2:TLSv1.3"}], @@ -1317,7 +1320,8 @@ s2s_domain_certfile(_Config) -> [?err(parse(#{<<"s2s">> => #{<<"domain_certfile">> => [maps:without([K], DomCert)]}})) || K <- maps:keys(DomCert)], [?err(parse(#{<<"s2s">> => #{<<"domain_certfile">> => [DomCert#{K := <<>>}]}})) - || K <- maps:keys(DomCert)]. + || K <- maps:keys(DomCert)], + ?err(parse(#{<<"s2s">> => #{<<"domain_certfile">> => [DomCert, DomCert]}})). s2s_shared(_Config) -> eq_host_config([#local_config{key = {s2s_shared, ?HOST}, value = <<"secret">>}], diff --git a/test/config_parser_SUITE_data/s2s_only.options b/test/config_parser_SUITE_data/s2s_only.options index 5486d39db8..12ad2d7f98 100644 --- a/test/config_parser_SUITE_data/s2s_only.options +++ b/test/config_parser_SUITE_data/s2s_only.options @@ -9,9 +9,14 @@ {local_config,{domain_certfile,"example.com"},"/path/to/example_com.pem"}. {local_config,{domain_certfile,"example.org"},"/path/to/example_org.pem"}. {local_config,{s2s_addr,<<"fed1">>},"127.0.0.1"}. +{local_config,{s2s_addr,<<"fed2">>},{"127.0.0.1", 8765}}. {local_config,{s2s_default_policy,<<"dummy_host">>},allow}. {local_config,{s2s_default_policy,<<"localhost">>},allow}. {local_config,{s2s_max_retry_delay,<<"dummy_host">>},30}. {local_config,{s2s_max_retry_delay,<<"localhost">>},30}. {local_config,{s2s_shared,<<"dummy_host">>},<<"shared secret">>}. {local_config,{s2s_shared,<<"localhost">>},<<"shared secret">>}. +{local_config,{{s2s_host,<<"fed1">>},<<"dummy_host">>},allow}. +{local_config,{{s2s_host,<<"fed1">>},<<"localhost">>},allow}. +{local_config,{{s2s_host,<<"reg1">>},<<"dummy_host">>},deny}. +{local_config,{{s2s_host,<<"reg1">>},<<"localhost">>},deny}. diff --git a/test/config_parser_SUITE_data/s2s_only.toml b/test/config_parser_SUITE_data/s2s_only.toml index 0f3fa3f8f2..ba94d37e97 100644 --- a/test/config_parser_SUITE_data/s2s_only.toml +++ b/test/config_parser_SUITE_data/s2s_only.toml @@ -3,6 +3,7 @@ "localhost", "dummy_host" ] + [s2s] use_starttls = "optional" certfile = "tools/ssl/mongooseim/server.pem" @@ -16,10 +17,23 @@ shared = "shared secret" max_retry_delay = 30 + [[s2s.host_policy]] + host = "fed1" + policy = "allow" + + [[s2s.host_policy]] + host = "reg1" + policy = "deny" + [[s2s.address]] host = "fed1" ip_address = "127.0.0.1" + [[s2s.address]] + host = "fed2" + ip_address = "127.0.0.1" + port = 8765 + [[s2s.domain_certfile]] domain = "example.com" certfile = "/path/to/example_com.pem" From 12d7b9aa15f89953bdf9e5945aa2cf49d2979aa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Wed, 25 Nov 2020 07:51:58 +0100 Subject: [PATCH 035/104] Clean up the validator module --- src/config/mongoose_config_validator_toml.erl | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index 2a6d51f710..aac78abfae 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -1,8 +1,9 @@ -module(mongoose_config_validator_toml). -export([validate/2, - validate/3]). --compile(export_all). + validate/3, + validate_section/2, + validate_list/2]). -include("mongoose.hrl"). -include("ejabberd_config.hrl"). @@ -1200,9 +1201,6 @@ validate_non_empty_binary(Value) when is_binary(Value), Value =/= <<>> -> ok. validate_binary(Value) when is_binary(Value) -> ok. -validate_hosts(Hosts = [_|_]) -> - validate_unique_items(Hosts). - validate_unique_items(Items) -> L = sets:size(sets:from_list(Items)), L = length(Items). @@ -1255,9 +1253,6 @@ validate_non_empty_string(Value) when is_list(Value), Value =/= "" -> ok. validate_non_empty_list(Value) when is_list(Value), Value =/= [] -> ok. -validate_root_or_host_config([]) -> ok; -validate_root_or_host_config([{host, _}, <<"host_config">>]) -> ok. - validate_jid(Jid) -> case jid:from_binary(Jid) of #jid{} -> From d9a0e99debdfffe05a740c34ec82cc0bd44b0abc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Wed, 2 Dec 2020 15:59:09 +0100 Subject: [PATCH 036/104] Specify the config for services in a declarative way New callback for services: config_spec/0 Also: make convert/2 accept only the correct types. Motivation: avoid the need for 'any' validator clauses for each of them. --- src/admin_extra/service_admin_extra.erl | 13 +++++- src/config/mongoose_config_parser_toml.erl | 45 +++---------------- src/config/mongoose_config_spec.erl | 22 ++++++++- src/config/mongoose_config_validator_toml.erl | 18 -------- src/mongoose_service.erl | 6 +++ .../service_mongoose_system_metrics.erl | 28 +++++++++++- 6 files changed, 69 insertions(+), 63 deletions(-) diff --git a/src/admin_extra/service_admin_extra.erl b/src/admin_extra/service_admin_extra.erl index 263f4e8432..97b5c8a4a7 100644 --- a/src/admin_extra/service_admin_extra.erl +++ b/src/admin_extra/service_admin_extra.erl @@ -28,7 +28,9 @@ -behaviour(mongoose_service). --export([start/1, stop/0]). +-include("ejabberd_config.hrl"). + +-export([start/1, stop/0, config_spec/0]). -define(SUBMODS, [node, accounts, sessions, vcard, roster, last, private, stanza, stats, gdpr, upload @@ -50,6 +52,15 @@ stop() -> ejabberd_commands:unregister_commands((mod_name(Submod)):commands()) end, ?SUBMODS). +-spec config_spec() -> mongoose_config_spec:config_section(). +config_spec() -> + #section{ + items = #{<<"submods">> => #list{items = #option{type = atom, + validate = {enum, ?SUBMODS}}, + validate = unique} + } + }. + mod_name(ModAtom) -> list_to_existing_atom(atom_to_list(?MODULE) ++ "_" ++ atom_to_list(ModAtom)). diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index fdb41e77c8..2e4f2388f4 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -94,9 +94,6 @@ parse_root(Path, Content) -> %% path: * -spec process_section(path(), toml_section() | [toml_section()]) -> config_list(). -process_section([<<"services">>] = Path, Content) -> - Services = parse_section(Path, Content), - [#local_config{key = services, value = Services}]; process_section([<<"modules">>|_] = Path, Content) -> Mods = parse_section(Path, Content), ?HOST_F([#local_config{key = {modules, Host}, value = Mods}]); @@ -111,27 +108,6 @@ pool_option([<<"workers">>|_], V) -> [{workers, V}]; pool_option([<<"strategy">>|_], V) -> [{strategy, b2a(V)}]; pool_option([<<"call_timeout">>|_], V) -> [{call_timeout, V}]. -%% path: services.* --spec process_service(path(), toml_section()) -> [option()]. -process_service([S|_] = Path, Opts) -> - [{b2a(S), parse_section(Path, Opts)}]. - -%% path: services.*.* --spec service_opt(path(), toml_value()) -> [option()]. -service_opt([<<"submods">>, <<"service_admin_extra">>|_] = Path, V) -> - List = parse_list(Path, V), - [{submods, List}]; -service_opt([<<"initial_report">>, <<"service_mongoose_system_metrics">>|_], V) -> - [{initial_report, V}]; -service_opt([<<"periodic_report">>, <<"service_mongoose_system_metrics">>|_], V) -> - [{periodic_report, V}]; -service_opt([<<"report">>, <<"service_mongoose_system_metrics">>|_], true) -> - [report]; -service_opt([<<"report">>, <<"service_mongoose_system_metrics">>|_], false) -> - [no_report]; -service_opt([<<"tracking_id">>, <<"service_mongoose_system_metrics">>|_], V) -> - [{tracking_id, b2l(V)}]. - %% path: (host_config[].)modules.* -spec process_module(path(), toml_section()) -> [option()]. process_module([Mod|_] = Path, Opts) -> @@ -928,10 +904,6 @@ iqdisc_value(Type, V) -> limit_keys([], V), Type. --spec service_admin_extra_submods(path(), toml_value()) -> [option()]. -service_admin_extra_submods(_, V) -> - [b2a(V)]. - welcome_message([<<"subject">>|_], Value) -> [{subject, b2l(Value)}]; welcome_message([<<"body">>|_], Value) -> @@ -1083,15 +1055,15 @@ process(_Path, V, undefined) -> V; process(_Path, V, F) when is_function(F, 1) -> F(V); process(Path, V, F) when is_function(F, 2) -> F(Path, V). -convert(V, boolean) -> V; -convert(V, binary) -> V; +convert(V, boolean) when is_boolean(V) -> V; +convert(V, binary) when is_binary(V) -> V; convert(V, string) -> binary_to_list(V); convert(V, atom) -> b2a(V); convert(<<"infinity">>, int_or_infinity) -> infinity; %% TODO maybe use TOML '+inf' -convert(V, int_or_infinity) -> V; +convert(V, int_or_infinity) when is_integer(V) -> V; convert(V, int_or_atom) when is_integer(V) -> V; convert(V, int_or_atom) -> b2a(V); -convert(V, integer) -> V. +convert(V, integer) when is_integer(V) -> V. format_spec(#section{format = Format}) -> Format; format_spec(#list{format = Format}) -> Format; @@ -1184,14 +1156,9 @@ node_to_string(Node) -> [binary_to_list(Node)]. -spec handler(path()) -> fun((path(), toml_value()) -> option()) | mongoose_config_spec:config_node(). handler([]) -> fun parse_root/2; -handler([Section]) when Section =:= <<"services">>; - Section =:= <<"modules">>; +handler([Section]) when Section =:= <<"modules">>; Section =:= <<"host_config">> -> fun process_section/2; -%% services -handler([_, <<"services">>]) -> fun process_service/2; -handler([_, _, <<"services">>]) -> fun service_opt/2; - %% modules handler([_, <<"modules">>]) -> fun process_module/2; handler([_, _, <<"modules">>]) -> fun module_opt/2; @@ -1299,8 +1266,6 @@ handler([_, <<"ldap_search_reported">>, <<"mod_vcard">>, <<"modules">>]) -> fun mod_vcard_ldap_search_reported/2; handler([_, <<"ldap_binary_search_fields">>, <<"mod_vcard">>, <<"modules">>]) -> fun mod_vcard_ldap_binary_search_fields/2; -handler([_, <<"submods">>, <<"service_admin_extra">>, <<"services">>]) -> - fun service_admin_extra_submods/2; %% host_config handler([_, <<"host_config">>]) -> fun process_host_item/2; diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index 40303148ea..12f17ddd10 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -4,9 +4,12 @@ -include("ejabberd_config.hrl"). --type config_node() :: #section{} | #option{} | #list{}. +-type config_node() :: config_section() | config_list() | config_option(). +-type config_section() :: #section{}. +-type config_list() :: #list{}. +-type config_option() :: #option{}. --export_type([config_node/0]). +-export_type([config_node/0, config_section/0, config_list/0, config_option/0]). handler(Path) -> handler(Path, root()). @@ -33,6 +36,7 @@ root() -> <<"listen">> => listen(), <<"auth">> => auth(), <<"outgoing_pools">> => outgoing_pools(), + <<"services">> => services(), <<"shaper">> => shaper(), <<"acl">> => acl(), <<"access">> => access(), @@ -700,6 +704,18 @@ tls_items() -> <<"versions">> => #list{items = #option{type = atom}} }. +%% path: (host_config[].)services +services() -> + Services = [{a2b(Service), mongoose_service:config_spec(Service)} || Service <- all_services()], + #section{ + items = maps:from_list(Services), + format = local_config + }. + +all_services() -> + [service_admin_extra, + service_mongoose_system_metrics]. + %% path: (host_config[].)shaper shaper() -> #section{ @@ -1091,6 +1107,8 @@ process_riak_credentials(KVs) -> b2a(B) -> binary_to_atom(B, utf8). +a2b(A) -> atom_to_binary(A, utf8). + wpool_strategy_values() -> [best_worker, random_worker, next_worker, available_worker, next_available_worker]. diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index aac78abfae..414ddaf474 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -17,23 +17,6 @@ validate(Path, [F]) when is_function(F, 1) -> validate(Path, F(?HOST)); -%% Services -validate([item, <<"submods">>, <<"service_admin_extra">>, <<"services">>], - [Value]) -> - validate_backend(service_admin_extra, Value); -validate([<<"initial_report">>, - <<"service_mongoose_system_metrics">>, <<"services">>], - [{initial_report, Value}]) -> - validate_non_negative_integer(Value); -validate([<<"periodic_report">>, - <<"service_mongoose_system_metrics">>, <<"services">>], - [{periodic_report, Value}]) -> - validate_non_negative_integer(Value); -validate([<<"tracking_id">>, - <<"service_mongoose_system_metrics">>, <<"services">>], - [{tracking_id, Value}]) -> - validate_non_empty_string(Value); - %% Modules validate([<<"callback_module">>, <<"http">>, <<"backend">>, <<"mod_event_pusher">>, <<"modules">>|_], @@ -1165,7 +1148,6 @@ validate([<<"timeout_action">>, <<"mod_ping">>, <<"modules">>|_], validate(_Path, _Value) -> ok. -validate(V, boolean, any) -> validate_boolean(V); validate(V, binary, domain) -> validate_binary_domain(V); validate(V, binary, non_empty) -> validate_non_empty_binary(V); validate(V, integer, non_negative) -> validate_non_negative_integer(V); diff --git a/src/mongoose_service.erl b/src/mongoose_service.erl index 65e0699add..14129bde1d 100644 --- a/src/mongoose_service.erl +++ b/src/mongoose_service.erl @@ -21,6 +21,7 @@ -export([start/0, stop/0, start_service/2, stop_service/1, + config_spec/1, is_loaded/1, assert_loaded/1, ensure_loaded/1, @@ -43,6 +44,7 @@ -callback start(Opts :: list()) -> any(). -callback stop() -> any(). +-callback config_spec() -> mongoose_config_spec:config_section(). %%optional: %%-callback deps() -> [service()]. @@ -70,6 +72,10 @@ stop_service(Service) -> true -> run_stop_service(Service) end. +-spec config_spec(service()) -> mongoose_config_spec:config_section(). +config_spec(Service) -> + Service:config_spec(). + -spec ensure_loaded(service()) -> ok. ensure_loaded(Service) -> Options = ejabberd_config:get_local_option_or_default(services, []), diff --git a/src/system_metrics/service_mongoose_system_metrics.erl b/src/system_metrics/service_mongoose_system_metrics.erl index b576a933be..ab779c6c7b 100644 --- a/src/system_metrics/service_mongoose_system_metrics.erl +++ b/src/system_metrics/service_mongoose_system_metrics.erl @@ -4,6 +4,8 @@ -behaviour(mongoose_service). -behaviour(gen_server). +-include("ejabberd_config.hrl"). + -define(DEFAULT_INITIAL_REPORT, timer:minutes(5)). -define(DEFAULT_REPORT_AFTER, timer:hours(3)). -ifdef(PROD_NODE). @@ -15,7 +17,7 @@ -include("mongoose.hrl"). --export([start/1, stop/0]). +-export([start/1, stop/0, config_spec/0]). -export([start_link/1, init/1, handle_call/3, @@ -23,6 +25,9 @@ handle_info/2, terminate/2]). +%% config spec callbacks +-export([process_report_option/1]). + -export([verify_if_configured/0]). -record(system_metrics_state, {report_after, reporter_monitor = none, @@ -54,6 +59,24 @@ start(Args) -> stop() -> ejabberd_sup:stop_child(?MODULE). +-spec config_spec() -> mongoose_config_spec:config_section(). +config_spec() -> + #section{ + items = #{<<"initial_report">> => #option{type = integer, + validate = non_negative}, + <<"periodic_report">> => #option{type = integer, + validate = non_negative}, + <<"report">> => #option{type = boolean, + process = fun ?MODULE:process_report_option/1, + format = item}, + <<"tracking_id">> => #option{type = string, + validate = non_empty} + } + }. + +process_report_option(true) -> report; +process_report_option(false) -> no_report. + -spec start_link(proplists:proplist()) -> {ok, pid()}. start_link(Args) -> gen_server:start_link({local, ?MODULE}, ?MODULE, Args, []). @@ -165,7 +188,8 @@ msg_removed_from_config() -> <<"We're sorry to hear you don't want to share the system's metrics with us. " "These metrics would enable us to improve MongooseIM and know where to focus our efforts. " "To stop being notified, you can add this to the services section of your config file: \n" - " '{service_mongoose_system_metrics, [no_report]}' \n" + " [services.service_mongoose_system_metrics]\n" + " report = false\n" "For more info on how to customise, read, enable, and disable the metrics visit: \n" "- MongooseIM docs - \n" " https://mongooseim.readthedocs.io/en/latest/operation-and-maintenance/System-Metrics-Privacy-Policy/ \n" From a070e68fcf77962bd4fb33f1caf8955d525b85b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Thu, 3 Dec 2020 09:31:07 +0100 Subject: [PATCH 037/104] Cover a missing case in tests --- test/config_parser_SUITE.erl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index 44f8e0ab8e..710a57e603 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -2894,6 +2894,8 @@ service_mongoose_system_metrics(_Config) -> parse(T(#{<<"periodic_report">> => 5000}))), ?eq(servopts(M, [{tracking_id, "UA-123456789"}]), parse(T(#{<<"tracking_id">> => <<"UA-123456789">>}))), + ?eq(servopts(M, [no_report]), + parse(T(#{<<"report">> => false}))), %% error cases ?err(parse(T(#{<<"initial_report">> => <<"forever">>}))), ?err(parse(T(#{<<"periodic_report">> => <<"forever">>}))), From ed4b0161aa6dfb74a313bb2ff0f0e21b665cf5c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Thu, 3 Dec 2020 16:07:27 +0100 Subject: [PATCH 038/104] Unify testing error cases in the TOML tests 'errf' used an older (incorrect) version where it expected a function, this has changed and 'err_host_config' was already updated. Without this change all tests for module would fail. --- test/config_parser_SUITE.erl | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index 710a57e603..1c3170b360 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -16,8 +16,7 @@ -define(add_loc(X), {X, #{line => ?LINE}}). -define(eqf(Expected, Actual), eq_host_config(Expected, Actual)). --define(errf(Config), - begin ?err(parse_with_host(Config)), ?err(parse_host_config(Config)) end). +-define(errf(Config), err_host_config(Config)). %% Constructs HOF to pass into run_multi/1 function %% It's a HOF, so it would always pass if not passed into run_multi/1 @@ -3155,10 +3154,6 @@ compare_ordered_lists([H1|T1], [H2|T2], F) -> compare_ordered_lists([], [], _) -> ok. -parse_with_host(Config) -> - [F] = parse(Config), - apply(F, [?HOST]). - set_pl(K, V, List) -> lists:keyreplace(K, 1, List, {K, V}). From 3d8cc6c8a20d6373b928d1b2b7831129804b2f13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Thu, 3 Dec 2020 16:13:28 +0100 Subject: [PATCH 039/104] Make the declarative spec for the first module: 'mod_adhoc' - Add a new callback: 'config_spec/0' for modules (now only mod_adhoc) - Make the code handle imperative and declarative config for modules (requires a temporary macro) --- src/config/mongoose_config_parser_toml.erl | 25 +++++----------- src/config/mongoose_config_spec.erl | 30 +++++++++++++++++++ src/config/mongoose_config_validator_toml.erl | 6 ---- src/gen_mod.erl | 7 +++++ src/mod_adhoc.erl | 10 +++++++ 5 files changed, 54 insertions(+), 24 deletions(-) diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index 2e4f2388f4..935dc3bea6 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -92,16 +92,6 @@ parse_root(Path, Content) -> ensure_keys([<<"general">>], Content), parse_section(Path, Content). -%% path: * --spec process_section(path(), toml_section() | [toml_section()]) -> config_list(). -process_section([<<"modules">>|_] = Path, Content) -> - Mods = parse_section(Path, Content), - ?HOST_F([#local_config{key = {modules, Host}, value = Mods}]); -process_section([<<"host_config">>] = Path, Content) -> - parse_list(Path, Content); -process_section(Path, Content) -> - parse_section(Path, Content). - %% path: (host_config[].)modules.mod_event_pusher.backend.push.wpool.* -spec pool_option(path(), toml_value()) -> [option()]. pool_option([<<"workers">>|_], V) -> [{workers, V}]; @@ -122,8 +112,6 @@ post_process_module(Mod, Opts) -> %% path: (host_config[].)modules.*.* -spec module_opt(path(), toml_value()) -> [option()]. -module_opt([<<"report_commands_node">>, <<"mod_adhoc">>|_], V) -> - [{report_commands_node, V}]; module_opt([<<"validity_period">>, <<"mod_auth_token">>|_] = Path, V) -> parse_list(Path, V); module_opt([<<"inactivity">>, <<"mod_bosh">>|_], V) -> @@ -1153,16 +1141,17 @@ node_to_string({host, _}) -> []; node_to_string({tls, TLSAtom}) -> [atom_to_list(TLSAtom)]; node_to_string(Node) -> [binary_to_list(Node)]. +-define(HAS_NO_SPEC(Mod), Mod =/= <<"mod_adhoc">>). % TODO temporary, remove with 'handler/1' + -spec handler(path()) -> fun((path(), toml_value()) -> option()) | mongoose_config_spec:config_node(). handler([]) -> fun parse_root/2; -handler([Section]) when Section =:= <<"modules">>; - Section =:= <<"host_config">> -> fun process_section/2; +handler([<<"host_config">>]) -> fun parse_list/2; %% modules -handler([_, <<"modules">>]) -> fun process_module/2; -handler([_, _, <<"modules">>]) -> fun module_opt/2; -handler([_, <<"riak">>, _, <<"modules">>]) -> +handler([Mod, <<"modules">>]) when ?HAS_NO_SPEC(Mod) -> fun process_module/2; +handler([_, Mod, <<"modules">>]) when ?HAS_NO_SPEC(Mod) -> fun module_opt/2; +handler([_, <<"riak">>, Mod, <<"modules">>]) when ?HAS_NO_SPEC(Mod) -> fun riak_opts/2; handler([_, <<"ip_access">>, <<"mod_register">>, <<"modules">>]) -> fun mod_register_ip_access_rule/2; @@ -1271,7 +1260,7 @@ handler([_, <<"ldap_binary_search_fields">>, <<"mod_vcard">>, <<"modules">>]) -> handler([_, <<"host_config">>]) -> fun process_host_item/2; handler([<<"auth">>, _, <<"host_config">>] = P) -> handler_for_host(P); handler([<<"modules">>, _, <<"host_config">>] = P) -> handler_for_host(P); -handler([_, _, <<"host_config">>]) -> fun process_section/2; +handler([<<"general">>, _, <<"host_config">>]) -> fun parse_section/2; handler([_, <<"general">>, _, <<"host_config">>] = P) -> handler_for_host(P); handler([_, <<"s2s">>, _, <<"host_config">>] = P) -> handler_for_host(P); handler(Path) -> diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index 12f17ddd10..4abe642aa8 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -37,6 +37,7 @@ root() -> <<"auth">> => auth(), <<"outgoing_pools">> => outgoing_pools(), <<"services">> => services(), + <<"modules">> => modules(), <<"shaper">> => shaper(), <<"acl">> => acl(), <<"access">> => access(), @@ -716,6 +717,35 @@ all_services() -> [service_admin_extra, service_mongoose_system_metrics]. +%% path: (host_config[].)modules +modules() -> + Modules = [{a2b(Module), gen_mod:config_spec(Module)} || Module <- all_modules()], + #section{ + items = maps:from_list(Modules), + format = host_local_config + }. + +all_modules() -> + [mod_adhoc]. + +%% path: (host_config[].)modules.*.iqdisc +iqdisc() -> + #section{ + items = #{<<"type">> => #option{type = atom, + validate = {enum, [no_queue, one_queue, parallel, queues]}}, + <<"workers">> => #option{type = integer, + validate = positive}}, + required = [<<"type">>], + process = fun ?MODULE:format_iqdisc/1 + }. + +format_iqdisc(KVs) -> + {[[{type, Type}]], WorkersOpts} = proplists:split(KVs, [type]), + iqdisc(Type, WorkersOpts). + +iqdisc(queues, [{workers, N}]) -> {queues, N}; +iqdisc(Type, []) -> Type. + %% path: (host_config[].)shaper shaper() -> #section{ diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index 414ddaf474..d975cfb47c 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -356,12 +356,6 @@ validate([<<"body">>, <<"welcome_message">>, <<"mod_register">>, <<"modules">>|_ validate([<<"subject">>, <<"welcome_message">>, <<"mod_register">>, <<"modules">>|_], [{subject, V}]) -> validate_string(V); -validate([<<"iqdisc">>, <<"mod_adhoc">>, <<"modules">>|_], - [{iqdisc, V}]) -> - validate_iqdisc(V); -validate([<<"report_commands_node">>, <<"mod_adhoc">>, <<"modules">>|_], - [{report_commands_node, V}]) -> - validate_boolean(V); validate([<<"cache_life_time">>, <<"mod_caps">>, <<"modules">>|_], [{cache_life_time, V}]) -> validate_non_negative_integer_or_infinity(V); diff --git a/src/gen_mod.erl b/src/gen_mod.erl index a3587758b4..34babd4130 100644 --- a/src/gen_mod.erl +++ b/src/gen_mod.erl @@ -51,6 +51,7 @@ stop_module/2, stop_module_keep_config/2, reload_module/3, + config_spec/1, % Get/set opts by host or from a list get_opt/2, get_opt/3, @@ -93,6 +94,8 @@ %% undefined. -callback start(Host :: jid:server(), Opts :: list()) -> any(). -callback stop(Host :: jid:server()) -> any(). +-callback config_spec() -> mongoose_config_spec:config_section(). +-optional_callbacks([config_spec/0]). %% Optional callback specifying module dependencies. %% The dependent module can specify parameters with which the dependee should be @@ -250,6 +253,10 @@ reload_module(Host, Module, Opts) -> stop_module_keep_config(Host, Module), start_module(Host, Module, Opts). +-spec config_spec(module()) -> mongoose_config_spec:config_section(). +config_spec(Module) -> + Module:config_spec(). + -spec wait_for_process(atom() | pid() | {atom(), atom()}) -> 'ok'. wait_for_process(Process) -> MonitorReference = erlang:monitor(process, Process), diff --git a/src/mod_adhoc.erl b/src/mod_adhoc.erl index a8fd0df534..10d79f1fa2 100644 --- a/src/mod_adhoc.erl +++ b/src/mod_adhoc.erl @@ -30,6 +30,7 @@ -export([start/2, stop/1, + config_spec/0, process_local_iq/4, process_sm_iq/4, get_local_commands/5, @@ -44,6 +45,7 @@ -include("mongoose.hrl"). -include("jlib.hrl"). -include("adhoc.hrl"). +-include("ejabberd_config.hrl"). start(Host, Opts) -> IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue), @@ -69,6 +71,14 @@ hooks(Host) -> {disco_sm_items, Host, ?MODULE, get_sm_commands, 99}, {adhoc_local_items, Host, ?MODULE, ping_item, 100}, {adhoc_local_commands, Host, ?MODULE, ping_command, 100}]. + +-spec config_spec() -> mongoose_config_spec:config_section(). +config_spec() -> + #section{ + items = #{<<"report_commands_node">> => #option{type = boolean}, + <<"iqdisc">> => mongoose_config_spec:iqdisc()} + }. + %%------------------------------------------------------------------------- -spec get_local_commands(Acc :: {result, [exml:element()]} | {error, any()} | empty, From 16a62761608cf335cd7e580d74d7d3caccfed108 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Fri, 4 Dec 2020 08:17:03 +0100 Subject: [PATCH 040/104] Simplify tests for mod_adhoc Use the 'eqf' and 'errf' macros instead of '_eqf' and '_errf'. Motivation: - Less likely to confues returning the function with invoking it (this error is already in the tests e.g. for mod_bosh) - 'run_multi' returns multiple stacktraces, but only one line number so it is difficult to tell which failed case it refers to. - Less surprising code, more consistency (most tests do not use run_multi) This change is going to be repeated for other modules as well. --- test/config_parser_SUITE.erl | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index 1c3170b360..658ed8315c 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -1336,16 +1336,13 @@ s2s_max_retry_delay(_Config) -> mod_adhoc(_Config) -> check_iqdisc(mod_adhoc), - run_multi(mod_adhoc_cases()). - -mod_adhoc_cases() -> M = fun(K, V) -> modopts(mod_adhoc, [{K, V}]) end, T = fun(K, V) -> #{<<"modules">> => #{<<"mod_adhoc">> => #{K => V}}} end, %% report_commands_node is boolean - [?_eqf(M(report_commands_node, true), T(<<"report_commands_node">>, true)), - ?_eqf(M(report_commands_node, false), T(<<"report_commands_node">>, false)), - %% not boolean - ?_errf(T(<<"report_commands_node">>, <<"hello">>))]. + ?eqf(M(report_commands_node, true), T(<<"report_commands_node">>, true)), + ?eqf(M(report_commands_node, false), T(<<"report_commands_node">>, false)), + %% not boolean + ?errf(T(<<"report_commands_node">>, <<"hello">>)). mod_auth_token(_Config) -> check_iqdisc(mod_auth_token), From 529381839c3448c56525defc2e0141637eae18af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Fri, 4 Dec 2020 08:24:52 +0100 Subject: [PATCH 041/104] Make the config of mod_auth_token declarative --- src/config/mongoose_config_parser_toml.erl | 13 ++----- src/config/mongoose_config_spec.erl | 9 ++++- src/config/mongoose_config_validator_toml.erl | 17 ---------- src/mod_auth_token.erl | 34 ++++++++++++++++++- 4 files changed, 44 insertions(+), 29 deletions(-) diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index 935dc3bea6..538d1f5ef8 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -112,8 +112,6 @@ post_process_module(Mod, Opts) -> %% path: (host_config[].)modules.*.* -spec module_opt(path(), toml_value()) -> [option()]. -module_opt([<<"validity_period">>, <<"mod_auth_token">>|_] = Path, V) -> - parse_list(Path, V); module_opt([<<"inactivity">>, <<"mod_bosh">>|_], V) -> [{inactivity, int_or_infinity(V)}]; module_opt([<<"max_wait">>, <<"mod_bosh">>|_], V) -> @@ -474,11 +472,6 @@ riak_opts([<<"search_index">>|_], V) -> mod_register_ip_access_rule(_, #{<<"address">> := Addr, <<"policy">> := Policy}) -> [{b2a(Policy), b2l(Addr)}]. --spec mod_auth_token_validity_periods(path(), toml_section()) -> [option()]. -mod_auth_token_validity_periods(_, - #{<<"token">> := Token, <<"value">> := Value, <<"unit">> := Unit}) -> - [{{validity_period, b2a(Token)}, {Value, b2a(Unit)}}]. - -spec mod_disco_server_info(path(), toml_section()) -> [option()]. mod_disco_server_info(Path, #{<<"module">> := <<"all">>, <<"name">> := Name, <<"urls">> := Urls}) -> URLList = parse_list([<<"urls">> | Path], Urls), @@ -1141,7 +1134,9 @@ node_to_string({host, _}) -> []; node_to_string({tls, TLSAtom}) -> [atom_to_list(TLSAtom)]; node_to_string(Node) -> [binary_to_list(Node)]. --define(HAS_NO_SPEC(Mod), Mod =/= <<"mod_adhoc">>). % TODO temporary, remove with 'handler/1' +-define(HAS_NO_SPEC(Mod), + Mod =/= <<"mod_adhoc">>, + Mod =/= <<"mod_auth_token">>). % TODO temporary, remove with 'handler/1' -spec handler(path()) -> fun((path(), toml_value()) -> option()) | mongoose_config_spec:config_node(). @@ -1159,8 +1154,6 @@ handler([_, <<"registration_watchers">>, <<"mod_register">>, <<"modules">>]) -> fun(_, V) -> [V] end; handler([_, <<"welcome_message">>, <<"mod_register">>, <<"modules">>]) -> fun welcome_message/2; -handler([_, <<"validity_period">>, <<"mod_auth_token">>, <<"modules">>]) -> - fun mod_auth_token_validity_periods/2; handler([_, <<"extra_domains">>, <<"mod_disco">>, <<"modules">>]) -> fun(_, V) -> [V] end; handler([_, <<"server_info">>, <<"mod_disco">>, <<"modules">>]) -> diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index 4abe642aa8..71d9eaf52f 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -726,7 +726,14 @@ modules() -> }. all_modules() -> - [mod_adhoc]. + [mod_adhoc, + mod_auth_token%%, + %% mod_bosh, + %% mod_caps, + %% mod_csi, + %% mod_disco, + %% mod_event_pusher + ]. %% path: (host_config[].)modules.*.iqdisc iqdisc() -> diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index d975cfb47c..f782f55ef0 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -644,11 +644,6 @@ validate([<<"simple">>, <<"mod_mam_meta">>, <<"modules">>|_], validate([<<"user_prefs_store">>, <<"mod_mam_meta">>, <<"modules">>|_], [{user_prefs_store, V}]) -> validate_enum(V, [false,rdbms,cassandra,mnesia]); -validate([<<"iqdisc">>, <<"mod_auth_token">>, <<"modules">>|_], - [{iqdisc, V}]) -> - validate_iqdisc(V); -validate([<<"validity_period">>,<<"mod_auth_token">>,<<"modules">>|_], Vs) -> - lists:foreach(fun validate_validity_period/1, Vs); validate([<<"listen_port">>, <<"mod_jingle_sip">>, <<"modules">>|_], [{listen_port, V}]) -> validate_network_port(V); @@ -1242,18 +1237,6 @@ validate_iqdisc(one_queue) -> ok; validate_iqdisc(parallel) -> ok; validate_iqdisc({queues, N}) when is_integer(N), N > 0 -> ok. --spec validate_auth_token_domain(mod_auth_token:token_type()) -> ok. -validate_auth_token_domain(Type) -> - validate_enum(Type, [access, refresh, provision]). - -validate_validity_period({{validity_period, Token}, {Value, Unit}}) -> - validate_auth_token_domain(Token), - validate_non_negative_integer(Value), - validate_period_unit(Unit). - -validate_period_unit(Unit) -> - validate_enum(Unit, [days, hours, minutes, seconds]). - validate_ip_access({Access, IPMask}) -> validate_enum(Access, [allow, deny]), validate_ip_mask_string(IPMask). diff --git a/src/mod_auth_token.erl b/src/mod_auth_token.erl index 5f0765af73..d58e1efa80 100644 --- a/src/mod_auth_token.erl +++ b/src/mod_auth_token.erl @@ -7,10 +7,15 @@ -include("ejabberd_commands.hrl"). -include("jlib.hrl"). -include("mod_auth_token.hrl"). +-include("ejabberd_config.hrl"). %% gen_mod callbacks -export([start/2, - stop/1]). + stop/1, + config_spec/0]). + +%% Config spec callbacks +-export([process_validity_period/1]). %% Hook handlers -export([clean_tokens/3]). @@ -92,6 +97,33 @@ stop(Domain) -> || {Hook, Handler, Priority} <- hook_handlers() ], ok. +-spec config_spec() -> mongoose_config_spec:config_section(). +config_spec() -> + #section{ + items = #{<<"validity_period">> => #list{items = validity_period_spec(), + format = none}, + <<"iqdisc">> => mongoose_config_spec:iqdisc() + } + }. + +validity_period_spec() -> + #section{ + items = #{<<"token">> => #option{type = atom, + validate = {enum, [access, refresh, provision]}}, + <<"value">> => #option{type = integer, + validate = non_negative}, + <<"unit">> => #option{type = atom, + validate = {enum, [days, hours, minutes, seconds]}} + }, + required = all, + process = fun ?MODULE:process_validity_period/1 + }. + +process_validity_period(KVs) -> + {[[{token, Token}], [{value, Value}], [{unit, Unit}]], []} = + proplists:split(KVs, [token, value, unit]), + {{validity_period, Token}, {Value, Unit}}. + default_opts(Opts) -> [{backend, rdbms} || not proplists:is_defined(backend, Opts)] ++ Opts. From 94e6d4010b462cb4afc6a41668f0a79ba1da3d9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Fri, 4 Dec 2020 08:26:02 +0100 Subject: [PATCH 042/104] Simplify tests for mod_auth_token - For motivation see the commit for mod_adhoc - Fix indentation as well --- test/config_parser_SUITE.erl | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index 658ed8315c..2d6620f3a8 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -1346,24 +1346,21 @@ mod_adhoc(_Config) -> mod_auth_token(_Config) -> check_iqdisc(mod_auth_token), - run_multi(mod_auth_token_cases()). - -mod_auth_token_cases() -> P = fun(X) -> - Opts = #{<<"validity_period">> => X}, - #{<<"modules">> => #{<<"mod_auth_token">> => Opts}} - end, - [?_eqf(modopts(mod_auth_token, [{{validity_period,access}, {13,minutes}}, - {{validity_period,refresh}, {31,days}}]), - P([#{<<"token">> => <<"access">>, <<"value">> => 13, <<"unit">> => <<"minutes">>}, - #{<<"token">> => <<"refresh">>, <<"value">> => 31, <<"unit">> => <<"days">>}])), - ?_errf(P([#{<<"token">> => <<"access">>, <<"value">> => <<"13">>, <<"unit">> => <<"minutes">>}])), - ?_errf(P([#{<<"token">> => <<"access">>, <<"value">> => 13, <<"unit">> => <<"minute">>}])), - ?_errf(P([#{<<"token">> => <<"Access">>, <<"value">> => 13, <<"unit">> => <<"minutes">>}])), - ?_errf(P([#{<<"value">> => 13, <<"unit">> => <<"minutes">>}])), - ?_errf(P([#{<<"token">> => <<"access">>, <<"unit">> => <<"minutes">>}])), - ?_errf(P([#{<<"token">> => <<"access">>, <<"value">> => 13}]))]. - + Opts = #{<<"validity_period">> => X}, + #{<<"modules">> => #{<<"mod_auth_token">> => Opts}} + end, + ?eqf(modopts(mod_auth_token, [{{validity_period, access}, {13, minutes}}, + {{validity_period, refresh}, {31, days}}]), + P([#{<<"token">> => <<"access">>, <<"value">> => 13, <<"unit">> => <<"minutes">>}, + #{<<"token">> => <<"refresh">>, <<"value">> => 31, <<"unit">> => <<"days">>}])), + ?errf(P([#{<<"token">> => <<"access">>, <<"value">> => <<"13">>, + <<"unit">> => <<"minutes">>}])), + ?errf(P([#{<<"token">> => <<"access">>, <<"value">> => 13, <<"unit">> => <<"minute">>}])), + ?errf(P([#{<<"token">> => <<"Access">>, <<"value">> => 13, <<"unit">> => <<"minutes">>}])), + ?errf(P([#{<<"value">> => 13, <<"unit">> => <<"minutes">>}])), + ?errf(P([#{<<"token">> => <<"access">>, <<"unit">> => <<"minutes">>}])), + ?errf(P([#{<<"token">> => <<"access">>, <<"value">> => 13}])). mod_bosh(_Config) -> run_multi(mod_bosh_cases()). From 488669f2aaed9644702ba7fdb1f0e903b8366af1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Fri, 4 Dec 2020 09:00:17 +0100 Subject: [PATCH 043/104] Make the config of mod_bosh declarative Also: - Change 'maxpause' to 'max_pause' to be consistent with 'max_wait' - Validate 'max_pause' (was not validated) - Add unit test for 'max_pause' (was missing) - Simplify tests --- doc/modules/mod_bosh.md | 6 ++-- src/config/mongoose_config_parser_toml.erl | 11 ++----- src/config/mongoose_config_spec.erl | 4 +-- src/config/mongoose_config_validator_toml.erl | 9 ------ src/mod_bosh.erl | 19 +++++++++++- test/config_parser_SUITE.erl | 29 +++++++++---------- test/config_parser_SUITE_data/modules.toml | 2 +- 7 files changed, 40 insertions(+), 40 deletions(-) diff --git a/doc/modules/mod_bosh.md b/doc/modules/mod_bosh.md index bc8a544652..a98b13ff07 100644 --- a/doc/modules/mod_bosh.md +++ b/doc/modules/mod_bosh.md @@ -30,10 +30,10 @@ Please note that a long-polling request is not considered to be an inactivity. Enables/disables [acks](http://xmpp.org/extensions/xep-0124.html#ack-request) sent by server. -#### `modules.mod_bosh.maxpause` +#### `modules.mod_bosh.max_pause` * **Syntax:** positive integer * **Default:** `120` - * **Example:** `maxpause = 30` + * **Example:** `max_pause = 30` Maximum allowed pause in seconds (e.g. to switch between pages and then resume connection) to request by client-side. @@ -56,5 +56,5 @@ In the module section: inactivity = 20 max_wait = "infinity" server_acks = true - maxpause = 120 + max_pause = 120 ``` diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index 538d1f5ef8..71e0e95be6 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -112,14 +112,6 @@ post_process_module(Mod, Opts) -> %% path: (host_config[].)modules.*.* -spec module_opt(path(), toml_value()) -> [option()]. -module_opt([<<"inactivity">>, <<"mod_bosh">>|_], V) -> - [{inactivity, int_or_infinity(V)}]; -module_opt([<<"max_wait">>, <<"mod_bosh">>|_], V) -> - [{max_wait, int_or_infinity(V)}]; -module_opt([<<"server_acks">>, <<"mod_bosh">>|_], V) -> - [{server_acks, V}]; -module_opt([<<"maxpause">>, <<"mod_bosh">>|_], V) -> - [{maxpause, V}]; module_opt([<<"cache_size">>, <<"mod_caps">>|_], V) -> [{cache_size, V}]; module_opt([<<"cache_life_time">>, <<"mod_caps">>|_], V) -> @@ -1136,7 +1128,8 @@ node_to_string(Node) -> [binary_to_list(Node)]. -define(HAS_NO_SPEC(Mod), Mod =/= <<"mod_adhoc">>, - Mod =/= <<"mod_auth_token">>). % TODO temporary, remove with 'handler/1' + Mod =/= <<"mod_auth_token">>, + Mod =/= <<"mod_bosh">>). % TODO temporary, remove with 'handler/1' -spec handler(path()) -> fun((path(), toml_value()) -> option()) | mongoose_config_spec:config_node(). diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index 71d9eaf52f..0d9bd458ae 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -727,8 +727,8 @@ modules() -> all_modules() -> [mod_adhoc, - mod_auth_token%%, - %% mod_bosh, + mod_auth_token, + mod_bosh%%, %% mod_caps, %% mod_csi, %% mod_disco, diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index f782f55ef0..68a8ac808c 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -749,15 +749,6 @@ validate([<<"iqdisc">>, <<"mod_private">>, <<"modules">>|_], validate([<<"bucket_type">>, <<"riak">>, <<"mod_private">>, <<"modules">>|_], [{bucket_type, V}]) -> validate_non_empty_binary(V); -validate([<<"inactivity">>, <<"mod_bosh">>, <<"modules">>|_], - [{inactivity, V}]) -> - validate_non_negative_integer_or_infinity(V); -validate([<<"max_wait">>, <<"mod_bosh">>, <<"modules">>|_], - [{max_wait, V}]) -> - validate_non_negative_integer_or_infinity(V); -validate([<<"server_acks">>, <<"mod_bosh">>, <<"modules">>|_], - [{server_acks, V}]) -> - validate_boolean(V); validate([<<"aff_changes">>, <<"mod_inbox">>, <<"modules">>|_], [{aff_changes, V}]) -> validate_boolean(V); diff --git a/src/mod_bosh.erl b/src/mod_bosh.erl index c06264556a..caac2ff6ee 100644 --- a/src/mod_bosh.erl +++ b/src/mod_bosh.erl @@ -21,7 +21,8 @@ %% gen_mod callbacks -export([start/2, - stop/1]). + stop/1, + config_spec/0]). %% cowboy_loop_handler callbacks -export([init/2, @@ -40,6 +41,7 @@ -include("jlib.hrl"). -include_lib("exml/include/exml_stream.hrl"). -include("mod_bosh.hrl"). +-include("ejabberd_config.hrl"). -define(DEFAULT_MAX_AGE, 1728000). %% 20 days in seconds -define(DEFAULT_INACTIVITY, 30). %% seconds @@ -146,6 +148,21 @@ start(_Host, Opts) -> stop(_Host) -> ok. + +-spec config_spec() -> mongoose_config_spec:config_section(). +config_spec() -> + #section{ + items = #{<<"inactivity">> => #option{type = int_or_infinity, + validate = non_negative}, + <<"max_wait">> => #option{type = int_or_infinity, + validate = non_negative}, + <<"server_acks">> => #option{type = boolean}, + <<"max_pause">> => #option{type = integer, + validate = positive, + format = {kv, maxpause}} + } + }. + %%-------------------------------------------------------------------- %% Hooks handlers %%-------------------------------------------------------------------- diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index 2d6620f3a8..c8f80472b2 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -1363,23 +1363,22 @@ mod_auth_token(_Config) -> ?errf(P([#{<<"token">> => <<"access">>, <<"value">> => 13}])). mod_bosh(_Config) -> - run_multi(mod_bosh_cases()). - -mod_bosh_cases() -> T = fun(K, V) -> #{<<"modules">> => #{<<"mod_bosh">> => #{K => V}}} end, M = fun(K, V) -> modopts(mod_bosh, [{K, V}]) end, - [?_eqf(M(inactivity, 10), T(<<"inactivity">>, 10)), - ?_eqf(M(inactivity, infinity), T(<<"inactivity">>, <<"infinity">>)), - ?_eqf(M(inactivity, 10), T(<<"inactivity">>, 10)), - ?_eqf(M(max_wait, infinity), T(<<"max_wait">>, <<"infinity">>)), - ?_eqf(M(server_acks, true), T(<<"server_acks">>, true)), - ?_eqf(M(server_acks, false), T(<<"server_acks">>, false)), - ?errf(T(<<"inactivity">>, -1)), - ?errf(T(<<"inactivity">>, <<"10">>)), - ?errf(T(<<"inactivity">>, <<"inactivity">>)), - ?errf(T(<<"max_wait">>, <<"10">>)), - ?errf(T(<<"max_wait">>, -1)), - ?errf(T(<<"server_acks">>, -1))]. + ?eqf(M(inactivity, 10), T(<<"inactivity">>, 10)), + ?eqf(M(inactivity, infinity), T(<<"inactivity">>, <<"infinity">>)), + ?eqf(M(inactivity, 10), T(<<"inactivity">>, 10)), + ?eqf(M(max_wait, infinity), T(<<"max_wait">>, <<"infinity">>)), + ?eqf(M(server_acks, true), T(<<"server_acks">>, true)), + ?eqf(M(server_acks, false), T(<<"server_acks">>, false)), + ?eqf(M(maxpause, 10), T(<<"max_pause">>, 10)), + ?errf(T(<<"inactivity">>, -1)), + ?errf(T(<<"inactivity">>, <<"10">>)), + ?errf(T(<<"inactivity">>, <<"inactivity">>)), + ?errf(T(<<"max_wait">>, <<"10">>)), + ?errf(T(<<"max_wait">>, -1)), + ?errf(T(<<"server_acks">>, -1)), + ?errf(T(<<"maxpause">>, 0)). mod_caps(_Config) -> run_multi(mod_caps_cases()). diff --git a/test/config_parser_SUITE_data/modules.toml b/test/config_parser_SUITE_data/modules.toml index 49d4bbec87..68e180e49c 100644 --- a/test/config_parser_SUITE_data/modules.toml +++ b/test/config_parser_SUITE_data/modules.toml @@ -18,7 +18,7 @@ inactivity = 20 max_wait = "infinity" server_acks = true - maxpause = 120 + max_pause = 120 [modules.mod_caps] cache_size = 1000 From 96f915175cf28f74688f184bfce2bf1a6b6bc62c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Fri, 4 Dec 2020 09:32:11 +0100 Subject: [PATCH 044/104] Make the config of mod_caps declarative - Fix defaults and allowed values in docs - Fix validators which were accepting incorrect values - Make tests test the new validators - Simplify tests --- doc/modules/mod_caps.md | 10 +++++----- src/config/mongoose_config_parser_toml.erl | 7 ++----- src/config/mongoose_config_spec.erl | 4 ++-- src/config/mongoose_config_validator_toml.erl | 6 ------ src/mod_caps.erl | 13 ++++++++++++- test/config_parser_SUITE.erl | 15 ++++++--------- 6 files changed, 27 insertions(+), 28 deletions(-) diff --git a/doc/modules/mod_caps.md b/doc/modules/mod_caps.md index 747b7a0722..07bee4aeb5 100644 --- a/doc/modules/mod_caps.md +++ b/doc/modules/mod_caps.md @@ -8,16 +8,16 @@ It is not this module's responsibility to intercept and answer disco requests ro This module expects two optional arguments that apply to [cache tab](https://github.com/processone/cache_tab): #### `modules.mod_caps.cache_size` -* **Syntax:** non-negative integer +* **Syntax:** positive integer * **Default:** `1000` * **Example:** `cache_size = 2000` The size of a cache_tab (the amount of entries) holding the information about capabilities of each user. #### `modules.mod_caps.cache_life_time` -* **Syntax:** non-negative integer or the string `"infinity"` -* **Default:** `86` -* **Example:** `cache_life_time = 30` +* **Syntax:** positive integer +* **Default:** `86_400` (24 hours) +* **Example:** `cache_life_time = 10_000` Time (in seconds) after which entries will be removed. @@ -26,5 +26,5 @@ Time (in seconds) after which entries will be removed. ```toml [modules.mod_caps] cache_size = 2000 - cache_life_time = 86 + cache_life_time = 10_000 ``` diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index 71e0e95be6..0d7ee0f1ed 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -112,10 +112,6 @@ post_process_module(Mod, Opts) -> %% path: (host_config[].)modules.*.* -spec module_opt(path(), toml_value()) -> [option()]. -module_opt([<<"cache_size">>, <<"mod_caps">>|_], V) -> - [{cache_size, V}]; -module_opt([<<"cache_life_time">>, <<"mod_caps">>|_], V) -> - [{cache_life_time, V}]; module_opt([<<"buffer_max">>, <<"mod_csi">>|_], V) -> [{buffer_max, int_or_infinity(V)}]; module_opt([<<"extra_domains">>, <<"mod_disco">>|_] = Path, V) -> @@ -1129,7 +1125,8 @@ node_to_string(Node) -> [binary_to_list(Node)]. -define(HAS_NO_SPEC(Mod), Mod =/= <<"mod_adhoc">>, Mod =/= <<"mod_auth_token">>, - Mod =/= <<"mod_bosh">>). % TODO temporary, remove with 'handler/1' + Mod =/= <<"mod_bosh">>, + Mod =/= <<"mod_caps">>). % TODO temporary, remove with 'handler/1' -spec handler(path()) -> fun((path(), toml_value()) -> option()) | mongoose_config_spec:config_node(). diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index 0d9bd458ae..d953e7bbe9 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -728,8 +728,8 @@ modules() -> all_modules() -> [mod_adhoc, mod_auth_token, - mod_bosh%%, - %% mod_caps, + mod_bosh, + mod_caps%%, %% mod_csi, %% mod_disco, %% mod_event_pusher diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index 68a8ac808c..3b68026584 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -356,12 +356,6 @@ validate([<<"body">>, <<"welcome_message">>, <<"mod_register">>, <<"modules">>|_ validate([<<"subject">>, <<"welcome_message">>, <<"mod_register">>, <<"modules">>|_], [{subject, V}]) -> validate_string(V); -validate([<<"cache_life_time">>, <<"mod_caps">>, <<"modules">>|_], - [{cache_life_time, V}]) -> - validate_non_negative_integer_or_infinity(V); -validate([<<"cache_size">>, <<"mod_caps">>, <<"modules">>|_], - [{cache_size, V}]) -> - validate_non_negative_integer(V); validate([<<"type">>, _, <<"service">>, <<"mod_extdisco">>, <<"modules">>|_], [{type, V}]) -> validate_non_empty_atom(V); diff --git a/src/mod_caps.erl b/src/mod_caps.erl index 451e0d9e4d..3512758de7 100644 --- a/src/mod_caps.erl +++ b/src/mod_caps.erl @@ -39,7 +39,7 @@ disco_features/5, disco_identity/5, disco_info/5]). %% gen_mod callbacks --export([start/2, start_link/2, stop/1]). +-export([start/2, start_link/2, stop/1, config_spec/0]). %% gen_server callbacks -export([init/1, handle_info/2, handle_call/3, @@ -53,6 +53,7 @@ -export([delete_caps/1, make_disco_hash/2]). -include("mongoose.hrl"). +-include("ejabberd_config.hrl"). -include("jlib.hrl"). @@ -96,6 +97,16 @@ stop(Host) -> gen_server:call(Proc, stop), ejabberd_sup:stop_child(Proc). +-spec config_spec() -> mongoose_config_spec:config_section(). +config_spec() -> + #section{ + items = #{<<"cache_size">> => #option{type = integer, + validate = positive}, + <<"cache_life_time">> => #option{type = integer, + validate = positive} + } + }. + get_features_list(Host, Caps) -> case get_features(Host, Caps) of unknown -> []; diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index c8f80472b2..f962b11f4a 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -1381,17 +1381,14 @@ mod_bosh(_Config) -> ?errf(T(<<"maxpause">>, 0)). mod_caps(_Config) -> - run_multi(mod_caps_cases()). - -mod_caps_cases() -> T = fun(K, V) -> #{<<"modules">> => #{<<"mod_caps">> => #{K => V}}} end, M = fun(K, V) -> modopts(mod_caps, [{K, V}]) end, - [?_eqf(M(cache_size, 10), T(<<"cache_size">>, 10)), - ?_eqf(M(cache_life_time, 10), T(<<"cache_life_time">>, 10)), - ?_errf(T(<<"cache_size">>, -1)), - ?_errf(T(<<"cache_size">>, <<"infinity">>)), - ?_errf(T(<<"cache_life_time">>, -1)), - ?_errf(T(<<"cache_life_time">>, <<"cache_life_time">>))]. + ?eqf(M(cache_size, 10), T(<<"cache_size">>, 10)), + ?eqf(M(cache_life_time, 10), T(<<"cache_life_time">>, 10)), + ?errf(T(<<"cache_size">>, 0)), + ?errf(T(<<"cache_size">>, <<"infinity">>)), + ?errf(T(<<"cache_life_time">>, 0)), + ?errf(T(<<"cache_life_time">>, <<"infinity">>)). mod_carboncopy(_Config) -> check_iqdisc(mod_carboncopy). From d5df5e26d97d529d6216f9bc272ed2b874fd1997 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Fri, 4 Dec 2020 10:18:50 +0100 Subject: [PATCH 045/104] Make the config of mod_carboncopy declarative --- src/config/mongoose_config_parser_toml.erl | 3 ++- src/config/mongoose_config_spec.erl | 3 ++- src/config/mongoose_config_validator_toml.erl | 3 --- src/mod_carboncopy.erl | 7 ++++++- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index 0d7ee0f1ed..062414e52b 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -1126,7 +1126,8 @@ node_to_string(Node) -> [binary_to_list(Node)]. Mod =/= <<"mod_adhoc">>, Mod =/= <<"mod_auth_token">>, Mod =/= <<"mod_bosh">>, - Mod =/= <<"mod_caps">>). % TODO temporary, remove with 'handler/1' + Mod =/= <<"mod_caps">>, + Mod =/= <<"mod_carboncopy">>). % TODO temporary, remove with 'handler/1' -spec handler(path()) -> fun((path(), toml_value()) -> option()) | mongoose_config_spec:config_node(). diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index d953e7bbe9..689968cfd2 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -729,7 +729,8 @@ all_modules() -> [mod_adhoc, mod_auth_token, mod_bosh, - mod_caps%%, + mod_caps, + mod_carboncopy%%, %% mod_csi, %% mod_disco, %% mod_event_pusher diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index 3b68026584..d90a7d940f 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -809,9 +809,6 @@ validate([<<"rooms_in_rosters">>, <<"mod_muc_light">>, <<"modules">>|_], validate([<<"rooms_per_page">>, <<"mod_muc_light">>, <<"modules">>|_], [{rooms_per_page, V}]) -> validate_positive_integer_or_infinity(V); -validate([<<"iqdisc">>, <<"mod_carboncopy">>, <<"modules">>|_], - [{iqdisc, V}]) -> - validate_iqdisc(V); validate([<<"access_max_user_messages">>, <<"mod_offline">>, <<"modules">>|_], [{access_max_user_messages, V}]) -> validate_access_rule(V); diff --git a/src/mod_carboncopy.erl b/src/mod_carboncopy.erl index 6b8214eb7a..af1081ce64 100644 --- a/src/mod_carboncopy.erl +++ b/src/mod_carboncopy.erl @@ -32,6 +32,7 @@ %% API -export([start/2, stop/1, + config_spec/0, is_carbon_copy/1, classify_packet/1]). @@ -51,7 +52,7 @@ -include("mongoose.hrl"). -include("jlib.hrl"). -include_lib("session.hrl"). - +-include("ejabberd_config.hrl"). -type classification() :: 'ignore' | 'forward'. @@ -86,6 +87,10 @@ stop(Host) -> ejabberd_hooks:delete(user_receive_packet, Host, ?MODULE, user_receive_packet, 89), ejabberd_hooks:delete(unset_presence_hook, Host, ?MODULE, remove_connection, 10). +-spec config_spec() -> mongoose_config_spec:config_section(). +config_spec() -> + #section{items = #{<<"iqdisc">> => mongoose_config_spec:iqdisc()}}. + iq_handler2(From, To, Acc, IQ) -> iq_handler(From, To, Acc, IQ, ?NS_CC_2). iq_handler1(From, To, Acc, IQ) -> From a738ab9ea63506706b96d02389f7739e56c20a80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Fri, 4 Dec 2020 14:36:09 +0100 Subject: [PATCH 046/104] Make the config of mod_csi declarative --- src/config/mongoose_config_parser_toml.erl | 5 ++--- src/config/mongoose_config_spec.erl | 4 ++-- src/config/mongoose_config_validator_toml.erl | 3 --- src/mod_csi.erl | 9 +++++++++ test/config_parser_SUITE.erl | 9 +++------ 5 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index 062414e52b..d9f03edbdc 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -112,8 +112,6 @@ post_process_module(Mod, Opts) -> %% path: (host_config[].)modules.*.* -spec module_opt(path(), toml_value()) -> [option()]. -module_opt([<<"buffer_max">>, <<"mod_csi">>|_], V) -> - [{buffer_max, int_or_infinity(V)}]; module_opt([<<"extra_domains">>, <<"mod_disco">>|_] = Path, V) -> Domains = parse_list(Path, V), [{extra_domains, Domains}]; @@ -1127,7 +1125,8 @@ node_to_string(Node) -> [binary_to_list(Node)]. Mod =/= <<"mod_auth_token">>, Mod =/= <<"mod_bosh">>, Mod =/= <<"mod_caps">>, - Mod =/= <<"mod_carboncopy">>). % TODO temporary, remove with 'handler/1' + Mod =/= <<"mod_carboncopy">>, + Mod =/= <<"mod_csi">>). % TODO temporary, remove with 'handler/1' -spec handler(path()) -> fun((path(), toml_value()) -> option()) | mongoose_config_spec:config_node(). diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index 689968cfd2..9e07f03832 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -730,8 +730,8 @@ all_modules() -> mod_auth_token, mod_bosh, mod_caps, - mod_carboncopy%%, - %% mod_csi, + mod_carboncopy, + mod_csi%%, %% mod_disco, %% mod_event_pusher ]. diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index d90a7d940f..23044c64a4 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -653,9 +653,6 @@ validate([<<"proxy_port">>, <<"mod_jingle_sip">>, <<"modules">>|_], validate([<<"sdp_origin">>, <<"mod_jingle_sip">>, <<"modules">>|_], [{sdp_origin, V}]) -> validate_ip_address(V); -validate([<<"buffer_max">>, <<"mod_csi">>, <<"modules">>|_], - [{buffer_max, V}]) -> - validate_non_negative_integer_or_infinity(V); validate([<<"iqdisc">>, <<"mod_sic">>, <<"modules">>|_], [{iqdisc, V}]) -> validate_iqdisc(V); diff --git a/src/mod_csi.erl b/src/mod_csi.erl index 327980498c..99d160983d 100644 --- a/src/mod_csi.erl +++ b/src/mod_csi.erl @@ -9,9 +9,11 @@ -export([start/2]). -export([stop/1]). +-export([config_spec/0]). -export([add_csi_feature/2]). -include("jlib.hrl"). +-include("ejabberd_config.hrl"). -type state() :: active | inactive. @@ -27,6 +29,13 @@ stop(Host) -> {Name, Module, Function, Priority} <- hooks()], ok. +-spec config_spec() -> mongoose_config_spec:config_section(). +config_spec() -> + #section{ + items = #{<<"buffer_max">> => #option{type = int_or_infinity, + validate = non_negative}} + }. + hooks() -> [{c2s_stream_features, ?MODULE, add_csi_feature, 60}]. diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index f962b11f4a..f3c46ae85b 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -1394,14 +1394,11 @@ mod_carboncopy(_Config) -> check_iqdisc(mod_carboncopy). mod_csi(_Config) -> - run_multi(mod_csi_cases()). - -mod_csi_cases() -> T = fun(K, V) -> #{<<"modules">> => #{<<"mod_csi">> => #{K => V}}} end, M = fun(K, V) -> modopts(mod_csi, [{K, V}]) end, - [?_eqf(M(buffer_max, 10), T(<<"buffer_max">>, 10)), - ?_eqf(M(buffer_max, infinity), T(<<"buffer_max">>, <<"infinity">>)), - ?_errf(T(<<"buffer_max">>, -1))]. + ?eqf(M(buffer_max, 10), T(<<"buffer_max">>, 10)), + ?eqf(M(buffer_max, infinity), T(<<"buffer_max">>, <<"infinity">>)), + ?errf(T(<<"buffer_max">>, -1)). mod_disco(_Config) -> T = fun(K, V) -> #{<<"modules">> => #{<<"mod_disco">> => #{K => V}}} end, From 7361efcc3fc3943f94b7a0f889fef918b92ae7b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Fri, 4 Dec 2020 16:23:54 +0100 Subject: [PATCH 047/104] Make the config of mod_disco declarative - Rename 'module' in server_info to 'modules' It contains multiple modules and was named 'module' to avoid a name clash that no longer occurs. - Disambiguate the type of 'modules' Instead of allowing the atom 'all', make this key optional. - Fix validators and tests - Update docs --- doc/modules/mod_disco.md | 14 ++--- src/config/mongoose_config_parser_toml.erl | 28 +--------- src/config/mongoose_config_spec.erl | 4 +- src/config/mongoose_config_validator_toml.erl | 18 ------- src/mod_disco.erl | 31 +++++++++++ test/config_parser_SUITE.erl | 53 +++++++------------ test/config_parser_SUITE_data/modules.toml | 4 +- 7 files changed, 63 insertions(+), 89 deletions(-) diff --git a/doc/modules/mod_disco.md b/doc/modules/mod_disco.md index bb739ce497..d20b6484c6 100644 --- a/doc/modules/mod_disco.md +++ b/doc/modules/mod_disco.md @@ -23,17 +23,17 @@ Please note that `mod_disco` doesn't verify these domains, so if no handlers are * **Example:** ```toml server_info = [ - {module = "all", name = "abuse-address", urls = ["admin@example.com"]} + {name = "abuse-address", urls = ["admin@example.com"]} ] ``` Adds extra disco information to all or chosen modules. New fields will be added in a manner compliant with [XEP-0157](https://xmpp.org/extensions/xep-0157.html). -Required keys and their values for each entry: +Keys and their values for each entry: -* `module` - the string `"all"` or an array of module names for which the additional server information is to be returned -* `name` - a non-empty string with the name of the field -* `urls` - an array of valid addresses +* `name` - required, a non-empty string with the name of the field +* `urls` - required, an array of valid addresses +* `modules` - optional, an array of module names for which the additional server information is to be returned. By default the server information is returned for all modules. #### `modules.mod_disco.users_can_see_hidden_services` * **Syntax:** boolean @@ -51,8 +51,8 @@ will still receive full disco results. iqdisc.type = "one_queue" extra_domains = ["some_domain", "another_domain"] server_info = [ - {module = "all", name = "abuse-address", urls = ["admin@example.com"]}, - {module = ["mod_muc", "mod_disco"], name = "friendly-spirits", urls = ["spirit1@localhost", "spirit2@localhost"]} + {name = "abuse-address", urls = ["admin@example.com"]}, + {name = "friendly-spirits", urls = ["spirit1@localhost", "spirit2@localhost"], modules = ["mod_muc", "mod_disco"]} ] users_can_see_hidden_services = true ``` diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index d9f03edbdc..2a72840fdc 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -112,14 +112,6 @@ post_process_module(Mod, Opts) -> %% path: (host_config[].)modules.*.* -spec module_opt(path(), toml_value()) -> [option()]. -module_opt([<<"extra_domains">>, <<"mod_disco">>|_] = Path, V) -> - Domains = parse_list(Path, V), - [{extra_domains, Domains}]; -module_opt([<<"server_info">>, <<"mod_disco">>|_] = Path, V) -> - Info = parse_list(Path, V), - [{server_info, Info}]; -module_opt([<<"users_can_see_hidden_services">>, <<"mod_disco">>|_], V) -> - [{users_can_see_hidden_services, V}]; module_opt([<<"backend">>, <<"mod_event_pusher">>|_] = Path, V) -> Backends = parse_section(Path, V), [{backends, Backends}]; @@ -458,15 +450,6 @@ riak_opts([<<"search_index">>|_], V) -> mod_register_ip_access_rule(_, #{<<"address">> := Addr, <<"policy">> := Policy}) -> [{b2a(Policy), b2l(Addr)}]. --spec mod_disco_server_info(path(), toml_section()) -> [option()]. -mod_disco_server_info(Path, #{<<"module">> := <<"all">>, <<"name">> := Name, <<"urls">> := Urls}) -> - URLList = parse_list([<<"urls">> | Path], Urls), - [{all, b2l(Name), URLList}]; -mod_disco_server_info(Path, #{<<"module">> := Modules, <<"name">> := Name, <<"urls">> := Urls}) -> - Mods = parse_list([<<"module">> | Path], Modules), - URLList = parse_list([<<"urls">> | Path], Urls), - [{Mods, b2l(Name), URLList}]. - -spec mod_event_pusher_backend_sns(path(), toml_section()) -> [option()]. mod_event_pusher_backend_sns(Path, Opts) -> SnsOpts = parse_section(Path, Opts), @@ -1126,7 +1109,8 @@ node_to_string(Node) -> [binary_to_list(Node)]. Mod =/= <<"mod_bosh">>, Mod =/= <<"mod_caps">>, Mod =/= <<"mod_carboncopy">>, - Mod =/= <<"mod_csi">>). % TODO temporary, remove with 'handler/1' + Mod =/= <<"mod_csi">>, + Mod =/= <<"mod_disco">>). % TODO temporary, remove with 'handler/1' -spec handler(path()) -> fun((path(), toml_value()) -> option()) | mongoose_config_spec:config_node(). @@ -1144,14 +1128,6 @@ handler([_, <<"registration_watchers">>, <<"mod_register">>, <<"modules">>]) -> fun(_, V) -> [V] end; handler([_, <<"welcome_message">>, <<"mod_register">>, <<"modules">>]) -> fun welcome_message/2; -handler([_, <<"extra_domains">>, <<"mod_disco">>, <<"modules">>]) -> - fun(_, V) -> [V] end; -handler([_, <<"server_info">>, <<"mod_disco">>, <<"modules">>]) -> - fun mod_disco_server_info/2; -handler([_, <<"urls">>, _, <<"server_info">>, <<"mod_disco">>, <<"modules">>]) -> - fun(_, V) -> [b2l(V)] end; -handler([_, <<"module">>, _, <<"server_info">>, <<"mod_disco">>, <<"modules">>]) -> - fun(_, V) -> [b2a(V)] end; handler([<<"sns">>, <<"backend">>, <<"mod_event_pusher">>, <<"modules">>]) -> fun mod_event_pusher_backend_sns/2; handler([<<"push">>, <<"backend">>, <<"mod_event_pusher">>, <<"modules">>]) -> diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index 9e07f03832..b2e99ea3d0 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -731,8 +731,8 @@ all_modules() -> mod_bosh, mod_caps, mod_carboncopy, - mod_csi%%, - %% mod_disco, + mod_csi, + mod_disco%%, %% mod_event_pusher ]. diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index 23044c64a4..e0c894456c 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -755,24 +755,6 @@ validate([<<"remove_on_kicked">>, <<"mod_inbox">>, <<"modules">>|_], validate([item, <<"reset_markers">>, <<"mod_inbox">>, <<"modules">>|_], [V]) -> validate_chat_marker_type(V); -validate([item, <<"extra_domains">>, <<"mod_disco">>, <<"modules">>|_], - [V]) -> - validate_binary_domain(V); -validate([item, <<"module">>, item, <<"server_info">>, <<"mod_disco">>, <<"modules">>|_], - [V]) -> - validate_module(V); -validate([<<"name">>, item, <<"server_info">>, <<"mod_disco">>, <<"modules">>|_], - [V]) -> - validate_non_empty_binary(V); -validate([item, <<"urls">>, item, <<"server_info">>, <<"mod_disco">>, <<"modules">>|_], - [V]) -> - validate_url(V); -validate([item, <<"urls">>, <<"mod_disco">>, <<"modules">>|_], - [V]) -> - validate_url(V); -validate([<<"users_can_see_hidden_services">>, <<"mod_disco">>, <<"modules">>|_], - [{users_can_see_hidden_services, V}]) -> - validate_boolean(V); validate([<<"all_can_configure">>, <<"mod_muc_light">>, <<"modules">>|_], [{all_can_configure, V}]) -> validate_boolean(V); diff --git a/src/mod_disco.erl b/src/mod_disco.erl index 119eed1f01..934307f3db 100644 --- a/src/mod_disco.erl +++ b/src/mod_disco.erl @@ -32,6 +32,8 @@ -export([start/2, stop/1, + config_spec/0, + process_server_info/1, process_local_iq_items/4, process_local_iq_info/4, get_local_identity/5, @@ -52,6 +54,7 @@ -include("mongoose.hrl"). -include("jlib.hrl"). +-include("ejabberd_config.hrl"). -type feature() :: any(). @@ -76,6 +79,34 @@ stop(Host) -> unregister_host(Host), ejabberd_hooks:delete(disco_local_identity, Host, ?MODULE, get_local_identity, 100). +-spec config_spec() -> mongoose_config_spec:config_section(). +config_spec() -> + #section{ + items = #{<<"extra_domains">> => #list{items = #option{type = binary, + validate = domain}}, + <<"server_info">> => #list{items = server_info_spec()}, + <<"users_can_see_hidden_services">> => #option{type = boolean}, + <<"iqdisc">> => mongoose_config_spec:iqdisc() + } + }. + +server_info_spec() -> + #section{ + items = #{<<"name">> => #option{type = string, + validate = non_empty}, + <<"urls">> => #list{items = #option{type = string, + validate = url}}, + <<"modules">> => #list{items = #option{type = atom, + validate = module}} + }, + required = [<<"name">>, <<"urls">>], + process = fun ?MODULE:process_server_info/1 + }. + +process_server_info(KVs) -> + {[[{name, Name}], [{urls, URLs}]], _} = proplists:split(KVs, [name, urls]), + Modules = proplists:get_value(modules, KVs, all), + {Modules, Name, URLs}. register_subhost(Host, Subhost) -> case gen_mod:is_loaded(Host, ?MODULE) of diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index f3c46ae85b..6815379cd3 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -1401,6 +1401,7 @@ mod_csi(_Config) -> ?errf(T(<<"buffer_max">>, -1)). mod_disco(_Config) -> + check_iqdisc(mod_disco), T = fun(K, V) -> #{<<"modules">> => #{<<"mod_disco">> => #{K => V}}} end, ?eqf(modopts(mod_disco, [{users_can_see_hidden_services, true}]), T(<<"users_can_see_hidden_services">>, true)), @@ -1411,46 +1412,30 @@ mod_disco(_Config) -> T(<<"extra_domains">>, [<<"localhost">>, <<"erlang-solutions.com">>])), ?eqf(modopts(mod_disco, [{extra_domains, []}]), T(<<"extra_domains">>, [])), + Info = #{<<"name">> => <<"abuse-address">>, + <<"urls">> => [<<"admin@example.com">>]}, ?eqf(modopts(mod_disco, [{server_info, [{all, "abuse-address", ["admin@example.com"]}, {[mod_muc, mod_disco], "friendly-spirits", - ["spirit1@localhost", "spirit2@localhost"]}]} ]), - T(<<"server_info">>, [#{<<"module">> => <<"all">>, <<"name">> => <<"abuse-address">>, - <<"urls">> => [<<"admin@example.com">>]}, - #{<<"module">> => [<<"mod_muc">>, <<"mod_disco">>], - <<"name">> => <<"friendly-spirits">>, - <<"urls">> => [<<"spirit1@localhost">>, <<"spirit2@localhost">>]} ])), - %% Correct version, used as a prototype to make invalid versions -%% ?errf(T(<<"server_info">>, [#{<<"module">> => <<"all">>, <<"name">> => <<"abuse-address">>, -%% <<"urls">> => [<<"admin@example.com">>]}])), - %% Invalid name - ?errf(T(<<"server_info">>, [#{<<"module">> => <<"all">>, <<"name">> => 1, - <<"urls">> => [<<"admin@example.com">>]}])), - %% Mising name - ?errf(T(<<"server_info">>, [#{<<"module">> => <<"all">>, - <<"urls">> => [<<"admin@example.com">>]}])), - %% Invalid module - ?errf(T(<<"server_info">>, [#{<<"module">> => <<"roll">>, - <<"name">> => <<"abuse-address">>, - <<"urls">> => [<<"admin@example.com">>]}])), - %% Invalid module - ?errf(T(<<"server_info">>, [#{<<"module">> => [<<"meow_meow_meow">>], - <<"name">> => <<"abuse-address">>, - <<"urls">> => [<<"admin@example.com">>]}])), - %% Missing urls - ?errf(T(<<"server_info">>, [#{<<"module">> => <<"all">>, - <<"name">> => <<"abuse-address">>}])), - %% Missing module - ?errf(T(<<"server_info">>, [#{<<"name">> => <<"abuse-address">>, - <<"urls">> => [<<"admin@example.com">>]}])), - %% Invalid url - ?errf(T(<<"server_info">>, [#{<<"module">> => <<"all">>, - <<"name">> => <<"abuse-address">>, - <<"urls">> => [1]}])), + ["spirit1@localhost", "spirit2@localhost"]}]} + ]), + T(<<"server_info">>, [Info, #{<<"modules">> => [<<"mod_muc">>, <<"mod_disco">>], + <<"name">> => <<"friendly-spirits">>, + <<"urls">> => [<<"spirit1@localhost">>, + <<"spirit2@localhost">>]} + ])), ?errf(T(<<"users_can_see_hidden_services">>, 1)), ?errf(T(<<"users_can_see_hidden_services">>, <<"true">>)), ?errf(T(<<"extra_domains">>, [<<"user@localhost">>])), ?errf(T(<<"extra_domains">>, [1])), - ?errf(T(<<"extra_domains">>, <<"domains domains domains">>)). + ?errf(T(<<"extra_domains">>, <<"domains domains domains">>)), + ?errf(T(<<"server_info">>, [Info#{<<"name">> => 1}])), + ?errf(T(<<"server_info">>, [Info#{<<"name">> => <<"">>}])), + ?errf(T(<<"server_info">>, [Info#{<<"modules">> => <<"roll">>}])), + ?errf(T(<<"server_info">>, [Info#{<<"modules">> => [<<"meow_meow_meow">>]}])), + ?errf(T(<<"server_info">>, [Info#{<<"urls">> => [1]}])), + ?errf(T(<<"server_info">>, [Info#{<<"urls">> => [<<"">>]}])), + ?errf(T(<<"server_info">>, [maps:remove(<<"name">>, Info)])), + ?errf(T(<<"server_info">>, [maps:remove(<<"urls">>, Info)])). mod_extdisco(_Config) -> T = fun(Opts) -> #{<<"modules">> => #{<<"mod_extdisco">> => Opts}} end, diff --git a/test/config_parser_SUITE_data/modules.toml b/test/config_parser_SUITE_data/modules.toml index 68e180e49c..c5fd9ead94 100644 --- a/test/config_parser_SUITE_data/modules.toml +++ b/test/config_parser_SUITE_data/modules.toml @@ -34,8 +34,8 @@ iqdisc.type = "one_queue" extra_domains = ["some_domain", "another_domain"] server_info = [ - {module = "all", name = "abuse-address", urls = ["admin@example.com"]}, - {module = ["mod_muc", "mod_disco"], name = "friendly-spirits", urls = ["spirit1@localhost", "spirit2@localhost"]} + {name = "abuse-address", urls = ["admin@example.com"]}, + {name = "friendly-spirits", urls = ["spirit1@localhost", "spirit2@localhost"], modules = ["mod_muc", "mod_disco"]} ] users_can_see_hidden_services = true From 281595d73ad5636886e2bcd63b42e5fab407d42f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Mon, 7 Dec 2020 13:45:01 +0100 Subject: [PATCH 048/104] Make the config of mod_event_pusher declarative - Split the config spec by backend - Remove the 'muc_host' option which was unused in the code - Use '_' as a separator in the '{module, Prefix}' validator to allow using the ?MODULE macro - Move the check for mandatory sns options to the config spec --- src/config/mongoose_config_parser_toml.erl | 130 +---------------- src/config/mongoose_config_spec.erl | 37 ++--- src/config/mongoose_config_validator_toml.erl | 132 +----------------- src/event_pusher/mod_event_pusher.erl | 16 ++- src/event_pusher/mod_event_pusher_http.erl | 13 +- src/event_pusher/mod_event_pusher_push.erl | 16 ++- src/event_pusher/mod_event_pusher_rabbit.erl | 25 +++- src/event_pusher/mod_event_pusher_sns.erl | 38 +++-- test/config_parser_SUITE_data/modules.options | 4 - test/config_parser_SUITE_data/modules.toml | 1 - 10 files changed, 117 insertions(+), 295 deletions(-) diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index 2a72840fdc..e45e4869cb 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -92,12 +92,6 @@ parse_root(Path, Content) -> ensure_keys([<<"general">>], Content), parse_section(Path, Content). -%% path: (host_config[].)modules.mod_event_pusher.backend.push.wpool.* --spec pool_option(path(), toml_value()) -> [option()]. -pool_option([<<"workers">>|_], V) -> [{workers, V}]; -pool_option([<<"strategy">>|_], V) -> [{strategy, b2a(V)}]; -pool_option([<<"call_timeout">>|_], V) -> [{call_timeout, V}]. - %% path: (host_config[].)modules.* -spec process_module(path(), toml_section()) -> [option()]. process_module([Mod|_] = Path, Opts) -> @@ -112,9 +106,6 @@ post_process_module(Mod, Opts) -> %% path: (host_config[].)modules.*.* -spec module_opt(path(), toml_value()) -> [option()]. -module_opt([<<"backend">>, <<"mod_event_pusher">>|_] = Path, V) -> - Backends = parse_section(Path, V), - [{backends, Backends}]; module_opt([<<"service">>, <<"mod_extdisco">>|_] = Path, V) -> parse_list(Path, V); module_opt([<<"host">>, <<"mod_http_upload">>|_], V) -> @@ -450,98 +441,6 @@ riak_opts([<<"search_index">>|_], V) -> mod_register_ip_access_rule(_, #{<<"address">> := Addr, <<"policy">> := Policy}) -> [{b2a(Policy), b2l(Addr)}]. --spec mod_event_pusher_backend_sns(path(), toml_section()) -> [option()]. -mod_event_pusher_backend_sns(Path, Opts) -> - SnsOpts = parse_section(Path, Opts), - [{sns, SnsOpts}]. - --spec mod_event_pusher_backend_push(path(), toml_section()) -> [option()]. -mod_event_pusher_backend_push(Path, Opts) -> - PushOpts = parse_section(Path, Opts), - [{push, PushOpts}]. - --spec mod_event_pusher_backend_http(path(), toml_section()) -> [option()]. -mod_event_pusher_backend_http(Path, Opts) -> - HttpOpts = parse_section(Path, Opts), - [{http, HttpOpts}]. - --spec mod_event_pusher_backend_rabbit(path(), toml_section()) -> [option()]. -mod_event_pusher_backend_rabbit(Path, Opts) -> - ROpts = parse_section(Path, Opts), - [{rabbit, ROpts}]. - --spec mod_event_pusher_backend_sns_opts(path(), toml_value()) -> [option()]. -mod_event_pusher_backend_sns_opts([<<"presence_updates_topic">>|_], V) -> - [{presence_updates_topic, b2l(V)}]; -mod_event_pusher_backend_sns_opts([<<"pm_messages_topic">>|_], V) -> - [{pm_messages_topic, b2l(V)}]; -mod_event_pusher_backend_sns_opts([<<"muc_messages_topic">>|_], V) -> - [{muc_messages_topic, b2l(V)}]; -mod_event_pusher_backend_sns_opts([<<"plugin_module">>|_], V) -> - [{plugin_module, b2a(V)}]; -mod_event_pusher_backend_sns_opts([<<"muc_host">>|_], V) -> - [{muc_host, b2l(V)}]; -mod_event_pusher_backend_sns_opts([<<"sns_host">>|_], V) -> - [{sns_host, b2l(V)}]; -mod_event_pusher_backend_sns_opts([<<"region">>|_], V) -> - [{region, b2l(V)}]; -mod_event_pusher_backend_sns_opts([<<"access_key_id">>|_], V) -> - [{access_key_id, b2l(V)}]; -mod_event_pusher_backend_sns_opts([<<"secret_access_key">>|_], V) -> - [{secret_access_key, b2l(V)}]; -mod_event_pusher_backend_sns_opts([<<"account_id">>|_], V) -> - [{account_id, b2l(V)}]; -mod_event_pusher_backend_sns_opts([<<"pool_size">>|_], V) -> - [{pool_size, V}]; -mod_event_pusher_backend_sns_opts([<<"publish_retry_count">>|_], V) -> - [{publish_retry_count, V}]; -mod_event_pusher_backend_sns_opts([<<"publish_retry_time_ms">>|_], V) -> - [{publish_retry_time_ms, V}]. - --spec mod_event_pusher_backend_push_opts(path(), toml_value()) -> [option()]. -mod_event_pusher_backend_push_opts([<<"backend">>|_], V) -> - [{backend, b2a(V)}]; -mod_event_pusher_backend_push_opts([<<"wpool">>|_] = Path, V) -> - WpoolOpts = parse_section(Path, V), - [{wpool, WpoolOpts}]; -mod_event_pusher_backend_push_opts([<<"plugin_module">>|_], V) -> - [{plugin_module, b2a(V)}]; -mod_event_pusher_backend_push_opts([<<"virtual_pubsub_hosts">> |_] = Path, V) -> - VPH = parse_list(Path, V), - [{virtual_pubsub_hosts, VPH}]. - --spec mod_event_pusher_backend_http_opts(path(), toml_value()) -> [option()]. -mod_event_pusher_backend_http_opts([<<"pool_name">>|_], V) -> - [{pool_name, b2a(V)}]; -mod_event_pusher_backend_http_opts([<<"path">>|_], V) -> - [{path, b2l(V)}]; -mod_event_pusher_backend_http_opts([<<"callback_module">>|_], V) -> - [{callback_module, b2a(V)}]. - --spec mod_event_pusher_backend_rabbit_opts(path(), toml_value()) -> [option()]. -mod_event_pusher_backend_rabbit_opts([<<"presence_exchange">>|_] = Path, V) -> - [{presence_exchange, parse_section(Path, V)}]; -mod_event_pusher_backend_rabbit_opts([<<"chat_msg_exchange">>|_] = Path, V) -> - [{chat_msg_exchange, parse_section(Path, V)}]; -mod_event_pusher_backend_rabbit_opts([<<"groupchat_msg_exchange">>|_] = Path, V) -> - [{groupchat_msg_exchange, parse_section(Path, V)}]. - --spec mod_event_pusher_rabbit_presence_ex(path(), toml_value()) -> [option()]. -mod_event_pusher_rabbit_presence_ex([<<"name">>|_], V) -> - [{name, V}]; -mod_event_pusher_rabbit_presence_ex([<<"type">>|_], V) -> - [{type, V}]. - --spec mod_event_pusher_rabbit_msg_ex(path(), toml_value()) -> [option()]. -mod_event_pusher_rabbit_msg_ex([<<"name">>|_], V) -> - [{name, V}]; -mod_event_pusher_rabbit_msg_ex([<<"type">>|_], V) -> - [{type, V}]; -mod_event_pusher_rabbit_msg_ex([<<"sent_topic">>|_], V) -> - [{sent_topic, V}]; -mod_event_pusher_rabbit_msg_ex([<<"recv_topic">>|_], V) -> - [{recv_topic, V}]. - -spec mod_extdisco_service(path(), toml_value()) -> [option()]. mod_extdisco_service([_, <<"service">>|_] = Path, V) -> [parse_section(Path, V)]; @@ -1110,7 +1009,8 @@ node_to_string(Node) -> [binary_to_list(Node)]. Mod =/= <<"mod_caps">>, Mod =/= <<"mod_carboncopy">>, Mod =/= <<"mod_csi">>, - Mod =/= <<"mod_disco">>). % TODO temporary, remove with 'handler/1' + Mod =/= <<"mod_disco">>, + Mod =/= <<"mod_event_pusher">>). % TODO temporary, remove with 'handler/1' -spec handler(path()) -> fun((path(), toml_value()) -> option()) | mongoose_config_spec:config_node(). @@ -1128,32 +1028,6 @@ handler([_, <<"registration_watchers">>, <<"mod_register">>, <<"modules">>]) -> fun(_, V) -> [V] end; handler([_, <<"welcome_message">>, <<"mod_register">>, <<"modules">>]) -> fun welcome_message/2; -handler([<<"sns">>, <<"backend">>, <<"mod_event_pusher">>, <<"modules">>]) -> - fun mod_event_pusher_backend_sns/2; -handler([<<"push">>, <<"backend">>, <<"mod_event_pusher">>, <<"modules">>]) -> - fun mod_event_pusher_backend_push/2; -handler([<<"http">>, <<"backend">>, <<"mod_event_pusher">>, <<"modules">>]) -> - fun mod_event_pusher_backend_http/2; -handler([<<"rabbit">>, <<"backend">>, <<"mod_event_pusher">>, <<"modules">>]) -> - fun mod_event_pusher_backend_rabbit/2; -handler([_, <<"sns">>, <<"backend">>, <<"mod_event_pusher">>, <<"modules">>]) -> - fun mod_event_pusher_backend_sns_opts/2; -handler([_, <<"push">>, <<"backend">>, <<"mod_event_pusher">>, <<"modules">>]) -> - fun mod_event_pusher_backend_push_opts/2; -handler([_, <<"http">>, <<"backend">>, <<"mod_event_pusher">>, <<"modules">>]) -> - fun mod_event_pusher_backend_http_opts/2; -handler([_, <<"rabbit">>, <<"backend">>, <<"mod_event_pusher">>, <<"modules">>]) -> - fun mod_event_pusher_backend_rabbit_opts/2; -handler([_,<<"wpool">>, <<"push">>, <<"backend">>, <<"mod_event_pusher">>, <<"modules">>]) -> - fun pool_option/2; -handler([_,<<"virtual_pubsub_hosts">>, <<"push">>, <<"backend">>, <<"mod_event_pusher">>, <<"modules">>]) -> - fun (_, V) -> [b2l(V)] end; -handler([_,<<"presence_exchange">>, <<"rabbit">>, <<"backend">>, <<"mod_event_pusher">>, <<"modules">>]) -> - fun mod_event_pusher_rabbit_presence_ex/2; -handler([_,<<"chat_msg_exchange">>, <<"rabbit">>, <<"backend">>, <<"mod_event_pusher">>, <<"modules">>]) -> - fun mod_event_pusher_rabbit_msg_ex/2; -handler([_,<<"groupchat_msg_exchange">>, <<"rabbit">>, <<"backend">>, <<"mod_event_pusher">>, <<"modules">>]) -> - fun mod_event_pusher_rabbit_msg_ex/2; handler([_, <<"service">>, <<"mod_extdisco">>, <<"modules">>]) -> fun mod_extdisco_service/2; handler([_, _, <<"service">>, <<"mod_extdisco">>, <<"modules">>]) -> diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index b2e99ea3d0..30cc732c1e 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -67,7 +67,7 @@ general() -> <<"all_metrics_are_global">> => #option{type = boolean, format = local_config}, <<"sm_backend">> => #option{type = atom, - validate = {module, ejabberd_sm_}, + validate = {module, ejabberd_sm}, process = fun ?MODULE:process_sm_backend/1, format = config}, <<"max_fsm_queue">> => #option{type = integer, @@ -339,7 +339,7 @@ http_handler_required(_) -> []. auth() -> #section{ items = #{<<"methods">> => #list{items = #option{type = atom, - validate = {module, ejabberd_auth_}}, + validate = {module, ejabberd_auth}}, format = {kv, auth_method}}, <<"password">> => auth_password(), <<"scram_iterations">> => #option{type = integer, @@ -350,7 +350,7 @@ auth() -> format = {kv, cyrsasl_external}}, <<"sasl_mechanisms">> => #list{items = #option{type = atom, - validate = {module, cyrsasl_}, + validate = {module, cyrsasl}, process = fun ?MODULE:process_sasl_mechanism/1}}, <<"anonymous">> => auth_anonymous(), <<"external">> => auth_external(), @@ -518,23 +518,27 @@ outgoing_pools() -> %% path: outgoing_pools.*.* outgoing_pool(Type) -> + WPool = wpool_items(), #section{ - items = #{<<"scope">> => #option{type = atom, - validate = {enum, [global, host, single_host]}}, - <<"host">> => #option{type = binary, - validate = non_empty}, - <<"connection">> => outgoing_pool_connection(Type), - <<"workers">> => #option{type = integer, - validate = positive}, - <<"strategy">> => #option{type = atom, - validate = {enum, wpool_strategy_values()}}, - <<"call_timeout">> => #option{type = integer, - validate = positive} + items = WPool#{<<"scope">> => #option{type = atom, + validate = {enum, [global, host, single_host]}}, + <<"host">> => #option{type = binary, + validate = non_empty}, + <<"connection">> => outgoing_pool_connection(Type) }, process = fun ?MODULE:process_pool/2, format = item }. +wpool_items() -> + #{<<"workers">> => #option{type = integer, + validate = positive}, + <<"strategy">> => #option{type = atom, + validate = {enum, wpool_strategy_values()}}, + <<"call_timeout">> => #option{type = integer, + validate = positive} + }. + %% path: outgoing_pools.*.*.connection outgoing_pool_connection(<<"cassandra">>) -> #section{ @@ -732,9 +736,8 @@ all_modules() -> mod_caps, mod_carboncopy, mod_csi, - mod_disco%%, - %% mod_event_pusher - ]. + mod_disco, + mod_event_pusher]. %% path: (host_config[].)modules.*.iqdisc iqdisc() -> diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index e0c894456c..00456808a5 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -18,130 +18,6 @@ validate(Path, [F]) when is_function(F, 1) -> validate(Path, F(?HOST)); %% Modules -validate([<<"callback_module">>, <<"http">>, <<"backend">>, - <<"mod_event_pusher">>, <<"modules">>|_], - [{callback_module, V}]) -> - validate_module(V); -validate([<<"path">>, <<"http">>, <<"backend">>, - <<"mod_event_pusher">>, <<"modules">>|_], - [{path, V}]) -> - validate_string(V); -validate([<<"pool_name">>, <<"http">>, <<"backend">>, - <<"mod_event_pusher">>, <<"modules">>|_], - [{pool_name, V}]) -> - validate_pool_name(V); -validate([<<"backend">>, <<"push">>, <<"backend">>, - <<"mod_event_pusher">>, <<"modules">>|_], - [{backend, V}]) -> - validate_backend(mod_event_pusher_push, V); -validate([<<"plugin_module">>, <<"push">>, <<"backend">>, - <<"mod_event_pusher">>, <<"modules">>|_], - [{plugin_module, V}]) -> - validate_module(V); -validate([item, <<"virtual_pubsub_hosts">>, <<"push">>, <<"backend">>, - <<"mod_event_pusher">>, <<"modules">>|_], - [V]) -> - validate_domain_template(V); -validate([<<"strategy">>, <<"wpool">>, <<"push">>, <<"backend">>, - <<"mod_event_pusher">>, <<"modules">>|_], - [{strategy, V}]) -> - validate_wpool_strategy(V); -validate([<<"workers">>, <<"wpool">>, <<"push">>, <<"backend">>, - <<"mod_event_pusher">>, <<"modules">>|_], - [{workers, V}]) -> - validate_positive_integer(V); -validate([<<"name">>, <<"chat_msg_exchange">>, <<"rabbit">>, <<"backend">>, - <<"mod_event_pusher">>, <<"modules">>|_], - [{name, V}]) -> - validate_non_empty_binary(V); -validate([<<"type">>, <<"chat_msg_exchange">>, <<"rabbit">>, <<"backend">>, - <<"mod_event_pusher">>, <<"modules">>|_], - [{type, V}]) -> - validate_non_empty_binary(V); -validate([<<"recv_topic">>, <<"chat_msg_exchange">>, <<"rabbit">>, <<"backend">>, - <<"mod_event_pusher">>, <<"modules">>|_], - [{recv_topic, V}]) -> - validate_non_empty_binary(V); -validate([<<"sent_topic">>, <<"chat_msg_exchange">>, <<"rabbit">>, <<"backend">>, - <<"mod_event_pusher">>, <<"modules">>|_], - [{sent_topic, V}]) -> - validate_non_empty_binary(V); -validate([<<"name">>, <<"groupchat_msg_exchange">>, <<"rabbit">>, <<"backend">>, - <<"mod_event_pusher">>, <<"modules">>|_], - [{name, V}]) -> - validate_non_empty_binary(V); -validate([<<"type">>, <<"groupchat_msg_exchange">>, <<"rabbit">>, <<"backend">>, - <<"mod_event_pusher">>, <<"modules">>|_], - [{type, V}]) -> - validate_non_empty_binary(V); -validate([<<"recv_topic">>, <<"groupchat_msg_exchange">>, <<"rabbit">>, <<"backend">>, - <<"mod_event_pusher">>, <<"modules">>|_], - [{recv_topic, V}]) -> - validate_non_empty_binary(V); -validate([<<"sent_topic">>, <<"groupchat_msg_exchange">>, <<"rabbit">>, <<"backend">>, - <<"mod_event_pusher">>, <<"modules">>|_], - [{sent_topic, V}]) -> - validate_non_empty_binary(V); -validate([<<"name">>, <<"presence_exchange">>, <<"rabbit">>, <<"backend">>, - <<"mod_event_pusher">>, <<"modules">>|_], - [{name, V}]) -> - validate_non_empty_binary(V); -validate([<<"type">>, <<"presence_exchange">>, <<"rabbit">>, <<"backend">>, - <<"mod_event_pusher">>, <<"modules">>|_], - [{type, V}]) -> - validate_non_empty_binary(V); -validate([<<"access_key_id">>, <<"sns">>, <<"backend">>, - <<"mod_event_pusher">>, <<"modules">>|_], - [{access_key_id, V}]) -> - validate_string(V); -validate([<<"account_id">>, <<"sns">>, <<"backend">>, - <<"mod_event_pusher">>, <<"modules">>|_], - [{account_id, V}]) -> - validate_string(V); -validate([<<"muc_host">>, <<"sns">>, <<"backend">>, - <<"mod_event_pusher">>, <<"modules">>|_], - [{muc_host, V}]) -> - validate_domain_template(V); -validate([<<"muc_messages_topic">>, <<"sns">>, <<"backend">>, - <<"mod_event_pusher">>, <<"modules">>|_], - [{muc_messages_topic, V}]) -> - validate_string(V); -validate([<<"plugin_module">>, <<"sns">>, <<"backend">>, - <<"mod_event_pusher">>, <<"modules">>|_], - [{plugin_module, V}]) -> - validate_module(V); -validate([<<"pm_messages_topic">>, <<"sns">>, <<"backend">>, - <<"mod_event_pusher">>, <<"modules">>|_], - [{pm_messages_topic, V}]) -> - validate_string(V); -validate([<<"pool_size">>, <<"sns">>, <<"backend">>, - <<"mod_event_pusher">>, <<"modules">>|_], - [{pool_size, V}]) -> - validate_non_negative_integer(V); -validate([<<"presence_updates_topic">>, <<"sns">>, <<"backend">>, - <<"mod_event_pusher">>, <<"modules">>|_], - [{presence_updates_topic, V}]) -> - validate_string(V); -validate([<<"publish_retry_count">>, <<"sns">>, <<"backend">>, - <<"mod_event_pusher">>, <<"modules">>|_], - [{publish_retry_count, V}]) -> - validate_non_negative_integer(V); -validate([<<"publish_retry_time_ms">>, <<"sns">>, <<"backend">>, - <<"mod_event_pusher">>, <<"modules">>|_], - [{publish_retry_time_ms, V}]) -> - validate_non_negative_integer(V); -validate([<<"region">>, <<"sns">>, <<"backend">>, - <<"mod_event_pusher">>, <<"modules">>|_], - [{region, V}]) -> - validate_string(V); -validate([<<"secret_access_key">>, <<"sns">>, <<"backend">>, - <<"mod_event_pusher">>, <<"modules">>|_], - [{secret_access_key, V}]) -> - validate_string(V); -validate([<<"sns_host">>, <<"sns">>, <<"backend">>, - <<"mod_event_pusher">>, <<"modules">>|_], - [{sns_host, V}]) -> - validate_string(V); validate([<<"enabled">>, <<"bounce">>, <<"mod_global_distrib">>, <<"modules">>|_], [{enabled, true}]) -> @@ -1106,12 +982,14 @@ validate(V, integer, port) -> validate_port(V); validate(V, int_or_infinity, non_negative) -> validate_non_negative_integer_or_infinity(V); validate(V, int_or_infinity, positive) -> validate_positive_integer_or_infinity(V); validate(V, string, url) -> validate_url(V); +validate(V, string, domain_template) -> validate_domain_template(V); validate(V, string, ip_address) -> validate_ip_address(V); validate(V, string, non_empty) -> validate_non_empty_string(V); validate(V, atom, module) -> validate_module(V); validate(V, atom, {module, Prefix}) -> - validate_module(list_to_atom(atom_to_list(Prefix) ++ atom_to_list(V))); + validate_module(list_to_atom(atom_to_list(Prefix) ++ "_" ++ atom_to_list(V))); validate(V, atom, loglevel) -> validate_loglevel(V); +validate(V, atom, pool_name) -> validate_non_empty_atom(V); validate(V, atom, non_empty) -> validate_non_empty_atom(V); validate(V, _, {enum, Values}) -> validate_enum(V, Values); validate(_V, _, any) -> ok. @@ -1293,10 +1171,6 @@ validate_network_port(Value) -> validate_range(Value, Min, Max) when Value >= Min, Value =< Max -> ok. -validate_wpool_strategy(Value) -> - validate_enum(Value, [best_worker, random_worker, next_worker, - available_worker, next_available_worker]). - validate_filename(Filename) -> case file:read_file_info(Filename) of {ok, _} -> diff --git a/src/event_pusher/mod_event_pusher.erl b/src/event_pusher/mod_event_pusher.erl index 7cfefd17e9..a1c9d3f62f 100644 --- a/src/event_pusher/mod_event_pusher.erl +++ b/src/event_pusher/mod_event_pusher.erl @@ -22,11 +22,12 @@ -include("jlib.hrl"). -include("mod_event_pusher_events.hrl"). +-include("ejabberd_config.hrl"). -type event() :: #user_status_event{} | #chat_event{} | #unack_msg_event{}. -export_type([event/0]). --export([deps/2, start/2, stop/1, push_event/3]). +-export([deps/2, start/2, stop/1, config_spec/0, push_event/3]). -export([config_metrics/1]). @@ -68,6 +69,16 @@ start(Host, Opts) -> stop(Host) -> ets:delete(ets_name(Host)). +-spec config_spec() -> mongoose_config_spec:config_section(). +config_spec() -> + BackendItems = [{atom_to_binary(B, utf8), + (translate_backend(B)):config_spec()} || B <- all_backends()], + #section{ + items = #{<<"backend">> => #section{items = maps:from_list(BackendItems), + format = {kv, backends}} + } + }. + %%-------------------------------------------------------------------- %% Helpers %%-------------------------------------------------------------------- @@ -144,3 +155,6 @@ get_backend(BackendsWithOpts, Backend) -> Backend -> {backend, Backend} end. + +all_backends() -> + [sns, push, http, rabbit]. diff --git a/src/event_pusher/mod_event_pusher_http.erl b/src/event_pusher/mod_event_pusher_http.erl index 691748707b..2a30335bc1 100644 --- a/src/event_pusher/mod_event_pusher_http.erl +++ b/src/event_pusher/mod_event_pusher_http.erl @@ -37,10 +37,11 @@ -include("jlib.hrl"). %% API --export([start/2, stop/1, push_event/3]). +-export([start/2, stop/1, config_spec/0, push_event/3]). -include("mongoose.hrl"). -include("jlib.hrl"). +-include("ejabberd_config.hrl"). -define(DEFAULT_POOL_NAME, http_pool). -define(DEFAULT_PATH, ""). @@ -56,6 +57,16 @@ start(Host, _Opts) -> stop(_Host) -> ok. +config_spec() -> + #section{ + items = #{<<"pool_name">> => #option{type = atom, + validate = pool_name}, + <<"path">> => #option{type = string}, + <<"callback_module">> => #option{type = atom, + validate = module} + } + }. + push_event(Acc, _Host, #chat_event{direction = Dir, from = From, to = To, packet = Packet}) -> lists:map(fun(Opts) -> push_event(Acc, Dir, From, To, Packet, Opts) end, gen_mod:get_module_opt(From#jid.lserver, ?MODULE, configs, [])), diff --git a/src/event_pusher/mod_event_pusher_push.erl b/src/event_pusher/mod_event_pusher_push.erl index 3d0e847623..61361c5f8f 100644 --- a/src/event_pusher/mod_event_pusher_push.erl +++ b/src/event_pusher/mod_event_pusher_push.erl @@ -19,6 +19,7 @@ -include("mod_event_pusher_events.hrl"). -include("mongoose.hrl"). -include("jlib.hrl"). +-include("ejabberd_config.hrl"). -define(SESSION_KEY, publish_service). @@ -27,7 +28,7 @@ %%-------------------------------------------------------------------- %% gen_mod behaviour --export([start/2, stop/1]). +-export([start/2, stop/1, config_spec/0]). %% mod_event_pusher behaviour -export([push_event/3]). @@ -108,6 +109,19 @@ stop(Host) -> mongoose_wpool:stop(generic, Host, pusher_push), ok. +-spec config_spec() -> mongoose_config_spec:config_section(). +config_spec() -> + #section{ + items = #{<<"backend">> => #option{type = atom, + validate = {module, ?MODULE}}, + <<"wpool">> => #section{items = mongoose_config_spec:wpool_items()}, + <<"plugin_module">> => #option{type = atom, + validate = module}, + <<"virtual_pubsub_hosts">> => #list{items = #option{type = string, + validate = domain_template}} + } + }. + %%-------------------------------------------------------------------- %% mod_event_pusher callbacks %%-------------------------------------------------------------------- diff --git a/src/event_pusher/mod_event_pusher_rabbit.erl b/src/event_pusher/mod_event_pusher_rabbit.erl index 4e400f6f70..fac9a97425 100644 --- a/src/event_pusher/mod_event_pusher_rabbit.erl +++ b/src/event_pusher/mod_event_pusher_rabbit.erl @@ -23,6 +23,7 @@ -include_lib("mongooseim/include/mongoose.hrl"). -include_lib("mongooseim/include/mod_event_pusher_events.hrl"). +-include("ejabberd_config.hrl"). -behaviour(gen_mod). -behaviour(mongoose_module_metrics). @@ -50,7 +51,7 @@ %%%=================================================================== %% MIM module callbacks --export([start/2, stop/1]). +-export([start/2, stop/1, config_spec/0]). %% API -export([push_event/3]). @@ -69,6 +70,28 @@ start(Host, _Opts) -> stop(_Host) -> ok. +-spec config_spec() -> mongoose_config_spec:config_section(). +config_spec() -> + #section{ + items = #{<<"presence_exchange">> => #section{items = exch_items()}, + <<"chat_msg_exchange">> => #section{items = msg_exch_items()}, + <<"groupchat_msg_exchange">> => #section{items = msg_exch_items()} + } + }. + +msg_exch_items() -> + ExchItems = exch_items(), + ExchItems#{<<"sent_topic">> => #option{type = binary, + validate = non_empty}, + <<"recv_topic">> => #option{type = binary, + validate = non_empty}}. + +exch_items() -> + #{<<"name">> => #option{type = binary, + validate = non_empty}, + <<"type">> => #option{type = binary, + validate = non_empty}}. + push_event(Acc, _, #user_status_event{jid = UserJID, status = Status}) -> handle_user_presence_change(UserJID, Status), Acc; diff --git a/src/event_pusher/mod_event_pusher_sns.erl b/src/event_pusher/mod_event_pusher_sns.erl index e40f8bd577..1acf543716 100644 --- a/src/event_pusher/mod_event_pusher_sns.erl +++ b/src/event_pusher/mod_event_pusher_sns.erl @@ -7,6 +7,7 @@ -include("mongoose.hrl"). -include("jlib.hrl"). -include_lib("erlcloud/include/erlcloud_aws.hrl"). +-include("ejabberd_config.hrl"). -callback user_guid(UserJID :: jid:jid()) -> user_guid(). -callback message_attributes(TopicARN :: topic_arn(), UserJID :: jid:jid(), @@ -32,7 +33,7 @@ %%%=================================================================== %% MIM module callbacks --export([start/2, stop/1]). +-export([start/2, stop/1, config_spec/0]). %% API -export([try_publish/5, push_event/3]). @@ -49,17 +50,6 @@ start(Host, Opts) -> {ok, _} = mongoose_wpool:start(generic, Host, pusher_sns, [{workers, WorkerNum}, {strategy, available_worker}]), - %% Check for required options - RequiredOptions = [access_key_id, secret_access_key, region, account_id, sns_host], - lists:foreach( - fun(Option) -> - case opt(Host, Option) of - undefined -> - error({missing_required_option, Option}); - _ -> ok - end - end, RequiredOptions), - ok. -spec stop(Host :: jid:server()) -> ok. @@ -67,6 +57,30 @@ stop(Host) -> mongoose_wpool:stop(generic, Host, pusher_sns), ok. +-spec config_spec() -> mongoose_config_spec:config_section(). +config_spec() -> + #section{ + items = #{<<"presence_updates_topic">> => #option{type = string}, + <<"pm_messages_topic">> => #option{type = string}, + <<"muc_messages_topic">> => #option{type = string}, + <<"plugin_module">> => #option{type = atom, + validate = module}, + <<"sns_host">> => #option{type = string}, + <<"region">> => #option{type = string}, + <<"access_key_id">> => #option{type = string}, + <<"secret_access_key">> => #option{type = string}, + <<"account_id">> => #option{type = string}, + <<"pool_size">> => #option{type = integer, + validate = positive}, + <<"publish_retry_count">> => #option{type = integer, + validate = non_negative}, + <<"publish_retry_time_ms">> => #option{type = integer, + validate = non_negative} + }, + required = [<<"access_key_id">>, <<"secret_access_key">>, + <<"region">>, <<"account_id">>, <<"sns_host">>] + }. + push_event(Acc, _, #user_status_event{jid = UserJID, status = Status}) -> user_presence_changed(UserJID, Status == online), Acc; diff --git a/test/config_parser_SUITE_data/modules.options b/test/config_parser_SUITE_data/modules.options index e8258328a9..65b335b525 100644 --- a/test/config_parser_SUITE_data/modules.options +++ b/test/config_parser_SUITE_data/modules.options @@ -48,7 +48,6 @@ {mod_event_pusher_sns, [{access_key_id,"AKIAIOSFODNN7EXAMPLE"}, {account_id,"123456789012"}, - {muc_host,"conference.HOST"}, {muc_messages_topic,"user_messagegroup_sent"}, {plugin_module,mod_event_pusher_sns_defaults}, {pm_messages_topic,"user_message_sent"}, @@ -250,7 +249,6 @@ {sns, [{access_key_id,"AKIAIOSFODNN7EXAMPLE"}, {account_id,"123456789012"}, - {muc_host,"conference.HOST"}, {muc_messages_topic,"user_messagegroup_sent"}, {plugin_module,mod_event_pusher_sns_defaults}, {pm_messages_topic,"user_message_sent"}, @@ -319,7 +317,6 @@ {mod_event_pusher_sns, [{access_key_id,"AKIAIOSFODNN7EXAMPLE"}, {account_id,"123456789012"}, - {muc_host,"conference.HOST"}, {muc_messages_topic,"user_messagegroup_sent"}, {plugin_module,mod_event_pusher_sns_defaults}, {pm_messages_topic,"user_message_sent"}, @@ -521,7 +518,6 @@ {sns, [{access_key_id,"AKIAIOSFODNN7EXAMPLE"}, {account_id,"123456789012"}, - {muc_host,"conference.HOST"}, {muc_messages_topic,"user_messagegroup_sent"}, {plugin_module,mod_event_pusher_sns_defaults}, {pm_messages_topic,"user_message_sent"}, diff --git a/test/config_parser_SUITE_data/modules.toml b/test/config_parser_SUITE_data/modules.toml index c5fd9ead94..3c9ae3b79b 100644 --- a/test/config_parser_SUITE_data/modules.toml +++ b/test/config_parser_SUITE_data/modules.toml @@ -45,7 +45,6 @@ backend.sns.region = "eu-west-1" backend.sns.account_id = "123456789012" backend.sns.sns_host = "sns.eu-west-1.amazonaws.com" - backend.sns.muc_host = "conference.HOST" backend.sns.plugin_module = "mod_event_pusher_sns_defaults" backend.sns.presence_updates_topic = "user_presence_updated" backend.sns.pm_messages_topic = "user_message_sent" From 89dd954911a03a9012ed14035e87ef609e336840 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Tue, 8 Dec 2020 08:48:20 +0100 Subject: [PATCH 049/104] Split and unify mod_event_pusher tests - Make the test cases less bulky - Simplify code structure to follow other test cases --- test/config_parser_SUITE.erl | 255 ++++++++++++++++------------------- 1 file changed, 118 insertions(+), 137 deletions(-) diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index 6815379cd3..0aab42c0fd 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -174,7 +174,10 @@ groups() -> mod_disco, mod_inbox, mod_global_distrib, - mod_event_pusher, + mod_event_pusher_sns, + mod_event_pusher_push, + mod_event_pusher_http, + mod_event_pusher_rabbit, mod_extdisco, mod_http_upload, mod_jingle_sip, @@ -1641,144 +1644,122 @@ mod_global_distrib(_Config) -> ?errf(T(Base#{<<"hosts_refresh_interval">> => <<"kek">>})), ?errf(T(Base#{<<"hosts_refresh_interval">> => -1})). -mod_event_pusher(_Config) -> - T = fun(Backend, Opt, Value) -> - Opts = #{<<"backend">> => #{ - atom_to_binary(Backend, utf8) => - #{atom_to_binary(Opt, utf8) => Value}}}, - #{<<"modules">> => #{<<"mod_event_pusher">> => Opts}} +mod_event_pusher_sns(_Config) -> + RequiredOpts = #{<<"access_key_id">> => <<"AKIAIOSFODNN7EXAMPLE">>, + <<"secret_access_key">> => <<"KEY">>, + <<"region">> => <<"eu-west-1">>, + <<"account_id">> => <<"123456789012">>, + <<"sns_host">> => <<"sns.eu-west-1.amazonaws.com">>}, + ExpectedCfg = [{access_key_id, "AKIAIOSFODNN7EXAMPLE"}, + {secret_access_key, "KEY"}, + {region, "eu-west-1"}, + {account_id, "123456789012"}, + {sns_host, "sns.eu-west-1.amazonaws.com"}], + T = fun(Opts) -> #{<<"modules">> => + #{<<"mod_event_pusher">> => + #{<<"backend">> => #{<<"sns">> => Opts}}}} end, - M = fun(Backend, Opt, Value) -> - Backends = [{Backend, [{Opt, Value}]}], - modopts(mod_event_pusher, [{backends, Backends}]) + M = fun(Cfg) -> modopts(mod_event_pusher, [{backends, [{sns, Cfg}]}]) end, + ?eqf(M(ExpectedCfg), + T(RequiredOpts)), + ?eqf(M(ExpectedCfg ++ [{presence_updates_topic, "pres"}]), + T(RequiredOpts#{<<"presence_updates_topic">> => <<"pres">>})), + ?eqf(M(ExpectedCfg ++ [{pm_messages_topic, "pm"}]), + T(RequiredOpts#{<<"pm_messages_topic">> => <<"pm">>})), + ?eqf(M(ExpectedCfg ++ [{muc_messages_topic, "muc"}]), + T(RequiredOpts#{<<"muc_messages_topic">> => <<"muc">>})), + ?eqf(M(ExpectedCfg ++ [{plugin_module, mod_event_pusher_sns_defaults}]), + T(RequiredOpts#{<<"plugin_module">> => <<"mod_event_pusher_sns_defaults">>})), + ?eqf(M(ExpectedCfg ++ [{pool_size, 10}]), + T(RequiredOpts#{<<"pool_size">> => 10})), + ?eqf(M(ExpectedCfg ++ [{publish_retry_count, 1}]), + T(RequiredOpts#{<<"publish_retry_count">> => 1})), + ?eqf(M(ExpectedCfg ++ [{publish_retry_time_ms, 100}]), + T(RequiredOpts#{<<"publish_retry_time_ms">> => 100})), + [?errf(T(maps:remove(Key, RequiredOpts))) || Key <- maps:keys(RequiredOpts)], + [?errf(T(RequiredOpts#{Key => 1})) || Key <- maps:keys(RequiredOpts)], + ?errf(T(RequiredOpts#{<<"presence_updates_topic">> => #{}})), + ?errf(T(RequiredOpts#{<<"pm_messages_topic">> => true})), + ?errf(T(RequiredOpts#{<<"muc_messages_topic">> => [1, 2]})), + ?errf(T(RequiredOpts#{<<"plugin_module">> => <<"plug_and_play">>})), + ?errf(T(RequiredOpts#{<<"pool_size">> => 0})), + ?errf(T(RequiredOpts#{<<"publish_retry_count">> => -1})), + ?errf(T(RequiredOpts#{<<"publish_retry_time_ms">> => -1})). + +mod_event_pusher_push(_Config) -> + T = fun(Opts) -> #{<<"modules">> => + #{<<"mod_event_pusher">> => + #{<<"backend">> => #{<<"push">> => Opts}}}} end, - [?eqf(M(Backend, Opt, Mim), T(Backend, Opt, Toml)) - || {Backend, Opt, Toml, Mim} <- mod_event_pusher_valid_opts()], - [begin - FullToml = T(Backend, Opt, Toml), - try - ?errf(FullToml) - catch Class:Error:Stacktrace -> - erlang:raise(Class, #{what => passed_but_shouldnt, - backend => Backend, opt => Opt, - full_toml => FullToml, - toml => Toml, reason => Error}, - Stacktrace) - end - end - || {Backend, Opt, Toml} <- mod_event_pusher_ivalid_opts()], - ok. - -mod_event_pusher_valid_opts() -> - %% {BackendName, BackendOptionName, TomlValue, MongooseValue} - [%% sns - {sns, access_key_id, <<"AKIAIOSFODNN7EXAMPLE">>,"AKIAIOSFODNN7EXAMPLE"}, - {sns, secret_access_key, <<"KEY">>, "KEY"}, - {sns, region, <<"eu-west-1">>, "eu-west-1"}, - {sns, account_id, <<"123456789012">>, "123456789012"}, - {sns, sns_host, <<"sns.eu-west-1.amazonaws.com">>, "sns.eu-west-1.amazonaws.com"}, - {sns, muc_host, <<"conference.HOST">>, "conference.HOST"}, - {sns, plugin_module, <<"mod_event_pusher_sns_defaults">>, mod_event_pusher_sns_defaults}, - {sns, presence_updates_topic, <<"user_presence_updated">>, "user_presence_updated"}, - {sns, pm_messages_topic, <<"user_message_sent">>, "user_message_sent"}, - {sns, muc_messages_topic, <<"user_messagegroup_sent">>, "user_messagegroup_sent"}, - {sns, pool_size, 100, 100}, - {sns, publish_retry_count, 2, 2}, - {sns, publish_retry_time_ms, 50, 50}, - %% push - {push, plugin_module, <<"mod_event_pusher_push_plugin_defaults">>, mod_event_pusher_push_plugin_defaults}, - {push, virtual_pubsub_hosts, [<<"host1">>, <<"host2">>], ["host1", "host2"]}, - {push, backend, <<"mnesia">>, mnesia}, - {push, wpool, #{<<"workers">> => 200}, [{workers, 200}]}, - %% http - {http, pool_name, <<"http_pool">>, http_pool}, - {http, path, <<"/notifications">>, "/notifications"}, - {http, callback_module, <<"mod_event_pusher_http_defaults">>, mod_event_pusher_http_defaults}, - %% rabbit - {rabbit, presence_exchange, - #{<<"name">> => <<"presence">>, <<"type">> => <<"topic">>}, - [{name, <<"presence">>}, {type, <<"topic">>}]}, - {rabbit, chat_msg_exchange, - #{<<"name">> => <<"chat_msg">>, - <<"recv_topic">> => <<"chat_msg_recv">>, - <<"sent_topic">> => <<"chat_msg_sent">>}, - [{name, <<"chat_msg">>}, - {recv_topic, <<"chat_msg_recv">>}, - {sent_topic, <<"chat_msg_sent">>}]}, - {rabbit, groupchat_msg_exchange, - #{<<"name">> => <<"groupchat_msg">>, - <<"sent_topic">> => <<"groupchat_msg_sent">>, - <<"recv_topic">> => <<"groupchat_msg_recv">>}, - [{name, <<"groupchat_msg">>}, - {recv_topic, <<"groupchat_msg_recv">>}, - {sent_topic, <<"groupchat_msg_sent">>}]}]. - -mod_event_pusher_ivalid_opts() -> - %% {BackendName, BackendOptionName, TomlValue} - [%% sns - {sns, access_key_id, 1}, - {sns, secret_access_key, 1}, - {sns, region, 1}, - {sns, account_id, 1}, - {sns, sns_host, 1}, - {sns, muc_host, 1}, - {sns, muc_host, <<"kek kek">>}, - {sns, plugin_module, <<"wow_cool_but_missing">>}, - {sns, plugin_module, 1}, - {sns, presence_updates_topic, 1}, - {sns, pm_messages_topic, 1}, - {sns, muc_messages_topic, 1}, - {sns, pool_size, <<"1">>}, - {sns, publish_retry_count, <<"1">>}, - {sns, publish_retry_time_ms, <<"1">>}, - %% push - {push, plugin_module, <<"wow_cool_but_missing">>}, - {push, plugin_module, 1}, - {push, virtual_pubsub_hosts, [<<"host with whitespace">>]}, - {push, backend, <<"mnesiAD">>}, - {push, wpool, #{<<"workers">> => <<"500">>}}, - %% http - {http, pool_name, 1}, - {http, path, 1}, - {http, callback_module, <<"wow_cool_but_missing">>}, - {http, callback_module, 1}, - %% rabbit - {rabbit, presence_exchange, - #{<<"namesss">> => <<"presence">>, <<"type">> => <<"topic">>}}, - {rabbit, presence_exchange, - #{<<"name">> => <<"presence">>, <<"typessss">> => <<"topic">>}}, - {rabbit, presence_exchange, - #{<<"name">> => 1, <<"type">> => <<"topic">>}}, - {rabbit, presence_exchange, - #{<<"name">> => <<"presence">>, <<"type">> => 1}} - ] ++ make_chat_exchange_invalid_opts(chat_msg_exchange) - ++ make_chat_exchange_invalid_opts(groupchat_msg_exchange). - -make_chat_exchange_invalid_opts(Exchange) -> - [{rabbit, Exchange, Val} || Val <- chat_exchange_invalid_opts()]. - -chat_exchange_invalid_opts() -> - [#{<<"names4">> => <<"chat_msg">>, - <<"recv_topic">> => <<"chat_msg_recv">>, - <<"sent_topic">> => <<"chat_msg_sent">>}, - #{<<"name">> => <<"chat_msg">>, - <<"recv_topicsss">> => <<"chat_msg_recv">>, - <<"sent_topic">> => <<"chat_msg_sent">>}, - #{<<"name">> => <<"chat_msg">>, - <<"recv_topics33">> => <<"chat_msg_recv">>, - <<"sent_topic">> => <<"chat_msg_sent">>}, - #{<<"name">> => <<"chat_msg">>, - <<"recv_topic">> => <<"chat_msg_recv">>, - <<"sent_topics444">> => <<"chat_msg_sent">>}, - #{<<"name">> => 1, - <<"recv_topic">> => <<"chat_msg_recv">>, - <<"sent_topic">> => <<"chat_msg_sent">>}, - #{<<"name">> => <<"chat_msg">>, - <<"recv_topic">> => 1, - <<"sent_topic">> => <<"chat_msg_sent">>}, - #{<<"name">> => <<"chat_msg">>, - <<"recv_topic">> => <<"chat_msg_recv">>, - <<"sent_topic">> => 1}]. + M = fun(Cfg) -> modopts(mod_event_pusher, [{backends, [{push, Cfg}]}]) end, + ?eqf(M([{backend, rdbms}]), + T(#{<<"backend">> => <<"rdbms">>})), + ?eqf(M([{wpool, [{workers, 200}]}]), + T(#{<<"wpool">> => #{<<"workers">> => 200}})), + ?eqf(M([{plugin_module, mod_event_pusher_push_plugin_defaults}]), + T(#{<<"plugin_module">> => <<"mod_event_pusher_push_plugin_defaults">>})), + ?eqf(M([{virtual_pubsub_hosts, ["host1", "host2"]}]), + T(#{<<"virtual_pubsub_hosts">> => [<<"host1">>, <<"host2">>]})), + ?errf(T(#{<<"backend">> => <<"redis">>})), + ?errf(T(#{<<"wpool">> => true})), + ?errf(T(#{<<"wpool">> => #{<<"workers">> => <<"500">>}})), + ?errf(T(#{<<"plugin_module">> => <<"wow_cool_but_missing">>})), + ?errf(T(#{<<"plugin_module">> => 1})), + ?errf(T(#{<<"virtual_pubsub_hosts">> => [<<"host with whitespace">>]})). + +mod_event_pusher_http(_Config) -> + T = fun(Opts) -> #{<<"modules">> => + #{<<"mod_event_pusher">> => + #{<<"backend">> => #{<<"http">> => Opts}}}} + end, + M = fun(Cfg) -> modopts(mod_event_pusher, [{backends, [{http, Cfg}]}]) end, + ?eqf(M([{pool_name, http_pool}]), + T(#{<<"pool_name">> => <<"http_pool">>})), + ?eqf(M([{path, "/notifications"}]), + T(#{<<"path">> => <<"/notifications">>})), + ?eqf(M([{callback_module, mod_event_pusher_http_defaults}]), + T(#{<<"callback_module">> => <<"mod_event_pusher_http_defaults">>})), + ?errf(T(#{<<"pool_name">> => <<>>})), + ?errf(T(#{<<"path">> => true})), + ?errf(T(#{<<"callback_module">> => <<"wow_cool_but_missing">>})), + ?errf(T(#{<<"callback_module">> => 1})). + +mod_event_pusher_rabbit(_Config) -> + T = fun(Opts) -> #{<<"modules">> => + #{<<"mod_event_pusher">> => + #{<<"backend">> => #{<<"rabbit">> => Opts}}}} + end, + M = fun(Cfg) -> modopts(mod_event_pusher, [{backends, [{rabbit, Cfg}]}]) end, + ?eqf(M([{presence_exchange, [{name, <<"pres">>}]}]), + T(#{<<"presence_exchange">> => #{<<"name">> => <<"pres">>}})), + ?eqf(M([{presence_exchange, [{type, <<"topic">>}]}]), + T(#{<<"presence_exchange">> => #{<<"type">> => <<"topic">>}})), + + %% first two keys are the same as before, test them together + ?eqf(M([{chat_msg_exchange, [{name, <<"pres1">>}, + {type, <<"topic1">>}]}]), + T(#{<<"chat_msg_exchange">> => #{<<"name">> => <<"pres1">>, + <<"type">> => <<"topic1">>}})), + ?eqf(M([{chat_msg_exchange, [{sent_topic, <<"sent_topic1">>}]}]), + T(#{<<"chat_msg_exchange">> => #{<<"sent_topic">> => <<"sent_topic1">>}})), + ?eqf(M([{chat_msg_exchange, [{recv_topic, <<"recv_topic1">>}]}]), + T(#{<<"chat_msg_exchange">> => #{<<"recv_topic">> => <<"recv_topic1">>}})), + + %% all keys are the same as before, test them together + ?eqf(M([{groupchat_msg_exchange, [{name, <<"pres2">>}, + {type, <<"topic2">>}, + {sent_topic, <<"sent_topic2">>}, + {recv_topic, <<"recv_topic2">>}]}]), + T(#{<<"groupchat_msg_exchange">> => #{<<"name">> => <<"pres2">>, + <<"type">> => <<"topic2">>, + <<"sent_topic">> => <<"sent_topic2">>, + <<"recv_topic">> => <<"recv_topic2">>}})), + + Exchanges = [<<"presence_exchange">>, <<"chat_msg_exchange">>, <<"groupchat_msg_exchange">>], + Keys = [<<"name">>, <<"topic">>, <<"sent_topic">>, <<"recv_topic">>], + [?errf(T(#{Exch => #{Key => <<>>}})) || Exch <- Exchanges, Key <- Keys], + [?errf(T(#{Exch => #{<<"badkey">> => <<"goodvalue">>}})) || Exch <- Exchanges], + ?errf(T(#{<<"money_exchange">> => #{<<"name">> => <<"kantor">>}})). mod_http_upload(_Config) -> T = fun(Opts) -> #{<<"modules">> => #{<<"mod_http_upload">> => Opts}} end, From 4a14801f381abc45b386644a3b85cdbc71a3186c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Tue, 8 Dec 2020 08:33:27 +0100 Subject: [PATCH 050/104] Amend mod_event_pusher docs --- doc/modules/mod_event_pusher_push.md | 11 +++++------ doc/modules/mod_event_pusher_sns.md | 11 ++--------- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/doc/modules/mod_event_pusher_push.md b/doc/modules/mod_event_pusher_push.md index bfc7cf1deb..70aac8f13f 100644 --- a/doc/modules/mod_event_pusher_push.md +++ b/doc/modules/mod_event_pusher_push.md @@ -33,12 +33,11 @@ with 100 asynchronous workers that will handle all push notification related wor Backend to use for storing the registrations. #### `modules.mod_event_pusher_push.wpool` -* **Syntax:** Array of TOML tables. See description. -* **Default:** `[]` -* **Example:** `wpool = [workers = 200]` +* **Syntax:** TOML table with worker pool options +* **Default:** `{}` +* **Example:** `wpool.workers = 200` -List of options that will be passed to the `worker_pool` library that handles all the requests. -Please refer to the [Project Site](https://github.com/inaka/worker_pool) for more details. +Array of options that will be passed to the `worker_pool` library that handles all the requests. The options allowed here are the same as for the [outgoing connection pools](../../advanced-configuration/outgoing-connections#worker-pool-options). #### `modules.mod_event_pusher_push.plugin_module` * **Syntax:** non-empty string @@ -50,7 +49,7 @@ See the [relevant section](#plugin-module) for more details. #### `modules.mod_event_pusher_push.virtual_pubsub_hosts` * **Syntax:** array of strings -* **Default:** `["pubsub.@HOSTS@"]` +* **Default:** `[]` * **Example:** `virtual_pubsub_hosts = ["host1", "host2"]` The list of "simulated" Publish-Subscribe domains. You may use the `@HOSTS@` pattern in the domain name. diff --git a/doc/modules/mod_event_pusher_sns.md b/doc/modules/mod_event_pusher_sns.md index fa0af36084..d7d64df6b1 100644 --- a/doc/modules/mod_event_pusher_sns.md +++ b/doc/modules/mod_event_pusher_sns.md @@ -15,7 +15,7 @@ Full topics for notifications (ARN as defined in [Amazon Resource Names][aws-arn #### `modules.mod_event_pusher_sns.presence_updates_topic` * **Syntax:** string -* **Default:** `""` +* **Default:** no default is given * **Example:** `presence_updates_topic = "user_presence_updated"` Defines Amazon SNS Topic for presence change notifications. Remove this option to disable these notifications. @@ -41,13 +41,6 @@ Defines Amazon SNS Topic for group message notifications. Remove this option to Sets a callback module used for creating user's GUID used in notifications (from user's JID) and for defining custom attributes attached to a published SNS message. -#### `modules.mod_event_pusher_sns.muc_host` -* **Syntax:** string -* **Default:** `"conference.@HOST@"` -* **Example:** `muc_host = "conference.HOST"` - -Messages from this MUC host will be sent to the set SNS topic for MUCs. - #### `modules.mod_event_pusher_sns.sns_host` * **Syntax:** string * **Default:** none, this option is mandatory @@ -85,7 +78,7 @@ The [AWS region][aws-region] to use for requests. 12 digit number as defined in [AWS Account Identifiers][aws-acct-identifier] to use for creating TopicArn for publishing notifications. #### `modules.mod_event_pusher_sns.pool_size` -* **Syntax:** non-negative integer +* **Syntax:** positive integer * **Default:** `100` * **Example:** `pool_size = 100` From ac644dac07bf4ed1fe74380a555d758ffffddad9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Wed, 9 Dec 2020 14:27:18 +0100 Subject: [PATCH 051/104] Move config spec records to a separate header file --- include/ejabberd_config.hrl | 15 --------------- include/mongoose_config_spec.hrl | 19 +++++++++++++++++++ src/admin_extra/service_admin_extra.erl | 2 +- src/config/mongoose_config_parser_toml.erl | 1 + src/config/mongoose_config_spec.erl | 2 +- src/config/mongoose_config_validator_toml.erl | 2 +- src/event_pusher/mod_event_pusher.erl | 2 +- src/event_pusher/mod_event_pusher_http.erl | 2 +- src/event_pusher/mod_event_pusher_push.erl | 2 +- src/event_pusher/mod_event_pusher_rabbit.erl | 2 +- src/event_pusher/mod_event_pusher_sns.erl | 2 +- src/mod_adhoc.erl | 2 +- src/mod_auth_token.erl | 2 +- src/mod_bosh.erl | 2 +- src/mod_caps.erl | 2 +- src/mod_carboncopy.erl | 2 +- src/mod_csi.erl | 2 +- src/mod_disco.erl | 2 +- .../service_mongoose_system_metrics.erl | 2 +- 19 files changed, 36 insertions(+), 31 deletions(-) create mode 100644 include/mongoose_config_spec.hrl diff --git a/include/ejabberd_config.hrl b/include/ejabberd_config.hrl index f70b95d790..d47c9a11ee 100644 --- a/include/ejabberd_config.hrl +++ b/include/ejabberd_config.hrl @@ -27,19 +27,4 @@ value :: mongoose_config_parser:value() }). --record(section, {items, - validate_keys = any, - required = [], - validate = any, - process, - format = default}). --record(list, {items, - validate = any, - process, - format = default}). --record(option, {type, - validate = any, - process, - format = default}). - -endif. diff --git a/include/mongoose_config_spec.hrl b/include/mongoose_config_spec.hrl new file mode 100644 index 0000000000..2a59f51880 --- /dev/null +++ b/include/mongoose_config_spec.hrl @@ -0,0 +1,19 @@ +-ifndef(MONGOOSEIM_CONFIG_SPEC_HRL). +-define(MONGOOSEIM_CONFIG_SPEC_HRL, true). + +-record(section, {items, + validate_keys = any, + required = [], + validate = any, + process, + format = default}). +-record(list, {items, + validate = any, + process, + format = default}). +-record(option, {type, + validate = any, + process, + format = default}). + +-endif. diff --git a/src/admin_extra/service_admin_extra.erl b/src/admin_extra/service_admin_extra.erl index 97b5c8a4a7..ba25002c53 100644 --- a/src/admin_extra/service_admin_extra.erl +++ b/src/admin_extra/service_admin_extra.erl @@ -28,7 +28,7 @@ -behaviour(mongoose_service). --include("ejabberd_config.hrl"). +-include("mongoose_config_spec.hrl"). -export([start/1, stop/0, config_spec/0]). diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index e45e4869cb..9123211840 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -11,6 +11,7 @@ -endif. -include("mongoose.hrl"). +-include("mongoose_config_spec.hrl"). -include("ejabberd_config.hrl"). %% Used to create per-host config when the list of hosts is not known yet diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index 30cc732c1e..f10c728211 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -2,7 +2,7 @@ -compile(export_all). --include("ejabberd_config.hrl"). +-include("mongoose_config_spec.hrl"). -type config_node() :: config_section() | config_list() | config_option(). -type config_section() :: #section{}. diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index 00456808a5..4d734163d9 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -6,7 +6,7 @@ validate_list/2]). -include("mongoose.hrl"). --include("ejabberd_config.hrl"). +-include("mongoose_config_spec.hrl"). -include_lib("jid/include/jid.hrl"). -define(HOST, 'HOST'). diff --git a/src/event_pusher/mod_event_pusher.erl b/src/event_pusher/mod_event_pusher.erl index a1c9d3f62f..c34a942958 100644 --- a/src/event_pusher/mod_event_pusher.erl +++ b/src/event_pusher/mod_event_pusher.erl @@ -22,7 +22,7 @@ -include("jlib.hrl"). -include("mod_event_pusher_events.hrl"). --include("ejabberd_config.hrl"). +-include("mongoose_config_spec.hrl"). -type event() :: #user_status_event{} | #chat_event{} | #unack_msg_event{}. -export_type([event/0]). diff --git a/src/event_pusher/mod_event_pusher_http.erl b/src/event_pusher/mod_event_pusher_http.erl index 2a30335bc1..f652d491be 100644 --- a/src/event_pusher/mod_event_pusher_http.erl +++ b/src/event_pusher/mod_event_pusher_http.erl @@ -41,7 +41,7 @@ -include("mongoose.hrl"). -include("jlib.hrl"). --include("ejabberd_config.hrl"). +-include("mongoose_config_spec.hrl"). -define(DEFAULT_POOL_NAME, http_pool). -define(DEFAULT_PATH, ""). diff --git a/src/event_pusher/mod_event_pusher_push.erl b/src/event_pusher/mod_event_pusher_push.erl index 61361c5f8f..23fd116a64 100644 --- a/src/event_pusher/mod_event_pusher_push.erl +++ b/src/event_pusher/mod_event_pusher_push.erl @@ -19,7 +19,7 @@ -include("mod_event_pusher_events.hrl"). -include("mongoose.hrl"). -include("jlib.hrl"). --include("ejabberd_config.hrl"). +-include("mongoose_config_spec.hrl"). -define(SESSION_KEY, publish_service). diff --git a/src/event_pusher/mod_event_pusher_rabbit.erl b/src/event_pusher/mod_event_pusher_rabbit.erl index fac9a97425..b81db8ca79 100644 --- a/src/event_pusher/mod_event_pusher_rabbit.erl +++ b/src/event_pusher/mod_event_pusher_rabbit.erl @@ -23,7 +23,7 @@ -include_lib("mongooseim/include/mongoose.hrl"). -include_lib("mongooseim/include/mod_event_pusher_events.hrl"). --include("ejabberd_config.hrl"). +-include("mongoose_config_spec.hrl"). -behaviour(gen_mod). -behaviour(mongoose_module_metrics). diff --git a/src/event_pusher/mod_event_pusher_sns.erl b/src/event_pusher/mod_event_pusher_sns.erl index 1acf543716..b4cda33b95 100644 --- a/src/event_pusher/mod_event_pusher_sns.erl +++ b/src/event_pusher/mod_event_pusher_sns.erl @@ -7,7 +7,7 @@ -include("mongoose.hrl"). -include("jlib.hrl"). -include_lib("erlcloud/include/erlcloud_aws.hrl"). --include("ejabberd_config.hrl"). +-include("mongoose_config_spec.hrl"). -callback user_guid(UserJID :: jid:jid()) -> user_guid(). -callback message_attributes(TopicARN :: topic_arn(), UserJID :: jid:jid(), diff --git a/src/mod_adhoc.erl b/src/mod_adhoc.erl index 10d79f1fa2..5b08b71b5f 100644 --- a/src/mod_adhoc.erl +++ b/src/mod_adhoc.erl @@ -45,7 +45,7 @@ -include("mongoose.hrl"). -include("jlib.hrl"). -include("adhoc.hrl"). --include("ejabberd_config.hrl"). +-include("mongoose_config_spec.hrl"). start(Host, Opts) -> IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue), diff --git a/src/mod_auth_token.erl b/src/mod_auth_token.erl index d58e1efa80..408d9a61e6 100644 --- a/src/mod_auth_token.erl +++ b/src/mod_auth_token.erl @@ -7,7 +7,7 @@ -include("ejabberd_commands.hrl"). -include("jlib.hrl"). -include("mod_auth_token.hrl"). --include("ejabberd_config.hrl"). +-include("mongoose_config_spec.hrl"). %% gen_mod callbacks -export([start/2, diff --git a/src/mod_bosh.erl b/src/mod_bosh.erl index caac2ff6ee..c5064c8b07 100644 --- a/src/mod_bosh.erl +++ b/src/mod_bosh.erl @@ -41,7 +41,7 @@ -include("jlib.hrl"). -include_lib("exml/include/exml_stream.hrl"). -include("mod_bosh.hrl"). --include("ejabberd_config.hrl"). +-include("mongoose_config_spec.hrl"). -define(DEFAULT_MAX_AGE, 1728000). %% 20 days in seconds -define(DEFAULT_INACTIVITY, 30). %% seconds diff --git a/src/mod_caps.erl b/src/mod_caps.erl index 3512758de7..6724b70e0b 100644 --- a/src/mod_caps.erl +++ b/src/mod_caps.erl @@ -53,7 +53,7 @@ -export([delete_caps/1, make_disco_hash/2]). -include("mongoose.hrl"). --include("ejabberd_config.hrl"). +-include("mongoose_config_spec.hrl"). -include("jlib.hrl"). diff --git a/src/mod_carboncopy.erl b/src/mod_carboncopy.erl index af1081ce64..5685800b40 100644 --- a/src/mod_carboncopy.erl +++ b/src/mod_carboncopy.erl @@ -52,7 +52,7 @@ -include("mongoose.hrl"). -include("jlib.hrl"). -include_lib("session.hrl"). --include("ejabberd_config.hrl"). +-include("mongoose_config_spec.hrl"). -type classification() :: 'ignore' | 'forward'. diff --git a/src/mod_csi.erl b/src/mod_csi.erl index 99d160983d..42cf850396 100644 --- a/src/mod_csi.erl +++ b/src/mod_csi.erl @@ -13,7 +13,7 @@ -export([add_csi_feature/2]). -include("jlib.hrl"). --include("ejabberd_config.hrl"). +-include("mongoose_config_spec.hrl"). -type state() :: active | inactive. diff --git a/src/mod_disco.erl b/src/mod_disco.erl index 934307f3db..109242c7b7 100644 --- a/src/mod_disco.erl +++ b/src/mod_disco.erl @@ -54,7 +54,7 @@ -include("mongoose.hrl"). -include("jlib.hrl"). --include("ejabberd_config.hrl"). +-include("mongoose_config_spec.hrl"). -type feature() :: any(). diff --git a/src/system_metrics/service_mongoose_system_metrics.erl b/src/system_metrics/service_mongoose_system_metrics.erl index 2b5199bbe2..7587b71ee7 100644 --- a/src/system_metrics/service_mongoose_system_metrics.erl +++ b/src/system_metrics/service_mongoose_system_metrics.erl @@ -4,7 +4,7 @@ -behaviour(mongoose_service). -behaviour(gen_server). --include("ejabberd_config.hrl"). +-include("mongoose_config_spec.hrl"). -define(DEFAULT_INITIAL_REPORT, timer:minutes(5)). -define(DEFAULT_REPORT_AFTER, timer:hours(3)). From ed5ea4763fd052935982c06f13a49fabc2abff17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Wed, 9 Dec 2020 08:05:37 +0100 Subject: [PATCH 052/104] Make the config of mod_muc declarative --- src/config/mongoose_config_parser_toml.erl | 113 +----------- src/config/mongoose_config_spec.erl | 3 +- src/config/mongoose_config_validator_toml.erl | 162 +----------------- src/mod_muc.erl | 105 ++++++++++++ 4 files changed, 111 insertions(+), 272 deletions(-) diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index 9123211840..42411406d1 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -175,53 +175,6 @@ module_opt([<<"muc">>, <<"mod_mam_meta">>|_] = Path, V) -> [{muc, Muc}]; module_opt([_, <<"mod_mam_meta">>|_] = Path, V) -> mod_mam_opts(Path, V); -module_opt([<<"host">>, <<"mod_muc">>|_], V) -> - [{host, b2l(V)}]; -module_opt([<<"access">>, <<"mod_muc">>|_], V) -> - [{access, b2a(V)}]; -module_opt([<<"access_create">>, <<"mod_muc">>|_], V) -> - [{access_create, b2a(V)}]; -module_opt([<<"access_admin">>, <<"mod_muc">>|_], V) -> - [{access_admin, b2a(V)}]; -module_opt([<<"access_persistent">>, <<"mod_muc">>|_], V) -> - [{access_persistent, b2a(V)}]; -module_opt([<<"history_size">>, <<"mod_muc">>|_], V) -> - [{history_size, V}]; -module_opt([<<"room_shaper">>, <<"mod_muc">>|_], V) -> - [{room_shaper, b2a(V)}]; -module_opt([<<"max_room_id">>, <<"mod_muc">>|_], V) -> - [{max_room_id, int_or_infinity(V)}]; -module_opt([<<"max_room_name">>, <<"mod_muc">>|_], V) -> - [{max_room_name, int_or_infinity(V)}]; -module_opt([<<"max_room_desc">>, <<"mod_muc">>|_], V) -> - [{max_room_desc, int_or_infinity(V)}]; -module_opt([<<"min_message_interval">>, <<"mod_muc">>|_], V) -> - [{min_message_interval, V}]; -module_opt([<<"min_presence_interval">>, <<"mod_muc">>|_], V) -> - [{min_presence_interval, V}]; -module_opt([<<"max_users">>, <<"mod_muc">>|_], V) -> - [{max_users, V}]; -module_opt([<<"max_users_admin_threshold">>, <<"mod_muc">>|_], V) -> - [{max_users_admin_threshold, V}]; -module_opt([<<"user_message_shaper">>, <<"mod_muc">>|_], V) -> - [{user_message_shaper, b2a(V)}]; -module_opt([<<"user_presence_shaper">>, <<"mod_muc">>|_], V) -> - [{user_presence_shaper, b2a(V)}]; -module_opt([<<"max_user_conferences">>, <<"mod_muc">>|_], V) -> - [{max_user_conferences, V}]; -module_opt([<<"http_auth_pool">>, <<"mod_muc">>|_], V) -> - [{http_auth_pool, b2a(V)}]; -module_opt([<<"load_permanent_rooms_at_startup">>, <<"mod_muc">>|_], V) -> - [{load_permanent_rooms_at_startup, V}]; -module_opt([<<"hibernate_timeout">>, <<"mod_muc">>|_], V) -> - [{hibernate_timeout, V}]; -module_opt([<<"hibernated_room_check_interval">>, <<"mod_muc">>|_], V) -> - [{hibernated_room_check_interval, int_or_infinity(V)}]; -module_opt([<<"hibernated_room_timeout">>, <<"mod_muc">>|_], V) -> - [{hibernated_room_timeout, int_or_infinity(V)}]; -module_opt([<<"default_room">>, <<"mod_muc">>|_] = Path, V) -> - Defaults = parse_section(Path, V), - [{default_room_options, Defaults}]; module_opt([<<"outdir">>, <<"mod_muc_log">>|_], V) -> [{outdir, b2l(V)}]; module_opt([<<"access_log">>, <<"mod_muc_log">>|_], V) -> @@ -593,63 +546,6 @@ mod_mam_opts([<<"extra_lookup_params">>|_], V) -> mod_mam_opts([<<"riak">>|_] = Path, V) -> parse_section(Path, V). --spec mod_muc_default_room(path(), toml_value()) -> [option()]. -mod_muc_default_room([<<"title">>|_], V) -> - [{title, V}]; -mod_muc_default_room([<<"description">>|_], V) -> - [{description, V}]; -mod_muc_default_room([<<"allow_change_subj">>|_], V) -> - [{allow_change_subj, V}]; -mod_muc_default_room([<<"allow_query_users">>|_], V) -> - [{allow_query_users, V}]; -mod_muc_default_room([<<"allow_private_messages">>|_], V) -> - [{allow_private_messages, V}]; -mod_muc_default_room([<<"allow_visitor_status">>|_], V) -> - [{allow_visitor_status, V}]; -mod_muc_default_room([<<"allow_visitor_nickchange">>|_], V) -> - [{allow_visitor_nickchange, V}]; -mod_muc_default_room([<<"public">>|_], V) -> - [{public, V}]; -mod_muc_default_room([<<"public_list">>|_], V) -> - [{public_list, V}]; -mod_muc_default_room([<<"persistent">>|_], V) -> - [{persistent, V}]; -mod_muc_default_room([<<"moderated">>|_], V) -> - [{moderated, V}]; -mod_muc_default_room([<<"members_by_default">>|_], V) -> - [{members_by_default, V}]; -mod_muc_default_room([<<"members_only">>|_], V) -> - [{members_only, V}]; -mod_muc_default_room([<<"allow_user_invites">>|_], V) -> - [{allow_user_invites, V}]; -mod_muc_default_room([<<"allow_multiple_sessions">>|_], V) -> - [{allow_multiple_sessions, V}]; -mod_muc_default_room([<<"password_protected">>|_], V) -> - [{password_protected, V}]; -mod_muc_default_room([<<"password">>|_], V) -> - [{password, V}]; -mod_muc_default_room([<<"anonymous">>|_], V) -> - [{anonymous, V}]; -mod_muc_default_room([<<"max_users">>|_], V) -> - [{max_users, V}]; -mod_muc_default_room([<<"logging">>|_], V) -> - [{logging, V}]; -mod_muc_default_room([<<"maygetmemberlist">>|_] = Path, V) -> - List = parse_list(Path, V), - [{maygetmemberlist, List}]; -mod_muc_default_room([<<"affiliations">>|_] = Path, V) -> - Affs = parse_list(Path, V), - [{affiliations, Affs}]; -mod_muc_default_room([<<"subject">>|_], V) -> - [{subject, V}]; -mod_muc_default_room([<<"subject_author">>|_], V) -> - [{subject_author, V}]. - --spec mod_muc_default_room_affiliations(path(), toml_section()) -> [option()]. -mod_muc_default_room_affiliations(_, #{<<"user">> := User, <<"server">> := Server, - <<"resource">> := Resource, <<"affiliation">> := Aff}) -> - [{{User, Server, Resource}, b2a(Aff)}]. - -spec mod_muc_log_top_link(path(), toml_value()) -> [option()]. mod_muc_log_top_link([<<"target">>|_], V) -> [b2l(V)]; @@ -1011,7 +907,8 @@ node_to_string(Node) -> [binary_to_list(Node)]. Mod =/= <<"mod_carboncopy">>, Mod =/= <<"mod_csi">>, Mod =/= <<"mod_disco">>, - Mod =/= <<"mod_event_pusher">>). % TODO temporary, remove with 'handler/1' + Mod =/= <<"mod_event_pusher">>, + Mod =/= <<"mod_muc">>). % TODO temporary, remove with 'handler/1' -spec handler(path()) -> fun((path(), toml_value()) -> option()) | mongoose_config_spec:config_node(). @@ -1057,12 +954,6 @@ handler([_, <<"keys">>, <<"mod_keystore">>, <<"modules">>]) -> fun mod_keystore_keys/2; handler([_, _, <<"mod_mam_meta">>, <<"modules">>]) -> fun mod_mam_opts/2; -handler([_, <<"default_room">>, <<"mod_muc">>, <<"modules">>]) -> - fun mod_muc_default_room/2; -handler([_, <<"maygetmemberlist">>, <<"default_room">>, <<"mod_muc">>, <<"modules">>]) -> - fun (_, V) -> [b2a(V)] end; -handler([_, <<"affiliations">>, <<"default_room">>, <<"mod_muc">>, <<"modules">>]) -> - fun mod_muc_default_room_affiliations/2; handler([_, <<"top_link">>, <<"mod_muc_log">>, <<"modules">>]) -> fun mod_muc_log_top_link/2; handler([_, <<"config_schema">>, <<"mod_muc_light">>, <<"modules">>]) -> diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index f10c728211..7ee2d15611 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -737,7 +737,8 @@ all_modules() -> mod_carboncopy, mod_csi, mod_disco, - mod_event_pusher]. + mod_event_pusher, + mod_muc]. %% path: (host_config[].)modules.*.iqdisc iqdisc() -> diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index 4d734163d9..4da1c17e36 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -807,155 +807,6 @@ validate([item, <<"plugins">>, <<"mod_pubsub">>, <<"modules">>|_], validate([<<"sync_broadcast">>, <<"mod_pubsub">>, <<"modules">>|_], [{sync_broadcast, V}]) -> validate_boolean(V); -validate([<<"access">>, <<"mod_muc">>, <<"modules">>|_], - [{access, V}]) -> - validate_access_rule(V); -validate([<<"access_admin">>, <<"mod_muc">>, <<"modules">>|_], - [{access_admin, V}]) -> - validate_access_rule(V); -validate([<<"access_create">>, <<"mod_muc">>, <<"modules">>|_], - [{access_create, V}]) -> - validate_access_rule(V); -validate([<<"access_persistent">>, <<"mod_muc">>, <<"modules">>|_], - [{access_persistent, V}]) -> - validate_access_rule(V); -validate([<<"backend">>, <<"mod_muc">>, <<"modules">>|_], - [{backend, V}]) -> - validate_backend(mod_muc_db, V); -validate([item, <<"affiliations">>, <<"default_room">>, - <<"mod_muc">>, <<"modules">>|_], - [V]) -> - validate_muc_affiliation_rule(V); -validate([<<"allow_change_subj">>, <<"default_room">>, - <<"mod_muc">>, <<"modules">>|_], - [{allow_change_subj, V}]) -> - validate_boolean(V); -validate([<<"allow_multiple_sessions">>, <<"default_room">>, - <<"mod_muc">>, <<"modules">>|_], - [{allow_multiple_sessions, V}]) -> - validate_boolean(V); -validate([<<"allow_private_messages">>, <<"default_room">>, - <<"mod_muc">>, <<"modules">>|_], - [{allow_private_messages, V}]) -> - validate_boolean(V); -validate([<<"allow_query_users">>, <<"default_room">>, - <<"mod_muc">>, <<"modules">>|_], - [{allow_query_users, V}]) -> - validate_boolean(V); -validate([<<"allow_user_invites">>, <<"default_room">>, - <<"mod_muc">>, <<"modules">>|_], - [{allow_user_invites, V}]) -> - validate_boolean(V); -validate([<<"allow_visitor_nickchange">>, <<"default_room">>, - <<"mod_muc">>, <<"modules">>|_], - [{allow_visitor_nickchange, V}]) -> - validate_boolean(V); -validate([<<"allow_visitor_status">>, <<"default_room">>, - <<"mod_muc">>, <<"modules">>|_], - [{allow_visitor_status, V}]) -> - validate_boolean(V); -validate([<<"anonymous">>, <<"default_room">>, <<"mod_muc">>, <<"modules">>|_], - [{anonymous, V}]) -> - validate_boolean(V); -validate([<<"description">>, <<"default_room">>, <<"mod_muc">>, <<"modules">>|_], - [{description, V}]) -> - validate_binary(V); -validate([<<"logging">>, <<"default_room">>, <<"mod_muc">>, <<"modules">>|_], - [{logging, V}]) -> - validate_boolean(V); -validate([<<"max_users">>, <<"default_room">>, <<"mod_muc">>, <<"modules">>|_], - [{max_users, V}]) -> - validate_positive_integer(V); -validate([item, <<"maygetmemberlist">>, <<"default_room">>, - <<"mod_muc">>, <<"modules">>|_], - [V]) -> - validate_non_empty_atom(V); -validate([<<"members_by_default">>, <<"default_room">>, - <<"mod_muc">>, <<"modules">>|_], - [{members_by_default, V}]) -> - validate_boolean(V); -validate([<<"members_only">>, <<"default_room">>, <<"mod_muc">>, <<"modules">>|_], - [{members_only, V}]) -> - validate_boolean(V); -validate([<<"moderated">>, <<"default_room">>, <<"mod_muc">>, <<"modules">>|_], - [{moderated, V}]) -> - validate_boolean(V); -validate([<<"password">>, <<"default_room">>, <<"mod_muc">>, <<"modules">>|_], - [{password, V}]) -> - validate_string(V); -validate([<<"password_protected">>, <<"default_room">>, - <<"mod_muc">>, <<"modules">>|_], - [{password_protected, V}]) -> - validate_boolean(V); -validate([<<"persistent">>, <<"default_room">>, <<"mod_muc">>, <<"modules">>|_], - [{persistent, V}]) -> - validate_boolean(V); -validate([<<"public">>, <<"default_room">>, <<"mod_muc">>, <<"modules">>|_], - [{public, V}]) -> - validate_boolean(V); -validate([<<"public_list">>, <<"default_room">>, <<"mod_muc">>, <<"modules">>|_], - [{public_list, V}]) -> - validate_boolean(V); -validate([<<"subject">>, <<"default_room">>, <<"mod_muc">>, <<"modules">>|_], - [{subject, V}]) -> - validate_string(V); -validate([<<"subject_author">>, <<"default_room">>, <<"mod_muc">>, <<"modules">>|_], - [{subject_author, V}]) -> - validate_string(V); -validate([<<"title">>, <<"default_room">>, <<"mod_muc">>, <<"modules">>|_], - [{title, V}]) -> - validate_string(V); -validate([<<"hibernated_room_check_interval">>, <<"mod_muc">>, <<"modules">>|_], - [{hibernated_room_check_interval, V}]) -> - validate_non_negative_integer_or_infinity(V); -validate([<<"hibernated_room_timeout">>, <<"mod_muc">>, <<"modules">>|_], - [{hibernated_room_timeout, V}]) -> - validate_non_negative_integer_or_infinity(V); -validate([<<"history_size">>, <<"mod_muc">>, <<"modules">>|_], - [{history_size, V}]) -> - validate_non_negative_integer(V); -validate([<<"host">>, <<"mod_muc">>, <<"modules">>|_], - [{host, V}]) -> - validate_domain_template(V); -validate([<<"http_auth_pool">>, <<"mod_muc">>, <<"modules">>|_], - [{http_auth_pool, V}]) -> - validate_pool_name(V); -validate([<<"load_permanent_rooms_at_startup">>, <<"mod_muc">>, <<"modules">>|_], - [{load_permanent_rooms_at_startup, V}]) -> - validate_boolean(V); -validate([<<"max_room_desc">>, <<"mod_muc">>, <<"modules">>|_], - [{max_room_desc, V}]) -> - validate_non_negative_integer_or_infinity(V); -validate([<<"max_room_id">>, <<"mod_muc">>, <<"modules">>|_], - [{max_room_id, V}]) -> - validate_non_negative_integer_or_infinity(V); -validate([<<"max_room_name">>, <<"mod_muc">>, <<"modules">>|_], - [{max_room_name, V}]) -> - validate_non_negative_integer_or_infinity(V); -validate([<<"max_user_conferences">>, <<"mod_muc">>, <<"modules">>|_], - [{max_user_conferences, V}]) -> - validate_non_negative_integer(V); -validate([<<"max_users">>, <<"mod_muc">>, <<"modules">>|_], - [{max_users, V}]) -> - validate_positive_integer(V); -validate([<<"max_users_admin_threshold">>, <<"mod_muc">>, <<"modules">>|_], - [{max_users_admin_threshold, V}]) -> - validate_positive_integer(V); -validate([<<"min_message_interval">>, <<"mod_muc">>, <<"modules">>|_], - [{min_message_interval, V}]) -> - validate_non_negative_integer(V); -validate([<<"min_presence_interval">>, <<"mod_muc">>, <<"modules">>|_], - [{min_presence_interval, V}]) -> - validate_non_negative_integer(V); -validate([<<"room_shaper">>, <<"mod_muc">>, <<"modules">>|_], - [{room_shaper, V}]) -> - validate_shaper_name(V); -validate([<<"user_message_shaper">>, <<"mod_muc">>, <<"modules">>|_], - [{user_message_shaper, V}]) -> - validate_shaper_name(V); -validate([<<"user_presence_shaper">>, <<"mod_muc">>, <<"modules">>|_], - [{user_presence_shaper, V}]) -> - validate_shaper_name(V); validate([<<"iqdisc">>, <<"mod_ping">>, <<"modules">>|_], [{iqdisc, V}]) -> validate_iqdisc(V); @@ -990,6 +841,8 @@ validate(V, atom, {module, Prefix}) -> validate_module(list_to_atom(atom_to_list(Prefix) ++ "_" ++ atom_to_list(V))); validate(V, atom, loglevel) -> validate_loglevel(V); validate(V, atom, pool_name) -> validate_non_empty_atom(V); +validate(V, atom, shaper) -> validate_non_empty_atom(V); +validate(V, atom, access_rule) -> validate_non_empty_atom(V); validate(V, atom, non_empty) -> validate_non_empty_atom(V); validate(V, _, {enum, Values}) -> validate_enum(V, Values); validate(_V, _, any) -> ok. @@ -1009,8 +862,6 @@ validate_loglevel(Level) -> validate_non_empty_binary(Value) when is_binary(Value), Value =/= <<>> -> ok. -validate_binary(Value) when is_binary(Value) -> ok. - validate_unique_items(Items) -> L = sets:size(sets:from_list(Items)), L = length(Items). @@ -1193,12 +1044,6 @@ validate_keystore_key({Name, {file, Path}}) -> validate_non_empty_atom(Name), validate_filename(Path). -validate_muc_affiliation_rule({{User, Server, Resource}, Affiliation}) -> - validate_non_empty_binary(User), - validate_binary_domain(Server), - validate_binary(Resource), - validate_non_empty_atom(Affiliation). - validate_maybe_css_file(false) -> ok; validate_maybe_css_file(Bin) -> @@ -1266,6 +1111,3 @@ validate_pool_name(V) -> validate_access_rule(V) -> validate_non_empty_atom(V). - -validate_shaper_name(V) -> - validate_non_empty_atom(V). diff --git a/src/mod_muc.erl b/src/mod_muc.erl index 08efd10025..47f25b2859 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -35,6 +35,8 @@ -export([start_link/2, start/2, stop/1, + config_spec/0, + process_room_affiliation/1, room_destroyed/3, store_room/4, restore_room/3, @@ -71,6 +73,7 @@ -include("mongoose.hrl"). -include("jlib.hrl"). -include("mongoose_rsm.hrl"). +-include("mongoose_config_spec.hrl"). -export_type([access/0, room/0, @@ -164,6 +167,108 @@ stop(Host) -> stop_supervisor(Host), stop_gen_server(Host). +-spec config_spec() -> mongoose_config_spec:config_section(). +config_spec() -> + #section{ + items = #{<<"backend">> => #option{type = atom, + validate = {module, mod_muc_db}}, + <<"host">> => #option{type = string, + validate = domain_template}, + <<"access">> => #option{type = atom, + validate = access_rule}, + <<"access_create">> => #option{type = atom, + validate = access_rule}, + <<"access_admin">> => #option{type = atom, + validate = access_rule}, + <<"access_persistent">> => #option{type = atom, + validate = access_rule}, + <<"history_size">> => #option{type = integer, + validate = non_negative}, + <<"room_shaper">> => #option{type = atom, + validate = shaper}, + <<"max_room_id">> => #option{type = int_or_infinity, + validate = non_negative}, + <<"max_room_name">> => #option{type = int_or_infinity, + validate = non_negative}, + <<"max_room_desc">> => #option{type = int_or_infinity, + validate = non_negative}, + <<"min_message_interval">> => #option{type = integer, + validate = non_negative}, + <<"min_presence_interval">> => #option{type = integer, + validate = non_negative}, + <<"max_users">> => #option{type = integer, + validate = positive}, + <<"max_users_admin_threshold">> => #option{type = integer, + validate = positive}, + <<"user_message_shaper">> => #option{type = atom, + validate = shaper}, + <<"user_presence_shaper">> => #option{type = atom, + validate = shaper}, + <<"max_user_conferences">> => #option{type = integer, + validate = non_negative}, + <<"http_auth_pool">> => #option{type = atom, + validate = pool_name}, + <<"load_permanent_rooms_at_startup">> => #option{type = boolean}, + <<"hibernate_timeout">> => #option{type = int_or_infinity, + validate = non_negative}, + <<"hibernated_room_check_interval">> => #option{type = int_or_infinity, + validate = non_negative}, + <<"hibernated_room_timeout">> => #option{type = int_or_infinity, + validate = non_negative}, + <<"default_room">> => default_room_config_spec() + } + }. + +default_room_config_spec() -> + #section{ + items = #{<<"title">> => #option{type = binary}, + <<"description">> => #option{type = binary}, + <<"allow_change_subj">> => #option{type = boolean}, + <<"allow_query_users">> => #option{type = boolean}, + <<"allow_private_messages">> => #option{type = boolean}, + <<"allow_visitor_status">> => #option{type = boolean}, + <<"allow_visitor_nickchange">> => #option{type = boolean}, + <<"public">> => #option{type = boolean}, + <<"public_list">> => #option{type = boolean}, + <<"persistent">> => #option{type = boolean}, + <<"moderated">> => #option{type = boolean}, + <<"members_by_default">> => #option{type = boolean}, + <<"members_only">> => #option{type = boolean}, + <<"allow_user_invites">> => #option{type = boolean}, + <<"allow_multiple_sessions">> => #option{type = boolean}, + <<"password_protected">> => #option{type = boolean}, + <<"password">> => #option{type = binary}, + <<"anonymous">> => #option{type = boolean}, + <<"max_users">> => #option{type = integer, + validate = positive}, + <<"logging">> => #option{type = boolean}, + <<"maygetmemberlist">> => #list{items = #option{type = atom, + validate = non_empty}}, + <<"affiliations">> => #list{items = default_room_affiliations_spec()}, + <<"subject">> => #option{type = binary}, + <<"subject_author">> => #option{type = binary} + }, + format = {kv, default_room_options} + }. + +default_room_affiliations_spec() -> + #section{ + items = #{<<"user">> => #option{type = binary, + validate = non_empty}, + <<"server">> => #option{type = binary, + validate = domain}, + <<"resource">> => #option{type = binary}, + <<"affiliation">> => #option{type = atom, + validate = non_empty}}, + required = all, + process = fun ?MODULE:process_room_affiliation/1 + }. + +process_room_affiliation(KVs) -> + {[[{user, User}], [{server, Server}], [{resource, Res}], [{affiliation, Aff}]], []} = + proplists:split(KVs, [user, server, resource, affiliation]), + {{User, Server, Res}, Aff}. + stop_gen_server(Host) -> Proc = gen_mod:get_module_proc(Host, ?PROCNAME), gen_server:call(Proc, stop), From 8af76f7c43416e4ea0dfabc671617edf37033614 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Wed, 9 Dec 2020 08:05:55 +0100 Subject: [PATCH 053/104] Simplify tests for mod_muc - Split into 3 test cases for clarity - Some options were missing, they were added - Tests rewritten from scratch, there is no 1-to-1 relationship with the previous test cases. --- test/config_parser_SUITE.erl | 371 ++++++++++++++++------------------- 1 file changed, 167 insertions(+), 204 deletions(-) diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index 957572c660..8d3b822145 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -185,6 +185,8 @@ groups() -> mod_last, mod_mam_meta, mod_muc, + mod_muc_default_room, + mod_muc_default_room_affiliations, mod_muc_log, mod_muc_light, mod_offline, @@ -1997,210 +1999,171 @@ mam_failing_riak_cases(T) -> mod_muc(_Config) -> T = fun(Opts) -> #{<<"modules">> => #{<<"mod_muc">> => Opts}} end, - Base = #{ - <<"access">> => <<"all">>, - <<"access_admin">> => <<"none">>, - <<"access_create">> => <<"all">>, - <<"access_persistent">> => <<"all">>, - <<"backend">> => <<"mnesia">>, - <<"hibernated_room_check_interval">> => <<"infinity">>, - <<"hibernated_room_timeout">> => <<"infinity">>, - <<"history_size">> => 20, - <<"host">> => <<"conference.@HOST@">>, - <<"http_auth_pool">> => <<"none">>, - <<"load_permanent_rooms_at_startup">> => false, - <<"max_room_desc">> => <<"infinity">>, - <<"max_room_id">> => <<"infinity">>, - <<"max_room_name">> => <<"infinity">>, - <<"max_user_conferences">> => 0, - <<"max_users">> => 200, - <<"max_users_admin_threshold">> => 5, - <<"min_message_interval">> => 0, - <<"min_presence_interval">> => 0, - <<"room_shaper">> => <<"none">>, - <<"user_message_shaper">> => <<"none">>, - <<"user_presence_shaper">> => <<"none">> - }, - MBase = [{access,all}, - {access_admin,none}, - {access_create,all}, - {access_persistent,all}, - {backend,mnesia}, - {hibernated_room_check_interval,infinity}, - {hibernated_room_timeout,infinity}, - {history_size,20}, - {host,"conference.@HOST@"}, - {http_auth_pool,none}, - {load_permanent_rooms_at_startup,false}, - {max_room_desc,infinity}, - {max_room_id,infinity}, - {max_room_name,infinity}, - {max_user_conferences,0}, - {max_users,200}, - {max_users_admin_threshold,5}, - {min_message_interval,0}, - {min_presence_interval,0}, - {room_shaper,none}, - {user_message_shaper,none}, - {user_presence_shaper,none}], - ensure_sorted(MBase), - run_multi( - %% Test configurations with one option only - check_one_opts(mod_muc, MBase, Base, T) ++ [ - ?_eqf(modopts(mod_muc, MBase), T(Base)), - ?_eqf(modopts(mod_muc, [{default_room_options,[]}]), - T(#{<<"default_room">> => #{}})) - ] ++ some_muc_opts_cases(T) - ++ some_room_opts_cases(T) - ++ bad_muc_opts_cases(T) - ++ bad_room_opts_cases(T) - ). - -some_muc_opts_cases(T) -> - [some_muc_opts_case(T, K, Toml, Mim) || {K, Toml, Mim} <- some_muc_opts()]. - -some_muc_opts_case(T, K, Toml, Mim) -> - ?_eqf(modopts(mod_muc, [{K, Mim}]), T(#{a2b(K) => Toml})). - -bad_muc_opts_cases(T) -> - [bad_muc_opts_case(T, K, Toml) || {K, Toml} <- bad_muc_opts()]. - -bad_muc_opts_case(T, K, Toml) -> - ?_errf(T(#{a2b(K) => Toml})). - -some_room_opts_cases(T) -> - [some_room_opts_case(T, K, Toml, Mim) || {K, Toml, Mim} <- some_room_opts()]. - -some_room_opts_case(T, K, Toml, Mim) -> - ?_eqf(modopts(mod_muc, [{default_room_options, [{K, Mim}]}]), - T(#{<<"default_room">> => #{a2b(K) => Toml}})). - -bad_room_opts_cases(T) -> - [bad_room_opts_case(T, K, Toml) || {K, Toml} <- bad_room_opts()]. - -bad_room_opts_case(T, K, Toml) -> - ?_errf(T(#{<<"default_room">> => #{a2b(K) => Toml}})). - -some_muc_opts() -> - %% name toml mim - [{hibernated_room_check_interval, 1, 1}, - {hibernated_room_timeout, 1, 1}, - {history_size, 0, 0}, - {host, <<"good">>, "good"}, - {http_auth_pool, <<"deadpool">>, deadpool}, - {load_permanent_rooms_at_startup, true, true}, - {max_room_desc, 10, 10}, - {max_room_id, 10, 10}, - {max_room_name, 10, 10}, - {max_user_conferences, 10, 10}, - {max_users, 10, 10}, - {max_users_admin_threshold, 10, 10}, - {min_message_interval, 10, 10}, - {min_presence_interval, 10, 10}, - {room_shaper, <<"good">>, good}, - {user_message_shaper, <<"good">>, good}, - {user_presence_shaper, <<"good">>, good}]. - -bad_muc_opts() -> - %% name toml - [{access, 1}, - {access_admin, 1}, - {access_create, 1}, - {access_persistent, 1}, - {backend, 1}, - {backend, <<"meowmoew">>}, - {hibernated_room_check_interval, -1}, - {hibernated_room_timeout, -1}, - {history_size, -1}, - {host, 1}, - {host, <<"bad bad bad">>}, - {http_auth_pool, 1}, - {load_permanent_rooms_at_startup, 1}, - {max_room_desc, -1}, - {max_room_id, -1}, - {max_room_name, -1}, - {max_user_conferences, -1}, - {max_users, -1}, - {max_users_admin_threshold, -1}, - {min_message_interval, -1}, - {min_presence_interval, -1}, - {room_shaper, 1}, - {user_message_shaper, 1}, - {user_presence_shaper, 1}]. - -some_room_opts() -> - [{title, <<"Test">>, <<"Test">>}, - {description, <<"Test">>, <<"Test">>}, - {allow_change_subj, true, true}, - {allow_query_users, true, true}, - {allow_private_messages, true, true}, - {allow_visitor_status, true, true}, - {allow_visitor_nickchange, true, true}, - {public, true, true}, - {public_list, true, true}, - {moderated, true, true}, - {members_by_default, true, true}, - {members_only, true, true}, - {allow_user_invites, true, true}, - {allow_multiple_sessions, true, true}, - {password_protected, true, true}, - {password, <<"secret">>, <<"secret">>}, - {anonymous, true, true}, - {max_users, 10, 10}, - {logging, true, true}, - {maygetmemberlist, [<<"moderator">>, <<"user">>], [moderator, user]}, - {affiliations, [#{<<"user">> => <<"Alice">>, <<"server">> => <<"home">>, - <<"resource">> => <<>>, <<"affiliation">> => <<"member">>}], - [{{<<"Alice">>, <<"home">>, <<>>}, member}]}, - {subject, <<"Fight">>, <<"Fight">>}, - {subject_author, <<"meow">>, <<"meow">>} - ]. - -bad_room_opts() -> - [{title, 1}, - {description, 1}, - {allow_change_subj, 1}, - {allow_query_users, 1}, - {allow_private_messages, 1}, - {allow_visitor_status, 1}, - {allow_visitor_nickchange, 1}, - {public, 1}, - {public_list, 1}, - {persistent, 1}, - {moderated, 1}, - {members_by_default, 1}, - {members_only, 1}, - {allow_user_invites, 1}, - {allow_multiple_sessions, 1}, - {password_protected, 1}, - {password, 1}, - {anonymous, 1}, - {max_users, -1}, - {logging, 1}, - {maygetmemberlist, 1}, - {maygetmemberlist, [1]}, - {maygetmemberlist, #{}}, - {subject, 1}, - {subject_author, 1}, - {affiliations, [1]}, - {affiliations, 1}, - {affiliations, [#{<<"user">> => <<"Alice">>, <<"server">> => <<"home home">>, - <<"resource">> => <<>>, <<"affiliation">> => <<"member">>}]}, - {affiliations, [#{<<"user">> => 1, <<"server">> => <<"home">>, - <<"resource">> => <<>>, <<"affiliation">> => <<"member">>}]}, - {affiliations, [#{<<"user">> => <<"Alice">>, <<"server">> => 1, - <<"resource">> => <<>>, <<"affiliation">> => <<"member">>}]}, - {affiliations, [#{<<"user">> => <<"Alice">>, <<"server">> => <<"home">>, - <<"resource">> => 1, <<"affiliation">> => <<"member">>}]}, - {affiliations, [#{<<"user">> => <<"Alice">>, <<"server">> => <<"home">>, - <<"resource">> => <<>>, <<"affiliation">> => 1}]}, - {affiliations, [#{<<"server">> => <<"home">>, - <<"resource">> => <<>>, <<"affiliation">> => <<"member">>}]}, - {affiliations, [#{<<"user">> => <<"Alice">>, - <<"resource">> => <<>>, <<"affiliation">> => <<"member">>}]}, - {affiliations, [#{<<"user">> => <<"Alice">>, <<"server">> => <<"home">>, - <<"affiliation">> => <<"member">>}]} %% Resource required - ]. + M = fun(Cfg) -> modopts(mod_muc, Cfg) end, + ?eqf(M([{host, "conference.@HOST@"}]), + T(#{<<"host">> => <<"conference.@HOST@">>})), + ?eqf(M([{backend, mnesia}]), + T(#{<<"backend">> => <<"mnesia">>})), + ?eqf(M([{access, all}]), + T(#{<<"access">> => <<"all">>})), + ?eqf(M([{access_create, admin}]), + T(#{<<"access_create">> => <<"admin">>})), + ?eqf(M([{access_admin, none}]), + T(#{<<"access_admin">> => <<"none">>})), + ?eqf(M([{access_persistent, all}]), + T(#{<<"access_persistent">> => <<"all">>})), + ?eqf(M([{history_size, 20}]), + T(#{<<"history_size">> => 20})), + ?eqf(M([{room_shaper, muc_room_shaper}]), + T(#{<<"room_shaper">> => <<"muc_room_shaper">>})), + ?eqf(M([{max_room_id, infinity}]), + T(#{<<"max_room_id">> => <<"infinity">>})), + ?eqf(M([{max_room_name, 30}]), + T(#{<<"max_room_name">> => 30})), + ?eqf(M([{max_room_desc, 0}]), + T(#{<<"max_room_desc">> => 0})), + ?eqf(M([{min_message_interval, 10}]), + T(#{<<"min_message_interval">> => 10})), + ?eqf(M([{min_presence_interval, 0}]), + T(#{<<"min_presence_interval">> => 0})), + ?eqf(M([{max_users, 30}]), + T(#{<<"max_users">> => 30})), + ?eqf(M([{max_users_admin_threshold, 2}]), + T(#{<<"max_users_admin_threshold">> => 2})), + ?eqf(M([{user_message_shaper, muc_msg_shaper}]), + T(#{<<"user_message_shaper">> => <<"muc_msg_shaper">>})), + ?eqf(M([{user_presence_shaper, muc_pres_shaper}]), + T(#{<<"user_presence_shaper">> => <<"muc_pres_shaper">>})), + ?eqf(M([{max_user_conferences, 10}]), + T(#{<<"max_user_conferences">> => 10})), + ?eqf(M([{http_auth_pool, external_auth}]), + T(#{<<"http_auth_pool">> => <<"external_auth">>})), + ?eqf(M([{load_permanent_rooms_at_startup, true}]), + T(#{<<"load_permanent_rooms_at_startup">> => true})), + ?eqf(M([{hibernate_timeout, infinity}]), + T(#{<<"hibernate_timeout">> => <<"infinity">>})), + ?eqf(M([{hibernated_room_check_interval, 5000}]), + T(#{<<"hibernated_room_check_interval">> => 5000})), + ?eqf(M([{hibernated_room_timeout, 0}]), + T(#{<<"hibernated_room_timeout">> => 0})), + ?errf(T(#{<<"host">> => <<>>})), + ?errf(T(#{<<"backend">> => <<"amnesia">>})), + ?errf(T(#{<<"access">> => <<>>})), + ?errf(T(#{<<"access_create">> => 1})), + ?errf(T(#{<<"access_admin">> => []})), + ?errf(T(#{<<"access_persistent">> => true})), + ?errf(T(#{<<"history_size">> => <<"20">>})), + ?errf(T(#{<<"room_shaper">> => <<>>})), + ?errf(T(#{<<"max_room_id">> => #{}})), + ?errf(T(#{<<"max_room_name">> => <<"infinite!">>})), + ?errf(T(#{<<"max_room_desc">> => -1})), + ?errf(T(#{<<"min_message_interval">> => -10})), + ?errf(T(#{<<"min_presence_interval">> => <<"infinity">>})), + ?errf(T(#{<<"max_users">> => 0})), + ?errf(T(#{<<"max_users_admin_threshold">> => 0})), + ?errf(T(#{<<"user_message_shaper">> => []})), + ?errf(T(#{<<"user_presence_shaper">> => <<>>})), + ?errf(T(#{<<"max_user_conferences">> => -1})), + ?errf(T(#{<<"http_auth_pool">> => <<>>})), + ?errf(T(#{<<"load_permanent_rooms_at_startup">> => <<"true">>})), + ?errf(T(#{<<"hibernate_timeout">> => <<"really big">>})), + ?errf(T(#{<<"hibernated_room_check_interval">> => -1})), + ?errf(T(#{<<"hibernated_room_timeout">> => false})). + +mod_muc_default_room(_Config) -> + T = fun(Opts) -> #{<<"modules">> => + #{<<"mod_muc">> => #{<<"default_room">> => Opts}}} end, + M = fun(Cfg) -> modopts(mod_muc, [{default_room_options, Cfg}]) end, + ?eqf(M([]), T(#{})), + ?eqf(M([{title, <<"living room">>}]), + T(#{<<"title">> => <<"living room">>})), + ?eqf(M([{description, <<"a room that is alive">>}]), + T(#{<<"description">> => <<"a room that is alive">>})), + ?eqf(M([{allow_change_subj, true}]), + T(#{<<"allow_change_subj">> => true})), + ?eqf(M([{allow_query_users, false}]), + T(#{<<"allow_query_users">> => false})), + ?eqf(M([{allow_private_messages, true}]), + T(#{<<"allow_private_messages">> => true})), + ?eqf(M([{allow_visitor_status, false}]), + T(#{<<"allow_visitor_status">> => false})), + ?eqf(M([{allow_visitor_nickchange, true}]), + T(#{<<"allow_visitor_nickchange">> => true})), + ?eqf(M([{public, false}]), + T(#{<<"public">> => false})), + ?eqf(M([{public_list, true}]), + T(#{<<"public_list">> => true})), + ?eqf(M([{persistent, true}]), + T(#{<<"persistent">> => true})), + ?eqf(M([{moderated, false}]), + T(#{<<"moderated">> => false})), + ?eqf(M([{members_by_default, true}]), + T(#{<<"members_by_default">> => true})), + ?eqf(M([{members_only, false}]), + T(#{<<"members_only">> => false})), + ?eqf(M([{allow_user_invites, true}]), + T(#{<<"allow_user_invites">> => true})), + ?eqf(M([{allow_multiple_sessions, false}]), + T(#{<<"allow_multiple_sessions">> => false})), + ?eqf(M([{password_protected, true}]), + T(#{<<"password_protected">> => true})), + ?eqf(M([{password, <<"secret">>}]), + T(#{<<"password">> => <<"secret">>})), + ?eqf(M([{anonymous, true}]), + T(#{<<"anonymous">> => true})), + ?eqf(M([{max_users, 100}]), + T(#{<<"max_users">> => 100})), + ?eqf(M([{logging, false}]), + T(#{<<"logging">> => false})), + ?eqf(M([{maygetmemberlist, [moderator]}]), + T(#{<<"maygetmemberlist">> => [<<"moderator">>]})), + ?eqf(M([{subject, <<"Lambda days">>}]), + T(#{<<"subject">> => <<"Lambda days">>})), + ?eqf(M([{subject_author, <<"Alice">>}]), + T(#{<<"subject_author">> => <<"Alice">>})), + ?errf(T(<<"bad value">>)), + ?errf(T(#{<<"title">> => true})), + ?errf(T(#{<<"description">> => 1})), + ?errf(T(#{<<"allow_change_subj">> => <<"true">>})), + ?errf(T(#{<<"allow_query_users">> => <<>>})), + ?errf(T(#{<<"allow_private_messages">> => 1})), + ?errf(T(#{<<"allow_visitor_status">> => []})), + ?errf(T(#{<<"allow_visitor_nickchange">> => #{}})), + ?errf(T(#{<<"public">> => 0})), + ?errf(T(#{<<"public_list">> => [false]})), + ?errf(T(#{<<"persistent">> => 1})), + ?errf(T(#{<<"moderated">> => <<"yes">>})), + ?errf(T(#{<<"members_by_default">> => 0})), + ?errf(T(#{<<"members_only">> => [true]})), + ?errf(T(#{<<"allow_user_invites">> => <<>>})), + ?errf(T(#{<<"allow_multiple_sessions">> => []})), + ?errf(T(#{<<"password_protected">> => #{}})), + ?errf(T(#{<<"password">> => false})), + ?errf(T(#{<<"anonymous">> => <<"maybe">>})), + ?errf(T(#{<<"max_users">> => 0})), + ?errf(T(#{<<"logging">> => [true, false]})), + ?errf(T(#{<<"maygetmemberlist">> => <<"moderator">>})), + ?errf(T(#{<<"maygetmemberlist">> => [<<>>]})), + ?errf(T(#{<<"subject">> => [<<"subjective">>]})), + ?errf(T(#{<<"subject_author">> => 1})). + +mod_muc_default_room_affiliations(_Config) -> + T = fun(Opts) -> #{<<"modules">> => + #{<<"mod_muc">> => + #{<<"default_room">> => #{<<"affiliations">> => Opts}}}} end, + M = fun(Cfg) -> modopts(mod_muc, [{default_room_options, [{affiliations, Cfg}]}]) end, + RequiredOpts = #{<<"user">> => <<"alice">>, + <<"server">> => <<"localhost">>, + <<"resource">> => <<"phone">>, + <<"affiliation">> => <<"moderator">>}, + ExpectedCfg = {{<<"alice">>, <<"localhost">>, <<"phone">>}, moderator}, + ?eqf(M([]), T([])), + ?eqf(M([ExpectedCfg]), T([RequiredOpts])), + [?errf(T([maps:remove(Key, RequiredOpts)])) || Key <- maps:keys(RequiredOpts)], + ?errf(T([RequiredOpts#{<<"user">> := <<>>}])), + ?errf(T([RequiredOpts#{<<"server">> := <<"domain? not really!">>}])), + ?errf(T([RequiredOpts#{<<"resource">> := false}])), + ?errf(T([RequiredOpts#{<<"affiliation">> := <<>>}])). mod_muc_log(_Config) -> T = fun(Opts) -> #{<<"modules">> => #{<<"mod_muc_log">> => Opts}} end, From 4c8afa3c165b77bf66b21b452970a8425fc4b81b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Wed, 9 Dec 2020 11:53:58 +0100 Subject: [PATCH 054/104] Make the config of mod_muc_light declarative - Change the format of config_schema to disambiguate the field type. Each type has its own field name. Benefits: - Easy validation of the value for each type (was not done before) - No need for an amibuguous type like 'integer_or_float_or_string' The only drawback is the need to ensure that only one value is provided. - Add some missing validators. --- src/config/mongoose_config_parser_toml.erl | 38 ++----------- src/config/mongoose_config_spec.erl | 3 +- src/config/mongoose_config_validator_toml.erl | 42 -------------- src/muc_light/mod_muc_light.erl | 56 ++++++++++++++++++- 4 files changed, 60 insertions(+), 79 deletions(-) diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index 42411406d1..77bec700d8 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -196,29 +196,6 @@ module_opt([<<"top_link">>, <<"mod_muc_log">>|_] = Path, V) -> [{top_link, Link}]; module_opt([<<"spam_prevention">>, <<"mod_muc_log">>|_], V) -> [{spam_prevention, V}]; -module_opt([<<"host">>, <<"mod_muc_light">>|_], V) -> - [{host, b2l(V)}]; -module_opt([<<"equal_occupants">>, <<"mod_muc_light">>|_], V) -> - [{equal_occupants, V}]; -module_opt([<<"legacy_mode">>, <<"mod_muc_light">>|_], V) -> - [{legacy_mode, V}]; -module_opt([<<"rooms_per_user">>, <<"mod_muc_light">>|_], V) -> - [{rooms_per_user, int_or_infinity(V)}]; -module_opt([<<"blocking">>, <<"mod_muc_light">>|_], V) -> - [{blocking, V}]; -module_opt([<<"all_can_configure">>, <<"mod_muc_light">>|_], V) -> - [{all_can_configure, V}]; -module_opt([<<"all_can_invite">>, <<"mod_muc_light">>|_], V) -> - [{all_can_invite, V}]; -module_opt([<<"max_occupants">>, <<"mod_muc_light">>|_], V) -> - [{max_occupants, int_or_infinity(V)}]; -module_opt([<<"rooms_per_page">>, <<"mod_muc_light">>|_], V) -> - [{rooms_per_page, int_or_infinity(V)}]; -module_opt([<<"rooms_in_rosters">>, <<"mod_muc_light">>|_], V) -> - [{rooms_in_rosters, V}]; -module_opt([<<"config_schema">>, <<"mod_muc_light">>|_] = Path, V) -> - Configs = parse_list(Path, V), - [{config_schema, Configs}]; module_opt([<<"access_max_user_messages">>, <<"mod_offline">>|_], V) -> [{access_max_user_messages, b2a(V)}]; module_opt([<<"send_pings">>, <<"mod_ping">>|_], V) -> @@ -552,13 +529,6 @@ mod_muc_log_top_link([<<"target">>|_], V) -> mod_muc_log_top_link([<<"text">>|_], V) -> [b2l(V)]. --spec mod_muc_light_config_schema(path(), toml_section()) -> [option()]. -mod_muc_light_config_schema(_, #{<<"field">> := Field, <<"value">> := Val, - <<"internal_key">> := Key, <<"type">> := Type}) -> - [{b2l(Field), Val, b2a(Key), b2a(Type)}]; -mod_muc_light_config_schema(_, #{<<"field">> := Field, <<"value">> := Val}) -> - [{b2l(Field), b2l(Val)}]. - -spec mod_pubsub_pep_mapping(path(), toml_section()) -> [option()]. mod_pubsub_pep_mapping(_, #{<<"namespace">> := Name, <<"node">> := Node}) -> [{b2l(Name), b2l(Node)}]. @@ -809,7 +779,8 @@ convert(<<"infinity">>, int_or_infinity) -> infinity; %% TODO maybe use TOML '+i convert(V, int_or_infinity) when is_integer(V) -> V; convert(V, int_or_atom) when is_integer(V) -> V; convert(V, int_or_atom) -> b2a(V); -convert(V, integer) when is_integer(V) -> V. +convert(V, integer) when is_integer(V) -> V; +convert(V, float) when is_float(V) -> V. format_spec(#section{format = Format}) -> Format; format_spec(#list{format = Format}) -> Format; @@ -908,7 +879,8 @@ node_to_string(Node) -> [binary_to_list(Node)]. Mod =/= <<"mod_csi">>, Mod =/= <<"mod_disco">>, Mod =/= <<"mod_event_pusher">>, - Mod =/= <<"mod_muc">>). % TODO temporary, remove with 'handler/1' + Mod =/= <<"mod_muc">>, + Mod =/= <<"mod_muc_light">>). % TODO temporary, remove with 'handler/1' -spec handler(path()) -> fun((path(), toml_value()) -> option()) | mongoose_config_spec:config_node(). @@ -956,8 +928,6 @@ handler([_, _, <<"mod_mam_meta">>, <<"modules">>]) -> fun mod_mam_opts/2; handler([_, <<"top_link">>, <<"mod_muc_log">>, <<"modules">>]) -> fun mod_muc_log_top_link/2; -handler([_, <<"config_schema">>, <<"mod_muc_light">>, <<"modules">>]) -> - fun mod_muc_light_config_schema/2; handler([_, <<"plugins">>, <<"mod_pubsub">>, <<"modules">>]) -> fun(_, V) -> [V] end; handler([_, <<"pep_mapping">>, <<"mod_pubsub">>, <<"modules">>]) -> diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index 7ee2d15611..84eac7e1d0 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -738,7 +738,8 @@ all_modules() -> mod_csi, mod_disco, mod_event_pusher, - mod_muc]. + mod_muc, + mod_muc_light]. %% path: (host_config[].)modules.*.iqdisc iqdisc() -> diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index 4da1c17e36..f70a7118af 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -631,39 +631,6 @@ validate([<<"remove_on_kicked">>, <<"mod_inbox">>, <<"modules">>|_], validate([item, <<"reset_markers">>, <<"mod_inbox">>, <<"modules">>|_], [V]) -> validate_chat_marker_type(V); -validate([<<"all_can_configure">>, <<"mod_muc_light">>, <<"modules">>|_], - [{all_can_configure, V}]) -> - validate_boolean(V); -validate([<<"all_can_invite">>, <<"mod_muc_light">>, <<"modules">>|_], - [{all_can_invite, V}]) -> - validate_boolean(V); -validate([<<"backend">>, <<"mod_muc_light">>, <<"modules">>|_], - [{backend, V}]) -> - validate_backend(mod_muc_light_db, V); -validate([<<"blocking">>, <<"mod_muc_light">>, <<"modules">>|_], - [{blocking, V}]) -> - validate_boolean(V); -validate([item, <<"config_schema">>, <<"mod_muc_light">>, <<"modules">>|_], - [V]) -> - validate_muc_config_schema(V); -validate([<<"equal_occupants">>, <<"mod_muc_light">>, <<"modules">>|_], - [{equal_occupants, V}]) -> - validate_boolean(V); -validate([<<"host">>, <<"mod_muc_light">>, <<"modules">>|_], - [{host, V}]) -> - validate_domain_template(V); -validate([<<"legacy_mode">>, <<"mod_muc_light">>, <<"modules">>|_], - [{legacy_mode, V}]) -> - validate_boolean(V); -validate([<<"max_occupants">>, <<"mod_muc_light">>, <<"modules">>|_], - [{max_occupants, V}]) -> - validate_positive_integer_or_infinity(V); -validate([<<"rooms_in_rosters">>, <<"mod_muc_light">>, <<"modules">>|_], - [{rooms_in_rosters, V}]) -> - validate_boolean(V); -validate([<<"rooms_per_page">>, <<"mod_muc_light">>, <<"modules">>|_], - [{rooms_per_page, V}]) -> - validate_positive_integer_or_infinity(V); validate([<<"access_max_user_messages">>, <<"mod_offline">>, <<"modules">>|_], [{access_max_user_messages, V}]) -> validate_access_rule(V); @@ -1053,15 +1020,6 @@ validate_top_link({Url, Text}) -> validate_url(Url), validate_non_empty_string(Text). -validate_muc_config_schema({Field, Value}) -> - validate_non_empty_string(Field), - validate_string(Value); -validate_muc_config_schema({Field, Value, InternalField, FieldType}) - when is_list(Value); is_float(Value); is_integer(Value) -> - validate_non_empty_string(Field), - validate_enum(FieldType, [binary, integer, float]), - validate_non_empty_atom(InternalField). - validate_pubsub_nodetree(Value) -> validate_non_empty_binary(Value), validate_backend(nodetree, b2a(Value)). diff --git a/src/muc_light/mod_muc_light.erl b/src/muc_light/mod_muc_light.erl index 59a9fdd23c..99ea5320c0 100644 --- a/src/muc_light/mod_muc_light.erl +++ b/src/muc_light/mod_muc_light.erl @@ -17,6 +17,7 @@ -include("mod_muc_light.hrl"). -include("mod_roster.hrl"). -include("mongoose_rsm.hrl"). +-include("mongoose_config_spec.hrl"). -behaviour(gen_mod). -behaviour(mongoose_packet_handler). @@ -27,12 +28,15 @@ -export([config_schema/1, default_config/1]). %% For Administration API --export([try_to_create_room/3, +-export([try_to_create_room/3, change_room_config/4, delete_room/1]). %% gen_mod callbacks --export([start/2, stop/1]). +-export([start/2, stop/1, config_spec/0]). + +%% config processing callback +-export([process_config_schema/1]). %% Packet handler export -export([process_packet/5]). @@ -192,6 +196,54 @@ stop(Host) -> ok. +-spec config_spec() -> mongoose_config_spec:config_section(). +config_spec() -> + #section{ + items = #{<<"backend">> => #option{type = atom, + validate = {module, mod_muc_light_db}}, + <<"host">> => #option{type = string, + validate = domain_template}, + <<"equal_occupants">> => #option{type = boolean}, + <<"legacy_mode">> => #option{type = boolean}, + <<"rooms_per_user">> => #option{type = int_or_infinity, + validate = positive}, + <<"blocking">> => #option{type = boolean}, + <<"all_can_configure">> => #option{type = boolean}, + <<"all_can_invite">> => #option{type = boolean}, + <<"max_occupants">> => #option{type = int_or_infinity, + validate = positive}, + <<"rooms_per_page">> => #option{type = int_or_infinity, + validate = positive}, + <<"rooms_in_rosters">> => #option{type = boolean}, + <<"config_schema">> => #list{items = config_schema_spec()} + } + }. + +config_schema_spec() -> + #section{ + items = #{<<"field">> => #option{type = string, + validate = non_empty}, + <<"string_value">> => #option{type = binary}, + <<"integer_value">> => #option{type = integer}, + <<"float_value">> => #option{type = float}, + <<"internal_key">> => #option{type = atom, + validate = non_empty} + }, + required = [<<"field">>], + process = fun ?MODULE:process_config_schema/1 + }. + +process_config_schema(KVs) -> + {[[{field, FieldName}], InternalKeyOpts], ValueOpts} = + proplists:split(KVs, [field, internal_key]), + {Value, Type} = process_config_schema_value(ValueOpts), + InternalKey = proplists:get_value(internal_key, InternalKeyOpts, list_to_atom(FieldName)), + {FieldName, Value, InternalKey, Type}. + +process_config_schema_value([{string_value, Val}]) -> {Val, binary}; +process_config_schema_value([{integer_value, Val}]) -> {Val, integer}; +process_config_schema_value([{float_value, Val}]) -> {Val, float}. + hooks(Host, MUCHost) -> [{is_muc_room_owner, MUCHost, ?MODULE, is_room_owner, 50}, {muc_room_pid, MUCHost, ?MODULE, muc_room_pid, 50}, From 0d5d35424e684c6403edb78f68f624fdf6794650 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Wed, 9 Dec 2020 11:58:29 +0100 Subject: [PATCH 055/104] Test and document the changed format of 'mod_muc_light' config - Simplify and split tests - Capture untested cases --- doc/modules/mod_muc_light.md | 52 +++------ test/config_parser_SUITE.erl | 109 ++++++++++-------- test/config_parser_SUITE_data/modules.options | 4 +- test/config_parser_SUITE_data/modules.toml | 5 +- 4 files changed, 82 insertions(+), 88 deletions(-) diff --git a/doc/modules/mod_muc_light.md b/doc/modules/mod_muc_light.md index c5123b92f9..c4409ef791 100644 --- a/doc/modules/mod_muc_light.md +++ b/doc/modules/mod_muc_light.md @@ -97,51 +97,34 @@ When enabled, rooms the user occupies are included in their roster. * **Default:** ```toml - [[modules.mod_muc_light.config_schema]] + [[modules.mod_muc_light.config_schema]] field = "roomname" - value = "Untitled" - - [[modules.mod_muc_light.config_schema]] + string_value = "Untitled" + + [[modules.mod_muc_light.config_schema]] field = "subject" - value = "" + string_value = "" ``` - * **Example:** + * **Example:** ```toml - [[modules.mod_muc_light.config_schema]] + [[modules.mod_muc_light.config_schema]] field = "display-lines" - value = 30 + integer_value = 30 internal_key = "display_lines" - type = "integer" ``` Defines fields allowed in the room configuration. - -Allowed `config_schema` items are (may be mixed): - -* Field name and a default value. The value has to be a string. An example: - -```toml -field = "field_name" -value = "default_value" -``` - -* Field name, a default value, an internal key representation string and a type. - Valid config field types are: - - * `binary` (i.e. any valid XML CDATA) - * `integer` - * `float` -Useful only for debugging or custom applications. An example: +Each `config_schema` item is a TOML table with the following keys: -```toml -field = "display-lines" -value = 30 -internal_key = "display_lines" -type = "integer" -``` +* `field` - mandatory, non-empty string - field name. +* `string_value`, `integer_value`, `float_value` - exactly one of them has to be present, depending on the type of the field: + * `string_value` - string, + * `integer_value` - integer, + * `float_value` - floating-point number. +* `internal_key` - optional, non-empty string - field name used in the internal representation, useful only for debugging or custom applications. By default it is the same as `field`. **WARNING!** Lack of the `roomname` field will cause room names in Disco results and Roster items be set to the room username. @@ -163,13 +146,12 @@ and Roster items be set to the room username. [[modules.mod_muc_light.config_schema]] field = "roomname" - value = "The Room" + string_value = "The Room" [[modules.mod_muc_light.config_schema]] field = "display-lines" - value = 30 + integer_value = 30 internal_key = "display_lines" - type = "integer" ``` ## Metrics diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index 8d3b822145..5cd665919e 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -189,6 +189,7 @@ groups() -> mod_muc_default_room_affiliations, mod_muc_log, mod_muc_light, + mod_muc_light_config_schema, mod_offline, mod_ping, mod_privacy, @@ -2208,54 +2209,66 @@ mod_muc_log_bad_opts() -> mod_muc_light(_Config) -> T = fun(Opts) -> #{<<"modules">> => #{<<"mod_muc_light">> => Opts}} end, - run_multi( - generic_opts_cases(mod_muc_light, T, mod_muc_light_opts()) ++ - generic_bad_opts_cases(T, mod_muc_light_bad_opts()) - ). - -mod_muc_light_opts() -> - [{host, <<"muclight.@HOST@">>, "muclight.@HOST@"}, - {backend, <<"mnesia">>, mnesia}, - {equal_occupants, true, true}, - {legacy_mode, true, true}, - {rooms_per_user, 1, 1}, - {rooms_per_user, <<"infinity">>, infinity}, - {blocking, true, true}, - {all_can_configure, true, true}, - {all_can_invite, true, true}, - {max_occupants, 1, 1}, - {max_occupants, <<"infinity">>, infinity}, - {rooms_per_page, 1, 1}, - {rooms_per_page, <<"infinity">>, infinity}, - {rooms_in_rosters, true, true}, - {config_schema, [ - #{<<"field">> => <<"roomname">>, <<"value">> => <<"My Room">>}, - #{<<"field">> => <<"subject">>, <<"value">> => <<"Hi">>}, - #{<<"field">> => <<"priority">>, <<"value">> => 0, - <<"internal_key">> => <<"priority">>, <<"type">> => <<"integer">>} - ], - [{"roomname", "My Room"}, {"subject", "Hi"}, - {"priority", 0, priority, integer}]} - ]. - -mod_muc_light_bad_opts() -> - [{host, 1}, - {host, <<"test test">>}, - {equal_occupants, 1}, - {equal_occupants, #{}}, - {legacy_mode, 1}, - {rooms_per_user, true}, - {blocking, 1}, - {all_can_configure, 1}, - {all_can_invite, 1}, - {max_occupants, true}, - {rooms_per_page, false}, - {rooms_in_rosters, 1}, - {config_schema, [ #{<<"field">> => 1, <<"value">> => <<"ok">>} ]}, - {config_schema, [ #{<<"field">> => <<"subject">>} ]}, - {config_schema, [ #{<<"field">> => <<"priority">>, <<"value">> => 0, - <<"internal_key">> => <<"priority">>, <<"type">> => <<"bad_integer">>} ]} - ]. + M = fun(Cfg) -> modopts(mod_muc_light, Cfg) end, + ?eqf(M([{backend, mnesia}]), + T(#{<<"backend">> => <<"mnesia">>})), + ?eqf(M([{host, "muclight.@HOST@"}]), + T(#{<<"host">> => <<"muclight.@HOST@">>})), + ?eqf(M([{equal_occupants, true}]), + T(#{<<"equal_occupants">> => true})), + ?eqf(M([{legacy_mode, false}]), + T(#{<<"legacy_mode">> => false})), + ?eqf(M([{rooms_per_user, 100}]), + T(#{<<"rooms_per_user">> => 100})), + ?eqf(M([{blocking, false}]), + T(#{<<"blocking">> => false})), + ?eqf(M([{all_can_configure, true}]), + T(#{<<"all_can_configure">> => true})), + ?eqf(M([{all_can_invite, false}]), + T(#{<<"all_can_invite">> => false})), + ?eqf(M([{max_occupants, infinity}]), + T(#{<<"max_occupants">> => <<"infinity">>})), + ?eqf(M([{rooms_per_page, 10}]), + T(#{<<"rooms_per_page">> => 10})), + ?eqf(M([{rooms_in_rosters, true}]), + T(#{<<"rooms_in_rosters">> => true})), + ?errf(T(#{<<"backend">> => <<"frontend">>})), + ?errf(T(#{<<"host">> => <<"what is a domain?!">>})), + ?errf(T(#{<<"equal_occupants">> => <<"true">>})), + ?errf(T(#{<<"legacy_mode">> => 1234})), + ?errf(T(#{<<"rooms_per_user">> => 0})), + ?errf(T(#{<<"blocking">> => <<"true">>})), + ?errf(T(#{<<"all_can_configure">> => []})), + ?errf(T(#{<<"all_can_invite">> => #{}})), + ?errf(T(#{<<"max_occupants">> => <<"seven">>})), + ?errf(T(#{<<"rooms_per_page">> => false})), + ?errf(T(#{<<"rooms_in_rosters">> => [1, 2, 3]})). + +mod_muc_light_config_schema(_Config) -> + T = fun(Opts) -> #{<<"modules">> => + #{<<"mod_muc_light">> => #{<<"config_schema">> => Opts}}} end, + M = fun(Cfg) -> modopts(mod_muc_light, [{config_schema, Cfg}]) end, + Field = #{<<"field">> => <<"my_field">>}, + ?eqf(M([]), T([])), + ?eqf(M([{"my_field", <<"My Room">>, my_field, binary}]), + T([Field#{<<"string_value">> => <<"My Room">>}])), + ?eqf(M([{"my_field", 1, my_field, integer}]), + T([Field#{<<"integer_value">> => 1}])), + ?eqf(M([{"my_field", 0.5, my_field, float}]), + T([Field#{<<"float_value">> => 0.5}])), + ?eqf(M([{"my_field", 0, your_field, integer}]), + T([Field#{<<"integer_value">> => 0, + <<"internal_key">> => <<"your_field">>}])), + ?errf(T([#{<<"string_value">> => <<"My Room">>}])), + ?errf(T([#{<<"field">> => <<>>, + <<"string_value">> => <<"My Room">>}])), + ?errf(T([Field#{<<"string_value">> => 0}])), + ?errf(T([Field#{<<"integer_value">> => 1.5}])), + ?errf(T([Field#{<<"float_value">> => 1}])), + ?errf(T([Field#{<<"integer_value">> => 0, + <<"string_value">> => <<"My Room">>}])), + ?errf(T([Field#{<<"integer_value">> => 0, + <<"internal_key">> => <<>>}])). mod_offline(_Config) -> T = fun(Opts) -> #{<<"modules">> => #{<<"mod_offline">> => Opts}} end, diff --git a/test/config_parser_SUITE_data/modules.options b/test/config_parser_SUITE_data/modules.options index 65b335b525..134ae4622a 100644 --- a/test/config_parser_SUITE_data/modules.options +++ b/test/config_parser_SUITE_data/modules.options @@ -196,7 +196,7 @@ {host,"muclight.example.com"}, {equal_occupants,true}, {config_schema, - [{"roomname","The Room"}, + [{"roomname",<<"The Room">>,roomname,binary}, {"display-lines",30,display_lines,integer}]}, {blocking,false}, {all_can_invite,true}, @@ -465,7 +465,7 @@ {host,"muclight.example.com"}, {equal_occupants,true}, {config_schema, - [{"roomname","The Room"}, + [{"roomname",<<"The Room">>,roomname,binary}, {"display-lines",30,display_lines,integer}]}, {blocking,false}, {all_can_invite,true}, diff --git a/test/config_parser_SUITE_data/modules.toml b/test/config_parser_SUITE_data/modules.toml index 3c9ae3b79b..533aad907a 100644 --- a/test/config_parser_SUITE_data/modules.toml +++ b/test/config_parser_SUITE_data/modules.toml @@ -201,13 +201,12 @@ [[modules.mod_muc_light.config_schema]] field = "roomname" - value = "The Room" + string_value = "The Room" [[modules.mod_muc_light.config_schema]] field = "display-lines" - value = 30 + integer_value = 30 internal_key = "display_lines" - type = "integer" [modules.mod_offline] access_max_user_messages = "max_user_offline_messages" From 9a714913345a0ef57f295f41609142648283d861 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Mon, 14 Dec 2020 13:54:50 +0100 Subject: [PATCH 056/104] Make the config of mod_muc_log declarative - Disambiguate and fix the type of css_file It was inconsistent: sometimes binary, sometimes string The value 'false' was not necessary as the option can be omitted. --- src/config/mongoose_config_parser_toml.erl | 32 +------------ src/config/mongoose_config_spec.erl | 3 +- src/config/mongoose_config_validator_toml.erl | 37 +------------- src/mod_muc_log.erl | 48 +++++++++++++++++-- 4 files changed, 49 insertions(+), 71 deletions(-) diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index 77bec700d8..8d0a12c622 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -175,27 +175,6 @@ module_opt([<<"muc">>, <<"mod_mam_meta">>|_] = Path, V) -> [{muc, Muc}]; module_opt([_, <<"mod_mam_meta">>|_] = Path, V) -> mod_mam_opts(Path, V); -module_opt([<<"outdir">>, <<"mod_muc_log">>|_], V) -> - [{outdir, b2l(V)}]; -module_opt([<<"access_log">>, <<"mod_muc_log">>|_], V) -> - [{access_log, b2a(V)}]; -module_opt([<<"dirtype">>, <<"mod_muc_log">>|_], V) -> - [{dirtype, b2a(V)}]; -module_opt([<<"dirname">>, <<"mod_muc_log">>|_], V) -> - [{dirname, b2a(V)}]; -module_opt([<<"file_format">>, <<"mod_muc_log">>|_], V) -> - [{file_format, b2a(V)}]; -module_opt([<<"css_file">>, <<"mod_muc_log">>|_], <<"false">>) -> - [{cssfile, false}]; -module_opt([<<"css_file">>, <<"mod_muc_log">>|_], V) -> - [{cssfile, V}]; -module_opt([<<"timezone">>, <<"mod_muc_log">>|_], V) -> - [{timezone, b2a(V)}]; -module_opt([<<"top_link">>, <<"mod_muc_log">>|_] = Path, V) -> - Link = list_to_tuple(parse_section(Path, V)), - [{top_link, Link}]; -module_opt([<<"spam_prevention">>, <<"mod_muc_log">>|_], V) -> - [{spam_prevention, V}]; module_opt([<<"access_max_user_messages">>, <<"mod_offline">>|_], V) -> [{access_max_user_messages, b2a(V)}]; module_opt([<<"send_pings">>, <<"mod_ping">>|_], V) -> @@ -523,12 +502,6 @@ mod_mam_opts([<<"extra_lookup_params">>|_], V) -> mod_mam_opts([<<"riak">>|_] = Path, V) -> parse_section(Path, V). --spec mod_muc_log_top_link(path(), toml_value()) -> [option()]. -mod_muc_log_top_link([<<"target">>|_], V) -> - [b2l(V)]; -mod_muc_log_top_link([<<"text">>|_], V) -> - [b2l(V)]. - -spec mod_pubsub_pep_mapping(path(), toml_section()) -> [option()]. mod_pubsub_pep_mapping(_, #{<<"namespace">> := Name, <<"node">> := Node}) -> [{b2l(Name), b2l(Node)}]. @@ -880,7 +853,8 @@ node_to_string(Node) -> [binary_to_list(Node)]. Mod =/= <<"mod_disco">>, Mod =/= <<"mod_event_pusher">>, Mod =/= <<"mod_muc">>, - Mod =/= <<"mod_muc_light">>). % TODO temporary, remove with 'handler/1' + Mod =/= <<"mod_muc_light">>, + Mod =/= <<"mod_muc_log">>). % TODO temporary, remove with 'handler/1' -spec handler(path()) -> fun((path(), toml_value()) -> option()) | mongoose_config_spec:config_node(). @@ -926,8 +900,6 @@ handler([_, <<"keys">>, <<"mod_keystore">>, <<"modules">>]) -> fun mod_keystore_keys/2; handler([_, _, <<"mod_mam_meta">>, <<"modules">>]) -> fun mod_mam_opts/2; -handler([_, <<"top_link">>, <<"mod_muc_log">>, <<"modules">>]) -> - fun mod_muc_log_top_link/2; handler([_, <<"plugins">>, <<"mod_pubsub">>, <<"modules">>]) -> fun(_, V) -> [V] end; handler([_, <<"pep_mapping">>, <<"mod_pubsub">>, <<"modules">>]) -> diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index 84eac7e1d0..b1be09eb5b 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -739,7 +739,8 @@ all_modules() -> mod_disco, mod_event_pusher, mod_muc, - mod_muc_light]. + mod_muc_light, + mod_muc_log]. %% path: (host_config[].)modules.*.iqdisc iqdisc() -> diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index f70a7118af..d2bbed907b 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -640,33 +640,6 @@ validate([<<"backend">>, <<"mod_offline">>, <<"modules">>|_], validate([<<"bucket_type">>, <<"riak">>, <<"mod_offline">>, <<"modules">>|_], [{bucket_type, V}]) -> validate_non_empty_binary(V); -validate([<<"access_log">>, <<"mod_muc_log">>, <<"modules">>|_], - [{access_log, V}]) -> - validate_access_rule(V); -validate([<<"css_file">>, <<"mod_muc_log">>, <<"modules">>|_], - [{cssfile, V}]) -> - validate_maybe_css_file(V); -validate([<<"dirname">>, <<"mod_muc_log">>, <<"modules">>|_], - [{dirname, V}]) -> - validate_enum(V, [room_jid,room_name]); -validate([<<"dirtype">>, <<"mod_muc_log">>, <<"modules">>|_], - [{dirtype, V}]) -> - validate_enum(V, [subdirs,plain]); -validate([<<"file_format">>, <<"mod_muc_log">>, <<"modules">>|_], - [{file_format, V}]) -> - validate_enum(V, [html,plaintext]); -validate([<<"outdir">>, <<"mod_muc_log">>, <<"modules">>|_], - [{outdir, V}]) -> - validate_dirname(V); -validate([<<"spam_prevention">>, <<"mod_muc_log">>, <<"modules">>|_], - [{spam_prevention, V}]) -> - validate_boolean(V); -validate([<<"timezone">>, <<"mod_muc_log">>, <<"modules">>|_], - [{timezone, V}]) -> - validate_enum(V, [local,universal]); -validate([<<"top_link">>, <<"mod_muc_log">>, <<"modules">>|_], - [{top_link, V}]) -> - validate_top_link(V); validate([<<"access_createnode">>, <<"mod_pubsub">>, <<"modules">>|_], [{access_createnode, V}]) -> validate_access_rule(V); @@ -803,6 +776,7 @@ validate(V, string, url) -> validate_url(V); validate(V, string, domain_template) -> validate_domain_template(V); validate(V, string, ip_address) -> validate_ip_address(V); validate(V, string, non_empty) -> validate_non_empty_string(V); +validate(V, string, dirname) -> validate_dirname(V); validate(V, atom, module) -> validate_module(V); validate(V, atom, {module, Prefix}) -> validate_module(list_to_atom(atom_to_list(Prefix) ++ "_" ++ atom_to_list(V))); @@ -1011,15 +985,6 @@ validate_keystore_key({Name, {file, Path}}) -> validate_non_empty_atom(Name), validate_filename(Path). -validate_maybe_css_file(false) -> - ok; -validate_maybe_css_file(Bin) -> - validate_non_empty_binary(Bin). %% Could be more precise type - -validate_top_link({Url, Text}) -> - validate_url(Url), - validate_non_empty_string(Text). - validate_pubsub_nodetree(Value) -> validate_non_empty_binary(Value), validate_backend(nodetree, b2a(Value)). diff --git a/src/mod_muc_log.erl b/src/mod_muc_log.erl index fa8bc7b0d2..892f61b342 100644 --- a/src/mod_muc_log.erl +++ b/src/mod_muc_log.erl @@ -38,6 +38,10 @@ add_to_log/5, set_room_occupants/4]). +%% Config callbacks +-export([config_spec/0, + process_top_link/1]). + %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). @@ -45,6 +49,7 @@ -include("mongoose.hrl"). -include("jlib.hrl"). -include("mod_muc_room.hrl"). +-include("mongoose_config_spec.hrl"). -define(T(Text), translate:translate(Lang, Text)). -define(PROCNAME, ejabberd_mod_muc_log). @@ -72,7 +77,7 @@ dir_type :: dir_type(), dir_name :: dir_name(), file_format :: file_format(), - css_file :: file:filename() | false, + css_file :: binary() | false, access, lang :: ejabberd:lang(), timezone, @@ -116,6 +121,42 @@ stop(Host) -> gen_server:call(Proc, stop), ejabberd_sup:stop_child(Proc). +-spec config_spec() -> mongoose_config_spec:config_section(). +config_spec() -> + #section{ + items = #{<<"outdir">> => #option{type = string, + validate = dirname}, + <<"access_log">> => #option{type = atom, + validate = access_rule}, + <<"dirtype">> => #option{type = atom, + validate = {enum, [subdirs, plain]}}, + <<"dirname">> => #option{type = atom, + validate = {enum, [room_jid, room_name]}}, + <<"file_format">> => #option{type = atom, + validate = {enum, [html, plaintext]}}, + <<"css_file">> => #option{type = binary, + validate = non_empty, + format = {kv, cssfile}}, + <<"timezone">> => #option{type = atom, + validate = {enum, [local, universal]}}, + <<"top_link">> => top_link_config_spec(), + <<"spam_prevention">> => #option{type = boolean} + } + }. + +top_link_config_spec() -> + #section{ + items = #{<<"target">> => #option{type = string, + validate = url}, + <<"text">> => #option{type = string, + validate = non_empty}}, + required = all, + process = fun ?MODULE:process_top_link/1 + }. + +process_top_link(KVs) -> + {[[{target, Target}], [{text, Text}]], []} = proplists:split(KVs, [target, text]), + {Target, Text}. -spec add_to_log(jid:server(), Type :: any(), Data :: any(), mod_muc:room(), list()) -> 'ok'. @@ -767,7 +808,7 @@ fw(F, S, FileFormat) -> -spec put_header(file:io_device(), Room :: room(), Date :: binary(), - CSSFile :: boolean(), Lang :: ejabberd:lang(), HourOffset :: integer(), + CSSFile :: false | binary(), Lang :: ejabberd:lang(), HourOffset :: integer(), DatePrev :: binary(), DateNext :: binary(), TopLink :: tuple(), file_format(), OccupantsMap :: #{binary() => [jid_nick_role()]}) -> 'ok'. put_header(_, _, _, _, _, _, _, _, _, plaintext, _) -> @@ -858,8 +899,7 @@ put_header_css(F, false) -> " font-family: monospace; letter-spacing: 1px;}">>), fw(F, <<"//-->">>), fw(F, <<"">>); -put_header_css(F, CSSFileStr) -> - CSSFile = list_to_binary(CSSFileStr), +put_header_css(F, CSSFile) -> fw(F, <<"">>). From da83730b7dc08f5355e5f642936bb11646a14d17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Mon, 14 Dec 2020 13:59:22 +0100 Subject: [PATCH 057/104] Document and test the new config for mod_muc_log - Update the docs and fix minor issues - Rewrite tests: - Simplify and split the test cases (one test case per subsection) - One successful case and one error case per option --- doc/modules/mod_muc_log.md | 14 +++---- test/config_parser_SUITE.erl | 75 ++++++++++++++++++------------------ 2 files changed, 43 insertions(+), 46 deletions(-) diff --git a/doc/modules/mod_muc_log.md b/doc/modules/mod_muc_log.md index 917d8b755d..604a6ad5e1 100644 --- a/doc/modules/mod_muc_log.md +++ b/doc/modules/mod_muc_log.md @@ -52,13 +52,11 @@ Specifies the format of output files: ### `modules.mod_muc_log.css_file` * **Syntax:** non-empty string -* **Default:** `"false"` +* **Default:** not set - the default styles for HTML logs are used * **Example:** `css_file = "path/to/css/file"` -Specifies the css file used for logs rendering: - -* `"false"`: Uses default styles for HTML logs. -* `path to custom CSS file`: Links custom CSS inside HTML logs. Please note it won't be copied to the logs directory but the given path will be linked in HTML files instead. +Specifies the css file used for logs rendering. +Please note it won't be copied to the logs directory but the given path will be linked in HTML files instead. ### `modules.mod_muc_log.timezone` * **Syntax:** string, one of `"local"`, `"universal"` @@ -71,9 +69,9 @@ Specifies the timezone to be used in timestamps written into the logs: * `universal`: Uses GMT. ### `modules.mod_muc_log.top_link` -* **Syntax:** TOML table with the following keys: `"target"`, `"text"` and string values. -* **Default:** `[target = "", text = ""]` -* **Example:** `top_link = [target = "/", text = "Home"]` +* **Syntax:** TOML table with the following mandatory keys: `"target"`, `"text"` and string values. +* **Default:** `{target = "/", text = "Home"}` +* **Example:** `top_link = {target = "/top", text = "Top page"}` Allows setting a custom link at the top of the HTML log file. First tuple element is the link target and the second one is the text to be displayed. diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index 5cd665919e..4826623198 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -188,6 +188,7 @@ groups() -> mod_muc_default_room, mod_muc_default_room_affiliations, mod_muc_log, + mod_muc_log_top_link, mod_muc_light, mod_muc_light_config_schema, mod_offline, @@ -2168,44 +2169,42 @@ mod_muc_default_room_affiliations(_Config) -> mod_muc_log(_Config) -> T = fun(Opts) -> #{<<"modules">> => #{<<"mod_muc_log">> => Opts}} end, - run_multi( - generic_opts_cases(mod_muc_log, T, mod_muc_log_opts()) ++ - generic_renamed_opts_cases(mod_muc_log, T, mod_muc_log_renamed_opts()) ++ - generic_bad_opts_cases(T, mod_muc_log_bad_opts()) - ). - -mod_muc_log_renamed_opts() -> - %% toml-name mim-name toml mim - [{css_file, cssfile, <<"path/to/css_file">>, <<"path/to/css_file">>}, - {css_file, cssfile, false, false}]. - -mod_muc_log_opts() -> - %% name toml mim - [{outdir, <<"www/muc">>, "www/muc"}, - {access_log, <<"muc_admin">>, muc_admin}, - {dirtype, <<"subdirs">>, subdirs}, - {dirtype, <<"plain">>, plain}, - {file_format, <<"html">>, html}, - {file_format, <<"plaintext">>, plaintext}, - {timezone, <<"local">>, local}, - {timezone, <<"universal">>, universal}, - {spam_prevention, true, true}, - {top_link, #{<<"target">> => <<"https://esl.github.io/MongooseDocs/latest/modules/mod_muc_log/">>, - <<"text">> => <<"docs">>}, - {"https://esl.github.io/MongooseDocs/latest/modules/mod_muc_log/", "docs"}}]. - -mod_muc_log_bad_opts() -> - %% toml-name toml - [{outdir, 1}, - {outdir, <<"does/not/exist">>}, - {access_log, 1}, - {dirtype, <<"subways">>}, - {file_format, <<"haskelencodedlove">>}, - {timezone, <<"galactive">>}, - {spam_prevention, 69}, - {top_link, #{<<"target">> => 1, <<"text">> => <<"docs">>}}, - {top_link, #{<<"target">> => <<"https://esl.github.io/MongooseDocs/">>, <<"text">> => <<>>}} - ]. + M = fun(Cfg) -> modopts(mod_muc_log, Cfg) end, + ?eqf(M([{outdir, "www/muc"}]), + T(#{<<"outdir">> => <<"www/muc">>})), + ?eqf(M([{access_log, muc_admin}]), + T(#{<<"access_log">> => <<"muc_admin">>})), + ?eqf(M([{dirtype, subdirs}]), + T(#{<<"dirtype">> => <<"subdirs">>})), + ?eqf(M([{dirname, room_name}]), + T(#{<<"dirname">> => <<"room_name">>})), + ?eqf(M([{file_format, html}]), + T(#{<<"file_format">> => <<"html">>})), + ?eqf(M([{cssfile, <<"path/to/css_file">>}]), + T(#{<<"css_file">> => <<"path/to/css_file">>})), + ?eqf(M([{timezone, local}]), + T(#{<<"timezone">> => <<"local">>})), + ?eqf(M([{spam_prevention, false}]), + T(#{<<"spam_prevention">> => false})), + ?errf(T(#{<<"outdir">> => <<"does/not/exist">>})), + ?errf(T(#{<<"access_log">> => 1})), + ?errf(T(#{<<"dirtype">> => <<"imaginary">>})), + ?errf(T(#{<<"dirname">> => <<"dyrektory">>})), + ?errf(T(#{<<"file_format">> => <<"none">>})), + ?errf(T(#{<<"css_file">> => <<>>})), + ?errf(T(#{<<"timezone">> => <<"yes">>})), + ?errf(T(#{<<"spam_prevention">> => <<"spam and eggs and spam">>})). + +mod_muc_log_top_link(_Config) -> + T = fun(Opts) -> #{<<"modules">> => #{<<"mod_muc_log">> => #{<<"top_link">> => Opts}}} end, + M = fun(Cfg) -> modopts(mod_muc_log, [{top_link, Cfg}]) end, + RequiredOpts = #{<<"target">> => <<"https://esl.github.io/MongooseDocs/">>, + <<"text">> => <<"Docs">>}, + ExpectedCfg = {"https://esl.github.io/MongooseDocs/", "Docs"}, + ?eqf(M(ExpectedCfg), T(RequiredOpts)), + [?errf(T(maps:remove(K, RequiredOpts))) || K <- maps:keys(RequiredOpts)], + ?errf(T(RequiredOpts#{<<"target">> => true})), + ?errf(T(RequiredOpts#{<<"text">> => <<"">>})). mod_muc_light(_Config) -> T = fun(Opts) -> #{<<"modules">> => #{<<"mod_muc_light">> => Opts}} end, From 47ab8f5e0decd4839cd36768d7161967323d3878 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Mon, 14 Dec 2020 15:35:42 +0100 Subject: [PATCH 058/104] Make the config of mod_offline declarative Also: simplify tests --- src/config/mongoose_config_parser_toml.erl | 5 ++--- src/config/mongoose_config_spec.erl | 3 ++- src/config/mongoose_config_validator_toml.erl | 9 -------- src/offline/mod_offline.erl | 20 +++++++++++++++++- test/config_parser_SUITE.erl | 21 ++++++++++--------- 5 files changed, 34 insertions(+), 24 deletions(-) diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index 8d0a12c622..360cd730f2 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -175,8 +175,6 @@ module_opt([<<"muc">>, <<"mod_mam_meta">>|_] = Path, V) -> [{muc, Muc}]; module_opt([_, <<"mod_mam_meta">>|_] = Path, V) -> mod_mam_opts(Path, V); -module_opt([<<"access_max_user_messages">>, <<"mod_offline">>|_], V) -> - [{access_max_user_messages, b2a(V)}]; module_opt([<<"send_pings">>, <<"mod_ping">>|_], V) -> [{send_pings, V}]; module_opt([<<"ping_interval">>, <<"mod_ping">>|_], V) -> @@ -854,7 +852,8 @@ node_to_string(Node) -> [binary_to_list(Node)]. Mod =/= <<"mod_event_pusher">>, Mod =/= <<"mod_muc">>, Mod =/= <<"mod_muc_light">>, - Mod =/= <<"mod_muc_log">>). % TODO temporary, remove with 'handler/1' + Mod =/= <<"mod_muc_log">>, + Mod =/= <<"mod_offline">>). % TODO temporary, remove with 'handler/1' -spec handler(path()) -> fun((path(), toml_value()) -> option()) | mongoose_config_spec:config_node(). diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index b1be09eb5b..c371e90e9f 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -740,7 +740,8 @@ all_modules() -> mod_event_pusher, mod_muc, mod_muc_light, - mod_muc_log]. + mod_muc_log, + mod_offline]. %% path: (host_config[].)modules.*.iqdisc iqdisc() -> diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index d2bbed907b..11126a65bc 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -631,15 +631,6 @@ validate([<<"remove_on_kicked">>, <<"mod_inbox">>, <<"modules">>|_], validate([item, <<"reset_markers">>, <<"mod_inbox">>, <<"modules">>|_], [V]) -> validate_chat_marker_type(V); -validate([<<"access_max_user_messages">>, <<"mod_offline">>, <<"modules">>|_], - [{access_max_user_messages, V}]) -> - validate_access_rule(V); -validate([<<"backend">>, <<"mod_offline">>, <<"modules">>|_], - [{backend, V}]) -> - validate_backend(mod_offline, V); -validate([<<"bucket_type">>, <<"riak">>, <<"mod_offline">>, <<"modules">>|_], - [{bucket_type, V}]) -> - validate_non_empty_binary(V); validate([<<"access_createnode">>, <<"mod_pubsub">>, <<"modules">>|_], [{access_createnode, V}]) -> validate_access_rule(V); diff --git a/src/offline/mod_offline.erl b/src/offline/mod_offline.erl index ef2b49d3d5..66aea93d39 100644 --- a/src/offline/mod_offline.erl +++ b/src/offline/mod_offline.erl @@ -34,7 +34,7 @@ -behaviour(mongoose_module_metrics). %% gen_mod handlers --export([start/2, stop/1]). +-export([start/2, stop/1, config_spec/0]). %% Hook handlers -export([inspect_packet/4, @@ -66,6 +66,7 @@ -include("jlib.hrl"). -include("amp.hrl"). -include("mod_offline.hrl"). +-include("mongoose_config_spec.hrl"). -define(PROCNAME, ejabberd_offline). @@ -144,6 +145,23 @@ stop(Host) -> stop_worker(Host), ok. +-spec config_spec() -> mongoose_config_spec:config_section(). +config_spec() -> + #section{ + items = #{<<"access_max_user_messages">> => #option{type = atom, + validate = access_rule}, + <<"backend">> => #option{type = atom, + validate = {module, mod_offline}}, + <<"riak">> => riak_config_spec() + } + }. + +riak_config_spec() -> + #section{items = #{<<"bucket_type">> => #option{type = binary, + validate = non_empty}}, + format = none + }. + hooks(Host) -> DefaultHooks = [ {offline_message_hook, Host, ?MODULE, inspect_packet, 50}, diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index 4826623198..d1491039d2 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -2271,16 +2271,17 @@ mod_muc_light_config_schema(_Config) -> mod_offline(_Config) -> T = fun(Opts) -> #{<<"modules">> => #{<<"mod_offline">> => Opts}} end, - Base = #{<<"access_max_user_messages">> => <<"max_user_offline_messages">>, - <<"backend">> => <<"riak">>, - <<"riak">> => #{<<"bucket_type">> => <<"test">>}}, - MBase = [{access_max_user_messages, max_user_offline_messages}, - {backend, riak}, - {bucket_type, <<"test">>}], - ?eqf(modopts(mod_offline, MBase), T(Base)), - ?errf(T(Base#{<<"access_max_user_messages">> => 1})), - ?errf(T(Base#{<<"backend">> => <<"riak_is_the_best">>})), - ?errf(T(Base#{<<"riak">> => #{<<"bucket_type">> => 1}})). + M = fun(Cfg) -> modopts(mod_offline, Cfg) end, + ?eqf(M([{access_max_user_messages, max_user_offline_messages}]), + T(#{<<"access_max_user_messages">> => <<"max_user_offline_messages">>})), + ?eqf(M([{backend, rdbms}]), + T(#{<<"backend">> => <<"rdbms">>})), + ?eqf(M([{bucket_type, <<"test">>}]), + T(#{<<"riak">> => #{<<"bucket_type">> => <<"test">>}})), + ?errf(T(#{<<"access_max_user_messages">> => 1})), + ?errf(T(#{<<"backend">> => <<"riak_is_the_best">>})), + ?errf(T(#{<<"riak">> => #{<<"bucket_type">> => 1}})), + ?errf(T(#{<<"riak">> => #{<<"bucket">> => <<"leaky">>}})). mod_ping(_Config) -> T = fun(Opts) -> #{<<"modules">> => #{<<"mod_ping">> => Opts}} end, From cf1d1663a58def30de272d0019a11b39bfcc7ce0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Mon, 14 Dec 2020 16:11:29 +0100 Subject: [PATCH 059/104] Make the config of mod_ping declarative Also: - add time units to doc - simplify tests --- doc/modules/mod_ping.md | 4 +-- src/config/mongoose_config_parser_toml.erl | 11 ++----- src/config/mongoose_config_spec.erl | 3 +- src/config/mongoose_config_validator_toml.erl | 15 ---------- src/mod_ping.erl | 17 ++++++++++- test/config_parser_SUITE.erl | 29 +++++++++---------- 6 files changed, 35 insertions(+), 44 deletions(-) diff --git a/doc/modules/mod_ping.md b/doc/modules/mod_ping.md index bfa81a173d..115d17397b 100644 --- a/doc/modules/mod_ping.md +++ b/doc/modules/mod_ping.md @@ -12,7 +12,7 @@ This module implements XMPP Ping functionality as described in [XEP-0199: XMPP P If set to true, the server will send ping iqs to the client if they are not active for a `ping_interval`. ### `modules.mod_ping.ping_interval` -* **Syntax:** positive integer +* **Syntax:** positive integer (seconds) * **Default:** `60` * **Example:** `ping_interval = 30` @@ -26,7 +26,7 @@ Defines the client inactivity timeout after which the server will send a ping re Defines if the client connection should be closed if it doesn't reply to a ping request in less than `ping_req_timeout`. ### `modules.mod_ping.ping_req_timeout` -* **Syntax:** positive integer +* **Syntax:** positive integer (seconds) * **Default:** `32` * **Example:** `ping_req_timeout = 60` diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index 360cd730f2..4afc9627c5 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -175,14 +175,6 @@ module_opt([<<"muc">>, <<"mod_mam_meta">>|_] = Path, V) -> [{muc, Muc}]; module_opt([_, <<"mod_mam_meta">>|_] = Path, V) -> mod_mam_opts(Path, V); -module_opt([<<"send_pings">>, <<"mod_ping">>|_], V) -> - [{send_pings, V}]; -module_opt([<<"ping_interval">>, <<"mod_ping">>|_], V) -> - [{ping_interval, V}]; -module_opt([<<"timeout_action">>, <<"mod_ping">>|_], V) -> - [{timeout_action, b2a(V)}]; -module_opt([<<"ping_req_timeout">>, <<"mod_ping">>|_], V) -> - [{ping_req_timeout, V}]; module_opt([<<"host">>, <<"mod_pubsub">>|_], V) -> [{host, b2l(V)}]; module_opt([<<"access_createnode">>, <<"mod_pubsub">>|_], V) -> @@ -853,7 +845,8 @@ node_to_string(Node) -> [binary_to_list(Node)]. Mod =/= <<"mod_muc">>, Mod =/= <<"mod_muc_light">>, Mod =/= <<"mod_muc_log">>, - Mod =/= <<"mod_offline">>). % TODO temporary, remove with 'handler/1' + Mod =/= <<"mod_offline">>, + Mod =/= <<"mod_ping">>). % TODO temporary, remove with 'handler/1' -spec handler(path()) -> fun((path(), toml_value()) -> option()) | mongoose_config_spec:config_node(). diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index c371e90e9f..c081c484e1 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -741,7 +741,8 @@ all_modules() -> mod_muc, mod_muc_light, mod_muc_log, - mod_offline]. + mod_offline, + mod_ping]. %% path: (host_config[].)modules.*.iqdisc iqdisc() -> diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index 11126a65bc..8c480f0154 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -738,21 +738,6 @@ validate([item, <<"plugins">>, <<"mod_pubsub">>, <<"modules">>|_], validate([<<"sync_broadcast">>, <<"mod_pubsub">>, <<"modules">>|_], [{sync_broadcast, V}]) -> validate_boolean(V); -validate([<<"iqdisc">>, <<"mod_ping">>, <<"modules">>|_], - [{iqdisc, V}]) -> - validate_iqdisc(V); -validate([<<"ping_interval">>, <<"mod_ping">>, <<"modules">>|_], - [{ping_interval, V}]) -> - validate_positive_integer(V); -validate([<<"ping_req_timeout">>, <<"mod_ping">>, <<"modules">>|_], - [{ping_req_timeout, V}]) -> - validate_positive_integer(V); -validate([<<"send_pings">>, <<"mod_ping">>, <<"modules">>|_], - [{send_pings, V}]) -> - validate_boolean(V); -validate([<<"timeout_action">>, <<"mod_ping">>, <<"modules">>|_], - [{timeout_action, V}]) -> - validate_enum(V, [none,kill]); validate(_Path, _Value) -> ok. diff --git a/src/mod_ping.erl b/src/mod_ping.erl index 7d426a2377..f634c9ea15 100644 --- a/src/mod_ping.erl +++ b/src/mod_ping.erl @@ -12,13 +12,14 @@ -xep([{xep, 199}, {version, "2.0"}]). -include("mongoose.hrl"). -include("jlib.hrl"). +-include("mongoose_config_spec.hrl"). -define(DEFAULT_SEND_PINGS, false). % bool() -define(DEFAULT_PING_INTERVAL, 60). % seconds -define(DEFAULT_PING_REQ_TIMEOUT, 32). %% gen_mod callbacks --export([start/2, stop/1]). +-export([start/2, stop/1, config_spec/0]). %% Hook callbacks -export([iq_ping/4, @@ -109,6 +110,20 @@ stop(Host) -> gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_PING), mod_disco:unregister_feature(Host, ?NS_PING). +-spec config_spec() -> mongoose_config_spec:config_section(). +config_spec() -> + #section{ + items = #{<<"send_pings">> => #option{type = boolean}, + <<"ping_interval">> => #option{type = integer, + validate = positive}, + <<"timeout_action">> => #option{type = atom, + validate = {enum, [none, kill]}}, + <<"ping_req_timeout">> => #option{type = integer, + validate = positive}, + <<"iqdisc">> => mongoose_config_spec:iqdisc() + } + }. + %%==================================================================== %% IQ handlers %%==================================================================== diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index d1491039d2..ed97a16301 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -2285,22 +2285,19 @@ mod_offline(_Config) -> mod_ping(_Config) -> T = fun(Opts) -> #{<<"modules">> => #{<<"mod_ping">> => Opts}} end, - Base = #{<<"iqdisc">> => #{<<"type">> => <<"no_queue">>}, - <<"ping_req_timeout">> => 32, - <<"send_pings">> => true, - <<"timeout_action">> => <<"none">>}, - MBase = [{iqdisc, no_queue}, - {ping_req_timeout, 32}, - {send_pings, true}, - {timeout_action, none}], - ensure_sorted(MBase), - ?eqf(modopts(mod_ping, MBase), T(Base)), - ?errf(T(Base#{<<"send_pings">> => 1})), - ?errf(T(Base#{<<"ping_interval">> => -1})), - ?errf(T(Base#{<<"timeout_action">> => 1})), - ?errf(T(Base#{<<"timeout_action">> => <<"kill_them_all">>})), - ?errf(T(Base#{<<"ping_req_timeout">> => -1})), - ?errf(T(Base#{<<"ping_req_timeout">> => <<"32">>})), + M = fun(Cfg) -> modopts(mod_ping, Cfg) end, + ?eqf(M([{send_pings, true}]), + T(#{<<"send_pings">> => true})), + ?eqf(M([{ping_interval, 10}]), + T(#{<<"ping_interval">> => 10})), + ?eqf(M([{timeout_action, kill}]), + T(#{<<"timeout_action">> => <<"kill">>})), + ?eqf(M([{ping_req_timeout, 20}]), + T(#{<<"ping_req_timeout">> => 20})), + ?errf(T(#{<<"send_pings">> => 1})), + ?errf(T(#{<<"ping_interval">> => 0})), + ?errf(T(#{<<"timeout_action">> => <<"kill_them_all">>})), + ?errf(T(#{<<"ping_req_timeout">> => 0})), check_iqdisc(mod_ping). mod_privacy(_Config) -> From 184bb91ed4d6833bc4565a4e121c9f28c0c15087 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Tue, 15 Dec 2020 15:06:12 +0100 Subject: [PATCH 060/104] Make the config of mod_pubsub declarative - Fix 'pep_mapping' - it should contain binaries, not strings --- src/config/mongoose_config_parser_toml.erl | 86 +----------- src/config/mongoose_config_spec.erl | 3 +- src/config/mongoose_config_validator_toml.erl | 128 +----------------- src/pubsub/mod_pubsub.erl | 77 +++++++++++ 4 files changed, 84 insertions(+), 210 deletions(-) diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index 4afc9627c5..8ca1f08cc0 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -175,37 +175,6 @@ module_opt([<<"muc">>, <<"mod_mam_meta">>|_] = Path, V) -> [{muc, Muc}]; module_opt([_, <<"mod_mam_meta">>|_] = Path, V) -> mod_mam_opts(Path, V); -module_opt([<<"host">>, <<"mod_pubsub">>|_], V) -> - [{host, b2l(V)}]; -module_opt([<<"access_createnode">>, <<"mod_pubsub">>|_], V) -> - [{access_createnode, b2a(V)}]; -module_opt([<<"max_items_node">>, <<"mod_pubsub">>|_], V) -> - [{max_items_node, V}]; -module_opt([<<"max_subscriptions_node">>, <<"mod_pubsub">>|_], <<"infinity">>) -> - []; -module_opt([<<"max_subscriptions_node">>, <<"mod_pubsub">>|_], V) -> - [{max_subscriptions_node, V}]; -module_opt([<<"nodetree">>, <<"mod_pubsub">>|_], V) -> - [{nodetree, V}]; -module_opt([<<"ignore_pep_from_offline">>, <<"mod_pubsub">>|_], V) -> - [{ignore_pep_from_offline, V}]; -module_opt([<<"last_item_cache">>, <<"mod_pubsub">>|_], false) -> - [{last_item_cache, false}]; -module_opt([<<"last_item_cache">>, <<"mod_pubsub">>|_], V) -> - [{last_item_cache, b2a(V)}]; -module_opt([<<"plugins">>, <<"mod_pubsub">>|_] = Path, V) -> - Plugs = parse_list(Path, V), - [{plugins, Plugs}]; -module_opt([<<"pep_mapping">>, <<"mod_pubsub">>|_] = Path, V) -> - Mappings = parse_list(Path, V), - [{pep_mapping, Mappings}]; -module_opt([<<"default_node_config">>, <<"mod_pubsub">>|_] = Path, V) -> - Config = parse_section(Path, V), - [{default_node_config, Config}]; -module_opt([<<"item_publisher">>, <<"mod_pubsub">>|_], V) -> - [{item_publisher, V}]; -module_opt([<<"sync_broadcast">>, <<"mod_pubsub">>|_], V) -> - [{sync_broadcast, V}]; module_opt([<<"pool_name">>, <<"mod_push_service_mongoosepush">>|_], V) -> [{pool_name, b2a(V)}]; module_opt([<<"api_version">>, <<"mod_push_service_mongoosepush">>|_], V) -> @@ -492,50 +461,6 @@ mod_mam_opts([<<"extra_lookup_params">>|_], V) -> mod_mam_opts([<<"riak">>|_] = Path, V) -> parse_section(Path, V). --spec mod_pubsub_pep_mapping(path(), toml_section()) -> [option()]. -mod_pubsub_pep_mapping(_, #{<<"namespace">> := Name, <<"node">> := Node}) -> - [{b2l(Name), b2l(Node)}]. - --spec mod_pubsub_default_node_config(path(), toml_section()) -> [option()]. -mod_pubsub_default_node_config([<<"access_model">>|_], Value) -> - [{access_model, b2a(Value)}]; -mod_pubsub_default_node_config([<<"deliver_notifications">>|_], Value) -> - [{deliver_notifications, Value}]; -mod_pubsub_default_node_config([<<"deliver_payloads">>|_], Value) -> - [{deliver_payloads, Value}]; -mod_pubsub_default_node_config([<<"max_items">>|_], Value) -> - [{max_items, Value}]; -mod_pubsub_default_node_config([<<"max_payload_size">>|_], Value) -> - [{max_payload_size, Value}]; -mod_pubsub_default_node_config([<<"node_type">>|_], Value) -> - [{node_type, b2a(Value)}]; -mod_pubsub_default_node_config([<<"notification_type">>|_], Value) -> - [{notification_type, b2a(Value)}]; -mod_pubsub_default_node_config([<<"notify_config">>|_], Value) -> - [{notify_config, Value}]; -mod_pubsub_default_node_config([<<"notify_delete">>|_], Value) -> - [{notify_delete, Value}]; -mod_pubsub_default_node_config([<<"notify_retract">>|_], Value) -> - [{notify_retract, Value}]; -mod_pubsub_default_node_config([<<"persist_items">>|_], Value) -> - [{persist_items, Value}]; -mod_pubsub_default_node_config([<<"presence_based_delivery">>|_], Value) -> - [{presence_based_delivery, Value}]; -mod_pubsub_default_node_config([<<"publish_model">>|_], Value) -> - [{publish_model, b2a(Value)}]; -mod_pubsub_default_node_config([<<"purge_offline">>|_], Value) -> - [{purge_offline, Value}]; -mod_pubsub_default_node_config([<<"roster_groups_allowed">>|_] = Path, Value) -> - Groups = parse_list(Path, Value), - [{roster_groups_allowed, Groups}]; -mod_pubsub_default_node_config([<<"send_last_published_item">>|_], Value) -> - [{send_last_published_item, b2a(Value)}]; -mod_pubsub_default_node_config([<<"subscribe">>|_], Value) -> - [{subscribe, Value}]. - -mod_pubsub_roster_groups_allowed(_, Value) -> - [Value]. - -spec mod_revproxy_routes(path(), toml_section()) -> [option()]. mod_revproxy_routes(_, #{<<"host">> := Host, <<"path">> := Path, <<"method">> := Method, <<"upstream">> := Upstream}) -> @@ -846,7 +771,8 @@ node_to_string(Node) -> [binary_to_list(Node)]. Mod =/= <<"mod_muc_light">>, Mod =/= <<"mod_muc_log">>, Mod =/= <<"mod_offline">>, - Mod =/= <<"mod_ping">>). % TODO temporary, remove with 'handler/1' + Mod =/= <<"mod_ping">>, + Mod =/= <<"mod_pubsub">>). % TODO temporary, remove with 'handler/1' -spec handler(path()) -> fun((path(), toml_value()) -> option()) | mongoose_config_spec:config_node(). @@ -892,14 +818,6 @@ handler([_, <<"keys">>, <<"mod_keystore">>, <<"modules">>]) -> fun mod_keystore_keys/2; handler([_, _, <<"mod_mam_meta">>, <<"modules">>]) -> fun mod_mam_opts/2; -handler([_, <<"plugins">>, <<"mod_pubsub">>, <<"modules">>]) -> - fun(_, V) -> [V] end; -handler([_, <<"pep_mapping">>, <<"mod_pubsub">>, <<"modules">>]) -> - fun mod_pubsub_pep_mapping/2; -handler([_, <<"default_node_config">>, <<"mod_pubsub">>, <<"modules">>]) -> - fun mod_pubsub_default_node_config/2; -handler([_, <<"roster_groups_allowed">>, <<"default_node_config">>, <<"mod_pubsub">>, <<"modules">>]) -> - fun mod_pubsub_roster_groups_allowed/2; handler([_, <<"routes">>, <<"mod_revproxy">>, <<"modules">>]) -> fun mod_revproxy_routes/2; handler([_, <<"stale_h">>, <<"mod_stream_management">>, <<"modules">>]) -> diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index c081c484e1..ccbad4ecde 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -742,7 +742,8 @@ all_modules() -> mod_muc_light, mod_muc_log, mod_offline, - mod_ping]. + mod_ping, + mod_pubsub]. %% path: (host_config[].)modules.*.iqdisc iqdisc() -> diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index 8c480f0154..20ea5c4680 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -631,118 +631,14 @@ validate([<<"remove_on_kicked">>, <<"mod_inbox">>, <<"modules">>|_], validate([item, <<"reset_markers">>, <<"mod_inbox">>, <<"modules">>|_], [V]) -> validate_chat_marker_type(V); -validate([<<"access_createnode">>, <<"mod_pubsub">>, <<"modules">>|_], - [{access_createnode, V}]) -> - validate_access_rule(V); -validate([<<"backend">>, <<"mod_pubsub">>, <<"modules">>|_], - [{backend, V}]) -> - validate_backend(mod_pubsub_db, V); -validate([<<"access_model">>, <<"default_node_config">>, - <<"mod_pubsub">>, <<"modules">>|_], - [{access_model, V}]) -> - validate_non_empty_atom(V); -validate([<<"deliver_notifications">>, <<"default_node_config">>, - <<"mod_pubsub">>, <<"modules">>|_], - [{deliver_notifications, V}]) -> - validate_boolean(V); -validate([<<"deliver_payloads">>, <<"default_node_config">>, - <<"mod_pubsub">>, <<"modules">>|_], - [{deliver_payloads, V}]) -> - validate_boolean(V); -validate([<<"max_items">>, <<"default_node_config">>, - <<"mod_pubsub">>, <<"modules">>|_], - [{max_items, V}]) -> - validate_non_negative_integer(V); -validate([<<"max_payload_size">>, <<"default_node_config">>, - <<"mod_pubsub">>, <<"modules">>|_], - [{max_payload_size, V}]) -> - validate_non_negative_integer(V); -validate([<<"node_type">>, <<"default_node_config">>, - <<"mod_pubsub">>, <<"modules">>|_], - [{node_type, V}]) -> - validate_non_empty_atom(V); -validate([<<"notification_type">>, <<"default_node_config">>, - <<"mod_pubsub">>, <<"modules">>|_], - [{notification_type, V}]) -> - validate_non_empty_atom(V); -validate([<<"notify_config">>, <<"default_node_config">>, - <<"mod_pubsub">>, <<"modules">>|_], - [{notify_config, V}]) -> - validate_boolean(V); -validate([<<"notify_delete">>, <<"default_node_config">>, - <<"mod_pubsub">>, <<"modules">>|_], - [{notify_delete, V}]) -> - validate_boolean(V); -validate([<<"notify_retract">>, <<"default_node_config">>, - <<"mod_pubsub">>, <<"modules">>|_], - [{notify_retract, V}]) -> - validate_boolean(V); -validate([<<"persist_items">>, <<"default_node_config">>, - <<"mod_pubsub">>, <<"modules">>|_], - [{persist_items, V}]) -> - validate_boolean(V); -validate([<<"presence_based_delivery">>, <<"default_node_config">>, - <<"mod_pubsub">>, <<"modules">>|_], - [{presence_based_delivery, V}]) -> - validate_boolean(V); -validate([<<"publish_model">>, <<"default_node_config">>, - <<"mod_pubsub">>, <<"modules">>|_], - [{publish_model, V}]) -> - validate_non_empty_atom(V); -validate([<<"purge_offline">>, <<"default_node_config">>, - <<"mod_pubsub">>, <<"modules">>|_], - [{purge_offline, V}]) -> - validate_boolean(V); -validate([item, <<"roster_groups_allowed">>, <<"default_node_config">>, - <<"mod_pubsub">>, <<"modules">>|_], - [V]) -> - validate_non_empty_binary(V); -validate([<<"send_last_published_item">>, <<"default_node_config">>, - <<"mod_pubsub">>, <<"modules">>|_], - [{send_last_published_item, V}]) -> - validate_non_empty_atom(V); -validate([<<"subscribe">>, <<"default_node_config">>, - <<"mod_pubsub">>, <<"modules">>|_], - [{subscribe, V}]) -> - validate_boolean(V); -validate([<<"host">>, <<"mod_pubsub">>, <<"modules">>|_], - [{host, V}]) -> - validate_domain_template(V); -validate([<<"ignore_pep_from_offline">>, <<"mod_pubsub">>, <<"modules">>|_], - [{ignore_pep_from_offline, V}]) -> - validate_boolean(V); -validate([<<"iqdisc">>, <<"mod_pubsub">>, <<"modules">>|_], - [{iqdisc, V}]) -> - validate_iqdisc(V); -validate([<<"item_publisher">>, <<"mod_pubsub">>, <<"modules">>|_], - [{item_publisher, V}]) -> - validate_boolean(V); -validate([<<"last_item_cache">>, <<"mod_pubsub">>, <<"modules">>|_], - [{last_item_cache, V}]) -> - validate_enum(V, [mnesia,rdbms,false]); -validate([<<"max_items_node">>, <<"mod_pubsub">>, <<"modules">>|_], - [{max_items_node, V}]) -> - validate_non_negative_integer(V); -validate([<<"max_subscriptions_node">>, <<"mod_pubsub">>, <<"modules">>|_], - [{max_subscriptions_node, V}]) -> - validate_non_negative_integer(V); -validate([<<"nodetree">>, <<"mod_pubsub">>, <<"modules">>|_], - [{nodetree, V}]) -> - validate_pubsub_nodetree(V); -validate([item, <<"pep_mapping">>, <<"mod_pubsub">>, <<"modules">>|_], - [V]) -> - validate_pubsub_pep_mapping(V); -validate([item, <<"plugins">>, <<"mod_pubsub">>, <<"modules">>|_], - [V]) -> - validate_pubsub_plugin(V); -validate([<<"sync_broadcast">>, <<"mod_pubsub">>, <<"modules">>|_], - [{sync_broadcast, V}]) -> - validate_boolean(V); + validate(_Path, _Value) -> ok. validate(V, binary, domain) -> validate_binary_domain(V); validate(V, binary, non_empty) -> validate_non_empty_binary(V); +validate(V, binary, {module, Prefix}) -> + validate_module(list_to_atom(atom_to_list(Prefix) ++ "_" ++ binary_to_list(V))); validate(V, integer, non_negative) -> validate_non_negative_integer(V); validate(V, integer, positive) -> validate_positive_integer(V); validate(V, integer, port) -> validate_port(V); @@ -961,21 +857,6 @@ validate_keystore_key({Name, {file, Path}}) -> validate_non_empty_atom(Name), validate_filename(Path). -validate_pubsub_nodetree(Value) -> - validate_non_empty_binary(Value), - validate_backend(nodetree, b2a(Value)). - -validate_pubsub_plugin(Value) -> - validate_non_empty_binary(Value), - validate_backend(node, b2a(Value)). - -validate_pubsub_pep_mapping({Namespace, Id}) -> - validate_non_empty_string(Namespace), - validate_non_empty_string(Id). - -b2a(Bin) -> - binary_to_atom(Bin, utf8). - validate_revproxy_route({Host, Path, Method, Upstream}) -> validate_non_empty_string(Host), validate_string(Path), @@ -1007,6 +888,3 @@ validate_ldap_uids(Attribute) -> validate_pool_name(V) -> validate_non_empty_atom(V). - -validate_access_rule(V) -> - validate_non_empty_atom(V). diff --git a/src/pubsub/mod_pubsub.erl b/src/pubsub/mod_pubsub.erl index 476ea8aeec..1a9e441ca3 100644 --- a/src/pubsub/mod_pubsub.erl +++ b/src/pubsub/mod_pubsub.erl @@ -56,6 +56,7 @@ -include("adhoc.hrl"). -include("jlib.hrl"). -include("pubsub.hrl"). +-include("mongoose_config_spec.hrl"). -define(STDTREE, <<"tree">>). -define(STDNODE, <<"flat">>). @@ -93,6 +94,10 @@ -export([start_link/2, start/2, stop/1, deps/2, init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). + +%% Config callbacks +-export([config_spec/0, process_pep_mapping/1]). + -export([default_host/0]). -export([get_personal_data/2]). @@ -240,6 +245,78 @@ stop(Host) -> gen_server:call(Proc, stop), ejabberd_sup:stop_child(Proc). +-spec config_spec() -> mongoose_config_spec:config_section(). +config_spec() -> + #section{ + items = #{<<"iqdisc">> => mongoose_config_spec:iqdisc(), + <<"host">> => #option{type = string, + validate = domain_template}, + <<"backend">> => #option{type = atom, + validate = {module, mod_pubsub_db}}, + <<"access_createnode">> => #option{type = atom, + validate = access_rule}, + <<"max_items_node">> => #option{type = integer, + validate = non_negative}, + <<"max_subscriptions_node">> => #option{type = integer, + validate = non_negative}, + <<"nodetree">> => #option{type = binary, + validate = {module, nodetree}}, + <<"ignore_pep_from_offline">> => #option{type = boolean}, + <<"last_item_cache">> => #option{type = atom, + validate = {enum, [mnesia, rdbms, false]}}, + <<"plugins">> => #list{items = #option{type = binary, + validate = {module, node}}}, + <<"pep_mapping">> => #list{items = pep_mapping_config_spec()}, + <<"default_node_config">> => default_node_config_spec(), + <<"item_publisher">> => #option{type = boolean}, + <<"sync_broadcast">> => #option{type = boolean} + } + }. + +pep_mapping_config_spec() -> + #section{ + items = #{<<"namespace">> => #option{type = binary, + validate = non_empty}, + <<"node">> => #option{type = binary, + validate = non_empty}}, + required = all, + process = fun ?MODULE:process_pep_mapping/1 + }. + +default_node_config_spec() -> + #section{ + items = #{<<"access_model">> => #option{type = atom, + validate = non_empty}, + <<"deliver_notifications">> => #option{type = boolean}, + <<"deliver_payloads">> => #option{type = boolean}, + <<"max_items">> => #option{type = integer, + validate = non_negative}, + <<"max_payload_size">> => #option{type = integer, + validate = non_negative}, + <<"node_type">> => #option{type = atom, + validate = non_empty}, + <<"notification_type">> => #option{type = atom, + validate = non_empty}, + <<"notify_config">> => #option{type = boolean}, + <<"notify_delete">> => #option{type = boolean}, + <<"notify_retract">> => #option{type = boolean}, + <<"persist_items">> => #option{type = boolean}, + <<"presence_based_delivery">> => #option{type = boolean}, + <<"publish_model">> => #option{type = atom, + validate = non_empty}, + <<"purge_offline">> => #option{type = boolean}, + <<"roster_groups_allowed">> => #list{items = #option{type = binary, + validate = non_empty}}, + <<"send_last_published_item">> => #option{type = atom, + validate = non_empty}, + <<"subscribe">> => #option{type = boolean} + } + }. + +process_pep_mapping(KVs) -> + {[[{namespace, NameSpace}], [{node, Node}]], []} = proplists:split(KVs, [namespace, node]), + {NameSpace, Node}. + -spec default_host() -> binary(). default_host() -> <<"pubsub.@HOST@">>. From b5e2f90c04ea08136cc92c19f4a93f0d709c0ac4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Tue, 15 Dec 2020 15:15:40 +0100 Subject: [PATCH 061/104] Update docs and tests for mod_pubsub --- doc/modules/mod_pubsub.md | 9 +- test/config_parser_SUITE.erl | 213 +++++++++--------- test/config_parser_SUITE_data/modules.options | 4 +- 3 files changed, 110 insertions(+), 116 deletions(-) diff --git a/doc/modules/mod_pubsub.md b/doc/modules/mod_pubsub.md index 54553d1546..b808eb54c0 100644 --- a/doc/modules/mod_pubsub.md +++ b/doc/modules/mod_pubsub.md @@ -86,20 +86,19 @@ If enabled, PubSub will cache the last published items in the nodes. It may incr List of enabled pubsub plugins. ### `modules.mod_pubsub.pep_mapping` -* **Syntax:** TOML table with the following keys: `"namespace"`, `"node"` and string values. +* **Syntax:** Array of TOML tables with the following keys: `"namespace"`, `"node"` and string values. * **Default:** `[]` * **Example:** `pep_mapping = [{namespace = "urn:xmpp:microblog:0", node = "mb"}]` This permits creating a Key-Value list to define a custom node plugin on a given PEP namespace. E.g. pair `{"urn:xmpp:microblog:0", "mb"}` will use module `node_mb` instead of `node_pep` when the specified namespace is used. - ### `modules.mod_pubsub.default_node_config` * **Syntax:** TOML table with the following values: string, boolean or non-negative integer. -* **Default:** `[]` -* **Example:** `default_node_config = {deliver_payloads = true, max_payload_size, 10000, node_type = "leaf"}` +* **Default:** `{}` +* **Example:** `default_node_config = {deliver_payloads = true, max_payload_size = 10000, node_type = "leaf"}` -Overrides the default node configuration, regradless of the node plugin. +Overrides the default node configuration, regardless of the node plugin. Node configuration still uses the default configuration defined by the node plugin, and overrides any items by the value defined in this configurable list. ### `modules.mod_pubsub.item_publisher` diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index ed97a16301..8b836b8b1a 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -196,6 +196,8 @@ groups() -> mod_privacy, mod_private, mod_pubsub, + mod_pubsub_pep_mapping, + mod_pubsub_default_node_config, mod_push_service_mongoosepush, mod_register, mod_revproxy, @@ -2334,116 +2336,109 @@ mod_private(_Config) -> check_iqdisc(mod_private). mod_pubsub(_Config) -> - %% TODO default_node_config + check_iqdisc(mod_pubsub), T = fun(Opts) -> #{<<"modules">> => #{<<"mod_pubsub">> => Opts}} end, - Base = #{<<"backend">> => <<"mnesia">>, - <<"host">> => <<"pubsub.@HOST@">>, - <<"access_createnode">> => <<"all">>, - <<"max_items_node">> => 10, - <<"max_subscriptions_node">> => 10, - <<"nodetree">> => <<"tree">>, - <<"ignore_pep_from_offline">> => true, - <<"last_item_cache">> => false, - <<"plugins">> => [<<"flat">>], - <<"pep_mapping">> => [#{<<"namespace">> => <<"urn:xmpp:microblog:0">>, - <<"node">> => <<"mb">>}], - <<"item_publisher">> => false, - <<"sync_broadcast">> => true}, - MBase = [{backend, mnesia}, - {access_createnode, all}, - {host, "pubsub.@HOST@"}, - {max_items_node, 10}, - {max_subscriptions_node, 10}, - {nodetree, <<"tree">>}, - {ignore_pep_from_offline, true}, - {last_item_cache, false}, - {plugins, [<<"flat">>]}, - {pep_mapping, [{"urn:xmpp:microblog:0", "mb"}]}, - {item_publisher, false}, - {sync_broadcast, true}], - ?eqf(modopts(mod_pubsub, lists:sort(MBase)), T(Base)), - ?eqf(modopts(mod_pubsub, [{last_item_cache, mnesia}]), - T(#{<<"last_item_cache">> => <<"mnesia">>})), - ?eqf(modopts(mod_pubsub, []), %% The option is undefined, i.e. parser just removes it - T(#{<<"max_subscriptions_node">> => <<"infinity">>})), - run_multi( - good_default_node_config_opts(T) ++ - bad_default_node_config_opts(T) ++ - generic_bad_opts_cases(T, mod_pubsub_bad_opts())), - check_iqdisc(mod_pubsub). - -good_default_node_config_opts(T) -> - [good_default_node_config_opt(T, K, Toml, Mim) - || {K, Toml, Mim} <- default_node_config_opts()]. - -good_default_node_config_opt(T, K, Toml, Mim) -> - MBase = [{default_node_config, [{K, Mim}]}], - Base = #{<<"default_node_config">> => #{a2b(K) => Toml}}, - ?_eqf(modopts(mod_pubsub, MBase), T(Base)). - -bad_default_node_config_opts(T) -> - [bad_default_node_config_opt(T, K, Toml) - || {K, Toml} <- default_node_config_bad_opts()]. - -bad_default_node_config_opt(T, K, Toml) -> - Base = #{<<"default_node_config">> => #{a2b(K) => Toml}}, - ?_errf(T(Base)). - -default_node_config_opts() -> - [{access_model, <<"open">>, open}, - {deliver_notifications, true, true}, - {deliver_payloads, true, true}, - {max_items, 10, 10}, - {max_payload_size, 10000, 10000}, - {node_type, <<"leaf">>, leaf}, - {notification_type, <<"headline">>, headline}, - {notify_config, false, false}, - {notify_delete, false, false}, - {notify_retract, false, false}, - {persist_items, true, true}, - {presence_based_delivery, true, true}, - {publish_model, <<"open">>, open}, - {purge_offline, false, false}, - {roster_groups_allowed, [<<"friends">>], [<<"friends">>]}, - {send_last_published_item, <<"on_sub_and_presence">>, on_sub_and_presence}, - {subscribe, true, true}]. - -default_node_config_bad_opts() -> - [{access_model, 1}, - {deliver_notifications, 1}, - {deliver_payloads, 1}, - {max_items, -1}, - {max_payload_size, -1}, - {node_type, 1}, - {notification_type, 1}, - {notify_config, 1}, - {notify_delete, 1}, - {notify_retract, 1}, - {persist_items, 1}, - {presence_based_delivery, 1}, - {publish_model, 1}, - {purge_offline, 1}, - {roster_groups_allowed, [1]}, - {roster_groups_allowed, 1}, - {send_last_published_item, 1}, - {subscribe, 1}]. - -mod_pubsub_bad_opts() -> - [{backend, 1}, - {access_createnode, 1}, - {host, 1}, - {host, <<"aaa aaa">>}, - {max_items_node, -1}, - {max_subscriptions_node, -1}, - {nodetree, -1}, - {nodetree, <<"oops">>}, - {ignore_pep_from_offline, 1}, - {last_item_cache, 1}, - {plugins, [<<"fat">>]}, - {pep_mapping, [#{<<"namespace">> => 1, <<"node">> => <<"mb">>}]}, - {pep_mapping, [#{<<"namespace">> => <<"urn:xmpp:microblog:0">>, <<"node">> => 1}]}, - {item_publisher, 1}, - {sync_broadcast, 1}]. + M = fun(Cfg) -> modopts(mod_pubsub, Cfg) end, + ?eqf(M([{host, "pub.@HOST@"}]), + T(#{<<"host">> => <<"pub.@HOST@">>})), + ?eqf(M([{backend, rdbms}]), + T(#{<<"backend">> => <<"rdbms">>})), + ?eqf(M([{access_createnode, all}]), + T(#{<<"access_createnode">> => <<"all">>})), + ?eqf(M([{max_items_node, 20}]), + T(#{<<"max_items_node">> => 20})), + ?eqf(M([{max_subscriptions_node, 30}]), + T(#{<<"max_subscriptions_node">> => 30})), + ?eqf(M([{nodetree, <<"tree">>}]), + T(#{<<"nodetree">> => <<"tree">>})), + ?eqf(M([{ignore_pep_from_offline, false}]), + T(#{<<"ignore_pep_from_offline">> => false})), + ?eqf(M([{last_item_cache, rdbms}]), + T(#{<<"last_item_cache">> => <<"rdbms">>})), + ?eqf(M([{plugins, [<<"flat">>, <<"dag">>]}]), + T(#{<<"plugins">> => [<<"flat">>, <<"dag">>]})), + ?eqf(M([{item_publisher, true}]), + T(#{<<"item_publisher">> => true})), + ?eqf(M([{sync_broadcast, false}]), + T(#{<<"sync_broadcast">> => false})), + ?errf(T(#{<<"host">> => <<"">>})), + ?errf(T(#{<<"backend">> => <<"amnesia">>})), + ?errf(T(#{<<"access_createnode">> => <<"">>})), + ?errf(T(#{<<"max_items_node">> => -1})), + ?errf(T(#{<<"max_subscriptions_node">> => 3.1415})), + ?errf(T(#{<<"nodetree">> => <<"christmas_tree">>})), + ?errf(T(#{<<"ignore_pep_from_offline">> => <<"maybe">>})), + ?errf(T(#{<<"last_item_cache">> => false})), + ?errf(T(#{<<"plugins">> => [<<"deep">>]})), + ?errf(T(#{<<"item_publisher">> => 1})), + ?errf(T(#{<<"sync_broadcast">> => []})). + +mod_pubsub_pep_mapping(_Config) -> + T = fun(Opts) -> #{<<"modules">> => #{<<"mod_pubsub">> => + #{<<"pep_mapping">> => Opts}}} end, + M = fun(Cfg) -> modopts(mod_pubsub, [{pep_mapping, Cfg}]) end, + RequiredOpts = #{<<"namespace">> => <<"urn:xmpp:microblog:0">>, + <<"node">> => <<"mb">>}, + ?eqf(M([{<<"urn:xmpp:microblog:0">>, <<"mb">>}]), + T([RequiredOpts])), + [?errf(T([maps:remove(Key, RequiredOpts)])) || Key <- maps:keys(RequiredOpts)], + [?errf(T([RequiredOpts#{Key => <<>>}])) || Key <- maps:keys(RequiredOpts)]. + +mod_pubsub_default_node_config(_Config) -> + T = fun(Opts) -> #{<<"modules">> => #{<<"mod_pubsub">> => + #{<<"default_node_config">> => Opts}}} end, + M = fun(Cfg) -> modopts(mod_pubsub, [{default_node_config, Cfg}]) end, + ?eqf(M([{access_model, open}]), + T(#{<<"access_model">> => <<"open">>})), + ?eqf(M([{deliver_notifications, true}]), + T(#{<<"deliver_notifications">> => true})), + ?eqf(M([{deliver_payloads, false}]), + T(#{<<"deliver_payloads">> => false})), + ?eqf(M([{max_items, 1000}]), + T(#{<<"max_items">> => 1000})), + ?eqf(M([{max_payload_size, 1000}]), + T(#{<<"max_payload_size">> => 1000})), + ?eqf(M([{node_type, dag}]), + T(#{<<"node_type">> => <<"dag">>})), + ?eqf(M([{notification_type, headline}]), + T(#{<<"notification_type">> => <<"headline">>})), + ?eqf(M([{notify_config, true}]), + T(#{<<"notify_config">> => true})), + ?eqf(M([{notify_delete, false}]), + T(#{<<"notify_delete">> => false})), + ?eqf(M([{notify_retract, true}]), + T(#{<<"notify_retract">> => true})), + ?eqf(M([{persist_items, false}]), + T(#{<<"persist_items">> => false})), + ?eqf(M([{presence_based_delivery, true}]), + T(#{<<"presence_based_delivery">> => true})), + ?eqf(M([{publish_model, open}]), + T(#{<<"publish_model">> => <<"open">>})), + ?eqf(M([{purge_offline, false}]), + T(#{<<"purge_offline">> => false})), + ?eqf(M([{roster_groups_allowed, [<<"friends">>]}]), + T(#{<<"roster_groups_allowed">> => [<<"friends">>]})), + ?eqf(M([{send_last_published_item, on_sub_and_presence}]), + T(#{<<"send_last_published_item">> => <<"on_sub_and_presence">>})), + ?eqf(M([{subscribe, true}]), + T(#{<<"subscribe">> => true})), + ?errf(T(#{<<"access_model">> => <<>>})), + ?errf(T(#{<<"deliver_notifications">> => <<"yes">>})), + ?errf(T(#{<<"deliver_payloads">> => 0})), + ?errf(T(#{<<"max_items">> => -1})), + ?errf(T(#{<<"max_payload_size">> => -1})), + ?errf(T(#{<<"node_type">> => [<<"dag">>]})), + ?errf(T(#{<<"notification_type">> => <<>>})), + ?errf(T(#{<<"notify_config">> => <<"false">>})), + ?errf(T(#{<<"notify_delete">> => [true]})), + ?errf(T(#{<<"notify_retract">> => #{}})), + ?errf(T(#{<<"persist_items">> => 1})), + ?errf(T(#{<<"presence_based_delivery">> => []})), + ?errf(T(#{<<"publish_model">> => <<"">>})), + ?errf(T(#{<<"purge_offline">> => 1})), + ?errf(T(#{<<"roster_groups_allowed">> => [<<>>]})), + ?errf(T(#{<<"send_last_published_item">> => <<>>})), + ?errf(T(#{<<"subscribe">> => <<"never">>})). mod_push_service_mongoosepush(_Config) -> T = fun(Opts) -> #{<<"modules">> => #{<<"mod_push_service_mongoosepush">> => Opts}} end, diff --git a/test/config_parser_SUITE_data/modules.options b/test/config_parser_SUITE_data/modules.options index 134ae4622a..8bcd3a7242 100644 --- a/test/config_parser_SUITE_data/modules.options +++ b/test/config_parser_SUITE_data/modules.options @@ -172,7 +172,7 @@ {ignore_pep_from_offline,false}, {last_item_cache,mnesia}, {max_items_node,1000}, - {pep_mapping,[{"urn:xmpp:microblog:0","mb"}]}, + {pep_mapping,[{<<"urn:xmpp:microblog:0">>,<<"mb">>}]}, {plugins,[<<"flat">>,<<"pep">>]}]}, {mod_mam_rdbms_prefs,[pm]}, {mod_mam_meta, @@ -441,7 +441,7 @@ {ignore_pep_from_offline,false}, {last_item_cache,mnesia}, {max_items_node,1000}, - {pep_mapping,[{"urn:xmpp:microblog:0","mb"}]}, + {pep_mapping,[{<<"urn:xmpp:microblog:0">>,<<"mb">>}]}, {plugins,[<<"flat">>,<<"pep">>]}]}, {mod_mam_rdbms_prefs,[pm]}, {mod_mam_meta, From 409922a087e3f7f6a67eeaa84f6a32c1b57558f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Tue, 15 Dec 2020 16:49:08 +0100 Subject: [PATCH 062/104] Make the config of mongoose_push_service_mongoosepush declarative - Validate 'api_version' explicitely - the module checked it anyway - Simplify tests, update docs --- doc/modules/mod_push_service_mongoosepush.md | 2 +- src/config/mongoose_config_parser_toml.erl | 9 ++------ src/config/mongoose_config_spec.erl | 3 ++- src/config/mongoose_config_validator_toml.erl | 9 -------- src/mod_push_service_mongoosepush.erl | 15 ++++++++++++- test/config_parser_SUITE.erl | 21 +++++++++---------- 6 files changed, 29 insertions(+), 30 deletions(-) diff --git a/doc/modules/mod_push_service_mongoosepush.md b/doc/modules/mod_push_service_mongoosepush.md index 77f50eec41..4e48086c8f 100644 --- a/doc/modules/mod_push_service_mongoosepush.md +++ b/doc/modules/mod_push_service_mongoosepush.md @@ -19,7 +19,7 @@ It must be defined in [outgoing_pools setting](../advanced-configuration/outgoin The name of the pool to use (as defined in `outgoing_pools`). ### `modules.mod_push_service_mongoosepush.api_version` -* **Syntax:** string +* **Syntax:** string, `"v2"` or `"v3"` * **Default:** `"v3"` * **Example:** `api_version = "v3"` diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index 8ca1f08cc0..cc21ec33be 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -175,12 +175,6 @@ module_opt([<<"muc">>, <<"mod_mam_meta">>|_] = Path, V) -> [{muc, Muc}]; module_opt([_, <<"mod_mam_meta">>|_] = Path, V) -> mod_mam_opts(Path, V); -module_opt([<<"pool_name">>, <<"mod_push_service_mongoosepush">>|_], V) -> - [{pool_name, b2a(V)}]; -module_opt([<<"api_version">>, <<"mod_push_service_mongoosepush">>|_], V) -> - [{api_version, b2l(V)}]; -module_opt([<<"max_http_connections">>, <<"mod_push_service_mongoosepush">>|_], V) -> - [{max_http_connections, V}]; module_opt([<<"access">>, <<"mod_register">>|_], V) -> [{access, b2a(V)}]; module_opt([<<"registration_watchers">>, <<"mod_register">>|_] = Path, V) -> @@ -772,7 +766,8 @@ node_to_string(Node) -> [binary_to_list(Node)]. Mod =/= <<"mod_muc_log">>, Mod =/= <<"mod_offline">>, Mod =/= <<"mod_ping">>, - Mod =/= <<"mod_pubsub">>). % TODO temporary, remove with 'handler/1' + Mod =/= <<"mod_pubsub">>, + Mod =/= <<"mod_push_service_mongoosepush">>). % TODO temporary, remove with 'handler/1' -spec handler(path()) -> fun((path(), toml_value()) -> option()) | mongoose_config_spec:config_node(). diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index ccbad4ecde..f90c81716e 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -743,7 +743,8 @@ all_modules() -> mod_muc_log, mod_offline, mod_ping, - mod_pubsub]. + mod_pubsub, + mod_push_service_mongoosepush]. %% path: (host_config[].)modules.*.iqdisc iqdisc() -> diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index 20ea5c4680..38090c7050 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -283,15 +283,6 @@ validate([<<"secret_access_key">>, <<"s3">>, <<"mod_http_upload">>, <<"modules"> validate([<<"token_bytes">>, <<"mod_http_upload">>, <<"modules">>|_], [{token_bytes, V}]) -> validate_positive_integer(V); -validate([<<"api_version">>, <<"mod_push_service_mongoosepush">>, <<"modules">>|_], - [{api_version, V}]) -> - validate_string(V); -validate([<<"max_http_connections">>, <<"mod_push_service_mongoosepush">>, <<"modules">>|_], - [{max_http_connections, V}]) -> - validate_non_negative_integer(V); -validate([<<"pool_name">>, <<"mod_push_service_mongoosepush">>, <<"modules">>|_], - [{pool_name, V}]) -> - validate_pool_name(V); validate([<<"backend">>, <<"mod_last">>, <<"modules">>|_], [{backend, V}]) -> validate_backend(mod_last, V); diff --git a/src/mod_push_service_mongoosepush.erl b/src/mod_push_service_mongoosepush.erl index 552224f3a6..8faad3b0b9 100644 --- a/src/mod_push_service_mongoosepush.erl +++ b/src/mod_push_service_mongoosepush.erl @@ -16,13 +16,14 @@ -include("mongoose.hrl"). -include("jlib.hrl"). +-include("mongoose_config_spec.hrl"). %%-------------------------------------------------------------------- %% Exports %%-------------------------------------------------------------------- %% gen_mod handlers --export([start/2, stop/1]). +-export([start/2, stop/1, config_spec/0]). %% Hooks and IQ handlers -export([push_notifications/4]). @@ -69,6 +70,18 @@ stop(Host) -> ok. +-spec config_spec() -> mongoose_config_spec:config_section(). +config_spec() -> + #section{ + items = #{<<"pool_name">> => #option{type = atom, + validate = pool_name}, + <<"api_version">> => #option{type = string, + validate = {enum, ["v2", "v3"]}}, + <<"max_http_connections">> => #option{type = integer, + validate = non_negative} + } + }. + %%-------------------------------------------------------------------- %% Hooks %%-------------------------------------------------------------------- diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index 8b836b8b1a..ca1ca1b1d3 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -2442,17 +2442,16 @@ mod_pubsub_default_node_config(_Config) -> mod_push_service_mongoosepush(_Config) -> T = fun(Opts) -> #{<<"modules">> => #{<<"mod_push_service_mongoosepush">> => Opts}} end, - Base = #{<<"pool_name">> => <<"test_pool">>, - <<"api_version">> => <<"v3">>, - <<"max_http_connections">> => 100}, - MBase = [{pool_name, test_pool}, - {api_version, "v3"}, - {max_http_connections, 100}], - ?eqf(modopts(mod_push_service_mongoosepush, lists:sort(MBase)), T(Base)), - ?errf(T(Base#{<<"pool_name">> => 1})), - ?errf(T(Base#{<<"api_version">> => 1})), - ?errf(T(Base#{<<"max_http_connections">> => -1})), - ok. + M = fun(Cfg) -> modopts(mod_push_service_mongoosepush, Cfg) end, + ?eqf(M([{pool_name, test_pool}]), + T(#{<<"pool_name">> => <<"test_pool">>})), + ?eqf(M([{api_version, "v3"}]), + T(#{<<"api_version">> => <<"v3">>})), + ?eqf(M([{max_http_connections, 100}]), + T(#{<<"max_http_connections">> => 100})), + ?errf(T(#{<<"pool_name">> => 1})), + ?errf(T(#{<<"api_version">> => <<"v4">>})), + ?errf(T(#{<<"max_http_connections">> => -1})). mod_register(_Config) -> ?eqf(modopts(mod_register, From aa7ae03cedc1121aff04d24f3f6628bd01f02e70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Tue, 15 Dec 2020 17:20:37 +0100 Subject: [PATCH 063/104] Make the config of mod_privacy declarative --- src/config/mongoose_config_parser_toml.erl | 1 + src/config/mongoose_config_spec.erl | 1 + src/config/mongoose_config_validator_toml.erl | 12 -------- src/mod_privacy.erl | 21 ++++++++++++++ test/config_parser_SUITE.erl | 29 +++++++++---------- 5 files changed, 36 insertions(+), 28 deletions(-) diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index cc21ec33be..d858cafcc3 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -766,6 +766,7 @@ node_to_string(Node) -> [binary_to_list(Node)]. Mod =/= <<"mod_muc_log">>, Mod =/= <<"mod_offline">>, Mod =/= <<"mod_ping">>, + Mod =/= <<"mod_privacy">>, Mod =/= <<"mod_pubsub">>, Mod =/= <<"mod_push_service_mongoosepush">>). % TODO temporary, remove with 'handler/1' diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index f90c81716e..f0e164bcfc 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -743,6 +743,7 @@ all_modules() -> mod_muc_log, mod_offline, mod_ping, + mod_privacy, mod_pubsub, mod_push_service_mongoosepush]. diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index 38090c7050..fbc3ee32c2 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -298,18 +298,6 @@ validate([<<"iqdisc">>, <<"mod_time">>, <<"modules">>|_], validate([item, <<"routes">>, <<"mod_revproxy">>, <<"modules">>|_], [V]) -> validate_revproxy_route(V); -validate([<<"backend">>, <<"mod_privacy">>, <<"modules">>|_], - [{backend, V}]) -> - validate_backend(mod_privacy, V); -validate([<<"bucket_type">>, <<"riak">>, <<"mod_privacy">>, <<"modules">>|_], - [{bucket_type, V}]) -> - validate_non_empty_binary(V); -validate([<<"defaults_bucket_type">>, <<"riak">>, <<"mod_privacy">>, <<"modules">>|_], - [{defaults_bucket_type, V}]) -> - validate_non_empty_binary(V); -validate([<<"names_bucket_type">>, <<"riak">>, <<"mod_privacy">>, <<"modules">>|_], - [{names_bucket_type, V}]) -> - validate_non_empty_binary(V); validate([<<"archive_chat_markers">>, <<"mod_mam_meta">>, <<"modules">>|_], [{archive_chat_markers, V}]) -> validate_boolean(V); diff --git a/src/mod_privacy.erl b/src/mod_privacy.erl index 33f6a5a0c5..3ac6ea3a52 100644 --- a/src/mod_privacy.erl +++ b/src/mod_privacy.erl @@ -32,6 +32,7 @@ -export([start/2, stop/1, + config_spec/0, process_iq_set/4, process_iq_get/5, get_user_list/3, @@ -44,6 +45,7 @@ -include("mongoose.hrl"). -include("jlib.hrl"). -include("mod_privacy.hrl"). +-include("mongoose_config_spec.hrl"). -export_type([list_item/0]). @@ -150,6 +152,25 @@ stop(Host) -> ejabberd_hooks:delete(anonymous_purge_hook, Host, ?MODULE, remove_user, 50). +config_spec() -> + #section{ + items = #{<<"backend">> => #option{type = atom, + validate = {module, mod_privacy}}, + <<"riak">> => riak_config_spec()} + }. + +riak_config_spec() -> + #section{ + items = #{<<"defaults_bucket_type">> => #option{type = binary, + validate = non_empty}, + <<"names_bucket_type">> => #option{type = binary, + validate = non_empty}, + <<"bucket_type">> => #option{type = binary, + validate = non_empty} + }, + format = none + }. + %% Handlers %% ------------------------------------------------------------------ diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index ca1ca1b1d3..14c0a2272e 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -2304,22 +2304,19 @@ mod_ping(_Config) -> mod_privacy(_Config) -> T = fun(Opts) -> #{<<"modules">> => #{<<"mod_privacy">> => Opts}} end, - Riak = #{<<"defaults_bucket_type">> => <<"privacy_defaults">>, - <<"names_bucket_type">> => <<"privacy_lists_names">>, - <<"bucket_type">> => <<"privacy_defaults">>}, - Base = #{<<"backend">> => <<"mnesia">>, - <<"riak">> => Riak}, - MBase = [{backend, mnesia}, - %% Riak opts - {defaults_bucket_type, <<"privacy_defaults">>}, - {names_bucket_type, <<"privacy_lists_names">>}, - {bucket_type, <<"privacy_defaults">>}], - ?eqf(modopts(mod_privacy, lists:sort(MBase)), T(Base)), - ?errf(T(Base#{<<"backend">> => 1})), - ?errf(T(Base#{<<"backend">> => <<"mongoddt">>})), - ?errf(T(Base#{<<"riak">> => #{<<"defaults_bucket_type">> => 1}})), - ?errf(T(Base#{<<"riak">> => #{<<"names_bucket_type">> => 1}})), - ?errf(T(Base#{<<"riak">> => #{<<"bucket_type">> => 1}})). + M = fun(Cfg) -> modopts(mod_privacy, Cfg) end, + ?eqf(M([{backend, mnesia}]), + T(#{<<"backend">> => <<"mnesia">>})), + ?eqf(M([{defaults_bucket_type, <<"defaults">>}]), + T(#{<<"riak">> => #{<<"defaults_bucket_type">> => <<"defaults">>}})), + ?eqf(M([{names_bucket_type, <<"names">>}]), + T(#{<<"riak">> => #{<<"names_bucket_type">> => <<"names">>}})), + ?eqf(M([{bucket_type, <<"bucket">>}]), + T(#{<<"riak">> => #{<<"bucket_type">> => <<"bucket">>}})), + ?errf(T(#{<<"backend">> => <<"mongoddt">>})), + ?errf(T(#{<<"riak">> => #{<<"defaults_bucket_type">> => <<>>}})), + ?errf(T(#{<<"riak">> => #{<<"names_bucket_type">> => 1}})), + ?errf(T(#{<<"riak">> => #{<<"bucket_type">> => 1}})). mod_private(_Config) -> T = fun(Opts) -> #{<<"modules">> => #{<<"mod_private">> => Opts}} end, From 523b42efa5f3801dbca06d2fa094528c7c691961 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Wed, 16 Dec 2020 08:07:12 +0100 Subject: [PATCH 064/104] Make the config of mod_private declarative --- src/config/mongoose_config_parser_toml.erl | 1 + src/config/mongoose_config_spec.erl | 1 + src/config/mongoose_config_validator_toml.erl | 9 --------- src/mod_private.erl | 17 +++++++++++++++++ test/config_parser_SUITE.erl | 17 +++++++---------- 5 files changed, 26 insertions(+), 19 deletions(-) diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index d858cafcc3..37b5a9c1ca 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -767,6 +767,7 @@ node_to_string(Node) -> [binary_to_list(Node)]. Mod =/= <<"mod_offline">>, Mod =/= <<"mod_ping">>, Mod =/= <<"mod_privacy">>, + Mod =/= <<"mod_private">>, Mod =/= <<"mod_pubsub">>, Mod =/= <<"mod_push_service_mongoosepush">>). % TODO temporary, remove with 'handler/1' diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index f0e164bcfc..388f331a08 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -744,6 +744,7 @@ all_modules() -> mod_offline, mod_ping, mod_privacy, + mod_private, mod_pubsub, mod_push_service_mongoosepush]. diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index fbc3ee32c2..129442d917 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -586,15 +586,6 @@ validate([<<"search_index">>, <<"riak">>, <<"mod_vcard">>, <<"modules">>|_], validate([<<"search">>, <<"mod_vcard">>, <<"modules">>|_], [{search, V}]) -> validate_boolean(V); -validate([<<"backend">>, <<"mod_private">>, <<"modules">>|_], - [{backend, V}]) -> - validate_backend(mod_private, V); -validate([<<"iqdisc">>, <<"mod_private">>, <<"modules">>|_], - [{iqdisc, V}]) -> - validate_iqdisc(V); -validate([<<"bucket_type">>, <<"riak">>, <<"mod_private">>, <<"modules">>|_], - [{bucket_type, V}]) -> - validate_non_empty_binary(V); validate([<<"aff_changes">>, <<"mod_inbox">>, <<"modules">>|_], [{aff_changes, V}]) -> validate_boolean(V); diff --git a/src/mod_private.erl b/src/mod_private.erl index 812fed5334..ef7774a10e 100644 --- a/src/mod_private.erl +++ b/src/mod_private.erl @@ -31,6 +31,7 @@ -export([start/2, stop/1, + config_spec/0, process_sm_iq/4, remove_user/3]). @@ -40,6 +41,7 @@ -include("mongoose.hrl"). -include("jlib.hrl"). +-include("mongoose_config_spec.hrl"). -xep([{xep, 49}, {version, "1.2"}]). %% ------------------------------------------------------------------ @@ -109,6 +111,21 @@ stop(Host) -> ejabberd_hooks:delete(get_personal_data, Host, ?MODULE, get_personal_data, 50), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_PRIVATE). +config_spec() -> + #section{ + items = #{<<"iqdisc">> => mongoose_config_spec:iqdisc(), + <<"backend">> => #option{type = atom, + validate = {module, mod_private}}, + <<"riak">> => riak_config_spec()} + }. + +riak_config_spec() -> + #section{ + items = #{<<"bucket_type">> => #option{type = binary, + validate = non_empty} + }, + format = none + }. %% ------------------------------------------------------------------ %% Handlers diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index 14c0a2272e..5307a4f3e1 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -2320,16 +2320,13 @@ mod_privacy(_Config) -> mod_private(_Config) -> T = fun(Opts) -> #{<<"modules">> => #{<<"mod_private">> => Opts}} end, - Riak = #{<<"bucket_type">> => <<"private_stuff">>}, - Base = #{<<"backend">> => <<"riak">>, - <<"riak">> => Riak}, - MBase = [{backend, riak}, - %% Riak opts - {bucket_type, <<"private_stuff">>}], - ?eqf(modopts(mod_private, lists:sort(MBase)), T(Base)), - ?errf(T(Base#{<<"backend">> => 1})), - ?errf(T(Base#{<<"backend">> => <<"mongoddt">>})), - ?errf(T(Base#{<<"riak">> => #{<<"bucket_type">> => 1}})), + M = fun(Cfg) -> modopts(mod_private, Cfg) end, + ?eqf(M([{backend, riak}]), + T(#{<<"backend">> => <<"riak">>})), + ?eqf(M([{bucket_type, <<"private_stuff">>}]), + T(#{<<"riak">> => #{<<"bucket_type">> => <<"private_stuff">>}})), + ?errf(T(#{<<"backend">> => <<"mssql">>})), + ?errf(T(#{<<"riak">> => #{<<"bucket_type">> => 1}})), check_iqdisc(mod_private). mod_pubsub(_Config) -> From 22afcefbbd5563a8b82d3b1a84851ce0587bfd89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Thu, 17 Dec 2020 15:04:41 +0100 Subject: [PATCH 065/104] Make the config of mod_mam_meta declarative - Accept PM- and MUC-specific options only in their subsections - Accept riak options only at the top level (there is no separate riak backend for PM and MUC) - Do not accept 'false' for 'user_prefs_store' (let the user omit this option instead) - Do not enable 'pm' by default Motivation: it was only like this for the 'cfg' config, make it consistent with TOML as there is no 'cfg' now --- src/config/mongoose_config_parser_toml.erl | 70 +------ src/config/mongoose_config_spec.erl | 1 + src/config/mongoose_config_validator_toml.erl | 195 ------------------ src/mam/mod_mam_meta.erl | 72 ++++++- test/mod_mam_meta_SUITE.erl | 25 +-- 5 files changed, 85 insertions(+), 278 deletions(-) diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index 37b5a9c1ca..20c4b8b390 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -99,9 +99,6 @@ process_module([Mod|_] = Path, Opts) -> %% Sort option keys to ensure options could be matched in tests post_process_module(b2a(Mod), parse_section(Path, Opts)). -post_process_module(mod_mam_meta, Opts) -> - %% Disable the archiving by default - [{mod_mam_meta, lists:sort(defined_or_false(muc, defined_or_false(pm, Opts)))}]; post_process_module(Mod, Opts) -> [{Mod, lists:sort(Opts)}]. @@ -167,14 +164,6 @@ module_opt([<<"ram_key_size">>, <<"mod_keystore">>|_], V) -> module_opt([<<"keys">>, <<"mod_keystore">>|_] = Path, V) -> Keys = parse_list(Path, V), [{keys, Keys}]; -module_opt([<<"pm">>, <<"mod_mam_meta">>|_] = Path, V) -> - PM = parse_section(Path, V), - [{pm, PM}]; -module_opt([<<"muc">>, <<"mod_mam_meta">>|_] = Path, V) -> - Muc = parse_section(Path, V), - [{muc, Muc}]; -module_opt([_, <<"mod_mam_meta">>|_] = Path, V) -> - mod_mam_opts(Path, V); module_opt([<<"access">>, <<"mod_register">>|_], V) -> [{access, b2a(V)}]; module_opt([<<"registration_watchers">>, <<"mod_register">>|_] = Path, V) -> @@ -407,54 +396,6 @@ mod_keystore_keys(_, #{<<"name">> := Name, <<"type">> := <<"ram">>}) -> mod_keystore_keys(_, #{<<"name">> := Name, <<"type">> := <<"file">>, <<"path">> := Path}) -> [{b2a(Name), {file, b2l(Path)}}]. --spec mod_mam_opts(path(), toml_value()) -> [option()]. -mod_mam_opts([<<"backend">>|_], V) -> - [{backend, b2a(V)}]; -mod_mam_opts([<<"no_stanzaid_element">>|_], V) -> - [{no_stanzaid_element, V}]; -mod_mam_opts([<<"is_archivable_message">>|_], V) -> - [{is_archivable_message, b2a(V)}]; -mod_mam_opts([<<"message_retraction">>|_], V) -> - [{message_retraction, V}]; -mod_mam_opts([<<"user_prefs_store">>|_], false) -> - [{user_prefs_store, false}]; -mod_mam_opts([<<"user_prefs_store">>|_], V) -> - [{user_prefs_store, b2a(V)}]; -mod_mam_opts([<<"full_text_search">>|_], V) -> - [{full_text_search, V}]; -mod_mam_opts([<<"cache_users">>|_], V) -> - [{cache_users, V}]; -mod_mam_opts([<<"rdbms_message_format">>|_], V) -> - [{rdbms_message_format, b2a(V)}]; -mod_mam_opts([<<"async_writer">>|_], V) -> - [{async_writer, V}]; -mod_mam_opts([<<"flush_interval">>|_], V) -> - [{flush_interval, V}]; -mod_mam_opts([<<"max_batch_size">>|_], V) -> - [{max_batch_size, V}]; -mod_mam_opts([<<"default_result_limit">>|_], V) -> - [{default_result_limit, V}]; -mod_mam_opts([<<"max_result_limit">>|_], V) -> - [{max_result_limit, V}]; -mod_mam_opts([<<"archive_chat_markers">>|_], V) -> - [{archive_chat_markers, V}]; -mod_mam_opts([<<"archive_groupchats">>|_], V) -> - [{archive_groupchats, V}]; -mod_mam_opts([<<"async_writer_rdbms_pool">>|_], V) -> - [{async_writer_rdbms_pool, b2a(V)}]; -mod_mam_opts([<<"db_jid_format">>|_], V) -> - [{db_jid_format, b2a(V)}]; -mod_mam_opts([<<"db_message_format">>|_], V) -> - [{db_message_format, b2a(V)}]; -mod_mam_opts([<<"simple">>|_], V) -> - [{simple, V}]; -mod_mam_opts([<<"host">>|_], V) -> - [{host, b2l(V)}]; -mod_mam_opts([<<"extra_lookup_params">>|_], V) -> - [{extra_lookup_params, b2a(V)}]; -mod_mam_opts([<<"riak">>|_] = Path, V) -> - parse_section(Path, V). - -spec mod_revproxy_routes(path(), toml_section()) -> [option()]. mod_revproxy_routes(_, #{<<"host">> := Host, <<"path">> := Path, <<"method">> := Method, <<"upstream">> := Upstream}) -> @@ -761,6 +702,7 @@ node_to_string(Node) -> [binary_to_list(Node)]. Mod =/= <<"mod_csi">>, Mod =/= <<"mod_disco">>, Mod =/= <<"mod_event_pusher">>, + Mod =/= <<"mod_mam_meta">>, Mod =/= <<"mod_muc">>, Mod =/= <<"mod_muc_light">>, Mod =/= <<"mod_muc_log">>, @@ -813,8 +755,6 @@ handler([_,<<"tls">>, <<"connections">>, <<"mod_global_distrib">>, <<"modules">> fun mod_global_distrib_tls_option/2; handler([_, <<"keys">>, <<"mod_keystore">>, <<"modules">>]) -> fun mod_keystore_keys/2; -handler([_, _, <<"mod_mam_meta">>, <<"modules">>]) -> - fun mod_mam_opts/2; handler([_, <<"routes">>, <<"mod_revproxy">>, <<"modules">>]) -> fun mod_revproxy_routes/2; handler([_, <<"stale_h">>, <<"mod_stream_management">>, <<"modules">>]) -> @@ -865,14 +805,6 @@ handler_for_host(Path) -> item_key([<<"host_config">>], #{<<"host">> := Host}) -> {host, Host}; item_key(_, _) -> item. -defined_or_false(Key, Opts) -> - case proplists:is_defined(Key, Opts) of - true -> - []; - false -> - [{Key, false}] - end ++ Opts. - %% Processing of the parsed options -spec get_hosts(config_list()) -> [ejabberd:server()]. diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index 388f331a08..eaf3470d76 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -738,6 +738,7 @@ all_modules() -> mod_csi, mod_disco, mod_event_pusher, + mod_mam_meta, mod_muc, mod_muc_light, mod_muc_log, diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index 129442d917..fabd623062 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -298,201 +298,6 @@ validate([<<"iqdisc">>, <<"mod_time">>, <<"modules">>|_], validate([item, <<"routes">>, <<"mod_revproxy">>, <<"modules">>|_], [V]) -> validate_revproxy_route(V); -validate([<<"archive_chat_markers">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{archive_chat_markers, V}]) -> - validate_boolean(V); -validate([<<"archive_groupchats">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{archive_groupchats, V}]) -> - validate_boolean(V); -validate([<<"async_writer">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{async_writer, V}]) -> - validate_boolean(V); -validate([<<"async_writer_rdbms_pool">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{async_writer_rdbms_pool, V}]) -> - validate_non_empty_atom(V); -validate([<<"backend">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{backend, V}]) -> - validate_enum(V, [rdbms,riak,cassandra,elasticsearch]); -validate([<<"cache_users">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{cache_users, V}]) -> - validate_boolean(V); -validate([<<"db_jid_format">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{db_jid_format, V}]) -> - validate_module(V); -validate([<<"db_message_format">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{db_message_format, V}]) -> - validate_module(V); -validate([<<"default_result_limit">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{default_result_limit, V}]) -> - validate_non_negative_integer(V); -validate([<<"extra_lookup_params">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{extra_lookup_params, V}]) -> - validate_module(V); -validate([<<"flush_interval">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{flush_interval, V}]) -> - validate_non_negative_integer(V); -validate([<<"full_text_search">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{full_text_search, V}]) -> - validate_boolean(V); -validate([<<"host">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{host, V}]) -> - validate_domain_template(V); -validate([<<"is_archivable_message">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{is_archivable_message, V}]) -> - validate_module(V); -validate([<<"max_batch_size">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{max_batch_size, V}]) -> - validate_non_negative_integer(V); -validate([<<"max_result_limit">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{max_result_limit, V}]) -> - validate_non_negative_integer(V); -validate([<<"message_retraction">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{message_retraction, V}]) -> - validate_boolean(V); -validate([<<"archive_chat_markers">>, <<"muc">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{archive_chat_markers, V}]) -> - validate_boolean(V); -validate([<<"archive_groupchats">>, <<"muc">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{archive_groupchats, V}]) -> - validate_boolean(V); -validate([<<"async_writer">>, <<"muc">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{async_writer, V}]) -> - validate_boolean(V); -validate([<<"async_writer_rdbms_pool">>, <<"muc">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{async_writer_rdbms_pool, V}]) -> - validate_non_empty_atom(V); -validate([<<"backend">>, <<"muc">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{backend, V}]) -> - validate_enum(V, [rdbms,riak,cassandra,elasticsearch]); -validate([<<"cache_users">>, <<"muc">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{cache_users, V}]) -> - validate_boolean(V); -validate([<<"db_jid_format">>, <<"muc">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{db_jid_format, V}]) -> - validate_module(V); -validate([<<"db_message_format">>, <<"muc">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{db_message_format, V}]) -> - validate_module(V); -validate([<<"default_result_limit">>, <<"muc">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{default_result_limit, V}]) -> - validate_non_negative_integer(V); -validate([<<"extra_lookup_params">>, <<"muc">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{extra_lookup_params, V}]) -> - validate_module(V); -validate([<<"flush_interval">>, <<"muc">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{flush_interval, V}]) -> - validate_non_negative_integer(V); -validate([<<"full_text_search">>, <<"muc">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{full_text_search, V}]) -> - validate_boolean(V); -validate([<<"host">>, <<"muc">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{host, V}]) -> - validate_domain_template(V); -validate([<<"is_archivable_message">>, <<"muc">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{is_archivable_message, V}]) -> - validate_module(V); -validate([<<"max_batch_size">>, <<"muc">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{max_batch_size, V}]) -> - validate_non_negative_integer(V); -validate([<<"max_result_limit">>, <<"muc">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{max_result_limit, V}]) -> - validate_non_negative_integer(V); -validate([<<"message_retraction">>, <<"muc">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{message_retraction, V}]) -> - validate_boolean(V); -validate([<<"no_stanzaid_element">>, <<"muc">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{no_stanzaid_element, V}]) -> - validate_boolean(V); -validate([<<"rdbms_message_format">>, <<"muc">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{rdbms_message_format, V}]) -> - validate_enum(V, [simple,internal]); -validate([<<"simple">>, <<"muc">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{simple, V}]) -> - validate_boolean(V); -validate([<<"user_prefs_store">>, <<"muc">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{user_prefs_store, V}]) -> - validate_enum(V, [false,rdbms,cassandra,mnesia]); -validate([<<"no_stanzaid_element">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{no_stanzaid_element, V}]) -> - validate_boolean(V); -validate([<<"archive_chat_markers">>, <<"pm">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{archive_chat_markers, V}]) -> - validate_boolean(V); -validate([<<"archive_groupchats">>, <<"pm">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{archive_groupchats, V}]) -> - validate_boolean(V); -validate([<<"async_writer">>, <<"pm">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{async_writer, V}]) -> - validate_boolean(V); -validate([<<"async_writer_rdbms_pool">>, <<"pm">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{async_writer_rdbms_pool, V}]) -> - validate_non_empty_atom(V); -validate([<<"backend">>, <<"pm">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{backend, V}]) -> - validate_enum(V, [rdbms,riak,cassandra,elasticsearch]); -validate([<<"cache_users">>, <<"pm">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{cache_users, V}]) -> - validate_boolean(V); -validate([<<"db_jid_format">>, <<"pm">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{db_jid_format, V}]) -> - validate_module(V); -validate([<<"db_message_format">>, <<"pm">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{db_message_format, V}]) -> - validate_module(V); -validate([<<"default_result_limit">>, <<"pm">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{default_result_limit, V}]) -> - validate_non_negative_integer(V); -validate([<<"extra_lookup_params">>, <<"pm">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{extra_lookup_params, V}]) -> - validate_module(V); -validate([<<"flush_interval">>, <<"pm">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{flush_interval, V}]) -> - validate_non_negative_integer(V); -validate([<<"full_text_search">>, <<"pm">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{full_text_search, V}]) -> - validate_boolean(V); -validate([<<"host">>, <<"pm">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{host, V}]) -> - validate_domain_template(V); -validate([<<"is_archivable_message">>, <<"pm">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{is_archivable_message, V}]) -> - validate_module(V); -validate([<<"max_batch_size">>, <<"pm">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{max_batch_size, V}]) -> - validate_non_negative_integer(V); -validate([<<"max_result_limit">>, <<"pm">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{max_result_limit, V}]) -> - validate_non_negative_integer(V); -validate([<<"message_retraction">>, <<"pm">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{message_retraction, V}]) -> - validate_boolean(V); -validate([<<"no_stanzaid_element">>, <<"pm">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{no_stanzaid_element, V}]) -> - validate_boolean(V); -validate([<<"rdbms_message_format">>, <<"pm">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{rdbms_message_format, V}]) -> - validate_enum(V, [simple,internal]); -validate([<<"simple">>, <<"pm">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{simple, V}]) -> - validate_boolean(V); -validate([<<"user_prefs_store">>, <<"pm">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{user_prefs_store, V}]) -> - validate_enum(V, [false,rdbms,cassandra,mnesia]); -validate([<<"rdbms_message_format">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{rdbms_message_format, V}]) -> - validate_enum(V, [simple,internal]); -validate([<<"bucket_type">>, <<"riak">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{bucket_type, V}]) -> - validate_non_empty_binary(V); -validate([<<"search_index">>, <<"riak">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{search_index, V}]) -> - validate_non_empty_binary(V); -validate([<<"simple">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{simple, V}]) -> - validate_boolean(V); -validate([<<"user_prefs_store">>, <<"mod_mam_meta">>, <<"modules">>|_], - [{user_prefs_store, V}]) -> - validate_enum(V, [false,rdbms,cassandra,mnesia]); validate([<<"listen_port">>, <<"mod_jingle_sip">>, <<"modules">>|_], [{listen_port, V}]) -> validate_network_port(V); diff --git a/src/mam/mod_mam_meta.erl b/src/mam/mod_mam_meta.erl index 2280a4c968..857a47d6e8 100644 --- a/src/mam/mod_mam_meta.erl +++ b/src/mam/mod_mam_meta.erl @@ -20,10 +20,13 @@ -type deps() :: #{module() => proplists:proplist()}. --export([start/2, stop/1, deps/2, get_mam_module_configuration/3, get_mam_module_opt/4]). +-export([start/2, stop/1, config_spec/0, + deps/2, get_mam_module_configuration/3, get_mam_module_opt/4]). -export([config_metrics/1]). +-include("mongoose_config_spec.hrl"). + %%-------------------------------------------------------------------- %% API %%-------------------------------------------------------------------- @@ -37,13 +40,78 @@ start(_Host, _Opts) -> stop(_Host) -> ok. +-spec config_spec() -> mongoose_config_spec:config_section(). +config_spec() -> + Items = config_items(), + #section{ + items = Items#{<<"pm">> => #section{items = maps:merge(Items, pm_config_items())}, + <<"muc">> => #section{items = maps:merge(Items, muc_config_items())}, + <<"riak">> => riak_config_spec()} + }. + +config_items() -> + #{%% General options + <<"backend">> => #option{type = atom, + validate = {enum, [rdbms, riak, cassandra, elasticsearch]}}, + <<"no_stanzaid_element">> => #option{type = boolean}, + <<"is_archivable_message">> => #option{type = atom, + validate = module}, + <<"archive_chat_markers">> => #option{type = boolean}, + <<"message_retraction">> => #option{type = boolean}, + + %% RDBMS-specific options + <<"cache_users">> => #option{type = boolean}, + <<"rdbms_message_format">> => #option{type = atom, + validate = {enum, [simple, internal]}}, + <<"async_writer">> => #option{type = boolean}, + <<"flush_interval">> => #option{type = integer, + validate = non_negative}, + <<"max_batch_size">> => #option{type = integer, + validate = non_negative}, + + %% Common backend options + <<"user_prefs_store">> => #option{type = atom, + validate = {enum, [rdbms, cassandra, mnesia]}}, + <<"full_text_search">> => #option{type = boolean}, + + %% Undocumented low-level options + <<"default_result_limit">> => #option{type = integer, + validate = non_negative}, + <<"max_result_limit">> => #option{type = integer, + validate = non_negative}, + <<"async_writer_rdbms_pool">> => #option{type = atom, + validate = pool_name}, + <<"db_jid_format">> => #option{type = atom, + validate = module}, + <<"db_message_format">> => #option{type = atom, + validate = module}, + <<"simple">> => #option{type = boolean}, + <<"extra_lookup_params">> => #option{type = atom, + validate = module} + }. + +pm_config_items() -> + #{<<"archive_groupchats">> => #option{type = boolean}}. + +muc_config_items() -> + #{<<"host">> => #option{type = string, + validate = domain_template}}. + +riak_config_spec() -> + #section{ + items = #{<<"search_index">> => #option{type = binary, + validate = non_empty}, + <<"bucket_type">> => #option{type = binary, + validate = non_empty}}, + format = none + }. -spec deps(_Host :: jid:server(), Opts :: proplists:proplist()) -> gen_mod:deps_list(). deps(_Host, Opts0) -> Opts = normalize(Opts0), - DepsWithPm = handle_nested_opts(pm, Opts, [], #{}), + DepsWithPm = handle_nested_opts(pm, Opts, false, #{}), DepsWithPmAndMuc = handle_nested_opts(muc, Opts, false, DepsWithPm), [{Dep, Args, hard} || {Dep, Args} <- maps:to_list(DepsWithPmAndMuc)]. diff --git a/test/mod_mam_meta_SUITE.erl b/test/mod_mam_meta_SUITE.erl index 07d28c4288..1499f080fd 100644 --- a/test/mod_mam_meta_SUITE.erl +++ b/test/mod_mam_meta_SUITE.erl @@ -7,8 +7,8 @@ all() -> [ overrides_general_options, sets_rdbms_as_default_backend, - assumes_pm_by_default, - handles_disabled_pm, + handles_only_pm, + handles_only_muc, disables_sync_writer_on_async_writer, disables_sync_muc_writer_on_async_writer, produces_valid_configurations, @@ -44,14 +44,16 @@ sets_rdbms_as_default_backend(_Config) -> ?assert(lists:keymember(mod_mam_rdbms_arch, 1, Deps)). -assumes_pm_by_default(_Config) -> - Deps = deps([]), - ?assert(lists:keymember(mod_mam, 1, Deps)). +handles_only_pm(_Config) -> + Deps = deps([{pm, []}]), + ?assert(lists:keymember(mod_mam, 1, Deps)), + ?assertNot(lists:keymember(mod_mam_muc, 1, Deps)). -handles_disabled_pm(_Config) -> - Deps = deps([{pm, false}, {muc, []}]), - ?assertNot(lists:keymember(mod_mam, 1, Deps)). +handles_only_muc(_Config) -> + Deps = deps([{muc, []}]), + ?assertNot(lists:keymember(mod_mam, 1, Deps)), + ?assert(lists:keymember(mod_mam_muc, 1, Deps)). disables_sync_writer_on_async_writer(_Config) -> @@ -61,7 +63,7 @@ disables_sync_writer_on_async_writer(_Config) -> disables_sync_muc_writer_on_async_writer(_Config) -> - Deps = deps([{pm, false}, {muc, [async_writer]}]), + Deps = deps([{muc, [async_writer]}]), {_, Args, _} = lists:keyfind(mod_mam_muc_rdbms_arch, 1, Deps), ?assert(lists:member(no_writer, Args)). @@ -133,7 +135,6 @@ example_muc_only_no_pref_good_performance(_Config) -> Deps = deps([ cache_users, async_writer, - {pm, false}, {muc, [{host, "muc.@HOST@"}]} ]), @@ -150,6 +151,7 @@ example_muc_only_no_pref_good_performance(_Config) -> example_pm_only_good_performance(_Config) -> Deps = deps([ + {pm, []}, cache_users, async_writer, {user_prefs_store, mnesia} @@ -186,8 +188,7 @@ meck_config() -> [{mod_mam, [here, is, some, config]}]; (modules, <<"meta_no_mod_mam_config">>) -> [{mod_mam_meta, [{backend, rdbms}, - {muc, []}, - {pm, false}]}]; + {muc, []}]}]; (modules, <<"meta_valid_mod_mam_config">>) -> [{mod_mam_meta, [{backend, rdbms}, cache_users, From 53202fcfe36aafce0312f5e4400153e9c1f9d8dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Thu, 17 Dec 2020 15:07:20 +0100 Subject: [PATCH 066/104] Update docs and tests for mod_mam_meta - Fix minor mistakes in docs - Simplify the tests and divide them by PM and MUC - Change some header levels not to render text in capital letters --- doc/modules/mod_mam.md | 12 +- test/config_parser_SUITE.erl | 207 ++++++++++++++--------------------- 2 files changed, 91 insertions(+), 128 deletions(-) diff --git a/doc/modules/mod_mam.md b/doc/modules/mod_mam.md index 7e1660842c..41767bb962 100644 --- a/doc/modules/mod_mam.md +++ b/doc/modules/mod_mam.md @@ -207,11 +207,11 @@ If the buffer is full, messages are flushed to a database immediately and the fl ### Common backend options #### `modules.mod_mam_meta.user_prefs_store` -* **Syntax:** one of `false`, `"rdbms"`, `"cassandra"`, `"mnesia"` -* **Default:** `false` -* **Example:** `modules.mod_mam_meta.user_prefs_store = 30` +* **Syntax:** one of `"rdbms"`, `"cassandra"`, `"mnesia"` +* **Default:** not set +* **Example:** `modules.mod_mam_meta.user_prefs_store = "rdbms"` -Leaving this option as `false` will prevent users from setting their archiving preferences. +Leaving this option unset will prevent users from setting their archiving preferences. It will also increase performance. The possible values are: @@ -258,14 +258,14 @@ This backend works with Riak KV 2.0 and above, but we recommend version 2.1.1. #### Riak-specific options -##### `modules.mod_mam_meta.riak.bucket_type` +#### `modules.mod_mam_meta.riak.bucket_type` * **Syntax:** non-empty string * **Default:** `"mam_yz"` * **Example:** `modules.mod_mam_meta.riak.bucket_type = "mam_yz"` Riak bucket type. -##### `modules.mod_mam_meta.riak.search_index` +#### `modules.mod_mam_meta.riak.search_index` * **Syntax:** non-empty string * **Default:** `"mam"` * **Example:** `modules.mod_mam_meta.riak.search_index = "mam"` diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index 5307a4f3e1..3a30319554 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -184,6 +184,8 @@ groups() -> mod_keystore, mod_last, mod_mam_meta, + mod_mam_meta_pm, + mod_mam_meta_muc, mod_muc, mod_muc_default_room, mod_muc_default_room_affiliations, @@ -1878,128 +1880,89 @@ mod_last(_Config) -> mod_mam_meta(_Config) -> T = fun(Opts) -> #{<<"modules">> => #{<<"mod_mam_meta">> => Opts}} end, - %% You can define options in mod_mam and they would work. - %% - %% We _could_ validate that `host' option does not exist in the PM - %% section, but it would just make everything harder. - %% - %% Same with `no_stanzaid_element' for PM. - Common = #{<<"archive_chat_markers">> => true, - <<"archive_groupchats">> => true, - <<"async_writer">> => true, - <<"async_writer_rdbms_pool">> => <<"poop">>, - <<"backend">> => <<"riak">>, - <<"cache_users">> => true, - <<"db_jid_format">> => <<"mam_jid_rfc">>, % module - <<"db_message_format">> => <<"mam_message_xml">>, % module - <<"default_result_limit">> => 50, - <<"extra_lookup_params">> => <<"mod_mam_utils">>, - <<"host">> => <<"conf.localhost">>, - <<"flush_interval">> => 500, - <<"full_text_search">> => true, - <<"max_batch_size">> => 50, - <<"max_result_limit">> => 50, - <<"message_retraction">> => true, - <<"rdbms_message_format">> => <<"simple">>, - <<"simple">> => true, - <<"no_stanzaid_element">> => true, - <<"is_archivable_message">> => <<"mod_mam_utils">>, - <<"user_prefs_store">> => false}, %% or rdbms. but not true - MCommon = [{archive_chat_markers, true}, - {archive_groupchats, true}, - {async_writer, true}, - {async_writer_rdbms_pool, poop}, - {backend, riak}, - {cache_users, true}, - {db_jid_format, mam_jid_rfc}, - {db_message_format, mam_message_xml}, - {default_result_limit, 50}, - {extra_lookup_params, mod_mam_utils}, - {flush_interval, 500}, - {full_text_search, true}, - %% While applied just for MUC, it could be specified as root option - %% Still, it probably should've been called muc_host from the - %% beginning - {host, "conf.localhost"}, - {is_archivable_message, mod_mam_utils}, - {max_batch_size, 50}, - {max_result_limit, 50}, - {message_retraction, true}, - {no_stanzaid_element, true}, - {rdbms_message_format, simple}, - {simple, true}, - {user_prefs_store, false}], - ensure_sorted(MCommon), - Riak = #{<<"bucket_type">> => <<"mam_yz">>, <<"search_index">> => <<"mam">>}, - MRiak = [{bucket_type, <<"mam_yz">>}, {search_index, <<"mam">>}], - Base = Common#{ - <<"pm">> => Common, - <<"muc">> => Common, - %% Separate section for riak. We don't need it in pm or in muc, - %% because there is no separate riak module for muc. - <<"riak">> => Riak}, - MBase0 = [{muc, MCommon}, - {pm, MCommon}] - ++ MRiak, %% This one is flatten into mim opts - MBase = pl_merge(MCommon, MBase0), - %% It's not easy to test riak options with check_one_opts function, - %% so skip it. - %% We also skip single muc/pm options on this step. - KeysForOneOpts = binaries_to_atoms(maps:keys(Common)), - TPM = fun(Map) -> T(#{<<"pm">> => Map}) end, - TMuc = fun(Map) -> T(#{<<"muc">> => Map}) end, - TB = fun(Map) -> T(maps:merge(Base, Map)) end, - %% by default parser adds pm and muc keys set to false - Hook = fun(Mim, Toml) -> {lists:sort([{pm, false}, {muc, false}|Mim]), Toml} end, - run_multi( - %% Test configurations with one option only - check_one_opts(mod_mam_meta, MBase, Base, T, KeysForOneOpts, Hook) ++ [ - ?_eqf(modopts(mod_mam_meta, [{muc, false}, {pm, false}]), T(#{})), - ?_eqf(modopts(mod_mam_meta, MBase), T(Base)), - %% Second format for user_prefs_store - ?_eqf(modopts(mod_mam_meta, pl_merge(MBase, [{user_prefs_store, rdbms}])), - T(Base#{<<"user_prefs_store">> => <<"rdbms">>})) - ] - ++ mam_failing_cases(T) - ++ mam_failing_cases(TPM) - ++ mam_failing_cases(TMuc) - ++ mam_failing_cases(TB) - ++ mam_failing_riak_cases(T) - ). - -mam_failing_cases(T) -> - [?_errf(T(#{<<"pm">> => false})), % should be a section - ?_errf(T(#{<<"muc">> => false})), % should be a section - ?_errf(T(#{<<"archive_chat_markers">> => 1})), - ?_errf(T(#{<<"archive_groupchats">> => 1})), - ?_errf(T(#{<<"async_writer">> => 1})), - ?_errf(T(#{<<"async_writer_rdbms_pool">> => 1})), - ?_errf(T(#{<<"backend">> => 1})), - ?_errf(T(#{<<"cache_users">> => 1})), - ?_errf(T(#{<<"db_jid_format">> => 1})), - ?_errf(T(#{<<"db_jid_format">> => <<"does_not_exist_mod">>})), - ?_errf(T(#{<<"db_message_format">> => 1})), - ?_errf(T(#{<<"db_message_format">> => <<"does_not_exist_mod">>})), - ?_errf(T(#{<<"default_result_limit">> => <<"meow">>})), - ?_errf(T(#{<<"default_result_limit">> => -20})), - ?_errf(T(#{<<"extra_lookup_params">> => -1})), - ?_errf(T(#{<<"extra_lookup_params">> => <<"aaaaaaa_not_exist">>})), - ?_errf(T(#{<<"host">> => <<"meow meow">>})), - ?_errf(T(#{<<"flush_interval">> => <<"meow">>})), - ?_errf(T(#{<<"flush_interval">> => -20})), - ?_errf(T(#{<<"full_text_search">> => -1})), - ?_errf(T(#{<<"max_batch_size">> => -1})), - ?_errf(T(#{<<"max_result_limit">> => -1})), - ?_errf(T(#{<<"message_retraction">> => -1})), - ?_errf(T(#{<<"rdbms_message_format">> => <<"verysimple">>})), - ?_errf(T(#{<<"simple">> => 1})), - ?_errf(T(#{<<"no_stanzaid_element">> => -1})), - ?_errf(T(#{<<"is_archivable_message">> => -1})), - ?_errf(T(#{<<"user_prefs_store">> => 1}))]. - -mam_failing_riak_cases(T) -> - [?_errf(T(#{<<"riak">> => #{<<"bucket_type">> => 1}})), - ?_errf(T(#{<<"riak">> => #{<<"search_index">> => 1}}))]. + M = fun(Cfg) -> modopts(mod_mam_meta, Cfg) end, + test_mod_mam_meta(T, M), + ?eqf(M([{bucket_type, <<"mam_bucket">>}]), + T(#{<<"riak">> => #{<<"bucket_type">> => <<"mam_bucket">>}})), + ?eqf(M([{search_index, <<"mam_index">>}]), + T(#{<<"riak">> => #{<<"search_index">> => <<"mam_index">>}})), + ?errf(T(#{<<"riak">> => #{<<"bucket_type">> => <<>>}})), + ?errf(T(#{<<"riak">> => #{<<"search_index">> => <<>>}})). + +mod_mam_meta_pm(_Config) -> + T = fun(Opts) -> #{<<"modules">> => #{<<"mod_mam_meta">> => #{<<"pm">> => Opts}}} end, + M = fun(Cfg) -> modopts(mod_mam_meta, [{pm, Cfg}]) end, + test_mod_mam_meta(T, M), + ?eqf(M([{archive_groupchats, true}]), + T(#{<<"archive_groupchats">> => true})), + ?errf(T(#{<<"archive_groupchats">> => <<"not really">>})). + +mod_mam_meta_muc(_Config) -> + T = fun(Opts) -> #{<<"modules">> => #{<<"mod_mam_meta">> => #{<<"muc">> => Opts}}} end, + M = fun(Cfg) -> modopts(mod_mam_meta, [{muc, Cfg}]) end, + test_mod_mam_meta(T, M), + ?eqf(M([{host, "muc.@HOST@"}]), + T(#{<<"host">> => <<"muc.@HOST@">>})), + ?errf(T(#{<<"host">> => <<"is this a host? no.">>})). + +test_mod_mam_meta(T, M) -> + ?eqf(M([{backend, rdbms}]), + T(#{<<"backend">> => <<"rdbms">>})), + ?eqf(M([{no_stanzaid_element, true}]), + T(#{<<"no_stanzaid_element">> => true})), + ?eqf(M([{is_archivable_message, mod_mam_utils}]), + T(#{<<"is_archivable_message">> => <<"mod_mam_utils">>})), + ?eqf(M([{archive_chat_markers, false}]), + T(#{<<"archive_chat_markers">> => false})), + ?eqf(M([{message_retraction, true}]), + T(#{<<"message_retraction">> => true})), + ?eqf(M([{cache_users, false}]), + T(#{<<"cache_users">> => false})), + ?eqf(M([{rdbms_message_format, simple}]), + T(#{<<"rdbms_message_format">> => <<"simple">>})), + ?eqf(M([{async_writer, true}]), + T(#{<<"async_writer">> => true})), + ?eqf(M([{flush_interval, 1500}]), + T(#{<<"flush_interval">> => 1500})), + ?eqf(M([{max_batch_size, 50}]), + T(#{<<"max_batch_size">> => 50})), + ?eqf(M([{user_prefs_store, rdbms}]), + T(#{<<"user_prefs_store">> => <<"rdbms">>})), + ?eqf(M([{full_text_search, false}]), + T(#{<<"full_text_search">> => false})), + ?eqf(M([{default_result_limit, 100}]), + T(#{<<"default_result_limit">> => 100})), + ?eqf(M([{max_result_limit, 1000}]), + T(#{<<"max_result_limit">> => 1000})), + ?eqf(M([{async_writer_rdbms_pool, async_pool}]), + T(#{<<"async_writer_rdbms_pool">> => <<"async_pool">>})), + ?eqf(M([{db_jid_format, mam_jid_rfc}]), + T(#{<<"db_jid_format">> => <<"mam_jid_rfc">>})), + ?eqf(M([{db_message_format, mam_message_xml}]), + T(#{<<"db_message_format">> => <<"mam_message_xml">>})), + ?eqf(M([{simple, false}]), + T(#{<<"simple">> => false})), + ?eqf(M([{extra_lookup_params, mod_mam_utils}]), + T(#{<<"extra_lookup_params">> => <<"mod_mam_utils">>})), + ?errf(T(#{<<"backend">> => <<"notepad">>})), + ?errf(T(#{<<"no_stanzaid_element">> => <<"true">>})), + ?errf(T(#{<<"is_archivable_message">> => <<"mod_mam_fake">>})), + ?errf(T(#{<<"archive_chat_markers">> => <<"maybe">>})), + ?errf(T(#{<<"message_retraction">> => 1})), + ?errf(T(#{<<"cache_users">> => []})), + ?errf(T(#{<<"rdbms_message_format">> => <<"complex">>})), + ?errf(T(#{<<"async_writer">> => #{}})), + ?errf(T(#{<<"flush_interval">> => -1})), + ?errf(T(#{<<"max_batch_size">> => -1})), + ?errf(T(#{<<"user_prefs_store">> => <<"textfile">>})), + ?errf(T(#{<<"full_text_search">> => <<"disabled">>})), + ?errf(T(#{<<"default_result_limit">> => -1})), + ?errf(T(#{<<"max_result_limit">> => -2})), + ?errf(T(#{<<"async_writer_rdbms_pool">> => <<>>})), + ?errf(T(#{<<"db_jid_format">> => <<"not_a_module">>})), + ?errf(T(#{<<"db_message_format">> => <<"not_a_module">>})), + ?errf(T(#{<<"simple">> => <<"yes">>})), + ?errf(T(#{<<"extra_lookup_params">> => <<"bad_module">>})). mod_muc(_Config) -> T = fun(Opts) -> #{<<"modules">> => #{<<"mod_muc">> => Opts}} end, From 49a0341972131f298bb34e2438738176bbe1471a Mon Sep 17 00:00:00 2001 From: Jan Ciesla Date: Mon, 14 Dec 2020 17:22:06 +0100 Subject: [PATCH 067/104] Make the config of mod_register declarative --- src/config/mongoose_config_parser_toml.erl | 34 +------------ src/config/mongoose_config_spec.erl | 3 +- src/config/mongoose_config_validator_toml.erl | 23 +-------- src/mod_register.erl | 50 ++++++++++++++++++- 4 files changed, 55 insertions(+), 55 deletions(-) diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index 20c4b8b390..7798c64750 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -164,17 +164,6 @@ module_opt([<<"ram_key_size">>, <<"mod_keystore">>|_], V) -> module_opt([<<"keys">>, <<"mod_keystore">>|_] = Path, V) -> Keys = parse_list(Path, V), [{keys, Keys}]; -module_opt([<<"access">>, <<"mod_register">>|_], V) -> - [{access, b2a(V)}]; -module_opt([<<"registration_watchers">>, <<"mod_register">>|_] = Path, V) -> - [{registration_watchers, parse_list(Path, V)}]; -module_opt([<<"password_strength">>, <<"mod_register">>|_], V) -> - [{password_strength, V}]; -module_opt([<<"ip_access">>, <<"mod_register">>|_] = Path, V) -> - Rules = parse_list(Path, V), - [{ip_access, Rules}]; -module_opt([<<"welcome_message">>, <<"mod_register">>|_] = Path, V) -> - parse_section(Path, V, fun process_welcome_message/1); module_opt([<<"routes">>, <<"mod_revproxy">>|_] = Path, V) -> Routes = parse_list(Path, V), [{routes, Routes}]; @@ -271,11 +260,6 @@ module_opt([<<"ldap_deref">>|_], V) -> module_opt([<<"riak">>|_] = Path, V) -> parse_section(Path, V). -process_welcome_message(Props) -> - Subject = proplists:get_value(subject, Props, ""), - Body = proplists:get_value(body, Props, ""), - [{welcome_message, {Subject, Body}}]. - %% path: (host_config[].)modules.*.riak.* -spec riak_opts(path(), toml_section()) -> [option()]. riak_opts([<<"defaults_bucket_type">>|_], V) -> @@ -289,10 +273,6 @@ riak_opts([<<"bucket_type">>|_], V) -> riak_opts([<<"search_index">>|_], V) -> [{search_index, V}]. --spec mod_register_ip_access_rule(path(), toml_section()) -> [option()]. -mod_register_ip_access_rule(_, #{<<"address">> := Addr, <<"policy">> := Policy}) -> - [{b2a(Policy), b2l(Addr)}]. - -spec mod_extdisco_service(path(), toml_value()) -> [option()]. mod_extdisco_service([_, <<"service">>|_] = Path, V) -> [parse_section(Path, V)]; @@ -443,11 +423,6 @@ iqdisc_value(Type, V) -> limit_keys([], V), Type. -welcome_message([<<"subject">>|_], Value) -> - [{subject, b2l(Value)}]; -welcome_message([<<"body">>|_], Value) -> - [{body, b2l(Value)}]. - %% path: host_config[] -spec process_host_item(path(), toml_section()) -> config_list(). process_host_item(Path, M) -> @@ -711,7 +686,8 @@ node_to_string(Node) -> [binary_to_list(Node)]. Mod =/= <<"mod_privacy">>, Mod =/= <<"mod_private">>, Mod =/= <<"mod_pubsub">>, - Mod =/= <<"mod_push_service_mongoosepush">>). % TODO temporary, remove with 'handler/1' + Mod =/= <<"mod_push_service_mongoosepush">>, + Mod =/= <<"mod_register">>). % TODO temporary, remove with 'handler/1' -spec handler(path()) -> fun((path(), toml_value()) -> option()) | mongoose_config_spec:config_node(). @@ -723,12 +699,6 @@ handler([Mod, <<"modules">>]) when ?HAS_NO_SPEC(Mod) -> fun process_module/2; handler([_, Mod, <<"modules">>]) when ?HAS_NO_SPEC(Mod) -> fun module_opt/2; handler([_, <<"riak">>, Mod, <<"modules">>]) when ?HAS_NO_SPEC(Mod) -> fun riak_opts/2; -handler([_, <<"ip_access">>, <<"mod_register">>, <<"modules">>]) -> - fun mod_register_ip_access_rule/2; -handler([_, <<"registration_watchers">>, <<"mod_register">>, <<"modules">>]) -> - fun(_, V) -> [V] end; -handler([_, <<"welcome_message">>, <<"mod_register">>, <<"modules">>]) -> - fun welcome_message/2; handler([_, <<"service">>, <<"mod_extdisco">>, <<"modules">>]) -> fun mod_extdisco_service/2; handler([_, _, <<"service">>, <<"mod_extdisco">>, <<"modules">>]) -> diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index eaf3470d76..99b3907508 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -747,7 +747,8 @@ all_modules() -> mod_privacy, mod_private, mod_pubsub, - mod_push_service_mongoosepush]. + mod_push_service_mongoosepush, + mod_register]. %% path: (host_config[].)modules.*.iqdisc iqdisc() -> diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index fabd623062..46f85e2e1b 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -211,27 +211,6 @@ validate([<<"iqdisc">>, <<"mod_version">>, <<"modules">>|_], validate([<<"os_info">>, <<"mod_version">>, <<"modules">>|_], [{os_info, V}]) -> validate_boolean(V); -validate([<<"access">>, <<"mod_register">>, <<"modules">>|_], - [{access, V}]) -> - validate_non_empty_atom(V); -validate([item, <<"ip_access">>, <<"mod_register">>, <<"modules">>|_], - [V]) -> - validate_ip_access(V); -validate([<<"iqdisc">>, <<"mod_register">>, <<"modules">>|_], - [{iqdisc, V}]) -> - validate_iqdisc(V); -validate([<<"password_strength">>, <<"mod_register">>, <<"modules">>|_], - [{password_strength, V}]) -> - validate_non_negative_integer(V); -validate([item, <<"registration_watchers">>, <<"mod_register">>, <<"modules">>|_], - [V]) -> - validate_jid(V); -validate([<<"body">>, <<"welcome_message">>, <<"mod_register">>, <<"modules">>|_], - [{body, V}]) -> - validate_string(V); -validate([<<"subject">>, <<"welcome_message">>, <<"mod_register">>, <<"modules">>|_], - [{subject, V}]) -> - validate_string(V); validate([<<"type">>, _, <<"service">>, <<"mod_extdisco">>, <<"modules">>|_], [{type, V}]) -> validate_non_empty_atom(V); @@ -414,6 +393,7 @@ validate(V, binary, domain) -> validate_binary_domain(V); validate(V, binary, non_empty) -> validate_non_empty_binary(V); validate(V, binary, {module, Prefix}) -> validate_module(list_to_atom(atom_to_list(Prefix) ++ "_" ++ binary_to_list(V))); +validate(V, binary, jid) -> validate_jid(V); validate(V, integer, non_negative) -> validate_non_negative_integer(V); validate(V, integer, positive) -> validate_positive_integer(V); validate(V, integer, port) -> validate_port(V); @@ -422,6 +402,7 @@ validate(V, int_or_infinity, positive) -> validate_positive_integer_or_infinity( validate(V, string, url) -> validate_url(V); validate(V, string, domain_template) -> validate_domain_template(V); validate(V, string, ip_address) -> validate_ip_address(V); +validate(V, string, ip_mask) -> validate_ip_mask_string(V); validate(V, string, non_empty) -> validate_non_empty_string(V); validate(V, string, dirname) -> validate_dirname(V); validate(V, atom, module) -> validate_module(V); diff --git a/src/mod_register.erl b/src/mod_register.erl index 456a74dea2..656b469a59 100644 --- a/src/mod_register.erl +++ b/src/mod_register.erl @@ -31,14 +31,18 @@ -export([start/2, stop/1, + config_spec/0, clean_opts/1, stream_feature_register/2, unauthenticated_iq_register/4, try_register/5, - process_iq/4]). + process_iq/4, + process_ip_access/1, + process_welcome_message/1]). -include("mongoose.hrl"). -include("jlib.hrl"). +-include("mongoose_config_spec.hrl"). start(Host, Opts) -> IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue), @@ -65,6 +69,50 @@ stop(Host) -> gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_REGISTER), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_REGISTER). +-spec config_spec() -> mongoose_config_spec:config_section(). +config_spec() -> + #section{ + items = #{<<"iqdisc">> => mongoose_config_spec:iqdisc(), + <<"access">> => #option{type = atom, + validate = non_empty}, + <<"welcome_message">> => welcome_message_spec(), + <<"registration_watchers">> => #list{items = #option{type = binary, + validate = jid}}, + <<"password_strength">> => #option{type = integer, + validate = non_negative}, + <<"ip_access">> => #list{items = ip_access_spec()} + } + }. + +welcome_message_spec() -> + #section{ + items = #{<<"body">> => #option{type = string, + validate = non_empty}, + <<"subject">> => #option{type = string, + validate = non_empty}}, + process = fun ?MODULE:process_welcome_message/1 + }. + +ip_access_spec() -> + #section{ + items = #{<<"address">> => #option{type = string, + validate = ip_mask}, + <<"policy">> => #option{type = atom, + validate = {enum, [allow, deny]}} + }, + required = all, + process = fun ?MODULE:process_ip_access/1 + }. + +process_ip_access(KVs) -> + {[[{address, Address}], [{policy, Policy}]], []} = proplists:split(KVs, [address, policy]), + {Policy, Address}. + +process_welcome_message(KVs) -> + Body = proplists:get_value(body, KVs, ""), + Subject = proplists:get_value(subject, KVs, ""), + {Subject, Body}. + clean_opts(Opts) -> lists:map(fun clean_opt/1, Opts). From 67bb911e2081356bb0c5bb54548cc7221ecf0f9b Mon Sep 17 00:00:00 2001 From: Jan Ciesla Date: Mon, 14 Dec 2020 18:46:31 +0100 Subject: [PATCH 068/104] Make the config of mod_roster declarative --- src/config/mongoose_config_parser_toml.erl | 7 +-- src/config/mongoose_config_spec.erl | 3 +- src/config/mongoose_config_validator_toml.erl | 18 -------- src/mod_roster.erl | 22 ++++++++++ test/config_parser_SUITE.erl | 43 +++++++++---------- 5 files changed, 47 insertions(+), 46 deletions(-) diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index 7798c64750..f41f6bca6b 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -167,10 +167,6 @@ module_opt([<<"keys">>, <<"mod_keystore">>|_] = Path, V) -> module_opt([<<"routes">>, <<"mod_revproxy">>|_] = Path, V) -> Routes = parse_list(Path, V), [{routes, Routes}]; -module_opt([<<"versioning">>, <<"mod_roster">>|_], V) -> - [{versioning, V}]; -module_opt([<<"store_current_id">>, <<"mod_roster">>|_], V) -> - [{store_current_id, V}]; module_opt([<<"ldap_useruid">>, <<"mod_shared_roster_ldap">>|_], V) -> [{ldap_useruid, b2l(V)}]; module_opt([<<"ldap_groupattr">>, <<"mod_shared_roster_ldap">>|_], V) -> @@ -687,7 +683,8 @@ node_to_string(Node) -> [binary_to_list(Node)]. Mod =/= <<"mod_private">>, Mod =/= <<"mod_pubsub">>, Mod =/= <<"mod_push_service_mongoosepush">>, - Mod =/= <<"mod_register">>). % TODO temporary, remove with 'handler/1' + Mod =/= <<"mod_register">>, + Mod =/= <<"mod_roster">>). % TODO temporary, remove with 'handler/1' -spec handler(path()) -> fun((path(), toml_value()) -> option()) | mongoose_config_spec:config_node(). diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index 99b3907508..d1b325b00a 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -748,7 +748,8 @@ all_modules() -> mod_private, mod_pubsub, mod_push_service_mongoosepush, - mod_register]. + mod_register, + mod_roster]. %% path: (host_config[].)modules.*.iqdisc iqdisc() -> diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index 46f85e2e1b..afbe57859e 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -295,24 +295,6 @@ validate([<<"sdp_origin">>, <<"mod_jingle_sip">>, <<"modules">>|_], validate([<<"iqdisc">>, <<"mod_sic">>, <<"modules">>|_], [{iqdisc, V}]) -> validate_iqdisc(V); -validate([<<"backend">>, <<"mod_roster">>, <<"modules">>|_], - [{backend, V}]) -> - validate_backend(mod_roster, V); -validate([<<"iqdisc">>, <<"mod_roster">>, <<"modules">>|_], - [{iqdisc, V}]) -> - validate_iqdisc(V); -validate([<<"bucket_type">>, <<"riak">>, <<"mod_roster">>, <<"modules">>|_], - [{bucket_type, V}]) -> - validate_non_empty_binary(V); -validate([<<"version_bucket_type">>, <<"riak">>, <<"mod_roster">>, <<"modules">>|_], - [{version_bucket_type, V}]) -> - validate_non_empty_binary(V); -validate([<<"store_current_id">>, <<"mod_roster">>, <<"modules">>|_], - [{store_current_id, V}]) -> - validate_boolean(V); -validate([<<"versioning">>, <<"mod_roster">>, <<"modules">>|_], - [{versioning, V}]) -> - validate_boolean(V); validate([item, <<"keys">>, <<"mod_keystore">>, <<"modules">>|_], [V]) -> validate_keystore_key(V); diff --git a/src/mod_roster.erl b/src/mod_roster.erl index ff8a9f9f91..c2e9965110 100644 --- a/src/mod_roster.erl +++ b/src/mod_roster.erl @@ -42,6 +42,7 @@ -export([start/2, stop/1, + config_spec/0, process_iq/4, process_local_iq/4, get_user_roster/2, @@ -75,6 +76,7 @@ -include("mongoose.hrl"). -include("jlib.hrl"). -include("mod_roster.hrl"). +-include("mongoose_config_spec.hrl"). -export_type([roster/0, sub_presence/0]). @@ -212,6 +214,26 @@ stop(Host) -> ejabberd_hooks:delete(hooks(Host)), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_ROSTER). +-spec config_spec() -> mongoose_config_spec:config_section(). +config_spec() -> + #section{ + items = #{<<"iqdisc">> => mongoose_config_spec:iqdisc(), + <<"versioning">> => #option{type = boolean}, + <<"store_current_id">> => #option{type = boolean}, + <<"backend">> => #option{type = atom, + validate = {module, mod_roster}}, + <<"riak">> => riak_config_spec() + } + }. + +riak_config_spec() -> + #section{items = #{<<"bucket_type">> => #option{type = binary, + validate = non_empty}, + <<"version_bucket_type">> => #option{type = binary, + validate = non_empty}}, + format = none + }. + hooks(Host) -> [{roster_get, Host, ?MODULE, get_user_roster, 50}, {roster_in_subscription, Host, ?MODULE, in_subscription, 50}, diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index 3a30319554..744d3f6919 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -2520,29 +2520,28 @@ mod_revproxy(_Config) -> ]). mod_roster(_Config) -> - Riak = #{<<"bucket_type">> => <<"rosters">>, - <<"version_bucket_type">> => <<"roster_versions">>}, - Base = #{<<"iqdisc">> => #{<<"type">> => <<"one_queue">>}, - <<"versioning">> => false, - <<"store_current_id">> => false, - <<"backend">> => <<"mnesia">>, - <<"riak">> => Riak}, - MBase = [{iqdisc, one_queue}, - {versioning, false}, - {store_current_id, false}, - {backend, mnesia}, - {bucket_type, <<"rosters">>}, - {version_bucket_type, <<"roster_versions">>}], T = fun(Opts) -> #{<<"modules">> => #{<<"mod_roster">> => Opts}} end, - run_multi([ - ?_eqf(modopts(mod_roster, lists:sort(MBase)), T(Base)), - ?_errf(T(#{<<"versioning">> => 1})), - ?_errf(T(#{<<"store_current_id">> => 1})), - ?_errf(T(#{<<"backend">> => 1})), - ?_errf(T(#{<<"backend">> => <<"iloveyou">>})), - ?_errf(T(#{<<"riak">> => #{<<"version_bucket_type">> => 1}})), - ?_errf(T(#{<<"riak">> => #{<<"bucket_type">> => 1}})) - ]), + M = fun(Cfg) -> modopts(mod_roster, Cfg) end, + + ?eqf(M([{iqdisc, one_queue}]), + T(#{<<"iqdisc">> => #{<<"type">> => <<"one_queue">>}})), + ?eqf(M([{store_current_id, false}]), + T(#{<<"store_current_id">> => false})), + ?eqf(M([{versioning, false}]), + T(#{<<"versioning">> => false})), + ?eqf(M([{backend, mnesia}]), + T(#{<<"backend">> => <<"mnesia">>})), + ?eqf(M([{bucket_type, <<"rosters">>}]), + T(#{<<"riak">> => #{<<"bucket_type">> => <<"rosters">>}})), + ?eqf(M([{version_bucket_type, <<"roster_versions">>}]), + T(#{<<"riak">> => #{<<"version_bucket_type">> => <<"roster_versions">>}})), + + ?errf(T(#{<<"versioning">> => 1})), + ?errf(T(#{<<"store_current_id">> => 1})), + ?errf(T(#{<<"backend">> => 1})), + ?errf(T(#{<<"backend">> => <<"iloveyou">>})), + ?errf(T(#{<<"riak">> => #{<<"version_bucket_type">> => 1}})), + ?errf(T(#{<<"riak">> => #{<<"bucket_type">> => 1}})), check_iqdisc(mod_roster). mod_shared_roster_ldap(_Config) -> From 72c12571536f2af767be1d58d039f03f74696434 Mon Sep 17 00:00:00 2001 From: Jan Ciesla Date: Tue, 15 Dec 2020 16:28:12 +0100 Subject: [PATCH 069/104] Make the config of mod_shared_roster_ldap declarative --- doc/modules/mod_shared_roster_ldap.md | 14 +- src/config/mongoose_config_parser_toml.erl | 35 +---- src/config/mongoose_config_spec.erl | 3 +- src/config/mongoose_config_validator_toml.erl | 57 -------- src/mod_shared_roster_ldap.erl | 38 ++++- test/config_parser_SUITE.erl | 137 ++++++++---------- 6 files changed, 109 insertions(+), 175 deletions(-) diff --git a/doc/modules/mod_shared_roster_ldap.md b/doc/modules/mod_shared_roster_ldap.md index a47cda3210..377dfb933f 100644 --- a/doc/modules/mod_shared_roster_ldap.md +++ b/doc/modules/mod_shared_roster_ldap.md @@ -1,9 +1,9 @@ ## Module Description -This module, when enabled, will inject roster entries fetched from LDAP. -It might get quite complicated to configure it properly, so fasten your seatbelts and prepare for a ride. +This module, when enabled, will inject roster entries fetched from LDAP. +It might get quite complicated to configure it properly, so fasten your seatbelts and prepare for a ride. -When a default value for an option is defined with "top-level/XXX", it means that the default value is equal to a top-level parameter in `mongooseim.toml` of the same name. +When a default value for an option is defined with "top-level/XXX", it means that the default value is equal to a top-level parameter in `mongooseim.toml` of the same name. If it is not defined, XXX becomes the default value. ## Options: general @@ -75,28 +75,28 @@ Allows extracting the user ID with a regular expression. Enables checking if a shared roster entry actually exists in the XMPP database. ### `modules.mod_shared_roster_ldap.ldap_user_cache_validity` -* **Syntax:** non-negative integer +* **Syntax:** positive integer * **Default:** top-level/`300` * **Example:** `ldap_user_cache_validity = 300` Specifies in seconds how long are the roster entries kept in the cache. ### `modules.mod_shared_roster_ldap.ldap_group_cache_validity` -* **Syntax:** non-negative integer +* **Syntax:** positive integer * **Default:** top-level/`300` * **Example:** `ldap_group_cache_validity = 300` Specifies in seconds how long is the user's membership in a group kept in the cache. ### `modules.mod_shared_roster_ldap.ldap_user_cache_size` -* **Syntax:** non-negative integer +* **Syntax:** positive integer * **Default:** top-level/`1000` * **Example:** `ldap_user_cache_size = 1000` Specifies how many shared roster items are kept in the cache. ### `modules.mod_shared_roster_ldap.ldap_group_cache_size` -* **Syntax:** non-negative integer +* **Syntax:** positive integer * **Default:** top-level/`1000` * **Example:** `ldap_group_cache_size = 1000` diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index f41f6bca6b..2a20580a2e 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -167,38 +167,6 @@ module_opt([<<"keys">>, <<"mod_keystore">>|_] = Path, V) -> module_opt([<<"routes">>, <<"mod_revproxy">>|_] = Path, V) -> Routes = parse_list(Path, V), [{routes, Routes}]; -module_opt([<<"ldap_useruid">>, <<"mod_shared_roster_ldap">>|_], V) -> - [{ldap_useruid, b2l(V)}]; -module_opt([<<"ldap_groupattr">>, <<"mod_shared_roster_ldap">>|_], V) -> - [{ldap_groupattr, b2l(V)}]; -module_opt([<<"ldap_groupdesc">>, <<"mod_shared_roster_ldap">>|_], V) -> - [{ldap_groupdesc, b2l(V)}]; -module_opt([<<"ldap_userdesc">>, <<"mod_shared_roster_ldap">>|_], V) -> - [{ldap_userdesc, b2l(V)}]; -module_opt([<<"ldap_userid">>, <<"mod_shared_roster_ldap">>|_], V) -> - [{ldap_userid, b2l(V)}]; -module_opt([<<"ldap_memberattr">>, <<"mod_shared_roster_ldap">>|_], V) -> - [{ldap_memberattr, b2l(V)}]; -module_opt([<<"ldap_memberattr_format">>, <<"mod_shared_roster_ldap">>|_], V) -> - [{ldap_memberattr_format, b2l(V)}]; -module_opt([<<"ldap_memberattr_format_re">>, <<"mod_shared_roster_ldap">>|_], V) -> - [{ldap_memberattr_format_re, b2l(V)}]; -module_opt([<<"ldap_auth_check">>, <<"mod_shared_roster_ldap">>|_], V) -> - [{ldap_auth_check, V}]; -module_opt([<<"ldap_user_cache_validity">>, <<"mod_shared_roster_ldap">>|_], V) -> - [{ldap_user_cache_validity, V}]; -module_opt([<<"ldap_group_cache_validity">>, <<"mod_shared_roster_ldap">>|_], V) -> - [{ldap_group_cache_validity, V}]; -module_opt([<<"ldap_user_cache_size">>, <<"mod_shared_roster_ldap">>|_], V) -> - [{ldap_user_cache_size, V}]; -module_opt([<<"ldap_group_cache_size">>, <<"mod_shared_roster_ldap">>|_], V) -> - [{ldap_group_cache_size, V}]; -module_opt([<<"ldap_rfilter">>, <<"mod_shared_roster_ldap">>|_], V) -> - [{ldap_rfilter, b2l(V)}]; -module_opt([<<"ldap_gfilter">>, <<"mod_shared_roster_ldap">>|_], V) -> - [{ldap_gfilter, b2l(V)}]; -module_opt([<<"ldap_ufilter">>, <<"mod_shared_roster_ldap">>|_], V) -> - [{ldap_ufilter, b2l(V)}]; module_opt([<<"buffer_max">>, <<"mod_stream_management">>|_], <<"no_buffer">>) -> [{buffer_max, no_buffer}]; module_opt([<<"buffer_max">>, <<"mod_stream_management">>|_], V) -> @@ -684,7 +652,8 @@ node_to_string(Node) -> [binary_to_list(Node)]. Mod =/= <<"mod_pubsub">>, Mod =/= <<"mod_push_service_mongoosepush">>, Mod =/= <<"mod_register">>, - Mod =/= <<"mod_roster">>). % TODO temporary, remove with 'handler/1' + Mod =/= <<"mod_roster">>, + Mod =/= <<"mod_shared_roster_ldap">>). % TODO temporary, remove with 'handler/1' -spec handler(path()) -> fun((path(), toml_value()) -> option()) | mongoose_config_spec:config_node(). diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index d1b325b00a..1981366bbd 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -749,7 +749,8 @@ all_modules() -> mod_pubsub, mod_push_service_mongoosepush, mod_register, - mod_roster]. + mod_roster, + mod_shared_roster_ldap]. %% path: (host_config[].)modules.*.iqdisc iqdisc() -> diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index afbe57859e..273cedd223 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -148,63 +148,6 @@ validate([<<"geriatric">>, <<"stale_h">>, <<"mod_stream_management">>, <<"module validate([<<"repeat_after">>, <<"stale_h">>, <<"mod_stream_management">>, <<"modules">>|_], [{stale_h_repeat_after, V}]) -> validate_positive_integer(V); -validate([<<"ldap_auth_check">>, <<"mod_shared_roster_ldap">>, <<"modules">>|_], - [{ldap_auth_check, V}]) -> - validate_boolean(V); -validate([<<"ldap_base">>, <<"mod_shared_roster_ldap">>, <<"modules">>|_], - [{ldap_base, V}]) -> - validate_string(V); -validate([<<"ldap_deref">>, <<"mod_shared_roster_ldap">>, <<"modules">>|_], - [{ldap_deref, V}]) -> - validate_enum(V, [never,always,finding,searching]); -validate([<<"ldap_filter">>, <<"mod_shared_roster_ldap">>, <<"modules">>|_], - [{ldap_filter, V}]) -> - validate_string(V); -validate([<<"ldap_gfilter">>, <<"mod_shared_roster_ldap">>, <<"modules">>|_], - [{ldap_gfilter, V}]) -> - validate_string(V); -validate([<<"ldap_group_cache_size">>, <<"mod_shared_roster_ldap">>, <<"modules">>|_], - [{ldap_group_cache_size, V}]) -> - validate_non_negative_integer(V); -validate([<<"ldap_group_cache_validity">>, <<"mod_shared_roster_ldap">>, <<"modules">>|_], - [{ldap_group_cache_validity, V}]) -> - validate_non_negative_integer(V); -validate([<<"ldap_groupattr">>, <<"mod_shared_roster_ldap">>, <<"modules">>|_], - [{ldap_groupattr, V}]) -> - validate_string(V); -validate([<<"ldap_groupdesc">>, <<"mod_shared_roster_ldap">>, <<"modules">>|_], - [{ldap_groupdesc, V}]) -> - validate_string(V); -validate([<<"ldap_memberattr">>, <<"mod_shared_roster_ldap">>, <<"modules">>|_], - [{ldap_memberattr, V}]) -> - validate_string(V); -validate([<<"ldap_memberattr_format">>, <<"mod_shared_roster_ldap">>, <<"modules">>|_], - [{ldap_memberattr_format, V}]) -> - validate_string(V); -validate([<<"ldap_memberattr_format_re">>, <<"mod_shared_roster_ldap">>, <<"modules">>|_], - [{ldap_memberattr_format_re, V}]) -> - validate_string(V); -validate([<<"ldap_pool_tag">>, <<"mod_shared_roster_ldap">>, <<"modules">>|_], - [{ldap_pool_tag, V}]) -> - validate_pool_name(V); -validate([<<"ldap_rfilter">>, <<"mod_shared_roster_ldap">>, <<"modules">>|_], - [{ldap_rfilter, V}]) -> - validate_string(V); -validate([<<"ldap_ufilter">>, <<"mod_shared_roster_ldap">>, <<"modules">>|_], - [{ldap_ufilter, V}]) -> - validate_string(V); -validate([<<"ldap_user_cache_size">>, <<"mod_shared_roster_ldap">>, <<"modules">>|_], - [{ldap_user_cache_size, V}]) -> - validate_non_negative_integer(V); -validate([<<"ldap_user_cache_validity">>, <<"mod_shared_roster_ldap">>, <<"modules">>|_], - [{ldap_user_cache_validity, V}]) -> - validate_non_negative_integer(V); -validate([<<"ldap_userdesc">>, <<"mod_shared_roster_ldap">>, <<"modules">>|_], - [{ldap_userdesc, V}]) -> - validate_string(V); -validate([<<"ldap_useruid">>, <<"mod_shared_roster_ldap">>, <<"modules">>|_], - [{ldap_useruid, V}]) -> - validate_string(V); validate([<<"iqdisc">>, <<"mod_version">>, <<"modules">>|_], [{iqdisc, V}]) -> validate_iqdisc(V); diff --git a/src/mod_shared_roster_ldap.erl b/src/mod_shared_roster_ldap.erl index 60a3fa2936..5ddae48acb 100644 --- a/src/mod_shared_roster_ldap.erl +++ b/src/mod_shared_roster_ldap.erl @@ -33,7 +33,7 @@ -behaviour(mongoose_module_metrics). %% API --export([start_link/2, start/2, stop/1]). +-export([start_link/2, start/2, stop/1, config_spec/0]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, @@ -48,6 +48,7 @@ -include("mongoose.hrl"). -include("jlib.hrl"). -include("mod_roster.hrl"). +-include("mongoose_config_spec.hrl"). -include("eldap.hrl"). @@ -104,6 +105,37 @@ stop(Host) -> Proc = gen_mod:get_module_proc(Host, ?MODULE), ejabberd_sup:stop_child(Proc). +-spec config_spec() -> mongoose_config_spec:config_section(). +config_spec() -> + #section{ + items = #{<<"ldap_pool_tag">> => #option{type = atom, + validate = pool_name}, + <<"ldap_base">> => #option{type = string}, + <<"ldap_deref">> => #option{type = atom, + validate = {enum, [never, always, finding, searching]}}, + <<"ldap_groupattr">> => #option{type = string}, + <<"ldap_groupdesc">> => #option{type = string}, + <<"ldap_userdesc">> => #option{type = string}, + <<"ldap_useruid">> => #option{type = string}, + <<"ldap_memberattr">> => #option{type = string}, + <<"ldap_memberattr_format">> => #option{type = string}, + <<"ldap_memberattr_format_re">> => #option{type = string}, + <<"ldap_auth_check">> => #option{type = boolean}, + <<"ldap_user_cache_validity">> => #option{type = integer, + validate = positive}, + <<"ldap_group_cache_validity">> => #option{type = integer, + validate = positive}, + <<"ldap_user_cache_size">> => #option{type = integer, + validate = positive}, + <<"ldap_group_cache_size">> => #option{type = integer, + validate = positive}, + <<"ldap_rfilter">> => #option{type = string}, + <<"ldap_gfilter">> => #option{type = string}, + <<"ldap_ufilter">> => #option{type = string}, + <<"ldap_filter">> => #option{type = string} + } + }. + %%-------------------------------------------------------------------- %% Hooks %%-------------------------------------------------------------------- @@ -525,9 +557,7 @@ parse_options(Host, Opts) -> MP end, <<"">>), AuthCheck = eldap_utils:get_mod_opt(ldap_auth_check, Opts, - fun(on) -> true; - (off) -> false; - (false) -> false; + fun(false) -> false; (true) -> true end, true), UserCacheValidity = eldap_utils:get_mod_opt( diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index 744d3f6919..fee303eac2 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -2546,79 +2546,70 @@ mod_roster(_Config) -> mod_shared_roster_ldap(_Config) -> T = fun(Opts) -> #{<<"modules">> => #{<<"mod_shared_roster_ldap">> => Opts}} end, - MBase = [{ldap_pool_tag, default}, - {ldap_base, "string"}, - {ldap_deref, never}, - %% Options: attributes - {ldap_groupattr, "cn"}, - {ldap_groupdesc, "default"}, - {ldap_userdesc, "cn"}, - {ldap_useruid, "cn"}, - {ldap_memberattr, "memberUid"}, - {ldap_memberattr_format, "%u"}, - {ldap_memberattr_format_re,""}, - %% Options: parameters - {ldap_auth_check, true}, - {ldap_user_cache_validity, 300}, - {ldap_group_cache_validity, 300}, - {ldap_user_cache_size, 300}, - {ldap_group_cache_size, 300}, - %% Options: LDAP filters - {ldap_rfilter, "test"}, - {ldap_gfilter, "test"}, - {ldap_ufilter, "test"}, - {ldap_filter, "test"} - ], - Base = #{ - <<"ldap_pool_tag">> => <<"default">>, - <<"ldap_base">> => <<"string">>, - <<"ldap_deref">> => <<"never">>, - %% Options: attributes - <<"ldap_groupattr">> => <<"cn">>, - <<"ldap_groupdesc">> => <<"default">>, - <<"ldap_userdesc">> => <<"cn">>, - <<"ldap_useruid">> => <<"cn">>, - <<"ldap_memberattr">> => <<"memberUid">>, - <<"ldap_memberattr_format">> => <<"%u">>, - <<"ldap_memberattr_format_re">> => <<"">>, - %% Options: parameters - <<"ldap_auth_check">> => true, - <<"ldap_user_cache_validity">> => 300, - <<"ldap_group_cache_validity">> => 300, - <<"ldap_user_cache_size">> => 300, - <<"ldap_group_cache_size">> => 300, - %% Options: LDAP filters - <<"ldap_rfilter">> => <<"test">>, - <<"ldap_gfilter">> => <<"test">>, - <<"ldap_ufilter">> => <<"test">>, - <<"ldap_filter">> => <<"test">> - }, - run_multi( - check_one_opts(mod_shared_roster_ldap, MBase, Base, T) ++ [ - ?_eqf(modopts(mod_shared_roster_ldap, lists:sort(MBase)), T(Base)), - ?_errf(T(#{<<"ldap_pool_tag">> => 1})), - ?_errf(T(#{<<"ldap_base">> => 1})), - ?_errf(T(#{<<"ldap_deref">> => 1})), - %% Options: attributes - ?_errf(T(#{<<"ldap_groupattr">> => 1})), - ?_errf(T(#{<<"ldap_groupdesc">> => 1})), - ?_errf(T(#{<<"ldap_userdesc">> => 1})), - ?_errf(T(#{<<"ldap_useruid">> => 1})), - ?_errf(T(#{<<"ldap_memberattr">> => 1})), - ?_errf(T(#{<<"ldap_memberattr_format">> => 1})), - ?_errf(T(#{<<"ldap_memberattr_format_re">> => 1})), - %% Options: parameters - ?_errf(T(#{<<"ldap_auth_check">> => 1})), - ?_errf(T(#{<<"ldap_user_cache_validity">> => -1})), - ?_errf(T(#{<<"ldap_group_cache_validity">> => -1})), - ?_errf(T(#{<<"ldap_user_cache_size">> => -1})), - ?_errf(T(#{<<"ldap_group_cache_size">> => -1})), - %% Options: LDAP filters - ?_errf(T(#{<<"ldap_rfilter">> => 1})), - ?_errf(T(#{<<"ldap_gfilter">> => 1})), - ?_errf(T(#{<<"ldap_ufilter">> => 1})), - ?_errf(T(#{<<"ldap_filter">> => 1})) - ]). + M = fun(Cfg) -> modopts(mod_shared_roster_ldap, Cfg) end, + ?eqf(M([{ldap_pool_tag, default}]), + T(#{<<"ldap_pool_tag">> => <<"default">>})), + ?eqf(M([{ldap_base, "string"}]), + T(#{<<"ldap_base">> => <<"string">>})), + ?eqf(M([{ldap_deref, never}]), + T(#{<<"ldap_deref">> => <<"never">>})), + %% Options: attributes + ?eqf(M([ {ldap_groupattr, "cn"}]), + T(#{<<"ldap_groupattr">> => <<"cn">>})), + ?eqf(M([{ldap_groupdesc, "default"}]), + T(#{<<"ldap_groupdesc">> => <<"default">>})), + ?eqf(M([{ldap_userdesc, "cn"}]), + T(#{<<"ldap_userdesc">> => <<"cn">>})), + ?eqf(M([{ldap_useruid, "cn"}]), + T(#{<<"ldap_useruid">> => <<"cn">>})), + ?eqf(M([{ldap_memberattr, "memberUid"}]), + T(#{<<"ldap_memberattr">> => <<"memberUid">>})), + ?eqf(M([{ldap_memberattr_format, "%u"}]), + T(#{<<"ldap_memberattr_format">> => <<"%u">>})), + ?eqf(M([{ldap_memberattr_format_re,""}]), + T(#{<<"ldap_memberattr_format_re">> => <<"">>})), + %% Options: parameters + ?eqf(M([ {ldap_auth_check, true}]), + T(#{<<"ldap_auth_check">> => true})), + ?eqf(M([{ldap_user_cache_validity, 300}]), + T(#{<<"ldap_user_cache_validity">> => 300})), + ?eqf(M([{ldap_group_cache_validity, 300}]), + T(#{<<"ldap_group_cache_validity">> => 300})), + ?eqf(M([{ldap_user_cache_size, 300}]), + T(#{<<"ldap_user_cache_size">> => 300})), + ?eqf(M([{ldap_group_cache_size, 300}]), + T(#{<<"ldap_group_cache_size">> => 300})), + %% Options: LDAP filters + ?eqf(M([{ldap_rfilter, "rfilter_test"}]), + T(#{<<"ldap_rfilter">> => <<"rfilter_test">>})), + ?eqf(M([{ldap_gfilter, "gfilter_test"}]), + T(#{<<"ldap_gfilter">> => <<"gfilter_test">>})), + ?eqf(M([{ldap_ufilter, "ufilter_test"}]), + T(#{<<"ldap_ufilter">> => <<"ufilter_test">>})), + ?eqf(M([{ldap_filter, "filter_test"}]), + T(#{<<"ldap_filter">> => <<"filter_test">>})), + ?errf(T(#{<<"ldap_pool_tag">> => 1})), + ?errf(T(#{<<"ldap_base">> => 1})), + ?errf(T(#{<<"ldap_deref">> => 1})), + %% Options: attributes + ?errf(T(#{<<"ldap_groupattr">> => 1})), + ?errf(T(#{<<"ldap_groupdesc">> => 1})), + ?errf(T(#{<<"ldap_userdesc">> => 1})), + ?errf(T(#{<<"ldap_useruid">> => 1})), + ?errf(T(#{<<"ldap_memberattr">> => 1})), + ?errf(T(#{<<"ldap_memberattr_format">> => 1})), + ?errf(T(#{<<"ldap_memberattr_format_re">> => 1})), + %% Options: parameters + ?errf(T(#{<<"ldap_auth_check">> => 1})), + ?errf(T(#{<<"ldap_user_cache_validity">> => -1})), + ?errf(T(#{<<"ldap_group_cache_validity">> => -1})), + ?errf(T(#{<<"ldap_user_cache_size">> => -1})), + ?errf(T(#{<<"ldap_group_cache_size">> => -1})), + %% Options: LDAP filters + ?errf(T(#{<<"ldap_rfilter">> => 1})), + ?errf(T(#{<<"ldap_gfilter">> => 1})), + ?errf(T(#{<<"ldap_ufilter">> => 1})), + ?errf(T(#{<<"ldap_filter">> => 1})). mod_sic(_Config) -> check_iqdisc(mod_sic). From fb4e71b4d5b665800c73ce483e0cf5d45832977b Mon Sep 17 00:00:00 2001 From: Jan Ciesla Date: Tue, 15 Dec 2020 21:39:51 +0100 Subject: [PATCH 070/104] Make the config of mod_stream_management declarative --- src/config/mongoose_config_parser_toml.erl | 29 ++---------- src/config/mongoose_config_spec.erl | 3 +- src/config/mongoose_config_validator_toml.erl | 22 ++------- src/mod_stream_management.erl | 29 +++++++++++- test/config_parser_SUITE.erl | 46 ++++++++----------- 5 files changed, 59 insertions(+), 70 deletions(-) diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index 2a20580a2e..43b7b6edf6 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -167,19 +167,6 @@ module_opt([<<"keys">>, <<"mod_keystore">>|_] = Path, V) -> module_opt([<<"routes">>, <<"mod_revproxy">>|_] = Path, V) -> Routes = parse_list(Path, V), [{routes, Routes}]; -module_opt([<<"buffer_max">>, <<"mod_stream_management">>|_], <<"no_buffer">>) -> - [{buffer_max, no_buffer}]; -module_opt([<<"buffer_max">>, <<"mod_stream_management">>|_], V) -> - [{buffer_max, int_or_infinity(V)}]; -module_opt([<<"ack_freq">>, <<"mod_stream_management">>|_], <<"never">>) -> - [{ack_freq, never}]; -module_opt([<<"ack_freq">>, <<"mod_stream_management">>|_], V) -> - [{ack_freq, V}]; -module_opt([<<"resume_timeout">>, <<"mod_stream_management">>|_], V) -> - [{resume_timeout, V}]; -module_opt([<<"stale_h">>, <<"mod_stream_management">>|_] = Path, V) -> - Stale = parse_section(Path, V), - [{stale_h, Stale}]; module_opt([<<"host">>, <<"mod_vcard">>|_], V) -> [{host, b2l(V)}]; module_opt([<<"search">>, <<"mod_vcard">>|_], V) -> @@ -347,14 +334,6 @@ mod_revproxy_routes(_, #{<<"host">> := Host, <<"path">> := Path, <<"method">> := mod_revproxy_routes(_, #{<<"host">> := Host, <<"path">> := Path, <<"upstream">> := Upstream}) -> [{b2l(Host), b2l(Path), b2l(Upstream)}]. --spec mod_stream_management_stale_h(path(), toml_value()) -> [option()]. -mod_stream_management_stale_h([<<"enabled">>|_], V) -> - [{enabled, V}]; -mod_stream_management_stale_h([<<"repeat_after">>|_], V) -> - [{stale_h_repeat_after, V}]; -mod_stream_management_stale_h([<<"geriatric">>|_], V) -> - [{stale_h_geriatric, V}]. - -spec mod_vcard_ldap_uids(path(), toml_section()) -> [option()]. mod_vcard_ldap_uids(_, #{<<"attr">> := Attr, <<"format">> := Format}) -> [{b2l(Attr), b2l(Format)}]; @@ -539,6 +518,9 @@ convert(V, string) -> binary_to_list(V); convert(V, atom) -> b2a(V); convert(<<"infinity">>, int_or_infinity) -> infinity; %% TODO maybe use TOML '+inf' convert(V, int_or_infinity) when is_integer(V) -> V; +convert(<<"infinity">>, int_or_infinity_or_atom) -> infinity; +convert(<<"no_buffer">>, int_or_infinity_or_atom) -> no_buffer; +convert(V, int_or_infinity_or_atom) when is_integer(V) -> V; convert(V, int_or_atom) when is_integer(V) -> V; convert(V, int_or_atom) -> b2a(V); convert(V, integer) when is_integer(V) -> V; @@ -653,7 +635,8 @@ node_to_string(Node) -> [binary_to_list(Node)]. Mod =/= <<"mod_push_service_mongoosepush">>, Mod =/= <<"mod_register">>, Mod =/= <<"mod_roster">>, - Mod =/= <<"mod_shared_roster_ldap">>). % TODO temporary, remove with 'handler/1' + Mod =/= <<"mod_shared_roster_ldap">>, + Mod =/= <<"mod_stream_management">>). % TODO temporary, remove with 'handler/1' -spec handler(path()) -> fun((path(), toml_value()) -> option()) | mongoose_config_spec:config_node(). @@ -693,8 +676,6 @@ handler([_, <<"keys">>, <<"mod_keystore">>, <<"modules">>]) -> fun mod_keystore_keys/2; handler([_, <<"routes">>, <<"mod_revproxy">>, <<"modules">>]) -> fun mod_revproxy_routes/2; -handler([_, <<"stale_h">>, <<"mod_stream_management">>, <<"modules">>]) -> - fun mod_stream_management_stale_h/2; handler([_, <<"ldap_uids">>, <<"mod_vcard">>, <<"modules">>]) -> fun mod_vcard_ldap_uids/2; handler([_, <<"ldap_vcard_map">>, <<"mod_vcard">>, <<"modules">>]) -> diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index 1981366bbd..9d26c2549d 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -750,7 +750,8 @@ all_modules() -> mod_push_service_mongoosepush, mod_register, mod_roster, - mod_shared_roster_ldap]. + mod_shared_roster_ldap, + mod_stream_management]. %% path: (host_config[].)modules.*.iqdisc iqdisc() -> diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index 273cedd223..f58658279e 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -130,24 +130,6 @@ validate([<<"pool">>, <<"redis">>, <<"mod_global_distrib">>, <<"modules">>|_], validate([<<"refresh_after">>, <<"redis">>, <<"mod_global_distrib">>, <<"modules">>|_], [{refresh_after, V}]) -> validate_non_negative_integer(V); -validate([<<"ack_freq">>, <<"mod_stream_management">>, <<"modules">>|_], - [{ack_freq, V}]) -> - validate_positive_integer_or_atom(V, never); -validate([<<"buffer_max">>, <<"mod_stream_management">>, <<"modules">>|_], - [{buffer_max, V}]) -> - validate_positive_integer_or_infinity_or_atom(V, no_buffer); -validate([<<"resume_timeout">>, <<"mod_stream_management">>, <<"modules">>|_], - [{resume_timeout, V}]) -> - validate_positive_integer(V); -validate([<<"enabled">>, <<"stale_h">>, <<"mod_stream_management">>, <<"modules">>|_], - [{enabled, V}]) -> - validate_boolean(V); -validate([<<"geriatric">>, <<"stale_h">>, <<"mod_stream_management">>, <<"modules">>|_], - [{stale_h_geriatric, V}]) -> - validate_positive_integer(V); -validate([<<"repeat_after">>, <<"stale_h">>, <<"mod_stream_management">>, <<"modules">>|_], - [{stale_h_repeat_after, V}]) -> - validate_positive_integer(V); validate([<<"iqdisc">>, <<"mod_version">>, <<"modules">>|_], [{iqdisc, V}]) -> validate_iqdisc(V); @@ -324,6 +306,10 @@ validate(V, integer, positive) -> validate_positive_integer(V); validate(V, integer, port) -> validate_port(V); validate(V, int_or_infinity, non_negative) -> validate_non_negative_integer_or_infinity(V); validate(V, int_or_infinity, positive) -> validate_positive_integer_or_infinity(V); +validate(V, int_or_infinity_or_atom, positive) -> + validate_positive_integer_or_infinity_or_atom(V, no_buffer); +validate(V, int_or_atom, positive) -> + validate_positive_integer_or_atom(V, never); validate(V, string, url) -> validate_url(V); validate(V, string, domain_template) -> validate_domain_template(V); validate(V, string, ip_address) -> validate_ip_address(V); diff --git a/src/mod_stream_management.erl b/src/mod_stream_management.erl index e868a5fba9..7ea6177202 100644 --- a/src/mod_stream_management.erl +++ b/src/mod_stream_management.erl @@ -5,7 +5,8 @@ %% `gen_mod' callbacks -export([start/2, - stop/1]). + stop/1, + config_spec/0]). %% `ejabberd_hooks' handlers -export([add_sm_feature/2, @@ -40,6 +41,7 @@ -include("mongoose.hrl"). -include("jlib.hrl"). +-include("mongoose_config_spec.hrl"). -record(sm_session, {smid :: smid(), @@ -73,6 +75,31 @@ stop(Host) -> true -> stream_management_stale_h:stop() end. +-spec config_spec() -> mongoose_config_spec:config_section(). +config_spec() -> + #section{ + items = #{<<"buffer_max">> => #option{type = int_or_infinity_or_atom, + validate = positive}, + <<"ack_freq">> => #option{type = int_or_atom, + validate = positive}, + <<"resume_timeout">> => #option{type = integer, + validate = positive}, + <<"stale_h">> => stale_h_config_spec() + } + }. + +stale_h_config_spec() -> + #section{ + items = #{<<"enabled">> => #option{type = boolean}, + <<"repeat_after">> => #option{type = integer, + validate = positive, + format = {kv, stale_h_repeat_after}}, + <<"geriatric">> => #option{type = integer, + validate = positive, + format = {kv, stale_h_geriatric}} + } + }. + %% %% `ejabberd_hooks' handlers %% diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index fee303eac2..b90865220c 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -207,6 +207,7 @@ groups() -> mod_shared_roster_ldap, mod_sic, mod_stream_management, + mod_stream_management_stale_h, mod_time, mod_vcard, mod_version]}, @@ -2616,33 +2617,26 @@ mod_sic(_Config) -> mod_stream_management(_Config) -> T = fun(Opts) -> #{<<"modules">> => #{<<"mod_stream_management">> => Opts}} end, - Base = #{ - <<"buffer_max">> => 100, - <<"ack_freq">> => 1, - <<"resume_timeout">> => 600, - <<"stale_h">> => #{<<"enabled">> => true, - <<"repeat_after">> => 1800, - <<"geriatric">> => 3600} - }, - MBase = [ - {buffer_max, 100}, - {ack_freq, 1}, - {resume_timeout, 600}, - {stale_h, [{enabled, true}, - {stale_h_geriatric, 3600}, - {stale_h_repeat_after, 1800}]} - ], - ?eqf(modopts(mod_stream_management, lists:sort(MBase)), T(Base)), - ?eqf(modopts(mod_stream_management, [{buffer_max, no_buffer}]), - T(#{<<"buffer_max">> => <<"no_buffer">>})), + M = fun(Cfg) -> modopts(mod_stream_management, Cfg) end, + ?eqf(M([{buffer_max, no_buffer}]), T(#{<<"buffer_max">> => <<"no_buffer">>})), + ?eqf(M([{ack_freq, 1}]), T(#{<<"ack_freq">> => 1})), + ?eqf(M([{resume_timeout, 600}]), T(#{<<"resume_timeout">> => 600})), + ?errf(T(#{<<"buffer_max">> => -1})), - ?errf(T(#{<<"ack_freq">> => -1})), - ?errf(T(#{<<"resume_timeout">> => -1})), - ?errf(T(#{<<"stale_h">> => #{<<"enabled">> => <<"true">>}})), - ?errf(T(#{<<"stale_h">> => #{<<"enabled">> => 1}})), - ?errf(T(#{<<"stale_h">> => #{<<"repeat_after">> => -1}})), - ?errf(T(#{<<"stale_h">> => #{<<"geriatric">> => -1}})), - ok. + ?errf(T(#{<<"ack_freq">> => <<"one">>})), + ?errf(T(#{<<"resume_timeout">> => true})). + +mod_stream_management_stale_h(_Config) -> + T = fun(Opts) -> #{<<"modules">> => + #{<<"mod_stream_management">> => #{<<"stale_h">> => Opts}}} end, + M = fun(Cfg) -> modopts(mod_stream_management, [{stale_h, Cfg}]) end, + ?eqf(M([{enabled, true}]), T(#{<<"enabled">> => true})), + ?eqf(M([{stale_h_repeat_after, 1800}]), T(#{<<"repeat_after">> => 1800})), + ?eqf(M([{stale_h_geriatric, 3600}]), T(#{<<"geriatric">> => 3600})), + + ?errf(T(#{<<"enabled">> => <<"true">>})), + ?errf(T(#{<<"repeat_after">> => -1})), + ?errf(T(#{<<"geriatric">> => <<"one">>})). mod_time(_Config) -> check_iqdisc(mod_time). From bd60a20eb199b517124259ad01d89c674218f8c3 Mon Sep 17 00:00:00 2001 From: Jan Ciesla Date: Thu, 17 Dec 2020 16:03:33 +0100 Subject: [PATCH 071/104] Make the config of mod_vcard declarative --- src/config/mongoose_config_parser_toml.erl | 60 +----- src/config/mongoose_config_spec.erl | 3 +- src/config/mongoose_config_validator_toml.erl | 74 ------- src/mod_vcard.erl | 116 +++++++++++ test/config_parser_SUITE.erl | 190 ++++++++++-------- 5 files changed, 226 insertions(+), 217 deletions(-) diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index 43b7b6edf6..c0ed7600c0 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -167,29 +167,6 @@ module_opt([<<"keys">>, <<"mod_keystore">>|_] = Path, V) -> module_opt([<<"routes">>, <<"mod_revproxy">>|_] = Path, V) -> Routes = parse_list(Path, V), [{routes, Routes}]; -module_opt([<<"host">>, <<"mod_vcard">>|_], V) -> - [{host, b2l(V)}]; -module_opt([<<"search">>, <<"mod_vcard">>|_], V) -> - [{search, V}]; -module_opt([<<"matches">>, <<"mod_vcard">>|_], V) -> - [{matches, int_or_infinity(V)}]; -module_opt([<<"ldap_vcard_map">>, <<"mod_vcard">>|_] = Path, V) -> - Maps = parse_list(Path, V), - [{ldap_vcard_map, Maps}]; -module_opt([<<"ldap_uids">>, <<"mod_vcard">>|_] = Path, V) -> - List = parse_list(Path, V), - [{ldap_uids, List}]; -module_opt([<<"ldap_search_fields">>, <<"mod_vcard">>|_] = Path, V) -> - Fields = parse_list(Path, V), - [{ldap_search_fields, Fields}]; -module_opt([<<"ldap_search_reported">>, <<"mod_vcard">>|_] = Path, V) -> - Reported = parse_list(Path, V), - [{ldap_search_reported, Reported}]; -module_opt([<<"ldap_search_operator">>, <<"mod_vcard">>|_], V) -> - [{ldap_search_operator, b2a(V)}]; -module_opt([<<"ldap_binary_search_fields">>, <<"mod_vcard">>|_] = Path, V) -> - List = parse_list(Path, V), - [{ldap_binary_search_fields, List}]; module_opt([<<"os_info">>, <<"mod_version">>|_], V) -> [{os_info, V}]; % General options @@ -334,30 +311,6 @@ mod_revproxy_routes(_, #{<<"host">> := Host, <<"path">> := Path, <<"method">> := mod_revproxy_routes(_, #{<<"host">> := Host, <<"path">> := Path, <<"upstream">> := Upstream}) -> [{b2l(Host), b2l(Path), b2l(Upstream)}]. --spec mod_vcard_ldap_uids(path(), toml_section()) -> [option()]. -mod_vcard_ldap_uids(_, #{<<"attr">> := Attr, <<"format">> := Format}) -> - [{b2l(Attr), b2l(Format)}]; -mod_vcard_ldap_uids(_, #{<<"attr">> := Attr}) -> - [b2l(Attr)]. - - --spec mod_vcard_ldap_vcard_map(path(), toml_section()) -> [option()]. -mod_vcard_ldap_vcard_map(_, #{<<"vcard_field">> := VF, <<"ldap_pattern">> := LP, - <<"ldap_field">> := LF}) -> - [{VF, LP, [LF]}]. - --spec mod_vcard_ldap_search_fields(path(), toml_section()) -> [option()]. -mod_vcard_ldap_search_fields(_, #{<<"search_field">> := SF, <<"ldap_field">> := LF}) -> - [{SF, LF}]. - --spec mod_vcard_ldap_search_reported(path(), toml_section()) -> [option()]. -mod_vcard_ldap_search_reported(_, #{<<"search_field">> := SF, <<"vcard_field">> := VF}) -> - [{SF, VF}]. - --spec mod_vcard_ldap_binary_search_fields(path(), toml_section()) -> [option()]. -mod_vcard_ldap_binary_search_fields(_, V) -> - [V]. - -spec iqdisc_value(atom(), toml_section()) -> option(). iqdisc_value(queues, #{<<"workers">> := Workers} = V) -> limit_keys([<<"workers">>], V), @@ -636,7 +589,8 @@ node_to_string(Node) -> [binary_to_list(Node)]. Mod =/= <<"mod_register">>, Mod =/= <<"mod_roster">>, Mod =/= <<"mod_shared_roster_ldap">>, - Mod =/= <<"mod_stream_management">>). % TODO temporary, remove with 'handler/1' + Mod =/= <<"mod_stream_management">>, + Mod =/= <<"mod_vcard">>). % TODO temporary, remove with 'handler/1' -spec handler(path()) -> fun((path(), toml_value()) -> option()) | mongoose_config_spec:config_node(). @@ -676,16 +630,6 @@ handler([_, <<"keys">>, <<"mod_keystore">>, <<"modules">>]) -> fun mod_keystore_keys/2; handler([_, <<"routes">>, <<"mod_revproxy">>, <<"modules">>]) -> fun mod_revproxy_routes/2; -handler([_, <<"ldap_uids">>, <<"mod_vcard">>, <<"modules">>]) -> - fun mod_vcard_ldap_uids/2; -handler([_, <<"ldap_vcard_map">>, <<"mod_vcard">>, <<"modules">>]) -> - fun mod_vcard_ldap_vcard_map/2; -handler([_, <<"ldap_search_fields">>, <<"mod_vcard">>, <<"modules">>]) -> - fun mod_vcard_ldap_search_fields/2; -handler([_, <<"ldap_search_reported">>, <<"mod_vcard">>, <<"modules">>]) -> - fun mod_vcard_ldap_search_reported/2; -handler([_, <<"ldap_binary_search_fields">>, <<"mod_vcard">>, <<"modules">>]) -> - fun mod_vcard_ldap_binary_search_fields/2; %% host_config handler([_, <<"host_config">>]) -> fun process_host_item/2; diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index 9d26c2549d..b5aa32a173 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -751,7 +751,8 @@ all_modules() -> mod_register, mod_roster, mod_shared_roster_ldap, - mod_stream_management]. + mod_stream_management, + mod_vcard]. %% path: (host_config[].)modules.*.iqdisc iqdisc() -> diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index f58658279e..87158d46e4 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -226,57 +226,6 @@ validate([item, <<"keys">>, <<"mod_keystore">>, <<"modules">>|_], validate([<<"ram_key_size">>, <<"mod_keystore">>, <<"modules">>|_], [{ram_key_size, V}]) -> validate_non_negative_integer(V); -validate([<<"backend">>, <<"mod_vcard">>, <<"modules">>|_], - [{backend, V}]) -> - validate_backend(mod_vcard, V); -validate([<<"host">>, <<"mod_vcard">>, <<"modules">>|_], - [{host, V}]) -> - validate_domain_template(V); -validate([<<"iqdisc">>, <<"mod_vcard">>, <<"modules">>|_], - [{iqdisc, V}]) -> - validate_iqdisc(V); -validate([<<"ldap_base">>, <<"mod_vcard">>, <<"modules">>|_], - [{ldap_base, V}]) -> - validate_string(V); -validate([item, <<"ldap_binary_search_fields">>, <<"mod_vcard">>, <<"modules">>|_], - [V]) -> - validate_non_empty_binary(V); -validate([<<"ldap_deref">>, <<"mod_vcard">>, <<"modules">>|_], - [{ldap_deref, V}]) -> - validate_enum(V, [never,always,finding,searching]); -validate([<<"ldap_filter">>, <<"mod_vcard">>, <<"modules">>|_], - [{ldap_filter, V}]) -> - validate_string(V); -validate([<<"ldap_pool_tag">>, <<"mod_vcard">>, <<"modules">>|_], - [{ldap_pool_tag, V}]) -> - validate_pool_name(V); -validate([item, <<"ldap_search_fields">>, <<"mod_vcard">>, <<"modules">>|_], - [V]) -> - validate_ldap_search_field(V); -validate([<<"ldap_search_operator">>, <<"mod_vcard">>, <<"modules">>|_], - [{ldap_search_operator, V}]) -> - validate_enum(V, ['or','and']); -validate([item, <<"ldap_search_reported">>, <<"mod_vcard">>, <<"modules">>|_], - [V]) -> - validate_ldap_search_reported(V); -validate([item, <<"ldap_uids">>, <<"mod_vcard">>, <<"modules">>|_], - [V]) -> - validate_ldap_uids(V); -validate([item, <<"ldap_vcard_map">>, <<"mod_vcard">>, <<"modules">>|_], - [V]) -> - validate_ldap_vcard_map(V); -validate([<<"matches">>, <<"mod_vcard">>, <<"modules">>|_], - [{matches, V}]) -> - validate_non_negative_integer_or_infinity(V); -validate([<<"bucket_type">>, <<"riak">>, <<"mod_vcard">>, <<"modules">>|_], - [{bucket_type, V}]) -> - validate_non_empty_binary(V); -validate([<<"search_index">>, <<"riak">>, <<"mod_vcard">>, <<"modules">>|_], - [{search_index, V}]) -> - validate_non_empty_binary(V); -validate([<<"search">>, <<"mod_vcard">>, <<"modules">>|_], - [{search, V}]) -> - validate_boolean(V); validate([<<"aff_changes">>, <<"mod_inbox">>, <<"modules">>|_], [{aff_changes, V}]) -> validate_boolean(V); @@ -407,10 +356,6 @@ validate_iqdisc(one_queue) -> ok; validate_iqdisc(parallel) -> ok; validate_iqdisc({queues, N}) when is_integer(N), N > 0 -> ok. -validate_ip_access({Access, IPMask}) -> - validate_enum(Access, [allow, deny]), - validate_ip_mask_string(IPMask). - validate_backend(Mod, Backend) -> validate_module(backend_module:backend_module(Mod, Backend)). @@ -534,24 +479,5 @@ validate_revproxy_route({Host, Path, Upstream}) -> validate_string(Path), validate_non_empty_string(Upstream). -validate_ldap_vcard_map({VCardField, LDAPPattern, LDAPFields}) -> - validate_non_empty_binary(VCardField), - validate_non_empty_binary(LDAPPattern), - lists:foreach(fun validate_non_empty_binary/1, LDAPFields). - -validate_ldap_search_field({SearchField, LDAPField}) -> - validate_non_empty_binary(SearchField), - validate_non_empty_binary(LDAPField). - -validate_ldap_search_reported({SearchField, VCardField}) -> - validate_non_empty_binary(SearchField), - validate_non_empty_binary(VCardField). - -validate_ldap_uids({Attribute, Format}) -> - validate_non_empty_string(Attribute), - validate_non_empty_string(Format); -validate_ldap_uids(Attribute) -> - validate_non_empty_string(Attribute). - validate_pool_name(V) -> validate_non_empty_atom(V). diff --git a/src/mod_vcard.erl b/src/mod_vcard.erl index 1029f881ba..158b758f40 100644 --- a/src/mod_vcard.erl +++ b/src/mod_vcard.erl @@ -42,10 +42,18 @@ -include("jlib.hrl"). -include("mod_vcard.hrl"). -include("mongoose_rsm.hrl"). +-include("mongoose_config_spec.hrl"). %% gen_mod handlers -export([start/2, stop/1]). +%% config_spec +-export([config_spec/0, + process_map_spec/1, + process_search_spec/1, + process_search_reported_spec/1, + process_ldap_uids/1]). + %% gen_server handlers -export([init/1, handle_info/2, @@ -196,6 +204,114 @@ stop(VHost) -> gen_server:call(Proc, stop), ejabberd_sup:stop_child(Proc). +%%-------------------------------------------------------------------- +%% config_spec +%%-------------------------------------------------------------------- + +-spec config_spec() -> mongoose_config_spec:config_section(). +config_spec() -> + #section{ + items = #{<<"iqdisc">> => mongoose_config_spec:iqdisc(), + <<"host">> => #option{type = string, + validate = non_empty}, + <<"search">> => #option{type = boolean}, + <<"backend">> => #option{type = atom, + validate = {module, mod_vcard}}, + <<"matches">> => #option{type = int_or_infinity, + validate = non_negative}, + <<"ldap_pool_tag">> => #option{type = atom, + validate = non_empty}, + <<"ldap_base">> => #option{type = string}, + <<"ldap_uids">> => #list{items = ldap_uids_spec()}, + <<"ldap_filter">> => #option{type = string}, + <<"ldap_deref">> => #option{type = atom, + validate = {enum, [never, always, finding, searching]}}, + <<"ldap_vcard_map">> => #list{items = ldap_vcard_map_spec()}, + <<"ldap_search_fields">> => #list{items = ldap_search_fields_spec()}, + <<"ldap_search_reported">> => #list{items = ldap_search_reported_spec()}, + <<"ldap_search_operator">> => #option{type = atom, + validate = {enum, ['or', 'and']}}, + <<"ldap_binary_search_fields">> => #list{items = #option{type = binary, + validate = non_empty}}, + <<"riak">> => riak_config_spec() + } + }. + +ldap_uids_spec() -> + #section{ + items = #{<<"attr">> => #option{type = string}, + <<"format">> => #option{type = string}}, + process = fun ?MODULE:process_ldap_uids/1, + required = [<<"attr">>] +}. + +process_ldap_uids(KVs) -> + {[AttrOpts, FormatOpts], []} = proplists:split(KVs, [attr, format]), + case {AttrOpts, FormatOpts} of + {[{attr, Attr}], []} -> Attr; + {[{attr, Attr}], [{format, Format}]} -> {Attr, Format} + end. + +ldap_vcard_map_spec() -> + #section{ + items = #{<<"vcard_field">> => #option{type = binary, + validate = non_empty}, + <<"ldap_pattern">> => #option{type = binary, + validate = non_empty}, + <<"ldap_field">> => #option{type = binary, + validate = non_empty} + }, + required = all, + process = fun ?MODULE:process_map_spec/1 + }. + +ldap_search_fields_spec() -> + #section{ + items = #{<<"search_field">> => #option{type = binary, + validate = non_empty}, + <<"ldap_field">> => #option{type = binary, + validate = non_empty} + }, + required = all, + process = fun ?MODULE:process_search_spec/1 + }. + +ldap_search_reported_spec() -> + #section{ + items = #{<<"search_field">> => #option{type = binary, + validate = non_empty}, + <<"vcard_field">> => #option{type = binary, + validate = non_empty} + }, + required = all, + process = fun ?MODULE:process_search_reported_spec/1 + }. + +riak_config_spec() -> + #section{ + items = #{<<"bucket_type">> => #option{type = binary, + validate = non_empty}, + <<"search_index">> => #option{type = binary, + validate = non_empty} + }, + format = none + }. + +process_map_spec(KVs) -> + {[[{vcard_field, VF}], [{ldap_pattern, LP}], [{ldap_field, LF}]], []} = + proplists:split(KVs, [vcard_field, ldap_pattern, ldap_field]), + {VF, LP, [LF]}. + +process_search_spec(KVs) -> + {[[{search_field, SF}], [{ldap_field, LF}]], []} = + proplists:split(KVs, [search_field, ldap_field]), + {SF, LF}. + +process_search_reported_spec(KVs) -> + {[[{search_field, SF}], [{vcard_field, VF}]], []} = + proplists:split(KVs, [search_field, vcard_field]), + {SF, VF}. + %%-------------------------------------------------------------------- %% mongoose_packet_handler callbacks %%-------------------------------------------------------------------- diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index b90865220c..0fc2aaab4d 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -210,6 +210,10 @@ groups() -> mod_stream_management_stale_h, mod_time, mod_vcard, + mod_vcard_ldap_uids, + mod_vcard_ldap_vcard_map, + mod_vcard_ldap_search_fields, + mod_vcard_ldap_search_reported, mod_version]}, {services, [parallel], [service_admin_extra, service_mongoose_system_metrics]} @@ -2642,91 +2646,109 @@ mod_time(_Config) -> check_iqdisc(mod_time). mod_vcard(_Config) -> + check_iqdisc(mod_vcard), T = fun(Opts) -> #{<<"modules">> => #{<<"mod_vcard">> => Opts}} end, - MBase = [{iqdisc, one_queue}, - {host, "vjud.@HOST@"}, - {search, true}, - {backend, mnesia}, - {matches, infinity}, - %% ldap - {ldap_pool_tag, default}, - {ldap_base, "ou=Users,dc=ejd,dc=com"}, - {ldap_deref, never}, - {ldap_uids, [{"mail", "%u@mail.example.org"}, "name"]}, - {ldap_filter, "(&(objectClass=shadowAccount)(memberOf=Jabber Users))"}, - %% MIM accepts {"FAMILY", "%s", ["sn", "cn"]} form too - {ldap_vcard_map, [{<<"FAMILY">>, <<"%s">>, [<<"sn">>]}]}, %% iolists - {ldap_search_fields, [{<<"Full Name">>, <<"cn">>}]}, %% pair of iolists - {ldap_search_reported, [{<<"Full Name">>, <<"FN">>}]}, %% iolists - {ldap_search_operator, 'or'}, - {ldap_binary_search_fields, [<<"PHOTO">>]}, - %% riak - {bucket_type, <<"vcard">>}, - {search_index, <<"vcard">>} - ], - Riak = #{<<"bucket_type">> => <<"vcard">>, - <<"search_index">> => <<"vcard">>}, - Base = #{ - <<"iqdisc">> => #{<<"type">> => <<"one_queue">>}, - <<"host">> => <<"vjud.@HOST@">>, - <<"search">> => true, - <<"backend">> => <<"mnesia">>, - <<"matches">> => <<"infinity">>, - %% ldap - <<"ldap_pool_tag">> => <<"default">>, - <<"ldap_base">> => <<"ou=Users,dc=ejd,dc=com">>, - <<"ldap_deref">> => <<"never">>, - <<"ldap_uids">> => [#{<<"attr">> => <<"mail">>, - <<"format">> => <<"%u@mail.example.org">>}, - #{<<"attr">> => <<"name">>}], - <<"ldap_filter">> => <<"(&(objectClass=shadowAccount)(memberOf=Jabber Users))">>, - <<"ldap_vcard_map">> => [#{<<"vcard_field">> => <<"FAMILY">>, - <<"ldap_pattern">> => <<"%s">>, - <<"ldap_field">> => <<"sn">>}], - <<"ldap_search_fields">> => [#{<<"search_field">> => <<"Full Name">>, <<"ldap_field">> => <<"cn">>}], - <<"ldap_search_reported">> => [#{<<"search_field">> => <<"Full Name">>, <<"vcard_field">> => <<"FN">>}], - <<"ldap_search_operator">> => <<"or">>, % atom - <<"ldap_binary_search_fields">> => [<<"PHOTO">>], - <<"riak">> => Riak - }, - run_multi(check_one_opts_with_same_field_name(mod_vcard, MBase, Base, T) - ++ [ ?_eqf(modopts(mod_vcard, lists:sort(MBase)), T(Base)), - ?_eqf(modopts(mod_vcard, [{matches, 1}]), T(#{<<"matches">> => 1})) ] - ++ generic_bad_opts_cases(T, mod_vcard_bad_opts())), - check_iqdisc(mod_vcard). - -mod_vcard_bad_opts() -> - M = #{<<"vcard_field">> => <<"FAMILY">>, - <<"ldap_pattern">> => <<"%s">>, - <<"ldap_field">> => <<"sn">>}, - [{host, 1}, - {host, <<"test test">>}, - {search, 1}, - {backend, 1}, - {backend, <<"mememesia">>}, - {matches, -1}, - {ldap_pool_tag, -1}, - {ldap_base, -1}, - {ldap_deref, <<"nevernever">>}, - {ldap_deref, -1}, - {ldap_uids, -1}, - {ldap_uids, [#{}]}, - {ldap_uids, [#{<<"attr">> => 1, <<"format">> => <<"ok">>}]}, - {ldap_uids, [#{<<"attr">> => <<"ok">>, <<"format">> => 1}]}, - {ldap_uids, [#{<<"format">> => <<"ok">>}]}, - {ldap_filter, 1}, - {ldap_vcard_map, [M#{<<"vcard_field">> => 1}]}, - {ldap_vcard_map, [M#{<<"ldap_pattern">> => 1}]}, - {ldap_vcard_map, [M#{<<"ldap_pattern">> => 1}]}, - {ldap_search_fields, [#{<<"search_field">> => 1, <<"ldap_field">> => <<"cn">>}]}, - {ldap_search_fields, [#{<<"search_field">> => <<"Full Name">>, <<"ldap_field">> => 1}]}, - {ldap_search_reported, [#{<<"search_field">> => 1, <<"vcard_field">> => <<"FN">>}]}, - {ldap_search_reported, [#{<<"search_field">> => <<"Full Name">>, <<"vcard_field">> => 1}]}, - {ldap_search_operator, <<"more">>}, - {ldap_binary_search_fields, [1]}, - {ldap_binary_search_fields, 1}, - {riak, #{<<"bucket_type">> => 1}}, - {riak, #{<<"search_index">> => 1}}]. + M = fun(Cfg) -> modopts(mod_vcard, Cfg) end, + ?eqf(M([{iqdisc, one_queue}]), + T(#{<<"iqdisc">> => #{<<"type">> => <<"one_queue">>}})), + ?eqf(M([{host, "vjud.@HOST@"}]), + T(#{<<"host">> => <<"vjud.@HOST@">>})), + ?eqf(M([{search, true}]), + T(#{<<"search">> => true})), + ?eqf(M([{backend, mnesia}]), + T(#{<<"backend">> => <<"mnesia">>})), + ?eqf(M([{matches, infinity}]), + T(#{<<"matches">> => <<"infinity">>})), + %% ldap + ?eqf(M([{ldap_pool_tag, default}]), + T(#{<<"ldap_pool_tag">> => <<"default">>})), + ?eqf(M([{ldap_base, "ou=Users,dc=ejd,dc=com"}]), + T(#{<<"ldap_base">> => <<"ou=Users,dc=ejd,dc=com">>})), + ?eqf(M([{ldap_filter, "(&(objectClass=shadowAccount)(memberOf=Jabber Users))"}]), + T(#{<<"ldap_filter">> => <<"(&(objectClass=shadowAccount)(memberOf=Jabber Users))">>})), + ?eqf(M([{ldap_deref, never}]), + T(#{<<"ldap_deref">> => <<"never">>})), + ?eqf(M([{ldap_search_operator, 'or'}]), + T(#{<<"ldap_search_operator">> => <<"or">>})), + ?eqf(M([{ldap_binary_search_fields, [<<"PHOTO">>]}]), + T(#{<<"ldap_binary_search_fields">> => [<<"PHOTO">>]})), + %% riak + ?eqf(M([{bucket_type, <<"vcard">>}]), + T(#{<<"riak">> => #{<<"bucket_type">> => <<"vcard">>}})), + ?eqf(M([{search_index, <<"vcard">>}]), + T(#{<<"riak">> => #{<<"search_index">> => <<"vcard">>}})), + + ?errf(T(#{<<"host">> => 1})), + ?errf(T(#{<<"search">> => 1})), + ?errf(T(#{<<"backend">> => <<"mememesia">>})), + ?errf(T(#{<<"matches">> => -1})), + %% ldap + ?errf(T(#{<<"ldap_pool_tag">> => -1})), + ?errf(T(#{<<"ldap_base">> => -1})), + ?errf(T(#{<<"ldap_field">> => -1})), + ?errf(T(#{<<"ldap_deref">> => <<"nevernever">>})), + ?errf(T(#{<<"ldap_search_operator">> => <<"more">>})), + ?errf(T(#{<<"ldap_binary_search_fields">> => [1]})), + %% riak + ?errf(T(#{<<"riak">> => #{<<"bucket_type">> => 1}})), + ?errf(T(#{<<"riak">> => #{<<"search_index">> => 1}})). + +mod_vcard_ldap_uids(_Config) -> + T = fun(Opts) -> #{<<"modules">> => + #{<<"mod_vcard">> => #{<<"ldap_uids">> => Opts}}} end, + M = fun(Cfg) -> modopts(mod_vcard, [{ldap_uids, Cfg}]) end, + RequiredOpts = #{<<"attr">> => <<"name">>}, + ExpectedCfg = "name", + ?eqf(M([]), T([])), + ?eqf(M([ExpectedCfg]), T([RequiredOpts])), + ?eqf(M([{"name", "%u@mail.example.org"}]), + T([RequiredOpts#{<<"format">> => <<"%u@mail.example.org">>}])), + ?eqf(M([{"name", "%u@mail.example.org"}, ExpectedCfg]), + T([RequiredOpts#{<<"format">> => <<"%u@mail.example.org">>}, RequiredOpts])), + [?errf(T([maps:remove(Key, RequiredOpts)])) || Key <- maps:keys(RequiredOpts)], + ?errf(T(RequiredOpts#{<<"attr">> := 1})), + ?errf(T(RequiredOpts#{<<"format">> => true})). + +mod_vcard_ldap_vcard_map(_Config) -> + T = fun(Opts) -> #{<<"modules">> => + #{<<"mod_vcard">> => #{<<"ldap_vcard_map">> => Opts}}} end, + M = fun(Cfg) -> modopts(mod_vcard, [{ldap_vcard_map, Cfg}]) end, + RequiredOpts = #{<<"vcard_field">> => <<"FAMILY">>, + <<"ldap_pattern">> => <<"%s">>, + <<"ldap_field">> => <<"sn">>}, + ExpectedCfg = {<<"FAMILY">>, <<"%s">>, [<<"sn">>]}, + ?eqf(M([]), T([])), + ?eqf(M([ExpectedCfg]), T([RequiredOpts])), + [?errf(T([maps:remove(Key, RequiredOpts)])) || Key <- maps:keys(RequiredOpts)], + ?errf(T(RequiredOpts#{<<"vcard_field">> := false})), + ?errf(T(RequiredOpts#{<<"ldap_pattern">> := false})), + ?errf(T(RequiredOpts#{<<"ldap_field">> := -1})). + +mod_vcard_ldap_search_fields(_Config) -> + T = fun(Opts) -> #{<<"modules">> => + #{<<"mod_vcard">> => #{<<"ldap_search_fields">> => Opts}}} end, + M = fun(Cfg) -> modopts(mod_vcard, [{ldap_search_fields, Cfg}]) end, + RequiredOpts = #{<<"search_field">> => <<"Full Name">>, + <<"ldap_field">> => <<"cn">>}, + ExpectedCfg = {<<"Full Name">>, <<"cn">>}, + ?eqf(M([]), T([])), + ?eqf(M([ExpectedCfg]), T([RequiredOpts])), + [?errf(T([maps:remove(Key, RequiredOpts)])) || Key <- maps:keys(RequiredOpts)], + ?errf(T(RequiredOpts#{<<"search_field">> := false})), + ?errf(T(RequiredOpts#{<<"ldap_field">> := -1})). + +mod_vcard_ldap_search_reported(_Config) -> + T = fun(Opts) -> #{<<"modules">> => + #{<<"mod_vcard">> => #{<<"ldap_search_reported">> => Opts}}} end, + M = fun(Cfg) -> modopts(mod_vcard, [{ldap_search_reported, Cfg}]) end, + RequiredOpts = #{<<"search_field">> => <<"Full Name">>, + <<"vcard_field">> => <<"FN">>}, + ExpectedCfg = {<<"Full Name">>, <<"FN">>}, + ?eqf(M([]), T([])), + ?eqf(M([ExpectedCfg]), T([RequiredOpts])), + [?errf(T([maps:remove(Key, RequiredOpts)])) || Key <- maps:keys(RequiredOpts)], + ?errf(T(RequiredOpts#{<<"search_field">> := false})), + ?errf(T(RequiredOpts#{<<"vcard_field">> := -1})). mod_version(_Config) -> T = fun(Opts) -> #{<<"modules">> => #{<<"mod_version">> => Opts}} end, From 193b23394415abd45180aebdaa6746fd84e4c3fd Mon Sep 17 00:00:00 2001 From: Jan Ciesla Date: Thu, 17 Dec 2020 16:13:30 +0100 Subject: [PATCH 072/104] Make the config of mod_version declarative --- src/config/mongoose_config_parser_toml.erl | 5 ++--- src/config/mongoose_config_spec.erl | 3 ++- src/config/mongoose_config_validator_toml.erl | 6 ------ src/mod_version.erl | 12 ++++++++++-- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index c0ed7600c0..aa90a5860b 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -167,8 +167,6 @@ module_opt([<<"keys">>, <<"mod_keystore">>|_] = Path, V) -> module_opt([<<"routes">>, <<"mod_revproxy">>|_] = Path, V) -> Routes = parse_list(Path, V), [{routes, Routes}]; -module_opt([<<"os_info">>, <<"mod_version">>|_], V) -> - [{os_info, V}]; % General options module_opt([<<"iqdisc">>|_], V) -> {Type, Opts} = maps:take(<<"type">>, V), @@ -590,7 +588,8 @@ node_to_string(Node) -> [binary_to_list(Node)]. Mod =/= <<"mod_roster">>, Mod =/= <<"mod_shared_roster_ldap">>, Mod =/= <<"mod_stream_management">>, - Mod =/= <<"mod_vcard">>). % TODO temporary, remove with 'handler/1' + Mod =/= <<"mod_vcard">>, + Mod =/= <<"mod_version">>). % TODO temporary, remove with 'handler/1' -spec handler(path()) -> fun((path(), toml_value()) -> option()) | mongoose_config_spec:config_node(). diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index b5aa32a173..ceb6e90b6e 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -752,7 +752,8 @@ all_modules() -> mod_roster, mod_shared_roster_ldap, mod_stream_management, - mod_vcard]. + mod_vcard, + mod_version]. %% path: (host_config[].)modules.*.iqdisc iqdisc() -> diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index 87158d46e4..7e4bc2aeea 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -130,12 +130,6 @@ validate([<<"pool">>, <<"redis">>, <<"mod_global_distrib">>, <<"modules">>|_], validate([<<"refresh_after">>, <<"redis">>, <<"mod_global_distrib">>, <<"modules">>|_], [{refresh_after, V}]) -> validate_non_negative_integer(V); -validate([<<"iqdisc">>, <<"mod_version">>, <<"modules">>|_], - [{iqdisc, V}]) -> - validate_iqdisc(V); -validate([<<"os_info">>, <<"mod_version">>, <<"modules">>|_], - [{os_info, V}]) -> - validate_boolean(V); validate([<<"type">>, _, <<"service">>, <<"mod_extdisco">>, <<"modules">>|_], [{type, V}]) -> validate_non_empty_atom(V); diff --git a/src/mod_version.erl b/src/mod_version.erl index b66a835360..dcd9b8062f 100644 --- a/src/mod_version.erl +++ b/src/mod_version.erl @@ -5,8 +5,9 @@ -include("jlib.hrl"). -include("mongoose.hrl"). +-include("mongoose_config_spec.hrl"). --export([start/2, stop/1, process_iq/4]). +-export([start/2, stop/1, config_spec/0,process_iq/4]). -xep([{xep, 92}, {version, "1.1"}]). @@ -22,6 +23,14 @@ stop(Host) -> mod_disco:unregister_feature(Host, ?NS_VERSION), gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_VERSION). +-spec config_spec() -> mongoose_config_spec:config_section(). +config_spec() -> + #section{ + items = #{<<"iqdisc">> => mongoose_config_spec:iqdisc(), + <<"os_info">> => #option{type = boolean} + } + }. + -spec process_iq(jid:jid(),jid:jid(), mongoose_acc:t(), jlib:iq()) -> {mongoose_acc:t(), jlib:iq()}. process_iq(_From, _To, Acc, #iq{type = set, sub_el = SubEl} = IQ) -> {Acc, IQ#iq{type = error, sub_el = [SubEl, mongoose_xmpp_errors:not_allowed()]}}; @@ -65,4 +74,3 @@ os_info() -> integer_to_list(Minor) ++ "." ++ integer_to_list(Release) ). - From 729695ec31682e727c27d41a85e9fbc9ecb656ae Mon Sep 17 00:00:00 2001 From: Jan Ciesla Date: Thu, 17 Dec 2020 20:58:36 +0100 Subject: [PATCH 073/104] Remove TOML validation and parsing for deprecated mod_revproxy --- src/config/mongoose_config_parser_toml.erl | 12 --------- src/config/mongoose_config_validator_toml.erl | 13 --------- test/config_parser_SUITE.erl | 27 ------------------- test/config_parser_SUITE_data/modules.options | 12 --------- test/config_parser_SUITE_data/modules.toml | 17 ------------ 5 files changed, 81 deletions(-) diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index aa90a5860b..81d9c8af5f 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -164,9 +164,6 @@ module_opt([<<"ram_key_size">>, <<"mod_keystore">>|_], V) -> module_opt([<<"keys">>, <<"mod_keystore">>|_] = Path, V) -> Keys = parse_list(Path, V), [{keys, Keys}]; -module_opt([<<"routes">>, <<"mod_revproxy">>|_] = Path, V) -> - Routes = parse_list(Path, V), - [{routes, Routes}]; % General options module_opt([<<"iqdisc">>|_], V) -> {Type, Opts} = maps:take(<<"type">>, V), @@ -302,13 +299,6 @@ mod_keystore_keys(_, #{<<"name">> := Name, <<"type">> := <<"ram">>}) -> mod_keystore_keys(_, #{<<"name">> := Name, <<"type">> := <<"file">>, <<"path">> := Path}) -> [{b2a(Name), {file, b2l(Path)}}]. --spec mod_revproxy_routes(path(), toml_section()) -> [option()]. -mod_revproxy_routes(_, #{<<"host">> := Host, <<"path">> := Path, <<"method">> := Method, - <<"upstream">> := Upstream}) -> - [{b2l(Host), b2l(Path), b2l(Method), b2l(Upstream)}]; -mod_revproxy_routes(_, #{<<"host">> := Host, <<"path">> := Path, <<"upstream">> := Upstream}) -> - [{b2l(Host), b2l(Path), b2l(Upstream)}]. - -spec iqdisc_value(atom(), toml_section()) -> option(). iqdisc_value(queues, #{<<"workers">> := Workers} = V) -> limit_keys([<<"workers">>], V), @@ -627,8 +617,6 @@ handler([_,<<"tls">>, <<"connections">>, <<"mod_global_distrib">>, <<"modules">> fun mod_global_distrib_tls_option/2; handler([_, <<"keys">>, <<"mod_keystore">>, <<"modules">>]) -> fun mod_keystore_keys/2; -handler([_, <<"routes">>, <<"mod_revproxy">>, <<"modules">>]) -> - fun mod_revproxy_routes/2; %% host_config handler([_, <<"host_config">>]) -> fun process_host_item/2; diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index 7e4bc2aeea..cd8ad683d5 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -193,9 +193,6 @@ validate([<<"bucket_type">>, <<"riak">>, <<"mod_last">>, <<"modules">>|_], validate([<<"iqdisc">>, <<"mod_time">>, <<"modules">>|_], [{iqdisc, V}]) -> validate_iqdisc(V); -validate([item, <<"routes">>, <<"mod_revproxy">>, <<"modules">>|_], - [V]) -> - validate_revproxy_route(V); validate([<<"listen_port">>, <<"mod_jingle_sip">>, <<"modules">>|_], [{listen_port, V}]) -> validate_network_port(V); @@ -463,15 +460,5 @@ validate_keystore_key({Name, {file, Path}}) -> validate_non_empty_atom(Name), validate_filename(Path). -validate_revproxy_route({Host, Path, Method, Upstream}) -> - validate_non_empty_string(Host), - validate_string(Path), - validate_string(Method), - validate_non_empty_string(Upstream); -validate_revproxy_route({Host, Path, Upstream}) -> - validate_non_empty_string(Host), - validate_string(Path), - validate_non_empty_string(Upstream). - validate_pool_name(V) -> validate_non_empty_atom(V). diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index 0fc2aaab4d..cb93e7faef 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -202,7 +202,6 @@ groups() -> mod_pubsub_default_node_config, mod_push_service_mongoosepush, mod_register, - mod_revproxy, mod_roster, mod_shared_roster_ldap, mod_sic, @@ -2498,32 +2497,6 @@ registration_watchers(JidBins) -> Opts = #{<<"registration_watchers">> => JidBins}, #{<<"modules">> => #{<<"mod_register">> => Opts}}. -mod_revproxy(_Config) -> - T = fun(Opts) -> #{<<"modules">> => #{<<"mod_revproxy">> => Opts}} end, - R = fun(Route) -> T(#{<<"routes">> => [Route]}) end, - Base = #{<<"routes">> => [R1 = #{ - <<"host">> => <<"www.erlang-solutions.com">>, - <<"path">> => <<"/admin">>, - <<"method">> => <<"_">>, - <<"upstream">> => <<"https://www.erlang-solutions.com/">> - }, #{ - <<"host">> => <<"example.com">>, - <<"path">> => <<"/test">>, - <<"upstream">> => <<"https://example.com/">> - }]}, - MBase = [{routes, [{"www.erlang-solutions.com", "/admin", "_", - "https://www.erlang-solutions.com/"}, - {"example.com", "/test", "https://example.com/"}]}], - run_multi([ - ?_eqf(modopts(mod_revproxy, MBase), T(Base)), - ?_errf(R(R1#{<<"host">> => 1})), - ?_errf(R(R1#{<<"path">> => 1})), - ?_errf(R(R1#{<<"method">> => 1})), - ?_errf(R(R1#{<<"upstream">> => 1})), - ?_errf(R(R1#{<<"upstream">> => <<>>})), - ?_errf(R(R1#{<<"host">> => <<>>})) - ]). - mod_roster(_Config) -> T = fun(Opts) -> #{<<"modules">> => #{<<"mod_roster">> => Opts}} end, M = fun(Cfg) -> modopts(mod_roster, Cfg) end, diff --git a/test/config_parser_SUITE_data/modules.options b/test/config_parser_SUITE_data/modules.options index 8bcd3a7242..1ca15b6c3f 100644 --- a/test/config_parser_SUITE_data/modules.options +++ b/test/config_parser_SUITE_data/modules.options @@ -260,12 +260,6 @@ {secret_access_key, "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"}, {sns_host,"sns.eu-west-1.amazonaws.com"}]}]}]}, - {mod_revproxy, - [{routes, - [{"www.erlang-solutions.com","/admin","_", - "https://www.erlang-solutions.com/"}, - {":var.com","/:var","http://localhost:8080/"}, - {":domain.com","/","_","http://localhost:8080/:domain"}]}]}, {mod_carboncopy,[{iqdisc,no_queue}]}, {mod_version,[{os_info,true}]}]}. {local_config, @@ -529,11 +523,5 @@ {secret_access_key, "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"}, {sns_host,"sns.eu-west-1.amazonaws.com"}]}]}]}, - {mod_revproxy, - [{routes, - [{"www.erlang-solutions.com","/admin","_", - "https://www.erlang-solutions.com/"}, - {":var.com","/:var","http://localhost:8080/"}, - {":domain.com","/","_","http://localhost:8080/:domain"}]}]}, {mod_carboncopy,[{iqdisc,no_queue}]}, {mod_version,[{os_info,true}]}]}. diff --git a/test/config_parser_SUITE_data/modules.toml b/test/config_parser_SUITE_data/modules.toml index 533aad907a..c624a4e514 100644 --- a/test/config_parser_SUITE_data/modules.toml +++ b/test/config_parser_SUITE_data/modules.toml @@ -242,23 +242,6 @@ registration_watchers = ["JID1", "JID2"] password_strength = 32 - [[modules.mod_revproxy.routes]] - host = "www.erlang-solutions.com" - path = "/admin" - method = "_" - upstream = "https://www.erlang-solutions.com/" - - [[modules.mod_revproxy.routes]] - host = ":var.com" - path = "/:var" - upstream = "http://localhost:8080/" - - [[modules.mod_revproxy.routes]] - host = ":domain.com" - path = "/" - method = "_" - upstream = "http://localhost:8080/:domain" - [modules.mod_roster] versioning = true store_current_id = true From 854cb43af05fdad15637a8e99a0e05597afdbbe1 Mon Sep 17 00:00:00 2001 From: Jan Ciesla Date: Fri, 18 Dec 2020 13:09:23 +0100 Subject: [PATCH 074/104] TOML declarative modules spec: apply review comments --- doc/modules/mod_register.md | 10 ++++---- doc/modules/mod_vcard.md | 2 +- doc/modules/mod_version.md | 4 ++-- src/config/mongoose_config_parser_toml.erl | 3 --- src/mod_register.erl | 8 +++---- src/mod_roster.erl | 8 +++---- src/mod_vcard.erl | 28 +++++----------------- src/mod_version.erl | 2 +- test/config_parser_SUITE.erl | 14 +++++------ 9 files changed, 29 insertions(+), 50 deletions(-) diff --git a/doc/modules/mod_register.md b/doc/modules/mod_register.md index 5aa09c3a5a..e096f38dda 100644 --- a/doc/modules/mod_register.md +++ b/doc/modules/mod_register.md @@ -6,7 +6,7 @@ This module implements [XEP-0077: In-Band Registration](http://xmpp.org/extensio ### `modules.mod_register.iqdisc.type` * **Syntax:** string, one of `"one_queue"`, `"no_queue"`, `"queues"`, `"parallel"` -* **Default:** `"no_queue"` +* **Default:** `"one_queue"` Strategy to handle incoming stanzas. For details, please refer to [IQ processing policies](../../advanced-configuration/Modules/#iq-processing-policies). @@ -44,15 +44,17 @@ The entropy calculation algorithm is described in a section below. ### `modules.mod_register.ip_access` * **Syntax:** Array of TOML tables with the following mandatory content: - - `address` - string, IP address - - `policy` - string, one of: `"allow"`, `"deny"`. + + - `address` - string, IP address + - `policy` - string, one of: `"allow"`, `"deny"`. + * **Default:** `[]` * **Example:** `ip_access = [ {address = "127.0.0.0/8", policy = "allow"}, {address = "0.0.0.0/0", policy = "deny"} ]` -Access list for specified IPs or networks. +Access list for specified IPs or networks. Default value allows registration from every IP. ## Example configuration diff --git a/doc/modules/mod_vcard.md b/doc/modules/mod_vcard.md index 7dc47c9fda..ebbdc21e3e 100644 --- a/doc/modules/mod_vcard.md +++ b/doc/modules/mod_vcard.md @@ -6,7 +6,7 @@ This module provides support for vCards, as specified in [XEP-0054: vcard-temp]( ### `modules.mod_vcard.iqdisc.type` * **Syntax:** string, one of `"one_queue"`, `"no_queue"`, `"queues"`, `"parallel"` -* **Default:** `"no_queue"` +* **Default:** `"one_queue"` Strategy to handle incoming stanzas. For details, please refer to [IQ processing policies](../../advanced-configuration/Modules/#iq-processing-policies). diff --git a/doc/modules/mod_version.md b/doc/modules/mod_version.md index c447b3df63..6f4f9378f4 100644 --- a/doc/modules/mod_version.md +++ b/doc/modules/mod_version.md @@ -6,7 +6,7 @@ This module provides the functionality specified in [XEP-0092: Software Version] ### `modules.mod_version.iqdisc.type` * **Syntax:** string, one of `"one_queue"`, `"no_queue"`, `"queues"`, `"parallel"` -* **Default:** `"no_queue"` +* **Default:** `"one_queue"` Strategy to handle incoming stanzas. For details, please refer to [IQ processing policies](../../advanced-configuration/Modules/#iq-processing-policies). @@ -18,7 +18,7 @@ Strategy to handle incoming stanzas. For details, please refer to Determines whether information about the operating system will be included. -## Example configuration +## Example configuration ```toml [modules.mod_version] diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index 81d9c8af5f..c46aac5be1 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -335,9 +335,6 @@ b2a(B) -> binary_to_atom(B, utf8). b2l(B) -> binary_to_list(B). -int_or_infinity(I) when is_integer(I) -> I; -int_or_infinity(<<"infinity">>) -> infinity. - -spec limit_keys([toml_key()], toml_section()) -> any(). limit_keys(Keys, Section) -> case maps:keys(maps:without(Keys, Section)) of diff --git a/src/mod_register.erl b/src/mod_register.erl index 656b469a59..5931cf657b 100644 --- a/src/mod_register.erl +++ b/src/mod_register.erl @@ -74,7 +74,7 @@ config_spec() -> #section{ items = #{<<"iqdisc">> => mongoose_config_spec:iqdisc(), <<"access">> => #option{type = atom, - validate = non_empty}, + validate = access_rule}, <<"welcome_message">> => welcome_message_spec(), <<"registration_watchers">> => #list{items = #option{type = binary, validate = jid}}, @@ -86,10 +86,8 @@ config_spec() -> welcome_message_spec() -> #section{ - items = #{<<"body">> => #option{type = string, - validate = non_empty}, - <<"subject">> => #option{type = string, - validate = non_empty}}, + items = #{<<"body">> => #option{type = string}, + <<"subject">> => #option{type = string}}, process = fun ?MODULE:process_welcome_message/1 }. diff --git a/src/mod_roster.erl b/src/mod_roster.erl index c2e9965110..d643359ac8 100644 --- a/src/mod_roster.erl +++ b/src/mod_roster.erl @@ -218,11 +218,11 @@ stop(Host) -> config_spec() -> #section{ items = #{<<"iqdisc">> => mongoose_config_spec:iqdisc(), - <<"versioning">> => #option{type = boolean}, - <<"store_current_id">> => #option{type = boolean}, - <<"backend">> => #option{type = atom, + <<"versioning">> => #option{type = boolean}, + <<"store_current_id">> => #option{type = boolean}, + <<"backend">> => #option{type = atom, validate = {module, mod_roster}}, - <<"riak">> => riak_config_spec() + <<"riak">> => riak_config_spec() } }. diff --git a/src/mod_vcard.erl b/src/mod_vcard.erl index 158b758f40..580039568f 100644 --- a/src/mod_vcard.erl +++ b/src/mod_vcard.erl @@ -51,8 +51,7 @@ -export([config_spec/0, process_map_spec/1, process_search_spec/1, - process_search_reported_spec/1, - process_ldap_uids/1]). + process_search_reported_spec/1]). %% gen_server handlers -export([init/1, @@ -213,16 +212,16 @@ config_spec() -> #section{ items = #{<<"iqdisc">> => mongoose_config_spec:iqdisc(), <<"host">> => #option{type = string, - validate = non_empty}, + validate = domain_template}, <<"search">> => #option{type = boolean}, <<"backend">> => #option{type = atom, validate = {module, mod_vcard}}, <<"matches">> => #option{type = int_or_infinity, validate = non_negative}, <<"ldap_pool_tag">> => #option{type = atom, - validate = non_empty}, + validate = pool_name}, <<"ldap_base">> => #option{type = string}, - <<"ldap_uids">> => #list{items = ldap_uids_spec()}, + <<"ldap_uids">> => #list{items = mongoose_config_spec:ldap_uids()}, <<"ldap_filter">> => #option{type = string}, <<"ldap_deref">> => #option{type = atom, validate = {enum, [never, always, finding, searching]}}, @@ -232,26 +231,11 @@ config_spec() -> <<"ldap_search_operator">> => #option{type = atom, validate = {enum, ['or', 'and']}}, <<"ldap_binary_search_fields">> => #list{items = #option{type = binary, - validate = non_empty}}, + validate = non_empty}}, <<"riak">> => riak_config_spec() } }. -ldap_uids_spec() -> - #section{ - items = #{<<"attr">> => #option{type = string}, - <<"format">> => #option{type = string}}, - process = fun ?MODULE:process_ldap_uids/1, - required = [<<"attr">>] -}. - -process_ldap_uids(KVs) -> - {[AttrOpts, FormatOpts], []} = proplists:split(KVs, [attr, format]), - case {AttrOpts, FormatOpts} of - {[{attr, Attr}], []} -> Attr; - {[{attr, Attr}], [{format, Format}]} -> {Attr, Format} - end. - ldap_vcard_map_spec() -> #section{ items = #{<<"vcard_field">> => #option{type = binary, @@ -279,7 +263,7 @@ ldap_search_fields_spec() -> ldap_search_reported_spec() -> #section{ items = #{<<"search_field">> => #option{type = binary, - validate = non_empty}, + validate = non_empty}, <<"vcard_field">> => #option{type = binary, validate = non_empty} }, diff --git a/src/mod_version.erl b/src/mod_version.erl index dcd9b8062f..113f8ba259 100644 --- a/src/mod_version.erl +++ b/src/mod_version.erl @@ -7,7 +7,7 @@ -include("mongoose.hrl"). -include("mongoose_config_spec.hrl"). --export([start/2, stop/1, config_spec/0,process_iq/4]). +-export([start/2, stop/1, config_spec/0, process_iq/4]). -xep([{xep, 92}, {version, "1.1"}]). diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index cb93e7faef..078636307e 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -2501,12 +2501,10 @@ mod_roster(_Config) -> T = fun(Opts) -> #{<<"modules">> => #{<<"mod_roster">> => Opts}} end, M = fun(Cfg) -> modopts(mod_roster, Cfg) end, - ?eqf(M([{iqdisc, one_queue}]), - T(#{<<"iqdisc">> => #{<<"type">> => <<"one_queue">>}})), + ?eqf(M([{versioning, false}]), + T(#{<<"versioning">> => false})), ?eqf(M([{store_current_id, false}]), T(#{<<"store_current_id">> => false})), - ?eqf(M([{versioning, false}]), - T(#{<<"versioning">> => false})), ?eqf(M([{backend, mnesia}]), T(#{<<"backend">> => <<"mnesia">>})), ?eqf(M([{bucket_type, <<"rosters">>}]), @@ -2687,8 +2685,8 @@ mod_vcard_ldap_vcard_map(_Config) -> #{<<"mod_vcard">> => #{<<"ldap_vcard_map">> => Opts}}} end, M = fun(Cfg) -> modopts(mod_vcard, [{ldap_vcard_map, Cfg}]) end, RequiredOpts = #{<<"vcard_field">> => <<"FAMILY">>, - <<"ldap_pattern">> => <<"%s">>, - <<"ldap_field">> => <<"sn">>}, + <<"ldap_pattern">> => <<"%s">>, + <<"ldap_field">> => <<"sn">>}, ExpectedCfg = {<<"FAMILY">>, <<"%s">>, [<<"sn">>]}, ?eqf(M([]), T([])), ?eqf(M([ExpectedCfg]), T([RequiredOpts])), @@ -2702,7 +2700,7 @@ mod_vcard_ldap_search_fields(_Config) -> #{<<"mod_vcard">> => #{<<"ldap_search_fields">> => Opts}}} end, M = fun(Cfg) -> modopts(mod_vcard, [{ldap_search_fields, Cfg}]) end, RequiredOpts = #{<<"search_field">> => <<"Full Name">>, - <<"ldap_field">> => <<"cn">>}, + <<"ldap_field">> => <<"cn">>}, ExpectedCfg = {<<"Full Name">>, <<"cn">>}, ?eqf(M([]), T([])), ?eqf(M([ExpectedCfg]), T([RequiredOpts])), @@ -2715,7 +2713,7 @@ mod_vcard_ldap_search_reported(_Config) -> #{<<"mod_vcard">> => #{<<"ldap_search_reported">> => Opts}}} end, M = fun(Cfg) -> modopts(mod_vcard, [{ldap_search_reported, Cfg}]) end, RequiredOpts = #{<<"search_field">> => <<"Full Name">>, - <<"vcard_field">> => <<"FN">>}, + <<"vcard_field">> => <<"FN">>}, ExpectedCfg = {<<"Full Name">>, <<"FN">>}, ?eqf(M([]), T([])), ?eqf(M([ExpectedCfg]), T([RequiredOpts])), From fe3857a1fa0d2e9046a0fe50513fd12ea054d97d Mon Sep 17 00:00:00 2001 From: Jan Ciesla Date: Fri, 18 Dec 2020 14:52:52 +0100 Subject: [PATCH 075/104] Make the config of mod_last declarative --- src/config/mongoose_config_parser_toml.erl | 1 + src/config/mongoose_config_spec.erl | 1 + src/config/mongoose_config_validator_toml.erl | 9 ------- src/mod_last.erl | 24 ++++++++++++++++++- test/config_parser_SUITE.erl | 19 +++++++-------- 5 files changed, 34 insertions(+), 20 deletions(-) diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index c46aac5be1..5bfedbd67f 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -561,6 +561,7 @@ node_to_string(Node) -> [binary_to_list(Node)]. Mod =/= <<"mod_csi">>, Mod =/= <<"mod_disco">>, Mod =/= <<"mod_event_pusher">>, + Mod =/= <<"mod_last">>, Mod =/= <<"mod_mam_meta">>, Mod =/= <<"mod_muc">>, Mod =/= <<"mod_muc_light">>, diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index ceb6e90b6e..1e046a4aad 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -738,6 +738,7 @@ all_modules() -> mod_csi, mod_disco, mod_event_pusher, + mod_last, mod_mam_meta, mod_muc, mod_muc_light, diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index cd8ad683d5..9d665a916c 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -181,15 +181,6 @@ validate([<<"secret_access_key">>, <<"s3">>, <<"mod_http_upload">>, <<"modules"> validate([<<"token_bytes">>, <<"mod_http_upload">>, <<"modules">>|_], [{token_bytes, V}]) -> validate_positive_integer(V); -validate([<<"backend">>, <<"mod_last">>, <<"modules">>|_], - [{backend, V}]) -> - validate_backend(mod_last, V); -validate([<<"iqdisc">>, <<"mod_last">>, <<"modules">>|_], - [{iqdisc, V}]) -> - validate_iqdisc(V); -validate([<<"bucket_type">>, <<"riak">>, <<"mod_last">>, <<"modules">>|_], - [{bucket_type, V}]) -> - validate_non_empty_binary(V); validate([<<"iqdisc">>, <<"mod_time">>, <<"modules">>|_], [{iqdisc, V}]) -> validate_iqdisc(V); diff --git a/src/mod_last.erl b/src/mod_last.erl index 4fde5f748a..0fe11988fb 100644 --- a/src/mod_last.erl +++ b/src/mod_last.erl @@ -35,6 +35,7 @@ -export([ start/2, stop/1, + config_spec/0, process_local_iq/4, process_sm_iq/4, on_presence_update/5, @@ -48,6 +49,7 @@ -export([config_metrics/1]). -include("mongoose.hrl"). +-include("mongoose_config_spec.hrl"). -include("jlib.hrl"). @@ -108,6 +110,27 @@ stop(Host) -> gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_LAST), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_LAST). +%%% +%%% config_spec +%%% + +-spec config_spec() -> mongoose_config_spec:config_section(). +config_spec() -> + #section{ + items = #{<<"iqdisc">> => mongoose_config_spec:iqdisc(), + <<"backend">> => #option{type = atom, + validate = {module, mod_last}}, + <<"riak">> => riak_config_spec() + } + }. + +riak_config_spec() -> + #section{items = #{<<"bucket_type">> => #option{type = binary, + validate = non_empty} + }, + format = none + }. + %%% %%% Uptime of ejabberd node %%% @@ -254,4 +277,3 @@ session_cleanup(Acc, LUser, LServer, LResource, _SID) -> config_metrics(Host) -> OptsToReport = [{backend, mnesia}], %list of tuples {option, defualt_value} mongoose_module_metrics:opts_for_module(Host, ?MODULE, OptsToReport). - diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index 078636307e..00492c9fd6 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -1870,17 +1870,16 @@ mod_keystore(_Config) -> ?errf(T(Base#{<<"keys">> => [InvalidTypeKey]})). mod_last(_Config) -> + check_iqdisc(mod_last), T = fun(Opts) -> #{<<"modules">> => #{<<"mod_last">> => Opts}} end, - Base = #{<<"iqdisc">> => #{<<"type">> => <<"one_queue">>}, - <<"backend">> => <<"riak">>, - <<"riak">> => #{<<"bucket_type">> => <<"test">>}}, - MBase = [{backend, riak}, - {bucket_type, <<"test">>}, - {iqdisc, one_queue}], - ?eqf(modopts(mod_last, MBase), T(Base)), - ?errf(T(Base#{<<"backend">> => <<"riak_is_the_best">>})), - ?errf(T(Base#{<<"riak">> => #{<<"bucket_type">> => 1}})), - check_iqdisc(mod_last). + M = fun(Cfg) -> modopts(mod_last, Cfg) end, + ?eqf(M([{backend, mnesia}]), + T(#{<<"backend">> => <<"mnesia">>})), + ?eqf(M([{bucket_type, <<"test">>}]), + T(#{<<"riak">> => #{<<"bucket_type">> => <<"test">>}})), + + ?errf(T(#{<<"backend">> => <<"frontend">>})), + ?errf(T(#{<<"riak">> => #{<<"bucket_type">> => 1}})). mod_mam_meta(_Config) -> T = fun(Opts) -> #{<<"modules">> => #{<<"mod_mam_meta">> => Opts}} end, From d0f2a8432cd045168a8db179afdd1aad28323e05 Mon Sep 17 00:00:00 2001 From: Leszek Witkowicz Date: Mon, 7 Dec 2020 14:47:24 +0100 Subject: [PATCH 076/104] make mod_extdisco config declarative --- src/config/mongoose_config_parser_toml.erl | 23 +------ src/config/mongoose_config_spec.erl | 1 + src/config/mongoose_config_validator_toml.erl | 20 ------ src/mod_extdisco.erl | 30 ++++++++- test/config_parser_SUITE.erl | 61 +++++++++---------- 5 files changed, 58 insertions(+), 77 deletions(-) diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index 5bfedbd67f..cb5770a241 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -104,8 +104,6 @@ post_process_module(Mod, Opts) -> %% path: (host_config[].)modules.*.* -spec module_opt(path(), toml_value()) -> [option()]. -module_opt([<<"service">>, <<"mod_extdisco">>|_] = Path, V) -> - parse_list(Path, V); module_opt([<<"host">>, <<"mod_http_upload">>|_], V) -> [{host, b2l(V)}]; module_opt([<<"backend">>, <<"mod_http_upload">>|_], V) -> @@ -196,22 +194,6 @@ riak_opts([<<"bucket_type">>|_], V) -> riak_opts([<<"search_index">>|_], V) -> [{search_index, V}]. --spec mod_extdisco_service(path(), toml_value()) -> [option()]. -mod_extdisco_service([_, <<"service">>|_] = Path, V) -> - [parse_section(Path, V)]; -mod_extdisco_service([<<"type">>|_], V) -> - [{type, b2a(V)}]; -mod_extdisco_service([<<"host">>|_], V) -> - [{host, b2l(V)}]; -mod_extdisco_service([<<"port">>|_], V) -> - [{port, V}]; -mod_extdisco_service([<<"transport">>|_], V) -> - [{transport, b2l(V)}]; -mod_extdisco_service([<<"username">>|_], V) -> - [{username, b2l(V)}]; -mod_extdisco_service([<<"password">>|_], V) -> - [{password, b2l(V)}]. - -spec mod_http_upload_s3(path(), toml_value()) -> [option()]. mod_http_upload_s3([<<"bucket_url">>|_], V) -> [{bucket_url, b2l(V)}]; @@ -561,6 +543,7 @@ node_to_string(Node) -> [binary_to_list(Node)]. Mod =/= <<"mod_csi">>, Mod =/= <<"mod_disco">>, Mod =/= <<"mod_event_pusher">>, + Mod =/= <<"mod_extdisco">>, Mod =/= <<"mod_last">>, Mod =/= <<"mod_mam_meta">>, Mod =/= <<"mod_muc">>, @@ -589,10 +572,6 @@ handler([Mod, <<"modules">>]) when ?HAS_NO_SPEC(Mod) -> fun process_module/2; handler([_, Mod, <<"modules">>]) when ?HAS_NO_SPEC(Mod) -> fun module_opt/2; handler([_, <<"riak">>, Mod, <<"modules">>]) when ?HAS_NO_SPEC(Mod) -> fun riak_opts/2; -handler([_, <<"service">>, <<"mod_extdisco">>, <<"modules">>]) -> - fun mod_extdisco_service/2; -handler([_, _, <<"service">>, <<"mod_extdisco">>, <<"modules">>]) -> - fun mod_extdisco_service/2; handler([_, <<"s3">>, <<"mod_http_upload">>, <<"modules">>]) -> fun mod_http_upload_s3/2; handler([_, <<"reset_markers">>, <<"mod_inbox">>, <<"modules">>]) -> diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index 1e046a4aad..2a3f73b0a9 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -738,6 +738,7 @@ all_modules() -> mod_csi, mod_disco, mod_event_pusher, + mod_extdisco, mod_last, mod_mam_meta, mod_muc, diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index 9d665a916c..64e0bb949e 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -130,24 +130,6 @@ validate([<<"pool">>, <<"redis">>, <<"mod_global_distrib">>, <<"modules">>|_], validate([<<"refresh_after">>, <<"redis">>, <<"mod_global_distrib">>, <<"modules">>|_], [{refresh_after, V}]) -> validate_non_negative_integer(V); -validate([<<"type">>, _, <<"service">>, <<"mod_extdisco">>, <<"modules">>|_], - [{type, V}]) -> - validate_non_empty_atom(V); -validate([<<"host">>, _,<<"service">>, <<"mod_extdisco">>, <<"modules">>|_], - [{host, V}]) -> - validate_non_empty_list(V); -validate([<<"port">>, _,<<"service">>, <<"mod_extdisco">>, <<"modules">>|_], - [{port, V}]) -> - validate_port(V); -validate([<<"transport">>,_, <<"service">>, <<"mod_extdisco">>, <<"modules">>|_], - [{transport, V}]) -> - validate_non_empty_list(V); -validate([<<"username">>, _,<<"service">>, <<"mod_extdisco">>, <<"modules">>|_], - [{username, V}]) -> - validate_non_empty_list(V); -validate([<<"password">>, _,<<"service">>, <<"mod_extdisco">>, <<"modules">>|_], - [{password, V}]) -> - validate_non_empty_list(V); validate([<<"backend">>, <<"mod_http_upload">>, <<"modules">>|_], [{backend, V}]) -> validate_backend(mod_http_upload, V); @@ -323,8 +305,6 @@ validate_non_empty_atom(Value) when is_atom(Value), Value =/= '' -> ok. validate_non_empty_string(Value) when is_list(Value), Value =/= "" -> ok. -validate_non_empty_list(Value) when is_list(Value), Value =/= [] -> ok. - validate_jid(Jid) -> case jid:from_binary(Jid) of #jid{} -> diff --git a/src/mod_extdisco.erl b/src/mod_extdisco.erl index 2854643956..87f20b8b9a 100644 --- a/src/mod_extdisco.erl +++ b/src/mod_extdisco.erl @@ -20,12 +20,13 @@ -behaviour(gen_mod). %% gen_mod callbacks. --export([start/2, stop/1]). +-export([start/2, stop/1, config_spec/0]). -export([process_iq/4]). --include("mongoose.hrl"). -include("jlib.hrl"). +-include("mongoose.hrl"). +-include("mongoose_config_spec.hrl"). -spec start(jid:server(), list()) -> ok. start(Host, Opts) -> @@ -39,6 +40,31 @@ stop(Host) -> mod_disco:unregister_feature(Host, ?NS_EXTDISCO), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_EXTDISCO). +-spec config_spec() -> mongoose_config_spec:config_section(). +config_spec() -> + #section{ + items = #{<<"service">> => #list{items = service_config_spec(), + format = none}} + }. + +service_config_spec() -> + #section{ + items = #{<<"type">> => #option{type = atom, + validate = non_empty}, + <<"host">> => #option{type = string, + validate = non_empty}, + <<"port">> => #option{type = integer, + validate = port}, + <<"transport">> => #option{type = string, + validate = {enum, ["udp", "tcp"]}}, + <<"username">> => #option{type = string, + validate = non_empty}, + <<"password">> => #option{type = string, + validate = non_empty} + }, + required = [<<"type">>, <<"host">>] + }. + -spec process_iq(jid:jid(), jid:jid(), mongoose_acc:t(), jlib:iq()) -> {mongoose_acc:t(), jlib:iq()}. process_iq(_From, _To, Acc, #iq{type = get, sub_el = SubEl} = IQ) -> diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index 00492c9fd6..9af441c550 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -1453,40 +1453,33 @@ mod_disco(_Config) -> ?errf(T(<<"server_info">>, [maps:remove(<<"urls">>, Info)])). mod_extdisco(_Config) -> - T = fun(Opts) -> #{<<"modules">> => #{<<"mod_extdisco">> => Opts}} end, - Service = #{ + T = fun(Opts) -> #{<<"modules">> => + #{<<"mod_extdisco">> => + #{<<"service">> => [Opts]}}} + end, + M = fun(Opts) -> modopts(mod_extdisco, [Opts]) end, + RequiredOpts = #{ <<"type">> => <<"stun">>, - <<"host">> => <<"stun1">>, - <<"port">> => 3478, - <<"transport">> => <<"udp">>, - <<"username">> => <<"username">>, - <<"password">> => <<"password">>}, - Base = #{<<"service">> => [Service]}, - MBase = [{host, "stun1"}, - {password, "password"}, - {port, 3478}, - {transport, "udp"}, - {type, stun}, - {username, "username"}], - ?eqf(modopts(mod_extdisco, [MBase]), T(Base)), - %% Invalid service type - ?errf(T(Base#{<<"service">> => [Base#{<<"type">> => -1}]})), - ?errf(T(Base#{<<"service">> => [Base#{<<"type">> => ["stun"]}]})), - %% Invalid host - ?errf(T(Base#{<<"service">> => [Base#{<<"host">> => [1]}]})), - ?errf(T(Base#{<<"service">> => [Base#{<<"host">> => true}]})), - %% Invalid port - ?errf(T(Base#{<<"service">> => [Base#{<<"port">> => -1}]})), - ?errf(T(Base#{<<"service">> => [Base#{<<"port">> => 9999999}]})), - ?errf(T(Base#{<<"service">> => [Base#{<<"port">> => "port"}]})), - %% Invalid transport - ?errf(T(Base#{<<"service">> => [Base#{<<"transport">> => -1}]})), - ?errf(T(Base#{<<"service">> => [Base#{<<"transport">> => ""}]})), - %% Invalid username - ?errf(T(Base#{<<"service">> => [Base#{<<"username">> => -2}]})), - %% Invalid password - ?errf(T(Base#{<<"service">> => [Base#{<<"password">> => 1}]})), - ?errf(T(Base#{<<"service">> => [Base#{<<"password">> => [<<"test">>]}]})). + <<"host">> => <<"stun1">>}, + ExpectedCfg = [{host, "stun1"}, + {type, stun}], + ?eqf(M(ExpectedCfg), T(RequiredOpts)), + ?eqf(M(ExpectedCfg ++ [{port, 3478}]), + T(RequiredOpts#{<<"port">> => 3478})), + ?eqf(M(ExpectedCfg ++ [{transport, "udp"}]), + T(RequiredOpts#{<<"transport">> => <<"udp">>})), + ?eqf(M(ExpectedCfg ++ [{username, "username"}]), + T(RequiredOpts#{<<"username">> => <<"username">>})), + ?eqf(M(ExpectedCfg ++ [{password, "password"}]), + T(RequiredOpts#{<<"password">> => <<"password">>})), + [?errf(T(maps:remove(Key, RequiredOpts))) || Key <- maps:keys(RequiredOpts)], + [?errf(T(RequiredOpts#{Key => 1})) || Key <- maps:keys(RequiredOpts)], + ?errf(T(RequiredOpts#{<<"type">> => <<"">>})), + ?errf(T(RequiredOpts#{<<"host">> => <<"">>})), + ?errf(T(RequiredOpts#{<<"port">> => -1})), + ?errf(T(RequiredOpts#{<<"transport">> => <<"">>})), + ?errf(T(RequiredOpts#{<<"username">> => <<"">>})), + ?errf(T(RequiredOpts#{<<"password">> => <<"">>})). mod_inbox(_Config) -> T = fun(K, V) -> #{<<"modules">> => #{<<"mod_inbox">> => #{K => V}}} end, @@ -2900,6 +2893,8 @@ compare_values({auth_opts, _}, V1, V2) -> compare_unordered_lists(V1, V2, fun handle_auth_opt/2); compare_values(outgoing_pools, V1, V2) -> compare_unordered_lists(V1, V2, fun handle_conn_pool/2); +compare_values({modules, _}, [{mod_extdisco, V1}], [{mod_extdisco, V2}]) -> + compare_ordered_lists(V1, V2, fun compare_unordered_lists/2); compare_values({modules, _}, V1, V2) -> compare_unordered_lists(V1, V2, fun handle_modules/2); compare_values({services, _}, V1, V2) -> From 7533dbbe7df4ad3c431f82ea28c0f274c3337181 Mon Sep 17 00:00:00 2001 From: Leszek Witkowicz Date: Thu, 10 Dec 2020 13:18:19 +0100 Subject: [PATCH 077/104] make mod_inbox config declarative --- src/config/mongoose_config_parser_toml.erl | 15 +------ src/config/mongoose_config_spec.erl | 1 + src/config/mongoose_config_validator_toml.erl | 21 ---------- src/inbox/mod_inbox.erl | 24 +++++++++-- test/config_parser_SUITE.erl | 41 +++++++------------ 5 files changed, 37 insertions(+), 65 deletions(-) diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index cb5770a241..9083097203 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -117,16 +117,6 @@ module_opt([<<"max_file_size">>, <<"mod_http_upload">>|_], V) -> module_opt([<<"s3">>, <<"mod_http_upload">>|_] = Path, V) -> S3Opts = parse_section(Path, V), [{s3, S3Opts}]; -module_opt([<<"reset_markers">>, <<"mod_inbox">>|_] = Path, V) -> - Markers = parse_list(Path, V), - [{reset_markers, Markers}]; -module_opt([<<"groupchat">>, <<"mod_inbox">>|_] = Path, V) -> - GChats = parse_list(Path, V), - [{groupchat, GChats}]; -module_opt([<<"aff_changes">>, <<"mod_inbox">>|_], V) -> - [{aff_changes, V}]; -module_opt([<<"remove_on_kicked">>, <<"mod_inbox">>|_], V) -> - [{remove_on_kicked, V}]; module_opt([<<"global_host">>, <<"mod_global_distrib">>|_], V) -> [{global_host, b2l(V)}]; module_opt([<<"local_host">>, <<"mod_global_distrib">>|_], V) -> @@ -544,6 +534,7 @@ node_to_string(Node) -> [binary_to_list(Node)]. Mod =/= <<"mod_disco">>, Mod =/= <<"mod_event_pusher">>, Mod =/= <<"mod_extdisco">>, + Mod =/= <<"mod_inbox">>, Mod =/= <<"mod_last">>, Mod =/= <<"mod_mam_meta">>, Mod =/= <<"mod_muc">>, @@ -574,10 +565,6 @@ handler([_, <<"riak">>, Mod, <<"modules">>]) when ?HAS_NO_SPEC(Mod) -> fun riak_opts/2; handler([_, <<"s3">>, <<"mod_http_upload">>, <<"modules">>]) -> fun mod_http_upload_s3/2; -handler([_, <<"reset_markers">>, <<"mod_inbox">>, <<"modules">>]) -> - fun(_, V) -> [b2a(V)] end; -handler([_, <<"groupchat">>, <<"mod_inbox">>, <<"modules">>]) -> - fun(_, V) -> [b2a(V)] end; handler([_, <<"connections">>, <<"mod_global_distrib">>, <<"modules">>]) -> fun mod_global_distrib_connections/2; handler([_, <<"cache">>, <<"mod_global_distrib">>, <<"modules">>]) -> diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index 2a3f73b0a9..4e4350d133 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -739,6 +739,7 @@ all_modules() -> mod_disco, mod_event_pusher, mod_extdisco, + mod_inbox, mod_last, mod_mam_meta, mod_muc, diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index 64e0bb949e..9df85a2b5d 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -190,21 +190,6 @@ validate([item, <<"keys">>, <<"mod_keystore">>, <<"modules">>|_], validate([<<"ram_key_size">>, <<"mod_keystore">>, <<"modules">>|_], [{ram_key_size, V}]) -> validate_non_negative_integer(V); -validate([<<"aff_changes">>, <<"mod_inbox">>, <<"modules">>|_], - [{aff_changes, V}]) -> - validate_boolean(V); -validate([item, <<"groupchat">>, <<"mod_inbox">>, <<"modules">>|_], - [V]) -> - validate_groupchat_type(V); -validate([<<"iqdisc">>, <<"mod_inbox">>, <<"modules">>|_], - [{iqdisc, V}]) -> - validate_iqdisc(V); -validate([<<"remove_on_kicked">>, <<"mod_inbox">>, <<"modules">>|_], - [{remove_on_kicked, V}]) -> - validate_boolean(V); -validate([item, <<"reset_markers">>, <<"mod_inbox">>, <<"modules">>|_], - [V]) -> - validate_chat_marker_type(V); validate(_Path, _Value) -> ok. @@ -321,12 +306,6 @@ validate_iqdisc({queues, N}) when is_integer(N), N > 0 -> ok. validate_backend(Mod, Backend) -> validate_module(backend_module:backend_module(Mod, Backend)). -validate_chat_marker_type(Type) -> - validate_enum(Type, [displayed, received, acknowledged]). - -validate_groupchat_type(Type) -> - validate_enum(Type, [muc, muclight]). - validate_domain(Domain) when is_list(Domain) -> #jid{luser = <<>>, lresource = <<>>} = jid:from_binary(list_to_binary(Domain)), validate_domain_res(Domain). diff --git a/src/inbox/mod_inbox.erl b/src/inbox/mod_inbox.erl index ad34221aa8..a5fff7dad7 100644 --- a/src/inbox/mod_inbox.erl +++ b/src/inbox/mod_inbox.erl @@ -8,16 +8,20 @@ %%%------------------------------------------------------------------- -module(mod_inbox). -author("ludwikbukowski"). + +-behaviour(gen_mod). -behaviour(mongoose_module_metrics). --include("mod_inbox.hrl"). + -include("jlib.hrl"). --include("mongoose_ns.hrl"). +-include("mod_inbox.hrl"). -include("mongoose.hrl"). +-include("mongoose_config_spec.hrl"). -include("mongoose_logger.hrl"). +-include("mongoose_ns.hrl"). -export([get_personal_data/2]). --export([start/2, stop/1, deps/2]). +-export([start/2, stop/1, deps/2, config_spec/0]). -export([process_iq/4, process_iq_conversation/4, user_send_packet/4, @@ -148,6 +152,20 @@ stop(Host) -> ejabberd_hooks:delete(hooks(Host)), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_ESL_INBOX). +-spec config_spec() -> mongoose_config_spec:config_section(). +config_spec() -> + #section{ + items = #{<<"reset_markers">> => #list{items = #option{type = atom, + validate = {enum, [displayed, + received, + acknowledged]}}}, + <<"groupchat">> => #list{items = #option{type = atom, + validate = {enum, [muc, muclight]}}}, + <<"aff_changes">> => #option{type = boolean}, + <<"remove_on_kicked">> => #option{type = boolean}, + <<"iqdisc">> => mongoose_config_spec:iqdisc() + } + }. %%%%%%%%%%%%%%%%%%% %% Process IQ diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index 9af441c550..f19e338abf 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -1482,33 +1482,20 @@ mod_extdisco(_Config) -> ?errf(T(RequiredOpts#{<<"password">> => <<"">>})). mod_inbox(_Config) -> - T = fun(K, V) -> #{<<"modules">> => #{<<"mod_inbox">> => #{K => V}}} end, - ?eqf(modopts(mod_inbox, [{reset_markers, [displayed, received, acknowledged]}]), - T(<<"reset_markers">>, [<<"displayed">>, <<"received">>, <<"acknowledged">>])), - ?eqf(modopts(mod_inbox, [{reset_markers, []}]), - T(<<"reset_markers">>, [])), - ?eqf(modopts(mod_inbox, [{groupchat, [muc, muclight]}]), - T(<<"groupchat">>, [<<"muc">>, <<"muclight">>])), - ?eqf(modopts(mod_inbox, [{groupchat, []}]), - T(<<"groupchat">>, [])), - ?eqf(modopts(mod_inbox, [{aff_changes, true}]), - T(<<"aff_changes">>, true)), - ?eqf(modopts(mod_inbox, [{aff_changes, false}]), - T(<<"aff_changes">>, false)), - ?eqf(modopts(mod_inbox, [{remove_on_kicked, true}]), - T(<<"remove_on_kicked">>, true)), - ?eqf(modopts(mod_inbox, [{remove_on_kicked, false}]), - T(<<"remove_on_kicked">>, false)), - ?errf(T(<<"reset_markers">>, 1)), - ?errf(T(<<"reset_markers">>, <<"test">>)), - ?errf(T(<<"reset_markers">>, [<<"test">>])), - ?errf(T(<<"groupchat">>, [<<"test">>])), - ?errf(T(<<"groupchat">>, <<"test">>)), - ?errf(T(<<"groupchat">>, true)), - ?errf(T(<<"aff_changes">>, 1)), - ?errf(T(<<"aff_changes">>, <<"true">>)), - ?errf(T(<<"remove_on_kicked">>, 1)), - ?errf(T(<<"remove_on_kicked">>, <<"true">>)), + T = fun(Opts) -> #{<<"modules">> => #{<<"mod_inbox">> => Opts}} end, + M = fun(Opts) -> modopts(mod_inbox, Opts) end, + ?eqf(M([{reset_markers, [displayed, received, acknowledged]}]), + T(#{<<"reset_markers">> => [<<"displayed">>, <<"received">>, <<"acknowledged">>]})), + ?eqf(M([{groupchat, [muc, muclight]}]), + T(#{<<"groupchat">> => [<<"muc">>, <<"muclight">>]})), + ?eqf(M([{aff_changes, true}]), + T(#{<<"aff_changes">> => true})), + ?eqf(M([{remove_on_kicked, false}]), + T(#{<<"remove_on_kicked">> => false})), + ?errf(T(#{<<"reset_markers">> => 1})), + ?errf(T(#{<<"groupchat">> => [<<"test">>]})), + ?errf(T(#{<<"aff_changes">> => 1})), + ?errf(T(#{<<"remove_on_kicked">> => 1})), check_iqdisc(mod_inbox). mod_global_distrib(_Config) -> From 18db18fb8a7101e7e8f731e13abfd2f1360e2d8d Mon Sep 17 00:00:00 2001 From: Leszek Witkowicz Date: Thu, 10 Dec 2020 13:54:50 +0100 Subject: [PATCH 078/104] make mod_jingle_sip config declarative --- src/config/mongoose_config_parser_toml.erl | 11 +------- src/config/mongoose_config_spec.erl | 1 + src/config/mongoose_config_validator_toml.erl | 16 +----------- src/jingle_sip/mod_jingle_sip.erl | 25 +++++++++++++++---- test/config_parser_SUITE.erl | 8 ++---- 5 files changed, 25 insertions(+), 36 deletions(-) diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index 9083097203..9d02e59656 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -137,16 +137,6 @@ module_opt([<<"redis">>, <<"mod_global_distrib">>|_] = Path, V) -> [{redis, Redis}]; module_opt([<<"hosts_refresh_interval">>, <<"mod_global_distrib">>|_], V) -> [{hosts_refresh_interval, V}]; -module_opt([<<"proxy_host">>, <<"mod_jingle_sip">>|_], V) -> - [{proxy_host, b2l(V)}]; -module_opt([<<"proxy_port">>, <<"mod_jingle_sip">>|_], V) -> - [{proxy_port, V}]; -module_opt([<<"listen_port">>, <<"mod_jingle_sip">>|_], V) -> - [{listen_port, V}]; -module_opt([<<"local_host">>, <<"mod_jingle_sip">>|_], V) -> - [{local_host, b2l(V)}]; -module_opt([<<"sdp_origin">>, <<"mod_jingle_sip">>|_], V) -> - [{sdp_origin, b2l(V)}]; module_opt([<<"ram_key_size">>, <<"mod_keystore">>|_], V) -> [{ram_key_size, V}]; module_opt([<<"keys">>, <<"mod_keystore">>|_] = Path, V) -> @@ -535,6 +525,7 @@ node_to_string(Node) -> [binary_to_list(Node)]. Mod =/= <<"mod_event_pusher">>, Mod =/= <<"mod_extdisco">>, Mod =/= <<"mod_inbox">>, + Mod =/= <<"mod_jingle_sip">>, Mod =/= <<"mod_last">>, Mod =/= <<"mod_mam_meta">>, Mod =/= <<"mod_muc">>, diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index 4e4350d133..1710af3a8e 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -740,6 +740,7 @@ all_modules() -> mod_event_pusher, mod_extdisco, mod_inbox, + mod_jingle_sip, mod_last, mod_mam_meta, mod_muc, diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index 9df85a2b5d..29b66be421 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -166,21 +166,6 @@ validate([<<"token_bytes">>, <<"mod_http_upload">>, <<"modules">>|_], validate([<<"iqdisc">>, <<"mod_time">>, <<"modules">>|_], [{iqdisc, V}]) -> validate_iqdisc(V); -validate([<<"listen_port">>, <<"mod_jingle_sip">>, <<"modules">>|_], - [{listen_port, V}]) -> - validate_network_port(V); -validate([<<"local_host">>, <<"mod_jingle_sip">>, <<"modules">>|_], - [{local_host, V}]) -> - validate_network_address(V); -validate([<<"proxy_host">>, <<"mod_jingle_sip">>, <<"modules">>|_], - [{proxy_host, V}]) -> - validate_network_address(V); -validate([<<"proxy_port">>, <<"mod_jingle_sip">>, <<"modules">>|_], - [{proxy_port, V}]) -> - validate_network_port(V); -validate([<<"sdp_origin">>, <<"mod_jingle_sip">>, <<"modules">>|_], - [{sdp_origin, V}]) -> - validate_ip_address(V); validate([<<"iqdisc">>, <<"mod_sic">>, <<"modules">>|_], [{iqdisc, V}]) -> validate_iqdisc(V); @@ -212,6 +197,7 @@ validate(V, string, url) -> validate_url(V); validate(V, string, domain_template) -> validate_domain_template(V); validate(V, string, ip_address) -> validate_ip_address(V); validate(V, string, ip_mask) -> validate_ip_mask_string(V); +validate(V, string, network_address) -> validate_network_address(V); validate(V, string, non_empty) -> validate_non_empty_string(V); validate(V, string, dirname) -> validate_dirname(V); validate(V, atom, module) -> validate_module(V); diff --git a/src/jingle_sip/mod_jingle_sip.erl b/src/jingle_sip/mod_jingle_sip.erl index 468b4a1751..9983f94653 100644 --- a/src/jingle_sip/mod_jingle_sip.erl +++ b/src/jingle_sip/mod_jingle_sip.erl @@ -21,17 +21,17 @@ -behaviour(gen_mod). -behaviour(mongoose_module_metrics). --include_lib("nksip/include/nksip.hrl"). --include_lib("nksip/include/nksip_call.hrl"). --include("mongoose.hrl"). -include("jlib.hrl"). - +-include("mongoose.hrl"). +-include("mongoose_config_spec.hrl"). -include_lib("nklib/include/nklib.hrl"). +-include_lib("nksip/include/nksip.hrl"). +-include_lib("nksip/include/nksip_call.hrl"). -define(SERVICE, "mim_sip"). %% gen_mod callbacks --export([start/2, stop/1]). +-export([start/2, stop/1, config_spec/0]). -export([intercept_jingle_stanza/2]). @@ -85,6 +85,21 @@ stop(Host) -> ejabberd_hooks:delete(hooks(Host)), ok. +-spec config_spec() -> mongoose_config_spec:config_section(). +config_spec() -> + #section{ + items = #{<<"proxy_host">> => #option{type = string, + validate = network_address}, + <<"proxy_port">> => #option{type = integer, + validate = port}, + <<"listen_port">> => #option{type = integer, + validate = port}, + <<"local_host">> => #option{type = string, + validate = network_address}, + <<"sdp_origin">> => #option{type = string, + validate = ip_address} + } + }. hooks(Host) -> [{c2s_preprocessing_hook, Host, ?MODULE, intercept_jingle_stanza, 75}]. diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index f19e338abf..f00e2dff54 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -1806,22 +1806,18 @@ mod_jingle_sip(_Config) -> <<"listen_port">> => 5601, <<"local_host">> => <<"localhost">>, <<"sdp_origin">> => <<"127.0.0.1">> - }, + }, MBase = [ {listen_port, 5601}, {local_host, "localhost"}, {proxy_host, "proxxxy"}, {proxy_port, 5600}, {sdp_origin, "127.0.0.1"} - ], + ], ?eqf(modopts(mod_jingle_sip, MBase), T(Base)), ?errf(T(Base#{<<"proxy_host">> => -1})), - ?errf(T(Base#{<<"proxy_host">> => <<"test test">>})), ?errf(T(Base#{<<"listen_port">> => -1})), - ?errf(T(Base#{<<"listen_port">> => 10000000})), - ?errf(T(Base#{<<"proxy_port">> => -1})), ?errf(T(Base#{<<"proxy_port">> => 10000000})), - ?errf(T(Base#{<<"local_host">> => 1})), ?errf(T(Base#{<<"local_host">> => <<"ok ok">>})), ?errf(T(Base#{<<"sdp_origin">> => <<"aaaaaaaaa">>})). From e84c2afc8977d3da78460493f182efed372e10f5 Mon Sep 17 00:00:00 2001 From: Leszek Witkowicz Date: Fri, 11 Dec 2020 14:25:38 +0100 Subject: [PATCH 079/104] make mod_keystore config declarative --- doc/modules/mod_keystore.md | 10 +++--- src/config/mongoose_config_parser_toml.erl | 14 +------- src/config/mongoose_config_spec.erl | 1 + src/config/mongoose_config_validator_toml.erl | 14 +------- src/mod_keystore.erl | 34 +++++++++++++++++-- test/config_parser_SUITE.erl | 20 +++++++---- 6 files changed, 53 insertions(+), 40 deletions(-) diff --git a/doc/modules/mod_keystore.md b/doc/modules/mod_keystore.md index 715ba99209..e7d8324f9d 100644 --- a/doc/modules/mod_keystore.md +++ b/doc/modules/mod_keystore.md @@ -28,9 +28,9 @@ for each virtual XMPP domain to avoid sharing keys between domains!** Size to use when generating RAM-only keys (designated by type `ram`). ### `modules.mod_keystore.keys` -* **Syntax:** Array of TOML tables with the following keys: `"name"`, `"type"`, `"file"`, and following values: {name = `string`, type = `values: "file", "ram"`, file = `string`}. +* **Syntax:** Array of TOML tables with the following keys: `"name"`, `"type"`, `"path"`, and following values: {name = `string`, type = `values: "file", "ram"`, path = `string`}. * **Default:** `[]` -* **Example:** `modules.mod_keystore.keys = [name = "access_psk", type = "file", path = "priv/access_psk"]` +* **Example:** `modules.mod_keystore.keys = [{name = "access_psk", type = "file", path = "priv/access_psk"}]` Names, types, and optional filepaths of the keys. @@ -71,7 +71,7 @@ for each virtual XMPP domain): host = "second.com" [host_config.modules.mod_keystore] - keys = [{name = "access_secret, type = "ram"}, - {name = "access_psk, type = "file", path = "priv/second_access_psk"}, - {name = "provision_psk, type = "file", path = "priv/second_provision_psk"}] + keys = [{name = "access_secret", type = "ram"}, + {name = "access_psk," type = "file", path = "priv/second_access_psk"}, + {name = "provision_psk", type = "file", path = "priv/second_provision_psk"}] ``` diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index 9d02e59656..ee7f339391 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -137,11 +137,6 @@ module_opt([<<"redis">>, <<"mod_global_distrib">>|_] = Path, V) -> [{redis, Redis}]; module_opt([<<"hosts_refresh_interval">>, <<"mod_global_distrib">>|_], V) -> [{hosts_refresh_interval, V}]; -module_opt([<<"ram_key_size">>, <<"mod_keystore">>|_], V) -> - [{ram_key_size, V}]; -module_opt([<<"keys">>, <<"mod_keystore">>|_] = Path, V) -> - Keys = parse_list(Path, V), - [{keys, Keys}]; % General options module_opt([<<"iqdisc">>|_], V) -> {Type, Opts} = maps:take(<<"type">>, V), @@ -255,12 +250,6 @@ mod_global_distrib_connections_endpoints(_, #{<<"host">> := Host, <<"port">> := mod_global_distrib_connections_advertised_endpoints(_, #{<<"host">> := Host, <<"port">> := Port}) -> [{b2l(Host), Port}]. --spec mod_keystore_keys(path(), toml_section()) -> [option()]. -mod_keystore_keys(_, #{<<"name">> := Name, <<"type">> := <<"ram">>}) -> - [{b2a(Name), ram}]; -mod_keystore_keys(_, #{<<"name">> := Name, <<"type">> := <<"file">>, <<"path">> := Path}) -> - [{b2a(Name), {file, b2l(Path)}}]. - -spec iqdisc_value(atom(), toml_section()) -> option(). iqdisc_value(queues, #{<<"workers">> := Workers} = V) -> limit_keys([<<"workers">>], V), @@ -526,6 +515,7 @@ node_to_string(Node) -> [binary_to_list(Node)]. Mod =/= <<"mod_extdisco">>, Mod =/= <<"mod_inbox">>, Mod =/= <<"mod_jingle_sip">>, + Mod =/= <<"mod_keystore">>, Mod =/= <<"mod_last">>, Mod =/= <<"mod_mam_meta">>, Mod =/= <<"mod_muc">>, @@ -570,8 +560,6 @@ handler([_,<<"advertised_endpoints">>, <<"connections">>, <<"mod_global_distrib" fun mod_global_distrib_connections_advertised_endpoints/2; handler([_,<<"tls">>, <<"connections">>, <<"mod_global_distrib">>, <<"modules">>]) -> fun mod_global_distrib_tls_option/2; -handler([_, <<"keys">>, <<"mod_keystore">>, <<"modules">>]) -> - fun mod_keystore_keys/2; %% host_config handler([_, <<"host_config">>]) -> fun process_host_item/2; diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index 1710af3a8e..be2375f778 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -741,6 +741,7 @@ all_modules() -> mod_extdisco, mod_inbox, mod_jingle_sip, + mod_keystore, mod_last, mod_mam_meta, mod_muc, diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index 29b66be421..6b7b67d612 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -169,13 +169,6 @@ validate([<<"iqdisc">>, <<"mod_time">>, <<"modules">>|_], validate([<<"iqdisc">>, <<"mod_sic">>, <<"modules">>|_], [{iqdisc, V}]) -> validate_iqdisc(V); -validate([item, <<"keys">>, <<"mod_keystore">>, <<"modules">>|_], - [V]) -> - validate_keystore_key(V); -validate([<<"ram_key_size">>, <<"mod_keystore">>, <<"modules">>|_], - [{ram_key_size, V}]) -> - validate_non_negative_integer(V); - validate(_Path, _Value) -> ok. @@ -198,6 +191,7 @@ validate(V, string, domain_template) -> validate_domain_template(V); validate(V, string, ip_address) -> validate_ip_address(V); validate(V, string, ip_mask) -> validate_ip_mask_string(V); validate(V, string, network_address) -> validate_network_address(V); +validate(V, string, filename) -> validate_filename(V); validate(V, string, non_empty) -> validate_non_empty_string(V); validate(V, string, dirname) -> validate_dirname(V); validate(V, atom, module) -> validate_module(V); @@ -390,11 +384,5 @@ validate_dirname(Dirname) -> error(#{what => invalid_dirname, dirname => Dirname, reason => Reason}) end. -validate_keystore_key({Name, ram}) -> - validate_non_empty_atom(Name); -validate_keystore_key({Name, {file, Path}}) -> - validate_non_empty_atom(Name), - validate_filename(Path). - validate_pool_name(V) -> validate_non_empty_atom(V). diff --git a/src/mod_keystore.erl b/src/mod_keystore.erl index 123e9faae0..1cefc48d6b 100644 --- a/src/mod_keystore.erl +++ b/src/mod_keystore.erl @@ -5,7 +5,8 @@ %% gen_mod callbacks -export([start/2, - stop/1]). + stop/1, + config_spec/0]). %% Hook handlers -export([get_key/2]). @@ -14,6 +15,7 @@ -export([validate_opts/1]). -export([config_metrics/1]). +-export([process_keys/1]). %% Public types -export_type([key/0, @@ -22,8 +24,9 @@ key_name/0, raw_key/0]). --include("mongoose.hrl"). -include("mod_keystore.hrl"). +-include("mongoose.hrl"). +-include("mongoose_config_spec.hrl"). -define(DEFAULT_RAM_KEY_SIZE, 2048). -define(IOL2B(L), iolist_to_binary(L)). @@ -79,6 +82,33 @@ stop(Domain) -> clear_keystore_ets(Domain), ok. +-spec config_spec() -> mongoose_config_spec:config_section(). +config_spec() -> + #section{ + items = #{<<"ram_key_size">> => #option{type = integer, + validate = non_negative}, + <<"keys">> => #list{items = keys_spec()} + } + }. + +keys_spec() -> + #section{ + items = #{<<"name">> => #option{type = atom, + validate = non_empty}, + <<"type">> => #option{type = atom, + validate = {enum, [file, ram]}}, + <<"path">> => #option{type = string, + validate = filename} + }, + required = [<<"name">>, <<"type">>], + process = fun ?MODULE:process_keys/1 + }. + +process_keys([{name, Name},{type, ram}]) -> + {Name, ram}; +process_keys([{name, Name}, {path, File}, {type, file}]) -> + {Name, {file, File}}. + %% %% Hook handlers %% diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index f00e2dff54..abcadb614a 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -1454,7 +1454,7 @@ mod_disco(_Config) -> mod_extdisco(_Config) -> T = fun(Opts) -> #{<<"modules">> => - #{<<"mod_extdisco">> => + #{<<"mod_extdisco">> => #{<<"service">> => [Opts]}}} end, M = fun(Opts) -> modopts(mod_extdisco, [Opts]) end, @@ -1822,7 +1822,11 @@ mod_jingle_sip(_Config) -> ?errf(T(Base#{<<"sdp_origin">> => <<"aaaaaaaaa">>})). mod_keystore(_Config) -> - T = fun(Opts) -> #{<<"modules">> => #{<<"mod_keystore">> => Opts}} end, + T = fun(Keys, Size) -> #{<<"modules">> => + #{<<"mod_keystore">> => + #{<<"keys">> => Keys, + <<"ram_key_size">> => Size}}} + end, Keys = [#{<<"name">> => <<"access_secret">>, <<"type">> => <<"ram">>}, #{<<"name">> => <<"access_psk">>, @@ -1836,14 +1840,16 @@ mod_keystore(_Config) -> <<"path">> => <<"does/not/esit">>}, InvalidTypeKey = #{<<"name">> => <<"provision_psk">>, <<"type">> => <<"some_cooool_type">>}, + Size = 10000, + InvalidTypeSize = -1, MKeys = [{access_secret, ram}, {access_psk, {file, "priv/access_psk"}}, {provision_psk, {file, "priv/provision_psk"}}], - Base = #{<<"keys">> => Keys, <<"ram_key_size">> => 10000}, - MBase = [{keys, MKeys}, {ram_key_size, 10000}], - ?eqf(modopts(mod_keystore, MBase), T(Base)), - ?errf(T(Base#{<<"keys">> => [NotExistingKey]})), - ?errf(T(Base#{<<"keys">> => [InvalidTypeKey]})). + MBase = [{keys, MKeys}, {ram_key_size, Size}], + ?eqf(modopts(mod_keystore, MBase), T(Keys, Size)), + ?errf(T([NotExistingKey], Size)), + ?errf(T([InvalidTypeKey], Size)), + ?errf(T(Keys, InvalidTypeSize)). mod_last(_Config) -> check_iqdisc(mod_last), From 3aa201a7db05249519a0ac83fdc23cb05e18cb33 Mon Sep 17 00:00:00 2001 From: Leszek Witkowicz Date: Fri, 11 Dec 2020 20:57:27 +0100 Subject: [PATCH 080/104] make mod_global_distrib config declarative --- doc/modules/mod_global_distrib.md | 14 +- src/config/mongoose_config_parser_toml.erl | 116 +---------------- src/config/mongoose_config_spec.erl | 1 + src/config/mongoose_config_validator_toml.erl | 116 +---------------- src/global_distrib/mod_global_distrib.erl | 123 +++++++++++++++++- test/config_parser_SUITE.erl | 42 +++--- 6 files changed, 146 insertions(+), 266 deletions(-) diff --git a/doc/modules/mod_global_distrib.md b/doc/modules/mod_global_distrib.md index 049b24fa18..b43763431a 100644 --- a/doc/modules/mod_global_distrib.md +++ b/doc/modules/mod_global_distrib.md @@ -136,6 +136,8 @@ The interval telling how often Redis should be asked if new hosts appeared. ### Connections' options +**Note:** This section is mandatory. + #### `modules.mod_global_distrib.connections.endpoints` * **Syntax:** Array of TOML tables with the following keys: `host` and `port`, and the following values: {host = `string`, port = `non_negative_integer`} * **Default:** `[{host = "LocalHost", port = 5555}]` @@ -146,8 +148,8 @@ A list of endpoints on which the server will listen for connections. The endpoint list will be shared with other datacenters via the replicated backend. #### `modules.mod_global_distrib.connections.advertised_endpoints` -* **Syntax:** Array of TOML tables with the following keys: `host` and `port`, and the following values: {host = `string`, port = `non_negative_integer`} **or** `false` -* **Default:** `false` +* **Syntax:** Array of TOML tables with the following keys: `host` and `port`, and the following values: {host = `string`, port = `non_negative_integer`} +* **Default:** not set * **Example:** `advertised_endpoints = [{host = "172.16.0.2", port = 5555}]` A list of endpoints which will be advertised in Redis and therefore used to establish connection with this node by other nodes. If not specified, `endpoints` value (after resolution) is considered `advertised_endpoints`. The host may be either IP or domain, just like in case of endpoints. The difference is, the domain name won't be resolved but inserted directly to the mappings backend instead. @@ -175,7 +177,7 @@ A separate timer is maintained for every remote domain. Endpoint refresh interval, when array of endpoints is empty. #### `modules.mod_global_distrib.connections.disabled_gc_interval` -* **Syntax:** non-negative integer, value given in seconds +* **Syntax:** positive integer, value given in seconds * **Default:** `60` * **Example:** `disabled_gc_interval = 60` @@ -184,9 +186,11 @@ It means that disabled endpoints are periodically verified and if Global Distrib ### TLS options +**Note:** This section is mandatory. + #### `modules.mod_global_distrib.connections.tls.enabled` * **Syntax:** boolean -* **Default:** `false` +* **Default:** `false`, this option is mandatory * **Example:** `enabled = true` To enable TLS support the `cacertfile` and `certfile` options have to be present. @@ -226,7 +230,7 @@ Cipher suites to use with StartTLS or TLS. Please refer to the [OpenSSL document Name of the redis pool defined in [outgoing pools](../advanced-configuration/outgoing-connections.md). #### `modules.mod_global_distrib.redis.expire_after` -* **Syntax:** non-negative integer +* **Syntax:** positive integer * **Default:** `120` * **Example:** `expire_after = 120` diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index ee7f339391..a360b5be18 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -117,26 +117,6 @@ module_opt([<<"max_file_size">>, <<"mod_http_upload">>|_], V) -> module_opt([<<"s3">>, <<"mod_http_upload">>|_] = Path, V) -> S3Opts = parse_section(Path, V), [{s3, S3Opts}]; -module_opt([<<"global_host">>, <<"mod_global_distrib">>|_], V) -> - [{global_host, b2l(V)}]; -module_opt([<<"local_host">>, <<"mod_global_distrib">>|_], V) -> - [{local_host, b2l(V)}]; -module_opt([<<"message_ttl">>, <<"mod_global_distrib">>|_], V) -> - [{message_ttl, V}]; -module_opt([<<"connections">>, <<"mod_global_distrib">>|_] = Path, V) -> - Conns = parse_section(Path, V), - [{connections, Conns}]; -module_opt([<<"cache">>, <<"mod_global_distrib">>|_] = Path, V) -> - Cache = parse_section(Path, V), - [{cache, Cache}]; -module_opt([<<"bounce">>, <<"mod_global_distrib">>|_] = Path, V) -> - Bounce = parse_section(Path, V, fun format_global_distrib_bounce/1), - [{bounce, Bounce}]; -module_opt([<<"redis">>, <<"mod_global_distrib">>|_] = Path, V) -> - Redis = parse_section(Path, V), - [{redis, Redis}]; -module_opt([<<"hosts_refresh_interval">>, <<"mod_global_distrib">>|_], V) -> - [{hosts_refresh_interval, V}]; % General options module_opt([<<"iqdisc">>|_], V) -> {Type, Opts} = maps:take(<<"type">>, V), @@ -181,75 +161,6 @@ mod_http_upload_s3([<<"access_key_id">>|_], V) -> mod_http_upload_s3([<<"secret_access_key">>|_], V) -> [{secret_access_key, b2l(V)}]. --spec mod_global_distrib_connections(path(), toml_value()) -> [option()]. -mod_global_distrib_connections([<<"endpoints">>|_] = Path, V) -> - Endpoints = parse_list(Path, V), - [{endpoints, Endpoints}]; -mod_global_distrib_connections([<<"advertised_endpoints">>|_], false) -> - [{advertised_endpoints, false}]; -mod_global_distrib_connections([<<"advertised_endpoints">>|_] = Path, V) -> - Endpoints = parse_list(Path, V), - [{advertised_endpoints, Endpoints}]; -mod_global_distrib_connections([<<"connections_per_endpoint">>|_], V) -> - [{connections_per_endpoint, V}]; -mod_global_distrib_connections([<<"endpoint_refresh_interval">>|_], V) -> - [{endpoint_refresh_interval, V}]; -mod_global_distrib_connections([<<"endpoint_refresh_interval_when_empty">>|_], V) -> - [{endpoint_refresh_interval_when_empty, V}]; -mod_global_distrib_connections([<<"disabled_gc_interval">>|_], V) -> - [{disabled_gc_interval, V}]; -mod_global_distrib_connections([<<"tls">>|_] = Path, V) -> - TLSOpts = parse_section(Path, V, fun format_global_distrib_tls/1), - [{tls_opts, TLSOpts}]. - --spec format_global_distrib_tls([option()]) -> option(). -format_global_distrib_tls(Opts) -> - case proplists:lookup(enabled, Opts) of - {enabled, true} -> proplists:delete(enabled, Opts); - _ -> false - end. - --spec mod_global_distrib_cache(path(), toml_value()) -> [option()]. -mod_global_distrib_cache([<<"cache_missed">>|_], V) -> - [{cache_missed, V}]; -mod_global_distrib_cache([<<"domain_lifetime_seconds">>|_], V) -> - [{domain_lifetime_seconds, V}]; -mod_global_distrib_cache([<<"jid_lifetime_seconds">>|_], V) -> - [{jid_lifetime_seconds, V}]; -mod_global_distrib_cache([<<"max_jids">>|_], V) -> - [{max_jids, V}]. - --spec mod_global_distrib_redis(path(), toml_value()) -> [option()]. -mod_global_distrib_redis([<<"pool">>|_], V) -> - [{pool, b2a(V)}]; -mod_global_distrib_redis([<<"expire_after">>|_], V) -> - [{expire_after, V}]; -mod_global_distrib_redis([<<"refresh_after">>|_], V) -> - [{refresh_after, V}]. - --spec mod_global_distrib_bounce(path(), toml_value()) -> [option()]. -mod_global_distrib_bounce([<<"resend_after_ms">>|_], V) -> - [{resend_after_ms, V}]; -mod_global_distrib_bounce([<<"max_retries">>|_], V) -> - [{max_retries, V}]; -mod_global_distrib_bounce([<<"enabled">>|_], V) -> - [{enabled, V}]. - --spec format_global_distrib_bounce([option()]) -> option(). -format_global_distrib_bounce(Opts) -> - case proplists:lookup(enabled, Opts) of - {enabled, false} -> false; - _ -> proplists:delete(enabled, Opts) - end. - --spec mod_global_distrib_connections_endpoints(path(), toml_section()) -> [option()]. -mod_global_distrib_connections_endpoints(_, #{<<"host">> := Host, <<"port">> := Port}) -> - [{b2l(Host), Port}]. - --spec mod_global_distrib_connections_advertised_endpoints(path(), toml_section()) -> [option()]. -mod_global_distrib_connections_advertised_endpoints(_, #{<<"host">> := Host, <<"port">> := Port}) -> - [{b2l(Host), Port}]. - -spec iqdisc_value(atom(), toml_section()) -> option(). iqdisc_value(queues, #{<<"workers">> := Workers} = V) -> limit_keys([<<"workers">>], V), @@ -264,18 +175,6 @@ process_host_item(Path, M) -> {_Host, Sections} = maps:take(<<"host">>, M), parse_section(Path, Sections). -%% path: (host_config[].)modules.mod_global_distrib.connections.tls.* --spec fast_tls_option(path(), toml_value()) -> [option()]. -fast_tls_option([<<"certfile">>|_], V) -> [{certfile, b2l(V)}]; -fast_tls_option([<<"cacertfile">>|_], V) -> [{cafile, b2l(V)}]; -fast_tls_option([<<"dhfile">>|_], V) -> [{dhfile, b2l(V)}]; -fast_tls_option([<<"ciphers">>|_], V) -> [{ciphers, b2l(V)}]. - -mod_global_distrib_tls_option([<<"enabled">>|_], V) -> - [{enabled, V}]; -mod_global_distrib_tls_option(P, V) -> - fast_tls_option(P, V). - set_overrides(Overrides, State) -> lists:foldl(fun({override, Scope}, CurrentState) -> mongoose_config_parser:override(Scope, CurrentState) @@ -513,6 +412,7 @@ node_to_string(Node) -> [binary_to_list(Node)]. Mod =/= <<"mod_disco">>, Mod =/= <<"mod_event_pusher">>, Mod =/= <<"mod_extdisco">>, + Mod =/= <<"mod_global_distrib">>, Mod =/= <<"mod_inbox">>, Mod =/= <<"mod_jingle_sip">>, Mod =/= <<"mod_keystore">>, @@ -546,20 +446,6 @@ handler([_, <<"riak">>, Mod, <<"modules">>]) when ?HAS_NO_SPEC(Mod) -> fun riak_opts/2; handler([_, <<"s3">>, <<"mod_http_upload">>, <<"modules">>]) -> fun mod_http_upload_s3/2; -handler([_, <<"connections">>, <<"mod_global_distrib">>, <<"modules">>]) -> - fun mod_global_distrib_connections/2; -handler([_, <<"cache">>, <<"mod_global_distrib">>, <<"modules">>]) -> - fun mod_global_distrib_cache/2; -handler([_, <<"bounce">>, <<"mod_global_distrib">>, <<"modules">>]) -> - fun mod_global_distrib_bounce/2; -handler([_, <<"redis">>, <<"mod_global_distrib">>, <<"modules">>]) -> - fun mod_global_distrib_redis/2; -handler([_,<<"endpoints">>, <<"connections">>, <<"mod_global_distrib">>, <<"modules">>]) -> - fun mod_global_distrib_connections_endpoints/2; -handler([_,<<"advertised_endpoints">>, <<"connections">>, <<"mod_global_distrib">>, <<"modules">>]) -> - fun mod_global_distrib_connections_advertised_endpoints/2; -handler([_,<<"tls">>, <<"connections">>, <<"mod_global_distrib">>, <<"modules">>]) -> - fun mod_global_distrib_tls_option/2; %% host_config handler([_, <<"host_config">>]) -> fun process_host_item/2; diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index be2375f778..f233791af4 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -739,6 +739,7 @@ all_modules() -> mod_disco, mod_event_pusher, mod_extdisco, + mod_global_distrib, mod_inbox, mod_jingle_sip, mod_keystore, diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index 6b7b67d612..ca21603ae2 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -18,118 +18,6 @@ validate(Path, [F]) when is_function(F, 1) -> validate(Path, F(?HOST)); %% Modules -validate([<<"enabled">>, <<"bounce">>, - <<"mod_global_distrib">>, <<"modules">>|_], - [{enabled, true}]) -> - ok; -validate([<<"bounce">>, <<"mod_global_distrib">>, <<"modules">>|_], - [{bounce, false}]) -> - ok; -validate([<<"max_retries">>, <<"bounce">>, - <<"mod_global_distrib">>, <<"modules">>|_], - [{max_retries, V}]) -> - validate_non_negative_integer(V); -validate([<<"resend_after_ms">>, <<"bounce">>, - <<"mod_global_distrib">>, <<"modules">>|_], - [{resend_after_ms, V}]) -> - validate_non_negative_integer(V); -validate([<<"cache_missed">>, <<"cache">>, - <<"mod_global_distrib">>, <<"modules">>|_], - [{cache_missed, V}]) -> - validate_boolean(V); -validate([<<"domain_lifetime_seconds">>, <<"cache">>, - <<"mod_global_distrib">>, <<"modules">>|_], - [{domain_lifetime_seconds, V}]) -> - validate_non_negative_integer(V); -validate([<<"jid_lifetime_seconds">>, <<"cache">>, - <<"mod_global_distrib">>, <<"modules">>|_], - [{jid_lifetime_seconds, V}]) -> - validate_non_negative_integer(V); -validate([<<"max_jids">>, <<"cache">>, - <<"mod_global_distrib">>, <<"modules">>|_], - [{max_jids, V}]) -> - validate_non_negative_integer(V); -validate([<<"advertised_endpoints">>, <<"connections">>, - <<"mod_global_distrib">>, <<"modules">>|_], - [false]) -> - ok; -validate([<<"host">>, item, <<"advertised_endpoints">>, <<"connections">>, - <<"mod_global_distrib">>, <<"modules">>|_], - [V]) -> - validate_network_address(V); -validate([<<"port">>, item, <<"advertised_endpoints">>, <<"connections">>, - <<"mod_global_distrib">>, <<"modules">>|_], - [V]) -> - validate_network_port(V); -validate([<<"connections_per_endpoint">>, <<"connections">>, - <<"mod_global_distrib">>, <<"modules">>|_], - [{connections_per_endpoint, V}]) -> - validate_non_negative_integer(V); -validate([<<"disabled_gc_interval">>, <<"connections">>, - <<"mod_global_distrib">>, <<"modules">>|_], - [{disabled_gc_interval, V}]) -> - validate_non_negative_integer(V); -validate([<<"endpoint_refresh_interval">>, <<"connections">>, - <<"mod_global_distrib">>, <<"modules">>|_], - [{endpoint_refresh_interval, V}]) -> - validate_positive_integer(V); -validate([<<"endpoint_refresh_interval_when_empty">>, <<"connections">>, - <<"mod_global_distrib">>, <<"modules">>|_], - [{endpoint_refresh_interval_when_empty, V}]) -> - validate_positive_integer(V); -validate([<<"host">>, item, <<"endpoints">>, <<"connections">>, - <<"mod_global_distrib">>, <<"modules">>|_], - [V]) -> - validate_network_address(V); -validate([<<"port">>, item, <<"endpoints">>, <<"connections">>, - <<"mod_global_distrib">>, <<"modules">>|_], - [V]) -> - validate_network_port(V); -validate([<<"tls">>, <<"connections">>, - <<"mod_global_distrib">>, <<"modules">>|_], - [{tls_opts, false}]) -> - ok; -validate([<<"enabled">>, <<"tls">>, <<"connections">>, - <<"mod_global_distrib">>, <<"modules">>|_], - [{enabled, true}]) -> - ok; -validate([<<"cacertfile">>, <<"tls">>, <<"connections">>, - <<"mod_global_distrib">>, <<"modules">>|_], - [{cafile, V}]) -> - validate_filename(V); -validate([<<"certfile">>, <<"tls">>, <<"connections">>, - <<"mod_global_distrib">>, <<"modules">>|_], - [{certfile, V}]) -> - validate_filename(V); -validate([<<"ciphers">>, <<"tls">>, <<"connections">>, - <<"mod_global_distrib">>, <<"modules">>|_], - [{ciphers, V}]) -> - validate_string(V); -validate([<<"dhfile">>, <<"tls">>, <<"connections">>, - <<"mod_global_distrib">>, <<"modules">>|_], - [{dhfile, V}]) -> - validate_filename(V); -validate([<<"global_host">>, <<"mod_global_distrib">>, <<"modules">>|_], - [{global_host, V}]) -> - validate_domain(V); -validate([<<"hosts_refresh_interval">>, <<"mod_global_distrib">>, <<"modules">>|_], - [{hosts_refresh_interval, V}]) -> - validate_non_negative_integer(V); -validate([<<"local_host">>, <<"mod_global_distrib">>, <<"modules">>|_], - [{local_host, V}]) -> - validate_domain(V); -validate([<<"message_ttl">>, <<"mod_global_distrib">>, <<"modules">>|_], - [{message_ttl, V}]) -> - validate_non_negative_integer(V); -validate([<<"expire_after">>, <<"redis">>, <<"mod_global_distrib">>, <<"modules">>|_], - [{expire_after, V}]) -> - validate_non_negative_integer(V); -validate([<<"pool">>, <<"redis">>, <<"mod_global_distrib">>, <<"modules">>|_], - [{pool, V}]) -> - validate_pool_name(V); -validate([<<"refresh_after">>, <<"redis">>, <<"mod_global_distrib">>, <<"modules">>|_], - [{refresh_after, V}]) -> - validate_non_negative_integer(V); validate([<<"backend">>, <<"mod_http_upload">>, <<"modules">>|_], [{backend, V}]) -> validate_backend(mod_http_upload, V); @@ -187,6 +75,7 @@ validate(V, int_or_infinity_or_atom, positive) -> validate(V, int_or_atom, positive) -> validate_positive_integer_or_atom(V, never); validate(V, string, url) -> validate_url(V); +validate(V, string, domain) -> validate_domain(V); validate(V, string, domain_template) -> validate_domain_template(V); validate(V, string, ip_address) -> validate_ip_address(V); validate(V, string, ip_mask) -> validate_ip_mask_string(V); @@ -362,9 +251,6 @@ safe_call_validator(F, Value) -> #{reason => Reason, stacktrace => Stacktrace} end. -validate_network_port(Value) -> - validate_range(Value, 0, 65535). - validate_range(Value, Min, Max) when Value >= Min, Value =< Max -> ok. diff --git a/src/global_distrib/mod_global_distrib.erl b/src/global_distrib/mod_global_distrib.erl index cf3f412ff2..35c17e4821 100644 --- a/src/global_distrib/mod_global_distrib.erl +++ b/src/global_distrib/mod_global_distrib.erl @@ -21,14 +21,15 @@ -behaviour(gen_mod). -behaviour(mongoose_module_metrics). --include("mongoose.hrl"). --include("jlib.hrl"). -include("global_distrib_metrics.hrl"). +-include("jlib.hrl"). +-include("mongoose.hrl"). +-include("mongoose_config_spec.hrl"). --export([deps/2, start/2, stop/1]). +-export([deps/2, start/2, stop/1, config_spec/0]). -export([find_metadata/2, get_metadata/3, remove_metadata/2, put_metadata/3]). -export([maybe_reroute/1]). - +-export([process_endpoints/1, process_tls/1, process_bounce/1]). %%-------------------------------------------------------------------- %% gen_mod API %% See "gen_mod logic" block below in this file @@ -47,6 +48,120 @@ start(Host, Opts0) -> stop(Host) -> mod_global_distrib_utils:stop(?MODULE, Host, fun stop/0). +-spec config_spec() -> mongoose_config_spec:config_section(). +config_spec() -> + #section{ + items = #{<<"global_host">> => #option{type = string, + validate = domain}, + <<"local_host">> => #option{type = string, + validate = domain}, + <<"message_ttl">> => #option{type = integer, + validate = non_negative}, + <<"hosts_refresh_interval">> => #option{type = integer, + validate = non_negative}, + <<"connections">> => connections_spec(), + <<"redis">> => redis_spec(), + <<"cache">> => cache_spec(), + <<"bounce">> => bounce_spec() + }, + required = [<<"global_host">>, <<"local_host">>, <<"connections">>] + }. + +connections_spec() -> + #section{ + items = #{<<"endpoints">> => #list{items = endpoints_spec()}, + <<"advertised_endpoints">> => #list{items = endpoints_spec()}, + <<"connections_per_endpoint">> => #option{type = integer, + validate = non_negative}, + <<"endpoint_refresh_interval">> => #option{type = integer, + validate = positive}, + <<"endpoint_refresh_interval_when_empty">> => #option{type = integer, + validate = positive}, + <<"disabled_gc_interval">> => #option{type = integer, + validate = positive}, + <<"tls">> => tls_spec() + }, + required = [<<"tls">>] + }. + +endpoints_spec() -> + #section{ + items = #{<<"host">> => #option{type = string, + validate = network_address}, + <<"port">> => #option{type = integer, + validate = port} + }, + required = all, + process = fun ?MODULE:process_endpoints/1 + }. + +tls_spec() -> + #section{ + items = #{<<"enabled">> => #option{type = boolean}, + <<"certfile">> => #option{type = string, + validate = filename}, + <<"cacertfile">> => #option{type = string, + validate = filename, + format = {kv, cafile}}, + <<"ciphers">> => #option{type = string}, + <<"dhfile">> => #option{type = string, + validate = filename} + }, + required = [<<"enabled">>], + process = fun ?MODULE:process_tls/1, + format = {kv, tls_opts} + }. + +redis_spec() -> + #section{ + items = #{<<"pool">> => #option{type = atom, + validate = pool_name}, + <<"expire_after">> => #option{type = integer, + validate = positive}, + <<"refresh_after">> => #option{type = integer, + validate = non_negative} + } + }. + +cache_spec() -> + #section{ + items = #{<<"cache_missed">> => #option{type = boolean}, + <<"domain_lifetime_seconds">> => #option{type = integer, + validate = non_negative}, + <<"jid_lifetime_seconds">> => #option{type = integer, + validate = non_negative}, + <<"max_jids">> => #option{type = integer, + validate = non_negative} + } + }. + +bounce_spec() -> + #section{ + items = #{<<"enabled">> => #option{type = boolean}, + <<"resend_after_ms">> => #option{type = integer, + validate = non_negative}, + <<"max_retries">> => #option{type = integer, + validate = non_negative} + }, + process = fun ?MODULE:process_bounce/1 + }. + +process_endpoints(KV) -> + {[[{host, Host}],[{port, Port}]], []} = proplists:split(KV, [host, port]), + {Host, Port}. + +process_tls(KV) -> + case proplists:get_value(enabled, KV, false) of + true -> proplists:delete(enabled, KV); + false -> false + end. + +process_bounce(KVs) -> + {[EnabledOpts], Opts} = proplists:split(KVs, [enabled]), + bounce_value(EnabledOpts, Opts). + +bounce_value([{enabled, false}], _) -> false; +bounce_value(_, Opts) -> Opts. %%-------------------------------------------------------------------- %% public functions diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index abcadb614a..6c20de66ad 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -1577,23 +1577,22 @@ mod_global_distrib(_Config) -> {message_ttl, 42}, {redis, RedisOpts} ]), T(Base)), - ?eqf(modopts(mod_global_distrib, - set_pl(connections, - set_pl(advertised_endpoints, false, ConnOpts), - MBase)), - T(Base#{<<"connections">> => TConnOpts#{ - <<"advertised_endpoints">> => false}})), ?eqf(modopts(mod_global_distrib, set_pl(connections, set_pl(tls_opts, false, ConnOpts), MBase)), T(Base#{<<"connections">> => TConnOpts#{<<"tls">> => #{<<"enabled">> => false}}})), ?eqf(modopts(mod_global_distrib, - set_pl(connections, - set_pl(tls_opts, false, ConnOpts), - MBase)), - T(Base#{<<"connections">> => TConnOpts#{<<"tls">> => #{<<"enabled">> => false}}})), + set_pl(bounce, false, MBase)), + T(Base#{<<"bounce">> => TBounceOpts#{<<"enabled">> => false}})), + %% Global Opts + ?errf(T(Base#{<<"global_host">> => 1})), + ?errf(T(Base#{<<"local_host">> => 1})), + ?errf(T(Base#{<<"message_ttl">> => <<"kek">>})), + ?errf(T(Base#{<<"hosts_refresh_interval">> => -1})), %% Connection opts + ?errf(T(Base#{<<"connections">> => TConnOpts#{ + <<"tls">> =>TTOpts#{<<"enabled">> => <<"yes">>}}})), ?errf(T(Base#{<<"connections">> => TConnOpts#{ <<"tls">> =>TTOpts#{<<"certfile">> => <<"/this/does/not/exist">>}}})), ?errf(T(Base#{<<"connections">> => TConnOpts#{ @@ -1606,17 +1605,14 @@ mod_global_distrib(_Config) -> <<"endpoints">> =>[#{<<"host">> => 234, <<"port">> => 5555}]}})), ?errf(T(Base#{<<"connections">> => TConnOpts#{ <<"advertised_endpoints">> =>[#{<<"host">> => 234, <<"port">> => 5555}]}})), - ?errf(T(Base#{<<"connections">> => TConnOpts#{ - <<"connections_per_endpoint">> => -1}})), - ?errf(T(Base#{<<"connections">> => TConnOpts#{ - <<"connections_per_endpoint">> => <<"kek">>}})), ?errf(T(Base#{<<"connections">> => TConnOpts#{<<"connections_per_endpoint">> => -1}})), - ?errf(T(Base#{<<"connections">> => TConnOpts#{<<"disabled_gc_interval">> => -1}})), + ?errf(T(Base#{<<"connections">> => TConnOpts#{<<"disabled_gc_interval">> => 0}})), ?errf(T(Base#{<<"connections">> => TConnOpts#{<<"endpoint_refresh_interval">> => -1}})), - ?errf(T(Base#{<<"connections">> => TConnOpts#{<<"endpoint_refresh_interval_when_empty">> => -1}})), + ?errf(T(Base#{<<"connections">> => TConnOpts#{ + <<"endpoint_refresh_interval_when_empty">> => -1}})), %% Redis Opts ?errf(T(Base#{<<"redis">> => TRedisOpts#{<<"pool">> => -1}})), - ?errf(T(Base#{<<"redis">> => TRedisOpts#{<<"expire_after">> => -1}})), + ?errf(T(Base#{<<"redis">> => TRedisOpts#{<<"expire_after">> => 0}})), ?errf(T(Base#{<<"redis">> => TRedisOpts#{<<"refresh_after">> => -1}})), %% Cache Opts ?errf(T(Base#{<<"cache">> => TCacheOpts#{<<"cache_missed">> => 1}})), @@ -1624,17 +1620,9 @@ mod_global_distrib(_Config) -> ?errf(T(Base#{<<"cache">> => TCacheOpts#{<<"jid_lifetime_seconds">> => -1}})), ?errf(T(Base#{<<"cache">> => TCacheOpts#{<<"max_jids">> => -1}})), %% Bouncing Opts + ?errf(T(Base#{<<"bounce">> => TCacheOpts#{<<"enabled">> => <<"yes">>}})), ?errf(T(Base#{<<"bounce">> => TCacheOpts#{<<"resend_after_ms">> => -1}})), - ?errf(T(Base#{<<"bounce">> => TCacheOpts#{<<"max_retries">> => -1}})), - %% Global Opts - ?errf(T(Base#{<<"global_host">> => <<"example omm omm omm">>})), - ?errf(T(Base#{<<"global_host">> => 1})), - ?errf(T(Base#{<<"local_host">> => <<"example omm omm omm">>})), - ?errf(T(Base#{<<"local_host">> => 1})), - ?errf(T(Base#{<<"message_ttl">> => <<"kek">>})), - ?errf(T(Base#{<<"message_ttl">> => -1})), - ?errf(T(Base#{<<"hosts_refresh_interval">> => <<"kek">>})), - ?errf(T(Base#{<<"hosts_refresh_interval">> => -1})). + ?errf(T(Base#{<<"bounce">> => TCacheOpts#{<<"max_retries">> => -1}})). mod_event_pusher_sns(_Config) -> RequiredOpts = #{<<"access_key_id">> => <<"AKIAIOSFODNN7EXAMPLE">>, From cbf0e4e27c00d7be106991ff423466116df708c7 Mon Sep 17 00:00:00 2001 From: Leszek Witkowicz Date: Thu, 17 Dec 2020 17:53:57 +0100 Subject: [PATCH 081/104] update mod_global_distrib tests --- test/config_parser_SUITE.erl | 364 +++++++++++++++++++++++------------ 1 file changed, 241 insertions(+), 123 deletions(-) diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index 6c20de66ad..b76394ab29 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -174,6 +174,13 @@ groups() -> mod_disco, mod_inbox, mod_global_distrib, + mod_global_config_connections, + mod_global_config_connections_endpoints, + mod_global_config_connections_advertised_endpoints, + mod_global_config_connections_tls, + mod_global_config_redis, + mod_global_config_cache, + mod_global_config_bounce, mod_event_pusher_sns, mod_event_pusher_push, mod_event_pusher_http, @@ -1499,130 +1506,241 @@ mod_inbox(_Config) -> check_iqdisc(mod_inbox). mod_global_distrib(_Config) -> - ConnOpts = [ - {advertised_endpoints, [{"172.16.0.1", 5555}, {"localhost", 80}, {"example.com", 5555}]}, - {connections_per_endpoint, 22}, - {disabled_gc_interval, 60}, - {endpoint_refresh_interval, 120}, - {endpoint_refresh_interval_when_empty, 5}, - {endpoints, [{"172.16.0.2", 5555}, {"localhost", 80}, {"example.com", 5555}]}, - {tls_opts, [ - {cafile, "/dev/null"}, - {certfile, "/dev/null"}, - {ciphers, "TLS_AES_256_GCM_SHA384"}, - {dhfile, "/dev/null"} - ]} - ], - CacheOpts = [ {cache_missed, false}, {domain_lifetime_seconds, 60}, - {jid_lifetime_seconds, 30}, {max_jids, 9999} ], - BounceOpts = [ {max_retries, 3}, {resend_after_ms, 300} ], - RedisOpts = [ {expire_after, 120}, {pool, global_distrib}, {refresh_after, 60} ], - TTOpts = #{ - <<"enabled">> => true, - <<"certfile">> => <<"/dev/null">>, - <<"cacertfile">> => <<"/dev/null">>, - <<"dhfile">> => <<"/dev/null">>, - <<"ciphers">> => <<"TLS_AES_256_GCM_SHA384">> - }, - TConnOpts = #{ - <<"endpoints">> => [#{<<"host">> => <<"172.16.0.2">>, <<"port">> => 5555}, - #{<<"host">> => <<"localhost">>, <<"port">> => 80}, - #{<<"host">> => <<"example.com">>, <<"port">> => 5555}], - <<"advertised_endpoints">> => - [#{<<"host">> => <<"172.16.0.1">>, <<"port">> => 5555}, - #{<<"host">> => <<"localhost">>, <<"port">> => 80}, - #{<<"host">> => <<"example.com">>, <<"port">> => 5555}], - <<"connections_per_endpoint">> => 22, - <<"disabled_gc_interval">> => 60, - <<"endpoint_refresh_interval">> => 120, - <<"endpoint_refresh_interval_when_empty">> => 5, - <<"tls">> => TTOpts - }, - TCacheOpts = #{ <<"cache_missed">> => false, - <<"domain_lifetime_seconds">> => 60, - <<"jid_lifetime_seconds">> => 30, - <<"max_jids">> => 9999 }, - TBounceOpts = #{ <<"resend_after_ms">> => 300, <<"max_retries">> => 3 }, - TRedisOpts = #{ <<"pool">> => <<"global_distrib">>, - <<"expire_after">> => 120, - <<"refresh_after">> => 60 }, T = fun(Opts) -> #{<<"modules">> => #{<<"mod_global_distrib">> => Opts}} end, - Base = #{ - <<"global_host">> => <<"example.com">>, - <<"local_host">> => <<"datacenter1.example.com">>, - <<"message_ttl">> => 42, - <<"hosts_refresh_interval">> => 100, - <<"connections">> => TConnOpts, - <<"cache">> => TCacheOpts, - <<"bounce">> => TBounceOpts, - <<"redis">> => TRedisOpts - }, - MBase = [ - {bounce, BounceOpts}, - {cache, CacheOpts}, - {connections, ConnOpts}, - {global_host, "example.com"}, - {hosts_refresh_interval, 100}, - {local_host, "datacenter1.example.com"}, - {message_ttl, 42}, - {redis, RedisOpts} - ], - ?eqf(modopts(mod_global_distrib, [ - {bounce, BounceOpts}, - {cache, CacheOpts}, - {connections, ConnOpts}, - {global_host, "example.com"}, - {hosts_refresh_interval, 100}, - {local_host, "datacenter1.example.com"}, - {message_ttl, 42}, - {redis, RedisOpts} - ]), T(Base)), - ?eqf(modopts(mod_global_distrib, - set_pl(connections, - set_pl(tls_opts, false, ConnOpts), - MBase)), - T(Base#{<<"connections">> => TConnOpts#{<<"tls">> => #{<<"enabled">> => false}}})), - ?eqf(modopts(mod_global_distrib, - set_pl(bounce, false, MBase)), - T(Base#{<<"bounce">> => TBounceOpts#{<<"enabled">> => false}})), - %% Global Opts - ?errf(T(Base#{<<"global_host">> => 1})), - ?errf(T(Base#{<<"local_host">> => 1})), - ?errf(T(Base#{<<"message_ttl">> => <<"kek">>})), - ?errf(T(Base#{<<"hosts_refresh_interval">> => -1})), - %% Connection opts - ?errf(T(Base#{<<"connections">> => TConnOpts#{ - <<"tls">> =>TTOpts#{<<"enabled">> => <<"yes">>}}})), - ?errf(T(Base#{<<"connections">> => TConnOpts#{ - <<"tls">> =>TTOpts#{<<"certfile">> => <<"/this/does/not/exist">>}}})), - ?errf(T(Base#{<<"connections">> => TConnOpts#{ - <<"tls">> =>TTOpts#{<<"dhfile">> => <<"/this/does/not/exist">>}}})), - ?errf(T(Base#{<<"connections">> => TConnOpts#{ - <<"tls">> =>TTOpts#{<<"cacertfile">> => <<"/this/does/not/exist">>}}})), - ?errf(T(Base#{<<"connections">> => TConnOpts#{ - <<"tls">> =>TTOpts#{<<"ciphers">> => 42}}})), - ?errf(T(Base#{<<"connections">> => TConnOpts#{ - <<"endpoints">> =>[#{<<"host">> => 234, <<"port">> => 5555}]}})), - ?errf(T(Base#{<<"connections">> => TConnOpts#{ - <<"advertised_endpoints">> =>[#{<<"host">> => 234, <<"port">> => 5555}]}})), - ?errf(T(Base#{<<"connections">> => TConnOpts#{<<"connections_per_endpoint">> => -1}})), - ?errf(T(Base#{<<"connections">> => TConnOpts#{<<"disabled_gc_interval">> => 0}})), - ?errf(T(Base#{<<"connections">> => TConnOpts#{<<"endpoint_refresh_interval">> => -1}})), - ?errf(T(Base#{<<"connections">> => TConnOpts#{ - <<"endpoint_refresh_interval_when_empty">> => -1}})), - %% Redis Opts - ?errf(T(Base#{<<"redis">> => TRedisOpts#{<<"pool">> => -1}})), - ?errf(T(Base#{<<"redis">> => TRedisOpts#{<<"expire_after">> => 0}})), - ?errf(T(Base#{<<"redis">> => TRedisOpts#{<<"refresh_after">> => -1}})), - %% Cache Opts - ?errf(T(Base#{<<"cache">> => TCacheOpts#{<<"cache_missed">> => 1}})), - ?errf(T(Base#{<<"cache">> => TCacheOpts#{<<"domain_lifetime_seconds">> => -1}})), - ?errf(T(Base#{<<"cache">> => TCacheOpts#{<<"jid_lifetime_seconds">> => -1}})), - ?errf(T(Base#{<<"cache">> => TCacheOpts#{<<"max_jids">> => -1}})), - %% Bouncing Opts - ?errf(T(Base#{<<"bounce">> => TCacheOpts#{<<"enabled">> => <<"yes">>}})), - ?errf(T(Base#{<<"bounce">> => TCacheOpts#{<<"resend_after_ms">> => -1}})), - ?errf(T(Base#{<<"bounce">> => TCacheOpts#{<<"max_retries">> => -1}})). + M = fun(Opts) -> modopts(mod_global_distrib, Opts) end, + RequiredOpts = #{ + <<"global_host">> => <<"example.com">>, + <<"local_host">> => <<"datacenter1.example.com">>, + <<"connections">> => #{ + <<"tls">> => #{ + <<"enabled">> => true}}}, + ExpectedCfg = [{connections, [{tls_opts,[]}]}, + {global_host, "example.com"}, + {local_host, "datacenter1.example.com"}], + ?eqf(M(ExpectedCfg), T(RequiredOpts)), + ?eqf(M(ExpectedCfg ++ [{message_ttl, 42}]), + T(RequiredOpts#{<<"message_ttl">> => 42})), + ?eqf(M(ExpectedCfg ++ [{hosts_refresh_interval, 100}]), + T(RequiredOpts#{<<"hosts_refresh_interval">> => 100})), + [?errf(T(maps:remove(Key, RequiredOpts))) || Key <- maps:keys(RequiredOpts)], + ?errf(T(RequiredOpts#{<<"global_host">> => <<"">>})), + ?errf(T(RequiredOpts#{<<"local_host">> => <<"">>})), + ?errf(T(RequiredOpts#{<<"message_ttl">> => -1})), + ?errf(T(RequiredOpts#{<<"hosts_refresh_interval">> => -1})), + ?errf(T(RequiredOpts#{<<"connections">> => #{}})). + +mod_global_config_connections(_Config) -> + RequiredOpts = #{ + <<"tls">> => #{ + <<"enabled">> => true}}, + T = fun(Opts) -> #{<<"modules">> => #{ + <<"mod_global_distrib">> => #{ + <<"global_host">> => <<"example.com">>, + <<"local_host">> => <<"datacenter1.example.com">>, + <<"connections">> => maps:merge(RequiredOpts, Opts)}}} end, + ExpectedCfg = [{tls_opts,[]}], + M = fun(Opts) -> modopts(mod_global_distrib, + [{global_host, "example.com"}, + {local_host, "datacenter1.example.com"}, + {connections, ExpectedCfg ++ Opts}]) end, + ?eqf(M([]), T(#{})), + ?eqf(M([{endpoints, [{"172.16.0.2", 5555}, + {"localhost", 80}, + {"example.com", 5555}]}]), + T(#{<<"endpoints">> => [#{<<"host">> => <<"172.16.0.2">>, + <<"port">> => 5555}, + #{<<"host">> => <<"localhost">>, + <<"port">> => 80}, + #{<<"host">> => <<"example.com">>, + <<"port">> => 5555}]})), + ?eqf(M([{advertised_endpoints, [{"172.16.0.1", 5555}, + {"localhost", 80}, + {"example.com", 5555}]}]), + T(#{<<"advertised_endpoints">> => + [#{<<"host">> => <<"172.16.0.1">>, + <<"port">> => 5555}, + #{<<"host">> => <<"localhost">>, + <<"port">> => 80}, + #{<<"host">> => <<"example.com">>, + <<"port">> => 5555}]})), + ?eqf(M([{connections_per_endpoint, 22}]), + T(#{<<"connections_per_endpoint">> => 22})), + ?eqf(M([{endpoint_refresh_interval, 120}]), + T(#{<<"endpoint_refresh_interval">> => 120})), + ?eqf(M([{endpoint_refresh_interval_when_empty, 5}]), + T(#{<<"endpoint_refresh_interval_when_empty">> => 5})), + ?eqf(M([{disabled_gc_interval, 60}]), + T(#{<<"disabled_gc_interval">> => 60})), + ?errf(T(#{<<"endpoints">> => #{}})), + ?errf(T(#{<<"advertised_endpoints">> => #{}})), + ?errf(T(#{<<"connections_per_endpoint">> => -1})), + ?errf(T(#{<<"endpoint_refresh_interval">> => 0})), + ?errf(T(#{<<"endpoint_refresh_interval_when_empty">> => 0})), + ?errf(T(#{<<"disabled_gc_interval">> => 0})), + ?errf(T(#{<<"tls">> => #{}})), + ?errf(#{<<"modules">> => #{ + <<"mod_global_distrib">> => #{ + <<"global_host">> => <<"example.com">>, + <<"local_host">> => <<"datacenter1.example.com">>, + <<"connections">> => #{}}}}). + +mod_global_config_connections_endpoints(_Config) -> + T = fun(Opts) -> #{<<"modules">> => #{ + <<"mod_global_distrib">> => #{ + <<"global_host">> => <<"example.com">>, + <<"local_host">> => <<"datacenter1.example.com">>, + <<"connections">> => #{ + <<"endpoints">> => Opts, + <<"tls">> => #{ + <<"enabled">> => true}}}}} end, + M = fun(Opts) -> modopts(mod_global_distrib, + [{global_host, "example.com"}, + {local_host, "datacenter1.example.com"}, + {connections, [{endpoints, Opts}, {tls_opts, []}]}]) end, + ?eqf(M([{"172.16.0.2", 5555}, {"localhost", 80}, {"example.com", 5555}]), + T([#{<<"host">> => <<"172.16.0.2">>, <<"port">> => 5555}, + #{<<"host">> => <<"localhost">>, <<"port">> => 80}, + #{<<"host">> => <<"example.com">>, <<"port">> => 5555}])), + ?errf(T([#{<<"host">> => <<"172.16.0.2">>}])), + ?errf(T([#{<<"port">> => 5555}])), + ?errf(T([#{<<"host">> => <<"">>}])), + ?errf(T([#{<<"port">> => -1}])). + +mod_global_config_connections_advertised_endpoints(_Config) -> + T = fun(Opts) -> #{<<"modules">> => #{ + <<"mod_global_distrib">> => #{ + <<"global_host">> => <<"example.com">>, + <<"local_host">> => <<"datacenter1.example.com">>, + <<"connections">> => #{ + <<"advertised_endpoints">> => Opts, + <<"tls">> => #{ + <<"enabled">> => true}}}}} end, + M = fun(Opts) -> modopts(mod_global_distrib, + [{global_host, "example.com"}, + {local_host, "datacenter1.example.com"}, + {connections, [{advertised_endpoints, Opts}, {tls_opts, []}]}]) end, + ?eqf(M([{"172.16.0.2", 5555}, {"localhost", 80}, {"example.com", 5555}]), + T([#{<<"host">> => <<"172.16.0.2">>, <<"port">> => 5555}, + #{<<"host">> => <<"localhost">>, <<"port">> => 80}, + #{<<"host">> => <<"example.com">>, <<"port">> => 5555}])), + ?errf(T([#{<<"host">> => <<"172.16.0.2">>}])), + ?errf(T([#{<<"port">> => 5555}])), + ?errf(T([#{<<"host">> => <<"">>}])), + ?errf(T([#{<<"port">> => -1}])). + +mod_global_config_connections_tls(_Config) -> + RequiredOpts = #{<<"enabled">> => true}, + T = fun(Opts) -> #{<<"modules">> => #{ + <<"mod_global_distrib">> => #{ + <<"global_host">> => <<"example.com">>, + <<"local_host">> => <<"datacenter1.example.com">>, + <<"connections">> => #{ + <<"tls">> => maps:merge(RequiredOpts, Opts)}}}} end, + M = fun(Opts) -> modopts(mod_global_distrib, + [{global_host, "example.com"}, + {local_host, "datacenter1.example.com"}, + {connections, [{tls_opts, Opts}]}]) end, + ?eqf(M([]), T(#{})), + ?eqf(M(false), + T(#{<<"enabled">> => false})), + ?eqf(M([{certfile, "/dev/null"}]), + T(#{<<"certfile">> => <<"/dev/null">>})), + ?eqf(M([{cafile, "/dev/null"}]), + T(#{<<"cacertfile">> => <<"/dev/null">>})), + ?eqf(M([{dhfile, "/dev/null"}]), + T(#{<<"dhfile">> => <<"/dev/null">>})), + ?eqf(M([{ciphers, "TLS_AES_256_GCM_SHA384"}]), + T(#{<<"ciphers">> => <<"TLS_AES_256_GCM_SHA384">>})), + ?errf(T(#{<<"enabled">> => <<"yes">>})), + ?errf(T(#{<<"certfile">> => <<"/this/does/not/exist">>})), + ?errf(T(#{<<"cacertfile">> => <<"/this/does/not/exist">>})), + ?errf(T(#{<<"dhfile">> => <<"/this/does/not/exist">>})), + ?errf(T(#{<<"ciphers">> => 42})), + ?errf(#{<<"modules">> => #{ + <<"mod_global_distrib">> => #{ + <<"global_host">> => <<"example.com">>, + <<"local_host">> => <<"datacenter1.example.com">>, + <<"connections">> => #{ + <<"tls">> => #{}}}}}). + +mod_global_config_redis(_Config) -> + T = fun(Opts) -> #{<<"modules">> => #{ + <<"mod_global_distrib">> => #{ + <<"global_host">> => <<"example.com">>, + <<"local_host">> => <<"datacenter1.example.com">>, + <<"connections">> => #{ + <<"tls">> => #{ + <<"enabled">> => true}}, + <<"redis">> => Opts}}} end, + M = fun(Opts) -> modopts(mod_global_distrib, + [{global_host, "example.com"}, + {local_host, "datacenter1.example.com"}, + {connections, [{tls_opts,[]}]}, + {redis, Opts}]) end, + ?eqf(M([]), T(#{})), + ?eqf(M([{pool, global_distrib}]), + T(#{<<"pool">> => <<"global_distrib">>})), + ?eqf(M([{expire_after, 120}]), + T(#{<<"expire_after">> => 120})), + ?eqf(M([{refresh_after, 60}]), + T(#{<<"refresh_after">> => 60})), + ?errf(T(#{<<"pool">> => <<"">>})), + ?errf(T(#{<<"expire_after">> => 0})), + ?errf(T(#{<<"refresh_after">> => -1})). + +mod_global_config_cache(_Config) -> + T = fun(Opts) -> #{<<"modules">> => #{ + <<"mod_global_distrib">> => #{ + <<"global_host">> => <<"example.com">>, + <<"local_host">> => <<"datacenter1.example.com">>, + <<"connections">> => #{ + <<"tls">> => #{ + <<"enabled">> => true}}, + <<"cache">> => Opts}}} end, + M = fun(Opts) -> modopts(mod_global_distrib, + [{global_host, "example.com"}, + {local_host, "datacenter1.example.com"}, + {connections, [{tls_opts,[]}]}, + {cache, Opts}]) end, + ?eqf(M([]), T(#{})), + ?eqf(M([{cache_missed, false}]), + T(#{<<"cache_missed">> => false})), + ?eqf(M([{domain_lifetime_seconds, 60}]), + T(#{<<"domain_lifetime_seconds">> => 60})), + ?eqf(M([{jid_lifetime_seconds, 30}]), + T(#{<<"jid_lifetime_seconds">> => 30})), + ?eqf(M([{max_jids, 9999}]), + T(#{<<"max_jids">> => 9999})), + ?errf(T(#{<<"cache_missed">> => <<"yes">>})), + ?errf(T(#{<<"domain_lifetime_seconds">> => -1})), + ?errf(T(#{<<"jid_lifetime_seconds">> => -1})), + ?errf(T(#{<<"max_jids">> => -1})). + +mod_global_config_bounce(_Config) -> + T = fun(Opts) -> #{<<"modules">> => #{ + <<"mod_global_distrib">> => #{ + <<"global_host">> => <<"example.com">>, + <<"local_host">> => <<"datacenter1.example.com">>, + <<"connections">> => #{ + <<"tls">> => #{ + <<"enabled">> => true}}, + <<"bounce">> => Opts}}} end, + M = fun(Opts) -> modopts(mod_global_distrib, + [{global_host, "example.com"}, + {local_host, "datacenter1.example.com"}, + {connections, [{tls_opts,[]}]}, + {bounce, Opts}]) end, + ?eqf(M(false), + T(#{<<"enabled">> => false})), + ?eqf(M([]), + T(#{<<"enabled">> => true})), + ?eqf(M([{resend_after_ms, 300}]), + T(#{<<"resend_after_ms">> => 300})), + ?eqf(M([{max_retries, 3}]), + T(#{<<"max_retries">> => 3})), + ?errf(T(#{<<"enabled">> => <<"">>})), + ?errf(T(#{<<"resend_after_ms">> => -1})), + ?errf(T(#{<<"max_retries">> => -1})). mod_event_pusher_sns(_Config) -> RequiredOpts = #{<<"access_key_id">> => <<"AKIAIOSFODNN7EXAMPLE">>, From dfd17bddd86098cff913829b9069f6e25749fa2e Mon Sep 17 00:00:00 2001 From: Leszek Witkowicz Date: Mon, 14 Dec 2020 16:12:16 +0100 Subject: [PATCH 082/104] make mod_http_upload config declarative --- doc/modules/mod_http_upload.md | 14 ++-- src/config/mongoose_config_parser_toml.erl | 28 +------- src/config/mongoose_config_spec.erl | 1 + src/config/mongoose_config_validator_toml.erl | 33 --------- src/http_upload/mod_http_upload.erl | 34 ++++++++- test/config_parser_SUITE.erl | 71 ++++++++----------- 6 files changed, 71 insertions(+), 110 deletions(-) diff --git a/doc/modules/mod_http_upload.md b/doc/modules/mod_http_upload.md index 3212b25560..683fb82f03 100644 --- a/doc/modules/mod_http_upload.md +++ b/doc/modules/mod_http_upload.md @@ -29,7 +29,7 @@ Subdomain for the upload service to reside under. `@HOST@` is replaced with each Backend to use for generating slots. Currently only `"s3"` can be used. ### `modules.mod_http_upload.expiration_time` -* **Syntax:** non-negative integer +* **Syntax:** positive integer * **Default:** `60` * **Example:** `expiration_time = 120` @@ -44,11 +44,11 @@ Number of random bytes of a token that will be used in a generated URL. The text representation of the token will be twice as long as the number of bytes, e.g. for the default value the token in the URL will be 64 characters long. ### `modules.mod_http_upload.max_file_size` -* **Syntax:** non-negative integer +* **Syntax:** positive integer * **Default:** not set - no size limit * **Example:** `max_file_size = 10485760` -Maximum file size (in bytes) accepted by the module. Disabled if set to `"undefined"`. +Maximum file size (in bytes) accepted by the module. ### `modules.mod_http_upload.s3` * **Syntax:** Array of TOML tables. See description. @@ -57,6 +57,8 @@ Maximum file size (in bytes) accepted by the module. Disabled if set to `"undefi Options specific to [S3][s3] backend. +**Note:** this section is mandatory. + ### [S3][s3] backend options #### `s3.bucket_url` @@ -76,21 +78,21 @@ This allows users to read the uploaded files even if the bucket is private. The #### `s3.region` * **Syntax:** string -* **Default:** `""` +* **Default:** `""`, this option is mandatory * **Example:** `s3.region = "https://s3-eu-west-1.amazonaws.com/mybucket"` The [AWS region][aws-region] to use for requests. #### `s3.access_key_id` * **Syntax:** string -* **Default:** `""` +* **Default:** `""`, this option is mandatory * **Example:** `s3.access_key_id = "AKIAIOSFODNN7EXAMPLE"` [ID of the access key][aws-keys] to use for authorization. #### `s3.secret_access_key` * **Syntax:** string -* **Default:** `""` +* **Default:** `""`, this option is mandatory * **Example:** `s3.secret_access_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"` [Secret access key][aws-keys] to use for authorization. diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index a360b5be18..b33888de1a 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -104,19 +104,6 @@ post_process_module(Mod, Opts) -> %% path: (host_config[].)modules.*.* -spec module_opt(path(), toml_value()) -> [option()]. -module_opt([<<"host">>, <<"mod_http_upload">>|_], V) -> - [{host, b2l(V)}]; -module_opt([<<"backend">>, <<"mod_http_upload">>|_], V) -> - [{backend, b2a(V)}]; -module_opt([<<"expiration_time">>, <<"mod_http_upload">>|_], V) -> - [{expiration_time, V}]; -module_opt([<<"token_bytes">>, <<"mod_http_upload">>|_], V) -> - [{token_bytes, V}]; -module_opt([<<"max_file_size">>, <<"mod_http_upload">>|_], V) -> - [{max_file_size, V}]; -module_opt([<<"s3">>, <<"mod_http_upload">>|_] = Path, V) -> - S3Opts = parse_section(Path, V), - [{s3, S3Opts}]; % General options module_opt([<<"iqdisc">>|_], V) -> {Type, Opts} = maps:take(<<"type">>, V), @@ -149,18 +136,6 @@ riak_opts([<<"bucket_type">>|_], V) -> riak_opts([<<"search_index">>|_], V) -> [{search_index, V}]. --spec mod_http_upload_s3(path(), toml_value()) -> [option()]. -mod_http_upload_s3([<<"bucket_url">>|_], V) -> - [{bucket_url, b2l(V)}]; -mod_http_upload_s3([<<"add_acl">>|_], V) -> - [{add_acl, V}]; -mod_http_upload_s3([<<"region">>|_], V) -> - [{region, b2l(V)}]; -mod_http_upload_s3([<<"access_key_id">>|_], V) -> - [{access_key_id, b2l(V)}]; -mod_http_upload_s3([<<"secret_access_key">>|_], V) -> - [{secret_access_key, b2l(V)}]. - -spec iqdisc_value(atom(), toml_section()) -> option(). iqdisc_value(queues, #{<<"workers">> := Workers} = V) -> limit_keys([<<"workers">>], V), @@ -413,6 +388,7 @@ node_to_string(Node) -> [binary_to_list(Node)]. Mod =/= <<"mod_event_pusher">>, Mod =/= <<"mod_extdisco">>, Mod =/= <<"mod_global_distrib">>, + Mod =/= <<"mod_http_upload">>, Mod =/= <<"mod_inbox">>, Mod =/= <<"mod_jingle_sip">>, Mod =/= <<"mod_keystore">>, @@ -444,8 +420,6 @@ handler([Mod, <<"modules">>]) when ?HAS_NO_SPEC(Mod) -> fun process_module/2; handler([_, Mod, <<"modules">>]) when ?HAS_NO_SPEC(Mod) -> fun module_opt/2; handler([_, <<"riak">>, Mod, <<"modules">>]) when ?HAS_NO_SPEC(Mod) -> fun riak_opts/2; -handler([_, <<"s3">>, <<"mod_http_upload">>, <<"modules">>]) -> - fun mod_http_upload_s3/2; %% host_config handler([_, <<"host_config">>]) -> fun process_host_item/2; diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index f233791af4..7f2476ff88 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -740,6 +740,7 @@ all_modules() -> mod_event_pusher, mod_extdisco, mod_global_distrib, + mod_http_upload, mod_inbox, mod_jingle_sip, mod_keystore, diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index ca21603ae2..1ee9554513 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -18,39 +18,6 @@ validate(Path, [F]) when is_function(F, 1) -> validate(Path, F(?HOST)); %% Modules -validate([<<"backend">>, <<"mod_http_upload">>, <<"modules">>|_], - [{backend, V}]) -> - validate_backend(mod_http_upload, V); -validate([<<"expiration_time">>, <<"mod_http_upload">>, <<"modules">>|_], - [{expiration_time, V}]) -> - validate_non_negative_integer(V); -validate([<<"host">>, <<"mod_http_upload">>, <<"modules">>|_], - [{host, V}]) -> - validate_domain_template(V); -validate([<<"iqdisc">>, <<"mod_http_upload">>, <<"modules">>|_], - [{iqdisc, V}]) -> - validate_iqdisc(V); -validate([<<"max_file_size">>, <<"mod_http_upload">>, <<"modules">>|_], - [{max_file_size, V}]) -> - validate_non_negative_integer(V); -validate([<<"access_key_id">>, <<"s3">>, <<"mod_http_upload">>, <<"modules">>|_], - [{access_key_id, V}]) -> - validate_string(V); -validate([<<"add_acl">>, <<"s3">>, <<"mod_http_upload">>, <<"modules">>|_], - [{add_acl, V}]) -> - validate_boolean(V); -validate([<<"bucket_url">>, <<"s3">>, <<"mod_http_upload">>, <<"modules">>|_], - [{bucket_url, V}]) -> - validate_url(V); -validate([<<"region">>, <<"s3">>, <<"mod_http_upload">>, <<"modules">>|_], - [{region, V}]) -> - validate_string(V); -validate([<<"secret_access_key">>, <<"s3">>, <<"mod_http_upload">>, <<"modules">>|_], - [{secret_access_key, V}]) -> - validate_string(V); -validate([<<"token_bytes">>, <<"mod_http_upload">>, <<"modules">>|_], - [{token_bytes, V}]) -> - validate_positive_integer(V); validate([<<"iqdisc">>, <<"mod_time">>, <<"modules">>|_], [{iqdisc, V}]) -> validate_iqdisc(V); diff --git a/src/http_upload/mod_http_upload.erl b/src/http_upload/mod_http_upload.erl index 82ab51c8cb..da89d71e57 100644 --- a/src/http_upload/mod_http_upload.erl +++ b/src/http_upload/mod_http_upload.erl @@ -24,12 +24,13 @@ -include("jlib.hrl"). -include("mongoose.hrl"). +-include("mongoose_config_spec.hrl"). -define(DEFAULT_TOKEN_BYTES, 32). -define(DEFAULT_MAX_FILE_SIZE, 10 * 1024 * 1024). % 10 MB -define(DEFAULT_SUBHOST, <<"upload.@HOST@">>). --export([start/2, stop/1, iq_handler/4, get_urls/5]). +-export([start/2, stop/1, iq_handler/4, get_urls/5, config_spec/0]). %% Hook implementations -export([get_disco_identity/5, get_disco_items/5, get_disco_features/5, get_disco_info/5]). @@ -122,6 +123,37 @@ get_urls(Host, Filename, Size, ContentType, Timeout) -> mod_http_upload_backend:create_slot(UTCDateTime, Token, Filename, ContentType, Size, NewOpts). +-spec config_spec() -> mongoose_config_spec:config_section(). +config_spec() -> + #section{ + items = #{<<"iqdisc">> => mongoose_config_spec:iqdisc(), + <<"host">> => #option{type = string, + validate = domain_template}, + <<"backend">> => #option{type = atom, + validate = {module, mod_http_upload}}, + <<"expiration_time">> => #option{type = integer, + validate = positive}, + <<"token_bytes">> => #option{type = integer, + validate = positive}, + <<"max_file_size">> => #option{type = integer, + validate = positive}, + <<"s3">> => s3_spec() + }, + required = [<<"s3">>] + }. + +s3_spec() -> + #section{ + items = #{<<"bucket_url">> => #option{type = string, + validate = url}, + <<"add_acl">> => #option{type = boolean}, + <<"region">> => #option{type = string}, + <<"access_key_id">> => #option{type = string}, + <<"secret_access_key">> => #option{type = string} + }, + required = [<<"bucket_url">>, <<"region">>, <<"access_key_id">>, <<"secret_access_key">>] + }. + -spec get_disco_identity(Acc :: term(), From :: jid:jid(), To :: jid:jid(), Node :: binary(), ejabberd:lang()) -> [exml:element()] | term(). get_disco_identity(Acc, _From, _To, _Node = <<>>, Lang) -> diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index b76394ab29..3b27bc75e2 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -1860,49 +1860,34 @@ mod_event_pusher_rabbit(_Config) -> ?errf(T(#{<<"money_exchange">> => #{<<"name">> => <<"kantor">>}})). mod_http_upload(_Config) -> - T = fun(Opts) -> #{<<"modules">> => #{<<"mod_http_upload">> => Opts}} end, - S3 = #{ - <<"bucket_url">> => <<"https://s3-eu-west-1.amazonaws.com/mybucket">>, - <<"add_acl">> => true, - <<"region">> => <<"antarctica-1">>, - <<"access_key_id">> => <<"PLEASE">>, - <<"secret_access_key">> => <<"ILOVEU">> - }, - Base = #{ - <<"iqdisc">> => #{<<"type">> => <<"one_queue">>}, - <<"host">> => <<"upload.@HOST@">>, - <<"backend">> => <<"s3">>, - <<"expiration_time">> => 666, - <<"token_bytes">> => 32, - <<"max_file_size">> => 42, - <<"s3">> => S3 - }, - MS3 = [{access_key_id, "PLEASE"}, - {add_acl, true}, - {bucket_url, "https://s3-eu-west-1.amazonaws.com/mybucket"}, - {region, "antarctica-1"}, - {secret_access_key, "ILOVEU"}], - MBase = [{backend, s3}, - {expiration_time, 666}, - {host, "upload.@HOST@"}, - {iqdisc, one_queue}, - {max_file_size, 42}, - {s3, MS3}, - {token_bytes, 32}], - ?eqf(modopts(mod_http_upload, MBase), T(Base)), - ?errf(T(Base#{<<"host">> => -1})), - ?errf(T(Base#{<<"host">> => <<" f g ">>})), - ?errf(T(Base#{<<"backend">> => <<"dev_null_as_a_service">>})), - ?errf(T(Base#{<<"expiration_time">> => <<>>})), - ?errf(T(Base#{<<"expiration_time">> => -1})), - ?errf(T(Base#{<<"token_bytes">> => -1})), - ?errf(T(Base#{<<"max_file_size">> => -1})), - ?errf(T(Base#{<<"s3">> => S3#{<<"access_key_id">> => -1}})), - ?errf(T(Base#{<<"s3">> => S3#{<<"add_acl">> => -1}})), - ?errf(T(Base#{<<"s3">> => S3#{<<"bucket_url">> => -1}})), - ?errf(T(Base#{<<"s3">> => S3#{<<"region">> => -1}})), - ?errf(T(Base#{<<"s3">> => S3#{<<"secret_access_key">> => -1}})), - check_iqdisc(mod_http_upload). + T = fun(Opts) -> #{<<"modules">> => #{ + <<"mod_http_upload">> => Opts}} end, + M = fun(Opts) -> modopts(mod_http_upload, Opts) end, + RequiredOpts = #{<<"s3">> => #{ + <<"bucket_url">> => <<"https://s3-eu-west-1.amazonaws.com/mybucket">>, + <<"region">> => <<"antarctica-1">>, + <<"access_key_id">> => <<"PLEASE">>, + <<"secret_access_key">> => <<"ILOVEU">> + }}, + ExpectedCfg = [{s3, [{access_key_id, "PLEASE"}, + {bucket_url, "https://s3-eu-west-1.amazonaws.com/mybucket"}, + {region, "antarctica-1"}, + {secret_access_key, "ILOVEU"}]}], + ?eqf(M(ExpectedCfg), T(RequiredOpts)), + ?eqf(M(ExpectedCfg ++ [{host, "upload.@HOST@"}]), + T(RequiredOpts#{<<"host">> => <<"upload.@HOST@">>})), + ?eqf(M(ExpectedCfg ++ [{backend, s3}]), + T(RequiredOpts#{<<"backend">> => <<"s3">>})), + ?eqf(M(ExpectedCfg ++ [{expiration_time, 666}]), + T(RequiredOpts#{<<"expiration_time">> => 666})), + ?eqf(M(ExpectedCfg ++ [{token_bytes, 32}]), + T(RequiredOpts#{<<"token_bytes">> => 32})), + ?eqf(M(ExpectedCfg ++ [{max_file_size, 42}]), + T(RequiredOpts#{<<"max_file_size">> => 42})), + ?errf(T(#{<<"backend">> => <<"">>})), + ?errf(T(#{<<"expiration_time">> => 0})), + ?errf(T(#{<<"token_bytes">> => 0})), + ?errf(T(#{<<"max_file_size">> => 0})). mod_jingle_sip(_Config) -> T = fun(Opts) -> #{<<"modules">> => #{<<"mod_jingle_sip">> => Opts}} end, From 4353e9e54b9dc648d4c0871fe40cc976c94673cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Fri, 18 Dec 2020 14:05:30 +0100 Subject: [PATCH 083/104] Finish tests for mod_http_upload - Add iqdisc test - Add s3 tests --- test/config_parser_SUITE.erl | 75 ++++++++++++++++++++++++------------ 1 file changed, 50 insertions(+), 25 deletions(-) diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index 3b27bc75e2..fcc836960c 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -187,6 +187,7 @@ groups() -> mod_event_pusher_rabbit, mod_extdisco, mod_http_upload, + mod_http_upload_s3, mod_jingle_sip, mod_keystore, mod_last, @@ -1860,34 +1861,55 @@ mod_event_pusher_rabbit(_Config) -> ?errf(T(#{<<"money_exchange">> => #{<<"name">> => <<"kantor">>}})). mod_http_upload(_Config) -> - T = fun(Opts) -> #{<<"modules">> => #{ - <<"mod_http_upload">> => Opts}} end, - M = fun(Opts) -> modopts(mod_http_upload, Opts) end, - RequiredOpts = #{<<"s3">> => #{ - <<"bucket_url">> => <<"https://s3-eu-west-1.amazonaws.com/mybucket">>, - <<"region">> => <<"antarctica-1">>, - <<"access_key_id">> => <<"PLEASE">>, - <<"secret_access_key">> => <<"ILOVEU">> - }}, - ExpectedCfg = [{s3, [{access_key_id, "PLEASE"}, - {bucket_url, "https://s3-eu-west-1.amazonaws.com/mybucket"}, - {region, "antarctica-1"}, - {secret_access_key, "ILOVEU"}]}], + T = fun(Opts) -> #{<<"modules">> => #{<<"mod_http_upload">> => Opts}} end, + M = fun(Cfg) -> modopts(mod_http_upload, Cfg) end, + RequiredOpts = #{<<"s3">> => http_upload_s3_required_opts()}, + ExpectedCfg = [{s3, http_upload_s3_expected_cfg()}], ?eqf(M(ExpectedCfg), T(RequiredOpts)), ?eqf(M(ExpectedCfg ++ [{host, "upload.@HOST@"}]), T(RequiredOpts#{<<"host">> => <<"upload.@HOST@">>})), ?eqf(M(ExpectedCfg ++ [{backend, s3}]), - T(RequiredOpts#{<<"backend">> => <<"s3">>})), + T(RequiredOpts#{<<"backend">> => <<"s3">>})), ?eqf(M(ExpectedCfg ++ [{expiration_time, 666}]), T(RequiredOpts#{<<"expiration_time">> => 666})), ?eqf(M(ExpectedCfg ++ [{token_bytes, 32}]), T(RequiredOpts#{<<"token_bytes">> => 32})), ?eqf(M(ExpectedCfg ++ [{max_file_size, 42}]), T(RequiredOpts#{<<"max_file_size">> => 42})), - ?errf(T(#{<<"backend">> => <<"">>})), - ?errf(T(#{<<"expiration_time">> => 0})), - ?errf(T(#{<<"token_bytes">> => 0})), - ?errf(T(#{<<"max_file_size">> => 0})). + ?errf(T(#{})), %% missing 's3' + ?errf(T(RequiredOpts#{<<"backend">> => <<"">>})), + ?errf(T(RequiredOpts#{<<"expiration_time">> => 0})), + ?errf(T(RequiredOpts#{<<"token_bytes">> => 0})), + ?errf(T(RequiredOpts#{<<"max_file_size">> => 0})), + check_iqdisc(mod_http_upload, ExpectedCfg, RequiredOpts). + +mod_http_upload_s3(_Config) -> + T = fun(Opts) -> #{<<"modules">> => #{<<"mod_http_upload">> => + #{<<"s3">> => Opts}}} end, + M = fun(Cfg) -> modopts(mod_http_upload, [{s3, Cfg}]) end, + RequiredOpts = http_upload_s3_required_opts(), + ExpectedCfg = http_upload_s3_expected_cfg(), + ?eqf(M(ExpectedCfg), T(RequiredOpts)), + ?eqf(M(ExpectedCfg ++ [{add_acl, true}]), + T(RequiredOpts#{<<"add_acl">> => true})), + [?errf(T(maps:remove(Key, RequiredOpts))) || Key <- maps:keys(RequiredOpts)], + ?errf(T(RequiredOpts#{<<"bucket_url">> => <<>>})), + ?errf(T(RequiredOpts#{<<"region">> => true})), + ?errf(T(RequiredOpts#{<<"access_key_id">> => []})), + ?errf(T(RequiredOpts#{<<"secret_access_key">> => 3})), + ?errf(T(RequiredOpts#{<<"add_acl">> => <<"true">>})). + +http_upload_s3_required_opts() -> + #{<<"bucket_url">> => <<"https://s3-eu-west-1.amazonaws.com/mybucket">>, + <<"region">> => <<"antarctica-1">>, + <<"access_key_id">> => <<"PLEASE">>, + <<"secret_access_key">> => <<"ILOVEU">>}. + +http_upload_s3_expected_cfg() -> + [{access_key_id, "PLEASE"}, + {bucket_url, "https://s3-eu-west-1.amazonaws.com/mybucket"}, + {region, "antarctica-1"}, + {secret_access_key, "ILOVEU"}]. mod_jingle_sip(_Config) -> T = fun(Opts) -> #{<<"modules">> => #{<<"mod_jingle_sip">> => Opts}} end, @@ -2834,16 +2856,19 @@ service_mongoose_system_metrics(_Config) -> iqdisc({queues, Workers}) -> #{<<"type">> => <<"queues">>, <<"workers">> => Workers}; iqdisc(Atom) -> #{<<"type">> => atom_to_binary(Atom, utf8)}. -iq_disc_generic(Module, Value) -> - Opts = #{<<"iqdisc">> => Value}, +iq_disc_generic(Module, RequiredOpts, Value) -> + Opts = RequiredOpts#{<<"iqdisc">> => Value}, #{<<"modules">> => #{atom_to_binary(Module, utf8) => Opts}}. check_iqdisc(Module) -> - ?eqf(modopts(Module, [{iqdisc, {queues, 10}}]), - iq_disc_generic(Module, iqdisc({queues, 10}))), - ?eqf(modopts(Module, [{iqdisc, parallel}]), - iq_disc_generic(Module, iqdisc(parallel))), - ?errf(iq_disc_generic(Module, iqdisc(bad_haha))). + check_iqdisc(Module, [], #{}). + +check_iqdisc(Module, ExpectedCfg, RequiredOpts) -> + ?eqf(modopts(Module, ExpectedCfg ++ [{iqdisc, {queues, 10}}]), + iq_disc_generic(Module, RequiredOpts, iqdisc({queues, 10}))), + ?eqf(modopts(Module, ExpectedCfg ++ [{iqdisc, parallel}]), + iq_disc_generic(Module, RequiredOpts, iqdisc(parallel))), + ?errf(iq_disc_generic(Module, RequiredOpts, iqdisc(bad_haha))). modopts(Mod, Opts) -> [#local_config{key = {modules, ?HOST}, value = [{Mod, Opts}]}]. From 76cb2b7c60f28de087331d5a2b931c5358a31989 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Fri, 18 Dec 2020 14:40:24 +0100 Subject: [PATCH 084/104] Apply review comments for mod_keystore - Fix punctuation in docs - Narrow down key type to 'atom' - Fix parsing of the KV list --- doc/modules/mod_keystore.md | 14 +++++----- src/mod_keystore.erl | 12 ++++---- test/config_parser_SUITE.erl | 53 +++++++++++++++++------------------- 3 files changed, 39 insertions(+), 40 deletions(-) diff --git a/doc/modules/mod_keystore.md b/doc/modules/mod_keystore.md index e7d8324f9d..ed4967eb69 100644 --- a/doc/modules/mod_keystore.md +++ b/doc/modules/mod_keystore.md @@ -50,9 +50,9 @@ Simple configuration - single tenant (i.e. server hosting just one XMPP domain): ```toml [modules.mod_keystore] - keys = [{name = "access_secret, type = "ram"}, - {name = "access_psk, type = "file", path = "priv/access_psk"}, - {name = "provision_psk, type = "file", path = "priv/provision_psk"}] + keys = [{name = "access_secret", type = "ram"}, + {name = "access_psk", type = "file", path = "priv/access_psk"}, + {name = "provision_psk", type = "file", path = "priv/provision_psk"}] ``` Multi-tenant setup (`mod_keystore` configured differently @@ -63,15 +63,15 @@ for each virtual XMPP domain): host = "first.com" [host_config.modules.mod_keystore] - keys = [{name = "access_secret, type = "ram"}, - {name = "access_psk, type = "file", path = "priv/first_access_psk"}, - {name = "provision_psk, type = "file", path = "priv/first_provision_psk"}] + keys = [{name = "access_secret", type = "ram"}, + {name = "access_psk", type = "file", path = "priv/first_access_psk"}, + {name = "provision_psk", type = "file", path = "priv/first_provision_psk"}] [[host_config]] host = "second.com" [host_config.modules.mod_keystore] keys = [{name = "access_secret", type = "ram"}, - {name = "access_psk," type = "file", path = "priv/second_access_psk"}, + {name = "access_psk", type = "file", path = "priv/second_access_psk"}, {name = "provision_psk", type = "file", path = "priv/second_provision_psk"}] ``` diff --git a/src/mod_keystore.erl b/src/mod_keystore.erl index 1cefc48d6b..766cd61fed 100644 --- a/src/mod_keystore.erl +++ b/src/mod_keystore.erl @@ -35,7 +35,7 @@ %% The name doesn't differentiate between virtual hosts %% (i.e. there are multiple keys with the same name, %% one per each XMPP domain). --type key_name() :: any(). +-type key_name() :: atom(). %% A key ID is used to uniquely identify a key for storage backends. %% It's used to maintain separate instances of a key with the same name %% for different virtual hosts. @@ -104,10 +104,12 @@ keys_spec() -> process = fun ?MODULE:process_keys/1 }. -process_keys([{name, Name},{type, ram}]) -> - {Name, ram}; -process_keys([{name, Name}, {path, File}, {type, file}]) -> - {Name, {file, File}}. +process_keys(KVs) -> + {[[{name, Name}], [{type, Type}]], PathOpts} = proplists:split(KVs, [name, type]), + process_key_opts(Name, Type, PathOpts). + +process_key_opts(Name, ram, []) -> {Name, ram}; +process_key_opts(Name, file, [{path, Path}]) -> {Name, {file, Path}}. %% %% Hook handlers diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index fcc836960c..cfe8f9bc79 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -190,6 +190,7 @@ groups() -> mod_http_upload_s3, mod_jingle_sip, mod_keystore, + mod_keystore_keys, mod_last, mod_mam_meta, mod_mam_meta_pm, @@ -1935,34 +1936,30 @@ mod_jingle_sip(_Config) -> ?errf(T(Base#{<<"sdp_origin">> => <<"aaaaaaaaa">>})). mod_keystore(_Config) -> - T = fun(Keys, Size) -> #{<<"modules">> => - #{<<"mod_keystore">> => - #{<<"keys">> => Keys, - <<"ram_key_size">> => Size}}} - end, - Keys = [#{<<"name">> => <<"access_secret">>, - <<"type">> => <<"ram">>}, - #{<<"name">> => <<"access_psk">>, - <<"type">> => <<"file">>, - <<"path">> => <<"priv/access_psk">>}, - #{<<"name">> => <<"provision_psk">>, - <<"type">> => <<"file">>, - <<"path">> => <<"priv/provision_psk">>}], - NotExistingKey = #{<<"name">> => <<"provision_psk">>, - <<"type">> => <<"file">>, - <<"path">> => <<"does/not/esit">>}, - InvalidTypeKey = #{<<"name">> => <<"provision_psk">>, - <<"type">> => <<"some_cooool_type">>}, - Size = 10000, - InvalidTypeSize = -1, - MKeys = [{access_secret, ram}, - {access_psk, {file, "priv/access_psk"}}, - {provision_psk, {file, "priv/provision_psk"}}], - MBase = [{keys, MKeys}, {ram_key_size, Size}], - ?eqf(modopts(mod_keystore, MBase), T(Keys, Size)), - ?errf(T([NotExistingKey], Size)), - ?errf(T([InvalidTypeKey], Size)), - ?errf(T(Keys, InvalidTypeSize)). + T = fun(Opts) -> #{<<"modules">> => #{<<"mod_keystore">> => Opts}} end, + M = fun(Cfg) -> modopts(mod_keystore, Cfg) end, + ?eqf(M([{ram_key_size, 1024}]), + T(#{<<"ram_key_size">> => 1024})), + ?errf(T(#{<<"ram_key_size">> => -1})). + +mod_keystore_keys(_Config) -> + T = fun(Opts) -> #{<<"modules">> => #{<<"mod_keystore">> => + #{<<"keys">> => Opts}}} + end, + M = fun(Cfg) -> modopts(mod_keystore, [{keys, Cfg}]) end, + RequiredOpts = #{<<"name">> => <<"access_secret">>, + <<"type">> => <<"ram">>}, + ?eqf(M([{access_secret, ram}]), + T([RequiredOpts])), + ?eqf(M([{access_secret, {file, "priv/access_psk"}}]), + T([RequiredOpts#{<<"type">> => <<"file">>, + <<"path">> => <<"priv/access_psk">>}])), + [?errf(T([maps:remove(Key, RequiredOpts)])) || Key <- maps:keys(RequiredOpts)], + ?errf(T([RequiredOpts#{<<"name">> => <<>>}])), + ?errf(T([RequiredOpts#{<<"type">> => <<"rampampam">>}])), + ?errf(T([RequiredOpts#{<<"type">> => <<"file">>}])), + ?errf(T([RequiredOpts#{<<"type">> => <<"file">>, + <<"path">> => <<"does/not/exists">>}])). mod_last(_Config) -> check_iqdisc(mod_last), From 4f826487dff90aee8df7316f77de9da89ac42055 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Fri, 18 Dec 2020 14:51:38 +0100 Subject: [PATCH 085/104] Refactor tests for mod_jingle_sip --- test/config_parser_SUITE.erl | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index cfe8f9bc79..a404238b80 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -1914,26 +1914,22 @@ http_upload_s3_expected_cfg() -> mod_jingle_sip(_Config) -> T = fun(Opts) -> #{<<"modules">> => #{<<"mod_jingle_sip">> => Opts}} end, - Base = #{ - <<"proxy_host">> => <<"proxxxy">>, - <<"proxy_port">> => 5600, - <<"listen_port">> => 5601, - <<"local_host">> => <<"localhost">>, - <<"sdp_origin">> => <<"127.0.0.1">> - }, - MBase = [ - {listen_port, 5601}, - {local_host, "localhost"}, - {proxy_host, "proxxxy"}, - {proxy_port, 5600}, - {sdp_origin, "127.0.0.1"} - ], - ?eqf(modopts(mod_jingle_sip, MBase), T(Base)), - ?errf(T(Base#{<<"proxy_host">> => -1})), - ?errf(T(Base#{<<"listen_port">> => -1})), - ?errf(T(Base#{<<"proxy_port">> => 10000000})), - ?errf(T(Base#{<<"local_host">> => <<"ok ok">>})), - ?errf(T(Base#{<<"sdp_origin">> => <<"aaaaaaaaa">>})). + M = fun(Cfg) -> modopts(mod_jingle_sip, Cfg) end, + ?eqf(M([{proxy_host, "proxxxy"}]), + T(#{<<"proxy_host">> => <<"proxxxy">>})), + ?eqf(M([{proxy_port, 5601}]), + T(#{<<"proxy_port">> => 5601})), + ?eqf(M([{listen_port, 5602}]), + T(#{<<"listen_port">> => 5602})), + ?eqf(M([{local_host, "localhost"}]), + T(#{<<"local_host">> => <<"localhost">>})), + ?eqf(M([{sdp_origin, "127.0.0.1"}]), + T(#{<<"sdp_origin">> => <<"127.0.0.1">>})), + ?errf(T(#{<<"proxy_host">> => 1})), + ?errf(T(#{<<"proxy_port">> => 1000000})), + ?errf(T(#{<<"listen_port">> => -1})), + ?errf(T(#{<<"local_host">> => <<>>})), + ?errf(T(#{<<"sdp_origin">> => <<"abc">>})). mod_keystore(_Config) -> T = fun(Opts) -> #{<<"modules">> => #{<<"mod_keystore">> => Opts}} end, From 57008a880cecc6a7cdfebf76bde6a3c3fb8c0c7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Mon, 21 Dec 2020 10:12:25 +0100 Subject: [PATCH 086/104] Clean up unused functions --- src/config/mongoose_config_parser_toml.erl | 9 --------- src/config/mongoose_config_validator_toml.erl | 8 -------- 2 files changed, 17 deletions(-) diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index b33888de1a..f942c81f63 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -174,15 +174,6 @@ ensure_keys(Keys, Section) -> MissingKeys -> error(#{what => missing_mandatory_keys, missing_keys => MissingKeys}) end. -%% Parse with post-processing, this needs to be eliminated by fixing the internal config structure --spec parse_section(path(), toml_section(), fun(([option()]) -> option())) -> option(). -parse_section(Path, V, PostProcessF) -> - L = parse_section(Path, V), - case extract_errors(L) of - [] -> PostProcessF(L); - Errors -> Errors - end. - -spec parse_section(path(), toml_section()) -> [option()]. parse_section(Path, M) -> lists:flatmap(fun({K, V}) -> diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index 1ee9554513..9cd3ec2081 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -80,8 +80,6 @@ validate_unique_items(Items) -> L = sets:size(sets:from_list(Items)), L = length(Items). -validate_boolean(Value) when is_boolean(Value) -> ok. - validate_module(Mod) -> case code:ensure_loaded(Mod) of {module, _} -> @@ -139,9 +137,6 @@ validate_iqdisc(one_queue) -> ok; validate_iqdisc(parallel) -> ok; validate_iqdisc({queues, N}) when is_integer(N), N > 0 -> ok. -validate_backend(Mod, Backend) -> - validate_module(backend_module:backend_module(Mod, Backend)). - validate_domain(Domain) when is_list(Domain) -> #jid{luser = <<>>, lresource = <<>>} = jid:from_binary(list_to_binary(Domain)), validate_domain_res(Domain). @@ -236,6 +231,3 @@ validate_dirname(Dirname) -> Reason -> error(#{what => invalid_dirname, dirname => Dirname, reason => Reason}) end. - -validate_pool_name(V) -> - validate_non_empty_atom(V). From 36c516c391eec3c8c5b5e2c10c582c53b9d614f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Mon, 21 Dec 2020 10:13:32 +0100 Subject: [PATCH 087/104] Make TLS for global_distrib disabled by default This change fixes the inconsistency between the code and docs in the TLS options for mod_global_distrib. The TLS options are now disabled by default, because they require providing 'certfile' and 'cacertfile' anyway - the "enabled by default" solution did not work out of the box. Now the user can: - either use it without TLS (default) - or specify the cert files and have working TLS Note: refactored the tests as well. --- doc/modules/mod_global_distrib.md | 14 +- src/global_distrib/mod_global_distrib.erl | 31 +-- test/config_parser_SUITE.erl | 289 ++++++++------------- test/config_parser_SUITE_data/modules.toml | 1 - 4 files changed, 121 insertions(+), 214 deletions(-) diff --git a/doc/modules/mod_global_distrib.md b/doc/modules/mod_global_distrib.md index b43763431a..917b1beb7c 100644 --- a/doc/modules/mod_global_distrib.md +++ b/doc/modules/mod_global_distrib.md @@ -136,8 +136,6 @@ The interval telling how often Redis should be asked if new hosts appeared. ### Connections' options -**Note:** This section is mandatory. - #### `modules.mod_global_distrib.connections.endpoints` * **Syntax:** Array of TOML tables with the following keys: `host` and `port`, and the following values: {host = `string`, port = `non_negative_integer`} * **Default:** `[{host = "LocalHost", port = 5555}]` @@ -186,18 +184,11 @@ It means that disabled endpoints are periodically verified and if Global Distrib ### TLS options -**Note:** This section is mandatory. - -#### `modules.mod_global_distrib.connections.tls.enabled` -* **Syntax:** boolean -* **Default:** `false`, this option is mandatory -* **Example:** `enabled = true` +**Note:** By default `tls` is disabled and all data will be sent via standard TCP connections. -To enable TLS support the `cacertfile` and `certfile` options have to be present. +To enable TLS support, the `cacertfile` and `certfile` options have to be present. These options will be passed to the `fast_tls` driver. -If `tls` is disabled, all data will be sent via standard TCP connections. - #### `modules.mod_global_distrib.connections.tls.certfile` * **Syntax:** string, path in the file system * **Default:** none, this options is mandatory to enable TLS support @@ -319,7 +310,6 @@ The endpoints used for connection to a remote datacenter may be overridden by gl local_host = "datacenter1.example.com" connections.endpoints = [{host = "172.16.0.2", port = 5555}] connections.advertised_endpoints = [{host = "172.16.0.2", port = 5555}] - connections.tls.enabled = true connections.tls.certfile = "priv/dc1.pem" connections.tls.cacertfile = "priv/ca.pem" connections.connections_per_endpoint = 30 diff --git a/src/global_distrib/mod_global_distrib.erl b/src/global_distrib/mod_global_distrib.erl index 35c17e4821..c438abfa1f 100644 --- a/src/global_distrib/mod_global_distrib.erl +++ b/src/global_distrib/mod_global_distrib.erl @@ -29,7 +29,7 @@ -export([deps/2, start/2, stop/1, config_spec/0]). -export([find_metadata/2, get_metadata/3, remove_metadata/2, put_metadata/3]). -export([maybe_reroute/1]). --export([process_endpoints/1, process_tls/1, process_bounce/1]). +-export([process_endpoints/1, process_bounce/1]). %%-------------------------------------------------------------------- %% gen_mod API %% See "gen_mod logic" block below in this file @@ -64,7 +64,7 @@ config_spec() -> <<"cache">> => cache_spec(), <<"bounce">> => bounce_spec() }, - required = [<<"global_host">>, <<"local_host">>, <<"connections">>] + required = [<<"global_host">>, <<"local_host">>] }. connections_spec() -> @@ -80,8 +80,7 @@ connections_spec() -> <<"disabled_gc_interval">> => #option{type = integer, validate = positive}, <<"tls">> => tls_spec() - }, - required = [<<"tls">>] + } }. endpoints_spec() -> @@ -97,8 +96,7 @@ endpoints_spec() -> tls_spec() -> #section{ - items = #{<<"enabled">> => #option{type = boolean}, - <<"certfile">> => #option{type = string, + items = #{<<"certfile">> => #option{type = string, validate = filename}, <<"cacertfile">> => #option{type = string, validate = filename, @@ -107,8 +105,7 @@ tls_spec() -> <<"dhfile">> => #option{type = string, validate = filename} }, - required = [<<"enabled">>], - process = fun ?MODULE:process_tls/1, + required = [<<"certfile">>, <<"cacertfile">>], format = {kv, tls_opts} }. @@ -119,7 +116,7 @@ redis_spec() -> <<"expire_after">> => #option{type = integer, validate = positive}, <<"refresh_after">> => #option{type = integer, - validate = non_negative} + validate = non_negative} } }. @@ -147,19 +144,13 @@ bounce_spec() -> }. process_endpoints(KV) -> - {[[{host, Host}],[{port, Port}]], []} = proplists:split(KV, [host, port]), + {[[{host, Host}], [{port, Port}]], []} = proplists:split(KV, [host, port]), {Host, Port}. -process_tls(KV) -> - case proplists:get_value(enabled, KV, false) of - true -> proplists:delete(enabled, KV); - false -> false - end. - process_bounce(KVs) -> {[EnabledOpts], Opts} = proplists:split(KVs, [enabled]), bounce_value(EnabledOpts, Opts). - + bounce_value([{enabled, false}], _) -> false; bounce_value(_, Opts) -> Opts. @@ -314,7 +305,8 @@ get_bound_connection(Server, GDID, Pid) when is_pid(Pid) -> -spec deps(Opts :: proplists:proplist()) -> gen_mod:deps_list(). deps(Opts) -> - ConnectionsOpts = proplists:get_value(connections, Opts, []), + ConnectionsOpts = + lists:ukeysort(1, proplists:get_value(connections, Opts, []) ++ default_conn_opts()), CacheOpts = proplists:get_value(cache, Opts, []), BounceOpts = proplists:get_value(bounce, Opts, []), @@ -328,6 +320,9 @@ deps(Opts) -> _ -> [{mod_global_distrib_bounce, BounceOpts ++ Opts, hard} | Deps0] end. +default_conn_opts() -> + [{tls_opts, false}]. + -spec start() -> any(). start() -> mongoose_metrics:ensure_metric(global, ?GLOBAL_DISTRIB_DELIVERED_WITH_TTL, histogram), diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index a404238b80..8827f66655 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -174,13 +174,13 @@ groups() -> mod_disco, mod_inbox, mod_global_distrib, - mod_global_config_connections, - mod_global_config_connections_endpoints, - mod_global_config_connections_advertised_endpoints, - mod_global_config_connections_tls, - mod_global_config_redis, - mod_global_config_cache, - mod_global_config_bounce, + mod_global_distrib_connections, + mod_global_distrib_connections_endpoints, + mod_global_distrib_connections_advertised_endpoints, + mod_global_distrib_connections_tls, + mod_global_distrib_redis, + mod_global_distrib_cache, + mod_global_distrib_bounce, mod_event_pusher_sns, mod_event_pusher_push, mod_event_pusher_http, @@ -1509,16 +1509,9 @@ mod_inbox(_Config) -> mod_global_distrib(_Config) -> T = fun(Opts) -> #{<<"modules">> => #{<<"mod_global_distrib">> => Opts}} end, - M = fun(Opts) -> modopts(mod_global_distrib, Opts) end, - RequiredOpts = #{ - <<"global_host">> => <<"example.com">>, - <<"local_host">> => <<"datacenter1.example.com">>, - <<"connections">> => #{ - <<"tls">> => #{ - <<"enabled">> => true}}}, - ExpectedCfg = [{connections, [{tls_opts,[]}]}, - {global_host, "example.com"}, - {local_host, "datacenter1.example.com"}], + M = fun(Cfg) -> modopts(mod_global_distrib, Cfg) end, + RequiredOpts = global_distrib_required_opts(), + ExpectedCfg = global_distrib_expected_config(), ?eqf(M(ExpectedCfg), T(RequiredOpts)), ?eqf(M(ExpectedCfg ++ [{message_ttl, 42}]), T(RequiredOpts#{<<"message_ttl">> => 42})), @@ -1528,43 +1521,18 @@ mod_global_distrib(_Config) -> ?errf(T(RequiredOpts#{<<"global_host">> => <<"">>})), ?errf(T(RequiredOpts#{<<"local_host">> => <<"">>})), ?errf(T(RequiredOpts#{<<"message_ttl">> => -1})), - ?errf(T(RequiredOpts#{<<"hosts_refresh_interval">> => -1})), - ?errf(T(RequiredOpts#{<<"connections">> => #{}})). + ?errf(T(RequiredOpts#{<<"hosts_refresh_interval">> => -1})). -mod_global_config_connections(_Config) -> - RequiredOpts = #{ - <<"tls">> => #{ - <<"enabled">> => true}}, - T = fun(Opts) -> #{<<"modules">> => #{ - <<"mod_global_distrib">> => #{ - <<"global_host">> => <<"example.com">>, - <<"local_host">> => <<"datacenter1.example.com">>, - <<"connections">> => maps:merge(RequiredOpts, Opts)}}} end, - ExpectedCfg = [{tls_opts,[]}], - M = fun(Opts) -> modopts(mod_global_distrib, - [{global_host, "example.com"}, - {local_host, "datacenter1.example.com"}, - {connections, ExpectedCfg ++ Opts}]) end, +mod_global_distrib_connections(_Config) -> + RequiredOpts = global_distrib_required_opts(), + T = fun(Opts) -> #{<<"modules">> => + #{<<"mod_global_distrib">> => + RequiredOpts#{<<"connections">> => Opts}}} + end, + M = fun(Cfg) -> modopts(mod_global_distrib, + global_distrib_expected_config() ++ [{connections, Cfg}]) + end, ?eqf(M([]), T(#{})), - ?eqf(M([{endpoints, [{"172.16.0.2", 5555}, - {"localhost", 80}, - {"example.com", 5555}]}]), - T(#{<<"endpoints">> => [#{<<"host">> => <<"172.16.0.2">>, - <<"port">> => 5555}, - #{<<"host">> => <<"localhost">>, - <<"port">> => 80}, - #{<<"host">> => <<"example.com">>, - <<"port">> => 5555}]})), - ?eqf(M([{advertised_endpoints, [{"172.16.0.1", 5555}, - {"localhost", 80}, - {"example.com", 5555}]}]), - T(#{<<"advertised_endpoints">> => - [#{<<"host">> => <<"172.16.0.1">>, - <<"port">> => 5555}, - #{<<"host">> => <<"localhost">>, - <<"port">> => 80}, - #{<<"host">> => <<"example.com">>, - <<"port">> => 5555}]})), ?eqf(M([{connections_per_endpoint, 22}]), T(#{<<"connections_per_endpoint">> => 22})), ?eqf(M([{endpoint_refresh_interval, 120}]), @@ -1573,112 +1541,69 @@ mod_global_config_connections(_Config) -> T(#{<<"endpoint_refresh_interval_when_empty">> => 5})), ?eqf(M([{disabled_gc_interval, 60}]), T(#{<<"disabled_gc_interval">> => 60})), - ?errf(T(#{<<"endpoints">> => #{}})), - ?errf(T(#{<<"advertised_endpoints">> => #{}})), ?errf(T(#{<<"connections_per_endpoint">> => -1})), ?errf(T(#{<<"endpoint_refresh_interval">> => 0})), ?errf(T(#{<<"endpoint_refresh_interval_when_empty">> => 0})), - ?errf(T(#{<<"disabled_gc_interval">> => 0})), - ?errf(T(#{<<"tls">> => #{}})), - ?errf(#{<<"modules">> => #{ - <<"mod_global_distrib">> => #{ - <<"global_host">> => <<"example.com">>, - <<"local_host">> => <<"datacenter1.example.com">>, - <<"connections">> => #{}}}}). - -mod_global_config_connections_endpoints(_Config) -> - T = fun(Opts) -> #{<<"modules">> => #{ - <<"mod_global_distrib">> => #{ - <<"global_host">> => <<"example.com">>, - <<"local_host">> => <<"datacenter1.example.com">>, - <<"connections">> => #{ - <<"endpoints">> => Opts, - <<"tls">> => #{ - <<"enabled">> => true}}}}} end, - M = fun(Opts) -> modopts(mod_global_distrib, - [{global_host, "example.com"}, - {local_host, "datacenter1.example.com"}, - {connections, [{endpoints, Opts}, {tls_opts, []}]}]) end, - ?eqf(M([{"172.16.0.2", 5555}, {"localhost", 80}, {"example.com", 5555}]), - T([#{<<"host">> => <<"172.16.0.2">>, <<"port">> => 5555}, - #{<<"host">> => <<"localhost">>, <<"port">> => 80}, - #{<<"host">> => <<"example.com">>, <<"port">> => 5555}])), - ?errf(T([#{<<"host">> => <<"172.16.0.2">>}])), - ?errf(T([#{<<"port">> => 5555}])), - ?errf(T([#{<<"host">> => <<"">>}])), - ?errf(T([#{<<"port">> => -1}])). - -mod_global_config_connections_advertised_endpoints(_Config) -> - T = fun(Opts) -> #{<<"modules">> => #{ - <<"mod_global_distrib">> => #{ - <<"global_host">> => <<"example.com">>, - <<"local_host">> => <<"datacenter1.example.com">>, - <<"connections">> => #{ - <<"advertised_endpoints">> => Opts, - <<"tls">> => #{ - <<"enabled">> => true}}}}} end, - M = fun(Opts) -> modopts(mod_global_distrib, - [{global_host, "example.com"}, - {local_host, "datacenter1.example.com"}, - {connections, [{advertised_endpoints, Opts}, {tls_opts, []}]}]) end, - ?eqf(M([{"172.16.0.2", 5555}, {"localhost", 80}, {"example.com", 5555}]), - T([#{<<"host">> => <<"172.16.0.2">>, <<"port">> => 5555}, - #{<<"host">> => <<"localhost">>, <<"port">> => 80}, - #{<<"host">> => <<"example.com">>, <<"port">> => 5555}])), - ?errf(T([#{<<"host">> => <<"172.16.0.2">>}])), - ?errf(T([#{<<"port">> => 5555}])), - ?errf(T([#{<<"host">> => <<"">>}])), - ?errf(T([#{<<"port">> => -1}])). - -mod_global_config_connections_tls(_Config) -> - RequiredOpts = #{<<"enabled">> => true}, - T = fun(Opts) -> #{<<"modules">> => #{ - <<"mod_global_distrib">> => #{ - <<"global_host">> => <<"example.com">>, - <<"local_host">> => <<"datacenter1.example.com">>, - <<"connections">> => #{ - <<"tls">> => maps:merge(RequiredOpts, Opts)}}}} end, - M = fun(Opts) -> modopts(mod_global_distrib, - [{global_host, "example.com"}, - {local_host, "datacenter1.example.com"}, - {connections, [{tls_opts, Opts}]}]) end, - ?eqf(M([]), T(#{})), - ?eqf(M(false), - T(#{<<"enabled">> => false})), - ?eqf(M([{certfile, "/dev/null"}]), - T(#{<<"certfile">> => <<"/dev/null">>})), - ?eqf(M([{cafile, "/dev/null"}]), - T(#{<<"cacertfile">> => <<"/dev/null">>})), - ?eqf(M([{dhfile, "/dev/null"}]), - T(#{<<"dhfile">> => <<"/dev/null">>})), - ?eqf(M([{ciphers, "TLS_AES_256_GCM_SHA384"}]), - T(#{<<"ciphers">> => <<"TLS_AES_256_GCM_SHA384">>})), - ?errf(T(#{<<"enabled">> => <<"yes">>})), - ?errf(T(#{<<"certfile">> => <<"/this/does/not/exist">>})), - ?errf(T(#{<<"cacertfile">> => <<"/this/does/not/exist">>})), - ?errf(T(#{<<"dhfile">> => <<"/this/does/not/exist">>})), - ?errf(T(#{<<"ciphers">> => 42})), - ?errf(#{<<"modules">> => #{ - <<"mod_global_distrib">> => #{ - <<"global_host">> => <<"example.com">>, - <<"local_host">> => <<"datacenter1.example.com">>, - <<"connections">> => #{ - <<"tls">> => #{}}}}}). - -mod_global_config_redis(_Config) -> - T = fun(Opts) -> #{<<"modules">> => #{ - <<"mod_global_distrib">> => #{ - <<"global_host">> => <<"example.com">>, - <<"local_host">> => <<"datacenter1.example.com">>, - <<"connections">> => #{ - <<"tls">> => #{ - <<"enabled">> => true}}, - <<"redis">> => Opts}}} end, - M = fun(Opts) -> modopts(mod_global_distrib, - [{global_host, "example.com"}, - {local_host, "datacenter1.example.com"}, - {connections, [{tls_opts,[]}]}, - {redis, Opts}]) end, + ?errf(T(#{<<"disabled_gc_interval">> => 0})). + +mod_global_distrib_connections_endpoints(_Config) -> + check_mod_global_distrib_endpoints(<<"endpoints">>). + +mod_global_distrib_connections_advertised_endpoints(_Config) -> + check_mod_global_distrib_endpoints(<<"advertised_endpoints">>). + +check_mod_global_distrib_endpoints(OptKey) -> + CfgKey = binary_to_atom(OptKey, utf8), + RequiredModOpts = global_distrib_required_opts(), + T = fun(Opts) -> #{<<"modules">> => + #{<<"mod_global_distrib">> => + RequiredModOpts#{<<"connections">> => #{OptKey => Opts}}}} + end, + M = fun(Cfg) -> modopts(mod_global_distrib, + global_distrib_expected_config() ++ + [{connections, [{CfgKey, Cfg}]}]) + end, + RequiredOpts = #{<<"host">> => <<"172.16.0.2">>, + <<"port">> => 5555}, + ?eqf(M([{"172.16.0.2", 5555}]), T([RequiredOpts])), + [?errf(T(maps:remove(Key, RequiredOpts))) || Key <- maps:keys(RequiredOpts)], + ?errf(T([RequiredOpts#{<<"host">> => <<>>}])), + ?errf(T([RequiredOpts#{<<"port">> => -1}])). + +mod_global_distrib_connections_tls(_Config) -> + RequiredModOpts = global_distrib_required_opts(), + T = fun(Opts) -> #{<<"modules">> => + #{<<"mod_global_distrib">> => + RequiredModOpts#{<<"connections">> => #{<<"tls">> => Opts}}}} + end, + M = fun(Cfg) -> modopts(mod_global_distrib, + global_distrib_expected_config() ++ + [{connections, [{tls_opts, Cfg}]}]) + end, + RequiredOpts = #{<<"certfile">> => <<"priv/cert.pem">>, + <<"cacertfile">> => <<"priv/ca.pem">>}, + ExpectedCfg = [{certfile, "priv/cert.pem"}, + {cafile, "priv/ca.pem"}], + ?eqf(M(ExpectedCfg), T(RequiredOpts)), + ?eqf(M(ExpectedCfg ++ [{ciphers, "TLS_AES_256_GCM_SHA384"}]), + T(RequiredOpts#{<<"ciphers">> => <<"TLS_AES_256_GCM_SHA384">>})), + ?eqf(M(ExpectedCfg ++ [{dhfile, "priv/cert.pem"}]), + T(RequiredOpts#{<<"dhfile">> => <<"priv/cert.pem">>})), + [?errf(T(maps:remove(Key, RequiredOpts))) || Key <- maps:keys(RequiredOpts)], + ?errf(T(RequiredOpts#{<<"certfile">> => <<"/this/does/not/exist">>})), + ?errf(T(RequiredOpts#{<<"cacertfile">> => <<"/this/does/not/exist">>})), + ?errf(T(RequiredOpts#{<<"dhfile">> => <<"/this/does/not/exist">>})), + ?errf(T(RequiredOpts#{<<"ciphers">> => 42})). + +mod_global_distrib_redis(_Config) -> + RequiredModOpts = global_distrib_required_opts(), + T = fun(Opts) -> #{<<"modules">> => + #{<<"mod_global_distrib">> => + RequiredModOpts#{<<"redis">> => Opts}}} + end, + M = fun(Cfg) -> modopts(mod_global_distrib, + global_distrib_expected_config() ++ [{redis, Cfg}]) + end, ?eqf(M([]), T(#{})), ?eqf(M([{pool, global_distrib}]), T(#{<<"pool">> => <<"global_distrib">>})), @@ -1690,20 +1615,15 @@ mod_global_config_redis(_Config) -> ?errf(T(#{<<"expire_after">> => 0})), ?errf(T(#{<<"refresh_after">> => -1})). -mod_global_config_cache(_Config) -> - T = fun(Opts) -> #{<<"modules">> => #{ - <<"mod_global_distrib">> => #{ - <<"global_host">> => <<"example.com">>, - <<"local_host">> => <<"datacenter1.example.com">>, - <<"connections">> => #{ - <<"tls">> => #{ - <<"enabled">> => true}}, - <<"cache">> => Opts}}} end, - M = fun(Opts) -> modopts(mod_global_distrib, - [{global_host, "example.com"}, - {local_host, "datacenter1.example.com"}, - {connections, [{tls_opts,[]}]}, - {cache, Opts}]) end, +mod_global_distrib_cache(_Config) -> + RequiredModOpts = global_distrib_required_opts(), + T = fun(Opts) -> #{<<"modules">> => + #{<<"mod_global_distrib">> => + RequiredModOpts#{<<"cache">> => Opts}}} + end, + M = fun(Cfg) -> modopts(mod_global_distrib, + global_distrib_expected_config() ++ [{cache, Cfg}]) + end, ?eqf(M([]), T(#{})), ?eqf(M([{cache_missed, false}]), T(#{<<"cache_missed">> => false})), @@ -1718,20 +1638,15 @@ mod_global_config_cache(_Config) -> ?errf(T(#{<<"jid_lifetime_seconds">> => -1})), ?errf(T(#{<<"max_jids">> => -1})). -mod_global_config_bounce(_Config) -> - T = fun(Opts) -> #{<<"modules">> => #{ - <<"mod_global_distrib">> => #{ - <<"global_host">> => <<"example.com">>, - <<"local_host">> => <<"datacenter1.example.com">>, - <<"connections">> => #{ - <<"tls">> => #{ - <<"enabled">> => true}}, - <<"bounce">> => Opts}}} end, - M = fun(Opts) -> modopts(mod_global_distrib, - [{global_host, "example.com"}, - {local_host, "datacenter1.example.com"}, - {connections, [{tls_opts,[]}]}, - {bounce, Opts}]) end, +mod_global_distrib_bounce(_Config) -> + RequiredModOpts = global_distrib_required_opts(), + T = fun(Opts) -> #{<<"modules">> => + #{<<"mod_global_distrib">> => + RequiredModOpts#{<<"bounce">> => Opts}}} + end, + M = fun(Cfg) -> modopts(mod_global_distrib, + global_distrib_expected_config() ++ [{bounce, Cfg}]) + end, ?eqf(M(false), T(#{<<"enabled">> => false})), ?eqf(M([]), @@ -1744,6 +1659,14 @@ mod_global_config_bounce(_Config) -> ?errf(T(#{<<"resend_after_ms">> => -1})), ?errf(T(#{<<"max_retries">> => -1})). +global_distrib_required_opts() -> + #{<<"global_host">> => <<"example.com">>, + <<"local_host">> => <<"datacenter1.example.com">>}. + +global_distrib_expected_config() -> + [{global_host, "example.com"}, + {local_host, "datacenter1.example.com"}]. + mod_event_pusher_sns(_Config) -> RequiredOpts = #{<<"access_key_id">> => <<"AKIAIOSFODNN7EXAMPLE">>, <<"secret_access_key">> => <<"KEY">>, diff --git a/test/config_parser_SUITE_data/modules.toml b/test/config_parser_SUITE_data/modules.toml index c624a4e514..c249c15033 100644 --- a/test/config_parser_SUITE_data/modules.toml +++ b/test/config_parser_SUITE_data/modules.toml @@ -111,7 +111,6 @@ local_host = "datacenter1.example.com" connections.endpoints = [{host = "172.16.0.2", port = 5555}] connections.advertised_endpoints = [{host = "172.16.0.2", port = 5555}] - connections.tls.enabled = true connections.tls.certfile = "priv/dc1.pem" connections.tls.cacertfile = "priv/ca.pem" connections.connections_per_endpoint = 30 From 61050ce42e72af27d68c84264d48a31df1d2e225 Mon Sep 17 00:00:00 2001 From: Leszek Witkowicz Date: Mon, 21 Dec 2020 12:22:26 +0100 Subject: [PATCH 088/104] make mod_sic config declarative --- src/config/mongoose_config_parser_toml.erl | 1 + src/config/mongoose_config_spec.erl | 1 + src/config/mongoose_config_validator_toml.erl | 3 --- src/mod_sic.erl | 8 +++++++- test/config_parser_SUITE.erl | 3 ++- 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index f942c81f63..6949ddf88a 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -397,6 +397,7 @@ node_to_string(Node) -> [binary_to_list(Node)]. Mod =/= <<"mod_register">>, Mod =/= <<"mod_roster">>, Mod =/= <<"mod_shared_roster_ldap">>, + Mod =/= <<"mod_sic">>, Mod =/= <<"mod_stream_management">>, Mod =/= <<"mod_vcard">>, Mod =/= <<"mod_version">>). % TODO temporary, remove with 'handler/1' diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index 7f2476ff88..35c83e15ef 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -758,6 +758,7 @@ all_modules() -> mod_register, mod_roster, mod_shared_roster_ldap, + mod_sic, mod_stream_management, mod_vcard, mod_version]. diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index 9cd3ec2081..49600e92ec 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -21,9 +21,6 @@ validate(Path, [F]) when is_function(F, 1) -> validate([<<"iqdisc">>, <<"mod_time">>, <<"modules">>|_], [{iqdisc, V}]) -> validate_iqdisc(V); -validate([<<"iqdisc">>, <<"mod_sic">>, <<"modules">>|_], - [{iqdisc, V}]) -> - validate_iqdisc(V); validate(_Path, _Value) -> ok. diff --git a/src/mod_sic.erl b/src/mod_sic.erl index 1577f5b48c..01b429f321 100644 --- a/src/mod_sic.erl +++ b/src/mod_sic.erl @@ -31,12 +31,14 @@ -export([start/2, stop/1, + config_spec/0, process_local_iq/4, process_sm_iq/4 ]). --include("mongoose.hrl"). -include("jlib.hrl"). +-include("mongoose.hrl"). +-include("mongoose_config_spec.hrl"). -define(NS_SIC, <<"urn:xmpp:sic:1">>). @@ -51,6 +53,10 @@ stop(Host) -> gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_SIC), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_SIC). +-spec config_spec() -> mongoose_config_spec:config_section(). +config_spec() -> + #section{ + items = #{<<"iqdisc">> => mongoose_config_spec:iqdisc()}}. process_local_iq(#jid{} = JID, _To, Acc, #iq{type = 'get', sub_el = _SubEl} = IQ) -> diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index 8827f66655..c753e817cc 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -2598,7 +2598,8 @@ mod_shared_roster_ldap(_Config) -> ?errf(T(#{<<"ldap_filter">> => 1})). mod_sic(_Config) -> - check_iqdisc(mod_sic). + check_iqdisc(mod_sic), + ?eqf(modopts(mod_sic, []), #{<<"modules">> => #{<<"mod_sic">> => #{}}}). mod_stream_management(_Config) -> T = fun(Opts) -> #{<<"modules">> => #{<<"mod_stream_management">> => Opts}} end, From c81d4c34e4c6fdf9e28c640c1250f58f106bdb3f Mon Sep 17 00:00:00 2001 From: Leszek Witkowicz Date: Mon, 21 Dec 2020 12:28:29 +0100 Subject: [PATCH 089/104] make mod_time config declarative --- src/config/mongoose_config_parser_toml.erl | 1 + src/config/mongoose_config_spec.erl | 1 + src/config/mongoose_config_validator_toml.erl | 8 -------- src/mod_time.erl | 14 ++++++++++++-- test/config_parser_SUITE.erl | 3 ++- 5 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index 6949ddf88a..7c0f0a346b 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -399,6 +399,7 @@ node_to_string(Node) -> [binary_to_list(Node)]. Mod =/= <<"mod_shared_roster_ldap">>, Mod =/= <<"mod_sic">>, Mod =/= <<"mod_stream_management">>, + Mod =/= <<"mod_time">>, Mod =/= <<"mod_vcard">>, Mod =/= <<"mod_version">>). % TODO temporary, remove with 'handler/1' diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index 35c83e15ef..eba35f9b01 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -760,6 +760,7 @@ all_modules() -> mod_shared_roster_ldap, mod_sic, mod_stream_management, + mod_time, mod_vcard, mod_version]. diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index 49600e92ec..db21770155 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -18,9 +18,6 @@ validate(Path, [F]) when is_function(F, 1) -> validate(Path, F(?HOST)); %% Modules -validate([<<"iqdisc">>, <<"mod_time">>, <<"modules">>|_], - [{iqdisc, V}]) -> - validate_iqdisc(V); validate(_Path, _Value) -> ok. @@ -129,11 +126,6 @@ validate_jid(Jid) -> error(#{what => validate_jid_failed, value => Jid}) end. -validate_iqdisc(no_queue) -> ok; -validate_iqdisc(one_queue) -> ok; -validate_iqdisc(parallel) -> ok; -validate_iqdisc({queues, N}) when is_integer(N), N > 0 -> ok. - validate_domain(Domain) when is_list(Domain) -> #jid{luser = <<>>, lresource = <<>>} = jid:from_binary(list_to_binary(Domain)), validate_domain_res(Domain). diff --git a/src/mod_time.erl b/src/mod_time.erl index eec14305dc..1f993d05bf 100644 --- a/src/mod_time.erl +++ b/src/mod_time.erl @@ -6,13 +6,19 @@ %%%------------------------------------------------------------------- -module(mod_time). -author('ludwik.bukowski@erlang-solutions.com'). + -behaviour(gen_mod). -behaviour(mongoose_module_metrics). --export([start/2, stop/1, process_local_iq/4]). --include("mongoose.hrl"). + +-export([start/2, stop/1, config_spec/0, process_local_iq/4]). + -include("jlib.hrl"). +-include("mongoose.hrl"). +-include("mongoose_config_spec.hrl"). + -xep([{xep, 202}, {version, "2.0"}]). -xep([{xep, 82}, {version, "1.1"}]). + start(Host, Opts) -> mod_disco:register_feature(Host, ?NS_TIME), IQDisc = gen_mod:get_opt(iqdisc, Opts, @@ -26,6 +32,10 @@ stop(Host) -> gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_TIME). +-spec config_spec() -> mongoose_config_spec:config_section(). +config_spec() -> + #section{ + items = #{<<"iqdisc">> => mongoose_config_spec:iqdisc()}}. process_local_iq(_From, _To, Acc, #iq{type = set, sub_el = SubEl} = IQ) -> {Acc, IQ#iq{type = error, sub_el = [SubEl, mongoose_xmpp_errors:not_allowed()]}}; diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index c753e817cc..2505b3cc2e 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -2625,7 +2625,8 @@ mod_stream_management_stale_h(_Config) -> ?errf(T(#{<<"geriatric">> => <<"one">>})). mod_time(_Config) -> - check_iqdisc(mod_time). + check_iqdisc(mod_time), + ?eqf(modopts(mod_time, []), #{<<"modules">> => #{<<"mod_time">> => #{}}}). mod_vcard(_Config) -> check_iqdisc(mod_vcard), From 43dbf7cecce7201295ca7a210692f52f5b6432dc Mon Sep 17 00:00:00 2001 From: Jan Ciesla Date: Mon, 21 Dec 2020 20:22:35 +0100 Subject: [PATCH 090/104] Remove int_or_inf_or_atom from mod_stream_management --- doc/modules/mod_stream_management.md | 14 +++++++ src/config/mongoose_config_parser_toml.erl | 3 -- src/config/mongoose_config_validator_toml.erl | 11 ------ src/mod_stream_management.erl | 37 ++++++++++++++----- test/config_parser_SUITE.erl | 6 ++- 5 files changed, 46 insertions(+), 25 deletions(-) diff --git a/doc/modules/mod_stream_management.md b/doc/modules/mod_stream_management.md index 1eb9ce2060..c9bd7f95ff 100644 --- a/doc/modules/mod_stream_management.md +++ b/doc/modules/mod_stream_management.md @@ -7,6 +7,13 @@ while the management of the session tables and configuration is implemented in ## Options +### `modules.mod_stream_management.buffer` +* **Syntax:** boolean +* **Default:** true +* **Example:** `buffer = false` + +Enables buffer for messages to be acknowledged. + ### `modules.mod_stream_management.buffer_max` * **Syntax:** positive integer or string `"infinity"` or string `"no_buffer"` * **Default:** `100` @@ -14,6 +21,13 @@ while the management of the session tables and configuration is implemented in Buffer size for messages yet to be acknowledged. +### `modules.mod_stream_management.ack` +* **Syntax:** boolean +* **Default:** true +* **Example:** `ack = false` + +Enables ack requests to be sent from the server to the client. + ### `modules.mod_stream_management.ack_freq` * **Syntax:** positive integer or string `"never"` * **Default:** `1` diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index 7c0f0a346b..6f460f77e8 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -272,9 +272,6 @@ convert(V, string) -> binary_to_list(V); convert(V, atom) -> b2a(V); convert(<<"infinity">>, int_or_infinity) -> infinity; %% TODO maybe use TOML '+inf' convert(V, int_or_infinity) when is_integer(V) -> V; -convert(<<"infinity">>, int_or_infinity_or_atom) -> infinity; -convert(<<"no_buffer">>, int_or_infinity_or_atom) -> no_buffer; -convert(V, int_or_infinity_or_atom) when is_integer(V) -> V; convert(V, int_or_atom) when is_integer(V) -> V; convert(V, int_or_atom) -> b2a(V); convert(V, integer) when is_integer(V) -> V; diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index db21770155..20812491a1 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -31,10 +31,6 @@ validate(V, integer, positive) -> validate_positive_integer(V); validate(V, integer, port) -> validate_port(V); validate(V, int_or_infinity, non_negative) -> validate_non_negative_integer_or_infinity(V); validate(V, int_or_infinity, positive) -> validate_positive_integer_or_infinity(V); -validate(V, int_or_infinity_or_atom, positive) -> - validate_positive_integer_or_infinity_or_atom(V, no_buffer); -validate(V, int_or_atom, positive) -> - validate_positive_integer_or_atom(V, never); validate(V, string, url) -> validate_url(V); validate(V, string, domain) -> validate_domain(V); validate(V, string, domain_template) -> validate_domain_template(V); @@ -92,13 +88,6 @@ validate_non_negative_integer_or_infinity(infinity) -> ok. validate_positive_integer_or_infinity(Value) when is_integer(Value), Value > 0 -> ok; validate_positive_integer_or_infinity(infinity) -> ok. -validate_positive_integer_or_atom(Value, Atom) when is_atom(Value), Value == Atom -> ok; -validate_positive_integer_or_atom(Value, _) when is_integer(Value), Value > 0 -> ok. - -validate_positive_integer_or_infinity_or_atom(Value, _) when is_integer(Value), Value > 0 -> ok; -validate_positive_integer_or_infinity_or_atom(infinity, _) -> ok; -validate_positive_integer_or_infinity_or_atom(Value, Atom) when is_atom(Value), Value == Atom -> ok. - validate_enum(Value, Values) -> case lists:member(Value, Values) of true -> diff --git a/src/mod_stream_management.erl b/src/mod_stream_management.erl index 7ea6177202..50ffda4b07 100644 --- a/src/mod_stream_management.erl +++ b/src/mod_stream_management.erl @@ -6,7 +6,8 @@ %% `gen_mod' callbacks -export([start/2, stop/1, - config_spec/0]). + config_spec/0, + process_buffer_and_ack/1]). %% `ejabberd_hooks' handlers -export([add_sm_feature/2, @@ -78,16 +79,34 @@ stop(Host) -> -spec config_spec() -> mongoose_config_spec:config_section(). config_spec() -> #section{ - items = #{<<"buffer_max">> => #option{type = int_or_infinity_or_atom, - validate = positive}, - <<"ack_freq">> => #option{type = int_or_atom, - validate = positive}, - <<"resume_timeout">> => #option{type = integer, - validate = positive}, - <<"stale_h">> => stale_h_config_spec() - } + items = #{<<"buffer">> => #option{type = boolean}, + <<"buffer_max">> => #option{type = int_or_infinity, + validate = positive}, + <<"ack">> => #option{type = boolean}, + <<"ack_freq">> => #option{type = integer, + validate = positive}, + <<"resume_timeout">> => #option{type = integer, + validate = positive}, + <<"stale_h">> => stale_h_config_spec() + }, + process = fun ?MODULE:process_buffer_and_ack/1 }. +process_buffer_and_ack(KVs) -> + {[Buffer, Ack], Opts} = proplists:split(KVs, [buffer, ack]), + OptsWithBuffer = check_buffer(Buffer, Opts), + check_ack(Ack, OptsWithBuffer). + +check_buffer([{buffer, false}], Opts) -> + lists:ukeysort(1, [{buffer_max, no_buffer}] ++ Opts); +check_buffer(_, Opts) -> + Opts. + +check_ack([{ack, false}], Opts) -> + lists:ukeysort(1, [{ack_freq, never}] ++ Opts); +check_ack(_, Opts) -> + Opts. + stale_h_config_spec() -> #section{ items = #{<<"enabled">> => #option{type = boolean}, diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index 2505b3cc2e..d33f8b2db3 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -2604,12 +2604,14 @@ mod_sic(_Config) -> mod_stream_management(_Config) -> T = fun(Opts) -> #{<<"modules">> => #{<<"mod_stream_management">> => Opts}} end, M = fun(Cfg) -> modopts(mod_stream_management, Cfg) end, - ?eqf(M([{buffer_max, no_buffer}]), T(#{<<"buffer_max">> => <<"no_buffer">>})), + ?eqf(M([{buffer_max, no_buffer}]), T(#{<<"buffer">> => false})), + ?eqf(M([{ack_freq, never}]), T(#{<<"ack">> => false})), + ?eqf(M([{buffer_max, 10}]), T(#{<<"buffer_max">> => 10})), ?eqf(M([{ack_freq, 1}]), T(#{<<"ack_freq">> => 1})), ?eqf(M([{resume_timeout, 600}]), T(#{<<"resume_timeout">> => 600})), ?errf(T(#{<<"buffer_max">> => -1})), - ?errf(T(#{<<"ack_freq">> => <<"one">>})), + ?errf(T(#{<<"ack_freq">> => false})), ?errf(T(#{<<"resume_timeout">> => true})). mod_stream_management_stale_h(_Config) -> From be01a4b29ebc33a6f9b29a3297bb38d2ebfbfde9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Mon, 21 Dec 2020 16:09:55 +0100 Subject: [PATCH 091/104] Remove imperative config parsing for modules --- src/config/mongoose_config_parser_toml.erl | 102 --------------------- src/config/mongoose_config_spec.erl | 14 ++- test/config_parser_SUITE.erl | 7 +- 3 files changed, 15 insertions(+), 108 deletions(-) diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index 7c0f0a346b..30b13db26c 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -93,57 +93,6 @@ parse_root(Path, Content) -> ensure_keys([<<"general">>], Content), parse_section(Path, Content). -%% path: (host_config[].)modules.* --spec process_module(path(), toml_section()) -> [option()]. -process_module([Mod|_] = Path, Opts) -> - %% Sort option keys to ensure options could be matched in tests - post_process_module(b2a(Mod), parse_section(Path, Opts)). - -post_process_module(Mod, Opts) -> - [{Mod, lists:sort(Opts)}]. - -%% path: (host_config[].)modules.*.* --spec module_opt(path(), toml_value()) -> [option()]. -% General options -module_opt([<<"iqdisc">>|_], V) -> - {Type, Opts} = maps:take(<<"type">>, V), - [{iqdisc, iqdisc_value(b2a(Type), Opts)}]; -module_opt([<<"backend">>|_], V) -> - [{backend, b2a(V)}]; -%% LDAP-specific options -module_opt([<<"ldap_pool_tag">>|_], V) -> - [{ldap_pool_tag, b2a(V)}]; -module_opt([<<"ldap_base">>|_], V) -> - [{ldap_base, b2l(V)}]; -module_opt([<<"ldap_filter">>|_], V) -> - [{ldap_filter, b2l(V)}]; -module_opt([<<"ldap_deref">>|_], V) -> - [{ldap_deref, b2a(V)}]; -%% Backend-specific options -module_opt([<<"riak">>|_] = Path, V) -> - parse_section(Path, V). - -%% path: (host_config[].)modules.*.riak.* --spec riak_opts(path(), toml_section()) -> [option()]. -riak_opts([<<"defaults_bucket_type">>|_], V) -> - [{defaults_bucket_type, V}]; -riak_opts([<<"names_bucket_type">>|_], V) -> - [{names_bucket_type, V}]; -riak_opts([<<"version_bucket_type">>|_], V) -> - [{version_bucket_type, V}]; -riak_opts([<<"bucket_type">>|_], V) -> - [{bucket_type, V}]; -riak_opts([<<"search_index">>|_], V) -> - [{search_index, V}]. - --spec iqdisc_value(atom(), toml_section()) -> option(). -iqdisc_value(queues, #{<<"workers">> := Workers} = V) -> - limit_keys([<<"workers">>], V), - {queues, Workers}; -iqdisc_value(Type, V) -> - limit_keys([], V), - Type. - %% path: host_config[] -spec process_host_item(path(), toml_section()) -> config_list(). process_host_item(Path, M) -> @@ -158,15 +107,6 @@ set_overrides(Overrides, State) -> %% TODO replace with binary_to_existing_atom where possible, prevent atom leak b2a(B) -> binary_to_atom(B, utf8). -b2l(B) -> binary_to_list(B). - --spec limit_keys([toml_key()], toml_section()) -> any(). -limit_keys(Keys, Section) -> - case maps:keys(maps:without(Keys, Section)) of - [] -> ok; - ExtraKeys -> error(#{what => unexpected_keys, unexpected_keys => ExtraKeys}) - end. - -spec ensure_keys([toml_key()], toml_section()) -> any(). ensure_keys(Keys, Section) -> case lists:filter(fun(Key) -> not maps:is_key(Key, Section) end, Keys) of @@ -365,55 +305,13 @@ path_to_string(Path) -> node_to_string(item) -> []; node_to_string({host, _}) -> []; -node_to_string({tls, TLSAtom}) -> [atom_to_list(TLSAtom)]; node_to_string(Node) -> [binary_to_list(Node)]. --define(HAS_NO_SPEC(Mod), - Mod =/= <<"mod_adhoc">>, - Mod =/= <<"mod_auth_token">>, - Mod =/= <<"mod_bosh">>, - Mod =/= <<"mod_caps">>, - Mod =/= <<"mod_carboncopy">>, - Mod =/= <<"mod_csi">>, - Mod =/= <<"mod_disco">>, - Mod =/= <<"mod_event_pusher">>, - Mod =/= <<"mod_extdisco">>, - Mod =/= <<"mod_global_distrib">>, - Mod =/= <<"mod_http_upload">>, - Mod =/= <<"mod_inbox">>, - Mod =/= <<"mod_jingle_sip">>, - Mod =/= <<"mod_keystore">>, - Mod =/= <<"mod_last">>, - Mod =/= <<"mod_mam_meta">>, - Mod =/= <<"mod_muc">>, - Mod =/= <<"mod_muc_light">>, - Mod =/= <<"mod_muc_log">>, - Mod =/= <<"mod_offline">>, - Mod =/= <<"mod_ping">>, - Mod =/= <<"mod_privacy">>, - Mod =/= <<"mod_private">>, - Mod =/= <<"mod_pubsub">>, - Mod =/= <<"mod_push_service_mongoosepush">>, - Mod =/= <<"mod_register">>, - Mod =/= <<"mod_roster">>, - Mod =/= <<"mod_shared_roster_ldap">>, - Mod =/= <<"mod_sic">>, - Mod =/= <<"mod_stream_management">>, - Mod =/= <<"mod_time">>, - Mod =/= <<"mod_vcard">>, - Mod =/= <<"mod_version">>). % TODO temporary, remove with 'handler/1' - -spec handler(path()) -> fun((path(), toml_value()) -> option()) | mongoose_config_spec:config_node(). handler([]) -> fun parse_root/2; handler([<<"host_config">>]) -> fun parse_list/2; -%% modules -handler([Mod, <<"modules">>]) when ?HAS_NO_SPEC(Mod) -> fun process_module/2; -handler([_, Mod, <<"modules">>]) when ?HAS_NO_SPEC(Mod) -> fun module_opt/2; -handler([_, <<"riak">>, Mod, <<"modules">>]) when ?HAS_NO_SPEC(Mod) -> - fun riak_opts/2; - %% host_config handler([_, <<"host_config">>]) -> fun process_host_item/2; handler([<<"auth">>, _, <<"host_config">>] = P) -> handler_for_host(P); diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index eba35f9b01..84db619bd4 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -711,25 +711,29 @@ tls_items() -> %% path: (host_config[].)services services() -> - Services = [{a2b(Service), mongoose_service:config_spec(Service)} || Service <- all_services()], + Services = [{a2b(Service), mongoose_service:config_spec(Service)} + || Service <- configurable_services()], #section{ items = maps:from_list(Services), format = local_config }. -all_services() -> +configurable_services() -> [service_admin_extra, service_mongoose_system_metrics]. %% path: (host_config[].)modules modules() -> - Modules = [{a2b(Module), gen_mod:config_spec(Module)} || Module <- all_modules()], + Modules = [{a2b(Module), gen_mod:config_spec(Module)} + || Module <- configurable_modules()], + Items = maps:from_list(Modules), #section{ - items = maps:from_list(Modules), + items = Items#{default => #section{items = #{}}}, + validate_keys = module, format = host_local_config }. -all_modules() -> +configurable_modules() -> [mod_adhoc, mod_auth_token, mod_bosh, diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index 2505b3cc2e..954f0b3b4c 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -222,7 +222,8 @@ groups() -> mod_vcard_ldap_vcard_map, mod_vcard_ldap_search_fields, mod_vcard_ldap_search_reported, - mod_version]}, + mod_version, + modules_without_config]}, {services, [parallel], [service_admin_extra, service_mongoose_system_metrics]} ]. @@ -2739,6 +2740,10 @@ mod_version(_Config) -> ?errf(T(#{<<"os_info">> => 1})), check_iqdisc(mod_version). +modules_without_config(_Config) -> + ?eqf(modopts(mod_amp, []), #{<<"modules">> => #{<<"mod_amp">> => #{}}}), + ?errf(#{<<"modules">> => #{<<"mod_wrong">> => #{}}}). + %% Services service_admin_extra(_Config) -> From c53d250d8dfa7e0f129e6924d71ec2d0ca19cb02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Mon, 21 Dec 2020 16:34:32 +0100 Subject: [PATCH 092/104] Remove validate/2 as no validators are left --- src/config/mongoose_config_parser_toml.erl | 5 ++--- src/config/mongoose_config_validator_toml.erl | 15 +-------------- 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index 30b13db26c..ae02e497ff 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -162,9 +162,8 @@ handle_step(validate, {ParsedValue, Spec}) -> {ParsedValue, Spec} end; handle_step(validate, ParsedValue) -> - fun(Path, _Value) -> - mongoose_config_validator_toml:validate(Path, ParsedValue), - ParsedValue + fun(_Path, _Value) -> + ParsedValue %% no validation here, this will be removed very soon end; handle_step(process, {ParsedValue, Spec}) -> fun(Path, _Value) -> diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index db21770155..71c5b7a5be 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -1,7 +1,6 @@ -module(mongoose_config_validator_toml). --export([validate/2, - validate/3, +-export([validate/3, validate_section/2, validate_list/2]). @@ -9,18 +8,6 @@ -include("mongoose_config_spec.hrl"). -include_lib("jid/include/jid.hrl"). --define(HOST, 'HOST'). - --spec validate(mongoose_config_parser_toml:path(), - mongoose_config_parser_toml:option() | mongoose_config_parser_toml:config_list()) -> - any(). -validate(Path, [F]) when is_function(F, 1) -> - validate(Path, F(?HOST)); - -%% Modules -validate(_Path, _Value) -> - ok. - validate(V, binary, domain) -> validate_binary_domain(V); validate(V, binary, non_empty) -> validate_non_empty_binary(V); validate(V, binary, {module, Prefix}) -> From c25eac6ad9e896c269ca81ef5e94efadc9095315 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Tue, 22 Dec 2020 08:19:19 +0100 Subject: [PATCH 093/104] Parse host_config declaratively Also: - extend declarative parsing to the root - remove imperative leftovers that are unused now - simplify some functions, e.g. handler/2 --- src/config/mongoose_config_parser_toml.erl | 81 +++------------------- src/config/mongoose_config_spec.erl | 65 +++++++++++++---- 2 files changed, 61 insertions(+), 85 deletions(-) diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index ae02e497ff..e6277ec974 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -66,39 +66,11 @@ process(Content) -> config_error(Errors) -> {config_error, "Could not read the TOML configuration file", Errors}. -%% Config processing functions are annotated with TOML paths -%% Path syntax: dotted, like TOML keys with the following additions: -%% - '[]' denotes an element in a list -%% - '( ... )' encloses an optional prefix -%% - '*' is a wildcard for names - usually that name is passed as an argument -%% If the path is the same as for the previous function, it is not repeated. -%% -%% Example: (host_config[].)access.* -%% Meaning: either a key in the 'access' section, e.g. -%% [access] -%% local = ... -%% or the same, but prefixed for a specific host, e.g. -%% [[host_config]] -%% host = "myhost" -%% host_config.access -%% local = ... - %% root path -spec parse(toml_section()) -> config_list(). parse(Content) -> handle([], Content). --spec parse_root(path(), toml_section()) -> config_list(). -parse_root(Path, Content) -> - ensure_keys([<<"general">>], Content), - parse_section(Path, Content). - -%% path: host_config[] --spec process_host_item(path(), toml_section()) -> config_list(). -process_host_item(Path, M) -> - {_Host, Sections} = maps:take(<<"host">>, M), - parse_section(Path, Sections). - set_overrides(Overrides, State) -> lists:foldl(fun({override, Scope}, CurrentState) -> mongoose_config_parser:override(Scope, CurrentState) @@ -154,30 +126,20 @@ handle_step(parse, Spec) when is_tuple(Spec) -> Errors -> Errors end end; -handle_step(parse, Handler) -> - Handler; handle_step(validate, {ParsedValue, Spec}) -> fun(_Path, _Value) -> validate(ParsedValue, Spec), {ParsedValue, Spec} end; -handle_step(validate, ParsedValue) -> - fun(_Path, _Value) -> - ParsedValue %% no validation here, this will be removed very soon - end; handle_step(process, {ParsedValue, Spec}) -> fun(Path, _Value) -> ProcessedValue = process(Path, ParsedValue, process_spec(Spec)), {ProcessedValue, Spec} end; -handle_step(process, V) -> - fun(_, _) -> V end; handle_step(format, {ParsedValue, Spec}) -> fun(Path, _Value) -> format(Path, ParsedValue, format_spec(Spec)) - end; -handle_step(format, V) -> - fun(_, _) -> V end. + end. check_required_keys(#section{required = all, items = Items}, Section) -> ensure_keys(maps:keys(Items), Section); @@ -259,6 +221,8 @@ format(_Path, V, {kv, Key}) -> [{Key, V}]; format(_Path, V, item) -> [V]; +format(_Path, _V, skip) -> + []; format([Key|_], V, prepend_key) -> L = [b2a(Key) | tuple_to_list(V)], [list_to_tuple(L)]; @@ -306,41 +270,12 @@ node_to_string(item) -> []; node_to_string({host, _}) -> []; node_to_string(Node) -> [binary_to_list(Node)]. --spec handler(path()) -> - fun((path(), toml_value()) -> option()) | mongoose_config_spec:config_node(). -handler([]) -> fun parse_root/2; -handler([<<"host_config">>]) -> fun parse_list/2; - -%% host_config -handler([_, <<"host_config">>]) -> fun process_host_item/2; -handler([<<"auth">>, _, <<"host_config">>] = P) -> handler_for_host(P); -handler([<<"modules">>, _, <<"host_config">>] = P) -> handler_for_host(P); -handler([<<"general">>, _, <<"host_config">>]) -> fun parse_section/2; -handler([_, <<"general">>, _, <<"host_config">>] = P) -> handler_for_host(P); -handler([_, <<"s2s">>, _, <<"host_config">>] = P) -> handler_for_host(P); handler(Path) -> - reverse_handler(lists:reverse(Path)). - -reverse_handler([<<"host_config">>, {host, _} | Subtree]) -> - handler(lists:reverse(Subtree)); -reverse_handler(Path) -> - mongoose_config_spec:handler(Path). - -%% 1. Strip host_config, choose the handler for the remaining path -%% 2. Wrap the handler in a fun that calls the resulting function F for the current host --spec handler_for_host(path()) -> - fun((path(), toml_value()) -> option()) | mongoose_config_spec:config_node(). -handler_for_host(Path) -> - [<<"host_config">>, {host, Host} | Rest] = lists:reverse(Path), - case handler(lists:reverse(Rest)) of - Handler when is_function(Handler) -> - fun(PathArg, ValueArg) -> - ConfigFunctions = Handler(PathArg, ValueArg), - lists:flatmap(fun(F) -> F(Host) end, ConfigFunctions) - end; - Spec -> - Spec - end. + SimplePath = lists:reverse(lists:map(fun simplify_node/1, Path)), + mongoose_config_spec:handler(SimplePath). + +simplify_node({host, _}) -> item; +simplify_node(Node) -> Node. -spec item_key(path(), toml_value()) -> tuple() | item. item_key([<<"host_config">>], #{<<"host">> := Host}) -> {host, Host}; diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index 84db619bd4..29e4bdb5a8 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -14,25 +14,38 @@ handler(Path) -> handler(Path, root()). -handler([Node], #section{items = Items}) when is_binary(Node) -> - case maps:is_key(Node, Items) of - true -> maps:get(Node, Items); - false -> maps:get(default, Items) - end; -handler([item], #list{items = Item}) -> - Item; handler([Node|Rest], #section{items = Items}) when is_binary(Node) -> Item = case maps:is_key(Node, Items) of true -> maps:get(Node, Items); false -> maps:get(default, Items) end, handler(Rest, Item); -handler([item|Rest], #list{items = Items}) -> - handler(Rest, Items). +handler([item|Rest], #list{items = Item}) -> + handler(Rest, Item); +handler([], Node) -> + Node. + +%% Config processing functions are annotated with TOML paths +%% Path syntax: dotted, like TOML keys with the following additions: +%% - '[]' denotes an element in a list +%% - '( ... )' encloses an optional prefix +%% - '*' is a wildcard for names - usually that name is passed as an argument +%% If the path is the same as for the previous function, it is not repeated. +%% +%% Example: (host_config[].)access.* +%% Meaning: either a key in the 'access' section, e.g. +%% [access] +%% local = ... +%% or the same, but prefixed for a specific host, e.g. +%% [[host_config]] +%% host = "myhost" +%% host_config.access +%% local = ... root() -> + General = general(), #section{ - items = #{<<"general">> => general(), + items = #{<<"general">> => General#section{required = [<<"hosts">>]}, <<"listen">> => listen(), <<"auth">> => auth(), <<"outgoing_pools">> => outgoing_pools(), @@ -41,12 +54,41 @@ root() -> <<"shaper">> => shaper(), <<"acl">> => acl(), <<"access">> => access(), - <<"s2s">> => s2s() + <<"s2s">> => s2s(), + <<"host_config">> => #list{items = host_config(), + format = none} }, required = [<<"general">>], format = none }. +%% path: host_config[] +host_config() -> + #section{ + items = #{%% Host is only validated here - it is stored in the path, + %% see mongoose_config_parser_toml:item_key/1 + <<"host">> => #option{type = binary, + validate = non_empty, + format = skip}, + + %% Sections below are allowed in host_config, + %% but only options with these formats are accepted: + %% - host_local_config + %% - host_or_global_config + %% - host_or_global_acl + %% Any other options would be caught by + %% mongoose_config_parser_toml:format/3 + <<"general">> => general(), + <<"auth">> => auth(), + <<"modules">> => modules(), + <<"shaper">> => shaper(), + <<"acl">> => acl(), + <<"access">> => access(), + <<"s2s">> => s2s() + }, + format = none + }. + %% path: general general() -> #section{ @@ -100,7 +142,6 @@ general() -> <<"hide_service_name">> => #option{type = boolean, format = host_local_config} }, - required = [<<"hosts">>], format = none }. From 99a5180fe78ad2533d93e284de131d22fb2ae4b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Tue, 22 Dec 2020 10:16:25 +0100 Subject: [PATCH 094/104] Build the config spec only once Pass the current node as an argument of the 'handle' function instead of traversing the structure from the root for each node. Parsing time for test/config_parser_SUITE_data/mongooseim-pgsql.toml: before: ~ 33 ms after: ~ 8 ms --- src/config/mongoose_config_parser_toml.erl | 82 +++++++++++----------- src/config/mongoose_config_spec.erl | 14 ---- 2 files changed, 42 insertions(+), 54 deletions(-) diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index e6277ec974..e547d0e020 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -66,10 +66,9 @@ process(Content) -> config_error(Errors) -> {config_error, "Could not read the TOML configuration file", Errors}. -%% root path -spec parse(toml_section()) -> config_list(). parse(Content) -> - handle([], Content). + handle([], Content, mongoose_config_spec:root()). set_overrides(Overrides, State) -> lists:foldl(fun({override, Scope}, CurrentState) -> @@ -86,59 +85,68 @@ ensure_keys(Keys, Section) -> MissingKeys -> error(#{what => missing_mandatory_keys, missing_keys => MissingKeys}) end. --spec parse_section(path(), toml_section()) -> [option()]. -parse_section(Path, M) -> +-spec parse_section(path(), toml_section(), mongoose_config_spec:config_section()) -> [option()]. +parse_section(Path, M, #section{items = Items}) -> lists:flatmap(fun({K, V}) -> - handle([K|Path], V) + handle([K|Path], V, get_spec_for_key(K, Items)) end, lists:sort(maps:to_list(M))). --spec parse_list(path(), [toml_value()]) -> [option()]. -parse_list(Path, L) -> +-spec get_spec_for_key(toml_key(), map()) -> mongoose_config_spec:config_node(). +get_spec_for_key(Key, Items) -> + case maps:is_key(Key, Items) of + true -> + maps:get(Key, Items); + false -> + case maps:find(default, Items) of + {ok, Spec} -> Spec; + error -> error(#{what => unexpected_key, key => Key}) + end + end. + +-spec parse_list(path(), [toml_value()], mongoose_config_spec:config_list()) -> [option()]. +parse_list(Path, L, #list{items = ItemSpec}) -> lists:flatmap(fun(Elem) -> Key = item_key(Path, Elem), - handle([Key|Path], Elem) + handle([Key|Path], Elem, ItemSpec) end, L). --spec handle(path(), toml_value()) -> option(). -handle(Path, Value) -> +-spec handle(path(), toml_value(), mongoose_config_spec:config_node()) -> option(). +handle(Path, Value, Spec) -> lists:foldl(fun(_, [#{what := _, class := error}] = Error) -> Error; (StepName, AccIn) -> - try_call(handle_step(StepName, AccIn), StepName, Path, Value) - end, Path, [handle, parse, validate, process, format]). + try_call(handle_step(StepName, AccIn), StepName, Path, Value, Spec) + end, Value, [parse, validate, process, format]). -handle_step(handle, _) -> - fun(Path, _Value) -> handler(Path) end; -handle_step(parse, Spec) when is_tuple(Spec) -> - fun(Path, Value) -> +handle_step(parse, Value) -> + fun(Path, Spec) -> ParsedValue = case Spec of #section{} = Spec when is_map(Value) -> check_required_keys(Spec, Value), validate_keys(Spec, Value), - parse_section(Path, Value); + parse_section(Path, Value, Spec); #list{} when is_list(Value) -> - parse_list(Path, Value); + parse_list(Path, Value, Spec); #option{type = Type} when not is_list(Value), not is_map(Value) -> convert(Value, Type) end, case extract_errors(ParsedValue) of - [] -> {ParsedValue, Spec}; + [] -> ParsedValue; Errors -> Errors end end; -handle_step(validate, {ParsedValue, Spec}) -> - fun(_Path, _Value) -> +handle_step(validate, ParsedValue) -> + fun(_Path, Spec) -> validate(ParsedValue, Spec), - {ParsedValue, Spec} + ParsedValue end; -handle_step(process, {ParsedValue, Spec}) -> - fun(Path, _Value) -> - ProcessedValue = process(Path, ParsedValue, process_spec(Spec)), - {ProcessedValue, Spec} +handle_step(process, ParsedValue) -> + fun(Path, Spec) -> + process(Path, ParsedValue, process_spec(Spec)) end; -handle_step(format, {ParsedValue, Spec}) -> - fun(Path, _Value) -> - format(Path, ParsedValue, format_spec(Spec)) +handle_step(format, ProcessedValue) -> + fun(Path, Spec) -> + format(Path, ProcessedValue, format_spec(Spec)) end. check_required_keys(#section{required = all, items = Items}, Section) -> @@ -235,10 +243,12 @@ get_host(Path) -> _ -> global end. --spec try_call(fun((path(), any()) -> option()), atom(), path(), toml_value()) -> option(). -try_call(F, StepName, Path, Value) -> +-spec try_call(fun((path(), any()) -> option()), + atom(), path(), toml_value(), + mongoose_config_spec:config_node()) -> option(). +try_call(F, StepName, Path, Value, Spec) -> try - F(Path, Value) + F(Path, Spec) catch error:Reason:Stacktrace -> BasicFields = #{what => toml_processing_failed, class => error, @@ -251,7 +261,6 @@ try_call(F, StepName, Path, Value) -> end. -spec error_text(atom()) -> string(). -error_text(handle) -> "Unexpected option in the TOML configuration file"; error_text(parse) -> "Malformed option in the TOML configuration file"; error_text(validate) -> "Incorrect option value in the TOML configuration file"; error_text(process) -> "Unable to process a value the TOML configuration file"; @@ -270,13 +279,6 @@ node_to_string(item) -> []; node_to_string({host, _}) -> []; node_to_string(Node) -> [binary_to_list(Node)]. -handler(Path) -> - SimplePath = lists:reverse(lists:map(fun simplify_node/1, Path)), - mongoose_config_spec:handler(SimplePath). - -simplify_node({host, _}) -> item; -simplify_node(Node) -> Node. - -spec item_key(path(), toml_value()) -> tuple() | item. item_key([<<"host_config">>], #{<<"host">> := Host}) -> {host, Host}; item_key(_, _) -> item. diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index 29e4bdb5a8..e6bd1486cb 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -11,20 +11,6 @@ -export_type([config_node/0, config_section/0, config_list/0, config_option/0]). -handler(Path) -> - handler(Path, root()). - -handler([Node|Rest], #section{items = Items}) when is_binary(Node) -> - Item = case maps:is_key(Node, Items) of - true -> maps:get(Node, Items); - false -> maps:get(default, Items) - end, - handler(Rest, Item); -handler([item|Rest], #list{items = Item}) -> - handler(Rest, Item); -handler([], Node) -> - Node. - %% Config processing functions are annotated with TOML paths %% Path syntax: dotted, like TOML keys with the following additions: %% - '[]' denotes an element in a list From f3c238ca1389ec09ae9813c1caa5ae83c68d5a99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Tue, 22 Dec 2020 10:53:17 +0100 Subject: [PATCH 095/104] Simplify handle_step - do not create fun's for each step --- src/config/mongoose_config_parser_toml.erl | 61 +++++++++------------- 1 file changed, 26 insertions(+), 35 deletions(-) diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index e547d0e020..7fa92c25bc 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -114,40 +114,32 @@ parse_list(Path, L, #list{items = ItemSpec}) -> handle(Path, Value, Spec) -> lists:foldl(fun(_, [#{what := _, class := error}] = Error) -> Error; - (StepName, AccIn) -> - try_call(handle_step(StepName, AccIn), StepName, Path, Value, Spec) + (StepName, Acc) -> + try_step(StepName, Path, Value, Acc, Spec) end, Value, [parse, validate, process, format]). -handle_step(parse, Value) -> - fun(Path, Spec) -> - ParsedValue = case Spec of - #section{} = Spec when is_map(Value) -> - check_required_keys(Spec, Value), - validate_keys(Spec, Value), - parse_section(Path, Value, Spec); - #list{} when is_list(Value) -> - parse_list(Path, Value, Spec); - #option{type = Type} when not is_list(Value), not is_map(Value) -> - convert(Value, Type) - end, - case extract_errors(ParsedValue) of - [] -> ParsedValue; - Errors -> Errors - end - end; -handle_step(validate, ParsedValue) -> - fun(_Path, Spec) -> - validate(ParsedValue, Spec), - ParsedValue +handle_step(parse, Path, Value, Spec) -> + ParsedValue = case Spec of + #section{} = Spec when is_map(Value) -> + check_required_keys(Spec, Value), + validate_keys(Spec, Value), + parse_section(Path, Value, Spec); + #list{} when is_list(Value) -> + parse_list(Path, Value, Spec); + #option{type = Type} when not is_list(Value), not is_map(Value) -> + convert(Value, Type) + end, + case extract_errors(ParsedValue) of + [] -> ParsedValue; + Errors -> Errors end; -handle_step(process, ParsedValue) -> - fun(Path, Spec) -> - process(Path, ParsedValue, process_spec(Spec)) - end; -handle_step(format, ProcessedValue) -> - fun(Path, Spec) -> - format(Path, ProcessedValue, format_spec(Spec)) - end. +handle_step(validate, _Path, ParsedValue, Spec) -> + validate(ParsedValue, Spec), + ParsedValue; +handle_step(process, Path, ParsedValue, Spec) -> + process(Path, ParsedValue, process_spec(Spec)); +handle_step(format, Path, ProcessedValue, Spec) -> + format(Path, ProcessedValue, format_spec(Spec)). check_required_keys(#section{required = all, items = Items}, Section) -> ensure_keys(maps:keys(Items), Section); @@ -243,12 +235,11 @@ get_host(Path) -> _ -> global end. --spec try_call(fun((path(), any()) -> option()), - atom(), path(), toml_value(), +-spec try_step(atom(), path(), toml_value(), term(), mongoose_config_spec:config_node()) -> option(). -try_call(F, StepName, Path, Value, Spec) -> +try_step(StepName, Path, Value, Acc, Spec) -> try - F(Path, Spec) + handle_step(StepName, Path, Acc, Spec) catch error:Reason:Stacktrace -> BasicFields = #{what => toml_processing_failed, class => error, From 07a15f7eefb6aed8daebb3f0a7f56e61346024d4 Mon Sep 17 00:00:00 2001 From: Leszek Witkowicz Date: Tue, 22 Dec 2020 10:55:23 +0100 Subject: [PATCH 096/104] apply comments --- doc/modules/mod_stream_management.md | 8 ++++---- test/config_parser_SUITE.erl | 6 ++++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/doc/modules/mod_stream_management.md b/doc/modules/mod_stream_management.md index c9bd7f95ff..87609851fd 100644 --- a/doc/modules/mod_stream_management.md +++ b/doc/modules/mod_stream_management.md @@ -15,9 +15,9 @@ while the management of the session tables and configuration is implemented in Enables buffer for messages to be acknowledged. ### `modules.mod_stream_management.buffer_max` -* **Syntax:** positive integer or string `"infinity"` or string `"no_buffer"` +* **Syntax:** positive integer or string `"infinity"` * **Default:** `100` -* **Example:** `buffer_max = "no_buffer"` +* **Example:** `buffer_max = 500` Buffer size for messages yet to be acknowledged. @@ -29,9 +29,9 @@ Buffer size for messages yet to be acknowledged. Enables ack requests to be sent from the server to the client. ### `modules.mod_stream_management.ack_freq` -* **Syntax:** positive integer or string `"never"` +* **Syntax:** positive integer * **Default:** `1` -* **Example:** `ack_freq = "never"` +* **Example:** `ack_freq = 3` Frequency of ack requests sent from the server to the client, e.g. 1 means a request after each stanza, 3 means a request after each 3 stanzas. diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index d33f8b2db3..52938e321e 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -2605,13 +2605,15 @@ mod_stream_management(_Config) -> T = fun(Opts) -> #{<<"modules">> => #{<<"mod_stream_management">> => Opts}} end, M = fun(Cfg) -> modopts(mod_stream_management, Cfg) end, ?eqf(M([{buffer_max, no_buffer}]), T(#{<<"buffer">> => false})), - ?eqf(M([{ack_freq, never}]), T(#{<<"ack">> => false})), ?eqf(M([{buffer_max, 10}]), T(#{<<"buffer_max">> => 10})), + ?eqf(M([{ack_freq, never}]), T(#{<<"ack">> => false})), ?eqf(M([{ack_freq, 1}]), T(#{<<"ack_freq">> => 1})), ?eqf(M([{resume_timeout, 600}]), T(#{<<"resume_timeout">> => 600})), + ?errf(T(#{<<"buffer">> => 0})), ?errf(T(#{<<"buffer_max">> => -1})), - ?errf(T(#{<<"ack_freq">> => false})), + ?errf(T(#{<<"ack">> => <<"false">>})), + ?errf(T(#{<<"ack_freq">> => 0})), ?errf(T(#{<<"resume_timeout">> => true})). mod_stream_management_stale_h(_Config) -> From 62eac1a8ccf69fbec584e3b44c1877fa5451ad02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Tue, 5 Jan 2021 07:58:59 +0100 Subject: [PATCH 097/104] Rename the validator module as it is not TOML-specific now --- src/config/mongoose_config_parser_toml.erl | 10 +++++----- src/config/mongoose_config_spec.erl | 2 +- ...alidator_toml.erl => mongoose_config_validator.erl} | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) rename src/config/{mongoose_config_validator_toml.erl => mongoose_config_validator.erl} (99%) diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index 4de804d9c8..ae21eebd41 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -149,15 +149,15 @@ check_required_keys(#section{required = Required}, Section) -> validate_keys(#section{validate_keys = undefined}, _Section) -> ok; validate_keys(#section{validate_keys = Validator}, Section) -> lists:foreach(fun(Key) -> - mongoose_config_validator_toml:validate(b2a(Key), atom, Validator) + mongoose_config_validator:validate(b2a(Key), atom, Validator) end, maps:keys(Section)). validate(Value, #section{validate = Validator}) -> - mongoose_config_validator_toml:validate_section(Value, Validator); + mongoose_config_validator:validate_section(Value, Validator); validate(Value, #list{validate = Validator}) -> - mongoose_config_validator_toml:validate_list(Value, Validator); + mongoose_config_validator:validate_list(Value, Validator); validate(Value, #option{type = Type, validate = Validator}) -> - mongoose_config_validator_toml:validate(Value, Type, Validator). + mongoose_config_validator:validate(Value, Type, Validator). process_spec(#section{process = Process}) -> Process; process_spec(#list{process = Process}) -> Process; @@ -184,7 +184,7 @@ format_spec(#option{format = Format}) -> Format. format(Path, KVs, {foreach, Format}) when is_atom(Format) -> Keys = lists:map(fun({K, _}) -> K end, KVs), - mongoose_config_validator_toml:validate_list(Keys, unique), + mongoose_config_validator:validate_list(Keys, unique), lists:flatmap(fun({K, V}) -> format(Path, V, {Format, K}) end, KVs); format([Key|_] = Path, V, host_local_config) -> format(Path, V, {host_local_config, b2a(Key)}); diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index e6bd1486cb..e02b5941e6 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -1098,7 +1098,7 @@ process_sasl_external(V) when V =:= standard; V =:= auth_id -> V; process_sasl_external(M) -> - mongoose_config_validator_toml:validate(M, atom, module), + mongoose_config_validator:validate(M, atom, module), {mod, M}. process_sasl_mechanism(V) -> diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator.erl similarity index 99% rename from src/config/mongoose_config_validator_toml.erl rename to src/config/mongoose_config_validator.erl index 07c7a11764..5142a4d0e1 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator.erl @@ -1,4 +1,4 @@ --module(mongoose_config_validator_toml). +-module(mongoose_config_validator). -export([validate/3, validate_section/2, From 24404241855f5c58a5641414d4681fc2b978a6f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Tue, 5 Jan 2021 08:45:15 +0100 Subject: [PATCH 098/104] Add type specs to the declarative TOML config parser - Put the types in the right module * formatting info and option types in the 'spec' module * validators in the 'validator' module * lower-level items in the 'parser' module - Rename the 'option()' type to 'config_part()' * there is already a 'config_option' type * the type was counter-intuitive as it did not correspond to a single option --- include/mongoose_config_spec.hrl | 31 +++++---- src/config/mongoose_config_parser_toml.erl | 74 ++++++++++++++-------- src/config/mongoose_config_spec.erl | 47 +++++++++++++- src/config/mongoose_config_validator.erl | 15 +++++ 4 files changed, 127 insertions(+), 40 deletions(-) diff --git a/include/mongoose_config_spec.hrl b/include/mongoose_config_spec.hrl index 2a59f51880..0eed1d1127 100644 --- a/include/mongoose_config_spec.hrl +++ b/include/mongoose_config_spec.hrl @@ -1,19 +1,22 @@ -ifndef(MONGOOSEIM_CONFIG_SPEC_HRL). -define(MONGOOSEIM_CONFIG_SPEC_HRL, true). --record(section, {items, - validate_keys = any, - required = [], - validate = any, - process, - format = default}). --record(list, {items, - validate = any, - process, - format = default}). --record(option, {type, - validate = any, - process, - format = default}). +-record(section, {items :: #{mongoose_config_parser_toml:toml_key() | default => + mongoose_config_spec:config_node()}, + validate_keys = any :: mongoose_config_validator:validator(), + required = [] :: [mongoose_config_parser_toml:toml_key()] | all, + validate = any :: mongoose_config_validator:section_validator(), + process :: undefined | mongoose_config_parser_toml:list_processor(), + format = default :: mongoose_config_spec:format()}). + +-record(list, {items :: mongoose_config_spec:config_node(), + validate = any :: mongoose_config_validator:list_validator(), + process :: undefined | mongoose_config_parser_toml:list_processor(), + format = default :: mongoose_config_spec:format()}). + +-record(option, {type :: mongoose_config_spec:option_type(), + validate = any :: mongoose_config_validator:validator(), + process :: undefined | mongoose_config_parser_toml:processor(), + format = default :: mongoose_config_spec:format()}). -endif. diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index ae21eebd41..224f078f7f 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -23,18 +23,29 @@ -type toml_section() :: tomerl:section(). %% Output: list of config records, containing key-value pairs --type option() :: term(). % a part of a config value OR a list of them, may contain config errors --type top_level_option() :: #config{} | #local_config{} | acl:acl(). +-type option_value() :: atom() | binary() | string() | float(). % parsed leaf value +-type config_part() :: term(). % any part of a top-level option value, may contain config errors +-type top_level_config() :: #config{} | #local_config{} | acl:acl(). -type config_error() :: #{class := error, what := atom(), text := string(), any() => any()}. -type override() :: {override, atom()}. --type config() :: top_level_option() | config_error() | override(). --type config_list() :: [config() | fun((ejabberd:server()) -> [config()])]. % see HOST_F +-type config() :: top_level_config() | config_error() | override(). +-type config_list() :: [config() | fun((jid:server()) -> [config()])]. % see HOST_F + +-type list_processor() :: fun((path(), [config_part()]) -> config_part()) + | fun(([config_part()]) -> config_part()). + +-type processor() :: fun((path(), config_part()) -> config_part()) + | fun((config_part()) -> config_part()). + +-type step() :: parse | validate | process | format. %% Path from the currently processed config node to the root %% - toml_key(): key in a toml_section() %% - item: item in a list -%% - tuple(): item in a list, tagged with data from the item, e.g. host name --type path() :: [toml_key() | item | tuple()]. +%% - {host, Host}: item in the list of hosts in host_config +-type path() :: [toml_key() | item | {host, jid:server()}]. + +-export_type([toml_key/0, option_value/0, config_part/0, list_processor/0, processor/0]). -spec parse_file(FileName :: string()) -> mongoose_config_parser:state(). parse_file(FileName) -> @@ -85,7 +96,8 @@ ensure_keys(Keys, Section) -> MissingKeys -> error(#{what => missing_mandatory_keys, missing_keys => MissingKeys}) end. --spec parse_section(path(), toml_section(), mongoose_config_spec:config_section()) -> [option()]. +-spec parse_section(path(), toml_section(), mongoose_config_spec:config_section()) -> + [config_part()]. parse_section(Path, M, #section{items = Items}) -> lists:flatmap(fun({K, V}) -> handle([K|Path], V, get_spec_for_key(K, Items)) @@ -103,21 +115,23 @@ get_spec_for_key(Key, Items) -> end end. --spec parse_list(path(), [toml_value()], mongoose_config_spec:config_list()) -> [option()]. +-spec parse_list(path(), [toml_value()], mongoose_config_spec:config_list()) -> [config_part()]. parse_list(Path, L, #list{items = ItemSpec}) -> lists:flatmap(fun(Elem) -> Key = item_key(Path, Elem), handle([Key|Path], Elem, ItemSpec) end, L). --spec handle(path(), toml_value(), mongoose_config_spec:config_node()) -> option(). +-spec handle(path(), toml_value(), mongoose_config_spec:config_node()) -> config_part(). handle(Path, Value, Spec) -> lists:foldl(fun(_, [#{what := _, class := error}] = Error) -> Error; - (StepName, Acc) -> - try_step(StepName, Path, Value, Acc, Spec) + (Step, Acc) -> + try_step(Step, Path, Value, Acc, Spec) end, Value, [parse, validate, process, format]). +-spec handle_step(step(), path(), toml_value(), mongoose_config_spec:config_node()) -> + config_part(). handle_step(parse, Path, Value, Spec) -> ParsedValue = case Spec of #section{} = Spec when is_map(Value) -> @@ -141,17 +155,19 @@ handle_step(process, Path, ParsedValue, Spec) -> handle_step(format, Path, ProcessedValue, Spec) -> format(Path, ProcessedValue, format_spec(Spec)). +-spec check_required_keys(mongoose_config_spec:config_section(), toml_section()) -> any(). check_required_keys(#section{required = all, items = Items}, Section) -> ensure_keys(maps:keys(Items), Section); check_required_keys(#section{required = Required}, Section) -> ensure_keys(Required, Section). -validate_keys(#section{validate_keys = undefined}, _Section) -> ok; +-spec validate_keys(mongoose_config_spec:config_section(), toml_section()) -> any(). validate_keys(#section{validate_keys = Validator}, Section) -> lists:foreach(fun(Key) -> mongoose_config_validator:validate(b2a(Key), atom, Validator) end, maps:keys(Section)). +-spec validate(config_part(), mongoose_config_spec:config_node()) -> any(). validate(Value, #section{validate = Validator}) -> mongoose_config_validator:validate_section(Value, Validator); validate(Value, #list{validate = Validator}) -> @@ -159,14 +175,19 @@ validate(Value, #list{validate = Validator}) -> validate(Value, #option{type = Type, validate = Validator}) -> mongoose_config_validator:validate(Value, Type, Validator). +-spec process_spec(mongoose_config_spec:config_section() | + mongoose_config_spec:config_list()) -> undefined | list_processor(); + (mongoose_config_spec:config_option()) -> undefined | processor(). process_spec(#section{process = Process}) -> Process; process_spec(#list{process = Process}) -> Process; process_spec(#option{process = Process}) -> Process. +-spec process(path(), config_part(), undefined | processor()) -> config_part(). process(_Path, V, undefined) -> V; process(_Path, V, F) when is_function(F, 1) -> F(V); process(Path, V, F) when is_function(F, 2) -> F(Path, V). +-spec convert(toml_value(), mongoose_config_spec:option_type()) -> option_value(). convert(V, boolean) when is_boolean(V) -> V; convert(V, binary) when is_binary(V) -> V; convert(V, string) -> binary_to_list(V); @@ -178,10 +199,12 @@ convert(V, int_or_atom) -> b2a(V); convert(V, integer) when is_integer(V) -> V; convert(V, float) when is_float(V) -> V. +-spec format_spec(mongoose_config_spec:config_node()) -> mongoose_config_spec:format(). format_spec(#section{format = Format}) -> Format; format_spec(#list{format = Format}) -> Format; format_spec(#option{format = Format}) -> Format. +-spec format(path(), config_part(), mongoose_config_spec:format()) -> config_part(). format(Path, KVs, {foreach, Format}) when is_atom(Format) -> Keys = lists:map(fun({K, _}) -> K end, KVs), mongoose_config_validator:validate_list(Keys, unique), @@ -223,32 +246,33 @@ format(_Path, _V, skip) -> format([Key|_], V, prepend_key) -> L = [b2a(Key) | tuple_to_list(V)], [list_to_tuple(L)]; -format(_Path, V, none) -> +format(_Path, V, none) when is_list(V) -> V. +-spec get_host(path()) -> jid:server() | global. get_host(Path) -> case lists:reverse(Path) of [<<"host_config">>, {host, Host} | _] -> Host; _ -> global end. --spec try_step(atom(), path(), toml_value(), term(), - mongoose_config_spec:config_node()) -> option(). -try_step(StepName, Path, Value, Acc, Spec) -> +-spec try_step(step(), path(), toml_value(), term(), + mongoose_config_spec:config_node()) -> config_part(). +try_step(Step, Path, Value, Acc, Spec) -> try - handle_step(StepName, Path, Acc, Spec) + handle_step(Step, Path, Acc, Spec) catch error:Reason:Stacktrace -> BasicFields = #{what => toml_processing_failed, class => error, stacktrace => Stacktrace, - text => error_text(StepName), + text => error_text(Step), toml_path => path_to_string(Path), toml_value => Value}, ErrorFields = error_fields(Reason), [maps:merge(BasicFields, ErrorFields)] end. --spec error_text(atom()) -> string(). +-spec error_text(step()) -> string(). error_text(parse) -> "Malformed option in the TOML configuration file"; error_text(validate) -> "Incorrect option value in the TOML configuration file"; error_text(process) -> "Unable to process a value the TOML configuration file"; @@ -267,13 +291,13 @@ node_to_string(item) -> []; node_to_string({host, _}) -> []; node_to_string(Node) -> [binary_to_list(Node)]. --spec item_key(path(), toml_value()) -> tuple() | item. +-spec item_key(path(), toml_value()) -> {host, jid:server()} | item. item_key([<<"host_config">>], #{<<"host">> := Host}) -> {host, Host}; item_key(_, _) -> item. %% Processing of the parsed options --spec get_hosts(config_list()) -> [ejabberd:server()]. +-spec get_hosts(config_list()) -> [jid:server()]. get_hosts(Config) -> case lists:filter(fun(#config{key = hosts}) -> true; (_) -> false @@ -282,7 +306,7 @@ get_hosts(Config) -> [#config{value = Hosts}] -> Hosts end. --spec build_state([ejabberd:server()], [top_level_option()], [override()]) -> +-spec build_state([jid:server()], [top_level_config()], [override()]) -> mongoose_config_parser:state(). build_state(Hosts, Opts, Overrides) -> lists:foldl(fun(F, StateIn) -> F(StateIn) end, @@ -293,21 +317,21 @@ build_state(Hosts, Opts, Overrides) -> fun mongoose_config_parser:add_dep_modules/1, fun(S) -> set_overrides(Overrides, S) end]). -%% Any nested option() may be a config_error() - this function extracts them all recursively +%% Any nested config_part() may be a config_error() - this function extracts them all recursively -spec extract_errors([config()]) -> [config_error()]. extract_errors(Config) -> extract(fun(#{what := _, class := error}) -> true; (_) -> false end, Config). --spec extract(fun((option()) -> boolean()), option()) -> [option()]. +-spec extract(fun((config_part()) -> boolean()), config_part()) -> [config_part()]. extract(Pred, Data) -> case Pred(Data) of true -> [Data]; false -> extract_items(Pred, Data) end. --spec extract_items(fun((option()) -> boolean()), option()) -> [option()]. +-spec extract_items(fun((config_part()) -> boolean()), config_part()) -> [config_part()]. extract_items(Pred, L) when is_list(L) -> lists:flatmap(fun(El) -> extract(Pred, El) end, L); extract_items(Pred, T) when is_tuple(T) -> extract_items(Pred, tuple_to_list(T)); extract_items(Pred, M) when is_map(M) -> extract_items(Pred, maps:to_list(M)); diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index e02b5941e6..12ad1d0368 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -9,7 +9,52 @@ -type config_list() :: #list{}. -type config_option() :: #option{}. --export_type([config_node/0, config_section/0, config_list/0, config_option/0]). +-type option_type() :: boolean | binary | string | atom | int_or_infinity + | int_or_atom | integer | float. + +%% The format describes how the TOML Key and the parsed and processed Value +%% are packed into the resulting list of configuration options. +-type format() :: top_level_config_format() | config_part_format(). + +%% The value becomes a top-level config record, acl record or 'override' tuple +-type top_level_config_format() :: + % {override, Value} + override + + % Config records, see the type below for details + | config_record_format() + + % Config record for each {K, V} in Value, which has to be a list + | {foreach, config_record_format()} + + % Config record, the key is replaced with NewKey + | {config_record_format(), NewKey :: term()} + + % #config{} with either key = {Tag, Key, Host} - inside host_config + % or key = {Tag, Key, global} - at the top level + | {host_or_global_config, Tag :: term()} + + % Like above, but for an acl record + | host_or_global_acl. + +%% The value becomes a top-level config record: #config{} or #local_config{} +-type config_record_format() :: + config % #config{} + | local_config % #local_config{} + | host_local_config. % Inside host_config: #local_config{key = {Key, Host}} + % Otherwise: one such record for each configured host + +%% The value becomes a nested config part - key-value pair or just a value +-type config_part_format() :: + default % {Key, Value} for section items, Value for list items + | item % only Value + | skip % nothing - the item is ignored + | none % no formatting - Value must be a list and is injected into the parent list + | {kv, NewKey :: term()} % {NewKey, Value} - replaces the key with NewKey + | prepend_key. % {Key, V1, ..., Vn} when Value = {V1, ..., Vn} + +-export_type([config_node/0, config_section/0, config_list/0, config_option/0, + format/0, option_type/0]). %% Config processing functions are annotated with TOML paths %% Path syntax: dotted, like TOML keys with the following additions: diff --git a/src/config/mongoose_config_validator.erl b/src/config/mongoose_config_validator.erl index 5142a4d0e1..afc9ff88a4 100644 --- a/src/config/mongoose_config_validator.erl +++ b/src/config/mongoose_config_validator.erl @@ -8,6 +8,19 @@ -include("mongoose_config_spec.hrl"). -include_lib("jid/include/jid.hrl"). +-type validator() :: + any | non_empty | non_negative | positive | module | {module, Prefix :: atom()} + | jid | domain | domain_template | url | ip_address | ip_mask | network_address | port + | filename | dirname | loglevel | pool_name | shaper | access_rule | {enum, list()}. + +-type section_validator() :: any | non_empty. + +-type list_validator() :: any | non_empty | unique | unique_non_empty. + +-export_type([validator/0, section_validator/0, list_validator/0]). + +-spec validate(mongoose_config_parser_toml:option_value(), + mongoose_config_spec:option_type(), validator()) -> any(). validate(V, binary, domain) -> validate_binary_domain(V); validate(V, binary, non_empty) -> validate_non_empty_binary(V); validate(V, binary, {module, Prefix}) -> @@ -38,11 +51,13 @@ validate(V, atom, non_empty) -> validate_non_empty_atom(V); validate(V, _, {enum, Values}) -> validate_enum(V, Values); validate(_V, _, any) -> ok. +-spec validate_list([mongoose_config_parser_toml:config_part()], list_validator()) -> any(). validate_list([_|_], non_empty) -> ok; validate_list(L = [_|_], unique_non_empty) -> validate_unique_items(L); validate_list(L, unique) -> validate_unique_items(L); validate_list(L, any) when is_list(L) -> ok. +-spec validate_section([mongoose_config_parser_toml:config_part()], section_validator()) -> any(). validate_section([_|_], non_empty) -> ok; validate_section(L, any) when is_list(L) -> ok. From b0c3e058e4c5984c54306242ab49fa9101798e19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Tue, 5 Jan 2021 11:17:59 +0100 Subject: [PATCH 099/104] Do no export all functions from mongooseim_config_spec Also: - make function names consistent - remove an unused function --- src/config/mongoose_config_spec.erl | 53 ++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index 12ad1d0368..00d23b6b57 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -1,6 +1,46 @@ -module(mongoose_config_spec). --compile(export_all). +%% entry point - returns the entire spec +-export([root/0]). + +%% spec parts used by modules and services +-export([wpool_items/0, + iqdisc/0, + ldap_uids/0]). + +%% callbacks for the 'process' step +-export([process_host/1, + process_sm_backend/1, + process_ctl_access_rule/1, + process_ip_version/1, + process_listener/2, + process_verify_peer/1, + process_sni/1, + process_xmpp_tls/1, + process_fast_tls/1, + process_http_handler/2, + process_sasl_external/1, + process_sasl_mechanism/1, + process_auth/1, + process_auth_password/1, + process_jwt_secret/1, + process_ldap_uids/1, + process_ldap_dn_filter/1, + process_ldap_local_filter/1, + process_pool/2, + process_cassandra_auth/1, + process_rdbms_connection/1, + process_riak_tls/1, + process_cassandra_server/1, + process_riak_credentials/1, + process_iqdisc/1, + process_shaper/1, + process_acl_item/1, + process_access_rule_item/1, + process_s2s_address_family/1, + process_s2s_host_policy/1, + process_s2s_address/1, + process_s2s_domain_cert/1]). -include("mongoose_config_spec.hrl"). @@ -128,7 +168,7 @@ general() -> format = local_config}, <<"hosts">> => #list{items = #option{type = binary, validate = non_empty, - process = fun ?MODULE:prepare_host/1}, + process = fun ?MODULE:process_host/1}, validate = unique_non_empty, format = config}, <<"registration_timeout">> => #option{type = int_or_infinity, @@ -848,10 +888,10 @@ iqdisc() -> <<"workers">> => #option{type = integer, validate = positive}}, required = [<<"type">>], - process = fun ?MODULE:format_iqdisc/1 + process = fun ?MODULE:process_iqdisc/1 }. -format_iqdisc(KVs) -> +process_iqdisc(KVs) -> {[[{type, Type}]], WorkersOpts} = proplists:split(KVs, [type]), iqdisc(Type, WorkersOpts). @@ -1033,7 +1073,7 @@ process_ctl_access_rule(KVs) -> process_sm_backend(Backend) -> {Backend, []}. -prepare_host(Host) -> +process_host(Host) -> Node = jid:nodeprep(Host), true = Node =/= error, Node. @@ -1041,9 +1081,6 @@ prepare_host(Host) -> process_sni(false) -> disable. -process_lasse_handler([{module, Module}]) -> - [Module]. - process_verify_peer(false) -> verify_none; process_verify_peer(true) -> verify_peer. From 56c4c29c96cebfdfd998b4ceb6dd5940e4bfc00d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Tue, 5 Jan 2021 11:21:49 +0100 Subject: [PATCH 100/104] Get rid of clean_opts/clear_opts Motivation: - it is not needed anymore in the only module that used it - validation and processing of the opts should be done in the declarative config spec Also: remove an unused function from big tests. That was the only function which used the string version of registration_watchers for mod_register. This version is unsupported now. --- big_tests/tests/accounts_SUITE.erl | 3 --- src/gen_mod.erl | 16 +--------------- src/mod_register.erl | 10 ---------- 3 files changed, 1 insertion(+), 28 deletions(-) diff --git a/big_tests/tests/accounts_SUITE.erl b/big_tests/tests/accounts_SUITE.erl index 16dc14454b..baf4f07e04 100644 --- a/big_tests/tests/accounts_SUITE.erl +++ b/big_tests/tests/accounts_SUITE.erl @@ -392,8 +392,5 @@ enable_watcher(Config, Watcher) -> disable_watcher(Config) -> restore_mod_register_options(Config). -watcher(Watcher) -> - {registration_watchers, [binary_to_list(Watcher)]}. - domain() -> ct:get_config({hosts, mim, domain}). diff --git a/src/gen_mod.erl b/src/gen_mod.erl index 34babd4130..fc63100a8a 100644 --- a/src/gen_mod.erl +++ b/src/gen_mod.erl @@ -126,7 +126,7 @@ start_module(Host, Module, Opts) -> start_module_for_host(Host, Module, Opts0) -> {links, LinksBefore} = erlang:process_info(self(), links), - Opts = clear_opts(Module, Opts0), + Opts = proplists:unfold(Opts0), set_module_opts_mnesia(Host, Module, Opts), ets:insert(ejabberd_modules, #ejabberd_module{module_host = {Module, Host}, opts = Opts}), try @@ -473,20 +473,6 @@ get_module_proc(Host, Base) -> is_loaded(Host, Module) -> ets:member(ejabberd_modules, {Module, Host}). - --spec clear_opts(atom(), list()) -> list(). -clear_opts(Module, Opts0) -> - Opts = proplists:unfold(Opts0), - %% the module has to be loaded, - %% otherwise the erlang:function_exported/3 returns false - code:ensure_loaded(Module), - case erlang:function_exported(Module, clean_opts, 1) of - true -> - Module:clean_opts(Opts); - _ -> - Opts - end. - -spec get_deps(Host :: jid:server(), Module :: module(), Opts :: proplists:proplist()) -> module_deps_list(). get_deps(Host, Module, Opts) -> diff --git a/src/mod_register.erl b/src/mod_register.erl index 5931cf657b..2e55963aee 100644 --- a/src/mod_register.erl +++ b/src/mod_register.erl @@ -32,7 +32,6 @@ -export([start/2, stop/1, config_spec/0, - clean_opts/1, stream_feature_register/2, unauthenticated_iq_register/4, try_register/5, @@ -111,15 +110,6 @@ process_welcome_message(KVs) -> Subject = proplists:get_value(subject, KVs, ""), {Subject, Body}. -clean_opts(Opts) -> - lists:map(fun clean_opt/1, Opts). - -clean_opt({registration_watchers, Watchers}) -> - CleanWatchers = lists:map(fun mongoose_bin:string_to_binary/1, Watchers), - {registration_watchers, CleanWatchers}; -clean_opt(Item) -> - Item. - stream_feature_register(Acc, _Host) -> [#xmlel{name = <<"register">>, attrs = [{<<"xmlns">>, ?NS_FEATURE_IQREGISTER}]} | Acc]. From d777685eedcae0aee1b69ea31be6e79f80c9a3b9 Mon Sep 17 00:00:00 2001 From: Leszek Witkowicz Date: Thu, 7 Jan 2021 17:10:39 +0100 Subject: [PATCH 101/104] correct validate_domain_template to handle `@HOSTS@` wildcard properly --- src/config/mongoose_config_validator.erl | 10 ++++++---- src/event_pusher/mod_event_pusher_push.erl | 2 +- src/mod_vcard.erl | 2 +- test/config_parser_SUITE.erl | 2 ++ 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/config/mongoose_config_validator.erl b/src/config/mongoose_config_validator.erl index afc9ff88a4..a9bf35ef3a 100644 --- a/src/config/mongoose_config_validator.erl +++ b/src/config/mongoose_config_validator.erl @@ -11,7 +11,8 @@ -type validator() :: any | non_empty | non_negative | positive | module | {module, Prefix :: atom()} | jid | domain | domain_template | url | ip_address | ip_mask | network_address | port - | filename | dirname | loglevel | pool_name | shaper | access_rule | {enum, list()}. + | filename | dirname | loglevel | pool_name | shaper | access_rule | {enum, list()} + | {domain_template, string()}. -type section_validator() :: any | non_empty. @@ -33,7 +34,8 @@ validate(V, int_or_infinity, non_negative) -> validate_non_negative_integer_or_i validate(V, int_or_infinity, positive) -> validate_positive_integer_or_infinity(V); validate(V, string, url) -> validate_url(V); validate(V, string, domain) -> validate_domain(V); -validate(V, string, domain_template) -> validate_domain_template(V); +validate(V, string, {domain_template, Pattern}) -> validate_domain_template(V, Pattern); +validate(V, string, domain_template) -> validate_domain_template(V, "@HOST@"); validate(V, string, ip_address) -> validate_ip_address(V); validate(V, string, ip_mask) -> validate_ip_mask_string(V); validate(V, string, network_address) -> validate_network_address(V); @@ -141,8 +143,8 @@ validate_binary_domain(Domain) when is_binary(Domain) -> #jid{luser = <<>>, lresource = <<>>} = jid:from_binary(Domain), validate_domain_res(binary_to_list(Domain)). -validate_domain_template(Domain) -> - validate_binary_domain(gen_mod:make_subhost(Domain, <<"example.com">>)). +validate_domain_template(Domain, Pattern) -> + validate_binary_domain(re:replace(Domain, Pattern, <<"example.com">>, [{return, binary}])). validate_url(Url) -> validate_non_empty_string(Url). diff --git a/src/event_pusher/mod_event_pusher_push.erl b/src/event_pusher/mod_event_pusher_push.erl index 23fd116a64..4ba18f0c02 100644 --- a/src/event_pusher/mod_event_pusher_push.erl +++ b/src/event_pusher/mod_event_pusher_push.erl @@ -118,7 +118,7 @@ config_spec() -> <<"plugin_module">> => #option{type = atom, validate = module}, <<"virtual_pubsub_hosts">> => #list{items = #option{type = string, - validate = domain_template}} + validate = {domain_template, "@HOSTS@"}}} } }. diff --git a/src/mod_vcard.erl b/src/mod_vcard.erl index 580039568f..db052e76fb 100644 --- a/src/mod_vcard.erl +++ b/src/mod_vcard.erl @@ -427,7 +427,7 @@ process_sm_iq(From, To, Acc, #iq{type = set, sub_el = VCARD} = IQ) -> vcard_error(IQ, mongoose_xmpp_errors:not_allowed()) end, {Acc, Res}; -process_sm_iq(From, To, Acc, #iq{type = get, sub_el = SubEl} = IQ) -> +process_sm_iq(_, To, Acc, #iq{type = get, sub_el = SubEl} = IQ) -> #jid{luser = LUser, lserver = LServer} = To, Res = try mod_vcard_backend:get_vcard(LUser, LServer) of {ok, VCARD} -> diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index 786bd8ad99..267115856d 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -1724,6 +1724,8 @@ mod_event_pusher_push(_Config) -> T(#{<<"plugin_module">> => <<"mod_event_pusher_push_plugin_defaults">>})), ?eqf(M([{virtual_pubsub_hosts, ["host1", "host2"]}]), T(#{<<"virtual_pubsub_hosts">> => [<<"host1">>, <<"host2">>]})), + ?eqf(M([{virtual_pubsub_hosts, ["pubsub.@HOSTS@"]}]), + T(#{<<"virtual_pubsub_hosts">> => [<<"pubsub.@HOSTS@">>]})), ?errf(T(#{<<"backend">> => <<"redis">>})), ?errf(T(#{<<"wpool">> => true})), ?errf(T(#{<<"wpool">> => #{<<"workers">> => <<"500">>}})), From b9ff8427ab123a8c463da6083466fd6a8a03bcae Mon Sep 17 00:00:00 2001 From: Leszek Witkowicz Date: Thu, 14 Jan 2021 15:41:40 +0100 Subject: [PATCH 102/104] update migration guide with TOML updates --- doc/migrations/4.0.1_4.x.x.md | 43 +++++++++++++++++++++++++++++++++++ mkdocs.yml | 1 + 2 files changed, 44 insertions(+) diff --git a/doc/migrations/4.0.1_4.x.x.md b/doc/migrations/4.0.1_4.x.x.md index eff708d517..f2afdc60a0 100644 --- a/doc/migrations/4.0.1_4.x.x.md +++ b/doc/migrations/4.0.1_4.x.x.md @@ -6,3 +6,46 @@ Currently, only the `urn:xmpp:http:upload:0` XMLNS is served. All major, modern client libraries and applications support the 0.3.0+ specification. If you experience any issues with making requests to the HTTP File Upload service, please update your client. +## Retirement of old `*.cfg` format + +Since ____ release, we are no longer supporting `*.cfg` MongooseIM configuration format. For further application's usage, please use `TOML` format. + +## Minor changes in `TOML` config format + +* [`mod_bosh.max_pause`](../../modules/mod_bosh/#modulesmod_boshmax_pause) instead of `maxpause` + +* [`mod_caps.cache_life_time`](../../modules/mod_caps/#modulesmod_capscache_life_time): the value should be provided milliseconds + +* [`mod_disco.server_info.module`](../../modules/mod_disco/#modulesmod_discoserver_info): the field is optional, no longer required + +* [`mod_register`](../../modules/mod_register/#modulesmod_registeriqdisctype), [`mod_vcard`](../../modules/mod_vcard/#modulesmod_vcardiqdisctype) & [`mod_version`](../../modules/mod_version/#modulesmod_versioniqdisctype): `iqdisc.type` default value was changed from `"no_queue"` to `"one_queue"` + +* [`mod_event_pusher_push.virtual_pubsub_hosts`](../../modules/mod_event_pusher_push/#modulesmod_event_pusher_pushvirtual_pubsub_hosts): the default value was changed to `[]` + +* [`mod_event_pusher_sns.presence_updates_topic`](../../modules/mod_event_pusher_sns/#modulesmod_event_pusher_snspresence_updates_topic): default not set + +* `mod_event_pusher_sns.muc_host`: the option was removed + +* [`mod_global_distrib.connections.advertised_endpoints`](../../modules/mod_global_distrib/#modulesmod_global_distribconnectionsadvertised_endpoints): default not set (`false` is no longer accepted) + +* `mod_global_distrib.connections.tls.enabled`: flag was removed, TLS is enabled by providing [`cacertfile`](../../modules/mod_global_distrib/#modulesmod_global_distribconnectionstlscacertfile) and [`certfile`](../../modules/mod_global_distrib/#modulesmod_global_distribconnectionstlscertfile) options + +* [`mod_http_upload.s3`](../../modules/mod_http_upload/#s3-backend-options): the section is mandatory + +* [`mod_mam_meta.user_prefs_store`](../../modules/mod_mam/#modulesmod_mam_metauser_prefs_store): `false` value is no longer allowed + +* [`mod_muc_light.config_schema`](../../modules/mod_muc_light/#modulesmod_muc_lightconfig_schema): minor config fields simplification + +* `mod_muc_log`: change of default values for [`css_file`](../../modules/mod_muc_log/#modulesmod_muc_logcss_file) and [`top_link`](../../modules/mod_muc_log/#modulesmod_muc_logtop_link) + +* `mod_ping`: [`ping_interval`](../../modules/mod_ping/#modulesmod_pingping_interval) and [`ping_req_timeout`](../../modules/mod_ping/#modulesmod_pingping_req_timeout) explicitly said to be in seconds + +* [`mod_push_service_mongoosepush.api_version`](../../modules/mod_push_service_mongoosepush/#modulesmod_push_service_mongoosepushapi_version): the value is now limited to be `"v2"` or `"v3"` + +* `mod_stream_management`: minor adjustments of [`buffer_max`](../../modules/mod_stream_management/#modulesmod_stream_managementbuffer_max) and [`ack_freq`](../../modules/mod_stream_management/#modulesmod_stream_managementack_freq) options, [`buffer`](../../modules/mod_stream_management/#modulesmod_stream_managementbuffer) and [`ack`](../../modules/mod_stream_management/#modulesmod_stream_managementack) booleans were added + +* [`listen.c2s.tls.ciphers`](../../advanced-configuration/listen/#listenc2stlsciphers), [`listen.http.tls.ciphers`](../../advanced-configuration/listen/#listenhttptlsciphers) and [`outgoing_pools.*.*.connection.tls.ciphers`](../../advanced-configuration/outgoing-connections/#outgoing_poolsconnectiontlsciphers): the ciphers should now be formatted as a specification string + +* [listen.http.handlers.mod_websockets.ping_rate](../../advanced-configuration/listen/#listenhttphandlersmod_websocketsping_rate): `none` value is no longer allowed + +* `outgoing_pools.generic`: the option was removed diff --git a/mkdocs.yml b/mkdocs.yml index b425ac1e6d..7e8a5b4fb5 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -36,6 +36,7 @@ nav: - '3.6.0 to 3.7.0': 'migrations/3.6.0_3.7.0.md' - '3.7.0 to 4.0.0': 'migrations/3.7.0_4.0.0.md' - '4.0.0 to 4.0.1': 'migrations/4.0.0_4.0.1.md' + - '4.0.1 to 4.x.x': 'migrations/4.0.1_4.x.x.md' - 'MAM MUC migration helper': 'migrations/jid-from-mam-muc-script.md' - Platform: - 'Contributions to ecosystem': 'Contributions.md' From a05dd9fa677ca660a2f386210ec878895a80166d Mon Sep 17 00:00:00 2001 From: Leszek Witkowicz Date: Fri, 15 Jan 2021 10:23:08 +0100 Subject: [PATCH 103/104] remove config_format_SUITE from default.spec --- big_tests/default.spec | 1 - 1 file changed, 1 deletion(-) diff --git a/big_tests/default.spec b/big_tests/default.spec index 8f4adc9819..dc6178ebd6 100644 --- a/big_tests/default.spec +++ b/big_tests/default.spec @@ -21,7 +21,6 @@ {suites, "tests", cluster_commands_SUITE}. {suites, "tests", component_SUITE}. {suites, "tests", conf_reload_SUITE}. -{suites, "tests", config_format_SUITE}. {suites, "tests", connect_SUITE}. {suites, "tests", disco_and_caps_SUITE}. {suites, "tests", ejabberdctl_SUITE}. From 2c00f8593baaeca71b6336ce8daa2f01b5845679 Mon Sep 17 00:00:00 2001 From: Leszek Witkowicz Date: Fri, 15 Jan 2021 15:12:51 +0100 Subject: [PATCH 104/104] apply review comments --- doc/migrations/4.0.1_4.x.x.md | 34 +++++++++------------------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/doc/migrations/4.0.1_4.x.x.md b/doc/migrations/4.0.1_4.x.x.md index f2afdc60a0..be20038882 100644 --- a/doc/migrations/4.0.1_4.x.x.md +++ b/doc/migrations/4.0.1_4.x.x.md @@ -6,46 +6,30 @@ Currently, only the `urn:xmpp:http:upload:0` XMLNS is served. All major, modern client libraries and applications support the 0.3.0+ specification. If you experience any issues with making requests to the HTTP File Upload service, please update your client. -## Retirement of old `*.cfg` format +## Retirement of the old `*.cfg` format -Since ____ release, we are no longer supporting `*.cfg` MongooseIM configuration format. For further application's usage, please use `TOML` format. +Since release 4.x.x, we are no longer supporting the `*.cfg` MongooseIM configuration format. Please use the `TOML` format instead. -## Minor changes in `TOML` config format +## Minor changes in the `TOML` config format * [`mod_bosh.max_pause`](../../modules/mod_bosh/#modulesmod_boshmax_pause) instead of `maxpause` -* [`mod_caps.cache_life_time`](../../modules/mod_caps/#modulesmod_capscache_life_time): the value should be provided milliseconds - * [`mod_disco.server_info.module`](../../modules/mod_disco/#modulesmod_discoserver_info): the field is optional, no longer required -* [`mod_register`](../../modules/mod_register/#modulesmod_registeriqdisctype), [`mod_vcard`](../../modules/mod_vcard/#modulesmod_vcardiqdisctype) & [`mod_version`](../../modules/mod_version/#modulesmod_versioniqdisctype): `iqdisc.type` default value was changed from `"no_queue"` to `"one_queue"` - -* [`mod_event_pusher_push.virtual_pubsub_hosts`](../../modules/mod_event_pusher_push/#modulesmod_event_pusher_pushvirtual_pubsub_hosts): the default value was changed to `[]` - -* [`mod_event_pusher_sns.presence_updates_topic`](../../modules/mod_event_pusher_sns/#modulesmod_event_pusher_snspresence_updates_topic): default not set - -* `mod_event_pusher_sns.muc_host`: the option was removed - * [`mod_global_distrib.connections.advertised_endpoints`](../../modules/mod_global_distrib/#modulesmod_global_distribconnectionsadvertised_endpoints): default not set (`false` is no longer accepted) -* `mod_global_distrib.connections.tls.enabled`: flag was removed, TLS is enabled by providing [`cacertfile`](../../modules/mod_global_distrib/#modulesmod_global_distribconnectionstlscacertfile) and [`certfile`](../../modules/mod_global_distrib/#modulesmod_global_distribconnectionstlscertfile) options - -* [`mod_http_upload.s3`](../../modules/mod_http_upload/#s3-backend-options): the section is mandatory +* `mod_global_distrib.connections.tls.enabled`: the flag was removed, TLS is enabled by providing the [`cacertfile`](../../modules/mod_global_distrib/#modulesmod_global_distribconnectionstlscacertfile) and [`certfile`](../../modules/mod_global_distrib/#modulesmod_global_distribconnectionstlscertfile) options -* [`mod_mam_meta.user_prefs_store`](../../modules/mod_mam/#modulesmod_mam_metauser_prefs_store): `false` value is no longer allowed +* [`mod_http_upload.max_file_size`](../../modules/mod_http_upload/#modulesmod_http_uploadmax_file_size): `undefined` is no longer allowed -* [`mod_muc_light.config_schema`](../../modules/mod_muc_light/#modulesmod_muc_lightconfig_schema): minor config fields simplification +* [`mod_mam_meta.user_prefs_store`](../../modules/mod_mam/#modulesmod_mam_metauser_prefs_store): `false` is no longer allowed -* `mod_muc_log`: change of default values for [`css_file`](../../modules/mod_muc_log/#modulesmod_muc_logcss_file) and [`top_link`](../../modules/mod_muc_log/#modulesmod_muc_logtop_link) +* [`mod_muc_light.config_schema`](../../modules/mod_muc_light/#modulesmod_muc_lightconfig_schema): the usage of `value` and `type` fields was replaced with one of the following fields: `string_value`, `integer_value` or `float_value` -* `mod_ping`: [`ping_interval`](../../modules/mod_ping/#modulesmod_pingping_interval) and [`ping_req_timeout`](../../modules/mod_ping/#modulesmod_pingping_req_timeout) explicitly said to be in seconds - -* [`mod_push_service_mongoosepush.api_version`](../../modules/mod_push_service_mongoosepush/#modulesmod_push_service_mongoosepushapi_version): the value is now limited to be `"v2"` or `"v3"` +* [`mod_muc_log.css_file`](../../modules/mod_muc_log/#modulesmod_muc_logcss_file): the default value was changed from `"false"` to `not set` * `mod_stream_management`: minor adjustments of [`buffer_max`](../../modules/mod_stream_management/#modulesmod_stream_managementbuffer_max) and [`ack_freq`](../../modules/mod_stream_management/#modulesmod_stream_managementack_freq) options, [`buffer`](../../modules/mod_stream_management/#modulesmod_stream_managementbuffer) and [`ack`](../../modules/mod_stream_management/#modulesmod_stream_managementack) booleans were added * [`listen.c2s.tls.ciphers`](../../advanced-configuration/listen/#listenc2stlsciphers), [`listen.http.tls.ciphers`](../../advanced-configuration/listen/#listenhttptlsciphers) and [`outgoing_pools.*.*.connection.tls.ciphers`](../../advanced-configuration/outgoing-connections/#outgoing_poolsconnectiontlsciphers): the ciphers should now be formatted as a specification string -* [listen.http.handlers.mod_websockets.ping_rate](../../advanced-configuration/listen/#listenhttphandlersmod_websocketsping_rate): `none` value is no longer allowed - -* `outgoing_pools.generic`: the option was removed +* [listen.http.handlers.mod_websockets.ping_rate](../../advanced-configuration/listen/#listenhttphandlersmod_websocketsping_rate): `none` is no longer allowed