feat: add rule event 'client.check_authz_complete'
This commit is contained in:
parent
65164fb046
commit
4791c64b73
|
@ -60,6 +60,8 @@ check_authorization_cache(ClientInfo, PubSub, Topic) ->
|
||||||
emqx_authz_cache:put_authz_cache(PubSub, Topic, AuthzResult),
|
emqx_authz_cache:put_authz_cache(PubSub, Topic, AuthzResult),
|
||||||
AuthzResult;
|
AuthzResult;
|
||||||
AuthzResult ->
|
AuthzResult ->
|
||||||
|
emqx:run_hook('client.check_authz_complete',
|
||||||
|
[ClientInfo, PubSub, Topic, AuthzResult, cache]),
|
||||||
inc_acl_metrics(cache_hit),
|
inc_acl_metrics(cache_hit),
|
||||||
AuthzResult
|
AuthzResult
|
||||||
end.
|
end.
|
||||||
|
|
|
@ -283,14 +283,18 @@ authorize(#{username := Username,
|
||||||
peerhost := IpAddress
|
peerhost := IpAddress
|
||||||
} = Client, PubSub, Topic, DefaultResult, Sources) ->
|
} = Client, PubSub, Topic, DefaultResult, Sources) ->
|
||||||
case do_authorize(Client, PubSub, Topic, Sources) of
|
case do_authorize(Client, PubSub, Topic, Sources) of
|
||||||
{matched, allow} ->
|
{{matched, allow}, AuthzSource}->
|
||||||
|
emqx:run_hook('client.check_authz_complete',
|
||||||
|
[Client, PubSub, Topic, allow, AuthzSource]),
|
||||||
?SLOG(info, #{msg => "authorization_permission_allowed",
|
?SLOG(info, #{msg => "authorization_permission_allowed",
|
||||||
username => Username,
|
username => Username,
|
||||||
ipaddr => IpAddress,
|
ipaddr => IpAddress,
|
||||||
topic => Topic}),
|
topic => Topic}),
|
||||||
emqx_metrics:inc(?METRIC_ALLOW),
|
emqx_metrics:inc(?METRIC_ALLOW),
|
||||||
{stop, allow};
|
{stop, allow};
|
||||||
{matched, deny} ->
|
{{matched, deny}, AuthzSource}->
|
||||||
|
emqx:run_hook('client.check_authz_complete',
|
||||||
|
[Client, PubSub, Topic, deny, AuthzSource]),
|
||||||
?SLOG(info, #{msg => "authorization_permission_denied",
|
?SLOG(info, #{msg => "authorization_permission_denied",
|
||||||
username => Username,
|
username => Username,
|
||||||
ipaddr => IpAddress,
|
ipaddr => IpAddress,
|
||||||
|
@ -298,6 +302,8 @@ authorize(#{username := Username,
|
||||||
emqx_metrics:inc(?METRIC_DENY),
|
emqx_metrics:inc(?METRIC_DENY),
|
||||||
{stop, deny};
|
{stop, deny};
|
||||||
nomatch ->
|
nomatch ->
|
||||||
|
emqx:run_hook('client.check_authz_complete',
|
||||||
|
[Client, PubSub, Topic, DefaultResult, default]),
|
||||||
?SLOG(info, #{msg => "authorization_failed_nomatch",
|
?SLOG(info, #{msg => "authorization_failed_nomatch",
|
||||||
username => Username,
|
username => Username,
|
||||||
ipaddr => IpAddress,
|
ipaddr => IpAddress,
|
||||||
|
@ -316,7 +322,7 @@ do_authorize(Client, PubSub, Topic,
|
||||||
Module = authz_module(Type),
|
Module = authz_module(Type),
|
||||||
case Module:authorize(Client, PubSub, Topic, Connector) of
|
case Module:authorize(Client, PubSub, Topic, Connector) of
|
||||||
nomatch -> do_authorize(Client, PubSub, Topic, Tail);
|
nomatch -> do_authorize(Client, PubSub, Topic, Tail);
|
||||||
Matched -> Matched
|
Matched -> {Matched, Type}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
-export([ on_client_connected/3
|
-export([ on_client_connected/3
|
||||||
, on_client_disconnected/4
|
, on_client_disconnected/4
|
||||||
, on_client_connack/4
|
, on_client_connack/4
|
||||||
|
, on_client_check_authz_complete/6
|
||||||
, on_session_subscribed/4
|
, on_session_subscribed/4
|
||||||
, on_session_unsubscribed/4
|
, on_session_unsubscribed/4
|
||||||
, on_message_publish/2
|
, on_message_publish/2
|
||||||
|
@ -62,6 +63,7 @@ event_names() ->
|
||||||
[ 'client.connected'
|
[ 'client.connected'
|
||||||
, 'client.disconnected'
|
, 'client.disconnected'
|
||||||
, 'client.connack'
|
, 'client.connack'
|
||||||
|
, 'client.check_authz_complete'
|
||||||
, 'session.subscribed'
|
, 'session.subscribed'
|
||||||
, 'session.unsubscribed'
|
, 'session.unsubscribed'
|
||||||
, 'message.publish'
|
, 'message.publish'
|
||||||
|
@ -114,6 +116,14 @@ on_client_connack(ConnInfo, Reason, _, Env) ->
|
||||||
apply_event('client.connack',
|
apply_event('client.connack',
|
||||||
fun() -> eventmsg_connack(ConnInfo, Reason) end, Env).
|
fun() -> eventmsg_connack(ConnInfo, Reason) end, Env).
|
||||||
|
|
||||||
|
on_client_check_authz_complete(ClientInfo, PubSub, Topic, Result, AuthzSource, Env) ->
|
||||||
|
apply_event('client.check_authz_complete',
|
||||||
|
fun() -> eventmsg_check_authz_complete(ClientInfo,
|
||||||
|
PubSub,
|
||||||
|
Topic,
|
||||||
|
Result,
|
||||||
|
AuthzSource) end, Env).
|
||||||
|
|
||||||
on_client_disconnected(ClientInfo, Reason, ConnInfo, Env) ->
|
on_client_disconnected(ClientInfo, Reason, ConnInfo, Env) ->
|
||||||
apply_event('client.disconnected',
|
apply_event('client.disconnected',
|
||||||
fun() -> eventmsg_disconnected(ClientInfo, ConnInfo, Reason) end, Env).
|
fun() -> eventmsg_disconnected(ClientInfo, ConnInfo, Reason) end, Env).
|
||||||
|
@ -269,6 +279,21 @@ eventmsg_connack(_ConnInfo = #{
|
||||||
conn_props => printable_maps(ConnProps)
|
conn_props => printable_maps(ConnProps)
|
||||||
}).
|
}).
|
||||||
|
|
||||||
|
eventmsg_check_authz_complete(_ClientInfo = #{
|
||||||
|
clientid := ClientId,
|
||||||
|
username := Username,
|
||||||
|
peerhost := PeerHost
|
||||||
|
}, PubSub, Topic, Result, AuthzSource) ->
|
||||||
|
with_basic_columns('client.check_authz_complete',
|
||||||
|
#{clientid => ClientId,
|
||||||
|
username => Username,
|
||||||
|
peerhost => ntoa(PeerHost),
|
||||||
|
topic => Topic,
|
||||||
|
action => PubSub,
|
||||||
|
authz_source => AuthzSource,
|
||||||
|
result => Result
|
||||||
|
}).
|
||||||
|
|
||||||
eventmsg_sub_or_unsub(Event, _ClientInfo = #{
|
eventmsg_sub_or_unsub(Event, _ClientInfo = #{
|
||||||
clientid := ClientId,
|
clientid := ClientId,
|
||||||
username := Username,
|
username := Username,
|
||||||
|
@ -413,6 +438,7 @@ event_info() ->
|
||||||
, event_info_client_connected()
|
, event_info_client_connected()
|
||||||
, event_info_client_disconnected()
|
, event_info_client_disconnected()
|
||||||
, event_info_client_connack()
|
, event_info_client_connack()
|
||||||
|
, event_info_client_check_authz_complete()
|
||||||
, event_info_session_subscribed()
|
, event_info_session_subscribed()
|
||||||
, event_info_session_unsubscribed()
|
, event_info_session_unsubscribed()
|
||||||
, event_info_delivery_dropped()
|
, event_info_delivery_dropped()
|
||||||
|
@ -477,6 +503,13 @@ event_info_client_connack() ->
|
||||||
{<<"client connack">>, <<"连接确认"/utf8>>},
|
{<<"client connack">>, <<"连接确认"/utf8>>},
|
||||||
<<"SELECT * FROM \"$events/client_connack\"">>
|
<<"SELECT * FROM \"$events/client_connack\"">>
|
||||||
).
|
).
|
||||||
|
event_info_client_check_authz_complete() ->
|
||||||
|
event_info_common(
|
||||||
|
'client.check_authz_complete',
|
||||||
|
{<<"client check authz complete">>, <<"鉴权结果"/utf8>>},
|
||||||
|
{<<"client check authz complete">>, <<"鉴权结果"/utf8>>},
|
||||||
|
<<"SELECT * FROM \"$events/client_check_authz_complete\"">>
|
||||||
|
).
|
||||||
event_info_session_subscribed() ->
|
event_info_session_subscribed() ->
|
||||||
event_info_common(
|
event_info_common(
|
||||||
'session.subscribed',
|
'session.subscribed',
|
||||||
|
@ -547,6 +580,13 @@ test_columns('client.connack') ->
|
||||||
, {<<"username">>, <<"u_emqx">>}
|
, {<<"username">>, <<"u_emqx">>}
|
||||||
, {<<"reason_code">>, <<"sucess">>}
|
, {<<"reason_code">>, <<"sucess">>}
|
||||||
];
|
];
|
||||||
|
test_columns('client.check_authz_complete') ->
|
||||||
|
[ {<<"clientid">>, <<"c_emqx">>}
|
||||||
|
, {<<"username">>, <<"u_emqx">>}
|
||||||
|
, {<<"topic">>, <<"t/1">>}
|
||||||
|
, {<<"action">>, <<"publish">>}
|
||||||
|
, {<<"result">>, <<"allow">>}
|
||||||
|
];
|
||||||
test_columns('session.unsubscribed') ->
|
test_columns('session.unsubscribed') ->
|
||||||
test_columns('session.subscribed');
|
test_columns('session.subscribed');
|
||||||
test_columns('session.subscribed') ->
|
test_columns('session.subscribed') ->
|
||||||
|
@ -664,6 +704,18 @@ columns_with_exam('client.connack') ->
|
||||||
, {<<"timestamp">>, erlang:system_time(millisecond)}
|
, {<<"timestamp">>, erlang:system_time(millisecond)}
|
||||||
, {<<"node">>, node()}
|
, {<<"node">>, node()}
|
||||||
];
|
];
|
||||||
|
columns_with_exam('client.check_authz_complete') ->
|
||||||
|
[ {<<"event">>, 'client.check_authz_complete'}
|
||||||
|
, {<<"clientid">>, <<"c_emqx">>}
|
||||||
|
, {<<"username">>, <<"u_emqx">>}
|
||||||
|
, {<<"peerhost">>, <<"192.168.0.10">>}
|
||||||
|
, {<<"topic">>, <<"t/a">>}
|
||||||
|
, {<<"action">>, <<"publish">>}
|
||||||
|
, {<<"authz_source">>, <<"cache">>}
|
||||||
|
, {<<"result">>, <<"allow">>}
|
||||||
|
, {<<"timestamp">>, erlang:system_time(millisecond)}
|
||||||
|
, {<<"node">>, node()}
|
||||||
|
];
|
||||||
columns_with_exam('session.subscribed') ->
|
columns_with_exam('session.subscribed') ->
|
||||||
[ columns_example_props(sub_props)
|
[ columns_example_props(sub_props)
|
||||||
] ++ columns_message_sub_unsub('session.subscribed');
|
] ++ columns_message_sub_unsub('session.subscribed');
|
||||||
|
@ -775,6 +827,8 @@ ntoa(IpAddr) ->
|
||||||
event_name(<<"$events/client_connected", _/binary>>) -> 'client.connected';
|
event_name(<<"$events/client_connected", _/binary>>) -> 'client.connected';
|
||||||
event_name(<<"$events/client_disconnected", _/binary>>) -> 'client.disconnected';
|
event_name(<<"$events/client_disconnected", _/binary>>) -> 'client.disconnected';
|
||||||
event_name(<<"$events/client_connack", _/binary>>) -> 'client.connack';
|
event_name(<<"$events/client_connack", _/binary>>) -> 'client.connack';
|
||||||
|
event_name(<<"$events/client_check_authz_complete", _/binary>>) ->
|
||||||
|
'client.check_authz_complete';
|
||||||
event_name(<<"$events/session_subscribed", _/binary>>) -> 'session.subscribed';
|
event_name(<<"$events/session_subscribed", _/binary>>) -> 'session.subscribed';
|
||||||
event_name(<<"$events/session_unsubscribed", _/binary>>) ->
|
event_name(<<"$events/session_unsubscribed", _/binary>>) ->
|
||||||
'session.unsubscribed';
|
'session.unsubscribed';
|
||||||
|
@ -788,6 +842,8 @@ event_name(_) -> 'message.publish'.
|
||||||
event_topic('client.connected') -> <<"$events/client_connected">>;
|
event_topic('client.connected') -> <<"$events/client_connected">>;
|
||||||
event_topic('client.disconnected') -> <<"$events/client_disconnected">>;
|
event_topic('client.disconnected') -> <<"$events/client_disconnected">>;
|
||||||
event_topic('client.connack') -> <<"$events/client_connack">>;
|
event_topic('client.connack') -> <<"$events/client_connack">>;
|
||||||
|
event_topic('client.check_authz_complete') ->
|
||||||
|
<<"$events/client_check_authz_complete">>;
|
||||||
event_topic('session.subscribed') -> <<"$events/session_subscribed">>;
|
event_topic('session.subscribed') -> <<"$events/session_subscribed">>;
|
||||||
event_topic('session.unsubscribed') -> <<"$events/session_unsubscribed">>;
|
event_topic('session.unsubscribed') -> <<"$events/session_unsubscribed">>;
|
||||||
event_topic('message.delivered') -> <<"$events/message_delivered">>;
|
event_topic('message.delivered') -> <<"$events/message_delivered">>;
|
||||||
|
|
|
@ -105,13 +105,24 @@ groups() ->
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
application:load(emqx_conf),
|
application:load(emqx_conf),
|
||||||
ok = emqx_common_test_helpers:start_apps([emqx_conf, emqx_rule_engine]),
|
ok = emqx_common_test_helpers:start_apps(
|
||||||
|
[emqx_conf, emqx_rule_engine, emqx_authz],
|
||||||
|
fun set_special_configs/1),
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
end_per_suite(_Config) ->
|
end_per_suite(_Config) ->
|
||||||
emqx_common_test_helpers:stop_apps([emqx_conf, emqx_rule_engine]),
|
emqx_common_test_helpers:stop_apps([emqx_conf, emqx_rule_engine]),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
|
set_special_configs(emqx_authz) ->
|
||||||
|
{ok, _} = emqx:update_config(
|
||||||
|
[authorization],
|
||||||
|
#{<<"no_match">> => atom_to_binary(allow),
|
||||||
|
<<"cache">> => #{<<"enable">> => atom_to_binary(true)},
|
||||||
|
<<"sources">> => []}),
|
||||||
|
ok;
|
||||||
|
set_special_configs(_) ->
|
||||||
|
ok.
|
||||||
on_resource_create(_id, _) -> #{}.
|
on_resource_create(_id, _) -> #{}.
|
||||||
on_resource_destroy(_id, _) -> ok.
|
on_resource_destroy(_id, _) -> ok.
|
||||||
on_get_resource_status(_id, _) -> #{}.
|
on_get_resource_status(_id, _) -> #{}.
|
||||||
|
@ -140,6 +151,7 @@ init_per_testcase(t_events, Config) ->
|
||||||
SQL = "SELECT * FROM \"$events/client_connected\", "
|
SQL = "SELECT * FROM \"$events/client_connected\", "
|
||||||
"\"$events/client_disconnected\", "
|
"\"$events/client_disconnected\", "
|
||||||
"\"$events/client_connack\", "
|
"\"$events/client_connack\", "
|
||||||
|
"\"$events/client_check_authz_complete\", "
|
||||||
"\"$events/session_subscribed\", "
|
"\"$events/session_subscribed\", "
|
||||||
"\"$events/session_unsubscribed\", "
|
"\"$events/session_unsubscribed\", "
|
||||||
"\"$events/message_acked\", "
|
"\"$events/message_acked\", "
|
||||||
|
@ -361,6 +373,7 @@ client_disconnected(Client, Client2) ->
|
||||||
session_subscribed(Client2) ->
|
session_subscribed(Client2) ->
|
||||||
{ok, _, _} = emqtt:subscribe(Client2, #{'User-Property' => {<<"topic_name">>, <<"t1">>}}, <<"t1">>, 1),
|
{ok, _, _} = emqtt:subscribe(Client2, #{'User-Property' => {<<"topic_name">>, <<"t1">>}}, <<"t1">>, 1),
|
||||||
verify_event('session.subscribed'),
|
verify_event('session.subscribed'),
|
||||||
|
verify_event('client.check_authz_complete'),
|
||||||
ok.
|
ok.
|
||||||
session_unsubscribed(Client2) ->
|
session_unsubscribed(Client2) ->
|
||||||
{ok, _, _} = emqtt:unsubscribe(Client2, #{'User-Property' => {<<"topic_name">>, <<"t1">>}}, <<"t1">>),
|
{ok, _, _} = emqtt:unsubscribe(Client2, #{'User-Property' => {<<"topic_name">>, <<"t1">>}}, <<"t1">>),
|
||||||
|
@ -1671,7 +1684,25 @@ verify_event_fields('client.connack', Fields) ->
|
||||||
?assertMatch(#{'Session-Expiry-Interval' := 60}, Properties),
|
?assertMatch(#{'Session-Expiry-Interval' := 60}, Properties),
|
||||||
?assert(0 =< TimestampElapse andalso TimestampElapse =< 60*1000),
|
?assert(0 =< TimestampElapse andalso TimestampElapse =< 60*1000),
|
||||||
?assert(0 =< RcvdAtElapse andalso RcvdAtElapse =< 60*1000),
|
?assert(0 =< RcvdAtElapse andalso RcvdAtElapse =< 60*1000),
|
||||||
?assert(EventAt =< Timestamp).
|
?assert(EventAt =< Timestamp);
|
||||||
|
|
||||||
|
verify_event_fields('client.check_authz_complete', Fields) ->
|
||||||
|
#{clientid := ClientId,
|
||||||
|
action := Action,
|
||||||
|
result := Result,
|
||||||
|
topic := Topic,
|
||||||
|
authz_source := AuthzSource,
|
||||||
|
username := Username
|
||||||
|
} = Fields,
|
||||||
|
?assertEqual(<<"t1">>, Topic),
|
||||||
|
?assert(lists:member(Action, [subscribe, publish])),
|
||||||
|
?assert(lists:member(Result, [allow, deny])),
|
||||||
|
?assert(lists:member(AuthzSource, [cache, default, file,
|
||||||
|
http, mongodb, mysql, redis,
|
||||||
|
postgresql, built_in_database])),
|
||||||
|
?assert(lists:member(ClientId, [<<"c_event">>, <<"c_event2">>])),
|
||||||
|
?assert(lists:member(Username, [<<"u_event">>, <<"u_event2">>])).
|
||||||
|
|
||||||
|
|
||||||
verify_peername(PeerName) ->
|
verify_peername(PeerName) ->
|
||||||
case string:split(PeerName, ":") of
|
case string:split(PeerName, ":") of
|
||||||
|
|
Loading…
Reference in New Issue