./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_domain(V);
24
:-(
validate(V, binary, url) -> validate_non_empty_binary(V);
25 1121 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 45 validate(V, integer, non_negative) -> validate_non_negative_integer(V);
32 1695 validate(V, integer, positive) -> validate_positive_integer(V);
33 1440 validate(V, integer, port) -> validate_port(V);
34 728 validate(V, int_or_infinity, non_negative) -> validate_non_negative_integer_or_infinity(V);
35 459 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 104 validate(V, string, subdomain_template) -> validate_subdomain_template(V);
39 716 validate(V, string, ip_address) -> validate_ip_address(V);
40 208 validate(V, string, ip_mask) -> validate_ip_mask_string(V);
41
:-(
validate(V, string, network_address) -> validate_network_address(V);
42 1045 validate(V, string, filename) -> validate_filename(V);
43 1234 validate(V, string, non_empty) -> validate_non_empty_string(V);
44
:-(
validate(V, string, dirname) -> validate_dirname(V);
45 2322 validate(V, atom, module) -> validate_module(V);
46 validate(V, atom, {module, Prefix}) ->
47 227 validate_module(list_to_atom(atom_to_list(Prefix) ++ "_" ++ atom_to_list(V)));
48 104 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 104 validate(V, atom, access_rule) -> validate_non_empty_atom(V);
52 3816 validate(V, atom, non_empty) -> validate_non_empty_atom(V);
53 1807 validate(V, _, {enum, Values}) -> validate_enum(V, Values);
54 31064 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 45 validate_list(L = [_|_], unique_non_empty) -> validate_unique_items(L);
59 12767 validate_list(L, unique) -> validate_unique_items(L);
60 3882 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 14159 validate_section(L, any) when is_list(L) -> ok.
65
66 %% validators
67
68 validate_loglevel(Level) ->
69 104 mongoose_logs:loglevel_keyword_to_number(Level).
70
71 1121 validate_non_empty_binary(Value) when is_binary(Value), Value =/= <<>> -> ok.
72
73 validate_unique_items(Items) ->
74 12812 L = sets:size(sets:from_list(Items)),
75 12812 L = length(Items).
76
77 validate_module(Mod) ->
78 2549 case code:ensure_loaded(Mod) of
79 {module, _} ->
80 2549 ok;
81 Other ->
82
:-(
error(#{what => module_not_found, module => Mod, reason => Other})
83 end.
84
85 1695 validate_positive_integer(Value) when is_integer(Value), Value > 0 -> ok.
86
87 45 validate_non_negative_integer(Value) when is_integer(Value), Value >= 0 -> ok.
88
89 728 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 355 validate_positive_integer_or_infinity(Value) when is_integer(Value), Value > 0 -> ok;
93 104 validate_positive_integer_or_infinity(infinity) -> ok.
94
95 validate_enum(Value, Values) ->
96 1807 case lists:member(Value, Values) of
97 true ->
98 1807 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 716 {ok, _} = inet:parse_address(Value).
107
108 1440 validate_port(Value) when is_integer(Value), Value >= 0, Value =< 65535 -> ok.
109
110 3920 validate_non_empty_atom(Value) when is_atom(Value), Value =/= '' -> ok.
111
112 1442 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_subdomain_template(SubdomainTemplate) ->
126 104 case mongoose_subdomain_utils:make_subdomain_pattern(SubdomainTemplate) of
127 {fqdn, Domain} ->
128
:-(
validate_domain(Domain);
129 Pattern ->
130 104 Domain = binary_to_list(mongoose_subdomain_utils:get_fqdn(Pattern, <<"example.com">>)),
131 104 case inet_parse:domain(Domain) of
132 true ->
133 104 ok;
134 false ->
135
:-(
error(#{what => validate_subdomain_template_failed,
136 text => <<"Invalid subdomain template">>,
137 subdomain_template => SubdomainTemplate})
138 end
139 end.
140
141 validate_domain(Domain) when is_binary(Domain) ->
142
:-(
validate_domain(binary_to_list(Domain));
143 validate_domain(Domain) ->
144
:-(
validate_domain_name(Domain),
145
:-(
resolve_domain(Domain).
146
147 validate_domain_name(Domain) ->
148
:-(
case inet_parse:domain(Domain) of
149 true ->
150
:-(
ok;
151 false ->
152
:-(
error(#{what => validate_domain_failed,
153 text => <<"Invalid domain name">>,
154 domain => Domain})
155 end.
156
157 resolve_domain(Domain) ->
158
:-(
case inet_res:gethostbyname(Domain) of
159 {ok, _} ->
160
:-(
ok;
161 {error, Reason} -> %% timeout, nxdomain
162
:-(
?LOG_WARNING(#{what => cfg_validate_domain,
163 reason => Reason, domain => Domain,
164 text => <<"Couldn't resolve domain. "
165
:-(
"It could cause issues with production installations">>}),
166
:-(
ignore
167 end.
168
169
170 validate_url(Url) ->
171
:-(
validate_non_empty_string(Url).
172
173 validate_string(Value) ->
174 208 is_binary(unicode:characters_to_binary(Value)).
175
176 validate_ip_mask_string(IPMaskString) ->
177 208 validate_non_empty_string(IPMaskString),
178 208 {ok, IPMask} = mongoose_lib:parse_ip_netmask(IPMaskString),
179 208 validate_ip_mask(IPMask).
180
181 validate_ip_mask({IP, Mask}) ->
182 208 validate_string(inet:ntoa(IP)),
183 208 case IP of
184 {_,_,_,_} ->
185 208 validate_ipv4_mask(Mask);
186 _ ->
187
:-(
validate_ipv6_mask(Mask)
188 end.
189
190 validate_ipv4_mask(Mask) ->
191 208 validate_range(Mask, 0, 32).
192
193 validate_ipv6_mask(Mask) ->
194
:-(
validate_range(Mask, 0, 128).
195
196 validate_network_address(Value) ->
197
:-(
?LOG_DEBUG(#{what => validate_network_address,
198
:-(
value => Value}),
199
:-(
validate_oneof(Value, [fun validate_domain/1, fun validate_ip_address/1]).
200
201 validate_oneof(Value, Funs) ->
202
:-(
Results = [safe_call_validator(F, Value) || F <- Funs],
203
:-(
case lists:any(fun(R) -> R =:= ok end, Results) of
204 true ->
205
:-(
ok;
206 false ->
207
:-(
error(#{what => validate_oneof_failed,
208 validation_results => Results})
209 end.
210
211 safe_call_validator(F, Value) ->
212
:-(
try
213
:-(
F(Value),
214
:-(
ok
215 catch error:Reason:Stacktrace ->
216
:-(
#{reason => Reason, stacktrace => Stacktrace}
217 end.
218
219 validate_range(Value, Min, Max) when Value >= Min, Value =< Max ->
220 208 ok.
221
222 validate_filename(Filename) ->
223 1045 case file:read_file_info(Filename) of
224 {ok, _} ->
225 1045 ok;
226 Reason ->
227
:-(
error(#{what => invalid_filename, filename => Filename, reason => Reason})
228 end.
229
230 validate_dirname(Dirname) ->
231
:-(
case file:list_dir(Dirname) of
232 {ok, _} ->
233
:-(
ok;
234 Reason ->
235
:-(
error(#{what => invalid_dirname, dirname => Dirname, reason => Reason})
236 end.
Line Hits Source