./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("mongoose_config_spec.hrl").
9 -include_lib("jid/include/jid.hrl").
10
11 -type validator() ::
12 any | non_empty | non_negative | positive | module | {module, Prefix :: atom()}
13 | jid | domain | subdomain_template | url | ip_address | ip_mask | network_address | port
14 | filename | dirname | loglevel | pool_name | shaper | access_rule | {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_binary_domain(V);
25
:-(
validate(V, binary, url) -> validate_non_empty_binary(V);
26 806 validate(V, binary, non_empty) -> validate_non_empty_binary(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 196 validate(V, integer, non_negative) -> validate_non_negative_integer(V);
32 1682 validate(V, integer, positive) -> validate_positive_integer(V);
33 1203 validate(V, integer, port) -> validate_port(V);
34 574 validate(V, int_or_infinity, non_negative) -> validate_non_negative_integer_or_infinity(V);
35 114 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 82 validate(V, string, subdomain_template) -> validate_subdomain_template(V);
39 551 validate(V, string, ip_address) -> validate_ip_address(V);
40 164 validate(V, string, ip_mask) -> validate_ip_mask_string(V);
41
:-(
validate(V, string, network_address) -> validate_network_address(V);
42 394 validate(V, string, filename) -> validate_filename(V);
43 2635 validate(V, string, non_empty) -> validate_non_empty_string(V);
44
:-(
validate(V, string, dirname) -> validate_dirname(V);
45 2857 validate(V, atom, module) -> validate_module(V);
46 validate(V, atom, {module, Prefix}) ->
47 183 validate_module(list_to_atom(atom_to_list(Prefix) ++ "_" ++ atom_to_list(V)));
48 82 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 82 validate(V, atom, access_rule) -> validate_non_empty_atom(V);
52 3226 validate(V, atom, non_empty) -> validate_non_empty_atom(V);
53 2085 validate(V, _, {enum, Values}) -> validate_enum(V, Values);
54 29820 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 32 validate_list(L = [_|_], unique_non_empty) -> validate_unique_items(L);
59 6348 validate_list(L, unique) -> validate_unique_items(L);
60 4039 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 10993 validate_section(L, any) when is_list(L) -> ok.
65
66 %% validators
67
68 validate_loglevel(Level) ->
69 82 mongoose_logs:loglevel_keyword_to_number(Level).
70
71 806 validate_non_empty_binary(Value) when is_binary(Value), Value =/= <<>> -> ok.
72
73 validate_unique_items(Items) ->
74 6380 L = sets:size(sets:from_list(Items)),
75 6380 L = length(Items).
76
77 validate_module(Mod) ->
78 3040 case code:ensure_loaded(Mod) of
79 {module, _} ->
80 3040 ok;
81 Other ->
82
:-(
error(#{what => module_not_found, module => Mod, reason => Other})
83 end.
84
85 1682 validate_positive_integer(Value) when is_integer(Value), Value > 0 -> ok.
86
87 196 validate_non_negative_integer(Value) when is_integer(Value), Value >= 0 -> ok.
88
89 574 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 32 validate_positive_integer_or_infinity(Value) when is_integer(Value), Value > 0 -> ok;
93 82 validate_positive_integer_or_infinity(infinity) -> ok.
94
95 validate_enum(Value, Values) ->
96 2085 case lists:member(Value, Values) of
97 true ->
98 2085 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 551 {ok, _} = inet:parse_address(Value).
107
108 1203 validate_port(Value) when is_integer(Value), Value >= 0, Value =< 65535 -> ok.
109
110 3308 validate_non_empty_atom(Value) when is_atom(Value), Value =/= '' -> ok.
111
112 2799 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 82 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 82 ?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 82 ignore
143 end.
144
145 validate_binary_domain(Domain) when is_binary(Domain) ->
146 82 #jid{luser = <<>>, lresource = <<>>} = jid:from_binary(Domain),
147 82 validate_domain_res(binary_to_list(Domain)).
148
149 validate_subdomain_template(SubdomainTemplate) ->
150 82 Pattern = mongoose_subdomain_utils:make_subdomain_pattern(SubdomainTemplate),
151 82 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 82 validate_binary_domain(Domain).
155
156 validate_url(Url) ->
157
:-(
validate_non_empty_string(Url).
158
159 validate_string(Value) ->
160 164 is_binary(unicode:characters_to_binary(Value)).
161
162 validate_ip_mask_string(IPMaskString) ->
163 164 validate_non_empty_string(IPMaskString),
164 164 {ok, IPMask} = mongoose_lib:parse_ip_netmask(IPMaskString),
165 164 validate_ip_mask(IPMask).
166
167 validate_ip_mask({IP, Mask}) ->
168 164 validate_string(inet:ntoa(IP)),
169 164 case IP of
170 {_,_,_,_} ->
171 164 validate_ipv4_mask(Mask);
172 _ ->
173
:-(
validate_ipv6_mask(Mask)
174 end.
175
176 validate_ipv4_mask(Mask) ->
177 164 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 164 ok.
207
208 validate_filename(Filename) ->
209 394 case file:read_file_info(Filename) of
210 {ok, _} ->
211 394 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