Merge pull request #13177 from lafirest/sync-authn-com

feat: sync `client.check_authn_complete` to release-57
This commit is contained in:
lafirest 2024-06-05 19:51:10 +08:00 committed by GitHub
commit d79193fde4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 190 additions and 12 deletions

View File

@ -56,31 +56,31 @@ authenticate(Credential) ->
NotSuperUser = #{is_superuser => false},
case pre_hook_authenticate(Credential) of
ok ->
inc_authn_metrics(anonymous),
on_authentication_complete(Credential, NotSuperUser, anonymous),
{ok, NotSuperUser};
continue ->
case run_hooks('client.authenticate', [Credential], ignore) of
ignore ->
inc_authn_metrics(anonymous),
on_authentication_complete(Credential, NotSuperUser, anonymous),
{ok, NotSuperUser};
ok ->
inc_authn_metrics(ok),
on_authentication_complete(Credential, NotSuperUser, ok),
{ok, NotSuperUser};
{ok, _AuthResult} = OkResult ->
inc_authn_metrics(ok),
{ok, AuthResult} = OkResult ->
on_authentication_complete(Credential, AuthResult, ok),
OkResult;
{ok, _AuthResult, _AuthData} = OkResult ->
inc_authn_metrics(ok),
{ok, AuthResult, _AuthData} = OkResult ->
on_authentication_complete(Credential, AuthResult, ok),
OkResult;
{error, _Reason} = Error ->
inc_authn_metrics(error),
{error, Reason} = Error ->
on_authentication_complete(Credential, Reason, error),
Error;
%% {continue, AuthCache} | {continue, AuthData, AuthCache}
Other ->
Other
end;
{error, _Reason} = Error ->
inc_authn_metrics(error),
{error, Reason} = Error ->
on_authentication_complete(Credential, Reason, error),
Error
end.
@ -240,3 +240,27 @@ inc_authn_metrics(ok) ->
inc_authn_metrics(anonymous) ->
emqx_metrics:inc('authentication.success.anonymous'),
emqx_metrics:inc('authentication.success').
on_authentication_complete(Credential, Reason, error) ->
emqx_hooks:run(
'client.check_authn_complete',
[
Credential,
#{
reason_code => Reason
}
]
),
inc_authn_metrics(error);
on_authentication_complete(Credential, Result, Type) ->
emqx_hooks:run(
'client.check_authn_complete',
[
Credential,
Result#{
reason_code => success,
is_anonymous => (Type =:= anonymous)
}
]
),
inc_authn_metrics(Type).

View File

@ -44,6 +44,7 @@
'client.disconnected',
'client.authorize',
'client.check_authz_complete',
'client.check_authn_complete',
'client.authenticate',
'client.subscribe',
'client.unsubscribe',

View File

