From cb8c9d6bd10e79f5f61530889938f27842e33536 Mon Sep 17 00:00:00 2001 From: Gustaw Lippa Date: Mon, 21 Feb 2022 16:58:37 +0100 Subject: [PATCH 1/6] Fix LServer being used instead of a HostType --- src/vcard/mod_vcard_mnesia.erl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vcard/mod_vcard_mnesia.erl b/src/vcard/mod_vcard_mnesia.erl index f4d4a50ddf..8ff8c0fd5d 100644 --- a/src/vcard/mod_vcard_mnesia.erl +++ b/src/vcard/mod_vcard_mnesia.erl @@ -57,23 +57,23 @@ set_vcard(HostType, User, LServer, VCard, VCardSearch) -> mongoose_hooks:vcard_set(HostType, LServer, LUser, VCard), ok. -search(_HostType, LServer, Data) -> +search(HostType, LServer, Data) -> MatchHead = make_matchhead(LServer, Data), - R = do_search(LServer, MatchHead), + R = do_search(HostType, LServer, MatchHead), lists:map(fun record_to_item/1, R). -do_search(_, #vcard_search{_ = '_'}) -> +do_search(_, _, #vcard_search{_ = '_'}) -> []; -do_search(LServer, MatchHeadIn) -> +do_search(HostType, LServer, MatchHeadIn) -> MatchHead = MatchHeadIn#vcard_search{us = {'_', LServer}}, case catch mnesia:dirty_select(vcard_search, [{MatchHead, [], ['$_']}]) of {'EXIT', Reason} -> - ?LOG_ERROR(#{what => vcard_search_failed, server => LServer, + ?LOG_ERROR(#{what => vcard_search_failed, server => LServer, host_type => HostType, reason => Reason}), []; Rs -> - case mod_vcard:get_results_limit(LServer) of + case mod_vcard:get_results_limit(HostType) of infinity -> Rs; Val -> From 8007dd932eb20b475b8e06632036e108673ae374 Mon Sep 17 00:00:00 2001 From: Gustaw Lippa Date: Mon, 21 Feb 2022 11:07:14 +0100 Subject: [PATCH 2/6] Put mod_vcard config options in a map --- big_tests/tests/domain_removal_SUITE.erl | 2 +- big_tests/tests/gdpr_SUITE.erl | 2 +- .../service_mongoose_system_metrics_SUITE.erl | 3 +- big_tests/tests/vcard_SUITE.erl | 60 ++++++++---------- big_tests/tests/vcard_simple_SUITE.erl | 16 ++--- doc/modules/mod_vcard.md | 18 +++--- include/mod_vcard.hrl | 2 - src/vcard/mod_vcard.erl | 35 +++++------ src/vcard/mod_vcard_ldap.erl | 3 +- test/common/config_parser_helper.erl | 31 ++++++---- test/config_parser_SUITE.erl | 61 ++++++++++--------- 11 files changed, 114 insertions(+), 119 deletions(-) diff --git a/big_tests/tests/domain_removal_SUITE.erl b/big_tests/tests/domain_removal_SUITE.erl index 23952fc6e6..8e363720da 100644 --- a/big_tests/tests/domain_removal_SUITE.erl +++ b/big_tests/tests/domain_removal_SUITE.erl @@ -99,7 +99,7 @@ group_to_modules(roster_removal) -> group_to_modules(offline_removal) -> [{mod_offline, [{backend, rdbms}]}]; group_to_modules(vcard_removal) -> - [{mod_vcard, [{backend, rdbms}]}]; + [{mod_vcard, config_parser_helper:mod_config(mod_vcard, #{backend => rdbms})}]; group_to_modules(last_removal) -> [{mod_last, [{backend, rdbms}]}]. diff --git a/big_tests/tests/gdpr_SUITE.erl b/big_tests/tests/gdpr_SUITE.erl index 33f762d4a7..bc1ecb5deb 100644 --- a/big_tests/tests/gdpr_SUITE.erl +++ b/big_tests/tests/gdpr_SUITE.erl @@ -398,7 +398,7 @@ pick_enabled_backend() -> vcard_required_modules() -> - [{mod_vcard, [{backend, pick_enabled_backend()}]}]. + [{mod_vcard, config_parser_helper:mod_config(mod_vcard, #{backend => pick_enabled_backend()})}]. offline_required_modules() -> [{mod_offline, [{backend, pick_enabled_backend()}]}]. diff --git a/big_tests/tests/service_mongoose_system_metrics_SUITE.erl b/big_tests/tests/service_mongoose_system_metrics_SUITE.erl index a0a46a594c..64f384cf3c 100644 --- a/big_tests/tests/service_mongoose_system_metrics_SUITE.erl +++ b/big_tests/tests/service_mongoose_system_metrics_SUITE.erl @@ -162,7 +162,8 @@ init_per_testcase(xmpp_components_are_reported, Config) -> Config1; init_per_testcase(module_backend_is_reported, Config) -> create_events_collection(), - dynamic_modules:ensure_modules(host_type(), [{mod_vcard, []}]), + DefModVCardConfig = config_parser_helper:default_mod_config(mod_vcard), + dynamic_modules:ensure_modules(host_type(), [{mod_vcard, DefModVCardConfig}]), enable_system_metrics(mim()), Config; init_per_testcase(xmpp_stanzas_counts_are_reported = CN, Config) -> diff --git a/big_tests/tests/vcard_SUITE.erl b/big_tests/tests/vcard_SUITE.erl index 19b1cc0e04..113023ece6 100644 --- a/big_tests/tests/vcard_SUITE.erl +++ b/big_tests/tests/vcard_SUITE.erl @@ -166,7 +166,7 @@ init_per_group(Group, Config) when Group == rw; Group == params_limited_infinity end; init_per_group(ldap_only, Config) -> VCardConfig = ?config(mod_vcard_opts, Config), - case proplists:get_value(backend, VCardConfig) of + case maps:get(backend, VCardConfig) of ldap -> Config1 = restart_and_prepare_vcard(ldap_only, Config), insert_alice_photo(Config1); @@ -1015,12 +1015,7 @@ prepare_vcards(Config) -> Config. get_backend(Config) -> - case lists:keyfind(backend, 1, ?config(mod_vcard_opts, Config)) of - {backend, Backend} -> - Backend; - _ -> - mnesia - end. + maps:get(backend, ?config(mod_vcard_opts, Config), mnesia). prepare_vcard(ldap, JID, Fields) -> [User, Server] = binary:split(JID, <<"@">>), @@ -1155,7 +1150,6 @@ prepare_vcard_module(Config) -> %% Keep the old config, so we can undo our changes, once finished testing Config1 = dynamic_modules:save_modules(host_types(), Config), %% Get a list of options, we can use as a prototype to start new modules - _HostType = domain_helper:host_type(), VCardOpts = dynamic_modules:get_saved_config(host_type(), mod_vcard, Config1), [{mod_vcard_opts, VCardOpts} | Config1]. @@ -1167,46 +1161,43 @@ stop_vcard_mod(_Config) -> ok. params_all(Config) -> - add_backend_param([], ?config(mod_vcard_opts, Config)). + add_backend_param(#{}, ?config(mod_vcard_opts, Config)). params_limited(Config) -> - add_backend_param([{matches, 1}, - {host, subhost_pattern("directory.@HOST@")}], + add_backend_param(#{matches => 1, + host => subhost_pattern("directory.@HOST@")}, ?config(mod_vcard_opts, Config)). params_limited_infinity(Config) -> - add_backend_param([{matches, infinity}, - {host, subhost_pattern("directory.@HOST@")}], + add_backend_param(#{matches => infinity, + host => subhost_pattern("directory.@HOST@")}, ?config(mod_vcard_opts, Config)). params_no(Config) -> - add_backend_param([{search, false}, - {host, subhost_pattern("vjud.@HOST@")}], + add_backend_param(#{search => false, + host => subhost_pattern("vjud.@HOST@")}, ?config(mod_vcard_opts, Config)). params_ldap_only(Config) -> Reported = [{<<"Full Name">>, <<"FN">>}, - {<<"Given Name">>, <<"FIRST">>}, - {<<"Middle Name">>, <<"MIDDLE">>}, - {<<"Family Name">>, <<"LAST">>}, - {<<"Nickname">>, <<"NICK">>}, - {<<"Birthday">>, <<"BDAY">>}, - {<<"Country">>, <<"CTRY">>}, - {<<"City">>, <<"LOCALITY">>}, - {<<"Email">>, <<"EMAIL">>}, - {<<"Organization Name">>, <<"ORGNAME">>}, - {<<"Organization Unit">>, <<"ORGUNIT">>}, - {<<"Photo">>, <<"PHOTO">>}], - add_backend_param([{ldap_search_operator, 'or'}, - {ldap_binary_search_fields, [<<"PHOTO">>]}, - {ldap_search_reported, Reported}], + {<<"Given Name">>, <<"FIRST">>}, + {<<"Middle Name">>, <<"MIDDLE">>}, + {<<"Family Name">>, <<"LAST">>}, + {<<"Nickname">>, <<"NICK">>}, + {<<"Birthday">>, <<"BDAY">>}, + {<<"Country">>, <<"CTRY">>}, + {<<"City">>, <<"LOCALITY">>}, + {<<"Email">>, <<"EMAIL">>}, + {<<"Organization Name">>, <<"ORGNAME">>}, + {<<"Organization Unit">>, <<"ORGUNIT">>}, + {<<"Photo">>, <<"PHOTO">>}], + add_backend_param(#{ldap_search_operator => 'or', + ldap_binary_search_fields => [<<"PHOTO">>], + ldap_search_reported => Reported}, ?config(mod_vcard_opts, Config)). add_backend_param(Opts, CurrentVCardConfig) -> - F = fun({Key, _} = Item, Cfg) -> - lists:keystore(Key, 1, Cfg, Item) - end, - lists:foldl(F, CurrentVCardConfig, Opts). + maps:merge(CurrentVCardConfig, Opts). %%---------------------- %% xmlel shortcuts @@ -1549,8 +1540,7 @@ secondary_domain() -> ct:get_config({hosts, mim, secondary_domain}). wait_for_riak() -> - HostType = ct:get_config({hosts, mim, host_type}), - case mam_helper:is_riak_enabled(HostType) of + case mam_helper:is_riak_enabled(host_type()) of true -> timer:sleep(timer:seconds(3)); %give some time to Yokozuna to index vcards false -> diff --git a/big_tests/tests/vcard_simple_SUITE.erl b/big_tests/tests/vcard_simple_SUITE.erl index 3ba2e0bb3c..bc6f8cae07 100644 --- a/big_tests/tests/vcard_simple_SUITE.erl +++ b/big_tests/tests/vcard_simple_SUITE.erl @@ -454,12 +454,14 @@ configure_mod_vcard(Config) -> end. ldap_opts() -> - [{backend,ldap}, {host, subhost_pattern("vjud.@HOST@")}, - {ldap_uids, [{<<"uid">>}]}, %% equivalent to {<<"uid">>, <<"%u">>} - {ldap_filter,<<"(objectClass=inetOrgPerson)">>}, - {ldap_base,"ou=Users,dc=esl,dc=com"}, - {ldap_search_fields, [{"Full Name","cn"},{"User","uid"}]}, - {ldap_vcard_map,[{"FN","%s",["cn"]}]}]. + VCardOpts = #{backend => ldap, + host => subhost_pattern("vjud.@HOST@"), + ldap_uids => [{<<"uid">>}], %% equivalent to {<<"uid">>, <<"%u">>} + ldap_filter => <<"(objectClass=inetOrgPerson)">>, + ldap_base => "ou=Users,dc=esl,dc=com", + ldap_search_fields => [{"Full Name", "cn"}, {"User", "uid"}], + ldap_vcard_map => [{"FN", "%s", ["cn"]}]}, + config_parser_helper:mod_config(mod_vcard, VCardOpts). ensure_started(HostType, Opts) -> dynamic_modules:stop(HostType, mod_vcard), @@ -469,7 +471,7 @@ prepare_vcard_module(Config) -> %% Keep the old config, so we can undo our changes, once finished testing Config1 = dynamic_modules:save_modules(host_types(), Config), %% Get a list of options, we can use as a prototype to start new modules - VCardOpts = dynamic_modules:get_saved_config(host_type(), mod_vcard, Config1), + VCardOpts = config_parser_helper:default_mod_config(mod_vcard), [{mod_vcard_opts, VCardOpts} | Config1]. restore_vcard_module(Config) -> diff --git a/doc/modules/mod_vcard.md b/doc/modules/mod_vcard.md index a70042c62b..a43be3822d 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:** `"one_queue"` +* **Default:** `"parallel"` Strategy to handle incoming stanzas. For details, please refer to [IQ processing policies](../configuration/Modules.md#iq-processing-policies). @@ -47,18 +47,18 @@ Maximum search results to be returned to the user. The following options are the same as for the [LDAP authentication module](../authentication-methods/ldap.md#configuration-options): -* [`modules.mod_vcard.ldap_pool_tag`](../authentication-methods/ldap.md#authldappool_tag) -* [`modules.mod_vcard.ldap_base`](../authentication-methods/ldap.md#authldapbase) -* [`modules.mod_vcard.ldap_uids`](../authentication-methods/ldap.md#authldapuids) -* [`modules.mod_vcard.ldap_filter`](../authentication-methods/ldap.md#authldapfilter) -* [`modules.mod_vcard.ldap_deref`](../authentication-methods/ldap.md#authldapderef) +* #### [`modules.mod_vcard.ldap_pool_tag`](../authentication-methods/ldap.md#authldappool_tag) +* #### [`modules.mod_vcard.ldap_base`](../authentication-methods/ldap.md#authldapbase) +* #### [`modules.mod_vcard.ldap_uids`](../authentication-methods/ldap.md#authldapuids) +* #### [`modules.mod_vcard.ldap_filter`](../authentication-methods/ldap.md#authldapfilter) +* #### [`modules.mod_vcard.ldap_deref`](../authentication-methods/ldap.md#authldapderef) #### `modules.mod_vcard.ldap_vcard_map` * **Syntax:** Array of TOML tables with the following keys: `"vcard_field"`, `"ldap_pattern"`, `"ldap_field"` and string values. * **Default:** see description * **Example:** `ldap_vcard_map = [{vcard_field = "FN", ldap_pattern = "%s", ldap_field = "displayName"}]` -Mappings between VCard and LDAP fields. For the default settings, please see `[MongooseIM root]/src/mod_vcard_ldap.erl`, line 79. +Mappings between VCard and LDAP fields. For the default settings, please see `[MongooseIM root]/src/mod_vcard_ldap.erl`. #### `modules.mod_vcard.ldap_search_fields` * **Syntax:** Array of TOML tables with the following keys: `"search_field"`, `"ldap_field"` and string values. @@ -66,7 +66,7 @@ Mappings between VCard and LDAP fields. For the default settings, please see `[M * **Example:** `ldap_search_fields = [{search_field = "User", ldap_field = "%u"}]` Mappings between the human-readable search fields and LDAP fields. -For the default settings, please see `[MongooseIM root]/src/mod_vcard_ldap.erl`, line 101. +For the default settings, please see `[MongooseIM root]/src/mod_vcard_ldap.erl`. #### `modules.mod_vcard.ldap_search_reported` * **Syntax:** Array of TOML tables with the following keys: `"search_field"`, `"vcard_field"` and string values. @@ -74,7 +74,7 @@ For the default settings, please see `[MongooseIM root]/src/mod_vcard_ldap.erl`, * **Example:** `ldap_search_reported = [{search_field = "Full Name", vcard_field = "FN"}]` Mappings between the human-readable search fields and VCard fields. -For the default settings, please see `[MongooseIM root]/src/mod_vcard_ldap.erl`, line 114. +For the default settings, please see `[MongooseIM root]/src/mod_vcard_ldap.erl`. #### `modules.mod_vcard.ldap_search_operator` * **Syntax:** string, one of `"or"`, `"and"` diff --git a/include/mod_vcard.hrl b/include/mod_vcard.hrl index 015af54d24..bacb5c02a5 100644 --- a/include/mod_vcard.hrl +++ b/include/mod_vcard.hrl @@ -13,8 +13,6 @@ orgunit, lorgunit}). -record(vcard, {us, vcard}). --define(JUD_MATCHES, 30). - -define(TLFIELD(Type, Label, Var), #xmlel{name = <<"field">>, attrs = diff --git a/src/vcard/mod_vcard.erl b/src/vcard/mod_vcard.erl index d8f5b90d4e..cf5e32770a 100644 --- a/src/vcard/mod_vcard.erl +++ b/src/vcard/mod_vcard.erl @@ -76,7 +76,6 @@ -export([default_search_fields/0]). -export([get_results_limit/1]). -export([get_default_reported_fields/1]). --export([default_host/0]). %% GDPR related -export([get_personal_data/3]). @@ -84,7 +83,7 @@ -export([config_metrics/1]). -ignore_xref([ - behaviour_info/1, process_packet/5, + process_packet/5, get_personal_data/3, remove_user/3, remove_domain/3, set_vcard/4, start_link/2 ]). @@ -128,21 +127,13 @@ default_search_fields() -> -spec get_results_limit(mongooseim:host_type()) -> non_neg_integer() | infinity. get_results_limit(HostType) -> - case gen_mod:get_module_opt(HostType, mod_vcard, matches, ?JUD_MATCHES) of + case gen_mod:get_module_opt(HostType, mod_vcard, matches) of infinity -> infinity; Val when is_integer(Val) and (Val > 0) -> - Val; - Val -> - ?LOG_ERROR(#{what => illegal_option_error, value => {matches, Val}, - default_value => ?JUD_MATCHES}), - ?JUD_MATCHES + Val end. --spec default_host() -> mongoose_subdomain_utils:subdomain_pattern(). -default_host() -> - mongoose_subdomain_utils:make_subdomain_pattern(<<"vjud.@HOST@">>). - %%-------------------------------------------------------------------- %% gen_mod callbacks %%-------------------------------------------------------------------- @@ -185,8 +176,7 @@ hooks2() -> {set_vcard, set_vcard, 50}, {get_personal_data, get_personal_data, 50}]. -start_iq_handlers(HostType, Opts) -> - IQDisc = gen_mod:get_opt(iqdisc, Opts, parallel), +start_iq_handlers(HostType, #{iqdisc := IQDisc}) -> gen_iq_handler:add_iq_handler_for_domain(HostType, ?NS_VCARD, ejabberd_sm, fun ?MODULE:process_sm_iq/5, #{}, IQDisc), gen_iq_handler:add_iq_handler_for_domain(HostType, ?NS_VCARD, ejabberd_local, @@ -203,7 +193,7 @@ stop_backend(HostType) -> maybe_register_search(false, _HostType, _Opts) -> ok; maybe_register_search(true, HostType, Opts) -> - SubdomainPattern = gen_mod:get_opt(host, Opts, default_host()), + SubdomainPattern = gen_mod:get_opt(host, Opts), PacketHandler = mongoose_packet_handler:new(?MODULE, #{pid => self()}), %% Always register, even if search functionality is disabled. %% So, we can send 503 error, instead of 404 error. @@ -212,7 +202,7 @@ maybe_register_search(true, HostType, Opts) -> maybe_unregister_search(false, _HostType) -> ok; maybe_unregister_search(true, HostType) -> - SubdomainPattern = gen_mod:get_module_opt(HostType, ?MODULE, host, default_host()), + SubdomainPattern = gen_mod:get_module_opt(HostType, ?MODULE, host), mongoose_domain_api:unregister_subdomain(HostType, SubdomainPattern). %%-------------------------------------------------------------------- @@ -246,7 +236,13 @@ config_spec() -> <<"ldap_binary_search_fields">> => #list{items = #option{type = binary, validate = non_empty}}, <<"riak">> => riak_config_spec() - } + }, + defaults = #{<<"iqdisc">> => parallel, + <<"host">> => mongoose_subdomain_utils:make_subdomain_pattern("vjud.@HOST@"), + <<"search">> => true, + <<"backend">> => mnesia, + <<"matches">> => 30}, + format_items = map }. ldap_vcard_map_spec() -> @@ -339,7 +335,7 @@ start_link(HostType, Opts) -> init([HostType, Opts]) -> process_flag(trap_exit, true), - Search = gen_mod:get_opt(search, Opts, true), + Search = gen_mod:get_opt(search, Opts), maybe_register_search(Search, HostType, Opts), {ok, #state{host_type = HostType, search = Search}}. @@ -823,8 +819,7 @@ get_default_reported_fields(Lang) -> ]}. config_metrics(Host) -> - OptsToReport = [{backend, mnesia}], %list of tuples {option, defualt_value} - mongoose_module_metrics:opts_for_module(Host, ?MODULE, OptsToReport). + mongoose_module_metrics:opts_for_module(Host, ?MODULE, [backend]). vcard_error(IQ = #iq{sub_el = VCARD}, ReasonEl) -> IQ#iq{type = error, sub_el = [VCARD, ReasonEl]}. diff --git a/src/vcard/mod_vcard_ldap.erl b/src/vcard/mod_vcard_ldap.erl index ee1a4b3901..5005021246 100644 --- a/src/vcard/mod_vcard_ldap.erl +++ b/src/vcard/mod_vcard_ldap.erl @@ -402,7 +402,8 @@ process_pattern(Str, {User, Domain}, AttrValues) -> get_state(HostType, LServer) -> Opts = gen_mod:get_loaded_module_opts(HostType, mod_vcard), - MyHost = gen_mod:get_opt_subhost(LServer, Opts, mod_vcard:default_host()), + Val = gen_mod:get_opt(host, Opts), + MyHost = mongoose_subdomain_utils:get_fqdn(Val, LServer), Matches = eldap_utils:get_mod_opt(matches, Opts, fun(infinity) -> infinity; (I) when is_integer(I), I>=0 -> I diff --git a/test/common/config_parser_helper.erl b/test/common/config_parser_helper.erl index 333dc28fd0..b0de7952d5 100644 --- a/test/common/config_parser_helper.erl +++ b/test/common/config_parser_helper.erl @@ -40,7 +40,7 @@ options("host_types") -> program => "/usr/bin/bash"}, http => #{}})}, {{modules, <<"another host type">>}, #{mod_offline => []}}, - {{modules, <<"localhost">>}, #{mod_vcard => []}}, + {{modules, <<"localhost">>}, #{mod_vcard => default_mod_config(mod_vcard)}}, {{modules, <<"some host type">>}, #{}}, {{modules, <<"this is host type">>}, #{}}, {{modules, <<"yet another host type">>}, #{mod_amp => []}}, @@ -625,16 +625,17 @@ all_modules() -> {host, {fqdn, <<"muc.example.com">>}}, {http_auth_pool, my_auth_pool}], mod_vcard => - [{host, {fqdn, <<"directory.example.com">>}}, - {ldap_search_fields, - [{<<"User">>, <<"%u">>}, {<<"Full Name">>, <<"displayName">>}]}, - {ldap_search_reported, - [{<<"Full Name">>, <<"FN">>}, {<<"Given Name">>, <<"FIRST">>}]}, - {ldap_vcard_map, - [{<<"FAMILY">>, <<"%s">>, [<<"sn">>]}, - {<<"FN">>, <<"%s">>, [<<"displayName">>]}]}, - {matches, 1}, - {search, true}], + mod_config(mod_vcard, + #{host => {fqdn, <<"directory.example.com">>}, + ldap_search_fields => + [{<<"User">>, <<"%u">>}, {<<"Full Name">>, <<"displayName">>}], + ldap_search_reported => + [{<<"Full Name">>, <<"FN">>}, {<<"Given Name">>, <<"FIRST">>}], + ldap_vcard_map => + [{<<"FAMILY">>, <<"%s">>, [<<"sn">>]}, + {<<"FN">>, <<"%s">>, [<<"displayName">>]}], + matches => 1, + search => true}), mod_mam_muc_rdbms_arch => [{muc, true}, {db_jid_format, mam_jid_rfc}, {db_message_format, mam_message_xml}], mod_stream_management => @@ -662,7 +663,7 @@ pgsql_modules() -> {welcome_message, {"Hello", "I am MongooseIM"}}], mod_roster => [{backend, rdbms}], mod_sic => [], mod_stream_management => [], - mod_vcard => [{backend, rdbms}, {host, {prefix, <<"vjud.">>}}]}. + mod_vcard => mod_config(mod_vcard, #{backend => rdbms, host => {prefix, <<"vjud.">>}})}. auth_with_methods(Methods) -> maps:merge(default_auth(), Methods#{methods => lists:sort(maps:keys(Methods))}). @@ -835,5 +836,11 @@ default_mod_config(mod_inbox) -> iqdisc => no_queue}; default_mod_config(mod_private) -> #{iqdisc => one_queue, backend => rdbms}; +default_mod_config(mod_vcard) -> + #{iqdisc => parallel, + host => {prefix, <<"vjud.">>}, + search => true, + backend => mnesia, + matches => 30}; default_mod_config(mod_version) -> #{iqdisc => no_queue, os_info => false}. diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index 94e9504d61..9488fa6a53 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -2924,38 +2924,39 @@ mod_time(_Config) -> ?cfgh(modopts(mod_time, []), #{<<"modules">> => #{<<"mod_time">> => #{}}}). mod_vcard(_Config) -> - check_iqdisc(mod_vcard), + check_module_defaults(mod_vcard), + check_iqdisc_map(mod_vcard), + P = [modules, mod_vcard], T = fun(Opts) -> #{<<"modules">> => #{<<"mod_vcard">> => Opts}} end, - M = fun(Cfg) -> modopts(mod_vcard, Cfg) end, - ?cfgh(M([{iqdisc, one_queue}]), + ?cfgh(P ++ [iqdisc], one_queue, T(#{<<"iqdisc">> => #{<<"type">> => <<"one_queue">>}})), - ?cfgh(M([{host, {prefix, <<"vjud.">>}}]), + ?cfgh(P ++ [host], {prefix, <<"vjud.">>}, T(#{<<"host">> => <<"vjud.@HOST@">>})), - ?cfgh(M([{host, {fqdn, <<"vjud.test">>}}]), + ?cfgh(P ++ [host], {fqdn, <<"vjud.test">>}, T(#{<<"host">> => <<"vjud.test">>})), - ?cfgh(M([{search, true}]), + ?cfgh(P ++ [search], true, T(#{<<"search">> => true})), - ?cfgh(M([{backend, mnesia}]), + ?cfgh(P ++ [backend], mnesia, T(#{<<"backend">> => <<"mnesia">>})), - ?cfgh(M([{matches, infinity}]), + ?cfgh(P ++ [matches], infinity, T(#{<<"matches">> => <<"infinity">>})), %% ldap - ?cfgh(M([{ldap_pool_tag, default}]), + ?cfgh(P ++ [ldap_pool_tag], default, T(#{<<"ldap_pool_tag">> => <<"default">>})), - ?cfgh(M([{ldap_base, "ou=Users,dc=ejd,dc=com"}]), + ?cfgh(P ++ [ldap_base], "ou=Users,dc=ejd,dc=com", T(#{<<"ldap_base">> => <<"ou=Users,dc=ejd,dc=com">>})), - ?cfgh(M([{ldap_filter, <<"(&(objectClass=shadowAccount)(memberOf=Jabber Users))">>}]), + ?cfgh(P ++ [ldap_filter], <<"(&(objectClass=shadowAccount)(memberOf=Jabber Users))">>, T(#{<<"ldap_filter">> => <<"(&(objectClass=shadowAccount)(memberOf=Jabber Users))">>})), - ?cfgh(M([{ldap_deref, never}]), + ?cfgh(P ++ [ldap_deref], never, T(#{<<"ldap_deref">> => <<"never">>})), - ?cfgh(M([{ldap_search_operator, 'or'}]), + ?cfgh(P ++ [ldap_search_operator], 'or', T(#{<<"ldap_search_operator">> => <<"or">>})), - ?cfgh(M([{ldap_binary_search_fields, [<<"PHOTO">>]}]), + ?cfgh(P ++ [ldap_binary_search_fields], [<<"PHOTO">>], T(#{<<"ldap_binary_search_fields">> => [<<"PHOTO">>]})), %% riak - ?cfgh(M([{bucket_type, <<"vcard">>}]), + ?cfgh(P ++ [bucket_type], <<"vcard">>, T(#{<<"riak">> => #{<<"bucket_type">> => <<"vcard">>}})), - ?cfgh(M([{search_index, <<"vcard">>}]), + ?cfgh(P ++ [search_index], <<"vcard">>, T(#{<<"riak">> => #{<<"search_index">> => <<"vcard">>}})), ?errh(T(#{<<"host">> => 1})), @@ -2977,58 +2978,58 @@ mod_vcard(_Config) -> ?errh(T(#{<<"riak">> => #{<<"search_index">> => 1}})). mod_vcard_ldap_uids(_Config) -> + P = [modules, mod_vcard, ldap_uids], 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">>, - ?cfgh(M([]), T([])), - ?cfgh(M([ExpectedCfg]), T([RequiredOpts])), - ?cfgh(M([{<<"name">>, <<"%u@mail.example.org">>}]), + ?cfgh(P, [], T([])), + ?cfgh(P, [ExpectedCfg], T([RequiredOpts])), + ?cfgh(P, [{<<"name">>, <<"%u@mail.example.org">>}], T([RequiredOpts#{<<"format">> => <<"%u@mail.example.org">>}])), - ?cfgh(M([{<<"name">>, <<"%u@mail.example.org">>}, ExpectedCfg]), + ?cfgh(P, [{<<"name">>, <<"%u@mail.example.org">>}, ExpectedCfg], T([RequiredOpts#{<<"format">> => <<"%u@mail.example.org">>}, RequiredOpts])), [?errh(T([maps:remove(Key, RequiredOpts)])) || Key <- maps:keys(RequiredOpts)], ?errh(T(RequiredOpts#{<<"attr">> := 1})), ?errh(T(RequiredOpts#{<<"format">> => true})). mod_vcard_ldap_vcard_map(_Config) -> + P = [modules, mod_vcard, ldap_vcard_map], 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">>]}, - ?cfgh(M([]), T([])), - ?cfgh(M([ExpectedCfg]), T([RequiredOpts])), + ?cfgh(P, [], T([])), + ?cfgh(P, [ExpectedCfg], T([RequiredOpts])), [?errh(T([maps:remove(Key, RequiredOpts)])) || Key <- maps:keys(RequiredOpts)], ?errh(T(RequiredOpts#{<<"vcard_field">> := false})), ?errh(T(RequiredOpts#{<<"ldap_pattern">> := false})), ?errh(T(RequiredOpts#{<<"ldap_field">> := -1})). mod_vcard_ldap_search_fields(_Config) -> + P = [modules, mod_vcard, ldap_search_fields], 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">>}, - ?cfgh(M([]), T([])), - ?cfgh(M([ExpectedCfg]), T([RequiredOpts])), + ?cfgh(P, [], T([])), + ?cfgh(P, [ExpectedCfg], T([RequiredOpts])), [?errh(T([maps:remove(Key, RequiredOpts)])) || Key <- maps:keys(RequiredOpts)], ?errh(T(RequiredOpts#{<<"search_field">> := false})), ?errh(T(RequiredOpts#{<<"ldap_field">> := -1})). mod_vcard_ldap_search_reported(_Config) -> + P = [modules, mod_vcard, ldap_search_reported], 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">>}, - ?cfgh(M([]), T([])), - ?cfgh(M([ExpectedCfg]), T([RequiredOpts])), + ?cfgh(P, [], T([])), + ?cfgh(P, [ExpectedCfg], T([RequiredOpts])), [?errh(T([maps:remove(Key, RequiredOpts)])) || Key <- maps:keys(RequiredOpts)], ?errh(T(RequiredOpts#{<<"search_field">> := false})), ?errh(T(RequiredOpts#{<<"vcard_field">> := -1})). From e7095f9859059ebdb30cbcd6975e9999d9fee1b9 Mon Sep 17 00:00:00 2001 From: Gustaw Lippa Date: Mon, 21 Feb 2022 15:54:39 +0100 Subject: [PATCH 3/6] Correct default --- doc/authentication-methods/ldap.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/authentication-methods/ldap.md b/doc/authentication-methods/ldap.md index a527a3ca1e..0dec22cdad 100644 --- a/doc/authentication-methods/ldap.md +++ b/doc/authentication-methods/ldap.md @@ -73,8 +73,8 @@ LDAP base directory which stores user accounts. * **Syntax:** array of TOML tables with the following content: * `attr` - string, mandatory, name of the attribute * `format` - pattern, default: `"%u"`, requires `attr` -* **Default:** `[{attr = "my_uid"}]` -* **Example:** `uids = [{attr = "my_uid", format = "%u@example.org"}, {attr = "another_uid"}]` +* **Default:** `[{attr = "uid"}]` +* **Example:** `uids = [{attr = "uid", format = "%u@example.org"}, {attr = "another_uid"}]` List of LDAP attributes that contain the user name (user's part of the JID), used to search for user accounts. They are used as alternatives - it is enough if one of them contains the name. From ba516c187ee3c8d1eeaf7c7085121d079d675cde Mon Sep 17 00:00:00 2001 From: Gustaw Lippa Date: Mon, 21 Feb 2022 17:00:16 +0100 Subject: [PATCH 4/6] Add default VCard LDAP values to config --- big_tests/tests/vcard_SUITE.erl | 1 - src/vcard/mod_vcard.erl | 10 +++++- src/vcard/mod_vcard_ldap.erl | 34 ++++++++++++-------- test/common/config_parser_helper.erl | 48 +++++++++++++++++++++++++++- 4 files changed, 76 insertions(+), 17 deletions(-) diff --git a/big_tests/tests/vcard_SUITE.erl b/big_tests/tests/vcard_SUITE.erl index 113023ece6..2c770d163d 100644 --- a/big_tests/tests/vcard_SUITE.erl +++ b/big_tests/tests/vcard_SUITE.erl @@ -170,7 +170,6 @@ init_per_group(ldap_only, Config) -> ldap -> Config1 = restart_and_prepare_vcard(ldap_only, Config), insert_alice_photo(Config1); - %Config1; _ -> {skip, "this group is only for ldap vCard backend"} end; diff --git a/src/vcard/mod_vcard.erl b/src/vcard/mod_vcard.erl index cf5e32770a..1a45281e3b 100644 --- a/src/vcard/mod_vcard.erl +++ b/src/vcard/mod_vcard.erl @@ -241,7 +241,15 @@ config_spec() -> <<"host">> => mongoose_subdomain_utils:make_subdomain_pattern("vjud.@HOST@"), <<"search">> => true, <<"backend">> => mnesia, - <<"matches">> => 30}, + <<"matches">> => 30, + <<"ldap_pool_tag">> => default, + <<"ldap_uids">> => [{<<"uid">>, <<"%u">>}], + <<"ldap_vcard_map">> => mod_vcard_ldap:default_vcard_map(), + <<"ldap_search_fields">> => mod_vcard_ldap:default_search_fields(), + <<"ldap_search_reported">> => mod_vcard_ldap:default_search_reported(), + <<"ldap_search_operator">> => 'and', + <<"ldap_binary_search_fields">> => [] + }, format_items = map }. diff --git a/src/vcard/mod_vcard_ldap.erl b/src/vcard/mod_vcard_ldap.erl index 5005021246..8a8c783bcd 100644 --- a/src/vcard/mod_vcard_ldap.erl +++ b/src/vcard/mod_vcard_ldap.erl @@ -41,6 +41,10 @@ search_fields/2, search_reported_fields/3]). +-export([default_vcard_map/0, + default_search_fields/0, + default_search_reported/0]). + -include("eldap.hrl"). -include("mod_vcard.hrl"). -include("jlib.hrl"). @@ -172,6 +176,18 @@ search_reported_fields(HostType, LServer, Lang) -> end, SearchReported)}. +%%-------------------------------------------------------------------- +%% API +%%-------------------------------------------------------------------- +default_vcard_map() -> + ?VCARD_MAP. + +default_search_fields() -> + ?SEARCH_FIELDS. + +default_search_reported() -> + ?SEARCH_REPORTED. + %%-------------------------------------------------------------------- %% Internal %%-------------------------------------------------------------------- @@ -404,12 +420,8 @@ get_state(HostType, LServer) -> Opts = gen_mod:get_loaded_module_opts(HostType, mod_vcard), Val = gen_mod:get_opt(host, Opts), MyHost = mongoose_subdomain_utils:get_fqdn(Val, LServer), - Matches = eldap_utils:get_mod_opt(matches, Opts, - fun(infinity) -> infinity; - (I) when is_integer(I), I>=0 -> I - end, 30), - EldapID = eldap_utils:get_mod_opt(ldap_pool_tag, Opts, - fun(A) when is_atom(A) -> A end, default), + Matches = gen_mod:get_opt(matches, Opts), + EldapID = gen_mod:get_opt(ldap_pool_tag, Opts), Base = eldap_utils:get_base(Opts), DerefAliases = eldap_utils:get_deref_aliases(Opts), UIDs = eldap_utils:get_uids(LServer, Opts), @@ -452,14 +464,8 @@ get_state(HostType, LServer) -> end end, SearchReported) ++ UIDAttrs), - SearchOperatorFun = fun - ('or') -> 'or'; - (_) -> 'and' - end, - SearchOperator = eldap_utils:get_mod_opt(ldap_search_operator, Opts, - SearchOperatorFun, 'and'), - BinaryFields = eldap_utils:get_mod_opt(ldap_binary_search_fields, Opts, - fun(X) -> X end, []), + SearchOperator = gen_mod:get_opt(ldap_search_operator, Opts), + BinaryFields = gen_mod:get_opt(ldap_binary_search_fields, Opts), #state{serverhost = LServer, myhost = MyHost, eldap_id = {HostType, EldapID}, diff --git a/test/common/config_parser_helper.erl b/test/common/config_parser_helper.erl index b0de7952d5..2e2f573c45 100644 --- a/test/common/config_parser_helper.erl +++ b/test/common/config_parser_helper.erl @@ -841,6 +841,52 @@ default_mod_config(mod_vcard) -> host => {prefix, <<"vjud.">>}, search => true, backend => mnesia, - matches => 30}; + matches => 30, + ldap_pool_tag => default, + ldap_uids => [{<<"uid">>, <<"%u">>}], + ldap_vcard_map => [{<<"NICKNAME">>, <<"%u">>, []}, + {<<"FN">>, <<"%s">>, [<<"displayName">>]}, + {<<"FAMILY">>, <<"%s">>, [<<"sn">>]}, + {<<"GIVEN">>, <<"%s">>, [<<"givenName">>]}, + {<<"MIDDLE">>, <<"%s">>, [<<"initials">>]}, + {<<"ORGNAME">>, <<"%s">>, [<<"o">>]}, + {<<"ORGUNIT">>, <<"%s">>, [<<"ou">>]}, + {<<"CTRY">>, <<"%s">>, [<<"c">>]}, + {<<"LOCALITY">>, <<"%s">>, [<<"l">>]}, + {<<"STREET">>, <<"%s">>, [<<"street">>]}, + {<<"REGION">>, <<"%s">>, [<<"st">>]}, + {<<"PCODE">>, <<"%s">>, [<<"postalCode">>]}, + {<<"TITLE">>, <<"%s">>, [<<"title">>]}, + {<<"URL">>, <<"%s">>, [<<"labeleduri">>]}, + {<<"DESC">>, <<"%s">>, [<<"description">>]}, + {<<"TEL">>, <<"%s">>, [<<"telephoneNumber">>]}, + {<<"EMAIL">>, <<"%s">>, [<<"mail">>]}, + {<<"BDAY">>, <<"%s">>, [<<"birthDay">>]}, + {<<"ROLE">>, <<"%s">>, [<<"employeeType">>]}, + {<<"PHOTO">>, <<"%s">>, [<<"jpegPhoto">>]}], + ldap_search_fields => [{<<"User">>, <<"%u">>}, + {<<"Full Name">>, <<"displayName">>}, + {<<"Given Name">>, <<"givenName">>}, + {<<"Middle Name">>, <<"initials">>}, + {<<"Family Name">>, <<"sn">>}, + {<<"Nickname">>, <<"%u">>}, + {<<"Birthday">>, <<"birthDay">>}, + {<<"Country">>, <<"c">>}, {<<"City">>, <<"l">>}, + {<<"Email">>, <<"mail">>}, + {<<"Organization Name">>, <<"o">>}, + {<<"Organization Unit">>, <<"ou">>}], + ldap_search_reported => [{<<"Full Name">>, <<"FN">>}, + {<<"Given Name">>, <<"FIRST">>}, + {<<"Middle Name">>, <<"MIDDLE">>}, + {<<"Family Name">>, <<"LAST">>}, + {<<"Nickname">>, <<"NICK">>}, + {<<"Birthday">>, <<"BDAY">>}, + {<<"Country">>, <<"CTRY">>}, + {<<"City">>, <<"LOCALITY">>}, + {<<"Email">>, <<"EMAIL">>}, + {<<"Organization Name">>, <<"ORGNAME">>}, + {<<"Organization Unit">>, <<"ORGUNIT">>}], + ldap_search_operator => 'and', + ldap_binary_search_fields => []}; default_mod_config(mod_version) -> #{iqdisc => no_queue, os_info => false}. From 4004b585afabe28463c2a702770796ac387a2f8d Mon Sep 17 00:00:00 2001 From: Gustaw Lippa Date: Tue, 22 Feb 2022 09:59:41 +0100 Subject: [PATCH 5/6] Remove old options from the example --- big_tests/tests/vcard_SUITE.erl | 1 - doc/modules/mod_vcard.md | 2 -- 2 files changed, 3 deletions(-) diff --git a/big_tests/tests/vcard_SUITE.erl b/big_tests/tests/vcard_SUITE.erl index 2c770d163d..374bd12a74 100644 --- a/big_tests/tests/vcard_SUITE.erl +++ b/big_tests/tests/vcard_SUITE.erl @@ -904,7 +904,6 @@ search_open_limited(Config) -> escalus_stanza:search_iq(DirJID, escalus_stanza:search_fields(Fields))), escalus:assert(is_iq_result, Res), - %% {allow_return_all, false} [] = search_result_item_tuples(Res) end). diff --git a/doc/modules/mod_vcard.md b/doc/modules/mod_vcard.md index a43be3822d..623259eb9b 100644 --- a/doc/modules/mod_vcard.md +++ b/doc/modules/mod_vcard.md @@ -110,8 +110,6 @@ Riak index name. ```toml [modules.mod_vcard] - allow_return_all = true - search_all_hosts = true matches = 1 search = true host = "directory.example.com" From b1ee09832a0987cc537bc1ec2510f5ab6f01e03c Mon Sep 17 00:00:00 2001 From: Gustaw Lippa Date: Tue, 22 Feb 2022 11:14:05 +0100 Subject: [PATCH 6/6] Add Riak defaults to mod_vcard config The values are used only when the Riak backend is specified. --- big_tests/tests/gdpr_SUITE.erl | 9 ++++++++- src/vcard/mod_vcard.erl | 11 +++++++++-- src/vcard/mod_vcard_riak.erl | 4 ++-- test/config_parser_SUITE.erl | 8 ++++---- 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/big_tests/tests/gdpr_SUITE.erl b/big_tests/tests/gdpr_SUITE.erl index bc1ecb5deb..613cf57cf1 100644 --- a/big_tests/tests/gdpr_SUITE.erl +++ b/big_tests/tests/gdpr_SUITE.erl @@ -398,7 +398,14 @@ pick_enabled_backend() -> vcard_required_modules() -> - [{mod_vcard, config_parser_helper:mod_config(mod_vcard, #{backend => pick_enabled_backend()})}]. + Backend = pick_enabled_backend(), + [{mod_vcard, config_parser_helper:mod_config(mod_vcard, vcard_backend_opts(Backend))}]. + +vcard_backend_opts(riak) -> + #{backend => riak, riak => #{bucket_type => <<"vcard">>, + search_index => <<"vcard">>}}; +vcard_backend_opts(Backend) -> + #{backend => Backend}. offline_required_modules() -> [{mod_offline, [{backend, pick_enabled_backend()}]}]. diff --git a/src/vcard/mod_vcard.erl b/src/vcard/mod_vcard.erl index 1a45281e3b..8e24000eff 100644 --- a/src/vcard/mod_vcard.erl +++ b/src/vcard/mod_vcard.erl @@ -250,7 +250,8 @@ config_spec() -> <<"ldap_search_operator">> => 'and', <<"ldap_binary_search_fields">> => [] }, - format_items = map + format_items = map, + process = fun remove_unused_backend_opts/1 }. ldap_vcard_map_spec() -> @@ -295,7 +296,10 @@ riak_config_spec() -> <<"search_index">> => #option{type = binary, validate = non_empty} }, - wrap = none + include = always, + format_items = map, + defaults = #{<<"bucket_type">> => <<"vcard">>, + <<"search_index">> => <<"vcard">>} }. process_map_spec(KVs) -> @@ -313,6 +317,9 @@ process_search_reported_spec(KVs) -> proplists:split(KVs, [search_field, vcard_field]), {SF, VF}. +remove_unused_backend_opts(Opts = #{backend := riak}) -> Opts; +remove_unused_backend_opts(Opts) -> maps:remove(riak, Opts). + %%-------------------------------------------------------------------- %% mongoose_packet_handler callbacks for search %%-------------------------------------------------------------------- diff --git a/src/vcard/mod_vcard_riak.erl b/src/vcard/mod_vcard_riak.erl index 6337c57964..38648f57d3 100644 --- a/src/vcard/mod_vcard_riak.erl +++ b/src/vcard/mod_vcard_riak.erl @@ -136,7 +136,7 @@ extract_field(Props, {_, Field}) -> bucket_type(HostType, LServer) -> - {gen_mod:get_module_opt(HostType, mod_vcard, bucket_type, <<"vcard">>), <<"vcard_", LServer/binary>>}. + {gen_mod:get_module_opt(HostType, mod_vcard, [riak, bucket_type]), <<"vcard_", LServer/binary>>}. yz_vcard_index(HostType) -> - gen_mod:get_module_opt(HostType, mod_vcard, search_index, <<"vcard">>). + gen_mod:get_module_opt(HostType, mod_vcard, [riak, search_index]). diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index 9488fa6a53..110e6cafbd 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -2954,10 +2954,10 @@ mod_vcard(_Config) -> ?cfgh(P ++ [ldap_binary_search_fields], [<<"PHOTO">>], T(#{<<"ldap_binary_search_fields">> => [<<"PHOTO">>]})), %% riak - ?cfgh(P ++ [bucket_type], <<"vcard">>, - T(#{<<"riak">> => #{<<"bucket_type">> => <<"vcard">>}})), - ?cfgh(P ++ [search_index], <<"vcard">>, - T(#{<<"riak">> => #{<<"search_index">> => <<"vcard">>}})), + ?cfgh(P ++ [riak, bucket_type], <<"vcard">>, + T(#{<<"backend">> => <<"riak">>, <<"riak">> => #{<<"bucket_type">> => <<"vcard">>}})), + ?cfgh(P ++ [riak, search_index], <<"vcard">>, + T(#{<<"backend">> => <<"riak">>, <<"riak">> => #{<<"search_index">> => <<"vcard">>}})), ?errh(T(#{<<"host">> => 1})), ?errh(T(#{<<"host">> => <<"is this a host? no.">>})),