mirror of
https://github.com/processone/ejabberd.git
synced 2024-12-12 17:01:52 +01:00
Use new configuration validator
This commit is contained in:
parent
d48c067681
commit
a02cff0e78
11
Makefile.in
11
Makefile.in
@ -119,6 +119,11 @@ update:
|
||||
xref: all
|
||||
$(REBAR) skip_deps=true xref
|
||||
|
||||
hooks: all
|
||||
tools/hook_deps.sh ebin
|
||||
|
||||
options: all
|
||||
tools/opt_types.sh ebin
|
||||
|
||||
translations:
|
||||
tools/prepare-tr.sh
|
||||
@ -335,8 +340,8 @@ dialyzer/erlang.plt:
|
||||
@mkdir -p dialyzer
|
||||
@dialyzer --build_plt --output_plt dialyzer/erlang.plt \
|
||||
-o dialyzer/erlang.log --apps kernel stdlib sasl crypto \
|
||||
public_key ssl mnesia inets odbc tools compiler erts \
|
||||
runtime_tools asn1 observer xmerl et gs wx syntax_tools; \
|
||||
public_key ssl mnesia inets odbc compiler erts \
|
||||
os_mon asn1 syntax_tools; \
|
||||
status=$$? ; if [ $$status -ne 2 ]; then exit $$status; else exit 0; fi
|
||||
|
||||
dialyzer/deps.plt:
|
||||
@ -377,4 +382,4 @@ test:
|
||||
|
||||
.PHONY: src edoc dialyzer Makefile TAGS clean clean-rel distclean rel \
|
||||
install uninstall uninstall-binary uninstall-all translations deps test \
|
||||
quicktest erlang_plt deps_plt ejabberd_plt
|
||||
quicktest erlang_plt deps_plt ejabberd_plt xref hooks options
|
||||
|
@ -12,22 +12,10 @@
|
||||
### ******* MAKE SURE YOU INDENT SECTIONS CORRECTLY *******
|
||||
### *******************************************************
|
||||
### Refer to http://en.wikipedia.org/wiki/YAML for the brief description.
|
||||
### However, ejabberd treats different literals as different types:
|
||||
###
|
||||
### - unquoted or single-quoted strings. They are called "atoms".
|
||||
### Example: dog, 'Jupiter', '3.14159', YELLOW
|
||||
###
|
||||
### - numeric literals. Example: 3, -45.0, .0
|
||||
###
|
||||
### - quoted or folded strings.
|
||||
### Examples of quoted string: "Lizzard", "orange".
|
||||
### Example of folded string:
|
||||
### > Art thou not Romeo,
|
||||
### and a Montague?
|
||||
###
|
||||
|
||||
hosts:
|
||||
- "localhost"
|
||||
- localhost
|
||||
|
||||
loglevel: 4
|
||||
log_rotate_size: 10485760
|
||||
@ -36,8 +24,8 @@ log_rotate_count: 1
|
||||
log_rate_limit: 100
|
||||
|
||||
certfiles:
|
||||
- "/etc/letsencrypt/live/localhost/fullchain.pem"
|
||||
- "/etc/letsencrypt/live/localhost/privkey.pem"
|
||||
- /etc/letsencrypt/live/localhost/fullchain.pem
|
||||
- /etc/letsencrypt/live/localhost/privkey.pem
|
||||
|
||||
listen:
|
||||
-
|
||||
@ -84,25 +72,25 @@ acl:
|
||||
user_regexp: ""
|
||||
loopback:
|
||||
ip:
|
||||
- "127.0.0.0/8"
|
||||
- "::1/128"
|
||||
- 127.0.0.0/8
|
||||
- ::1/128
|
||||
|
||||
access_rules:
|
||||
local:
|
||||
- allow: local
|
||||
allow: local
|
||||
c2s:
|
||||
- deny: blocked
|
||||
- allow
|
||||
deny: blocked
|
||||
allow: all
|
||||
announce:
|
||||
- allow: admin
|
||||
allow: admin
|
||||
configure:
|
||||
- allow: admin
|
||||
allow: admin
|
||||
muc_create:
|
||||
- allow: local
|
||||
allow: local
|
||||
pubsub_createnode:
|
||||
- allow: local
|
||||
allow: local
|
||||
trusted_network:
|
||||
- allow: loopback
|
||||
allow: loopback
|
||||
|
||||
api_permissions:
|
||||
"console commands":
|
||||
@ -112,26 +100,26 @@ api_permissions:
|
||||
what: "*"
|
||||
"admin access":
|
||||
who:
|
||||
- access:
|
||||
- allow:
|
||||
- acl: loopback
|
||||
- acl: admin
|
||||
- oauth:
|
||||
- scope: "ejabberd:admin"
|
||||
- access:
|
||||
- allow:
|
||||
- acl: loopback
|
||||
- acl: admin
|
||||
access:
|
||||
allow:
|
||||
acl: loopback
|
||||
acl: admin
|
||||
oauth:
|
||||
scope: "ejabberd:admin"
|
||||
access:
|
||||
allow:
|
||||
acl: loopback
|
||||
acl: admin
|
||||
what:
|
||||
- "*"
|
||||
- "!stop"
|
||||
- "!start"
|
||||
"public commands":
|
||||
who:
|
||||
- ip: "127.0.0.1/8"
|
||||
ip: 127.0.0.1/8
|
||||
what:
|
||||
- "status"
|
||||
- "connected_users_number"
|
||||
- status
|
||||
- connected_users_number
|
||||
|
||||
shaper:
|
||||
normal: 1000
|
||||
@ -140,11 +128,11 @@ shaper:
|
||||
shaper_rules:
|
||||
max_user_sessions: 10
|
||||
max_user_offline_messages:
|
||||
- 5000: admin
|
||||
- 100
|
||||
5000: admin
|
||||
100: all
|
||||
c2s_shaper:
|
||||
- none: admin
|
||||
- normal
|
||||
none: admin
|
||||
normal: all
|
||||
s2s_shaper: fast
|
||||
|
||||
modules:
|
||||
@ -163,7 +151,7 @@ modules:
|
||||
mod_fail2ban: {}
|
||||
mod_http_api: {}
|
||||
mod_http_upload:
|
||||
put_url: "https://@HOST@:5443/upload"
|
||||
put_url: https://@HOST@:5443/upload
|
||||
mod_last: {}
|
||||
mod_mam:
|
||||
## Mnesia is limited to 2GB, better to use an SQL backend
|
||||
@ -196,11 +184,11 @@ modules:
|
||||
mod_pubsub:
|
||||
access_createnode: pubsub_createnode
|
||||
plugins:
|
||||
- "flat"
|
||||
- "pep"
|
||||
- flat
|
||||
- pep
|
||||
force_node_config:
|
||||
## Avoid buggy clients to make their bookmarks public
|
||||
"storage:bookmarks":
|
||||
storage:bookmarks:
|
||||
access_model: whitelist
|
||||
mod_push: {}
|
||||
mod_push_keepalive: {}
|
||||
|
@ -23,7 +23,7 @@
|
||||
path = [] :: [binary()],
|
||||
q = [] :: [{binary() | nokey, binary()}],
|
||||
us = {<<>>, <<>>} :: {binary(), binary()},
|
||||
auth :: {binary(), binary()} | {oauth, binary(), []} | undefined,
|
||||
auth :: {binary(), binary()} | {oauth, binary(), []} | undefined | invalid,
|
||||
lang = <<"">> :: binary(),
|
||||
data = <<"">> :: binary(),
|
||||
ip :: {inet:ip_address(), inet:port_number()},
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
-type local_hint() :: integer() | {apply, atom(), atom()}.
|
||||
|
||||
-record(route, {domain :: binary() | '_',
|
||||
server_host :: binary() | '_',
|
||||
-record(route, {domain :: binary(),
|
||||
server_host :: binary(),
|
||||
pid :: undefined | pid(),
|
||||
local_hint :: local_hint() | undefined | '_'}).
|
||||
local_hint :: local_hint() | undefined}).
|
||||
|
@ -30,7 +30,7 @@
|
||||
-type info() :: [{conn, atom()} | {ip, ip()} | {node, atom()}
|
||||
| {oor, boolean()} | {auth_module, atom()}
|
||||
| {num_stanzas_in, non_neg_integer()}
|
||||
| offline].
|
||||
| {atom(), term()}].
|
||||
-type prio() :: undefined | integer().
|
||||
|
||||
-endif.
|
||||
|
@ -17,19 +17,5 @@
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-define(SQL_MARK, sql__mark_).
|
||||
-define(SQL(SQL), ?SQL_MARK(SQL)).
|
||||
|
||||
-define(SQL_UPSERT_MARK, sql_upsert__mark_).
|
||||
-define(SQL_UPSERT(Host, Table, Fields),
|
||||
ejabberd_sql:sql_query(Host, ?SQL_UPSERT_MARK(Table, Fields))).
|
||||
-define(SQL_UPSERT_T(Table, Fields),
|
||||
ejabberd_sql:sql_query_t(?SQL_UPSERT_MARK(Table, Fields))).
|
||||
|
||||
-define(SQL_INSERT_MARK, sql_insert__mark_).
|
||||
-define(SQL_INSERT(Table, Fields), ?SQL_INSERT_MARK(Table, Fields)).
|
||||
|
||||
-record(sql_query, {hash, format_query, format_res, args, loc}).
|
||||
|
||||
-record(sql_escape, {string, integer, boolean, in_array_string}).
|
||||
-compile([{parse_transform, ejabberd_sql_pt}]).
|
||||
-include("ejabberd_sql.hrl").
|
||||
|
@ -44,6 +44,7 @@
|
||||
attributes = [] :: [{binary(), [binary()]}]}).
|
||||
|
||||
-type tlsopts() :: [{encrypt, tls | starttls | none} |
|
||||
{tls_certfile, binary() | undefined} |
|
||||
{tls_cacertfile, binary() | undefined} |
|
||||
{tls_depth, non_neg_integer() | undefined} |
|
||||
{tls_verify, hard | soft | false}].
|
||||
@ -61,3 +62,18 @@
|
||||
-type eldap_config() :: #eldap_config{}.
|
||||
-type eldap_search() :: #eldap_search{}.
|
||||
-type eldap_entry() :: #eldap_entry{}.
|
||||
|
||||
-define(eldap_config(M, H),
|
||||
#eldap_config{
|
||||
servers = M:ldap_servers(H),
|
||||
backups = M:ldap_backups(H),
|
||||
tls_options = [{encrypt, M:ldap_encrypt(H)},
|
||||
{tls_verify, M:ldap_tls_verify(H)},
|
||||
{tls_certfile, M:ldap_tls_certfile(H)},
|
||||
{tls_cacertfile, M:ldap_tls_cacertfile(H)},
|
||||
{tls_depth, M:ldap_tls_depth(H)}],
|
||||
port = M:ldap_port(H),
|
||||
dn = M:ldap_rootdn(H),
|
||||
password = M:ldap_password(H),
|
||||
base = M:ldap_base(H),
|
||||
deref_aliases = M:ldap_deref_aliases(H)}).
|
||||
|
@ -22,19 +22,19 @@
|
||||
-compile([{parse_transform, lager_transform}]).
|
||||
|
||||
-define(DEBUG(Format, Args),
|
||||
lager:debug(Format, Args)).
|
||||
lager:debug(Format, Args), ok).
|
||||
|
||||
-define(INFO_MSG(Format, Args),
|
||||
lager:info(Format, Args)).
|
||||
lager:info(Format, Args), ok).
|
||||
|
||||
-define(WARNING_MSG(Format, Args),
|
||||
lager:warning(Format, Args)).
|
||||
lager:warning(Format, Args), ok).
|
||||
|
||||
-define(ERROR_MSG(Format, Args),
|
||||
lager:error(Format, Args)).
|
||||
lager:error(Format, Args), ok).
|
||||
|
||||
-define(CRITICAL_MSG(Format, Args),
|
||||
lager:critical(Format, Args)).
|
||||
lager:critical(Format, Args), ok).
|
||||
|
||||
%% Use only when trying to troubleshoot test problem with ExUnit
|
||||
-define(EXUNIT_LOG(Format, Args),
|
||||
|
@ -19,12 +19,12 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-record(archive_msg,
|
||||
{us = {<<"">>, <<"">>} :: {binary(), binary()} | '$2',
|
||||
id = <<>> :: binary() | '_',
|
||||
timestamp = erlang:timestamp() :: erlang:timestamp() | '_' | '$1',
|
||||
peer = {<<"">>, <<"">>, <<"">>} :: ljid() | '_' | '$3' | undefined,
|
||||
bare_peer = {<<"">>, <<"">>, <<"">>} :: ljid() | '_' | '$3',
|
||||
packet = #xmlel{} :: xmlel() | message() | '_',
|
||||
{us = {<<"">>, <<"">>} :: {binary(), binary()},
|
||||
id = <<>> :: binary(),
|
||||
timestamp = erlang:timestamp() :: erlang:timestamp(),
|
||||
peer = {<<"">>, <<"">>, <<"">>} :: ljid() | undefined,
|
||||
bare_peer = {<<"">>, <<"">>, <<"">>} :: ljid(),
|
||||
packet = #xmlel{} :: xmlel() | message(),
|
||||
nick = <<"">> :: binary(),
|
||||
type = chat :: chat | groupchat}).
|
||||
|
||||
|
@ -24,11 +24,13 @@
|
||||
|
||||
-record(lqueue,
|
||||
{
|
||||
queue :: p1_queue:queue(),
|
||||
max = 0 :: integer()
|
||||
queue = p1_queue:new() :: p1_queue:queue(),
|
||||
max = 0 :: integer()
|
||||
}).
|
||||
|
||||
-type lqueue() :: #lqueue{}.
|
||||
-type lqueue_elem() :: {binary(), message(), boolean(),
|
||||
erlang:timestamp(), non_neg_integer()}.
|
||||
|
||||
-record(config,
|
||||
{
|
||||
@ -63,7 +65,7 @@
|
||||
captcha_whitelist = (?SETS):empty() :: gb_sets:set(),
|
||||
mam = false :: boolean(),
|
||||
pubsub = <<"">> :: binary(),
|
||||
lang = ejabberd_config:get_mylang() :: binary()
|
||||
lang = ejabberd_option:language() :: binary()
|
||||
}).
|
||||
|
||||
-type config() :: #config{}.
|
||||
@ -89,8 +91,8 @@
|
||||
{
|
||||
message_time = 0 :: integer(),
|
||||
presence_time = 0 :: integer(),
|
||||
message_shaper = none :: shaper:shaper(),
|
||||
presence_shaper = none :: shaper:shaper(),
|
||||
message_shaper = none :: ejabberd_shaper:shaper(),
|
||||
presence_shaper = none :: ejabberd_shaper:shaper(),
|
||||
message :: message() | undefined,
|
||||
presence :: {binary(), presence()} | undefined
|
||||
}).
|
||||
@ -110,11 +112,11 @@
|
||||
robots = #{} :: map(),
|
||||
nicks = #{} :: map(),
|
||||
affiliations = #{} :: map(),
|
||||
history :: lqueue(),
|
||||
history = #lqueue{} :: lqueue(),
|
||||
subject = [] :: [text()],
|
||||
subject_author = <<"">> :: binary(),
|
||||
just_created = erlang:system_time(microsecond) :: true | integer(),
|
||||
activity = treap:empty() :: treap:treap(),
|
||||
room_shaper = none :: shaper:shaper(),
|
||||
room_shaper = none :: ejabberd_shaper:shaper(),
|
||||
room_queue :: p1_queue:queue() | undefined
|
||||
}).
|
||||
|
@ -26,11 +26,12 @@
|
||||
{fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.36"}}},
|
||||
{xmpp, ".*", {git, "https://github.com/processone/xmpp", {tag, "1.3.4"}}},
|
||||
{fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.19"}}},
|
||||
{yconf, ".*", {git, "https://github.com/processone/yconf", "master"}},
|
||||
{jiffy, ".*", {git, "https://github.com/davisp/jiffy", {tag, "0.14.8"}}},
|
||||
{p1_oauth2, ".*", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.5"}}},
|
||||
{pkix, ".*", {git, "https://github.com/processone/pkix", {tag, "1.0.2"}}},
|
||||
{jose, ".*", {git, "https://github.com/potatosalad/erlang-jose", {tag, "1.8.4"}}},
|
||||
{eimp, ".*", {git, "https://github.com/processone/eimp", {tag, "1.0.11"}}},
|
||||
{eimp, ".*", {git, "https://github.com/processone/eimp", "master"}},
|
||||
{mqtree, ".*", {git, "https://github.com/processone/mqtree", {tag, "1.0.3"}}},
|
||||
{if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.28"}}}},
|
||||
{if_var_true, sip, {esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.29"}}}},
|
||||
|
293
src/ELDAPv3.erl
293
src/ELDAPv3.erl
File diff suppressed because it is too large
Load Diff
998
src/acl.erl
998
src/acl.erl
File diff suppressed because it is too large
Load Diff
529
src/econf.erl
Normal file
529
src/econf.erl
Normal file
@ -0,0 +1,529 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : econf.erl
|
||||
%%% Purpose : Validator for ejabberd configuration options
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License along
|
||||
%%% with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
-module(econf).
|
||||
|
||||
%% API
|
||||
-export([parse/3, validate/2, fail/1, format_error/2, replace_macros/1]).
|
||||
%% Simple types
|
||||
-export([pos_int/0, pos_int/1, non_neg_int/0, non_neg_int/1]).
|
||||
-export([int/0, int/2, number/1, octal/0]).
|
||||
-export([binary/0, binary/1]).
|
||||
-export([string/0, string/1]).
|
||||
-export([enum/1, bool/0, atom/0, any/0]).
|
||||
%% Complex types
|
||||
-export([url/0, url/1]).
|
||||
-export([file/0, file/1]).
|
||||
-export([directory/0, directory/1]).
|
||||
-export([ip/0, ipv4/0, ipv6/0, ip_mask/0, port/0]).
|
||||
-export([re/0, glob/0]).
|
||||
-export([path/0, binary_sep/1]).
|
||||
-export([beam/0, beam/1]).
|
||||
-export([timeout/1, timeout/2]).
|
||||
%% Composite types
|
||||
-export([list/1, list/2]).
|
||||
-export([list_or_single/1, list_or_single/2]).
|
||||
-export([map/2, map/3]).
|
||||
-export([either/2, and_then/2, non_empty/1]).
|
||||
-export([options/1, options/2]).
|
||||
%% Custom types
|
||||
-export([acl/0, shaper/0, url_or_file/0, lang/0]).
|
||||
-export([pem/0, queue_type/0]).
|
||||
-export([jid/0, user/0, domain/0, resource/0]).
|
||||
-export([db_type/1, ldap_filter/0, well_known/2]).
|
||||
-ifdef(SIP).
|
||||
-export([sip_uri/0]).
|
||||
-endif.
|
||||
|
||||
-type error_reason() :: term().
|
||||
-type error_return() :: {error, error_reason(), yconf:ctx()}.
|
||||
-type validator() :: yconf:validator().
|
||||
-type validator(T) :: yconf:validator(T).
|
||||
-type validators() :: yconf:validators().
|
||||
-export_type([validator/0, validator/1, validators/0]).
|
||||
-export_type([error_reason/0, error_return/0]).
|
||||
|
||||
%%%===================================================================
|
||||
%%% API
|
||||
%%%===================================================================
|
||||
parse(File, Validators, Options) ->
|
||||
try yconf:parse(File, Validators, Options)
|
||||
catch _:{?MODULE, Reason, Ctx} ->
|
||||
{error, Reason, Ctx}
|
||||
end.
|
||||
|
||||
validate(Validator, Y) ->
|
||||
try yconf:validate(Validator, Y)
|
||||
catch _:{?MODULE, Reason, Ctx} ->
|
||||
{error, Reason, Ctx}
|
||||
end.
|
||||
|
||||
replace_macros(Y) ->
|
||||
yconf:replace_macros(Y).
|
||||
|
||||
-spec fail(error_reason()) -> no_return().
|
||||
fail(Reason) ->
|
||||
yconf:fail(?MODULE, Reason).
|
||||
|
||||
format_error({bad_module, Mod}, Ctx)
|
||||
when Ctx == [listen, module];
|
||||
Ctx == [listen, request_handlers] ->
|
||||
Mods = ejabberd_config:beams(all),
|
||||
format("~s: unknown ~s: ~s. Did you mean ~s?",
|
||||
[yconf:format_ctx(Ctx),
|
||||
format_module_type(Ctx), Mod,
|
||||
misc:best_match(Mod, Mods)]);
|
||||
format_error({bad_module, Mod}, Ctx)
|
||||
when Ctx == [modules] ->
|
||||
Mods = lists:filter(
|
||||
fun(M) ->
|
||||
case atom_to_list(M) of
|
||||
"mod_" ++ _ -> true;
|
||||
_ -> false
|
||||
end
|
||||
end, ejabberd_config:beams(all)),
|
||||
format("~s: unknown ~s: ~s. Did you mean ~s?",
|
||||
[yconf:format_ctx(Ctx),
|
||||
format_module_type(Ctx), Mod,
|
||||
misc:best_match(Mod, Mods)]);
|
||||
format_error({bad_export, {F, A}, Mod}, Ctx)
|
||||
when Ctx == [listen, module];
|
||||
Ctx == [listen, request_handlers];
|
||||
Ctx == [modules] ->
|
||||
Type = format_module_type(Ctx),
|
||||
Slogan = yconf:format_ctx(Ctx),
|
||||
case lists:member(Mod, ejabberd_config:beams(local)) of
|
||||
true ->
|
||||
format("~s: '~s' is not a ~s", [Slogan, Mod, Type]);
|
||||
false ->
|
||||
case lists:member(Mod, ejabberd_config:beams(external)) of
|
||||
true ->
|
||||
format("~s: third-party ~s '~s' doesn't export "
|
||||
"function ~s/~B. If it's really a ~s, "
|
||||
"consider to upgrade it",
|
||||
[Slogan, Type, Mod, F, A, Type]);
|
||||
false ->
|
||||
format("~s: '~s' doesn't match any known ~s",
|
||||
[Slogan, Mod, Type])
|
||||
end
|
||||
end;
|
||||
format_error({unknown_option, [], _} = Why, Ctx) ->
|
||||
format("~s. There are no available options",
|
||||
[yconf:format_error(Why, Ctx)]);
|
||||
format_error({unknown_option, Known, Opt} = Why, Ctx) ->
|
||||
format("~s. Did you mean ~s? ~s",
|
||||
[yconf:format_error(Why, Ctx),
|
||||
misc:best_match(Opt, Known),
|
||||
format_known("Available options", Known)]);
|
||||
format_error({bad_enum, Known, Bad} = Why, Ctx) ->
|
||||
format("~s. Did you mean ~s? ~s",
|
||||
[yconf:format_error(Why, Ctx),
|
||||
misc:best_match(Bad, Known),
|
||||
format_known("Possible values", Known)]);
|
||||
format_error({bad_yaml, _, _} = Why, _) ->
|
||||
format_error(Why);
|
||||
format_error(Reason, Ctx) ->
|
||||
[H|T] = format_error(Reason),
|
||||
yconf:format_ctx(Ctx) ++ ": " ++ [string:to_lower(H)|T].
|
||||
|
||||
format_error({bad_db_type, _, Atom}) ->
|
||||
format("unsupported database: ~s", [Atom]);
|
||||
format_error({bad_lang, Lang}) ->
|
||||
format("Invalid language tag: ~s", [Lang]);
|
||||
format_error({bad_pem, Why, Path}) ->
|
||||
format("Failed to read PEM file '~s': ~s",
|
||||
[Path, pkix:format_error(Why)]);
|
||||
format_error({bad_cert, Why, Path}) ->
|
||||
format_error({bad_pem, Why, Path});
|
||||
format_error({bad_jid, Bad}) ->
|
||||
format("Invalid XMPP address: ~s", [Bad]);
|
||||
format_error({bad_user, Bad}) ->
|
||||
format("Invalid user part: ~s", [Bad]);
|
||||
format_error({bad_domain, Bad}) ->
|
||||
format("Invalid domain: ~s", [Bad]);
|
||||
format_error({bad_resource, Bad}) ->
|
||||
format("Invalid resource part: ~s", [Bad]);
|
||||
format_error({bad_ldap_filter, Bad}) ->
|
||||
format("Invalid LDAP filter: ~s", [Bad]);
|
||||
format_error({bad_sip_uri, Bad}) ->
|
||||
format("Invalid SIP URI: ~s", [Bad]);
|
||||
format_error({route_conflict, R}) ->
|
||||
format("Failed to reuse route '~s' because it's "
|
||||
"already registered on a virtual host",
|
||||
[R]);
|
||||
format_error({listener_dup, AddrPort}) ->
|
||||
format("Overlapping listeners found at ~s",
|
||||
[format_addr_port(AddrPort)]);
|
||||
format_error({listener_conflict, AddrPort1, AddrPort2}) ->
|
||||
format("Overlapping listeners found at ~s and ~s",
|
||||
[format_addr_port(AddrPort1),
|
||||
format_addr_port(AddrPort2)]);
|
||||
format_error({invalid_syntax, Reason}) ->
|
||||
format("~s", [Reason]);
|
||||
format_error({missing_module_dep, Mod, DepMod}) ->
|
||||
format("module ~s depends on module ~s, "
|
||||
"which is not found in the config",
|
||||
[Mod, DepMod]);
|
||||
format_error(eimp_error) ->
|
||||
format("ejabberd is built without image converter support", []);
|
||||
format_error({mqtt_codec, Reason}) ->
|
||||
mqtt_codec:format_error(Reason);
|
||||
format_error(Reason) ->
|
||||
yconf:format_error(Reason).
|
||||
|
||||
format_module_type([listen, module]) ->
|
||||
"listening module";
|
||||
format_module_type([listen, request_handlers]) ->
|
||||
"HTTP request handler";
|
||||
format_module_type([modules]) ->
|
||||
"ejabberd module".
|
||||
|
||||
format_known(_, Known) when length(Known) > 20 ->
|
||||
"";
|
||||
format_known(Prefix, Known) ->
|
||||
[Prefix, " are: ", format_join(Known)].
|
||||
|
||||
format_join([]) ->
|
||||
"(empty)";
|
||||
format_join([H|_] = L) when is_atom(H) ->
|
||||
format_join([atom_to_binary(A, utf8) || A <- L]);
|
||||
format_join(L) ->
|
||||
str:join(lists:sort(L), <<", ">>).
|
||||
|
||||
%%%===================================================================
|
||||
%%% Validators from yconf
|
||||
%%%===================================================================
|
||||
pos_int() ->
|
||||
yconf:pos_int().
|
||||
|
||||
pos_int(Inf) ->
|
||||
yconf:pos_int(Inf).
|
||||
|
||||
non_neg_int() ->
|
||||
yconf:non_neg_int().
|
||||
|
||||
non_neg_int(Inf) ->
|
||||
yconf:non_neg_int(Inf).
|
||||
|
||||
int() ->
|
||||
yconf:int().
|
||||
|
||||
int(Min, Max) ->
|
||||
yconf:int(Min, Max).
|
||||
|
||||
number(Min) ->
|
||||
yconf:number(Min).
|
||||
|
||||
octal() ->
|
||||
yconf:octal().
|
||||
|
||||
binary() ->
|
||||
yconf:binary().
|
||||
|
||||
binary(Re) ->
|
||||
yconf:binary(Re).
|
||||
|
||||
enum(L) ->
|
||||
yconf:enum(L).
|
||||
|
||||
bool() ->
|
||||
yconf:bool().
|
||||
|
||||
atom() ->
|
||||
yconf:atom().
|
||||
|
||||
string() ->
|
||||
yconf:string().
|
||||
|
||||
string(Re) ->
|
||||
yconf:string(Re).
|
||||
|
||||
any() ->
|
||||
yconf:any().
|
||||
|
||||
url() ->
|
||||
yconf:url().
|
||||
|
||||
url(Schemes) ->
|
||||
yconf:url(Schemes).
|
||||
|
||||
file() ->
|
||||
yconf:file().
|
||||
|
||||
file(Type) ->
|
||||
yconf:file(Type).
|
||||
|
||||
directory() ->
|
||||
yconf:directory().
|
||||
|
||||
directory(Type) ->
|
||||
yconf:directory(Type).
|
||||
|
||||
ip() ->
|
||||
yconf:ip().
|
||||
|
||||
ipv4() ->
|
||||
yconf:ipv4().
|
||||
|
||||
ipv6() ->
|
||||
yconf:ipv6().
|
||||
|
||||
ip_mask() ->
|
||||
yconf:ip_mask().
|
||||
|
||||
port() ->
|
||||
yconf:port().
|
||||
|
||||
re() ->
|
||||
yconf:re().
|
||||
|
||||
glob() ->
|
||||
yconf:glob().
|
||||
|
||||
path() ->
|
||||
yconf:path().
|
||||
|
||||
binary_sep(Sep) ->
|
||||
yconf:binary_sep(Sep).
|
||||
|
||||
beam() ->
|
||||
yconf:beam().
|
||||
|
||||
beam(Exports) ->
|
||||
yconf:beam(Exports).
|
||||
|
||||
timeout(Units) ->
|
||||
yconf:timeout(Units).
|
||||
|
||||
timeout(Units, Inf) ->
|
||||
yconf:timeout(Units, Inf).
|
||||
|
||||
non_empty(F) ->
|
||||
yconf:non_empty(F).
|
||||
|
||||
list(F) ->
|
||||
yconf:list(F).
|
||||
|
||||
list(F, Opts) ->
|
||||
yconf:list(F, Opts).
|
||||
|
||||
list_or_single(F) ->
|
||||
yconf:list_or_single(F).
|
||||
|
||||
list_or_single(F, Opts) ->
|
||||
yconf:list_or_single(F, Opts).
|
||||
|
||||
map(F1, F2) ->
|
||||
yconf:map(F1, F2).
|
||||
|
||||
map(F1, F2, Opts) ->
|
||||
yconf:map(F1, F2, Opts).
|
||||
|
||||
either(F1, F2) ->
|
||||
yconf:either(F1, F2).
|
||||
|
||||
and_then(F1, F2) ->
|
||||
yconf:and_then(F1, F2).
|
||||
|
||||
options(V) ->
|
||||
yconf:options(V).
|
||||
|
||||
options(V, O) ->
|
||||
yconf:options(V, O).
|
||||
|
||||
%%%===================================================================
|
||||
%%% Custom validators
|
||||
%%%===================================================================
|
||||
acl() ->
|
||||
either(
|
||||
atom(),
|
||||
acl:access_rules_validator()).
|
||||
|
||||
shaper() ->
|
||||
either(
|
||||
atom(),
|
||||
ejabberd_shaper:shaper_rules_validator()).
|
||||
|
||||
-spec url_or_file() -> yconf:validator({file | url, binary()}).
|
||||
url_or_file() ->
|
||||
either(
|
||||
and_then(url(), fun(URL) -> {url, URL} end),
|
||||
and_then(file(), fun(File) -> {file, File} end)).
|
||||
|
||||
-spec lang() -> yconf:validator(binary()).
|
||||
lang() ->
|
||||
and_then(
|
||||
binary(),
|
||||
fun(Lang) ->
|
||||
try xmpp_lang:check(Lang)
|
||||
catch _:_ -> fail({bad_lang, Lang})
|
||||
end
|
||||
end).
|
||||
|
||||
-spec pem() -> yconf:validator(binary()).
|
||||
pem() ->
|
||||
and_then(
|
||||
path(),
|
||||
fun(Path) ->
|
||||
case pkix:is_pem_file(Path) of
|
||||
true -> Path;
|
||||
{false, Reason} ->
|
||||
fail({bad_pem, Reason, Path})
|
||||
end
|
||||
end).
|
||||
|
||||
-spec jid() -> yconf:validator(jid:jid()).
|
||||
jid() ->
|
||||
and_then(
|
||||
binary(),
|
||||
fun(Val) ->
|
||||
try jid:decode(Val)
|
||||
catch _:{bad_jid, _} = Reason -> fail(Reason)
|
||||
end
|
||||
end).
|
||||
|
||||
-spec user() -> yconf:validator(binary()).
|
||||
user() ->
|
||||
and_then(
|
||||
binary(),
|
||||
fun(Val) ->
|
||||
case jid:nodeprep(Val) of
|
||||
error -> fail({bad_user, Val});
|
||||
U -> U
|
||||
end
|
||||
end).
|
||||
|
||||
-spec domain() -> yconf:validator(binary()).
|
||||
domain() ->
|
||||
and_then(
|
||||
non_empty(binary()),
|
||||
fun(Val) ->
|
||||
try jid:tolower(jid:decode(Val)) of
|
||||
{<<"">>, Domain, <<"">>} -> Domain;
|
||||
_ -> fail({bad_domain, Val})
|
||||
catch _:{bad_jid, _} ->
|
||||
fail({bad_domain, Val})
|
||||
end
|
||||
end).
|
||||
|
||||
-spec resource() -> yconf:validator(binary()).
|
||||
resource() ->
|
||||
and_then(
|
||||
binary(),
|
||||
fun(Val) ->
|
||||
case jid:resourceprep(Val) of
|
||||
error -> fail({bad_resource, Val});
|
||||
R -> R
|
||||
end
|
||||
end).
|
||||
|
||||
-spec db_type(module()) -> yconf:validator(atom()).
|
||||
db_type(M) ->
|
||||
and_then(
|
||||
atom(),
|
||||
fun(T) ->
|
||||
case code:ensure_loaded(db_module(M, T)) of
|
||||
{module, _} -> T;
|
||||
{error, _} -> fail({bad_db_type, M, T})
|
||||
end
|
||||
end).
|
||||
|
||||
-spec queue_type() -> yconf:validator(ram | file).
|
||||
queue_type() ->
|
||||
enum([ram, file]).
|
||||
|
||||
-spec ldap_filter() -> yconf:validator(binary()).
|
||||
ldap_filter() ->
|
||||
and_then(
|
||||
binary(),
|
||||
fun(Val) ->
|
||||
case eldap_filter:parse(Val) of
|
||||
{ok, _} -> Val;
|
||||
_ -> fail({bad_ldap_filter, Val})
|
||||
end
|
||||
end).
|
||||
|
||||
well_known(queue_type, _) ->
|
||||
queue_type();
|
||||
well_known(db_type, M) ->
|
||||
db_type(M);
|
||||
well_known(ram_db_type, M) ->
|
||||
db_type(M);
|
||||
well_known(cache_life_time, _) ->
|
||||
pos_int(infinity);
|
||||
well_known(cache_size, _) ->
|
||||
pos_int(infinity);
|
||||
well_known(use_cache, _) ->
|
||||
bool();
|
||||
well_known(cache_missed, _) ->
|
||||
bool();
|
||||
well_known(host, _) ->
|
||||
host();
|
||||
well_known(hosts, _) ->
|
||||
list(host(), [unique]).
|
||||
|
||||
-ifdef(SIP).
|
||||
sip_uri() ->
|
||||
and_then(
|
||||
binary(),
|
||||
fun(Val) ->
|
||||
case esip:decode_uri(Val) of
|
||||
error -> fail({bad_sip_uri, Val});
|
||||
URI -> URI
|
||||
end
|
||||
end).
|
||||
-endif.
|
||||
|
||||
-spec host() -> yconf:validator(binary()).
|
||||
host() ->
|
||||
fun(Domain) ->
|
||||
Host = ejabberd_config:get_myname(),
|
||||
Hosts = ejabberd_config:get_option(hosts),
|
||||
Domain1 = (binary())(Domain),
|
||||
Domain2 = misc:expand_keyword(<<"@HOST@">>, Domain1, Host),
|
||||
Domain3 = (domain())(Domain2),
|
||||
case lists:member(Domain3, Hosts) of
|
||||
true -> fail({route_conflict, Domain3});
|
||||
false -> Domain3
|
||||
end
|
||||
end.
|
||||
|
||||
%%%===================================================================
|
||||
%%% Internal functions
|
||||
%%%===================================================================
|
||||
-spec db_module(module(), atom()) -> module().
|
||||
db_module(M, Type) ->
|
||||
try list_to_atom(atom_to_list(M) ++ "_" ++ atom_to_list(Type))
|
||||
catch _:system_limit ->
|
||||
fail({bad_length, 255})
|
||||
end.
|
||||
|
||||
format_addr_port({IP, Port}) ->
|
||||
IPStr = case tuple_size(IP) of
|
||||
4 -> inet:ntoa(IP);
|
||||
8 -> "[" ++ inet:ntoa(IP) ++ "]"
|
||||
end,
|
||||
IPStr ++ ":" ++ integer_to_list(Port).
|
||||
|
||||
-spec format(iolist(), list()) -> string().
|
||||
format(Fmt, Args) ->
|
||||
lists:flatten(io_lib:format(Fmt, Args)).
|
@ -38,7 +38,7 @@
|
||||
-protocol({xep, 270, '1.0'}).
|
||||
|
||||
-export([start/0, stop/0, halt/0, start_app/1, start_app/2,
|
||||
get_pid_file/0, check_app/1, module_name/1, is_loaded/0]).
|
||||
get_pid_file/0, check_apps/0, module_name/1, is_loaded/0]).
|
||||
|
||||
-include("logger.hrl").
|
||||
|
||||
@ -49,8 +49,8 @@ stop() ->
|
||||
application:stop(ejabberd).
|
||||
|
||||
halt() ->
|
||||
application:stop(lager),
|
||||
application:stop(sasl),
|
||||
_ = application:stop(lager),
|
||||
_ = application:stop(sasl),
|
||||
erlang:halt(1, [{flush, true}]).
|
||||
|
||||
%% @spec () -> false | string()
|
||||
@ -71,21 +71,15 @@ start_app(App, Type) ->
|
||||
StartFlag = not is_loaded(),
|
||||
start_app(App, Type, StartFlag).
|
||||
|
||||
check_app(App) ->
|
||||
StartFlag = not is_loaded(),
|
||||
spawn(fun() -> check_app_modules(App, StartFlag) end),
|
||||
ok.
|
||||
|
||||
is_loaded() ->
|
||||
Apps = application:which_applications(),
|
||||
lists:keymember(ejabberd, 1, Apps).
|
||||
|
||||
start_app(App, Type, StartFlag) when not is_list(App) ->
|
||||
start_app(App, Type, StartFlag) when is_atom(App) ->
|
||||
start_app([App], Type, StartFlag);
|
||||
start_app([App|Apps], Type, StartFlag) ->
|
||||
case application:start(App,Type) of
|
||||
ok ->
|
||||
spawn(fun() -> check_app_modules(App, StartFlag) end),
|
||||
start_app(Apps, Type, StartFlag);
|
||||
{error, {already_started, _}} ->
|
||||
start_app(Apps, Type, StartFlag);
|
||||
@ -93,23 +87,23 @@ start_app([App|Apps], Type, StartFlag) ->
|
||||
case lists:member(DepApp, [App|Apps]) of
|
||||
true ->
|
||||
Reason = io_lib:format(
|
||||
"failed to start application '~p': "
|
||||
"circular dependency on '~p' detected",
|
||||
"Failed to start Erlang application '~s': "
|
||||
"circular dependency with '~s' detected",
|
||||
[App, DepApp]),
|
||||
exit_or_halt(Reason, StartFlag);
|
||||
false ->
|
||||
start_app([DepApp,App|Apps], Type, StartFlag)
|
||||
end;
|
||||
Err ->
|
||||
Reason = io_lib:format("failed to start application '~p': ~p",
|
||||
[App, Err]),
|
||||
{error, Why} ->
|
||||
Reason = io_lib:format(
|
||||
"Failed to start Erlang application '~s': ~s. ~s",
|
||||
[App, format_error(Why), hint()]),
|
||||
exit_or_halt(Reason, StartFlag)
|
||||
end;
|
||||
start_app([], _Type, _StartFlag) ->
|
||||
ok.
|
||||
|
||||
check_app_modules(App, StartFlag) ->
|
||||
sleep(5000),
|
||||
case application:get_key(App, modules) of
|
||||
{ok, Mods} ->
|
||||
lists:foreach(
|
||||
@ -118,12 +112,12 @@ check_app_modules(App, StartFlag) ->
|
||||
non_existing ->
|
||||
File = get_module_file(App, Mod),
|
||||
Reason = io_lib:format(
|
||||
"couldn't find module ~s "
|
||||
"needed for application '~p'",
|
||||
[File, App]),
|
||||
"Couldn't find file ~s needed "
|
||||
"for Erlang application '~s'. ~s",
|
||||
[File, App, hint()]),
|
||||
exit_or_halt(Reason, StartFlag);
|
||||
_ ->
|
||||
sleep(10)
|
||||
ok
|
||||
end
|
||||
end, Mods);
|
||||
_ ->
|
||||
@ -131,6 +125,23 @@ check_app_modules(App, StartFlag) ->
|
||||
ok
|
||||
end.
|
||||
|
||||
check_apps() ->
|
||||
spawn(
|
||||
fun() ->
|
||||
Apps = [ejabberd |
|
||||
[App || {App, _, _} <- application:which_applications(),
|
||||
App /= ejabberd]],
|
||||
?DEBUG("Checking consistency of applications: ~s",
|
||||
[misc:join_atoms(Apps, <<", ">>)]),
|
||||
misc:peach(
|
||||
fun(App) ->
|
||||
check_app_modules(App, true)
|
||||
end, Apps),
|
||||
?DEBUG("All applications are intact", []),
|
||||
lists:foreach(fun erlang:garbage_collect/1, processes())
|
||||
end).
|
||||
|
||||
-spec exit_or_halt(iodata(), boolean()) -> no_return().
|
||||
exit_or_halt(Reason, StartFlag) ->
|
||||
?CRITICAL_MSG(Reason, []),
|
||||
if StartFlag ->
|
||||
@ -140,9 +151,6 @@ exit_or_halt(Reason, StartFlag) ->
|
||||
erlang:error(application_start_failed)
|
||||
end.
|
||||
|
||||
sleep(N) ->
|
||||
timer:sleep(p1_rand:uniform(N)).
|
||||
|
||||
get_module_file(App, Mod) ->
|
||||
BaseName = atom_to_list(Mod),
|
||||
case code:lib_dir(App, ebin) of
|
||||
@ -177,3 +185,12 @@ erlang_name(Atom) when is_atom(Atom) ->
|
||||
misc:atom_to_binary(Atom);
|
||||
erlang_name(Bin) when is_binary(Bin) ->
|
||||
Bin.
|
||||
|
||||
format_error({Reason, File}) when is_list(Reason), is_list(File) ->
|
||||
Reason ++ ": " ++ File;
|
||||
format_error(Term) ->
|
||||
io_lib:format("~p", [Term]).
|
||||
|
||||
hint() ->
|
||||
"This usually means that ejabberd or Erlang "
|
||||
"was compiled/installed incorrectly.".
|
||||
|
@ -29,17 +29,13 @@
|
||||
-include("logger.hrl").
|
||||
|
||||
-behaviour(gen_server).
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
%% API
|
||||
-export([start_link/0,
|
||||
parse_api_permissions/1,
|
||||
can_access/2,
|
||||
invalidate/0,
|
||||
opt_type/1,
|
||||
show_current_definitions/0,
|
||||
register_permission_addon/2,
|
||||
unregister_permission_addon/1]).
|
||||
validator/0,
|
||||
show_current_definitions/0]).
|
||||
|
||||
%% gen_server callbacks
|
||||
-export([init/1,
|
||||
@ -51,16 +47,29 @@
|
||||
|
||||
-define(SERVER, ?MODULE).
|
||||
|
||||
-record(state, {
|
||||
definitions = none,
|
||||
fragments_generators = []
|
||||
}).
|
||||
-record(state,
|
||||
{definitions = none :: none | [definition()]}).
|
||||
|
||||
-type state() :: #state{}.
|
||||
-type rule() :: {access, acl:access()} |
|
||||
{acl, all | none | acl:acl_rule()}.
|
||||
-type what() :: all | none | [atom() | {tag, atom()}].
|
||||
-type who() :: rule() | {oauth, {[binary()], [rule()]}}.
|
||||
-type from() :: atom().
|
||||
-type permission() :: {binary(), {[from()], [who()], {what(), what()}}}.
|
||||
-type definition() :: {binary(), {[from()], [who()], [atom()] | all}}.
|
||||
-type caller_info() :: #{caller_module => module(),
|
||||
caller_host => global | binary(),
|
||||
tag => binary() | none,
|
||||
extra_permissions => [definition()],
|
||||
atom() => term()}.
|
||||
|
||||
-export_type([permission/0]).
|
||||
|
||||
%%%===================================================================
|
||||
%%% API
|
||||
%%%===================================================================
|
||||
|
||||
-spec can_access(atom(), map()) -> allow | deny.
|
||||
-spec can_access(atom(), caller_info()) -> allow | deny.
|
||||
can_access(Cmd, CallerInfo) ->
|
||||
gen_server:call(?MODULE, {can_access, Cmd, CallerInfo}).
|
||||
|
||||
@ -68,65 +77,24 @@ can_access(Cmd, CallerInfo) ->
|
||||
invalidate() ->
|
||||
gen_server:cast(?MODULE, invalidate).
|
||||
|
||||
-spec register_permission_addon(atom(), fun()) -> ok.
|
||||
register_permission_addon(Name, Fun) ->
|
||||
gen_server:call(?MODULE, {register_config_fragment_generator, Name, Fun}).
|
||||
|
||||
-spec unregister_permission_addon(atom()) -> ok.
|
||||
unregister_permission_addon(Name) ->
|
||||
gen_server:call(?MODULE, {unregister_config_fragment_generator, Name}).
|
||||
|
||||
-spec show_current_definitions() -> any().
|
||||
-spec show_current_definitions() -> [definition()].
|
||||
show_current_definitions() ->
|
||||
gen_server:call(?MODULE, show_current_definitions).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% Starts the server
|
||||
%%
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec start_link() -> {ok, Pid :: pid()} | ignore | {error, Reason :: term()}.
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
|
||||
|
||||
%%%===================================================================
|
||||
%%% gen_server callbacks
|
||||
%%%===================================================================
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @private
|
||||
%% @doc
|
||||
%% Initializes the server
|
||||
%%
|
||||
%% @spec init(Args) -> {ok, State} |
|
||||
%% {ok, State, Timeout} |
|
||||
%% ignore |
|
||||
%% {stop, Reason}
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec init(Args :: term()) ->
|
||||
{ok, State :: #state{}} | {ok, State :: #state{}, timeout() | hibernate} |
|
||||
{stop, Reason :: term()} | ignore.
|
||||
-spec init([]) -> {ok, state()}.
|
||||
init([]) ->
|
||||
ejabberd_hooks:add(config_reloaded, ?MODULE, invalidate, 90),
|
||||
{ok, #state{}}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @private
|
||||
%% @doc
|
||||
%% Handling call messages
|
||||
%%
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec handle_call(Request :: term(), From :: {pid(), Tag :: term()},
|
||||
State :: #state{}) ->
|
||||
{reply, Reply :: term(), NewState :: #state{}} |
|
||||
{reply, Reply :: term(), NewState :: #state{}, timeout() | hibernate} |
|
||||
{noreply, NewState :: #state{}} |
|
||||
{noreply, NewState :: #state{}, timeout() | hibernate} |
|
||||
{stop, Reason :: term(), Reply :: term(), NewState :: #state{}} |
|
||||
{stop, Reason :: term(), NewState :: #state{}}.
|
||||
-spec handle_call({can_access, atom(), caller_info()} |
|
||||
show_current_definitions | term(),
|
||||
term(), state()) -> {reply, term(), state()}.
|
||||
handle_call({can_access, Cmd, CallerInfo}, _From, State) ->
|
||||
CallerModule = maps:get(caller_module, CallerInfo, none),
|
||||
Host = maps:get(caller_host, CallerInfo, global),
|
||||
@ -134,123 +102,61 @@ handle_call({can_access, Cmd, CallerInfo}, _From, State) ->
|
||||
{State2, Defs0} = get_definitions(State),
|
||||
Defs = maps:get(extra_permissions, CallerInfo, []) ++ Defs0,
|
||||
Res = lists:foldl(
|
||||
fun({Name, _} = Def, none) ->
|
||||
case matches_definition(Def, Cmd, CallerModule, Tag, Host, CallerInfo) of
|
||||
true ->
|
||||
?DEBUG("Command '~p' execution allowed by rule '~s' (CallerInfo=~p)", [Cmd, Name, CallerInfo]),
|
||||
allow;
|
||||
_ ->
|
||||
none
|
||||
end;
|
||||
(_, Val) ->
|
||||
Val
|
||||
end, none, Defs),
|
||||
fun({Name, _} = Def, none) ->
|
||||
case matches_definition(Def, Cmd, CallerModule, Tag, Host, CallerInfo) of
|
||||
true ->
|
||||
?DEBUG("Command '~p' execution allowed by rule "
|
||||
"'~s' (CallerInfo=~p)", [Cmd, Name, CallerInfo]),
|
||||
allow;
|
||||
_ ->
|
||||
none
|
||||
end;
|
||||
(_, Val) ->
|
||||
Val
|
||||
end, none, Defs),
|
||||
Res2 = case Res of
|
||||
allow -> allow;
|
||||
_ ->
|
||||
?DEBUG("Command '~p' execution denied (CallerInfo=~p)", [Cmd, CallerInfo]),
|
||||
?DEBUG("Command '~p' execution denied "
|
||||
"(CallerInfo=~p)", [Cmd, CallerInfo]),
|
||||
deny
|
||||
end,
|
||||
{reply, Res2, State2};
|
||||
handle_call(show_current_definitions, _From, State) ->
|
||||
{State2, Defs} = get_definitions(State),
|
||||
{reply, Defs, State2};
|
||||
handle_call({register_config_fragment_generator, Name, Fun}, _From, #state{fragments_generators = Gens} = State) ->
|
||||
NGens = lists:keystore(Name, 1, Gens, {Name, Fun}),
|
||||
{reply, ok, State#state{fragments_generators = NGens}};
|
||||
handle_call({unregister_config_fragment_generator, Name}, _From, #state{fragments_generators = Gens} = State) ->
|
||||
NGens = lists:keydelete(Name, 1, Gens),
|
||||
{reply, ok, State#state{fragments_generators = NGens}};
|
||||
handle_call(_Request, _From, State) ->
|
||||
{reply, ok, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @private
|
||||
%% @doc
|
||||
%% Handling cast messages
|
||||
%%
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec handle_cast(Request :: term(), State :: #state{}) ->
|
||||
{noreply, NewState :: #state{}} |
|
||||
{noreply, NewState :: #state{}, timeout() | hibernate} |
|
||||
{stop, Reason :: term(), NewState :: #state{}}.
|
||||
-spec handle_cast(invalidate | term(), state()) -> {noreply, state()}.
|
||||
handle_cast(invalidate, State) ->
|
||||
{noreply, State#state{definitions = none}};
|
||||
handle_cast(_Request, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @private
|
||||
%% @doc
|
||||
%% Handling all non call/cast messages
|
||||
%%
|
||||
%% @spec handle_info(Info, State) -> {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, State}
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec handle_info(Info :: timeout() | term(), State :: #state{}) ->
|
||||
{noreply, NewState :: #state{}} |
|
||||
{noreply, NewState :: #state{}, timeout() | hibernate} |
|
||||
{stop, Reason :: term(), NewState :: #state{}}.
|
||||
handle_info(_Info, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @private
|
||||
%% @doc
|
||||
%% This function is called by a gen_server when it is about to
|
||||
%% terminate. It should be the opposite of Module:init/1 and do any
|
||||
%% necessary cleaning up. When it returns, the gen_server terminates
|
||||
%% with Reason. The return value is ignored.
|
||||