Merge pull request #12150 from zhongwencool/audit-filter-http-api

fix: audit create_at/duration_ms filter not working
This commit is contained in:
zhongwencool 2023-12-13 17:00:19 +08:00 committed by GitHub
commit 363fe4fd96
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 65 additions and 11 deletions

View File

@ -119,7 +119,7 @@ log_to_db(Log) ->
Audit0 = to_audit(Log), Audit0 = to_audit(Log),
Audit = Audit0#?AUDIT{ Audit = Audit0#?AUDIT{
node = node(), node = node(),
created_at = erlang:system_time(microsecond) created_at = erlang:system_time(millisecond)
}, },
mria:dirty_write(?AUDIT, Audit). mria:dirty_write(?AUDIT, Audit).

View File

@ -33,7 +33,7 @@
{<<"gte_created_at">>, timestamp}, {<<"gte_created_at">>, timestamp},
{<<"lte_created_at">>, timestamp}, {<<"lte_created_at">>, timestamp},
{<<"gte_duration_ms">>, timestamp}, {<<"gte_duration_ms">>, timestamp},
{<<"lte_duration_ms">>, timestamp} {<<"lte_duration_ms">>, integer}
]). ]).
-define(DISABLE_MSG, <<"Audit is disabled">>). -define(DISABLE_MSG, <<"Audit is disabled">>).
@ -290,16 +290,16 @@ gen_match_spec([{http_status_code, '=:=', T} | Qs], Audit, Conn) ->
gen_match_spec([{http_method, '=:=', T} | Qs], Audit, Conn) -> gen_match_spec([{http_method, '=:=', T} | Qs], Audit, Conn) ->
gen_match_spec(Qs, Audit#?AUDIT{http_method = T}, Conn); gen_match_spec(Qs, Audit#?AUDIT{http_method = T}, Conn);
gen_match_spec([{created_at, Hold, T} | Qs], Audit, Conn) -> gen_match_spec([{created_at, Hold, T} | Qs], Audit, Conn) ->
gen_match_spec(Qs, Audit#?AUDIT{created_at = '$1'}, [{'$1', Hold, T} | Conn]); gen_match_spec(Qs, Audit#?AUDIT{created_at = '$1'}, [{Hold, '$1', T} | Conn]);
gen_match_spec([{created_at, Hold1, T1, Hold2, T2} | Qs], Audit, Conn) -> gen_match_spec([{created_at, Hold1, T1, Hold2, T2} | Qs], Audit, Conn) ->
gen_match_spec(Qs, Audit#?AUDIT{created_at = '$1'}, [ gen_match_spec(Qs, Audit#?AUDIT{created_at = '$1'}, [
{'$1', Hold1, T1}, {'$1', Hold2, T2} | Conn {Hold1, '$1', T1}, {Hold2, '$1', T2} | Conn
]); ]);
gen_match_spec([{duration_ms, Hold, T} | Qs], Audit, Conn) -> gen_match_spec([{duration_ms, Hold, T} | Qs], Audit, Conn) ->
gen_match_spec(Qs, Audit#?AUDIT{duration_ms = '$2'}, [{'$2', Hold, T} | Conn]); gen_match_spec(Qs, Audit#?AUDIT{duration_ms = '$2'}, [{Hold, '$2', T} | Conn]);
gen_match_spec([{duration_ms, Hold1, T1, Hold2, T2} | Qs], Audit, Conn) -> gen_match_spec([{duration_ms, Hold1, T1, Hold2, T2} | Qs], Audit, Conn) ->
gen_match_spec(Qs, Audit#?AUDIT{duration_ms = '$2'}, [ gen_match_spec(Qs, Audit#?AUDIT{duration_ms = '$2'}, [
{'$2', Hold1, T1}, {'$2', Hold2, T2} | Conn {Hold1, '$2', T1}, {Hold2, '$2', T2} | Conn
]). ]).
format(Audit) -> format(Audit) ->

View File

@ -109,7 +109,7 @@ t_disabled(_) ->
Size1 = mnesia:table_info(emqx_audit, size), Size1 = mnesia:table_info(emqx_audit, size),
{ok, Logs} = emqx_mgmt_api_configs_SUITE:get_config("log"), {ok, Logs} = emqx_mgmt_api_configs_SUITE:get_config("log"),
Logs1 = emqx_utils_maps:deep_put([<<"audit">>, <<"max_filter_size">>], Logs, 100), Logs1 = emqx_utils_maps:deep_put([<<"audit">>, <<"max_filter_size">>], Logs, 199),
NewLogs = emqx_utils_maps:deep_put([<<"audit">>, <<"enable">>], Logs1, false), NewLogs = emqx_utils_maps:deep_put([<<"audit">>, <<"enable">>], Logs1, false),
{ok, _} = emqx_mgmt_api_configs_SUITE:update_config("log", NewLogs), {ok, _} = emqx_mgmt_api_configs_SUITE:update_config("log", NewLogs),
{ok, GetLog1} = emqx_mgmt_api_configs_SUITE:get_config("log"), {ok, GetLog1} = emqx_mgmt_api_configs_SUITE:get_config("log"),
@ -139,6 +139,11 @@ t_disabled(_) ->
ok. ok.
t_cli(_Config) -> t_cli(_Config) ->
Size = mnesia:table_info(emqx_audit, size),
TimeInt = erlang:system_time(millisecond) - 10,
Time = integer_to_list(TimeInt),
DateStr = calendar:system_time_to_rfc3339(TimeInt, [{unit, millisecond}]),
Date = emqx_http_lib:uri_encode(DateStr),
ok = emqx_ctl:run_command(["conf", "show", "log"]), ok = emqx_ctl:run_command(["conf", "show", "log"]),
AuditPath = emqx_mgmt_api_test_util:api_path(["audit"]), AuditPath = emqx_mgmt_api_test_util:api_path(["audit"]),
AuthHeader = emqx_mgmt_api_test_util:auth_header_(), AuthHeader = emqx_mgmt_api_test_util:auth_header_(),
@ -160,7 +165,7 @@ t_cli(_Config) ->
Data Data
), ),
%% check filter %% check cli filter
{ok, Res1} = emqx_mgmt_api_test_util:request_api(get, AuditPath, "from=cli", AuthHeader), {ok, Res1} = emqx_mgmt_api_test_util:request_api(get, AuditPath, "from=cli", AuthHeader),
#{<<"data">> := Data1} = emqx_utils_json:decode(Res1, [return_maps]), #{<<"data">> := Data1} = emqx_utils_json:decode(Res1, [return_maps]),
?assertEqual(Data, Data1), ?assertEqual(Data, Data1),
@ -168,10 +173,41 @@ t_cli(_Config) ->
get, AuditPath, "from=erlang_console", AuthHeader get, AuditPath, "from=erlang_console", AuthHeader
), ),
?assertMatch(#{<<"data">> := []}, emqx_utils_json:decode(Res2, [return_maps])), ?assertMatch(#{<<"data">> := []}, emqx_utils_json:decode(Res2, [return_maps])),
%% check created_at filter
{ok, Res3} = emqx_mgmt_api_test_util:request_api(
get, AuditPath, "gte_created_at=" ++ Time, AuthHeader
),
#{<<"data">> := Data3} = emqx_utils_json:decode(Res3, [return_maps]),
?assertEqual(1, erlang:length(Data3)),
{ok, Res31} = emqx_mgmt_api_test_util:request_api(
get, AuditPath, "gte_created_at=" ++ Date, AuthHeader
),
?assertEqual(Res3, Res31),
{ok, Res4} = emqx_mgmt_api_test_util:request_api(
get, AuditPath, "lte_created_at=" ++ Time, AuthHeader
),
#{<<"data">> := Data4} = emqx_utils_json:decode(Res4, [return_maps]),
?assertEqual(Size, erlang:length(Data4)),
{ok, Res41} = emqx_mgmt_api_test_util:request_api(
get, AuditPath, "lte_created_at=" ++ Date, AuthHeader
),
?assertEqual(Res4, Res41),
%% check duration_ms filter
{ok, Res5} = emqx_mgmt_api_test_util:request_api(
get, AuditPath, "gte_duration_ms=0", AuthHeader
),
#{<<"data">> := Data5} = emqx_utils_json:decode(Res5, [return_maps]),
?assertEqual(Size + 1, erlang:length(Data5)),
{ok, Res6} = emqx_mgmt_api_test_util:request_api(
get, AuditPath, "lte_duration_ms=-1", AuthHeader
),
?assertMatch(#{<<"data">> := []}, emqx_utils_json:decode(Res6, [return_maps])),
ok. ok.
t_max_size(_Config) -> t_max_size(_Config) ->
{ok, _} = emqx:update_config([log, audit, max_filter_size], 1000), {ok, _} = emqx:update_config([log, audit, max_filter_size], 999),
SizeFun = SizeFun =
fun() -> fun() ->
AuditPath = emqx_mgmt_api_test_util:api_path(["audit"]), AuditPath = emqx_mgmt_api_test_util:api_path(["audit"]),
@ -188,9 +224,14 @@ t_max_size(_Config) ->
end, end,
lists:duplicate(100, 1) lists:duplicate(100, 1)
), ),
timer:sleep(110), LogCount = wait_for_dirty_write_log_done(1500),
Size1 = SizeFun(), Size1 = SizeFun(),
?assert(Size1 - InitSize >= 100, {Size1, InitSize}), ?assert(Size1 - InitSize >= 100, #{
api => Size1,
init => InitSize,
log_size => LogCount,
config => emqx:get_config([log, audit, max_filter_size])
}),
{ok, _} = emqx:update_config([log, audit, max_filter_size], 10), {ok, _} = emqx:update_config([log, audit, max_filter_size], 10),
%% wait for clean_expired %% wait for clean_expired
timer:sleep(250), timer:sleep(250),
@ -246,3 +287,16 @@ kickout_clients() ->
{ok, Clients2} = emqx_mgmt_api_test_util:request_api(get, ClientsPath), {ok, Clients2} = emqx_mgmt_api_test_util:request_api(get, ClientsPath),
ClientsResponse2 = emqx_utils_json:decode(Clients2, [return_maps]), ClientsResponse2 = emqx_utils_json:decode(Clients2, [return_maps]),
?assertMatch(#{<<"data">> := []}, ClientsResponse2). ?assertMatch(#{<<"data">> := []}, ClientsResponse2).
wait_for_dirty_write_log_done(MaxMs) ->
Size = mnesia:table_info(emqx_audit, size),
wait_for_dirty_write_log_done(Size, MaxMs).
wait_for_dirty_write_log_done(Size, RemainMs) when RemainMs =< 0 -> Size;
wait_for_dirty_write_log_done(Prev, RemainMs) ->
SleepMs = 100,
ct:sleep(SleepMs),
case mnesia:table_info(emqx_audit, size) of
Prev -> Prev;
New -> wait_for_dirty_write_log_done(New, RemainMs - SleepMs)
end.