@ -282,6 +282,18 @@ fields("ctx_check_authz_complete") ->
{"authz_source", sc(binary(), #{desc => ?DESC("event_authz_source")})},
{"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")})},
{"reason_code", sc(binary(), #{desc => ?DESC("event_ctx_authn_reason_code")})},
{"peername", sc(binary(), #{desc => ?DESC("event_peername")})},
{"is_anonymous", sc(boolean(), #{desc => ?DESC("event_is_anonymous"), required => false})},
{"is_superuser", sc(boolean(), #{desc => ?DESC("event_is_superuser"), required => false})}
];
fields("ctx_bridge_mqtt") ->
Event = '$bridges/mqtt:*',
EventBin = atom_to_binary(Event),
@ -330,6 +342,7 @@ rule_input_message_context() ->
ref("ctx_disconnected"),
ref("ctx_connack"),
ref("ctx_check_authz_complete"),
ref("ctx_check_authn_complete"),
ref("ctx_bridge_mqtt"),
ref("ctx_delivery_dropped"),
ref("ctx_schema_validation_failed")

View File

@ -40,6 +40,7 @@
on_client_disconnected/4,
on_client_connack/4,
on_client_check_authz_complete/6,
on_client_check_authn_complete/3,
on_session_subscribed/4,
on_session_unsubscribed/4,
on_message_publish/2,
@ -182,6 +183,18 @@ on_client_check_authz_complete(
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) ->
apply_event(
'client.disconnected',
@ -438,6 +451,35 @@ eventmsg_check_authz_complete(
#{}
).
eventmsg_check_authn_complete(
_ClientInfo = #{
clientid := ClientId,
username := Username,
peerhost := PeerHost,
peerport := PeerPort
},
Result
) ->
#{
reason_code := Reason,
is_superuser := IsSuperuser,
is_anonymous := IsAnonymous
} = maps:merge(
#{is_anonymous => false, is_superuser => false}, Result
),
with_basic_columns(
'client.check_authn_complete',
#{
clientid => ClientId,
username => Username,
peername => ntoa({PeerHost, PeerPort}),
reason_code => force_to_bin(Reason),
is_anonymous => IsAnonymous,
is_superuser => IsSuperuser
},
#{}
).
eventmsg_sub_or_unsub(
Event,
_ClientInfo = #{
@ -679,6 +721,7 @@ event_info() ->
event_info_client_disconnected(),
event_info_client_connack(),
event_info_client_check_authz_complete(),
event_info_client_check_authn_complete(),
event_info_session_subscribed(),
event_info_session_unsubscribed(),
event_info_delivery_dropped(),
@ -770,6 +813,13 @@ event_info_client_check_authz_complete() ->
{<<"client check authz complete">>, <<"授权结果"/utf8>>},
<<"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_common(
'session.subscribed',
@ -854,6 +904,14 @@ test_columns('client.check_authz_complete') ->
{<<"action">>, [<<"publish">>, <<"the action of publish or subscribe">>]},
{<<"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">>]},
{<<"reason_code">>, [<<"sucess">>, <<"the reason code">>]},
{<<"is_superuser">>, [true, <<"Whether this is a superuser">>]},
{<<"is_anonymous">>, [false, <<"Whether this is a superuser">>]}
];
test_columns('session.unsubscribed') ->
test_columns('session.subscribed');
test_columns('session.subscribed') ->
@ -1023,6 +1081,18 @@ columns_with_exam('client.check_authz_complete') ->
{<<"timestamp">>, erlang:system_time(millisecond)},
{<<"node">>, node()}
];
columns_with_exam('client.check_authn_complete') ->
[
{<<"event">>, 'client.check_authz_complete'},
{<<"clientid">>, <<"c_emqx">>},
{<<"username">>, <<"u_emqx">>},
{<<"peername">>, <<"192.168.0.10:56431">>},
{<<"reason_code">>, <<"sucess">>},
{<<"is_superuser">>, true},
{<<"is_anonymous">>, false},
{<<"timestamp">>, erlang:system_time(millisecond)},
{<<"node">>, node()}
];
columns_with_exam('session.subscribed') ->
[columns_example_props(sub_props)] ++ columns_message_sub_unsub('session.subscribed');
columns_with_exam('session.unsubscribed') ->
@ -1124,6 +1194,7 @@ hook_fun('client.connected') -> fun ?MODULE:on_client_connected/3;
hook_fun('client.disconnected') -> fun ?MODULE:on_client_disconnected/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_authn_complete') -> fun ?MODULE:on_client_check_authn_complete/3;
hook_fun('session.subscribed') -> fun ?MODULE:on_session_subscribed/4;
hook_fun('session.unsubscribed') -> fun ?MODULE:on_session_unsubscribed/4;
hook_fun('message.delivered') -> fun ?MODULE:on_message_delivered/3;
@ -1139,6 +1210,11 @@ reason({shutdown, Reason}) when is_atom(Reason) -> Reason;
reason({Error, _}) when is_atom(Error) -> Error;
reason(_) -> internal_error.
force_to_bin(Bin) when is_binary(Bin) ->
Bin;
force_to_bin(Term) ->
emqx_utils_conv:bin(io_lib:format("~p", [Term])).
ntoa(undefined) ->
undefined;
ntoa(IpOrIpPort) ->
@ -1149,6 +1225,7 @@ event_name(<<"$events/client_connected">>) -> 'client.connected';
event_name(<<"$events/client_disconnected">>) -> 'client.disconnected';
event_name(<<"$events/client_connack">>) -> 'client.connack';
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_unsubscribed">>) -> 'session.unsubscribed';
event_name(<<"$events/message_delivered">>) -> 'message.delivered';
@ -1163,6 +1240,7 @@ event_topic('client.connected') -> <<"$events/client_connected">>;
event_topic('client.disconnected') -> <<"$events/client_disconnected">>;
event_topic('client.connack') -> <<"$events/client_connack">>;
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.unsubscribed') -> <<"$events/session_unsubscribed">>;
event_topic('message.delivered') -> <<"$events/message_delivered">>;

View File

@ -237,6 +237,7 @@ init_per_testcase(t_events, Config) ->
"\"$events/client_disconnected\", "
"\"$events/client_connack\", "
"\"$events/client_check_authz_complete\", "
"\"$events/client_check_authn_complete\", "
"\"$events/session_subscribed\", "
"\"$events/session_unsubscribed\", "
"\"$events/message_acked\", "
@ -1084,6 +1085,7 @@ client_connected(Client, Client2) ->
{ok, _} = emqtt:connect(Client2),
verify_event('client.connack'),
verify_event('client.connected'),
verify_event('client.check_authn_complete'),
ok.
client_disconnected(Client, Client2) ->
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(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) ->
case string:split(PeerName, ":") of

View File

@ -265,6 +265,22 @@ t_rule_test_smoke(_Config) ->
<<"sql">> => <<"SELECT\n *\nFROM\n \"t/#\"">>
}
},
#{
expected => #{code => 412},
input =>
#{
<<"context">> =>
#{
<<"clientid">> => <<"c_emqx">>,
<<"event_type">> => <<"client_check_authn_complete">>,
<<"reason_code">> => <<"sucess">>,
<<"is_superuser">> => true,
<<"is_anonymous">> => false,
<<"username">> => <<"u_emqx">>
},
<<"sql">> => <<"SELECT\n *\nFROM\n \"t/#\"">>
}
},
#{
expected => #{code => 412},
input =>

View File

@ -195,6 +195,29 @@ t_ctx_check_authz_complete(_) ->
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,
reason_code => <<"sucess">>,
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(_) ->
SQL =
<<"SELECT from_clientid, from_username, reason, topic, qos FROM \"$events/delivery_dropped\"">>,

View File

@ -0,0 +1 @@
Add new rule engine event `$events/client_check_authn_complete` for authentication completion event.

View File

@ -390,4 +390,13 @@ event_ctx_disconnected_reason.desc:
event_ctx_disconnected_reason.label:
"""Disconnect Reason"""
event_is_anonymous.desc:
"""True if this user is anonymous."""
event_is_superuser.desc:
"""True if this is a super user."""
event_ctx_authn_reason_code.desc:
"""The reason code"""
}