feat(events): add new hook && event `client.check_authn_complete`
This commit is contained in:
parent
93d5e77698
commit
e4e53844d5
|
@ -56,21 +56,21 @@ authenticate(Credential) ->
|
||||||
NotSuperUser = #{is_superuser => false},
|
NotSuperUser = #{is_superuser => false},
|
||||||
case pre_hook_authenticate(Credential) of
|
case pre_hook_authenticate(Credential) of
|
||||||
ok ->
|
ok ->
|
||||||
inc_authn_metrics(anonymous),
|
on_authentication_complete(Credential, NotSuperUser, anonymous),
|
||||||
{ok, NotSuperUser};
|
{ok, NotSuperUser};
|
||||||
continue ->
|
continue ->
|
||||||
case run_hooks('client.authenticate', [Credential], ignore) of
|
case run_hooks('client.authenticate', [Credential], ignore) of
|
||||||
ignore ->
|
ignore ->
|
||||||
inc_authn_metrics(anonymous),
|
on_authentication_complete(Credential, NotSuperUser, anonymous),
|
||||||
{ok, NotSuperUser};
|
{ok, NotSuperUser};
|
||||||
ok ->
|
ok ->
|
||||||
inc_authn_metrics(ok),
|
on_authentication_complete(Credential, NotSuperUser, ok),
|
||||||
{ok, NotSuperUser};
|
{ok, NotSuperUser};
|
||||||
{ok, _AuthResult} = OkResult ->
|
{ok, AuthResult} = OkResult ->
|
||||||
inc_authn_metrics(ok),
|
on_authentication_complete(Credential, AuthResult, ok),
|
||||||
OkResult;
|
OkResult;
|
||||||
{ok, _AuthResult, _AuthData} = OkResult ->
|
{ok, AuthResult, _AuthData} = OkResult ->
|
||||||
inc_authn_metrics(ok),
|
on_authentication_complete(Credential, AuthResult, ok),
|
||||||
OkResult;
|
OkResult;
|
||||||
{error, _Reason} = Error ->
|
{error, _Reason} = Error ->
|
||||||
inc_authn_metrics(error),
|
inc_authn_metrics(error),
|
||||||
|
@ -240,3 +240,10 @@ inc_authn_metrics(ok) ->
|
||||||
inc_authn_metrics(anonymous) ->
|
inc_authn_metrics(anonymous) ->
|
||||||
emqx_metrics:inc('authentication.success.anonymous'),
|
emqx_metrics:inc('authentication.success.anonymous'),
|
||||||
emqx_metrics:inc('authentication.success').
|
emqx_metrics:inc('authentication.success').
|
||||||
|
|
||||||
|
on_authentication_complete(Credential, Result, Type) ->
|
||||||
|
emqx_hooks:run(
|
||||||
|
'client.check_authn_complete',
|
||||||
|
[Credential, Result#{is_anonymous => (Type =:= anonymous)}]
|
||||||
|
),
|
||||||
|
inc_authn_metrics(Type).
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
'client.disconnected',
|
'client.disconnected',
|
||||||
'client.authorize',
|
'client.authorize',
|
||||||
'client.check_authz_complete',
|
'client.check_authz_complete',
|
||||||
|
'client.check_authn_complete',
|
||||||
'client.authenticate',
|
'client.authenticate',
|
||||||
'client.subscribe',
|
'client.subscribe',
|
||||||
'client.unsubscribe',
|
'client.unsubscribe',
|
||||||
|
|
|
@ -282,6 +282,17 @@ fields("ctx_check_authz_complete") ->
|
||||||
{"authz_source", sc(binary(), #{desc => ?DESC("event_authz_source")})},
|
{"authz_source", sc(binary(), #{desc => ?DESC("event_authz_source")})},
|
||||||
{"result", sc(binary(), #{desc => ?DESC("event_result")})}
|
{"result", sc(binary(), #{desc => ?DESC("event_result")})}
|
||||||
];
|
];
|
||||||
|
fields("ctx_check_authn_complete") ->
|
||||||
|
Event = 'client.check_authn_complete',
|
||||||
|
[
|
||||||
|
{"event_type", event_type_sc(Event)},
|
||||||
|
{"event", event_sc(Event)},
|
||||||
|
{"clientid", sc(binary(), #{desc => ?DESC("event_clientid")})},
|
||||||
|
{"username", sc(binary(), #{desc => ?DESC("event_username")})},
|
||||||
|
{"peerhost", sc(binary(), #{desc => ?DESC("event_peerhost")})},
|
||||||
|
{"is_anonymous", sc(boolean(), #{desc => ?DESC("event_is_anonymous")})},
|
||||||
|
{"is_superuser", sc(boolean(), #{desc => ?DESC("event_is_superuser")})}
|
||||||
|
];
|
||||||
fields("ctx_bridge_mqtt") ->
|
fields("ctx_bridge_mqtt") ->
|
||||||
Event = '$bridges/mqtt:*',
|
Event = '$bridges/mqtt:*',
|
||||||
EventBin = atom_to_binary(Event),
|
EventBin = atom_to_binary(Event),
|
||||||
|
@ -330,6 +341,7 @@ rule_input_message_context() ->
|
||||||
ref("ctx_disconnected"),
|
ref("ctx_disconnected"),
|
||||||
ref("ctx_connack"),
|
ref("ctx_connack"),
|
||||||
ref("ctx_check_authz_complete"),
|
ref("ctx_check_authz_complete"),
|
||||||
|
ref("ctx_check_authn_complete"),
|
||||||
ref("ctx_bridge_mqtt"),
|
ref("ctx_bridge_mqtt"),
|
||||||
ref("ctx_delivery_dropped"),
|
ref("ctx_delivery_dropped"),
|
||||||
ref("ctx_schema_validation_failed")
|
ref("ctx_schema_validation_failed")
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
on_client_disconnected/4,
|
on_client_disconnected/4,
|
||||||
on_client_connack/4,
|
on_client_connack/4,
|
||||||
on_client_check_authz_complete/6,
|
on_client_check_authz_complete/6,
|
||||||
|
on_client_check_authn_complete/3,
|
||||||
on_session_subscribed/4,
|
on_session_subscribed/4,
|
||||||
on_session_unsubscribed/4,
|
on_session_unsubscribed/4,
|
||||||
on_message_publish/2,
|
on_message_publish/2,
|
||||||
|
@ -182,6 +183,18 @@ on_client_check_authz_complete(
|
||||||
Conf
|
Conf
|
||||||
).
|
).
|
||||||
|
|
||||||
|
on_client_check_authn_complete(ClientInfo, Result, Conf) ->
|
||||||
|
apply_event(
|
||||||
|
'client.check_authn_complete',
|
||||||
|
fun() ->
|
||||||
|
eventmsg_check_authn_complete(
|
||||||
|
ClientInfo,
|
||||||
|
Result
|
||||||
|
)
|
||||||
|
end,
|
||||||
|
Conf
|
||||||
|
).
|
||||||
|
|
||||||
on_client_disconnected(ClientInfo, Reason, ConnInfo, Conf) ->
|
on_client_disconnected(ClientInfo, Reason, ConnInfo, Conf) ->
|
||||||
apply_event(
|
apply_event(
|
||||||
'client.disconnected',
|
'client.disconnected',
|
||||||
|
@ -438,6 +451,27 @@ eventmsg_check_authz_complete(
|
||||||
#{}
|
#{}
|
||||||
).
|
).
|
||||||
|
|
||||||
|
eventmsg_check_authn_complete(
|
||||||
|
_ClientInfo = #{
|
||||||
|
clientid := ClientId,
|
||||||
|
username := Username,
|
||||||
|
peerhost := PeerHost
|
||||||
|
},
|
||||||
|
#{is_anonymous := IsAnonymous} = Result
|
||||||
|
) ->
|
||||||
|
IsSuperuser = maps:get(is_superuser, Result, false),
|
||||||
|
with_basic_columns(
|
||||||
|
'client.check_authn_complete',
|
||||||
|
#{
|
||||||
|
clientid => ClientId,
|
||||||
|
username => Username,
|
||||||
|
peerhost => ntoa(PeerHost),
|
||||||
|
is_anonymous => IsAnonymous,
|
||||||
|
is_superuser => IsSuperuser
|
||||||
|
},
|
||||||
|
#{}
|
||||||
|
).
|
||||||
|
|
||||||
eventmsg_sub_or_unsub(
|
eventmsg_sub_or_unsub(
|
||||||
Event,
|
Event,
|
||||||
_ClientInfo = #{
|
_ClientInfo = #{
|
||||||
|
@ -679,6 +713,7 @@ event_info() ->
|
||||||
event_info_client_disconnected(),
|
event_info_client_disconnected(),
|
||||||
event_info_client_connack(),
|
event_info_client_connack(),
|
||||||
event_info_client_check_authz_complete(),
|
event_info_client_check_authz_complete(),
|
||||||
|
event_info_client_check_authn_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(),
|
||||||
|
@ -770,6 +805,13 @@ event_info_client_check_authz_complete() ->
|
||||||
{<<"client check authz complete">>, <<"授权结果"/utf8>>},
|
{<<"client check authz complete">>, <<"授权结果"/utf8>>},
|
||||||
<<"SELECT * FROM \"$events/client_check_authz_complete\"">>
|
<<"SELECT * FROM \"$events/client_check_authz_complete\"">>
|
||||||
).
|
).
|
||||||
|
event_info_client_check_authn_complete() ->
|
||||||
|
event_info_common(
|
||||||
|
'client.check_authn_complete',
|
||||||
|
{<<"client check authn complete">>, <<"认证结果"/utf8>>},
|
||||||
|
{<<"client check authn complete">>, <<"认证结果"/utf8>>},
|
||||||
|
<<"SELECT * FROM \"$events/client_check_authn_complete\"">>
|
||||||
|
).
|
||||||
event_info_session_subscribed() ->
|
event_info_session_subscribed() ->
|
||||||
event_info_common(
|
event_info_common(
|
||||||
'session.subscribed',
|
'session.subscribed',
|
||||||
|
@ -854,6 +896,13 @@ test_columns('client.check_authz_complete') ->
|
||||||
{<<"action">>, [<<"publish">>, <<"the action of publish or subscribe">>]},
|
{<<"action">>, [<<"publish">>, <<"the action of publish or subscribe">>]},
|
||||||
{<<"result">>, [<<"allow">>, <<"the authz check complete result">>]}
|
{<<"result">>, [<<"allow">>, <<"the authz check complete result">>]}
|
||||||
];
|
];
|
||||||
|
test_columns('client.check_authn_complete') ->
|
||||||
|
[
|
||||||
|
{<<"clientid">>, [<<"c_emqx">>, <<"the clientid if the client">>]},
|
||||||
|
{<<"username">>, [<<"u_emqx">>, <<"the username if the client">>]},
|
||||||
|
{<<"is_superuser">>, [true, <<"Whether this is a superuser">>]},
|
||||||
|
{<<"is_anonymous">>, [false, <<"Whether this is a superuser">>]}
|
||||||
|
];
|
||||||
test_columns('session.unsubscribed') ->
|
test_columns('session.unsubscribed') ->
|
||||||
test_columns('session.subscribed');
|
test_columns('session.subscribed');
|
||||||
test_columns('session.subscribed') ->
|
test_columns('session.subscribed') ->
|
||||||
|
@ -1023,6 +1072,17 @@ columns_with_exam('client.check_authz_complete') ->
|
||||||
{<<"timestamp">>, erlang:system_time(millisecond)},
|
{<<"timestamp">>, erlang:system_time(millisecond)},
|
||||||
{<<"node">>, node()}
|
{<<"node">>, node()}
|
||||||
];
|
];
|
||||||
|
columns_with_exam('client.check_authn_complete') ->
|
||||||
|
[
|
||||||
|
{<<"event">>, 'client.check_authz_complete'},
|
||||||
|
{<<"clientid">>, <<"c_emqx">>},
|
||||||
|
{<<"username">>, <<"u_emqx">>},
|
||||||
|
{<<"peerhost">>, <<"192.168.0.10">>},
|
||||||
|
{<<"is_superuser">>, true},
|
||||||
|
{<<"is_anonymous">>, false},
|
||||||
|
{<<"timestamp">>, erlang:system_time(millisecond)},
|
||||||
|
{<<"node">>, node()}
|
||||||
|
];
|
||||||
columns_with_exam('session.subscribed') ->
|
columns_with_exam('session.subscribed') ->
|
||||||
[columns_example_props(sub_props)] ++ columns_message_sub_unsub('session.subscribed');
|
[columns_example_props(sub_props)] ++ columns_message_sub_unsub('session.subscribed');
|
||||||
columns_with_exam('session.unsubscribed') ->
|
columns_with_exam('session.unsubscribed') ->
|
||||||
|
@ -1124,6 +1184,7 @@ hook_fun('client.connected') -> fun ?MODULE:on_client_connected/3;
|
||||||
hook_fun('client.disconnected') -> fun ?MODULE:on_client_disconnected/4;
|
hook_fun('client.disconnected') -> fun ?MODULE:on_client_disconnected/4;
|
||||||
hook_fun('client.connack') -> fun ?MODULE:on_client_connack/4;
|
hook_fun('client.connack') -> fun ?MODULE:on_client_connack/4;
|
||||||
hook_fun('client.check_authz_complete') -> fun ?MODULE:on_client_check_authz_complete/6;
|
hook_fun('client.check_authz_complete') -> fun ?MODULE:on_client_check_authz_complete/6;
|
||||||
|
hook_fun('client.check_authn_complete') -> fun ?MODULE:on_client_check_authn_complete/3;
|
||||||
hook_fun('session.subscribed') -> fun ?MODULE:on_session_subscribed/4;
|
hook_fun('session.subscribed') -> fun ?MODULE:on_session_subscribed/4;
|
||||||
hook_fun('session.unsubscribed') -> fun ?MODULE:on_session_unsubscribed/4;
|
hook_fun('session.unsubscribed') -> fun ?MODULE:on_session_unsubscribed/4;
|
||||||
hook_fun('message.delivered') -> fun ?MODULE:on_message_delivered/3;
|
hook_fun('message.delivered') -> fun ?MODULE:on_message_delivered/3;
|
||||||
|
@ -1149,6 +1210,7 @@ event_name(<<"$events/client_connected">>) -> 'client.connected';
|
||||||
event_name(<<"$events/client_disconnected">>) -> 'client.disconnected';
|
event_name(<<"$events/client_disconnected">>) -> 'client.disconnected';
|
||||||
event_name(<<"$events/client_connack">>) -> 'client.connack';
|
event_name(<<"$events/client_connack">>) -> 'client.connack';
|
||||||
event_name(<<"$events/client_check_authz_complete">>) -> 'client.check_authz_complete';
|
event_name(<<"$events/client_check_authz_complete">>) -> 'client.check_authz_complete';
|
||||||
|
event_name(<<"$events/client_check_authn_complete">>) -> 'client.check_authn_complete';
|
||||||
event_name(<<"$events/session_subscribed">>) -> 'session.subscribed';
|
event_name(<<"$events/session_subscribed">>) -> 'session.subscribed';
|
||||||
event_name(<<"$events/session_unsubscribed">>) -> 'session.unsubscribed';
|
event_name(<<"$events/session_unsubscribed">>) -> 'session.unsubscribed';
|
||||||
event_name(<<"$events/message_delivered">>) -> 'message.delivered';
|
event_name(<<"$events/message_delivered">>) -> 'message.delivered';
|
||||||
|
@ -1163,6 +1225,7 @@ 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('client.check_authz_complete') -> <<"$events/client_check_authz_complete">>;
|
||||||
|
event_topic('client.check_authn_complete') -> <<"$events/client_check_authn_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">>;
|
||||||
|
|
|
@ -237,6 +237,7 @@ init_per_testcase(t_events, Config) ->
|
||||||
"\"$events/client_disconnected\", "
|
"\"$events/client_disconnected\", "
|
||||||
"\"$events/client_connack\", "
|
"\"$events/client_connack\", "
|
||||||
"\"$events/client_check_authz_complete\", "
|
"\"$events/client_check_authz_complete\", "
|
||||||
|
"\"$events/client_check_authn_complete\", "
|
||||||
"\"$events/session_subscribed\", "
|
"\"$events/session_subscribed\", "
|
||||||
"\"$events/session_unsubscribed\", "
|
"\"$events/session_unsubscribed\", "
|
||||||
"\"$events/message_acked\", "
|
"\"$events/message_acked\", "
|
||||||
|
@ -1084,6 +1085,7 @@ client_connected(Client, Client2) ->
|
||||||
{ok, _} = emqtt:connect(Client2),
|
{ok, _} = emqtt:connect(Client2),
|
||||||
verify_event('client.connack'),
|
verify_event('client.connack'),
|
||||||
verify_event('client.connected'),
|
verify_event('client.connected'),
|
||||||
|
verify_event('client.check_authn_complete'),
|
||||||
ok.
|
ok.
|
||||||
client_disconnected(Client, Client2) ->
|
client_disconnected(Client, Client2) ->
|
||||||
ok = emqtt:disconnect(Client, 0, #{'User-Property' => {<<"reason">>, <<"normal">>}}),
|
ok = emqtt:disconnect(Client, 0, #{'User-Property' => {<<"reason">>, <<"normal">>}}),
|
||||||
|
@ -4196,7 +4198,18 @@ verify_event_fields('client.check_authz_complete', Fields) ->
|
||||||
])
|
])
|
||||||
),
|
),
|
||||||
?assert(lists:member(ClientId, [<<"c_event">>, <<"c_event2">>])),
|
?assert(lists:member(ClientId, [<<"c_event">>, <<"c_event2">>])),
|
||||||
?assert(lists:member(Username, [<<"u_event">>, <<"u_event2">>])).
|
?assert(lists:member(Username, [<<"u_event">>, <<"u_event2">>]));
|
||||||
|
verify_event_fields('client.check_authn_complete', Fields) ->
|
||||||
|
#{
|
||||||
|
clientid := ClientId,
|
||||||
|
username := Username,
|
||||||
|
is_anonymous := IsAnonymous,
|
||||||
|
is_superuser := IsSuperuser
|
||||||
|
} = Fields,
|
||||||
|
?assert(lists:member(ClientId, [<<"c_event">>, <<"c_event2">>])),
|
||||||
|
?assert(lists:member(Username, [<<"u_event">>, <<"u_event2">>])),
|
||||||
|
?assert(erlang:is_boolean(IsAnonymous)),
|
||||||
|
?assert(erlang:is_boolean(IsSuperuser)).
|
||||||
|
|
||||||
verify_peername(PeerName) ->
|
verify_peername(PeerName) ->
|
||||||
case string:split(PeerName, ":") of
|
case string:split(PeerName, ":") of
|
||||||
|
|
|
@ -265,6 +265,21 @@ t_rule_test_smoke(_Config) ->
|
||||||
<<"sql">> => <<"SELECT\n *\nFROM\n \"t/#\"">>
|
<<"sql">> => <<"SELECT\n *\nFROM\n \"t/#\"">>
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
#{
|
||||||
|
expected => #{code => 412},
|
||||||
|
input =>
|
||||||
|
#{
|
||||||
|
<<"context">> =>
|
||||||
|
#{
|
||||||
|
<<"clientid">> => <<"c_emqx">>,
|
||||||
|
<<"event_type">> => <<"client_check_authn_complete">>,
|
||||||
|
<<"is_superuser">> => true,
|
||||||
|
<<"is_anonymous">> => false,
|
||||||
|
<<"username">> => <<"u_emqx">>
|
||||||
|
},
|
||||||
|
<<"sql">> => <<"SELECT\n *\nFROM\n \"t/#\"">>
|
||||||
|
}
|
||||||
|
},
|
||||||
#{
|
#{
|
||||||
expected => #{code => 412},
|
expected => #{code => 412},
|
||||||
input =>
|
input =>
|
||||||
|
|
|
@ -195,6 +195,28 @@ t_ctx_check_authz_complete(_) ->
|
||||||
|
|
||||||
do_test(SQL, Context, Expected).
|
do_test(SQL, Context, Expected).
|
||||||
|
|
||||||
|
t_ctx_check_authn_complete(_) ->
|
||||||
|
SQL =
|
||||||
|
<<
|
||||||
|
"SELECT clientid, username, is_superuser, is_anonymous\n"
|
||||||
|
"FROM \"$events/client_check_authn_complete\""
|
||||||
|
>>,
|
||||||
|
|
||||||
|
Context =
|
||||||
|
#{
|
||||||
|
clientid => <<"c_emqx">>,
|
||||||
|
event_type => client_check_authn_complete,
|
||||||
|
is_superuser => true,
|
||||||
|
is_anonymous => false
|
||||||
|
},
|
||||||
|
Expected = check_result(
|
||||||
|
[clientid, username, is_superuser, is_anonymous],
|
||||||
|
[],
|
||||||
|
Context
|
||||||
|
),
|
||||||
|
|
||||||
|
do_test(SQL, Context, Expected).
|
||||||
|
|
||||||
t_ctx_delivery_dropped(_) ->
|
t_ctx_delivery_dropped(_) ->
|
||||||
SQL =
|
SQL =
|
||||||
<<"SELECT from_clientid, from_username, reason, topic, qos FROM \"$events/delivery_dropped\"">>,
|
<<"SELECT from_clientid, from_username, reason, topic, qos FROM \"$events/delivery_dropped\"">>,
|
||||||
|
|
|
@ -390,4 +390,10 @@ event_ctx_disconnected_reason.desc:
|
||||||
event_ctx_disconnected_reason.label:
|
event_ctx_disconnected_reason.label:
|
||||||
"""Disconnect Reason"""
|
"""Disconnect Reason"""
|
||||||
|
|
||||||
|
event_is_anonymous.desc:
|
||||||
|
"""True if this user is anonymous."""
|
||||||
|
|
||||||
|
event_is_superuser.desc:
|
||||||
|
"""True if this is a super user."""
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue