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