./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 | host_type_or_global | pool_name | shaper | access_rule
14 | {enum, list()}.
15
16 -type section_validator() :: any | non_empty.
17
18 -type list_validator() :: any | non_empty | unique | unique_non_empty.
19
20 -export_type([validator/0, section_validator/0, list_validator/0]).
21
22 -spec validate(mongoose_config_parser_toml:option_value(),
23 mongoose_config_spec:option_type(), validator()) -> any().
24
:-(
validate(V, binary, domain) -> validate_domain(V);
25
:-(
validate(V, binary, url) -> validate_non_empty_binary(V);
26 964 validate(V, binary, non_empty) -> validate_non_empty_binary(V);
27
:-(
validate(V, binary, subdomain_template) -> validate_subdomain_template(V);
28 validate(V, binary, {module, Prefix}) ->
29
:-(
validate_module(list_to_atom(atom_to_list(Prefix) ++ "_" ++ binary_to_list(V)));
30
:-(
validate(V, binary, jid) -> validate_jid(V);
31
:-(
validate(V, binary, ldap_filter) -> validate_ldap_filter(V);
32 42 validate(V, integer, non_negative) -> validate_non_negative_integer(V);
33 1358 validate(V, integer, positive) -> validate_positive_integer(V);
34 1140 validate(V, integer, port) -> validate_port(V);
35 616 validate(V, int_or_infinity, non_negative) -> validate_non_negative_integer_or_infinity(V);
36 394 validate(V, int_or_infinity, positive) -> validate_positive_integer_or_infinity(V);
37
:-(
validate(V, string, url) -> validate_url(V);
38
:-(
validate(V, string, domain) -> validate_domain(V);
39 88 validate(V, string, subdomain_template) -> validate_subdomain_template(V);
40 612 validate(V, string, ip_address) -> validate_ip_address(V);
41 176 validate(V, string, ip_mask) -> validate_ip_mask_string(V);
42
:-(
validate(V, string, network_address) -> validate_network_address(V);
43 980 validate(V, string, filename) -> validate_filename(V);
44 1316 validate(V, string, non_empty) -> validate_non_empty_string(V);
45
:-(
validate(V, string, dirname) -> validate_dirname(V);
46 1889 validate(V, atom, module) -> validate_module(V);
47 validate(V, atom, {module, Prefix}) ->
48 195 validate_module(list_to_atom(atom_to_list(Prefix) ++ "_" ++ atom_to_list(V)));
49 88 validate(V, atom, loglevel) -> validate_loglevel(V);
50 88 validate(V, atom, pool_name) -> validate_non_empty_atom(V);
51
:-(
validate(global, binary_or_global, host_type_or_global) -> ok;
52 88 validate(V, binary_or_global, host_type_or_global) -> validate_non_empty_binary(V);
53
:-(
validate(V, atom, host_type_or_global) -> validate_non_empty_atom(V);
54
:-(
validate(V, atom, shaper) -> validate_non_empty_atom(V);
55 88 validate(V, atom, access_rule) -> validate_non_empty_atom(V);
56 3248 validate(V, atom, non_empty) -> validate_non_empty_atom(V);
57 1458 validate(V, _, {enum, Values}) -> validate_enum(V, Values);
58 26416 validate(_V, _, any) -> ok.
59
60 -spec validate_list([mongoose_config_parser_toml:config_part()], list_validator()) -> any().
61
:-(
validate_list([_|_], non_empty) -> ok;
62 42 validate_list(L = [_|_], unique_non_empty) -> validate_unique_items(L);
63 10412 validate_list(L, unique) -> validate_unique_items(L);
64 3204 validate_list(L, any) when is_list(L) -> ok.
65
66 -spec validate_section([mongoose_config_parser_toml:config_part()], section_validator()) -> any().
67
:-(
validate_section([_|_], non_empty) -> ok;
68 11509 validate_section(L, any) when is_list(L) -> ok.
69
70 %% validators
71
72 validate_loglevel(Level) ->
73 88 mongoose_logs:loglevel_keyword_to_number(Level).
74
75 1052 validate_non_empty_binary(Value) when is_binary(Value), Value =/= <<>> -> ok.
76
77 validate_unique_items(Items) ->
78 10454 L = sets:size(sets:from_list(Items)),
79 10454 L = length(Items).
80
81 validate_module(Mod) ->
82 2084 case code:ensure_loaded(Mod) of
83 {module, _} ->
84 2084 ok;
85 Other ->
86
:-(
error(#{what => module_not_found, module => Mod, reason => Other})
87 end.
88
89 1358 validate_positive_integer(Value) when is_integer(Value), Value > 0 -> ok.
90
91 42 validate_non_negative_integer(Value) when is_integer(Value), Value >= 0 -> ok.
92
93 616 validate_non_negative_integer_or_infinity(Value) when is_integer(Value), Value >= 0 -> ok;
94
:-(
validate_non_negative_integer_or_infinity(infinity) -> ok.
95
96 306 validate_positive_integer_or_infinity(Value) when is_integer(Value), Value > 0 -> ok;
97 88 validate_positive_integer_or_infinity(infinity) -> ok.
98
99 validate_enum(Value, Values) ->
100 1458 case lists:member(Value, Values) of
101 true ->
102 1458 ok;
103 false ->
104
:-(
error(#{what => validate_enum_failed,
105 value => Value,
106 allowed_values => Values})
107 end.
108
109 validate_ip_address(Value) ->
110 612 {ok, _} = inet:parse_address(Value).
111
112 1140 validate_port(Value) when is_integer(Value), Value >= 0, Value =< 65535 -> ok.
113
114 3424 validate_non_empty_atom(Value) when is_atom(Value), Value =/= '' -> ok.
115
116 1492 validate_non_empty_string(Value) when is_list(Value), Value =/= "" -> ok.
117
118 validate_jid(Jid) ->
119
:-(
case jid:from_binary(Jid) of
120 #jid{} ->
121
:-(
ok;
122 _ ->
123
:-(
error(#{what => validate_jid_failed, value => Jid})
124 end.
125
126 validate_ldap_filter(Value) ->
127
:-(
{ok, _} = eldap_filter:parse(Value).
128
129 validate_subdomain_template(SubdomainTemplate) ->
130 88 case mongoose_subdomain_utils:make_subdomain_pattern(SubdomainTemplate) of
131 {fqdn, Domain} ->
132
:-(
validate_domain(Domain);
133 Pattern ->
134 88 Domain = binary_to_list(mongoose_subdomain_utils:get_fqdn(Pattern, <<"example.com">>)),
135 88 case inet_parse:domain(Domain) of
136 true ->
137 88 ok;
138 false ->
139
:-(
error(#{what => validate_subdomain_template_failed,
140 text => <<"Invalid subdomain template">>,
141 subdomain_template => SubdomainTemplate})
142 end
143 end.
144
145 validate_domain(Domain) when is_binary(Domain) ->
146
:-(
validate_domain(binary_to_list(Domain));
147 validate_domain(Domain) ->
148
:-(
validate_domain_name(Domain),
149
:-(
resolve_domain(Domain).
150
151 validate_domain_name(Domain) ->
152
:-(
case inet_parse:domain(Domain) of
153 true ->
154
:-(
ok;
155 false ->
156
:-(
error(#{what => validate_domain_failed,
157 text => <<"Invalid domain name">>,
158 domain => Domain})
159 end.
160
161 resolve_domain(Domain) ->
162
:-(
case inet_res:gethostbyname(Domain) of
163 {ok, _} ->
164
:-(
ok;
165 {error, Reason} -> %% timeout, nxdomain
166
:-(
?LOG_WARNING(#{what => cfg_validate_domain,
167 reason => Reason, domain => Domain,
168 text => <<"Couldn't resolve domain. "
169
:-(
"It could cause issues with production installations">>}),
170
:-(
ignore
171 end.
172
173
174 validate_url(Url) ->
175
:-(
validate_non_empty_string(Url).
176
177 validate_string(Value) ->
178 176 is_binary(unicode:characters_to_binary(Value)).
179
180 validate_ip_mask_string(IPMaskString) ->
181 176 validate_non_empty_string(IPMaskString),
182 176 {ok, IPMask} = mongoose_lib:parse_ip_netmask(IPMaskString),
183 176 validate_ip_mask(IPMask).
184
185 validate_ip_mask({IP, Mask}) ->
186 176 validate_string(inet:ntoa(IP)),
187 176 case IP of
188 {_,_,_,_} ->
189 176 validate_ipv4_mask(Mask);
190 _ ->
191
:-(
validate_ipv6_mask(Mask)
192 end.
193
194 validate_ipv4_mask(Mask) ->
195 176 validate_range(Mask, 0, 32).
196
197 validate_ipv6_mask(Mask) ->
198
:-(
validate_range(Mask, 0, 128).
199
200 validate_network_address(Value) ->
201
:-(
?LOG_DEBUG(#{what => validate_network_address,
202
:-(
value => Value}),
203
:-(
validate_oneof(Value, [fun validate_domain/1, fun validate_ip_address/1]).
204
205 validate_oneof(Value, Funs) ->
206
:-(
Results = [safe_call_validator(F, Value) || F <- Funs],
207
:-(
case lists:any(fun(R) -> R =:= ok end, Results) of
208 true ->
209
:-(
ok;
210 false ->
211
:-(
error(#{what => validate_oneof_failed,
212 validation_results => Results})
213 end.
214
215 safe_call_validator(F, Value) ->
216
:-(
try
217
:-(
F(Value),
218
:-(
ok
219 catch error:Reason:Stacktrace ->
220
:-(
#{reason => Reason, stacktrace => Stacktrace}
221 end.
222
223 validate_range(Value, Min, Max) when Value >= Min, Value =< Max ->
224 176 ok.
225
226 validate_filename(Filename) ->
227 980 case file:read_file_info(Filename) of
228 {ok, _} ->
229 980 ok;
230 Reason ->
231
:-(
error(#{what => invalid_filename, filename => Filename, reason => Reason})
232 end.
233
234 validate_dirname(Dirname) ->
235
:-(
case file:list_dir(Dirname) of
236 {ok, _} ->
237
:-(
ok;
238 Reason ->
239
:-(
error(#{what => invalid_dirname, dirname => Dirname, reason => Reason})
240 end.
Line Hits Source