./ct_report/coverage/mongoose_config_validator.COVER.html

1 -module(mongoose_config_validator).
2
3 -export([validate/3,
4 validate_section/2,
5 validate_list/2]).
6
7 -include("mongoose.hrl").
8 -include_lib("jid/include/jid.hrl").
9
10 -type validator() ::
11 any | non_empty | non_negative | positive | module | {module, Prefix :: atom()}
12 | jid | domain | subdomain_template | url | ip_address | ip_mask | network_address | port
13 | filename | dirname | loglevel | pool_name | shaper | access_rule | {enum, list()}.
14
15 -type section_validator() :: any | non_empty.
16
17 -type list_validator() :: any | non_empty | unique | unique_non_empty.
18
19 -export_type([validator/0, section_validator/0, list_validator/0]).
20
21 -spec validate(mongoose_config_parser_toml:option_value(),
22 mongoose_config_spec:option_type(), validator()) -> any().
23
:-(
validate(V, binary, domain) -> validate_binary_domain(V);
24
:-(
validate(V, binary, url) -> validate_non_empty_binary(V);
25 467 validate(V, binary, non_empty) -> validate_non_empty_binary(V);
26
:-(
validate(V, binary, subdomain_template) -> validate_subdomain_template(V);
27 validate(V, binary, {module, Prefix}) ->
28
:-(
validate_module(list_to_atom(atom_to_list(Prefix) ++ "_" ++ binary_to_list(V)));
29
:-(
validate(V, binary, jid) -> validate_jid(V);
30
:-(
validate(V, binary, ldap_filter) -> validate_ldap_filter(V);
31 25 validate(V, integer, non_negative) -> validate_non_negative_integer(V);
32 705 validate(V, integer, positive) -> validate_positive_integer(V);
33 553 validate(V, integer, port) -> validate_port(V);
34 294 validate(V, int_or_infinity, non_negative) -> validate_non_negative_integer_or_infinity(V);
35 193 validate(V, int_or_infinity, positive) -> validate_positive_integer_or_infinity(V);
36
:-(
validate(V, string, url) -> validate_url(V);
37
:-(
validate(V, string, domain) -> validate_domain(V);
38 42 validate(V, string, subdomain_template) -> validate_subdomain_template(V);
39 301 validate(V, string, ip_address) -> validate_ip_address(V);
40 84 validate(V, string, ip_mask) -> validate_ip_mask_string(V);
41
:-(
validate(V, string, network_address) -> validate_network_address(V);
42 491 validate(V, string, filename) -> validate_filename(V);
43 637 validate(V, string, non_empty) -> validate_non_empty_string(V);
44
:-(
validate(V, string, dirname) -> validate_dirname(V);
45 917 validate(V, atom, module) -> validate_module(V);
46 validate(V, atom, {module, Prefix}) ->
47 145 validate_module(list_to_atom(atom_to_list(Prefix) ++ "_" ++ atom_to_list(V)));
48 42 validate(V, atom, loglevel) -> validate_loglevel(V);
49
:-(
validate(V, atom, pool_name) -> validate_non_empty_atom(V);
50
:-(
validate(V, atom, shaper) -> validate_non_empty_atom(V);
51 42 validate(V, atom, access_rule) -> validate_non_empty_atom(V);
52 1610 validate(V, atom, non_empty) -> validate_non_empty_atom(V);
53 802 validate(V, _, {enum, Values}) -> validate_enum(V, Values);
54 12715 validate(_V, _, any) -> ok.
55
56 -spec validate_list([mongoose_config_parser_toml:config_part()], list_validator()) -> any().
57
:-(
validate_list([_|_], non_empty) -> ok;
58 25 validate_list(L = [_|_], unique_non_empty) -> validate_unique_items(L);
59 5051 validate_list(L, unique) -> validate_unique_items(L);
60 1589 validate_list(L, any) when is_list(L) -> ok.
61
62 -spec validate_section([mongoose_config_parser_toml:config_part()], section_validator()) -> any().
63
:-(
validate_section([_|_], non_empty) -> ok;
64 5578 validate_section(L, any) when is_list(L) -> ok.
65
66 %% validators
67
68 validate_loglevel(Level) ->
69 42 mongoose_logs:loglevel_keyword_to_number(Level).
70
71 467 validate_non_empty_binary(Value) when is_binary(Value), Value =/= <<>> -> ok.
72
73 validate_unique_items(Items) ->
74 5076 L = sets:size(sets:from_list(Items)),
75 5076 L = length(Items).
76
77 validate_module(Mod) ->
78 1062 case code:ensure_loaded(Mod) of
79 {module, _} ->
80 1062 ok;
81 Other ->
82
:-(
error(#{what => module_not_found, module => Mod, reason => Other})
83 end.
84
85 705 validate_positive_integer(Value) when is_integer(Value), Value > 0 -> ok.
86
87 25 validate_non_negative_integer(Value) when is_integer(Value), Value >= 0 -> ok.
88
89 294 validate_non_negative_integer_or_infinity(Value) when is_integer(Value), Value >= 0 -> ok;
90
:-(
validate_non_negative_integer_or_infinity(infinity) -> ok.
91
92 151 validate_positive_integer_or_infinity(Value) when is_integer(Value), Value > 0 -> ok;
93 42 validate_positive_integer_or_infinity(infinity) -> ok.
94
95 validate_enum(Value, Values) ->
96 802 case lists:member(Value, Values) of
97 true ->
98 802 ok;
99 false ->
100
:-(
error(#{what => validate_enum_failed,
101 value => Value,
102 allowed_values => Values})
103 end.
104
105 validate_ip_address(Value) ->
106 301 {ok, _} = inet:parse_address(Value).
107
108 553 validate_port(Value) when is_integer(Value), Value >= 0, Value =< 65535 -> ok.
109
110 1652 validate_non_empty_atom(Value) when is_atom(Value), Value =/= '' -> ok.
111
112 721 validate_non_empty_string(Value) when is_list(Value), Value =/= "" -> ok.
113
114 validate_jid(Jid) ->
115
:-(
case jid:from_binary(Jid) of
116 #jid{} ->
117
:-(
ok;
118 _ ->
119
:-(
error(#{what => validate_jid_failed, value => Jid})
120 end.
121
122 validate_ldap_filter(Value) ->
123
:-(
{ok, _} = eldap_filter:parse(Value).
124
125 validate_domain(Domain) when is_list(Domain) ->
126
:-(
#jid{luser = <<>>, lresource = <<>>} = jid:from_binary(list_to_binary(Domain)),
127
:-(
validate_domain_res(Domain).
128
129 validate_domain_res(Domain) ->
130 42 case inet_res:gethostbyname(Domain) of
131 {ok, _} ->
132
:-(
ok;
133 {error,formerr} ->
134
:-(
error(#{what => cfg_validate_domain_failed,
135 reason => formerr, text => <<"Invalid domain name">>,
136 domain => Domain});
137 {error,Reason} -> %% timeout, nxdomain
138 42 ?LOG_WARNING(#{what => cfg_validate_domain,
139 reason => Reason, domain => Domain,
140 text => <<"Couldn't resolve domain. "
141
:-(
"It could cause issues with production installations">>}),
142 42 ignore
143 end.
144
145 validate_binary_domain(Domain) when is_binary(Domain) ->
146 42 #jid{luser = <<>>, lresource = <<>>} = jid:from_binary(Domain),
147 42 validate_domain_res(binary_to_list(Domain)).
148
149 validate_subdomain_template(SubdomainTemplate) ->
150 42 Pattern = mongoose_subdomain_utils:make_subdomain_pattern(SubdomainTemplate),
151 42 Domain = mongoose_subdomain_utils:get_fqdn(Pattern, <<"example.com">>),
152 %% TODO: do we want warning printed by validate_domain_res, especially when
153 %% validating modules.mod_event_pusher_push.virtual_pubsub_hosts option
154 42 validate_binary_domain(Domain).
155
156 validate_url(Url) ->
157
:-(
validate_non_empty_string(Url).
158
159 validate_string(Value) ->
160 84 is_binary(unicode:characters_to_binary(Value)).
161
162 validate_ip_mask_string(IPMaskString) ->
163 84 validate_non_empty_string(IPMaskString),
164 84 {ok, IPMask} = mongoose_lib:parse_ip_netmask(IPMaskString),
165 84 validate_ip_mask(IPMask).
166
167 validate_ip_mask({IP, Mask}) ->
168 84 validate_string(inet:ntoa(IP)),
169 84 case IP of
170 {_,_,_,_} ->
171 84 validate_ipv4_mask(Mask);
172 _ ->
173
:-(
validate_ipv6_mask(Mask)
174 end.
175
176 validate_ipv4_mask(Mask) ->
177 84 validate_range(Mask, 0, 32).
178
179 validate_ipv6_mask(Mask) ->
180
:-(
validate_range(Mask, 0, 128).
181
182 validate_network_address(Value) ->
183
:-(
?LOG_DEBUG(#{what => validate_network_address,
184
:-(
value => Value}),
185
:-(
validate_oneof(Value, [fun validate_domain/1, fun validate_ip_address/1]).
186
187 validate_oneof(Value, Funs) ->
188
:-(
Results = [safe_call_validator(F, Value) || F <- Funs],
189
:-(
case lists:any(fun(R) -> R =:= ok end, Results) of
190 true ->
191
:-(
ok;
192 false ->
193
:-(
error(#{what => validate_oneof_failed,
194 validation_results => Results})
195 end.
196
197 safe_call_validator(F, Value) ->
198
:-(
try
199
:-(
F(Value),
200
:-(
ok
201 catch error:Reason:Stacktrace ->
202
:-(
#{reason => Reason, stacktrace => Stacktrace}
203 end.
204
205 validate_range(Value, Min, Max) when Value >= Min, Value =< Max ->
206 84 ok.
207
208 validate_filename(Filename) ->
209 491 case file:read_file_info(Filename) of
210 {ok, _} ->
211 491 ok;
212 Reason ->
213
:-(
error(#{what => invalid_filename, filename => Filename, reason => Reason})
214 end.
215
216 validate_dirname(Dirname) ->
217
:-(
case file:list_dir(Dirname) of
218 {ok, _} ->
219
:-(
ok;
220 Reason ->
221
:-(
error(#{what => invalid_dirname, dirname => Dirname, reason => Reason})
222 end.
Line Hits Source